Version Description
- Fixed #1586
Download this release
Release Info
Developer | Unyson |
Plugin | Unyson |
Version | 2.5.5 |
Comparing to | |
See all releases |
Code changes from version 2.5.4 to 2.5.5
- framework/LICENSE +674 -674
- framework/bootstrap-helpers.php +130 -130
- framework/bootstrap.php +103 -103
- framework/core/Fw.php +91 -91
- framework/core/class-fw-manifest.php +509 -509
- framework/core/components/backend.php +2146 -2146
- framework/core/components/extensions.php +661 -661
- framework/core/components/extensions/class-fw-extension-default.php +11 -11
- framework/core/components/extensions/manager/available-extensions.php +273 -273
- framework/core/components/extensions/manager/class--fw-extensions-manager.php +3219 -3219
- framework/core/components/extensions/manager/includes/class--fw-extensions-delete-upgrader-skin.php +28 -28
- framework/core/components/extensions/manager/includes/class--fw-extensions-install-upgrader-skin.php +28 -28
- framework/core/components/extensions/manager/includes/parsedown/LICENSE.txt +19 -19
- framework/core/components/extensions/manager/includes/parsedown/Parsedown.php +1527 -1527
- framework/core/components/extensions/manager/static/extension-page.css +22 -22
- framework/core/components/extensions/manager/static/extension-page.js +2 -2
- framework/core/components/extensions/manager/static/extensions-page.css +236 -236
- framework/core/components/extensions/manager/static/extensions-page.js +107 -107
- framework/core/components/extensions/manager/static/unyson-font-icon/fonts/icomoon.svg +10 -10
- framework/core/components/extensions/manager/static/unyson-font-icon/style.css +28 -28
- framework/core/components/extensions/manager/views/delete-form.php +54 -54
- framework/core/components/extensions/manager/views/extension-page-header.php +50 -50
- framework/core/components/extensions/manager/views/extension.php +360 -360
- framework/core/components/extensions/manager/views/extensions-page.php +218 -218
- framework/core/components/extensions/manager/views/install-form.php +51 -51
- framework/core/components/theme.php +203 -203
- framework/core/extends/class-fw-container-type.php +232 -232
- framework/core/extends/class-fw-extension.php +507 -507
- framework/core/extends/class-fw-option-type.php +397 -397
- framework/core/extends/interface-fw-option-handler.php +12 -12
- framework/extensions/blog/class-fw-extension-blog.php +89 -89
- framework/extensions/blog/manifest.php +13 -13
- framework/extensions/update/class-fw-extension-update.php +982 -982
- framework/extensions/update/config.php +9 -9
- framework/extensions/update/extensions/github-update/class-fw-extension-github-update.php +443 -443
- framework/extensions/update/extensions/github-update/manifest.php +5 -5
- framework/extensions/update/includes/classes/class--fw-ext-update-extensions-list-table.php +128 -128
- framework/extensions/update/includes/classes/class--fw-ext-update-extensions-upgrader-skin.php +36 -36
- framework/extensions/update/includes/classes/class--fw-ext-update-framework-upgrader-skin.php +33 -33
- framework/extensions/update/includes/classes/class--fw-ext-update-theme-upgrader-skin.php +33 -33
- framework/extensions/update/includes/extends/class-fw-ext-update-service.php +105 -105
- framework/extensions/update/manifest.php +10 -10
- framework/extensions/update/static.php +14 -14
- framework/extensions/update/static/css/admin-update-page.css +2 -2
- framework/extensions/update/views/updates-list.php +113 -113
- framework/helpers/class-fw-access-key.php +43 -43
- framework/helpers/class-fw-cache.php +302 -302
- framework/helpers/class-fw-dumper.php +123 -123
- framework/helpers/class-fw-flash-messages.php +312 -312
- framework/helpers/class-fw-form.php +585 -585
- framework/helpers/class-fw-request.php +90 -90
- framework/helpers/class-fw-resize.php +177 -177
- framework/helpers/class-fw-session.php +36 -36
- framework/helpers/class-fw-wp-filesystem.php +312 -312
- framework/helpers/class-fw-wp-list-table.php +968 -968
- framework/helpers/class-fw-wp-meta.php +113 -113
- framework/helpers/class-fw-wp-option.php +84 -84
- framework/helpers/database.php +540 -884
framework/LICENSE
CHANGED
@@ -1,674 +1,674 @@
|
|
1 |
-
GNU GENERAL PUBLIC LICENSE
|
2 |
-
Version 3, 29 June 2007
|
3 |
-
|
4 |
-
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
5 |
-
Everyone is permitted to copy and distribute verbatim copies
|
6 |
-
of this license document, but changing it is not allowed.
|
7 |
-
|
8 |
-
Preamble
|
9 |
-
|
10 |
-
The GNU General Public License is a free, copyleft license for
|
11 |
-
software and other kinds of works.
|
12 |
-
|
13 |
-
The licenses for most software and other practical works are designed
|
14 |
-
to take away your freedom to share and change the works. By contrast,
|
15 |
-
the GNU General Public License is intended to guarantee your freedom to
|
16 |
-
share and change all versions of a program--to make sure it remains free
|
17 |
-
software for all its users. We, the Free Software Foundation, use the
|
18 |
-
GNU General Public License for most of our software; it applies also to
|
19 |
-
any other work released this way by its authors. You can apply it to
|
20 |
-
your programs, too.
|
21 |
-
|
22 |
-
When we speak of free software, we are referring to freedom, not
|
23 |
-
price. Our General Public Licenses are designed to make sure that you
|
24 |
-
have the freedom to distribute copies of free software (and charge for
|
25 |
-
them if you wish), that you receive source code or can get it if you
|
26 |
-
want it, that you can change the software or use pieces of it in new
|
27 |
-
free programs, and that you know you can do these things.
|
28 |
-
|
29 |
-
To protect your rights, we need to prevent others from denying you
|
30 |
-
these rights or asking you to surrender the rights. Therefore, you have
|
31 |
-
certain responsibilities if you distribute copies of the software, or if
|
32 |
-
you modify it: responsibilities to respect the freedom of others.
|
33 |
-
|
34 |
-
For example, if you distribute copies of such a program, whether
|
35 |
-
gratis or for a fee, you must pass on to the recipients the same
|
36 |
-
freedoms that you received. You must make sure that they, too, receive
|
37 |
-
or can get the source code. And you must show them these terms so they
|
38 |
-
know their rights.
|
39 |
-
|
40 |
-
Developers that use the GNU GPL protect your rights with two steps:
|
41 |
-
(1) assert copyright on the software, and (2) offer you this License
|
42 |
-
giving you legal permission to copy, distribute and/or modify it.
|
43 |
-
|
44 |
-
For the developers' and authors' protection, the GPL clearly explains
|
45 |
-
that there is no warranty for this free software. For both users' and
|
46 |
-
authors' sake, the GPL requires that modified versions be marked as
|
47 |
-
changed, so that their problems will not be attributed erroneously to
|
48 |
-
authors of previous versions.
|
49 |
-
|
50 |
-
Some devices are designed to deny users access to install or run
|
51 |
-
modified versions of the software inside them, although the manufacturer
|
52 |
-
can do so. This is fundamentally incompatible with the aim of
|
53 |
-
protecting users' freedom to change the software. The systematic
|
54 |
-
pattern of such abuse occurs in the area of products for individuals to
|
55 |
-
use, which is precisely where it is most unacceptable. Therefore, we
|
56 |
-
have designed this version of the GPL to prohibit the practice for those
|
57 |
-
products. If such problems arise substantially in other domains, we
|
58 |
-
stand ready to extend this provision to those domains in future versions
|
59 |
-
of the GPL, as needed to protect the freedom of users.
|
60 |
-
|
61 |
-
Finally, every program is threatened constantly by software patents.
|
62 |
-
States should not allow patents to restrict development and use of
|
63 |
-
software on general-purpose computers, but in those that do, we wish to
|
64 |
-
avoid the special danger that patents applied to a free program could
|
65 |
-
make it effectively proprietary. To prevent this, the GPL assures that
|
66 |
-
patents cannot be used to render the program non-free.
|
67 |
-
|
68 |
-
The precise terms and conditions for copying, distribution and
|
69 |
-
modification follow.
|
70 |
-
|
71 |
-
TERMS AND CONDITIONS
|
72 |
-
|
73 |
-
0. Definitions.
|
74 |
-
|
75 |
-
"This License" refers to version 3 of the GNU General Public License.
|
76 |
-
|
77 |
-
"Copyright" also means copyright-like laws that apply to other kinds of
|
78 |
-
works, such as semiconductor masks.
|
79 |
-
|
80 |
-
"The Program" refers to any copyrightable work licensed under this
|
81 |
-
License. Each licensee is addressed as "you". "Licensees" and
|
82 |
-
"recipients" may be individuals or organizations.
|
83 |
-
|
84 |
-
To "modify" a work means to copy from or adapt all or part of the work
|
85 |
-
in a fashion requiring copyright permission, other than the making of an
|
86 |
-
exact copy. The resulting work is called a "modified version" of the
|
87 |
-
earlier work or a work "based on" the earlier work.
|
88 |
-
|
89 |
-
A "covered work" means either the unmodified Program or a work based
|
90 |
-
on the Program.
|
91 |
-
|
92 |
-
To "propagate" a work means to do anything with it that, without
|
93 |
-
permission, would make you directly or secondarily liable for
|
94 |
-
infringement under applicable copyright law, except executing it on a
|
95 |
-
computer or modifying a private copy. Propagation includes copying,
|
96 |
-
distribution (with or without modification), making available to the
|
97 |
-
public, and in some countries other activities as well.
|
98 |
-
|
99 |
-
To "convey" a work means any kind of propagation that enables other
|
100 |
-
parties to make or receive copies. Mere interaction with a user through
|
101 |
-
a computer network, with no transfer of a copy, is not conveying.
|
102 |
-
|
103 |
-
An interactive user interface displays "Appropriate Legal Notices"
|
104 |
-
to the extent that it includes a convenient and prominently visible
|
105 |
-
feature that (1) displays an appropriate copyright notice, and (2)
|
106 |
-
tells the user that there is no warranty for the work (except to the
|
107 |
-
extent that warranties are provided), that licensees may convey the
|
108 |
-
work under this License, and how to view a copy of this License. If
|
109 |
-
the interface presents a list of user commands or options, such as a
|
110 |
-
menu, a prominent item in the list meets this criterion.
|
111 |
-
|
112 |
-
1. Source Code.
|
113 |
-
|
114 |
-
The "source code" for a work means the preferred form of the work
|
115 |
-
for making modifications to it. "Object code" means any non-source
|
116 |
-
form of a work.
|
117 |
-
|
118 |
-
A "Standard Interface" means an interface that either is an official
|
119 |
-
standard defined by a recognized standards body, or, in the case of
|
120 |
-
interfaces specified for a particular programming language, one that
|
121 |
-
is widely used among developers working in that language.
|
122 |
-
|
123 |
-
The "System Libraries" of an executable work include anything, other
|
124 |
-
than the work as a whole, that (a) is included in the normal form of
|
125 |
-
packaging a Major Component, but which is not part of that Major
|
126 |
-
Component, and (b) serves only to enable use of the work with that
|
127 |
-
Major Component, or to implement a Standard Interface for which an
|
128 |
-
implementation is available to the public in source code form. A
|
129 |
-
"Major Component", in this context, means a major essential component
|
130 |
-
(kernel, window system, and so on) of the specific operating system
|
131 |
-
(if any) on which the executable work runs, or a compiler used to
|
132 |
-
produce the work, or an object code interpreter used to run it.
|
133 |
-
|
134 |
-
The "Corresponding Source" for a work in object code form means all
|
135 |
-
the source code needed to generate, install, and (for an executable
|
136 |
-
work) run the object code and to modify the work, including scripts to
|
137 |
-
control those activities. However, it does not include the work's
|
138 |
-
System Libraries, or general-purpose tools or generally available free
|
139 |
-
programs which are used unmodified in performing those activities but
|
140 |
-
which are not part of the work. For example, Corresponding Source
|
141 |
-
includes interface definition files associated with source files for
|
142 |
-
the work, and the source code for shared libraries and dynamically
|
143 |
-
linked subprograms that the work is specifically designed to require,
|
144 |
-
such as by intimate data communication or control flow between those
|
145 |
-
subprograms and other parts of the work.
|
146 |
-
|
147 |
-
The Corresponding Source need not include anything that users
|
148 |
-
can regenerate automatically from other parts of the Corresponding
|
149 |
-
Source.
|
150 |
-
|
151 |
-
The Corresponding Source for a work in source code form is that
|
152 |
-
same work.
|
153 |
-
|
154 |
-
2. Basic Permissions.
|
155 |
-
|
156 |
-
All rights granted under this License are granted for the term of
|
157 |
-
copyright on the Program, and are irrevocable provided the stated
|
158 |
-
conditions are met. This License explicitly affirms your unlimited
|
159 |
-
permission to run the unmodified Program. The output from running a
|
160 |
-
covered work is covered by this License only if the output, given its
|
161 |
-
content, constitutes a covered work. This License acknowledges your
|
162 |
-
rights of fair use or other equivalent, as provided by copyright law.
|
163 |
-
|
164 |
-
You may make, run and propagate covered works that you do not
|
165 |
-
convey, without conditions so long as your license otherwise remains
|
166 |
-
in force. You may convey covered works to others for the sole purpose
|
167 |
-
of having them make modifications exclusively for you, or provide you
|
168 |
-
with facilities for running those works, provided that you comply with
|
169 |
-
the terms of this License in conveying all material for which you do
|
170 |
-
not control copyright. Those thus making or running the covered works
|
171 |
-
for you must do so exclusively on your behalf, under your direction
|
172 |
-
and control, on terms that prohibit them from making any copies of
|
173 |
-
your copyrighted material outside their relationship with you.
|
174 |
-
|
175 |
-
Conveying under any other circumstances is permitted solely under
|
176 |
-
the conditions stated below. Sublicensing is not allowed; section 10
|
177 |
-
makes it unnecessary.
|
178 |
-
|
179 |
-
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
180 |
-
|
181 |
-
No covered work shall be deemed part of an effective technological
|
182 |
-
measure under any applicable law fulfilling obligations under article
|
183 |
-
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
184 |
-
similar laws prohibiting or restricting circumvention of such
|
185 |
-
measures.
|
186 |
-
|
187 |
-
When you convey a covered work, you waive any legal power to forbid
|
188 |
-
circumvention of technological measures to the extent such circumvention
|
189 |
-
is effected by exercising rights under this License with respect to
|
190 |
-
the covered work, and you disclaim any intention to limit operation or
|
191 |
-
modification of the work as a means of enforcing, against the work's
|
192 |
-
users, your or third parties' legal rights to forbid circumvention of
|
193 |
-
technological measures.
|
194 |
-
|
195 |
-
4. Conveying Verbatim Copies.
|
196 |
-
|
197 |
-
You may convey verbatim copies of the Program's source code as you
|
198 |
-
receive it, in any medium, provided that you conspicuously and
|
199 |
-
appropriately publish on each copy an appropriate copyright notice;
|
200 |
-
keep intact all notices stating that this License and any
|
201 |
-
non-permissive terms added in accord with section 7 apply to the code;
|
202 |
-
keep intact all notices of the absence of any warranty; and give all
|
203 |
-
recipients a copy of this License along with the Program.
|
204 |
-
|
205 |
-
You may charge any price or no price for each copy that you convey,
|
206 |
-
and you may offer support or warranty protection for a fee.
|
207 |
-
|
208 |
-
5. Conveying Modified Source Versions.
|
209 |
-
|
210 |
-
You may convey a work based on the Program, or the modifications to
|
211 |
-
produce it from the Program, in the form of source code under the
|
212 |
-
terms of section 4, provided that you also meet all of these conditions:
|
213 |
-
|
214 |
-
a) The work must carry prominent notices stating that you modified
|
215 |
-
it, and giving a relevant date.
|
216 |
-
|
217 |
-
b) The work must carry prominent notices stating that it is
|
218 |
-
released under this License and any conditions added under section
|
219 |
-
7. This requirement modifies the requirement in section 4 to
|
220 |
-
"keep intact all notices".
|
221 |
-
|
222 |
-
c) You must license the entire work, as a whole, under this
|
223 |
-
License to anyone who comes into possession of a copy. This
|
224 |
-
License will therefore apply, along with any applicable section 7
|
225 |
-
additional terms, to the whole of the work, and all its parts,
|
226 |
-
regardless of how they are packaged. This License gives no
|
227 |
-
permission to license the work in any other way, but it does not
|
228 |
-
invalidate such permission if you have separately received it.
|
229 |
-
|
230 |
-
d) If the work has interactive user interfaces, each must display
|
231 |
-
Appropriate Legal Notices; however, if the Program has interactive
|
232 |
-
interfaces that do not display Appropriate Legal Notices, your
|
233 |
-
work need not make them do so.
|
234 |
-
|
235 |
-
A compilation of a covered work with other separate and independent
|
236 |
-
works, which are not by their nature extensions of the covered work,
|
237 |
-
and which are not combined with it such as to form a larger program,
|
238 |
-
in or on a volume of a storage or distribution medium, is called an
|
239 |
-
"aggregate" if the compilation and its resulting copyright are not
|
240 |
-
used to limit the access or legal rights of the compilation's users
|
241 |
-
beyond what the individual works permit. Inclusion of a covered work
|
242 |
-
in an aggregate does not cause this License to apply to the other
|
243 |
-
parts of the aggregate.
|
244 |
-
|
245 |
-
6. Conveying Non-Source Forms.
|
246 |
-
|
247 |
-
You may convey a covered work in object code form under the terms
|
248 |
-
of sections 4 and 5, provided that you also convey the
|
249 |
-
machine-readable Corresponding Source under the terms of this License,
|
250 |
-
in one of these ways:
|
251 |
-
|
252 |
-
a) Convey the object code in, or embodied in, a physical product
|
253 |
-
(including a physical distribution medium), accompanied by the
|
254 |
-
Corresponding Source fixed on a durable physical medium
|
255 |
-
customarily used for software interchange.
|
256 |
-
|
257 |
-
b) Convey the object code in, or embodied in, a physical product
|
258 |
-
(including a physical distribution medium), accompanied by a
|
259 |
-
written offer, valid for at least three years and valid for as
|
260 |
-
long as you offer spare parts or customer support for that product
|
261 |
-
model, to give anyone who possesses the object code either (1) a
|
262 |
-
copy of the Corresponding Source for all the software in the
|
263 |
-
product that is covered by this License, on a durable physical
|
264 |
-
medium customarily used for software interchange, for a price no
|
265 |
-
more than your reasonable cost of physically performing this
|
266 |
-
conveying of source, or (2) access to copy the
|
267 |
-
Corresponding Source from a network server at no charge.
|
268 |
-
|
269 |
-
c) Convey individual copies of the object code with a copy of the
|
270 |
-
written offer to provide the Corresponding Source. This
|
271 |
-
alternative is allowed only occasionally and noncommercially, and
|
272 |
-
only if you received the object code with such an offer, in accord
|
273 |
-
with subsection 6b.
|
274 |
-
|
275 |
-
d) Convey the object code by offering access from a designated
|
276 |
-
place (gratis or for a charge), and offer equivalent access to the
|
277 |
-
Corresponding Source in the same way through the same place at no
|
278 |
-
further charge. You need not require recipients to copy the
|
279 |
-
Corresponding Source along with the object code. If the place to
|
280 |
-
copy the object code is a network server, the Corresponding Source
|
281 |
-
may be on a different server (operated by you or a third party)
|
282 |
-
that supports equivalent copying facilities, provided you maintain
|
283 |
-
clear directions next to the object code saying where to find the
|
284 |
-
Corresponding Source. Regardless of what server hosts the
|
285 |
-
Corresponding Source, you remain obligated to ensure that it is
|
286 |
-
available for as long as needed to satisfy these requirements.
|
287 |
-
|
288 |
-
e) Convey the object code using peer-to-peer transmission, provided
|
289 |
-
you inform other peers where the object code and Corresponding
|
290 |
-
Source of the work are being offered to the general public at no
|
291 |
-
charge under subsection 6d.
|
292 |
-
|
293 |
-
A separable portion of the object code, whose source code is excluded
|
294 |
-
from the Corresponding Source as a System Library, need not be
|
295 |
-
included in conveying the object code work.
|
296 |
-
|
297 |
-
A "User Product" is either (1) a "consumer product", which means any
|
298 |
-
tangible personal property which is normally used for personal, family,
|
299 |
-
or household purposes, or (2) anything designed or sold for incorporation
|
300 |
-
into a dwelling. In determining whether a product is a consumer product,
|
301 |
-
doubtful cases shall be resolved in favor of coverage. For a particular
|
302 |
-
product received by a particular user, "normally used" refers to a
|
303 |
-
typical or common use of that class of product, regardless of the status
|
304 |
-
of the particular user or of the way in which the particular user
|
305 |
-
actually uses, or expects or is expected to use, the product. A product
|
306 |
-
is a consumer product regardless of whether the product has substantial
|
307 |
-
commercial, industrial or non-consumer uses, unless such uses represent
|
308 |
-
the only significant mode of use of the product.
|
309 |
-
|
310 |
-
"Installation Information" for a User Product means any methods,
|
311 |
-
procedures, authorization keys, or other information required to install
|
312 |
-
and execute modified versions of a covered work in that User Product from
|
313 |
-
a modified version of its Corresponding Source. The information must
|
314 |
-
suffice to ensure that the continued functioning of the modified object
|
315 |
-
code is in no case prevented or interfered with solely because
|
316 |
-
modification has been made.
|
317 |
-
|
318 |
-
If you convey an object code work under this section in, or with, or
|
319 |
-
specifically for use in, a User Product, and the conveying occurs as
|
320 |
-
part of a transaction in which the right of possession and use of the
|
321 |
-
User Product is transferred to the recipient in perpetuity or for a
|
322 |
-
fixed term (regardless of how the transaction is characterized), the
|
323 |
-
Corresponding Source conveyed under this section must be accompanied
|
324 |
-
by the Installation Information. But this requirement does not apply
|
325 |
-
if neither you nor any third party retains the ability to install
|
326 |
-
modified object code on the User Product (for example, the work has
|
327 |
-
been installed in ROM).
|
328 |
-
|
329 |
-
The requirement to provide Installation Information does not include a
|
330 |
-
requirement to continue to provide support service, warranty, or updates
|
331 |
-
for a work that has been modified or installed by the recipient, or for
|
332 |
-
the User Product in which it has been modified or installed. Access to a
|
333 |
-
network may be denied when the modification itself materially and
|
334 |
-
adversely affects the operation of the network or violates the rules and
|
335 |
-
protocols for communication across the network.
|
336 |
-
|
337 |
-
Corresponding Source conveyed, and Installation Information provided,
|
338 |
-
in accord with this section must be in a format that is publicly
|
339 |
-
documented (and with an implementation available to the public in
|
340 |
-
source code form), and must require no special password or key for
|
341 |
-
unpacking, reading or copying.
|
342 |
-
|
343 |
-
7. Additional Terms.
|
344 |
-
|
345 |
-
"Additional permissions" are terms that supplement the terms of this
|
346 |
-
License by making exceptions from one or more of its conditions.
|
347 |
-
Additional permissions that are applicable to the entire Program shall
|
348 |
-
be treated as though they were included in this License, to the extent
|
349 |
-
that they are valid under applicable law. If additional permissions
|
350 |
-
apply only to part of the Program, that part may be used separately
|
351 |
-
under those permissions, but the entire Program remains governed by
|
352 |
-
this License without regard to the additional permissions.
|
353 |
-
|
354 |
-
When you convey a copy of a covered work, you may at your option
|
355 |
-
remove any additional permissions from that copy, or from any part of
|
356 |
-
it. (Additional permissions may be written to require their own
|
357 |
-
removal in certain cases when you modify the work.) You may place
|
358 |
-
additional permissions on material, added by you to a covered work,
|
359 |
-
for which you have or can give appropriate copyright permission.
|
360 |
-
|
361 |
-
Notwithstanding any other provision of this License, for material you
|
362 |
-
add to a covered work, you may (if authorized by the copyright holders of
|
363 |
-
that material) supplement the terms of this License with terms:
|
364 |
-
|
365 |
-
a) Disclaiming warranty or limiting liability differently from the
|
366 |
-
terms of sections 15 and 16 of this License; or
|
367 |
-
|
368 |
-
b) Requiring preservation of specified reasonable legal notices or
|
369 |
-
author attributions in that material or in the Appropriate Legal
|
370 |
-
Notices displayed by works containing it; or
|
371 |
-
|
372 |
-
c) Prohibiting misrepresentation of the origin of that material, or
|
373 |
-
requiring that modified versions of such material be marked in
|
374 |
-
reasonable ways as different from the original version; or
|
375 |
-
|
376 |
-
d) Limiting the use for publicity purposes of names of licensors or
|
377 |
-
authors of the material; or
|
378 |
-
|
379 |
-
e) Declining to grant rights under trademark law for use of some
|
380 |
-
trade names, trademarks, or service marks; or
|
381 |
-
|
382 |
-
f) Requiring indemnification of licensors and authors of that
|
383 |
-
material by anyone who conveys the material (or modified versions of
|
384 |
-
it) with contractual assumptions of liability to the recipient, for
|
385 |
-
any liability that these contractual assumptions directly impose on
|
386 |
-
those licensors and authors.
|
387 |
-
|
388 |
-
All other non-permissive additional terms are considered "further
|
389 |
-
restrictions" within the meaning of section 10. If the Program as you
|
390 |
-
received it, or any part of it, contains a notice stating that it is
|
391 |
-
governed by this License along with a term that is a further
|
392 |
-
restriction, you may remove that term. If a license document contains
|
393 |
-
a further restriction but permits relicensing or conveying under this
|
394 |
-
License, you may add to a covered work material governed by the terms
|
395 |
-
of that license document, provided that the further restriction does
|
396 |
-
not survive such relicensing or conveying.
|
397 |
-
|
398 |
-
If you add terms to a covered work in accord with this section, you
|
399 |
-
must place, in the relevant source files, a statement of the
|
400 |
-
additional terms that apply to those files, or a notice indicating
|
401 |
-
where to find the applicable terms.
|
402 |
-
|
403 |
-
Additional terms, permissive or non-permissive, may be stated in the
|
404 |
-
form of a separately written license, or stated as exceptions;
|
405 |
-
the above requirements apply either way.
|
406 |
-
|
407 |
-
8. Termination.
|
408 |
-
|
409 |
-
You may not propagate or modify a covered work except as expressly
|
410 |
-
provided under this License. Any attempt otherwise to propagate or
|
411 |
-
modify it is void, and will automatically terminate your rights under
|
412 |
-
this License (including any patent licenses granted under the third
|
413 |
-
paragraph of section 11).
|
414 |
-
|
415 |
-
However, if you cease all violation of this License, then your
|
416 |
-
license from a particular copyright holder is reinstated (a)
|
417 |
-
provisionally, unless and until the copyright holder explicitly and
|
418 |
-
finally terminates your license, and (b) permanently, if the copyright
|
419 |
-
holder fails to notify you of the violation by some reasonable means
|
420 |
-
prior to 60 days after the cessation.
|
421 |
-
|
422 |
-
Moreover, your license from a particular copyright holder is
|
423 |
-
reinstated permanently if the copyright holder notifies you of the
|
424 |
-
violation by some reasonable means, this is the first time you have
|
425 |
-
received notice of violation of this License (for any work) from that
|
426 |
-
copyright holder, and you cure the violation prior to 30 days after
|
427 |
-
your receipt of the notice.
|
428 |
-
|
429 |
-
Termination of your rights under this section does not terminate the
|
430 |
-
licenses of parties who have received copies or rights from you under
|
431 |
-
this License. If your rights have been terminated and not permanently
|
432 |
-
reinstated, you do not qualify to receive new licenses for the same
|
433 |
-
material under section 10.
|
434 |
-
|
435 |
-
9. Acceptance Not Required for Having Copies.
|
436 |
-
|
437 |
-
You are not required to accept this License in order to receive or
|
438 |
-
run a copy of the Program. Ancillary propagation of a covered work
|
439 |
-
occurring solely as a consequence of using peer-to-peer transmission
|
440 |
-
to receive a copy likewise does not require acceptance. However,
|
441 |
-
nothing other than this License grants you permission to propagate or
|
442 |
-
modify any covered work. These actions infringe copyright if you do
|
443 |
-
not accept this License. Therefore, by modifying or propagating a
|
444 |
-
covered work, you indicate your acceptance of this License to do so.
|
445 |
-
|
446 |
-
10. Automatic Licensing of Downstream Recipients.
|
447 |
-
|
448 |
-
Each time you convey a covered work, the recipient automatically
|
449 |
-
receives a license from the original licensors, to run, modify and
|
450 |
-
propagate that work, subject to this License. You are not responsible
|
451 |
-
for enforcing compliance by third parties with this License.
|
452 |
-
|
453 |
-
An "entity transaction" is a transaction transferring control of an
|
454 |
-
organization, or substantially all assets of one, or subdividing an
|
455 |
-
organization, or merging organizations. If propagation of a covered
|
456 |
-
work results from an entity transaction, each party to that
|
457 |
-
transaction who receives a copy of the work also receives whatever
|
458 |
-
licenses to the work the party's predecessor in interest had or could
|
459 |
-
give under the previous paragraph, plus a right to possession of the
|
460 |
-
Corresponding Source of the work from the predecessor in interest, if
|
461 |
-
the predecessor has it or can get it with reasonable efforts.
|
462 |
-
|
463 |
-
You may not impose any further restrictions on the exercise of the
|
464 |
-
rights granted or affirmed under this License. For example, you may
|
465 |
-
not impose a license fee, royalty, or other charge for exercise of
|
466 |
-
rights granted under this License, and you may not initiate litigation
|
467 |
-
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
468 |
-
any patent claim is infringed by making, using, selling, offering for
|
469 |
-
sale, or importing the Program or any portion of it.
|
470 |
-
|
471 |
-
11. Patents.
|
472 |
-
|
473 |
-
A "contributor" is a copyright holder who authorizes use under this
|
474 |
-
License of the Program or a work on which the Program is based. The
|
475 |
-
work thus licensed is called the contributor's "contributor version".
|
476 |
-
|
477 |
-
A contributor's "essential patent claims" are all patent claims
|
478 |
-
owned or controlled by the contributor, whether already acquired or
|
479 |
-
hereafter acquired, that would be infringed by some manner, permitted
|
480 |
-
by this License, of making, using, or selling its contributor version,
|
481 |
-
but do not include claims that would be infringed only as a
|
482 |
-
consequence of further modification of the contributor version. For
|
483 |
-
purposes of this definition, "control" includes the right to grant
|
484 |
-
patent sublicenses in a manner consistent with the requirements of
|
485 |
-
this License.
|
486 |
-
|
487 |
-
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
488 |
-
patent license under the contributor's essential patent claims, to
|
489 |
-
make, use, sell, offer for sale, import and otherwise run, modify and
|
490 |
-
propagate the contents of its contributor version.
|
491 |
-
|
492 |
-
In the following three paragraphs, a "patent license" is any express
|
493 |
-
agreement or commitment, however denominated, not to enforce a patent
|
494 |
-
(such as an express permission to practice a patent or covenant not to
|
495 |
-
sue for patent infringement). To "grant" such a patent license to a
|
496 |
-
party means to make such an agreement or commitment not to enforce a
|
497 |
-
patent against the party.
|
498 |
-
|
499 |
-
If you convey a covered work, knowingly relying on a patent license,
|
500 |
-
and the Corresponding Source of the work is not available for anyone
|
501 |
-
to copy, free of charge and under the terms of this License, through a
|
502 |
-
publicly available network server or other readily accessible means,
|
503 |
-
then you must either (1) cause the Corresponding Source to be so
|
504 |
-
available, or (2) arrange to deprive yourself of the benefit of the
|
505 |
-
patent license for this particular work, or (3) arrange, in a manner
|
506 |
-
consistent with the requirements of this License, to extend the patent
|
507 |
-
license to downstream recipients. "Knowingly relying" means you have
|
508 |
-
actual knowledge that, but for the patent license, your conveying the
|
509 |
-
covered work in a country, or your recipient's use of the covered work
|
510 |
-
in a country, would infringe one or more identifiable patents in that
|
511 |
-
country that you have reason to believe are valid.
|
512 |
-
|
513 |
-
If, pursuant to or in connection with a single transaction or
|
514 |
-
arrangement, you convey, or propagate by procuring conveyance of, a
|
515 |
-
covered work, and grant a patent license to some of the parties
|
516 |
-
receiving the covered work authorizing them to use, propagate, modify
|
517 |
-
or convey a specific copy of the covered work, then the patent license
|
518 |
-
you grant is automatically extended to all recipients of the covered
|
519 |
-
work and works based on it.
|
520 |
-
|
521 |
-
A patent license is "discriminatory" if it does not include within
|
522 |
-
the scope of its coverage, prohibits the exercise of, or is
|
523 |
-
conditioned on the non-exercise of one or more of the rights that are
|
524 |
-
specifically granted under this License. You may not convey a covered
|
525 |
-
work if you are a party to an arrangement with a third party that is
|
526 |
-
in the business of distributing software, under which you make payment
|
527 |
-
to the third party based on the extent of your activity of conveying
|
528 |
-
the work, and under which the third party grants, to any of the
|
529 |
-
parties who would receive the covered work from you, a discriminatory
|
530 |
-
patent license (a) in connection with copies of the covered work
|
531 |
-
conveyed by you (or copies made from those copies), or (b) primarily
|
532 |
-
for and in connection with specific products or compilations that
|
533 |
-
contain the covered work, unless you entered into that arrangement,
|
534 |
-
or that patent license was granted, prior to 28 March 2007.
|
535 |
-
|
536 |
-
Nothing in this License shall be construed as excluding or limiting
|
537 |
-
any implied license or other defenses to infringement that may
|
538 |
-
otherwise be available to you under applicable patent law.
|
539 |
-
|
540 |
-
12. No Surrender of Others' Freedom.
|
541 |
-
|
542 |
-
If conditions are imposed on you (whether by court order, agreement or
|
543 |
-
otherwise) that contradict the conditions of this License, they do not
|
544 |
-
excuse you from the conditions of this License. If you cannot convey a
|
545 |
-
covered work so as to satisfy simultaneously your obligations under this
|
546 |
-
License and any other pertinent obligations, then as a consequence you may
|
547 |
-
not convey it at all. For example, if you agree to terms that obligate you
|
548 |
-
to collect a royalty for further conveying from those to whom you convey
|
549 |
-
the Program, the only way you could satisfy both those terms and this
|
550 |
-
License would be to refrain entirely from conveying the Program.
|
551 |
-
|
552 |
-
13. Use with the GNU Affero General Public License.
|
553 |
-
|
554 |
-
Notwithstanding any other provision of this License, you have
|
555 |
-
permission to link or combine any covered work with a work licensed
|
556 |
-
under version 3 of the GNU Affero General Public License into a single
|
557 |
-
combined work, and to convey the resulting work. The terms of this
|
558 |
-
License will continue to apply to the part which is the covered work,
|
559 |
-
but the special requirements of the GNU Affero General Public License,
|
560 |
-
section 13, concerning interaction through a network will apply to the
|
561 |
-
combination as such.
|
562 |
-
|
563 |
-
14. Revised Versions of this License.
|
564 |
-
|
565 |
-
The Free Software Foundation may publish revised and/or new versions of
|
566 |
-
the GNU General Public License from time to time. Such new versions will
|
567 |
-
be similar in spirit to the present version, but may differ in detail to
|
568 |
-
address new problems or concerns.
|
569 |
-
|
570 |
-
Each version is given a distinguishing version number. If the
|
571 |
-
Program specifies that a certain numbered version of the GNU General
|
572 |
-
Public License "or any later version" applies to it, you have the
|
573 |
-
option of following the terms and conditions either of that numbered
|
574 |
-
version or of any later version published by the Free Software
|
575 |
-
Foundation. If the Program does not specify a version number of the
|
576 |
-
GNU General Public License, you may choose any version ever published
|
577 |
-
by the Free Software Foundation.
|
578 |
-
|
579 |
-
If the Program specifies that a proxy can decide which future
|
580 |
-
versions of the GNU General Public License can be used, that proxy's
|
581 |
-
public statement of acceptance of a version permanently authorizes you
|
582 |
-
to choose that version for the Program.
|
583 |
-
|
584 |
-
Later license versions may give you additional or different
|
585 |
-
permissions. However, no additional obligations are imposed on any
|
586 |
-
author or copyright holder as a result of your choosing to follow a
|
587 |
-
later version.
|
588 |
-
|
589 |
-
15. Disclaimer of Warranty.
|
590 |
-
|
591 |
-
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
592 |
-
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
593 |
-
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
594 |
-
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
595 |
-
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
596 |
-
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
597 |
-
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
598 |
-
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
599 |
-
|
600 |
-
16. Limitation of Liability.
|
601 |
-
|
602 |
-
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
603 |
-
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
604 |
-
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
605 |
-
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
606 |
-
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
607 |
-
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
608 |
-
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
609 |
-
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
610 |
-
SUCH DAMAGES.
|
611 |
-
|
612 |
-
17. Interpretation of Sections 15 and 16.
|
613 |
-
|
614 |
-
If the disclaimer of warranty and limitation of liability provided
|
615 |
-
above cannot be given local legal effect according to their terms,
|
616 |
-
reviewing courts shall apply local law that most closely approximates
|
617 |
-
an absolute waiver of all civil liability in connection with the
|
618 |
-
Program, unless a warranty or assumption of liability accompanies a
|
619 |
-
copy of the Program in return for a fee.
|
620 |
-
|
621 |
-
END OF TERMS AND CONDITIONS
|
622 |
-
|
623 |
-
How to Apply These Terms to Your New Programs
|
624 |
-
|
625 |
-
If you develop a new program, and you want it to be of the greatest
|
626 |
-
possible use to the public, the best way to achieve this is to make it
|
627 |
-
free software which everyone can redistribute and change under these terms.
|
628 |
-
|
629 |
-
To do so, attach the following notices to the program. It is safest
|
630 |
-
to attach them to the start of each source file to most effectively
|
631 |
-
state the exclusion of warranty; and each file should have at least
|
632 |
-
the "copyright" line and a pointer to where the full notice is found.
|
633 |
-
|
634 |
-
{one line to give the program's name and a brief idea of what it does.}
|
635 |
-
Copyright (C) {year} {name of author}
|
636 |
-
|
637 |
-
This program is free software: you can redistribute it and/or modify
|
638 |
-
it under the terms of the GNU General Public License as published by
|
639 |
-
the Free Software Foundation, either version 3 of the License, or
|
640 |
-
(at your option) any later version.
|
641 |
-
|
642 |
-
This program is distributed in the hope that it will be useful,
|
643 |
-
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
644 |
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
645 |
-
GNU General Public License for more details.
|
646 |
-
|
647 |
-
You should have received a copy of the GNU General Public License
|
648 |
-
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
649 |
-
|
650 |
-
Also add information on how to contact you by electronic and paper mail.
|
651 |
-
|
652 |
-
If the program does terminal interaction, make it output a short
|
653 |
-
notice like this when it starts in an interactive mode:
|
654 |
-
|
655 |
-
{project} Copyright (C) {year} {fullname}
|
656 |
-
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
657 |
-
This is free software, and you are welcome to redistribute it
|
658 |
-
under certain conditions; type `show c' for details.
|
659 |
-
|
660 |
-
The hypothetical commands `show w' and `show c' should show the appropriate
|
661 |
-
parts of the General Public License. Of course, your program's commands
|
662 |
-
might be different; for a GUI interface, you would use an "about box".
|
663 |
-
|
664 |
-
You should also get your employer (if you work as a programmer) or school,
|
665 |
-
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
666 |
-
For more information on this, and how to apply and follow the GNU GPL, see
|
667 |
-
<http://www.gnu.org/licenses/>.
|
668 |
-
|
669 |
-
The GNU General Public License does not permit incorporating your program
|
670 |
-
into proprietary programs. If your program is a subroutine library, you
|
671 |
-
may consider it more useful to permit linking proprietary applications with
|
672 |
-
the library. If this is what you want to do, use the GNU Lesser General
|
673 |
-
Public License instead of this License. But first, please read
|
674 |
-
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
1 |
+
GNU GENERAL PUBLIC LICENSE
|
2 |
+
Version 3, 29 June 2007
|
3 |
+
|
4 |
+
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
5 |
+
Everyone is permitted to copy and distribute verbatim copies
|
6 |
+
of this license document, but changing it is not allowed.
|
7 |
+
|
8 |
+
Preamble
|
9 |
+
|
10 |
+
The GNU General Public License is a free, copyleft license for
|
11 |
+
software and other kinds of works.
|
12 |
+
|
13 |
+
The licenses for most software and other practical works are designed
|
14 |
+
to take away your freedom to share and change the works. By contrast,
|
15 |
+
the GNU General Public License is intended to guarantee your freedom to
|
16 |
+
share and change all versions of a program--to make sure it remains free
|
17 |
+
software for all its users. We, the Free Software Foundation, use the
|
18 |
+
GNU General Public License for most of our software; it applies also to
|
19 |
+
any other work released this way by its authors. You can apply it to
|
20 |
+
your programs, too.
|
21 |
+
|
22 |
+
When we speak of free software, we are referring to freedom, not
|
23 |
+
price. Our General Public Licenses are designed to make sure that you
|
24 |
+
have the freedom to distribute copies of free software (and charge for
|
25 |
+
them if you wish), that you receive source code or can get it if you
|
26 |
+
want it, that you can change the software or use pieces of it in new
|
27 |
+
free programs, and that you know you can do these things.
|
28 |
+
|
29 |
+
To protect your rights, we need to prevent others from denying you
|
30 |
+
these rights or asking you to surrender the rights. Therefore, you have
|
31 |
+
certain responsibilities if you distribute copies of the software, or if
|
32 |
+
you modify it: responsibilities to respect the freedom of others.
|
33 |
+
|
34 |
+
For example, if you distribute copies of such a program, whether
|
35 |
+
gratis or for a fee, you must pass on to the recipients the same
|
36 |
+
freedoms that you received. You must make sure that they, too, receive
|
37 |
+
or can get the source code. And you must show them these terms so they
|
38 |
+
know their rights.
|
39 |
+
|
40 |
+
Developers that use the GNU GPL protect your rights with two steps:
|
41 |
+
(1) assert copyright on the software, and (2) offer you this License
|
42 |
+
giving you legal permission to copy, distribute and/or modify it.
|
43 |
+
|
44 |
+
For the developers' and authors' protection, the GPL clearly explains
|
45 |
+
that there is no warranty for this free software. For both users' and
|
46 |
+
authors' sake, the GPL requires that modified versions be marked as
|
47 |
+
changed, so that their problems will not be attributed erroneously to
|
48 |
+
authors of previous versions.
|
49 |
+
|
50 |
+
Some devices are designed to deny users access to install or run
|
51 |
+
modified versions of the software inside them, although the manufacturer
|
52 |
+
can do so. This is fundamentally incompatible with the aim of
|
53 |
+
protecting users' freedom to change the software. The systematic
|
54 |
+
pattern of such abuse occurs in the area of products for individuals to
|
55 |
+
use, which is precisely where it is most unacceptable. Therefore, we
|
56 |
+
have designed this version of the GPL to prohibit the practice for those
|
57 |
+
products. If such problems arise substantially in other domains, we
|
58 |
+
stand ready to extend this provision to those domains in future versions
|
59 |
+
of the GPL, as needed to protect the freedom of users.
|
60 |
+
|
61 |
+
Finally, every program is threatened constantly by software patents.
|
62 |
+
States should not allow patents to restrict development and use of
|
63 |
+
software on general-purpose computers, but in those that do, we wish to
|
64 |
+
avoid the special danger that patents applied to a free program could
|
65 |
+
make it effectively proprietary. To prevent this, the GPL assures that
|
66 |
+
patents cannot be used to render the program non-free.
|
67 |
+
|
68 |
+
The precise terms and conditions for copying, distribution and
|
69 |
+
modification follow.
|
70 |
+
|
71 |
+
TERMS AND CONDITIONS
|
72 |
+
|
73 |
+
0. Definitions.
|
74 |
+
|
75 |
+
"This License" refers to version 3 of the GNU General Public License.
|
76 |
+
|
77 |
+
"Copyright" also means copyright-like laws that apply to other kinds of
|
78 |
+
works, such as semiconductor masks.
|
79 |
+
|
80 |
+
"The Program" refers to any copyrightable work licensed under this
|
81 |
+
License. Each licensee is addressed as "you". "Licensees" and
|
82 |
+
"recipients" may be individuals or organizations.
|
83 |
+
|
84 |
+
To "modify" a work means to copy from or adapt all or part of the work
|
85 |
+
in a fashion requiring copyright permission, other than the making of an
|
86 |
+
exact copy. The resulting work is called a "modified version" of the
|
87 |
+
earlier work or a work "based on" the earlier work.
|
88 |
+
|
89 |
+
A "covered work" means either the unmodified Program or a work based
|
90 |
+
on the Program.
|
91 |
+
|
92 |
+
To "propagate" a work means to do anything with it that, without
|
93 |
+
permission, would make you directly or secondarily liable for
|
94 |
+
infringement under applicable copyright law, except executing it on a
|
95 |
+
computer or modifying a private copy. Propagation includes copying,
|
96 |
+
distribution (with or without modification), making available to the
|
97 |
+
public, and in some countries other activities as well.
|
98 |
+
|
99 |
+
To "convey" a work means any kind of propagation that enables other
|
100 |
+
parties to make or receive copies. Mere interaction with a user through
|
101 |
+
a computer network, with no transfer of a copy, is not conveying.
|
102 |
+
|
103 |
+
An interactive user interface displays "Appropriate Legal Notices"
|
104 |
+
to the extent that it includes a convenient and prominently visible
|
105 |
+
feature that (1) displays an appropriate copyright notice, and (2)
|
106 |
+
tells the user that there is no warranty for the work (except to the
|
107 |
+
extent that warranties are provided), that licensees may convey the
|
108 |
+
work under this License, and how to view a copy of this License. If
|
109 |
+
the interface presents a list of user commands or options, such as a
|
110 |
+
menu, a prominent item in the list meets this criterion.
|
111 |
+
|
112 |
+
1. Source Code.
|
113 |
+
|
114 |
+
The "source code" for a work means the preferred form of the work
|
115 |
+
for making modifications to it. "Object code" means any non-source
|
116 |
+
form of a work.
|
117 |
+
|
118 |
+
A "Standard Interface" means an interface that either is an official
|
119 |
+
standard defined by a recognized standards body, or, in the case of
|
120 |
+
interfaces specified for a particular programming language, one that
|
121 |
+
is widely used among developers working in that language.
|
122 |
+
|
123 |
+
The "System Libraries" of an executable work include anything, other
|
124 |
+
than the work as a whole, that (a) is included in the normal form of
|
125 |
+
packaging a Major Component, but which is not part of that Major
|
126 |
+
Component, and (b) serves only to enable use of the work with that
|
127 |
+
Major Component, or to implement a Standard Interface for which an
|
128 |
+
implementation is available to the public in source code form. A
|
129 |
+
"Major Component", in this context, means a major essential component
|
130 |
+
(kernel, window system, and so on) of the specific operating system
|
131 |
+
(if any) on which the executable work runs, or a compiler used to
|
132 |
+
produce the work, or an object code interpreter used to run it.
|
133 |
+
|
134 |
+
The "Corresponding Source" for a work in object code form means all
|
135 |
+
the source code needed to generate, install, and (for an executable
|
136 |
+
work) run the object code and to modify the work, including scripts to
|
137 |
+
control those activities. However, it does not include the work's
|
138 |
+
System Libraries, or general-purpose tools or generally available free
|
139 |
+
programs which are used unmodified in performing those activities but
|
140 |
+
which are not part of the work. For example, Corresponding Source
|
141 |
+
includes interface definition files associated with source files for
|
142 |
+
the work, and the source code for shared libraries and dynamically
|
143 |
+
linked subprograms that the work is specifically designed to require,
|
144 |
+
such as by intimate data communication or control flow between those
|
145 |
+
subprograms and other parts of the work.
|
146 |
+
|
147 |
+
The Corresponding Source need not include anything that users
|
148 |
+
can regenerate automatically from other parts of the Corresponding
|
149 |
+
Source.
|
150 |
+
|
151 |
+
The Corresponding Source for a work in source code form is that
|
152 |
+
same work.
|
153 |
+
|
154 |
+
2. Basic Permissions.
|
155 |
+
|
156 |
+
All rights granted under this License are granted for the term of
|
157 |
+
copyright on the Program, and are irrevocable provided the stated
|
158 |
+
conditions are met. This License explicitly affirms your unlimited
|
159 |
+
permission to run the unmodified Program. The output from running a
|
160 |
+
covered work is covered by this License only if the output, given its
|
161 |
+
content, constitutes a covered work. This License acknowledges your
|
162 |
+
rights of fair use or other equivalent, as provided by copyright law.
|
163 |
+
|
164 |
+
You may make, run and propagate covered works that you do not
|
165 |
+
convey, without conditions so long as your license otherwise remains
|
166 |
+
in force. You may convey covered works to others for the sole purpose
|
167 |
+
of having them make modifications exclusively for you, or provide you
|
168 |
+
with facilities for running those works, provided that you comply with
|
169 |
+
the terms of this License in conveying all material for which you do
|
170 |
+
not control copyright. Those thus making or running the covered works
|
171 |
+
for you must do so exclusively on your behalf, under your direction
|
172 |
+
and control, on terms that prohibit them from making any copies of
|
173 |
+
your copyrighted material outside their relationship with you.
|
174 |
+
|
175 |
+
Conveying under any other circumstances is permitted solely under
|
176 |
+
the conditions stated below. Sublicensing is not allowed; section 10
|
177 |
+
makes it unnecessary.
|
178 |
+
|
179 |
+
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
180 |
+
|
181 |
+
No covered work shall be deemed part of an effective technological
|
182 |
+
measure under any applicable law fulfilling obligations under article
|
183 |
+
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
184 |
+
similar laws prohibiting or restricting circumvention of such
|
185 |
+
measures.
|
186 |
+
|
187 |
+
When you convey a covered work, you waive any legal power to forbid
|
188 |
+
circumvention of technological measures to the extent such circumvention
|
189 |
+
is effected by exercising rights under this License with respect to
|
190 |
+
the covered work, and you disclaim any intention to limit operation or
|
191 |
+
modification of the work as a means of enforcing, against the work's
|
192 |
+
users, your or third parties' legal rights to forbid circumvention of
|
193 |
+
technological measures.
|
194 |
+
|
195 |
+
4. Conveying Verbatim Copies.
|
196 |
+
|
197 |
+
You may convey verbatim copies of the Program's source code as you
|
198 |
+
receive it, in any medium, provided that you conspicuously and
|
199 |
+
appropriately publish on each copy an appropriate copyright notice;
|
200 |
+
keep intact all notices stating that this License and any
|
201 |
+
non-permissive terms added in accord with section 7 apply to the code;
|
202 |
+
keep intact all notices of the absence of any warranty; and give all
|
203 |
+
recipients a copy of this License along with the Program.
|
204 |
+
|
205 |
+
You may charge any price or no price for each copy that you convey,
|
206 |
+
and you may offer support or warranty protection for a fee.
|
207 |
+
|
208 |
+
5. Conveying Modified Source Versions.
|
209 |
+
|
210 |
+
You may convey a work based on the Program, or the modifications to
|
211 |
+
produce it from the Program, in the form of source code under the
|
212 |
+
terms of section 4, provided that you also meet all of these conditions:
|
213 |
+
|
214 |
+
a) The work must carry prominent notices stating that you modified
|
215 |
+
it, and giving a relevant date.
|
216 |
+
|
217 |
+
b) The work must carry prominent notices stating that it is
|
218 |
+
released under this License and any conditions added under section
|
219 |
+
7. This requirement modifies the requirement in section 4 to
|
220 |
+
"keep intact all notices".
|
221 |
+
|
222 |
+
c) You must license the entire work, as a whole, under this
|
223 |
+
License to anyone who comes into possession of a copy. This
|
224 |
+
License will therefore apply, along with any applicable section 7
|
225 |
+
additional terms, to the whole of the work, and all its parts,
|
226 |
+
regardless of how they are packaged. This License gives no
|
227 |
+
permission to license the work in any other way, but it does not
|
228 |
+
invalidate such permission if you have separately received it.
|
229 |
+
|
230 |
+
d) If the work has interactive user interfaces, each must display
|
231 |
+
Appropriate Legal Notices; however, if the Program has interactive
|
232 |
+
interfaces that do not display Appropriate Legal Notices, your
|
233 |
+
work need not make them do so.
|
234 |
+
|
235 |
+
A compilation of a covered work with other separate and independent
|
236 |
+
works, which are not by their nature extensions of the covered work,
|
237 |
+
and which are not combined with it such as to form a larger program,
|
238 |
+
in or on a volume of a storage or distribution medium, is called an
|
239 |
+
"aggregate" if the compilation and its resulting copyright are not
|
240 |
+
used to limit the access or legal rights of the compilation's users
|
241 |
+
beyond what the individual works permit. Inclusion of a covered work
|
242 |
+
in an aggregate does not cause this License to apply to the other
|
243 |
+
parts of the aggregate.
|
244 |
+
|
245 |
+
6. Conveying Non-Source Forms.
|
246 |
+
|
247 |
+
You may convey a covered work in object code form under the terms
|
248 |
+
of sections 4 and 5, provided that you also convey the
|
249 |
+
machine-readable Corresponding Source under the terms of this License,
|
250 |
+
in one of these ways:
|
251 |
+
|
252 |
+
a) Convey the object code in, or embodied in, a physical product
|
253 |
+
(including a physical distribution medium), accompanied by the
|
254 |
+
Corresponding Source fixed on a durable physical medium
|
255 |
+
customarily used for software interchange.
|
256 |
+
|
257 |
+
b) Convey the object code in, or embodied in, a physical product
|
258 |
+
(including a physical distribution medium), accompanied by a
|
259 |
+
written offer, valid for at least three years and valid for as
|
260 |
+
long as you offer spare parts or customer support for that product
|
261 |
+
model, to give anyone who possesses the object code either (1) a
|
262 |
+
copy of the Corresponding Source for all the software in the
|
263 |
+
product that is covered by this License, on a durable physical
|
264 |
+
medium customarily used for software interchange, for a price no
|
265 |
+
more than your reasonable cost of physically performing this
|
266 |
+
conveying of source, or (2) access to copy the
|
267 |
+
Corresponding Source from a network server at no charge.
|
268 |
+
|
269 |
+
c) Convey individual copies of the object code with a copy of the
|
270 |
+
written offer to provide the Corresponding Source. This
|
271 |
+
alternative is allowed only occasionally and noncommercially, and
|
272 |
+
only if you received the object code with such an offer, in accord
|
273 |
+
with subsection 6b.
|
274 |
+
|
275 |
+
d) Convey the object code by offering access from a designated
|
276 |
+
place (gratis or for a charge), and offer equivalent access to the
|
277 |
+
Corresponding Source in the same way through the same place at no
|
278 |
+
further charge. You need not require recipients to copy the
|
279 |
+
Corresponding Source along with the object code. If the place to
|
280 |
+
copy the object code is a network server, the Corresponding Source
|
281 |
+
may be on a different server (operated by you or a third party)
|
282 |
+
that supports equivalent copying facilities, provided you maintain
|
283 |
+
clear directions next to the object code saying where to find the
|
284 |
+
Corresponding Source. Regardless of what server hosts the
|
285 |
+
Corresponding Source, you remain obligated to ensure that it is
|
286 |
+
available for as long as needed to satisfy these requirements.
|
287 |
+
|
288 |
+
e) Convey the object code using peer-to-peer transmission, provided
|
289 |
+
you inform other peers where the object code and Corresponding
|
290 |
+
Source of the work are being offered to the general public at no
|
291 |
+
charge under subsection 6d.
|
292 |
+
|
293 |
+
A separable portion of the object code, whose source code is excluded
|
294 |
+
from the Corresponding Source as a System Library, need not be
|
295 |
+
included in conveying the object code work.
|
296 |
+
|
297 |
+
A "User Product" is either (1) a "consumer product", which means any
|
298 |
+
tangible personal property which is normally used for personal, family,
|
299 |
+
or household purposes, or (2) anything designed or sold for incorporation
|
300 |
+
into a dwelling. In determining whether a product is a consumer product,
|
301 |
+
doubtful cases shall be resolved in favor of coverage. For a particular
|
302 |
+
product received by a particular user, "normally used" refers to a
|
303 |
+
typical or common use of that class of product, regardless of the status
|
304 |
+
of the particular user or of the way in which the particular user
|
305 |
+
actually uses, or expects or is expected to use, the product. A product
|
306 |
+
is a consumer product regardless of whether the product has substantial
|
307 |
+
commercial, industrial or non-consumer uses, unless such uses represent
|
308 |
+
the only significant mode of use of the product.
|
309 |
+
|
310 |
+
"Installation Information" for a User Product means any methods,
|
311 |
+
procedures, authorization keys, or other information required to install
|
312 |
+
and execute modified versions of a covered work in that User Product from
|
313 |
+
a modified version of its Corresponding Source. The information must
|
314 |
+
suffice to ensure that the continued functioning of the modified object
|
315 |
+
code is in no case prevented or interfered with solely because
|
316 |
+
modification has been made.
|
317 |
+
|
318 |
+
If you convey an object code work under this section in, or with, or
|
319 |
+
specifically for use in, a User Product, and the conveying occurs as
|
320 |
+
part of a transaction in which the right of possession and use of the
|
321 |
+
User Product is transferred to the recipient in perpetuity or for a
|
322 |
+
fixed term (regardless of how the transaction is characterized), the
|
323 |
+
Corresponding Source conveyed under this section must be accompanied
|
324 |
+
by the Installation Information. But this requirement does not apply
|
325 |
+
if neither you nor any third party retains the ability to install
|
326 |
+
modified object code on the User Product (for example, the work has
|
327 |
+
been installed in ROM).
|
328 |
+
|
329 |
+
The requirement to provide Installation Information does not include a
|
330 |
+
requirement to continue to provide support service, warranty, or updates
|
331 |
+
for a work that has been modified or installed by the recipient, or for
|
332 |
+
the User Product in which it has been modified or installed. Access to a
|
333 |
+
network may be denied when the modification itself materially and
|
334 |
+
adversely affects the operation of the network or violates the rules and
|
335 |
+
protocols for communication across the network.
|
336 |
+
|
337 |
+
Corresponding Source conveyed, and Installation Information provided,
|
338 |
+
in accord with this section must be in a format that is publicly
|
339 |
+
documented (and with an implementation available to the public in
|
340 |
+
source code form), and must require no special password or key for
|
341 |
+
unpacking, reading or copying.
|
342 |
+
|
343 |
+
7. Additional Terms.
|
344 |
+
|
345 |
+
"Additional permissions" are terms that supplement the terms of this
|
346 |
+
License by making exceptions from one or more of its conditions.
|
347 |
+
Additional permissions that are applicable to the entire Program shall
|
348 |
+
be treated as though they were included in this License, to the extent
|
349 |
+
that they are valid under applicable law. If additional permissions
|
350 |
+
apply only to part of the Program, that part may be used separately
|
351 |
+
under those permissions, but the entire Program remains governed by
|
352 |
+
this License without regard to the additional permissions.
|
353 |
+
|
354 |
+
When you convey a copy of a covered work, you may at your option
|
355 |
+
remove any additional permissions from that copy, or from any part of
|
356 |
+
it. (Additional permissions may be written to require their own
|
357 |
+
removal in certain cases when you modify the work.) You may place
|
358 |
+
additional permissions on material, added by you to a covered work,
|
359 |
+
for which you have or can give appropriate copyright permission.
|
360 |
+
|
361 |
+
Notwithstanding any other provision of this License, for material you
|
362 |
+
add to a covered work, you may (if authorized by the copyright holders of
|
363 |
+
that material) supplement the terms of this License with terms:
|
364 |
+
|
365 |
+
a) Disclaiming warranty or limiting liability differently from the
|
366 |
+
terms of sections 15 and 16 of this License; or
|
367 |
+
|
368 |
+
b) Requiring preservation of specified reasonable legal notices or
|
369 |
+
author attributions in that material or in the Appropriate Legal
|
370 |
+
Notices displayed by works containing it; or
|
371 |
+
|
372 |
+
c) Prohibiting misrepresentation of the origin of that material, or
|
373 |
+
requiring that modified versions of such material be marked in
|
374 |
+
reasonable ways as different from the original version; or
|
375 |
+
|
376 |
+
d) Limiting the use for publicity purposes of names of licensors or
|
377 |
+
authors of the material; or
|
378 |
+
|
379 |
+
e) Declining to grant rights under trademark law for use of some
|
380 |
+
trade names, trademarks, or service marks; or
|
381 |
+
|
382 |
+
f) Requiring indemnification of licensors and authors of that
|
383 |
+
material by anyone who conveys the material (or modified versions of
|
384 |
+
it) with contractual assumptions of liability to the recipient, for
|
385 |
+
any liability that these contractual assumptions directly impose on
|
386 |
+
those licensors and authors.
|
387 |
+
|
388 |
+
All other non-permissive additional terms are considered "further
|
389 |
+
restrictions" within the meaning of section 10. If the Program as you
|
390 |
+
received it, or any part of it, contains a notice stating that it is
|
391 |
+
governed by this License along with a term that is a further
|
392 |
+
restriction, you may remove that term. If a license document contains
|
393 |
+
a further restriction but permits relicensing or conveying under this
|
394 |
+
License, you may add to a covered work material governed by the terms
|
395 |
+
of that license document, provided that the further restriction does
|
396 |
+
not survive such relicensing or conveying.
|
397 |
+
|
398 |
+
If you add terms to a covered work in accord with this section, you
|
399 |
+
must place, in the relevant source files, a statement of the
|
400 |
+
additional terms that apply to those files, or a notice indicating
|
401 |
+
where to find the applicable terms.
|
402 |
+
|
403 |
+
Additional terms, permissive or non-permissive, may be stated in the
|
404 |
+
form of a separately written license, or stated as exceptions;
|
405 |
+
the above requirements apply either way.
|
406 |
+
|
407 |
+
8. Termination.
|
408 |
+
|
409 |
+
You may not propagate or modify a covered work except as expressly
|
410 |
+
provided under this License. Any attempt otherwise to propagate or
|
411 |
+
modify it is void, and will automatically terminate your rights under
|
412 |
+
this License (including any patent licenses granted under the third
|
413 |
+
paragraph of section 11).
|
414 |
+
|
415 |
+
However, if you cease all violation of this License, then your
|
416 |
+
license from a particular copyright holder is reinstated (a)
|
417 |
+
provisionally, unless and until the copyright holder explicitly and
|
418 |
+
finally terminates your license, and (b) permanently, if the copyright
|
419 |
+
holder fails to notify you of the violation by some reasonable means
|
420 |
+
prior to 60 days after the cessation.
|
421 |
+
|
422 |
+
Moreover, your license from a particular copyright holder is
|
423 |
+
reinstated permanently if the copyright holder notifies you of the
|
424 |
+
violation by some reasonable means, this is the first time you have
|
425 |
+
received notice of violation of this License (for any work) from that
|
426 |
+
copyright holder, and you cure the violation prior to 30 days after
|
427 |
+
your receipt of the notice.
|
428 |
+
|
429 |
+
Termination of your rights under this section does not terminate the
|
430 |
+
licenses of parties who have received copies or rights from you under
|
431 |
+
this License. If your rights have been terminated and not permanently
|
432 |
+
reinstated, you do not qualify to receive new licenses for the same
|
433 |
+
material under section 10.
|
434 |
+
|
435 |
+
9. Acceptance Not Required for Having Copies.
|
436 |
+
|
437 |
+
You are not required to accept this License in order to receive or
|
438 |
+
run a copy of the Program. Ancillary propagation of a covered work
|
439 |
+
occurring solely as a consequence of using peer-to-peer transmission
|
440 |
+
to receive a copy likewise does not require acceptance. However,
|
441 |
+
nothing other than this License grants you permission to propagate or
|
442 |
+
modify any covered work. These actions infringe copyright if you do
|
443 |
+
not accept this License. Therefore, by modifying or propagating a
|
444 |
+
covered work, you indicate your acceptance of this License to do so.
|
445 |
+
|
446 |
+
10. Automatic Licensing of Downstream Recipients.
|
447 |
+
|
448 |
+
Each time you convey a covered work, the recipient automatically
|
449 |
+
receives a license from the original licensors, to run, modify and
|
450 |
+
propagate that work, subject to this License. You are not responsible
|
451 |
+
for enforcing compliance by third parties with this License.
|
452 |
+
|
453 |
+
An "entity transaction" is a transaction transferring control of an
|
454 |
+
organization, or substantially all assets of one, or subdividing an
|
455 |
+
organization, or merging organizations. If propagation of a covered
|
456 |
+
work results from an entity transaction, each party to that
|
457 |
+
transaction who receives a copy of the work also receives whatever
|
458 |
+
licenses to the work the party's predecessor in interest had or could
|
459 |
+
give under the previous paragraph, plus a right to possession of the
|
460 |
+
Corresponding Source of the work from the predecessor in interest, if
|
461 |
+
the predecessor has it or can get it with reasonable efforts.
|
462 |
+
|
463 |
+
You may not impose any further restrictions on the exercise of the
|
464 |
+
rights granted or affirmed under this License. For example, you may
|
465 |
+
not impose a license fee, royalty, or other charge for exercise of
|
466 |
+
rights granted under this License, and you may not initiate litigation
|
467 |
+
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
468 |
+
any patent claim is infringed by making, using, selling, offering for
|
469 |
+
sale, or importing the Program or any portion of it.
|
470 |
+
|
471 |
+
11. Patents.
|
472 |
+
|
473 |
+
A "contributor" is a copyright holder who authorizes use under this
|
474 |
+
License of the Program or a work on which the Program is based. The
|
475 |
+
work thus licensed is called the contributor's "contributor version".
|
476 |
+
|
477 |
+
A contributor's "essential patent claims" are all patent claims
|
478 |
+
owned or controlled by the contributor, whether already acquired or
|
479 |
+
hereafter acquired, that would be infringed by some manner, permitted
|
480 |
+
by this License, of making, using, or selling its contributor version,
|
481 |
+
but do not include claims that would be infringed only as a
|
482 |
+
consequence of further modification of the contributor version. For
|
483 |
+
purposes of this definition, "control" includes the right to grant
|
484 |
+
patent sublicenses in a manner consistent with the requirements of
|
485 |
+
this License.
|
486 |
+
|
487 |
+
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
488 |
+
patent license under the contributor's essential patent claims, to
|
489 |
+
make, use, sell, offer for sale, import and otherwise run, modify and
|
490 |
+
propagate the contents of its contributor version.
|
491 |
+
|
492 |
+
In the following three paragraphs, a "patent license" is any express
|
493 |
+
agreement or commitment, however denominated, not to enforce a patent
|
494 |
+
(such as an express permission to practice a patent or covenant not to
|
495 |
+
sue for patent infringement). To "grant" such a patent license to a
|
496 |
+
party means to make such an agreement or commitment not to enforce a
|
497 |
+
patent against the party.
|
498 |
+
|
499 |
+
If you convey a covered work, knowingly relying on a patent license,
|
500 |
+
and the Corresponding Source of the work is not available for anyone
|
501 |
+
to copy, free of charge and under the terms of this License, through a
|
502 |
+
publicly available network server or other readily accessible means,
|
503 |
+
then you must either (1) cause the Corresponding Source to be so
|
504 |
+
available, or (2) arrange to deprive yourself of the benefit of the
|
505 |
+
patent license for this particular work, or (3) arrange, in a manner
|
506 |
+
consistent with the requirements of this License, to extend the patent
|
507 |
+
license to downstream recipients. "Knowingly relying" means you have
|
508 |
+
actual knowledge that, but for the patent license, your conveying the
|
509 |
+
covered work in a country, or your recipient's use of the covered work
|
510 |
+
in a country, would infringe one or more identifiable patents in that
|
511 |
+
country that you have reason to believe are valid.
|
512 |
+
|
513 |
+
If, pursuant to or in connection with a single transaction or
|
514 |
+
arrangement, you convey, or propagate by procuring conveyance of, a
|
515 |
+
covered work, and grant a patent license to some of the parties
|
516 |
+
receiving the covered work authorizing them to use, propagate, modify
|
517 |
+
or convey a specific copy of the covered work, then the patent license
|
518 |
+
you grant is automatically extended to all recipients of the covered
|
519 |
+
work and works based on it.
|
520 |
+
|
521 |
+
A patent license is "discriminatory" if it does not include within
|
522 |
+
the scope of its coverage, prohibits the exercise of, or is
|
523 |
+
conditioned on the non-exercise of one or more of the rights that are
|
524 |
+
specifically granted under this License. You may not convey a covered
|
525 |
+
work if you are a party to an arrangement with a third party that is
|
526 |
+
in the business of distributing software, under which you make payment
|
527 |
+
to the third party based on the extent of your activity of conveying
|
528 |
+
the work, and under which the third party grants, to any of the
|
529 |
+
parties who would receive the covered work from you, a discriminatory
|
530 |
+
patent license (a) in connection with copies of the covered work
|
531 |
+
conveyed by you (or copies made from those copies), or (b) primarily
|
532 |
+
for and in connection with specific products or compilations that
|
533 |
+
contain the covered work, unless you entered into that arrangement,
|
534 |
+
or that patent license was granted, prior to 28 March 2007.
|
535 |
+
|
536 |
+
Nothing in this License shall be construed as excluding or limiting
|
537 |
+
any implied license or other defenses to infringement that may
|
538 |
+
otherwise be available to you under applicable patent law.
|
539 |
+
|
540 |
+
12. No Surrender of Others' Freedom.
|
541 |
+
|
542 |
+
If conditions are imposed on you (whether by court order, agreement or
|
543 |
+
otherwise) that contradict the conditions of this License, they do not
|
544 |
+
excuse you from the conditions of this License. If you cannot convey a
|
545 |
+
covered work so as to satisfy simultaneously your obligations under this
|
546 |
+
License and any other pertinent obligations, then as a consequence you may
|
547 |
+
not convey it at all. For example, if you agree to terms that obligate you
|
548 |
+
to collect a royalty for further conveying from those to whom you convey
|
549 |
+
the Program, the only way you could satisfy both those terms and this
|
550 |
+
License would be to refrain entirely from conveying the Program.
|
551 |
+
|
552 |
+
13. Use with the GNU Affero General Public License.
|
553 |
+
|
554 |
+
Notwithstanding any other provision of this License, you have
|
555 |
+
permission to link or combine any covered work with a work licensed
|
556 |
+
under version 3 of the GNU Affero General Public License into a single
|
557 |
+
combined work, and to convey the resulting work. The terms of this
|
558 |
+
License will continue to apply to the part which is the covered work,
|
559 |
+
but the special requirements of the GNU Affero General Public License,
|
560 |
+
section 13, concerning interaction through a network will apply to the
|
561 |
+
combination as such.
|
562 |
+
|
563 |
+
14. Revised Versions of this License.
|
564 |
+
|
565 |
+
The Free Software Foundation may publish revised and/or new versions of
|
566 |
+
the GNU General Public License from time to time. Such new versions will
|
567 |
+
be similar in spirit to the present version, but may differ in detail to
|
568 |
+
address new problems or concerns.
|
569 |
+
|
570 |
+
Each version is given a distinguishing version number. If the
|
571 |
+
Program specifies that a certain numbered version of the GNU General
|
572 |
+
Public License "or any later version" applies to it, you have the
|
573 |
+
option of following the terms and conditions either of that numbered
|
574 |
+
version or of any later version published by the Free Software
|
575 |
+
Foundation. If the Program does not specify a version number of the
|
576 |
+
GNU General Public License, you may choose any version ever published
|
577 |
+
by the Free Software Foundation.
|
578 |
+
|
579 |
+
If the Program specifies that a proxy can decide which future
|
580 |
+
versions of the GNU General Public License can be used, that proxy's
|
581 |
+
public statement of acceptance of a version permanently authorizes you
|
582 |
+
to choose that version for the Program.
|
583 |
+
|
584 |
+
Later license versions may give you additional or different
|
585 |
+
permissions. However, no additional obligations are imposed on any
|
586 |
+
author or copyright holder as a result of your choosing to follow a
|
587 |
+
later version.
|
588 |
+
|
589 |
+
15. Disclaimer of Warranty.
|
590 |
+
|
591 |
+
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
592 |
+
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
593 |
+
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
594 |
+
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
595 |
+
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
596 |
+
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
597 |
+
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
598 |
+
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
599 |
+
|
600 |
+
16. Limitation of Liability.
|
601 |
+
|
602 |
+
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
603 |
+
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
604 |
+
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
605 |
+
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
606 |
+
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
607 |
+
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
608 |
+
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
609 |
+
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
610 |
+
SUCH DAMAGES.
|
611 |
+
|
612 |
+
17. Interpretation of Sections 15 and 16.
|
613 |
+
|
614 |
+
If the disclaimer of warranty and limitation of liability provided
|
615 |
+
above cannot be given local legal effect according to their terms,
|
616 |
+
reviewing courts shall apply local law that most closely approximates
|
617 |
+
an absolute waiver of all civil liability in connection with the
|
618 |
+
Program, unless a warranty or assumption of liability accompanies a
|
619 |
+
copy of the Program in return for a fee.
|
620 |
+
|
621 |
+
END OF TERMS AND CONDITIONS
|
622 |
+
|
623 |
+
How to Apply These Terms to Your New Programs
|
624 |
+
|
625 |
+
If you develop a new program, and you want it to be of the greatest
|
626 |
+
possible use to the public, the best way to achieve this is to make it
|
627 |
+
free software which everyone can redistribute and change under these terms.
|
628 |
+
|
629 |
+
To do so, attach the following notices to the program. It is safest
|
630 |
+
to attach them to the start of each source file to most effectively
|
631 |
+
state the exclusion of warranty; and each file should have at least
|
632 |
+
the "copyright" line and a pointer to where the full notice is found.
|
633 |
+
|
634 |
+
{one line to give the program's name and a brief idea of what it does.}
|
635 |
+
Copyright (C) {year} {name of author}
|
636 |
+
|
637 |
+
This program is free software: you can redistribute it and/or modify
|
638 |
+
it under the terms of the GNU General Public License as published by
|
639 |
+
the Free Software Foundation, either version 3 of the License, or
|
640 |
+
(at your option) any later version.
|
641 |
+
|
642 |
+
This program is distributed in the hope that it will be useful,
|
643 |
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
644 |
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
645 |
+
GNU General Public License for more details.
|
646 |
+
|
647 |
+
You should have received a copy of the GNU General Public License
|
648 |
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
649 |
+
|
650 |
+
Also add information on how to contact you by electronic and paper mail.
|
651 |
+
|
652 |
+
If the program does terminal interaction, make it output a short
|
653 |
+
notice like this when it starts in an interactive mode:
|
654 |
+
|
655 |
+
{project} Copyright (C) {year} {fullname}
|
656 |
+
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
657 |
+
This is free software, and you are welcome to redistribute it
|
658 |
+
under certain conditions; type `show c' for details.
|
659 |
+
|
660 |
+
The hypothetical commands `show w' and `show c' should show the appropriate
|
661 |
+
parts of the General Public License. Of course, your program's commands
|
662 |
+
might be different; for a GUI interface, you would use an "about box".
|
663 |
+
|
664 |
+
You should also get your employer (if you work as a programmer) or school,
|
665 |
+
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
666 |
+
For more information on this, and how to apply and follow the GNU GPL, see
|
667 |
+
<http://www.gnu.org/licenses/>.
|
668 |
+
|
669 |
+
The GNU General Public License does not permit incorporating your program
|
670 |
+
into proprietary programs. If your program is a subroutine library, you
|
671 |
+
may consider it more useful to permit linking proprietary applications with
|
672 |
+
the library. If this is what you want to do, use the GNU Lesser General
|
673 |
+
Public License instead of this License. But first, please read
|
674 |
+
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
framework/bootstrap-helpers.php
CHANGED
@@ -1,130 +1,130 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Helper functions used while loading the framework
|
5 |
-
*/
|
6 |
-
|
7 |
-
/**
|
8 |
-
* Convert to Unix style directory separators
|
9 |
-
*/
|
10 |
-
function fw_fix_path($path) {
|
11 |
-
$fixed_path = untrailingslashit( str_replace(array('//', '\\'), array('/', '/'), $path) );
|
12 |
-
|
13 |
-
if (empty($fixed_path) && !empty($path)) {
|
14 |
-
$fixed_path = '/';
|
15 |
-
}
|
16 |
-
|
17 |
-
return $fixed_path;
|
18 |
-
}
|
19 |
-
|
20 |
-
/**
|
21 |
-
* Relative path of the framework customizations directory
|
22 |
-
* @param string $append
|
23 |
-
* @return string
|
24 |
-
*/
|
25 |
-
function fw_get_framework_customizations_dir_rel_path($append = '') {
|
26 |
-
static $cache = null;
|
27 |
-
|
28 |
-
if ($cache === null) {
|
29 |
-
$cache = apply_filters('fw_framework_customizations_dir_rel_path', '/framework-customizations');
|
30 |
-
}
|
31 |
-
|
32 |
-
return $cache . $append;
|
33 |
-
}
|
34 |
-
|
35 |
-
/** Child theme related functions */
|
36 |
-
{
|
37 |
-
/**
|
38 |
-
* Full path to the child-theme framework customizations directory
|
39 |
-
* @param string $rel_path
|
40 |
-
* @return null|string
|
41 |
-
*/
|
42 |
-
function fw_get_stylesheet_customizations_directory($rel_path = '') {
|
43 |
-
if (is_child_theme()) {
|
44 |
-
return get_stylesheet_directory() . fw_get_framework_customizations_dir_rel_path($rel_path);
|
45 |
-
} else {
|
46 |
-
// check is_child_theme() before using this function
|
47 |
-
return null;
|
48 |
-
}
|
49 |
-
}
|
50 |
-
|
51 |
-
/**
|
52 |
-
* URI to the child-theme framework customizations directory
|
53 |
-
* @param string $rel_path
|
54 |
-
* @return null|string
|
55 |
-
*/
|
56 |
-
function fw_get_stylesheet_customizations_directory_uri($rel_path = '') {
|
57 |
-
if (is_child_theme()) {
|
58 |
-
return get_stylesheet_directory_uri() . fw_get_framework_customizations_dir_rel_path($rel_path);
|
59 |
-
} else {
|
60 |
-
// check is_child_theme() before using this function
|
61 |
-
return null;
|
62 |
-
}
|
63 |
-
}
|
64 |
-
}
|
65 |
-
|
66 |
-
/** Parent theme related functions */
|
67 |
-
{
|
68 |
-
/**
|
69 |
-
* Full path to the parent-theme framework customizations directory
|
70 |
-
* @param string $rel_path
|
71 |
-
* @return string
|
72 |
-
*/
|
73 |
-
function fw_get_template_customizations_directory($rel_path = '') {
|
74 |
-
static $cache = null;
|
75 |
-
|
76 |
-
if ($cache === null) {
|
77 |
-
$cache = get_template_directory() . fw_get_framework_customizations_dir_rel_path();
|
78 |
-
}
|
79 |
-
|
80 |
-
return $cache . $rel_path;
|
81 |
-
}
|
82 |
-
|
83 |
-
/**
|
84 |
-
* URI to the parent-theme framework customizations directory
|
85 |
-
* @param string $rel_path
|
86 |
-
* @return string
|
87 |
-
*/
|
88 |
-
function fw_get_template_customizations_directory_uri($rel_path = '') {
|
89 |
-
static $cache = null;
|
90 |
-
|
91 |
-
if ($cache === null) {
|
92 |
-
$cache = get_template_directory_uri() . fw_get_framework_customizations_dir_rel_path();
|
93 |
-
}
|
94 |
-
|
95 |
-
return $cache . $rel_path;
|
96 |
-
}
|
97 |
-
}
|
98 |
-
|
99 |
-
/** Framework related functions */
|
100 |
-
{
|
101 |
-
/**
|
102 |
-
* Full path to the parent-theme/framework directory
|
103 |
-
* @param string $rel_path
|
104 |
-
* @return string
|
105 |
-
*/
|
106 |
-
function fw_get_framework_directory($rel_path = '') {
|
107 |
-
static $cache = null;
|
108 |
-
|
109 |
-
if ($cache === null) {
|
110 |
-
$cache = apply_filters('fw_framework_directory', dirname(__FILE__));
|
111 |
-
}
|
112 |
-
|
113 |
-
return $cache . $rel_path;
|
114 |
-
}
|
115 |
-
|
116 |
-
/**
|
117 |
-
* URI to the parent-theme/framework directory
|
118 |
-
* @param string $rel_path
|
119 |
-
* @return string
|
120 |
-
*/
|
121 |
-
function fw_get_framework_directory_uri($rel_path = '') {
|
122 |
-
static $cache = null;
|
123 |
-
|
124 |
-
if ($cache === null) {
|
125 |
-
$cache = apply_filters('fw_framework_directory_uri', get_template_directory_uri() . '/framework');
|
126 |
-
}
|
127 |
-
|
128 |
-
return $cache . $rel_path;
|
129 |
-
}
|
130 |
-
}
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Helper functions used while loading the framework
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Convert to Unix style directory separators
|
9 |
+
*/
|
10 |
+
function fw_fix_path($path) {
|
11 |
+
$fixed_path = untrailingslashit( str_replace(array('//', '\\'), array('/', '/'), $path) );
|
12 |
+
|
13 |
+
if (empty($fixed_path) && !empty($path)) {
|
14 |
+
$fixed_path = '/';
|
15 |
+
}
|
16 |
+
|
17 |
+
return $fixed_path;
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Relative path of the framework customizations directory
|
22 |
+
* @param string $append
|
23 |
+
* @return string
|
24 |
+
*/
|
25 |
+
function fw_get_framework_customizations_dir_rel_path($append = '') {
|
26 |
+
static $cache = null;
|
27 |
+
|
28 |
+
if ($cache === null) {
|
29 |
+
$cache = apply_filters('fw_framework_customizations_dir_rel_path', '/framework-customizations');
|
30 |
+
}
|
31 |
+
|
32 |
+
return $cache . $append;
|
33 |
+
}
|
34 |
+
|
35 |
+
/** Child theme related functions */
|
36 |
+
{
|
37 |
+
/**
|
38 |
+
* Full path to the child-theme framework customizations directory
|
39 |
+
* @param string $rel_path
|
40 |
+
* @return null|string
|
41 |
+
*/
|
42 |
+
function fw_get_stylesheet_customizations_directory($rel_path = '') {
|
43 |
+
if (is_child_theme()) {
|
44 |
+
return get_stylesheet_directory() . fw_get_framework_customizations_dir_rel_path($rel_path);
|
45 |
+
} else {
|
46 |
+
// check is_child_theme() before using this function
|
47 |
+
return null;
|
48 |
+
}
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* URI to the child-theme framework customizations directory
|
53 |
+
* @param string $rel_path
|
54 |
+
* @return null|string
|
55 |
+
*/
|
56 |
+
function fw_get_stylesheet_customizations_directory_uri($rel_path = '') {
|
57 |
+
if (is_child_theme()) {
|
58 |
+
return get_stylesheet_directory_uri() . fw_get_framework_customizations_dir_rel_path($rel_path);
|
59 |
+
} else {
|
60 |
+
// check is_child_theme() before using this function
|
61 |
+
return null;
|
62 |
+
}
|
63 |
+
}
|
64 |
+
}
|
65 |
+
|
66 |
+
/** Parent theme related functions */
|
67 |
+
{
|
68 |
+
/**
|
69 |
+
* Full path to the parent-theme framework customizations directory
|
70 |
+
* @param string $rel_path
|
71 |
+
* @return string
|
72 |
+
*/
|
73 |
+
function fw_get_template_customizations_directory($rel_path = '') {
|
74 |
+
static $cache = null;
|
75 |
+
|
76 |
+
if ($cache === null) {
|
77 |
+
$cache = get_template_directory() . fw_get_framework_customizations_dir_rel_path();
|
78 |
+
}
|
79 |
+
|
80 |
+
return $cache . $rel_path;
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* URI to the parent-theme framework customizations directory
|
85 |
+
* @param string $rel_path
|
86 |
+
* @return string
|
87 |
+
*/
|
88 |
+
function fw_get_template_customizations_directory_uri($rel_path = '') {
|
89 |
+
static $cache = null;
|
90 |
+
|
91 |
+
if ($cache === null) {
|
92 |
+
$cache = get_template_directory_uri() . fw_get_framework_customizations_dir_rel_path();
|
93 |
+
}
|
94 |
+
|
95 |
+
return $cache . $rel_path;
|
96 |
+
}
|
97 |
+
}
|
98 |
+
|
99 |
+
/** Framework related functions */
|
100 |
+
{
|
101 |
+
/**
|
102 |
+
* Full path to the parent-theme/framework directory
|
103 |
+
* @param string $rel_path
|
104 |
+
* @return string
|
105 |
+
*/
|
106 |
+
function fw_get_framework_directory($rel_path = '') {
|
107 |
+
static $cache = null;
|
108 |
+
|
109 |
+
if ($cache === null) {
|
110 |
+
$cache = apply_filters('fw_framework_directory', dirname(__FILE__));
|
111 |
+
}
|
112 |
+
|
113 |
+
return $cache . $rel_path;
|
114 |
+
}
|
115 |
+
|
116 |
+
/**
|
117 |
+
* URI to the parent-theme/framework directory
|
118 |
+
* @param string $rel_path
|
119 |
+
* @return string
|
120 |
+
*/
|
121 |
+
function fw_get_framework_directory_uri($rel_path = '') {
|
122 |
+
static $cache = null;
|
123 |
+
|
124 |
+
if ($cache === null) {
|
125 |
+
$cache = apply_filters('fw_framework_directory_uri', get_template_directory_uri() . '/framework');
|
126 |
+
}
|
127 |
+
|
128 |
+
return $cache . $rel_path;
|
129 |
+
}
|
130 |
+
}
|
framework/bootstrap.php
CHANGED
@@ -1,103 +1,103 @@
|
|
1 |
-
<?php if (!defined('ABSPATH')) die('Forbidden');
|
2 |
-
|
3 |
-
if (defined('FW')) {
|
4 |
-
/**
|
5 |
-
* The framework is already loaded.
|
6 |
-
*/
|
7 |
-
} else {
|
8 |
-
define('FW', true);
|
9 |
-
|
10 |
-
/**
|
11 |
-
* Load the framework on 'after_setup_theme' action when the theme information is available
|
12 |
-
* To prevent `undefined constant TEMPLATEPATH` errors when the framework is used as plugin
|
13 |
-
*/
|
14 |
-
add_action('after_setup_theme', '_action_init_framework');
|
15 |
-
|
16 |
-
function _action_init_framework() {
|
17 |
-
if (did_action('fw_init')) {
|
18 |
-
return;
|
19 |
-
}
|
20 |
-
|
21 |
-
do_action('fw_before_init');
|
22 |
-
|
23 |
-
$fw_dir = dirname(__FILE__);
|
24 |
-
|
25 |
-
include $fw_dir .'/bootstrap-helpers.php';
|
26 |
-
|
27 |
-
/**
|
28 |
-
* Load core
|
29 |
-
*/
|
30 |
-
{
|
31 |
-
require $fw_dir .'/core/Fw.php';
|
32 |
-
|
33 |
-
fw();
|
34 |
-
}
|
35 |
-
|
36 |
-
/**
|
37 |
-
* Load helpers
|
38 |
-
*/
|
39 |
-
foreach (
|
40 |
-
array(
|
41 |
-
'meta',
|
42 |
-
'class-fw-access-key',
|
43 |
-
'class-fw-dumper',
|
44 |
-
'general',
|
45 |
-
'class-fw-wp-filesystem',
|
46 |
-
'class-fw-cache',
|
47 |
-
'class-fw-form',
|
48 |
-
'class-fw-request',
|
49 |
-
'class-fw-session',
|
50 |
-
'class-fw-wp-option',
|
51 |
-
'class-fw-wp-meta',
|
52 |
-
'database',
|
53 |
-
'class-fw-flash-messages',
|
54 |
-
'class-fw-resize',
|
55 |
-
'class-fw-wp-list-table',
|
56 |
-
'type/class-fw-type',
|
57 |
-
'type/class-fw-type-register',
|
58 |
-
)
|
59 |
-
as $file
|
60 |
-
) {
|
61 |
-
require $fw_dir .'/helpers/'. $file .'.php';
|
62 |
-
}
|
63 |
-
|
64 |
-
/**
|
65 |
-
* Load includes
|
66 |
-
*/
|
67 |
-
foreach (array('hooks') as $file) {
|
68 |
-
require $fw_dir .'/includes/'. $file .'.php';
|
69 |
-
}
|
70 |
-
|
71 |
-
/**
|
72 |
-
* Init components
|
73 |
-
*/
|
74 |
-
{
|
75 |
-
$components = array(
|
76 |
-
/**
|
77 |
-
* Load the theme's hooks.php first, to give users the possibility to add_action()
|
78 |
-
* for `extensions` and `backend` components actions that can happen while their initialization
|
79 |
-
*/
|
80 |
-
'theme',
|
81 |
-
/**
|
82 |
-
* Load extensions before backend, to give extensions the possibility to add_action()
|
83 |
-
* for the `backend` component actions that can happen while its initialization
|
84 |
-
*/
|
85 |
-
'extensions',
|
86 |
-
'backend'
|
87 |
-
);
|
88 |
-
|
89 |
-
foreach ($components as $component) {
|
90 |
-
fw()->{$component}->_init();
|
91 |
-
}
|
92 |
-
|
93 |
-
foreach ($components as $component) {
|
94 |
-
fw()->{$component}->_after_components_init();
|
95 |
-
}
|
96 |
-
}
|
97 |
-
|
98 |
-
/**
|
99 |
-
* The framework is loaded
|
100 |
-
*/
|
101 |
-
do_action('fw_init');
|
102 |
-
}
|
103 |
-
}
|
1 |
+
<?php if (!defined('ABSPATH')) die('Forbidden');
|
2 |
+
|
3 |
+
if (defined('FW')) {
|
4 |
+
/**
|
5 |
+
* The framework is already loaded.
|
6 |
+
*/
|
7 |
+
} else {
|
8 |
+
define('FW', true);
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Load the framework on 'after_setup_theme' action when the theme information is available
|
12 |
+
* To prevent `undefined constant TEMPLATEPATH` errors when the framework is used as plugin
|
13 |
+
*/
|
14 |
+
add_action('after_setup_theme', '_action_init_framework');
|
15 |
+
|
16 |
+
function _action_init_framework() {
|
17 |
+
if (did_action('fw_init')) {
|
18 |
+
return;
|
19 |
+
}
|
20 |
+
|
21 |
+
do_action('fw_before_init');
|
22 |
+
|
23 |
+
$fw_dir = dirname(__FILE__);
|
24 |
+
|
25 |
+
include $fw_dir .'/bootstrap-helpers.php';
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Load core
|
29 |
+
*/
|
30 |
+
{
|
31 |
+
require $fw_dir .'/core/Fw.php';
|
32 |
+
|
33 |
+
fw();
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Load helpers
|
38 |
+
*/
|
39 |
+
foreach (
|
40 |
+
array(
|
41 |
+
'meta',
|
42 |
+
'class-fw-access-key',
|
43 |
+
'class-fw-dumper',
|
44 |
+
'general',
|
45 |
+
'class-fw-wp-filesystem',
|
46 |
+
'class-fw-cache',
|
47 |
+
'class-fw-form',
|
48 |
+
'class-fw-request',
|
49 |
+
'class-fw-session',
|
50 |
+
'class-fw-wp-option',
|
51 |
+
'class-fw-wp-meta',
|
52 |
+
'database',
|
53 |
+
'class-fw-flash-messages',
|
54 |
+
'class-fw-resize',
|
55 |
+
'class-fw-wp-list-table',
|
56 |
+
'type/class-fw-type',
|
57 |
+
'type/class-fw-type-register',
|
58 |
+
)
|
59 |
+
as $file
|
60 |
+
) {
|
61 |
+
require $fw_dir .'/helpers/'. $file .'.php';
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Load includes
|
66 |
+
*/
|
67 |
+
foreach (array('hooks') as $file) {
|
68 |
+
require $fw_dir .'/includes/'. $file .'.php';
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Init components
|
73 |
+
*/
|
74 |
+
{
|
75 |
+
$components = array(
|
76 |
+
/**
|
77 |
+
* Load the theme's hooks.php first, to give users the possibility to add_action()
|
78 |
+
* for `extensions` and `backend` components actions that can happen while their initialization
|
79 |
+
*/
|
80 |
+
'theme',
|
81 |
+
/**
|
82 |
+
* Load extensions before backend, to give extensions the possibility to add_action()
|
83 |
+
* for the `backend` component actions that can happen while its initialization
|
84 |
+
*/
|
85 |
+
'extensions',
|
86 |
+
'backend'
|
87 |
+
);
|
88 |
+
|
89 |
+
foreach ($components as $component) {
|
90 |
+
fw()->{$component}->_init();
|
91 |
+
}
|
92 |
+
|
93 |
+
foreach ($components as $component) {
|
94 |
+
fw()->{$component}->_after_components_init();
|
95 |
+
}
|
96 |
+
}
|
97 |
+
|
98 |
+
/**
|
99 |
+
* The framework is loaded
|
100 |
+
*/
|
101 |
+
do_action('fw_init');
|
102 |
+
}
|
103 |
+
}
|
framework/core/Fw.php
CHANGED
@@ -1,91 +1,91 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Main framework class that contains everything
|
5 |
-
*
|
6 |
-
* Convention: All public properties should be only instances of the components (except special property: manifest)
|
7 |
-
*/
|
8 |
-
final class _Fw
|
9 |
-
{
|
10 |
-
/** @var bool If already loaded */
|
11 |
-
private static $loaded = false;
|
12 |
-
|
13 |
-
/** @var FW_Framework_Manifest */
|
14 |
-
public $manifest;
|
15 |
-
|
16 |
-
/** @var _FW_Component_Extensions */
|
17 |
-
public $extensions;
|
18 |
-
|
19 |
-
/** @var _FW_Component_Backend */
|
20 |
-
public $backend;
|
21 |
-
|
22 |
-
/** @var _FW_Component_Theme */
|
23 |
-
public $theme;
|
24 |
-
|
25 |
-
public function __construct()
|
26 |
-
{
|
27 |
-
if (self::$loaded) {
|
28 |
-
trigger_error('Framework already loaded', E_USER_ERROR);
|
29 |
-
} else {
|
30 |
-
self::$loaded = true;
|
31 |
-
}
|
32 |
-
|
33 |
-
$fw_dir = fw_get_framework_directory();
|
34 |
-
|
35 |
-
// manifest
|
36 |
-
{
|
37 |
-
require $fw_dir .'/core/class-fw-manifest.php';
|
38 |
-
|
39 |
-
require $fw_dir .'/manifest.php';
|
40 |
-
/** @var array $manifest */
|
41 |
-
|
42 |
-
$this->manifest = new FW_Framework_Manifest($manifest);
|
43 |
-
|
44 |
-
add_action('fw_init', array($this, '_check_requirements'), 1);
|
45 |
-
}
|
46 |
-
|
47 |
-
require $fw_dir .'/core/extends/class-fw-extension.php';
|
48 |
-
require $fw_dir .'/core/extends/class-fw-option-type.php';
|
49 |
-
require $fw_dir .'/core/extends/class-fw-container-type.php';
|
50 |
-
require $fw_dir .'/core/extends/interface-fw-option-handler.php'; // option handler (experimental)
|
51 |
-
|
52 |
-
// components
|
53 |
-
{
|
54 |
-
require $fw_dir .'/core/components/extensions.php';
|
55 |
-
$this->extensions = new _FW_Component_Extensions();
|
56 |
-
|
57 |
-
require $fw_dir .'/core/components/backend.php';
|
58 |
-
$this->backend = new _FW_Component_Backend();
|
59 |
-
|
60 |
-
require $fw_dir .'/core/components/theme.php';
|
61 |
-
$this->theme = new _FW_Component_Theme();
|
62 |
-
}
|
63 |
-
}
|
64 |
-
|
65 |
-
/**
|
66 |
-
* @internal
|
67 |
-
*/
|
68 |
-
public function _check_requirements()
|
69 |
-
{
|
70 |
-
if (is_admin() && !$this->manifest->check_requirements()) {
|
71 |
-
FW_Flash_Messages::add(
|
72 |
-
'fw_requirements',
|
73 |
-
__('Framework requirements not met:', 'fw') .' '. $this->manifest->get_not_met_requirement_text(),
|
74 |
-
'warning'
|
75 |
-
);
|
76 |
-
}
|
77 |
-
}
|
78 |
-
}
|
79 |
-
|
80 |
-
/**
|
81 |
-
* @return _FW Framework instance
|
82 |
-
*/
|
83 |
-
function fw() {
|
84 |
-
static $FW = null; // cache
|
85 |
-
|
86 |
-
if ($FW === null) {
|
87 |
-
$FW = new _Fw();
|
88 |
-
}
|
89 |
-
|
90 |
-
return $FW;
|
91 |
-
}
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Main framework class that contains everything
|
5 |
+
*
|
6 |
+
* Convention: All public properties should be only instances of the components (except special property: manifest)
|
7 |
+
*/
|
8 |
+
final class _Fw
|
9 |
+
{
|
10 |
+
/** @var bool If already loaded */
|
11 |
+
private static $loaded = false;
|
12 |
+
|
13 |
+
/** @var FW_Framework_Manifest */
|
14 |
+
public $manifest;
|
15 |
+
|
16 |
+
/** @var _FW_Component_Extensions */
|
17 |
+
public $extensions;
|
18 |
+
|
19 |
+
/** @var _FW_Component_Backend */
|
20 |
+
public $backend;
|
21 |
+
|
22 |
+
/** @var _FW_Component_Theme */
|
23 |
+
public $theme;
|
24 |
+
|
25 |
+
public function __construct()
|
26 |
+
{
|
27 |
+
if (self::$loaded) {
|
28 |
+
trigger_error('Framework already loaded', E_USER_ERROR);
|
29 |
+
} else {
|
30 |
+
self::$loaded = true;
|
31 |
+
}
|
32 |
+
|
33 |
+
$fw_dir = fw_get_framework_directory();
|
34 |
+
|
35 |
+
// manifest
|
36 |
+
{
|
37 |
+
require $fw_dir .'/core/class-fw-manifest.php';
|
38 |
+
|
39 |
+
require $fw_dir .'/manifest.php';
|
40 |
+
/** @var array $manifest */
|
41 |
+
|
42 |
+
$this->manifest = new FW_Framework_Manifest($manifest);
|
43 |
+
|
44 |
+
add_action('fw_init', array($this, '_check_requirements'), 1);
|
45 |
+
}
|
46 |
+
|
47 |
+
require $fw_dir .'/core/extends/class-fw-extension.php';
|
48 |
+
require $fw_dir .'/core/extends/class-fw-option-type.php';
|
49 |
+
require $fw_dir .'/core/extends/class-fw-container-type.php';
|
50 |
+
require $fw_dir .'/core/extends/interface-fw-option-handler.php'; // option handler (experimental)
|
51 |
+
|
52 |
+
// components
|
53 |
+
{
|
54 |
+
require $fw_dir .'/core/components/extensions.php';
|
55 |
+
$this->extensions = new _FW_Component_Extensions();
|
56 |
+
|
57 |
+
require $fw_dir .'/core/components/backend.php';
|
58 |
+
$this->backend = new _FW_Component_Backend();
|
59 |
+
|
60 |
+
require $fw_dir .'/core/components/theme.php';
|
61 |
+
$this->theme = new _FW_Component_Theme();
|
62 |
+
}
|
63 |
+
}
|
64 |
+
|
65 |
+
/**
|
66 |
+
* @internal
|
67 |
+
*/
|
68 |
+
public function _check_requirements()
|
69 |
+
{
|
70 |
+
if (is_admin() && !$this->manifest->check_requirements()) {
|
71 |
+
FW_Flash_Messages::add(
|
72 |
+
'fw_requirements',
|
73 |
+
__('Framework requirements not met:', 'fw') .' '. $this->manifest->get_not_met_requirement_text(),
|
74 |
+
'warning'
|
75 |
+
);
|
76 |
+
}
|
77 |
+
}
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* @return _FW Framework instance
|
82 |
+
*/
|
83 |
+
function fw() {
|
84 |
+
static $FW = null; // cache
|
85 |
+
|
86 |
+
if ($FW === null) {
|
87 |
+
$FW = new _Fw();
|
88 |
+
}
|
89 |
+
|
90 |
+
return $FW;
|
91 |
+
}
|
framework/core/class-fw-manifest.php
CHANGED
@@ -1,510 +1,510 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
|
3 |
-
abstract class FW_Manifest
|
4 |
-
{
|
5 |
-
/**
|
6 |
-
* @var array
|
7 |
-
*/
|
8 |
-
protected $manifest;
|
9 |
-
|
10 |
-
/**
|
11 |
-
* The first requirement that was not met
|
12 |
-
* (that marks that the requirements are not met)
|
13 |
-
*
|
14 |
-
* @var array
|
15 |
-
* array(
|
16 |
-
* 'requirement' => 'wordpress|framework',
|
17 |
-
* 'requirements' => array('min_version' => '1.2.3', ...)
|
18 |
-
* )
|
19 |
-
* or
|
20 |
-
* array(
|
21 |
-
* 'requirement' => 'extensions',
|
22 |
-
* 'extension' => 'extension_name',
|
23 |
-
* 'requirements' => array('min_version' => '1.2.3', ...)
|
24 |
-
* )
|
25 |
-
*/
|
26 |
-
private $not_met_requirement;
|
27 |
-
|
28 |
-
/**
|
29 |
-
* When an requirement that sure will not change is not met and have no sense to execute check_requirements() again
|
30 |
-
* @var bool
|
31 |
-
*/
|
32 |
-
private $not_met_is_final = false;
|
33 |
-
|
34 |
-
/**
|
35 |
-
* Not met requirement and skipped (not verified) requirements after $this->not_met_requirement was found
|
36 |
-
* @var array
|
37 |
-
*/
|
38 |
-
private $requirements_for_verification;
|
39 |
-
|
40 |
-
private $requirements_verification_never_called = true;
|
41 |
-
|
42 |
-
/**
|
43 |
-
* @param array $manifest
|
44 |
-
*/
|
45 |
-
protected function __construct(array $manifest)
|
46 |
-
{
|
47 |
-
$manifest = array_merge(array(
|
48 |
-
'name' => null, // title
|
49 |
-
'uri' => null,
|
50 |
-
'description' => null,
|
51 |
-
'version' => '0.0.0',
|
52 |
-
'author' => null,
|
53 |
-
'author_uri' => null,
|
54 |
-
|
55 |
-
// Custom fields
|
56 |
-
'requirements' => array(),
|
57 |
-
), $manifest);
|
58 |
-
|
59 |
-
/**
|
60 |
-
* Merge $manifest['requirements']
|
61 |
-
*/
|
62 |
-
{
|
63 |
-
$requirements = $manifest['requirements'];
|
64 |
-
|
65 |
-
$manifest['requirements'] = array();
|
66 |
-
|
67 |
-
foreach ($this->get_default_requirements() as $default_requirement => $default_requirements) {
|
68 |
-
$manifest['requirements'][ $default_requirement ] = isset($requirements[$default_requirement])
|
69 |
-
? array_merge(
|
70 |
-
$default_requirements,
|
71 |
-
$requirements[$default_requirement]
|
72 |
-
)
|
73 |
-
: $default_requirements;
|
74 |
-
}
|
75 |
-
|
76 |
-
unset($requirements);
|
77 |
-
}
|
78 |
-
|
79 |
-
$this->requirements_for_verification = $manifest['requirements'];
|
80 |
-
|
81 |
-
$this->manifest = $manifest;
|
82 |
-
}
|
83 |
-
|
84 |
-
/**
|
85 |
-
* @return array { 'requirement' => array('min_version' => '..', 'max_version' => '..') }
|
86 |
-
*/
|
87 |
-
abstract protected function get_default_requirements();
|
88 |
-
|
89 |
-
/**
|
90 |
-
* @return bool
|
91 |
-
*/
|
92 |
-
public function requirements_met()
|
93 |
-
{
|
94 |
-
if ($this->not_met_is_final) {
|
95 |
-
return false;
|
96 |
-
}
|
97 |
-
|
98 |
-
if ($this->requirements_verification_never_called) {
|
99 |
-
$this->requirements_verification_never_called = false;
|
100 |
-
|
101 |
-
$this->check_requirements();
|
102 |
-
}
|
103 |
-
|
104 |
-
return empty($this->requirements_for_verification) && empty($this->not_met_requirement);
|
105 |
-
}
|
106 |
-
|
107 |
-
/**
|
108 |
-
* @return bool
|
109 |
-
*/
|
110 |
-
public function check_requirements()
|
111 |
-
{
|
112 |
-
if ($this->not_met_is_final) {
|
113 |
-
return false;
|
114 |
-
}
|
115 |
-
|
116 |
-
if ($this->requirements_met()) {
|
117 |
-
return true;
|
118 |
-
}
|
119 |
-
|
120 |
-
$this->not_met_requirement = array();
|
121 |
-
|
122 |
-
global $wp_version;
|
123 |
-
|
124 |
-
foreach ($this->requirements_for_verification as $requirement => $requirements) {
|
125 |
-
switch ($requirement) {
|
126 |
-
case 'wordpress':
|
127 |
-
if (
|
128 |
-
isset($requirements['min_version'])
|
129 |
-
&&
|
130 |
-
version_compare($wp_version, $requirements['min_version'], '<')
|
131 |
-
) {
|
132 |
-
$this->not_met_requirement = array(
|
133 |
-
'requirement' => $requirement,
|
134 |
-
'requirements' => $requirements
|
135 |
-
);
|
136 |
-
$this->not_met_is_final = true;
|
137 |
-
break 2;
|
138 |
-
}
|
139 |
-
|
140 |
-
if (
|
141 |
-
isset($requirements['max_version'])
|
142 |
-
&&
|
143 |
-
version_compare($wp_version, $requirements['max_version'], '>')
|
144 |
-
) {
|
145 |
-
$this->not_met_requirement = array(
|
146 |
-
'requirement' => $requirement,
|
147 |
-
'requirements' => $requirements
|
148 |
-
);
|
149 |
-
$this->not_met_is_final = true;
|
150 |
-
break 2;
|
151 |
-
}
|
152 |
-
|
153 |
-
// met
|
154 |
-
unset($this->requirements_for_verification[$requirement]);
|
155 |
-
break;
|
156 |
-
case 'framework':
|
157 |
-
if (
|
158 |
-
isset($requirements['min_version'])
|
159 |
-
&&
|
160 |
-
version_compare(fw()->manifest->get_version(), $requirements['min_version'], '<')
|
161 |
-
) {
|
162 |
-
$this->not_met_requirement = array(
|
163 |
-
'requirement' => $requirement,
|
164 |
-
'requirements' => $requirements
|
165 |
-
);
|
166 |
-
$this->not_met_is_final = true;
|
167 |
-
break 2;
|
168 |
-
}
|
169 |
-
|
170 |
-
if (
|
171 |
-
isset($requirements['max_version'])
|
172 |
-
&&
|
173 |
-
version_compare(fw()->manifest->get_version(), $requirements['max_version'], '>')
|
174 |
-
) {
|
175 |
-
$this->not_met_requirement = array(
|
176 |
-
'requirement' => $requirement,
|
177 |
-
'requirements' => $requirements
|
178 |
-
);
|
179 |
-
$this->not_met_is_final = true;
|
180 |
-
break 2;
|
181 |
-
}
|
182 |
-
|
183 |
-
// met
|
184 |
-
unset($this->requirements_for_verification[$requirement]);
|
185 |
-
break;
|
186 |
-
case 'extensions':
|
187 |
-
$extensions =& $requirements;
|
188 |
-
|
189 |
-
foreach ($extensions as $extension => $extension_requirements) {
|
190 |
-
$extension_instance = fw()->extensions->get($extension);
|
191 |
-
|
192 |
-
if (!$extension_instance) {
|
193 |
-
/**
|
194 |
-
* extension in requirements does not exists
|
195 |
-
* maybe try call this method later and maybe will exist, or it really does not exists
|
196 |
-
*/
|
197 |
-
$this->not_met_requirement = array(
|
198 |
-
'requirement' => $requirement,
|
199 |
-
'extension' => $extension,
|
200 |
-
'requirements' => $extension_requirements
|
201 |
-
);
|
202 |
-
break 3;
|
203 |
-
}
|
204 |
-
|
205 |
-
if (
|
206 |
-
isset($extension_requirements['min_version'])
|
207 |
-
&&
|
208 |
-
version_compare($extension_instance->manifest->get_version(), $extension_requirements['min_version'], '<')
|
209 |
-
) {
|
210 |
-
$this->not_met_requirement = array(
|
211 |
-
'requirement' => $requirement,
|
212 |
-
'extension' => $extension,
|
213 |
-
'requirements' => $extension_requirements
|
214 |
-
);
|
215 |
-
$this->not_met_is_final = true;
|
216 |
-
break 3;
|
217 |
-
}
|
218 |
-
|
219 |
-
if (
|
220 |
-
isset($extension_requirements['max_version'])
|
221 |
-
&&
|
222 |
-
version_compare($extension_instance->manifest->get_version(), $extension_requirements['max_version'], '>')
|
223 |
-
) {
|
224 |
-
$this->not_met_requirement = array(
|
225 |
-
'requirement' => $requirement,
|
226 |
-
'extension' => $extension,
|
227 |
-
'requirements' => $extension_requirements
|
228 |
-
);
|
229 |
-
$this->not_met_is_final = true;
|
230 |
-
break 3;
|
231 |
-
}
|
232 |
-
|
233 |
-
// met
|
234 |
-
unset($this->requirements_for_verification[$requirement][$extension]);
|
235 |
-
}
|
236 |
-
|
237 |
-
if (empty($this->requirements_for_verification[$requirement])) {
|
238 |
-
// all extensions requirements met
|
239 |
-
unset($this->requirements_for_verification[$requirement]);
|
240 |
-
}
|
241 |
-
break;
|
242 |
-
}
|
243 |
-
}
|
244 |
-
|
245 |
-
return $this->requirements_met();
|
246 |
-
}
|
247 |
-
|
248 |
-
public function get_version()
|
249 |
-
{
|
250 |
-
return $this->manifest['version'];
|
251 |
-
}
|
252 |
-
|
253 |
-
public function get_name()
|
254 |
-
{
|
255 |
-
return $this->manifest['name'];
|
256 |
-
}
|
257 |
-
|
258 |
-
/**
|
259 |
-
* @param string $multi_key
|
260 |
-
* @param mixed $default_value
|
261 |
-
* @return mixed
|
262 |
-
*/
|
263 |
-
public function get($multi_key, $default_value = null)
|
264 |
-
{
|
265 |
-
return fw_akg($multi_key, $this->manifest, $default_value);
|
266 |
-
}
|
267 |
-
|
268 |
-
/**
|
269 |
-
* Call this only after check_requirements() failed
|
270 |
-
* @return array
|
271 |
-
*/
|
272 |
-
public function get_not_met_requirement()
|
273 |
-
{
|
274 |
-
return $this->not_met_requirement;
|
275 |
-
}
|
276 |
-
|
277 |
-
/**
|
278 |
-
* Return user friendly requirement as text
|
279 |
-
* Call this only after check_requirements() failed
|
280 |
-
* @return string
|
281 |
-
*/
|
282 |
-
public function get_not_met_requirement_text()
|
283 |
-
{
|
284 |
-
if (!$this->not_met_requirement) {
|
285 |
-
return '';
|
286 |
-
}
|
287 |
-
|
288 |
-
$requirement = array();
|
289 |
-
|
290 |
-
foreach ($this->not_met_requirement['requirements'] as $req_key => $req) {
|
291 |
-
switch ($req_key) {
|
292 |
-
case 'min_version':
|
293 |
-
$requirement[] = __('minimum required version is', 'fw') .' '. $req;
|
294 |
-
break;
|
295 |
-
case 'max_version':
|
296 |
-
$requirement[] = __('maximum required version is', 'fw') .' '. $req;
|
297 |
-
break;
|
298 |
-
}
|
299 |
-
}
|
300 |
-
|
301 |
-
$requirement = implode(' '. __('and', 'fw') .' ', $requirement);
|
302 |
-
|
303 |
-
switch ($this->not_met_requirement['requirement']) {
|
304 |
-
case 'wordpress':
|
305 |
-
global $wp_version;
|
306 |
-
|
307 |
-
$requirement = sprintf(
|
308 |
-
__('Current WordPress version is %s, %s', 'fw'),
|
309 |
-
$wp_version, $requirement
|
310 |
-
);
|
311 |
-
break;
|
312 |
-
case 'framework':
|
313 |
-
$requirement = sprintf(
|
314 |
-
__('Current Framework version is %s, %s', 'fw'),
|
315 |
-
fw()->manifest->get_version(), $requirement
|
316 |
-
);
|
317 |
-
break;
|
318 |
-
case 'extensions':
|
319 |
-
$extension = fw()->extensions->get($this->not_met_requirement['extension']);
|
320 |
-
|
321 |
-
if ($extension) {
|
322 |
-
$requirement = sprintf(
|
323 |
-
__('Current version of the %s extension is %s, %s', 'fw'),
|
324 |
-
$extension->manifest->get_name(), $extension->manifest->get_version(), $requirement
|
325 |
-
);
|
326 |
-
} else {
|
327 |
-
if (empty($requirement)) {
|
328 |
-
$requirement = sprintf(
|
329 |
-
__('%s extension is required', 'fw'),
|
330 |
-
ucfirst($this->not_met_requirement['extension'])
|
331 |
-
);
|
332 |
-
} else {
|
333 |
-
$requirement = sprintf(
|
334 |
-
__('%s extension is required (%s)', 'fw'),
|
335 |
-
ucfirst($this->not_met_requirement['extension']), $requirement
|
336 |
-
);
|
337 |
-
}
|
338 |
-
}
|
339 |
-
break;
|
340 |
-
default:
|
341 |
-
$requirement = 'Unknown requirement "'. $this->not_met_requirement['requirement'] .'"';
|
342 |
-
}
|
343 |
-
|
344 |
-
return $requirement;
|
345 |
-
}
|
346 |
-
}
|
347 |
-
|
348 |
-
class FW_Framework_Manifest extends FW_Manifest
|
349 |
-
{
|
350 |
-
public function __construct(array $manifest)
|
351 |
-
{
|
352 |
-
if (empty($manifest['name'])) {
|
353 |
-
$manifest['name'] = __('Framework', 'fw');
|
354 |
-
}
|
355 |
-
|
356 |
-
parent::__construct($manifest);
|
357 |
-
}
|
358 |
-
|
359 |
-
protected function get_default_requirements()
|
360 |
-
{
|
361 |
-
return array(
|
362 |
-
'wordpress' => array(
|
363 |
-
'min_version' => '4.0',
|
364 |
-
/*'max_version' => '10000.0.0',*/
|
365 |
-
),
|
366 |
-
);
|
367 |
-
}
|
368 |
-
}
|
369 |
-
|
370 |
-
class FW_Theme_Manifest extends FW_Manifest
|
371 |
-
{
|
372 |
-
public function __construct(array $manifest)
|
373 |
-
{
|
374 |
-
$manifest_defaults = array(
|
375 |
-
/**
|
376 |
-
* You can use this in a wp_option id,
|
377 |
-
* so that option value will be different on a theme with different id.
|
378 |
-
*
|
379 |
-
* fixme: default value should be get_option( 'stylesheet' ) but it can't be changed now
|
380 |
-
* because there can be themes that has saved Theme Settings in wp_option: 'fw_theme_settings_options:default'
|
381 |
-
* changing this default value will result in Theme Settings options "reset".
|
382 |
-
*/
|
383 |
-
'id' => 'default',
|
384 |
-
'supported_extensions' => array(
|
385 |
-
/*
|
386 |
-
'extension_name' => array(),
|
387 |
-
*/
|
388 |
-
),
|
389 |
-
);
|
390 |
-
|
391 |
-
$theme = wp_get_theme();
|
392 |
-
|
393 |
-
foreach(array(
|
394 |
-
'name' => 'Name',
|
395 |
-
'uri' => 'ThemeURI',
|
396 |
-
'description' => 'Description',
|
397 |
-
'version' => 'Version',
|
398 |
-
'author' => 'Author',
|
399 |
-
'author_uri' => 'AuthorURI',
|
400 |
-
) as $manifest_key => $stylesheet_header) {
|
401 |
-
$header_value = trim($theme->get($stylesheet_header));
|
402 |
-
|
403 |
-
if ( is_child_theme() && $theme->parent() ) {
|
404 |
-
switch ($manifest_key) {
|
405 |
-
case 'version':
|
406 |
-
case 'uri':
|
407 |
-
case 'author':
|
408 |
-
case 'author_uri':
|
409 |
-
case 'license':
|
410 |
-
// force parent theme value
|
411 |
-
$header_value = $theme->parent()->get($stylesheet_header);
|
412 |
-
break;
|
413 |
-
default:
|
414 |
-
if (!$header_value) {
|
415 |
-
// use parent theme value only if child theme value is empty
|
416 |
-
$header_value = $theme->parent()->get($stylesheet_header);
|
417 |
-
}
|
418 |
-
}
|
419 |
-
}
|
420 |
-
|
421 |
-
if ($header_value) {
|
422 |
-
$manifest_defaults[$manifest_key] = $header_value;
|
423 |
-
}
|
424 |
-
}
|
425 |
-
|
426 |
-
parent::__construct(array_merge($manifest_defaults, $manifest));
|
427 |
-
}
|
428 |
-
|
429 |
-
protected function get_default_requirements()
|
430 |
-
{
|
431 |
-
return array(
|
432 |
-
'wordpress' => array(
|
433 |
-
'min_version' => '4.0',
|
434 |
-
/*'max_version' => '10000.0.0',*/
|
435 |
-
),
|
436 |
-
'framework' => array(
|
437 |
-
/*'min_version' => '0.0.0',
|
438 |
-
'max_version' => '1000.0.0'*/
|
439 |
-
),
|
440 |
-
'extensions' => array(
|
441 |
-
/*'extension_name' => array(
|
442 |
-
'min_version' => '0.0.0',
|
443 |
-
'max_version' => '1000.0.0'
|
444 |
-
)*/
|
445 |
-
)
|
446 |
-
);
|
447 |
-
}
|
448 |
-
|
449 |
-
public function get_id()
|
450 |
-
{
|
451 |
-
return $this->manifest['id'];
|
452 |
-
}
|
453 |
-
}
|
454 |
-
|
455 |
-
class FW_Extension_Manifest extends FW_Manifest
|
456 |
-
{
|
457 |
-
public function __construct(array $manifest)
|
458 |
-
{
|
459 |
-
parent::__construct($manifest);
|
460 |
-
|
461 |
-
unset($manifest);
|
462 |
-
|
463 |
-
// unset unnecessary keys
|
464 |
-
unset($this->manifest['id']);
|
465 |
-
|
466 |
-
$this->manifest = array_merge(array(
|
467 |
-
/**
|
468 |
-
* @type bool Display on the Extensions page or it's a hidden extension
|
469 |
-
*/
|
470 |
-
'display' => false,
|
471 |
-
/**
|
472 |
-
* @type bool If extension can exist alone
|
473 |
-
* false - There is no sense for it to exist alone, it exists only when is required by some other extension.
|
474 |
-
* true - Can exist alone without bothering about other extensions.
|
475 |
-
*/
|
476 |
-
'standalone' => false,
|
477 |
-
/**
|
478 |
-
* @type string Thumbnail used on the Extensions page
|
479 |
-
* All framework extensions has thumbnails set in the available extensions list
|
480 |
-
* but if your extension is not in that list and id located in the theme, you can set the thumbnail via this parameter
|
481 |
-
*/
|
482 |
-
'thumbnail' => null,
|
483 |
-
), $this->manifest);
|
484 |
-
}
|
485 |
-
|
486 |
-
protected function get_default_requirements()
|
487 |
-
{
|
488 |
-
return array(
|
489 |
-
'wordpress' => array(
|
490 |
-
'min_version' => '4.0',
|
491 |
-
/*'max_version' => '10000.0.0',*/
|
492 |
-
),
|
493 |
-
'framework' => array(
|
494 |
-
/*'min_version' => '0.0.0',
|
495 |
-
'max_version' => '1000.0.0'*/
|
496 |
-
),
|
497 |
-
'extensions' => array(
|
498 |
-
/*'extension_name' => array(
|
499 |
-
'min_version' => '0.0.0',
|
500 |
-
'max_version' => '1000.0.0'
|
501 |
-
)*/
|
502 |
-
)
|
503 |
-
);
|
504 |
-
}
|
505 |
-
|
506 |
-
public function get_required_extensions()
|
507 |
-
{
|
508 |
-
return $this->manifest['requirements']['extensions'];
|
509 |
-
}
|
510 |
}
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
|
3 |
+
abstract class FW_Manifest
|
4 |
+
{
|
5 |
+
/**
|
6 |
+
* @var array
|
7 |
+
*/
|
8 |
+
protected $manifest;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* The first requirement that was not met
|
12 |
+
* (that marks that the requirements are not met)
|
13 |
+
*
|
14 |
+
* @var array
|
15 |
+
* array(
|
16 |
+
* 'requirement' => 'wordpress|framework',
|
17 |
+
* 'requirements' => array('min_version' => '1.2.3', ...)
|
18 |
+
* )
|
19 |
+
* or
|
20 |
+
* array(
|
21 |
+
* 'requirement' => 'extensions',
|
22 |
+
* 'extension' => 'extension_name',
|
23 |
+
* 'requirements' => array('min_version' => '1.2.3', ...)
|
24 |
+
* )
|
25 |
+
*/
|
26 |
+
private $not_met_requirement;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* When an requirement that sure will not change is not met and have no sense to execute check_requirements() again
|
30 |
+
* @var bool
|
31 |
+
*/
|
32 |
+
private $not_met_is_final = false;
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Not met requirement and skipped (not verified) requirements after $this->not_met_requirement was found
|
36 |
+
* @var array
|
37 |
+
*/
|
38 |
+
private $requirements_for_verification;
|
39 |
+
|
40 |
+
private $requirements_verification_never_called = true;
|
41 |
+
|
42 |
+
/**
|
43 |
+
* @param array $manifest
|
44 |
+
*/
|
45 |
+
protected function __construct(array $manifest)
|
46 |
+
{
|
47 |
+
$manifest = array_merge(array(
|
48 |
+
'name' => null, // title
|
49 |
+
'uri' => null,
|
50 |
+
'description' => null,
|
51 |
+
'version' => '0.0.0',
|
52 |
+
'author' => null,
|
53 |
+
'author_uri' => null,
|
54 |
+
|
55 |
+
// Custom fields
|
56 |
+
'requirements' => array(),
|
57 |
+
), $manifest);
|
58 |
+
|
59 |
+
/**
|
60 |
+
* Merge $manifest['requirements']
|
61 |
+
*/
|
62 |
+
{
|
63 |
+
$requirements = $manifest['requirements'];
|
64 |
+
|
65 |
+
$manifest['requirements'] = array();
|
66 |
+
|
67 |
+
foreach ($this->get_default_requirements() as $default_requirement => $default_requirements) {
|
68 |
+
$manifest['requirements'][ $default_requirement ] = isset($requirements[$default_requirement])
|
69 |
+
? array_merge(
|
70 |
+
$default_requirements,
|
71 |
+
$requirements[$default_requirement]
|
72 |
+
)
|
73 |
+
: $default_requirements;
|
74 |
+
}
|
75 |
+
|
76 |
+
unset($requirements);
|
77 |
+
}
|
78 |
+
|
79 |
+
$this->requirements_for_verification = $manifest['requirements'];
|
80 |
+
|
81 |
+
$this->manifest = $manifest;
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* @return array { 'requirement' => array('min_version' => '..', 'max_version' => '..') }
|
86 |
+
*/
|
87 |
+
abstract protected function get_default_requirements();
|
88 |
+
|
89 |
+
/**
|
90 |
+
* @return bool
|
91 |
+
*/
|
92 |
+
public function requirements_met()
|
93 |
+
{
|
94 |
+
if ($this->not_met_is_final) {
|
95 |
+
return false;
|
96 |
+
}
|
97 |
+
|
98 |
+
if ($this->requirements_verification_never_called) {
|
99 |
+
$this->requirements_verification_never_called = false;
|
100 |
+
|
101 |
+
$this->check_requirements();
|
102 |
+
}
|
103 |
+
|
104 |
+
return empty($this->requirements_for_verification) && empty($this->not_met_requirement);
|
105 |
+
}
|
106 |
+
|
107 |
+
/**
|
108 |
+
* @return bool
|
109 |
+
*/
|
110 |
+
public function check_requirements()
|
111 |
+
{
|
112 |
+
if ($this->not_met_is_final) {
|
113 |
+
return false;
|
114 |
+
}
|
115 |
+
|
116 |
+
if ($this->requirements_met()) {
|
117 |
+
return true;
|
118 |
+
}
|
119 |
+
|
120 |
+
$this->not_met_requirement = array();
|
121 |
+
|
122 |
+
global $wp_version;
|
123 |
+
|
124 |
+
foreach ($this->requirements_for_verification as $requirement => $requirements) {
|
125 |
+
switch ($requirement) {
|
126 |
+
case 'wordpress':
|
127 |
+
if (
|
128 |
+
isset($requirements['min_version'])
|
129 |
+
&&
|
130 |
+
version_compare($wp_version, $requirements['min_version'], '<')
|
131 |
+
) {
|
132 |
+
$this->not_met_requirement = array(
|
133 |
+
'requirement' => $requirement,
|
134 |
+
'requirements' => $requirements
|
135 |
+
);
|
136 |
+
$this->not_met_is_final = true;
|
137 |
+
break 2;
|
138 |
+
}
|
139 |
+
|
140 |
+
if (
|
141 |
+
isset($requirements['max_version'])
|
142 |
+
&&
|
143 |
+
version_compare($wp_version, $requirements['max_version'], '>')
|
144 |
+
) {
|
145 |
+
$this->not_met_requirement = array(
|
146 |
+
'requirement' => $requirement,
|
147 |
+
'requirements' => $requirements
|
148 |
+
);
|
149 |
+
$this->not_met_is_final = true;
|
150 |
+
break 2;
|
151 |
+
}
|
152 |
+
|
153 |
+
// met
|
154 |
+
unset($this->requirements_for_verification[$requirement]);
|
155 |
+
break;
|
156 |
+
case 'framework':
|
157 |
+
if (
|
158 |
+
isset($requirements['min_version'])
|
159 |
+
&&
|
160 |
+
version_compare(fw()->manifest->get_version(), $requirements['min_version'], '<')
|
161 |
+
) {
|
162 |
+
$this->not_met_requirement = array(
|
163 |
+
'requirement' => $requirement,
|
164 |
+
'requirements' => $requirements
|
165 |
+
);
|
166 |
+
$this->not_met_is_final = true;
|
167 |
+
break 2;
|
168 |
+
}
|
169 |
+
|
170 |
+
if (
|
171 |
+
isset($requirements['max_version'])
|
172 |
+
&&
|
173 |
+
version_compare(fw()->manifest->get_version(), $requirements['max_version'], '>')
|
174 |
+
) {
|
175 |
+
$this->not_met_requirement = array(
|
176 |
+
'requirement' => $requirement,
|
177 |
+
'requirements' => $requirements
|
178 |
+
);
|
179 |
+
$this->not_met_is_final = true;
|
180 |
+
break 2;
|
181 |
+
}
|
182 |
+
|
183 |
+
// met
|
184 |
+
unset($this->requirements_for_verification[$requirement]);
|
185 |
+
break;
|
186 |
+
case 'extensions':
|
187 |
+
$extensions =& $requirements;
|
188 |
+
|
189 |
+
foreach ($extensions as $extension => $extension_requirements) {
|
190 |
+
$extension_instance = fw()->extensions->get($extension);
|
191 |
+
|
192 |
+
if (!$extension_instance) {
|
193 |
+
/**
|
194 |
+
* extension in requirements does not exists
|
195 |
+
* maybe try call this method later and maybe will exist, or it really does not exists
|
196 |
+
*/
|
197 |
+
$this->not_met_requirement = array(
|
198 |
+
'requirement' => $requirement,
|
199 |
+
'extension' => $extension,
|
200 |
+
'requirements' => $extension_requirements
|
201 |
+
);
|
202 |
+
break 3;
|
203 |
+
}
|
204 |
+
|
205 |
+
if (
|
206 |
+
isset($extension_requirements['min_version'])
|
207 |
+
&&
|
208 |
+
version_compare($extension_instance->manifest->get_version(), $extension_requirements['min_version'], '<')
|
209 |
+
) {
|
210 |
+
$this->not_met_requirement = array(
|
211 |
+
'requirement' => $requirement,
|
212 |
+
'extension' => $extension,
|
213 |
+
'requirements' => $extension_requirements
|
214 |
+
);
|
215 |
+
$this->not_met_is_final = true;
|
216 |
+
break 3;
|
217 |
+
}
|
218 |
+
|
219 |
+
if (
|
220 |
+
isset($extension_requirements['max_version'])
|
221 |
+
&&
|
222 |
+
version_compare($extension_instance->manifest->get_version(), $extension_requirements['max_version'], '>')
|
223 |
+
) {
|
224 |
+
$this->not_met_requirement = array(
|
225 |
+
'requirement' => $requirement,
|
226 |
+
'extension' => $extension,
|
227 |
+
'requirements' => $extension_requirements
|
228 |
+
);
|
229 |
+
$this->not_met_is_final = true;
|
230 |
+
break 3;
|
231 |
+
}
|
232 |
+
|
233 |
+
// met
|
234 |
+
unset($this->requirements_for_verification[$requirement][$extension]);
|
235 |
+
}
|
236 |
+
|
237 |
+
if (empty($this->requirements_for_verification[$requirement])) {
|
238 |
+
// all extensions requirements met
|
239 |
+
unset($this->requirements_for_verification[$requirement]);
|
240 |
+
}
|
241 |
+
break;
|
242 |
+
}
|
243 |
+
}
|
244 |
+
|
245 |
+
return $this->requirements_met();
|
246 |
+
}
|
247 |
+
|
248 |
+
public function get_version()
|
249 |
+
{
|
250 |
+
return $this->manifest['version'];
|
251 |
+
}
|
252 |
+
|
253 |
+
public function get_name()
|
254 |
+
{
|
255 |
+
return $this->manifest['name'];
|
256 |
+
}
|
257 |
+
|
258 |
+
/**
|
259 |
+
* @param string $multi_key
|
260 |
+
* @param mixed $default_value
|
261 |
+
* @return mixed
|
262 |
+
*/
|
263 |
+
public function get($multi_key, $default_value = null)
|
264 |
+
{
|
265 |
+
return fw_akg($multi_key, $this->manifest, $default_value);
|
266 |
+
}
|
267 |
+
|
268 |
+
/**
|
269 |
+
* Call this only after check_requirements() failed
|
270 |
+
* @return array
|
271 |
+
*/
|
272 |
+
public function get_not_met_requirement()
|
273 |
+
{
|
274 |
+
return $this->not_met_requirement;
|
275 |
+
}
|
276 |
+
|
277 |
+
/**
|
278 |
+
* Return user friendly requirement as text
|
279 |
+
* Call this only after check_requirements() failed
|
280 |
+
* @return string
|
281 |
+
*/
|
282 |
+
public function get_not_met_requirement_text()
|
283 |
+
{
|
284 |
+
if (!$this->not_met_requirement) {
|
285 |
+
return '';
|
286 |
+
}
|
287 |
+
|
288 |
+
$requirement = array();
|
289 |
+
|
290 |
+
foreach ($this->not_met_requirement['requirements'] as $req_key => $req) {
|
291 |
+
switch ($req_key) {
|
292 |
+
case 'min_version':
|
293 |
+
$requirement[] = __('minimum required version is', 'fw') .' '. $req;
|
294 |
+
break;
|
295 |
+
case 'max_version':
|
296 |
+
$requirement[] = __('maximum required version is', 'fw') .' '. $req;
|
297 |
+
break;
|
298 |
+
}
|
299 |
+
}
|
300 |
+
|
301 |
+
$requirement = implode(' '. __('and', 'fw') .' ', $requirement);
|
302 |
+
|
303 |
+
switch ($this->not_met_requirement['requirement']) {
|
304 |
+
case 'wordpress':
|
305 |
+
global $wp_version;
|
306 |
+
|
307 |
+
$requirement = sprintf(
|
308 |
+
__('Current WordPress version is %s, %s', 'fw'),
|
309 |
+
$wp_version, $requirement
|
310 |
+
);
|
311 |
+
break;
|
312 |
+
case 'framework':
|
313 |
+
$requirement = sprintf(
|
314 |
+
__('Current Framework version is %s, %s', 'fw'),
|
315 |
+
fw()->manifest->get_version(), $requirement
|
316 |
+
);
|
317 |
+
break;
|
318 |
+
case 'extensions':
|
319 |
+
$extension = fw()->extensions->get($this->not_met_requirement['extension']);
|
320 |
+
|
321 |
+
if ($extension) {
|
322 |
+
$requirement = sprintf(
|
323 |
+
__('Current version of the %s extension is %s, %s', 'fw'),
|
324 |
+
$extension->manifest->get_name(), $extension->manifest->get_version(), $requirement
|
325 |
+
);
|
326 |
+
} else {
|
327 |
+
if (empty($requirement)) {
|
328 |
+
$requirement = sprintf(
|
329 |
+
__('%s extension is required', 'fw'),
|
330 |
+
ucfirst($this->not_met_requirement['extension'])
|
331 |
+
);
|
332 |
+
} else {
|
333 |
+
$requirement = sprintf(
|
334 |
+
__('%s extension is required (%s)', 'fw'),
|
335 |
+
ucfirst($this->not_met_requirement['extension']), $requirement
|
336 |
+
);
|
337 |
+
}
|
338 |
+
}
|
339 |
+
break;
|
340 |
+
default:
|
341 |
+
$requirement = 'Unknown requirement "'. $this->not_met_requirement['requirement'] .'"';
|
342 |
+
}
|
343 |
+
|
344 |
+
return $requirement;
|
345 |
+
}
|
346 |
+
}
|
347 |
+
|
348 |
+
class FW_Framework_Manifest extends FW_Manifest
|
349 |
+
{
|
350 |
+
public function __construct(array $manifest)
|
351 |
+
{
|
352 |
+
if (empty($manifest['name'])) {
|
353 |
+
$manifest['name'] = __('Framework', 'fw');
|
354 |
+
}
|
355 |
+
|
356 |
+
parent::__construct($manifest);
|
357 |
+
}
|
358 |
+
|
359 |
+
protected function get_default_requirements()
|
360 |
+
{
|
361 |
+
return array(
|
362 |
+
'wordpress' => array(
|
363 |
+
'min_version' => '4.0',
|
364 |
+
/*'max_version' => '10000.0.0',*/
|
365 |
+
),
|
366 |
+
);
|
367 |
+
}
|
368 |
+
}
|
369 |
+
|
370 |
+
class FW_Theme_Manifest extends FW_Manifest
|
371 |
+
{
|
372 |
+
public function __construct(array $manifest)
|
373 |
+
{
|
374 |
+
$manifest_defaults = array(
|
375 |
+
/**
|
376 |
+
* You can use this in a wp_option id,
|
377 |
+
* so that option value will be different on a theme with different id.
|
378 |
+
*
|
379 |
+
* fixme: default value should be get_option( 'stylesheet' ) but it can't be changed now
|
380 |
+
* because there can be themes that has saved Theme Settings in wp_option: 'fw_theme_settings_options:default'
|
381 |
+
* changing this default value will result in Theme Settings options "reset".
|
382 |
+
*/
|
383 |
+
'id' => 'default',
|
384 |
+
'supported_extensions' => array(
|
385 |
+
/*
|
386 |
+
'extension_name' => array(),
|
387 |
+
*/
|
388 |
+
),
|
389 |
+
);
|
390 |
+
|
391 |
+
$theme = wp_get_theme();
|
392 |
+
|
393 |
+
foreach(array(
|
394 |
+
'name' => 'Name',
|
395 |
+
'uri' => 'ThemeURI',
|
396 |
+
'description' => 'Description',
|
397 |
+
'version' => 'Version',
|
398 |
+
'author' => 'Author',
|
399 |
+
'author_uri' => 'AuthorURI',
|
400 |
+
) as $manifest_key => $stylesheet_header) {
|
401 |
+
$header_value = trim($theme->get($stylesheet_header));
|
402 |
+
|
403 |
+
if ( is_child_theme() && $theme->parent() ) {
|
404 |
+
switch ($manifest_key) {
|
405 |
+
case 'version':
|
406 |
+
case 'uri':
|
407 |
+
case 'author':
|
408 |
+
case 'author_uri':
|
409 |
+
case 'license':
|
410 |
+
// force parent theme value
|
411 |
+
$header_value = $theme->parent()->get($stylesheet_header);
|
412 |
+
break;
|
413 |
+
default:
|
414 |
+
if (!$header_value) {
|
415 |
+
// use parent theme value only if child theme value is empty
|
416 |
+
$header_value = $theme->parent()->get($stylesheet_header);
|
417 |
+
}
|
418 |
+
}
|
419 |
+
}
|
420 |
+
|
421 |
+
if ($header_value) {
|
422 |
+
$manifest_defaults[$manifest_key] = $header_value;
|
423 |
+
}
|
424 |
+
}
|
425 |
+
|
426 |
+
parent::__construct(array_merge($manifest_defaults, $manifest));
|
427 |
+
}
|
428 |
+
|
429 |
+
protected function get_default_requirements()
|
430 |
+
{
|
431 |
+
return array(
|
432 |
+
'wordpress' => array(
|
433 |
+
'min_version' => '4.0',
|
434 |
+
/*'max_version' => '10000.0.0',*/
|
435 |
+
),
|
436 |
+
'framework' => array(
|
437 |
+
/*'min_version' => '0.0.0',
|
438 |
+
'max_version' => '1000.0.0'*/
|
439 |
+
),
|
440 |
+
'extensions' => array(
|
441 |
+
/*'extension_name' => array(
|
442 |
+
'min_version' => '0.0.0',
|
443 |
+
'max_version' => '1000.0.0'
|
444 |
+
)*/
|
445 |
+
)
|
446 |
+
);
|
447 |
+
}
|
448 |
+
|
449 |
+
public function get_id()
|
450 |
+
{
|
451 |
+
return $this->manifest['id'];
|
452 |
+
}
|
453 |
+
}
|
454 |
+
|
455 |
+
class FW_Extension_Manifest extends FW_Manifest
|
456 |
+
{
|
457 |
+
public function __construct(array $manifest)
|
458 |
+
{
|
459 |
+
parent::__construct($manifest);
|
460 |
+
|
461 |
+
unset($manifest);
|
462 |
+
|
463 |
+
// unset unnecessary keys
|
464 |
+
unset($this->manifest['id']);
|
465 |
+
|
466 |
+
$this->manifest = array_merge(array(
|
467 |
+
/**
|
468 |
+
* @type bool Display on the Extensions page or it's a hidden extension
|
469 |
+
*/
|
470 |
+
'display' => false,
|
471 |
+
/**
|
472 |
+
* @type bool If extension can exist alone
|
473 |
+
* false - There is no sense for it to exist alone, it exists only when is required by some other extension.
|
474 |
+
* true - Can exist alone without bothering about other extensions.
|
475 |
+
*/
|
476 |
+
'standalone' => false,
|
477 |
+
/**
|
478 |
+
* @type string Thumbnail used on the Extensions page
|
479 |
+
* All framework extensions has thumbnails set in the available extensions list
|
480 |
+
* but if your extension is not in that list and id located in the theme, you can set the thumbnail via this parameter
|
481 |
+
*/
|
482 |
+
'thumbnail' => null,
|
483 |
+
), $this->manifest);
|
484 |
+
}
|
485 |
+
|
486 |
+
protected function get_default_requirements()
|
487 |
+
{
|
488 |
+
return array(
|
489 |
+
'wordpress' => array(
|
490 |
+
'min_version' => '4.0',
|
491 |
+
/*'max_version' => '10000.0.0',*/
|
492 |
+
),
|
493 |
+
'framework' => array(
|
494 |
+
/*'min_version' => '0.0.0',
|
495 |
+
'max_version' => '1000.0.0'*/
|
496 |
+
),
|
497 |
+
'extensions' => array(
|
498 |
+
/*'extension_name' => array(
|
499 |
+
'min_version' => '0.0.0',
|
500 |
+
'max_version' => '1000.0.0'
|
501 |
+
)*/
|
502 |
+
)
|
503 |
+
);
|
504 |
+
}
|
505 |
+
|
506 |
+
public function get_required_extensions()
|
507 |
+
{
|
508 |
+
return $this->manifest['requirements']['extensions'];
|
509 |
+
}
|
510 |
}
|
framework/core/components/backend.php
CHANGED
@@ -1,2146 +1,2146 @@
|
|
1 |
-
<?php if ( ! defined( 'FW' ) ) {
|
2 |
-
die( 'Forbidden' );
|
3 |
-
}
|
4 |
-
|
5 |
-
/**
|
6 |
-
* Backend functionality
|
7 |
-
*/
|
8 |
-
final class _FW_Component_Backend {
|
9 |
-
|
10 |
-
/** @var callable */
|
11 |
-
private $print_meta_box_content_callback;
|
12 |
-
|
13 |
-
/** @var FW_Form */
|
14 |
-
private $settings_form;
|
15 |
-
|
16 |
-
private $available_render_designs = array( 'default', 'taxonomy', 'customizer' );
|
17 |
-
|
18 |
-
private $default_render_design = 'default';
|
19 |
-
|
20 |
-
/**
|
21 |
-
* Store option types for registration, until they will be required
|
22 |
-
* @var array|false
|
23 |
-
* array Can have some pending option types in it
|
24 |
-
* false Option types already requested and was registered, so do not use pending anymore
|
25 |
-
*/
|
26 |
-
private $option_types_pending_registration = array();
|
27 |
-
|
28 |
-
/**
|
29 |
-
* Contains all option types
|
30 |
-
* @var FW_Option_Type[]
|
31 |
-
*/
|
32 |
-
private $option_types = array();
|
33 |
-
|
34 |
-
/**
|
35 |
-
* @var FW_Option_Type_Undefined
|
36 |
-
*/
|
37 |
-
private $undefined_option_type;
|
38 |
-
|
39 |
-
/**
|
40 |
-
* Store container types for registration, until they will be required
|
41 |
-
* @var array|false
|
42 |
-
* array Can have some pending container types in it
|
43 |
-
* false Container types already requested and was registered, so do not use pending anymore
|
44 |
-
*/
|
45 |
-
private $container_types_pending_registration = array();
|
46 |
-
|
47 |
-
/**
|
48 |
-
* Contains all container types
|
49 |
-
* @var FW_Container_Type[]
|
50 |
-
*/
|
51 |
-
private $container_types = array();
|
52 |
-
|
53 |
-
/**
|
54 |
-
* @var FW_Container_Type_Undefined
|
55 |
-
*/
|
56 |
-
private $undefined_container_type;
|
57 |
-
|
58 |
-
private $static_registered = false;
|
59 |
-
|
60 |
-
/**
|
61 |
-
* @var FW_Access_Key
|
62 |
-
*/
|
63 |
-
private $access_key;
|
64 |
-
|
65 |
-
/**
|
66 |
-
* @internal
|
67 |
-
*/
|
68 |
-
public function _get_settings_page_slug() {
|
69 |
-
return 'fw-settings';
|
70 |
-
}
|
71 |
-
|
72 |
-
private function get_current_edit_taxonomy() {
|
73 |
-
static $cache_current_taxonomy_data = null;
|
74 |
-
|
75 |
-
if ( $cache_current_taxonomy_data !== null ) {
|
76 |
-
return $cache_current_taxonomy_data;
|
77 |
-
}
|
78 |
-
|
79 |
-
$result = array(
|
80 |
-
'taxonomy' => null,
|
81 |
-
'term_id' => 0,
|
82 |
-
);
|
83 |
-
|
84 |
-
do {
|
85 |
-
if ( ! is_admin() ) {
|
86 |
-
break;
|
87 |
-
}
|
88 |
-
|
89 |
-
// code from /wp-admin/admin.php line 110
|
90 |
-
{
|
91 |
-
if ( isset( $_REQUEST['taxonomy'] ) && taxonomy_exists( $_REQUEST['taxonomy'] ) ) {
|
92 |
-
$taxnow = $_REQUEST['taxonomy'];
|
93 |
-
} else {
|
94 |
-
$taxnow = '';
|
95 |
-
}
|
96 |
-
}
|
97 |
-
|
98 |
-
if ( empty( $taxnow ) ) {
|
99 |
-
break;
|
100 |
-
}
|
101 |
-
|
102 |
-
$result['taxonomy'] = $taxnow;
|
103 |
-
|
104 |
-
if ( empty( $_REQUEST['tag_ID'] ) ) {
|
105 |
-
return $result;
|
106 |
-
}
|
107 |
-
|
108 |
-
// code from /wp-admin/edit-tags.php
|
109 |
-
{
|
110 |
-
$tag_ID = (int) $_REQUEST['tag_ID'];
|
111 |
-
}
|
112 |
-
|
113 |
-
$result['term_id'] = $tag_ID;
|
114 |
-
} while ( false );
|
115 |
-
|
116 |
-
$cache_current_taxonomy_data = $result;
|
117 |
-
|
118 |
-
return $cache_current_taxonomy_data;
|
119 |
-
}
|
120 |
-
|
121 |
-
public function __construct() {
|
122 |
-
$this->print_meta_box_content_callback = create_function( '$post,$args', 'echo $args["args"];' );
|
123 |
-
}
|
124 |
-
|
125 |
-
/**
|
126 |
-
* @internal
|
127 |
-
*/
|
128 |
-
public function _init() {
|
129 |
-
if ( is_admin() ) {
|
130 |
-
$this->settings_form = new FW_Form('fw_settings', array(
|
131 |
-
'render' => array($this, '_settings_form_render'),
|
132 |
-
'validate' => array($this, '_settings_form_validate'),
|
133 |
-
'save' => array($this, '_settings_form_save'),
|
134 |
-
));
|
135 |
-
}
|
136 |
-
|
137 |
-
$this->add_actions();
|
138 |
-
$this->add_filters();
|
139 |
-
}
|
140 |
-
|
141 |
-
/**
|
142 |
-
* @internal
|
143 |
-
*/
|
144 |
-
public function _after_components_init() {}
|
145 |
-
|
146 |
-
private function get_access_key()
|
147 |
-
{
|
148 |
-
if (!$this->access_key) {
|
149 |
-
$this->access_key = new FW_Access_Key('fw_backend');
|
150 |
-
}
|
151 |
-
|
152 |
-
return $this->access_key;
|
153 |
-
}
|
154 |
-
|
155 |
-
private function add_actions() {
|
156 |
-
if ( is_admin() ) {
|
157 |
-
add_action('admin_menu', array($this, '_action_admin_menu'));
|
158 |
-
add_action('add_meta_boxes', array($this, '_action_create_post_meta_boxes'), 10, 2);
|
159 |
-
add_action('init', array($this, '_action_init'), 20);
|
160 |
-
add_action('admin_enqueue_scripts', array($this, '_action_admin_register_scripts'),
|
161 |
-
/**
|
162 |
-
* Usually when someone register/enqueue a script/style to be used in other places
|
163 |
-
* in 'admin_enqueue_scripts' actions with default (not set) priority 10, they use priority 9.
|
164 |
-
* Use here priority 8, in case those scripts/styles used in actions with priority 9
|
165 |
-
* are using scripts/styles registered here
|
166 |
-
*/
|
167 |
-
8
|
168 |
-
);
|
169 |
-
add_action('admin_enqueue_scripts', array($this, '_action_admin_enqueue_scripts'),
|
170 |
-
/**
|
171 |
-
* In case some custom defined option types are using script/styles registered
|
172 |
-
* in actions with default priority 10 (make sure the enqueue is executed after register)
|
173 |
-
*/
|
174 |
-
11
|
175 |
-
);
|
176 |
-
|
177 |
-
// render and submit options from javascript
|
178 |
-
{
|
179 |
-
add_action('wp_ajax_fw_backend_options_render', array($this, '_action_ajax_options_render'));
|
180 |
-
add_action('wp_ajax_fw_backend_options_get_values', array($this, '_action_ajax_options_get_values'));
|
181 |
-
}
|
182 |
-
}
|
183 |
-
|
184 |
-
add_action('save_post', array($this, '_action_save_post'), 7, 3);
|
185 |
-
add_action('wp_restore_post_revision', array($this, '_action_restore_post_revision'), 10, 2);
|
186 |
-
add_action('_wp_put_post_revision', array($this, '_action__wp_put_post_revision'));
|
187 |
-
|
188 |
-
add_action('customize_register', array($this, '_action_customize_register'), 7);
|
189 |
-
}
|
190 |
-
|
191 |
-
private function add_filters() {
|
192 |
-
if ( is_admin() ) {
|
193 |
-
add_filter('admin_footer_text', array($this, '_filter_admin_footer_text'), 11);
|
194 |
-
add_filter('update_footer', array($this, '_filter_footer_version'), 11);
|
195 |
-
}
|
196 |
-
}
|
197 |
-
|
198 |
-
/**
|
199 |
-
* @param string|FW_Option_Type $option_type_class
|
200 |
-
*
|
201 |
-
* @internal
|
202 |
-
*/
|
203 |
-
private function register_option_type( $option_type_class ) {
|
204 |
-
if ( is_array( $this->option_types_pending_registration ) ) {
|
205 |
-
// Option types never requested. Continue adding to pending
|
206 |
-
$this->option_types_pending_registration[] = $option_type_class;
|
207 |
-
} else {
|
208 |
-
if ( is_string( $option_type_class ) ) {
|
209 |
-
$option_type_class = new $option_type_class;
|
210 |
-
}
|
211 |
-
|
212 |
-
if ( ! is_subclass_of( $option_type_class, 'FW_Option_Type' ) ) {
|
213 |
-
trigger_error( 'Invalid option type class ' . get_class( $option_type_class ), E_USER_WARNING );
|
214 |
-
return;
|
215 |
-
}
|
216 |
-
|
217 |
-
/**
|
218 |
-
* @var FW_Option_Type $option_type_class
|
219 |
-
*/
|
220 |
-
|
221 |
-
$type = $option_type_class->get_type();
|
222 |
-
|
223 |
-
if ( isset( $this->option_types[ $type ] ) ) {
|
224 |
-
trigger_error( 'Option type "' . $type . '" already registered', E_USER_WARNING );
|
225 |
-
return;
|
226 |
-
}
|
227 |
-
|
228 |
-
$this->option_types[ $type ] = $option_type_class;
|
229 |
-
|
230 |
-
$this->option_types[ $type ]->_call_init($this->get_access_key());
|
231 |
-
}
|
232 |
-
}
|
233 |
-
|
234 |
-
/**
|
235 |
-
* @param string|FW_Container_Type $container_type_class
|
236 |
-
*
|
237 |
-
* @internal
|
238 |
-
*/
|
239 |
-
private function register_container_type( $container_type_class ) {
|
240 |
-
if ( is_array( $this->container_types_pending_registration ) ) {
|
241 |
-
// Container types never requested. Continue adding to pending
|
242 |
-
$this->container_types_pending_registration[] = $container_type_class;
|
243 |
-
} else {
|
244 |
-
if ( is_string( $container_type_class ) ) {
|
245 |
-
$container_type_class = new $container_type_class;
|
246 |
-
}
|
247 |
-
|
248 |
-
if ( ! is_subclass_of( $container_type_class, 'FW_Container_Type' ) ) {
|
249 |
-
trigger_error( 'Invalid container type class ' . get_class( $container_type_class ), E_USER_WARNING );
|
250 |
-
return;
|
251 |
-
}
|
252 |
-
|
253 |
-
/**
|
254 |
-
* @var FW_Container_Type $container_type_class
|
255 |
-
*/
|
256 |
-
|
257 |
-
$type = $container_type_class->get_type();
|
258 |
-
|
259 |
-
if ( isset( $this->container_types[ $type ] ) ) {
|
260 |
-
trigger_error( 'Container type "' . $type . '" already registered', E_USER_WARNING );
|
261 |
-
return;
|
262 |
-
}
|
263 |
-
|
264 |
-
$this->container_types[ $type ] = $container_type_class;
|
265 |
-
|
266 |
-
$this->container_types[ $type ]->_call_init($this->get_access_key());
|
267 |
-
}
|
268 |
-
}
|
269 |
-
|
270 |
-
private function register_static() {
|
271 |
-
if (
|
272 |
-
!doing_action('admin_enqueue_scripts')
|
273 |
-
&&
|
274 |
-
!did_action('admin_enqueue_scripts')
|
275 |
-
) {
|
276 |
-
/**
|
277 |
-
* Do not wp_enqueue/register_...() because at this point not all handles has been registered
|
278 |
-
* and maybe they are used in dependencies in handles that are going to be enqueued.
|
279 |
-
* So as a result some handles will not be equeued because of not registered dependecies.
|
280 |
-
*/
|
281 |
-
return;
|
282 |
-
}
|
283 |
-
|
284 |
-
if ( $this->static_registered ) {
|
285 |
-
return;
|
286 |
-
}
|
287 |
-
|
288 |
-
/**
|
289 |
-
* Register styles/scripts only in admin area, on frontend it's not allowed to use styles/scripts from framework backend core
|
290 |
-
* because they are meant to be used only in backend and can be changed in the future.
|
291 |
-
* If you want to use a style/script from framework backend core, copy it to your theme and enqueue as a theme style/script.
|
292 |
-
*/
|
293 |
-
if ( ! is_admin() ) {
|
294 |
-
$this->static_registered = true;
|
295 |
-
|
296 |
-
return;
|
297 |
-
}
|
298 |
-
|
299 |
-
wp_register_script(
|
300 |
-
'fw-events',
|
301 |
-
fw_get_framework_directory_uri( '/static/js/fw-events.js' ),
|
302 |
-
array( 'backbone' ),
|
303 |
-
fw()->manifest->get_version(),
|
304 |
-
true
|
305 |
-
);
|
306 |
-
|
307 |
-
wp_register_script(
|
308 |
-
'fw-ie-fixes',
|
309 |
-
fw_get_framework_directory_uri( '/static/js/ie-fixes.js' ),
|
310 |
-
array(),
|
311 |
-
fw()->manifest->get_version(),
|
312 |
-
true
|
313 |
-
);
|
314 |
-
|
315 |
-
{
|
316 |
-
wp_register_style(
|
317 |
-
'qtip',
|
318 |
-
fw_get_framework_directory_uri( '/static/libs/qtip/css/jquery.qtip.min.css' ),
|
319 |
-
array(),
|
320 |
-
fw()->manifest->get_version()
|
321 |
-
);
|
322 |
-
wp_register_script(
|
323 |
-
'qtip',
|
324 |
-
fw_get_framework_directory_uri( '/static/libs/qtip/jquery.qtip.min.js' ),
|
325 |
-
array( 'jquery' ),
|
326 |
-
fw()->manifest->get_version()
|
327 |
-
);
|
328 |
-
}
|
329 |
-
|
330 |
-
/**
|
331 |
-
* Important!
|
332 |
-
* Call wp_enqueue_media() before wp_enqueue_script('fw') (or using 'fw' in your script dependencies)
|
333 |
-
* otherwise fw.OptionsModal won't work
|
334 |
-
*/
|
335 |
-
{
|
336 |
-
wp_register_style(
|
337 |
-
'fw',
|
338 |
-
fw_get_framework_directory_uri( '/static/css/fw.css' ),
|
339 |
-
array( 'qtip' ),
|
340 |
-
fw()->manifest->get_version()
|
341 |
-
);
|
342 |
-
|
343 |
-
wp_register_script(
|
344 |
-
'fw',
|
345 |
-
fw_get_framework_directory_uri( '/static/js/fw.js' ),
|
346 |
-
array( 'jquery', 'fw-events', 'backbone', 'qtip' ),
|
347 |
-
fw()->manifest->get_version(),
|
348 |
-
true
|
349 |
-
);
|
350 |
-
|
351 |
-
wp_localize_script( 'fw', '_fw_localized', array(
|
352 |
-
'FW_URI' => fw_get_framework_directory_uri(),
|
353 |
-
'SITE_URI' => site_url(),
|
354 |
-
'LOADER_URI' => apply_filters( 'fw_loader_image', fw_get_framework_directory_uri() . '/static/img/logo.svg' ),
|
355 |
-
'l10n' => array(
|
356 |
-
'done' => __( 'Done', 'fw' ),
|
357 |
-
'ah_sorry' => __( 'Ah, Sorry', 'fw' ),
|
358 |
-
'save' => __( 'Save', 'fw' ),
|
359 |
-
'reset' => __( 'Reset', 'fw' ),
|
360 |
-
),
|
361 |
-
) );
|
362 |
-
}
|
363 |
-
|
364 |
-
{
|
365 |
-
wp_register_style(
|
366 |
-
'fw-backend-options',
|
367 |
-
fw_get_framework_directory_uri( '/static/css/backend-options.css' ),
|
368 |
-
array( 'fw' ),
|
369 |
-
fw()->manifest->get_version()
|
370 |
-
);
|
371 |
-
|
372 |
-
wp_register_script(
|
373 |
-
'fw-backend-options',
|
374 |
-
fw_get_framework_directory_uri( '/static/js/backend-options.js' ),
|
375 |
-
array( 'fw', 'fw-events', 'postbox', 'jquery-ui-tabs' ),
|
376 |
-
fw()->manifest->get_version(),
|
377 |
-
true
|
378 |
-
);
|
379 |
-
|
380 |
-
wp_localize_script( 'fw', '_fw_backend_options_localized', array(
|
381 |
-
'lazy_tabs' => fw()->theme->get_config('lazy_tabs')
|
382 |
-
) );
|
383 |
-
}
|
384 |
-
|
385 |
-
{
|
386 |
-
wp_register_style(
|
387 |
-
'fw-selectize',
|
388 |
-
fw_get_framework_directory_uri( '/static/libs/selectize/selectize.css' ),
|
389 |
-
array(),
|
390 |
-
fw()->manifest->get_version()
|
391 |
-
);
|
392 |
-
wp_register_script(
|
393 |
-
'fw-selectize',
|
394 |
-
fw_get_framework_directory_uri( '/static/libs/selectize/selectize.min.js' ),
|
395 |
-
array( 'jquery', 'fw-ie-fixes' ),
|
396 |
-
fw()->manifest->get_version(),
|
397 |
-
true
|
398 |
-
);
|
399 |
-
}
|
400 |
-
|
401 |
-
{
|
402 |
-
wp_register_script(
|
403 |
-
'fw-mousewheel',
|
404 |
-
fw_get_framework_directory_uri( '/static/libs/mousewheel/jquery.mousewheel.min.js' ),
|
405 |
-
array( 'jquery' ),
|
406 |
-
fw()->manifest->get_version(),
|
407 |
-
true
|
408 |
-
);
|
409 |
-
}
|
410 |
-
|
411 |
-
{
|
412 |
-
wp_register_style(
|
413 |
-
'fw-jscrollpane',
|
414 |
-
fw_get_framework_directory_uri( '/static/libs/jscrollpane/jquery.jscrollpane.css' ),
|
415 |
-
array(),
|
416 |
-
fw()->manifest->get_version()
|
417 |
-
);
|
418 |
-
wp_register_script( 'fw-jscrollpane',
|
419 |
-
fw_get_framework_directory_uri( '/static/libs/jscrollpane/jquery.jscrollpane.min.js' ),
|
420 |
-
array( 'jquery', 'fw-mousewheel' ),
|
421 |
-
fw()->manifest->get_version(),
|
422 |
-
true
|
423 |
-
);
|
424 |
-
}
|
425 |
-
|
426 |
-
wp_register_style(
|
427 |
-
'fw-font-awesome',
|
428 |
-
fw_get_framework_directory_uri( '/static/libs/font-awesome/css/font-awesome.min.css' ),
|
429 |
-
array(),
|
430 |
-
fw()->manifest->get_version()
|
431 |
-
);
|
432 |
-
|
433 |
-
wp_register_script(
|
434 |
-
'backbone-relational',
|
435 |
-
fw_get_framework_directory_uri( '/static/libs/backbone-relational/backbone-relational.js' ),
|
436 |
-
array( 'backbone' ),
|
437 |
-
fw()->manifest->get_version(),
|
438 |
-
true
|
439 |
-
);
|
440 |
-
|
441 |
-
wp_register_script(
|
442 |
-
'fw-uri',
|
443 |
-
fw_get_framework_directory_uri( '/static/libs/uri/URI.js' ),
|
444 |
-
array(),
|
445 |
-
fw()->manifest->get_version(),
|
446 |
-
true
|
447 |
-
);
|
448 |
-
|
449 |
-
wp_register_script(
|
450 |
-
'fw-moment',
|
451 |
-
fw_get_framework_directory_uri( '/static/libs/moment/moment.min.js' ),
|
452 |
-
array(),
|
453 |
-
fw()->manifest->get_version(),
|
454 |
-
true
|
455 |
-
);
|
456 |
-
|
457 |
-
wp_register_script(
|
458 |
-
'fw-form-helpers',
|
459 |
-
fw_get_framework_directory_uri( '/static/js/fw-form-helpers.js' ),
|
460 |
-
array( 'jquery' ),
|
461 |
-
fw()->manifest->get_version(),
|
462 |
-
true
|
463 |
-
);
|
464 |
-
|
465 |
-
wp_register_style(
|
466 |
-
'fw-unycon',
|
467 |
-
fw_get_framework_directory_uri( '/static/libs/unycon/unycon.css' ),
|
468 |
-
array(),
|
469 |
-
fw()->manifest->get_version()
|
470 |
-
);
|
471 |
-
|
472 |
-
$this->static_registered = true;
|
473 |
-
}
|
474 |
-
|
475 |
-
/**
|
476 |
-
* @internal
|
477 |
-
*/
|
478 |
-
public function _action_admin_menu() {
|
479 |
-
$data = array(
|
480 |
-
'capability' => 'manage_options',
|
481 |
-
'slug' => $this->_get_settings_page_slug(),
|
482 |
-
'content_callback' => array( $this, '_print_settings_page' ),
|
483 |
-
);
|
484 |
-
|
485 |
-
if ( ! current_user_can( $data['capability'] ) ) {
|
486 |
-
return;
|
487 |
-
}
|
488 |
-
|
489 |
-
if ( ! fw()->theme->locate_path('/options/settings.php') ) {
|
490 |
-
return;
|
491 |
-
}
|
492 |
-
|
493 |
-
/**
|
494 |
-
* Collect $hookname that contains $data['slug'] before the action
|
495 |
-
* and skip them in verification after action
|
496 |
-
*/
|
497 |
-
{
|
498 |
-
global $_registered_pages;
|
499 |
-
|
500 |
-
$found_hooknames = array();
|
501 |
-
|
502 |
-
if ( ! empty( $_registered_pages ) ) {
|
503 |
-
foreach ( $_registered_pages as $hookname => $b ) {
|
504 |
-
if ( strpos( $hookname, $data['slug'] ) !== false ) {
|
505 |
-
$found_hooknames[ $hookname ] = true;
|
506 |
-
}
|
507 |
-
}
|
508 |
-
}
|
509 |
-
}
|
510 |
-
|
511 |
-
/**
|
512 |
-
* Use this action if you what to add the settings page in a custom place in menu
|
513 |
-
* Usage example http://pastebin.com/gvAjGRm1
|
514 |
-
*/
|
515 |
-
do_action( 'fw_backend_add_custom_settings_menu', $data );
|
516 |
-
|
517 |
-
/**
|
518 |
-
* Check if settings menu was added in the action above
|
519 |
-
*/
|
520 |
-
{
|
521 |
-
$menu_exists = false;
|
522 |
-
|
523 |
-
if ( ! empty( $_registered_pages ) ) {
|
524 |
-
foreach ( $_registered_pages as $hookname => $b ) {
|
525 |
-
if ( isset( $found_hooknames[ $hookname ] ) ) {
|
526 |
-
continue;
|
527 |
-
}
|
528 |
-
|
529 |
-
if ( strpos( $hookname, $data['slug'] ) !== false ) {
|
530 |
-
$menu_exists = true;
|
531 |
-
break;
|
532 |
-
}
|
533 |
-
}
|
534 |
-
}
|
535 |
-
}
|
536 |
-
|
537 |
-
if ( $menu_exists ) {
|
538 |
-
return;
|
539 |
-
}
|
540 |
-
|
541 |
-
add_theme_page(
|
542 |
-
__( 'Theme Settings', 'fw' ),
|
543 |
-
__( 'Theme Settings', 'fw' ),
|
544 |
-
$data['capability'],
|
545 |
-
$data['slug'],
|
546 |
-
$data['content_callback']
|
547 |
-
);
|
548 |
-
|
549 |
-
add_action( 'admin_menu', array( $this, '_action_admin_change_theme_settings_order' ), 9999 );
|
550 |
-
}
|
551 |
-
|
552 |
-
public function _filter_admin_footer_text( $html ) {
|
553 |
-
if (
|
554 |
-
(
|
555 |
-
current_user_can( 'update_themes' )
|
556 |
-
||
|
557 |
-
current_user_can( 'update_plugins' )
|
558 |
-
)
|
559 |
-
&&
|
560 |
-
fw_current_screen_match(array(
|
561 |
-
'only' => array(
|
562 |
-
array('parent_base' => fw()->extensions->manager->get_page_slug()) // Unyson Extensions page
|
563 |
-
)
|
564 |
-
))
|
565 |
-
) {
|
566 |
-
return ( empty( $html ) ? '' : $html . '<br/>' )
|
567 |
-
. '<em>'
|
568 |
-
. str_replace(
|
569 |
-
array(
|
570 |
-
'{wp_review_link}',
|
571 |
-
'{facebook_share_link}',
|
572 |
-
'{twitter_share_link}',
|
573 |
-
),
|
574 |
-
array(
|
575 |
-
fw_html_tag('a', array(
|
576 |
-
'target' => '_blank',
|
577 |
-
'href' => 'https://wordpress.org/support/view/plugin-reviews/unyson?filter=5#postform',
|
578 |
-
), __('leave a review', 'fw')),
|
579 |
-
fw_html_tag('a', array(
|
580 |
-
'target' => '_blank',
|
581 |
-
'href' => 'https://www.facebook.com/sharer/sharer.php?'. http_build_query(array(
|
582 |
-
'u' => 'http://unyson.io',
|
583 |
-
)),
|
584 |
-
'onclick' => 'return !window.open(this.href, \'Facebook\', \'width=640,height=300\')',
|
585 |
-
), __('Facebook', 'fw')),
|
586 |
-
fw_html_tag('a', array(
|
587 |
-
'target' => '_blank',
|
588 |
-
'href' => 'https://twitter.com/home?'. http_build_query(array(
|
589 |
-
'status' => __('Unyson WordPress Framework is the fastest and easiest way to develop a premium theme. I highly recommend it', 'fw')
|
590 |
-
.' http://unyson.io/ #UnysonWP',
|
591 |
-
)),
|
592 |
-
'onclick' => 'return !window.open(this.href, \'Twitter\', \'width=640,height=430\')',
|
593 |
-
), __('Twitter', 'fw')),
|
594 |
-
),
|
595 |
-
__('If you like Unyson, {wp_review_link}, share on {facebook_share_link} or {twitter_share_link}.', 'fw')
|
596 |
-
)
|
597 |
-
. '</em>';
|
598 |
-
} else {
|
599 |
-
return $html;
|
600 |
-
}
|
601 |
-
}
|
602 |
-
|
603 |
-
/**
|
604 |
-
* Print framework version in the admin footer
|
605 |
-
*
|
606 |
-
* @param string $html
|
607 |
-
*
|
608 |
-
* @return string
|
609 |
-
* @internal
|
610 |
-
*/
|
611 |
-
public function _filter_footer_version( $html ) {
|
612 |
-
if ( current_user_can( 'update_themes' ) || current_user_can( 'update_plugins' ) ) {
|
613 |
-
return ( empty( $html ) ? '' : $html . ' | ' ) . fw()->manifest->get_name() . ' ' . fw()->manifest->get_version();
|
614 |
-
} else {
|
615 |
-
return $html;
|
616 |
-
}
|
617 |
-
}
|
618 |
-
|
619 |
-
public function _action_admin_change_theme_settings_order() {
|
620 |
-
global $submenu;
|
621 |
-
|
622 |
-
if ( ! isset( $submenu['themes.php'] ) ) {
|
623 |
-
// probably current user doesn't have this item in menu
|
624 |
-
return;
|
625 |
-
}
|
626 |
-
|
627 |
-
$id = $this->_get_settings_page_slug();
|
628 |
-
$index = null;
|
629 |
-
|
630 |
-
foreach ( $submenu['themes.php'] as $key => $sm ) {
|
631 |
-
if ( $sm[2] == $id ) {
|
632 |
-
$index = $key;
|
633 |
-
break;
|
634 |
-
}
|
635 |
-
}
|
636 |
-
|
637 |
-
if ( ! empty( $index ) ) {
|
638 |
-
$item = $submenu['themes.php'][ $index ];
|
639 |
-
unset( $submenu['themes.php'][ $index ] );
|
640 |
-
array_unshift( $submenu['themes.php'], $item );
|
641 |
-
}
|
642 |
-
}
|
643 |
-
|
644 |
-
public function _print_settings_page() {
|
645 |
-
echo '<div class="wrap">';
|
646 |
-
|
647 |
-
if ( fw()->theme->get_config( 'settings_form_side_tabs' ) ) {
|
648 |
-
// this is needed for flash messages (admin notices) to be displayed properly
|
649 |
-
echo '<h2 class="fw-hidden"></h2>';
|
650 |
-
} else {
|
651 |
-
echo '<h2>' . __( 'Theme Settings', 'fw' ) . '</h2><br/>';
|
652 |
-
}
|
653 |
-
|
654 |
-
$this->settings_form->render();
|
655 |
-
|
656 |
-
echo '</div>';
|
657 |
-
}
|
658 |
-
|
659 |
-
/**
|
660 |
-
* @param string $post_type
|
661 |
-
* @param WP_Post $post
|
662 |
-
*/
|
663 |
-
public function _action_create_post_meta_boxes( $post_type, $post ) {
|
664 |
-
$options = fw()->theme->get_post_options( $post_type );
|
665 |
-
|
666 |
-
if ( empty( $options ) ) {
|
667 |
-
return;
|
668 |
-
}
|
669 |
-
|
670 |
-
$collected = array();
|
671 |
-
|
672 |
-
fw_collect_options( $collected, $options, array(
|
673 |
-
'limit_option_types' => false,
|
674 |
-
'limit_container_types' => false,
|
675 |
-
'limit_level' => 1,
|
676 |
-
'info_wrapper' => true,
|
677 |
-
) );
|
678 |
-
|
679 |
-
if (empty($collected)) {
|
680 |
-
return;
|
681 |
-
}
|
682 |
-
|
683 |
-
$values = fw_get_db_post_option( $post->ID );
|
684 |
-
|
685 |
-
foreach ( $collected as &$option ) {
|
686 |
-
if (
|
687 |
-
$option['group'] === 'container'
|
688 |
-
&&
|
689 |
-
$option['option']['type'] === 'box'
|
690 |
-
) { // this is a box, add it as a metabox
|
691 |
-
$context = isset( $option['option']['context'] )
|
692 |
-
? $option['option']['context']
|
693 |
-
: 'normal';
|
694 |
-
$priority = isset( $option['option']['priority'] )
|
695 |
-
? $option['option']['priority']
|
696 |
-
: 'default';
|
697 |
-
|
698 |
-
add_meta_box(
|
699 |
-
'fw-options-box-' . $option['id'],
|
700 |
-
empty( $option['option']['title'] ) ? ' ' : $option['option']['title'],
|
701 |
-
$this->print_meta_box_content_callback,
|
702 |
-
$post_type,
|
703 |
-
$context,
|
704 |
-
$priority,
|
705 |
-
$this->render_options( $option['option']['options'], $values )
|
706 |
-
);
|
707 |
-
} else { // this is not a box, wrap it in auto-generated box
|
708 |
-
add_meta_box(
|
709 |
-
'fw-options-box:auto-generated:'. time() .':'. fw_unique_increment(),
|
710 |
-
' ',
|
711 |
-
$this->print_meta_box_content_callback,
|
712 |
-
$post_type,
|
713 |
-
'normal',
|
714 |
-
'default',
|
715 |
-
$this->render_options( array($option['id'] => $option['option']), $values )
|
716 |
-
);
|
717 |
-
}
|
718 |
-
}
|
719 |
-
}
|
720 |
-
|
721 |
-
/**
|
722 |
-
* @param object $term
|
723 |
-
*/
|
724 |
-
public function _action_create_taxonomy_options( $term ) {
|
725 |
-
$options = fw()->theme->get_taxonomy_options( $term->taxonomy );
|
726 |
-
|
727 |
-
if ( empty( $options ) ) {
|
728 |
-
return;
|
729 |
-
}
|
730 |
-
|
731 |
-
$collected = array();
|
732 |
-
|
733 |
-
fw_collect_options( $collected, $options, array(
|
734 |
-
'limit_option_types' => false,
|
735 |
-
'limit_container_types' => false,
|
736 |
-
'limit_level' => 1,
|
737 |
-
) );
|
738 |
-
|
739 |
-
if ( empty( $collected ) ) {
|
740 |
-
return;
|
741 |
-
}
|
742 |
-
|
743 |
-
$values = fw_get_db_term_option( $term->term_id, $term->taxonomy );
|
744 |
-
|
745 |
-
// fixes word_press style: .form-field input { width: 95% }
|
746 |
-
echo '<style type="text/css">.fw-option-type-radio input, .fw-option-type-checkbox input { width: auto; }</style>';
|
747 |
-
|
748 |
-
echo $this->render_options( $collected, $values, array(), 'taxonomy' );
|
749 |
-
}
|
750 |
-
|
751 |
-
/**
|
752 |
-
* @param string $taxonomy
|
753 |
-
*/
|
754 |
-
public function _action_create_add_taxonomy_options( $taxonomy ) {
|
755 |
-
$options = fw()->theme->get_taxonomy_options( $taxonomy );
|
756 |
-
if ( empty( $options ) ) {
|
757 |
-
return;
|
758 |
-
}
|
759 |
-
|
760 |
-
$collected = array();
|
761 |
-
|
762 |
-
fw_collect_options( $collected, $options, array(
|
763 |
-
'limit_option_types' => false,
|
764 |
-
'limit_container_types' => false,
|
765 |
-
'limit_level' => 1,
|
766 |
-
) );
|
767 |
-
|
768 |
-
if ( empty( $collected ) ) {
|
769 |
-
return;
|
770 |
-
}
|
771 |
-
|
772 |
-
// fixes word_press style: .form-field input { width: 95% }
|
773 |
-
echo '<style type="text/css">.fw-option-type-radio input, .fw-option-type-checkbox input { width: auto; }</style>';
|
774 |
-
|
775 |
-
echo '<div class="fw-force-xs">';
|
776 |
-
echo $this->render_options( $collected, array(), array(), 'taxonomy' );
|
777 |
-
echo '</div>';
|
778 |
-
|
779 |
-
echo '<script type="text/javascript">'
|
780 |
-
.'jQuery(function($){'
|
781 |
-
.' $("#submit").on("click", function(){'
|
782 |
-
.' $("html, body").animate({ scrollTop: $("#col-left").offset().top });'
|
783 |
-
.' });'
|
784 |
-
.'});'
|
785 |
-
.'</script>';
|
786 |
-
}
|
787 |
-
|
788 |
-
public function _action_init() {
|
789 |
-
$current_edit_taxonomy = $this->get_current_edit_taxonomy();
|
790 |
-
|
791 |
-
if ( $current_edit_taxonomy['taxonomy'] ) {
|
792 |
-
add_action(
|
793 |
-
$current_edit_taxonomy['taxonomy'] . '_edit_form',
|
794 |
-
array( $this, '_action_create_taxonomy_options' )
|
795 |
-
);
|
796 |
-
add_action(
|
797 |
-
$current_edit_taxonomy['taxonomy'] . '_add_form_fields',
|
798 |
-
array( $this, '_action_create_add_taxonomy_options' )
|
799 |
-
);
|
800 |
-
}
|
801 |
-
|
802 |
-
if ( ! empty( $_POST ) ) {
|
803 |
-
// is form submit
|
804 |
-
add_action( 'edited_term', array( $this, '_action_term_edit' ), 10, 3 );
|
805 |
-
|
806 |
-
if ($current_edit_taxonomy['taxonomy']) {
|
807 |
-
add_action(
|
808 |
-
'create_' . $current_edit_taxonomy['taxonomy'],
|
809 |
-
array($this, '_action_save_taxonomy_fields')
|
810 |
-
);
|
811 |
-
}
|
812 |
-
}
|
813 |
-
}
|
814 |
-
|
815 |
-
/**
|
816 |
-
* Experimental custom options save
|
817 |
-
* @param array $options
|
818 |
-
* @param array $values
|
819 |
-
* @return array
|
820 |
-
*/
|
821 |
-
private function process_options_handlers($options, $values)
|
822 |
-
{
|
823 |
-
$handled_values = array();
|
824 |
-
|
825 |
-
foreach (
|
826 |
-
fw_extract_only_options($options)
|
827 |
-
as $option_id => $option
|
828 |
-
) {
|
829 |
-
if (
|
830 |
-
isset($option['option_handler'])
|
831 |
-
&&
|
832 |
-
$option['option_handler'] instanceof FW_Option_Handler
|
833 |
-
) {
|
834 |
-
/*
|
835 |
-
* if the option has a custom option_handler
|
836 |
-
* the saving is delegated to the handler,
|
837 |
-
* so it does not go to the post_meta
|
838 |
-
*/
|
839 |
-
$option['option_handler']->save_option_value($option_id, $option, $values[$option_id]);
|
840 |
-
|
841 |
-
$handled_values[$option_id] = true;
|
842 |
-
}
|
843 |
-
}
|
844 |
-
|
845 |
-
return $handled_values;
|
846 |
-
}
|
847 |
-
|
848 |
-
/**
|
849 |
-
* Save meta from $_POST to fw options (post meta)
|
850 |
-
* @param int $post_id
|
851 |
-
* @param WP_Post $post
|
852 |
-
* @param bool $update
|
853 |
-
*/
|
854 |
-
public function _action_save_post( $post_id, $post, $update ) {
|
855 |
-
if (
|
856 |
-
isset($_POST['post_ID'])
|
857 |
-
&&
|
858 |
-
intval($_POST['post_ID']) === intval($post_id)
|
859 |
-
&&
|
860 |
-
!empty($_POST[ FW_Option_Type::get_default_name_prefix() ]) // this happens on Quick Edit
|
861 |
-
) {
|
862 |
-
/**
|
863 |
-
* This happens on regular post form submit
|
864 |
-
* All data from $_POST belongs this $post
|
865 |
-
* so we save them in its post meta
|
866 |
-
*/
|
867 |
-
|
868 |
-
static $post_options_save_happened = false;
|
869 |
-
if ($post_options_save_happened) {
|
870 |
-
/**
|
871 |
-
* Prevent multiple options save for same post
|
872 |
-
* It can happen from a recursion or wp_update_post() for same post id
|
873 |
-
*/
|
874 |
-
return;
|
875 |
-
} else {
|
876 |
-
$post_options_save_happened = true;
|
877 |
-
}
|
878 |
-
|
879 |
-
$old_values = (array)fw_get_db_post_option($post_id);
|
880 |
-
$current_values = fw_get_options_values_from_input(
|
881 |
-
fw()->theme->get_post_options($post->post_type)
|
882 |
-
);
|
883 |
-
|
884 |
-
fw_set_db_post_option(
|
885 |
-
$post_id,
|
886 |
-
null,
|
887 |
-
array_diff_key( // remove handled values
|
888 |
-
$current_values,
|
889 |
-
$this->process_options_handlers(
|
890 |
-
fw()->theme->get_post_options($post->post_type),
|
891 |
-
$current_values
|
892 |
-
)
|
893 |
-
)
|
894 |
-
);
|
895 |
-
|
896 |
-
/**
|
897 |
-
* @deprecated
|
898 |
-
* Use the 'fw_post_options_update' action
|
899 |
-
*/
|
900 |
-
do_action( 'fw_save_post_options', $post_id, $post, $old_values );
|
901 |
-
} elseif ($original_post_id = wp_is_post_autosave( $post_id )) {
|
902 |
-
do {
|
903 |
-
$parent = get_post($post->post_parent);
|
904 |
-
|
905 |
-
if ( ! $parent instanceof WP_Post ) {
|
906 |
-
break;
|
907 |
-
}
|
908 |
-
|
909 |
-
if (
|
910 |
-
isset($_POST['post_ID'])
|
911 |
-
&&
|
912 |
-
intval($_POST['post_ID']) === intval($parent->ID)
|
913 |
-
) {} else {
|
914 |
-
break;
|
915 |
-
}
|
916 |
-
|
917 |
-
if (empty($_POST[ FW_Option_Type::get_default_name_prefix() ])) {
|
918 |
-
// this happens on Quick Edit
|
919 |
-
break;
|
920 |
-
}
|
921 |
-
|
922 |
-
$current_values = fw_get_options_values_from_input(
|
923 |
-
fw()->theme->get_post_options($parent->post_type)
|
924 |
-
);
|
925 |
-
|
926 |
-
fw_set_db_post_option(
|
927 |
-
$post->ID,
|
928 |
-
null,
|
929 |
-
array_diff_key( // remove handled values
|
930 |
-
$current_values,
|
931 |
-
$this->process_options_handlers(
|
932 |
-
fw()->theme->get_post_options($parent->post_type),
|
933 |
-
$current_values
|
934 |
-
)
|
935 |
-
)
|
936 |
-
);
|
937 |
-
} while(false);
|
938 |
-
} elseif ($original_post_id = wp_is_post_revision( $post_id )) {
|
939 |
-
/**
|
940 |
-
* Do nothing, the
|
941 |
-
* - '_wp_put_post_revision'
|
942 |
-
* - 'wp_restore_post_revision'
|
943 |
-
* actions will handle this
|
944 |
-
*/
|
945 |
-
} else {
|
946 |
-
/**
|
947 |
-
* This happens on:
|
948 |
-
* - post add (auto-draft): do nothing
|
949 |
-
* - revision restore: do nothing, that is handled by the 'wp_restore_post_revision' action
|
950 |
-
*/
|
951 |
-
}
|
952 |
-
}
|
953 |
-
|
954 |
-
/**
|
955 |
-
* @param $post_id
|
956 |
-
* @param $revision_id
|
957 |
-
*/
|
958 |
-
public function _action_restore_post_revision($post_id, $revision_id)
|
959 |
-
{
|
960 |
-
/**
|
961 |
-
* Copy options meta from revision to post
|
962 |
-
*/
|
963 |
-
fw_set_db_post_option(
|
964 |
-
$post_id,
|
965 |
-
null,
|
966 |
-
(array)fw_get_db_post_option($revision_id, null, array())
|
967 |
-
);
|
968 |
-
}
|
969 |
-
|
970 |
-
/**
|
971 |
-
* @param $revision_id
|
972 |
-
*/
|
973 |
-
public function _action__wp_put_post_revision($revision_id)
|
974 |
-
{
|
975 |
-
/**
|
976 |
-
* Copy options meta from post to revision
|
977 |
-
*/
|
978 |
-
fw_set_db_post_option(
|
979 |
-
$revision_id,
|
980 |
-
null,
|
981 |
-
(array)fw_get_db_post_option(
|
982 |
-
wp_is_post_revision($revision_id),
|
983 |
-
null,
|
984 |
-
array()
|
985 |
-
)
|
986 |
-
);
|
987 |
-
}
|
988 |
-
|
989 |
-
/**
|
990 |
-
* Update all post meta `fw_option:<option-id>` with values from post options that has the 'save-in-separate-meta' parameter
|
991 |
-
*
|
992 |
-
* @param int $post_id
|
993 |
-
*
|
994 |
-
* @return bool
|
995 |
-
* @deprecated since 2.5.0
|
996 |
-
*/
|
997 |
-
public function _sync_post_separate_meta( $post_id ) {
|
998 |
-
$post_type = get_post_type( $post_id );
|
999 |
-
|
1000 |
-
if ( ! $post_type ) {
|
1001 |
-
return false;
|
1002 |
-
}
|
1003 |
-
|
1004 |
-
$meta_prefix = 'fw_option:';
|
1005 |
-
|
1006 |
-
/**
|
1007 |
-
* Collect all options that needs to be saved in separate meta
|
1008 |
-
*/
|
1009 |
-
{
|
1010 |
-
$options_values = fw_get_db_post_option( $post_id );
|
1011 |
-
|
1012 |
-
$separate_meta_options = array();
|
1013 |
-
|
1014 |
-
foreach (
|
1015 |
-
fw_extract_only_options( fw()->theme->get_post_options( $post_type ) )
|
1016 |
-
as $option_id => $option
|
1017 |
-
) {
|
1018 |
-
if (
|
1019 |
-
isset( $option['save-in-separate-meta'] )
|
1020 |
-
&&
|
1021 |
-
$option['save-in-separate-meta']
|
1022 |
-
&&
|
1023 |
-
array_key_exists( $option_id, $options_values )
|
1024 |
-
) {
|
1025 |
-
$separate_meta_options[ $meta_prefix . $option_id ] = $options_values[ $option_id ];
|
1026 |
-
}
|
1027 |
-
}
|
1028 |
-
|
1029 |
-
unset( $options_values );
|
1030 |
-
}
|
1031 |
-
|
1032 |
-
/**
|
1033 |
-
* Delete meta that starts with $meta_prefix
|
1034 |
-
*/
|
1035 |
-
{
|
1036 |
-
/** @var wpdb $wpdb */
|
1037 |
-
global $wpdb;
|
1038 |
-
|
1039 |
-
foreach (
|
1040 |
-
$wpdb->get_results(
|
1041 |
-
$wpdb->prepare(
|
1042 |
-
"SELECT meta_key " .
|
1043 |
-
"FROM {$wpdb->postmeta} " .
|
1044 |
-
"WHERE meta_key LIKE %s AND post_id = %d",
|
1045 |
-
$wpdb->esc_like( $meta_prefix ) . '%',
|
1046 |
-
$post_id
|
1047 |
-
)
|
1048 |
-
)
|
1049 |
-
as $row
|
1050 |
-
) {
|
1051 |
-
if ( array_key_exists( $row->meta_key, $separate_meta_options ) ) {
|
1052 |
-
/**
|
1053 |
-
* This meta exists and will be updated below.
|
1054 |
-
* Do not delete for performance reasons, instead of delete->insert will be performed only update
|
1055 |
-
*/
|
1056 |
-
continue;
|
1057 |
-
} else {
|
1058 |
-
// this option does not exist anymore
|
1059 |
-
delete_post_meta( $post_id, $row->meta_key );
|
1060 |
-
}
|
1061 |
-
}
|
1062 |
-
}
|
1063 |
-
|
1064 |
-
foreach ( $separate_meta_options as $meta_key => $option_value ) {
|
1065 |
-
fw_update_post_meta($post_id, $meta_key, $option_value );
|
1066 |
-
}
|
1067 |
-
|
1068 |
-
return true;
|
1069 |
-
}
|
1070 |
-
|
1071 |
-
/**
|
1072 |
-
* @param int $term_id
|
1073 |
-
*/
|
1074 |
-
public function _action_save_taxonomy_fields( $term_id ) {
|
1075 |
-
if (
|
1076 |
-
isset( $_POST['action'] )
|
1077 |
-
&&
|
1078 |
-
'add-tag' === $_POST['action']
|
1079 |
-
&&
|
1080 |
-
isset( $_POST['taxonomy'] )
|
1081 |
-
&&
|
1082 |
-
($taxonomy = get_taxonomy( $_POST['taxonomy'] ))
|
1083 |
-
&&
|
1084 |
-
current_user_can($taxonomy->cap->edit_terms)
|
1085 |
-
) { /* ok */ } else { return; }
|
1086 |
-
|
1087 |
-
$options = fw()->theme->get_taxonomy_options( $taxonomy->name );
|
1088 |
-
if ( empty( $options ) ) {
|
1089 |
-
return;
|
1090 |
-
}
|
1091 |
-
|
1092 |
-
fw_set_db_term_option(
|
1093 |
-
$term_id,
|
1094 |
-
$taxonomy->name,
|
1095 |
-
null,
|
1096 |
-
fw_get_options_values_from_input($options)
|
1097 |
-
);
|
1098 |
-
|
1099 |
-
do_action( 'fw_save_term_options', $term_id, $taxonomy->name, array() );
|
1100 |
-
}
|
1101 |
-
|
1102 |
-
public function _action_term_edit( $term_id, $tt_id, $taxonomy ) {
|
1103 |
-
if (
|
1104 |
-
isset( $_POST['action'] )
|
1105 |
-
&&
|
1106 |
-
'editedtag' === $_POST['action']
|
1107 |
-
&&
|
1108 |
-
isset( $_POST['taxonomy'] )
|
1109 |
-
&&
|
1110 |
-
($taxonomy = get_taxonomy( $_POST['taxonomy'] ))
|
1111 |
-
&&
|
1112 |
-
current_user_can($taxonomy->cap->edit_terms)
|
1113 |
-
) { /* ok */ } else { return; }
|
1114 |
-
|
1115 |
-
if (intval(FW_Request::POST('tag_ID')) != $term_id) {
|
1116 |
-
// the $_POST values belongs to another term, do not save them into this one
|
1117 |
-
return;
|
1118 |
-
}
|
1119 |
-
|
1120 |
-
$options = fw()->theme->get_taxonomy_options( $taxonomy->name );
|
1121 |
-
if ( empty( $options ) ) {
|
1122 |
-
return;
|
1123 |
-
}
|
1124 |
-
|
1125 |
-
$old_values = (array) fw_get_db_term_option( $term_id, $taxonomy->name );
|
1126 |
-
|
1127 |
-
fw_set_db_term_option(
|
1128 |
-
$term_id,
|
1129 |
-
$taxonomy->name,
|
1130 |
-
null,
|
1131 |
-
fw_get_options_values_from_input($options)
|
1132 |
-
);
|
1133 |
-
|
1134 |
-
do_action( 'fw_save_term_options', $term_id, $taxonomy->name, $old_values );
|
1135 |
-
}
|
1136 |
-
|
1137 |
-
public function _action_admin_register_scripts() {
|
1138 |
-
$this->register_static();
|
1139 |
-
}
|
1140 |
-
|
1141 |
-
public function _action_admin_enqueue_scripts() {
|
1142 |
-
global $current_screen, $plugin_page, $post;
|
1143 |
-
|
1144 |
-
/**
|
1145 |
-
* Enqueue settings options static in <head>
|
1146 |
-
*/
|
1147 |
-
{
|
1148 |
-
if ( $this->_get_settings_page_slug() === $plugin_page ) {
|
1149 |
-
fw()->backend->enqueue_options_static(
|
1150 |
-
fw()->theme->get_settings_options()
|
1151 |
-
);
|
1152 |
-
|
1153 |
-
do_action( 'fw_admin_enqueue_scripts:settings' );
|
1154 |
-
}
|
1155 |
-
}
|
1156 |
-
|
1157 |
-
/**
|
1158 |
-
* Enqueue post options static in <head>
|
1159 |
-
*/
|
1160 |
-
{
|
1161 |
-
if ( 'post' === $current_screen->base && $post ) {
|
1162 |
-
fw()->backend->enqueue_options_static(
|
1163 |
-
fw()->theme->get_post_options( $post->post_type )
|
1164 |
-
);
|
1165 |
-
|
1166 |
-
do_action( 'fw_admin_enqueue_scripts:post', $post );
|
1167 |
-
}
|
1168 |
-
}
|
1169 |
-
|
1170 |
-
/**
|
1171 |
-
* Enqueue term options static in <head>
|
1172 |
-
*/
|
1173 |
-
{
|
1174 |
-
if (
|
1175 |
-
'edit-tags' === $current_screen->base
|
1176 |
-
&&
|
1177 |
-
$current_screen->taxonomy
|
1178 |
-
) {
|
1179 |
-
fw()->backend->enqueue_options_static(
|
1180 |
-
fw()->theme->get_taxonomy_options( $current_screen->taxonomy )
|
1181 |
-
);
|
1182 |
-
|
1183 |
-
do_action( 'fw_admin_enqueue_scripts:term', $current_screen->taxonomy );
|
1184 |
-
}
|
1185 |
-
}
|
1186 |
-
}
|
1187 |
-
|
1188 |
-
/**
|
1189 |
-
* Render options html from input json
|
1190 |
-
*
|
1191 |
-
* POST vars:
|
1192 |
-
* - options: '[{option_id: {...}}, {option_id: {...}}, ...]' // Required // String JSON
|
1193 |
-
* - values: {option_id: value, option_id: {...}, ...} // Optional // Object
|
1194 |
-
* - data: {id_prefix: 'fw_options-a-b-', name_prefix: 'fw_options[a][b]'} // Optional // Object
|
1195 |
-
*/
|
1196 |
-
public function _action_ajax_options_render() {
|
1197 |
-
// options
|
1198 |
-
{
|
1199 |
-
if ( ! isset( $_POST['options'] ) ) {
|
1200 |
-
wp_send_json_error( array(
|
1201 |
-
'message' => 'No options'
|
1202 |
-
) );
|
1203 |
-
}
|
1204 |
-
|
1205 |
-
$options = json_decode( FW_Request::POST( 'options' ), true );
|
1206 |
-
|
1207 |
-
if ( ! $options ) {
|
1208 |
-
wp_send_json_error( array(
|
1209 |
-
'message' => 'Wrong options'
|
1210 |
-
) );
|
1211 |
-
}
|
1212 |
-
}
|
1213 |
-
|
1214 |
-
// values
|
1215 |
-
{
|
1216 |
-
if ( isset( $_POST['values'] ) ) {
|
1217 |
-
$values = FW_Request::POST( 'values' );
|
1218 |
-
|
1219 |
-
if (is_string($values)) {
|
1220 |
-
$values = json_decode($values, true);
|
1221 |
-
}
|
1222 |
-
} else {
|
1223 |
-
$values = array();
|
1224 |
-
}
|
1225 |
-
|
1226 |
-
$values = array_intersect_key($values, fw_extract_only_options($options));
|
1227 |
-
}
|
1228 |
-
|
1229 |
-
// data
|
1230 |
-
{
|
1231 |
-
if ( isset( $_POST['data'] ) ) {
|
1232 |
-
$data = FW_Request::POST( 'data' );
|
1233 |
-
} else {
|
1234 |
-
$data = array();
|
1235 |
-
}
|
1236 |
-
}
|
1237 |
-
|
1238 |
-
wp_send_json_success( array(
|
1239 |
-
'html' => fw()->backend->render_options( $options, $values, $data )
|
1240 |
-
) );
|
1241 |
-
}
|
1242 |
-
|
1243 |
-
/**
|
1244 |
-
* Get options values from html generated with 'fw_backend_options_render' ajax action
|
1245 |
-
*
|
1246 |
-
* POST vars:
|
1247 |
-
* - options: '[{option_id: {...}}, {option_id: {...}}, ...]' // Required // String JSON
|
1248 |
-
* - fw_options... // Use a jQuery "ajax form submit" to emulate real form submit
|
1249 |
-
*
|
1250 |
-
* Tip: Inside form html, add: <input type="hidden" name="options" value="[...json...]">
|
1251 |
-
*/
|
1252 |
-
public function _action_ajax_options_get_values() {
|
1253 |
-
// options
|
1254 |
-
{
|
1255 |
-
if ( ! isset( $_POST['options'] ) ) {
|
1256 |
-
wp_send_json_error( array(
|
1257 |
-
'message' => 'No options'
|
1258 |
-
) );
|
1259 |
-
}
|
1260 |
-
|
1261 |
-
$options = json_decode( FW_Request::POST( 'options' ), true );
|
1262 |
-
|
1263 |
-
if ( ! $options ) {
|
1264 |
-
wp_send_json_error( array(
|
1265 |
-
'message' => 'Wrong options'
|
1266 |
-
) );
|
1267 |
-
}
|
1268 |
-
}
|
1269 |
-
|
1270 |
-
// name_prefix
|
1271 |
-
{
|
1272 |
-
if ( isset( $_POST['name_prefix'] ) ) {
|
1273 |
-
$name_prefix = FW_Request::POST( 'name_prefix' );
|
1274 |
-
} else {
|
1275 |
-
$name_prefix = FW_Option_Type::get_default_name_prefix();
|
1276 |
-
}
|
1277 |
-
}
|
1278 |
-
|
1279 |
-
wp_send_json_success( array(
|
1280 |
-
'values' => fw_get_options_values_from_input(
|
1281 |
-
$options,
|
1282 |
-
FW_Request::POST( fw_html_attr_name_to_array_multi_key( $name_prefix ), array() )
|
1283 |
-
)
|
1284 |
-
) );
|
1285 |
-
}
|
1286 |
-
|
1287 |
-
public function _settings_form_render( $data ) {
|
1288 |
-
{
|
1289 |
-
$this->enqueue_options_static( array() );
|
1290 |
-
|
1291 |
-
wp_enqueue_script( 'fw-form-helpers' );
|
1292 |
-
}
|
1293 |
-
|
1294 |
-
$options = fw()->theme->get_settings_options();
|
1295 |
-
|
1296 |
-
if ( empty( $options ) ) {
|
1297 |
-
return $data;
|
1298 |
-
}
|
1299 |
-
|
1300 |
-
if ( $values = FW_Request::POST( FW_Option_Type::get_default_name_prefix() ) ) {
|
1301 |
-
// This is form submit, extract correct values from $_POST values
|
1302 |
-
$values = fw_get_options_values_from_input( $options, $values );
|
1303 |
-
} else {
|
1304 |
-
// Extract previously saved correct values
|
1305 |
-
$values = fw_get_db_settings_option();
|
1306 |
-
}
|
1307 |
-
|
1308 |
-
$ajax_submit = fw()->theme->get_config( 'settings_form_ajax_submit' );
|
1309 |
-
$side_tabs = fw()->theme->get_config( 'settings_form_side_tabs' );
|
1310 |
-
|
1311 |
-
$data['attr']['class'] = 'fw-settings-form';
|
1312 |
-
|
1313 |
-
if ( $side_tabs ) {
|
1314 |
-
$data['attr']['class'] .= ' fw-backend-side-tabs';
|
1315 |
-
}
|
1316 |
-
|
1317 |
-
$data['submit']['html'] = '<!-- -->'; // is generated in view
|
1318 |
-
|
1319 |
-
do_action( 'fw_settings_form_render', array(
|
1320 |
-
'ajax_submit' => $ajax_submit,
|
1321 |
-
'side_tabs' => $side_tabs,
|
1322 |
-
) );
|
1323 |
-
|
1324 |
-
fw_render_view( fw_get_framework_directory( '/views/backend-settings-form.php' ), array(
|
1325 |
-
'options' => $options,
|
1326 |
-
'values' => $values,
|
1327 |
-
'reset_input_name' => '_fw_reset_options',
|
1328 |
-
'ajax_submit' => $ajax_submit,
|
1329 |
-
'side_tabs' => $side_tabs,
|
1330 |
-
), false );
|
1331 |
-
|
1332 |
-
return $data;
|
1333 |
-
}
|
1334 |
-
|
1335 |
-
public function _settings_form_validate( $errors ) {
|
1336 |
-
if ( ! current_user_can( 'manage_options' ) ) {
|
1337 |
-
$errors['_no_permission'] = __( 'You have no permissions to change settings options', 'fw' );
|
1338 |
-
}
|
1339 |
-
|
1340 |
-
return $errors;
|
1341 |
-
}
|
1342 |
-
|
1343 |
-
public function _settings_form_save( $data ) {
|
1344 |
-
$flash_id = 'fw_settings_form_save';
|
1345 |
-
$old_values = (array) fw_get_db_settings_option();
|
1346 |
-
|
1347 |
-
if ( ! empty( $_POST['_fw_reset_options'] ) ) { // The "Reset" button was pressed
|
1348 |
-
fw_set_db_settings_option(
|
1349 |
-
null,
|
1350 |
-
/**
|
1351 |
-
* Some values that don't relate to design, like API credentials, are useful to not be wiped out.
|
1352 |
-
*
|
1353 |
-
* Usage:
|
1354 |
-
*
|
1355 |
-
* add_filter('fw_settings_form_reset:values', '_filter_add_persisted_option', 10, 2);
|
1356 |
-
* function _filter_add_persisted_option ($current_persisted, $old_values) {
|
1357 |
-
* $value_to_persist = fw_akg('my/multi/key', $old_values);
|
1358 |
-
* fw_aks('my/multi/key', $value_to_persist, $current_persisted);
|
1359 |
-
*
|
1360 |
-
* return $current_persisted;
|
1361 |
-
* }
|
1362 |
-
*/
|
1363 |
-
apply_filters('fw_settings_form_reset:values', array(), $old_values)
|
1364 |
-
);
|
1365 |
-
|
1366 |
-
FW_Flash_Messages::add( $flash_id, __( 'The options were successfully reset', 'fw' ), 'success' );
|
1367 |
-
|
1368 |
-
do_action( 'fw_settings_form_reset', $old_values );
|
1369 |
-
} else { // The "Save" button was pressed
|
1370 |
-
fw_set_db_settings_option(
|
1371 |
-
null,
|
1372 |
-
fw_get_options_values_from_input(
|
1373 |
-
fw()->theme->get_settings_options()
|
1374 |
-
)
|
1375 |
-
);
|
1376 |
-
|
1377 |
-
FW_Flash_Messages::add( $flash_id, __( 'The options were successfully saved', 'fw' ), 'success' );
|
1378 |
-
|
1379 |
-
do_action( 'fw_settings_form_saved', $old_values );
|
1380 |
-
}
|
1381 |
-
|
1382 |
-
$redirect_url = fw_current_url();
|
1383 |
-
|
1384 |
-
$data['redirect'] = $redirect_url;
|
1385 |
-
|
1386 |
-
return $data;
|
1387 |
-
}
|
1388 |
-
|
1389 |
-
/**
|
1390 |
-
* Render options array and return the generated HTML
|
1391 |
-
*
|
1392 |
-
* @param array $options
|
1393 |
-
* @param array $values Correct values returned by fw_get_options_values_from_input()
|
1394 |
-
* @param array $options_data {id_prefix => ..., name_prefix => ...}
|
1395 |
-
* @param string $design
|
1396 |
-
*
|
1397 |
-
* @return string HTML
|
1398 |
-
*/
|
1399 |
-
public function render_options( $options, $values = array(), $options_data = array(), $design = null ) {
|
1400 |
-
if (empty($design)) {
|
1401 |
-
$design = $this->default_render_design;
|
1402 |
-
}
|
1403 |
-
|
1404 |
-
if (
|
1405 |
-
!doing_action('admin_enqueue_scripts')
|
1406 |
-
&&
|
1407 |
-
!did_action('admin_enqueue_scripts')
|
1408 |
-
) {
|
1409 |
-
/**
|
1410 |
-
* Do not wp_enqueue/register_...() because at this point not all handles has been registered
|
1411 |
-
* and maybe they are used in dependencies in handles that are going to be enqueued.
|
1412 |
-
* So as a result some handles will not be equeued because of not registered dependecies.
|
1413 |
-
*/
|
1414 |
-
} else {
|
1415 |
-
/**
|
1416 |
-
* register scripts and styles
|
1417 |
-
* in case if this method is called before enqueue_scripts action
|
1418 |
-
* and option types has some of these in their dependencies
|
1419 |
-
*/
|
1420 |
-
$this->register_static();
|
1421 |
-
|
1422 |
-
wp_enqueue_media();
|
1423 |
-
wp_enqueue_style( 'fw-backend-options' );
|
1424 |
-
wp_enqueue_script( 'fw-backend-options' );
|
1425 |
-
}
|
1426 |
-
|
1427 |
-
$collected = array();
|
1428 |
-
|
1429 |
-
fw_collect_options( $collected, $options, array(
|
1430 |
-
'limit_option_types' => false,
|
1431 |
-
'limit_container_types' => false,
|
1432 |
-
'limit_level' => 1,
|
1433 |
-
'info_wrapper' => true,
|
1434 |
-
) );
|
1435 |
-
|
1436 |
-
if ( empty( $collected ) ) {
|
1437 |
-
return false;
|
1438 |
-
}
|
1439 |
-
|
1440 |
-
$html = '';
|
1441 |
-
|
1442 |
-
$option = reset( $collected );
|
1443 |
-
|
1444 |
-
$collected_type = array(
|
1445 |
-
'group' => $option['group'],
|
1446 |
-
'type' => $option['option']['type'],
|
1447 |
-
);
|
1448 |
-
$collected_type_options = array(
|
1449 |
-
$option['id'] => &$option['option']
|
1450 |
-
);
|
1451 |
-
|
1452 |
-
while ( $collected_type_options ) {
|
1453 |
-
$option = next( $collected );
|
1454 |
-
|
1455 |
-
if ( $option ) {
|
1456 |
-
if (
|
1457 |
-
$option['group'] === $collected_type['group']
|
1458 |
-
&&
|
1459 |
-
$option['option']['type'] === $collected_type['type']
|
1460 |
-
) {
|
1461 |
-
$collected_type_options[ $option['id'] ] = &$option['option'];
|
1462 |
-
continue;
|
1463 |
-
}
|
1464 |
-
}
|
1465 |
-
|
1466 |
-
switch ( $collected_type['group'] ) {
|
1467 |
-
case 'container':
|
1468 |
-
if ($design === 'taxonomy') {
|
1469 |
-
$html .= fw_render_view(
|
1470 |
-
fw_get_framework_directory('/views/backend-container-design-'. $design .'.php'),
|
1471 |
-
array(
|
1472 |
-
'type' => $collected_type['type'],
|
1473 |
-
'html' => $this->container_type($collected_type['type'])->render(
|
1474 |
-
$collected_type_options, $values, $options_data
|
1475 |
-
),
|
1476 |
-
)
|
1477 |
-
);
|
1478 |
-
} else {
|
1479 |
-
$html .= $this->container_type($collected_type['type'])->render(
|
1480 |
-
$collected_type_options, $values, $options_data
|
1481 |
-
);
|
1482 |
-
}
|
1483 |
-
break;
|
1484 |
-
case 'option':
|
1485 |
-
foreach ( $collected_type_options as $id => &$_option ) {
|
1486 |
-
$data = $options_data; // do not change directly to not affect next loops
|
1487 |
-
|
1488 |
-
$data['value'] = isset( $values[ $id ] ) ? $values[ $id ] : null;
|
1489 |
-
|
1490 |
-
$html .= $this->render_option(
|
1491 |
-
$id,
|
1492 |
-
$_option,
|
1493 |
-
$data,
|
1494 |
-
$design
|
1495 |
-
);
|
1496 |
-
}
|
1497 |
-
unset($_option);
|
1498 |
-
break;
|
1499 |
-
default:
|
1500 |
-
$html .= '<p><em>' . __( 'Unknown collected group', 'fw' ) . ': ' . $collected_type['group'] . '</em></p>';
|
1501 |
-
}
|
1502 |
-
|
1503 |
-
unset( $collected_type, $collected_type_options );
|
1504 |
-
|
1505 |
-
if ( $option ) {
|
1506 |
-
$collected_type = array(
|
1507 |
-
'group' => $option['group'],
|
1508 |
-
'type' => $option['option']['type'],
|
1509 |
-
);
|
1510 |
-
$collected_type_options = array(
|
1511 |
-
$option['id'] => &$option['option']
|
1512 |
-
);
|
1513 |
-
} else {
|
1514 |
-
$collected_type_options = array();
|
1515 |
-
}
|
1516 |
-
}
|
1517 |
-
|
1518 |
-
return $html;
|
1519 |
-
}
|
1520 |
-
|
1521 |
-
/**
|
1522 |
-
* Enqueue options static
|
1523 |
-
*
|
1524 |
-
* Useful when you have dynamic options html on the page (for e.g. options modal)
|
1525 |
-
* and in order to initialize that html properly, the option types scripts styles must be enqueued on the page
|
1526 |
-
*
|
1527 |
-
* @param array $options
|
1528 |
-
*/
|
1529 |
-
public function enqueue_options_static( $options ) {
|
1530 |
-
if (
|
1531 |
-
!doing_action('admin_enqueue_scripts')
|
1532 |
-
&&
|
1533 |
-
!did_action('admin_enqueue_scripts')
|
1534 |
-
) {
|
1535 |
-
/**
|
1536 |
-
* Do not wp_enqueue/register_...() because at this point not all handles has been registered
|
1537 |
-
* and maybe they are used in dependencies in handles that are going to be enqueued.
|
1538 |
-
* So as a result some handles will not be equeued because of not registered dependecies.
|
1539 |
-
*/
|
1540 |
-
return;
|
1541 |
-
} else {
|
1542 |
-
/**
|
1543 |
-
* register scripts and styles
|
1544 |
-
* in case if this method is called before enqueue_scripts action
|
1545 |
-
* and option types has some of these in their dependencies
|
1546 |
-
*/
|
1547 |
-
$this->register_static();
|
1548 |
-
|
1549 |
-
wp_enqueue_media();
|
1550 |
-
wp_enqueue_style( 'fw-backend-options' );
|
1551 |
-
wp_enqueue_script( 'fw-backend-options' );
|
1552 |
-
}
|
1553 |
-
|
1554 |
-
$collected = array();
|
1555 |
-
|
1556 |
-
fw_collect_options( $collected, $options, array(
|
1557 |
-
'limit_option_types' => false,
|
1558 |
-
'limit_container_types' => false,
|
1559 |
-
'limit_level' => 0,
|
1560 |
-
'info_wrapper' => true,
|
1561 |
-
) );
|
1562 |
-
|
1563 |
-
foreach ( $collected as &$option ) {
|
1564 |
-
if ($option['group'] === 'option') {
|
1565 |
-
fw()->backend->option_type($option['option']['type'])->enqueue_static($option['id'], $option['option']);
|
1566 |
-
} elseif ($option['group'] === 'container') {
|
1567 |
-
fw()->backend->container_type($option['option']['type'])->enqueue_static($option['id'], $option['option']);
|
1568 |
-
}
|
1569 |
-
}
|
1570 |
-
}
|
1571 |
-
|
1572 |
-
/**
|
1573 |
-
* Render option enclosed in backend design
|
1574 |
-
*
|
1575 |
-
* @param string $id
|
1576 |
-
* @param array $option
|
1577 |
-
* @param array $data
|
1578 |
-
* @param string $design default or taxonomy
|
1579 |
-
*
|
1580 |
-
* @return string
|
1581 |
-
*/
|
1582 |
-
public function render_option( $id, $option, $data = array(), $design = null ) {
|
1583 |
-
if (empty($design)) {
|
1584 |
-
$design = $this->default_render_design;
|
1585 |
-
}
|
1586 |
-
|
1587 |
-
if (
|
1588 |
-
!doing_action('admin_enqueue_scripts')
|
1589 |
-
&&
|
1590 |
-
!did_action('admin_enqueue_scripts')
|
1591 |
-
) {
|
1592 |
-
/**
|
1593 |
-
* Do not wp_enqueue/register_...() because at this point not all handles has been registered
|
1594 |
-
* and maybe they are used in dependencies in handles that are going to be enqueued.
|
1595 |
-
* So as a result some handles will not be equeued because of not registered dependecies.
|
1596 |
-
*/
|
1597 |
-
} else {
|
1598 |
-
$this->register_static();
|
1599 |
-
}
|
1600 |
-
|
1601 |
-
|
1602 |
-
if ( ! in_array( $design, $this->available_render_designs ) ) {
|
1603 |
-
trigger_error( 'Invalid render design specified: ' . $design, E_USER_WARNING );
|
1604 |
-
$design = 'post';
|
1605 |
-
}
|
1606 |
-
|
1607 |
-
if ( ! isset( $data['id_prefix'] ) ) {
|
1608 |
-
$data['id_prefix'] = FW_Option_Type::get_default_id_prefix();
|
1609 |
-
}
|
1610 |
-
|
1611 |
-
if (
|
1612 |
-
isset($option['option_handler']) &&
|
1613 |
-
$option['option_handler'] instanceof FW_Option_Handler
|
1614 |
-
) {
|
1615 |
-
|
1616 |
-
/*
|
1617 |
-
* if the option has a custom option_handler
|
1618 |
-
* then the handler provides the option's value
|
1619 |
-
*/
|
1620 |
-
$data['value'] = $option['option_handler']->get_option_value($id, $option, $data);
|
1621 |
-
}
|
1622 |
-
|
1623 |
-
$data = apply_filters(
|
1624 |
-
'fw:backend:option-render:data',
|
1625 |
-
$data
|
1626 |
-
);
|
1627 |
-
|
1628 |
-
return fw_render_view(fw_get_framework_directory('/views/backend-option-design-'. $design .'.php'), array(
|
1629 |
-
'id' => $id,
|
1630 |
-
'option' => $option,
|
1631 |
-
'data' => $data,
|
1632 |
-
) );
|
1633 |
-
}
|
1634 |
-
|
1635 |
-
/**
|
1636 |
-
* Render a meta box
|
1637 |
-
*
|
1638 |
-
* @param string $id
|
1639 |
-
* @param string $title
|
1640 |
-
* @param string $content HTML
|
1641 |
-
* @param array $other Optional elements
|
1642 |
-
*
|
1643 |
-
* @return string Generated meta box html
|
1644 |
-
*/
|
1645 |
-
public function render_box( $id, $title, $content, $other = array() ) {
|
1646 |
-
if ( ! function_exists( 'add_meta_box' ) ) {
|
1647 |
-
trigger_error( 'Try call this method later (\'admin_init\' action), add_meta_box() function does not exists yet.',
|
1648 |
-
E_USER_WARNING );
|
1649 |
-
|
1650 |
-
return '';
|
1651 |
-
}
|
1652 |
-
|
1653 |
-
$other = array_merge( array(
|
1654 |
-
'html_before_title' => false,
|
1655 |
-
'html_after_title' => false,
|
1656 |
-
'attr' => array(),
|
1657 |
-
), $other );
|
1658 |
-
|
1659 |
-
{
|
1660 |
-
$placeholders = array(
|
1661 |
-
'id' => '{{meta_box_id}}',
|
1662 |
-
'title' => '{{meta_box_title}}',
|
1663 |
-
'content' => '{{meta_box_content}}',
|
1664 |
-
);
|
1665 |
-
|
1666 |
-
// other placeholders
|
1667 |
-
{
|
1668 |
-
$placeholders['html_before_title'] = '{{meta_box_html_before_title}}';
|
1669 |
-
$placeholders['html_after_title'] = '{{meta_box_html_after_title}}';
|
1670 |
-
$placeholders['attr'] = '{{meta_box_attr}}';
|
1671 |
-
$placeholders['attr_class'] = '{{meta_box_attr_class}}';
|
1672 |
-
}
|
1673 |
-
}
|
1674 |
-
|
1675 |
-
$cache_key = 'fw_meta_box_template';
|
1676 |
-
|
1677 |
-
try {
|
1678 |
-
$meta_box_template = FW_Cache::get( $cache_key );
|
1679 |
-
} catch ( FW_Cache_Not_Found_Exception $e ) {
|
1680 |
-
$temp_screen_id = 'fw-temp-meta-box-screen-id-' . fw_unique_increment();
|
1681 |
-
$context = 'normal';
|
1682 |
-
|
1683 |
-
add_meta_box(
|
1684 |
-
$placeholders['id'],
|
1685 |
-
$placeholders['title'],
|
1686 |
-
$this->print_meta_box_content_callback,
|
1687 |
-
$temp_screen_id,
|
1688 |
-
$context,
|
1689 |
-
'default',
|
1690 |
-
$placeholders['content']
|
1691 |
-
);
|
1692 |
-
|
1693 |
-
ob_start();
|
1694 |
-
|
1695 |
-
do_meta_boxes( $temp_screen_id, $context, null );
|
1696 |
-
|
1697 |
-
$meta_box_template = ob_get_clean();
|
1698 |
-
|
1699 |
-
remove_meta_box( $id, $temp_screen_id, $context );
|
1700 |
-
|
1701 |
-
// remove wrapper div, leave only meta box div
|
1702 |
-
{
|
1703 |
-
// <div ...>
|
1704 |
-
{
|
1705 |
-
$meta_box_template = str_replace(
|
1706 |
-
'<div id="' . $context . '-sortables" class="meta-box-sortables">',
|
1707 |
-
'',
|
1708 |
-
$meta_box_template
|
1709 |
-
);
|
1710 |
-
}
|
1711 |
-
|
1712 |
-
// </div>
|
1713 |
-
{
|
1714 |
-
$meta_box_template = explode( '</div>', $meta_box_template );
|
1715 |
-
array_pop( $meta_box_template );
|
1716 |
-
$meta_box_template = implode( '</div>', $meta_box_template );
|
1717 |
-
}
|
1718 |
-
}
|
1719 |
-
|
1720 |
-
// add 'fw-postbox' class and some attr related placeholders
|
1721 |
-
$meta_box_template = str_replace(
|
1722 |
-
'class="postbox',
|
1723 |
-
$placeholders['attr'] . ' class="postbox fw-postbox' . $placeholders['attr_class'],
|
1724 |
-
$meta_box_template
|
1725 |
-
);
|
1726 |
-
|
1727 |
-
// add html_before|after_title placeholders
|
1728 |
-
{
|
1729 |
-
$meta_box_template = str_replace(
|
1730 |
-
'<span>' . $placeholders['title'] . '</span>',
|
1731 |
-
|
1732 |
-
/**
|
1733 |
-
* used <small> not <span> because there is a lot of css and js
|
1734 |
-
* that thinks inside <h2 class="hndle"> there is only one <span>
|
1735 |
-
* so do not brake their logic
|
1736 |
-
*/
|
1737 |
-
'<small class="fw-html-before-title">' . $placeholders['html_before_title'] . '</small>' .
|
1738 |
-
'<span>' . $placeholders['title'] . '</span>' .
|
1739 |
-
'<small class="fw-html-after-title">' . $placeholders['html_after_title'] . '</small>',
|
1740 |
-
|
1741 |
-
$meta_box_template
|
1742 |
-
);
|
1743 |
-
}
|
1744 |
-
|
1745 |
-
FW_Cache::set( $cache_key, $meta_box_template );
|
1746 |
-
}
|
1747 |
-
|
1748 |
-
// prepare attributes
|
1749 |
-
{
|
1750 |
-
$attr_class = '';
|
1751 |
-
if ( isset( $other['attr']['class'] ) ) {
|
1752 |
-
$attr_class = ' ' . $other['attr']['class'];
|
1753 |
-
|
1754 |
-
unset( $other['attr']['class'] );
|
1755 |
-
}
|
1756 |
-
|
1757 |
-
unset( $other['attr']['id'] );
|
1758 |
-
}
|
1759 |
-
|
1760 |
-
// replace placeholders with data/content
|
1761 |
-
return str_replace(
|
1762 |
-
array(
|
1763 |
-
$placeholders['id'],
|
1764 |
-
$placeholders['title'],
|
1765 |
-
$placeholders['content'],
|
1766 |
-
$placeholders['html_before_title'],
|
1767 |
-
$placeholders['html_after_title'],
|
1768 |
-
$placeholders['attr'],
|
1769 |
-
$placeholders['attr_class'],
|
1770 |
-
),
|
1771 |
-
array(
|
1772 |
-
esc_attr( $id ),
|
1773 |
-
$title,
|
1774 |
-
$content,
|
1775 |
-
$other['html_before_title'],
|
1776 |
-
$other['html_after_title'],
|
1777 |
-
fw_attr_to_html( $other['attr'] ),
|
1778 |
-
esc_attr( $attr_class )
|
1779 |
-
),
|
1780 |
-
$meta_box_template
|
1781 |
-
);
|
1782 |
-
}
|
1783 |
-
|
1784 |
-
/**
|
1785 |
-
* @param FW_Access_Key $access_key
|
1786 |
-
* @param string|FW_Option_Type $option_type_class
|
1787 |
-
*
|
1788 |
-
* @internal
|
1789 |
-
*/
|
1790 |
-
public function _register_option_type( FW_Access_Key $access_key, $option_type_class ) {
|
1791 |
-
if ( $access_key->get_key() !== 'fw_option_type' ) {
|
1792 |
-
trigger_error( 'Call denied', E_USER_ERROR );
|
1793 |
-
}
|
1794 |
-
|
1795 |
-
$this->register_option_type( $option_type_class );
|
1796 |
-
}
|
1797 |
-
|
1798 |
-
/**
|
1799 |
-
* @param FW_Access_Key $access_key
|
1800 |
-
* @param string|FW_Container_Type $container_type_class
|
1801 |
-
*
|
1802 |
-
* @internal
|
1803 |
-
*/
|
1804 |
-
public function _register_container_type( FW_Access_Key $access_key, $container_type_class ) {
|
1805 |
-
if ( $access_key->get_key() !== 'fw_container_type' ) {
|
1806 |
-
trigger_error( 'Call denied', E_USER_ERROR );
|
1807 |
-
}
|
1808 |
-
|
1809 |
-
$this->register_container_type( $container_type_class );
|
1810 |
-
}
|
1811 |
-
|
1812 |
-
/**
|
1813 |
-
* @param string $option_type
|
1814 |
-
*
|
1815 |
-
* @return FW_Option_Type|FW_Option_Type_Undefined
|
1816 |
-
*/
|
1817 |
-
public function option_type( $option_type ) {
|
1818 |
-
if ( is_array( $this->option_types_pending_registration ) ) {
|
1819 |
-
// This method is called first time
|
1820 |
-
|
1821 |
-
do_action('fw_option_types_init');
|
1822 |
-
|
1823 |
-
// Register pending option types
|
1824 |
-
{
|
1825 |
-
$pending_option_types = $this->option_types_pending_registration;
|
1826 |
-
|
1827 |
-
// clear this property, so register_option_type() will not add option types to pending anymore
|
1828 |
-
$this->option_types_pending_registration = false;
|
1829 |
-
|
1830 |
-
foreach ( $pending_option_types as $option_type_class ) {
|
1831 |
-
$this->register_option_type( $option_type_class );
|
1832 |
-
}
|
1833 |
-
|
1834 |
-
unset( $pending_option_types );
|
1835 |
-
}
|
1836 |
-
}
|
1837 |
-
|
1838 |
-
if ( isset( $this->option_types[ $option_type ] ) ) {
|
1839 |
-
return $this->option_types[ $option_type ];
|
1840 |
-
} else {
|
1841 |
-
if ( is_admin() ) {
|
1842 |
-
FW_Flash_Messages::add(
|
1843 |
-
'fw-get-option-type-undefined-' . $option_type,
|
1844 |
-
sprintf( __( 'Undefined option type: %s', 'fw' ), $option_type ),
|
1845 |
-
'warning'
|
1846 |
-
);
|
1847 |
-
}
|
1848 |
-
|
1849 |
-
if (!$this->undefined_option_type) {
|
1850 |
-
require_once fw_get_framework_directory('/includes/option-types/class-fw-option-type-undefined.php');
|
1851 |
-
|
1852 |
-
$this->undefined_option_type = new FW_Option_Type_Undefined();
|
1853 |
-
}
|
1854 |
-
|
1855 |
-
return $this->undefined_option_type;
|
1856 |
-
}
|
1857 |
-
}
|
1858 |
-
|
1859 |
-
/**
|
1860 |
-
* @param string $container_type
|
1861 |
-
*
|
1862 |
-
* @return FW_Container_Type|FW_Container_Type_Undefined
|
1863 |
-
*/
|
1864 |
-
public function container_type( $container_type ) {
|
1865 |
-
if ( is_array( $this->container_types_pending_registration ) ) {
|
1866 |
-
// This method is called first time
|
1867 |
-
|
1868 |
-
do_action('fw_container_types_init');
|
1869 |
-
|
1870 |
-
// Register pending container types
|
1871 |
-
{
|
1872 |
-
$pending_container_types = $this->container_types_pending_registration;
|
1873 |
-
|
1874 |
-
// clear this property, so register_container_type() will not add container types to pending anymore
|
1875 |
-
$this->container_types_pending_registration = false;
|
1876 |
-
|
1877 |
-
foreach ( $pending_container_types as $container_type_class ) {
|
1878 |
-
$this->register_container_type( $container_type_class );
|
1879 |
-
}
|
1880 |
-
|
1881 |
-
unset( $pending_container_types );
|
1882 |
-
}
|
1883 |
-
}
|
1884 |
-
|
1885 |
-
if ( isset( $this->container_types[ $container_type ] ) ) {
|
1886 |
-
return $this->container_types[ $container_type ];
|
1887 |
-
} else {
|
1888 |
-
if ( is_admin() ) {
|
1889 |
-
FW_Flash_Messages::add(
|
1890 |
-
'fw-get-container-type-undefined-' . $container_type,
|
1891 |
-
sprintf( __( 'Undefined container type: %s', 'fw' ), $container_type ),
|
1892 |
-
'warning'
|
1893 |
-
);
|
1894 |
-
}
|
1895 |
-
|
1896 |
-
if (!$this->undefined_container_type) {
|
1897 |
-
require_once fw_get_framework_directory('/includes/container-types/class-fw-container-type-undefined.php');
|
1898 |
-
|
1899 |
-
$this->undefined_container_type = new FW_Container_Type_Undefined();
|
1900 |
-
}
|
1901 |
-
|
1902 |
-
return $this->undefined_container_type;
|
1903 |
-
}
|
1904 |
-
}
|
1905 |
-
|
1906 |
-
/**
|
1907 |
-
* @param WP_Customize_Manager $wp_customize
|
1908 |
-
* @internal
|
1909 |
-
*/
|
1910 |
-
public function _action_customize_register($wp_customize) {
|
1911 |
-
if (is_admin()) {
|
1912 |
-
add_action('admin_enqueue_scripts', array($this, '_action_enqueue_customizer_static'));
|
1913 |
-
}
|
1914 |
-
|
1915 |
-
$this->customizer_register_options(
|
1916 |
-
$wp_customize,
|
1917 |
-
fw()->theme->get_customizer_options()
|
1918 |
-
);
|
1919 |
-
}
|
1920 |
-
|
1921 |
-
/**
|
1922 |
-
* @internal
|
1923 |
-
*/
|
1924 |
-
public function _action_enqueue_customizer_static()
|
1925 |
-
{
|
1926 |
-
{
|
1927 |
-
$options_for_enqueue = array();
|
1928 |
-
$customizer_options = fw()->theme->get_customizer_options();
|
1929 |
-
|
1930 |
-
/**
|
1931 |
-
* In customizer options is allowed to have container with unspecified (or not existing) 'type'
|
1932 |
-
* fw()->backend->enqueue_options_static() tries to enqueue both options and container static
|
1933 |
-
* not existing container types will throw notices.
|
1934 |
-
* To prevent that, extract and send it only options (without containers)
|
1935 |
-
*/
|
1936 |
-
fw_collect_options($options_for_enqueue, $customizer_options);
|
1937 |
-
|
1938 |
-
fw()->backend->enqueue_options_static($options_for_enqueue);
|
1939 |
-
|
1940 |
-
unset($options_for_enqueue, $customizer_options);
|
1941 |
-
}
|
1942 |
-
|
1943 |
-
wp_enqueue_script(
|
1944 |
-
'fw-backend-customizer',
|
1945 |
-
fw_get_framework_directory_uri( '/static/js/backend-customizer.js' ),
|
1946 |
-
array( 'jquery', 'fw-events', 'backbone' ),
|
1947 |
-
fw()->manifest->get_version(),
|
1948 |
-
true
|
1949 |
-
);
|
1950 |
-
wp_localize_script(
|
1951 |
-
'fw-backend-customizer',
|
1952 |
-
'_fw_backend_customizer_localized',
|
1953 |
-
array(
|
1954 |
-
'change_timeout' => apply_filters('fw_customizer_option_change_timeout', 333),
|
1955 |
-
)
|
1956 |
-
);
|
1957 |
-
|
1958 |
-
do_action('fw_admin_enqueue_scripts:customizer');
|
1959 |
-
}
|
1960 |
-
|
1961 |
-
/**
|
1962 |
-
* @param WP_Customize_Manager $wp_customize
|
1963 |
-
* @param array $options
|
1964 |
-
* @param array $parent_data {'type':'...','id':'...'}
|
1965 |
-
*/
|
1966 |
-
private function customizer_register_options($wp_customize, $options, $parent_data = array()) {
|
1967 |
-
$collected = array();
|
1968 |
-
|
1969 |
-
fw_collect_options( $collected, $options, array(
|
1970 |
-
'limit_option_types' => false,
|
1971 |
-
'limit_container_types' => false,
|
1972 |
-
'limit_level' => 1,
|
1973 |
-
'info_wrapper' => true,
|
1974 |
-
) );
|
1975 |
-
|
1976 |
-
if ( empty( $collected ) ) {
|
1977 |
-
return;
|
1978 |
-
}
|
1979 |
-
|
1980 |
-
foreach ($collected as &$opt) {
|
1981 |
-
switch ($opt['group']) {
|
1982 |
-
case 'container':
|
1983 |
-
// Check if has container options
|
1984 |
-
{
|
1985 |
-
$_collected = array();
|
1986 |
-
|
1987 |
-
fw_collect_options( $_collected, $opt['option']['options'], array(
|
1988 |
-
'limit_option_types' => array(),
|
1989 |
-
'limit_container_types' => false,
|
1990 |
-
'limit_level' => 1,
|
1991 |
-
'limit' => 1,
|
1992 |
-
'info_wrapper' => false,
|
1993 |
-
) );
|
1994 |
-
|
1995 |
-
$has_containers = !empty($_collected);
|
1996 |
-
|
1997 |
-
unset($_collected);
|
1998 |
-
}
|
1999 |
-
|
2000 |
-
$children_data = array(
|
2001 |
-
'group' => 'container',
|
2002 |
-
'id' => $opt['id']
|
2003 |
-
);
|
2004 |
-
|
2005 |
-
$args = array(
|
2006 |
-
'title' => empty($opt['option']['title'])
|
2007 |
-
? fw_id_to_title($opt['id'])
|
2008 |
-
: $opt['option']['title'],
|
2009 |
-
'description' => empty($opt['option']['desc'])
|
2010 |
-
? ''
|
2011 |
-
: $opt['option']['desc'],
|
2012 |
-
);
|
2013 |
-
|
2014 |
-
if (isset($opt['option']['wp-customizer-args']) && is_array($opt['option']['wp-customizer-args'])) {
|
2015 |
-
$args = array_merge($opt['option']['wp-customizer-args'], $args);
|
2016 |
-
}
|
2017 |
-
|
2018 |
-
if ($has_containers) {
|
2019 |
-
if ($parent_data) {
|
2020 |
-
trigger_error($opt['id'] .' panel can\'t have a parent ('. $parent_data['id'] .')', E_USER_WARNING);
|
2021 |
-
break;
|
2022 |
-
}
|
2023 |
-
|
2024 |
-
$wp_customize->add_panel($opt['id'], $args);
|
2025 |
-
|
2026 |
-
$children_data['customizer_type'] = 'panel';
|
2027 |
-
} else {
|
2028 |
-
if ($parent_data) {
|
2029 |
-
if ($parent_data['customizer_type'] === 'panel') {
|
2030 |
-
$args['panel'] = $parent_data['id'];
|
2031 |
-
} else {
|
2032 |
-
trigger_error($opt['id'] .' section can have only panel parent ('. $parent_data['id'] .')', E_USER_WARNING);
|
2033 |
-
break;
|
2034 |
-
}
|
2035 |
-
}
|
2036 |
-
|
2037 |
-
$wp_customize->add_section($opt['id'], $args);
|
2038 |
-
|
2039 |
-
$children_data['customizer_type'] = 'section';
|
2040 |
-
}
|
2041 |
-
|
2042 |
-
$this->customizer_register_options(
|
2043 |
-
$wp_customize,
|
2044 |
-
$opt['option']['options'],
|
2045 |
-
$children_data
|
2046 |
-
);
|
2047 |
-
|
2048 |
-
unset($children_data);
|
2049 |
-
break;
|
2050 |
-
case 'option':
|
2051 |
-
$setting_id = FW_Option_Type::get_default_name_prefix() .'['. $opt['id'] .']';
|
2052 |
-
|
2053 |
-
{
|
2054 |
-
$args_control = array(
|
2055 |
-
'label' => empty($opt['option']['label'])
|
2056 |
-
? fw_id_to_title($opt['id'])
|
2057 |
-
: $opt['option']['label'],
|
2058 |
-
'description' => empty($opt['option']['desc'])
|
2059 |
-
? ''
|
2060 |
-
: $opt['option']['desc'],
|
2061 |
-
'settings' => $setting_id,
|
2062 |
-
);
|
2063 |
-
|
2064 |
-
if (isset($opt['option']['wp-customizer-args']) && is_array($opt['option']['wp-customizer-args'])) {
|
2065 |
-
$args_control = array_merge($opt['option']['wp-customizer-args'], $args_control);
|
2066 |
-
}
|
2067 |
-
|
2068 |
-
if ($parent_data) {
|
2069 |
-
if ($parent_data['customizer_type'] === 'section') {
|
2070 |
-
$args_control['section'] = $parent_data['id'];
|
2071 |
-
} else {
|
2072 |
-
trigger_error('Invalid control parent: '. $parent_data['customizer_type'], E_USER_WARNING);
|
2073 |
-
break;
|
2074 |
-
}
|
2075 |
-
} else { // the option is not placed in a section, create a section automatically
|
2076 |
-
$args_control['section'] = 'fw_option_auto_section_'. $opt['id'];
|
2077 |
-
|
2078 |
-
$wp_customize->add_section($args_control['section'], array(
|
2079 |
-
'title' => empty($opt['option']['label'])
|
2080 |
-
? fw_id_to_title($opt['id'])
|
2081 |
-
: $opt['option']['label'],
|
2082 |
-
));
|
2083 |
-
}
|
2084 |
-
}
|
2085 |
-
|
2086 |
-
if (!class_exists('_FW_Customizer_Setting_Option')) {
|
2087 |
-
require_once fw_get_framework_directory('/includes/customizer/class--fw-customizer-setting-option.php');
|
2088 |
-
}
|
2089 |
-
|
2090 |
-
if (!class_exists('_FW_Customizer_Control_Option_Wrapper')) {
|
2091 |
-
require_once fw_get_framework_directory('/includes/customizer/class--fw-customizer-control-option-wrapper.php');
|
2092 |
-
}
|
2093 |
-
|
2094 |
-
{
|
2095 |
-
$args_setting = array(
|
2096 |
-
'default' => fw()->backend->option_type($opt['option']['type'])->get_value_from_input($opt['option'], null),
|
2097 |
-
'fw_option' => $opt['option'],
|
2098 |
-
);
|
2099 |
-
|
2100 |
-
if (isset($opt['option']['wp-customizer-setting-args']) && is_array($opt['option']['wp-customizer-setting-args'])) {
|
2101 |
-
$args_setting = array_merge($opt['option']['wp-customizer-setting-args'], $args_setting);
|
2102 |
-
}
|
2103 |
-
|
2104 |
-
$wp_customize->add_setting(
|
2105 |
-
new _FW_Customizer_Setting_Option(
|
2106 |
-
$wp_customize,
|
2107 |
-
$setting_id,
|
2108 |
-
$args_setting
|
2109 |
-
)
|
2110 |
-
);
|
2111 |
-
|
2112 |
-
unset($args_setting);
|
2113 |
-
}
|
2114 |
-
|
2115 |
-
// control must be registered after setting
|
2116 |
-
$wp_customize->add_control(
|
2117 |
-
new _FW_Customizer_Control_Option_Wrapper(
|
2118 |
-
$wp_customize,
|
2119 |
-
$opt['id'],
|
2120 |
-
$args_control
|
2121 |
-
)
|
2122 |
-
);
|
2123 |
-
break;
|
2124 |
-
default:
|
2125 |
-
trigger_error('Unknown group: '. $opt['group'], E_USER_WARNING);
|
2126 |
-
}
|
2127 |
-
}
|
2128 |
-
}
|
2129 |
-
|
2130 |
-
/**
|
2131 |
-
* For e.g. an option-type was rendered using 'customizer' design,
|
2132 |
-
* but inside it uses render_options() but it doesn't know the current render design
|
2133 |
-
* and the options will be rendered with 'default' design.
|
2134 |
-
* This method allows to specify the default design that will be used if not specified on render_options()
|
2135 |
-
* @param null|string $design
|
2136 |
-
* @internal
|
2137 |
-
*/
|
2138 |
-
public function _set_default_render_design($design = null)
|
2139 |
-
{
|
2140 |
-
if (empty($design) || !in_array($design, $this->available_render_designs)) {
|
2141 |
-
$this->default_render_design = 'default';
|
2142 |
-
} else {
|
2143 |
-
$this->default_render_design = $design;
|
2144 |
-
}
|
2145 |
-
}
|
2146 |
-
}
|
1 |
+
<?php if ( ! defined( 'FW' ) ) {
|
2 |
+
die( 'Forbidden' );
|
3 |
+
}
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Backend functionality
|
7 |
+
*/
|
8 |
+
final class _FW_Component_Backend {
|
9 |
+
|
10 |
+
/** @var callable */
|
11 |
+
private $print_meta_box_content_callback;
|
12 |
+
|
13 |
+
/** @var FW_Form */
|
14 |
+
private $settings_form;
|
15 |
+
|
16 |
+
private $available_render_designs = array( 'default', 'taxonomy', 'customizer' );
|
17 |
+
|
18 |
+
private $default_render_design = 'default';
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Store option types for registration, until they will be required
|
22 |
+
* @var array|false
|
23 |
+
* array Can have some pending option types in it
|
24 |
+
* false Option types already requested and was registered, so do not use pending anymore
|
25 |
+
*/
|
26 |
+
private $option_types_pending_registration = array();
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Contains all option types
|
30 |
+
* @var FW_Option_Type[]
|
31 |
+
*/
|
32 |
+
private $option_types = array();
|
33 |
+
|
34 |
+
/**
|
35 |
+
* @var FW_Option_Type_Undefined
|
36 |
+
*/
|
37 |
+
private $undefined_option_type;
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Store container types for registration, until they will be required
|
41 |
+
* @var array|false
|
42 |
+
* array Can have some pending container types in it
|
43 |
+
* false Container types already requested and was registered, so do not use pending anymore
|
44 |
+
*/
|
45 |
+
private $container_types_pending_registration = array();
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Contains all container types
|
49 |
+
* @var FW_Container_Type[]
|
50 |
+
*/
|
51 |
+
private $container_types = array();
|
52 |
+
|
53 |
+
/**
|
54 |
+
* @var FW_Container_Type_Undefined
|
55 |
+
*/
|
56 |
+
private $undefined_container_type;
|
57 |
+
|
58 |
+
private $static_registered = false;
|
59 |
+
|
60 |
+
/**
|
61 |
+
* @var FW_Access_Key
|
62 |
+
*/
|
63 |
+
private $access_key;
|
64 |
+
|
65 |
+
/**
|
66 |
+
* @internal
|
67 |
+
*/
|
68 |
+
public function _get_settings_page_slug() {
|
69 |
+
return 'fw-settings';
|
70 |
+
}
|
71 |
+
|
72 |
+
private function get_current_edit_taxonomy() {
|
73 |
+
static $cache_current_taxonomy_data = null;
|
74 |
+
|
75 |
+
if ( $cache_current_taxonomy_data !== null ) {
|
76 |
+
return $cache_current_taxonomy_data;
|
77 |
+
}
|
78 |
+
|
79 |
+
$result = array(
|
80 |
+
'taxonomy' => null,
|
81 |
+
'term_id' => 0,
|
82 |
+
);
|
83 |
+
|
84 |
+
do {
|
85 |
+
if ( ! is_admin() ) {
|
86 |
+
break;
|
87 |
+
}
|
88 |
+
|
89 |
+
// code from /wp-admin/admin.php line 110
|
90 |
+
{
|
91 |
+
if ( isset( $_REQUEST['taxonomy'] ) && taxonomy_exists( $_REQUEST['taxonomy'] ) ) {
|
92 |
+
$taxnow = $_REQUEST['taxonomy'];
|
93 |
+
} else {
|
94 |
+
$taxnow = '';
|
95 |
+
}
|
96 |
+
}
|
97 |
+
|
98 |
+
if ( empty( $taxnow ) ) {
|
99 |
+
break;
|
100 |
+
}
|
101 |
+
|
102 |
+
$result['taxonomy'] = $taxnow;
|
103 |
+
|
104 |
+
if ( empty( $_REQUEST['tag_ID'] ) ) {
|
105 |
+
return $result;
|
106 |
+
}
|
107 |
+
|
108 |
+
// code from /wp-admin/edit-tags.php
|
109 |
+
{
|
110 |
+
$tag_ID = (int) $_REQUEST['tag_ID'];
|
111 |
+
}
|
112 |
+
|
113 |
+
$result['term_id'] = $tag_ID;
|
114 |
+
} while ( false );
|
115 |
+
|
116 |
+
$cache_current_taxonomy_data = $result;
|
117 |
+
|
118 |
+
return $cache_current_taxonomy_data;
|
119 |
+
}
|
120 |
+
|
121 |
+
public function __construct() {
|
122 |
+
$this->print_meta_box_content_callback = create_function( '$post,$args', 'echo $args["args"];' );
|
123 |
+
}
|
124 |
+
|
125 |
+
/**
|
126 |
+
* @internal
|
127 |
+
*/
|
128 |
+
public function _init() {
|
129 |
+
if ( is_admin() ) {
|
130 |
+
$this->settings_form = new FW_Form('fw_settings', array(
|
131 |
+
'render' => array($this, '_settings_form_render'),
|
132 |
+
'validate' => array($this, '_settings_form_validate'),
|
133 |
+
'save' => array($this, '_settings_form_save'),
|
134 |
+
));
|
135 |
+
}
|
136 |
+
|
137 |
+
$this->add_actions();
|
138 |
+
$this->add_filters();
|
139 |
+
}
|
140 |
+
|
141 |
+
/**
|
142 |
+
* @internal
|
143 |
+
*/
|
144 |
+
public function _after_components_init() {}
|
145 |
+
|
146 |
+
private function get_access_key()
|
147 |
+
{
|
148 |
+
if (!$this->access_key) {
|
149 |
+
$this->access_key = new FW_Access_Key('fw_backend');
|
150 |
+
}
|
151 |
+
|
152 |
+
return $this->access_key;
|
153 |
+
}
|
154 |
+
|
155 |
+
private function add_actions() {
|
156 |
+
if ( is_admin() ) {
|
157 |
+
add_action('admin_menu', array($this, '_action_admin_menu'));
|
158 |
+
add_action('add_meta_boxes', array($this, '_action_create_post_meta_boxes'), 10, 2);
|
159 |
+
add_action('init', array($this, '_action_init'), 20);
|
160 |
+
add_action('admin_enqueue_scripts', array($this, '_action_admin_register_scripts'),
|
161 |
+
/**
|
162 |
+
* Usually when someone register/enqueue a script/style to be used in other places
|
163 |
+
* in 'admin_enqueue_scripts' actions with default (not set) priority 10, they use priority 9.
|
164 |
+
* Use here priority 8, in case those scripts/styles used in actions with priority 9
|
165 |
+
* are using scripts/styles registered here
|
166 |
+
*/
|
167 |
+
8
|
168 |
+
);
|
169 |
+
add_action('admin_enqueue_scripts', array($this, '_action_admin_enqueue_scripts'),
|
170 |
+
/**
|
171 |
+
* In case some custom defined option types are using script/styles registered
|
172 |
+
* in actions with default priority 10 (make sure the enqueue is executed after register)
|
173 |
+
*/
|
174 |
+
11
|
175 |
+
);
|
176 |
+
|
177 |
+
// render and submit options from javascript
|
178 |
+
{
|
179 |
+
add_action('wp_ajax_fw_backend_options_render', array($this, '_action_ajax_options_render'));
|
180 |
+
add_action('wp_ajax_fw_backend_options_get_values', array($this, '_action_ajax_options_get_values'));
|
181 |
+
}
|
182 |
+
}
|
183 |
+
|
184 |
+
add_action('save_post', array($this, '_action_save_post'), 7, 3);
|
185 |
+
add_action('wp_restore_post_revision', array($this, '_action_restore_post_revision'), 10, 2);
|
186 |
+
add_action('_wp_put_post_revision', array($this, '_action__wp_put_post_revision'));
|
187 |
+
|
188 |
+
add_action('customize_register', array($this, '_action_customize_register'), 7);
|
189 |
+
}
|
190 |
+
|
191 |
+
private function add_filters() {
|
192 |
+
if ( is_admin() ) {
|
193 |
+
add_filter('admin_footer_text', array($this, '_filter_admin_footer_text'), 11);
|
194 |
+
add_filter('update_footer', array($this, '_filter_footer_version'), 11);
|
195 |
+
}
|
196 |
+
}
|
197 |
+
|
198 |
+
/**
|
199 |
+
* @param string|FW_Option_Type $option_type_class
|
200 |
+
*
|
201 |
+
* @internal
|
202 |
+
*/
|
203 |
+
private function register_option_type( $option_type_class ) {
|
204 |
+
if ( is_array( $this->option_types_pending_registration ) ) {
|
205 |
+
// Option types never requested. Continue adding to pending
|
206 |
+
$this->option_types_pending_registration[] = $option_type_class;
|
207 |
+
} else {
|
208 |
+
if ( is_string( $option_type_class ) ) {
|
209 |
+
$option_type_class = new $option_type_class;
|
210 |
+
}
|
211 |
+
|
212 |
+
if ( ! is_subclass_of( $option_type_class, 'FW_Option_Type' ) ) {
|
213 |
+
trigger_error( 'Invalid option type class ' . get_class( $option_type_class ), E_USER_WARNING );
|
214 |
+
return;
|
215 |
+
}
|
216 |
+
|
217 |
+
/**
|
218 |
+
* @var FW_Option_Type $option_type_class
|
219 |
+
*/
|
220 |
+
|
221 |
+
$type = $option_type_class->get_type();
|
222 |
+
|
223 |
+
if ( isset( $this->option_types[ $type ] ) ) {
|
224 |
+
trigger_error( 'Option type "' . $type . '" already registered', E_USER_WARNING );
|
225 |
+
return;
|
226 |
+
}
|
227 |
+
|
228 |
+
$this->option_types[ $type ] = $option_type_class;
|
229 |
+
|
230 |
+
$this->option_types[ $type ]->_call_init($this->get_access_key());
|
231 |
+
}
|
232 |
+
}
|
233 |
+
|
234 |
+
/**
|
235 |
+
* @param string|FW_Container_Type $container_type_class
|
236 |
+
*
|
237 |
+
* @internal
|
238 |
+
*/
|
239 |
+
private function register_container_type( $container_type_class ) {
|
240 |
+
if ( is_array( $this->container_types_pending_registration ) ) {
|
241 |
+
// Container types never requested. Continue adding to pending
|
242 |
+
$this->container_types_pending_registration[] = $container_type_class;
|
243 |
+
} else {
|
244 |
+
if ( is_string( $container_type_class ) ) {
|
245 |
+
$container_type_class = new $container_type_class;
|
246 |
+
}
|
247 |
+
|
248 |
+
if ( ! is_subclass_of( $container_type_class, 'FW_Container_Type' ) ) {
|
249 |
+
trigger_error( 'Invalid container type class ' . get_class( $container_type_class ), E_USER_WARNING );
|
250 |
+
return;
|
251 |
+
}
|
252 |
+
|
253 |
+
/**
|
254 |
+
* @var FW_Container_Type $container_type_class
|
255 |
+
*/
|
256 |
+
|
257 |
+
$type = $container_type_class->get_type();
|
258 |
+
|
259 |
+
if ( isset( $this->container_types[ $type ] ) ) {
|
260 |
+
trigger_error( 'Container type "' . $type . '" already registered', E_USER_WARNING );
|
261 |
+
return;
|
262 |
+
}
|
263 |
+
|
264 |
+
$this->container_types[ $type ] = $container_type_class;
|
265 |
+
|
266 |
+
$this->container_types[ $type ]->_call_init($this->get_access_key());
|
267 |
+
}
|
268 |
+
}
|
269 |
+
|
270 |
+
private function register_static() {
|
271 |
+
if (
|
272 |
+
!doing_action('admin_enqueue_scripts')
|
273 |
+
&&
|
274 |
+
!did_action('admin_enqueue_scripts')
|
275 |
+
) {
|
276 |
+
/**
|
277 |
+
* Do not wp_enqueue/register_...() because at this point not all handles has been registered
|
278 |
+
* and maybe they are used in dependencies in handles that are going to be enqueued.
|
279 |
+
* So as a result some handles will not be equeued because of not registered dependecies.
|
280 |
+
*/
|
281 |
+
return;
|
282 |
+
}
|
283 |
+
|
284 |
+
if ( $this->static_registered ) {
|
285 |
+
return;
|
286 |
+
}
|
287 |
+
|
288 |
+
/**
|
289 |
+
* Register styles/scripts only in admin area, on frontend it's not allowed to use styles/scripts from framework backend core
|
290 |
+
* because they are meant to be used only in backend and can be changed in the future.
|
291 |
+
* If you want to use a style/script from framework backend core, copy it to your theme and enqueue as a theme style/script.
|
292 |
+
*/
|
293 |
+
if ( ! is_admin() ) {
|
294 |
+
$this->static_registered = true;
|
295 |
+
|
296 |
+
return;
|
297 |
+
}
|
298 |
+
|
299 |
+
wp_register_script(
|
300 |
+
'fw-events',
|
301 |
+
fw_get_framework_directory_uri( '/static/js/fw-events.js' ),
|
302 |
+
array( 'backbone' ),
|
303 |
+
fw()->manifest->get_version(),
|
304 |
+
true
|
305 |
+
);
|
306 |
+
|
307 |
+
wp_register_script(
|
308 |
+
'fw-ie-fixes',
|
309 |
+
fw_get_framework_directory_uri( '/static/js/ie-fixes.js' ),
|
310 |
+
array(),
|
311 |
+
fw()->manifest->get_version(),
|
312 |
+
true
|
313 |
+
);
|
314 |
+
|
315 |
+
{
|
316 |
+
wp_register_style(
|
317 |
+
'qtip',
|
318 |
+
fw_get_framework_directory_uri( '/static/libs/qtip/css/jquery.qtip.min.css' ),
|
319 |
+
array(),
|
320 |
+
fw()->manifest->get_version()
|
321 |
+
);
|
322 |
+
wp_register_script(
|
323 |
+
'qtip',
|
324 |
+
fw_get_framework_directory_uri( '/static/libs/qtip/jquery.qtip.min.js' ),
|
325 |
+
array( 'jquery' ),
|
326 |
+
fw()->manifest->get_version()
|
327 |
+
);
|
328 |
+
}
|
329 |
+
|
330 |
+
/**
|
331 |
+
* Important!
|
332 |
+
* Call wp_enqueue_media() before wp_enqueue_script('fw') (or using 'fw' in your script dependencies)
|
333 |
+
* otherwise fw.OptionsModal won't work
|
334 |
+
*/
|
335 |
+
{
|
336 |
+
wp_register_style(
|
337 |
+
'fw',
|
338 |
+
fw_get_framework_directory_uri( '/static/css/fw.css' ),
|
339 |
+
array( 'qtip' ),
|
340 |
+
fw()->manifest->get_version()
|
341 |
+
);
|
342 |
+
|
343 |
+
wp_register_script(
|
344 |
+
'fw',
|
345 |
+
fw_get_framework_directory_uri( '/static/js/fw.js' ),
|
346 |
+
array( 'jquery', 'fw-events', 'backbone', 'qtip' ),
|
347 |
+
fw()->manifest->get_version(),
|
348 |
+
true
|
349 |
+
);
|
350 |
+
|
351 |
+
wp_localize_script( 'fw', '_fw_localized', array(
|
352 |
+
'FW_URI' => fw_get_framework_directory_uri(),
|
353 |
+
'SITE_URI' => site_url(),
|
354 |
+
'LOADER_URI' => apply_filters( 'fw_loader_image', fw_get_framework_directory_uri() . '/static/img/logo.svg' ),
|
355 |
+
'l10n' => array(
|
356 |
+
'done' => __( 'Done', 'fw' ),
|
357 |
+
'ah_sorry' => __( 'Ah, Sorry', 'fw' ),
|
358 |
+
'save' => __( 'Save', 'fw' ),
|
359 |
+
'reset' => __( 'Reset', 'fw' ),
|
360 |
+
),
|
361 |
+
) );
|
362 |
+
}
|
363 |
+
|
364 |
+
{
|
365 |
+
wp_register_style(
|
366 |
+
'fw-backend-options',
|
367 |
+
fw_get_framework_directory_uri( '/static/css/backend-options.css' ),
|
368 |
+
array( 'fw' ),
|
369 |
+
fw()->manifest->get_version()
|
370 |
+
);
|
371 |
+
|
372 |
+
wp_register_script(
|
373 |
+
'fw-backend-options',
|
374 |
+
fw_get_framework_directory_uri( '/static/js/backend-options.js' ),
|
375 |
+
array( 'fw', 'fw-events', 'postbox', 'jquery-ui-tabs' ),
|
376 |
+
fw()->manifest->get_version(),
|
377 |
+
true
|
378 |
+
);
|
379 |
+
|
380 |
+
wp_localize_script( 'fw', '_fw_backend_options_localized', array(
|
381 |
+
'lazy_tabs' => fw()->theme->get_config('lazy_tabs')
|
382 |
+
) );
|
383 |
+
}
|
384 |
+
|
385 |
+
{
|
386 |
+
wp_register_style(
|
387 |
+
'fw-selectize',
|
388 |
+
fw_get_framework_directory_uri( '/static/libs/selectize/selectize.css' ),
|
389 |
+
array(),
|
390 |
+
fw()->manifest->get_version()
|
391 |
+
);
|
392 |
+
wp_register_script(
|
393 |
+
'fw-selectize',
|
394 |
+
fw_get_framework_directory_uri( '/static/libs/selectize/selectize.min.js' ),
|
395 |
+
array( 'jquery', 'fw-ie-fixes' ),
|
396 |
+
fw()->manifest->get_version(),
|
397 |
+
true
|
398 |
+
);
|
399 |
+
}
|
400 |
+
|
401 |
+
{
|
402 |
+
wp_register_script(
|
403 |
+
'fw-mousewheel',
|
404 |
+
fw_get_framework_directory_uri( '/static/libs/mousewheel/jquery.mousewheel.min.js' ),
|
405 |
+
array( 'jquery' ),
|
406 |
+
fw()->manifest->get_version(),
|
407 |
+
true
|
408 |
+
);
|
409 |
+
}
|
410 |
+
|
411 |
+
{
|
412 |
+
wp_register_style(
|
413 |
+
'fw-jscrollpane',
|
414 |
+
fw_get_framework_directory_uri( '/static/libs/jscrollpane/jquery.jscrollpane.css' ),
|
415 |
+
array(),
|
416 |
+
fw()->manifest->get_version()
|
417 |
+
);
|
418 |
+
wp_register_script( 'fw-jscrollpane',
|
419 |
+
fw_get_framework_directory_uri( '/static/libs/jscrollpane/jquery.jscrollpane.min.js' ),
|
420 |
+
array( 'jquery', 'fw-mousewheel' ),
|
421 |
+
fw()->manifest->get_version(),
|
422 |
+
true
|
423 |
+
);
|
424 |
+
}
|
425 |
+
|
426 |
+
wp_register_style(
|
427 |
+
'fw-font-awesome',
|
428 |
+
fw_get_framework_directory_uri( '/static/libs/font-awesome/css/font-awesome.min.css' ),
|
429 |
+
array(),
|
430 |
+
fw()->manifest->get_version()
|
431 |
+
);
|
432 |
+
|
433 |
+
wp_register_script(
|
434 |
+
'backbone-relational',
|
435 |
+
fw_get_framework_directory_uri( '/static/libs/backbone-relational/backbone-relational.js' ),
|
436 |
+
array( 'backbone' ),
|
437 |
+
fw()->manifest->get_version(),
|
438 |
+
true
|
439 |
+
);
|
440 |
+
|
441 |
+
wp_register_script(
|
442 |
+
'fw-uri',
|
443 |
+
fw_get_framework_directory_uri( '/static/libs/uri/URI.js' ),
|
444 |
+
array(),
|
445 |
+
fw()->manifest->get_version(),
|
446 |
+
true
|
447 |
+
);
|
448 |
+
|
449 |
+
wp_register_script(
|
450 |
+
'fw-moment',
|
451 |
+
fw_get_framework_directory_uri( '/static/libs/moment/moment.min.js' ),
|
452 |
+
array(),
|
453 |
+
fw()->manifest->get_version(),
|
454 |
+
true
|
455 |
+
);
|
456 |
+
|
457 |
+
wp_register_script(
|
458 |
+
'fw-form-helpers',
|
459 |
+
fw_get_framework_directory_uri( '/static/js/fw-form-helpers.js' ),
|
460 |
+
array( 'jquery' ),
|
461 |
+
fw()->manifest->get_version(),
|
462 |
+
true
|
463 |
+
);
|
464 |
+
|
465 |
+
wp_register_style(
|
466 |
+
'fw-unycon',
|
467 |
+
fw_get_framework_directory_uri( '/static/libs/unycon/unycon.css' ),
|
468 |
+
array(),
|
469 |
+
fw()->manifest->get_version()
|
470 |
+
);
|
471 |
+
|
472 |
+
$this->static_registered = true;
|
473 |
+
}
|
474 |
+
|
475 |
+
/**
|
476 |
+
* @internal
|
477 |
+
*/
|
478 |
+
public function _action_admin_menu() {
|
479 |
+
$data = array(
|
480 |
+
'capability' => 'manage_options',
|
481 |
+
'slug' => $this->_get_settings_page_slug(),
|
482 |
+
'content_callback' => array( $this, '_print_settings_page' ),
|
483 |
+
);
|
484 |
+
|
485 |
+
if ( ! current_user_can( $data['capability'] ) ) {
|
486 |
+
return;
|
487 |
+
}
|
488 |
+
|
489 |
+
if ( ! fw()->theme->locate_path('/options/settings.php') ) {
|
490 |
+
return;
|
491 |
+
}
|
492 |
+
|
493 |
+
/**
|
494 |
+
* Collect $hookname that contains $data['slug'] before the action
|
495 |
+
* and skip them in verification after action
|
496 |
+
*/
|
497 |
+
{
|
498 |
+
global $_registered_pages;
|
499 |
+
|
500 |
+
$found_hooknames = array();
|
501 |
+
|
502 |
+
if ( ! empty( $_registered_pages ) ) {
|
503 |
+
foreach ( $_registered_pages as $hookname => $b ) {
|
504 |
+
if ( strpos( $hookname, $data['slug'] ) !== false ) {
|
505 |
+
$found_hooknames[ $hookname ] = true;
|
506 |
+
}
|
507 |
+
}
|
508 |
+
}
|
509 |
+
}
|
510 |
+
|
511 |
+
/**
|
512 |
+
* Use this action if you what to add the settings page in a custom place in menu
|
513 |
+
* Usage example http://pastebin.com/gvAjGRm1
|
514 |
+
*/
|
515 |
+
do_action( 'fw_backend_add_custom_settings_menu', $data );
|
516 |
+
|
517 |
+
/**
|
518 |
+
* Check if settings menu was added in the action above
|
519 |
+
*/
|
520 |
+
{
|
521 |
+
$menu_exists = false;
|
522 |
+
|
523 |
+
if ( ! empty( $_registered_pages ) ) {
|
524 |
+
foreach ( $_registered_pages as $hookname => $b ) {
|
525 |
+
if ( isset( $found_hooknames[ $hookname ] ) ) {
|
526 |
+
continue;
|
527 |
+
}
|
528 |
+
|
529 |
+
if ( strpos( $hookname, $data['slug'] ) !== false ) {
|
530 |
+
$menu_exists = true;
|
531 |
+
break;
|
532 |
+
}
|
533 |
+
}
|
534 |
+
}
|
535 |
+
}
|
536 |
+
|
537 |
+
if ( $menu_exists ) {
|
538 |
+
return;
|
539 |
+
}
|
540 |
+
|
541 |
+
add_theme_page(
|
542 |
+
__( 'Theme Settings', 'fw' ),
|
543 |
+
__( 'Theme Settings', 'fw' ),
|
544 |
+
$data['capability'],
|
545 |
+
$data['slug'],
|
546 |
+
$data['content_callback']
|
547 |
+
);
|
548 |
+
|
549 |
+
add_action( 'admin_menu', array( $this, '_action_admin_change_theme_settings_order' ), 9999 );
|
550 |
+
}
|
551 |
+
|
552 |
+
public function _filter_admin_footer_text( $html ) {
|
553 |
+
if (
|
554 |
+
(
|
555 |
+
current_user_can( 'update_themes' )
|
556 |
+
||
|
557 |
+
current_user_can( 'update_plugins' )
|
558 |
+
)
|
559 |
+
&&
|
560 |
+
fw_current_screen_match(array(
|
561 |
+
'only' => array(
|
562 |
+
array('parent_base' => fw()->extensions->manager->get_page_slug()) // Unyson Extensions page
|
563 |
+
)
|
564 |
+
))
|
565 |
+
) {
|
566 |
+
return ( empty( $html ) ? '' : $html . '<br/>' )
|
567 |
+
. '<em>'
|
568 |
+
. str_replace(
|
569 |
+
array(
|
570 |
+
'{wp_review_link}',
|
571 |
+
'{facebook_share_link}',
|
572 |
+
'{twitter_share_link}',
|
573 |
+
),
|
574 |
+
array(
|
575 |
+
fw_html_tag('a', array(
|
576 |
+
'target' => '_blank',
|
577 |
+
'href' => 'https://wordpress.org/support/view/plugin-reviews/unyson?filter=5#postform',
|
578 |
+
), __('leave a review', 'fw')),
|
579 |
+
fw_html_tag('a', array(
|
580 |
+
'target' => '_blank',
|
581 |
+
'href' => 'https://www.facebook.com/sharer/sharer.php?'. http_build_query(array(
|
582 |
+
'u' => 'http://unyson.io',
|
583 |
+
)),
|
584 |
+
'onclick' => 'return !window.open(this.href, \'Facebook\', \'width=640,height=300\')',
|
585 |
+
), __('Facebook', 'fw')),
|
586 |
+
fw_html_tag('a', array(
|
587 |
+
'target' => '_blank',
|
588 |
+
'href' => 'https://twitter.com/home?'. http_build_query(array(
|
589 |
+
'status' => __('Unyson WordPress Framework is the fastest and easiest way to develop a premium theme. I highly recommend it', 'fw')
|
590 |
+
.' http://unyson.io/ #UnysonWP',
|
591 |
+
)),
|
592 |
+
'onclick' => 'return !window.open(this.href, \'Twitter\', \'width=640,height=430\')',
|
593 |
+
), __('Twitter', 'fw')),
|
594 |
+
),
|
595 |
+
__('If you like Unyson, {wp_review_link}, share on {facebook_share_link} or {twitter_share_link}.', 'fw')
|
596 |
+
)
|
597 |
+
. '</em>';
|
598 |
+
} else {
|
599 |
+
return $html;
|
600 |
+
}
|
601 |
+
}
|
602 |
+
|
603 |
+
/**
|
604 |
+
* Print framework version in the admin footer
|
605 |
+
*
|
606 |
+
* @param string $html
|
607 |
+
*
|
608 |
+
* @return string
|
609 |
+
* @internal
|
610 |
+
*/
|
611 |
+
public function _filter_footer_version( $html ) {
|
612 |
+
if ( current_user_can( 'update_themes' ) || current_user_can( 'update_plugins' ) ) {
|
613 |
+
return ( empty( $html ) ? '' : $html . ' | ' ) . fw()->manifest->get_name() . ' ' . fw()->manifest->get_version();
|
614 |
+
} else {
|
615 |
+
return $html;
|
616 |
+
}
|
617 |
+
}
|
618 |
+
|
619 |
+
public function _action_admin_change_theme_settings_order() {
|
620 |
+
global $submenu;
|
621 |
+
|
622 |
+
if ( ! isset( $submenu['themes.php'] ) ) {
|
623 |
+
// probably current user doesn't have this item in menu
|
624 |
+
return;
|
625 |
+
}
|
626 |
+
|
627 |
+
$id = $this->_get_settings_page_slug();
|
628 |
+
$index = null;
|
629 |
+
|
630 |
+
foreach ( $submenu['themes.php'] as $key => $sm ) {
|
631 |
+
if ( $sm[2] == $id ) {
|
632 |
+
$index = $key;
|
633 |
+
break;
|
634 |
+
}
|
635 |
+
}
|
636 |
+
|
637 |
+
if ( ! empty( $index ) ) {
|
638 |
+
$item = $submenu['themes.php'][ $index ];
|
639 |
+
unset( $submenu['themes.php'][ $index ] );
|
640 |
+
array_unshift( $submenu['themes.php'], $item );
|
641 |
+
}
|
642 |
+
}
|
643 |
+
|
644 |
+
public function _print_settings_page() {
|
645 |
+
echo '<div class="wrap">';
|
646 |
+
|
647 |
+
if ( fw()->theme->get_config( 'settings_form_side_tabs' ) ) {
|
648 |
+
// this is needed for flash messages (admin notices) to be displayed properly
|
649 |
+
echo '<h2 class="fw-hidden"></h2>';
|
650 |
+
} else {
|
651 |
+
echo '<h2>' . __( 'Theme Settings', 'fw' ) . '</h2><br/>';
|
652 |
+
}
|
653 |
+
|
654 |
+
$this->settings_form->render();
|
655 |
+
|
656 |
+
echo '</div>';
|
657 |
+
}
|
658 |
+
|
659 |
+
/**
|
660 |
+
* @param string $post_type
|
661 |
+
* @param WP_Post $post
|
662 |
+
*/
|
663 |
+
public function _action_create_post_meta_boxes( $post_type, $post ) {
|
664 |
+
$options = fw()->theme->get_post_options( $post_type );
|
665 |
+
|
666 |
+
if ( empty( $options ) ) {
|
667 |
+
return;
|
668 |
+
}
|
669 |
+
|
670 |
+
$collected = array();
|
671 |
+
|
672 |
+
fw_collect_options( $collected, $options, array(
|
673 |
+
'limit_option_types' => false,
|
674 |
+
'limit_container_types' => false,
|
675 |
+
'limit_level' => 1,
|
676 |
+
'info_wrapper' => true,
|
677 |
+
) );
|
678 |
+
|
679 |
+
if (empty($collected)) {
|
680 |
+
return;
|
681 |
+
}
|
682 |
+
|
683 |
+
$values = fw_get_db_post_option( $post->ID );
|
684 |
+
|
685 |
+
foreach ( $collected as &$option ) {
|
686 |
+
if (
|
687 |
+
$option['group'] === 'container'
|
688 |
+
&&
|
689 |
+
$option['option']['type'] === 'box'
|
690 |
+
) { // this is a box, add it as a metabox
|
691 |
+
$context = isset( $option['option']['context'] )
|
692 |
+
? $option['option']['context']
|
693 |
+
: 'normal';
|
694 |
+
$priority = isset( $option['option']['priority'] )
|
695 |
+
? $option['option']['priority']
|
696 |
+
: 'default';
|
697 |
+
|
698 |
+
add_meta_box(
|
699 |
+
'fw-options-box-' . $option['id'],
|
700 |
+
empty( $option['option']['title'] ) ? ' ' : $option['option']['title'],
|
701 |
+
$this->print_meta_box_content_callback,
|
702 |
+
$post_type,
|
703 |
+
$context,
|
704 |
+
$priority,
|
705 |
+
$this->render_options( $option['option']['options'], $values )
|
706 |
+
);
|
707 |
+
} else { // this is not a box, wrap it in auto-generated box
|
708 |
+
add_meta_box(
|
709 |
+
'fw-options-box:auto-generated:'. time() .':'. fw_unique_increment(),
|
710 |
+
' ',
|
711 |
+
$this->print_meta_box_content_callback,
|
712 |
+
$post_type,
|
713 |
+
'normal',
|
714 |
+
'default',
|
715 |
+
$this->render_options( array($option['id'] => $option['option']), $values )
|
716 |
+
);
|
717 |
+
}
|
718 |
+
}
|
719 |
+
}
|
720 |
+
|
721 |
+
/**
|
722 |
+
* @param object $term
|
723 |
+
*/
|
724 |
+
public function _action_create_taxonomy_options( $term ) {
|
725 |
+
$options = fw()->theme->get_taxonomy_options( $term->taxonomy );
|
726 |
+
|
727 |
+
if ( empty( $options ) ) {
|
728 |
+
return;
|
729 |
+
}
|
730 |
+
|
731 |
+
$collected = array();
|
732 |
+
|
733 |
+
fw_collect_options( $collected, $options, array(
|
734 |
+
'limit_option_types' => false,
|
735 |
+
'limit_container_types' => false,
|
736 |
+
'limit_level' => 1,
|
737 |
+
) );
|
738 |
+
|
739 |
+
if ( empty( $collected ) ) {
|
740 |
+
return;
|
741 |
+
}
|
742 |
+
|
743 |
+
$values = fw_get_db_term_option( $term->term_id, $term->taxonomy );
|
744 |
+
|
745 |
+
// fixes word_press style: .form-field input { width: 95% }
|
746 |
+
echo '<style type="text/css">.fw-option-type-radio input, .fw-option-type-checkbox input { width: auto; }</style>';
|
747 |
+
|
748 |
+
echo $this->render_options( $collected, $values, array(), 'taxonomy' );
|
749 |
+
}
|
750 |
+
|
751 |
+
/**
|
752 |
+
* @param string $taxonomy
|
753 |
+
*/
|
754 |
+
public function _action_create_add_taxonomy_options( $taxonomy ) {
|
755 |
+
$options = fw()->theme->get_taxonomy_options( $taxonomy );
|
756 |
+
if ( empty( $options ) ) {
|
757 |
+
return;
|
758 |
+
}
|
759 |
+
|
760 |
+
$collected = array();
|
761 |
+
|
762 |
+
fw_collect_options( $collected, $options, array(
|
763 |
+
'limit_option_types' => false,
|
764 |
+
'limit_container_types' => false,
|
765 |
+
'limit_level' => 1,
|
766 |
+
) );
|
767 |
+
|
768 |
+
if ( empty( $collected ) ) {
|
769 |
+
return;
|
770 |
+
}
|
771 |
+
|
772 |
+
// fixes word_press style: .form-field input { width: 95% }
|
773 |
+
echo '<style type="text/css">.fw-option-type-radio input, .fw-option-type-checkbox input { width: auto; }</style>';
|
774 |
+
|
775 |
+
echo '<div class="fw-force-xs">';
|
776 |
+
echo $this->render_options( $collected, array(), array(), 'taxonomy' );
|
777 |
+
echo '</div>';
|
778 |
+
|
779 |
+
echo '<script type="text/javascript">'
|
780 |
+
.'jQuery(function($){'
|
781 |
+
.' $("#submit").on("click", function(){'
|
782 |
+
.' $("html, body").animate({ scrollTop: $("#col-left").offset().top });'
|
783 |
+
.' });'
|
784 |
+
.'});'
|
785 |
+
.'</script>';
|
786 |
+
}
|
787 |
+
|
788 |
+
public function _action_init() {
|
789 |
+
$current_edit_taxonomy = $this->get_current_edit_taxonomy();
|
790 |
+
|
791 |
+
if ( $current_edit_taxonomy['taxonomy'] ) {
|
792 |
+
add_action(
|
793 |
+
$current_edit_taxonomy['taxonomy'] . '_edit_form',
|
794 |
+
array( $this, '_action_create_taxonomy_options' )
|
795 |
+
);
|
796 |
+
add_action(
|
797 |
+
$current_edit_taxonomy['taxonomy'] . '_add_form_fields',
|
798 |
+
array( $this, '_action_create_add_taxonomy_options' )
|
799 |
+
);
|
800 |
+
}
|
801 |
+
|
802 |
+
if ( ! empty( $_POST ) ) {
|
803 |
+
// is form submit
|
804 |
+
add_action( 'edited_term', array( $this, '_action_term_edit' ), 10, 3 );
|
805 |
+
|
806 |
+
if ($current_edit_taxonomy['taxonomy']) {
|
807 |
+
add_action(
|
808 |
+
'create_' . $current_edit_taxonomy['taxonomy'],
|
809 |
+
array($this, '_action_save_taxonomy_fields')
|
810 |
+
);
|
811 |
+
}
|
812 |
+
}
|
813 |
+
}
|
814 |
+
|
815 |
+
/**
|
816 |
+
* Experimental custom options save
|
817 |
+
* @param array $options
|
818 |
+
* @param array $values
|
819 |
+
* @return array
|
820 |
+
*/
|
821 |
+
private function process_options_handlers($options, $values)
|
822 |
+
{
|
823 |
+
$handled_values = array();
|
824 |
+
|
825 |
+
foreach (
|
826 |
+
fw_extract_only_options($options)
|
827 |
+
as $option_id => $option
|
828 |
+
) {
|
829 |
+
if (
|
830 |
+
isset($option['option_handler'])
|
831 |
+
&&
|
832 |
+
$option['option_handler'] instanceof FW_Option_Handler
|
833 |
+
) {
|
834 |
+
/*
|
835 |
+
* if the option has a custom option_handler
|
836 |
+
* the saving is delegated to the handler,
|
837 |
+
* so it does not go to the post_meta
|
838 |
+
*/
|
839 |
+
$option['option_handler']->save_option_value($option_id, $option, $values[$option_id]);
|
840 |
+
|
841 |
+
$handled_values[$option_id] = true;
|
842 |
+
}
|
843 |
+
}
|
844 |
+
|
845 |
+
return $handled_values;
|
846 |
+
}
|
847 |
+
|
848 |
+
/**
|
849 |
+
* Save meta from $_POST to fw options (post meta)
|
850 |
+
* @param int $post_id
|
851 |
+
* @param WP_Post $post
|
852 |
+
* @param bool $update
|
853 |
+
*/
|
854 |
+
public function _action_save_post( $post_id, $post, $update ) {
|
855 |
+
if (
|
856 |
+
isset($_POST['post_ID'])
|
857 |
+
&&
|
858 |
+
intval($_POST['post_ID']) === intval($post_id)
|
859 |
+
&&
|
860 |
+
!empty($_POST[ FW_Option_Type::get_default_name_prefix() ]) // this happens on Quick Edit
|
861 |
+
) {
|
862 |
+
/**
|
863 |
+
* This happens on regular post form submit
|
864 |
+
* All data from $_POST belongs this $post
|
865 |
+
* so we save them in its post meta
|
866 |
+
*/
|
867 |
+
|
868 |
+
static $post_options_save_happened = false;
|
869 |
+
if ($post_options_save_happened) {
|
870 |
+
/**
|
871 |
+
* Prevent multiple options save for same post
|
872 |
+
* It can happen from a recursion or wp_update_post() for same post id
|
873 |
+
*/
|
874 |
+
return;
|
875 |
+
} else {
|
876 |
+
$post_options_save_happened = true;
|
877 |
+
}
|
878 |
+
|
879 |
+
$old_values = (array)fw_get_db_post_option($post_id);
|
880 |
+
$current_values = fw_get_options_values_from_input(
|
881 |
+
fw()->theme->get_post_options($post->post_type)
|
882 |
+
);
|
883 |
+
|
884 |
+
fw_set_db_post_option(
|
885 |
+
$post_id,
|
886 |
+
null,
|
887 |
+
array_diff_key( // remove handled values
|
888 |
+
$current_values,
|
889 |
+
$this->process_options_handlers(
|
890 |
+
fw()->theme->get_post_options($post->post_type),
|
891 |
+
$current_values
|
892 |
+
)
|
893 |
+
)
|
894 |
+
);
|
895 |
+
|
896 |
+
/**
|
897 |
+
* @deprecated
|
898 |
+
* Use the 'fw_post_options_update' action
|
899 |
+
*/
|
900 |
+
do_action( 'fw_save_post_options', $post_id, $post, $old_values );
|
901 |
+
} elseif ($original_post_id = wp_is_post_autosave( $post_id )) {
|
902 |
+
do {
|
903 |
+
$parent = get_post($post->post_parent);
|
904 |
+
|
905 |
+
if ( ! $parent instanceof WP_Post ) {
|
906 |
+
break;
|
907 |
+
}
|
908 |
+
|
909 |
+
if (
|
910 |
+
isset($_POST['post_ID'])
|
911 |
+
&&
|
912 |
+
intval($_POST['post_ID']) === intval($parent->ID)
|
913 |
+
) {} else {
|
914 |
+
break;
|
915 |
+
}
|
916 |
+
|
917 |
+
if (empty($_POST[ FW_Option_Type::get_default_name_prefix() ])) {
|
918 |
+
// this happens on Quick Edit
|
919 |
+
break;
|
920 |
+
}
|
921 |
+
|
922 |
+
$current_values = fw_get_options_values_from_input(
|
923 |
+
fw()->theme->get_post_options($parent->post_type)
|
924 |
+
);
|
925 |
+
|
926 |
+
fw_set_db_post_option(
|
927 |
+
$post->ID,
|
928 |
+
null,
|
929 |
+
array_diff_key( // remove handled values
|
930 |
+
$current_values,
|
931 |
+
$this->process_options_handlers(
|
932 |
+
fw()->theme->get_post_options($parent->post_type),
|
933 |
+
$current_values
|
934 |
+
)
|
935 |
+
)
|
936 |
+
);
|
937 |
+
} while(false);
|
938 |
+
} elseif ($original_post_id = wp_is_post_revision( $post_id )) {
|
939 |
+
/**
|
940 |
+
* Do nothing, the
|
941 |
+
* - '_wp_put_post_revision'
|
942 |
+
* - 'wp_restore_post_revision'
|
943 |
+
* actions will handle this
|
944 |
+
*/
|
945 |
+
} else {
|
946 |
+
/**
|
947 |
+
* This happens on:
|
948 |
+
* - post add (auto-draft): do nothing
|
949 |
+
* - revision restore: do nothing, that is handled by the 'wp_restore_post_revision' action
|
950 |
+
*/
|
951 |
+
}
|
952 |
+
}
|
953 |
+
|
954 |
+
/**
|
955 |
+
* @param $post_id
|
956 |
+
* @param $revision_id
|
957 |
+
*/
|
958 |
+
public function _action_restore_post_revision($post_id, $revision_id)
|
959 |
+
{
|
960 |
+
/**
|
961 |
+
* Copy options meta from revision to post
|
962 |
+
*/
|
963 |
+
fw_set_db_post_option(
|
964 |
+
$post_id,
|
965 |
+
null,
|
966 |
+
(array)fw_get_db_post_option($revision_id, null, array())
|
967 |
+
);
|
968 |
+
}
|
969 |
+
|
970 |
+
/**
|
971 |
+
* @param $revision_id
|
972 |
+
*/
|
973 |
+
public function _action__wp_put_post_revision($revision_id)
|
974 |
+
{
|
975 |
+
/**
|
976 |
+
* Copy options meta from post to revision
|
977 |
+
*/
|
978 |
+
fw_set_db_post_option(
|
979 |
+
$revision_id,
|
980 |
+
null,
|
981 |
+
(array)fw_get_db_post_option(
|
982 |
+
wp_is_post_revision($revision_id),
|
983 |
+
null,
|
984 |
+
array()
|
985 |
+
)
|
986 |
+
);
|
987 |
+
}
|
988 |
+
|
989 |
+
/**
|
990 |
+
* Update all post meta `fw_option:<option-id>` with values from post options that has the 'save-in-separate-meta' parameter
|
991 |
+
*
|
992 |
+
* @param int $post_id
|
993 |
+
*
|
994 |
+
* @return bool
|
995 |
+
* @deprecated since 2.5.0
|
996 |
+
*/
|
997 |
+
public function _sync_post_separate_meta( $post_id ) {
|
998 |
+
$post_type = get_post_type( $post_id );
|
999 |
+
|
1000 |
+
if ( ! $post_type ) {
|
1001 |
+
return false;
|
1002 |
+
}
|
1003 |
+
|
1004 |
+
$meta_prefix = 'fw_option:';
|
1005 |
+
|
1006 |
+
/**
|
1007 |
+
* Collect all options that needs to be saved in separate meta
|
1008 |
+
*/
|
1009 |
+
{
|
1010 |
+
$options_values = fw_get_db_post_option( $post_id );
|
1011 |
+
|
1012 |
+
$separate_meta_options = array();
|
1013 |
+
|
1014 |
+
foreach (
|
1015 |
+
fw_extract_only_options( fw()->theme->get_post_options( $post_type ) )
|
1016 |
+
as $option_id => $option
|
1017 |
+
) {
|
1018 |
+
if (
|
1019 |
+
isset( $option['save-in-separate-meta'] )
|
1020 |
+
&&
|
1021 |
+
$option['save-in-separate-meta']
|
1022 |
+
&&
|
1023 |
+
array_key_exists( $option_id, $options_values )
|
1024 |
+
) {
|
1025 |
+
$separate_meta_options[ $meta_prefix . $option_id ] = $options_values[ $option_id ];
|
1026 |
+
}
|
1027 |
+
}
|
1028 |
+
|
1029 |
+
unset( $options_values );
|
1030 |
+
}
|
1031 |
+
|
1032 |
+
/**
|
1033 |
+
* Delete meta that starts with $meta_prefix
|
1034 |
+
*/
|
1035 |
+
{
|
1036 |
+
/** @var wpdb $wpdb */
|
1037 |
+
global $wpdb;
|
1038 |
+
|
1039 |
+
foreach (
|
1040 |
+
$wpdb->get_results(
|
1041 |
+
$wpdb->prepare(
|
1042 |
+
"SELECT meta_key " .
|
1043 |
+
"FROM {$wpdb->postmeta} " .
|
1044 |
+
"WHERE meta_key LIKE %s AND post_id = %d",
|
1045 |
+
$wpdb->esc_like( $meta_prefix ) . '%',
|
1046 |
+
$post_id
|
1047 |
+
)
|
1048 |
+
)
|
1049 |
+
as $row
|
1050 |
+
) {
|
1051 |
+
if ( array_key_exists( $row->meta_key, $separate_meta_options ) ) {
|
1052 |
+
/**
|
1053 |
+
* This meta exists and will be updated below.
|
1054 |
+
* Do not delete for performance reasons, instead of delete->insert will be performed only update
|
1055 |
+
*/
|
1056 |
+
continue;
|
1057 |
+
} else {
|
1058 |
+
// this option does not exist anymore
|
1059 |
+
delete_post_meta( $post_id, $row->meta_key );
|
1060 |
+
}
|
1061 |
+
}
|
1062 |
+
}
|
1063 |
+
|
1064 |
+
foreach ( $separate_meta_options as $meta_key => $option_value ) {
|
1065 |
+
fw_update_post_meta($post_id, $meta_key, $option_value );
|
1066 |
+
}
|
1067 |
+
|
1068 |
+
return true;
|
1069 |
+
}
|
1070 |
+
|
1071 |
+
/**
|
1072 |
+
* @param int $term_id
|
1073 |
+
*/
|
1074 |
+
public function _action_save_taxonomy_fields( $term_id ) {
|
1075 |
+
if (
|
1076 |
+
isset( $_POST['action'] )
|
1077 |
+
&&
|
1078 |
+
'add-tag' === $_POST['action']
|
1079 |
+
&&
|
1080 |
+
isset( $_POST['taxonomy'] )
|
1081 |
+
&&
|
1082 |
+
($taxonomy = get_taxonomy( $_POST['taxonomy'] ))
|
1083 |
+
&&
|
1084 |
+
current_user_can($taxonomy->cap->edit_terms)
|
1085 |
+
) { /* ok */ } else { return; }
|
1086 |
+
|
1087 |
+
$options = fw()->theme->get_taxonomy_options( $taxonomy->name );
|
1088 |
+
if ( empty( $options ) ) {
|
1089 |
+
return;
|
1090 |
+
}
|
1091 |
+
|
1092 |
+
fw_set_db_term_option(
|
1093 |
+
$term_id,
|
1094 |
+
$taxonomy->name,
|
1095 |
+
null,
|
1096 |
+
fw_get_options_values_from_input($options)
|
1097 |
+
);
|
1098 |
+
|
1099 |
+
do_action( 'fw_save_term_options', $term_id, $taxonomy->name, array() );
|
1100 |
+
}
|
1101 |
+
|
1102 |
+
public function _action_term_edit( $term_id, $tt_id, $taxonomy ) {
|
1103 |
+
if (
|
1104 |
+
isset( $_POST['action'] )
|
1105 |
+
&&
|
1106 |
+
'editedtag' === $_POST['action']
|
1107 |
+
&&
|
1108 |
+
isset( $_POST['taxonomy'] )
|
1109 |
+
&&
|
1110 |
+
($taxonomy = get_taxonomy( $_POST['taxonomy'] ))
|
1111 |
+
&&
|
1112 |
+
current_user_can($taxonomy->cap->edit_terms)
|
1113 |
+
) { /* ok */ } else { return; }
|
1114 |
+
|
1115 |
+
if (intval(FW_Request::POST('tag_ID')) != $term_id) {
|
1116 |
+
// the $_POST values belongs to another term, do not save them into this one
|
1117 |
+
return;
|
1118 |
+
}
|
1119 |
+
|
1120 |
+
$options = fw()->theme->get_taxonomy_options( $taxonomy->name );
|
1121 |
+
if ( empty( $options ) ) {
|
1122 |
+
return;
|
1123 |
+
}
|
1124 |
+
|
1125 |
+
$old_values = (array) fw_get_db_term_option( $term_id, $taxonomy->name );
|
1126 |
+
|
1127 |
+
fw_set_db_term_option(
|
1128 |
+
$term_id,
|
1129 |
+
$taxonomy->name,
|
1130 |
+
null,
|
1131 |
+
fw_get_options_values_from_input($options)
|
1132 |
+
);
|
1133 |
+
|
1134 |
+
do_action( 'fw_save_term_options', $term_id, $taxonomy->name, $old_values );
|
1135 |
+
}
|
1136 |
+
|
1137 |
+
public function _action_admin_register_scripts() {
|
1138 |
+
$this->register_static();
|
1139 |
+
}
|
1140 |
+
|
1141 |
+
public function _action_admin_enqueue_scripts() {
|
1142 |
+
global $current_screen, $plugin_page, $post;
|
1143 |
+
|
1144 |
+
/**
|
1145 |
+
* Enqueue settings options static in <head>
|
1146 |
+
*/
|
1147 |
+
{
|
1148 |
+
if ( $this->_get_settings_page_slug() === $plugin_page ) {
|
1149 |
+
fw()->backend->enqueue_options_static(
|
1150 |
+
fw()->theme->get_settings_options()
|
1151 |
+
);
|
1152 |
+
|
1153 |
+
do_action( 'fw_admin_enqueue_scripts:settings' );
|
1154 |
+
}
|
1155 |
+
}
|
1156 |
+
|
1157 |
+
/**
|
1158 |
+
* Enqueue post options static in <head>
|
1159 |
+
*/
|
1160 |
+
{
|
1161 |
+
if ( 'post' === $current_screen->base && $post ) {
|
1162 |
+
fw()->backend->enqueue_options_static(
|
1163 |
+
fw()->theme->get_post_options( $post->post_type )
|
1164 |
+
);
|
1165 |
+
|
1166 |
+
do_action( 'fw_admin_enqueue_scripts:post', $post );
|
1167 |
+
}
|
1168 |
+
}
|
1169 |
+
|
1170 |
+
/**
|
1171 |
+
* Enqueue term options static in <head>
|
1172 |
+
*/
|
1173 |
+
{
|
1174 |
+
if (
|
1175 |
+
'edit-tags' === $current_screen->base
|
1176 |
+
&&
|
1177 |
+
$current_screen->taxonomy
|
1178 |
+
) {
|
1179 |
+
fw()->backend->enqueue_options_static(
|
1180 |
+
fw()->theme->get_taxonomy_options( $current_screen->taxonomy )
|
1181 |
+
);
|
1182 |
+
|
1183 |
+
do_action( 'fw_admin_enqueue_scripts:term', $current_screen->taxonomy );
|
1184 |
+
}
|
1185 |
+
}
|
1186 |
+
}
|
1187 |
+
|
1188 |
+
/**
|
1189 |
+
* Render options html from input json
|
1190 |
+
*
|
1191 |
+
* POST vars:
|
1192 |
+
* - options: '[{option_id: {...}}, {option_id: {...}}, ...]' // Required // String JSON
|
1193 |
+
* - values: {option_id: value, option_id: {...}, ...} // Optional // Object
|
1194 |
+
* - data: {id_prefix: 'fw_options-a-b-', name_prefix: 'fw_options[a][b]'} // Optional // Object
|
1195 |
+
*/
|
1196 |
+
public function _action_ajax_options_render() {
|
1197 |
+
// options
|
1198 |
+
{
|
1199 |
+
if ( ! isset( $_POST['options'] ) ) {
|
1200 |
+
wp_send_json_error( array(
|
1201 |
+
'message' => 'No options'
|
1202 |
+
) );
|
1203 |
+
}
|
1204 |
+
|
1205 |
+
$options = json_decode( FW_Request::POST( 'options' ), true );
|
1206 |
+
|
1207 |
+
if ( ! $options ) {
|
1208 |
+
wp_send_json_error( array(
|
1209 |
+
'message' => 'Wrong options'
|
1210 |
+
) );
|
1211 |
+
}
|
1212 |
+
}
|
1213 |
+
|
1214 |
+
// values
|
1215 |
+
{
|
1216 |
+
if ( isset( $_POST['values'] ) ) {
|
1217 |
+
$values = FW_Request::POST( 'values' );
|
1218 |
+
|
1219 |
+
if (is_string($values)) {
|
1220 |
+
$values = json_decode($values, true);
|
1221 |
+
}
|
1222 |
+
} else {
|
1223 |
+
$values = array();
|
1224 |
+
}
|
1225 |
+
|
1226 |
+
$values = array_intersect_key($values, fw_extract_only_options($options));
|
1227 |
+
}
|
1228 |
+
|
1229 |
+
// data
|
1230 |
+
{
|
1231 |
+
if ( isset( $_POST['data'] ) ) {
|
1232 |
+
$data = FW_Request::POST( 'data' );
|
1233 |
+
} else {
|
1234 |
+
$data = array();
|
1235 |
+
}
|
1236 |
+
}
|
1237 |
+
|
1238 |
+
wp_send_json_success( array(
|
1239 |
+
'html' => fw()->backend->render_options( $options, $values, $data )
|
1240 |
+
) );
|
1241 |
+
}
|
1242 |
+
|
1243 |
+
/**
|
1244 |
+
* Get options values from html generated with 'fw_backend_options_render' ajax action
|
1245 |
+
*
|
1246 |
+
* POST vars:
|
1247 |
+
* - options: '[{option_id: {...}}, {option_id: {...}}, ...]' // Required // String JSON
|
1248 |
+
* - fw_options... // Use a jQuery "ajax form submit" to emulate real form submit
|
1249 |
+
*
|
1250 |
+
* Tip: Inside form html, add: <input type="hidden" name="options" value="[...json...]">
|
1251 |
+
*/
|
1252 |
+
public function _action_ajax_options_get_values() {
|
1253 |
+
// options
|
1254 |
+
{
|
1255 |
+
if ( ! isset( $_POST['options'] ) ) {
|
1256 |
+
wp_send_json_error( array(
|
1257 |
+
'message' => 'No options'
|
1258 |
+
) );
|
1259 |
+
}
|
1260 |
+
|
1261 |
+
$options = json_decode( FW_Request::POST( 'options' ), true );
|
1262 |
+
|
1263 |
+
if ( ! $options ) {
|
1264 |
+
wp_send_json_error( array(
|
1265 |
+
'message' => 'Wrong options'
|
1266 |
+
) );
|
1267 |
+
}
|
1268 |
+
}
|
1269 |
+
|
1270 |
+
// name_prefix
|
1271 |
+
{
|
1272 |
+
if ( isset( $_POST['name_prefix'] ) ) {
|
1273 |
+
$name_prefix = FW_Request::POST( 'name_prefix' );
|
1274 |
+
} else {
|
1275 |
+
$name_prefix = FW_Option_Type::get_default_name_prefix();
|
1276 |
+
}
|
1277 |
+
}
|
1278 |
+
|
1279 |
+
wp_send_json_success( array(
|
1280 |
+
'values' => fw_get_options_values_from_input(
|
1281 |
+
$options,
|
1282 |
+
FW_Request::POST( fw_html_attr_name_to_array_multi_key( $name_prefix ), array() )
|
1283 |
+
)
|
1284 |
+
) );
|
1285 |
+
}
|
1286 |
+
|
1287 |
+
public function _settings_form_render( $data ) {
|
1288 |
+
{
|
1289 |
+
$this->enqueue_options_static( array() );
|
1290 |
+
|
1291 |
+
wp_enqueue_script( 'fw-form-helpers' );
|
1292 |
+
}
|
1293 |
+
|
1294 |
+
$options = fw()->theme->get_settings_options();
|
1295 |
+
|
1296 |
+
if ( empty( $options ) ) {
|
1297 |
+
return $data;
|
1298 |
+
}
|
1299 |
+
|
1300 |
+
if ( $values = FW_Request::POST( FW_Option_Type::get_default_name_prefix() ) ) {
|
1301 |
+
// This is form submit, extract correct values from $_POST values
|
1302 |
+
$values = fw_get_options_values_from_input( $options, $values );
|
1303 |
+
} else {
|
1304 |
+
// Extract previously saved correct values
|
1305 |
+
$values = fw_get_db_settings_option();
|
1306 |
+
}
|
1307 |
+
|
1308 |
+
$ajax_submit = fw()->theme->get_config( 'settings_form_ajax_submit' );
|
1309 |
+
$side_tabs = fw()->theme->get_config( 'settings_form_side_tabs' );
|
1310 |
+
|
1311 |
+
$data['attr']['class'] = 'fw-settings-form';
|
1312 |
+
|
1313 |
+
if ( $side_tabs ) {
|
1314 |
+
$data['attr']['class'] .= ' fw-backend-side-tabs';
|
1315 |
+
}
|
1316 |
+
|
1317 |
+
$data['submit']['html'] = '<!-- -->'; // is generated in view
|
1318 |
+
|
1319 |
+
do_action( 'fw_settings_form_render', array(
|
1320 |
+
'ajax_submit' => $ajax_submit,
|
1321 |
+
'side_tabs' => $side_tabs,
|
1322 |
+
) );
|
1323 |
+
|
1324 |
+
fw_render_view( fw_get_framework_directory( '/views/backend-settings-form.php' ), array(
|
1325 |
+
'options' => $options,
|
1326 |
+
'values' => $values,
|
1327 |
+
'reset_input_name' => '_fw_reset_options',
|
1328 |
+
'ajax_submit' => $ajax_submit,
|
1329 |
+
'side_tabs' => $side_tabs,
|
1330 |
+
), false );
|
1331 |
+
|
1332 |
+
return $data;
|
1333 |
+
}
|
1334 |
+
|
1335 |
+
public function _settings_form_validate( $errors ) {
|
1336 |
+
if ( ! current_user_can( 'manage_options' ) ) {
|
1337 |
+
$errors['_no_permission'] = __( 'You have no permissions to change settings options', 'fw' );
|
1338 |
+
}
|
1339 |
+
|
1340 |
+
return $errors;
|
1341 |
+
}
|
1342 |
+
|
1343 |
+
public function _settings_form_save( $data ) {
|
1344 |
+
$flash_id = 'fw_settings_form_save';
|
1345 |
+
$old_values = (array) fw_get_db_settings_option();
|
1346 |
+
|
1347 |
+
if ( ! empty( $_POST['_fw_reset_options'] ) ) { // The "Reset" button was pressed
|
1348 |
+
fw_set_db_settings_option(
|
1349 |
+
null,
|
1350 |
+
/**
|
1351 |
+
* Some values that don't relate to design, like API credentials, are useful to not be wiped out.
|
1352 |
+
*
|
1353 |
+
* Usage:
|
1354 |
+
*
|
1355 |
+
* add_filter('fw_settings_form_reset:values', '_filter_add_persisted_option', 10, 2);
|
1356 |
+
* function _filter_add_persisted_option ($current_persisted, $old_values) {
|
1357 |
+
* $value_to_persist = fw_akg('my/multi/key', $old_values);
|
1358 |
+
* fw_aks('my/multi/key', $value_to_persist, $current_persisted);
|
1359 |
+
*
|
1360 |
+
* return $current_persisted;
|
1361 |
+
* }
|
1362 |
+
*/
|
1363 |
+
apply_filters('fw_settings_form_reset:values', array(), $old_values)
|
1364 |
+
);
|
1365 |
+
|
1366 |
+
FW_Flash_Messages::add( $flash_id, __( 'The options were successfully reset', 'fw' ), 'success' );
|
1367 |
+
|
1368 |
+
do_action( 'fw_settings_form_reset', $old_values );
|
1369 |
+
} else { // The "Save" button was pressed
|
1370 |
+
fw_set_db_settings_option(
|
1371 |
+
null,
|
1372 |
+
fw_get_options_values_from_input(
|
1373 |
+
fw()->theme->get_settings_options()
|
1374 |
+
)
|
1375 |
+
);
|
1376 |
+
|
1377 |
+
FW_Flash_Messages::add( $flash_id, __( 'The options were successfully saved', 'fw' ), 'success' );
|
1378 |
+
|
1379 |
+
do_action( 'fw_settings_form_saved', $old_values );
|
1380 |
+
}
|
1381 |
+
|
1382 |
+
$redirect_url = fw_current_url();
|
1383 |
+
|
1384 |
+
$data['redirect'] = $redirect_url;
|
1385 |
+
|
1386 |
+
return $data;
|
1387 |
+
}
|
1388 |
+
|
1389 |
+
/**
|
1390 |
+
* Render options array and return the generated HTML
|
1391 |
+
*
|
1392 |
+
* @param array $options
|
1393 |
+
* @param array $values Correct values returned by fw_get_options_values_from_input()
|
1394 |
+
* @param array $options_data {id_prefix => ..., name_prefix => ...}
|
1395 |
+
* @param string $design
|
1396 |
+
*
|
1397 |
+
* @return string HTML
|
1398 |
+
*/
|
1399 |
+
public function render_options( $options, $values = array(), $options_data = array(), $design = null ) {
|
1400 |
+
if (empty($design)) {
|
1401 |
+
$design = $this->default_render_design;
|
1402 |
+
}
|
1403 |
+
|
1404 |
+
if (
|
1405 |
+
!doing_action('admin_enqueue_scripts')
|
1406 |
+
&&
|
1407 |
+
!did_action('admin_enqueue_scripts')
|
1408 |
+
) {
|
1409 |
+
/**
|
1410 |
+
* Do not wp_enqueue/register_...() because at this point not all handles has been registered
|
1411 |
+
* and maybe they are used in dependencies in handles that are going to be enqueued.
|
1412 |
+
* So as a result some handles will not be equeued because of not registered dependecies.
|
1413 |
+
*/
|
1414 |
+
} else {
|
1415 |
+
/**
|
1416 |
+
* register scripts and styles
|
1417 |
+
* in case if this method is called before enqueue_scripts action
|
1418 |
+
* and option types has some of these in their dependencies
|
1419 |
+
*/
|
1420 |
+
$this->register_static();
|
1421 |
+
|
1422 |
+
wp_enqueue_media();
|
1423 |
+
wp_enqueue_style( 'fw-backend-options' );
|
1424 |
+
wp_enqueue_script( 'fw-backend-options' );
|
1425 |
+
}
|
1426 |
+
|
1427 |
+
$collected = array();
|
1428 |
+
|
1429 |
+
fw_collect_options( $collected, $options, array(
|
1430 |
+
'limit_option_types' => false,
|
1431 |
+
'limit_container_types' => false,
|
1432 |
+
'limit_level' => 1,
|
1433 |
+
'info_wrapper' => true,
|
1434 |
+
) );
|
1435 |
+
|
1436 |
+
if ( empty( $collected ) ) {
|
1437 |
+
return false;
|
1438 |
+
}
|
1439 |
+
|
1440 |
+
$html = '';
|
1441 |
+
|
1442 |
+
$option = reset( $collected );
|
1443 |
+
|
1444 |
+
$collected_type = array(
|
1445 |
+
'group' => $option['group'],
|
1446 |
+
'type' => $option['option']['type'],
|
1447 |
+
);
|
1448 |
+
$collected_type_options = array(
|
1449 |
+
$option['id'] => &$option['option']
|
1450 |
+
);
|
1451 |
+
|
1452 |
+
while ( $collected_type_options ) {
|
1453 |
+
$option = next( $collected );
|
1454 |
+
|
1455 |
+
if ( $option ) {
|
1456 |
+
if (
|
1457 |
+
$option['group'] === $collected_type['group']
|
1458 |
+
&&
|
1459 |
+
$option['option']['type'] === $collected_type['type']
|
1460 |
+
) {
|
1461 |
+
$collected_type_options[ $option['id'] ] = &$option['option'];
|
1462 |
+
continue;
|
1463 |
+
}
|
1464 |
+
}
|
1465 |
+
|
1466 |
+
switch ( $collected_type['group'] ) {
|
1467 |
+
case 'container':
|
1468 |
+
if ($design === 'taxonomy') {
|
1469 |
+
$html .= fw_render_view(
|
1470 |
+
fw_get_framework_directory('/views/backend-container-design-'. $design .'.php'),
|
1471 |
+
array(
|
1472 |
+
'type' => $collected_type['type'],
|
1473 |
+
'html' => $this->container_type($collected_type['type'])->render(
|
1474 |
+
$collected_type_options, $values, $options_data
|
1475 |
+
),
|
1476 |
+
)
|
1477 |
+
);
|
1478 |
+
} else {
|
1479 |
+
$html .= $this->container_type($collected_type['type'])->render(
|
1480 |
+
$collected_type_options, $values, $options_data
|
1481 |
+
);
|
1482 |
+
}
|
1483 |
+
break;
|
1484 |
+
case 'option':
|
1485 |
+
foreach ( $collected_type_options as $id => &$_option ) {
|
1486 |
+
$data = $options_data; // do not change directly to not affect next loops
|
1487 |
+
|
1488 |
+
$data['value'] = isset( $values[ $id ] ) ? $values[ $id ] : null;
|
1489 |
+
|
1490 |
+
$html .= $this->render_option(
|
1491 |
+
$id,
|
1492 |
+
$_option,
|
1493 |
+
$data,
|
1494 |
+
$design
|
1495 |
+
);
|
1496 |
+
}
|
1497 |
+
unset($_option);
|
1498 |
+
break;
|
1499 |
+
default:
|
1500 |
+
$html .= '<p><em>' . __( 'Unknown collected group', 'fw' ) . ': ' . $collected_type['group'] . '</em></p>';
|
1501 |
+
}
|
1502 |
+
|
1503 |
+
unset( $collected_type, $collected_type_options );
|
1504 |
+
|
1505 |
+
if ( $option ) {
|
1506 |
+
$collected_type = array(
|
1507 |
+
'group' => $option['group'],
|
1508 |
+
'type' => $option['option']['type'],
|
1509 |
+
);
|
1510 |
+
$collected_type_options = array(
|
1511 |
+
$option['id'] => &$option['option']
|
1512 |
+
);
|
1513 |
+
} else {
|
1514 |
+
$collected_type_options = array();
|
1515 |
+
}
|
1516 |
+
}
|
1517 |
+
|
1518 |
+
return $html;
|
1519 |
+
}
|
1520 |
+
|
1521 |
+
/**
|
1522 |
+
* Enqueue options static
|
1523 |
+
*
|
1524 |
+
* Useful when you have dynamic options html on the page (for e.g. options modal)
|
1525 |
+
* and in order to initialize that html properly, the option types scripts styles must be enqueued on the page
|
1526 |
+
*
|
1527 |
+
* @param array $options
|
1528 |
+
*/
|
1529 |
+
public function enqueue_options_static( $options ) {
|
1530 |
+
if (
|
1531 |
+
!doing_action('admin_enqueue_scripts')
|
1532 |
+
&&
|
1533 |
+
!did_action('admin_enqueue_scripts')
|
1534 |
+
) {
|
1535 |
+
/**
|
1536 |
+
* Do not wp_enqueue/register_...() because at this point not all handles has been registered
|
1537 |
+
* and maybe they are used in dependencies in handles that are going to be enqueued.
|
1538 |
+
* So as a result some handles will not be equeued because of not registered dependecies.
|
1539 |
+
*/
|
1540 |
+
return;
|
1541 |
+
} else {
|
1542 |
+
/**
|
1543 |
+
* register scripts and styles
|
1544 |
+
* in case if this method is called before enqueue_scripts action
|
1545 |
+
* and option types has some of these in their dependencies
|
1546 |
+
*/
|
1547 |
+
$this->register_static();
|
1548 |
+
|
1549 |
+
wp_enqueue_media();
|
1550 |
+
wp_enqueue_style( 'fw-backend-options' );
|
1551 |
+
wp_enqueue_script( 'fw-backend-options' );
|
1552 |
+
}
|
1553 |
+
|
1554 |
+
$collected = array();
|
1555 |
+
|
1556 |
+
fw_collect_options( $collected, $options, array(
|
1557 |
+
'limit_option_types' => false,
|
1558 |
+
'limit_container_types' => false,
|
1559 |
+
'limit_level' => 0,
|
1560 |
+
'info_wrapper' => true,
|
1561 |
+
) );
|
1562 |
+
|
1563 |
+
foreach ( $collected as &$option ) {
|
1564 |
+
if ($option['group'] === 'option') {
|
1565 |
+
fw()->backend->option_type($option['option']['type'])->enqueue_static($option['id'], $option['option']);
|
1566 |
+
} elseif ($option['group'] === 'container') {
|
1567 |
+
fw()->backend->container_type($option['option']['type'])->enqueue_static($option['id'], $option['option']);
|
1568 |
+
}
|
1569 |
+
}
|
1570 |
+
}
|
1571 |
+
|
1572 |
+
/**
|
1573 |
+
* Render option enclosed in backend design
|
1574 |
+
*
|
1575 |
+
* @param string $id
|
1576 |
+
* @param array $option
|
1577 |
+
* @param array $data
|
1578 |
+
* @param string $design default or taxonomy
|
1579 |
+
*
|
1580 |
+
* @return string
|
1581 |
+
*/
|
1582 |
+
public function render_option( $id, $option, $data = array(), $design = null ) {
|
1583 |
+
if (empty($design)) {
|
1584 |
+
$design = $this->default_render_design;
|
1585 |
+
}
|
1586 |
+
|
1587 |
+
if (
|
1588 |
+
!doing_action('admin_enqueue_scripts')
|
1589 |
+
&&
|
1590 |
+
!did_action('admin_enqueue_scripts')
|
1591 |
+
) {
|
1592 |
+
/**
|
1593 |
+
* Do not wp_enqueue/register_...() because at this point not all handles has been registered
|
1594 |
+
* and maybe they are used in dependencies in handles that are going to be enqueued.
|
1595 |
+
* So as a result some handles will not be equeued because of not registered dependecies.
|
1596 |
+
*/
|
1597 |
+
} else {
|
1598 |
+
$this->register_static();
|
1599 |
+
}
|
1600 |
+
|
1601 |
+
|
1602 |
+
if ( ! in_array( $design, $this->available_render_designs ) ) {
|
1603 |
+
trigger_error( 'Invalid render design specified: ' . $design, E_USER_WARNING );
|
1604 |
+
$design = 'post';
|
1605 |
+
}
|
1606 |
+
|
1607 |
+
if ( ! isset( $data['id_prefix'] ) ) {
|
1608 |
+
$data['id_prefix'] = FW_Option_Type::get_default_id_prefix();
|
1609 |
+
}
|
1610 |
+
|
1611 |
+
if (
|
1612 |
+
isset($option['option_handler']) &&
|
1613 |
+
$option['option_handler'] instanceof FW_Option_Handler
|
1614 |
+
) {
|
1615 |
+
|
1616 |
+
/*
|
1617 |
+
* if the option has a custom option_handler
|
1618 |
+
* then the handler provides the option's value
|
1619 |
+
*/
|
1620 |
+
$data['value'] = $option['option_handler']->get_option_value($id, $option, $data);
|
1621 |
+
}
|
1622 |
+
|
1623 |
+
$data = apply_filters(
|
1624 |
+
'fw:backend:option-render:data',
|
1625 |
+
$data
|
1626 |
+
);
|
1627 |
+
|
1628 |
+
return fw_render_view(fw_get_framework_directory('/views/backend-option-design-'. $design .'.php'), array(
|
1629 |
+
'id' => $id,
|
1630 |
+
'option' => $option,
|
1631 |
+
'data' => $data,
|
1632 |
+
) );
|
1633 |
+
}
|
1634 |
+
|
1635 |
+
/**
|
1636 |
+
* Render a meta box
|
1637 |
+
*
|
1638 |
+
* @param string $id
|
1639 |
+
* @param string $title
|
1640 |
+
* @param string $content HTML
|
1641 |
+
* @param array $other Optional elements
|
1642 |
+
*
|
1643 |
+
* @return string Generated meta box html
|
1644 |
+
*/
|
1645 |
+
public function render_box( $id, $title, $content, $other = array() ) {
|
1646 |
+
if ( ! function_exists( 'add_meta_box' ) ) {
|
1647 |
+
trigger_error( 'Try call this method later (\'admin_init\' action), add_meta_box() function does not exists yet.',
|
1648 |
+
E_USER_WARNING );
|
1649 |
+
|
1650 |
+
return '';
|
1651 |
+
}
|
1652 |
+
|
1653 |
+
$other = array_merge( array(
|
1654 |
+
'html_before_title' => false,
|
1655 |
+
'html_after_title' => false,
|
1656 |
+
'attr' => array(),
|
1657 |
+
), $other );
|
1658 |
+
|
1659 |
+
{
|
1660 |
+
$placeholders = array(
|
1661 |
+
'id' => '{{meta_box_id}}',
|
1662 |
+
'title' => '{{meta_box_title}}',
|
1663 |
+
'content' => '{{meta_box_content}}',
|
1664 |
+
);
|
1665 |
+
|
1666 |
+
// other placeholders
|
1667 |
+
{
|
1668 |
+
$placeholders['html_before_title'] = '{{meta_box_html_before_title}}';
|
1669 |
+
$placeholders['html_after_title'] = '{{meta_box_html_after_title}}';
|
1670 |
+
$placeholders['attr'] = '{{meta_box_attr}}';
|
1671 |
+
$placeholders['attr_class'] = '{{meta_box_attr_class}}';
|
1672 |
+
}
|
1673 |
+
}
|
1674 |
+
|
1675 |
+
$cache_key = 'fw_meta_box_template';
|
1676 |
+
|
1677 |
+
try {
|
1678 |
+
$meta_box_template = FW_Cache::get( $cache_key );
|
1679 |
+
} catch ( FW_Cache_Not_Found_Exception $e ) {
|
1680 |
+
$temp_screen_id = 'fw-temp-meta-box-screen-id-' . fw_unique_increment();
|
1681 |
+
$context = 'normal';
|
1682 |
+
|
1683 |
+
add_meta_box(
|
1684 |
+
$placeholders['id'],
|
1685 |
+
$placeholders['title'],
|
1686 |
+
$this->print_meta_box_content_callback,
|
1687 |
+
$temp_screen_id,
|
1688 |
+
$context,
|
1689 |
+
'default',
|
1690 |
+
$placeholders['content']
|
1691 |
+
);
|
1692 |
+
|
1693 |
+
ob_start();
|
1694 |
+
|
1695 |
+
do_meta_boxes( $temp_screen_id, $context, null );
|
1696 |
+
|
1697 |
+
$meta_box_template = ob_get_clean();
|
1698 |
+
|
1699 |
+
remove_meta_box( $id, $temp_screen_id, $context );
|
1700 |
+
|
1701 |
+
// remove wrapper div, leave only meta box div
|
1702 |
+
{
|
1703 |
+
// <div ...>
|
1704 |
+
{
|
1705 |
+
$meta_box_template = str_replace(
|
1706 |
+
'<div id="' . $context . '-sortables" class="meta-box-sortables">',
|
1707 |
+
'',
|
1708 |
+
$meta_box_template
|
1709 |
+
);
|
1710 |
+
}
|
1711 |
+
|
1712 |
+
// </div>
|
1713 |
+
{
|
1714 |
+
$meta_box_template = explode( '</div>', $meta_box_template );
|
1715 |
+
array_pop( $meta_box_template );
|
1716 |
+
$meta_box_template = implode( '</div>', $meta_box_template );
|
1717 |
+
}
|
1718 |
+
}
|
1719 |
+
|
1720 |
+
// add 'fw-postbox' class and some attr related placeholders
|
1721 |
+
$meta_box_template = str_replace(
|
1722 |
+
'class="postbox',
|
1723 |
+
$placeholders['attr'] . ' class="postbox fw-postbox' . $placeholders['attr_class'],
|
1724 |
+
$meta_box_template
|
1725 |
+
);
|
1726 |
+
|
1727 |
+
// add html_before|after_title placeholders
|
1728 |
+
{
|
1729 |
+
$meta_box_template = str_replace(
|
1730 |
+
'<span>' . $placeholders['title'] . '</span>',
|
1731 |
+
|
1732 |
+
/**
|
1733 |
+
* used <small> not <span> because there is a lot of css and js
|
1734 |
+
* that thinks inside <h2 class="hndle"> there is only one <span>
|
1735 |
+
* so do not brake their logic
|
1736 |
+
*/
|
1737 |
+
'<small class="fw-html-before-title">' . $placeholders['html_before_title'] . '</small>' .
|
1738 |
+
'<span>' . $placeholders['title'] . '</span>' .
|
1739 |
+
'<small class="fw-html-after-title">' . $placeholders['html_after_title'] . '</small>',
|
1740 |
+
|
1741 |
+
$meta_box_template
|
1742 |
+
);
|
1743 |
+
}
|
1744 |
+
|
1745 |
+
FW_Cache::set( $cache_key, $meta_box_template );
|
1746 |
+
}
|
1747 |
+
|
1748 |
+
// prepare attributes
|
1749 |
+
{
|
1750 |
+
$attr_class = '';
|
1751 |
+
if ( isset( $other['attr']['class'] ) ) {
|
1752 |
+
$attr_class = ' ' . $other['attr']['class'];
|
1753 |
+
|
1754 |
+
unset( $other['attr']['class'] );
|
1755 |
+
}
|
1756 |
+
|
1757 |
+
unset( $other['attr']['id'] );
|
1758 |
+
}
|
1759 |
+
|
1760 |
+
// replace placeholders with data/content
|
1761 |
+
return str_replace(
|
1762 |
+
array(
|
1763 |
+
$placeholders['id'],
|
1764 |
+
$placeholders['title'],
|
1765 |
+
$placeholders['content'],
|
1766 |
+
$placeholders['html_before_title'],
|
1767 |
+
$placeholders['html_after_title'],
|
1768 |
+
$placeholders['attr'],
|
1769 |
+
$placeholders['attr_class'],
|
1770 |
+
),
|
1771 |
+
array(
|
1772 |
+
esc_attr( $id ),
|
1773 |
+
$title,
|
1774 |
+
$content,
|
1775 |
+
$other['html_before_title'],
|
1776 |
+
$other['html_after_title'],
|
1777 |
+
fw_attr_to_html( $other['attr'] ),
|
1778 |
+
esc_attr( $attr_class )
|
1779 |
+
),
|
1780 |
+
$meta_box_template
|
1781 |
+
);
|
1782 |
+
}
|
1783 |
+
|
1784 |
+
/**
|
1785 |
+
* @param FW_Access_Key $access_key
|
1786 |
+
* @param string|FW_Option_Type $option_type_class
|
1787 |
+
*
|
1788 |
+
* @internal
|
1789 |
+
*/
|
1790 |
+
public function _register_option_type( FW_Access_Key $access_key, $option_type_class ) {
|
1791 |
+
if ( $access_key->get_key() !== 'fw_option_type' ) {
|
1792 |
+
trigger_error( 'Call denied', E_USER_ERROR );
|
1793 |
+
}
|
1794 |
+
|
1795 |
+
$this->register_option_type( $option_type_class );
|
1796 |
+
}
|
1797 |
+
|
1798 |
+
/**
|
1799 |
+
* @param FW_Access_Key $access_key
|
1800 |
+
* @param string|FW_Container_Type $container_type_class
|
1801 |
+
*
|
1802 |
+
* @internal
|
1803 |
+
*/
|
1804 |
+
public function _register_container_type( FW_Access_Key $access_key, $container_type_class ) {
|
1805 |
+
if ( $access_key->get_key() !== 'fw_container_type' ) {
|
1806 |
+
trigger_error( 'Call denied', E_USER_ERROR );
|
1807 |
+
}
|
1808 |
+
|
1809 |
+
$this->register_container_type( $container_type_class );
|
1810 |
+
}
|
1811 |
+
|
1812 |
+
/**
|
1813 |
+
* @param string $option_type
|
1814 |
+
*
|
1815 |
+
* @return FW_Option_Type|FW_Option_Type_Undefined
|
1816 |
+
*/
|
1817 |
+
public function option_type( $option_type ) {
|
1818 |
+
if ( is_array( $this->option_types_pending_registration ) ) {
|
1819 |
+
// This method is called first time
|
1820 |
+
|
1821 |
+
do_action('fw_option_types_init');
|
1822 |
+
|
1823 |
+
// Register pending option types
|
1824 |
+
{
|
1825 |
+
$pending_option_types = $this->option_types_pending_registration;
|
1826 |
+
|
1827 |
+
// clear this property, so register_option_type() will not add option types to pending anymore
|
1828 |
+
$this->option_types_pending_registration = false;
|
1829 |
+
|
1830 |
+
foreach ( $pending_option_types as $option_type_class ) {
|
1831 |
+
$this->register_option_type( $option_type_class );
|
1832 |
+
}
|
1833 |
+
|
1834 |
+
unset( $pending_option_types );
|
1835 |
+
}
|
1836 |
+
}
|
1837 |
+
|
1838 |
+
if ( isset( $this->option_types[ $option_type ] ) ) {
|
1839 |
+
return $this->option_types[ $option_type ];
|
1840 |
+
} else {
|
1841 |
+
if ( is_admin() ) {
|
1842 |
+
FW_Flash_Messages::add(
|
1843 |
+
'fw-get-option-type-undefined-' . $option_type,
|
1844 |
+
sprintf( __( 'Undefined option type: %s', 'fw' ), $option_type ),
|
1845 |
+
'warning'
|
1846 |
+
);
|
1847 |
+
}
|
1848 |
+
|
1849 |
+
if (!$this->undefined_option_type) {
|
1850 |
+
require_once fw_get_framework_directory('/includes/option-types/class-fw-option-type-undefined.php');
|
1851 |
+
|
1852 |
+
$this->undefined_option_type = new FW_Option_Type_Undefined();
|
1853 |
+
}
|
1854 |
+
|
1855 |
+
return $this->undefined_option_type;
|
1856 |
+
}
|
1857 |
+
}
|
1858 |
+
|
1859 |
+
/**
|
1860 |
+
* @param string $container_type
|
1861 |
+
*
|
1862 |
+
* @return FW_Container_Type|FW_Container_Type_Undefined
|
1863 |
+
*/
|
1864 |
+
public function container_type( $container_type ) {
|
1865 |
+
if ( is_array( $this->container_types_pending_registration ) ) {
|
1866 |
+
// This method is called first time
|
1867 |
+
|
1868 |
+
do_action('fw_container_types_init');
|
1869 |
+
|
1870 |
+
// Register pending container types
|
1871 |
+
{
|
1872 |
+
$pending_container_types = $this->container_types_pending_registration;
|
1873 |
+
|
1874 |
+
// clear this property, so register_container_type() will not add container types to pending anymore
|
1875 |
+
$this->container_types_pending_registration = false;
|
1876 |
+
|
1877 |
+
foreach ( $pending_container_types as $container_type_class ) {
|
1878 |
+
$this->register_container_type( $container_type_class );
|
1879 |
+
}
|
1880 |
+
|
1881 |
+
unset( $pending_container_types );
|
1882 |
+
}
|
1883 |
+
}
|
1884 |
+
|
1885 |
+
if ( isset( $this->container_types[ $container_type ] ) ) {
|
1886 |
+
return $this->container_types[ $container_type ];
|
1887 |
+
} else {
|
1888 |
+
if ( is_admin() ) {
|
1889 |
+
FW_Flash_Messages::add(
|
1890 |
+
'fw-get-container-type-undefined-' . $container_type,
|
1891 |
+
sprintf( __( 'Undefined container type: %s', 'fw' ), $container_type ),
|
1892 |
+
'warning'
|
1893 |
+
);
|
1894 |
+
}
|
1895 |
+
|
1896 |
+
if (!$this->undefined_container_type) {
|
1897 |
+
require_once fw_get_framework_directory('/includes/container-types/class-fw-container-type-undefined.php');
|
1898 |
+
|
1899 |
+
$this->undefined_container_type = new FW_Container_Type_Undefined();
|
1900 |
+
}
|
1901 |
+
|
1902 |
+
return $this->undefined_container_type;
|
1903 |
+
}
|
1904 |
+
}
|
1905 |
+
|
1906 |
+
/**
|
1907 |
+
* @param WP_Customize_Manager $wp_customize
|
1908 |
+
* @internal
|
1909 |
+
*/
|
1910 |
+
public function _action_customize_register($wp_customize) {
|
1911 |
+
if (is_admin()) {
|
1912 |
+
add_action('admin_enqueue_scripts', array($this, '_action_enqueue_customizer_static'));
|
1913 |
+
}
|
1914 |
+
|
1915 |
+
$this->customizer_register_options(
|
1916 |
+
$wp_customize,
|
1917 |
+
fw()->theme->get_customizer_options()
|
1918 |
+
);
|
1919 |
+
}
|
1920 |
+
|
1921 |
+
/**
|
1922 |
+
* @internal
|
1923 |
+
*/
|
1924 |
+
public function _action_enqueue_customizer_static()
|
1925 |
+
{
|
1926 |
+
{
|
1927 |
+
$options_for_enqueue = array();
|
1928 |
+
$customizer_options = fw()->theme->get_customizer_options();
|
1929 |
+
|
1930 |
+
/**
|
1931 |
+
* In customizer options is allowed to have container with unspecified (or not existing) 'type'
|
1932 |
+
* fw()->backend->enqueue_options_static() tries to enqueue both options and container static
|
1933 |
+
* not existing container types will throw notices.
|
1934 |
+
* To prevent that, extract and send it only options (without containers)
|
1935 |
+
*/
|
1936 |
+
fw_collect_options($options_for_enqueue, $customizer_options);
|
1937 |
+
|
1938 |
+
fw()->backend->enqueue_options_static($options_for_enqueue);
|
1939 |
+
|
1940 |
+
unset($options_for_enqueue, $customizer_options);
|
1941 |
+
}
|
1942 |
+
|
1943 |
+
wp_enqueue_script(
|
1944 |
+
'fw-backend-customizer',
|
1945 |
+
fw_get_framework_directory_uri( '/static/js/backend-customizer.js' ),
|
1946 |
+
array( 'jquery', 'fw-events', 'backbone' ),
|
1947 |
+
fw()->manifest->get_version(),
|
1948 |
+
true
|
1949 |
+
);
|
1950 |
+
wp_localize_script(
|
1951 |
+
'fw-backend-customizer',
|
1952 |
+
'_fw_backend_customizer_localized',
|
1953 |
+
array(
|
1954 |
+
'change_timeout' => apply_filters('fw_customizer_option_change_timeout', 333),
|
1955 |
+
)
|
1956 |
+
);
|
1957 |
+
|
1958 |
+
do_action('fw_admin_enqueue_scripts:customizer');
|
1959 |
+
}
|
1960 |
+
|
1961 |
+
/**
|
1962 |
+
* @param WP_Customize_Manager $wp_customize
|
1963 |
+
* @param array $options
|
1964 |
+
* @param array $parent_data {'type':'...','id':'...'}
|
1965 |
+
*/
|
1966 |
+
private function customizer_register_options($wp_customize, $options, $parent_data = array()) {
|
1967 |
+
$collected = array();
|
1968 |
+
|
1969 |
+
fw_collect_options( $collected, $options, array(
|
1970 |
+
'limit_option_types' => false,
|
1971 |
+
'limit_container_types' => false,
|
1972 |
+
'limit_level' => 1,
|
1973 |
+
'info_wrapper' => true,
|
1974 |
+
) );
|
1975 |
+
|
1976 |
+
if ( empty( $collected ) ) {
|
1977 |
+
return;
|
1978 |
+
}
|
1979 |
+
|
1980 |
+
foreach ($collected as &$opt) {
|
1981 |
+
switch ($opt['group']) {
|
1982 |
+
case 'container':
|
1983 |
+
// Check if has container options
|
1984 |
+
{
|
1985 |
+
$_collected = array();
|
1986 |
+
|
1987 |
+
fw_collect_options( $_collected, $opt['option']['options'], array(
|
1988 |
+
'limit_option_types' => array(),
|
1989 |
+
'limit_container_types' => false,
|
1990 |
+
'limit_level' => 1,
|
1991 |
+
'limit' => 1,
|
1992 |
+
'info_wrapper' => false,
|
1993 |
+
) );
|
1994 |
+
|
1995 |
+
$has_containers = !empty($_collected);
|
1996 |
+
|
1997 |
+
unset($_collected);
|
1998 |
+
}
|
1999 |
+
|
2000 |
+
$children_data = array(
|
2001 |
+
'group' => 'container',
|
2002 |
+
'id' => $opt['id']
|
2003 |
+
);
|
2004 |
+
|
2005 |
+
$args = array(
|
2006 |
+
'title' => empty($opt['option']['title'])
|
2007 |
+
? fw_id_to_title($opt['id'])
|
2008 |
+
: $opt['option']['title'],
|
2009 |
+
'description' => empty($opt['option']['desc'])
|
2010 |
+
? ''
|
2011 |
+
: $opt['option']['desc'],
|
2012 |
+
);
|
2013 |
+
|
2014 |
+
if (isset($opt['option']['wp-customizer-args']) && is_array($opt['option']['wp-customizer-args'])) {
|
2015 |
+
$args = array_merge($opt['option']['wp-customizer-args'], $args);
|
2016 |
+
}
|
2017 |
+
|
2018 |
+
if ($has_containers) {
|
2019 |
+
if ($parent_data) {
|
2020 |
+
trigger_error($opt['id'] .' panel can\'t have a parent ('. $parent_data['id'] .')', E_USER_WARNING);
|
2021 |
+
break;
|
2022 |
+
}
|
2023 |
+
|
2024 |
+
$wp_customize->add_panel($opt['id'], $args);
|
2025 |
+
|
2026 |
+
$children_data['customizer_type'] = 'panel';
|
2027 |
+
} else {
|
2028 |
+
if ($parent_data) {
|
2029 |
+
if ($parent_data['customizer_type'] === 'panel') {
|
2030 |
+
$args['panel'] = $parent_data['id'];
|
2031 |
+
} else {
|
2032 |
+
trigger_error($opt['id'] .' section can have only panel parent ('. $parent_data['id'] .')', E_USER_WARNING);
|
2033 |
+
break;
|
2034 |
+
}
|
2035 |
+
}
|
2036 |
+
|
2037 |
+
$wp_customize->add_section($opt['id'], $args);
|
2038 |
+
|
2039 |
+
$children_data['customizer_type'] = 'section';
|
2040 |
+
}
|
2041 |
+
|
2042 |
+
$this->customizer_register_options(
|
2043 |
+
$wp_customize,
|
2044 |
+
$opt['option']['options'],
|
2045 |
+
$children_data
|
2046 |
+
);
|
2047 |
+
|
2048 |
+
unset($children_data);
|
2049 |
+
break;
|
2050 |
+
case 'option':
|
2051 |
+
$setting_id = FW_Option_Type::get_default_name_prefix() .'['. $opt['id'] .']';
|
2052 |
+
|
2053 |
+
{
|
2054 |
+
$args_control = array(
|
2055 |
+
'label' => empty($opt['option']['label'])
|
2056 |
+
? fw_id_to_title($opt['id'])
|
2057 |
+
: $opt['option']['label'],
|
2058 |
+
'description' => empty($opt['option']['desc'])
|
2059 |
+
? ''
|
2060 |
+
: $opt['option']['desc'],
|
2061 |
+
'settings' => $setting_id,
|
2062 |
+
);
|
2063 |
+
|
2064 |
+
if (isset($opt['option']['wp-customizer-args']) && is_array($opt['option']['wp-customizer-args'])) {
|
2065 |
+
$args_control = array_merge($opt['option']['wp-customizer-args'], $args_control);
|
2066 |
+
}
|
2067 |
+
|
2068 |
+
if ($parent_data) {
|
2069 |
+
if ($parent_data['customizer_type'] === 'section') {
|
2070 |
+
$args_control['section'] = $parent_data['id'];
|
2071 |
+
} else {
|
2072 |
+
trigger_error('Invalid control parent: '. $parent_data['customizer_type'], E_USER_WARNING);
|
2073 |
+
break;
|
2074 |
+
}
|
2075 |
+
} else { // the option is not placed in a section, create a section automatically
|
2076 |
+
$args_control['section'] = 'fw_option_auto_section_'. $opt['id'];
|
2077 |
+
|
2078 |
+
$wp_customize->add_section($args_control['section'], array(
|
2079 |
+
'title' => empty($opt['option']['label'])
|
2080 |
+
? fw_id_to_title($opt['id'])
|
2081 |
+
: $opt['option']['label'],
|
2082 |
+
));
|
2083 |
+
}
|
2084 |
+
}
|
2085 |
+
|
2086 |
+
if (!class_exists('_FW_Customizer_Setting_Option')) {
|
2087 |
+
require_once fw_get_framework_directory('/includes/customizer/class--fw-customizer-setting-option.php');
|
2088 |
+
}
|
2089 |
+
|
2090 |
+
if (!class_exists('_FW_Customizer_Control_Option_Wrapper')) {
|
2091 |
+
require_once fw_get_framework_directory('/includes/customizer/class--fw-customizer-control-option-wrapper.php');
|
2092 |
+
}
|
2093 |
+
|
2094 |
+
{
|
2095 |
+
$args_setting = array(
|
2096 |
+
'default' => fw()->backend->option_type($opt['option']['type'])->get_value_from_input($opt['option'], null),
|
2097 |
+
'fw_option' => $opt['option'],
|
2098 |
+
);
|
2099 |
+
|
2100 |
+
if (isset($opt['option']['wp-customizer-setting-args']) && is_array($opt['option']['wp-customizer-setting-args'])) {
|
2101 |
+
$args_setting = array_merge($opt['option']['wp-customizer-setting-args'], $args_setting);
|
2102 |
+
}
|
2103 |
+
|
2104 |
+
$wp_customize->add_setting(
|
2105 |
+
new _FW_Customizer_Setting_Option(
|
2106 |
+
$wp_customize,
|
2107 |
+
$setting_id,
|
2108 |
+
$args_setting
|
2109 |
+
)
|
2110 |
+
);
|
2111 |
+
|
2112 |
+
unset($args_setting);
|
2113 |
+
}
|
2114 |
+
|
2115 |
+
// control must be registered after setting
|
2116 |
+
$wp_customize->add_control(
|
2117 |
+
new _FW_Customizer_Control_Option_Wrapper(
|
2118 |
+
$wp_customize,
|
2119 |
+
$opt['id'],
|
2120 |
+
$args_control
|
2121 |
+
)
|
2122 |
+
);
|
2123 |
+
break;
|
2124 |
+
default:
|
2125 |
+
trigger_error('Unknown group: '. $opt['group'], E_USER_WARNING);
|
2126 |
+
}
|
2127 |
+
}
|
2128 |
+
}
|
2129 |
+
|
2130 |
+
/**
|
2131 |
+
* For e.g. an option-type was rendered using 'customizer' design,
|
2132 |
+
* but inside it uses render_options() but it doesn't know the current render design
|
2133 |
+
* and the options will be rendered with 'default' design.
|
2134 |
+
* This method allows to specify the default design that will be used if not specified on render_options()
|
2135 |
+
* @param null|string $design
|
2136 |
+
* @internal
|
2137 |
+
*/
|
2138 |
+
public function _set_default_render_design($design = null)
|
2139 |
+
{
|
2140 |
+
if (empty($design) || !in_array($design, $this->available_render_designs)) {
|
2141 |
+
$this->default_render_design = 'default';
|
2142 |
+
} else {
|
2143 |
+
$this->default_render_design = $design;
|
2144 |
+
}
|
2145 |
+
}
|
2146 |
+
}
|
framework/core/components/extensions.php
CHANGED
@@ -1,661 +1,661 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Extensions component
|
5 |
-
*/
|
6 |
-
final class _FW_Component_Extensions
|
7 |
-
{
|
8 |
-
/**
|
9 |
-
* All existing extensions
|
10 |
-
* @var FW_Extension[] { 'extension_name' => instance }
|
11 |
-
*/
|
12 |
-
private static $all_extensions = array();
|
13 |
-
|
14 |
-
/**
|
15 |
-
* All existing extensions names arranged in hierarchical tree like they are in directories
|
16 |
-
* @var array
|
17 |
-
*/
|
18 |
-
private static $all_extensions_tree = array();
|
19 |
-
|
20 |
-
/**
|
21 |
-
* Active extensions
|
22 |
-
*
|
23 |
-
* On every extension activation, it will be pushed at the end of this array.
|
24 |
-
* The extensions order is important when including files.
|
25 |
-
* If extension A requires extension B, extension B is activated before extension A,
|
26 |
-
* and all files of the extension B (hooks.php, static.php, etc.) must be included before extension A
|
27 |
-
* For e.g. extension A may have in static.php:
|
28 |
-
* wp_enqueue_script( 'ext-A-script', 'script.js', array( 'ext-B-script' ) );
|
29 |
-
* so 'ext-B-script' must be registered before 'ext-A-script'
|
30 |
-
*
|
31 |
-
* @var FW_Extension[] { 'extension_name' => instance }
|
32 |
-
*/
|
33 |
-
private static $active_extensions = array();
|
34 |
-
|
35 |
-
/**
|
36 |
-
* Active extensions names arranged in hierarchical tree like they are in directories
|
37 |
-
* @var array
|
38 |
-
*/
|
39 |
-
private static $active_extensions_tree = array();
|
40 |
-
|
41 |
-
/**
|
42 |
-
* @var array { 'extension_name' => array('required_by', 'required_by') }
|
43 |
-
*/
|
44 |
-
private static $extensions_required_by_extensions = array();
|
45 |
-
|
46 |
-
/**
|
47 |
-
* @var array { 'extension_name' => &array() }
|
48 |
-
*/
|
49 |
-
private static $extension_to_all_tree = array();
|
50 |
-
|
51 |
-
/**
|
52 |
-
* @var array { 'extension_name' => &array() }
|
53 |
-
*/
|
54 |
-
private static $extension_to_active_tree = array();
|
55 |
-
|
56 |
-
/**
|
57 |
-
* @var FW_Access_Key
|
58 |
-
*/
|
59 |
-
private static $access_key;
|
60 |
-
|
61 |
-
/**
|
62 |
-
* @var null|_FW_Extensions_Manager
|
63 |
-
*/
|
64 |
-
public $manager;
|
65 |
-
|
66 |
-
/**
|
67 |
-
* Option name that stores the active extensions array
|
68 |
-
* @internal
|
69 |
-
*/
|
70 |
-
public function _get_active_extensions_db_option_name()
|
71 |
-
{
|
72 |
-
return 'fw_active_extensions';
|
73 |
-
}
|
74 |
-
|
75 |
-
/**
|
76 |
-
* @param null|string $extension_name Check if an extension is set as active in database
|
77 |
-
* @internal
|
78 |
-
* @return array|bool
|
79 |
-
*/
|
80 |
-
public function _get_db_active_extensions($extension_name = null)
|
81 |
-
{
|
82 |
-
$extensions = get_option($this->_get_active_extensions_db_option_name(), array());
|
83 |
-
|
84 |
-
if ($extension_name) {
|
85 |
-
return isset($extensions[$extension_name]);
|
86 |
-
} else {
|
87 |
-
return $extensions;
|
88 |
-
}
|
89 |
-
}
|
90 |
-
|
91 |
-
public function __construct()
|
92 |
-
{
|
93 |
-
require dirname(__FILE__) .'/extensions/class-fw-extension-default.php';
|
94 |
-
|
95 |
-
if (
|
96 |
-
/**
|
97 |
-
* Do not load in frontend because it has no functionality that can be useful in frontend
|
98 |
-
*/
|
99 |
-
is_admin()
|
100 |
-
||
|
101 |
-
/**
|
102 |
-
* While in cron request (on auto-update), is_admin() is false
|
103 |
-
* but we need the actions that moves extensions to a tmp dir then back, on plugin update
|
104 |
-
* todo: maybe move those actions here in this class?
|
105 |
-
*/
|
106 |
-
defined( 'DOING_CRON' )
|
107 |
-
) {
|
108 |
-
require dirname(__FILE__) .'/extensions/manager/class--fw-extensions-manager.php';
|
109 |
-
|
110 |
-
$this->manager = new _FW_Extensions_Manager();
|
111 |
-
}
|
112 |
-
}
|
113 |
-
|
114 |
-
/**
|
115 |
-
* Load extension from directory
|
116 |
-
*
|
117 |
-
* @param array $data
|
118 |
-
*/
|
119 |
-
private static function load_extensions($data)
|
120 |
-
{
|
121 |
-
if (false) {
|
122 |
-
$data = array(
|
123 |
-
'rel_path' => '/extension',
|
124 |
-
'path' => '/path/to/extension',
|
125 |
-
'uri' => 'https://uri.to/extension',
|
126 |
-
'customizations_locations' => array(
|
127 |
-
'/path/to/parent/theme/customizations/extensions/ext/rel/path' => 'https://uri.to/customization/path',
|
128 |
-
'/path/to/child/theme/customizations/extensions/ext/rel/path' => 'https://uri.to/customization/path',
|
129 |
-
),
|
130 |
-
|
131 |
-
'all_extensions_tree' => array(),
|
132 |
-
'all_extensions' => array(),
|
133 |
-
'current_depth' => 1,
|
134 |
-
'parent' => '&$parent_extension_instance',
|
135 |
-
);
|
136 |
-
}
|
137 |
-
|
138 |
-
/**
|
139 |
-
* Do not check all keys
|
140 |
-
* if one not set, then sure others are not set (this is a private method)
|
141 |
-
*/
|
142 |
-
if (!isset($data['all_extensions_tree'])) {
|
143 |
-
$data['all_extensions_tree'] = &self::$all_extensions_tree;
|
144 |
-
$data['all_extensions'] = &self::$all_extensions;
|
145 |
-
$data['current_depth'] = 1;
|
146 |
-
$data['rel_path'] = '';
|
147 |
-
$data['parent'] = null;
|
148 |
-
}
|
149 |
-
|
150 |
-
$dirs = glob($data['path'] .'/*', GLOB_ONLYDIR);
|
151 |
-
|
152 |
-
if (empty($dirs)) {
|
153 |
-
return;
|
154 |
-
}
|
155 |
-
|
156 |
-
if ($data['current_depth'] > 1) {
|
157 |
-
$customizations_locations = array();
|
158 |
-
|
159 |
-
foreach ($data['customizations_locations'] as $customization_path => $customization_uri) {
|
160 |
-
$customizations_locations[ $customization_path .'/extensions' ] = $customization_uri .'/extensions';
|
161 |
-
}
|
162 |
-
|
163 |
-
$data['customizations_locations'] = $customizations_locations;
|
164 |
-
}
|
165 |
-
|
166 |
-
foreach ($dirs as $extension_dir) {
|
167 |
-
$extension_name = basename($extension_dir);
|
168 |
-
|
169 |
-
{
|
170 |
-
$customizations_locations = array();
|
171 |
-
|
172 |
-
foreach ($data['customizations_locations'] as $customization_path => $customization_uri) {
|
173 |
-
$customizations_locations[ $customization_path .'/'. $extension_name ] = $customization_uri .'/'. $extension_name;
|
174 |
-
}
|
175 |
-
}
|
176 |
-
|
177 |
-
if (isset($data['all_extensions'][$extension_name])) {
|
178 |
-
if ($data['all_extensions'][$extension_name]->get_parent() !== $data['parent']) {
|
179 |
-
// extension with the same name exists in another tree
|
180 |
-
trigger_error(
|
181 |
-
'Extension "'. $extension_name .'" is already defined '.
|
182 |
-
'in "'. $data['all_extensions'][$extension_name]->get_declared_path() .'" '.
|
183 |
-
'found again in "'. $extension_dir .'"',
|
184 |
-
E_USER_ERROR
|
185 |
-
);
|
186 |
-
}
|
187 |
-
|
188 |
-
// this is a directory with customizations for an extension
|
189 |
-
|
190 |
-
self::load_extensions(array(
|
191 |
-
'rel_path' => $data['rel_path'] .'/'. $extension_name .'/extensions',
|
192 |
-
'path' => $data['path'] .'/'. $extension_name .'/extensions',
|
193 |
-
'uri' => $data['uri'] .'/'. $extension_name .'/extensions',
|
194 |
-
'customizations_locations' => $customizations_locations,
|
195 |
-
|
196 |
-
'all_extensions_tree' => &$data['all_extensions_tree'][$extension_name],
|
197 |
-
'all_extensions' => &$data['all_extensions'],
|
198 |
-
'current_depth' => $data['current_depth'] + 1,
|
199 |
-
'parent' => &$data['all_extensions'][$extension_name],
|
200 |
-
));
|
201 |
-
} else {
|
202 |
-
$class_file_name = 'class-fw-extension-'. $extension_name .'.php';
|
203 |
-
|
204 |
-
if (file_exists($extension_dir .'/manifest.php')) {
|
205 |
-
$data['all_extensions_tree'][$extension_name] = array();
|
206 |
-
|
207 |
-
self::$extension_to_all_tree[$extension_name] = &$data['all_extensions_tree'][$extension_name];
|
208 |
-
|
209 |
-
if (fw_include_file_isolated($extension_dir .'/'. $class_file_name)) {
|
210 |
-
$class_name = 'FW_Extension_'. fw_dirname_to_classname($extension_name);
|
211 |
-
} else {
|
212 |
-
$parent_class_name = get_class($data['parent']);
|
213 |
-
// check if parent extension has been defined custom Default class for its child extensions
|
214 |
-
if (class_exists($parent_class_name .'_Default')) {
|
215 |
-
$class_name = $parent_class_name .'_Default';
|
216 |
-
} else {
|
217 |
-
$class_name = 'FW_Extension_Default';
|
218 |
-
}
|
219 |
-
}
|
220 |
-
|
221 |
-
if (!is_subclass_of($class_name, 'FW_Extension')) {
|
222 |
-
trigger_error('Extension "'. $extension_name .'" must extend FW_Extension class', E_USER_ERROR);
|
223 |
-
}
|
224 |
-
|
225 |
-
$data['all_extensions'][$extension_name] = new $class_name(array(
|
226 |
-
'rel_path' => $data['rel_path'] .'/'. $extension_name,
|
227 |
-
'path' => $data['path'] .'/'. $extension_name,
|
228 |
-
'uri' => $data['uri'] .'/'. $extension_name,
|
229 |
-
'parent' => $data['parent'],
|
230 |
-
'depth' => $data['current_depth'],
|
231 |
-
'customizations_locations' => $customizations_locations,
|
232 |
-
));
|
233 |
-
} else {
|
234 |
-
/**
|
235 |
-
* The manifest file does not exist, do not load this extension.
|
236 |
-
* Maybe it's a directory with configurations for a not existing extension.
|
237 |
-
*/
|
238 |
-
continue;
|
239 |
-
}
|
240 |
-
|
241 |
-
self::load_extensions(array(
|
242 |
-
'rel_path' => $data['all_extensions'][$extension_name]->get_rel_path() .'/extensions',
|
243 |
-
'path' => $data['all_extensions'][$extension_name]->get_path() .'/extensions',
|
244 |
-
'uri' => $data['all_extensions'][$extension_name]->get_uri() .'/extensions',
|
245 |
-
'customizations_locations' => $customizations_locations,
|
246 |
-
|
247 |
-
'parent' => &$data['all_extensions'][$extension_name],
|
248 |
-
'all_extensions_tree' => &$data['all_extensions_tree'][$extension_name],
|
249 |
-
'all_extensions' => &$data['all_extensions'],
|
250 |
-
'current_depth' => $data['current_depth'] + 1,
|
251 |
-
));
|
252 |
-
}
|
253 |
-
}
|
254 |
-
}
|
255 |
-
|
256 |
-
/**
|
257 |
-
* Include file from all extension's locations: framework, parent, child
|
258 |
-
* @param string|FW_Extension $extension
|
259 |
-
* @param string $file_rel_path
|
260 |
-
* @param bool $themeFirst
|
261 |
-
* false - [framework, parent, child]
|
262 |
-
* true - [child, parent, framework]
|
263 |
-
* @param bool $onlyFirstFound
|
264 |
-
*/
|
265 |
-
private static function include_extension_file_all_locations($extension, $file_rel_path, $themeFirst = false, $onlyFirstFound = false)
|
266 |
-
{
|
267 |
-
if (is_string($extension)) {
|
268 |
-
$extension = fw()->extensions->get($extension);
|
269 |
-
}
|
270 |
-
|
271 |
-
$paths = $extension->get_customizations_locations();
|
272 |
-
$paths[$extension->get_path()] = $extension->get_uri();
|
273 |
-
|
274 |
-
if (!$themeFirst) {
|
275 |
-
$paths = array_reverse($paths);
|
276 |
-
}
|
277 |
-
|
278 |
-
foreach ($paths as $path => $uri) {
|
279 |
-
if (fw_include_file_isolated($path . $file_rel_path)) {
|
280 |
-
if ($onlyFirstFound) {
|
281 |
-
return;
|
282 |
-
}
|
283 |
-
}
|
284 |
-
}
|
285 |
-
}
|
286 |
-
|
287 |
-
/**
|
288 |
-
* Include all files from directory, from all extension's locations: framework, child, parent
|
289 |
-
* @param string|FW_Extension $extension
|
290 |
-
* @param string $dir_rel_path
|
291 |
-
* @param bool $themeFirst
|
292 |
-
* false - [framework, parent, child]
|
293 |
-
* true - [child, parent, framework]
|
294 |
-
*/
|
295 |
-
private static function include_extension_directory_all_locations($extension, $dir_rel_path, $themeFirst = false)
|
296 |
-
{
|
297 |
-
if (is_string($extension)) {
|
298 |
-
$extension = fw()->extensions->get($extension);
|
299 |
-
}
|
300 |
-
|
301 |
-
$paths = $extension->get_customizations_locations();
|
302 |
-
$paths[$extension->get_path()] = $extension->get_uri();
|
303 |
-
|
304 |
-
if (!$themeFirst) {
|
305 |
-
$paths = array_reverse($paths);
|
306 |
-
}
|
307 |
-
|
308 |
-
foreach ($paths as $path => $uri) {
|
309 |
-
if ($files = glob($path . $dir_rel_path .'/*.php')) {
|
310 |
-
foreach ($files as $dir_file_path) {
|
311 |
-
fw_include_file_isolated($dir_file_path);
|
312 |
-
}
|
313 |
-
}
|
314 |
-
}
|
315 |
-
}
|
316 |
-
|
317 |
-
public function get_locations()
|
318 |
-
{
|
319 |
-
$cache_key = 'fw_extensions_locations';
|
320 |
-
|
321 |
-
try {
|
322 |
-
return FW_Cache::get($cache_key);
|
323 |
-
} catch (FW_Cache_Not_Found_Exception $e) {
|
324 |
-
/**
|
325 |
-
* { '/hello/world/extensions' => 'https://hello.com/world/extensions' }
|
326 |
-
*/
|
327 |
-
$custom_locations = apply_filters('fw_extensions_locations', array());
|
328 |
-
|
329 |
-
{
|
330 |
-
$customizations_locations = array();
|
331 |
-
|
332 |
-
if (is_child_theme()) {
|
333 |
-
$customizations_locations[fw_get_stylesheet_customizations_directory('/extensions')]
|
334 |
-
= fw_get_stylesheet_customizations_directory_uri('/extensions');
|
335 |
-
}
|
336 |
-
|
337 |
-
$customizations_locations[fw_get_template_customizations_directory('/extensions')]
|
338 |
-
= fw_get_template_customizations_directory_uri('/extensions');
|
339 |
-
|
340 |
-
$customizations_locations += $custom_locations;
|
341 |
-
}
|
342 |
-
|
343 |
-
$locations = array();
|
344 |
-
|
345 |
-
$locations[ fw_get_framework_directory('/extensions') ] = array(
|
346 |
-
'path' => fw_get_framework_directory('/extensions'),
|
347 |
-
'uri' => fw_get_framework_directory_uri('/extensions'),
|
348 |
-
'customizations_locations' => $customizations_locations,
|
349 |
-
'is' => array(
|
350 |
-
'framework' => true,
|
351 |
-
'custom' => false,
|
352 |
-
'theme' => false,
|
353 |
-
),
|
354 |
-
);
|
355 |
-
|
356 |
-
foreach ($custom_locations as $path => $uri) {
|
357 |
-
unset($customizations_locations[$path]);
|
358 |
-
$locations[ $path ] = array(
|
359 |
-
'path' => $path,
|
360 |
-
'uri' => $uri,
|
361 |
-
'customizations_locations' => $customizations_locations,
|
362 |
-
'is' => array(
|
363 |
-
'framework' => false,
|
364 |
-
'custom' => true,
|
365 |
-
'theme' => false,
|
366 |
-
),
|
367 |
-
);
|
368 |
-
}
|
369 |
-
|
370 |
-
array_pop($customizations_locations);
|
371 |
-
$locations[ fw_get_template_customizations_directory('/extensions') ] = array(
|
372 |
-
'path' => fw_get_template_customizations_directory('/extensions'),
|
373 |
-
'uri' => fw_get_template_customizations_directory_uri('/extensions'),
|
374 |
-
'customizations_locations' => $customizations_locations,
|
375 |
-
'is' => array(
|
376 |
-
'framework' => false,
|
377 |
-
'custom' => false,
|
378 |
-
'theme' => true,
|
379 |
-
),
|
380 |
-
);
|
381 |
-
|
382 |
-
if (is_child_theme()) {
|
383 |
-
array_pop($customizations_locations);
|
384 |
-
$locations[ fw_get_stylesheet_customizations_directory('/extensions') ] = array(
|
385 |
-
'path' => fw_get_stylesheet_customizations_directory('/extensions'),
|
386 |
-
'uri' => fw_get_stylesheet_customizations_directory_uri('/extensions'),
|
387 |
-
'customizations_locations' => $customizations_locations,
|
388 |
-
'is' => array(
|
389 |
-
'framework' => false,
|
390 |
-
'custom' => false,
|
391 |
-
'theme' => true,
|
392 |
-
),
|
393 |
-
);
|
394 |
-
}
|
395 |
-
|
396 |
-
FW_Cache::set($cache_key, $locations);
|
397 |
-
|
398 |
-
return $locations;
|
399 |
-
}
|
400 |
-
}
|
401 |
-
|
402 |
-
private function load_all_extensions()
|
403 |
-
{
|
404 |
-
foreach ($this->get_locations() as $location) {
|
405 |
-
self::load_extensions(array(
|
406 |
-
'path' => $location['path'],
|
407 |
-
'uri' => $location['uri'],
|
408 |
-
'customizations_locations' => $location['customizations_locations'],
|
409 |
-
));
|
410 |
-
}
|
411 |
-
}
|
412 |
-
|
413 |
-
/**
|
414 |
-
* Activate extensions from given tree point
|
415 |
-
*
|
416 |
-
* @param null|string $parent_extension_name
|
417 |
-
*/
|
418 |
-
private function activate_extensions($parent_extension_name = null)
|
419 |
-
{
|
420 |
-
if ($parent_extension_name === null) {
|
421 |
-
$all_tree =& self::$all_extensions_tree;
|
422 |
-
} else {
|
423 |
-
$all_tree =& self::$extension_to_all_tree[$parent_extension_name];
|
424 |
-
}
|
425 |
-
|
426 |
-
foreach ($all_tree as $extension_name => &$sub_extensions) {
|
427 |
-
if (fw()->extensions->get($extension_name)) {
|
428 |
-
// extension already active
|
429 |
-
continue;
|
430 |
-
}
|
431 |
-
|
432 |
-
$extension =& self::$all_extensions[$extension_name];
|
433 |
-
|
434 |
-
if ($extension->manifest->check_requirements()) {
|
435 |
-
if (!$this->_get_db_active_extensions($extension_name)) {
|
436 |
-
// extension is not set as active
|
437 |
-
} elseif (
|
438 |
-
$extension->get_parent()
|
439 |
-
&&
|
440 |
-
!$extension->get_parent()->_child_extension_is_valid($extension)
|
441 |
-
) {
|
442 |
-
// extension does not pass parent extension rules
|
443 |
-
if (is_admin()) {
|
444 |
-
// show warning only in admin side
|
445 |
-
FW_Flash_Messages::add(
|
446 |
-
'fw-invalid-extension',
|
447 |
-
sprintf(__('Extension %s is invalid.', 'fw'), $extension->get_name()),
|
448 |
-
'warning'
|
449 |
-
);
|
450 |
-
}
|
451 |
-
} else {
|
452 |
-
// all requirements met, activate extension
|
453 |
-
$this->activate_extension($extension_name);
|
454 |
-
}
|
455 |
-
} else {
|
456 |
-
// requirements not met, tell required extensions that this extension is waiting for them
|
457 |
-
|
458 |
-
foreach ($extension->manifest->get_required_extensions() as $required_extension_name => $requirements) {
|
459 |
-
if (!isset(self::$extensions_required_by_extensions[$required_extension_name])) {
|
460 |
-
self::$extensions_required_by_extensions[$required_extension_name] = array();
|
461 |
-
}
|
462 |
-
|
463 |
-
self::$extensions_required_by_extensions[$required_extension_name][] = $extension_name;
|
464 |
-
}
|
465 |
-
}
|
466 |
-
}
|
467 |
-
unset($sub_extensions);
|
468 |
-
}
|
469 |
-
|
470 |
-
/**
|
471 |
-
* @param string $extension_name
|
472 |
-
* @return bool
|
473 |
-
*/
|
474 |
-
private function activate_extension($extension_name)
|
475 |
-
{
|
476 |
-
if (fw()->extensions->get($extension_name)) {
|
477 |
-
// already active
|
478 |
-
return false;
|
479 |
-
}
|
480 |
-
|
481 |
-
if (!self::$all_extensions[$extension_name]->manifest->requirements_met()) {
|
482 |
-
trigger_error('Wrong '. __METHOD__ .' call', E_USER_WARNING);
|
483 |
-
return false;
|
484 |
-
}
|
485 |
-
|
486 |
-
// add to active extensions so inside includes/ and extension it will be accessible from fw()->extensions->get(...)
|
487 |
-
self::$active_extensions[$extension_name] =& self::$all_extensions[$extension_name];
|
488 |
-
|
489 |
-
$parent = self::$all_extensions[$extension_name]->get_parent();
|
490 |
-
|
491 |
-
if ($parent) {
|
492 |
-
self::$extension_to_active_tree[ $parent->get_name() ][$extension_name] = array();
|
493 |
-
self::$extension_to_active_tree[$extension_name] =& self::$extension_to_active_tree[ $parent->get_name() ][$extension_name];
|
494 |
-
} else {
|
495 |
-
self::$active_extensions_tree[$extension_name] = array();
|
496 |
-
self::$extension_to_active_tree[$extension_name] =& self::$active_extensions_tree[$extension_name];
|
497 |
-
}
|
498 |
-
|
499 |
-
self::include_extension_directory_all_locations($extension_name, '/includes');
|
500 |
-
self::include_extension_file_all_locations($extension_name, '/helpers.php');
|
501 |
-
self::include_extension_file_all_locations($extension_name, '/hooks.php');
|
502 |
-
|
503 |
-
if (self::$all_extensions[$extension_name]->_call_init(self::$access_key) !== false) {
|
504 |
-
$this->activate_extensions($extension_name);
|
505 |
-
}
|
506 |
-
|
507 |
-
// check if other extensions are waiting for this extension and try to activate them
|
508 |
-
if (isset(self::$extensions_required_by_extensions[$extension_name])) {
|
509 |
-
foreach (self::$extensions_required_by_extensions[$extension_name] as $waiting_extension_name) {
|
510 |
-
if (self::$all_extensions[$waiting_extension_name]->manifest->check_requirements()) {
|
511 |
-
$waiting_extension = self::$all_extensions[$waiting_extension_name];
|
512 |
-
|
513 |
-
if (!$this->_get_db_active_extensions($waiting_extension_name)) {
|
514 |
-
// extension is set as active
|
515 |
-
} elseif (
|
516 |
-
$waiting_extension->get_parent()
|
517 |
-
&&
|
518 |
-
!$waiting_extension->get_parent()->_child_extension_is_valid($waiting_extension)
|
519 |
-
) {
|
520 |
-
// extension does not pass parent extension rules
|
521 |
-
if (is_admin()) {
|
522 |
-
// show warning only in admin side
|
523 |
-
FW_Flash_Messages::add(
|
524 |
-
'fw-invalid-extension',
|
525 |
-
sprintf(__('Extension %s is invalid.', 'fw'), $waiting_extension_name),
|
526 |
-
'warning'
|
527 |
-
);
|
528 |
-
}
|
529 |
-
} else {
|
530 |
-
$this->activate_extension($waiting_extension_name);
|
531 |
-
}
|
532 |
-
}
|
533 |
-
}
|
534 |
-
|
535 |
-
unset(self::$extensions_required_by_extensions[$extension_name]);
|
536 |
-
}
|
537 |
-
|
538 |
-
return true;
|
539 |
-
}
|
540 |
-
|
541 |
-
private function add_actions()
|
542 |
-
{
|
543 |
-
add_action('init', array($this, '_action_init'));
|
544 |
-
add_action('wp_enqueue_scripts', array($this, '_action_enqueue_scripts'));
|
545 |
-
add_action('admin_enqueue_scripts', array($this, '_action_enqueue_scripts'));
|
546 |
-
}
|
547 |
-
|
548 |
-
/**
|
549 |
-
* Give extensions possibility to access their active_tree
|
550 |
-
* @internal
|
551 |
-
*
|
552 |
-
* @param FW_Access_Key $access_key
|
553 |
-
* @param $extension_name
|
554 |
-
*
|
555 |
-
* @return array
|
556 |
-
*/
|
557 |
-
public function _get_extension_tree(FW_Access_Key $access_key, $extension_name)
|
558 |
-
{
|
559 |
-
if ($access_key->get_key() !== 'extension') {
|
560 |
-
trigger_error('Call denied', E_USER_ERROR);
|
561 |
-
}
|
562 |
-
|
563 |
-
return self::$extension_to_active_tree[$extension_name];
|
564 |
-
}
|
565 |
-
|
566 |
-
/**
|
567 |
-
* @internal
|
568 |
-
*/
|
569 |
-
public function _init()
|
570 |
-
{
|
571 |
-
self::$access_key = new FW_Access_Key('fw_extensions');
|
572 |
-
|
573 |
-
/**
|
574 |
-
* Extensions are about to activate.
|
575 |
-
* You can add subclasses to FW_Extension at this point.
|
576 |
-
*/
|
577 |
-
do_action('fw_extensions_before_init');
|
578 |
-
|
579 |
-
$this->load_all_extensions();
|
580 |
-
$this->add_actions();
|
581 |
-
}
|
582 |
-
|
583 |
-
/**
|
584 |
-
* @internal
|
585 |
-
*/
|
586 |
-
public function _after_components_init()
|
587 |
-
{
|
588 |
-
$this->activate_extensions();
|
589 |
-
|
590 |
-
/**
|
591 |
-
* Extensions are activated
|
592 |
-
* Now $this->get_children() inside extensions is available
|
593 |
-
*/
|
594 |
-
do_action('fw_extensions_init');
|
595 |
-
}
|
596 |
-
|
597 |
-
public function _action_init()
|
598 |
-
{
|
599 |
-
foreach (self::$active_extensions as &$extension) {
|
600 |
-
/** register posts and taxonomies */
|
601 |
-
self::include_extension_file_all_locations($extension, '/posts.php');
|
602 |
-
}
|
603 |
-
}
|
604 |
-
|
605 |
-
public function _action_enqueue_scripts()
|
606 |
-
{
|
607 |
-
foreach (self::$active_extensions as &$extension) {
|
608 |
-
/** js and css */
|
609 |
-
self::include_extension_file_all_locations($extension, '/static.php', true, true);
|
610 |
-
}
|
611 |
-
}
|
612 |
-
|
613 |
-
/**
|
614 |
-
* @param string $extension_name returned by FW_Extension::get_name()
|
615 |
-
* @return FW_Extension|null
|
616 |
-
*/
|
617 |
-
public function get($extension_name)
|
618 |
-
{
|
619 |
-
if (isset(self::$active_extensions[$extension_name])) {
|
620 |
-
return self::$active_extensions[$extension_name];
|
621 |
-
} else {
|
622 |
-
return null;
|
623 |
-
}
|
624 |
-
}
|
625 |
-
|
626 |
-
/**
|
627 |
-
* Get all active extensions
|
628 |
-
* @return FW_Extension[]
|
629 |
-
*/
|
630 |
-
public function get_all()
|
631 |
-
{
|
632 |
-
return self::$active_extensions;
|
633 |
-
}
|
634 |
-
|
635 |
-
/**
|
636 |
-
* Get extensions tree (how they are arranged in directories)
|
637 |
-
* @return array
|
638 |
-
*/
|
639 |
-
public function get_tree()
|
640 |
-
{
|
641 |
-
return self::$active_extensions_tree;
|
642 |
-
}
|
643 |
-
|
644 |
-
/**
|
645 |
-
* @return false
|
646 |
-
* @deprecated Use $extension->locate_path()
|
647 |
-
*/
|
648 |
-
public function locate_path()
|
649 |
-
{
|
650 |
-
return false;
|
651 |
-
}
|
652 |
-
|
653 |
-
/**
|
654 |
-
* @return false
|
655 |
-
* @deprecated Use $extension->locate_URI()
|
656 |
-
*/
|
657 |
-
public function locate_path_URI()
|
658 |
-
{
|
659 |
-
return false;
|
660 |
-
}
|
661 |
-
}
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Extensions component
|
5 |
+
*/
|
6 |
+
final class _FW_Component_Extensions
|
7 |
+
{
|
8 |
+
/**
|
9 |
+
* All existing extensions
|
10 |
+
* @var FW_Extension[] { 'extension_name' => instance }
|
11 |
+
*/
|
12 |
+
private static $all_extensions = array();
|
13 |
+
|
14 |
+
/**
|
15 |
+
* All existing extensions names arranged in hierarchical tree like they are in directories
|
16 |
+
* @var array
|
17 |
+
*/
|
18 |
+
private static $all_extensions_tree = array();
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Active extensions
|
22 |
+
*
|
23 |
+
* On every extension activation, it will be pushed at the end of this array.
|
24 |
+
* The extensions order is important when including files.
|
25 |
+
* If extension A requires extension B, extension B is activated before extension A,
|
26 |
+
* and all files of the extension B (hooks.php, static.php, etc.) must be included before extension A
|
27 |
+
* For e.g. extension A may have in static.php:
|
28 |
+
* wp_enqueue_script( 'ext-A-script', 'script.js', array( 'ext-B-script' ) );
|
29 |
+
* so 'ext-B-script' must be registered before 'ext-A-script'
|
30 |
+
*
|
31 |
+
* @var FW_Extension[] { 'extension_name' => instance }
|
32 |
+
*/
|
33 |
+
private static $active_extensions = array();
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Active extensions names arranged in hierarchical tree like they are in directories
|
37 |
+
* @var array
|
38 |
+
*/
|
39 |
+
private static $active_extensions_tree = array();
|
40 |
+
|
41 |
+
/**
|
42 |
+
* @var array { 'extension_name' => array('required_by', 'required_by') }
|
43 |
+
*/
|
44 |
+
private static $extensions_required_by_extensions = array();
|
45 |
+
|
46 |
+
/**
|
47 |
+
* @var array { 'extension_name' => &array() }
|
48 |
+
*/
|
49 |
+
private static $extension_to_all_tree = array();
|
50 |
+
|
51 |
+
/**
|
52 |
+
* @var array { 'extension_name' => &array() }
|
53 |
+
*/
|
54 |
+
private static $extension_to_active_tree = array();
|
55 |
+
|
56 |
+
/**
|
57 |
+
* @var FW_Access_Key
|
58 |
+
*/
|
59 |
+
private static $access_key;
|
60 |
+
|
61 |
+
/**
|
62 |
+
* @var null|_FW_Extensions_Manager
|
63 |
+
*/
|
64 |
+
public $manager;
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Option name that stores the active extensions array
|
68 |
+
* @internal
|
69 |
+
*/
|
70 |
+
public function _get_active_extensions_db_option_name()
|
71 |
+
{
|
72 |
+
return 'fw_active_extensions';
|
73 |
+
}
|
74 |
+
|
75 |
+
/**
|
76 |
+
* @param null|string $extension_name Check if an extension is set as active in database
|
77 |
+
* @internal
|
78 |
+
* @return array|bool
|
79 |
+
*/
|
80 |
+
public function _get_db_active_extensions($extension_name = null)
|
81 |
+
{
|
82 |
+
$extensions = get_option($this->_get_active_extensions_db_option_name(), array());
|
83 |
+
|
84 |
+
if ($extension_name) {
|
85 |
+
return isset($extensions[$extension_name]);
|
86 |
+
} else {
|
87 |
+
return $extensions;
|
88 |
+
}
|
89 |
+
}
|
90 |
+
|
91 |
+
public function __construct()
|
92 |
+
{
|
93 |
+
require dirname(__FILE__) .'/extensions/class-fw-extension-default.php';
|
94 |
+
|
95 |
+
if (
|
96 |
+
/**
|
97 |
+
* Do not load in frontend because it has no functionality that can be useful in frontend
|
98 |
+
*/
|
99 |
+
is_admin()
|
100 |
+
||
|
101 |
+
/**
|
102 |
+
* While in cron request (on auto-update), is_admin() is false
|
103 |
+
* but we need the actions that moves extensions to a tmp dir then back, on plugin update
|
104 |
+
* todo: maybe move those actions here in this class?
|
105 |
+
*/
|
106 |
+
defined( 'DOING_CRON' )
|
107 |
+
) {
|
108 |
+
require dirname(__FILE__) .'/extensions/manager/class--fw-extensions-manager.php';
|
109 |
+
|
110 |
+
$this->manager = new _FW_Extensions_Manager();
|
111 |
+
}
|
112 |
+
}
|
113 |
+
|
114 |
+
/**
|
115 |
+
* Load extension from directory
|
116 |
+
*
|
117 |
+
* @param array $data
|
118 |
+
*/
|
119 |
+
private static function load_extensions($data)
|
120 |
+
{
|
121 |
+
if (false) {
|
122 |
+
$data = array(
|
123 |
+
'rel_path' => '/extension',
|
124 |
+
'path' => '/path/to/extension',
|
125 |
+
'uri' => 'https://uri.to/extension',
|
126 |
+
'customizations_locations' => array(
|
127 |
+
'/path/to/parent/theme/customizations/extensions/ext/rel/path' => 'https://uri.to/customization/path',
|
128 |
+
'/path/to/child/theme/customizations/extensions/ext/rel/path' => 'https://uri.to/customization/path',
|
129 |
+
),
|
130 |
+
|
131 |
+
'all_extensions_tree' => array(),
|
132 |
+
'all_extensions' => array(),
|
133 |
+
'current_depth' => 1,
|
134 |
+
'parent' => '&$parent_extension_instance',
|
135 |
+
);
|
136 |
+
}
|
137 |
+
|
138 |
+
/**
|
139 |
+
* Do not check all keys
|
140 |
+
* if one not set, then sure others are not set (this is a private method)
|
141 |
+
*/
|
142 |
+
if (!isset($data['all_extensions_tree'])) {
|
143 |
+
$data['all_extensions_tree'] = &self::$all_extensions_tree;
|
144 |
+
$data['all_extensions'] = &self::$all_extensions;
|
145 |
+
$data['current_depth'] = 1;
|
146 |
+
$data['rel_path'] = '';
|
147 |
+
$data['parent'] = null;
|
148 |
+
}
|
149 |
+
|
150 |
+
$dirs = glob($data['path'] .'/*', GLOB_ONLYDIR);
|
151 |
+
|
152 |
+
if (empty($dirs)) {
|
153 |
+
return;
|
154 |
+
}
|
155 |
+
|
156 |
+
if ($data['current_depth'] > 1) {
|
157 |
+
$customizations_locations = array();
|
158 |
+
|
159 |
+
foreach ($data['customizations_locations'] as $customization_path => $customization_uri) {
|
160 |
+
$customizations_locations[ $customization_path .'/extensions' ] = $customization_uri .'/extensions';
|
161 |
+
}
|
162 |
+
|
163 |
+
$data['customizations_locations'] = $customizations_locations;
|
164 |
+
}
|
165 |
+
|
166 |
+
foreach ($dirs as $extension_dir) {
|
167 |
+
$extension_name = basename($extension_dir);
|
168 |
+
|
169 |
+
{
|
170 |
+
$customizations_locations = array();
|
171 |
+
|
172 |
+
foreach ($data['customizations_locations'] as $customization_path => $customization_uri) {
|
173 |
+
$customizations_locations[ $customization_path .'/'. $extension_name ] = $customization_uri .'/'. $extension_name;
|
174 |
+
}
|
175 |
+
}
|
176 |
+
|
177 |
+
if (isset($data['all_extensions'][$extension_name])) {
|
178 |
+
if ($data['all_extensions'][$extension_name]->get_parent() !== $data['parent']) {
|
179 |
+
// extension with the same name exists in another tree
|
180 |
+
trigger_error(
|
181 |
+
'Extension "'. $extension_name .'" is already defined '.
|
182 |
+
'in "'. $data['all_extensions'][$extension_name]->get_declared_path() .'" '.
|
183 |
+
'found again in "'. $extension_dir .'"',
|
184 |
+
E_USER_ERROR
|
185 |
+
);
|
186 |
+
}
|
187 |
+
|
188 |
+
// this is a directory with customizations for an extension
|
189 |
+
|
190 |
+
self::load_extensions(array(
|
191 |
+
'rel_path' => $data['rel_path'] .'/'. $extension_name .'/extensions',
|
192 |
+
'path' => $data['path'] .'/'. $extension_name .'/extensions',
|
193 |
+
'uri' => $data['uri'] .'/'. $extension_name .'/extensions',
|
194 |
+
'customizations_locations' => $customizations_locations,
|
195 |
+
|
196 |
+
'all_extensions_tree' => &$data['all_extensions_tree'][$extension_name],
|
197 |
+
'all_extensions' => &$data['all_extensions'],
|
198 |
+
'current_depth' => $data['current_depth'] + 1,
|
199 |
+
'parent' => &$data['all_extensions'][$extension_name],
|
200 |
+
));
|
201 |
+
} else {
|
202 |
+
$class_file_name = 'class-fw-extension-'. $extension_name .'.php';
|
203 |
+
|
204 |
+
if (file_exists($extension_dir .'/manifest.php')) {
|
205 |
+
$data['all_extensions_tree'][$extension_name] = array();
|
206 |
+
|
207 |
+
self::$extension_to_all_tree[$extension_name] = &$data['all_extensions_tree'][$extension_name];
|
208 |
+
|
209 |
+
if (fw_include_file_isolated($extension_dir .'/'. $class_file_name)) {
|
210 |
+
$class_name = 'FW_Extension_'. fw_dirname_to_classname($extension_name);
|
211 |
+
} else {
|
212 |
+
$parent_class_name = get_class($data['parent']);
|
213 |
+
// check if parent extension has been defined custom Default class for its child extensions
|
214 |
+
if (class_exists($parent_class_name .'_Default')) {
|
215 |
+
$class_name = $parent_class_name .'_Default';
|
216 |
+
} else {
|
217 |
+
$class_name = 'FW_Extension_Default';
|
218 |
+
}
|
219 |
+
}
|
220 |
+
|
221 |
+
if (!is_subclass_of($class_name, 'FW_Extension')) {
|
222 |
+
trigger_error('Extension "'. $extension_name .'" must extend FW_Extension class', E_USER_ERROR);
|
223 |
+
}
|
224 |
+
|
225 |
+
$data['all_extensions'][$extension_name] = new $class_name(array(
|
226 |
+
'rel_path' => $data['rel_path'] .'/'. $extension_name,
|
227 |
+
'path' => $data['path'] .'/'. $extension_name,
|
228 |
+
'uri' => $data['uri'] .'/'. $extension_name,
|
229 |
+
'parent' => $data['parent'],
|
230 |
+
'depth' => $data['current_depth'],
|
231 |
+
'customizations_locations' => $customizations_locations,
|
232 |
+
));
|
233 |
+
} else {
|
234 |
+
/**
|
235 |
+
* The manifest file does not exist, do not load this extension.
|
236 |
+
* Maybe it's a directory with configurations for a not existing extension.
|
237 |
+
*/
|
238 |
+
continue;
|
239 |
+
}
|
240 |
+
|
241 |
+
self::load_extensions(array(
|
242 |
+
'rel_path' => $data['all_extensions'][$extension_name]->get_rel_path() .'/extensions',
|
243 |
+
'path' => $data['all_extensions'][$extension_name]->get_path() .'/extensions',
|
244 |
+
'uri' => $data['all_extensions'][$extension_name]->get_uri() .'/extensions',
|
245 |
+
'customizations_locations' => $customizations_locations,
|
246 |
+
|
247 |
+
'parent' => &$data['all_extensions'][$extension_name],
|
248 |
+
'all_extensions_tree' => &$data['all_extensions_tree'][$extension_name],
|
249 |
+
'all_extensions' => &$data['all_extensions'],
|
250 |
+
'current_depth' => $data['current_depth'] + 1,
|
251 |
+
));
|
252 |
+
}
|
253 |
+
}
|
254 |
+
}
|
255 |
+
|
256 |
+
/**
|
257 |
+
* Include file from all extension's locations: framework, parent, child
|
258 |
+
* @param string|FW_Extension $extension
|
259 |
+
* @param string $file_rel_path
|
260 |
+
* @param bool $themeFirst
|
261 |
+
* false - [framework, parent, child]
|
262 |
+
* true - [child, parent, framework]
|
263 |
+
* @param bool $onlyFirstFound
|
264 |
+
*/
|
265 |
+
private static function include_extension_file_all_locations($extension, $file_rel_path, $themeFirst = false, $onlyFirstFound = false)
|
266 |
+
{
|
267 |
+
if (is_string($extension)) {
|
268 |
+
$extension = fw()->extensions->get($extension);
|
269 |
+
}
|
270 |
+
|
271 |
+
$paths = $extension->get_customizations_locations();
|
272 |
+
$paths[$extension->get_path()] = $extension->get_uri();
|
273 |
+
|
274 |
+
if (!$themeFirst) {
|
275 |
+
$paths = array_reverse($paths);
|
276 |
+
}
|
277 |
+
|
278 |
+
foreach ($paths as $path => $uri) {
|
279 |
+
if (fw_include_file_isolated($path . $file_rel_path)) {
|
280 |
+
if ($onlyFirstFound) {
|
281 |
+
return;
|
282 |
+
}
|
283 |
+
}
|
284 |
+
}
|
285 |
+
}
|
286 |
+
|
287 |
+
/**
|
288 |
+
* Include all files from directory, from all extension's locations: framework, child, parent
|
289 |
+
* @param string|FW_Extension $extension
|
290 |
+
* @param string $dir_rel_path
|
291 |
+
* @param bool $themeFirst
|
292 |
+
* false - [framework, parent, child]
|
293 |
+
* true - [child, parent, framework]
|
294 |
+
*/
|
295 |
+
private static function include_extension_directory_all_locations($extension, $dir_rel_path, $themeFirst = false)
|
296 |
+
{
|
297 |
+
if (is_string($extension)) {
|
298 |
+
$extension = fw()->extensions->get($extension);
|
299 |
+
}
|
300 |
+
|
301 |
+
$paths = $extension->get_customizations_locations();
|
302 |
+
$paths[$extension->get_path()] = $extension->get_uri();
|
303 |
+
|
304 |
+
if (!$themeFirst) {
|
305 |
+
$paths = array_reverse($paths);
|
306 |
+
}
|
307 |
+
|
308 |
+
foreach ($paths as $path => $uri) {
|
309 |
+
if ($files = glob($path . $dir_rel_path .'/*.php')) {
|
310 |
+
foreach ($files as $dir_file_path) {
|
311 |
+
fw_include_file_isolated($dir_file_path);
|
312 |
+
}
|
313 |
+
}
|
314 |
+
}
|
315 |
+
}
|
316 |
+
|
317 |
+
public function get_locations()
|
318 |
+
{
|
319 |
+
$cache_key = 'fw_extensions_locations';
|
320 |
+
|
321 |
+
try {
|
322 |
+
return FW_Cache::get($cache_key);
|
323 |
+
} catch (FW_Cache_Not_Found_Exception $e) {
|
324 |
+
/**
|
325 |
+
* { '/hello/world/extensions' => 'https://hello.com/world/extensions' }
|
326 |
+
*/
|
327 |
+
$custom_locations = apply_filters('fw_extensions_locations', array());
|
328 |
+
|
329 |
+
{
|
330 |
+
$customizations_locations = array();
|
331 |
+
|
332 |
+
if (is_child_theme()) {
|
333 |
+
$customizations_locations[fw_get_stylesheet_customizations_directory('/extensions')]
|
334 |
+
= fw_get_stylesheet_customizations_directory_uri('/extensions');
|
335 |
+
}
|
336 |
+
|
337 |
+
$customizations_locations[fw_get_template_customizations_directory('/extensions')]
|
338 |
+
= fw_get_template_customizations_directory_uri('/extensions');
|
339 |
+
|
340 |
+
$customizations_locations += $custom_locations;
|
341 |
+
}
|
342 |
+
|
343 |
+
$locations = array();
|
344 |
+
|
345 |
+
$locations[ fw_get_framework_directory('/extensions') ] = array(
|
346 |
+
'path' => fw_get_framework_directory('/extensions'),
|
347 |
+
'uri' => fw_get_framework_directory_uri('/extensions'),
|
348 |
+
'customizations_locations' => $customizations_locations,
|
349 |
+
'is' => array(
|
350 |
+
'framework' => true,
|
351 |
+
'custom' => false,
|
352 |
+
'theme' => false,
|
353 |
+
),
|
354 |
+
);
|
355 |
+
|
356 |
+
foreach ($custom_locations as $path => $uri) {
|
357 |
+
unset($customizations_locations[$path]);
|
358 |
+
$locations[ $path ] = array(
|
359 |
+
'path' => $path,
|
360 |
+
'uri' => $uri,
|
361 |
+
'customizations_locations' => $customizations_locations,
|
362 |
+
'is' => array(
|
363 |
+
'framework' => false,
|
364 |
+
'custom' => true,
|
365 |
+
'theme' => false,
|
366 |
+
),
|
367 |
+
);
|
368 |
+
}
|
369 |
+
|
370 |
+
array_pop($customizations_locations);
|
371 |
+
$locations[ fw_get_template_customizations_directory('/extensions') ] = array(
|
372 |
+
'path' => fw_get_template_customizations_directory('/extensions'),
|
373 |
+
'uri' => fw_get_template_customizations_directory_uri('/extensions'),
|
374 |
+
'customizations_locations' => $customizations_locations,
|
375 |
+
'is' => array(
|
376 |
+
'framework' => false,
|
377 |
+
'custom' => false,
|
378 |
+
'theme' => true,
|
379 |
+
),
|
380 |
+
);
|
381 |
+
|
382 |
+
if (is_child_theme()) {
|
383 |
+
array_pop($customizations_locations);
|
384 |
+
$locations[ fw_get_stylesheet_customizations_directory('/extensions') ] = array(
|
385 |
+
'path' => fw_get_stylesheet_customizations_directory('/extensions'),
|
386 |
+
'uri' => fw_get_stylesheet_customizations_directory_uri('/extensions'),
|
387 |
+
'customizations_locations' => $customizations_locations,
|
388 |
+
'is' => array(
|
389 |
+
'framework' => false,
|
390 |
+
'custom' => false,
|
391 |
+
'theme' => true,
|
392 |
+
),
|
393 |
+
);
|
394 |
+
}
|
395 |
+
|
396 |
+
FW_Cache::set($cache_key, $locations);
|
397 |
+
|
398 |
+
return $locations;
|
399 |
+
}
|
400 |
+
}
|
401 |
+
|
402 |
+
private function load_all_extensions()
|
403 |
+
{
|
404 |
+
foreach ($this->get_locations() as $location) {
|
405 |
+
self::load_extensions(array(
|
406 |
+
'path' => $location['path'],
|
407 |
+
'uri' => $location['uri'],
|
408 |
+
'customizations_locations' => $location['customizations_locations'],
|
409 |
+
));
|
410 |
+
}
|
411 |
+
}
|
412 |
+
|
413 |
+
/**
|
414 |
+
* Activate extensions from given tree point
|
415 |
+
*
|
416 |
+
* @param null|string $parent_extension_name
|
417 |
+
*/
|
418 |
+
private function activate_extensions($parent_extension_name = null)
|
419 |
+
{
|
420 |
+
if ($parent_extension_name === null) {
|
421 |
+
$all_tree =& self::$all_extensions_tree;
|
422 |
+
} else {
|
423 |
+
$all_tree =& self::$extension_to_all_tree[$parent_extension_name];
|
424 |
+
}
|
425 |
+
|
426 |
+
foreach ($all_tree as $extension_name => &$sub_extensions) {
|
427 |
+
if (fw()->extensions->get($extension_name)) {
|
428 |
+
// extension already active
|
429 |
+
continue;
|
430 |
+
}
|
431 |
+
|
432 |
+
$extension =& self::$all_extensions[$extension_name];
|
433 |
+
|
434 |
+
if ($extension->manifest->check_requirements()) {
|
435 |
+
if (!$this->_get_db_active_extensions($extension_name)) {
|
436 |
+
// extension is not set as active
|
437 |
+
} elseif (
|
438 |
+
$extension->get_parent()
|
439 |
+
&&
|
440 |
+
!$extension->get_parent()->_child_extension_is_valid($extension)
|
441 |
+
) {
|
442 |
+
// extension does not pass parent extension rules
|
443 |
+
if (is_admin()) {
|
444 |
+
// show warning only in admin side
|
445 |
+
FW_Flash_Messages::add(
|
446 |
+
'fw-invalid-extension',
|
447 |
+
sprintf(__('Extension %s is invalid.', 'fw'), $extension->get_name()),
|
448 |
+
'warning'
|
449 |
+
);
|
450 |
+
}
|
451 |
+
} else {
|
452 |
+
// all requirements met, activate extension
|
453 |
+
$this->activate_extension($extension_name);
|
454 |
+
}
|
455 |
+
} else {
|
456 |
+
// requirements not met, tell required extensions that this extension is waiting for them
|
457 |
+
|
458 |
+
foreach ($extension->manifest->get_required_extensions() as $required_extension_name => $requirements) {
|
459 |
+
if (!isset(self::$extensions_required_by_extensions[$required_extension_name])) {
|
460 |
+
self::$extensions_required_by_extensions[$required_extension_name] = array();
|
461 |
+
}
|
462 |
+
|
463 |
+
self::$extensions_required_by_extensions[$required_extension_name][] = $extension_name;
|
464 |
+
}
|
465 |
+
}
|
466 |
+
}
|
467 |
+
unset($sub_extensions);
|
468 |
+
}
|
469 |
+
|
470 |
+
/**
|
471 |
+
* @param string $extension_name
|
472 |
+
* @return bool
|
473 |
+
*/
|
474 |
+
private function activate_extension($extension_name)
|
475 |
+
{
|
476 |
+
if (fw()->extensions->get($extension_name)) {
|
477 |
+
// already active
|
478 |
+
return false;
|
479 |
+
}
|
480 |
+
|
481 |
+
if (!self::$all_extensions[$extension_name]->manifest->requirements_met()) {
|
482 |
+
trigger_error('Wrong '. __METHOD__ .' call', E_USER_WARNING);
|
483 |
+
return false;
|
484 |
+
}
|
485 |
+
|
486 |
+
// add to active extensions so inside includes/ and extension it will be accessible from fw()->extensions->get(...)
|
487 |
+
self::$active_extensions[$extension_name] =& self::$all_extensions[$extension_name];
|
488 |
+
|
489 |
+
$parent = self::$all_extensions[$extension_name]->get_parent();
|
490 |
+
|
491 |
+
if ($parent) {
|
492 |
+
self::$extension_to_active_tree[ $parent->get_name() ][$extension_name] = array();
|
493 |
+
self::$extension_to_active_tree[$extension_name] =& self::$extension_to_active_tree[ $parent->get_name() ][$extension_name];
|
494 |
+
} else {
|
495 |
+
self::$active_extensions_tree[$extension_name] = array();
|
496 |
+
self::$extension_to_active_tree[$extension_name] =& self::$active_extensions_tree[$extension_name];
|
497 |
+
}
|
498 |
+
|
499 |
+
self::include_extension_directory_all_locations($extension_name, '/includes');
|
500 |
+
self::include_extension_file_all_locations($extension_name, '/helpers.php');
|
501 |
+
self::include_extension_file_all_locations($extension_name, '/hooks.php');
|
502 |
+
|
503 |
+
if (self::$all_extensions[$extension_name]->_call_init(self::$access_key) !== false) {
|
504 |
+
$this->activate_extensions($extension_name);
|
505 |
+
}
|
506 |
+
|
507 |
+
// check if other extensions are waiting for this extension and try to activate them
|
508 |
+
if (isset(self::$extensions_required_by_extensions[$extension_name])) {
|
509 |
+
foreach (self::$extensions_required_by_extensions[$extension_name] as $waiting_extension_name) {
|
510 |
+
if (self::$all_extensions[$waiting_extension_name]->manifest->check_requirements()) {
|
511 |
+
$waiting_extension = self::$all_extensions[$waiting_extension_name];
|
512 |
+
|
513 |
+
if (!$this->_get_db_active_extensions($waiting_extension_name)) {
|
514 |
+
// extension is set as active
|
515 |
+
} elseif (
|
516 |
+
$waiting_extension->get_parent()
|
517 |
+
&&
|
518 |
+
!$waiting_extension->get_parent()->_child_extension_is_valid($waiting_extension)
|
519 |
+
) {
|
520 |
+
// extension does not pass parent extension rules
|
521 |
+
if (is_admin()) {
|
522 |
+
// show warning only in admin side
|
523 |
+
FW_Flash_Messages::add(
|
524 |
+
'fw-invalid-extension',
|
525 |
+
sprintf(__('Extension %s is invalid.', 'fw'), $waiting_extension_name),
|
526 |
+
'warning'
|
527 |
+
);
|
528 |
+
}
|
529 |
+
} else {
|
530 |
+
$this->activate_extension($waiting_extension_name);
|
531 |
+
}
|
532 |
+
}
|
533 |
+
}
|
534 |
+
|
535 |
+
unset(self::$extensions_required_by_extensions[$extension_name]);
|
536 |
+
}
|
537 |
+
|
538 |
+
return true;
|
539 |
+
}
|
540 |
+
|
541 |
+
private function add_actions()
|
542 |
+
{
|
543 |
+
add_action('init', array($this, '_action_init'));
|
544 |
+
add_action('wp_enqueue_scripts', array($this, '_action_enqueue_scripts'));
|
545 |
+
add_action('admin_enqueue_scripts', array($this, '_action_enqueue_scripts'));
|
546 |
+
}
|
547 |
+
|
548 |
+
/**
|
549 |
+
* Give extensions possibility to access their active_tree
|
550 |
+
* @internal
|
551 |
+
*
|
552 |
+
* @param FW_Access_Key $access_key
|
553 |
+
* @param $extension_name
|
554 |
+
*
|
555 |
+
* @return array
|
556 |
+
*/
|
557 |
+
public function _get_extension_tree(FW_Access_Key $access_key, $extension_name)
|
558 |
+
{
|
559 |
+
if ($access_key->get_key() !== 'extension') {
|
560 |
+
trigger_error('Call denied', E_USER_ERROR);
|
561 |
+
}
|
562 |
+
|
563 |
+
return self::$extension_to_active_tree[$extension_name];
|
564 |
+
}
|
565 |
+
|
566 |
+
/**
|
567 |
+
* @internal
|
568 |
+
*/
|
569 |
+
public function _init()
|
570 |
+
{
|
571 |
+
self::$access_key = new FW_Access_Key('fw_extensions');
|
572 |
+
|
573 |
+
/**
|
574 |
+
* Extensions are about to activate.
|
575 |
+
* You can add subclasses to FW_Extension at this point.
|
576 |
+
*/
|
577 |
+
do_action('fw_extensions_before_init');
|
578 |
+
|
579 |
+
$this->load_all_extensions();
|
580 |
+
$this->add_actions();
|
581 |
+
}
|
582 |
+
|
583 |
+
/**
|
584 |
+
* @internal
|
585 |
+
*/
|
586 |
+
public function _after_components_init()
|
587 |
+
{
|
588 |
+
$this->activate_extensions();
|
589 |
+
|
590 |
+
/**
|
591 |
+
* Extensions are activated
|
592 |
+
* Now $this->get_children() inside extensions is available
|
593 |
+
*/
|
594 |
+
do_action('fw_extensions_init');
|
595 |
+
}
|
596 |
+
|
597 |
+
public function _action_init()
|
598 |
+
{
|
599 |
+
foreach (self::$active_extensions as &$extension) {
|
600 |
+
/** register posts and taxonomies */
|
601 |
+
self::include_extension_file_all_locations($extension, '/posts.php');
|
602 |
+
}
|
603 |
+
}
|
604 |
+
|
605 |
+
public function _action_enqueue_scripts()
|
606 |
+
{
|
607 |
+
foreach (self::$active_extensions as &$extension) {
|
608 |
+
/** js and css */
|
609 |
+
self::include_extension_file_all_locations($extension, '/static.php', true, true);
|
610 |
+
}
|
611 |
+
}
|
612 |
+
|
613 |
+
/**
|
614 |
+
* @param string $extension_name returned by FW_Extension::get_name()
|
615 |
+
* @return FW_Extension|null
|
616 |
+
*/
|
617 |
+
public function get($extension_name)
|
618 |
+
{
|
619 |
+
if (isset(self::$active_extensions[$extension_name])) {
|
620 |
+
return self::$active_extensions[$extension_name];
|
621 |
+
} else {
|
622 |
+
return null;
|
623 |
+
}
|
624 |
+
}
|
625 |
+
|
626 |
+
/**
|
627 |
+
* Get all active extensions
|
628 |
+
* @return FW_Extension[]
|
629 |
+
*/
|
630 |
+
public function get_all()
|
631 |
+
{
|
632 |
+
return self::$active_extensions;
|
633 |
+
}
|
634 |
+
|
635 |
+
/**
|
636 |
+
* Get extensions tree (how they are arranged in directories)
|
637 |
+
* @return array
|
638 |
+
*/
|
639 |
+
public function get_tree()
|
640 |
+
{
|
641 |
+
return self::$active_extensions_tree;
|
642 |
+
}
|
643 |
+
|
644 |
+
/**
|
645 |
+
* @return false
|
646 |
+
* @deprecated Use $extension->locate_path()
|
647 |
+
*/
|
648 |
+
public function locate_path()
|
649 |
+
{
|
650 |
+
return false;
|
651 |
+
}
|
652 |
+
|
653 |
+
/**
|
654 |
+
* @return false
|
655 |
+
* @deprecated Use $extension->locate_URI()
|
656 |
+
*/
|
657 |
+
public function locate_path_URI()
|
658 |
+
{
|
659 |
+
return false;
|
660 |
+
}
|
661 |
+
}
|
framework/core/components/extensions/class-fw-extension-default.php
CHANGED
@@ -1,11 +1,11 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Instances of this class will be created for extensions without class
|
5 |
-
*/
|
6 |
-
class FW_Extension_Default extends FW_Extension
|
7 |
-
{
|
8 |
-
protected function _init()
|
9 |
-
{
|
10 |
-
}
|
11 |
-
}
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Instances of this class will be created for extensions without class
|
5 |
+
*/
|
6 |
+
class FW_Extension_Default extends FW_Extension
|
7 |
+
{
|
8 |
+
protected function _init()
|
9 |
+
{
|
10 |
+
}
|
11 |
+
}
|
framework/core/components/extensions/manager/available-extensions.php
CHANGED
@@ -1,273 +1,273 @@
|
|
1 |
-
<?php if ( ! defined( 'FW' ) ) {
|
2 |
-
die( 'Forbidden' );
|
3 |
-
}
|
4 |
-
|
5 |
-
$thumbnails_uri = fw_get_framework_directory_uri( '/core/components/extensions/manager/static/img/thumbnails' );
|
6 |
-
$github_account = 'ThemeFuse';
|
7 |
-
|
8 |
-
$extensions = array(
|
9 |
-
'slider' => array(
|
10 |
-
'display' => true,
|
11 |
-
'parent' => 'media',
|
12 |
-
'name' => __( 'Sliders', 'fw' ),
|
13 |
-
'description' => __( 'Adds a sliders module to your website from where you\'ll be able to create different built in jQuery sliders for your homepage and rest of the pages.', 'fw' ),
|
14 |
-
'thumbnail' => $thumbnails_uri . '/sliders.jpg',
|
15 |
-
'download' => array(
|
16 |
-
'github' => array(
|
17 |
-
'user_repo' => $github_account . '/Unyson-Sliders-Extension',
|
18 |
-
),
|
19 |
-
),
|
20 |
-
),
|
21 |
-
'media' => array(
|
22 |
-
'display' => false,
|
23 |
-
'parent' => null,
|
24 |
-
'name' => __( 'Media', 'fw' ),
|
25 |
-
'description' => '',
|
26 |
-
'thumbnail' => 'about:blank',
|
27 |
-
'download' => array(
|
28 |
-
'github' => array(
|
29 |
-
'user_repo' => $github_account . '/Unyson-Empty-Extension',
|
30 |
-
),
|
31 |
-
),
|
32 |
-
),
|
33 |
-
'population-method' => array(
|
34 |
-
'display' => false,
|
35 |
-
'parent' => 'media',
|
36 |
-
'name' => __( 'Population method', 'fw' ),
|
37 |
-
'description' => '',
|
38 |
-
'thumbnail' => 'about:blank',
|
39 |
-
'download' => array(
|
40 |
-
'github' => array(
|
41 |
-
'user_repo' => $github_account . '/Unyson-PopulationMethods-Extension',
|
42 |
-
),
|
43 |
-
),
|
44 |
-
),
|
45 |
-
'styling' => array(
|
46 |
-
'display' => true,
|
47 |
-
'parent' => null,
|
48 |
-
'name' => __( 'Styling', 'fw' ),
|
49 |
-
'description' => __( 'This extension lets you control the website visual style. Starting from predefined styles to changing specific fonts and colors across the website.', 'fw' ),
|
50 |
-
'thumbnail' => $thumbnails_uri . '/styling.jpg',
|
51 |
-
'download' => array(
|
52 |
-
'github' => array(
|
53 |
-
'user_repo' => $github_account . '/Unyson-Styling-Extension',
|
54 |
-
),
|
55 |
-
),
|
56 |
-
),
|
57 |
-
'megamenu' => array(
|
58 |
-
'display' => true,
|
59 |
-
'parent' => null,
|
60 |
-
'name' => __( 'Mega Menu', 'fw' ),
|
61 |
-
'description' => __( 'The Mega Menu extension adds a user-friendly drop down menu that will let you easily create highly customized menu configurations.', 'fw' ),
|
62 |
-
'thumbnail' => $thumbnails_uri . '/mega-menu.jpg',
|
63 |
-
'download' => array(
|
64 |
-
'github' => array(
|
65 |
-
'user_repo' => $github_account . '/Unyson-MegaMenu-Extension',
|
66 |
-
),
|
67 |
-
),
|
68 |
-
),
|
69 |
-
'portfolio' => array(
|
70 |
-
'display' => true,
|
71 |
-
'parent' => null,
|
72 |
-
'name' => __( 'Portfolio', 'fw' ),
|
73 |
-
'description' => __( 'This extension will add a fully fledged portfolio module that will let you display your projects using the built in portfolio pages.', 'fw' ),
|
74 |
-
'thumbnail' => $thumbnails_uri . '/portfolio.jpg',
|
75 |
-
'download' => array(
|
76 |
-
'github' => array(
|
77 |
-
'user_repo' => $github_account . '/Unyson-Portfolio-Extension',
|
78 |
-
),
|
79 |
-
),
|
80 |
-
),
|
81 |
-
'page-builder' => array(
|
82 |
-
'display' => true,
|
83 |
-
'parent' => 'shortcodes',
|
84 |
-
'name' => __( 'Page Builder', 'fw' ),
|
85 |
-
'description' => __( "Let's you easily build countless pages with the help of the drag and drop visual page builder that comes with a lot of already created shortcodes.", 'fw' ),
|
86 |
-
'thumbnail' => $thumbnails_uri . '/page-builder.jpg',
|
87 |
-
'download' => array(
|
88 |
-
'github' => array(
|
89 |
-
'user_repo' => $github_account . '/Unyson-PageBuilder-Extension',
|
90 |
-
),
|
91 |
-
),
|
92 |
-
),
|
93 |
-
'shortcodes' => array(
|
94 |
-
'display' => false,
|
95 |
-
'parent' => null,
|
96 |
-
'name' => __( 'Shortcodes', 'fw' ),
|
97 |
-
'description' => '',
|
98 |
-
'thumbnail' => 'about:blank',
|
99 |
-
'download' => array(
|
100 |
-
'github' => array(
|
101 |
-
'user_repo' => $github_account . '/Unyson-Shortcodes-Extension',
|
102 |
-
),
|
103 |
-
),
|
104 |
-
),
|
105 |
-
'breadcrumbs' => array(
|
106 |
-
'display' => true,
|
107 |
-
'parent' => null,
|
108 |
-
'name' => __( 'Breadcrumbs', 'fw' ),
|
109 |
-
'description' => __( 'Creates a simplified navigation menu for the pages that can be placed anywhere in the theme. This will make navigating the website much easier.', 'fw' ),
|
110 |
-
'thumbnail' => $thumbnails_uri . '/breadcrumbs.jpg',
|
111 |
-
'download' => array(
|
112 |
-
'github' => array(
|
113 |
-
'user_repo' => $github_account . '/Unyson-Breadcrumbs-Extension',
|
114 |
-
),
|
115 |
-
),
|
116 |
-
),
|
117 |
-
'seo' => array(
|
118 |
-
'display' => true,
|
119 |
-
'parent' => null,
|
120 |
-
'name' => __( 'SEO', 'fw' ),
|
121 |
-
'description' => __( 'This extension will enable you to have a fully optimized WordPress website by adding optimized meta titles, keywords and descriptions.', 'fw' ),
|
122 |
-
'thumbnail' => $thumbnails_uri . '/seo.jpg',
|
123 |
-
'download' => array(
|
124 |
-
'github' => array(
|
125 |
-
'user_repo' => $github_account . '/Unyson-SEO-Extension',
|
126 |
-
),
|
127 |
-
),
|
128 |
-
),
|
129 |
-
'sidebars' => array(
|
130 |
-
'display' => true,
|
131 |
-
'parent' => null,
|
132 |
-
'name' => __( 'Sidebars', 'fw' ),
|
133 |
-
'description' => __( 'Brings a new layer of customization freedom to your website by letting you add more than one sidebar to a page, or different sidebars on different pages.', 'fw' ),
|
134 |
-
'thumbnail' => $thumbnails_uri . '/sidebars.jpg',
|
135 |
-
'download' => array(
|
136 |
-
'github' => array(
|
137 |
-
'user_repo' => $github_account . '/Unyson-Sidebars-Extension',
|
138 |
-
),
|
139 |
-
),
|
140 |
-
),
|
141 |
-
'feedback' => array(
|
142 |
-
'display' => true,
|
143 |
-
'parent' => null,
|
144 |
-
'name' => __( 'Feedback', 'fw' ),
|
145 |
-
'description' => __( 'Adds the possibility to leave feedback (comments, reviews and rating) about your products, articles, etc. This replaces the default comments system.', 'fw' ),
|
146 |
-
'thumbnail' => $thumbnails_uri . '/feedback.jpg',
|
147 |
-
'download' => array(
|
148 |
-
'github' => array(
|
149 |
-
'user_repo' => $github_account . '/Unyson-Feedback-Extension',
|
150 |
-
),
|
151 |
-
),
|
152 |
-
),
|
153 |
-
'backup' => array(
|
154 |
-
'display' => true,
|
155 |
-
'parent' => null,
|
156 |
-
'name' => __( 'Backup', 'fw' ),
|
157 |
-
'description' => __( 'This extension lets you set up daily, weekly or monthly backup schedule. You can choose between a full backup or a data base only backup.', 'fw' ),
|
158 |
-
'thumbnail' => $thumbnails_uri . '/backup.jpg',
|
159 |
-
'download' => array(
|
160 |
-
'github' => array(
|
161 |
-
'user_repo' => $github_account . '/Unyson-Backup-Extension',
|
162 |
-
),
|
163 |
-
),
|
164 |
-
),
|
165 |
-
'backups' => array(
|
166 |
-
'display' => true,
|
167 |
-
'parent' => null,
|
168 |
-
'name' => __( 'Backup & Demo Content', 'fw' ),
|
169 |
-
'description' => __( 'This extension lets you create an automated backup schedule, import demo content or even create a demo content archive for migration purposes.', 'fw' ),
|
170 |
-
'thumbnail' => $thumbnails_uri . '/backups.jpg',
|
171 |
-
'download' => array(
|
172 |
-
'github' => array(
|
173 |
-
'user_repo' => $github_account . '/Unyson-Backups-Extension',
|
174 |
-
),
|
175 |
-
),
|
176 |
-
),
|
177 |
-
'events' => array(
|
178 |
-
'display' => true,
|
179 |
-
'parent' => null,
|
180 |
-
'name' => __( 'Events', 'fw' ),
|
181 |
-
'description' => __( 'This extension adds a fully fledged Events module to your theme. It comes with built in pages that contain a calendar where events can be added.', 'fw' ),
|
182 |
-
'thumbnail' => $thumbnails_uri . '/events.jpg',
|
183 |
-
'download' => array(
|
184 |
-
'github' => array(
|
185 |
-
'user_repo' => $github_account . '/Unyson-Events-Extension',
|
186 |
-
),
|
187 |
-
),
|
188 |
-
),
|
189 |
-
'analytics' => array(
|
190 |
-
'display' => true,
|
191 |
-
'parent' => null,
|
192 |
-
'name' => __( 'Analytics', 'fw' ),
|
193 |
-
'description' => __( 'Enables the possibility to add the Google Analytics tracking code that will let you get all the analytics about visitors, page views and more.', 'fw' ),
|
194 |
-
'thumbnail' => $thumbnails_uri . '/analytics.jpg',
|
195 |
-
'download' => array(
|
196 |
-
'github' => array(
|
197 |
-
'user_repo' => $github_account . '/Unyson-Analytics-Extension',
|
198 |
-
),
|
199 |
-
),
|
200 |
-
),
|
201 |
-
'builder' => array(
|
202 |
-
'display' => false,
|
203 |
-
'parent' => null,
|
204 |
-
'name' => __( 'Builder', 'fw' ),
|
205 |
-
'description' => '',
|
206 |
-
'thumbnail' => 'about:blank',
|
207 |
-
'download' => array(
|
208 |
-
'github' => array(
|
209 |
-
'user_repo' => $github_account . '/Unyson-Builder-Extension',
|
210 |
-
),
|
211 |
-
),
|
212 |
-
),
|
213 |
-
'learning' => array(
|
214 |
-
'display' => true,
|
215 |
-
'parent' => null,
|
216 |
-
'name' => __( 'Learning', 'fw' ),
|
217 |
-
'description' => __( 'This extension adds a Learning module to your theme. Using this extension you can add courses, lessons and tests for your users to take.', 'fw' ),
|
218 |
-
'thumbnail' => $thumbnails_uri . '/learning.jpg',
|
219 |
-
'download' => array(
|
220 |
-
'github' => array(
|
221 |
-
'user_repo' => $github_account . '/Unyson-Learning-Extension',
|
222 |
-
),
|
223 |
-
),
|
224 |
-
),
|
225 |
-
'forms' => array(
|
226 |
-
'display' => false,
|
227 |
-
'parent' => null,
|
228 |
-
'name' => __( 'Forms', 'fw' ),
|
229 |
-
'description' => __( 'This extension adds the possibility to create a contact form. Use the drag & drop form builder to create any contact form you\'ll ever want or need.', 'fw' ),
|
230 |
-
'thumbnail' => $thumbnails_uri . '/forms.jpg',
|
231 |
-
'download' => array(
|
232 |
-
'github' => array(
|
233 |
-
'user_repo' => $github_account . '/Unyson-Forms-Extension',
|
234 |
-
),
|
235 |
-
),
|
236 |
-
),
|
237 |
-
'mailer' => array(
|
238 |
-
'display' => false,
|
239 |
-
'parent' => null,
|
240 |
-
'name' => __( 'Mailer', 'fw' ),
|
241 |
-
'description' => __( 'This extension will let you set some global email options and it is used by other extensions (like Forms) to send emails.', 'fw' ),
|
242 |
-
'thumbnail' => $thumbnails_uri . '/mailer.jpg',
|
243 |
-
'download' => array(
|
244 |
-
'github' => array(
|
245 |
-
'user_repo' => $github_account . '/Unyson-Mailer-Extension',
|
246 |
-
),
|
247 |
-
),
|
248 |
-
),
|
249 |
-
'social' => array(
|
250 |
-
'display' => true,
|
251 |
-
'parent' => null,
|
252 |
-
'name' => __( 'Social', 'fw' ),
|
253 |
-
'description' => __( 'Use this extension to configure all your social related APIs. Other extensions will use the Social extension to connect to your social accounts.', 'fw' ),
|
254 |
-
'thumbnail' => $thumbnails_uri . '/social.jpg',
|
255 |
-
'download' => array(
|
256 |
-
'github' => array(
|
257 |
-
'user_repo' => $github_account . '/Unyson-Social-Extension',
|
258 |
-
),
|
259 |
-
),
|
260 |
-
),
|
261 |
-
'translation' => array(
|
262 |
-
'display' => true,
|
263 |
-
'parent' => null,
|
264 |
-
'name' => __( 'Translations', 'fw' ),
|
265 |
-
'description' => __( 'This extension lets you translate your website in any language or even add multiple languages for your users to change at their will from the front-end.', 'fw' ),
|
266 |
-
'thumbnail' => $thumbnails_uri . '/translation.jpg',
|
267 |
-
'download' => array(
|
268 |
-
'github' => array(
|
269 |
-
'user_repo' => $github_account . '/Unyson-Translation-Extension',
|
270 |
-
),
|
271 |
-
),
|
272 |
-
),
|
273 |
-
);
|
1 |
+
<?php if ( ! defined( 'FW' ) ) {
|
2 |
+
die( 'Forbidden' );
|
3 |
+
}
|
4 |
+
|
5 |
+
$thumbnails_uri = fw_get_framework_directory_uri( '/core/components/extensions/manager/static/img/thumbnails' );
|
6 |
+
$github_account = 'ThemeFuse';
|
7 |
+
|
8 |
+
$extensions = array(
|
9 |
+
'slider' => array(
|
10 |
+
'display' => true,
|
11 |
+
'parent' => 'media',
|
12 |
+
'name' => __( 'Sliders', 'fw' ),
|
13 |
+
'description' => __( 'Adds a sliders module to your website from where you\'ll be able to create different built in jQuery sliders for your homepage and rest of the pages.', 'fw' ),
|
14 |
+
'thumbnail' => $thumbnails_uri . '/sliders.jpg',
|
15 |
+
'download' => array(
|
16 |
+
'github' => array(
|
17 |
+
'user_repo' => $github_account . '/Unyson-Sliders-Extension',
|
18 |
+
),
|
19 |
+
),
|
20 |
+
),
|
21 |
+
'media' => array(
|
22 |
+
'display' => false,
|
23 |
+
'parent' => null,
|
24 |
+
'name' => __( 'Media', 'fw' ),
|
25 |
+
'description' => '',
|
26 |
+
'thumbnail' => 'about:blank',
|
27 |
+
'download' => array(
|
28 |
+
'github' => array(
|
29 |
+
'user_repo' => $github_account . '/Unyson-Empty-Extension',
|
30 |
+
),
|
31 |
+
),
|
32 |
+
),
|
33 |
+
'population-method' => array(
|
34 |
+
'display' => false,
|
35 |
+
'parent' => 'media',
|
36 |
+
'name' => __( 'Population method', 'fw' ),
|
37 |
+
'description' => '',
|
38 |
+
'thumbnail' => 'about:blank',
|
39 |
+
'download' => array(
|
40 |
+
'github' => array(
|
41 |
+
'user_repo' => $github_account . '/Unyson-PopulationMethods-Extension',
|
42 |
+
),
|
43 |
+
),
|
44 |
+
),
|
45 |
+
'styling' => array(
|
46 |
+
'display' => true,
|
47 |
+
'parent' => null,
|
48 |
+
'name' => __( 'Styling', 'fw' ),
|
49 |
+
'description' => __( 'This extension lets you control the website visual style. Starting from predefined styles to changing specific fonts and colors across the website.', 'fw' ),
|
50 |
+
'thumbnail' => $thumbnails_uri . '/styling.jpg',
|
51 |
+
'download' => array(
|
52 |
+
'github' => array(
|
53 |
+
'user_repo' => $github_account . '/Unyson-Styling-Extension',
|
54 |
+
),
|
55 |
+
),
|
56 |
+
),
|
57 |
+
'megamenu' => array(
|
58 |
+
'display' => true,
|
59 |
+
'parent' => null,
|
60 |
+
'name' => __( 'Mega Menu', 'fw' ),
|
61 |
+
'description' => __( 'The Mega Menu extension adds a user-friendly drop down menu that will let you easily create highly customized menu configurations.', 'fw' ),
|
62 |
+
'thumbnail' => $thumbnails_uri . '/mega-menu.jpg',
|
63 |
+
'download' => array(
|
64 |
+
'github' => array(
|
65 |
+
'user_repo' => $github_account . '/Unyson-MegaMenu-Extension',
|
66 |
+
),
|
67 |
+
),
|
68 |
+
),
|
69 |
+
'portfolio' => array(
|
70 |
+
'display' => true,
|
71 |
+
'parent' => null,
|
72 |
+
'name' => __( 'Portfolio', 'fw' ),
|
73 |
+
'description' => __( 'This extension will add a fully fledged portfolio module that will let you display your projects using the built in portfolio pages.', 'fw' ),
|
74 |
+
'thumbnail' => $thumbnails_uri . '/portfolio.jpg',
|
75 |
+
'download' => array(
|
76 |
+
'github' => array(
|
77 |
+
'user_repo' => $github_account . '/Unyson-Portfolio-Extension',
|
78 |
+
),
|
79 |
+
),
|
80 |
+
),
|
81 |
+
'page-builder' => array(
|
82 |
+
'display' => true,
|
83 |
+
'parent' => 'shortcodes',
|
84 |
+
'name' => __( 'Page Builder', 'fw' ),
|
85 |
+
'description' => __( "Let's you easily build countless pages with the help of the drag and drop visual page builder that comes with a lot of already created shortcodes.", 'fw' ),
|
86 |
+
'thumbnail' => $thumbnails_uri . '/page-builder.jpg',
|
87 |
+
'download' => array(
|
88 |
+
'github' => array(
|
89 |
+
'user_repo' => $github_account . '/Unyson-PageBuilder-Extension',
|
90 |
+
),
|
91 |
+
),
|
92 |
+
),
|
93 |
+
'shortcodes' => array(
|
94 |
+
'display' => false,
|
95 |
+
'parent' => null,
|
96 |
+
'name' => __( 'Shortcodes', 'fw' ),
|
97 |
+
'description' => '',
|
98 |
+
'thumbnail' => 'about:blank',
|
99 |
+
'download' => array(
|
100 |
+
'github' => array(
|
101 |
+
'user_repo' => $github_account . '/Unyson-Shortcodes-Extension',
|
102 |
+
),
|
103 |
+
),
|
104 |
+
),
|
105 |
+
'breadcrumbs' => array(
|
106 |
+
'display' => true,
|
107 |
+
'parent' => null,
|
108 |
+
'name' => __( 'Breadcrumbs', 'fw' ),
|
109 |
+
'description' => __( 'Creates a simplified navigation menu for the pages that can be placed anywhere in the theme. This will make navigating the website much easier.', 'fw' ),
|
110 |
+
'thumbnail' => $thumbnails_uri . '/breadcrumbs.jpg',
|
111 |
+
'download' => array(
|
112 |
+
'github' => array(
|
113 |
+
'user_repo' => $github_account . '/Unyson-Breadcrumbs-Extension',
|
114 |
+
),
|
115 |
+
),
|
116 |
+
),
|
117 |
+
'seo' => array(
|
118 |
+
'display' => true,
|
119 |
+
'parent' => null,
|
120 |
+
'name' => __( 'SEO', 'fw' ),
|
121 |
+
'description' => __( 'This extension will enable you to have a fully optimized WordPress website by adding optimized meta titles, keywords and descriptions.', 'fw' ),
|
122 |
+
'thumbnail' => $thumbnails_uri . '/seo.jpg',
|
123 |
+
'download' => array(
|
124 |
+
'github' => array(
|
125 |
+
'user_repo' => $github_account . '/Unyson-SEO-Extension',
|
126 |
+
),
|
127 |
+
),
|
128 |
+
),
|
129 |
+
'sidebars' => array(
|
130 |
+
'display' => true,
|
131 |
+
'parent' => null,
|
132 |
+
'name' => __( 'Sidebars', 'fw' ),
|
133 |
+
'description' => __( 'Brings a new layer of customization freedom to your website by letting you add more than one sidebar to a page, or different sidebars on different pages.', 'fw' ),
|
134 |
+
'thumbnail' => $thumbnails_uri . '/sidebars.jpg',
|
135 |
+
'download' => array(
|
136 |
+
'github' => array(
|
137 |
+
'user_repo' => $github_account . '/Unyson-Sidebars-Extension',
|
138 |
+
),
|
139 |
+
),
|
140 |
+
),
|
141 |
+
'feedback' => array(
|
142 |
+
'display' => true,
|
143 |
+
'parent' => null,
|
144 |
+
'name' => __( 'Feedback', 'fw' ),
|
145 |
+
'description' => __( 'Adds the possibility to leave feedback (comments, reviews and rating) about your products, articles, etc. This replaces the default comments system.', 'fw' ),
|
146 |
+
'thumbnail' => $thumbnails_uri . '/feedback.jpg',
|
147 |
+
'download' => array(
|
148 |
+
'github' => array(
|
149 |
+
'user_repo' => $github_account . '/Unyson-Feedback-Extension',
|
150 |
+
),
|
151 |
+
),
|
152 |
+
),
|
153 |
+
'backup' => array(
|
154 |
+
'display' => true,
|
155 |
+
'parent' => null,
|
156 |
+
'name' => __( 'Backup', 'fw' ),
|
157 |
+
'description' => __( 'This extension lets you set up daily, weekly or monthly backup schedule. You can choose between a full backup or a data base only backup.', 'fw' ),
|
158 |
+
'thumbnail' => $thumbnails_uri . '/backup.jpg',
|
159 |
+
'download' => array(
|
160 |
+
'github' => array(
|
161 |
+
'user_repo' => $github_account . '/Unyson-Backup-Extension',
|
162 |
+
),
|
163 |
+
),
|
164 |
+
),
|
165 |
+
'backups' => array(
|
166 |
+
'display' => true,
|
167 |
+
'parent' => null,
|
168 |
+
'name' => __( 'Backup & Demo Content', 'fw' ),
|
169 |
+
'description' => __( 'This extension lets you create an automated backup schedule, import demo content or even create a demo content archive for migration purposes.', 'fw' ),
|
170 |
+
'thumbnail' => $thumbnails_uri . '/backups.jpg',
|
171 |
+
'download' => array(
|
172 |
+
'github' => array(
|
173 |
+
'user_repo' => $github_account . '/Unyson-Backups-Extension',
|
174 |
+
),
|
175 |
+
),
|
176 |
+
),
|
177 |
+
'events' => array(
|
178 |
+
'display' => true,
|
179 |
+
'parent' => null,
|
180 |
+
'name' => __( 'Events', 'fw' ),
|
181 |
+
'description' => __( 'This extension adds a fully fledged Events module to your theme. It comes with built in pages that contain a calendar where events can be added.', 'fw' ),
|
182 |
+
'thumbnail' => $thumbnails_uri . '/events.jpg',
|
183 |
+
'download' => array(
|
184 |
+
'github' => array(
|
185 |
+
'user_repo' => $github_account . '/Unyson-Events-Extension',
|
186 |
+
),
|
187 |
+
),
|
188 |
+
),
|
189 |
+
'analytics' => array(
|
190 |
+
'display' => true,
|
191 |
+
'parent' => null,
|
192 |
+
'name' => __( 'Analytics', 'fw' ),
|
193 |
+
'description' => __( 'Enables the possibility to add the Google Analytics tracking code that will let you get all the analytics about visitors, page views and more.', 'fw' ),
|
194 |
+
'thumbnail' => $thumbnails_uri . '/analytics.jpg',
|
195 |
+
'download' => array(
|
196 |
+
'github' => array(
|
197 |
+
'user_repo' => $github_account . '/Unyson-Analytics-Extension',
|
198 |
+
),
|
199 |
+
),
|
200 |
+
),
|
201 |
+
'builder' => array(
|
202 |
+
'display' => false,
|
203 |
+
'parent' => null,
|
204 |
+
'name' => __( 'Builder', 'fw' ),
|
205 |
+
'description' => '',
|
206 |
+
'thumbnail' => 'about:blank',
|
207 |
+
'download' => array(
|
208 |
+
'github' => array(
|
209 |
+
'user_repo' => $github_account . '/Unyson-Builder-Extension',
|
210 |
+
),
|
211 |
+
),
|
212 |
+
),
|
213 |
+
'learning' => array(
|
214 |
+
'display' => true,
|
215 |
+
'parent' => null,
|
216 |
+
'name' => __( 'Learning', 'fw' ),
|
217 |
+
'description' => __( 'This extension adds a Learning module to your theme. Using this extension you can add courses, lessons and tests for your users to take.', 'fw' ),
|
218 |
+
'thumbnail' => $thumbnails_uri . '/learning.jpg',
|
219 |
+
'download' => array(
|
220 |
+
'github' => array(
|
221 |
+
'user_repo' => $github_account . '/Unyson-Learning-Extension',
|
222 |
+
),
|
223 |
+
),
|
224 |
+
),
|
225 |
+
'forms' => array(
|
226 |
+
'display' => false,
|
227 |
+
'parent' => null,
|
228 |
+
'name' => __( 'Forms', 'fw' ),
|
229 |
+
'description' => __( 'This extension adds the possibility to create a contact form. Use the drag & drop form builder to create any contact form you\'ll ever want or need.', 'fw' ),
|
230 |
+
'thumbnail' => $thumbnails_uri . '/forms.jpg',
|
231 |
+
'download' => array(
|
232 |
+
'github' => array(
|
233 |
+
'user_repo' => $github_account . '/Unyson-Forms-Extension',
|
234 |
+
),
|
235 |
+
),
|
236 |
+
),
|
237 |
+
'mailer' => array(
|
238 |
+
'display' => false,
|
239 |
+
'parent' => null,
|
240 |
+
'name' => __( 'Mailer', 'fw' ),
|
241 |
+
'description' => __( 'This extension will let you set some global email options and it is used by other extensions (like Forms) to send emails.', 'fw' ),
|
242 |
+
'thumbnail' => $thumbnails_uri . '/mailer.jpg',
|
243 |
+
'download' => array(
|
244 |
+
'github' => array(
|
245 |
+
'user_repo' => $github_account . '/Unyson-Mailer-Extension',
|
246 |
+
),
|
247 |
+
),
|
248 |
+
),
|
249 |
+
'social' => array(
|
250 |
+
'display' => true,
|
251 |
+
'parent' => null,
|
252 |
+
'name' => __( 'Social', 'fw' ),
|
253 |
+
'description' => __( 'Use this extension to configure all your social related APIs. Other extensions will use the Social extension to connect to your social accounts.', 'fw' ),
|
254 |
+
'thumbnail' => $thumbnails_uri . '/social.jpg',
|
255 |
+
'download' => array(
|
256 |
+
'github' => array(
|
257 |
+
'user_repo' => $github_account . '/Unyson-Social-Extension',
|
258 |
+
),
|
259 |
+
),
|
260 |
+
),
|
261 |
+
'translation' => array(
|
262 |
+
'display' => true,
|
263 |
+
'parent' => null,
|
264 |
+
'name' => __( 'Translations', 'fw' ),
|
265 |
+
'description' => __( 'This extension lets you translate your website in any language or even add multiple languages for your users to change at their will from the front-end.', 'fw' ),
|
266 |
+
'thumbnail' => $thumbnails_uri . '/translation.jpg',
|
267 |
+
'download' => array(
|
268 |
+
'github' => array(
|
269 |
+
'user_repo' => $github_account . '/Unyson-Translation-Extension',
|
270 |
+
),
|
271 |
+
),
|
272 |
+
),
|
273 |
+
);
|
framework/core/components/extensions/manager/class--fw-extensions-manager.php
CHANGED
@@ -1,3219 +1,3219 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Install/Activate/Deactivate/Remove Extensions
|
5 |
-
* @internal
|
6 |
-
*/
|
7 |
-
final class _FW_Extensions_Manager
|
8 |
-
{
|
9 |
-
/**
|
10 |
-
* @var FW_Form
|
11 |
-
*/
|
12 |
-
private $extension_settings_form;
|
13 |
-
|
14 |
-
/**
|
15 |
-
* @var Parsedown
|
16 |
-
*/
|
17 |
-
private $markdown_parser;
|
18 |
-
|
19 |
-
private $manifest_default_values = array(
|
20 |
-
'display' => false,
|
21 |
-
'standalone' => false,
|
22 |
-
);
|
23 |
-
|
24 |
-
private $download_timeout = 300;
|
25 |
-
|
26 |
-
private $default_thumbnail = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQIW2PUsHf9DwAC8AGtfm5YCAAAAABJRU5ErkJgggAA';
|
27 |
-
|
28 |
-
public function __construct()
|
29 |
-
{
|
30 |
-
// In any case/permission, make sure to not miss the plugin update actions to prevent extensions delete
|
31 |
-
{
|
32 |
-
add_action('fw_plugin_pre_update', array($this, '_action_plugin_pre_update'));
|
33 |
-
add_action('fw_plugin_post_update', array($this, '_action_plugin_post_update'));
|
34 |
-
}
|
35 |
-
|
36 |
-
if (!is_admin()) {
|
37 |
-
return;
|
38 |
-
}
|
39 |
-
|
40 |
-
if (!$this->can_activate() && !$this->can_install()) {
|
41 |
-
return;
|
42 |
-
}
|
43 |
-
|
44 |
-
/** Actions */
|
45 |
-
{
|
46 |
-
add_action('fw_init', array($this, '_action_fw_init'));
|
47 |
-
add_action('admin_menu', array($this, '_action_admin_menu'));
|
48 |
-
add_action('network_admin_menu', array($this, '_action_admin_menu'));
|
49 |
-
add_action('admin_footer', array($this, '_action_admin_footer'));
|
50 |
-
add_action('admin_enqueue_scripts', array($this, '_action_enqueue_scripts'));
|
51 |
-
add_action('fw_after_plugin_activate', array($this, '_action_after_plugin_activate'), 100);
|
52 |
-
add_action('after_switch_theme', array($this, '_action_theme_switch'));
|
53 |
-
add_action('admin_notices', array($this, '_action_admin_notices'));
|
54 |
-
|
55 |
-
if ($this->can_install()) {
|
56 |
-
add_action('wp_ajax_fw_extensions_check_direct_fs_access', array($this, '_action_ajax_check_direct_fs_access'));
|
57 |
-
add_action('wp_ajax_fw_extensions_install', array($this, '_action_ajax_install'));
|
58 |
-
add_action('wp_ajax_fw_extensions_uninstall', array($this, '_action_ajax_uninstall'));
|
59 |
-
}
|
60 |
-
}
|
61 |
-
|
62 |
-
/** Filters */
|
63 |
-
{
|
64 |
-
add_filter('fw_plugin_action_list', array($this, '_filter_plugin_action_list'));
|
65 |
-
}
|
66 |
-
}
|
67 |
-
|
68 |
-
/**
|
69 |
-
* If current user can:
|
70 |
-
* - activate extension
|
71 |
-
* - disable extensions
|
72 |
-
* - save extension settings options
|
73 |
-
* @return bool
|
74 |
-
*/
|
75 |
-
public function can_activate()
|
76 |
-
{
|
77 |
-
static $can_activate = null;
|
78 |
-
|
79 |
-
if ($can_activate === null) {
|
80 |
-
$can_activate = current_user_can('manage_options');
|
81 |
-
|
82 |
-
if ($can_activate) {
|
83 |
-
// also you can use this method to get the capability
|
84 |
-
$can_activate = 'manage_options';
|
85 |
-
}
|
86 |
-
|
87 |
-
if (!$can_activate) {
|
88 |
-
// make sure if can install, then also can activate. (can install) > (can activate)
|
89 |
-
$can_activate = $this->can_install();
|
90 |
-
}
|
91 |
-
}
|
92 |
-
|
93 |
-
return $can_activate;
|
94 |
-
}
|
95 |
-
|
96 |
-
/**
|
97 |
-
* If current user can:
|
98 |
-
* - install extensions
|
99 |
-
* - delete extensions
|
100 |
-
* @return bool
|
101 |
-
*/
|
102 |
-
public function can_install()
|
103 |
-
{
|
104 |
-
static $can_install = null;
|
105 |
-
|
106 |
-
if ($can_install === null) {
|
107 |
-
$capability = 'install_plugins';
|
108 |
-
|
109 |
-
if (is_multisite()) {
|
110 |
-
// only network admin can change files that affects the entire network
|
111 |
-
$can_install = current_user_can_for_blog(get_current_blog_id(), $capability);
|
112 |
-
} else {
|
113 |
-
$can_install = current_user_can($capability);
|
114 |
-
}
|
115 |
-
|
116 |
-
if ($can_install) {
|
117 |
-
// also you can use this method to get the capability
|
118 |
-
$can_install = $capability;
|
119 |
-
}
|
120 |
-
}
|
121 |
-
|
122 |
-
return $can_install;
|
123 |
-
}
|
124 |
-
|
125 |
-
public function get_page_slug()
|
126 |
-
{
|
127 |
-
return 'fw-extensions';
|
128 |
-
}
|
129 |
-
|
130 |
-
private function get_cache_key($sub_key)
|
131 |
-
{
|
132 |
-
return 'fw_extensions_manager/'. $sub_key;
|
133 |
-
}
|
134 |
-
|
135 |
-
private function get_uri($append = '')
|
136 |
-
{
|
137 |
-
return fw_get_framework_directory_uri('/core/components/extensions/manager'. $append);
|
138 |
-
}
|
139 |
-
|
140 |
-
private function get_markdown_parser()
|
141 |
-
{
|
142 |
-
if (!$this->markdown_parser) {
|
143 |
-
if (!class_exists('Parsedown')) {
|
144 |
-
require_once dirname(__FILE__) .'/includes/parsedown/Parsedown.php';
|
145 |
-
}
|
146 |
-
|
147 |
-
$this->markdown_parser = new Parsedown();
|
148 |
-
}
|
149 |
-
|
150 |
-
return $this->markdown_parser;
|
151 |
-
}
|
152 |
-
|
153 |
-
private function get_nonce($form) {
|
154 |
-
switch ($form) {
|
155 |
-
case 'install':
|
156 |
-
return array(
|
157 |
-
'name' => '_nonce_fw_extensions_install',
|
158 |
-
'action' => 'install',
|
159 |
-
);
|
160 |
-
case 'delete':
|
161 |
-
return array(
|
162 |
-
'name' => '_nonce_fw_extensions_delete',
|
163 |
-
'action' => 'delete',
|
164 |
-
);
|
165 |
-
case 'activate':
|
166 |
-
return array(
|
167 |
-
'name' => '_nonce_fw_extensions_activate',
|
168 |
-
'action' => 'activate',
|
169 |
-
);
|
170 |
-
case 'deactivate':
|
171 |
-
return array(
|
172 |
-
'name' => '_nonce_fw_extensions_deactivate',
|
173 |
-
'action' => 'deactivate',
|
174 |
-
);
|
175 |
-
default:
|
176 |
-
return array(
|
177 |
-
'name' => '_nonce_fw_extensions',
|
178 |
-
'action' => 'default',
|
179 |
-
);
|
180 |
-
}
|
181 |
-
}
|
182 |
-
|
183 |
-
/**
|
184 |
-
* Extensions available for download
|
185 |
-
* @return array {name => data}
|
186 |
-
*/
|
187 |
-
private function get_available_extensions()
|
188 |
-
{
|
189 |
-
try {
|
190 |
-
$cache_key = $this->get_cache_key( 'available_extensions' );
|
191 |
-
|
192 |
-
return FW_Cache::get($cache_key);
|
193 |
-
} catch (FW_Cache_Not_Found_Exception $e) {
|
194 |
-
$vars = fw_get_variables_from_file( dirname( __FILE__ ) . '/available-extensions.php', array(
|
195 |
-
'extensions' => array()
|
196 |
-
) );
|
197 |
-
|
198 |
-
{
|
199 |
-
$installed_extensions = $this->get_installed_extensions();
|
200 |
-
$supported_extensions = fw()->theme->manifest->get('supported_extensions', array());
|
201 |
-
|
202 |
-
if (isset($installed_extensions['backup'])) {
|
203 |
-
// make sure only Backup or Backups can be installed
|
204 |
-
unset($vars['extensions']['backups']);
|
205 |
-
}
|
206 |
-
|
207 |
-
foreach (
|
208 |
-
array('backup', 'styling', 'translation', 'learning')
|
209 |
-
as $obsolete_extension
|
210 |
-
) {
|
211 |
-
if (
|
212 |
-
!isset($supported_extensions[$obsolete_extension])
|
213 |
-
&&
|
214 |
-
!isset($installed_extensions[$obsolete_extension])
|
215 |
-
) {
|
216 |
-
unset($vars['extensions'][$obsolete_extension]);
|
217 |
-
}
|
218 |
-
}
|
219 |
-
}
|
220 |
-
|
221 |
-
FW_Cache::set($cache_key, $vars['extensions']);
|
222 |
-
|
223 |
-
return $vars['extensions'];
|
224 |
-
}
|
225 |
-
}
|
226 |
-
|
227 |
-
/**
|
228 |
-
* @internal
|
229 |
-
*/
|
230 |
-
public function _action_ajax_check_direct_fs_access()
|
231 |
-
{
|
232 |
-
if (!$this->can_install()) {
|
233 |
-
// if can't install, no need to know if has access or not
|
234 |
-
wp_send_json_error();
|
235 |
-
}
|
236 |
-
|
237 |
-
if (FW_WP_Filesystem::has_direct_access(fw_get_framework_directory('/extensions'))) {
|
238 |
-
wp_send_json_success();
|
239 |
-
} else {
|
240 |
-
wp_send_json_error();
|
241 |
-
}
|
242 |
-
}
|
243 |
-
|
244 |
-
/**
|
245 |
-
* @internal
|
246 |
-
*/
|
247 |
-
public function _action_ajax_install()
|
248 |
-
{
|
249 |
-
if (!$this->can_install()) {
|
250 |
-
// if can't install, no need to know if has access or not
|
251 |
-
wp_send_json_error();
|
252 |
-
}
|
253 |
-
|
254 |
-
if (!FW_WP_Filesystem::has_direct_access(fw_get_framework_directory('/extensions'))) {
|
255 |
-
wp_send_json_error();
|
256 |
-
}
|
257 |
-
|
258 |
-
$extension = (string)FW_Request::POST('extension');
|
259 |
-
|
260 |
-
$install_result = $this->install_extensions(array(
|
261 |
-
$extension => array()
|
262 |
-
), array(
|
263 |
-
'cancel_on_error' => true
|
264 |
-
));
|
265 |
-
|
266 |
-
if ($install_result === true) {
|
267 |
-
wp_send_json_success();
|
268 |
-
} else {
|
269 |
-
wp_send_json_error($install_result);
|
270 |
-
}
|
271 |
-
}
|
272 |
-
|
273 |
-
/**
|
274 |
-
* @internal
|
275 |
-
*/
|
276 |
-
public function _action_ajax_uninstall()
|
277 |
-
{
|
278 |
-
if (!$this->can_install()) {
|
279 |
-
// if can't install, no need to know if has access or not
|
280 |
-
wp_send_json_error();
|
281 |
-
}
|
282 |
-
|
283 |
-
if (!FW_WP_Filesystem::has_direct_access(fw_get_framework_directory('/extensions'))) {
|
284 |
-
wp_send_json_error();
|
285 |
-
}
|
286 |
-
|
287 |
-
$extension = (string)FW_Request::POST('extension');
|
288 |
-
|
289 |
-
$install_result = $this->uninstall_extensions(array(
|
290 |
-
$extension => array()
|
291 |
-
), array(
|
292 |
-
'cancel_on_error' => true
|
293 |
-
));
|
294 |
-
|
295 |
-
if ($install_result === true) {
|
296 |
-
wp_send_json_success();
|
297 |
-
} else {
|
298 |
-
wp_send_json_error($install_result);
|
299 |
-
}
|
300 |
-
}
|
301 |
-
|
302 |
-
/**
|
303 |
-
* @internal
|
304 |
-
*/
|
305 |
-
public function _action_after_plugin_activate()
|
306 |
-
{
|
307 |
-
$this->activate_theme_extensions();
|
308 |
-
$this->activate_extensions(
|
309 |
-
array_fill_keys(
|
310 |
-
array_keys(fw()->theme->manifest->get('supported_extensions', array())),
|
311 |
-
array()
|
312 |
-
)
|
313 |
-
);
|
314 |
-
|
315 |
-
if ($this->can_install()) {
|
316 |
-
if ($this->get_supported_extensions_for_install()) {
|
317 |
-
$link = $this->get_link();
|
318 |
-
|
319 |
-
wp_redirect($link . '&sub-page=install&supported');
|
320 |
-
exit;
|
321 |
-
}
|
322 |
-
}
|
323 |
-
}
|
324 |
-
|
325 |
-
/**
|
326 |
-
* Copy all extensions to a temp backup directory
|
327 |
-
* @internal
|
328 |
-
*/
|
329 |
-
public function _action_plugin_pre_update()
|
330 |
-
{
|
331 |
-
/** @var WP_Filesystem_Base $wp_filesystem */
|
332 |
-
global $wp_filesystem;
|
333 |
-
|
334 |
-
if (!$wp_filesystem || (is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code())) {
|
335 |
-
return;
|
336 |
-
}
|
337 |
-
|
338 |
-
// a directory outside the plugin
|
339 |
-
$tmp_dir = FW_WP_Filesystem::real_path_to_filesystem_path(
|
340 |
-
fw_fix_path(WP_CONTENT_DIR) .'/tmp/fw-plugin-update-extensions-backup'
|
341 |
-
);
|
342 |
-
$extensions_dir = FW_WP_Filesystem::real_path_to_filesystem_path(
|
343 |
-
fw_get_framework_directory('/extensions')
|
344 |
-
);
|
345 |
-
|
346 |
-
$error = false;
|
347 |
-
|
348 |
-
do {
|
349 |
-
if ($wp_filesystem->exists($tmp_dir)) {
|
350 |
-
if (!$wp_filesystem->delete($tmp_dir, true, 'd')) {
|
351 |
-
$error = __('Cannot remove the old extensions backup dir', 'fw');
|
352 |
-
break;
|
353 |
-
}
|
354 |
-
}
|
355 |
-
|
356 |
-
if (!FW_WP_Filesystem::mkdir_recursive($tmp_dir)) {
|
357 |
-
$error = __('Cannot create the extensions backup dir', 'fw');
|
358 |
-
break;
|
359 |
-
}
|
360 |
-
|
361 |
-
if (true !== copy_dir($extensions_dir, $tmp_dir)) {
|
362 |
-
$error = __('Cannot backup the extensions', 'fw');
|
363 |
-
break;
|
364 |
-
}
|
365 |
-
} while(false);
|
366 |
-
|
367 |
-
if ($error) {
|
368 |
-
trigger_error($error, E_USER_WARNING);
|
369 |
-
|
370 |
-
$wp_filesystem->delete($tmp_dir, true, 'd');
|
371 |
-
}
|
372 |
-
}
|
373 |
-
|
374 |
-
/**
|
375 |
-
* Copy all extensions from the temp backup directory to the framework extensions directory (recover)
|
376 |
-
* @internal
|
377 |
-
*/
|
378 |
-
public function _action_plugin_post_update()
|
379 |
-
{
|
380 |
-
/** @var WP_Filesystem_Base $wp_filesystem */
|
381 |
-
global $wp_filesystem;
|
382 |
-
|
383 |
-
if ( !$wp_filesystem || (is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code()) ) {
|
384 |
-
return;
|
385 |
-
}
|
386 |
-
|
387 |
-
// a directory outside the plugin
|
388 |
-
$tmp_dir = FW_WP_Filesystem::real_path_to_filesystem_path(
|
389 |
-
fw_fix_path( WP_CONTENT_DIR ) .'/tmp/fw-plugin-update-extensions-backup'
|
390 |
-
);
|
391 |
-
$extensions_dir = FW_WP_Filesystem::real_path_to_filesystem_path(
|
392 |
-
fw_get_framework_directory( '/extensions' )
|
393 |
-
);
|
394 |
-
|
395 |
-
if (!$wp_filesystem->exists($tmp_dir) || !$wp_filesystem->exists($extensions_dir)) {
|
396 |
-
return;
|
397 |
-
}
|
398 |
-
|
399 |
-
$error = false;
|
400 |
-
|
401 |
-
do {
|
402 |
-
if ($wp_filesystem->exists($extensions_dir)) {
|
403 |
-
/**
|
404 |
-
* Make sure to remove framework initial extensions
|
405 |
-
* The user do not need them because he already used the framework and has in backup the extensions he uses
|
406 |
-
*/
|
407 |
-
if (!$wp_filesystem->delete( $extensions_dir, true, 'd' )) {
|
408 |
-
$error = __( 'Cannot clear the extensions directory', 'fw' );
|
409 |
-
break;
|
410 |
-
}
|
411 |
-
|
412 |
-
if ( ! FW_WP_Filesystem::mkdir_recursive( $extensions_dir ) ) {
|
413 |
-
$error = __( 'Cannot recreate the extensions directory', 'fw' );
|
414 |
-
break;
|
415 |
-
}
|
416 |
-
}
|
417 |
-
|
418 |
-
if (true !== copy_dir($tmp_dir, $extensions_dir)) {
|
419 |
-
$error = __('Cannot recover the extensions', 'fw');
|
420 |
-
break;
|
421 |
-
}
|
422 |
-
} while(false);
|
423 |
-
|
424 |
-
if ($error) {
|
425 |
-
trigger_error($error, E_USER_WARNING);
|
426 |
-
} else {
|
427 |
-
// extensions successfully recovered, the backup is not needed anymore
|
428 |
-
$wp_filesystem->delete($tmp_dir, true, 'd');
|
429 |
-
}
|
430 |
-
}
|
431 |
-
|
432 |
-
/**
|
433 |
-
* Scan all directories for extensions
|
434 |
-
*
|
435 |
-
* @param bool $reset_cache
|
436 |
-
* @return array
|
437 |
-
*/
|
438 |
-
private function get_installed_extensions($reset_cache = false)
|
439 |
-
{
|
440 |
-
$cache_key = $this->get_cache_key('installed_extensions');
|
441 |
-
|
442 |
-
if ($reset_cache) {
|
443 |
-
FW_Cache::del($cache_key);
|
444 |
-
}
|
445 |
-
|
446 |
-
try {
|
447 |
-
return FW_Cache::get($cache_key);
|
448 |
-
} catch (FW_Cache_Not_Found_Exception $e) {
|
449 |
-
$extensions = array();
|
450 |
-
|
451 |
-
foreach (fw()->extensions->get_locations() as $location) {
|
452 |
-
// leave only used keys
|
453 |
-
$location = array(
|
454 |
-
'path' => $location['path'],
|
455 |
-
'is' => $location['is'],
|
456 |
-
);
|
457 |
-
|
458 |
-
$this->read_extensions($location, $extensions);
|
459 |
-
}
|
460 |
-
|
461 |
-
FW_Cache::set($cache_key, $extensions);
|
462 |
-
|
463 |
-
return $extensions;
|
464 |
-
}
|
465 |
-
}
|
466 |
-
|
467 |
-
/**
|
468 |
-
* used by $this->get_installed_extensions()
|
469 |
-
* @param string $location
|
470 |
-
* @param array $list
|
471 |
-
* @param null|string $parent_extension_name
|
472 |
-
*/
|
473 |
-
private function read_extensions($location, &$list, $parent_extension_name = null)
|
474 |
-
{
|
475 |
-
$paths = glob($location['path'] .'/*', GLOB_ONLYDIR | GLOB_NOSORT);
|
476 |
-
|
477 |
-
if (empty($paths)) {
|
478 |
-
return;
|
479 |
-
}
|
480 |
-
|
481 |
-
foreach ($paths as $extension_path) {
|
482 |
-
$extension_name = basename($extension_path);
|
483 |
-
|
484 |
-
if (isset($list[$extension_name])) {
|
485 |
-
// extension already found
|
486 |
-
} elseif (file_exists($extension_path .'/manifest.php')) {
|
487 |
-
$vars = fw_get_variables_from_file($extension_path .'/manifest.php', array(
|
488 |
-
'manifest' => array(),
|
489 |
-
));
|
490 |
-
|
491 |
-
$list[$extension_name] = array(
|
492 |
-
'path' => $extension_path,
|
493 |
-
'manifest' => $vars['manifest'],
|
494 |
-
'children' => array(),
|
495 |
-
'active' => (bool)fw()->extensions->get($extension_name),
|
496 |
-
'parent' => $parent_extension_name,
|
497 |
-
'is' => $location['is'],
|
498 |
-
);
|
499 |
-
|
500 |
-
if ($parent_extension_name) {
|
501 |
-
$list[ $parent_extension_name ]['children'][$extension_name] = array();
|
502 |
-
}
|
503 |
-
} else {
|
504 |
-
// it's a directory with customizations for an extension
|
505 |
-
continue;
|
506 |
-
}
|
507 |
-
|
508 |
-
$sub_extension_location = $location;
|
509 |
-
$sub_extension_location['path'] .= '/'. $extension_name .'/extensions';
|
510 |
-
|
511 |
-
$this->read_extensions(
|
512 |
-
$sub_extension_location,
|
513 |
-
$list,
|
514 |
-
$extension_name
|
515 |
-
);
|
516 |
-
}
|
517 |
-
}
|
518 |
-
|
519 |
-
private function get_tmp_dir($append = '')
|
520 |
-
{
|
521 |
-
return apply_filters('fw_tmp_dir', fw_fix_path(WP_CONTENT_DIR) .'/tmp') . $append;
|
522 |
-
}
|
523 |
-
|
524 |
-
/**
|
525 |
-
* @internal
|
526 |
-
*/
|
527 |
-
public function _action_fw_init()
|
528 |
-
{
|
529 |
-
$this->extension_settings_form = new FW_Form('fw_extension_settings', array(
|
530 |
-
'render' => array($this, '_extension_settings_form_render'),
|
531 |
-
'validate' => array($this, '_extension_settings_form_validate'),
|
532 |
-
'save' => array($this, '_extension_settings_form_save'),
|
533 |
-
));
|
534 |
-
|
535 |
-
if (is_admin() && $this->can_activate()) {
|
536 |
-
$db_wp_option_name = 'fw_extensions_activation';
|
537 |
-
|
538 |
-
if ($db_wp_option_value = get_option($db_wp_option_name, array())) {
|
539 |
-
$db_wp_option_value = array_merge(array(
|
540 |
-
'activated' => array(),
|
541 |
-
'deactivated' => array(),
|
542 |
-
), $db_wp_option_value);
|
543 |
-
|
544 |
-
/**
|
545 |
-
* Fire the 'fw_extensions_after_activation' action
|
546 |
-
*/
|
547 |
-
if ($db_wp_option_value['activated']) {
|
548 |
-
$succeeded_extensions = $failed_extensions = array();
|
549 |
-
|
550 |
-
foreach ($db_wp_option_value['activated'] as $extension_name => $not_used_var) {
|
551 |
-
if (fw_ext($extension_name)) {
|
552 |
-
$succeeded_extensions[$extension_name] = array();
|
553 |
-
} else {
|
554 |
-
$failed_extensions[$extension_name] = array();
|
555 |
-
}
|
556 |
-
}
|
557 |
-
|
558 |
-
if (!empty($succeeded_extensions)) {
|
559 |
-
do_action('fw_extensions_after_activation', $succeeded_extensions);
|
560 |
-
}
|
561 |
-
if (!empty($failed_extensions)) {
|
562 |
-
do_action('fw_extensions_activation_failed', $failed_extensions);
|
563 |
-
}
|
564 |
-
}
|
565 |
-
|
566 |
-
/**
|
567 |
-
* Fire the 'fw_extensions_after_deactivation' action
|
568 |
-
*/
|
569 |
-
if ($db_wp_option_value['deactivated']) {
|
570 |
-
$succeeded_extensions = $failed_extensions = array();
|
571 |
-
|
572 |
-
foreach ($db_wp_option_value['deactivated'] as $extension_name => $not_used_var) {
|
573 |
-
if (!fw_ext($extension_name)) {
|
574 |
-
$succeeded_extensions[$extension_name] = array();
|
575 |
-
} else {
|
576 |
-
$failed_extensions[$extension_name] = array();
|
577 |
-
}
|
578 |
-
}
|
579 |
-
|
580 |
-
if (!empty($succeeded_extensions)) {
|
581 |
-
do_action('fw_extensions_after_deactivation', $succeeded_extensions);
|
582 |
-
}
|
583 |
-
if (!empty($failed_extensions)) {
|
584 |
-
do_action('fw_extensions_deactivation_failed', $failed_extensions);
|
585 |
-
}
|
586 |
-
}
|
587 |
-
|
588 |
-
delete_option($db_wp_option_name);
|
589 |
-
}
|
590 |
-
}
|
591 |
-
}
|
592 |
-
|
593 |
-
/**
|
594 |
-
* Activate extensions with $manifest['display'] = false; $manifest['standalone'] = true;
|
595 |
-
* - First level extensions
|
596 |
-
* - Child extensions of the active extensions
|
597 |
-
*/
|
598 |
-
private function activate_hidden_standalone_extensions()
|
599 |
-
{
|
600 |
-
if (!is_admin()) {
|
601 |
-
return;
|
602 |
-
}
|
603 |
-
|
604 |
-
if (!$this->can_activate()) {
|
605 |
-
return;
|
606 |
-
}
|
607 |
-
|
608 |
-
$activate_extensions = array();
|
609 |
-
|
610 |
-
foreach (
|
611 |
-
// all disabled extensions
|
612 |
-
array_diff_key($this->get_installed_extensions(), fw()->extensions->get_all())
|
613 |
-
as $ext_name => $ext_data
|
614 |
-
) {
|
615 |
-
if ($ext_data['parent'] && !fw_ext($ext_data['parent'])) {
|
616 |
-
// child extensions of an inactive extension
|
617 |
-
continue;
|
618 |
-
}
|
619 |
-
|
620 |
-
if (false !== fw_akg(
|
621 |
-
'display',
|
622 |
-
$ext_data['manifest'],
|
623 |
-
$this->manifest_default_values['display']
|
624 |
-
)) {
|
625 |
-
// is visible
|
626 |
-
continue;
|
627 |
-
}
|
628 |
-
|
629 |
-
if (true !== fw_akg(
|
630 |
-
'standalone',
|
631 |
-
$ext_data['manifest'],
|
632 |
-
$this->manifest_default_values['standalone']
|
633 |
-
)) {
|
634 |
-
// not standalone
|
635 |
-
continue;
|
636 |
-
}
|
637 |
-
|
638 |
-
$collected = $this->get_extensions_for_activation($ext_name);
|
639 |
-
|
640 |
-
if (is_wp_error($collected)) {
|
641 |
-
if (defined('WP_DEBUG') && WP_DEBUG) {
|
642 |
-
if ($this->is_extensions_page()) {
|
643 |
-
// display this warning only on Unyson extensions page
|
644 |
-
FW_Flash_Messages::add('fw_ext_auto_activate_hidden_standalone',
|
645 |
-
sprintf(__('Cannot activate hidden standalone extension %s', 'fw'),
|
646 |
-
fw_akg('name', $ext_data['manifest'], fw_id_to_title($ext_name))
|
647 |
-
),
|
648 |
-
'error'
|
649 |
-
);
|
650 |
-
}
|
651 |
-
}
|
652 |
-
return;
|
653 |
-
}
|
654 |
-
|
655 |
-
$activate_extensions = array_merge($activate_extensions, $collected);
|
656 |
-
}
|
657 |
-
|
658 |
-
if (empty($activate_extensions)) {
|
659 |
-
return;
|
660 |
-
}
|
661 |
-
|
662 |
-
$option_name = fw()->extensions->_get_active_extensions_db_option_name();
|
663 |
-
|
664 |
-
$db_active_extensions = array_merge(get_option($option_name, array()), $activate_extensions);
|
665 |
-
|
666 |
-
update_option($option_name, $db_active_extensions);
|
667 |
-
}
|
668 |
-
|
669 |
-
/**
|
670 |
-
* @internal
|
671 |
-
*/
|
672 |
-
public function _action_admin_menu()
|
673 |
-
{
|
674 |
-
$capability = $this->can_activate();
|
675 |
-
|
676 |
-
if (!$capability) {
|
677 |
-
return;
|
678 |
-
}
|
679 |
-
|
680 |
-
$data = array(
|
681 |
-
'title' => fw()->manifest->get_name(),
|
682 |
-
'capability' => $capability,
|
683 |
-
'slug' => $this->get_page_slug(),
|
684 |
-
'content_callback' => array($this, '_display_page'),
|
685 |
-
);
|
686 |
-
|
687 |
-
/**
|
688 |
-
* Collect $hookname that contains $data['slug'] before the action
|
689 |
-
* and skip them in verification after action
|
690 |
-
*/
|
691 |
-
{
|
692 |
-
global $_registered_pages;
|
693 |
-
|
694 |
-
$found_hooknames = array();
|
695 |
-
|
696 |
-
if (!empty($_registered_pages)) {
|
697 |
-
foreach ( $_registered_pages as $hookname => $b ) {
|
698 |
-
if ( strpos( $hookname, $data['slug'] ) !== false ) {
|
699 |
-
$found_hooknames[$hookname] = true;
|
700 |
-
}
|
701 |
-
}
|
702 |
-
}
|
703 |
-
}
|
704 |
-
|
705 |
-
/**
|
706 |
-
* Use this action if you what to add the extensions page in a custom place in menu
|
707 |
-
* Usage example http://pastebin.com/2iWVRPAU
|
708 |
-
*/
|
709 |
-
do_action('fw_backend_add_custom_extensions_menu', $data);
|
710 |
-
|
711 |
-
/**
|
712 |
-
* Check if menu was added in the action above
|
713 |
-
*/
|
714 |
-
{
|
715 |
-
$menu_exists = false;
|
716 |
-
|
717 |
-
if (!empty($_registered_pages)) {
|
718 |
-
foreach ( $_registered_pages as $hookname => $b ) {
|
719 |
-
if (isset($found_hooknames[$hookname])) {
|
720 |
-
continue;
|
721 |
-
}
|
722 |
-
|
723 |
-
if ( strpos( $hookname, $data['slug'] ) !== false ) {
|
724 |
-
$menu_exists = true;
|
725 |
-
break;
|
726 |
-
}
|
727 |
-
}
|
728 |
-
}
|
729 |
-
}
|
730 |
-
|
731 |
-
if ($menu_exists) {
|
732 |
-
// do nothing
|
733 |
-
} else {
|
734 |
-
add_menu_page(
|
735 |
-
$data['title'],
|
736 |
-
$data['title'],
|
737 |
-
$data['capability'],
|
738 |
-
$data['slug'],
|
739 |
-
$data['content_callback'],
|
740 |
-
'none',
|
741 |
-
3
|
742 |
-
);
|
743 |
-
}
|
744 |
-
}
|
745 |
-
|
746 |
-
/**
|
747 |
-
* If output already started, we cannot set the redirect header, do redirect from js
|
748 |
-
*/
|
749 |
-
private function js_redirect()
|
750 |
-
{
|
751 |
-
echo
|
752 |
-
'<script type="text/javascript">'.
|
753 |
-
'window.location.replace("'. esc_js($this->get_link()) .'");'.
|
754 |
-
'</script>';
|
755 |
-
}
|
756 |
-
|
757 |
-
/**
|
758 |
-
* @internal
|
759 |
-
*/
|
760 |
-
public function _display_page()
|
761 |
-
{
|
762 |
-
$page = FW_Request::GET('sub-page');
|
763 |
-
|
764 |
-
switch ($page) {
|
765 |
-
case 'install':
|
766 |
-
$this->display_install_page();
|
767 |
-
break;
|
768 |
-
case 'delete':
|
769 |
-
$this->display_delete_page();
|
770 |
-
break;
|
771 |
-
case 'extension':
|
772 |
-
$this->display_extension_page();
|
773 |
-
break;
|
774 |
-
case 'activate':
|
775 |
-
$this->display_activate_page();
|
776 |
-
break;
|
777 |
-
case 'deactivate':
|
778 |
-
$this->display_deactivate_page();
|
779 |
-
break;
|
780 |
-
default:
|
781 |
-
$this->display_list_page();
|
782 |
-
}
|
783 |
-
}
|
784 |
-
|
785 |
-
private function display_list_page()
|
786 |
-
{
|
787 |
-
// note: static is enqueued in 'admin_enqueue_scripts' action
|
788 |
-
|
789 |
-
/** Prepare extensions list for view */
|
790 |
-
{
|
791 |
-
$lists = array(
|
792 |
-
'active' => array(),
|
793 |
-
'disabled' => array(),
|
794 |
-
'installed' => array(),
|
795 |
-
'available' => array(),
|
796 |
-
'supported' => array(),
|
797 |
-
);
|
798 |
-
|
799 |
-
foreach ($this->get_installed_extensions() as $ext_name => $ext_data) {
|
800 |
-
$lists[ $ext_data['active'] ? 'active' : 'disabled' ][$ext_name] = $ext_data;
|
801 |
-
}
|
802 |
-
|
803 |
-
$lists['installed'] = $lists['active'] + $lists['disabled'];
|
804 |
-
|
805 |
-
unset($ext_data); // prevent change by reference
|
806 |
-
|
807 |
-
foreach ($this->get_available_extensions() as $ext_name => $ext_data) {
|
808 |
-
$lists['available'][$ext_name] = array(
|
809 |
-
'name' => $ext_data['name'],
|
810 |
-
'description' => $ext_data['description'],
|
811 |
-
'thumbnail' => isset($ext_data['thumbnail'])
|
812 |
-
? $ext_data['thumbnail']
|
813 |
-
: (isset($lists['installed'][$ext_name])
|
814 |
-
? fw_akg('thumbnail', $lists['installed'][$ext_name]['manifest'], $this->default_thumbnail)
|
815 |
-
: $this->default_thumbnail),
|
816 |
-
'display' => isset($ext_data['display'])
|
817 |
-
? $ext_data['display']
|
818 |
-
: $this->manifest_default_values['display'],
|
819 |
-
);
|
820 |
-
}
|
821 |
-
|
822 |
-
foreach (fw()->theme->manifest->get('supported_extensions', array()) as $required_ext_name => $required_ext_data) {
|
823 |
-
if (isset($lists['installed'][ $required_ext_name ])) {
|
824 |
-
$lists['supported'][ $required_ext_name ] = array(
|
825 |
-
'name' => fw_akg( 'name', $lists['installed'][ $required_ext_name ]['manifest'], fw_id_to_title( $required_ext_name ) ),
|
826 |
-
'description' => fw_akg( 'description', $lists['installed'][ $required_ext_name ]['manifest'], '' ),
|
827 |
-
);
|
828 |
-
} elseif (isset($lists['available'][$required_ext_name])) {
|
829 |
-
$lists['supported'][ $required_ext_name ] = array(
|
830 |
-
'name' => $lists['available'][ $required_ext_name ]['name'],
|
831 |
-
'description' => $lists['available'][ $required_ext_name ]['description'],
|
832 |
-
);
|
833 |
-
} else {
|
834 |
-
$lists['supported'][ $required_ext_name ] = array(
|
835 |
-
'name' => fw_id_to_title( $required_ext_name ),
|
836 |
-
'description' => '',
|
837 |
-
);
|
838 |
-
}
|
839 |
-
}
|
840 |
-
}
|
841 |
-
|
842 |
-
echo '<div class="wrap">';
|
843 |
-
|
844 |
-
echo '<h2>'. sprintf(__('%s Extensions', 'fw'), fw()->manifest->get_name()) .'</h2><br/>';
|
845 |
-
|
846 |
-
echo '<div id="fw-extensions-list-wrapper">';
|
847 |
-
|
848 |
-
fw_render_view(dirname(__FILE__) .'/views/extensions-page.php', array(
|
849 |
-
'lists' => &$lists,
|
850 |
-
'link' => $this->get_link(),
|
851 |
-
'display_default_value' => $this->manifest_default_values['display'],
|
852 |
-
'default_thumbnail' => $this->default_thumbnail,
|
853 |
-
'nonces' => array(
|
854 |
-
'delete' => $this->get_nonce('delete'),
|
855 |
-
'install' => $this->get_nonce('install'),
|
856 |
-
'activate' => $this->get_nonce('activate'),
|
857 |
-
'deactivate' => $this->get_nonce('deactivate'),
|
858 |
-
),
|
859 |
-
'can_install' => $this->can_install(),
|
860 |
-
), false);
|
861 |
-
|
862 |
-
echo '</div>';
|
863 |
-
|
864 |
-
echo '</div>';
|
865 |
-
}
|
866 |
-
|
867 |
-
private function display_install_page()
|
868 |
-
{
|
869 |
-
$flash_id = 'fw_extensions_install';
|
870 |
-
|
871 |
-
if (!$this->can_install()) {
|
872 |
-
FW_Flash_Messages::add(
|
873 |
-
$flash_id,
|
874 |
-
__('You are not allowed to install extensions.', 'fw'),
|
875 |
-
'error'
|
876 |
-
);
|
877 |
-
$this->js_redirect();
|
878 |
-
return;
|
879 |
-
}
|
880 |
-
|
881 |
-
if (array_key_exists('supported', $_GET)) {
|
882 |
-
$supported = true;
|
883 |
-
$extensions = array_fill_keys(
|
884 |
-
array_keys($this->get_supported_extensions_for_install()),
|
885 |
-
array()
|
886 |
-
);
|
887 |
-
|
888 |
-
if (empty($extensions)) {
|
889 |
-
FW_Flash_Messages::add(
|
890 |
-
$flash_id,
|
891 |
-
__('All supported extensions are already installed.', 'fw'),
|
892 |
-
'info'
|
893 |
-
);
|
894 |
-
$this->js_redirect();
|
895 |
-
return;
|
896 |
-
}
|
897 |
-
} else {
|
898 |
-
$supported = false;
|
899 |
-
|
900 |
-
$extensions = array_fill_keys(
|
901 |
-
array_map( 'trim', explode( ',', FW_Request::GET( 'extension', '' ) )),
|
902 |
-
array()
|
903 |
-
);
|
904 |
-
|
905 |
-
// activate already installed extensions
|
906 |
-
$this->activate_extensions($extensions);
|
907 |
-
}
|
908 |
-
|
909 |
-
{
|
910 |
-
if (!class_exists('_FW_Extensions_Install_Upgrader_Skin')) {
|
911 |
-
fw_include_file_isolated(
|
912 |
-
dirname(__FILE__) .'/includes/class--fw-extensions-install-upgrader-skin.php'
|
913 |
-
);
|
914 |
-
}
|
915 |
-
|
916 |
-
$skin = new _FW_Extensions_Install_Upgrader_Skin(array(
|
917 |
-
'title' => $supported
|
918 |
-
? _n('Install Compatible Extension', 'Install Compatible Extensions', count($extensions), 'fw')
|
919 |
-
: _n('Install Extension', 'Install Extensions', count($extensions), 'fw'),
|
920 |
-
));
|
921 |
-
}
|
922 |
-
|
923 |
-
$skin->header();
|
924 |
-
|
925 |
-
do {
|
926 |
-
$nonce = $this->get_nonce('install');
|
927 |
-
|
928 |
-
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
929 |
-
if (!isset($_POST[$nonce['name']]) || !wp_verify_nonce($_POST[$nonce['name']], $nonce['action'])) {
|
930 |
-
$skin->error(__('Invalid nonce.', 'fw'));
|
931 |
-
}
|
932 |
-
|
933 |
-
if (!FW_WP_Filesystem::request_access(
|
934 |
-
fw_get_framework_directory('/extensions'), fw_current_url(), array($nonce['name'])
|
935 |
-
)) {
|
936 |
-
break;
|
937 |
-
}
|
938 |
-
|
939 |
-
$install_result = $this->install_extensions($extensions, array('verbose' => $skin));
|
940 |
-
|
941 |
-
if (is_wp_error($install_result)) {
|
942 |
-
$skin->error($install_result);
|
943 |
-
} elseif (is_array($install_result)) {
|
944 |
-
$error = array();
|
945 |
-
|
946 |
-
foreach ($install_result as $extension_name => $extension_result) {
|
947 |
-
if (is_wp_error($extension_result)) {
|
948 |
-
$error[] = $extension_result->get_error_message();
|
949 |
-
}
|
950 |
-
}
|
951 |
-
|
952 |
-
$error = '<ul><li>'. implode('</li><li>', $error) .'</li></ul>';
|
953 |
-
|
954 |
-
$skin->error($error);
|
955 |
-
} elseif ($install_result === true) {
|
956 |
-
$skin->set_result(true);
|
957 |
-
}
|
958 |
-
|
959 |
-
/** @var WP_Filesystem_Base $wp_filesystem */
|
960 |
-
global $wp_filesystem;
|
961 |
-
|
962 |
-
$wp_fs_tmp_dir = FW_WP_Filesystem::real_path_to_filesystem_path($this->get_tmp_dir());
|
963 |
-
|
964 |
-
if ($wp_filesystem->exists($wp_fs_tmp_dir)) {
|
965 |
-
if ( ! $wp_filesystem->rmdir( $wp_fs_tmp_dir, true ) ) {
|
966 |
-
$skin->error(
|
967 |
-
sprintf( __( 'Cannot remove temporary directory: %s', 'fw' ), $wp_fs_tmp_dir )
|
968 |
-
);
|
969 |
-
}
|
970 |
-
}
|
971 |
-
|
972 |
-
$skin->after(array(
|
973 |
-
'extensions_page_link' => $this->get_link()
|
974 |
-
));
|
975 |
-
} else {
|
976 |
-
echo '<form method="post">';
|
977 |
-
|
978 |
-
wp_nonce_field($nonce['action'], $nonce['name']);
|
979 |
-
|
980 |
-
$extension_titles = array();
|
981 |
-
foreach ($extensions as $extension_name => $not_used_var) {
|
982 |
-
$extension_titles[$extension_name] = $this->get_extension_title($extension_name);
|
983 |
-
}
|
984 |
-
|
985 |
-
fw_render_view(dirname(__FILE__) .'/views/install-form.php', array(
|
986 |
-
'extension_titles' => $extension_titles,
|
987 |
-
'list_page_link' => $this->get_link(),
|
988 |
-
'supported' => $supported
|
989 |
-
), false);
|
990 |
-
|
991 |
-
echo '</form>';
|
992 |
-
}
|
993 |
-
} while(false);
|
994 |
-
|
995 |
-
$skin->footer();
|
996 |
-
}
|
997 |
-
|
998 |
-
/**
|
999 |
-
* Download (and activate) extensions
|
1000 |
-
* After refresh they should be active, if all dependencies will be met and if parent-extension::_init() will not return false
|
1001 |
-
* @param array $extensions {'ext_1' => array(), 'ext_2' => array(), ...}
|
1002 |
-
* @param array $opts
|
1003 |
-
* @return WP_Error|bool|array
|
1004 |
-
* true: when all extensions succeeded
|
1005 |
-
* array: when some/all failed
|
1006 |
-
*/
|
1007 |
-
public function install_extensions(array $extensions, $opts = array())
|
1008 |
-
{
|
1009 |
-
{
|
1010 |
-
$opts = array_merge(array(
|
1011 |
-
/**
|
1012 |
-
* @type bool
|
1013 |
-
* false: return {'ext_1' => true|WP_Error, 'ext_2' => true|WP_Error, ...}
|
1014 |
-
* true: return first WP_Error or true on success
|
1015 |
-
*/
|
1016 |
-
'cancel_on_error' => false,
|
1017 |
-
/**
|
1018 |
-
* @type bool Activate installed extensions
|
1019 |
-
*/
|
1020 |
-
'activate' => true,
|
1021 |
-
/**
|
1022 |
-
* @type bool|WP_Upgrader_Skin
|
1023 |
-
*/
|
1024 |
-
'verbose' => false,
|
1025 |
-
), $opts);
|
1026 |
-
|
1027 |
-
$cancel_on_error = $opts['cancel_on_error']; // fixme: remove successfully installed extensions before error?
|
1028 |
-
$activate = $opts['activate'];
|
1029 |
-
$verbose = $opts['verbose'];
|
1030 |
-
|
1031 |
-
unset($opts);
|
1032 |
-
}
|
1033 |
-
|
1034 |
-
if (!$this->can_install()) {
|
1035 |
-
return new WP_Error(
|
1036 |
-
'access_denied',
|
1037 |
-
__('You have no permissions to install extensions', 'fw')
|
1038 |
-
);
|
1039 |
-
}
|
1040 |
-
|
1041 |
-
if (empty($extensions)) {
|
1042 |
-
return new WP_Error(
|
1043 |
-
'no_extensions',
|
1044 |
-
__('No extensions provided', 'fw')
|
1045 |
-
);
|
1046 |
-
}
|
1047 |
-
|
1048 |
-
global $wp_filesystem;
|
1049 |
-
|
1050 |
-
if (!$wp_filesystem || (is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code())) {
|
1051 |
-
return new WP_Error(
|
1052 |
-
'fs_not_initialized',
|
1053 |
-
__('WP Filesystem is not initialized', 'fw')
|
1054 |
-
);
|
1055 |
-
}
|
1056 |
-
|
1057 |
-
if (function_exists('ini_get')) {
|
1058 |
-
$timeout = intval(ini_get('max_execution_time'));
|
1059 |
-
} else {
|
1060 |
-
$timeout = false;
|
1061 |
-
}
|
1062 |
-
|
1063 |
-
$available_extensions = $this->get_available_extensions();
|
1064 |
-
$installed_extensions = $this->get_installed_extensions();
|
1065 |
-
|
1066 |
-
$result = $downloaded_extensions = array();
|
1067 |
-
$has_errors = false;
|
1068 |
-
|
1069 |
-
while (!empty($extensions)) {
|
1070 |
-
$not_used_var = reset($extensions);
|
1071 |
-
$extension_name = key($extensions);
|
1072 |
-
unset($extensions[$extension_name]);
|
1073 |
-
|
1074 |
-
$extensions_before_install = array_keys($installed_extensions);
|
1075 |
-
|
1076 |
-
if (isset($installed_extensions[$extension_name])) {
|
1077 |
-
$result[$extension_name] = new WP_Error(
|
1078 |
-
'extension_installed',
|
1079 |
-
sprintf(__('Extension "%s" is already installed.', 'fw'), $this->get_extension_title($extension_name))
|
1080 |
-
);
|
1081 |
-
$has_errors = true;
|
1082 |
-
|
1083 |
-
if ($cancel_on_error) {
|
1084 |
-
break;
|
1085 |
-
} else {
|
1086 |
-
continue;
|
1087 |
-
}
|
1088 |
-
}
|
1089 |
-
|
1090 |
-
if (!isset($available_extensions[ $extension_name ])) {
|
1091 |
-
$result[$extension_name] = new WP_Error(
|
1092 |
-
'extension_not_available',
|
1093 |
-
sprintf(
|
1094 |
-
__('Extension "%s" is not available for install.', 'fw'),
|
1095 |
-
$this->get_extension_title($extension_name)
|
1096 |
-
)
|
1097 |
-
);
|
1098 |
-
$has_errors = true;
|
1099 |
-
|
1100 |
-
if ($cancel_on_error) {
|
1101 |
-
break;
|
1102 |
-
} else {
|
1103 |
-
continue;
|
1104 |
-
}
|
1105 |
-
}
|
1106 |
-
|
1107 |
-
/**
|
1108 |
-
* Find parent extensions
|
1109 |
-
* they will be installed if does not exist
|
1110 |
-
*/
|
1111 |
-
{
|
1112 |
-
$parents = array($extension_name);
|
1113 |
-
|
1114 |
-
$current_parent = $extension_name;
|
1115 |
-
while (!empty($available_extensions[$current_parent]['parent'])) {
|
1116 |
-
$current_parent = $available_extensions[$current_parent]['parent'];
|
1117 |
-
|
1118 |
-
if (!isset($available_extensions[$current_parent])) {
|
1119 |
-
$result[$extension_name] = new WP_Error(
|
1120 |
-
'parent_extension_not_available',
|
1121 |
-
sprintf(
|
1122 |
-
__('Parent extension "%s" not available.', 'fw'),
|
1123 |
-
$this->get_extension_title($current_parent)
|
1124 |
-
)
|
1125 |
-
);
|
1126 |
-
$has_errors = true;
|
1127 |
-
|
1128 |
-
if ($cancel_on_error) {
|
1129 |
-
break 2;
|
1130 |
-
} else {
|
1131 |
-
continue 2;
|
1132 |
-
}
|
1133 |
-
}
|
1134 |
-
|
1135 |
-
$parents[] = $current_parent;
|
1136 |
-
}
|
1137 |
-
|
1138 |
-
$parents = array_reverse($parents);
|
1139 |
-
}
|
1140 |
-
|
1141 |
-
/**
|
1142 |
-
* Install parent extensions and the extension
|
1143 |
-
*/
|
1144 |
-
{
|
1145 |
-
$current_extension_path = fw_get_framework_directory();
|
1146 |
-
|
1147 |
-
foreach ($parents as $parent_extension_name) {
|
1148 |
-
$current_extension_path .= '/extensions/'. $parent_extension_name;
|
1149 |
-
|
1150 |
-
if (isset($installed_extensions[$parent_extension_name])) {
|
1151 |
-
// skip already installed extensions
|
1152 |
-
continue;
|
1153 |
-
}
|
1154 |
-
|
1155 |
-
if ($verbose) {
|
1156 |
-
$verbose_message = sprintf(__('Downloading the "%s" extension...', 'fw'),
|
1157 |
-
$this->get_extension_title($parent_extension_name)
|
1158 |
-
);
|
1159 |
-
|
1160 |
-
if (is_subclass_of($verbose, 'WP_Upgrader_Skin')) {
|
1161 |
-
$verbose->feedback($verbose_message);
|
1162 |
-
} else {
|
1163 |
-
echo fw_html_tag('p', array(), $verbose_message);
|
1164 |
-
}
|
1165 |
-
}
|
1166 |
-
|
1167 |
-
// increase timeout
|
1168 |
-
if ($timeout !== false && function_exists('set_time_limit')) {
|
1169 |
-
$timeout += 30;
|
1170 |
-
set_time_limit($timeout);
|
1171 |
-
}
|
1172 |
-
|
1173 |
-
$wp_fw_downloaded_dir = $this->download(
|
1174 |
-
$parent_extension_name,
|
1175 |
-
$available_extensions[$parent_extension_name]
|
1176 |
-
);
|
1177 |
-
|
1178 |
-
if (is_wp_error($wp_fw_downloaded_dir)) {
|
1179 |
-
if ($verbose) {
|
1180 |
-
$verbose_message = $wp_fw_downloaded_dir->get_error_message();
|
1181 |
-
|
1182 |
-
if (is_subclass_of($verbose, 'WP_Upgrader_Skin')) {
|
1183 |
-
$verbose->error($verbose_message);
|
1184 |
-
} else {
|
1185 |
-
echo fw_html_tag('p', array(), $verbose_message);
|
1186 |
-
}
|
1187 |
-
}
|
1188 |
-
|
1189 |
-
$result[$extension_name] = $wp_fw_downloaded_dir;
|
1190 |
-
$has_errors = true;
|
1191 |
-
|
1192 |
-
if ($cancel_on_error) {
|
1193 |
-
break 2;
|
1194 |
-
} else {
|
1195 |
-
continue 2;
|
1196 |
-
}
|
1197 |
-
}
|
1198 |
-
|
1199 |
-
if ($verbose) {
|
1200 |
-
$verbose_message = sprintf(__('Installing the "%s" extension...', 'fw'),
|
1201 |
-
$this->get_extension_title($parent_extension_name)
|
1202 |
-
);
|
1203 |
-
|
1204 |
-
if (is_subclass_of($verbose, 'WP_Upgrader_Skin')) {
|
1205 |
-
$verbose->feedback($verbose_message);
|
1206 |
-
} else {
|
1207 |
-
echo fw_html_tag('p', array(), $verbose_message);
|
1208 |
-
}
|
1209 |
-
}
|
1210 |
-
|
1211 |
-
$merge_result = $this->merge_extension(
|
1212 |
-
$wp_fw_downloaded_dir,
|
1213 |
-
FW_WP_Filesystem::real_path_to_filesystem_path($current_extension_path)
|
1214 |
-
);
|
1215 |
-
|
1216 |
-
if (is_wp_error($merge_result)) {
|
1217 |
-
if ($verbose) {
|
1218 |
-
$verbose_message = $merge_result->get_error_message();
|
1219 |
-
|
1220 |
-
if (is_subclass_of($verbose, 'WP_Upgrader_Skin')) {
|
1221 |
-
$verbose->error($verbose_message);
|
1222 |
-
} else {
|
1223 |
-
echo fw_html_tag('p', array(), $verbose_message);
|
1224 |
-
}
|
1225 |
-
}
|
1226 |
-
|
1227 |
-
$result[$extension_name] = $merge_result;
|
1228 |
-
$has_errors = true;
|
1229 |
-
|
1230 |
-
if ($cancel_on_error) {
|
1231 |
-
break 2;
|
1232 |
-
} else {
|
1233 |
-
continue 2;
|
1234 |
-
}
|
1235 |
-
}
|
1236 |
-
|
1237 |
-
if ($verbose) {
|
1238 |
-
$verbose_message = sprintf(__('The %s extension has been successfully installed.', 'fw'),
|
1239 |
-
$this->get_extension_title($parent_extension_name)
|
1240 |
-
);
|
1241 |
-
|
1242 |
-
if (is_subclass_of($verbose, 'WP_Upgrader_Skin')) {
|
1243 |
-
$verbose->feedback($verbose_message);
|
1244 |
-
} else {
|
1245 |
-
echo fw_html_tag('p', array(), $verbose_message);
|
1246 |
-
}
|
1247 |
-
}
|
1248 |
-
|
1249 |
-
$downloaded_extensions[$parent_extension_name] = array();
|
1250 |
-
|
1251 |
-
/**
|
1252 |
-
* Read again all extensions
|
1253 |
-
* The downloaded extension may contain more sub extensions
|
1254 |
-
*/
|
1255 |
-
{
|
1256 |
-
unset($installed_extensions);
|
1257 |
-
$installed_extensions = $this->get_installed_extensions(true);
|
1258 |
-
}
|
1259 |
-
}
|
1260 |
-
}
|
1261 |
-
|
1262 |
-
$result[$extension_name] = true;
|
1263 |
-
|
1264 |
-
/**
|
1265 |
-
* Collect required extensions of the newly installed extensions
|
1266 |
-
*/
|
1267 |
-
foreach (
|
1268 |
-
// new extensions
|
1269 |
-
array_diff(
|
1270 |
-
array_keys($installed_extensions),
|
1271 |
-
$extensions_before_install
|
1272 |
-
)
|
1273 |
-
as $new_extension_name
|
1274 |
-
) {
|
1275 |
-
foreach (
|
1276 |
-
array_keys(
|
1277 |
-
fw_akg(
|
1278 |
-
'requirements/extensions',
|
1279 |
-
$installed_extensions[$new_extension_name]['manifest'],
|
1280 |
-
array()
|
1281 |
-
)
|
1282 |
-
)
|
1283 |
-
as $required_extension_name
|
1284 |
-
) {
|
1285 |
-
if (isset($installed_extensions[$required_extension_name])) {
|
1286 |
-
// already installed
|
1287 |
-
continue;
|
1288 |
-
}
|
1289 |
-
|
1290 |
-
$extensions[$required_extension_name] = array();
|
1291 |
-
}
|
1292 |
-
}
|
1293 |
-
}
|
1294 |
-
|
1295 |
-
if ($activate) {
|
1296 |
-
$activate_extensions = array();
|
1297 |
-
|
1298 |
-
foreach ($result as $extension_name => $extension_result) {
|
1299 |
-
if (!is_wp_error($extension_result)) {
|
1300 |
-
$activate_extensions[$extension_name] = array();
|
1301 |
-
}
|
1302 |
-
}
|
1303 |
-
|
1304 |
-
if (!empty($activate_extensions)) {
|
1305 |
-
if ($verbose) {
|
1306 |
-
$verbose_message = _n(
|
1307 |
-
'Activating extension...',
|
1308 |
-
'Activating extensions...',
|
1309 |
-
count($activate_extensions),
|
1310 |
-
'fw'
|
1311 |
-
);
|
1312 |
-
|
1313 |
-
if (is_subclass_of($verbose, 'WP_Upgrader_Skin')) {
|
1314 |
-
$verbose->feedback($verbose_message);
|
1315 |
-
} else {
|
1316 |
-
echo fw_html_tag('p', array(), $verbose_message);
|
1317 |
-
}
|
1318 |
-
}
|
1319 |
-
|
1320 |
-
$activation_result = $this->activate_extensions($activate_extensions);
|
1321 |
-
|
1322 |
-
if ($verbose) {
|
1323 |
-
if (is_wp_error($activation_result)) {
|
1324 |
-
if (is_subclass_of($verbose, 'WP_Upgrader_Skin')) {
|
1325 |
-
$verbose->error($activation_result->get_error_message());
|
1326 |
-
} else {
|
1327 |
-
echo fw_html_tag('p', array(), $activation_result->get_error_message());
|
1328 |
-
}
|
1329 |
-
} elseif (is_array($activation_result)) {
|
1330 |
-
$verbose_message = array();
|
1331 |
-
|
1332 |
-
foreach ($activation_result as $extension_name => $extension_result) {
|
1333 |
-
if (is_wp_error($extension_result)) {
|
1334 |
-
$verbose_message[] = $extension_result->get_error_message();
|
1335 |
-
}
|
1336 |
-
}
|
1337 |
-
|
1338 |
-
$verbose_message = '<ul><li>' . implode('</li><li>', $verbose_message) . '</li></ul>';
|
1339 |
-
|
1340 |
-
if (is_subclass_of($verbose, 'WP_Upgrader_Skin')) {
|
1341 |
-
$verbose->error($verbose_message);
|
1342 |
-
} else {
|
1343 |
-
echo fw_html_tag('p', array(), $verbose_message);
|
1344 |
-
}
|
1345 |
-
} elseif ($activation_result === true) {
|
1346 |
-
$verbose_message = _n(
|
1347 |
-
'Extension has been successfully activated.',
|
1348 |
-
'Extensions has been successfully activated.',
|
1349 |
-
count($activate_extensions),
|
1350 |
-
'fw'
|
1351 |
-
);
|
1352 |
-
|
1353 |
-
if (is_subclass_of($verbose, 'WP_Upgrader_Skin')) {
|
1354 |
-
$verbose->feedback($verbose_message);
|
1355 |
-
} else {
|
1356 |
-
echo fw_html_tag('p', array(), $verbose_message);
|
1357 |
-
}
|
1358 |
-
}
|
1359 |
-
}
|
1360 |
-
}
|
1361 |
-
}
|
1362 |
-
|
1363 |
-
do_action('fw_extensions_install', $result);
|
1364 |
-
|
1365 |
-
if (
|
1366 |
-
$cancel_on_error
|
1367 |
-
&&
|
1368 |
-
$has_errors
|
1369 |
-
) {
|
1370 |
-
if (
|
1371 |
-
($last_result = end($result))
|
1372 |
-
&&
|
1373 |
-
is_wp_error($last_result)
|
1374 |
-
) {
|
1375 |
-
return $last_result;
|
1376 |
-
} else {
|
1377 |
-
// this should not happen, but just to be sure (for the future, if the code above will be changed)
|
1378 |
-
return new WP_Error(
|
1379 |
-
'installation_failed',
|
1380 |
-
_n('Cannot install extension', 'Cannot install extensions', count($extensions), 'fw')
|
1381 |
-
);
|
1382 |
-
}
|
1383 |
-
}
|
1384 |
-
|
1385 |
-
if ($has_errors) {
|
1386 |
-
return $result;
|
1387 |
-
} else {
|
1388 |
-
return true;
|
1389 |
-
}
|
1390 |
-
}
|
1391 |
-
|
1392 |
-
private function display_delete_page()
|
1393 |
-
{
|
1394 |
-
$flash_id = 'fw_extensions_delete';
|
1395 |
-
|
1396 |
-
if (!$this->can_install()) {
|
1397 |
-
FW_Flash_Messages::add(
|
1398 |
-
$flash_id,
|
1399 |
-
__('You are not allowed to delete extensions.', 'fw'),
|
1400 |
-
'error'
|
1401 |
-
);
|
1402 |
-
$this->js_redirect();
|
1403 |
-
return;
|
1404 |
-
}
|
1405 |
-
|
1406 |
-
$extensions = array_fill_keys(array_map('trim', explode(',', FW_Request::GET('extension', ''))), array());
|
1407 |
-
|
1408 |
-
{
|
1409 |
-
if (!class_exists('_FW_Extensions_Delete_Upgrader_Skin')) {
|
1410 |
-
fw_include_file_isolated(
|
1411 |
-
dirname(__FILE__) .'/includes/class--fw-extensions-delete-upgrader-skin.php'
|
1412 |
-
);
|
1413 |
-
}
|
1414 |
-
|
1415 |
-
$skin = new _FW_Extensions_Delete_Upgrader_Skin(array(
|
1416 |
-
'title' => _n('Delete Extension', 'Delete Extensions', count($extensions), 'fw'),
|
1417 |
-
));
|
1418 |
-
}
|
1419 |
-
|
1420 |
-
$skin->header();
|
1421 |
-
|
1422 |
-
do {
|
1423 |
-
$nonce = $this->get_nonce('delete');
|
1424 |
-
|
1425 |
-
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
1426 |
-
if (!isset($_POST[$nonce['name']]) || !wp_verify_nonce($_POST[$nonce['name']], $nonce['action'])) {
|
1427 |
-
$skin->error(__('Invalid nonce.', 'fw'));
|
1428 |
-
}
|
1429 |
-
|
1430 |
-
if (!FW_WP_Filesystem::request_access(
|
1431 |
-
fw_get_framework_directory('/extensions'), fw_current_url(), array($nonce['name'])
|
1432 |
-
)) {
|
1433 |
-
break;
|
1434 |
-
}
|
1435 |
-
|
1436 |
-
$uninstall_result = $this->uninstall_extensions($extensions, array('verbose' => $skin));
|
1437 |
-
|
1438 |
-
if (is_wp_error($uninstall_result)) {
|
1439 |
-
$skin->error($uninstall_result);
|
1440 |
-
} elseif (is_array($uninstall_result)) {
|
1441 |
-
$error = array();
|
1442 |
-
|
1443 |
-
foreach ($uninstall_result as $extension_name => $extension_result) {
|
1444 |
-
if (is_wp_error($extension_result)) {
|
1445 |
-
$error[] = $extension_result->get_error_message();
|
1446 |
-
}
|
1447 |
-
}
|
1448 |
-
|
1449 |
-
$error = '<ul><li>'. implode('</li><li>', $error) .'</li></ul>';
|
1450 |
-
|
1451 |
-
$skin->error($error);
|
1452 |
-
} elseif ($uninstall_result === true) {
|
1453 |
-
$skin->set_result(true);
|
1454 |
-
}
|
1455 |
-
|
1456 |
-
$skin->after(array(
|
1457 |
-
'extensions_page_link' => $this->get_link()
|
1458 |
-
));
|
1459 |
-
} else {
|
1460 |
-
echo '<form method="post">';
|
1461 |
-
|
1462 |
-
wp_nonce_field($nonce['action'], $nonce['name']);
|
1463 |
-
|
1464 |
-
fw_render_view(dirname(__FILE__) .'/views/delete-form.php', array(
|
1465 |
-
'extension_names' => array_keys($extensions),
|
1466 |
-
'installed_extensions' => $this->get_installed_extensions(),
|
1467 |
-
'list_page_link' => $this->get_link(),
|
1468 |
-
), false);
|
1469 |
-
|
1470 |
-
echo '</form>';
|
1471 |
-
}
|
1472 |
-
} while(false);
|
1473 |
-
|
1474 |
-
$skin->footer();
|
1475 |
-
}
|
1476 |
-
|
1477 |
-
/**
|
1478 |
-
* Remove extensions
|
1479 |
-
* @param array $extensions {'ext_1' => array(), 'ext_2' => array(), ...}
|
1480 |
-
* @param array $opts
|
1481 |
-
* @return WP_Error|bool|array
|
1482 |
-
* true: when all extensions succeeded
|
1483 |
-
* array: when some/all failed
|
1484 |
-
*/
|
1485 |
-
public function uninstall_extensions(array $extensions, $opts = array())
|
1486 |
-
{
|
1487 |
-
{
|
1488 |
-
$opts = array_merge(array(
|
1489 |
-
/**
|
1490 |
-
* @type bool
|
1491 |
-
* false: return {'ext_1' => true|WP_Error, 'ext_2' => true|WP_Error, ...}
|
1492 |
-
* true: return first WP_Error or true on success
|
1493 |
-
*/
|
1494 |
-
'cancel_on_error' => false,
|
1495 |
-
/**
|
1496 |
-
* @type bool|WP_Upgrader_Skin
|
1497 |
-
*/
|
1498 |
-
'verbose' => false,
|
1499 |
-
), $opts);
|
1500 |
-
|
1501 |
-
$cancel_on_error = $opts['cancel_on_error']; // fixme: install back successfully removed extensions before error?
|
1502 |
-
$verbose = $opts['verbose'];
|
1503 |
-
|
1504 |
-
unset($opts);
|
1505 |
-
}
|
1506 |
-
|
1507 |
-
if (!$this->can_install()) {
|
1508 |
-
return new WP_Error(
|
1509 |
-
'access_denied',
|
1510 |
-
__('You have no permissions to uninstall extensions', 'fw')
|
1511 |
-
);
|
1512 |
-
}
|
1513 |
-
|
1514 |
-
if (empty($extensions)) {
|
1515 |
-
return new WP_Error(
|
1516 |
-
'no_extensions',
|
1517 |
-
__('No extensions provided', 'fw')
|
1518 |
-
);
|
1519 |
-
}
|
1520 |
-
|
1521 |
-
/** @var WP_Filesystem_Base $wp_filesystem */
|
1522 |
-
global $wp_filesystem;
|
1523 |
-
|
1524 |
-
if (!$wp_filesystem || (is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code())) {
|
1525 |
-
return new WP_Error(
|
1526 |
-
'fs_not_initialized',
|
1527 |
-
__('WP Filesystem is not initialized', 'fw')
|
1528 |
-
);
|
1529 |
-
}
|
1530 |
-
|
1531 |
-
$installed_extensions = $this->get_installed_extensions();
|
1532 |
-
$extensions_before_uninstall = array_fill_keys(array_keys($installed_extensions), array());
|
1533 |
-
|
1534 |
-
$result = $uninstalled_extensions = array();
|
1535 |
-
$has_errors = false;
|
1536 |
-
|
1537 |
-
while (!empty($extensions)) {
|
1538 |
-
$not_used_var = reset($extensions);
|
1539 |
-
$extension_name = key($extensions);
|
1540 |
-
unset($extensions[$extension_name]);
|
1541 |
-
|
1542 |
-
$extension_title = $this->get_extension_title($extension_name);
|
1543 |
-
|
1544 |
-
if (!isset($installed_extensions[ $extension_name ])) {
|
1545 |
-
// already deleted
|
1546 |
-
$result[$extension_name] = true;
|
1547 |
-
continue;
|
1548 |
-
}
|
1549 |
-
|
1550 |
-
if (
|
1551 |
-
!isset($installed_extensions[ $extension_name ]['path'])
|
1552 |
-
||
|
1553 |
-
empty($installed_extensions[ $extension_name ]['path'])
|
1554 |
-
) {
|
1555 |
-
/**
|
1556 |
-
* This happens sometimes, but I don't know why
|
1557 |
-
* If the script will continue, it will delete the root folder
|
1558 |
-
*/
|
1559 |
-
fw_print(
|
1560 |
-
'Please report this to https://github.com/ThemeFuse/Unyson/issues',
|
1561 |
-
$extension_name,
|
1562 |
-
$installed_extensions
|
1563 |
-
);
|
1564 |
-
die;
|
1565 |
-
}
|
1566 |
-
|
1567 |
-
$wp_fs_extension_path = FW_WP_Filesystem::real_path_to_filesystem_path(
|
1568 |
-
$installed_extensions[ $extension_name ]['path']
|
1569 |
-
);
|
1570 |
-
|
1571 |
-
if (!$wp_filesystem->exists($wp_fs_extension_path)) {
|
1572 |
-
// already deleted, maybe because it was a sub-extension of an deleted extension
|
1573 |
-
$result[$extension_name] = true;
|
1574 |
-
continue;
|
1575 |
-
}
|
1576 |
-
|
1577 |
-
if ($verbose) {
|
1578 |
-
$verbose_message = sprintf(__('Deleting the "%s" extension...', 'fw'), $extension_title);
|
1579 |
-
|
1580 |
-
if (is_subclass_of($verbose, 'WP_Upgrader_Skin')) {
|
1581 |
-
$verbose->feedback($verbose_message);
|
1582 |
-
} else {
|
1583 |
-
echo fw_html_tag('p', array(), $verbose_message);
|
1584 |
-
}
|
1585 |
-
}
|
1586 |
-
|
1587 |
-
if (!$wp_filesystem->delete($wp_fs_extension_path, true, 'd')) {
|
1588 |
-
$result[$extension_name] = new WP_Error(
|
1589 |
-
'cannot_delete_directory',
|
1590 |
-
sprintf(__('Cannot delete the "%s" extension.', 'fw'), $extension_title)
|
1591 |
-
);
|
1592 |
-
$has_errors = true;
|
1593 |
-
|
1594 |
-
if ($cancel_on_error) {
|
1595 |
-
break;
|
1596 |
-
} else {
|
1597 |
-
continue;
|
1598 |
-
}
|
1599 |
-
} else {
|
1600 |
-
if ($verbose) {
|
1601 |
-
$verbose_message = sprintf(
|
1602 |
-
__('The %s extension has been successfully deleted.', 'fw'),
|
1603 |
-
$extension_title
|
1604 |
-
);
|
1605 |
-
|
1606 |
-
if (is_subclass_of($verbose, 'WP_Upgrader_Skin')) {
|
1607 |
-
$verbose->feedback($verbose_message);
|
1608 |
-
} else {
|
1609 |
-
echo fw_html_tag('p', array(), $verbose_message);
|
1610 |
-
}
|
1611 |
-
}
|
1612 |
-
|
1613 |
-
$result[$extension_name] = true;
|
1614 |
-
}
|
1615 |
-
|
1616 |
-
/**
|
1617 |
-
* Read again all extensions
|
1618 |
-
* The delete extension may contain more sub extensions
|
1619 |
-
*/
|
1620 |
-
{
|
1621 |
-
unset($installed_extensions);
|
1622 |
-
$installed_extensions = $this->get_installed_extensions(true);
|
1623 |
-
}
|
1624 |
-
|
1625 |
-
/**
|
1626 |
-
* Add for deletion not used extensions
|
1627 |
-
* For e.g. standalone=false extension that were required by the deleted extension
|
1628 |
-
* and now are not required by any other extension
|
1629 |
-
*/
|
1630 |
-
{
|
1631 |
-
$not_used_extensions = array_fill_keys(
|
1632 |
-
array_keys(
|
1633 |
-
array_diff_key(
|
1634 |
-
$installed_extensions,
|
1635 |
-
$this->get_used_extensions($extensions, array_keys($installed_extensions))
|
1636 |
-
)
|
1637 |
-
),
|
1638 |
-
array()
|
1639 |
-
);
|
1640 |
-
|
1641 |
-
$extensions = array_merge($extensions, $not_used_extensions);
|
1642 |
-
}
|
1643 |
-
}
|
1644 |
-
|
1645 |
-
do_action('fw_extensions_uninstall', $result);
|
1646 |
-
|
1647 |
-
if (
|
1648 |
-
$cancel_on_error
|
1649 |
-
&&
|
1650 |
-
$has_errors
|
1651 |
-
) {
|
1652 |
-
if (
|
1653 |
-
($last_result = end($result))
|
1654 |
-
&&
|
1655 |
-
is_wp_error($last_result)
|
1656 |
-
) {
|
1657 |
-
return $last_result;
|
1658 |
-
} else {
|
1659 |
-
// this should not happen, but just to be sure (for the future, if the code above will be changed)
|
1660 |
-
return new WP_Error(
|
1661 |
-
'uninstall_failed',
|
1662 |
-
_n('Cannot uninstall extension', 'Cannot uninstall extensions', count($extensions), 'fw')
|
1663 |
-
);
|
1664 |
-
}
|
1665 |
-
}
|
1666 |
-
|
1667 |
-
// remove from active list the deleted extensions
|
1668 |
-
{
|
1669 |
-
update_option(
|
1670 |
-
fw()->extensions->_get_active_extensions_db_option_name(),
|
1671 |
-
array_diff_key(
|
1672 |
-
fw()->extensions->_get_db_active_extensions(),
|
1673 |
-
array_diff_key(
|
1674 |
-
$extensions_before_uninstall,
|
1675 |
-
$installed_extensions
|
1676 |
-
)
|
1677 |
-
)
|
1678 |
-
);
|
1679 |
-
}
|
1680 |
-
|
1681 |
-
if ($has_errors) {
|
1682 |
-
return $result;
|
1683 |
-
} else {
|
1684 |
-
return true;
|
1685 |
-
}
|
1686 |
-
}
|
1687 |
-
|
1688 |
-
private function display_extension_page()
|
1689 |
-
{
|
1690 |
-
// note: static is enqueued in 'admin_enqueue_scripts' action
|
1691 |
-
|
1692 |
-
$extension_name = trim(FW_Request::GET('extension', ''));
|
1693 |
-
|
1694 |
-
$installed_extensions = $this->get_installed_extensions();
|
1695 |
-
|
1696 |
-
$flash_id = 'fw_extension_page';
|
1697 |
-
|
1698 |
-
{
|
1699 |
-
$error = '';
|
1700 |
-
|
1701 |
-
do {
|
1702 |
-
if (empty($extension_name)) {
|
1703 |
-
$error = __('Extension not specified.', 'fw');
|
1704 |
-
break;
|
1705 |
-
}
|
1706 |
-
|
1707 |
-
if (!isset($installed_extensions[$extension_name])) {
|
1708 |
-
$error = sprintf(__('Extension "%s" is not installed.', 'fw'), $this->get_extension_title($extension_name));
|
1709 |
-
break;
|
1710 |
-
}
|
1711 |
-
} while(false);
|
1712 |
-
|
1713 |
-
if ($error) {
|
1714 |
-
FW_Flash_Messages::add($flash_id, $error, 'error');
|
1715 |
-
$this->js_redirect();
|
1716 |
-
return;
|
1717 |
-
}
|
1718 |
-
}
|
1719 |
-
|
1720 |
-
{
|
1721 |
-
$tab = fw_akg('tab', $_GET, 'settings');
|
1722 |
-
|
1723 |
-
if (!in_array($tab, array('settings', 'docs'))) {
|
1724 |
-
$tab = 'settings';
|
1725 |
-
}
|
1726 |
-
}
|
1727 |
-
|
1728 |
-
$extension_title = $this->get_extension_title($extension_name);
|
1729 |
-
$link = $this->get_link();
|
1730 |
-
|
1731 |
-
echo '<div class="wrap" id="fw-extension-page">';
|
1732 |
-
|
1733 |
-
fw_render_view(dirname(__FILE__) .'/views/extension-page-header.php', array(
|
1734 |
-
'extension_name' => $extension_name,
|
1735 |
-
'extension_data' => $installed_extensions[$extension_name],
|
1736 |
-
'link_delete' => $link .'&sub-page=delete',
|
1737 |
-
'link_extension' => $link .'&sub-page=extension',
|
1738 |
-
'extension_title' => $extension_title,
|
1739 |
-
'tab' => $tab,
|
1740 |
-
'is_supported' =>
|
1741 |
-
fw()->theme->manifest->get('supported_extensions/'. $extension_name, false) !== false
|
1742 |
-
||
|
1743 |
-
$installed_extensions[$extension_name]['is']['theme']
|
1744 |
-
), false);
|
1745 |
-
|
1746 |
-
unset($installed_extensions);
|
1747 |
-
|
1748 |
-
echo '<div id="fw-extension-tab-content">';
|
1749 |
-
{
|
1750 |
-
$method_data = array();
|
1751 |
-
|
1752 |
-
switch ($tab) {
|
1753 |
-
case 'settings':
|
1754 |
-
$error = $this->display_extension_settings_page($extension_name, $method_data);
|
1755 |
-
break;
|
1756 |
-
case 'docs':
|
1757 |
-
$error = $this->display_extension_docs_page($extension_name, $method_data);
|
1758 |
-
break;
|
1759 |
-
}
|
1760 |
-
}
|
1761 |
-
echo '</div>';
|
1762 |
-
|
1763 |
-
echo '</div>';
|
1764 |
-
|
1765 |
-
if ($error) {
|
1766 |
-
FW_Flash_Messages::add($flash_id, $error, 'error');
|
1767 |
-
$this->js_redirect();
|
1768 |
-
return;
|
1769 |
-
}
|
1770 |
-
}
|
1771 |
-
|
1772 |
-
private function display_extension_settings_page($extension_name, $data)
|
1773 |
-
{
|
1774 |
-
if (!fw()->extensions->get($extension_name)) {
|
1775 |
-
return sprintf(
|
1776 |
-
__('Extension "%s" does not exist or is not active.', 'fw'),
|
1777 |
-
fw_htmlspecialchars($extension_name)
|
1778 |
-
);
|
1779 |
-
}
|
1780 |
-
|
1781 |
-
$extension = fw()->extensions->get($extension_name);
|
1782 |
-
|
1783 |
-
if (!$extension->get_settings_options()) {
|
1784 |
-
return sprintf(
|
1785 |
-
__('%s extension does not have settings.', 'fw'),
|
1786 |
-
$extension->manifest->get_name()
|
1787 |
-
);
|
1788 |
-
}
|
1789 |
-
|
1790 |
-
echo '<div id="fw-extension-settings">';
|
1791 |
-
|
1792 |
-
echo $this->extension_settings_form->render(array(
|
1793 |
-
'extension' => $extension,
|
1794 |
-
));
|
1795 |
-
|
1796 |
-
echo '</div>';
|
1797 |
-
}
|
1798 |
-
|
1799 |
-
private function display_extension_docs_page($extension_name, $data)
|
1800 |
-
{
|
1801 |
-
$installed_extensions = $this->get_installed_extensions();
|
1802 |
-
$docs_path = $installed_extensions[$extension_name]['path'] .'/readme.md.php';
|
1803 |
-
unset($installed_extensions);
|
1804 |
-
|
1805 |
-
if (!file_exists($docs_path)) {
|
1806 |
-
return __('Extension has no Install Instructions', 'fw');
|
1807 |
-
}
|
1808 |
-
|
1809 |
-
echo fw()->backend->render_box(
|
1810 |
-
'fw-extension-docs',
|
1811 |
-
'',
|
1812 |
-
fw()->backend->render_options(array(
|
1813 |
-
'docs' => array(
|
1814 |
-
'label' => false,
|
1815 |
-
'type' => 'html-full',
|
1816 |
-
'html' => $this->get_markdown_parser()->text(
|
1817 |
-
fw_render_view($docs_path, array())
|
1818 |
-
),
|
1819 |
-
),
|
1820 |
-
))
|
1821 |
-
);
|
1822 |
-
}
|
1823 |
-
|
1824 |
-
private function display_activate_page()
|
1825 |
-
{
|
1826 |
-
$error = '';
|
1827 |
-
|
1828 |
-
do {
|
1829 |
-
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
1830 |
-
$error = __('Invalid request method.', 'fw');
|
1831 |
-
break;
|
1832 |
-
}
|
1833 |
-
|
1834 |
-
$nonce = $this->get_nonce('activate');
|
1835 |
-
|
1836 |
-
if (!wp_verify_nonce(FW_Request::POST($nonce['name']), $nonce['action'])) {
|
1837 |
-
$error = __('Invalid nonce.', 'fw');
|
1838 |
-
break;
|
1839 |
-
}
|
1840 |
-
|
1841 |
-
if (!isset($_GET['extension'])) {
|
1842 |
-
$error = __('No extension specified.', 'fw');
|
1843 |
-
break;
|
1844 |
-
}
|
1845 |
-
|
1846 |
-
$activation_result = $this->activate_extensions(
|
1847 |
-
array_fill_keys(explode(',', $_GET['extension']), array())
|
1848 |
-
);
|
1849 |
-
|
1850 |
-
if (is_wp_error($activation_result)) {
|
1851 |
-
$error = $activation_result->get_error_message();
|
1852 |
-
} elseif (is_array($activation_result)) {
|
1853 |
-
$error = array();
|
1854 |
-
|
1855 |
-
foreach ($activation_result as $extension_name => $extension_result) {
|
1856 |
-
if (is_wp_error($extension_result)) {
|
1857 |
-
$error[] = $extension_result->get_error_message();
|
1858 |
-
}
|
1859 |
-
}
|
1860 |
-
|
1861 |
-
$error = '<ul><li>'. implode('</li><li>', $error) .'</li></ul>';
|
1862 |
-
}
|
1863 |
-
} while(false);
|
1864 |
-
|
1865 |
-
if ($error) {
|
1866 |
-
FW_Flash_Messages::add(
|
1867 |
-
'fw_extensions_activate_page',
|
1868 |
-
$error,
|
1869 |
-
'error'
|
1870 |
-
);
|
1871 |
-
$this->js_redirect();
|
1872 |
-
return;
|
1873 |
-
}
|
1874 |
-
|
1875 |
-
$this->js_redirect();
|
1876 |
-
}
|
1877 |
-
|
1878 |
-
/**
|
1879 |
-
* Add extensions to active extensions list in database
|
1880 |
-
* After refresh they should be active, if all dependencies will be met and if parent-extension::_init() will not return false
|
1881 |
-
* @param array $extensions {'ext_1' => array(), 'ext_2' => array(), ...}
|
1882 |
-
* @param bool $cancel_on_error
|
1883 |
-
* false: return {'ext_1' => true|WP_Error, 'ext_2' => true|WP_Error, ...}
|
1884 |
-
* true: return first WP_Error or true on success
|
1885 |
-
* @return WP_Error|bool|array
|
1886 |
-
* true: when all extensions succeeded
|
1887 |
-
* array: when some/all failed
|
1888 |
-
*/
|
1889 |
-
public function activate_extensions(array $extensions, $cancel_on_error = false)
|
1890 |
-
{
|
1891 |
-
if (!$this->can_activate()) {
|
1892 |
-
return new WP_Error(
|
1893 |
-
'access_denied',
|
1894 |
-
__('You have no permissions to activate extensions', 'fw')
|
1895 |
-
);
|
1896 |
-
}
|
1897 |
-
|
1898 |
-
if (empty($extensions)) {
|
1899 |
-
return new WP_Error(
|
1900 |
-
'no_extensions',
|
1901 |
-
__('No extensions provided', 'fw')
|
1902 |
-
);
|
1903 |
-
}
|
1904 |
-
|
1905 |
-
$installed_extensions = $this->get_installed_extensions();
|
1906 |
-
|
1907 |
-
$result = $extensions_for_activation = array();
|
1908 |
-
$has_errors = false;
|
1909 |
-
|
1910 |
-
foreach ($extensions as $extension_name => $not_used_var) {
|
1911 |
-
if (!isset($installed_extensions[$extension_name])) {
|
1912 |
-
$result[$extension_name] = new WP_Error(
|
1913 |
-
'extension_not_installed',
|
1914 |
-
sprintf(__('Extension "%s" does not exist.', 'fw'), $this->get_extension_title($extension_name))
|
1915 |
-
);
|
1916 |
-
$has_errors = true;
|
1917 |
-
|
1918 |
-
if ($cancel_on_error) {
|
1919 |
-
break;
|
1920 |
-
} else {
|
1921 |
-
continue;
|
1922 |
-
}
|
1923 |
-
}
|
1924 |
-
|
1925 |
-
$collected = $this->get_extensions_for_activation($extension_name);
|
1926 |
-
|
1927 |
-
if (is_wp_error($collected)) {
|
1928 |
-
$result[$extension_name] = $collected;
|
1929 |
-
$has_errors = true;
|
1930 |
-
|
1931 |
-
if ($cancel_on_error) {
|
1932 |
-
break;
|
1933 |
-
} else {
|
1934 |
-
continue;
|
1935 |
-
}
|
1936 |
-
}
|
1937 |
-
|
1938 |
-
$extensions_for_activation = array_merge($extensions_for_activation, $collected);
|
1939 |
-
|
1940 |
-
$result[$extension_name] = true;
|
1941 |
-
}
|
1942 |
-
|
1943 |
-
if (
|
1944 |
-
$cancel_on_error
|
1945 |
-
&&
|
1946 |
-
$has_errors
|
1947 |
-
) {
|
1948 |
-
if (
|
1949 |
-
($last_result = end($result))
|
1950 |
-
&&
|
1951 |
-
is_wp_error($last_result)
|
1952 |
-
) {
|
1953 |
-
return $last_result;
|
1954 |
-
} else {
|
1955 |
-
// this should not happen, but just to be sure (for the future, if the code above will be changed)
|
1956 |
-
return new WP_Error(
|
1957 |
-
'activation_failed',
|
1958 |
-
_n('Cannot activate extension', 'Cannot activate extensions', count($extensions), 'fw')
|
1959 |
-
);
|
1960 |
-
}
|
1961 |
-
}
|
1962 |
-
|
1963 |
-
update_option(
|
1964 |
-
fw()->extensions->_get_active_extensions_db_option_name(),
|
1965 |
-
array_merge(fw()->extensions->_get_db_active_extensions(), $extensions_for_activation)
|
1966 |
-
);
|
1967 |
-
|
1968 |
-
// remove already active extensions
|
1969 |
-
foreach ($extensions_for_activation as $extension_name => $not_used_var) {
|
1970 |
-
if (fw_ext($extension_name)) {
|
1971 |
-
unset($extensions_for_activation[$extension_name]);
|
1972 |
-
}
|
1973 |
-
}
|
1974 |
-
|
1975 |
-
/**
|
1976 |
-
* Prepare db wp option used to fire the 'fw_extensions_after_activation' action on next refresh
|
1977 |
-
*/
|
1978 |
-
{
|
1979 |
-
$db_wp_option_name = 'fw_extensions_activation';
|
1980 |
-
$db_wp_option_value = get_option($db_wp_option_name, array(
|
1981 |
-
'activated' => array(),
|
1982 |
-
'deactivated' => array(),
|
1983 |
-
));
|
1984 |
-
|
1985 |
-
/**
|
1986 |
-
* Keep adding to the existing value instead of resetting it on each method call
|
1987 |
-
* in case the method will be called multiple times
|
1988 |
-
*/
|
1989 |
-
$db_wp_option_value['activated'] = array_merge($db_wp_option_value['activated'], $extensions_for_activation);
|
1990 |
-
|
1991 |
-
/**
|
1992 |
-
* Remove activated extensions from deactivated
|
1993 |
-
*/
|
1994 |
-
$db_wp_option_value['deactivated'] = array_diff_key($db_wp_option_value['deactivated'], $db_wp_option_value['activated']);
|
1995 |
-
|
1996 |
-
update_option($db_wp_option_name, $db_wp_option_value, false);
|
1997 |
-
}
|
1998 |
-
|
1999 |
-
do_action('fw_extensions_before_activation', $extensions_for_activation);
|
2000 |
-
|
2001 |
-
if ($has_errors) {
|
2002 |
-
return $result;
|
2003 |
-
} else {
|
2004 |
-
return true;
|
2005 |
-
}
|
2006 |
-
}
|
2007 |
-
|
2008 |
-
private function collect_sub_extensions($ext_name, &$installed_extensions)
|
2009 |
-
{
|
2010 |
-
$result = array();
|
2011 |
-
|
2012 |
-
foreach ($installed_extensions[$ext_name]['children'] as $child_ext_name => $child_ext_data) {
|
2013 |
-
$result[$child_ext_name] = array();
|
2014 |
-
|
2015 |
-
$result += $this->collect_sub_extensions($child_ext_name, $installed_extensions);
|
2016 |
-
}
|
2017 |
-
|
2018 |
-
return $result;
|
2019 |
-
}
|
2020 |
-
|
2021 |
-
private function collect_required_extensions($ext_name, &$installed_extensions, &$collected)
|
2022 |
-
{
|
2023 |
-
if (!isset($installed_extensions[$ext_name])) {
|
2024 |
-
return;
|
2025 |
-
}
|
2026 |
-
|
2027 |
-
foreach (fw_akg('requirements/extensions', $installed_extensions[$ext_name]['manifest'], array()) as $req_ext_name => $req_ext_data) {
|
2028 |
-
if (isset($collected[$req_ext_name])) {
|
2029 |
-
// prevent requirements recursion
|
2030 |
-
continue;
|
2031 |
-
}
|
2032 |
-
|
2033 |
-
$collected[$req_ext_name] = array();
|
2034 |
-
|
2035 |
-
$this->collect_required_extensions($req_ext_name, $installed_extensions, $collected);
|
2036 |
-
}
|
2037 |
-
}
|
2038 |
-
|
2039 |
-
private function display_deactivate_page()
|
2040 |
-
{
|
2041 |
-
$installed_extensions = $this->get_installed_extensions();
|
2042 |
-
|
2043 |
-
$error = '';
|
2044 |
-
|
2045 |
-
do {
|
2046 |
-
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
2047 |
-
$error = __('Invalid request method.', 'fw');
|
2048 |
-
break;
|
2049 |
-
}
|
2050 |
-
|
2051 |
-
$nonce = $this->get_nonce('deactivate');
|
2052 |
-
|
2053 |
-
if (!wp_verify_nonce(FW_Request::POST($nonce['name']), $nonce['action'])) {
|
2054 |
-
$error = __('Invalid nonce.', 'fw');
|
2055 |
-
break;
|
2056 |
-
}
|
2057 |
-
|
2058 |
-
if (!isset($_GET['extension'])) {
|
2059 |
-
$error = __('No extension specified.', 'fw');
|
2060 |
-
break;
|
2061 |
-
}
|
2062 |
-
|
2063 |
-
$deactivation_result = $this->deactivate_extensions(
|
2064 |
-
array_fill_keys(explode(',', $_GET['extension']), array())
|
2065 |
-
);
|
2066 |
-
|
2067 |
-
if (is_wp_error($deactivation_result)) {
|
2068 |
-
$error = $deactivation_result->get_error_message();
|
2069 |
-
} elseif (is_array($deactivation_result)) {
|
2070 |
-
$error = array();
|
2071 |
-
|
2072 |
-
foreach ($deactivation_result as $extension_name => $extension_result) {
|
2073 |
-
if (is_wp_error($extension_result)) {
|
2074 |
-
$error[] = $extension_result->get_error_message();
|
2075 |
-
}
|
2076 |
-
}
|
2077 |
-
|
2078 |
-
$error = '<ul><li>'. implode('</li><li>', $error) .'</li></ul>';
|
2079 |
-
}
|
2080 |
-
} while(false);
|
2081 |
-
|
2082 |
-
if ($error) {
|
2083 |
-
FW_Flash_Messages::add(
|
2084 |
-
'fw_extensions_activate_page',
|
2085 |
-
$error,
|
2086 |
-
'error'
|
2087 |
-
);
|
2088 |
-
}
|
2089 |
-
|
2090 |
-
$this->js_redirect();
|
2091 |
-
}
|
2092 |
-
|
2093 |
-
/**
|
2094 |
-
* Remove extensions from active extensions list in database
|
2095 |
-
* After refresh they will be inactive
|
2096 |
-
* @param array $extensions {'ext_1' => array(), 'ext_2' => array(), ...}
|
2097 |
-
* @param bool $cancel_on_error
|
2098 |
-
* false: return {'ext_1' => true|WP_Error, 'ext_2' => true|WP_Error, ...}
|
2099 |
-
* true: return first WP_Error or true on success
|
2100 |
-
* @return WP_Error|bool|array
|
2101 |
-
* true: when all extensions succeeded
|
2102 |
-
* array: when some/all failed
|
2103 |
-
*/
|
2104 |
-
public function deactivate_extensions(array $extensions, $cancel_on_error = false)
|
2105 |
-
{
|
2106 |
-
if (!$this->can_activate()) {
|
2107 |
-
return new WP_Error(
|
2108 |
-
'access_denied',
|
2109 |
-
__('You have no permissions to deactivate extensions', 'fw')
|
2110 |
-
);
|
2111 |
-
}
|
2112 |
-
|
2113 |
-
if (empty($extensions)) {
|
2114 |
-
return new WP_Error(
|
2115 |
-
'no_extensions',
|
2116 |
-
__('No extensions provided', 'fw')
|
2117 |
-
);
|
2118 |
-
}
|
2119 |
-
|
2120 |
-
$installed_extensions = $this->get_installed_extensions();
|
2121 |
-
|
2122 |
-
$result = $extensions_for_deactivation = array();
|
2123 |
-
$has_errors = false;
|
2124 |
-
|
2125 |
-
foreach ($extensions as $extension_name => $not_used_var) {
|
2126 |
-
if (!isset($installed_extensions[$extension_name])) {
|
2127 |
-
// anyway remove from the active list
|
2128 |
-
$extensions_for_deactivation[$extension_name] = array();
|
2129 |
-
|
2130 |
-
$result[$extension_name] = new WP_Error(
|
2131 |
-
'extension_not_installed',
|
2132 |
-
sprintf(__( 'Extension "%s" does not exist.' , 'fw' ), $this->get_extension_title($extension_name))
|
2133 |
-
);
|
2134 |
-
$has_errors = true;
|
2135 |
-
|
2136 |
-
if ($cancel_on_error) {
|
2137 |
-
break;
|
2138 |
-
} else {
|
2139 |
-
continue;
|
2140 |
-
}
|
2141 |
-
}
|
2142 |
-
|
2143 |
-
$current_deactivating_extensions = array(
|
2144 |
-
$extension_name => array()
|
2145 |
-
);
|
2146 |
-
|
2147 |
-
// add sub-extensions for deactivation
|
2148 |
-
foreach ($this->collect_sub_extensions($extension_name, $installed_extensions) as $sub_extension_name => $sub_extension_data) {
|
2149 |
-
$current_deactivating_extensions[ $sub_extension_name ] = array();
|
2150 |
-
}
|
2151 |
-
|
2152 |
-
// add extensions that requires deactivated extensions
|
2153 |
-
$this->collect_extensions_that_requires($current_deactivating_extensions, $current_deactivating_extensions);
|
2154 |
-
|
2155 |
-
$extensions_for_deactivation = array_merge(
|
2156 |
-
$extensions_for_deactivation,
|
2157 |
-
$current_deactivating_extensions
|
2158 |
-
);
|
2159 |
-
|
2160 |
-
unset($current_deactivating_extensions);
|
2161 |
-
|
2162 |
-
$result[$extension_name] = true;
|
2163 |
-
}
|
2164 |
-
|
2165 |
-
if (
|
2166 |
-
$cancel_on_error
|
2167 |
-
&&
|
2168 |
-
$has_errors
|
2169 |
-
) {
|
2170 |
-
if (
|
2171 |
-
($last_result = end($result))
|
2172 |
-
&&
|
2173 |
-
is_wp_error($last_result)
|
2174 |
-
) {
|
2175 |
-
return $last_result;
|
2176 |
-
} else {
|
2177 |
-
// this should not happen, but just to be sure (for the future, if the code above will be changed)
|
2178 |
-
return new WP_Error(
|
2179 |
-
'deactivation_failed',
|
2180 |
-
_n('Cannot deactivate extension', 'Cannot activate extensions', count($extensions), 'fw')
|
2181 |
-
);
|
2182 |
-
}
|
2183 |
-
}
|
2184 |
-
|
2185 |
-
// add not used extensions for deactivation
|
2186 |
-
$extensions_for_deactivation = array_merge($extensions_for_deactivation,
|
2187 |
-
array_fill_keys(
|
2188 |
-
array_keys(
|
2189 |
-
array_diff_key(
|
2190 |
-
$installed_extensions,
|
2191 |
-
$this->get_used_extensions($extensions_for_deactivation, array_keys(fw()->extensions->get_all()))
|
2192 |
-
)
|
2193 |
-
),
|
2194 |
-
array()
|
2195 |
-
)
|
2196 |
-
);
|
2197 |
-
|
2198 |
-
update_option(
|
2199 |
-
fw()->extensions->_get_active_extensions_db_option_name(),
|
2200 |
-
array_diff_key(
|
2201 |
-
fw()->extensions->_get_db_active_extensions(),
|
2202 |
-
$extensions_for_deactivation
|
2203 |
-
)
|
2204 |
-
);
|
2205 |
-
|
2206 |
-
// remove already inactive extensions
|
2207 |
-
foreach ($extensions_for_deactivation as $extension_name => $not_used_var) {
|
2208 |
-
if (!fw_ext($extension_name)) {
|
2209 |
-
unset($extensions_for_deactivation[$extension_name]);
|
2210 |
-
}
|
2211 |
-
}
|
2212 |
-
|
2213 |
-
/**
|
2214 |
-
* Prepare db wp option used to fire the 'fw_extensions_after_deactivation' action on next refresh
|
2215 |
-
*/
|
2216 |
-
{
|
2217 |
-
$db_wp_option_name = 'fw_extensions_activation';
|
2218 |
-
$db_wp_option_value = get_option($db_wp_option_name, array(
|
2219 |
-
'activated' => array(),
|
2220 |
-
'deactivated' => array(),
|
2221 |
-
));
|
2222 |
-
|
2223 |
-
/**
|
2224 |
-
* Keep adding to the existing value instead of resetting it on each method call
|
2225 |
-
* in case the method will be called multiple times
|
2226 |
-
*/
|
2227 |
-
$db_wp_option_value['deactivated'] = array_merge($db_wp_option_value['deactivated'], $extensions_for_deactivation);
|
2228 |
-
|
2229 |
-
/**
|
2230 |
-
* Remove deactivated extensions from activated
|
2231 |
-
*/
|
2232 |
-
$db_wp_option_value['activated'] = array_diff_key($db_wp_option_value['activated'], $db_wp_option_value['deactivated']);
|
2233 |
-
|
2234 |
-
update_option($db_wp_option_name, $db_wp_option_value, false);
|
2235 |
-
}
|
2236 |
-
|
2237 |
-
do_action('fw_extensions_before_deactivation', $extensions_for_deactivation);
|
2238 |
-
|
2239 |
-
if ($has_errors) {
|
2240 |
-
return $result;
|
2241 |
-
} else {
|
2242 |
-
return true;
|
2243 |
-
}
|
2244 |
-
}
|
2245 |
-
|
2246 |
-
/**
|
2247 |
-
* @param array $data
|
2248 |
-
* @return array
|
2249 |
-
* @internal
|
2250 |
-
*/
|
2251 |
-
public function _extension_settings_form_render($data)
|
2252 |
-
{
|
2253 |
-
/**
|
2254 |
-
* @var FW_Extension $extension
|
2255 |
-
*/
|
2256 |
-
$extension = $data['data']['extension'];
|
2257 |
-
|
2258 |
-
do_action('fw_extension_settings_form_render:'. $extension->get_name());
|
2259 |
-
|
2260 |
-
echo fw_html_tag('input', array(
|
2261 |
-
'type' => 'hidden',
|
2262 |
-
'name' => 'fw_extension_name',
|
2263 |
-
'value' => $extension->get_name(),
|
2264 |
-
), true);
|
2265 |
-
|
2266 |
-
echo fw()->backend->render_options(
|
2267 |
-
$extension->get_settings_options(),
|
2268 |
-
fw_get_db_ext_settings_option($extension->get_name())
|
2269 |
-
);
|
2270 |
-
|
2271 |
-
$data['submit']['html'] = '';
|
2272 |
-
|
2273 |
-
echo '<p>';
|
2274 |
-
echo fw_html_tag('input', array(
|
2275 |
-
'type' => 'submit',
|
2276 |
-
'class' => 'button-primary',
|
2277 |
-
'value' => __('Save', 'fw'),
|
2278 |
-
));
|
2279 |
-
echo ' ';
|
2280 |
-
echo fw_html_tag('a', array(
|
2281 |
-
'href' => $this->get_link(),
|
2282 |
-
), __('Cancel', 'fw'));
|
2283 |
-
echo '</p>';
|
2284 |
-
|
2285 |
-
return $data;
|
2286 |
-
}
|
2287 |
-
|
2288 |
-
/**
|
2289 |
-
* @param array $errors
|
2290 |
-
* @return array
|
2291 |
-
* @internal
|
2292 |
-
*/
|
2293 |
-
public function _extension_settings_form_validate($errors)
|
2294 |
-
{
|
2295 |
-
do {
|
2296 |
-
if (!current_user_can($this->can_activate())) {
|
2297 |
-
$errors[] = __('You are not allowed to save extensions settings.', 'fw');
|
2298 |
-
break;
|
2299 |
-
}
|
2300 |
-
|
2301 |
-
$extension = fw()->extensions->get(FW_Request::POST('fw_extension_name'));
|
2302 |
-
|
2303 |
-
if (!$extension) {
|
2304 |
-
$errors[] = __('Invalid extension.', 'fw');
|
2305 |
-
break;
|
2306 |
-
}
|
2307 |
-
|
2308 |
-
if (!$extension->get_settings_options()) {
|
2309 |
-
$errors[] = __('Extension does not have settings options.', 'fw');
|
2310 |
-
break;
|
2311 |
-
}
|
2312 |
-
} while(false);
|
2313 |
-
|
2314 |
-
return $errors;
|
2315 |
-
}
|
2316 |
-
|
2317 |
-
/**
|
2318 |
-
* @param array $data
|
2319 |
-
* @return array
|
2320 |
-
* @internal
|
2321 |
-
*/
|
2322 |
-
public function _extension_settings_form_save($data)
|
2323 |
-
{
|
2324 |
-
$extension = fw()->extensions->get(FW_Request::POST('fw_extension_name'));
|
2325 |
-
|
2326 |
-
$options_before_save = (array)fw_get_db_ext_settings_option($extension->get_name());
|
2327 |
-
|
2328 |
-
fw_set_db_ext_settings_option(
|
2329 |
-
$extension->get_name(),
|
2330 |
-
null,
|
2331 |
-
array_merge(
|
2332 |
-
$options_before_save,
|
2333 |
-
fw_get_options_values_from_input(
|
2334 |
-
$extension->get_settings_options()
|
2335 |
-
)
|
2336 |
-
)
|
2337 |
-
);
|
2338 |
-
|
2339 |
-
FW_Flash_Messages::add(
|
2340 |
-
'fw_extension_settings_saved',
|
2341 |
-
__('Extensions settings successfully saved.', 'fw'),
|
2342 |
-
'success'
|
2343 |
-
);
|
2344 |
-
|
2345 |
-
$data['redirect'] = fw_current_url();
|
2346 |
-
|
2347 |
-
do_action('fw_extension_settings_form_saved:'. $extension->get_name(), $options_before_save);
|
2348 |
-
|
2349 |
-
return $data;
|
2350 |
-
}
|
2351 |
-
|
2352 |
-
/**
|
2353 |
-
* Download an extension
|
2354 |
-
*
|
2355 |
-
* global $wp_filesystem; must me initialized
|
2356 |
-
*
|
2357 |
-
* @param string $extension_name
|
2358 |
-
* @param array $data Extension data from the "available extensions" array
|
2359 |
-
* @return string|WP_Error WP Filesystem path to the downloaded directory
|
2360 |
-
*/
|
2361 |
-
private function download($extension_name, $data)
|
2362 |
-
{
|
2363 |
-
$wp_error_id = 'fw_extension_download';
|
2364 |
-
|
2365 |
-
if (empty($data['download'])) {
|
2366 |
-
return new WP_Error(
|
2367 |
-
$wp_error_id,
|
2368 |
-
sprintf(__('Extension "%s" has no download sources.', 'fw'), $this->get_extension_title($extension_name))
|
2369 |
-
);
|
2370 |
-
}
|
2371 |
-
|
2372 |
-
/** @var WP_Filesystem_Base $wp_filesystem */
|
2373 |
-
global $wp_filesystem;
|
2374 |
-
|
2375 |
-
// create temporary directory
|
2376 |
-
{
|
2377 |
-
$wp_fs_tmp_dir = FW_WP_Filesystem::real_path_to_filesystem_path($this->get_tmp_dir());
|
2378 |
-
|
2379 |
-
if ($wp_filesystem->exists($wp_fs_tmp_dir)) {
|
2380 |
-
// just in case it already exists, clear everything, it may contain old files
|
2381 |
-
if (!$wp_filesystem->rmdir($wp_fs_tmp_dir, true)) {
|
2382 |
-
return new WP_Error(
|
2383 |
-
$wp_error_id,
|
2384 |
-
sprintf(__('Cannot remove temporary directory: %s', 'fw'), $wp_fs_tmp_dir)
|
2385 |
-
);
|
2386 |
-
}
|
2387 |
-
}
|
2388 |
-
|
2389 |
-
if (!FW_WP_Filesystem::mkdir_recursive($wp_fs_tmp_dir)) {
|
2390 |
-
return new WP_Error(
|
2391 |
-
$wp_error_id,
|
2392 |
-
sprintf(__('Cannot create temporary directory: %s', 'fw'), $wp_fs_tmp_dir)
|
2393 |
-
);
|
2394 |
-
}
|
2395 |
-
}
|
2396 |
-
|
2397 |
-
$theme_ext_requirements = fw()->theme->manifest->get('requirements/extensions');
|
2398 |
-
|
2399 |
-
foreach ($data['download'] as $source => $source_data) {
|
2400 |
-
switch ($source) {
|
2401 |
-
case 'github':
|
2402 |
-
if (empty($source_data['user_repo'])) {
|
2403 |
-
return new WP_Error(
|
2404 |
-
$wp_error_id,
|
2405 |
-
sprintf(__('"%s" extension github source "user_repo" parameter is required', 'fw'), $this->get_extension_title($extension_name))
|
2406 |
-
);
|
2407 |
-
}
|
2408 |
-
|
2409 |
-
{
|
2410 |
-
$transient_name = 'fw_ext_mngr_gh_dl';
|
2411 |
-
$transient_ttl = HOUR_IN_SECONDS;
|
2412 |
-
|
2413 |
-
$cache = get_site_transient($transient_name);
|
2414 |
-
|
2415 |
-
if ($cache === false) {
|
2416 |
-
$cache = array();
|
2417 |
-
}
|
2418 |
-
}
|
2419 |
-
|
2420 |
-
if (isset($cache[ $source_data['user_repo'] ])) {
|
2421 |
-
$download_link = $cache[ $source_data['user_repo'] ]['zipball_url'];
|
2422 |
-
} else {
|
2423 |
-
$http = new WP_Http();
|
2424 |
-
|
2425 |
-
if (
|
2426 |
-
isset($theme_ext_requirements[$extension_name])
|
2427 |
-
&&
|
2428 |
-
isset($theme_ext_requirements[$extension_name]['max_version'])
|
2429 |
-
) {
|
2430 |
-
$tag = 'tags/v'. $theme_ext_requirements[$extension_name]['max_version'];
|
2431 |
-
} else {
|
2432 |
-
$tag = 'latest';
|
2433 |
-
}
|
2434 |
-
|
2435 |
-
$response = $http->get(
|
2436 |
-
apply_filters('fw_github_api_url', 'https://api.github.com')
|
2437 |
-
. '/repos/'. $source_data['user_repo'] .'/releases/'. $tag
|
2438 |
-
);
|
2439 |
-
|
2440 |
-
unset($http);
|
2441 |
-
|
2442 |
-
$response_code = intval(wp_remote_retrieve_response_code($response));
|
2443 |
-
|
2444 |
-
if ($response_code !== 200) {
|
2445 |
-
if ($response_code === 403) {
|
2446 |
-
if ($json_response = json_decode($response['body'], true)) {
|
2447 |
-
return new WP_Error(
|
2448 |
-
$wp_error_id,
|
2449 |
-
__('Github error:', 'fw') .' '. $json_response['message']
|
2450 |
-
);
|
2451 |
-
} else {
|
2452 |
-
return new WP_Error(
|
2453 |
-
$wp_error_id,
|
2454 |
-
sprintf(
|
2455 |
-
__( 'Failed to access Github repository "%s" releases. (Response code: %d)', 'fw' ),
|
2456 |
-
$source_data['user_repo'], $response_code
|
2457 |
-
)
|
2458 |
-
);
|
2459 |
-
}
|
2460 |
-
} elseif ($response_code) {
|
2461 |
-
return new WP_Error(
|
2462 |
-
$wp_error_id,
|
2463 |
-
sprintf(
|
2464 |
-
__( 'Failed to access Github repository "%s" releases. (Response code: %d)', 'fw' ),
|
2465 |
-
$source_data['user_repo'], $response_code
|
2466 |
-
)
|
2467 |
-
);
|
2468 |
-
} elseif (is_wp_error($response)) {
|
2469 |
-
return new WP_Error(
|
2470 |
-
$wp_error_id,
|
2471 |
-
sprintf(
|
2472 |
-
__( 'Failed to access Github repository "%s" releases. (%s)', 'fw' ),
|
2473 |
-
$source_data['user_repo'], $response->get_error_message()
|
2474 |
-
)
|
2475 |
-
);
|
2476 |
-
} else {
|
2477 |
-
return new WP_Error(
|
2478 |
-
$wp_error_id,
|
2479 |
-
sprintf(
|
2480 |
-
__( 'Failed to access Github repository "%s" releases.', 'fw' ),
|
2481 |
-
$source_data['user_repo']
|
2482 |
-
)
|
2483 |
-
);
|
2484 |
-
}
|
2485 |
-
}
|
2486 |
-
|
2487 |
-
$release = json_decode($response['body'], true);
|
2488 |
-
|
2489 |
-
unset($response);
|
2490 |
-
|
2491 |
-
if (empty($release)) {
|
2492 |
-
return new WP_Error(
|
2493 |
-
$wp_error_id,
|
2494 |
-
sprintf(
|
2495 |
-
__('"%s" extension github repository "%s" has no releases.', 'fw'),
|
2496 |
-
$this->get_extension_title($extension_name), $source_data['user_repo']
|
2497 |
-
)
|
2498 |
-
);
|
2499 |
-
}
|
2500 |
-
|
2501 |
-
{
|
2502 |
-
$cache[ $source_data['user_repo'] ] = array(
|
2503 |
-
'zipball_url' => 'https://github.com/'. $source_data['user_repo'] .'/archive/'. $release['tag_name'] .'.zip',
|
2504 |
-
'tag_name' => $release['tag_name']
|
2505 |
-
);
|
2506 |
-
|
2507 |
-
set_site_transient($transient_name, $cache, $transient_ttl);
|
2508 |
-
}
|
2509 |
-
|
2510 |
-
$download_link = $cache[ $source_data['user_repo'] ]['zipball_url'];
|
2511 |
-
|
2512 |
-
unset($release);
|
2513 |
-
}
|
2514 |
-
|
2515 |
-
{
|
2516 |
-
$http = new WP_Http();
|
2517 |
-
|
2518 |
-
$response = $http->request($download_link, array(
|
2519 |
-
'timeout' => $this->download_timeout,
|
2520 |
-
));
|
2521 |
-
|
2522 |
-
unset($http);
|
2523 |
-
|
2524 |
-
if (($response_code = intval(wp_remote_retrieve_response_code($response))) !== 200) {
|
2525 |
-
if ($response_code) {
|
2526 |
-
return new WP_Error(
|
2527 |
-
$wp_error_id,
|
2528 |
-
sprintf( __( 'Cannot download the "%s" extension zip. (Response code: %d)', 'fw' ),
|
2529 |
-
$this->get_extension_title( $extension_name ), $response_code
|
2530 |
-
)
|
2531 |
-
);
|
2532 |
-
} elseif (is_wp_error($response)) {
|
2533 |
-
return new WP_Error(
|
2534 |
-
$wp_error_id,
|
2535 |
-
sprintf( __( 'Cannot download the "%s" extension zip. %s', 'fw' ),
|
2536 |
-
$this->get_extension_title( $extension_name ),
|
2537 |
-
$response->get_error_message()
|
2538 |
-
)
|
2539 |
-
);
|
2540 |
-
} else {
|
2541 |
-
return new WP_Error(
|
2542 |
-
$wp_error_id,
|
2543 |
-
sprintf( __( 'Cannot download the "%s" extension zip.', 'fw' ),
|
2544 |
-
$this->get_extension_title( $extension_name )
|
2545 |
-
)
|
2546 |
-
);
|
2547 |
-
}
|
2548 |
-
}
|
2549 |
-
|
2550 |
-
$zip_path = $wp_fs_tmp_dir .'/temp.zip';
|
2551 |
-
|
2552 |
-
// save zip to file
|
2553 |
-
if (!$wp_filesystem->put_contents($zip_path, $response['body'])) {
|
2554 |
-
return new WP_Error(
|
2555 |
-
$wp_error_id,
|
2556 |
-
sprintf(__('Cannot save the "%s" extension zip.', 'fw'), $this->get_extension_title($extension_name))
|
2557 |
-
);
|
2558 |
-
}
|
2559 |
-
|
2560 |
-
unset($response);
|
2561 |
-
|
2562 |
-
$unzip_result = unzip_file(
|
2563 |
-
FW_WP_Filesystem::filesystem_path_to_real_path($zip_path),
|
2564 |
-
$wp_fs_tmp_dir
|
2565 |
-
);
|
2566 |
-
|
2567 |
-
if (is_wp_error($unzip_result)) {
|
2568 |
-
return $unzip_result;
|
2569 |
-
}
|
2570 |
-
|
2571 |
-
// remove zip file
|
2572 |
-
if (!$wp_filesystem->delete($zip_path, false, 'f')) {
|
2573 |
-
return new WP_Error(
|
2574 |
-
$wp_error_id,
|
2575 |
-
sprintf(__('Cannot remove the "%s" extension downloaded zip.', 'fw'), $this->get_extension_title($extension_name))
|
2576 |
-
);
|
2577 |
-
}
|
2578 |
-
|
2579 |
-
$unzipped_dir_files = $wp_filesystem->dirlist($wp_fs_tmp_dir);
|
2580 |
-
|
2581 |
-
if (!$unzipped_dir_files) {
|
2582 |
-
return new WP_Error(
|
2583 |
-
$wp_error_id,
|
2584 |
-
__('Cannot access the unzipped directory files.', 'fw')
|
2585 |
-
);
|
2586 |
-
}
|
2587 |
-
|
2588 |
-
/**
|
2589 |
-
* get first found directory
|
2590 |
-
* (if everything worked well, there should be only one directory)
|
2591 |
-
*/
|
2592 |
-
foreach ($unzipped_dir_files as $file) {
|
2593 |
-
if ($file['type'] == 'd') {
|
2594 |
-
return $wp_fs_tmp_dir .'/'. $file['name'];
|
2595 |
-
}
|
2596 |
-
}
|
2597 |
-
|
2598 |
-
return new WP_Error(
|
2599 |
-
$wp_error_id,
|
2600 |
-
sprintf(__('The unzipped "%s" extension directory not found.', 'fw'), $this->get_extension_title($extension_name))
|
2601 |
-
);
|
2602 |
-
}
|
2603 |
-
break;
|
2604 |
-
default:
|
2605 |
-
return new WP_Error(
|
2606 |
-
$wp_error_id,
|
2607 |
-
sprintf(__('Unknown "%s" extension download source "%s"', 'fw'), $this->get_extension_title($extension_name), $source)
|
2608 |
-
);
|
2609 |
-
}
|
2610 |
-
}
|
2611 |
-
}
|
2612 |
-
|
2613 |
-
/**
|
2614 |
-
* Merge the downloaded extension directory with the existing directory
|
2615 |
-
*
|
2616 |
-
* @param string $source_wp_fs_dir Downloaded extension directory
|
2617 |
-
* @param string $destination_wp_fs_dir
|
2618 |
-
*
|
2619 |
-
* @return null|WP_Error
|
2620 |
-
*/
|
2621 |
-
private function merge_extension($source_wp_fs_dir, $destination_wp_fs_dir)
|
2622 |
-
{
|
2623 |
-
/** @var WP_Filesystem_Base $wp_filesystem */
|
2624 |
-
global $wp_filesystem;
|
2625 |
-
|
2626 |
-
$wp_error_id = 'fw_extensions_merge';
|
2627 |
-
|
2628 |
-
$source_files = $wp_filesystem->dirlist($source_wp_fs_dir);
|
2629 |
-
|
2630 |
-
if ($source_files === false) {
|
2631 |
-
return new WP_Error(
|
2632 |
-
$wp_error_id,
|
2633 |
-
sprintf(__('Cannot read directory "%s".', 'fw'), $source_wp_fs_dir)
|
2634 |
-
);
|
2635 |
-
}
|
2636 |
-
|
2637 |
-
if (empty($source_files)) {
|
2638 |
-
// directory is empty, nothing to move
|
2639 |
-
return;
|
2640 |
-
}
|
2641 |
-
|
2642 |
-
/**
|
2643 |
-
* Prepare destination directory
|
2644 |
-
* Remove everything except the extensions/ directory
|
2645 |
-
*/
|
2646 |
-
if ($wp_filesystem->exists($destination_wp_fs_dir)) {
|
2647 |
-
$destination_files = $wp_filesystem->dirlist($destination_wp_fs_dir);
|
2648 |
-
|
2649 |
-
if ($destination_files === false) {
|
2650 |
-
return new WP_Error(
|
2651 |
-
$wp_error_id,
|
2652 |
-
sprintf(__('Cannot read directory "%s".', 'fw'), $destination_wp_fs_dir)
|
2653 |
-
);
|
2654 |
-
}
|
2655 |
-
|
2656 |
-
if (!empty($destination_files)) {
|
2657 |
-
// the directory contains some files, delete everything
|
2658 |
-
foreach ($destination_files as $file) {
|
2659 |
-
if ($file['name'] === 'extensions' && $file['type'] === 'd') {
|
2660 |
-
// do not touch the extensions/ directory
|
2661 |
-
continue;
|
2662 |
-
}
|
2663 |
-
|
2664 |
-
if (!$wp_filesystem->delete($destination_wp_fs_dir .'/'. $file['name'], true, $file['type'])) {
|
2665 |
-
return new WP_Error(
|
2666 |
-
$wp_error_id,
|
2667 |
-
sprintf(__('Cannot delete "%s".', 'fw'), $destination_wp_fs_dir .'/'. $file['name'])
|
2668 |
-
);
|
2669 |
-
}
|
2670 |
-
}
|
2671 |
-
|
2672 |
-
unset($destination_files);
|
2673 |
-
}
|
2674 |
-
} else {
|
2675 |
-
if (!FW_WP_Filesystem::mkdir_recursive($destination_wp_fs_dir)) {
|
2676 |
-
return new WP_Error(
|
2677 |
-
$wp_error_id,
|
2678 |
-
sprintf(__('Cannot create the "%s" directory.', 'fw'), $destination_wp_fs_dir)
|
2679 |
-
);
|
2680 |
-
}
|
2681 |
-
}
|
2682 |
-
|
2683 |
-
$has_sub_extensions = false;
|
2684 |
-
|
2685 |
-
foreach ($source_files as $file) {
|
2686 |
-
if ($file['name'] === 'extensions' && $file['type'] === 'd') {
|
2687 |
-
// do not touch the extensions/ directory
|
2688 |
-
$has_sub_extensions = true;
|
2689 |
-
continue;
|
2690 |
-
}
|
2691 |
-
|
2692 |
-
if (!$wp_filesystem->move($source_wp_fs_dir .'/'. $file['name'], $destination_wp_fs_dir .'/'. $file['name'])) {
|
2693 |
-
return new WP_Error(
|
2694 |
-
$wp_error_id,
|
2695 |
-
sprintf(
|
2696 |
-
__('Cannot move "%s" to "%s".', 'fw'),
|
2697 |
-
$source_wp_fs_dir .'/'. $file['name'],
|
2698 |
-
$destination_wp_fs_dir .'/'. $file['name']
|
2699 |
-
)
|
2700 |
-
);
|
2701 |
-
}
|
2702 |
-
}
|
2703 |
-
|
2704 |
-
unset($source_files);
|
2705 |
-
|
2706 |
-
if (!$has_sub_extensions) {
|
2707 |
-
return;
|
2708 |
-
}
|
2709 |
-
|
2710 |
-
$sub_extensions = $wp_filesystem->dirlist($source_wp_fs_dir .'/extensions');
|
2711 |
-
|
2712 |
-
if ($sub_extensions === false) {
|
2713 |
-
return new WP_Error(
|
2714 |
-
$wp_error_id,
|
2715 |
-
sprintf(__('Cannot read directory "%s".', 'fw'), $source_wp_fs_dir .'/extensions')
|
2716 |
-
);
|
2717 |
-
}
|
2718 |
-
|
2719 |
-
if (empty($sub_extensions)) {
|
2720 |
-
// directory is empty, nothing to remove
|
2721 |
-
return;
|
2722 |
-
}
|
2723 |
-
|
2724 |
-
foreach ($sub_extensions as $file) {
|
2725 |
-
if ($file['type'] !== 'd') {
|
2726 |
-
// wrong, only directories must exist in the extensions/ directory
|
2727 |
-
continue;
|
2728 |
-
}
|
2729 |
-
|
2730 |
-
$merge_result = $this->merge_extension(
|
2731 |
-
$source_wp_fs_dir .'/extensions/'. $file['name'],
|
2732 |
-
$destination_wp_fs_dir .'/extensions/'. $file['name']
|
2733 |
-
);
|
2734 |
-
|
2735 |
-
if (is_wp_error($merge_result)) {
|
2736 |
-
return $merge_result;
|
2737 |
-
}
|
2738 |
-
}
|
2739 |
-
}
|
2740 |
-
|
2741 |
-
private function get_supported_extensions_for_install()
|
2742 |
-
{
|
2743 |
-
$supported_extensions = fw()->theme->manifest->get('supported_extensions', array());
|
2744 |
-
|
2745 |
-
if (empty($supported_extensions)) {
|
2746 |
-
return array();
|
2747 |
-
}
|
2748 |
-
|
2749 |
-
// remove not available extensions
|
2750 |
-
$supported_extensions = array_intersect_key($supported_extensions, $this->get_available_extensions());
|
2751 |
-
|
2752 |
-
if (empty($supported_extensions)) {
|
2753 |
-
return array();
|
2754 |
-
}
|
2755 |
-
|
2756 |
-
// remove already installed extensions
|
2757 |
-
$supported_extensions = array_diff_key($supported_extensions, $this->get_installed_extensions());
|
2758 |
-
|
2759 |
-
if (empty($supported_extensions)) {
|
2760 |
-
return array();
|
2761 |
-
}
|
2762 |
-
|
2763 |
-
return $supported_extensions;
|
2764 |
-
}
|
2765 |
-
|
2766 |
-
/**
|
2767 |
-
* @param $actions
|
2768 |
-
* @return array
|
2769 |
-
* @internal
|
2770 |
-
*/
|
2771 |
-
public function _filter_plugin_action_list($actions)
|
2772 |
-
{
|
2773 |
-
return array_merge(
|
2774 |
-
array(
|
2775 |
-
'fw-extensions' => fw_html_tag('a', array(
|
2776 |
-
'href' => $this->get_link(),
|
2777 |
-
), fw()->manifest->get_name()),
|
2778 |
-
),
|
2779 |
-
$actions
|
2780 |
-
);
|
2781 |
-
}
|
2782 |
-
|
2783 |
-
/**
|
2784 |
-
* @return string Extensions page link
|
2785 |
-
*/
|
2786 |
-
private function get_link()
|
2787 |
-
{
|
2788 |
-
static $cache_link = null;
|
2789 |
-
|
2790 |
-
if ($cache_link === null) {
|
2791 |
-
$cache_link = menu_page_url( $this->get_page_slug(), false );
|
2792 |
-
|
2793 |
-
// https://core.trac.wordpress.org/ticket/28226
|
2794 |
-
if (is_multisite() && is_network_admin()) {
|
2795 |
-
$cache_link = self_admin_url(
|
2796 |
-
// extract relative link
|
2797 |
-
preg_replace('/^'. preg_quote(admin_url(), '/') .'/', '', $cache_link)
|
2798 |
-
);
|
2799 |
-
}
|
2800 |
-
}
|
2801 |
-
|
2802 |
-
return $cache_link;
|
2803 |
-
}
|
2804 |
-
|
2805 |
-
/**
|
2806 |
-
* @param array $skip_extensions {'ext' => mixed}
|
2807 |
-
* @param array $check_for_deps ['ext', 'ext', ...] Extensions to check if has in dependencies the used extensions
|
2808 |
-
*
|
2809 |
-
* @return array
|
2810 |
-
*/
|
2811 |
-
private function get_used_extensions($skip_extensions, $check_for_deps)
|
2812 |
-
{
|
2813 |
-
$used_extensions = array();
|
2814 |
-
|
2815 |
-
$installed_extensions = $this->get_installed_extensions();
|
2816 |
-
|
2817 |
-
foreach ($installed_extensions as $inst_ext_name => &$inst_ext_data) {
|
2818 |
-
if (isset($skip_extensions[ $inst_ext_name ])) {
|
2819 |
-
continue;
|
2820 |
-
}
|
2821 |
-
|
2822 |
-
if (isset($used_extensions[$inst_ext_name])) {
|
2823 |
-
// already marked as used
|
2824 |
-
continue;
|
2825 |
-
}
|
2826 |
-
|
2827 |
-
do {
|
2828 |
-
foreach ($check_for_deps as $deps_ext) {
|
2829 |
-
if (isset($skip_extensions[$deps_ext])) {
|
2830 |
-
continue;
|
2831 |
-
}
|
2832 |
-
|
2833 |
-
if (false !== fw_akg(
|
2834 |
-
'requirements/extensions/'. $inst_ext_name,
|
2835 |
-
$installed_extensions[$deps_ext]['manifest'],
|
2836 |
-
false
|
2837 |
-
)) {
|
2838 |
-
// is required by an active extension
|
2839 |
-
break 2;
|
2840 |
-
}
|
2841 |
-
}
|
2842 |
-
|
2843 |
-
if ( true === fw_akg(
|
2844 |
-
'standalone',
|
2845 |
-
$inst_ext_data['manifest'],
|
2846 |
-
$this->manifest_default_values['standalone']
|
2847 |
-
) ) {
|
2848 |
-
// can exist alone
|
2849 |
-
break;
|
2850 |
-
}
|
2851 |
-
|
2852 |
-
// not used
|
2853 |
-
continue 2;
|
2854 |
-
} while(false);
|
2855 |
-
|
2856 |
-
$used_extensions[$inst_ext_name] = array();
|
2857 |
-
|
2858 |
-
// Set all sub-extensions as used
|
2859 |
-
foreach ($this->collect_sub_extensions($inst_ext_name, $installed_extensions) as $sub_extension_name => $sub_extension_data) {
|
2860 |
-
if (isset($skip_extensions[$sub_extension_name])) {
|
2861 |
-
continue;
|
2862 |
-
}
|
2863 |
-
|
2864 |
-
$used_extensions[ $sub_extension_name ] = array();
|
2865 |
-
}
|
2866 |
-
|
2867 |
-
// Set all parents as used
|
2868 |
-
{
|
2869 |
-
$current_parent = $inst_ext_name;
|
2870 |
-
while ($current_parent = $installed_extensions[$current_parent]['parent']) {
|
2871 |
-
$used_extensions[$current_parent] = array();
|
2872 |
-
}
|
2873 |
-
}
|
2874 |
-
}
|
2875 |
-
unset($inst_ext_data);
|
2876 |
-
|
2877 |
-
// remove all skipped extensions and sub-extension from used extensions
|
2878 |
-
foreach (array_keys($skip_extensions) as $skip_extension_name) {
|
2879 |
-
unset($used_extensions[$skip_extension_name]);
|
2880 |
-
|
2881 |
-
if (isset($installed_extensions[$skip_extension_name])) {
|
2882 |
-
foreach ($this->collect_sub_extensions($skip_extension_name, $installed_extensions) as $sub_extension_name => $sub_extension_data) {
|
2883 |
-
unset($used_extensions[$sub_extension_name]);
|
2884 |
-
}
|
2885 |
-
}
|
2886 |
-
}
|
2887 |
-
|
2888 |
-
return $used_extensions;
|
2889 |
-
}
|
2890 |
-
|
2891 |
-
/**
|
2892 |
-
* @internal
|
2893 |
-
*/
|
2894 |
-
public function _action_admin_footer()
|
2895 |
-
{
|
2896 |
-
$this->activate_hidden_standalone_extensions();
|
2897 |
-
}
|
2898 |
-
|
2899 |
-
public function get_extension_title($extension_name)
|
2900 |
-
{
|
2901 |
-
$installed_extensions = $this->get_installed_extensions();
|
2902 |
-
|
2903 |
-
if (isset($installed_extensions[$extension_name])) {
|
2904 |
-
return fw_akg('name', $installed_extensions[$extension_name]['manifest'], fw_id_to_title($extension_name));
|
2905 |
-
}
|
2906 |
-
|
2907 |
-
unset($installed_extensions);
|
2908 |
-
|
2909 |
-
$available_extensions = $this->get_available_extensions();
|
2910 |
-
|
2911 |
-
if (isset($available_extensions[$extension_name])) {
|
2912 |
-
return $available_extensions[$extension_name]['name'];
|
2913 |
-
}
|
2914 |
-
|
2915 |
-
return fw_id_to_title($extension_name);
|
2916 |
-
}
|
2917 |
-
|
2918 |
-
public function is_extensions_page()
|
2919 |
-
{
|
2920 |
-
$current_screen = get_current_screen();
|
2921 |
-
|
2922 |
-
if (empty($current_screen)) {
|
2923 |
-
return false;
|
2924 |
-
}
|
2925 |
-
|
2926 |
-
return (
|
2927 |
-
property_exists($current_screen, 'base') && strpos($current_screen->base, $this->get_page_slug()) !== false
|
2928 |
-
&&
|
2929 |
-
property_exists($current_screen, 'id') && strpos($current_screen->id, $this->get_page_slug()) !== false
|
2930 |
-
&&
|
2931 |
-
!isset($_GET['sub-page'])
|
2932 |
-
);
|
2933 |
-
}
|
2934 |
-
|
2935 |
-
public function is_extension_page()
|
2936 |
-
{
|
2937 |
-
$current_screen = get_current_screen();
|
2938 |
-
|
2939 |
-
if (empty($current_screen)) {
|
2940 |
-
return false;
|
2941 |
-
}
|
2942 |
-
|
2943 |
-
return (
|
2944 |
-
property_exists($current_screen, 'base') && strpos($current_screen->base, $this->get_page_slug()) !== false
|
2945 |
-
&&
|
2946 |
-
property_exists($current_screen, 'id') && strpos($current_screen->id, $this->get_page_slug()) !== false
|
2947 |
-
&&
|
2948 |
-
isset($_GET['sub-page']) && $_GET['sub-page'] === 'extension'
|
2949 |
-
);
|
2950 |
-
}
|
2951 |
-
|
2952 |
-
/**
|
2953 |
-
* @internal
|
2954 |
-
*/
|
2955 |
-
public function _action_enqueue_scripts()
|
2956 |
-
{
|
2957 |
-
wp_enqueue_style(
|
2958 |
-
'fw-extensions-menu-icon',
|
2959 |
-
$this->get_uri('/static/unyson-font-icon/style.css'),
|
2960 |
-
array(),
|
2961 |
-
fw()->manifest->get_version()
|
2962 |
-
);
|
2963 |
-
|
2964 |
-
/**
|
2965 |
-
* Enqueue only on Extensions List page
|
2966 |
-
*/
|
2967 |
-
if ($this->is_extensions_page()) {
|
2968 |
-
wp_enqueue_style(
|
2969 |
-
'fw-extensions-page',
|
2970 |
-
$this->get_uri('/static/extensions-page.css'),
|
2971 |
-
array(
|
2972 |
-
'fw',
|
2973 |
-
'fw-unycon', 'fw-font-awesome', // in case some extension has font-icon thumbnail
|
2974 |
-
),
|
2975 |
-
fw()->manifest->get_version()
|
2976 |
-
);
|
2977 |
-
wp_enqueue_script(
|
2978 |
-
'fw-extensions-page',
|
2979 |
-
$this->get_uri('/static/extensions-page.js'),
|
2980 |
-
array('fw'),
|
2981 |
-
fw()->manifest->get_version(),
|
2982 |
-
true
|
2983 |
-
);
|
2984 |
-
wp_localize_script('fw-extensions-page', '_fw_extensions_script_data', array(
|
2985 |
-
'link' => $this->get_link(),
|
2986 |
-
));
|
2987 |
-
|
2988 |
-
/**
|
2989 |
-
* this is needed for fw.soleModal design
|
2990 |
-
* it is displayed when extension ajax install returns an error
|
2991 |
-
*/
|
2992 |
-
wp_enqueue_media();
|
2993 |
-
}
|
2994 |
-
|
2995 |
-
if ($this->is_extension_page()) {
|
2996 |
-
wp_enqueue_style(
|
2997 |
-
'fw-extension-page',
|
2998 |
-
$this->get_uri('/static/extension-page.css'),
|
2999 |
-
array('fw'),
|
3000 |
-
fw()->manifest->get_version()
|
3001 |
-
);
|
3002 |
-
wp_enqueue_script(
|
3003 |
-
'fw-extension-page',
|
3004 |
-
$this->get_uri('/static/extension-page.js'),
|
3005 |
-
array('fw'),
|
3006 |
-
fw()->manifest->get_version(),
|
3007 |
-
true
|
3008 |
-
);
|
3009 |
-
|
3010 |
-
/**
|
3011 |
-
* Enqueue extension settings options static
|
3012 |
-
*/
|
3013 |
-
if (
|
3014 |
-
isset($_GET['extension'])
|
3015 |
-
&&
|
3016 |
-
is_string($extension_name = $_GET['extension'])
|
3017 |
-
&&
|
3018 |
-
fw()->extensions->get($extension_name)
|
3019 |
-
&&
|
3020 |
-
($extension_settings_options = fw()->extensions->get($extension_name)->get_settings_options())
|
3021 |
-
) {
|
3022 |
-
fw()->backend->enqueue_options_static($extension_settings_options);
|
3023 |
-
}
|
3024 |
-
}
|
3025 |
-
}
|
3026 |
-
|
3027 |
-
private function activate_theme_extensions()
|
3028 |
-
{
|
3029 |
-
$db_active_extensions = fw()->extensions->_get_db_active_extensions();
|
3030 |
-
|
3031 |
-
foreach ($this->get_installed_extensions() as $extension_name => $extension) {
|
3032 |
-
if ($extension['is']['theme']) {
|
3033 |
-
$db_active_extensions[ $extension_name ] = array();
|
3034 |
-
}
|
3035 |
-
}
|
3036 |
-
|
3037 |
-
update_option(
|
3038 |
-
fw()->extensions->_get_active_extensions_db_option_name(),
|
3039 |
-
$db_active_extensions
|
3040 |
-
);
|
3041 |
-
}
|
3042 |
-
|
3043 |
-
/**
|
3044 |
-
* @internal
|
3045 |
-
*/
|
3046 |
-
public function _action_theme_switch()
|
3047 |
-
{
|
3048 |
-
$this->activate_theme_extensions();
|
3049 |
-
$this->activate_extensions(
|
3050 |
-
array_fill_keys(
|
3051 |
-
array_keys(fw()->theme->manifest->get('supported_extensions', array())),
|
3052 |
-
array()
|
3053 |
-
)
|
3054 |
-
);
|
3055 |
-
}
|
3056 |
-
|
3057 |
-
/**
|
3058 |
-
* @param array $collected The found extensions {'extension_name' => array()}
|
3059 |
-
* @param array $extensions {'extension_name' => array()}
|
3060 |
-
* @param bool $check_all Check all extensions or only active extensions
|
3061 |
-
*/
|
3062 |
-
private function collect_extensions_that_requires(&$collected, $extensions, $check_all = false)
|
3063 |
-
{
|
3064 |
-
if (empty($extensions)) {
|
3065 |
-
return;
|
3066 |
-
}
|
3067 |
-
|
3068 |
-
$found_extensions = array();
|
3069 |
-
|
3070 |
-
foreach ($this->get_installed_extensions() as $extension_name => $extension_data) {
|
3071 |
-
if (isset($collected[$extension_name])) {
|
3072 |
-
continue;
|
3073 |
-
}
|
3074 |
-
|
3075 |
-
if (!$check_all) {
|
3076 |
-
if (!fw_ext($extension_name)) {
|
3077 |
-
continue;
|
3078 |
-
}
|
3079 |
-
}
|
3080 |
-
|
3081 |
-
if (
|
3082 |
-
array_intersect_key(
|
3083 |
-
$extensions,
|
3084 |
-
fw_akg(
|
3085 |
-
'requirements/extensions',
|
3086 |
-
$extension_data['manifest'],
|
3087 |
-
array()
|
3088 |
-
)
|
3089 |
-
)
|
3090 |
-
) {
|
3091 |
-
$found_extensions[$extension_name] = $collected[$extension_name] = array();
|
3092 |
-
}
|
3093 |
-
}
|
3094 |
-
|
3095 |
-
$this->collect_extensions_that_requires($collected, $found_extensions, $check_all);
|
3096 |
-
}
|
3097 |
-
|
3098 |
-
/**
|
3099 |
-
* Get extension settings page link
|
3100 |
-
* @param string $extension_name
|
3101 |
-
* @return string
|
3102 |
-
*/
|
3103 |
-
public function get_extension_link($extension_name)
|
3104 |
-
{
|
3105 |
-
return $this->get_link() .'&sub-page=extension&extension='. $extension_name;
|
3106 |
-
}
|
3107 |
-
|
3108 |
-
/**
|
3109 |
-
* @param string $extension_name
|
3110 |
-
* @return array|WP_Error Extensions to merge with db active extensions list
|
3111 |
-
*/
|
3112 |
-
private function get_extensions_for_activation($extension_name)
|
3113 |
-
{
|
3114 |
-
$installed_extensions = $this->get_installed_extensions();
|
3115 |
-
|
3116 |
-
$wp_error_id = 'fw_ext_activation';
|
3117 |
-
|
3118 |
-
if (!isset($installed_extensions[$extension_name])) {
|
3119 |
-
return new WP_Error($wp_error_id,
|
3120 |
-
sprintf(
|
3121 |
-
__('Cannot activate the %s extension because it is not installed. %s', 'fw'),
|
3122 |
-
$this->get_extension_title($extension_name),
|
3123 |
-
fw_html_tag('a', array(
|
3124 |
-
'href' => $this->get_link() .'&sub-page=install&extension='. $extension_name
|
3125 |
-
), __('Install', 'fw'))
|
3126 |
-
)
|
3127 |
-
);
|
3128 |
-
}
|
3129 |
-
|
3130 |
-
{
|
3131 |
-
$extension_parents = array($extension_name);
|
3132 |
-
|
3133 |
-
$current_parent = $extension_name;
|
3134 |
-
while ($current_parent = $installed_extensions[$current_parent]['parent']) {
|
3135 |
-
$extension_parents[] = $current_parent;
|
3136 |
-
}
|
3137 |
-
|
3138 |
-
$extension_parents = array_reverse($extension_parents);
|
3139 |
-
}
|
3140 |
-
|
3141 |
-
$extensions = array();
|
3142 |
-
|
3143 |
-
foreach ($extension_parents as $parent_extension_name) {
|
3144 |
-
$extensions[ $parent_extension_name ] = array();
|
3145 |
-
}
|
3146 |
-
|
3147 |
-
// search sub-extensions
|
3148 |
-
foreach ($this->collect_sub_extensions($extension_name, $installed_extensions) as $sub_extension_name => $sub_extension_data) {
|
3149 |
-
$extensions[ $sub_extension_name ] = array();
|
3150 |
-
}
|
3151 |
-
|
3152 |
-
// search required extensions
|
3153 |
-
{
|
3154 |
-
$pending_required_search = $extensions;
|
3155 |
-
|
3156 |
-
while ($pending_required_search) {
|
3157 |
-
foreach (array_keys($pending_required_search) as $pend_req_extension_name) {
|
3158 |
-
unset($pending_required_search[$pend_req_extension_name]);
|
3159 |
-
|
3160 |
-
unset($required_extensions); // reset reference
|
3161 |
-
$required_extensions = array();
|
3162 |
-
$this->collect_required_extensions($pend_req_extension_name, $installed_extensions, $required_extensions);
|
3163 |
-
|
3164 |
-
foreach ($required_extensions as $required_extension_name => $required_extension_data) {
|
3165 |
-
if (!isset($installed_extensions[$required_extension_name])) {
|
3166 |
-
return new WP_Error($wp_error_id,
|
3167 |
-
sprintf(
|
3168 |
-
__('Cannot activate the %s extension because it is not installed. %s', 'fw'),
|
3169 |
-
$this->get_extension_title($required_extension_name),
|
3170 |
-
fw_html_tag('a', array(
|
3171 |
-
'href' => $this->get_link() .'&sub-page=install&extension='. $required_extension_name
|
3172 |
-
), __('Install', 'fw'))
|
3173 |
-
)
|
3174 |
-
);
|
3175 |
-
}
|
3176 |
-
|
3177 |
-
$extensions[$required_extension_name] = array();
|
3178 |
-
|
3179 |
-
// search sub-extensions
|
3180 |
-
foreach ($this->collect_sub_extensions($required_extension_name, $installed_extensions) as $sub_extension_name => $sub_extension_data) {
|
3181 |
-
if (isset($extensions[$sub_extension_name])) {
|
3182 |
-
continue;
|
3183 |
-
}
|
3184 |
-
|
3185 |
-
$extensions[$sub_extension_name] = array();
|
3186 |
-
|
3187 |
-
$pending_required_search[$sub_extension_name] = array();
|
3188 |
-
}
|
3189 |
-
}
|
3190 |
-
}
|
3191 |
-
}
|
3192 |
-
}
|
3193 |
-
|
3194 |
-
return $extensions;
|
3195 |
-
}
|
3196 |
-
|
3197 |
-
public function _action_admin_notices() {
|
3198 |
-
/**
|
3199 |
-
* In v2.4.12 was done a terrible mistake https://github.com/ThemeFuse/Unyson-Extensions-Approval/issues/160
|
3200 |
-
* Show a warning with link to install theme supported extensions
|
3201 |
-
*/
|
3202 |
-
if (
|
3203 |
-
!isset($_GET['supported']) // already on 'Install Supported Extensions' page
|
3204 |
-
&&
|
3205 |
-
$this->can_install()
|
3206 |
-
&&
|
3207 |
-
(($installed_extensions = $this->get_installed_extensions()) || true)
|
3208 |
-
&&
|
3209 |
-
!isset($installed_extensions['page-builder'])
|
3210 |
-
&&
|
3211 |
-
$this->get_supported_extensions_for_install()
|
3212 |
-
) {
|
3213 |
-
echo '<div class="error"> <p>'
|
3214 |
-
, fw_html_tag('a', array('href' => $this->get_link() .'&sub-page=install&supported'),
|
3215 |
-
__('Install theme compatible extensions', 'fw'))
|
3216 |
-
, '</p></div>';
|
3217 |
-
}
|
3218 |
-
}
|
3219 |
-
}
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Install/Activate/Deactivate/Remove Extensions
|
5 |
+
* @internal
|
6 |
+
*/
|
7 |
+
final class _FW_Extensions_Manager
|
8 |
+
{
|
9 |
+
/**
|
10 |
+
* @var FW_Form
|
11 |
+
*/
|
12 |
+
private $extension_settings_form;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* @var Parsedown
|
16 |
+
*/
|
17 |
+
private $markdown_parser;
|
18 |
+
|
19 |
+
private $manifest_default_values = array(
|
20 |
+
'display' => false,
|
21 |
+
'standalone' => false,
|
22 |
+
);
|
23 |
+
|
24 |
+
private $download_timeout = 300;
|
25 |
+
|
26 |
+
private $default_thumbnail = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQIW2PUsHf9DwAC8AGtfm5YCAAAAABJRU5ErkJgggAA';
|
27 |
+
|
28 |
+
public function __construct()
|
29 |
+
{
|
30 |
+
// In any case/permission, make sure to not miss the plugin update actions to prevent extensions delete
|
31 |
+
{
|
32 |
+
add_action('fw_plugin_pre_update', array($this, '_action_plugin_pre_update'));
|
33 |
+
add_action('fw_plugin_post_update', array($this, '_action_plugin_post_update'));
|
34 |
+
}
|
35 |
+
|
36 |
+
if (!is_admin()) {
|
37 |
+
return;
|
38 |
+
}
|
39 |
+
|
40 |
+
if (!$this->can_activate() && !$this->can_install()) {
|
41 |
+
return;
|
42 |
+
}
|
43 |
+
|
44 |
+
/** Actions */
|
45 |
+
{
|
46 |
+
add_action('fw_init', array($this, '_action_fw_init'));
|
47 |
+
add_action('admin_menu', array($this, '_action_admin_menu'));
|
48 |
+
add_action('network_admin_menu', array($this, '_action_admin_menu'));
|
49 |
+
add_action('admin_footer', array($this, '_action_admin_footer'));
|
50 |
+
add_action('admin_enqueue_scripts', array($this, '_action_enqueue_scripts'));
|
51 |
+
add_action('fw_after_plugin_activate', array($this, '_action_after_plugin_activate'), 100);
|
52 |
+
add_action('after_switch_theme', array($this, '_action_theme_switch'));
|
53 |
+
add_action('admin_notices', array($this, '_action_admin_notices'));
|
54 |
+
|
55 |
+
if ($this->can_install()) {
|
56 |
+
add_action('wp_ajax_fw_extensions_check_direct_fs_access', array($this, '_action_ajax_check_direct_fs_access'));
|
57 |
+
add_action('wp_ajax_fw_extensions_install', array($this, '_action_ajax_install'));
|
58 |
+
add_action('wp_ajax_fw_extensions_uninstall', array($this, '_action_ajax_uninstall'));
|
59 |
+
}
|
60 |
+
}
|
61 |
+
|
62 |
+
/** Filters */
|
63 |
+
{
|
64 |
+
add_filter('fw_plugin_action_list', array($this, '_filter_plugin_action_list'));
|
65 |
+
}
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* If current user can:
|
70 |
+
* - activate extension
|
71 |
+
* - disable extensions
|
72 |
+
* - save extension settings options
|
73 |
+
* @return bool
|
74 |
+
*/
|
75 |
+
public function can_activate()
|
76 |
+
{
|
77 |
+
static $can_activate = null;
|
78 |
+
|
79 |
+
if ($can_activate === null) {
|
80 |
+
$can_activate = current_user_can('manage_options');
|
81 |
+
|
82 |
+
if ($can_activate) {
|
83 |
+
// also you can use this method to get the capability
|
84 |
+
$can_activate = 'manage_options';
|
85 |
+
}
|
86 |
+
|
87 |
+
if (!$can_activate) {
|
88 |
+
// make sure if can install, then also can activate. (can install) > (can activate)
|
89 |
+
$can_activate = $this->can_install();
|
90 |
+
}
|
91 |
+
}
|
92 |
+
|
93 |
+
return $can_activate;
|
94 |
+
}
|
95 |
+
|
96 |
+
/**
|
97 |
+
* If current user can:
|
98 |
+
* - install extensions
|
99 |
+
* - delete extensions
|
100 |
+
* @return bool
|
101 |
+
*/
|
102 |
+
public function can_install()
|
103 |
+
{
|
104 |
+
static $can_install = null;
|
105 |
+
|
106 |
+
if ($can_install === null) {
|
107 |
+
$capability = 'install_plugins';
|
108 |
+
|
109 |
+
if (is_multisite()) {
|
110 |
+
// only network admin can change files that affects the entire network
|
111 |
+
$can_install = current_user_can_for_blog(get_current_blog_id(), $capability);
|
112 |
+
} else {
|
113 |
+
$can_install = current_user_can($capability);
|
114 |
+
}
|
115 |
+
|
116 |
+
if ($can_install) {
|
117 |
+
// also you can use this method to get the capability
|
118 |
+
$can_install = $capability;
|
119 |
+
}
|
120 |
+
}
|
121 |
+
|
122 |
+
return $can_install;
|
123 |
+
}
|
124 |
+
|
125 |
+
public function get_page_slug()
|
126 |
+
{
|
127 |
+
return 'fw-extensions';
|
128 |
+
}
|
129 |
+
|
130 |
+
private function get_cache_key($sub_key)
|
131 |
+
{
|
132 |
+
return 'fw_extensions_manager/'. $sub_key;
|
133 |
+
}
|
134 |
+
|
135 |
+
private function get_uri($append = '')
|
136 |
+
{
|
137 |
+
return fw_get_framework_directory_uri('/core/components/extensions/manager'. $append);
|
138 |
+
}
|
139 |
+
|
140 |
+
private function get_markdown_parser()
|
141 |
+
{
|
142 |
+
if (!$this->markdown_parser) {
|
143 |
+
if (!class_exists('Parsedown')) {
|
144 |
+
require_once dirname(__FILE__) .'/includes/parsedown/Parsedown.php';
|
145 |
+
}
|
146 |
+
|
147 |
+
$this->markdown_parser = new Parsedown();
|
148 |
+
}
|
149 |
+
|
150 |
+
return $this->markdown_parser;
|
151 |
+
}
|
152 |
+
|
153 |
+
private function get_nonce($form) {
|
154 |
+
switch ($form) {
|
155 |
+
case 'install':
|
156 |
+
return array(
|
157 |
+
'name' => '_nonce_fw_extensions_install',
|
158 |
+
'action' => 'install',
|
159 |
+
);
|
160 |
+
case 'delete':
|
161 |
+
return array(
|
162 |
+
'name' => '_nonce_fw_extensions_delete',
|
163 |
+
'action' => 'delete',
|
164 |
+
);
|
165 |
+
case 'activate':
|
166 |
+
return array(
|
167 |
+
'name' => '_nonce_fw_extensions_activate',
|
168 |
+
'action' => 'activate',
|
169 |
+
);
|
170 |
+
case 'deactivate':
|
171 |
+
return array(
|
172 |
+
'name' => '_nonce_fw_extensions_deactivate',
|
173 |
+
'action' => 'deactivate',
|
174 |
+
);
|
175 |
+
default:
|
176 |
+
return array(
|
177 |
+
'name' => '_nonce_fw_extensions',
|
178 |
+
'action' => 'default',
|
179 |
+
);
|
180 |
+
}
|
181 |
+
}
|
182 |
+
|
183 |
+
/**
|
184 |
+
* Extensions available for download
|
185 |
+
* @return array {name => data}
|
186 |
+
*/
|
187 |
+
private function get_available_extensions()
|
188 |
+
{
|
189 |
+
try {
|
190 |
+
$cache_key = $this->get_cache_key( 'available_extensions' );
|
191 |
+
|
192 |
+
return FW_Cache::get($cache_key);
|
193 |
+
} catch (FW_Cache_Not_Found_Exception $e) {
|
194 |
+
$vars = fw_get_variables_from_file( dirname( __FILE__ ) . '/available-extensions.php', array(
|
195 |
+
'extensions' => array()
|
196 |
+
) );
|
197 |
+
|
198 |
+
{
|
199 |
+
$installed_extensions = $this->get_installed_extensions();
|
200 |
+
$supported_extensions = fw()->theme->manifest->get('supported_extensions', array());
|
201 |
+
|
202 |
+
if (isset($installed_extensions['backup'])) {
|
203 |
+
// make sure only Backup or Backups can be installed
|
204 |
+
unset($vars['extensions']['backups']);
|
205 |
+
}
|
206 |
+
|
207 |
+
foreach (
|
208 |
+
array('backup', 'styling', 'translation', 'learning')
|
209 |
+
as $obsolete_extension
|
210 |
+
) {
|
211 |
+
if (
|
212 |
+
!isset($supported_extensions[$obsolete_extension])
|
213 |
+
&&
|
214 |
+
!isset($installed_extensions[$obsolete_extension])
|
215 |
+
) {
|
216 |
+
unset($vars['extensions'][$obsolete_extension]);
|
217 |
+
}
|
218 |
+
}
|
219 |
+
}
|
220 |
+
|
221 |
+
FW_Cache::set($cache_key, $vars['extensions']);
|
222 |
+
|
223 |
+
return $vars['extensions'];
|
224 |
+
}
|
225 |
+
}
|
226 |
+
|
227 |
+
/**
|
228 |
+
* @internal
|
229 |
+
*/
|
230 |
+
public function _action_ajax_check_direct_fs_access()
|
231 |
+
{
|
232 |
+
if (!$this->can_install()) {
|
233 |
+
// if can't install, no need to know if has access or not
|
234 |
+
wp_send_json_error();
|
235 |
+
}
|
236 |
+
|
237 |
+
if (FW_WP_Filesystem::has_direct_access(fw_get_framework_directory('/extensions'))) {
|
238 |
+
wp_send_json_success();
|
239 |
+
} else {
|
240 |
+
wp_send_json_error();
|
241 |
+
}
|
242 |
+
}
|
243 |
+
|
244 |
+
/**
|
245 |
+
* @internal
|
246 |
+
*/
|
247 |
+
public function _action_ajax_install()
|
248 |
+
{
|
249 |
+
if (!$this->can_install()) {
|
250 |
+
// if can't install, no need to know if has access or not
|
251 |
+
wp_send_json_error();
|
252 |
+
}
|
253 |
+
|
254 |
+
if (!FW_WP_Filesystem::has_direct_access(fw_get_framework_directory('/extensions'))) {
|
255 |
+
wp_send_json_error();
|
256 |
+
}
|
257 |
+
|
258 |
+
$extension = (string)FW_Request::POST('extension');
|
259 |
+
|
260 |
+
$install_result = $this->install_extensions(array(
|
261 |
+
$extension => array()
|
262 |
+
), array(
|
263 |
+
'cancel_on_error' => true
|
264 |
+
));
|
265 |
+
|
266 |
+
if ($install_result === true) {
|
267 |
+
wp_send_json_success();
|
268 |
+
} else {
|
269 |
+
wp_send_json_error($install_result);
|
270 |
+
}
|
271 |
+
}
|
272 |
+
|
273 |
+
/**
|
274 |
+
* @internal
|
275 |
+
*/
|
276 |
+
public function _action_ajax_uninstall()
|
277 |
+
{
|
278 |
+
if (!$this->can_install()) {
|
279 |
+
// if can't install, no need to know if has access or not
|
280 |
+
wp_send_json_error();
|
281 |
+
}
|
282 |
+
|
283 |
+
if (!FW_WP_Filesystem::has_direct_access(fw_get_framework_directory('/extensions'))) {
|
284 |
+
wp_send_json_error();
|
285 |
+
}
|
286 |
+
|
287 |
+
$extension = (string)FW_Request::POST('extension');
|
288 |
+
|
289 |
+
$install_result = $this->uninstall_extensions(array(
|
290 |
+
$extension => array()
|
291 |
+
), array(
|
292 |
+
'cancel_on_error' => true
|
293 |
+
));
|
294 |
+
|
295 |
+
if ($install_result === true) {
|
296 |
+
wp_send_json_success();
|
297 |
+
} else {
|
298 |
+
wp_send_json_error($install_result);
|
299 |
+
}
|
300 |
+
}
|
301 |
+
|
302 |
+
/**
|
303 |
+
* @internal
|
304 |
+
*/
|
305 |
+
public function _action_after_plugin_activate()
|
306 |
+
{
|
307 |
+
$this->activate_theme_extensions();
|
308 |
+
$this->activate_extensions(
|
309 |
+
array_fill_keys(
|
310 |
+
array_keys(fw()->theme->manifest->get('supported_extensions', array())),
|
311 |
+
array()
|
312 |
+
)
|
313 |
+
);
|
314 |
+
|
315 |
+
if ($this->can_install()) {
|
316 |
+
if ($this->get_supported_extensions_for_install()) {
|
317 |
+
$link = $this->get_link();
|
318 |
+
|
319 |
+
wp_redirect($link . '&sub-page=install&supported');
|
320 |
+
exit;
|
321 |
+
}
|
322 |
+
}
|
323 |
+
}
|
324 |
+
|
325 |
+
/**
|
326 |
+
* Copy all extensions to a temp backup directory
|
327 |
+
* @internal
|
328 |
+
*/
|
329 |
+
public function _action_plugin_pre_update()
|
330 |
+
{
|
331 |
+
/** @var WP_Filesystem_Base $wp_filesystem */
|
332 |
+
global $wp_filesystem;
|
333 |
+
|
334 |
+
if (!$wp_filesystem || (is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code())) {
|
335 |
+
return;
|
336 |
+
}
|
337 |
+
|
338 |
+
// a directory outside the plugin
|
339 |
+
$tmp_dir = FW_WP_Filesystem::real_path_to_filesystem_path(
|
340 |
+
fw_fix_path(WP_CONTENT_DIR) .'/tmp/fw-plugin-update-extensions-backup'
|
341 |
+
);
|
342 |
+
$extensions_dir = FW_WP_Filesystem::real_path_to_filesystem_path(
|
343 |
+
fw_get_framework_directory('/extensions')
|
344 |
+
);
|
345 |
+
|
346 |
+
$error = false;
|
347 |
+
|
348 |
+
do {
|
349 |
+
if ($wp_filesystem->exists($tmp_dir)) {
|
350 |
+
if (!$wp_filesystem->delete($tmp_dir, true, 'd')) {
|
351 |
+
$error = __('Cannot remove the old extensions backup dir', 'fw');
|
352 |
+
break;
|
353 |
+
}
|
354 |
+
}
|
355 |
+
|
356 |
+
if (!FW_WP_Filesystem::mkdir_recursive($tmp_dir)) {
|
357 |
+
$error = __('Cannot create the extensions backup dir', 'fw');
|
358 |
+
break;
|
359 |
+
}
|
360 |
+
|
361 |
+
if (true !== copy_dir($extensions_dir, $tmp_dir)) {
|
362 |
+
$error = __('Cannot backup the extensions', 'fw');
|
363 |
+
break;
|
364 |
+
}
|
365 |
+
} while(false);
|
366 |
+
|
367 |
+
if ($error) {
|
368 |
+
trigger_error($error, E_USER_WARNING);
|
369 |
+
|
370 |
+
$wp_filesystem->delete($tmp_dir, true, 'd');
|
371 |
+
}
|
372 |
+
}
|
373 |
+
|
374 |
+
/**
|
375 |
+
* Copy all extensions from the temp backup directory to the framework extensions directory (recover)
|
376 |
+
* @internal
|
377 |
+
*/
|
378 |
+
public function _action_plugin_post_update()
|
379 |
+
{
|
380 |
+
/** @var WP_Filesystem_Base $wp_filesystem */
|
381 |
+
global $wp_filesystem;
|
382 |
+
|
383 |
+
if ( !$wp_filesystem || (is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code()) ) {
|
384 |
+
return;
|
385 |
+
}
|
386 |
+
|
387 |
+
// a directory outside the plugin
|
388 |
+
$tmp_dir = FW_WP_Filesystem::real_path_to_filesystem_path(
|
389 |
+
fw_fix_path( WP_CONTENT_DIR ) .'/tmp/fw-plugin-update-extensions-backup'
|
390 |
+
);
|
391 |
+
$extensions_dir = FW_WP_Filesystem::real_path_to_filesystem_path(
|
392 |
+
fw_get_framework_directory( '/extensions' )
|
393 |
+
);
|
394 |
+
|
395 |
+
if (!$wp_filesystem->exists($tmp_dir) || !$wp_filesystem->exists($extensions_dir)) {
|
396 |
+
return;
|
397 |
+
}
|
398 |
+
|
399 |
+
$error = false;
|
400 |
+
|
401 |
+
do {
|
402 |
+
if ($wp_filesystem->exists($extensions_dir)) {
|
403 |
+
/**
|
404 |
+
* Make sure to remove framework initial extensions
|
405 |
+
* The user do not need them because he already used the framework and has in backup the extensions he uses
|
406 |
+
*/
|
407 |
+
if (!$wp_filesystem->delete( $extensions_dir, true, 'd' )) {
|
408 |
+
$error = __( 'Cannot clear the extensions directory', 'fw' );
|
409 |
+
break;
|
410 |
+
}
|
411 |
+
|
412 |
+
if ( ! FW_WP_Filesystem::mkdir_recursive( $extensions_dir ) ) {
|
413 |
+
$error = __( 'Cannot recreate the extensions directory', 'fw' );
|
414 |
+
break;
|
415 |
+
}
|
416 |
+
}
|
417 |
+
|
418 |
+
if (true !== copy_dir($tmp_dir, $extensions_dir)) {
|
419 |
+
$error = __('Cannot recover the extensions', 'fw');
|
420 |
+
break;
|
421 |
+
}
|
422 |
+
} while(false);
|
423 |
+
|
424 |
+
if ($error) {
|
425 |
+
trigger_error($error, E_USER_WARNING);
|
426 |
+
} else {
|
427 |
+
// extensions successfully recovered, the backup is not needed anymore
|
428 |
+
$wp_filesystem->delete($tmp_dir, true, 'd');
|
429 |
+
}
|
430 |
+
}
|
431 |
+
|
432 |
+
/**
|
433 |
+
* Scan all directories for extensions
|
434 |
+
*
|
435 |
+
* @param bool $reset_cache
|
436 |
+
* @return array
|
437 |
+
*/
|
438 |
+
private function get_installed_extensions($reset_cache = false)
|
439 |
+
{
|
440 |
+
$cache_key = $this->get_cache_key('installed_extensions');
|
441 |
+
|
442 |
+
if ($reset_cache) {
|
443 |
+
FW_Cache::del($cache_key);
|
444 |
+
}
|
445 |
+
|
446 |
+
try {
|
447 |
+
return FW_Cache::get($cache_key);
|
448 |
+
} catch (FW_Cache_Not_Found_Exception $e) {
|
449 |
+
$extensions = array();
|
450 |
+
|
451 |
+
foreach (fw()->extensions->get_locations() as $location) {
|
452 |
+
// leave only used keys
|
453 |
+
$location = array(
|
454 |
+
'path' => $location['path'],
|
455 |
+
'is' => $location['is'],
|
456 |
+
);
|
457 |
+
|
458 |
+
$this->read_extensions($location, $extensions);
|
459 |
+
}
|
460 |
+
|
461 |
+
FW_Cache::set($cache_key, $extensions);
|
462 |
+
|
463 |
+
return $extensions;
|
464 |
+
}
|
465 |
+
}
|
466 |
+
|
467 |
+
/**
|
468 |
+
* used by $this->get_installed_extensions()
|
469 |
+
* @param string $location
|
470 |
+
* @param array $list
|
471 |
+
* @param null|string $parent_extension_name
|
472 |
+
*/
|
473 |
+
private function read_extensions($location, &$list, $parent_extension_name = null)
|
474 |
+
{
|
475 |
+
$paths = glob($location['path'] .'/*', GLOB_ONLYDIR | GLOB_NOSORT);
|
476 |
+
|
477 |
+
if (empty($paths)) {
|
478 |
+
return;
|
479 |
+
}
|
480 |
+
|
481 |
+
foreach ($paths as $extension_path) {
|
482 |
+
$extension_name = basename($extension_path);
|
483 |
+
|
484 |
+
if (isset($list[$extension_name])) {
|
485 |
+
// extension already found
|
486 |
+
} elseif (file_exists($extension_path .'/manifest.php')) {
|
487 |
+
$vars = fw_get_variables_from_file($extension_path .'/manifest.php', array(
|
488 |
+
'manifest' => array(),
|
489 |
+
));
|
490 |
+
|
491 |
+
$list[$extension_name] = array(
|
492 |
+
'path' => $extension_path,
|
493 |
+
'manifest' => $vars['manifest'],
|
494 |
+
'children' => array(),
|
495 |
+
'active' => (bool)fw()->extensions->get($extension_name),
|
496 |
+
'parent' => $parent_extension_name,
|
497 |
+
'is' => $location['is'],
|
498 |
+
);
|
499 |
+
|
500 |
+
if ($parent_extension_name) {
|
501 |
+
$list[ $parent_extension_name ]['children'][$extension_name] = array();
|
502 |
+
}
|
503 |
+
} else {
|
504 |
+
// it's a directory with customizations for an extension
|
505 |
+
continue;
|
506 |
+
}
|
507 |
+
|
508 |
+
$sub_extension_location = $location;
|
509 |
+
$sub_extension_location['path'] .= '/'. $extension_name .'/extensions';
|
510 |
+
|
511 |
+
$this->read_extensions(
|
512 |
+
$sub_extension_location,
|
513 |
+
$list,
|
514 |
+
$extension_name
|
515 |
+
);
|
516 |
+
}
|
517 |
+
}
|
518 |
+
|
519 |
+
private function get_tmp_dir($append = '')
|
520 |
+
{
|
521 |
+
return apply_filters('fw_tmp_dir', fw_fix_path(WP_CONTENT_DIR) .'/tmp') . $append;
|
522 |
+
}
|
523 |
+
|
524 |
+
/**
|
525 |
+
* @internal
|
526 |
+
*/
|
527 |
+
public function _action_fw_init()
|
528 |
+
{
|
529 |
+
$this->extension_settings_form = new FW_Form('fw_extension_settings', array(
|
530 |
+
'render' => array($this, '_extension_settings_form_render'),
|
531 |
+
'validate' => array($this, '_extension_settings_form_validate'),
|
532 |
+
'save' => array($this, '_extension_settings_form_save'),
|
533 |
+
));
|
534 |
+
|
535 |
+
if (is_admin() && $this->can_activate()) {
|
536 |
+
$db_wp_option_name = 'fw_extensions_activation';
|
537 |
+
|
538 |
+
if ($db_wp_option_value = get_option($db_wp_option_name, array())) {
|
539 |
+
$db_wp_option_value = array_merge(array(
|
540 |
+
'activated' => array(),
|
541 |
+
'deactivated' => array(),
|
542 |
+
), $db_wp_option_value);
|
543 |
+
|
544 |
+
/**
|
545 |
+
* Fire the 'fw_extensions_after_activation' action
|
546 |
+
*/
|
547 |
+
if ($db_wp_option_value['activated']) {
|
548 |
+
$succeeded_extensions = $failed_extensions = array();
|
549 |
+
|
550 |
+
foreach ($db_wp_option_value['activated'] as $extension_name => $not_used_var) {
|
551 |
+
if (fw_ext($extension_name)) {
|
552 |
+
$succeeded_extensions[$extension_name] = array();
|
553 |
+
} else {
|
554 |
+
$failed_extensions[$extension_name] = array();
|
555 |
+
}
|
556 |
+
}
|
557 |
+
|
558 |
+
if (!empty($succeeded_extensions)) {
|
559 |
+
do_action('fw_extensions_after_activation', $succeeded_extensions);
|
560 |
+
}
|
561 |
+
if (!empty($failed_extensions)) {
|
562 |
+
do_action('fw_extensions_activation_failed', $failed_extensions);
|
563 |
+
}
|
564 |
+
}
|
565 |
+
|
566 |
+
/**
|
567 |
+
* Fire the 'fw_extensions_after_deactivation' action
|
568 |
+
*/
|
569 |
+
if ($db_wp_option_value['deactivated']) {
|
570 |
+
$succeeded_extensions = $failed_extensions = array();
|
571 |
+
|
572 |
+
foreach ($db_wp_option_value['deactivated'] as $extension_name => $not_used_var) {
|
573 |
+
if (!fw_ext($extension_name)) {
|
574 |
+
$succeeded_extensions[$extension_name] = array();
|
575 |
+
} else {
|
576 |
+
$failed_extensions[$extension_name] = array();
|
577 |
+
}
|
578 |
+
}
|
579 |
+
|
580 |
+
if (!empty($succeeded_extensions)) {
|
581 |
+
do_action('fw_extensions_after_deactivation', $succeeded_extensions);
|
582 |
+
}
|
583 |
+
if (!empty($failed_extensions)) {
|
584 |
+
do_action('fw_extensions_deactivation_failed', $failed_extensions);
|
585 |
+
}
|
586 |
+
}
|
587 |
+
|
588 |
+
delete_option($db_wp_option_name);
|
589 |
+
}
|
590 |
+
}
|
591 |
+
}
|
592 |
+
|
593 |
+
/**
|
594 |
+
* Activate extensions with $manifest['display'] = false; $manifest['standalone'] = true;
|
595 |
+
* - First level extensions
|
596 |
+
* - Child extensions of the active extensions
|
597 |
+
*/
|
598 |
+
private function activate_hidden_standalone_extensions()
|
599 |
+
{
|
600 |
+
if (!is_admin()) {
|
601 |
+
return;
|
602 |
+
}
|
603 |
+
|
604 |
+
if (!$this->can_activate()) {
|
605 |
+
return;
|
606 |
+
}
|
607 |
+
|
608 |
+
$activate_extensions = array();
|
609 |
+
|
610 |
+
foreach (
|
611 |
+
// all disabled extensions
|
612 |
+
array_diff_key($this->get_installed_extensions(), fw()->extensions->get_all())
|
613 |
+
as $ext_name => $ext_data
|
614 |
+
) {
|
615 |
+
if ($ext_data['parent'] && !fw_ext($ext_data['parent'])) {
|
616 |
+
// child extensions of an inactive extension
|
617 |
+
continue;
|
618 |
+
}
|
619 |
+
|
620 |
+
if (false !== fw_akg(
|
621 |
+
'display',
|
622 |
+
$ext_data['manifest'],
|
623 |
+
$this->manifest_default_values['display']
|
624 |
+
)) {
|
625 |
+
// is visible
|
626 |
+
continue;
|
627 |
+
}
|
628 |
+
|
629 |
+
if (true !== fw_akg(
|
630 |
+
'standalone',
|
631 |
+
$ext_data['manifest'],
|
632 |
+
$this->manifest_default_values['standalone']
|
633 |
+
)) {
|
634 |
+
// not standalone
|
635 |
+
continue;
|
636 |
+
}
|
637 |
+
|
638 |
+
$collected = $this->get_extensions_for_activation($ext_name);
|
639 |
+
|
640 |
+
if (is_wp_error($collected)) {
|
641 |
+
if (defined('WP_DEBUG') && WP_DEBUG) {
|
642 |
+
if ($this->is_extensions_page()) {
|
643 |
+
// display this warning only on Unyson extensions page
|
644 |
+
FW_Flash_Messages::add('fw_ext_auto_activate_hidden_standalone',
|
645 |
+
sprintf(__('Cannot activate hidden standalone extension %s', 'fw'),
|
646 |
+
fw_akg('name', $ext_data['manifest'], fw_id_to_title($ext_name))
|
647 |
+
),
|
648 |
+
'error'
|
649 |
+
);
|
650 |
+
}
|
651 |
+
}
|
652 |
+
return;
|
653 |
+
}
|
654 |
+
|
655 |
+
$activate_extensions = array_merge($activate_extensions, $collected);
|
656 |
+
}
|
657 |
+
|
658 |
+
if (empty($activate_extensions)) {
|
659 |
+
return;
|
660 |
+
}
|
661 |
+
|
662 |
+
$option_name = fw()->extensions->_get_active_extensions_db_option_name();
|
663 |
+
|
664 |
+
$db_active_extensions = array_merge(get_option($option_name, array()), $activate_extensions);
|
665 |
+
|
666 |
+
update_option($option_name, $db_active_extensions);
|
667 |
+
}
|
668 |
+
|
669 |
+
/**
|
670 |
+
* @internal
|
671 |
+
*/
|
672 |
+
public function _action_admin_menu()
|
673 |
+
{
|
674 |
+
$capability = $this->can_activate();
|
675 |
+
|
676 |
+
if (!$capability) {
|
677 |
+
return;
|
678 |
+
}
|
679 |
+
|
680 |
+
$data = array(
|
681 |
+
'title' => fw()->manifest->get_name(),
|
682 |
+
'capability' => $capability,
|
683 |
+
'slug' => $this->get_page_slug(),
|
684 |
+
'content_callback' => array($this, '_display_page'),
|
685 |
+
);
|
686 |
+
|
687 |
+
/**
|
688 |
+
* Collect $hookname that contains $data['slug'] before the action
|
689 |
+
* and skip them in verification after action
|
690 |
+
*/
|
691 |
+
{
|
692 |
+
global $_registered_pages;
|
693 |
+
|
694 |
+
$found_hooknames = array();
|
695 |
+
|
696 |
+
if (!empty($_registered_pages)) {
|
697 |
+
foreach ( $_registered_pages as $hookname => $b ) {
|
698 |
+
if ( strpos( $hookname, $data['slug'] ) !== false ) {
|
699 |
+
$found_hooknames[$hookname] = true;
|
700 |
+
}
|
701 |
+
}
|
702 |
+
}
|
703 |
+
}
|
704 |
+
|
705 |
+
/**
|
706 |
+
* Use this action if you what to add the extensions page in a custom place in menu
|
707 |
+
* Usage example http://pastebin.com/2iWVRPAU
|
708 |
+
*/
|
709 |
+
do_action('fw_backend_add_custom_extensions_menu', $data);
|
710 |
+
|
711 |
+
/**
|
712 |
+
* Check if menu was added in the action above
|
713 |
+
*/
|
714 |
+
{
|
715 |
+
$menu_exists = false;
|
716 |
+
|
717 |
+
if (!empty($_registered_pages)) {
|
718 |
+
foreach ( $_registered_pages as $hookname => $b ) {
|
719 |
+
if (isset($found_hooknames[$hookname])) {
|
720 |
+
continue;
|
721 |
+
}
|
722 |
+
|
723 |
+
if ( strpos( $hookname, $data['slug'] ) !== false ) {
|
724 |
+
$menu_exists = true;
|
725 |
+
break;
|
726 |
+
}
|
727 |
+
}
|
728 |
+
}
|
729 |
+
}
|
730 |
+
|
731 |
+
if ($menu_exists) {
|
732 |
+
// do nothing
|
733 |
+
} else {
|
734 |
+
add_menu_page(
|
735 |
+
$data['title'],
|
736 |
+
$data['title'],
|
737 |
+
$data['capability'],
|
738 |
+
$data['slug'],
|
739 |
+
$data['content_callback'],
|
740 |
+
'none',
|
741 |
+
3
|
742 |
+
);
|
743 |
+
}
|
744 |
+
}
|
745 |
+
|
746 |
+
/**
|
747 |
+
* If output already started, we cannot set the redirect header, do redirect from js
|
748 |
+
*/
|
749 |
+
private function js_redirect()
|
750 |
+
{
|
751 |
+
echo
|
752 |
+
'<script type="text/javascript">'.
|
753 |
+
'window.location.replace("'. esc_js($this->get_link()) .'");'.
|
754 |
+
'</script>';
|
755 |
+
}
|
756 |
+
|
757 |
+
/**
|
758 |
+
* @internal
|
759 |
+
*/
|
760 |
+
public function _display_page()
|
761 |
+
{
|
762 |
+
$page = FW_Request::GET('sub-page');
|
763 |
+
|
764 |
+
switch ($page) {
|
765 |
+
case 'install':
|
766 |
+
$this->display_install_page();
|
767 |
+
break;
|
768 |
+
case 'delete':
|
769 |
+
$this->display_delete_page();
|
770 |
+
break;
|
771 |
+
case 'extension':
|
772 |
+
$this->display_extension_page();
|
773 |
+
break;
|
774 |
+
case 'activate':
|
775 |
+
$this->display_activate_page();
|
776 |
+
break;
|
777 |
+
case 'deactivate':
|
778 |
+
$this->display_deactivate_page();
|
779 |
+
break;
|
780 |
+
default:
|
781 |
+
$this->display_list_page();
|
782 |
+
}
|
783 |
+
}
|
784 |
+
|
785 |
+
private function display_list_page()
|
786 |
+
{
|
787 |
+
// note: static is enqueued in 'admin_enqueue_scripts' action
|
788 |
+
|
789 |
+
/** Prepare extensions list for view */
|
790 |
+
{
|
791 |
+
$lists = array(
|
792 |
+
'active' => array(),
|
793 |
+
'disabled' => array(),
|
794 |
+
'installed' => array(),
|
795 |
+
'available' => array(),
|
796 |
+
'supported' => array(),
|
797 |
+
);
|
798 |
+
|
799 |
+
foreach ($this->get_installed_extensions() as $ext_name => $ext_data) {
|
800 |
+
$lists[ $ext_data['active'] ? 'active' : 'disabled' ][$ext_name] = $ext_data;
|
801 |
+
}
|
802 |
+
|
803 |
+
$lists['installed'] = $lists['active'] + $lists['disabled'];
|
804 |
+
|
805 |
+
unset($ext_data); // prevent change by reference
|
806 |
+
|
807 |
+
foreach ($this->get_available_extensions() as $ext_name => $ext_data) {
|
808 |
+
$lists['available'][$ext_name] = array(
|
809 |
+
'name' => $ext_data['name'],
|
810 |
+
'description' => $ext_data['description'],
|
811 |
+
'thumbnail' => isset($ext_data['thumbnail'])
|
812 |
+
? $ext_data['thumbnail']
|
813 |
+
: (isset($lists['installed'][$ext_name])
|
814 |
+
? fw_akg('thumbnail', $lists['installed'][$ext_name]['manifest'], $this->default_thumbnail)
|
815 |
+
: $this->default_thumbnail),
|
816 |
+
'display' => isset($ext_data['display'])
|
817 |
+
? $ext_data['display']
|
818 |
+
: $this->manifest_default_values['display'],
|
819 |
+
);
|
820 |
+
}
|
821 |
+
|
822 |
+
foreach (fw()->theme->manifest->get('supported_extensions', array()) as $required_ext_name => $required_ext_data) {
|
823 |
+
if (isset($lists['installed'][ $required_ext_name ])) {
|
824 |
+
$lists['supported'][ $required_ext_name ] = array(
|
825 |
+
'name' => fw_akg( 'name', $lists['installed'][ $required_ext_name ]['manifest'], fw_id_to_title( $required_ext_name ) ),
|
826 |
+
'description' => fw_akg( 'description', $lists['installed'][ $required_ext_name ]['manifest'], '' ),
|
827 |
+
);
|
828 |
+
} elseif (isset($lists['available'][$required_ext_name])) {
|
829 |
+
$lists['supported'][ $required_ext_name ] = array(
|
830 |
+
'name' => $lists['available'][ $required_ext_name ]['name'],
|
831 |
+
'description' => $lists['available'][ $required_ext_name ]['description'],
|
832 |
+
);
|
833 |
+
} else {
|
834 |
+
$lists['supported'][ $required_ext_name ] = array(
|
835 |
+
'name' => fw_id_to_title( $required_ext_name ),
|
836 |
+
'description' => '',
|
837 |
+
);
|
838 |
+
}
|
839 |
+
}
|
840 |
+
}
|
841 |
+
|
842 |
+
echo '<div class="wrap">';
|
843 |
+
|
844 |
+
echo '<h2>'. sprintf(__('%s Extensions', 'fw'), fw()->manifest->get_name()) .'</h2><br/>';
|
845 |
+
|
846 |
+
echo '<div id="fw-extensions-list-wrapper">';
|
847 |
+
|
848 |
+
fw_render_view(dirname(__FILE__) .'/views/extensions-page.php', array(
|
849 |
+
'lists' => &$lists,
|
850 |
+
'link' => $this->get_link(),
|
851 |
+
'display_default_value' => $this->manifest_default_values['display'],
|
852 |
+
'default_thumbnail' => $this->default_thumbnail,
|
853 |
+
'nonces' => array(
|
854 |
+
'delete' => $this->get_nonce('delete'),
|
855 |
+
'install' => $this->get_nonce('install'),
|
856 |
+
'activate' => $this->get_nonce('activate'),
|
857 |
+
'deactivate' => $this->get_nonce('deactivate'),
|
858 |
+
),
|
859 |
+
'can_install' => $this->can_install(),
|
860 |
+
), false);
|
861 |
+
|
862 |
+
echo '</div>';
|
863 |
+
|
864 |
+
echo '</div>';
|
865 |
+
}
|
866 |
+
|
867 |
+
private function display_install_page()
|
868 |
+
{
|
869 |
+
$flash_id = 'fw_extensions_install';
|
870 |
+
|
871 |
+
if (!$this->can_install()) {
|
872 |
+
FW_Flash_Messages::add(
|
873 |
+
$flash_id,
|
874 |
+
__('You are not allowed to install extensions.', 'fw'),
|
875 |
+
'error'
|
876 |
+
);
|
877 |
+
$this->js_redirect();
|
878 |
+
return;
|
879 |
+
}
|
880 |
+
|
881 |
+
if (array_key_exists('supported', $_GET)) {
|
882 |
+
$supported = true;
|
883 |
+
$extensions = array_fill_keys(
|
884 |
+
array_keys($this->get_supported_extensions_for_install()),
|
885 |
+
array()
|
886 |
+
);
|
887 |
+
|
888 |
+
if (empty($extensions)) {
|
889 |
+
FW_Flash_Messages::add(
|
890 |
+
$flash_id,
|
891 |
+
__('All supported extensions are already installed.', 'fw'),
|
892 |
+
'info'
|
893 |
+
);
|
894 |
+
$this->js_redirect();
|
895 |
+
return;
|
896 |
+
}
|
897 |
+
} else {
|
898 |
+
$supported = false;
|
899 |
+
|
900 |
+
$extensions = array_fill_keys(
|
901 |
+
array_map( 'trim', explode( ',', FW_Request::GET( 'extension', '' ) )),
|
902 |
+
array()
|
903 |
+
);
|
904 |
+
|
905 |
+
// activate already installed extensions
|
906 |
+
$this->activate_extensions($extensions);
|
907 |
+
}
|
908 |
+
|
909 |
+
{
|
910 |
+
if (!class_exists('_FW_Extensions_Install_Upgrader_Skin')) {
|
911 |
+
fw_include_file_isolated(
|
912 |
+
dirname(__FILE__) .'/includes/class--fw-extensions-install-upgrader-skin.php'
|
913 |
+
);
|
914 |
+
}
|
915 |
+
|
916 |
+
$skin = new _FW_Extensions_Install_Upgrader_Skin(array(
|
917 |
+
'title' => $supported
|
918 |
+
? _n('Install Compatible Extension', 'Install Compatible Extensions', count($extensions), 'fw')
|
919 |
+
: _n('Install Extension', 'Install Extensions', count($extensions), 'fw'),
|
920 |
+
));
|
921 |
+
}
|
922 |
+
|
923 |
+
$skin->header();
|
924 |
+
|
925 |
+
do {
|
926 |
+
$nonce = $this->get_nonce('install');
|
927 |
+
|
928 |
+
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
929 |
+
if (!isset($_POST[$nonce['name']]) || !wp_verify_nonce($_POST[$nonce['name']], $nonce['action'])) {
|
930 |
+
$skin->error(__('Invalid nonce.', 'fw'));
|
931 |
+
}
|
932 |
+
|
933 |
+
if (!FW_WP_Filesystem::request_access(
|
934 |
+
fw_get_framework_directory('/extensions'), fw_current_url(), array($nonce['name'])
|
935 |
+
)) {
|
936 |
+
break;
|
937 |
+
}
|
938 |
+
|
939 |
+
$install_result = $this->install_extensions($extensions, array('verbose' => $skin));
|
940 |
+
|
941 |
+
if (is_wp_error($install_result)) {
|
942 |
+
$skin->error($install_result);
|
943 |
+
} elseif (is_array($install_result)) {
|
944 |
+
$error = array();
|
945 |
+
|
946 |
+
foreach ($install_result as $extension_name => $extension_result) {
|
947 |
+
if (is_wp_error($extension_result)) {
|
948 |
+
$error[] = $extension_result->get_error_message();
|
949 |
+
}
|
950 |
+
}
|
951 |
+
|
952 |
+
$error = '<ul><li>'. implode('</li><li>', $error) .'</li></ul>';
|
953 |
+
|
954 |
+
$skin->error($error);
|
955 |
+
} elseif ($install_result === true) {
|
956 |
+
$skin->set_result(true);
|
957 |
+
}
|
958 |
+
|
959 |
+
/** @var WP_Filesystem_Base $wp_filesystem */
|
960 |
+
global $wp_filesystem;
|
961 |
+
|
962 |
+
$wp_fs_tmp_dir = FW_WP_Filesystem::real_path_to_filesystem_path($this->get_tmp_dir());
|
963 |
+
|
964 |
+
if ($wp_filesystem->exists($wp_fs_tmp_dir)) {
|
965 |
+
if ( ! $wp_filesystem->rmdir( $wp_fs_tmp_dir, true ) ) {
|
966 |
+
$skin->error(
|
967 |
+
sprintf( __( 'Cannot remove temporary directory: %s', 'fw' ), $wp_fs_tmp_dir )
|
968 |
+
);
|
969 |
+
}
|
970 |
+
}
|
971 |
+
|
972 |
+
$skin->after(array(
|
973 |
+
'extensions_page_link' => $this->get_link()
|
974 |
+
));
|
975 |
+
} else {
|
976 |
+
echo '<form method="post">';
|
977 |
+
|
978 |
+
wp_nonce_field($nonce['action'], $nonce['name']);
|
979 |
+
|
980 |
+
$extension_titles = array();
|
981 |
+
foreach ($extensions as $extension_name => $not_used_var) {
|
982 |
+
$extension_titles[$extension_name] = $this->get_extension_title($extension_name);
|
983 |
+
}
|
984 |
+
|
985 |
+
fw_render_view(dirname(__FILE__) .'/views/install-form.php', array(
|
986 |
+
'extension_titles' => $extension_titles,
|
987 |
+
'list_page_link' => $this->get_link(),
|
988 |
+
'supported' => $supported
|
989 |
+
), false);
|
990 |
+
|
991 |
+
echo '</form>';
|
992 |
+
}
|
993 |
+
} while(false);
|
994 |
+
|
995 |
+
$skin->footer();
|
996 |
+
}
|
997 |
+
|
998 |
+
/**
|
999 |
+
* Download (and activate) extensions
|
1000 |
+
* After refresh they should be active, if all dependencies will be met and if parent-extension::_init() will not return false
|
1001 |
+
* @param array $extensions {'ext_1' => array(), 'ext_2' => array(), ...}
|
1002 |
+
* @param array $opts
|
1003 |
+
* @return WP_Error|bool|array
|
1004 |
+
* true: when all extensions succeeded
|
1005 |
+
* array: when some/all failed
|
1006 |
+
*/
|
1007 |
+
public function install_extensions(array $extensions, $opts = array())
|
1008 |
+
{
|
1009 |
+
{
|
1010 |
+
$opts = array_merge(array(
|
1011 |
+
/**
|
1012 |
+
* @type bool
|
1013 |
+
* false: return {'ext_1' => true|WP_Error, 'ext_2' => true|WP_Error, ...}
|
1014 |
+
* true: return first WP_Error or true on success
|
1015 |
+
*/
|
1016 |
+
'cancel_on_error' => false,
|
1017 |
+
/**
|
1018 |
+
* @type bool Activate installed extensions
|
1019 |
+
*/
|
1020 |
+
'activate' => true,
|
1021 |
+
/**
|
1022 |
+
* @type bool|WP_Upgrader_Skin
|
1023 |
+
*/
|
1024 |
+
'verbose' => false,
|
1025 |
+
), $opts);
|
1026 |
+
|
1027 |
+
$cancel_on_error = $opts['cancel_on_error']; // fixme: remove successfully installed extensions before error?
|
1028 |
+
$activate = $opts['activate'];
|
1029 |
+
$verbose = $opts['verbose'];
|
1030 |
+
|
1031 |
+
unset($opts);
|
1032 |
+
}
|
1033 |
+
|
1034 |
+
if (!$this->can_install()) {
|
1035 |
+
return new WP_Error(
|
1036 |
+
'access_denied',
|
1037 |
+
__('You have no permissions to install extensions', 'fw')
|
1038 |
+
);
|
1039 |
+
}
|
1040 |
+
|
1041 |
+
if (empty($extensions)) {
|
1042 |
+
return new WP_Error(
|
1043 |
+
'no_extensions',
|
1044 |
+
__('No extensions provided', 'fw')
|
1045 |
+
);
|
1046 |
+
}
|
1047 |
+
|
1048 |
+
global $wp_filesystem;
|
1049 |
+
|
1050 |
+
if (!$wp_filesystem || (is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code())) {
|
1051 |
+
return new WP_Error(
|
1052 |
+
'fs_not_initialized',
|
1053 |
+
__('WP Filesystem is not initialized', 'fw')
|
1054 |
+
);
|
1055 |
+
}
|
1056 |
+
|
1057 |
+
if (function_exists('ini_get')) {
|
1058 |
+
$timeout = intval(ini_get('max_execution_time'));
|
1059 |
+
} else {
|
1060 |
+
$timeout = false;
|
1061 |
+
}
|
1062 |
+
|
1063 |
+
$available_extensions = $this->get_available_extensions();
|
1064 |
+
$installed_extensions = $this->get_installed_extensions();
|
1065 |
+
|
1066 |
+
$result = $downloaded_extensions = array();
|
1067 |
+
$has_errors = false;
|
1068 |
+
|
1069 |
+
while (!empty($extensions)) {
|
1070 |
+
$not_used_var = reset($extensions);
|
1071 |
+
$extension_name = key($extensions);
|
1072 |
+
unset($extensions[$extension_name]);
|
1073 |
+
|
1074 |
+
$extensions_before_install = array_keys($installed_extensions);
|
1075 |
+
|
1076 |
+
if (isset($installed_extensions[$extension_name])) {
|
1077 |
+
$result[$extension_name] = new WP_Error(
|
1078 |
+
'extension_installed',
|
1079 |
+
sprintf(__('Extension "%s" is already installed.', 'fw'), $this->get_extension_title($extension_name))
|
1080 |
+
);
|
1081 |
+
$has_errors = true;
|
1082 |
+
|
1083 |
+
if ($cancel_on_error) {
|
1084 |
+
break;
|
1085 |
+
} else {
|
1086 |
+
continue;
|
1087 |
+
}
|
1088 |
+
}
|
1089 |
+
|
1090 |
+
if (!isset($available_extensions[ $extension_name ])) {
|
1091 |
+
$result[$extension_name] = new WP_Error(
|
1092 |
+
'extension_not_available',
|
1093 |
+
sprintf(
|
1094 |
+
__('Extension "%s" is not available for install.', 'fw'),
|
1095 |
+
$this->get_extension_title($extension_name)
|
1096 |
+
)
|
1097 |
+
);
|
1098 |
+
$has_errors = true;
|
1099 |
+
|
1100 |
+
if ($cancel_on_error) {
|
1101 |
+
break;
|
1102 |
+
} else {
|
1103 |
+
continue;
|
1104 |
+
}
|
1105 |
+
}
|
1106 |
+
|
1107 |
+
/**
|
1108 |
+
* Find parent extensions
|
1109 |
+
* they will be installed if does not exist
|
1110 |
+
*/
|
1111 |
+
{
|
1112 |
+
$parents = array($extension_name);
|
1113 |
+
|
1114 |
+
$current_parent = $extension_name;
|
1115 |
+
while (!empty($available_extensions[$current_parent]['parent'])) {
|
1116 |
+
$current_parent = $available_extensions[$current_parent]['parent'];
|
1117 |
+
|
1118 |
+
if (!isset($available_extensions[$current_parent])) {
|
1119 |
+
$result[$extension_name] = new WP_Error(
|
1120 |
+
'parent_extension_not_available',
|
1121 |
+
sprintf(
|
1122 |
+
__('Parent extension "%s" not available.', 'fw'),
|
1123 |
+
$this->get_extension_title($current_parent)
|
1124 |
+
)
|
1125 |
+
);
|
1126 |
+
$has_errors = true;
|
1127 |
+
|
1128 |
+
if ($cancel_on_error) {
|
1129 |
+
break 2;
|
1130 |
+
} else {
|
1131 |
+
continue 2;
|
1132 |
+
}
|
1133 |
+
}
|
1134 |
+
|
1135 |
+
$parents[] = $current_parent;
|
1136 |
+
}
|
1137 |
+
|
1138 |
+
$parents = array_reverse($parents);
|
1139 |
+
}
|
1140 |
+
|
1141 |
+
/**
|
1142 |
+
* Install parent extensions and the extension
|
1143 |
+
*/
|
1144 |
+
{
|
1145 |
+
$current_extension_path = fw_get_framework_directory();
|
1146 |
+
|
1147 |
+
foreach ($parents as $parent_extension_name) {
|
1148 |
+
$current_extension_path .= '/extensions/'. $parent_extension_name;
|
1149 |
+
|
1150 |
+
if (isset($installed_extensions[$parent_extension_name])) {
|
1151 |
+
// skip already installed extensions
|
1152 |
+
continue;
|
1153 |
+
}
|
1154 |
+
|
1155 |
+
if ($verbose) {
|
1156 |
+
$verbose_message = sprintf(__('Downloading the "%s" extension...', 'fw'),
|
1157 |
+
$this->get_extension_title($parent_extension_name)
|
1158 |
+
);
|
1159 |
+
|
1160 |
+
if (is_subclass_of($verbose, 'WP_Upgrader_Skin')) {
|
1161 |
+
$verbose->feedback($verbose_message);
|
1162 |
+
} else {
|
1163 |
+
echo fw_html_tag('p', array(), $verbose_message);
|
1164 |
+
}
|
1165 |
+
}
|
1166 |
+
|
1167 |
+
// increase timeout
|
1168 |
+
if ($timeout !== false && function_exists('set_time_limit')) {
|
1169 |
+
$timeout += 30;
|
1170 |
+
set_time_limit($timeout);
|
1171 |
+
}
|
1172 |
+
|
1173 |
+
$wp_fw_downloaded_dir = $this->download(
|
1174 |
+
$parent_extension_name,
|
1175 |
+
$available_extensions[$parent_extension_name]
|
1176 |
+
);
|
1177 |
+
|
1178 |
+
if (is_wp_error($wp_fw_downloaded_dir)) {
|
1179 |
+
if ($verbose) {
|
1180 |
+
$verbose_message = $wp_fw_downloaded_dir->get_error_message();
|
1181 |
+
|
1182 |
+
if (is_subclass_of($verbose, 'WP_Upgrader_Skin')) {
|
1183 |
+
$verbose->error($verbose_message);
|
1184 |
+
} else {
|
1185 |
+
echo fw_html_tag('p', array(), $verbose_message);
|
1186 |
+
}
|
1187 |
+
}
|
1188 |
+
|
1189 |
+
$result[$extension_name] = $wp_fw_downloaded_dir;
|
1190 |
+
$has_errors = true;
|
1191 |
+
|
1192 |
+
if ($cancel_on_error) {
|
1193 |
+
break 2;
|
1194 |
+
} else {
|
1195 |
+
continue 2;
|
1196 |
+
}
|
1197 |
+
}
|
1198 |
+
|
1199 |
+
if ($verbose) {
|
1200 |
+
$verbose_message = sprintf(__('Installing the "%s" extension...', 'fw'),
|
1201 |
+
$this->get_extension_title($parent_extension_name)
|
1202 |
+
);
|
1203 |
+
|
1204 |
+
if (is_subclass_of($verbose, 'WP_Upgrader_Skin')) {
|
1205 |
+
$verbose->feedback($verbose_message);
|
1206 |
+
} else {
|
1207 |
+
echo fw_html_tag('p', array(), $verbose_message);
|
1208 |
+
}
|
1209 |
+
}
|
1210 |
+
|
1211 |
+
$merge_result = $this->merge_extension(
|
1212 |
+
$wp_fw_downloaded_dir,
|
1213 |
+
FW_WP_Filesystem::real_path_to_filesystem_path($current_extension_path)
|
1214 |
+
);
|
1215 |
+
|
1216 |
+
if (is_wp_error($merge_result)) {
|
1217 |
+
if ($verbose) {
|
1218 |
+
$verbose_message = $merge_result->get_error_message();
|
1219 |
+
|
1220 |
+
if (is_subclass_of($verbose, 'WP_Upgrader_Skin')) {
|
1221 |
+
$verbose->error($verbose_message);
|
1222 |
+
} else {
|
1223 |
+
echo fw_html_tag('p', array(), $verbose_message);
|
1224 |
+
}
|
1225 |
+
}
|
1226 |
+
|
1227 |
+
$result[$extension_name] = $merge_result;
|
1228 |
+
$has_errors = true;
|
1229 |
+
|
1230 |
+
if ($cancel_on_error) {
|
1231 |
+
break 2;
|
1232 |
+
} else {
|
1233 |
+
continue 2;
|
1234 |
+
}
|
1235 |
+
}
|
1236 |
+
|
1237 |
+
if ($verbose) {
|
1238 |
+
$verbose_message = sprintf(__('The %s extension has been successfully installed.', 'fw'),
|
1239 |
+
$this->get_extension_title($parent_extension_name)
|
1240 |
+
);
|
1241 |
+
|
1242 |
+
if (is_subclass_of($verbose, 'WP_Upgrader_Skin')) {
|
1243 |
+
$verbose->feedback($verbose_message);
|
1244 |
+
} else {
|
1245 |
+
echo fw_html_tag('p', array(), $verbose_message);
|
1246 |
+
}
|
1247 |
+
}
|
1248 |
+
|
1249 |
+
$downloaded_extensions[$parent_extension_name] = array();
|
1250 |
+
|
1251 |
+
/**
|
1252 |
+
* Read again all extensions
|
1253 |
+
* The downloaded extension may contain more sub extensions
|
1254 |
+
*/
|
1255 |
+
{
|
1256 |
+
unset($installed_extensions);
|
1257 |
+
$installed_extensions = $this->get_installed_extensions(true);
|
1258 |
+
}
|
1259 |
+
}
|
1260 |
+
}
|
1261 |
+
|
1262 |
+
$result[$extension_name] = true;
|
1263 |
+
|
1264 |
+
/**
|
1265 |
+
* Collect required extensions of the newly installed extensions
|
1266 |
+
*/
|
1267 |
+
foreach (
|
1268 |
+
// new extensions
|
1269 |
+
array_diff(
|
1270 |
+
array_keys($installed_extensions),
|
1271 |
+
$extensions_before_install
|
1272 |
+
)
|
1273 |
+
as $new_extension_name
|
1274 |
+
) {
|
1275 |
+
foreach (
|
1276 |
+
array_keys(
|
1277 |
+
fw_akg(
|
1278 |
+
'requirements/extensions',
|
1279 |
+
$installed_extensions[$new_extension_name]['manifest'],
|
1280 |
+
array()
|
1281 |
+
)
|
1282 |
+
)
|
1283 |
+
as $required_extension_name
|
1284 |
+
) {
|
1285 |
+
if (isset($installed_extensions[$required_extension_name])) {
|
1286 |
+
// already installed
|
1287 |
+
continue;
|
1288 |
+
}
|
1289 |
+
|
1290 |
+
$extensions[$required_extension_name] = array();
|
1291 |
+
}
|
1292 |
+
}
|
1293 |
+
}
|
1294 |
+
|
1295 |
+
if ($activate) {
|
1296 |
+
$activate_extensions = array();
|
1297 |
+
|
1298 |
+
foreach ($result as $extension_name => $extension_result) {
|
1299 |
+
if (!is_wp_error($extension_result)) {
|
1300 |
+
$activate_extensions[$extension_name] = array();
|
1301 |
+
}
|
1302 |
+
}
|
1303 |
+
|
1304 |
+
if (!empty($activate_extensions)) {
|
1305 |
+
if ($verbose) {
|
1306 |
+
$verbose_message = _n(
|
1307 |
+
'Activating extension...',
|
1308 |
+
'Activating extensions...',
|
1309 |
+
count($activate_extensions),
|
1310 |
+
'fw'
|
1311 |
+
);
|
1312 |
+
|
1313 |
+
if (is_subclass_of($verbose, 'WP_Upgrader_Skin')) {
|
1314 |
+
$verbose->feedback($verbose_message);
|
1315 |
+
} else {
|
1316 |
+
echo fw_html_tag('p', array(), $verbose_message);
|
1317 |
+
}
|
1318 |
+
}
|
1319 |
+
|
1320 |
+
$activation_result = $this->activate_extensions($activate_extensions);
|
1321 |
+
|
1322 |
+
if ($verbose) {
|
1323 |
+
if (is_wp_error($activation_result)) {
|
1324 |
+
if (is_subclass_of($verbose, 'WP_Upgrader_Skin')) {
|
1325 |
+
$verbose->error($activation_result->get_error_message());
|
1326 |
+
} else {
|
1327 |
+
echo fw_html_tag('p', array(), $activation_result->get_error_message());
|
1328 |
+
}
|
1329 |
+
} elseif (is_array($activation_result)) {
|
1330 |
+
$verbose_message = array();
|
1331 |
+
|
1332 |
+
foreach ($activation_result as $extension_name => $extension_result) {
|
1333 |
+
if (is_wp_error($extension_result)) {
|
1334 |
+
$verbose_message[] = $extension_result->get_error_message();
|
1335 |
+
}
|
1336 |
+
}
|
1337 |
+
|
1338 |
+
$verbose_message = '<ul><li>' . implode('</li><li>', $verbose_message) . '</li></ul>';
|
1339 |
+
|
1340 |
+
if (is_subclass_of($verbose, 'WP_Upgrader_Skin')) {
|
1341 |
+
$verbose->error($verbose_message);
|
1342 |
+
} else {
|
1343 |
+
echo fw_html_tag('p', array(), $verbose_message);
|
1344 |
+
}
|
1345 |
+
} elseif ($activation_result === true) {
|
1346 |
+
$verbose_message = _n(
|
1347 |
+
'Extension has been successfully activated.',
|
1348 |
+
'Extensions has been successfully activated.',
|
1349 |
+
count($activate_extensions),
|
1350 |
+
'fw'
|
1351 |
+
);
|
1352 |
+
|
1353 |
+
if (is_subclass_of($verbose, 'WP_Upgrader_Skin')) {
|
1354 |
+
$verbose->feedback($verbose_message);
|
1355 |
+
} else {
|
1356 |
+
echo fw_html_tag('p', array(), $verbose_message);
|
1357 |
+
}
|
1358 |
+
}
|
1359 |
+
}
|
1360 |
+
}
|
1361 |
+
}
|
1362 |
+
|
1363 |
+
do_action('fw_extensions_install', $result);
|
1364 |
+
|
1365 |
+
if (
|
1366 |
+
$cancel_on_error
|
1367 |
+
&&
|
1368 |
+
$has_errors
|
1369 |
+
) {
|
1370 |
+
if (
|
1371 |
+
($last_result = end($result))
|
1372 |
+
&&
|
1373 |
+
is_wp_error($last_result)
|
1374 |
+
) {
|
1375 |
+
return $last_result;
|
1376 |
+
} else {
|
1377 |
+
// this should not happen, but just to be sure (for the future, if the code above will be changed)
|
1378 |
+
return new WP_Error(
|
1379 |
+
'installation_failed',
|
1380 |
+
_n('Cannot install extension', 'Cannot install extensions', count($extensions), 'fw')
|
1381 |
+
);
|
1382 |
+
}
|
1383 |
+
}
|
1384 |
+
|
1385 |
+
if ($has_errors) {
|
1386 |
+
return $result;
|
1387 |
+
} else {
|
1388 |
+
return true;
|
1389 |
+
}
|
1390 |
+
}
|
1391 |
+
|
1392 |
+
private function display_delete_page()
|
1393 |
+
{
|
1394 |
+
$flash_id = 'fw_extensions_delete';
|
1395 |
+
|
1396 |
+
if (!$this->can_install()) {
|
1397 |
+
FW_Flash_Messages::add(
|
1398 |
+
$flash_id,
|
1399 |
+
__('You are not allowed to delete extensions.', 'fw'),
|
1400 |
+
'error'
|
1401 |
+
);
|
1402 |
+
$this->js_redirect();
|
1403 |
+
return;
|
1404 |
+
}
|
1405 |
+
|
1406 |
+
$extensions = array_fill_keys(array_map('trim', explode(',', FW_Request::GET('extension', ''))), array());
|
1407 |
+
|
1408 |
+
{
|
1409 |
+
if (!class_exists('_FW_Extensions_Delete_Upgrader_Skin')) {
|
1410 |
+
fw_include_file_isolated(
|
1411 |
+
dirname(__FILE__) .'/includes/class--fw-extensions-delete-upgrader-skin.php'
|
1412 |
+
);
|
1413 |
+
}
|
1414 |
+
|
1415 |
+
$skin = new _FW_Extensions_Delete_Upgrader_Skin(array(
|
1416 |
+
'title' => _n('Delete Extension', 'Delete Extensions', count($extensions), 'fw'),
|
1417 |
+
));
|
1418 |
+
}
|
1419 |
+
|
1420 |
+
$skin->header();
|
1421 |
+
|
1422 |
+
do {
|
1423 |
+
$nonce = $this->get_nonce('delete');
|
1424 |
+
|
1425 |
+
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
1426 |
+
if (!isset($_POST[$nonce['name']]) || !wp_verify_nonce($_POST[$nonce['name']], $nonce['action'])) {
|
1427 |
+
$skin->error(__('Invalid nonce.', 'fw'));
|
1428 |
+
}
|
1429 |
+
|
1430 |
+
if (!FW_WP_Filesystem::request_access(
|
1431 |
+
fw_get_framework_directory('/extensions'), fw_current_url(), array($nonce['name'])
|
1432 |
+
)) {
|
1433 |
+
break;
|
1434 |
+
}
|
1435 |
+
|
1436 |
+
$uninstall_result = $this->uninstall_extensions($extensions, array('verbose' => $skin));
|
1437 |
+
|
1438 |
+
if (is_wp_error($uninstall_result)) {
|
1439 |
+
$skin->error($uninstall_result);
|
1440 |
+
} elseif (is_array($uninstall_result)) {
|
1441 |
+
$error = array();
|
1442 |
+
|
1443 |
+
foreach ($uninstall_result as $extension_name => $extension_result) {
|
1444 |
+
if (is_wp_error($extension_result)) {
|
1445 |
+
$error[] = $extension_result->get_error_message();
|
1446 |
+
}
|
1447 |
+
}
|
1448 |
+
|
1449 |
+
$error = '<ul><li>'. implode('</li><li>', $error) .'</li></ul>';
|
1450 |
+
|
1451 |
+
$skin->error($error);
|
1452 |
+
} elseif ($uninstall_result === true) {
|
1453 |
+
$skin->set_result(true);
|
1454 |
+
}
|
1455 |
+
|
1456 |
+
$skin->after(array(
|
1457 |
+
'extensions_page_link' => $this->get_link()
|
1458 |
+
));
|
1459 |
+
} else {
|
1460 |
+
echo '<form method="post">';
|
1461 |
+
|
1462 |
+
wp_nonce_field($nonce['action'], $nonce['name']);
|
1463 |
+
|
1464 |
+
fw_render_view(dirname(__FILE__) .'/views/delete-form.php', array(
|
1465 |
+
'extension_names' => array_keys($extensions),
|
1466 |
+
'installed_extensions' => $this->get_installed_extensions(),
|
1467 |
+
'list_page_link' => $this->get_link(),
|
1468 |
+
), false);
|
1469 |
+
|
1470 |
+
echo '</form>';
|
1471 |
+
}
|
1472 |
+
} while(false);
|
1473 |
+
|
1474 |
+
$skin->footer();
|
1475 |
+
}
|
1476 |
+
|
1477 |
+
/**
|
1478 |
+
* Remove extensions
|
1479 |
+
* @param array $extensions {'ext_1' => array(), 'ext_2' => array(), ...}
|
1480 |
+
* @param array $opts
|
1481 |
+
* @return WP_Error|bool|array
|
1482 |
+
* true: when all extensions succeeded
|
1483 |
+
* array: when some/all failed
|
1484 |
+
*/
|
1485 |
+
public function uninstall_extensions(array $extensions, $opts = array())
|
1486 |
+
{
|
1487 |
+
{
|
1488 |
+
$opts = array_merge(array(
|
1489 |
+
/**
|
1490 |
+
* @type bool
|
1491 |
+
* false: return {'ext_1' => true|WP_Error, 'ext_2' => true|WP_Error, ...}
|
1492 |
+
* true: return first WP_Error or true on success
|
1493 |
+
*/
|
1494 |
+
'cancel_on_error' => false,
|
1495 |
+
/**
|
1496 |
+
* @type bool|WP_Upgrader_Skin
|
1497 |
+
*/
|
1498 |
+
'verbose' => false,
|
1499 |
+
), $opts);
|
1500 |
+
|
1501 |
+
$cancel_on_error = $opts['cancel_on_error']; // fixme: install back successfully removed extensions before error?
|
1502 |
+
$verbose = $opts['verbose'];
|
1503 |
+
|
1504 |
+
unset($opts);
|
1505 |
+
}
|
1506 |
+
|
1507 |
+
if (!$this->can_install()) {
|
1508 |
+
return new WP_Error(
|
1509 |
+
'access_denied',
|
1510 |
+
__('You have no permissions to uninstall extensions', 'fw')
|
1511 |
+
);
|
1512 |
+
}
|
1513 |
+
|
1514 |
+
if (empty($extensions)) {
|
1515 |
+
return new WP_Error(
|
1516 |
+
'no_extensions',
|
1517 |
+
__('No extensions provided', 'fw')
|
1518 |
+
);
|
1519 |
+
}
|
1520 |
+
|
1521 |
+
/** @var WP_Filesystem_Base $wp_filesystem */
|
1522 |
+
global $wp_filesystem;
|
1523 |
+
|
1524 |
+
if (!$wp_filesystem || (is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code())) {
|
1525 |
+
return new WP_Error(
|
1526 |
+
'fs_not_initialized',
|
1527 |
+
__('WP Filesystem is not initialized', 'fw')
|
1528 |
+
);
|
1529 |
+
}
|
1530 |
+
|
1531 |
+
$installed_extensions = $this->get_installed_extensions();
|
1532 |
+
$extensions_before_uninstall = array_fill_keys(array_keys($installed_extensions), array());
|
1533 |
+
|
1534 |
+
$result = $uninstalled_extensions = array();
|
1535 |
+
$has_errors = false;
|
1536 |
+
|
1537 |
+
while (!empty($extensions)) {
|
1538 |
+
$not_used_var = reset($extensions);
|
1539 |
+
$extension_name = key($extensions);
|
1540 |
+
unset($extensions[$extension_name]);
|
1541 |
+
|
1542 |
+
$extension_title = $this->get_extension_title($extension_name);
|
1543 |
+
|
1544 |
+
if (!isset($installed_extensions[ $extension_name ])) {
|
1545 |
+
// already deleted
|
1546 |
+
$result[$extension_name] = true;
|
1547 |
+
continue;
|
1548 |
+
}
|
1549 |
+
|
1550 |
+
if (
|
1551 |
+
!isset($installed_extensions[ $extension_name ]['path'])
|
1552 |
+
||
|
1553 |
+
empty($installed_extensions[ $extension_name ]['path'])
|
1554 |
+
) {
|
1555 |
+
/**
|
1556 |
+
* This happens sometimes, but I don't know why
|
1557 |
+
* If the script will continue, it will delete the root folder
|
1558 |
+
*/
|
1559 |
+
fw_print(
|
1560 |
+
'Please report this to https://github.com/ThemeFuse/Unyson/issues',
|
1561 |
+
$extension_name,
|
1562 |
+
$installed_extensions
|
1563 |
+
);
|
1564 |
+
die;
|
1565 |
+
}
|
1566 |
+
|
1567 |
+
$wp_fs_extension_path = FW_WP_Filesystem::real_path_to_filesystem_path(
|
1568 |
+
$installed_extensions[ $extension_name ]['path']
|
1569 |
+
);
|
1570 |
+
|
1571 |
+
if (!$wp_filesystem->exists($wp_fs_extension_path)) {
|
1572 |
+
// already deleted, maybe because it was a sub-extension of an deleted extension
|
1573 |
+
$result[$extension_name] = true;
|
1574 |
+
continue;
|
1575 |
+
}
|
1576 |
+
|
1577 |
+
if ($verbose) {
|
1578 |
+
$verbose_message = sprintf(__('Deleting the "%s" extension...', 'fw'), $extension_title);
|
1579 |
+
|
1580 |
+
if (is_subclass_of($verbose, 'WP_Upgrader_Skin')) {
|
1581 |
+
$verbose->feedback($verbose_message);
|
1582 |
+
} else {
|
1583 |
+
echo fw_html_tag('p', array(), $verbose_message);
|
1584 |
+
}
|
1585 |
+
}
|
1586 |
+
|
1587 |
+
if (!$wp_filesystem->delete($wp_fs_extension_path, true, 'd')) {
|
1588 |
+
$result[$extension_name] = new WP_Error(
|
1589 |
+
'cannot_delete_directory',
|
1590 |
+
sprintf(__('Cannot delete the "%s" extension.', 'fw'), $extension_title)
|
1591 |
+
);
|
1592 |
+
$has_errors = true;
|
1593 |
+
|
1594 |
+
if ($cancel_on_error) {
|
1595 |
+
break;
|
1596 |
+
} else {
|
1597 |
+
continue;
|
1598 |
+
}
|
1599 |
+
} else {
|
1600 |
+
if ($verbose) {
|
1601 |
+
$verbose_message = sprintf(
|
1602 |
+
__('The %s extension has been successfully deleted.', 'fw'),
|
1603 |
+
$extension_title
|
1604 |
+
);
|
1605 |
+
|
1606 |
+
if (is_subclass_of($verbose, 'WP_Upgrader_Skin')) {
|
1607 |
+
$verbose->feedback($verbose_message);
|
1608 |
+
} else {
|
1609 |
+
echo fw_html_tag('p', array(), $verbose_message);
|
1610 |
+
}
|
1611 |
+
}
|
1612 |
+
|
1613 |
+
$result[$extension_name] = true;
|
1614 |
+
}
|
1615 |
+
|
1616 |
+
/**
|
1617 |
+
* Read again all extensions
|
1618 |
+
* The delete extension may contain more sub extensions
|
1619 |
+
*/
|
1620 |
+
{
|
1621 |
+
unset($installed_extensions);
|
1622 |
+
$installed_extensions = $this->get_installed_extensions(true);
|
1623 |
+
}
|
1624 |
+
|
1625 |
+
/**
|
1626 |
+
* Add for deletion not used extensions
|
1627 |
+
* For e.g. standalone=false extension that were required by the deleted extension
|
1628 |
+
* and now are not required by any other extension
|
1629 |
+
*/
|
1630 |
+
{
|
1631 |
+
$not_used_extensions = array_fill_keys(
|
1632 |
+
array_keys(
|
1633 |
+
array_diff_key(
|
1634 |
+
$installed_extensions,
|
1635 |
+
$this->get_used_extensions($extensions, array_keys($installed_extensions))
|
1636 |
+
)
|
1637 |
+
),
|
1638 |
+
array()
|
1639 |
+
);
|
1640 |
+
|
1641 |
+
$extensions = array_merge($extensions, $not_used_extensions);
|
1642 |
+
}
|
1643 |
+
}
|
1644 |
+
|
1645 |
+
do_action('fw_extensions_uninstall', $result);
|
1646 |
+
|
1647 |
+
if (
|
1648 |
+
$cancel_on_error
|
1649 |
+
&&
|
1650 |
+
$has_errors
|
1651 |
+
) {
|
1652 |
+
if (
|
1653 |
+
($last_result = end($result))
|
1654 |
+
&&
|
1655 |
+
is_wp_error($last_result)
|
1656 |
+
) {
|
1657 |
+
return $last_result;
|
1658 |
+
} else {
|
1659 |
+
// this should not happen, but just to be sure (for the future, if the code above will be changed)
|
1660 |
+
return new WP_Error(
|
1661 |
+
'uninstall_failed',
|
1662 |
+
_n('Cannot uninstall extension', 'Cannot uninstall extensions', count($extensions), 'fw')
|
1663 |
+
);
|
1664 |
+
}
|
1665 |
+
}
|
1666 |
+
|
1667 |
+
// remove from active list the deleted extensions
|
1668 |
+
{
|
1669 |
+
update_option(
|
1670 |
+
fw()->extensions->_get_active_extensions_db_option_name(),
|
1671 |
+
array_diff_key(
|
1672 |
+
fw()->extensions->_get_db_active_extensions(),
|
1673 |
+
array_diff_key(
|
1674 |
+
$extensions_before_uninstall,
|
1675 |
+
$installed_extensions
|
1676 |
+
)
|
1677 |
+
)
|
1678 |
+
);
|
1679 |
+
}
|
1680 |
+
|
1681 |
+
if ($has_errors) {
|
1682 |
+
return $result;
|
1683 |
+
} else {
|
1684 |
+
return true;
|
1685 |
+
}
|
1686 |
+
}
|
1687 |
+
|
1688 |
+
private function display_extension_page()
|
1689 |
+
{
|
1690 |
+
// note: static is enqueued in 'admin_enqueue_scripts' action
|
1691 |
+
|
1692 |
+
$extension_name = trim(FW_Request::GET('extension', ''));
|
1693 |
+
|
1694 |
+
$installed_extensions = $this->get_installed_extensions();
|
1695 |
+
|
1696 |
+
$flash_id = 'fw_extension_page';
|
1697 |
+
|
1698 |
+
{
|
1699 |
+
$error = '';
|
1700 |
+
|
1701 |
+
do {
|
1702 |
+
if (empty($extension_name)) {
|
1703 |
+
$error = __('Extension not specified.', 'fw');
|
1704 |
+
break;
|
1705 |
+
}
|
1706 |
+
|
1707 |
+
if (!isset($installed_extensions[$extension_name])) {
|
1708 |
+
$error = sprintf(__('Extension "%s" is not installed.', 'fw'), $this->get_extension_title($extension_name));
|
1709 |
+
break;
|
1710 |
+
}
|
1711 |
+
} while(false);
|
1712 |
+
|
1713 |
+
if ($error) {
|
1714 |
+
FW_Flash_Messages::add($flash_id, $error, 'error');
|
1715 |
+
$this->js_redirect();
|
1716 |
+
return;
|
1717 |
+
}
|
1718 |
+
}
|
1719 |
+
|
1720 |
+
{
|
1721 |
+
$tab = fw_akg('tab', $_GET, 'settings');
|
1722 |
+
|
1723 |
+
if (!in_array($tab, array('settings', 'docs'))) {
|
1724 |
+
$tab = 'settings';
|
1725 |
+
}
|
1726 |
+
}
|
1727 |
+
|
1728 |
+
$extension_title = $this->get_extension_title($extension_name);
|
1729 |
+
$link = $this->get_link();
|
1730 |
+
|
1731 |
+
echo '<div class="wrap" id="fw-extension-page">';
|
1732 |
+
|
1733 |
+
fw_render_view(dirname(__FILE__) .'/views/extension-page-header.php', array(
|
1734 |
+
'extension_name' => $extension_name,
|
1735 |
+
'extension_data' => $installed_extensions[$extension_name],
|
1736 |
+
'link_delete' => $link .'&sub-page=delete',
|
1737 |
+
'link_extension' => $link .'&sub-page=extension',
|
1738 |
+
'extension_title' => $extension_title,
|
1739 |
+
'tab' => $tab,
|
1740 |
+
'is_supported' =>
|
1741 |
+
fw()->theme->manifest->get('supported_extensions/'. $extension_name, false) !== false
|
1742 |
+
||
|
1743 |
+
$installed_extensions[$extension_name]['is']['theme']
|
1744 |
+
), false);
|
1745 |
+
|
1746 |
+
unset($installed_extensions);
|
1747 |
+
|
1748 |
+
echo '<div id="fw-extension-tab-content">';
|
1749 |
+
{
|
1750 |
+
$method_data = array();
|
1751 |
+
|
1752 |
+
switch ($tab) {
|
1753 |
+
case 'settings':
|
1754 |
+
$error = $this->display_extension_settings_page($extension_name, $method_data);
|
1755 |
+
break;
|
1756 |
+
case 'docs':
|
1757 |
+
$error = $this->display_extension_docs_page($extension_name, $method_data);
|
1758 |
+
break;
|
1759 |
+
}
|
1760 |
+
}
|
1761 |
+
echo '</div>';
|
1762 |
+
|
1763 |
+
echo '</div>';
|
1764 |
+
|
1765 |
+
if ($error) {
|
1766 |
+
FW_Flash_Messages::add($flash_id, $error, 'error');
|
1767 |
+
$this->js_redirect();
|
1768 |
+
return;
|
1769 |
+
}
|
1770 |
+
}
|
1771 |
+
|
1772 |
+
private function display_extension_settings_page($extension_name, $data)
|
1773 |
+
{
|
1774 |
+
if (!fw()->extensions->get($extension_name)) {
|
1775 |
+
return sprintf(
|
1776 |
+
__('Extension "%s" does not exist or is not active.', 'fw'),
|
1777 |
+
fw_htmlspecialchars($extension_name)
|
1778 |
+
);
|
1779 |
+
}
|
1780 |
+
|
1781 |
+
$extension = fw()->extensions->get($extension_name);
|
1782 |
+
|
1783 |
+
if (!$extension->get_settings_options()) {
|
1784 |
+
return sprintf(
|
1785 |
+
__('%s extension does not have settings.', 'fw'),
|
1786 |
+
$extension->manifest->get_name()
|
1787 |
+
);
|
1788 |
+
}
|
1789 |
+
|
1790 |
+
echo '<div id="fw-extension-settings">';
|
1791 |
+
|
1792 |
+
echo $this->extension_settings_form->render(array(
|
1793 |
+
'extension' => $extension,
|
1794 |
+
));
|
1795 |
+
|
1796 |
+
echo '</div>';
|
1797 |
+
}
|
1798 |
+
|
1799 |
+
private function display_extension_docs_page($extension_name, $data)
|
1800 |
+
{
|
1801 |
+
$installed_extensions = $this->get_installed_extensions();
|
1802 |
+
$docs_path = $installed_extensions[$extension_name]['path'] .'/readme.md.php';
|
1803 |
+
unset($installed_extensions);
|
1804 |
+
|
1805 |
+
if (!file_exists($docs_path)) {
|
1806 |
+
return __('Extension has no Install Instructions', 'fw');
|
1807 |
+
}
|
1808 |
+
|
1809 |
+
echo fw()->backend->render_box(
|
1810 |
+
'fw-extension-docs',
|
1811 |
+
'',
|
1812 |
+
fw()->backend->render_options(array(
|
1813 |
+
'docs' => array(
|
1814 |
+
'label' => false,
|
1815 |
+
'type' => 'html-full',
|
1816 |
+
'html' => $this->get_markdown_parser()->text(
|
1817 |
+
fw_render_view($docs_path, array())
|
1818 |
+
),
|
1819 |
+
),
|
1820 |
+
))
|
1821 |
+
);
|
1822 |
+
}
|
1823 |
+
|
1824 |
+
private function display_activate_page()
|
1825 |
+
{
|
1826 |
+
$error = '';
|
1827 |
+
|
1828 |
+
do {
|
1829 |
+
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
1830 |
+
$error = __('Invalid request method.', 'fw');
|
1831 |
+
break;
|
1832 |
+
}
|
1833 |
+
|
1834 |
+
$nonce = $this->get_nonce('activate');
|
1835 |
+
|
1836 |
+
if (!wp_verify_nonce(FW_Request::POST($nonce['name']), $nonce['action'])) {
|
1837 |
+
$error = __('Invalid nonce.', 'fw');
|
1838 |
+
break;
|
1839 |
+
}
|
1840 |
+
|
1841 |
+
if (!isset($_GET['extension'])) {
|
1842 |
+
$error = __('No extension specified.', 'fw');
|
1843 |
+
break;
|
1844 |
+
}
|
1845 |
+
|
1846 |
+
$activation_result = $this->activate_extensions(
|
1847 |
+
array_fill_keys(explode(',', $_GET['extension']), array())
|
1848 |
+
);
|
1849 |
+
|
1850 |
+
if (is_wp_error($activation_result)) {
|
1851 |
+
$error = $activation_result->get_error_message();
|
1852 |
+
} elseif (is_array($activation_result)) {
|
1853 |
+
$error = array();
|
1854 |
+
|
1855 |
+
foreach ($activation_result as $extension_name => $extension_result) {
|
1856 |
+
if (is_wp_error($extension_result)) {
|
1857 |
+
$error[] = $extension_result->get_error_message();
|
1858 |
+
}
|
1859 |
+
}
|
1860 |
+
|
1861 |
+
$error = '<ul><li>'. implode('</li><li>', $error) .'</li></ul>';
|
1862 |
+
}
|
1863 |
+
} while(false);
|
1864 |
+
|
1865 |
+
if ($error) {
|
1866 |
+
FW_Flash_Messages::add(
|
1867 |
+
'fw_extensions_activate_page',
|
1868 |
+
$error,
|
1869 |
+
'error'
|
1870 |
+
);
|
1871 |
+
$this->js_redirect();
|
1872 |
+
return;
|
1873 |
+
}
|
1874 |
+
|
1875 |
+
$this->js_redirect();
|
1876 |
+
}
|
1877 |
+
|
1878 |
+
/**
|
1879 |
+
* Add extensions to active extensions list in database
|
1880 |
+
* After refresh they should be active, if all dependencies will be met and if parent-extension::_init() will not return false
|
1881 |
+
* @param array $extensions {'ext_1' => array(), 'ext_2' => array(), ...}
|
1882 |
+
* @param bool $cancel_on_error
|
1883 |
+
* false: return {'ext_1' => true|WP_Error, 'ext_2' => true|WP_Error, ...}
|
1884 |
+
* true: return first WP_Error or true on success
|
1885 |
+
* @return WP_Error|bool|array
|
1886 |
+
* true: when all extensions succeeded
|
1887 |
+
* array: when some/all failed
|
1888 |
+
*/
|
1889 |
+
public function activate_extensions(array $extensions, $cancel_on_error = false)
|
1890 |
+
{
|
1891 |
+
if (!$this->can_activate()) {
|
1892 |
+
return new WP_Error(
|
1893 |
+
'access_denied',
|
1894 |
+
__('You have no permissions to activate extensions', 'fw')
|
1895 |
+
);
|
1896 |
+
}
|
1897 |
+
|
1898 |
+
if (empty($extensions)) {
|
1899 |
+
return new WP_Error(
|
1900 |
+
'no_extensions',
|
1901 |
+
__('No extensions provided', 'fw')
|
1902 |
+
);
|
1903 |
+
}
|
1904 |
+
|
1905 |
+
$installed_extensions = $this->get_installed_extensions();
|
1906 |
+
|
1907 |
+
$result = $extensions_for_activation = array();
|
1908 |
+
$has_errors = false;
|
1909 |
+
|
1910 |
+
foreach ($extensions as $extension_name => $not_used_var) {
|
1911 |
+
if (!isset($installed_extensions[$extension_name])) {
|
1912 |
+
$result[$extension_name] = new WP_Error(
|
1913 |
+
'extension_not_installed',
|
1914 |
+
sprintf(__('Extension "%s" does not exist.', 'fw'), $this->get_extension_title($extension_name))
|
1915 |
+
);
|
1916 |
+
$has_errors = true;
|
1917 |
+
|
1918 |
+
if ($cancel_on_error) {
|
1919 |
+
break;
|
1920 |
+
} else {
|
1921 |
+
continue;
|
1922 |
+
}
|
1923 |
+
}
|
1924 |
+
|
1925 |
+
$collected = $this->get_extensions_for_activation($extension_name);
|
1926 |
+
|
1927 |
+
if (is_wp_error($collected)) {
|
1928 |
+
$result[$extension_name] = $collected;
|
1929 |
+
$has_errors = true;
|
1930 |
+
|
1931 |
+
if ($cancel_on_error) {
|
1932 |
+
break;
|
1933 |
+
} else {
|
1934 |
+
continue;
|
1935 |
+
}
|
1936 |
+
}
|
1937 |
+
|
1938 |
+
$extensions_for_activation = array_merge($extensions_for_activation, $collected);
|
1939 |
+
|
1940 |
+
$result[$extension_name] = true;
|
1941 |
+
}
|
1942 |
+
|
1943 |
+
if (
|
1944 |
+
$cancel_on_error
|
1945 |
+
&&
|
1946 |
+
$has_errors
|
1947 |
+
) {
|
1948 |
+
if (
|
1949 |
+
($last_result = end($result))
|
1950 |
+
&&
|
1951 |
+
is_wp_error($last_result)
|
1952 |
+
) {
|
1953 |
+
return $last_result;
|
1954 |
+
} else {
|
1955 |
+
// this should not happen, but just to be sure (for the future, if the code above will be changed)
|
1956 |
+
return new WP_Error(
|
1957 |
+
'activation_failed',
|
1958 |
+
_n('Cannot activate extension', 'Cannot activate extensions', count($extensions), 'fw')
|
1959 |
+
);
|
1960 |
+
}
|
1961 |
+
}
|
1962 |
+
|
1963 |
+
update_option(
|
1964 |
+
fw()->extensions->_get_active_extensions_db_option_name(),
|
1965 |
+
array_merge(fw()->extensions->_get_db_active_extensions(), $extensions_for_activation)
|
1966 |
+
);
|
1967 |
+
|
1968 |
+
// remove already active extensions
|
1969 |
+
foreach ($extensions_for_activation as $extension_name => $not_used_var) {
|
1970 |
+
if (fw_ext($extension_name)) {
|
1971 |
+
unset($extensions_for_activation[$extension_name]);
|
1972 |
+
}
|
1973 |
+
}
|
1974 |
+
|
1975 |
+
/**
|
1976 |
+
* Prepare db wp option used to fire the 'fw_extensions_after_activation' action on next refresh
|
1977 |
+
*/
|
1978 |
+
{
|
1979 |
+
$db_wp_option_name = 'fw_extensions_activation';
|
1980 |
+
$db_wp_option_value = get_option($db_wp_option_name, array(
|
1981 |
+
'activated' => array(),
|
1982 |
+
'deactivated' => array(),
|
1983 |
+
));
|
1984 |
+
|
1985 |
+
/**
|
1986 |
+
* Keep adding to the existing value instead of resetting it on each method call
|
1987 |
+
* in case the method will be called multiple times
|
1988 |
+
*/
|
1989 |
+
$db_wp_option_value['activated'] = array_merge($db_wp_option_value['activated'], $extensions_for_activation);
|
1990 |
+
|
1991 |
+
/**
|
1992 |
+
* Remove activated extensions from deactivated
|
1993 |
+
*/
|
1994 |
+
$db_wp_option_value['deactivated'] = array_diff_key($db_wp_option_value['deactivated'], $db_wp_option_value['activated']);
|
1995 |
+
|
1996 |
+
update_option($db_wp_option_name, $db_wp_option_value, false);
|
1997 |
+
}
|
1998 |
+
|
1999 |
+
do_action('fw_extensions_before_activation', $extensions_for_activation);
|
2000 |
+
|
2001 |
+
if ($has_errors) {
|
2002 |
+
return $result;
|
2003 |
+
} else {
|
2004 |
+
return true;
|
2005 |
+
}
|
2006 |
+
}
|
2007 |
+
|
2008 |
+
private function collect_sub_extensions($ext_name, &$installed_extensions)
|
2009 |
+
{
|
2010 |
+
$result = array();
|
2011 |
+
|
2012 |
+
foreach ($installed_extensions[$ext_name]['children'] as $child_ext_name => $child_ext_data) {
|
2013 |
+
$result[$child_ext_name] = array();
|
2014 |
+
|
2015 |
+
$result += $this->collect_sub_extensions($child_ext_name, $installed_extensions);
|
2016 |
+
}
|
2017 |
+
|
2018 |
+
return $result;
|
2019 |
+
}
|
2020 |
+
|
2021 |
+
private function collect_required_extensions($ext_name, &$installed_extensions, &$collected)
|
2022 |
+
{
|
2023 |
+
if (!isset($installed_extensions[$ext_name])) {
|
2024 |
+
return;
|
2025 |
+
}
|
2026 |
+
|
2027 |
+
foreach (fw_akg('requirements/extensions', $installed_extensions[$ext_name]['manifest'], array()) as $req_ext_name => $req_ext_data) {
|
2028 |
+
if (isset($collected[$req_ext_name])) {
|
2029 |
+
// prevent requirements recursion
|
2030 |
+
continue;
|
2031 |
+
}
|
2032 |
+
|
2033 |
+
$collected[$req_ext_name] = array();
|
2034 |
+
|
2035 |
+
$this->collect_required_extensions($req_ext_name, $installed_extensions, $collected);
|
2036 |
+
}
|
2037 |
+
}
|
2038 |
+
|
2039 |
+
private function display_deactivate_page()
|
2040 |
+
{
|
2041 |
+
$installed_extensions = $this->get_installed_extensions();
|
2042 |
+
|
2043 |
+
$error = '';
|
2044 |
+
|
2045 |
+
do {
|
2046 |
+
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
2047 |
+
$error = __('Invalid request method.', 'fw');
|
2048 |
+
break;
|
2049 |
+
}
|
2050 |
+
|
2051 |
+
$nonce = $this->get_nonce('deactivate');
|
2052 |
+
|
2053 |
+
if (!wp_verify_nonce(FW_Request::POST($nonce['name']), $nonce['action'])) {
|
2054 |
+
$error = __('Invalid nonce.', 'fw');
|
2055 |
+
break;
|
2056 |
+
}
|
2057 |
+
|
2058 |
+
if (!isset($_GET['extension'])) {
|
2059 |
+
$error = __('No extension specified.', 'fw');
|
2060 |
+
break;
|
2061 |
+
}
|
2062 |
+
|
2063 |
+
$deactivation_result = $this->deactivate_extensions(
|
2064 |
+
array_fill_keys(explode(',', $_GET['extension']), array())
|
2065 |
+
);
|
2066 |
+
|
2067 |
+
if (is_wp_error($deactivation_result)) {
|
2068 |
+
$error = $deactivation_result->get_error_message();
|
2069 |
+
} elseif (is_array($deactivation_result)) {
|
2070 |
+
$error = array();
|
2071 |
+
|
2072 |
+
foreach ($deactivation_result as $extension_name => $extension_result) {
|
2073 |
+
if (is_wp_error($extension_result)) {
|
2074 |
+
$error[] = $extension_result->get_error_message();
|
2075 |
+
}
|
2076 |
+
}
|
2077 |
+
|
2078 |
+
$error = '<ul><li>'. implode('</li><li>', $error) .'</li></ul>';
|
2079 |
+
}
|
2080 |
+
} while(false);
|
2081 |
+
|
2082 |
+
if ($error) {
|
2083 |
+
FW_Flash_Messages::add(
|
2084 |
+
'fw_extensions_activate_page',
|
2085 |
+
$error,
|
2086 |
+
'error'
|
2087 |
+
);
|
2088 |
+
}
|
2089 |
+
|
2090 |
+
$this->js_redirect();
|
2091 |
+
}
|
2092 |
+
|
2093 |
+
/**
|
2094 |
+
* Remove extensions from active extensions list in database
|
2095 |
+
* After refresh they will be inactive
|
2096 |
+
* @param array $extensions {'ext_1' => array(), 'ext_2' => array(), ...}
|
2097 |
+
* @param bool $cancel_on_error
|
2098 |
+
* false: return {'ext_1' => true|WP_Error, 'ext_2' => true|WP_Error, ...}
|
2099 |
+
* true: return first WP_Error or true on success
|
2100 |
+
* @return WP_Error|bool|array
|
2101 |
+
* true: when all extensions succeeded
|
2102 |
+
* array: when some/all failed
|
2103 |
+
*/
|
2104 |
+
public function deactivate_extensions(array $extensions, $cancel_on_error = false)
|
2105 |
+
{
|
2106 |
+
if (!$this->can_activate()) {
|
2107 |
+
return new WP_Error(
|
2108 |
+
'access_denied',
|
2109 |
+
__('You have no permissions to deactivate extensions', 'fw')
|
2110 |
+
);
|
2111 |
+
}
|
2112 |
+
|
2113 |
+
if (empty($extensions)) {
|
2114 |
+
return new WP_Error(
|
2115 |
+
'no_extensions',
|
2116 |
+
__('No extensions provided', 'fw')
|
2117 |
+
);
|
2118 |
+
}
|
2119 |
+
|
2120 |
+
$installed_extensions = $this->get_installed_extensions();
|
2121 |
+
|
2122 |
+
$result = $extensions_for_deactivation = array();
|
2123 |
+
$has_errors = false;
|
2124 |
+
|
2125 |
+
foreach ($extensions as $extension_name => $not_used_var) {
|
2126 |
+
if (!isset($installed_extensions[$extension_name])) {
|
2127 |
+
// anyway remove from the active list
|
2128 |
+
$extensions_for_deactivation[$extension_name] = array();
|
2129 |
+
|
2130 |
+
$result[$extension_name] = new WP_Error(
|
2131 |
+
'extension_not_installed',
|
2132 |
+
sprintf(__( 'Extension "%s" does not exist.' , 'fw' ), $this->get_extension_title($extension_name))
|
2133 |
+
);
|
2134 |
+
$has_errors = true;
|
2135 |
+
|
2136 |
+
if ($cancel_on_error) {
|
2137 |
+
break;
|
2138 |
+
} else {
|
2139 |
+
continue;
|
2140 |
+
}
|
2141 |
+
}
|
2142 |
+
|
2143 |
+
$current_deactivating_extensions = array(
|
2144 |
+
$extension_name => array()
|
2145 |
+
);
|
2146 |
+
|
2147 |
+
// add sub-extensions for deactivation
|
2148 |
+
foreach ($this->collect_sub_extensions($extension_name, $installed_extensions) as $sub_extension_name => $sub_extension_data) {
|
2149 |
+
$current_deactivating_extensions[ $sub_extension_name ] = array();
|
2150 |
+
}
|
2151 |
+
|
2152 |
+
// add extensions that requires deactivated extensions
|
2153 |
+
$this->collect_extensions_that_requires($current_deactivating_extensions, $current_deactivating_extensions);
|
2154 |
+
|
2155 |
+
$extensions_for_deactivation = array_merge(
|
2156 |
+
$extensions_for_deactivation,
|
2157 |
+
$current_deactivating_extensions
|
2158 |
+
);
|
2159 |
+
|
2160 |
+
unset($current_deactivating_extensions);
|
2161 |
+
|
2162 |
+
$result[$extension_name] = true;
|
2163 |
+
}
|
2164 |
+
|
2165 |
+
if (
|
2166 |
+
$cancel_on_error
|
2167 |
+
&&
|
2168 |
+
$has_errors
|
2169 |
+
) {
|
2170 |
+
if (
|
2171 |
+
($last_result = end($result))
|
2172 |
+
&&
|
2173 |
+
is_wp_error($last_result)
|
2174 |
+
) {
|
2175 |
+
return $last_result;
|
2176 |
+
} else {
|
2177 |
+
// this should not happen, but just to be sure (for the future, if the code above will be changed)
|
2178 |
+
return new WP_Error(
|
2179 |
+
'deactivation_failed',
|
2180 |
+
_n('Cannot deactivate extension', 'Cannot activate extensions', count($extensions), 'fw')
|
2181 |
+
);
|
2182 |
+
}
|
2183 |
+
}
|
2184 |
+
|
2185 |
+
// add not used extensions for deactivation
|
2186 |
+
$extensions_for_deactivation = array_merge($extensions_for_deactivation,
|
2187 |
+
array_fill_keys(
|
2188 |
+
array_keys(
|
2189 |
+
array_diff_key(
|
2190 |
+
$installed_extensions,
|
2191 |
+
$this->get_used_extensions($extensions_for_deactivation, array_keys(fw()->extensions->get_all()))
|
2192 |
+
)
|
2193 |
+
),
|
2194 |
+
array()
|
2195 |
+
)
|
2196 |
+
);
|
2197 |
+
|
2198 |
+
update_option(
|
2199 |
+
fw()->extensions->_get_active_extensions_db_option_name(),
|
2200 |
+
array_diff_key(
|
2201 |
+
fw()->extensions->_get_db_active_extensions(),
|
2202 |
+
$extensions_for_deactivation
|
2203 |
+
)
|
2204 |
+
);
|
2205 |
+
|
2206 |
+
// remove already inactive extensions
|
2207 |
+
foreach ($extensions_for_deactivation as $extension_name => $not_used_var) {
|
2208 |
+
if (!fw_ext($extension_name)) {
|
2209 |
+
unset($extensions_for_deactivation[$extension_name]);
|
2210 |
+
}
|
2211 |
+
}
|
2212 |
+
|
2213 |
+
/**
|
2214 |
+
* Prepare db wp option used to fire the 'fw_extensions_after_deactivation' action on next refresh
|
2215 |
+
*/
|
2216 |
+
{
|
2217 |
+
$db_wp_option_name = 'fw_extensions_activation';
|
2218 |
+
$db_wp_option_value = get_option($db_wp_option_name, array(
|
2219 |
+
'activated' => array(),
|
2220 |
+
'deactivated' => array(),
|
2221 |
+
));
|
2222 |
+
|
2223 |
+
/**
|
2224 |
+
* Keep adding to the existing value instead of resetting it on each method call
|
2225 |
+
* in case the method will be called multiple times
|
2226 |
+
*/
|
2227 |
+
$db_wp_option_value['deactivated'] = array_merge($db_wp_option_value['deactivated'], $extensions_for_deactivation);
|
2228 |
+
|
2229 |
+
/**
|
2230 |
+
* Remove deactivated extensions from activated
|
2231 |
+
*/
|
2232 |
+
$db_wp_option_value['activated'] = array_diff_key($db_wp_option_value['activated'], $db_wp_option_value['deactivated']);
|
2233 |
+
|
2234 |
+
update_option($db_wp_option_name, $db_wp_option_value, false);
|
2235 |
+
}
|
2236 |
+
|
2237 |
+
do_action('fw_extensions_before_deactivation', $extensions_for_deactivation);
|
2238 |
+
|
2239 |
+
if ($has_errors) {
|
2240 |
+
return $result;
|
2241 |
+
} else {
|
2242 |
+
return true;
|
2243 |
+
}
|
2244 |
+
}
|
2245 |
+
|
2246 |
+
/**
|
2247 |
+
* @param array $data
|
2248 |
+
* @return array
|
2249 |
+
* @internal
|
2250 |
+
*/
|
2251 |
+
public function _extension_settings_form_render($data)
|
2252 |
+
{
|
2253 |
+
/**
|
2254 |
+
* @var FW_Extension $extension
|
2255 |
+
*/
|
2256 |
+
$extension = $data['data']['extension'];
|
2257 |
+
|
2258 |
+
do_action('fw_extension_settings_form_render:'. $extension->get_name());
|
2259 |
+
|
2260 |
+
echo fw_html_tag('input', array(
|
2261 |
+
'type' => 'hidden',
|
2262 |
+
'name' => 'fw_extension_name',
|
2263 |
+
'value' => $extension->get_name(),
|
2264 |
+
), true);
|
2265 |
+
|
2266 |
+
echo fw()->backend->render_options(
|
2267 |
+
$extension->get_settings_options(),
|
2268 |
+
fw_get_db_ext_settings_option($extension->get_name())
|
2269 |
+
);
|
2270 |
+
|
2271 |
+
$data['submit']['html'] = '';
|
2272 |
+
|
2273 |
+
echo '<p>';
|
2274 |
+
echo fw_html_tag('input', array(
|
2275 |
+
'type' => 'submit',
|
2276 |
+
'class' => 'button-primary',
|
2277 |
+
'value' => __('Save', 'fw'),
|
2278 |
+
));
|
2279 |
+
echo ' ';
|
2280 |
+
echo fw_html_tag('a', array(
|
2281 |
+
'href' => $this->get_link(),
|
2282 |
+
), __('Cancel', 'fw'));
|
2283 |
+
echo '</p>';
|
2284 |
+
|
2285 |
+
return $data;
|
2286 |
+
}
|
2287 |
+
|
2288 |
+
/**
|
2289 |
+
* @param array $errors
|
2290 |
+
* @return array
|
2291 |
+
* @internal
|
2292 |
+
*/
|
2293 |
+
public function _extension_settings_form_validate($errors)
|
2294 |
+
{
|
2295 |
+
do {
|
2296 |
+
if (!current_user_can($this->can_activate())) {
|
2297 |
+
$errors[] = __('You are not allowed to save extensions settings.', 'fw');
|
2298 |
+
break;
|
2299 |
+
}
|
2300 |
+
|
2301 |
+
$extension = fw()->extensions->get(FW_Request::POST('fw_extension_name'));
|
2302 |
+
|
2303 |
+
if (!$extension) {
|
2304 |
+
$errors[] = __('Invalid extension.', 'fw');
|
2305 |
+
break;
|
2306 |
+
}
|
2307 |
+
|
2308 |
+
if (!$extension->get_settings_options()) {
|
2309 |
+
$errors[] = __('Extension does not have settings options.', 'fw');
|
2310 |
+
break;
|
2311 |
+
}
|
2312 |
+
} while(false);
|
2313 |
+
|
2314 |
+
return $errors;
|
2315 |
+
}
|
2316 |
+
|
2317 |
+
/**
|
2318 |
+
* @param array $data
|
2319 |
+
* @return array
|
2320 |
+
* @internal
|
2321 |
+
*/
|
2322 |
+
public function _extension_settings_form_save($data)
|
2323 |
+
{
|
2324 |
+
$extension = fw()->extensions->get(FW_Request::POST('fw_extension_name'));
|
2325 |
+
|
2326 |
+
$options_before_save = (array)fw_get_db_ext_settings_option($extension->get_name());
|
2327 |
+
|
2328 |
+
fw_set_db_ext_settings_option(
|
2329 |
+
$extension->get_name(),
|
2330 |
+
null,
|
2331 |
+
array_merge(
|
2332 |
+
$options_before_save,
|
2333 |
+
fw_get_options_values_from_input(
|
2334 |
+
$extension->get_settings_options()
|
2335 |
+
)
|
2336 |
+
)
|
2337 |
+
);
|
2338 |
+
|
2339 |
+
FW_Flash_Messages::add(
|
2340 |
+
'fw_extension_settings_saved',
|
2341 |
+
__('Extensions settings successfully saved.', 'fw'),
|
2342 |
+
'success'
|
2343 |
+
);
|
2344 |
+
|
2345 |
+
$data['redirect'] = fw_current_url();
|
2346 |
+
|
2347 |
+
do_action('fw_extension_settings_form_saved:'. $extension->get_name(), $options_before_save);
|
2348 |
+
|
2349 |
+
return $data;
|
2350 |
+
}
|
2351 |
+
|
2352 |
+
/**
|
2353 |
+
* Download an extension
|
2354 |
+
*
|
2355 |
+
* global $wp_filesystem; must me initialized
|
2356 |
+
*
|
2357 |
+
* @param string $extension_name
|
2358 |
+
* @param array $data Extension data from the "available extensions" array
|
2359 |
+
* @return string|WP_Error WP Filesystem path to the downloaded directory
|
2360 |
+
*/
|
2361 |
+
private function download($extension_name, $data)
|
2362 |
+
{
|
2363 |
+
$wp_error_id = 'fw_extension_download';
|
2364 |
+
|
2365 |
+
if (empty($data['download'])) {
|
2366 |
+
return new WP_Error(
|
2367 |
+
$wp_error_id,
|
2368 |
+
sprintf(__('Extension "%s" has no download sources.', 'fw'), $this->get_extension_title($extension_name))
|
2369 |
+
);
|
2370 |
+
}
|
2371 |
+
|
2372 |
+
/** @var WP_Filesystem_Base $wp_filesystem */
|
2373 |
+
global $wp_filesystem;
|
2374 |
+
|
2375 |
+
// create temporary directory
|
2376 |
+
{
|
2377 |
+
$wp_fs_tmp_dir = FW_WP_Filesystem::real_path_to_filesystem_path($this->get_tmp_dir());
|
2378 |
+
|
2379 |
+
if ($wp_filesystem->exists($wp_fs_tmp_dir)) {
|
2380 |
+
// just in case it already exists, clear everything, it may contain old files
|
2381 |
+
if (!$wp_filesystem->rmdir($wp_fs_tmp_dir, true)) {
|
2382 |
+
return new WP_Error(
|
2383 |
+
$wp_error_id,
|
2384 |
+
sprintf(__('Cannot remove temporary directory: %s', 'fw'), $wp_fs_tmp_dir)
|
2385 |
+
);
|
2386 |
+
}
|
2387 |
+
}
|
2388 |
+
|
2389 |
+
if (!FW_WP_Filesystem::mkdir_recursive($wp_fs_tmp_dir)) {
|
2390 |
+
return new WP_Error(
|
2391 |
+
$wp_error_id,
|
2392 |
+
sprintf(__('Cannot create temporary directory: %s', 'fw'), $wp_fs_tmp_dir)
|
2393 |
+
);
|
2394 |
+
}
|
2395 |
+
}
|
2396 |
+
|
2397 |
+
$theme_ext_requirements = fw()->theme->manifest->get('requirements/extensions');
|
2398 |
+
|
2399 |
+
foreach ($data['download'] as $source => $source_data) {
|
2400 |
+
switch ($source) {
|
2401 |
+
case 'github':
|
2402 |
+
if (empty($source_data['user_repo'])) {
|
2403 |
+
return new WP_Error(
|
2404 |
+
$wp_error_id,
|
2405 |
+
sprintf(__('"%s" extension github source "user_repo" parameter is required', 'fw'), $this->get_extension_title($extension_name))
|
2406 |
+
);
|
2407 |
+
}
|
2408 |
+
|
2409 |
+
{
|
2410 |
+
$transient_name = 'fw_ext_mngr_gh_dl';
|
2411 |
+
$transient_ttl = HOUR_IN_SECONDS;
|
2412 |
+
|
2413 |
+
$cache = get_site_transient($transient_name);
|
2414 |
+
|
2415 |
+
if ($cache === false) {
|
2416 |
+
$cache = array();
|
2417 |
+
}
|
2418 |
+
}
|
2419 |
+
|
2420 |
+
if (isset($cache[ $source_data['user_repo'] ])) {
|
2421 |
+
$download_link = $cache[ $source_data['user_repo'] ]['zipball_url'];
|
2422 |
+
} else {
|
2423 |
+
$http = new WP_Http();
|
2424 |
+
|
2425 |
+
if (
|
2426 |
+
isset($theme_ext_requirements[$extension_name])
|
2427 |
+
&&
|
2428 |
+
isset($theme_ext_requirements[$extension_name]['max_version'])
|
2429 |
+
) {
|
2430 |
+
$tag = 'tags/v'. $theme_ext_requirements[$extension_name]['max_version'];
|
2431 |
+
} else {
|
2432 |
+
$tag = 'latest';
|
2433 |
+
}
|
2434 |
+
|
2435 |
+
$response = $http->get(
|
2436 |
+
apply_filters('fw_github_api_url', 'https://api.github.com')
|
2437 |
+
. '/repos/'. $source_data['user_repo'] .'/releases/'. $tag
|
2438 |
+
);
|
2439 |
+
|
2440 |
+
unset($http);
|
2441 |
+
|
2442 |
+
$response_code = intval(wp_remote_retrieve_response_code($response));
|
2443 |
+
|
2444 |
+
if ($response_code !== 200) {
|
2445 |
+
if ($response_code === 403) {
|
2446 |
+
if ($json_response = json_decode($response['body'], true)) {
|
2447 |
+
return new WP_Error(
|
2448 |
+
$wp_error_id,
|
2449 |
+
__('Github error:', 'fw') .' '. $json_response['message']
|
2450 |
+
);
|
2451 |
+
} else {
|
2452 |
+
return new WP_Error(
|
2453 |
+
$wp_error_id,
|
2454 |
+
sprintf(
|
2455 |
+
__( 'Failed to access Github repository "%s" releases. (Response code: %d)', 'fw' ),
|
2456 |
+
$source_data['user_repo'], $response_code
|
2457 |
+
)
|
2458 |
+
);
|
2459 |
+
}
|
2460 |
+
} elseif ($response_code) {
|
2461 |
+
return new WP_Error(
|
2462 |
+
$wp_error_id,
|
2463 |
+
sprintf(
|
2464 |
+
__( 'Failed to access Github repository "%s" releases. (Response code: %d)', 'fw' ),
|
2465 |
+
$source_data['user_repo'], $response_code
|
2466 |
+
)
|
2467 |
+
);
|
2468 |
+
} elseif (is_wp_error($response)) {
|
2469 |
+
return new WP_Error(
|
2470 |
+
$wp_error_id,
|
2471 |
+
sprintf(
|
2472 |
+
__( 'Failed to access Github repository "%s" releases. (%s)', 'fw' ),
|
2473 |
+
$source_data['user_repo'], $response->get_error_message()
|
2474 |
+
)
|
2475 |
+
);
|
2476 |
+
} else {
|
2477 |
+
return new WP_Error(
|
2478 |
+
$wp_error_id,
|
2479 |
+
sprintf(
|
2480 |
+
__( 'Failed to access Github repository "%s" releases.', 'fw' ),
|
2481 |
+
$source_data['user_repo']
|
2482 |
+
)
|
2483 |
+
);
|
2484 |
+
}
|
2485 |
+
}
|
2486 |
+
|
2487 |
+
$release = json_decode($response['body'], true);
|
2488 |
+
|
2489 |
+
unset($response);
|
2490 |
+
|
2491 |
+
if (empty($release)) {
|
2492 |
+
return new WP_Error(
|
2493 |
+
$wp_error_id,
|
2494 |
+
sprintf(
|
2495 |
+
__('"%s" extension github repository "%s" has no releases.', 'fw'),
|
2496 |
+
$this->get_extension_title($extension_name), $source_data['user_repo']
|
2497 |
+
)
|
2498 |
+
);
|
2499 |
+
}
|
2500 |
+
|
2501 |
+
{
|
2502 |
+
$cache[ $source_data['user_repo'] ] = array(
|
2503 |
+
'zipball_url' => 'https://github.com/'. $source_data['user_repo'] .'/archive/'. $release['tag_name'] .'.zip',
|
2504 |
+
'tag_name' => $release['tag_name']
|
2505 |
+
);
|
2506 |
+
|
2507 |
+
set_site_transient($transient_name, $cache, $transient_ttl);
|
2508 |
+
}
|
2509 |
+
|
2510 |
+
$download_link = $cache[ $source_data['user_repo'] ]['zipball_url'];
|
2511 |
+
|
2512 |
+
unset($release);
|
2513 |
+
}
|
2514 |
+
|
2515 |
+
{
|
2516 |
+
$http = new WP_Http();
|
2517 |
+
|
2518 |
+
$response = $http->request($download_link, array(
|
2519 |
+
'timeout' => $this->download_timeout,
|
2520 |
+
));
|
2521 |
+
|
2522 |
+
unset($http);
|
2523 |
+
|
2524 |
+
if (($response_code = intval(wp_remote_retrieve_response_code($response))) !== 200) {
|
2525 |
+
if ($response_code) {
|
2526 |
+
return new WP_Error(
|
2527 |
+
$wp_error_id,
|
2528 |
+
sprintf( __( 'Cannot download the "%s" extension zip. (Response code: %d)', 'fw' ),
|
2529 |
+
$this->get_extension_title( $extension_name ), $response_code
|
2530 |
+
)
|
2531 |
+
);
|
2532 |
+
} elseif (is_wp_error($response)) {
|
2533 |
+
return new WP_Error(
|
2534 |
+
$wp_error_id,
|
2535 |
+
sprintf( __( 'Cannot download the "%s" extension zip. %s', 'fw' ),
|
2536 |
+
$this->get_extension_title( $extension_name ),
|
2537 |
+
$response->get_error_message()
|
2538 |
+
)
|
2539 |
+
);
|
2540 |
+
} else {
|
2541 |
+
return new WP_Error(
|
2542 |
+
$wp_error_id,
|
2543 |
+
sprintf( __( 'Cannot download the "%s" extension zip.', 'fw' ),
|
2544 |
+
$this->get_extension_title( $extension_name )
|
2545 |
+
)
|
2546 |
+
);
|
2547 |
+
}
|
2548 |
+
}
|
2549 |
+
|
2550 |
+
$zip_path = $wp_fs_tmp_dir .'/temp.zip';
|
2551 |
+
|
2552 |
+
// save zip to file
|
2553 |
+
if (!$wp_filesystem->put_contents($zip_path, $response['body'])) {
|
2554 |
+
return new WP_Error(
|
2555 |
+
$wp_error_id,
|
2556 |
+
sprintf(__('Cannot save the "%s" extension zip.', 'fw'), $this->get_extension_title($extension_name))
|
2557 |
+
);
|
2558 |
+
}
|
2559 |
+
|
2560 |
+
unset($response);
|
2561 |
+
|
2562 |
+
$unzip_result = unzip_file(
|
2563 |
+
FW_WP_Filesystem::filesystem_path_to_real_path($zip_path),
|
2564 |
+
$wp_fs_tmp_dir
|
2565 |
+
);
|
2566 |
+
|
2567 |
+
if (is_wp_error($unzip_result)) {
|
2568 |
+
return $unzip_result;
|
2569 |
+
}
|
2570 |
+
|
2571 |
+
// remove zip file
|
2572 |
+
if (!$wp_filesystem->delete($zip_path, false, 'f')) {
|
2573 |
+
return new WP_Error(
|
2574 |
+
$wp_error_id,
|
2575 |
+
sprintf(__('Cannot remove the "%s" extension downloaded zip.', 'fw'), $this->get_extension_title($extension_name))
|
2576 |
+
);
|
2577 |
+
}
|
2578 |
+
|
2579 |
+
$unzipped_dir_files = $wp_filesystem->dirlist($wp_fs_tmp_dir);
|
2580 |
+
|
2581 |
+
if (!$unzipped_dir_files) {
|
2582 |
+
return new WP_Error(
|
2583 |
+
$wp_error_id,
|
2584 |
+
__('Cannot access the unzipped directory files.', 'fw')
|
2585 |
+
);
|
2586 |
+
}
|
2587 |
+
|
2588 |
+
/**
|
2589 |
+
* get first found directory
|
2590 |
+
* (if everything worked well, there should be only one directory)
|
2591 |
+
*/
|
2592 |
+
foreach ($unzipped_dir_files as $file) {
|
2593 |
+
if ($file['type'] == 'd') {
|
2594 |
+
return $wp_fs_tmp_dir .'/'. $file['name'];
|
2595 |
+
}
|
2596 |
+
}
|
2597 |
+
|
2598 |
+
return new WP_Error(
|
2599 |
+
$wp_error_id,
|
2600 |
+
sprintf(__('The unzipped "%s" extension directory not found.', 'fw'), $this->get_extension_title($extension_name))
|
2601 |
+
);
|
2602 |
+
}
|
2603 |
+
break;
|
2604 |
+
default:
|
2605 |
+
return new WP_Error(
|
2606 |
+
$wp_error_id,
|
2607 |
+
sprintf(__('Unknown "%s" extension download source "%s"', 'fw'), $this->get_extension_title($extension_name), $source)
|
2608 |
+
);
|
2609 |
+
}
|
2610 |
+
}
|
2611 |
+
}
|
2612 |
+
|
2613 |
+
/**
|
2614 |
+
* Merge the downloaded extension directory with the existing directory
|
2615 |
+
*
|
2616 |
+
* @param string $source_wp_fs_dir Downloaded extension directory
|
2617 |
+
* @param string $destination_wp_fs_dir
|
2618 |
+
*
|
2619 |
+
* @return null|WP_Error
|
2620 |
+
*/
|
2621 |
+
private function merge_extension($source_wp_fs_dir, $destination_wp_fs_dir)
|
2622 |
+
{
|
2623 |
+
/** @var WP_Filesystem_Base $wp_filesystem */
|
2624 |
+
global $wp_filesystem;
|
2625 |
+
|
2626 |
+
$wp_error_id = 'fw_extensions_merge';
|
2627 |
+
|
2628 |
+
$source_files = $wp_filesystem->dirlist($source_wp_fs_dir);
|
2629 |
+
|
2630 |
+
if ($source_files === false) {
|
2631 |
+
return new WP_Error(
|
2632 |
+
$wp_error_id,
|
2633 |
+
sprintf(__('Cannot read directory "%s".', 'fw'), $source_wp_fs_dir)
|
2634 |
+
);
|
2635 |
+
}
|
2636 |
+
|
2637 |
+
if (empty($source_files)) {
|
2638 |
+
// directory is empty, nothing to move
|
2639 |
+
return;
|
2640 |
+
}
|
2641 |
+
|
2642 |
+
/**
|
2643 |
+
* Prepare destination directory
|
2644 |
+
* Remove everything except the extensions/ directory
|
2645 |
+
*/
|
2646 |
+
if ($wp_filesystem->exists($destination_wp_fs_dir)) {
|
2647 |
+
$destination_files = $wp_filesystem->dirlist($destination_wp_fs_dir);
|
2648 |
+
|
2649 |
+
if ($destination_files === false) {
|
2650 |
+
return new WP_Error(
|
2651 |
+
$wp_error_id,
|
2652 |
+
sprintf(__('Cannot read directory "%s".', 'fw'), $destination_wp_fs_dir)
|
2653 |
+
);
|
2654 |
+
}
|
2655 |
+
|
2656 |
+
if (!empty($destination_files)) {
|
2657 |
+
// the directory contains some files, delete everything
|
2658 |
+
foreach ($destination_files as $file) {
|
2659 |
+
if ($file['name'] === 'extensions' && $file['type'] === 'd') {
|
2660 |
+
// do not touch the extensions/ directory
|
2661 |
+
continue;
|
2662 |
+
}
|
2663 |
+
|
2664 |
+
if (!$wp_filesystem->delete($destination_wp_fs_dir .'/'. $file['name'], true, $file['type'])) {
|
2665 |
+
return new WP_Error(
|
2666 |
+
$wp_error_id,
|
2667 |
+
sprintf(__('Cannot delete "%s".', 'fw'), $destination_wp_fs_dir .'/'. $file['name'])
|
2668 |
+
);
|
2669 |
+
}
|
2670 |
+
}
|
2671 |
+
|
2672 |
+
unset($destination_files);
|
2673 |
+
}
|
2674 |
+
} else {
|
2675 |
+
if (!FW_WP_Filesystem::mkdir_recursive($destination_wp_fs_dir)) {
|
2676 |
+
return new WP_Error(
|
2677 |
+
$wp_error_id,
|
2678 |
+
sprintf(__('Cannot create the "%s" directory.', 'fw'), $destination_wp_fs_dir)
|
2679 |
+
);
|
2680 |
+
}
|
2681 |
+
}
|
2682 |
+
|
2683 |
+
$has_sub_extensions = false;
|
2684 |
+
|
2685 |
+
foreach ($source_files as $file) {
|
2686 |
+
if ($file['name'] === 'extensions' && $file['type'] === 'd') {
|
2687 |
+
// do not touch the extensions/ directory
|
2688 |
+
$has_sub_extensions = true;
|
2689 |
+
continue;
|
2690 |
+
}
|
2691 |
+
|
2692 |
+
if (!$wp_filesystem->move($source_wp_fs_dir .'/'. $file['name'], $destination_wp_fs_dir .'/'. $file['name'])) {
|
2693 |
+
return new WP_Error(
|
2694 |
+
$wp_error_id,
|
2695 |
+
sprintf(
|
2696 |
+
__('Cannot move "%s" to "%s".', 'fw'),
|
2697 |
+
$source_wp_fs_dir .'/'. $file['name'],
|
2698 |
+
$destination_wp_fs_dir .'/'. $file['name']
|
2699 |
+
)
|
2700 |
+
);
|
2701 |
+
}
|
2702 |
+
}
|
2703 |
+
|
2704 |
+
unset($source_files);
|
2705 |
+
|
2706 |
+
if (!$has_sub_extensions) {
|
2707 |
+
return;
|
2708 |
+
}
|
2709 |
+
|
2710 |
+
$sub_extensions = $wp_filesystem->dirlist($source_wp_fs_dir .'/extensions');
|
2711 |
+
|
2712 |
+
if ($sub_extensions === false) {
|
2713 |
+
return new WP_Error(
|
2714 |
+
$wp_error_id,
|
2715 |
+
sprintf(__('Cannot read directory "%s".', 'fw'), $source_wp_fs_dir .'/extensions')
|
2716 |
+
);
|
2717 |
+
}
|
2718 |
+
|
2719 |
+
if (empty($sub_extensions)) {
|
2720 |
+
// directory is empty, nothing to remove
|
2721 |
+
return;
|
2722 |
+
}
|
2723 |
+
|
2724 |
+
foreach ($sub_extensions as $file) {
|
2725 |
+
if ($file['type'] !== 'd') {
|
2726 |
+
// wrong, only directories must exist in the extensions/ directory
|
2727 |
+
continue;
|
2728 |
+
}
|
2729 |
+
|
2730 |
+
$merge_result = $this->merge_extension(
|
2731 |
+
$source_wp_fs_dir .'/extensions/'. $file['name'],
|
2732 |
+
$destination_wp_fs_dir .'/extensions/'. $file['name']
|
2733 |
+
);
|
2734 |
+
|
2735 |
+
if (is_wp_error($merge_result)) {
|
2736 |
+
return $merge_result;
|
2737 |
+
}
|
2738 |
+
}
|
2739 |
+
}
|
2740 |
+
|
2741 |
+
private function get_supported_extensions_for_install()
|
2742 |
+
{
|
2743 |
+
$supported_extensions = fw()->theme->manifest->get('supported_extensions', array());
|
2744 |
+
|
2745 |
+
if (empty($supported_extensions)) {
|
2746 |
+
return array();
|
2747 |
+
}
|
2748 |
+
|
2749 |
+
// remove not available extensions
|
2750 |
+
$supported_extensions = array_intersect_key($supported_extensions, $this->get_available_extensions());
|
2751 |
+
|
2752 |
+
if (empty($supported_extensions)) {
|
2753 |
+
return array();
|
2754 |
+
}
|
2755 |
+
|
2756 |
+
// remove already installed extensions
|
2757 |
+
$supported_extensions = array_diff_key($supported_extensions, $this->get_installed_extensions());
|
2758 |
+
|
2759 |
+
if (empty($supported_extensions)) {
|
2760 |
+
return array();
|
2761 |
+
}
|
2762 |
+
|
2763 |
+
return $supported_extensions;
|
2764 |
+
}
|
2765 |
+
|
2766 |
+
/**
|
2767 |
+
* @param $actions
|
2768 |
+
* @return array
|
2769 |
+
* @internal
|
2770 |
+
*/
|
2771 |
+
public function _filter_plugin_action_list($actions)
|
2772 |
+
{
|
2773 |
+
return array_merge(
|
2774 |
+
array(
|
2775 |
+
'fw-extensions' => fw_html_tag('a', array(
|
2776 |
+
'href' => $this->get_link(),
|
2777 |
+
), fw()->manifest->get_name()),
|
2778 |
+
),
|
2779 |
+
$actions
|
2780 |
+
);
|
2781 |
+
}
|
2782 |
+
|
2783 |
+
/**
|
2784 |
+
* @return string Extensions page link
|
2785 |
+
*/
|
2786 |
+
private function get_link()
|
2787 |
+
{
|
2788 |
+
static $cache_link = null;
|
2789 |
+
|
2790 |
+
if ($cache_link === null) {
|
2791 |
+
$cache_link = menu_page_url( $this->get_page_slug(), false );
|
2792 |
+
|
2793 |
+
// https://core.trac.wordpress.org/ticket/28226
|
2794 |
+
if (is_multisite() && is_network_admin()) {
|
2795 |
+
$cache_link = self_admin_url(
|
2796 |
+
// extract relative link
|
2797 |
+
preg_replace('/^'. preg_quote(admin_url(), '/') .'/', '', $cache_link)
|
2798 |
+
);
|
2799 |
+
}
|
2800 |
+
}
|
2801 |
+
|
2802 |
+
return $cache_link;
|
2803 |
+
}
|
2804 |
+
|
2805 |
+
/**
|
2806 |
+
* @param array $skip_extensions {'ext' => mixed}
|
2807 |
+
* @param array $check_for_deps ['ext', 'ext', ...] Extensions to check if has in dependencies the used extensions
|
2808 |
+
*
|
2809 |
+
* @return array
|
2810 |
+
*/
|
2811 |
+
private function get_used_extensions($skip_extensions, $check_for_deps)
|
2812 |
+
{
|
2813 |
+
$used_extensions = array();
|
2814 |
+
|
2815 |
+
$installed_extensions = $this->get_installed_extensions();
|
2816 |
+
|
2817 |
+
foreach ($installed_extensions as $inst_ext_name => &$inst_ext_data) {
|
2818 |
+
if (isset($skip_extensions[ $inst_ext_name ])) {
|
2819 |
+
continue;
|
2820 |
+
}
|
2821 |
+
|
2822 |
+
if (isset($used_extensions[$inst_ext_name])) {
|
2823 |
+
// already marked as used
|
2824 |
+
continue;
|
2825 |
+
}
|
2826 |
+
|
2827 |
+
do {
|
2828 |
+
foreach ($check_for_deps as $deps_ext) {
|
2829 |
+
if (isset($skip_extensions[$deps_ext])) {
|
2830 |
+
continue;
|
2831 |
+
}
|
2832 |
+
|
2833 |
+
if (false !== fw_akg(
|
2834 |
+
'requirements/extensions/'. $inst_ext_name,
|
2835 |
+
$installed_extensions[$deps_ext]['manifest'],
|
2836 |
+
false
|
2837 |
+
)) {
|
2838 |
+
// is required by an active extension
|
2839 |
+
break 2;
|
2840 |
+
}
|
2841 |
+
}
|
2842 |
+
|
2843 |
+
if ( true === fw_akg(
|
2844 |
+
'standalone',
|
2845 |
+
$inst_ext_data['manifest'],
|
2846 |
+
$this->manifest_default_values['standalone']
|
2847 |
+
) ) {
|
2848 |
+
// can exist alone
|
2849 |
+
break;
|
2850 |
+
}
|
2851 |
+
|
2852 |
+
// not used
|
2853 |
+
continue 2;
|
2854 |
+
} while(false);
|
2855 |
+
|
2856 |
+
$used_extensions[$inst_ext_name] = array();
|
2857 |
+
|
2858 |
+
// Set all sub-extensions as used
|
2859 |
+
foreach ($this->collect_sub_extensions($inst_ext_name, $installed_extensions) as $sub_extension_name => $sub_extension_data) {
|
2860 |
+
if (isset($skip_extensions[$sub_extension_name])) {
|
2861 |
+
continue;
|
2862 |
+
}
|
2863 |
+
|
2864 |
+
$used_extensions[ $sub_extension_name ] = array();
|
2865 |
+
}
|
2866 |
+
|
2867 |
+
// Set all parents as used
|
2868 |
+
{
|
2869 |
+
$current_parent = $inst_ext_name;
|
2870 |
+
while ($current_parent = $installed_extensions[$current_parent]['parent']) {
|
2871 |
+
$used_extensions[$current_parent] = array();
|
2872 |
+
}
|
2873 |
+
}
|
2874 |
+
}
|
2875 |
+
unset($inst_ext_data);
|
2876 |
+
|
2877 |
+
// remove all skipped extensions and sub-extension from used extensions
|
2878 |
+
foreach (array_keys($skip_extensions) as $skip_extension_name) {
|
2879 |
+
unset($used_extensions[$skip_extension_name]);
|
2880 |
+
|
2881 |
+
if (isset($installed_extensions[$skip_extension_name])) {
|
2882 |
+
foreach ($this->collect_sub_extensions($skip_extension_name, $installed_extensions) as $sub_extension_name => $sub_extension_data) {
|
2883 |
+
unset($used_extensions[$sub_extension_name]);
|
2884 |
+
}
|
2885 |
+
}
|
2886 |
+
}
|
2887 |
+
|
2888 |
+
return $used_extensions;
|
2889 |
+
}
|
2890 |
+
|
2891 |
+
/**
|
2892 |
+
* @internal
|
2893 |
+
*/
|
2894 |
+
public function _action_admin_footer()
|
2895 |
+
{
|
2896 |
+
$this->activate_hidden_standalone_extensions();
|
2897 |
+
}
|
2898 |
+
|
2899 |
+
public function get_extension_title($extension_name)
|
2900 |
+
{
|
2901 |
+
$installed_extensions = $this->get_installed_extensions();
|
2902 |
+
|
2903 |
+
if (isset($installed_extensions[$extension_name])) {
|
2904 |
+
return fw_akg('name', $installed_extensions[$extension_name]['manifest'], fw_id_to_title($extension_name));
|
2905 |
+
}
|
2906 |
+
|
2907 |
+
unset($installed_extensions);
|
2908 |
+
|
2909 |
+
$available_extensions = $this->get_available_extensions();
|
2910 |
+
|
2911 |
+
if (isset($available_extensions[$extension_name])) {
|
2912 |
+
return $available_extensions[$extension_name]['name'];
|
2913 |
+
}
|
2914 |
+
|
2915 |
+
return fw_id_to_title($extension_name);
|
2916 |
+
}
|
2917 |
+
|
2918 |
+
public function is_extensions_page()
|
2919 |
+
{
|
2920 |
+
$current_screen = get_current_screen();
|
2921 |
+
|
2922 |
+
if (empty($current_screen)) {
|
2923 |
+
return false;
|
2924 |
+
}
|
2925 |
+
|
2926 |
+
return (
|
2927 |
+
property_exists($current_screen, 'base') && strpos($current_screen->base, $this->get_page_slug()) !== false
|
2928 |
+
&&
|
2929 |
+
property_exists($current_screen, 'id') && strpos($current_screen->id, $this->get_page_slug()) !== false
|
2930 |
+
&&
|
2931 |
+
!isset($_GET['sub-page'])
|
2932 |
+
);
|
2933 |
+
}
|
2934 |
+
|
2935 |
+
public function is_extension_page()
|
2936 |
+
{
|
2937 |
+
$current_screen = get_current_screen();
|
2938 |
+
|
2939 |
+
if (empty($current_screen)) {
|
2940 |
+
return false;
|
2941 |
+
}
|
2942 |
+
|
2943 |
+
return (
|
2944 |
+
property_exists($current_screen, 'base') && strpos($current_screen->base, $this->get_page_slug()) !== false
|
2945 |
+
&&
|
2946 |
+
property_exists($current_screen, 'id') && strpos($current_screen->id, $this->get_page_slug()) !== false
|
2947 |
+
&&
|
2948 |
+
isset($_GET['sub-page']) && $_GET['sub-page'] === 'extension'
|
2949 |
+
);
|
2950 |
+
}
|
2951 |
+
|
2952 |
+
/**
|
2953 |
+
* @internal
|
2954 |
+
*/
|
2955 |
+
public function _action_enqueue_scripts()
|
2956 |
+
{
|
2957 |
+
wp_enqueue_style(
|
2958 |
+
'fw-extensions-menu-icon',
|
2959 |
+
$this->get_uri('/static/unyson-font-icon/style.css'),
|
2960 |
+
array(),
|
2961 |
+
fw()->manifest->get_version()
|
2962 |
+
);
|
2963 |
+
|
2964 |
+
/**
|
2965 |
+
* Enqueue only on Extensions List page
|
2966 |
+
*/
|
2967 |
+
if ($this->is_extensions_page()) {
|
2968 |
+
wp_enqueue_style(
|
2969 |
+
'fw-extensions-page',
|
2970 |
+
$this->get_uri('/static/extensions-page.css'),
|
2971 |
+
array(
|
2972 |
+
'fw',
|
2973 |
+
'fw-unycon', 'fw-font-awesome', // in case some extension has font-icon thumbnail
|
2974 |
+
),
|
2975 |
+
fw()->manifest->get_version()
|
2976 |
+
);
|
2977 |
+
wp_enqueue_script(
|
2978 |
+
'fw-extensions-page',
|
2979 |
+
$this->get_uri('/static/extensions-page.js'),
|
2980 |
+
array('fw'),
|
2981 |
+
fw()->manifest->get_version(),
|
2982 |
+
true
|
2983 |
+
);
|
2984 |
+
wp_localize_script('fw-extensions-page', '_fw_extensions_script_data', array(
|
2985 |
+
'link' => $this->get_link(),
|
2986 |
+
));
|
2987 |
+
|
2988 |
+
/**
|
2989 |
+
* this is needed for fw.soleModal design
|
2990 |
+
* it is displayed when extension ajax install returns an error
|
2991 |
+
*/
|
2992 |
+
wp_enqueue_media();
|
2993 |
+
}
|
2994 |
+
|
2995 |
+
if ($this->is_extension_page()) {
|
2996 |
+
wp_enqueue_style(
|
2997 |
+
'fw-extension-page',
|
2998 |
+
$this->get_uri('/static/extension-page.css'),
|
2999 |
+
array('fw'),
|
3000 |
+
fw()->manifest->get_version()
|
3001 |
+
);
|
3002 |
+
wp_enqueue_script(
|
3003 |
+
'fw-extension-page',
|
3004 |
+
$this->get_uri('/static/extension-page.js'),
|
3005 |
+
array('fw'),
|
3006 |
+
fw()->manifest->get_version(),
|
3007 |
+
true
|
3008 |
+
);
|
3009 |
+
|
3010 |
+
/**
|
3011 |
+
* Enqueue extension settings options static
|
3012 |
+
*/
|
3013 |
+
if (
|
3014 |
+
isset($_GET['extension'])
|
3015 |
+
&&
|
3016 |
+
is_string($extension_name = $_GET['extension'])
|
3017 |
+
&&
|
3018 |
+
fw()->extensions->get($extension_name)
|
3019 |
+
&&
|
3020 |
+
($extension_settings_options = fw()->extensions->get($extension_name)->get_settings_options())
|
3021 |
+
) {
|
3022 |
+
fw()->backend->enqueue_options_static($extension_settings_options);
|
3023 |
+
}
|
3024 |
+
}
|
3025 |
+
}
|
3026 |
+
|
3027 |
+
private function activate_theme_extensions()
|
3028 |
+
{
|
3029 |
+
$db_active_extensions = fw()->extensions->_get_db_active_extensions();
|
3030 |
+
|
3031 |
+
foreach ($this->get_installed_extensions() as $extension_name => $extension) {
|
3032 |
+
if ($extension['is']['theme']) {
|
3033 |
+
$db_active_extensions[ $extension_name ] = array();
|
3034 |
+
}
|
3035 |
+
}
|
3036 |
+
|
3037 |
+
update_option(
|
3038 |
+
fw()->extensions->_get_active_extensions_db_option_name(),
|
3039 |
+
$db_active_extensions
|
3040 |
+
);
|
3041 |
+
}
|
3042 |
+
|
3043 |
+
/**
|
3044 |
+
* @internal
|
3045 |
+
*/
|
3046 |
+
public function _action_theme_switch()
|
3047 |
+
{
|
3048 |
+
$this->activate_theme_extensions();
|
3049 |
+
$this->activate_extensions(
|
3050 |
+
array_fill_keys(
|
3051 |
+
array_keys(fw()->theme->manifest->get('supported_extensions', array())),
|
3052 |
+
array()
|
3053 |
+
)
|
3054 |
+
);
|
3055 |
+
}
|
3056 |
+
|
3057 |
+
/**
|
3058 |
+
* @param array $collected The found extensions {'extension_name' => array()}
|
3059 |
+
* @param array $extensions {'extension_name' => array()}
|
3060 |
+
* @param bool $check_all Check all extensions or only active extensions
|
3061 |
+
*/
|
3062 |
+
private function collect_extensions_that_requires(&$collected, $extensions, $check_all = false)
|
3063 |
+
{
|
3064 |
+
if (empty($extensions)) {
|
3065 |
+
return;
|
3066 |
+
}
|
3067 |
+
|
3068 |
+
$found_extensions = array();
|
3069 |
+
|
3070 |
+
foreach ($this->get_installed_extensions() as $extension_name => $extension_data) {
|
3071 |
+
if (isset($collected[$extension_name])) {
|
3072 |
+
continue;
|
3073 |
+
}
|
3074 |
+
|
3075 |
+
if (!$check_all) {
|
3076 |
+
if (!fw_ext($extension_name)) {
|
3077 |
+
continue;
|
3078 |
+
}
|
3079 |
+
}
|
3080 |
+
|
3081 |
+
if (
|
3082 |
+
array_intersect_key(
|
3083 |
+
$extensions,
|
3084 |
+
fw_akg(
|
3085 |
+
'requirements/extensions',
|
3086 |
+
$extension_data['manifest'],
|
3087 |
+
array()
|
3088 |
+
)
|
3089 |
+
)
|
3090 |
+
) {
|
3091 |
+
$found_extensions[$extension_name] = $collected[$extension_name] = array();
|
3092 |
+
}
|
3093 |
+
}
|
3094 |
+
|
3095 |
+
$this->collect_extensions_that_requires($collected, $found_extensions, $check_all);
|
3096 |
+
}
|
3097 |
+
|
3098 |
+
/**
|
3099 |
+
* Get extension settings page link
|
3100 |
+
* @param string $extension_name
|
3101 |
+
* @return string
|
3102 |
+
*/
|
3103 |
+
public function get_extension_link($extension_name)
|
3104 |
+
{
|
3105 |
+
return $this->get_link() .'&sub-page=extension&extension='. $extension_name;
|
3106 |
+
}
|
3107 |
+
|
3108 |
+
/**
|
3109 |
+
* @param string $extension_name
|
3110 |
+
* @return array|WP_Error Extensions to merge with db active extensions list
|
3111 |
+
*/
|
3112 |
+
private function get_extensions_for_activation($extension_name)
|
3113 |
+
{
|
3114 |
+
$installed_extensions = $this->get_installed_extensions();
|
3115 |
+
|
3116 |
+
$wp_error_id = 'fw_ext_activation';
|
3117 |
+
|
3118 |
+
if (!isset($installed_extensions[$extension_name])) {
|
3119 |
+
return new WP_Error($wp_error_id,
|
3120 |
+
sprintf(
|
3121 |
+
__('Cannot activate the %s extension because it is not installed. %s', 'fw'),
|
3122 |
+
$this->get_extension_title($extension_name),
|
3123 |
+
fw_html_tag('a', array(
|
3124 |
+
'href' => $this->get_link() .'&sub-page=install&extension='. $extension_name
|
3125 |
+
), __('Install', 'fw'))
|
3126 |
+
)
|
3127 |
+
);
|
3128 |
+
}
|
3129 |
+
|
3130 |
+
{
|
3131 |
+
$extension_parents = array($extension_name);
|
3132 |
+
|
3133 |
+
$current_parent = $extension_name;
|
3134 |
+
while ($current_parent = $installed_extensions[$current_parent]['parent']) {
|
3135 |
+
$extension_parents[] = $current_parent;
|
3136 |
+
}
|
3137 |
+
|
3138 |
+
$extension_parents = array_reverse($extension_parents);
|
3139 |
+
}
|
3140 |
+
|
3141 |
+
$extensions = array();
|
3142 |
+
|
3143 |
+
foreach ($extension_parents as $parent_extension_name) {
|
3144 |
+
$extensions[ $parent_extension_name ] = array();
|
3145 |
+
}
|
3146 |
+
|
3147 |
+
// search sub-extensions
|
3148 |
+
foreach ($this->collect_sub_extensions($extension_name, $installed_extensions) as $sub_extension_name => $sub_extension_data) {
|
3149 |
+
$extensions[ $sub_extension_name ] = array();
|
3150 |
+
}
|
3151 |
+
|
3152 |
+
// search required extensions
|
3153 |
+
{
|
3154 |
+
$pending_required_search = $extensions;
|
3155 |
+
|
3156 |
+
while ($pending_required_search) {
|
3157 |
+
foreach (array_keys($pending_required_search) as $pend_req_extension_name) {
|
3158 |
+
unset($pending_required_search[$pend_req_extension_name]);
|
3159 |
+
|
3160 |
+
unset($required_extensions); // reset reference
|
3161 |
+
$required_extensions = array();
|
3162 |
+
$this->collect_required_extensions($pend_req_extension_name, $installed_extensions, $required_extensions);
|
3163 |
+
|
3164 |
+
foreach ($required_extensions as $required_extension_name => $required_extension_data) {
|
3165 |
+
if (!isset($installed_extensions[$required_extension_name])) {
|
3166 |
+
return new WP_Error($wp_error_id,
|
3167 |
+
sprintf(
|
3168 |
+
__('Cannot activate the %s extension because it is not installed. %s', 'fw'),
|
3169 |
+
$this->get_extension_title($required_extension_name),
|
3170 |
+
fw_html_tag('a', array(
|
3171 |
+
'href' => $this->get_link() .'&sub-page=install&extension='. $required_extension_name
|
3172 |
+
), __('Install', 'fw'))
|
3173 |
+
)
|
3174 |
+
);
|
3175 |
+
}
|
3176 |
+
|
3177 |
+
$extensions[$required_extension_name] = array();
|
3178 |
+
|
3179 |
+
// search sub-extensions
|
3180 |
+
foreach ($this->collect_sub_extensions($required_extension_name, $installed_extensions) as $sub_extension_name => $sub_extension_data) {
|
3181 |
+
if (isset($extensions[$sub_extension_name])) {
|
3182 |
+
continue;
|
3183 |
+
}
|
3184 |
+
|
3185 |
+
$extensions[$sub_extension_name] = array();
|
3186 |
+
|
3187 |
+
$pending_required_search[$sub_extension_name] = array();
|
3188 |
+
}
|
3189 |
+
}
|
3190 |
+
}
|
3191 |
+
}
|
3192 |
+
}
|
3193 |
+
|
3194 |
+
return $extensions;
|
3195 |
+
}
|
3196 |
+
|
3197 |
+
public function _action_admin_notices() {
|
3198 |
+
/**
|
3199 |
+
* In v2.4.12 was done a terrible mistake https://github.com/ThemeFuse/Unyson-Extensions-Approval/issues/160
|
3200 |
+
* Show a warning with link to install theme supported extensions
|
3201 |
+
*/
|
3202 |
+
if (
|
3203 |
+
!isset($_GET['supported']) // already on 'Install Supported Extensions' page
|
3204 |
+
&&
|
3205 |
+
$this->can_install()
|
3206 |
+
&&
|
3207 |
+
(($installed_extensions = $this->get_installed_extensions()) || true)
|
3208 |
+
&&
|
3209 |
+
!isset($installed_extensions['page-builder'])
|
3210 |
+
&&
|
3211 |
+
$this->get_supported_extensions_for_install()
|
3212 |
+
) {
|
3213 |
+
echo '<div class="error"> <p>'
|
3214 |
+
, fw_html_tag('a', array('href' => $this->get_link() .'&sub-page=install&supported'),
|
3215 |
+
__('Install theme compatible extensions', 'fw'))
|
3216 |
+
, '</p></div>';
|
3217 |
+
}
|
3218 |
+
}
|
3219 |
+
}
|
framework/core/components/extensions/manager/includes/class--fw-extensions-delete-upgrader-skin.php
CHANGED
@@ -1,28 +1,28 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
|
3 |
-
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader-skins.php';
|
4 |
-
|
5 |
-
class _FW_Extensions_Delete_Upgrader_Skin extends WP_Upgrader_Skin
|
6 |
-
{
|
7 |
-
public function after($data = array())
|
8 |
-
{
|
9 |
-
$update_actions = array(
|
10 |
-
'extensions_page' => fw_html_tag(
|
11 |
-
'a',
|
12 |
-
array(
|
13 |
-
'href' => fw_akg('extensions_page_link', $data, '#'),
|
14 |
-
'title' => __('Go to extensions page', 'fw'),
|
15 |
-
'target' => '_parent',
|
16 |
-
),
|
17 |
-
__('Return to Extensions page', 'fw')
|
18 |
-
)
|
19 |
-
);
|
20 |
-
|
21 |
-
$this->feedback(implode(' | ', (array)$update_actions));
|
22 |
-
|
23 |
-
if ($this->result) {
|
24 |
-
// used for popup ajax form submit result
|
25 |
-
$this->feedback('<span success></span>');
|
26 |
-
}
|
27 |
-
}
|
28 |
-
}
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
|
3 |
+
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader-skins.php';
|
4 |
+
|
5 |
+
class _FW_Extensions_Delete_Upgrader_Skin extends WP_Upgrader_Skin
|
6 |
+
{
|
7 |
+
public function after($data = array())
|
8 |
+
{
|
9 |
+
$update_actions = array(
|
10 |
+
'extensions_page' => fw_html_tag(
|
11 |
+
'a',
|
12 |
+
array(
|
13 |
+
'href' => fw_akg('extensions_page_link', $data, '#'),
|
14 |
+
'title' => __('Go to extensions page', 'fw'),
|
15 |
+
'target' => '_parent',
|
16 |
+
),
|
17 |
+
__('Return to Extensions page', 'fw')
|
18 |
+
)
|
19 |
+
);
|
20 |
+
|
21 |
+
$this->feedback(implode(' | ', (array)$update_actions));
|
22 |
+
|
23 |
+
if ($this->result) {
|
24 |
+
// used for popup ajax form submit result
|
25 |
+
$this->feedback('<span success></span>');
|
26 |
+
}
|
27 |
+
}
|
28 |
+
}
|
framework/core/components/extensions/manager/includes/class--fw-extensions-install-upgrader-skin.php
CHANGED
@@ -1,28 +1,28 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
|
3 |
-
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader-skins.php';
|
4 |
-
|
5 |
-
class _FW_Extensions_Install_Upgrader_Skin extends WP_Upgrader_Skin
|
6 |
-
{
|
7 |
-
public function after($data = array())
|
8 |
-
{
|
9 |
-
$update_actions = array(
|
10 |
-
'extensions_page' => fw_html_tag(
|
11 |
-
'a',
|
12 |
-
array(
|
13 |
-
'href' => fw_akg('extensions_page_link', $data, '#'),
|
14 |
-
'title' => __('Go to extensions page', 'fw'),
|
15 |
-
'target' => '_parent',
|
16 |
-
),
|
17 |
-
__('Return to Extensions page', 'fw')
|
18 |
-
)
|
19 |
-
);
|
20 |
-
|
21 |
-
$this->feedback(implode(' | ', (array)$update_actions));
|
22 |
-
|
23 |
-
if ($this->result) {
|
24 |
-
// used for popup ajax form submit result
|
25 |
-
$this->feedback('<span success></span>');
|
26 |
-
}
|
27 |
-
}
|
28 |
-
}
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
|
3 |
+
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader-skins.php';
|
4 |
+
|
5 |
+
class _FW_Extensions_Install_Upgrader_Skin extends WP_Upgrader_Skin
|
6 |
+
{
|
7 |
+
public function after($data = array())
|
8 |
+
{
|
9 |
+
$update_actions = array(
|
10 |
+
'extensions_page' => fw_html_tag(
|
11 |
+
'a',
|
12 |
+
array(
|
13 |
+
'href' => fw_akg('extensions_page_link', $data, '#'),
|
14 |
+
'title' => __('Go to extensions page', 'fw'),
|
15 |
+
'target' => '_parent',
|
16 |
+
),
|
17 |
+
__('Return to Extensions page', 'fw')
|
18 |
+
)
|
19 |
+
);
|
20 |
+
|
21 |
+
$this->feedback(implode(' | ', (array)$update_actions));
|
22 |
+
|
23 |
+
if ($this->result) {
|
24 |
+
// used for popup ajax form submit result
|
25 |
+
$this->feedback('<span success></span>');
|
26 |
+
}
|
27 |
+
}
|
28 |
+
}
|
framework/core/components/extensions/manager/includes/parsedown/LICENSE.txt
CHANGED
@@ -1,20 +1,20 @@
|
|
1 |
-
The MIT License (MIT)
|
2 |
-
|
3 |
-
Copyright (c) 2013 Emanuil Rusev, erusev.com
|
4 |
-
|
5 |
-
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6 |
-
this software and associated documentation files (the "Software"), to deal in
|
7 |
-
the Software without restriction, including without limitation the rights to
|
8 |
-
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9 |
-
the Software, and to permit persons to whom the Software is furnished to do so,
|
10 |
-
subject to the following conditions:
|
11 |
-
|
12 |
-
The above copyright notice and this permission notice shall be included in all
|
13 |
-
copies or substantial portions of the Software.
|
14 |
-
|
15 |
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16 |
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17 |
-
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18 |
-
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19 |
-
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20 |
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
1 |
+
The MIT License (MIT)
|
2 |
+
|
3 |
+
Copyright (c) 2013 Emanuil Rusev, erusev.com
|
4 |
+
|
5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6 |
+
this software and associated documentation files (the "Software"), to deal in
|
7 |
+
the Software without restriction, including without limitation the rights to
|
8 |
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9 |
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10 |
+
subject to the following conditions:
|
11 |
+
|
12 |
+
The above copyright notice and this permission notice shall be included in all
|
13 |
+
copies or substantial portions of the Software.
|
14 |
+
|
15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17 |
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18 |
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19 |
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20 |
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
framework/core/components/extensions/manager/includes/parsedown/Parsedown.php
CHANGED
@@ -1,1528 +1,1528 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
#
|
4 |
-
#
|
5 |
-
# Parsedown
|
6 |
-
# http://parsedown.org
|
7 |
-
#
|
8 |
-
# (c) Emanuil Rusev
|
9 |
-
# http://erusev.com
|
10 |
-
#
|
11 |
-
# For the full license information, view the LICENSE file that was distributed
|
12 |
-
# with this source code.
|
13 |
-
#
|
14 |
-
#
|
15 |
-
|
16 |
-
class Parsedown
|
17 |
-
{
|
18 |
-
# ~
|
19 |
-
|
20 |
-
const version = '1.5.4';
|
21 |
-
|
22 |
-
# ~
|
23 |
-
|
24 |
-
function text($text)
|
25 |
-
{
|
26 |
-
# make sure no definitions are set
|
27 |
-
$this->DefinitionData = array();
|
28 |
-
|
29 |
-
# standardize line breaks
|
30 |
-
$text = str_replace(array("\r\n", "\r"), "\n", $text);
|
31 |
-
|
32 |
-
# remove surrounding line breaks
|
33 |
-
$text = trim($text, "\n");
|
34 |
-
|
35 |
-
# split text into lines
|
36 |
-
$lines = explode("\n", $text);
|
37 |
-
|
38 |
-
# iterate through lines to identify blocks
|
39 |
-
$markup = $this->lines($lines);
|
40 |
-
|
41 |
-
# trim line breaks
|
42 |
-
$markup = trim($markup, "\n");
|
43 |
-
|
44 |
-
return $markup;
|
45 |
-
}
|
46 |
-
|
47 |
-
#
|
48 |
-
# Setters
|
49 |
-
#
|
50 |
-
|
51 |
-
function setBreaksEnabled($breaksEnabled)
|
52 |
-
{
|
53 |
-
$this->breaksEnabled = $breaksEnabled;
|
54 |
-
|
55 |
-
return $this;
|
56 |
-
}
|
57 |
-
|
58 |
-
protected $breaksEnabled;
|
59 |
-
|
60 |
-
function setMarkupEscaped($markupEscaped)
|
61 |
-
{
|
62 |
-
$this->markupEscaped = $markupEscaped;
|
63 |
-
|
64 |
-
return $this;
|
65 |
-
}
|
66 |
-
|
67 |
-
protected $markupEscaped;
|
68 |
-
|
69 |
-
function setUrlsLinked($urlsLinked)
|
70 |
-
{
|
71 |
-
$this->urlsLinked = $urlsLinked;
|
72 |
-
|
73 |
-
return $this;
|
74 |
-
}
|
75 |
-
|
76 |
-
protected $urlsLinked = true;
|
77 |
-
|
78 |
-
#
|
79 |
-
# Lines
|
80 |
-
#
|
81 |
-
|
82 |
-
protected $BlockTypes = array(
|
83 |
-
'#' => array('Header'),
|
84 |
-
'*' => array('Rule', 'List'),
|
85 |
-
'+' => array('List'),
|
86 |
-
'-' => array('SetextHeader', 'Table', 'Rule', 'List'),
|
87 |
-
'0' => array('List'),
|
88 |
-
'1' => array('List'),
|
89 |
-
'2' => array('List'),
|
90 |
-
'3' => array('List'),
|
91 |
-
'4' => array('List'),
|
92 |
-
'5' => array('List'),
|
93 |
-
'6' => array('List'),
|
94 |
-
'7' => array('List'),
|
95 |
-
'8' => array('List'),
|
96 |
-
'9' => array('List'),
|
97 |
-
':' => array('Table'),
|
98 |
-
'<' => array('Comment', 'Markup'),
|
99 |
-
'=' => array('SetextHeader'),
|
100 |
-
'>' => array('Quote'),
|
101 |
-
'[' => array('Reference'),
|
102 |
-
'_' => array('Rule'),
|
103 |
-
'`' => array('FencedCode'),
|
104 |
-
'|' => array('Table'),
|
105 |
-
'~' => array('FencedCode'),
|
106 |
-
);
|
107 |
-
|
108 |
-
# ~
|
109 |
-
|
110 |
-
protected $unmarkedBlockTypes = array(
|
111 |
-
'Code',
|
112 |
-
);
|
113 |
-
|
114 |
-
#
|
115 |
-
# Blocks
|
116 |
-
#
|
117 |
-
|
118 |
-
private function lines(array $lines)
|
119 |
-
{
|
120 |
-
$CurrentBlock = null;
|
121 |
-
|
122 |
-
foreach ($lines as $line)
|
123 |
-
{
|
124 |
-
if (chop($line) === '')
|
125 |
-
{
|
126 |
-
if (isset($CurrentBlock))
|
127 |
-
{
|
128 |
-
$CurrentBlock['interrupted'] = true;
|
129 |
-
}
|
130 |
-
|
131 |
-
continue;
|
132 |
-
}
|
133 |
-
|
134 |
-
if (strpos($line, "\t") !== false)
|
135 |
-
{
|
136 |
-
$parts = explode("\t", $line);
|
137 |
-
|
138 |
-
$line = $parts[0];
|
139 |
-
|
140 |
-
unset($parts[0]);
|
141 |
-
|
142 |
-
foreach ($parts as $part)
|
143 |
-
{
|
144 |
-
$shortage = 4 - mb_strlen($line, 'utf-8') % 4;
|
145 |
-
|
146 |
-
$line .= str_repeat(' ', $shortage);
|
147 |
-
$line .= $part;
|
148 |
-
}
|
149 |
-
}
|
150 |
-
|
151 |
-
$indent = 0;
|
152 |
-
|
153 |
-
while (isset($line[$indent]) and $line[$indent] === ' ')
|
154 |
-
{
|
155 |
-
$indent ++;
|
156 |
-
}
|
157 |
-
|
158 |
-
$text = $indent > 0 ? substr($line, $indent) : $line;
|
159 |
-
|
160 |
-
# ~
|
161 |
-
|
162 |
-
$Line = array('body' => $line, 'indent' => $indent, 'text' => $text);
|
163 |
-
|
164 |
-
# ~
|
165 |
-
|
166 |
-
if (isset($CurrentBlock['continuable']))
|
167 |
-
{
|
168 |
-
$Block = $this->{'block'.$CurrentBlock['type'].'Continue'}($Line, $CurrentBlock);
|
169 |
-
|
170 |
-
if (isset($Block))
|
171 |
-
{
|
172 |
-
$CurrentBlock = $Block;
|
173 |
-
|
174 |
-
continue;
|
175 |
-
}
|
176 |
-
else
|
177 |
-
{
|
178 |
-
if (method_exists($this, 'block'.$CurrentBlock['type'].'Complete'))
|
179 |
-
{
|
180 |
-
$CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
|
181 |
-
}
|
182 |
-
}
|
183 |
-
}
|
184 |
-
|
185 |
-
# ~
|
186 |
-
|
187 |
-
$marker = $text[0];
|
188 |
-
|
189 |
-
# ~
|
190 |
-
|
191 |
-
$blockTypes = $this->unmarkedBlockTypes;
|
192 |
-
|
193 |
-
if (isset($this->BlockTypes[$marker]))
|
194 |
-
{
|
195 |
-
foreach ($this->BlockTypes[$marker] as $blockType)
|
196 |
-
{
|
197 |
-
$blockTypes []= $blockType;
|
198 |
-
}
|
199 |
-
}
|
200 |
-
|
201 |
-
#
|
202 |
-
# ~
|
203 |
-
|
204 |
-
foreach ($blockTypes as $blockType)
|
205 |
-
{
|
206 |
-
$Block = $this->{'block'.$blockType}($Line, $CurrentBlock);
|
207 |
-
|
208 |
-
if (isset($Block))
|
209 |
-
{
|
210 |
-
$Block['type'] = $blockType;
|
211 |
-
|
212 |
-
if ( ! isset($Block['identified']))
|
213 |
-
{
|
214 |
-
$Blocks []= $CurrentBlock;
|
215 |
-
|
216 |
-
$Block['identified'] = true;
|
217 |
-
}
|
218 |
-
|
219 |
-
if (method_exists($this, 'block'.$blockType.'Continue'))
|
220 |
-
{
|
221 |
-
$Block['continuable'] = true;
|
222 |
-
}
|
223 |
-
|
224 |
-
$CurrentBlock = $Block;
|
225 |
-
|
226 |
-
continue 2;
|
227 |
-
}
|
228 |
-
}
|
229 |
-
|
230 |
-
# ~
|
231 |
-
|
232 |
-
if (isset($CurrentBlock) and ! isset($CurrentBlock['type']) and ! isset($CurrentBlock['interrupted']))
|
233 |
-
{
|
234 |
-
$CurrentBlock['element']['text'] .= "\n".$text;
|
235 |
-
}
|
236 |
-
else
|
237 |
-
{
|
238 |
-
$Blocks []= $CurrentBlock;
|
239 |
-
|
240 |
-
$CurrentBlock = $this->paragraph($Line);
|
241 |
-
|
242 |
-
$CurrentBlock['identified'] = true;
|
243 |
-
}
|
244 |
-
}
|
245 |
-
|
246 |
-
# ~
|
247 |
-
|
248 |
-
if (isset($CurrentBlock['continuable']) and method_exists($this, 'block'.$CurrentBlock['type'].'Complete'))
|
249 |
-
{
|
250 |
-
$CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
|
251 |
-
}
|
252 |
-
|
253 |
-
# ~
|
254 |
-
|
255 |
-
$Blocks []= $CurrentBlock;
|
256 |
-
|
257 |
-
unset($Blocks[0]);
|
258 |
-
|
259 |
-
# ~
|
260 |
-
|
261 |
-
$markup = '';
|
262 |
-
|
263 |
-
foreach ($Blocks as $Block)
|
264 |
-
{
|
265 |
-
if (isset($Block['hidden']))
|
266 |
-
{
|
267 |
-
continue;
|
268 |
-
}
|
269 |
-
|
270 |
-
$markup .= "\n";
|
271 |
-
$markup .= isset($Block['markup']) ? $Block['markup'] : $this->element($Block['element']);
|
272 |
-
}
|
273 |
-
|
274 |
-
$markup .= "\n";
|
275 |
-
|
276 |
-
# ~
|
277 |
-
|
278 |
-
return $markup;
|
279 |
-
}
|
280 |
-
|
281 |
-
#
|
282 |
-
# Code
|
283 |
-
|
284 |
-
protected function blockCode($Line, $Block = null)
|
285 |
-
{
|
286 |
-
if (isset($Block) and ! isset($Block['type']) and ! isset($Block['interrupted']))
|
287 |
-
{
|
288 |
-
return;
|
289 |
-
}
|
290 |
-
|
291 |
-
if ($Line['indent'] >= 4)
|
292 |
-
{
|
293 |
-
$text = substr($Line['body'], 4);
|
294 |
-
|
295 |
-
$Block = array(
|
296 |
-
'element' => array(
|
297 |
-
'name' => 'pre',
|
298 |
-
'handler' => 'element',
|
299 |
-
'text' => array(
|
300 |
-
'name' => 'code',
|
301 |
-
'text' => $text,
|
302 |
-
),
|
303 |
-
),
|
304 |
-
);
|
305 |
-
|
306 |
-
return $Block;
|
307 |
-
}
|
308 |
-
}
|
309 |
-
|
310 |
-
protected function blockCodeContinue($Line, $Block)
|
311 |
-
{
|
312 |
-
if ($Line['indent'] >= 4)
|
313 |
-
{
|
314 |
-
if (isset($Block['interrupted']))
|
315 |
-
{
|
316 |
-
$Block['element']['text']['text'] .= "\n";
|
317 |
-
|
318 |
-
unset($Block['interrupted']);
|
319 |
-
}
|
320 |
-
|
321 |
-
$Block['element']['text']['text'] .= "\n";
|
322 |
-
|
323 |
-
$text = substr($Line['body'], 4);
|
324 |
-
|
325 |
-
$Block['element']['text']['text'] .= $text;
|
326 |
-
|
327 |
-
return $Block;
|
328 |
-
}
|
329 |
-
}
|
330 |
-
|
331 |
-
protected function blockCodeComplete($Block)
|
332 |
-
{
|
333 |
-
$text = $Block['element']['text']['text'];
|
334 |
-
|
335 |
-
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
|
336 |
-
|
337 |
-
$Block['element']['text']['text'] = $text;
|
338 |
-
|
339 |
-
return $Block;
|
340 |
-
}
|
341 |
-
|
342 |
-
#
|
343 |
-
# Comment
|
344 |
-
|
345 |
-
protected function blockComment($Line)
|
346 |
-
{
|
347 |
-
if ($this->markupEscaped)
|
348 |
-
{
|
349 |
-
return;
|
350 |
-
}
|
351 |
-
|
352 |
-
if (isset($Line['text'][3]) and $Line['text'][3] === '-' and $Line['text'][2] === '-' and $Line['text'][1] === '!')
|
353 |
-
{
|
354 |
-
$Block = array(
|
355 |
-
'markup' => $Line['body'],
|
356 |
-
);
|
357 |
-
|
358 |
-
if (preg_match('/-->$/', $Line['text']))
|
359 |
-
{
|
360 |
-
$Block['closed'] = true;
|
361 |
-
}
|
362 |
-
|
363 |
-
return $Block;
|
364 |
-
}
|
365 |
-
}
|
366 |
-
|
367 |
-
protected function blockCommentContinue($Line, array $Block)
|
368 |
-
{
|
369 |
-
if (isset($Block['closed']))
|
370 |
-
{
|
371 |
-
return;
|
372 |
-
}
|
373 |
-
|
374 |
-
$Block['markup'] .= "\n" . $Line['body'];
|
375 |
-
|
376 |
-
if (preg_match('/-->$/', $Line['text']))
|
377 |
-
{
|
378 |
-
$Block['closed'] = true;
|
379 |
-
}
|
380 |
-
|
381 |
-
return $Block;
|
382 |
-
}
|
383 |
-
|
384 |
-
#
|
385 |
-
# Fenced Code
|
386 |
-
|
387 |
-
protected function blockFencedCode($Line)
|
388 |
-
{
|
389 |
-
if (preg_match('/^['.$Line['text'][0].']{3,}[ ]*([\w-]+)?[ ]*$/', $Line['text'], $matches))
|
390 |
-
{
|
391 |
-
$Element = array(
|
392 |
-
'name' => 'code',
|
393 |
-
'text' => '',
|
394 |
-
);
|
395 |
-
|
396 |
-
if (isset($matches[1]))
|
397 |
-
{
|
398 |
-
$class = 'language-'.$matches[1];
|
399 |
-
|
400 |
-
$Element['attributes'] = array(
|
401 |
-
'class' => $class,
|
402 |
-
);
|
403 |
-
}
|
404 |
-
|
405 |
-
$Block = array(
|
406 |
-
'char' => $Line['text'][0],
|
407 |
-
'element' => array(
|
408 |
-
'name' => 'pre',
|
409 |
-
'handler' => 'element',
|
410 |
-
'text' => $Element,
|
411 |
-
),
|
412 |
-
);
|
413 |
-
|
414 |
-
return $Block;
|
415 |
-
}
|
416 |
-
}
|
417 |
-
|
418 |
-
protected function blockFencedCodeContinue($Line, $Block)
|
419 |
-
{
|
420 |
-
if (isset($Block['complete']))
|
421 |
-
{
|
422 |
-
return;
|
423 |
-
}
|
424 |
-
|
425 |
-
if (isset($Block['interrupted']))
|
426 |
-
{
|
427 |
-
$Block['element']['text']['text'] .= "\n";
|
428 |
-
|
429 |
-
unset($Block['interrupted']);
|
430 |
-
}
|
431 |
-
|
432 |
-
if (preg_match('/^'.$Block['char'].'{3,}[ ]*$/', $Line['text']))
|
433 |
-
{
|
434 |
-
$Block['element']['text']['text'] = substr($Block['element']['text']['text'], 1);
|
435 |
-
|
436 |
-
$Block['complete'] = true;
|
437 |
-
|
438 |
-
return $Block;
|
439 |
-
}
|
440 |
-
|
441 |
-
$Block['element']['text']['text'] .= "\n".$Line['body'];;
|
442 |
-
|
443 |
-
return $Block;
|
444 |
-
}
|
445 |
-
|
446 |
-
protected function blockFencedCodeComplete($Block)
|
447 |
-
{
|
448 |
-
$text = $Block['element']['text']['text'];
|
449 |
-
|
450 |
-
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
|
451 |
-
|
452 |
-
$Block['element']['text']['text'] = $text;
|
453 |
-
|
454 |
-
return $Block;
|
455 |
-
}
|
456 |
-
|
457 |
-
#
|
458 |
-
# Header
|
459 |
-
|
460 |
-
protected function blockHeader($Line)
|
461 |
-
{
|
462 |
-
if (isset($Line['text'][1]))
|
463 |
-
{
|
464 |
-
$level = 1;
|
465 |
-
|
466 |
-
while (isset($Line['text'][$level]) and $Line['text'][$level] === '#')
|
467 |
-
{
|
468 |
-
$level ++;
|
469 |
-
}
|
470 |
-
|
471 |
-
if ($level > 6)
|
472 |
-
{
|
473 |
-
return;
|
474 |
-
}
|
475 |
-
|
476 |
-
$text = trim($Line['text'], '# ');
|
477 |
-
|
478 |
-
$Block = array(
|
479 |
-
'element' => array(
|
480 |
-
'name' => 'h' . min(6, $level),
|
481 |
-
'text' => $text,
|
482 |
-
'handler' => 'line',
|
483 |
-
),
|
484 |
-
);
|
485 |
-
|
486 |
-
return $Block;
|
487 |
-
}
|
488 |
-
}
|
489 |
-
|
490 |
-
#
|
491 |
-
# List
|
492 |
-
|
493 |
-
protected function blockList($Line)
|
494 |
-
{
|
495 |
-
list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]+[.]');
|
496 |
-
|
497 |
-
if (preg_match('/^('.$pattern.'[ ]+)(.*)/', $Line['text'], $matches))
|
498 |
-
{
|
499 |
-
$Block = array(
|
500 |
-
'indent' => $Line['indent'],
|
501 |
-
'pattern' => $pattern,
|
502 |
-
'element' => array(
|
503 |
-
'name' => $name,
|
504 |
-
'handler' => 'elements',
|
505 |
-
),
|
506 |
-
);
|
507 |
-
|
508 |
-
$Block['li'] = array(
|
509 |
-
'name' => 'li',
|
510 |
-
'handler' => 'li',
|
511 |
-
'text' => array(
|
512 |
-
$matches[2],
|
513 |
-
),
|
514 |
-
);
|
515 |
-
|
516 |
-
$Block['element']['text'] []= & $Block['li'];
|
517 |
-
|
518 |
-
return $Block;
|
519 |
-
}
|
520 |
-
}
|
521 |
-
|
522 |
-
protected function blockListContinue($Line, array $Block)
|
523 |
-
{
|
524 |
-
if ($Block['indent'] === $Line['indent'] and preg_match('/^'.$Block['pattern'].'(?:[ ]+(.*)|$)/', $Line['text'], $matches))
|
525 |
-
{
|
526 |
-
if (isset($Block['interrupted']))
|
527 |
-
{
|
528 |
-
$Block['li']['text'] []= '';
|
529 |
-
|
530 |
-
unset($Block['interrupted']);
|
531 |
-
}
|
532 |
-
|
533 |
-
unset($Block['li']);
|
534 |
-
|
535 |
-
$text = isset($matches[1]) ? $matches[1] : '';
|
536 |
-
|
537 |
-
$Block['li'] = array(
|
538 |
-
'name' => 'li',
|
539 |
-
'handler' => 'li',
|
540 |
-
'text' => array(
|
541 |
-
$text,
|
542 |
-
),
|
543 |
-
);
|
544 |
-
|
545 |
-
$Block['element']['text'] []= & $Block['li'];
|
546 |
-
|
547 |
-
return $Block;
|
548 |
-
}
|
549 |
-
|
550 |
-
if ($Line['text'][0] === '[' and $this->blockReference($Line))
|
551 |
-
{
|
552 |
-
return $Block;
|
553 |
-
}
|
554 |
-
|
555 |
-
if ( ! isset($Block['interrupted']))
|
556 |
-
{
|
557 |
-
$text = preg_replace('/^[ ]{0,4}/', '', $Line['body']);
|
558 |
-
|
559 |
-
$Block['li']['text'] []= $text;
|
560 |
-
|
561 |
-
return $Block;
|
562 |
-
}
|
563 |
-
|
564 |
-
if ($Line['indent'] > 0)
|
565 |
-
{
|
566 |
-
$Block['li']['text'] []= '';
|
567 |
-
|
568 |
-
$text = preg_replace('/^[ ]{0,4}/', '', $Line['body']);
|
569 |
-
|
570 |
-
$Block['li']['text'] []= $text;
|
571 |
-
|
572 |
-
unset($Block['interrupted']);
|
573 |
-
|
574 |
-
return $Block;
|
575 |
-
}
|
576 |
-
}
|
577 |
-
|
578 |
-
#
|
579 |
-
# Quote
|
580 |
-
|
581 |
-
protected function blockQuote($Line)
|
582 |
-
{
|
583 |
-
if (preg_match('/^>[ ]?(.*)/', $Line['text'], $matches))
|
584 |
-
{
|
585 |
-
$Block = array(
|
586 |
-
'element' => array(
|
587 |
-
'name' => 'blockquote',
|
588 |
-
'handler' => 'lines',
|
589 |
-
'text' => (array) $matches[1],
|
590 |
-
),
|
591 |
-
);
|
592 |
-
|
593 |
-
return $Block;
|
594 |
-
}
|
595 |
-
}
|
596 |
-
|
597 |
-
protected function blockQuoteContinue($Line, array $Block)
|
598 |
-
{
|
599 |
-
if ($Line['text'][0] === '>' and preg_match('/^>[ ]?(.*)/', $Line['text'], $matches))
|
600 |
-
{
|
601 |
-
if (isset($Block['interrupted']))
|
602 |
-
{
|
603 |
-
$Block['element']['text'] []= '';
|
604 |
-
|
605 |
-
unset($Block['interrupted']);
|
606 |
-
}
|
607 |
-
|
608 |
-
$Block['element']['text'] []= $matches[1];
|
609 |
-
|
610 |
-
return $Block;
|
611 |
-
}
|
612 |
-
|
613 |
-
if ( ! isset($Block['interrupted']))
|
614 |
-
{
|
615 |
-
$Block['element']['text'] []= $Line['text'];
|
616 |
-
|
617 |
-
return $Block;
|
618 |
-
}
|
619 |
-
}
|
620 |
-
|
621 |
-
#
|
622 |
-
# Rule
|
623 |
-
|
624 |
-
protected function blockRule($Line)
|
625 |
-
{
|
626 |
-
if (preg_match('/^(['.$Line['text'][0].'])([ ]*\1){2,}[ ]*$/', $Line['text']))
|
627 |
-
{
|
628 |
-
$Block = array(
|
629 |
-
'element' => array(
|
630 |
-
'name' => 'hr'
|
631 |
-
),
|
632 |
-
);
|
633 |
-
|
634 |
-
return $Block;
|
635 |
-
}
|
636 |
-
}
|
637 |
-
|
638 |
-
#
|
639 |
-
# Setext
|
640 |
-
|
641 |
-
protected function blockSetextHeader($Line, array $Block = null)
|
642 |
-
{
|
643 |
-
if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
|
644 |
-
{
|
645 |
-
return;
|
646 |
-
}
|
647 |
-
|
648 |
-
if (chop($Line['text'], $Line['text'][0]) === '')
|
649 |
-
{
|
650 |
-
$Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2';
|
651 |
-
|
652 |
-
return $Block;
|
653 |
-
}
|
654 |
-
}
|
655 |
-
|
656 |
-
#
|
657 |
-
# Markup
|
658 |
-
|
659 |
-
protected function blockMarkup($Line)
|
660 |
-
{
|
661 |
-
if ($this->markupEscaped)
|
662 |
-
{
|
663 |
-
return;
|
664 |
-
}
|
665 |
-
|
666 |
-
if (preg_match('/^<(\w*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches))
|
667 |
-
{
|
668 |
-
$element = strtolower($matches[1]);
|
669 |
-
|
670 |
-
if (in_array($element, $this->textLevelElements))
|
671 |
-
{
|
672 |
-
return;
|
673 |
-
}
|
674 |
-
|
675 |
-
$Block = array(
|
676 |
-
'name' => $matches[1],
|
677 |
-
'depth' => 0,
|
678 |
-
'markup' => $Line['text'],
|
679 |
-
);
|
680 |
-
|
681 |
-
$length = strlen($matches[0]);
|
682 |
-
|
683 |
-
$remainder = substr($Line['text'], $length);
|
684 |
-
|
685 |
-
if (trim($remainder) === '')
|
686 |
-
{
|
687 |
-
if (isset($matches[2]) or in_array($matches[1], $this->voidElements))
|
688 |
-
{
|
689 |
-
$Block['closed'] = true;
|
690 |
-
|
691 |
-
$Block['void'] = true;
|
692 |
-
}
|
693 |
-
}
|
694 |
-
else
|
695 |
-
{
|
696 |
-
if (isset($matches[2]) or in_array($matches[1], $this->voidElements))
|
697 |
-
{
|
698 |
-
return;
|
699 |
-
}
|
700 |
-
|
701 |
-
if (preg_match('/<\/'.$matches[1].'>[ ]*$/i', $remainder))
|
702 |
-
{
|
703 |
-
$Block['closed'] = true;
|
704 |
-
}
|
705 |
-
}
|
706 |
-
|
707 |
-
return $Block;
|
708 |
-
}
|
709 |
-
}
|
710 |
-
|
711 |
-
protected function blockMarkupContinue($Line, array $Block)
|
712 |
-
{
|
713 |
-
if (isset($Block['closed']))
|
714 |
-
{
|
715 |
-
return;
|
716 |
-
}
|
717 |
-
|
718 |
-
if (preg_match('/^<'.$Block['name'].'(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*>/i', $Line['text'])) # open
|
719 |
-
{
|
720 |
-
$Block['depth'] ++;
|
721 |
-
}
|
722 |
-
|
723 |
-
if (preg_match('/(.*?)<\/'.$Block['name'].'>[ ]*$/i', $Line['text'], $matches)) # close
|
724 |
-
{
|
725 |
-
if ($Block['depth'] > 0)
|
726 |
-
{
|
727 |
-
$Block['depth'] --;
|
728 |
-
}
|
729 |
-
else
|
730 |
-
{
|
731 |
-
$Block['closed'] = true;
|
732 |
-
}
|
733 |
-
}
|
734 |
-
|
735 |
-
if (isset($Block['interrupted']))
|
736 |
-
{
|
737 |
-
$Block['markup'] .= "\n";
|
738 |
-
|
739 |
-
unset($Block['interrupted']);
|
740 |
-
}
|
741 |
-
|
742 |
-
$Block['markup'] .= "\n".$Line['body'];
|
743 |
-
|
744 |
-
return $Block;
|
745 |
-
}
|
746 |
-
|
747 |
-
#
|
748 |
-
# Reference
|
749 |
-
|
750 |
-
protected function blockReference($Line)
|
751 |
-
{
|
752 |
-
if (preg_match('/^\[(.+?)\]:[ ]*<?(\S+?)>?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $Line['text'], $matches))
|
753 |
-
{
|
754 |
-
$id = strtolower($matches[1]);
|
755 |
-
|
756 |
-
$Data = array(
|
757 |
-
'url' => $matches[2],
|
758 |
-
'title' => null,
|
759 |
-
);
|
760 |
-
|
761 |
-
if (isset($matches[3]))
|
762 |
-
{
|
763 |
-
$Data['title'] = $matches[3];
|
764 |
-
}
|
765 |
-
|
766 |
-
$this->DefinitionData['Reference'][$id] = $Data;
|
767 |
-
|
768 |
-
$Block = array(
|
769 |
-
'hidden' => true,
|
770 |
-
);
|
771 |
-
|
772 |
-
return $Block;
|
773 |
-
}
|
774 |
-
}
|
775 |
-
|
776 |
-
#
|
777 |
-
# Table
|
778 |
-
|
779 |
-
protected function blockTable($Line, array $Block = null)
|
780 |
-
{
|
781 |
-
if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
|
782 |
-
{
|
783 |
-
return;
|
784 |
-
}
|
785 |
-
|
786 |
-
if (strpos($Block['element']['text'], '|') !== false and chop($Line['text'], ' -:|') === '')
|
787 |
-
{
|
788 |
-
$alignments = array();
|
789 |
-
|
790 |
-
$divider = $Line['text'];
|
791 |
-
|
792 |
-
$divider = trim($divider);
|
793 |
-
$divider = trim($divider, '|');
|
794 |
-
|
795 |
-
$dividerCells = explode('|', $divider);
|
796 |
-
|
797 |
-
foreach ($dividerCells as $dividerCell)
|
798 |
-
{
|
799 |
-
$dividerCell = trim($dividerCell);
|
800 |
-
|
801 |
-
if ($dividerCell === '')
|
802 |
-
{
|
803 |
-
continue;
|
804 |
-
}
|
805 |
-
|
806 |
-
$alignment = null;
|
807 |
-
|
808 |
-
if ($dividerCell[0] === ':')
|
809 |
-
{
|
810 |
-
$alignment = 'left';
|
811 |
-
}
|
812 |
-
|
813 |
-
if (substr($dividerCell, - 1) === ':')
|
814 |
-
{
|
815 |
-
$alignment = $alignment === 'left' ? 'center' : 'right';
|
816 |
-
}
|
817 |
-
|
818 |
-
$alignments []= $alignment;
|
819 |
-
}
|
820 |
-
|
821 |
-
# ~
|
822 |
-
|
823 |
-
$HeaderElements = array();
|
824 |
-
|
825 |
-
$header = $Block['element']['text'];
|
826 |
-
|
827 |
-
$header = trim($header);
|
828 |
-
$header = trim($header, '|');
|
829 |
-
|
830 |
-
$headerCells = explode('|', $header);
|
831 |
-
|
832 |
-
foreach ($headerCells as $index => $headerCell)
|
833 |
-
{
|
834 |
-
$headerCell = trim($headerCell);
|
835 |
-
|
836 |
-
$HeaderElement = array(
|
837 |
-
'name' => 'th',
|
838 |
-
'text' => $headerCell,
|
839 |
-
'handler' => 'line',
|
840 |
-
);
|
841 |
-
|
842 |
-
if (isset($alignments[$index]))
|
843 |
-
{
|
844 |
-
$alignment = $alignments[$index];
|
845 |
-
|
846 |
-
$HeaderElement['attributes'] = array(
|
847 |
-
'style' => 'text-align: '.$alignment.';',
|
848 |
-
);
|
849 |
-
}
|
850 |
-
|
851 |
-
$HeaderElements []= $HeaderElement;
|
852 |
-
}
|
853 |
-
|
854 |
-
# ~
|
855 |
-
|
856 |
-
$Block = array(
|
857 |
-
'alignments' => $alignments,
|
858 |
-
'identified' => true,
|
859 |
-
'element' => array(
|
860 |
-
'name' => 'table',
|
861 |
-
'handler' => 'elements',
|
862 |
-
),
|
863 |
-
);
|
864 |
-
|
865 |
-
$Block['element']['text'] []= array(
|
866 |
-
'name' => 'thead',
|
867 |
-
'handler' => 'elements',
|
868 |
-
);
|
869 |
-
|
870 |
-
$Block['element']['text'] []= array(
|
871 |
-
'name' => 'tbody',
|
872 |
-
'handler' => 'elements',
|
873 |
-
'text' => array(),
|
874 |
-
);
|
875 |
-
|
876 |
-
$Block['element']['text'][0]['text'] []= array(
|
877 |
-
'name' => 'tr',
|
878 |
-
'handler' => 'elements',
|
879 |
-
'text' => $HeaderElements,
|
880 |
-
);
|
881 |
-
|
882 |
-
return $Block;
|
883 |
-
}
|
884 |
-
}
|
885 |
-
|
886 |
-
protected function blockTableContinue($Line, array $Block)
|
887 |
-
{
|
888 |
-
if (isset($Block['interrupted']))
|
889 |
-
{
|
890 |
-
return;
|
891 |
-
}
|
892 |
-
|
893 |
-
if ($Line['text'][0] === '|' or strpos($Line['text'], '|'))
|
894 |
-
{
|
895 |
-
$Elements = array();
|
896 |
-
|
897 |
-
$row = $Line['text'];
|
898 |
-
|
899 |
-
$row = trim($row);
|
900 |
-
$row = trim($row, '|');
|
901 |
-
|
902 |
-
preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]+`|`)+/', $row, $matches);
|
903 |
-
|
904 |
-
foreach ($matches[0] as $index => $cell)
|
905 |
-
{
|
906 |
-
$cell = trim($cell);
|
907 |
-
|
908 |
-
$Element = array(
|
909 |
-
'name' => 'td',
|
910 |
-
'handler' => 'line',
|
911 |
-
'text' => $cell,
|
912 |
-
);
|
913 |
-
|
914 |
-
if (isset($Block['alignments'][$index]))
|
915 |
-
{
|
916 |
-
$Element['attributes'] = array(
|
917 |
-
'style' => 'text-align: '.$Block['alignments'][$index].';',
|
918 |
-
);
|
919 |
-
}
|
920 |
-
|
921 |
-
$Elements []= $Element;
|
922 |
-
}
|
923 |
-
|
924 |
-
$Element = array(
|
925 |
-
'name' => 'tr',
|
926 |
-
'handler' => 'elements',
|
927 |
-
'text' => $Elements,
|
928 |
-
);
|
929 |
-
|
930 |
-
$Block['element']['text'][1]['text'] []= $Element;
|
931 |
-
|
932 |
-
return $Block;
|
933 |
-
}
|
934 |
-
}
|
935 |
-
|
936 |
-
#
|
937 |
-
# ~
|
938 |
-
#
|
939 |
-
|
940 |
-
protected function paragraph($Line)
|
941 |
-
{
|
942 |
-
$Block = array(
|
943 |
-
'element' => array(
|
944 |
-
'name' => 'p',
|
945 |
-
'text' => $Line['text'],
|
946 |
-
'handler' => 'line',
|
947 |
-
),
|
948 |
-
);
|
949 |
-
|
950 |
-
return $Block;
|
951 |
-
}
|
952 |
-
|
953 |
-
#
|
954 |
-
# Inline Elements
|
955 |
-
#
|
956 |
-
|
957 |
-
protected $InlineTypes = array(
|
958 |
-
'"' => array('SpecialCharacter'),
|
959 |
-
'!' => array('Image'),
|
960 |
-
'&' => array('SpecialCharacter'),
|
961 |
-
'*' => array('Emphasis'),
|
962 |
-
':' => array('Url'),
|
963 |
-
'<' => array('UrlTag', 'EmailTag', 'Markup', 'SpecialCharacter'),
|
964 |
-
'>' => array('SpecialCharacter'),
|
965 |
-
'[' => array('Link'),
|
966 |
-
'_' => array('Emphasis'),
|
967 |
-
'`' => array('Code'),
|
968 |
-
'~' => array('Strikethrough'),
|
969 |
-
'\\' => array('EscapeSequence'),
|
970 |
-
);
|
971 |
-
|
972 |
-
# ~
|
973 |
-
|
974 |
-
protected $inlineMarkerList = '!"*_&[:<>`~\\';
|
975 |
-
|
976 |
-
#
|
977 |
-
# ~
|
978 |
-
#
|
979 |
-
|
980 |
-
public function line($text)
|
981 |
-
{
|
982 |
-
$markup = '';
|
983 |
-
|
984 |
-
# $excerpt is based on the first occurrence of a marker
|
985 |
-
|
986 |
-
while ($excerpt = strpbrk($text, $this->inlineMarkerList))
|
987 |
-
{
|
988 |
-
$marker = $excerpt[0];
|
989 |
-
|
990 |
-
$markerPosition = strpos($text, $marker);
|
991 |
-
|
992 |
-
$Excerpt = array('text' => $excerpt, 'context' => $text);
|
993 |
-
|
994 |
-
foreach ($this->InlineTypes[$marker] as $inlineType)
|
995 |
-
{
|
996 |
-
$Inline = $this->{'inline'.$inlineType}($Excerpt);
|
997 |
-
|
998 |
-
if ( ! isset($Inline))
|
999 |
-
{
|
1000 |
-
continue;
|
1001 |
-
}
|
1002 |
-
|
1003 |
-
# makes sure that the inline belongs to "our" marker
|
1004 |
-
|
1005 |
-
if (isset($Inline['position']) and $Inline['position'] > $markerPosition)
|
1006 |
-
{
|
1007 |
-
continue;
|
1008 |
-
}
|
1009 |
-
|
1010 |
-
# sets a default inline position
|
1011 |
-
|
1012 |
-
if ( ! isset($Inline['position']))
|
1013 |
-
{
|
1014 |
-
$Inline['position'] = $markerPosition;
|
1015 |
-
}
|
1016 |
-
|
1017 |
-
# the text that comes before the inline
|
1018 |
-
$unmarkedText = substr($text, 0, $Inline['position']);
|
1019 |
-
|
1020 |
-
# compile the unmarked text
|
1021 |
-
$markup .= $this->unmarkedText($unmarkedText);
|
1022 |
-
|
1023 |
-
# compile the inline
|
1024 |
-
$markup .= isset($Inline['markup']) ? $Inline['markup'] : $this->element($Inline['element']);
|
1025 |
-
|
1026 |
-
# remove the examined text
|
1027 |
-
$text = substr($text, $Inline['position'] + $Inline['extent']);
|
1028 |
-
|
1029 |
-
continue 2;
|
1030 |
-
}
|
1031 |
-
|
1032 |
-
# the marker does not belong to an inline
|
1033 |
-
|
1034 |
-
$unmarkedText = substr($text, 0, $markerPosition + 1);
|
1035 |
-
|
1036 |
-
$markup .= $this->unmarkedText($unmarkedText);
|
1037 |
-
|
1038 |
-
$text = substr($text, $markerPosition + 1);
|
1039 |
-
}
|
1040 |
-
|
1041 |
-
$markup .= $this->unmarkedText($text);
|
1042 |
-
|
1043 |
-
return $markup;
|
1044 |
-
}
|
1045 |
-
|
1046 |
-
#
|
1047 |
-
# ~
|
1048 |
-
#
|
1049 |
-
|
1050 |
-
protected function inlineCode($Excerpt)
|
1051 |
-
{
|
1052 |
-
$marker = $Excerpt['text'][0];
|
1053 |
-
|
1054 |
-
if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(?<!'.$marker.')\1(?!'.$marker.')/s', $Excerpt['text'], $matches))
|
1055 |
-
{
|
1056 |
-
$text = $matches[2];
|
1057 |
-
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
|
1058 |
-
$text = preg_replace("/[ ]*\n/", ' ', $text);
|
1059 |
-
|
1060 |
-
return array(
|
1061 |
-
'extent' => strlen($matches[0]),
|
1062 |
-
'element' => array(
|
1063 |
-
'name' => 'code',
|
1064 |
-
'text' => $text,
|
1065 |
-
),
|
1066 |
-
);
|
1067 |
-
}
|
1068 |
-
}
|
1069 |
-
|
1070 |
-
protected function inlineEmailTag($Excerpt)
|
1071 |
-
{
|
1072 |
-
if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<((mailto:)?\S+?@\S+?)>/i', $Excerpt['text'], $matches))
|
1073 |
-
{
|
1074 |
-
$url = $matches[1];
|
1075 |
-
|
1076 |
-
if ( ! isset($matches[2]))
|
1077 |
-
{
|
1078 |
-
$url = 'mailto:' . $url;
|
1079 |
-
}
|
1080 |
-
|
1081 |
-
return array(
|
1082 |
-
'extent' => strlen($matches[0]),
|
1083 |
-
'element' => array(
|
1084 |
-
'name' => 'a',
|
1085 |
-
'text' => $matches[1],
|
1086 |
-
'attributes' => array(
|
1087 |
-
'href' => $url,
|
1088 |
-
),
|
1089 |
-
),
|
1090 |
-
);
|
1091 |
-
}
|
1092 |
-
}
|
1093 |
-
|
1094 |
-
protected function inlineEmphasis($Excerpt)
|
1095 |
-
{
|
1096 |
-
if ( ! isset($Excerpt['text'][1]))
|
1097 |
-
{
|
1098 |
-
return;
|
1099 |
-
}
|
1100 |
-
|
1101 |
-
$marker = $Excerpt['text'][0];
|
1102 |
-
|
1103 |
-
if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches))
|
1104 |
-
{
|
1105 |
-
$emphasis = 'strong';
|
1106 |
-
}
|
1107 |
-
elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches))
|
1108 |
-
{
|
1109 |
-
$emphasis = 'em';
|
1110 |
-
}
|
1111 |
-
else
|
1112 |
-
{
|
1113 |
-
return;
|
1114 |
-
}
|
1115 |
-
|
1116 |
-
return array(
|
1117 |
-
'extent' => strlen($matches[0]),
|
1118 |
-
'element' => array(
|
1119 |
-
'name' => $emphasis,
|
1120 |
-
'handler' => 'line',
|
1121 |
-
'text' => $matches[1],
|
1122 |
-
),
|
1123 |
-
);
|
1124 |
-
}
|
1125 |
-
|
1126 |
-
protected function inlineEscapeSequence($Excerpt)
|
1127 |
-
{
|
1128 |
-
if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters))
|
1129 |
-
{
|
1130 |
-
return array(
|
1131 |
-
'markup' => $Excerpt['text'][1],
|
1132 |
-
'extent' => 2,
|
1133 |
-
);
|
1134 |
-
}
|
1135 |
-
}
|
1136 |
-
|
1137 |
-
protected function inlineImage($Excerpt)
|
1138 |
-
{
|
1139 |
-
if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '[')
|
1140 |
-
{
|
1141 |
-
return;
|
1142 |
-
}
|
1143 |
-
|
1144 |
-
$Excerpt['text']= substr($Excerpt['text'], 1);
|
1145 |
-
|
1146 |
-
$Link = $this->inlineLink($Excerpt);
|
1147 |
-
|
1148 |
-
if ($Link === null)
|
1149 |
-
{
|
1150 |
-
return;
|
1151 |
-
}
|
1152 |
-
|
1153 |
-
$Inline = array(
|
1154 |
-
'extent' => $Link['extent'] + 1,
|
1155 |
-
'element' => array(
|
1156 |
-
'name' => 'img',
|
1157 |
-
'attributes' => array(
|
1158 |
-
'src' => $Link['element']['attributes']['href'],
|
1159 |
-
'alt' => $Link['element']['text'],
|
1160 |
-
),
|
1161 |
-
),
|
1162 |
-
);
|
1163 |
-
|
1164 |
-
$Inline['element']['attributes'] += $Link['element']['attributes'];
|
1165 |
-
|
1166 |
-
unset($Inline['element']['attributes']['href']);
|
1167 |
-
|
1168 |
-
return $Inline;
|
1169 |
-
}
|
1170 |
-
|
1171 |
-
protected function inlineLink($Excerpt)
|
1172 |
-
{
|
1173 |
-
$Element = array(
|
1174 |
-
'name' => 'a',
|
1175 |
-
'handler' => 'line',
|
1176 |
-
'text' => null,
|
1177 |
-
'attributes' => array(
|
1178 |
-
'href' => null,
|
1179 |
-
'title' => null,
|
1180 |
-
),
|
1181 |
-
);
|
1182 |
-
|
1183 |
-
$extent = 0;
|
1184 |
-
|
1185 |
-
$remainder = $Excerpt['text'];
|
1186 |
-
|
1187 |
-
if (preg_match('/\[((?:[^][]|(?R))*)\]/', $remainder, $matches))
|
1188 |
-
{
|
1189 |
-
$Element['text'] = $matches[1];
|
1190 |
-
|
1191 |
-
$extent += strlen($matches[0]);
|
1192 |
-
|
1193 |
-
$remainder = substr($remainder, $extent);
|
1194 |
-
}
|
1195 |
-
else
|
1196 |
-
{
|
1197 |
-
return;
|
1198 |
-
}
|
1199 |
-
|
1200 |
-
if (preg_match('/^[(]((?:[^ ()]|[(][^ )]+[)])+)(?:[ ]+("[^"]*"|\'[^\']*\'))?[)]/', $remainder, $matches))
|
1201 |
-
{
|
1202 |
-
$Element['attributes']['href'] = $matches[1];
|
1203 |
-
|
1204 |
-
if (isset($matches[2]))
|
1205 |
-
{
|
1206 |
-
$Element['attributes']['title'] = substr($matches[2], 1, - 1);
|
1207 |
-
}
|
1208 |
-
|
1209 |
-
$extent += strlen($matches[0]);
|
1210 |
-
}
|
1211 |
-
else
|
1212 |
-
{
|
1213 |
-
if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches))
|
1214 |
-
{
|
1215 |
-
$definition = strlen($matches[1]) ? $matches[1] : $Element['text'];
|
1216 |
-
$definition = strtolower($definition);
|
1217 |
-
|
1218 |
-
$extent += strlen($matches[0]);
|
1219 |
-
}
|
1220 |
-
else
|
1221 |
-
{
|
1222 |
-
$definition = strtolower($Element['text']);
|
1223 |
-
}
|
1224 |
-
|
1225 |
-
if ( ! isset($this->DefinitionData['Reference'][$definition]))
|
1226 |
-
{
|
1227 |
-
return;
|
1228 |
-
}
|
1229 |
-
|
1230 |
-
$Definition = $this->DefinitionData['Reference'][$definition];
|
1231 |
-
|
1232 |
-
$Element['attributes']['href'] = $Definition['url'];
|
1233 |
-
$Element['attributes']['title'] = $Definition['title'];
|
1234 |
-
}
|
1235 |
-
|
1236 |
-
$Element['attributes']['href'] = str_replace(array('&', '<'), array('&', '<'), $Element['attributes']['href']);
|
1237 |
-
|
1238 |
-
return array(
|
1239 |
-
'extent' => $extent,
|
1240 |
-
'element' => $Element,
|
1241 |
-
);
|
1242 |
-
}
|
1243 |
-
|
1244 |
-
protected function inlineMarkup($Excerpt)
|
1245 |
-
{
|
1246 |
-
if ($this->markupEscaped or strpos($Excerpt['text'], '>') === false)
|
1247 |
-
{
|
1248 |
-
return;
|
1249 |
-
}
|
1250 |
-
|
1251 |
-
if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w*[ ]*>/s', $Excerpt['text'], $matches))
|
1252 |
-
{
|
1253 |
-
return array(
|
1254 |
-
'markup' => $matches[0],
|
1255 |
-
'extent' => strlen($matches[0]),
|
1256 |
-
);
|
1257 |
-
}
|
1258 |
-
|
1259 |
-
if ($Excerpt['text'][1] === '!' and preg_match('/^<!---?[^>-](?:-?[^-])*-->/s', $Excerpt['text'], $matches))
|
1260 |
-
{
|
1261 |
-
return array(
|
1262 |
-
'markup' => $matches[0],
|
1263 |
-
'extent' => strlen($matches[0]),
|
1264 |
-
);
|
1265 |
-
}
|
1266 |
-
|
1267 |
-
if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w*(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*\/?>/s', $Excerpt['text'], $matches))
|
1268 |
-
{
|
1269 |
-
return array(
|
1270 |
-
'markup' => $matches[0],
|
1271 |
-
'extent' => strlen($matches[0]),
|
1272 |
-
);
|
1273 |
-
}
|
1274 |
-
}
|
1275 |
-
|
1276 |
-
protected function inlineSpecialCharacter($Excerpt)
|
1277 |
-
{
|
1278 |
-
if ($Excerpt['text'][0] === '&' and ! preg_match('/^&#?\w+;/', $Excerpt['text']))
|
1279 |
-
{
|
1280 |
-
return array(
|
1281 |
-
'markup' => '&',
|
1282 |
-
'extent' => 1,
|
1283 |
-
);
|
1284 |
-
}
|
1285 |
-
|
1286 |
-
$SpecialCharacter = array('>' => 'gt', '<' => 'lt', '"' => 'quot');
|
1287 |
-
|
1288 |
-
if (isset($SpecialCharacter[$Excerpt['text'][0]]))
|
1289 |
-
{
|
1290 |
-
return array(
|
1291 |
-
'markup' => '&'.$SpecialCharacter[$Excerpt['text'][0]].';',
|
1292 |
-
'extent' => 1,
|
1293 |
-
);
|
1294 |
-
}
|
1295 |
-
}
|
1296 |
-
|
1297 |
-
protected function inlineStrikethrough($Excerpt)
|
1298 |
-
{
|
1299 |
-
if ( ! isset($Excerpt['text'][1]))
|
1300 |
-
{
|
1301 |
-
return;
|
1302 |
-
}
|
1303 |
-
|
1304 |
-
if ($Excerpt['text'][1] === '~' and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches))
|
1305 |
-
{
|
1306 |
-
return array(
|
1307 |
-
'extent' => strlen($matches[0]),
|
1308 |
-
'element' => array(
|
1309 |
-
'name' => 'del',
|
1310 |
-
'text' => $matches[1],
|
1311 |
-
'handler' => 'line',
|
1312 |
-
),
|
1313 |
-
);
|
1314 |
-
}
|
1315 |
-
}
|
1316 |
-
|
1317 |
-
protected function inlineUrl($Excerpt)
|
1318 |
-
{
|
1319 |
-
if ($this->urlsLinked !== true or ! isset($Excerpt['text'][2]) or $Excerpt['text'][2] !== '/')
|
1320 |
-
{
|
1321 |
-
return;
|
1322 |
-
}
|
1323 |
-
|
1324 |
-
if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE))
|
1325 |
-
{
|
1326 |
-
$Inline = array(
|
1327 |
-
'extent' => strlen($matches[0][0]),
|
1328 |
-
'position' => $matches[0][1],
|
1329 |
-
'element' => array(
|
1330 |
-
'name' => 'a',
|
1331 |
-
'text' => $matches[0][0],
|
1332 |
-
'attributes' => array(
|
1333 |
-
'href' => $matches[0][0],
|
1334 |
-
),
|
1335 |
-
),
|
1336 |
-
);
|
1337 |
-
|
1338 |
-
return $Inline;
|
1339 |
-
}
|
1340 |
-
}
|
1341 |
-
|
1342 |
-
protected function inlineUrlTag($Excerpt)
|
1343 |
-
{
|
1344 |
-
if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w+:\/{2}[^ >]+)>/i', $Excerpt['text'], $matches))
|
1345 |
-
{
|
1346 |
-
$url = str_replace(array('&', '<'), array('&', '<'), $matches[1]);
|
1347 |
-
|
1348 |
-
return array(
|
1349 |
-
'extent' => strlen($matches[0]),
|
1350 |
-
'element' => array(
|
1351 |
-
'name' => 'a',
|
1352 |
-
'text' => $url,
|
1353 |
-
'attributes' => array(
|
1354 |
-
'href' => $url,
|
1355 |
-
),
|
1356 |
-
),
|
1357 |
-
);
|
1358 |
-
}
|
1359 |
-
}
|
1360 |
-
|
1361 |
-
# ~
|
1362 |
-
|
1363 |
-
protected function unmarkedText($text)
|
1364 |
-
{
|
1365 |
-
if ($this->breaksEnabled)
|
1366 |
-
{
|
1367 |
-
$text = preg_replace('/[ ]*\n/', "<br />\n", $text);
|
1368 |
-
}
|
1369 |
-
else
|
1370 |
-
{
|
1371 |
-
$text = preg_replace('/(?:[ ][ ]+|[ ]*\\\\)\n/', "<br />\n", $text);
|
1372 |
-
$text = str_replace(" \n", "\n", $text);
|
1373 |
-
}
|
1374 |
-
|
1375 |
-
return $text;
|
1376 |
-
}
|
1377 |
-
|
1378 |
-
#
|
1379 |
-
# Handlers
|
1380 |
-
#
|
1381 |
-
|
1382 |
-
protected function element(array $Element)
|
1383 |
-
{
|
1384 |
-
$markup = '<'.$Element['name'];
|
1385 |
-
|
1386 |
-
if (isset($Element['attributes']))
|
1387 |
-
{
|
1388 |
-
foreach ($Element['attributes'] as $name => $value)
|
1389 |
-
{
|
1390 |
-
if ($value === null)
|
1391 |
-
{
|
1392 |
-
continue;
|
1393 |
-
}
|
1394 |
-
|
1395 |
-
$markup .= ' '.$name.'="'.$value.'"';
|
1396 |
-
}
|
1397 |
-
}
|
1398 |
-
|
1399 |
-
if (isset($Element['text']))
|
1400 |
-
{
|
1401 |
-
$markup .= '>';
|
1402 |
-
|
1403 |
-
if (isset($Element['handler']))
|
1404 |
-
{
|
1405 |
-
$markup .= $this->{$Element['handler']}($Element['text']);
|
1406 |
-
}
|
1407 |
-
else
|
1408 |
-
{
|
1409 |
-
$markup .= $Element['text'];
|
1410 |
-
}
|
1411 |
-
|
1412 |
-
$markup .= '</'.$Element['name'].'>';
|
1413 |
-
}
|
1414 |
-
else
|
1415 |
-
{
|
1416 |
-
$markup .= ' />';
|
1417 |
-
}
|
1418 |
-
|
1419 |
-
return $markup;
|
1420 |
-
}
|
1421 |
-
|
1422 |
-
protected function elements(array $Elements)
|
1423 |
-
{
|
1424 |
-
$markup = '';
|
1425 |
-
|
1426 |
-
foreach ($Elements as $Element)
|
1427 |
-
{
|
1428 |
-
$markup .= "\n" . $this->element($Element);
|
1429 |
-
}
|
1430 |
-
|
1431 |
-
$markup .= "\n";
|
1432 |
-
|
1433 |
-
return $markup;
|
1434 |
-
}
|
1435 |
-
|
1436 |
-
# ~
|
1437 |
-
|
1438 |
-
protected function li($lines)
|
1439 |
-
{
|
1440 |
-
$markup = $this->lines($lines);
|
1441 |
-
|
1442 |
-
$trimmedMarkup = trim($markup);
|
1443 |
-
|
1444 |
-
if ( ! in_array('', $lines) and substr($trimmedMarkup, 0, 3) === '<p>')
|
1445 |
-
{
|
1446 |
-
$markup = $trimmedMarkup;
|
1447 |
-
$markup = substr($markup, 3);
|
1448 |
-
|
1449 |
-
$position = strpos($markup, "</p>");
|
1450 |
-
|
1451 |
-
$markup = substr_replace($markup, '', $position, 4);
|
1452 |
-
}
|
1453 |
-
|
1454 |
-
return $markup;
|
1455 |
-
}
|
1456 |
-
|
1457 |
-
#
|
1458 |
-
# Deprecated Methods
|
1459 |
-
#
|
1460 |
-
|
1461 |
-
function parse($text)
|
1462 |
-
{
|
1463 |
-
$markup = $this->text($text);
|
1464 |
-
|
1465 |
-
return $markup;
|
1466 |
-
}
|
1467 |
-
|
1468 |
-
#
|
1469 |
-
# Static Methods
|
1470 |
-
#
|
1471 |
-
|
1472 |
-
static function instance($name = 'default')
|
1473 |
-
{
|
1474 |
-
if (isset(self::$instances[$name]))
|
1475 |
-
{
|
1476 |
-
return self::$instances[$name];
|
1477 |
-
}
|
1478 |
-
|
1479 |
-
$instance = new static();
|
1480 |
-
|
1481 |
-
self::$instances[$name] = $instance;
|
1482 |
-
|
1483 |
-
return $instance;
|
1484 |
-
}
|
1485 |
-
|
1486 |
-
private static $instances = array();
|
1487 |
-
|
1488 |
-
#
|
1489 |
-
# Fields
|
1490 |
-
#
|
1491 |
-
|
1492 |
-
protected $DefinitionData;
|
1493 |
-
|
1494 |
-
#
|
1495 |
-
# Read-Only
|
1496 |
-
|
1497 |
-
protected $specialCharacters = array(
|
1498 |
-
'\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!', '|',
|
1499 |
-
);
|
1500 |
-
|
1501 |
-
protected $StrongRegex = array(
|
1502 |
-
'*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s',
|
1503 |
-
'_' => '/^__((?:\\\\_|[^_]|_[^_]*_)+?)__(?!_)/us',
|
1504 |
-
);
|
1505 |
-
|
1506 |
-
protected $EmRegex = array(
|
1507 |
-
'*' => '/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s',
|
1508 |
-
'_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us',
|
1509 |
-
);
|
1510 |
-
|
1511 |
-
protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*(?:\s*=\s*(?:[^"\'=<>`\s]+|"[^"]*"|\'[^\']*\'))?';
|
1512 |
-
|
1513 |
-
protected $voidElements = array(
|
1514 |
-
'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source',
|
1515 |
-
);
|
1516 |
-
|
1517 |
-
protected $textLevelElements = array(
|
1518 |
-
'a', 'br', 'bdo', 'abbr', 'blink', 'nextid', 'acronym', 'basefont',
|
1519 |
-
'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing',
|
1520 |
-
'i', 'rp', 'del', 'code', 'strike', 'marquee',
|
1521 |
-
'q', 'rt', 'ins', 'font', 'strong',
|
1522 |
-
's', 'tt', 'sub', 'mark',
|
1523 |
-
'u', 'xm', 'sup', 'nobr',
|
1524 |
-
'var', 'ruby',
|
1525 |
-
'wbr', 'span',
|
1526 |
-
'time',
|
1527 |
-
);
|
1528 |
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
#
|
4 |
+
#
|
5 |
+
# Parsedown
|
6 |
+
# http://parsedown.org
|
7 |
+
#
|
8 |
+
# (c) Emanuil Rusev
|
9 |
+
# http://erusev.com
|
10 |
+
#
|
11 |
+
# For the full license information, view the LICENSE file that was distributed
|
12 |
+
# with this source code.
|
13 |
+
#
|
14 |
+
#
|
15 |
+
|
16 |
+
class Parsedown
|
17 |
+
{
|
18 |
+
# ~
|
19 |
+
|
20 |
+
const version = '1.5.4';
|
21 |
+
|
22 |
+
# ~
|
23 |
+
|
24 |
+
function text($text)
|
25 |
+
{
|
26 |
+
# make sure no definitions are set
|
27 |
+
$this->DefinitionData = array();
|
28 |
+
|
29 |
+
# standardize line breaks
|
30 |
+
$text = str_replace(array("\r\n", "\r"), "\n", $text);
|
31 |
+
|
32 |
+
# remove surrounding line breaks
|
33 |
+
$text = trim($text, "\n");
|
34 |
+
|
35 |
+
# split text into lines
|
36 |
+
$lines = explode("\n", $text);
|
37 |
+
|
38 |
+
# iterate through lines to identify blocks
|
39 |
+
$markup = $this->lines($lines);
|
40 |
+
|
41 |
+
# trim line breaks
|
42 |
+
$markup = trim($markup, "\n");
|
43 |
+
|
44 |
+
return $markup;
|
45 |
+
}
|
46 |
+
|
47 |
+
#
|
48 |
+
# Setters
|
49 |
+
#
|
50 |
+
|
51 |
+
function setBreaksEnabled($breaksEnabled)
|
52 |
+
{
|
53 |
+
$this->breaksEnabled = $breaksEnabled;
|
54 |
+
|
55 |
+
return $this;
|
56 |
+
}
|
57 |
+
|
58 |
+
protected $breaksEnabled;
|
59 |
+
|
60 |
+
function setMarkupEscaped($markupEscaped)
|
61 |
+
{
|
62 |
+
$this->markupEscaped = $markupEscaped;
|
63 |
+
|
64 |
+
return $this;
|
65 |
+
}
|
66 |
+
|
67 |
+
protected $markupEscaped;
|
68 |
+
|
69 |
+
function setUrlsLinked($urlsLinked)
|
70 |
+
{
|
71 |
+
$this->urlsLinked = $urlsLinked;
|
72 |
+
|
73 |
+
return $this;
|
74 |
+
}
|
75 |
+
|
76 |
+
protected $urlsLinked = true;
|
77 |
+
|
78 |
+
#
|
79 |
+
# Lines
|
80 |
+
#
|
81 |
+
|
82 |
+
protected $BlockTypes = array(
|
83 |
+
'#' => array('Header'),
|
84 |
+
'*' => array('Rule', 'List'),
|
85 |
+
'+' => array('List'),
|
86 |
+
'-' => array('SetextHeader', 'Table', 'Rule', 'List'),
|
87 |
+
'0' => array('List'),
|
88 |
+
'1' => array('List'),
|
89 |
+
'2' => array('List'),
|
90 |
+
'3' => array('List'),
|
91 |
+
'4' => array('List'),
|
92 |
+
'5' => array('List'),
|
93 |
+
'6' => array('List'),
|
94 |
+
'7' => array('List'),
|
95 |
+
'8' => array('List'),
|
96 |
+
'9' => array('List'),
|
97 |
+
':' => array('Table'),
|
98 |
+
'<' => array('Comment', 'Markup'),
|
99 |
+
'=' => array('SetextHeader'),
|
100 |
+
'>' => array('Quote'),
|
101 |
+
'[' => array('Reference'),
|
102 |
+
'_' => array('Rule'),
|
103 |
+
'`' => array('FencedCode'),
|
104 |
+
'|' => array('Table'),
|
105 |
+
'~' => array('FencedCode'),
|
106 |
+
);
|
107 |
+
|
108 |
+
# ~
|
109 |
+
|
110 |
+
protected $unmarkedBlockTypes = array(
|
111 |
+
'Code',
|
112 |
+
);
|
113 |
+
|
114 |
+
#
|
115 |
+
# Blocks
|
116 |
+
#
|
117 |
+
|
118 |
+
private function lines(array $lines)
|
119 |
+
{
|
120 |
+
$CurrentBlock = null;
|
121 |
+
|
122 |
+
foreach ($lines as $line)
|
123 |
+
{
|
124 |
+
if (chop($line) === '')
|
125 |
+
{
|
126 |
+
if (isset($CurrentBlock))
|
127 |
+
{
|
128 |
+
$CurrentBlock['interrupted'] = true;
|
129 |
+
}
|
130 |
+
|
131 |
+
continue;
|
132 |
+
}
|
133 |
+
|
134 |
+
if (strpos($line, "\t") !== false)
|
135 |
+
{
|
136 |
+
$parts = explode("\t", $line);
|
137 |
+
|
138 |
+
$line = $parts[0];
|
139 |
+
|
140 |
+
unset($parts[0]);
|
141 |
+
|
142 |
+
foreach ($parts as $part)
|
143 |
+
{
|
144 |
+
$shortage = 4 - mb_strlen($line, 'utf-8') % 4;
|
145 |
+
|
146 |
+
$line .= str_repeat(' ', $shortage);
|
147 |
+
$line .= $part;
|
148 |
+
}
|
149 |
+
}
|
150 |
+
|
151 |
+
$indent = 0;
|
152 |
+
|
153 |
+
while (isset($line[$indent]) and $line[$indent] === ' ')
|
154 |
+
{
|
155 |
+
$indent ++;
|
156 |
+
}
|
157 |
+
|
158 |
+
$text = $indent > 0 ? substr($line, $indent) : $line;
|
159 |
+
|
160 |
+
# ~
|
161 |
+
|
162 |
+
$Line = array('body' => $line, 'indent' => $indent, 'text' => $text);
|
163 |
+
|
164 |
+
# ~
|
165 |
+
|
166 |
+
if (isset($CurrentBlock['continuable']))
|
167 |
+
{
|
168 |
+
$Block = $this->{'block'.$CurrentBlock['type'].'Continue'}($Line, $CurrentBlock);
|
169 |
+
|
170 |
+
if (isset($Block))
|
171 |
+
{
|
172 |
+
$CurrentBlock = $Block;
|
173 |
+
|
174 |
+
continue;
|
175 |
+
}
|
176 |
+
else
|
177 |
+
{
|
178 |
+
if (method_exists($this, 'block'.$CurrentBlock['type'].'Complete'))
|
179 |
+
{
|
180 |
+
$CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
|
181 |
+
}
|
182 |
+
}
|
183 |
+
}
|
184 |
+
|
185 |
+
# ~
|
186 |
+
|
187 |
+
$marker = $text[0];
|
188 |
+
|
189 |
+
# ~
|
190 |
+
|
191 |
+
$blockTypes = $this->unmarkedBlockTypes;
|
192 |
+
|
193 |
+
if (isset($this->BlockTypes[$marker]))
|
194 |
+
{
|
195 |
+
foreach ($this->BlockTypes[$marker] as $blockType)
|
196 |
+
{
|
197 |
+
$blockTypes []= $blockType;
|
198 |
+
}
|
199 |
+
}
|
200 |
+
|
201 |
+
#
|
202 |
+
# ~
|
203 |
+
|
204 |
+
foreach ($blockTypes as $blockType)
|
205 |
+
{
|
206 |
+
$Block = $this->{'block'.$blockType}($Line, $CurrentBlock);
|
207 |
+
|
208 |
+
if (isset($Block))
|
209 |
+
{
|
210 |
+
$Block['type'] = $blockType;
|
211 |
+
|
212 |
+
if ( ! isset($Block['identified']))
|
213 |
+
{
|
214 |
+
$Blocks []= $CurrentBlock;
|
215 |
+
|
216 |
+
$Block['identified'] = true;
|
217 |
+
}
|
218 |
+
|
219 |
+
if (method_exists($this, 'block'.$blockType.'Continue'))
|
220 |
+
{
|
221 |
+
$Block['continuable'] = true;
|
222 |
+
}
|
223 |
+
|
224 |
+
$CurrentBlock = $Block;
|
225 |
+
|
226 |
+
continue 2;
|
227 |
+
}
|
228 |
+
}
|
229 |
+
|
230 |
+
# ~
|
231 |
+
|
232 |
+
if (isset($CurrentBlock) and ! isset($CurrentBlock['type']) and ! isset($CurrentBlock['interrupted']))
|
233 |
+
{
|
234 |
+
$CurrentBlock['element']['text'] .= "\n".$text;
|
235 |
+
}
|
236 |
+
else
|
237 |
+
{
|
238 |
+
$Blocks []= $CurrentBlock;
|
239 |
+
|
240 |
+
$CurrentBlock = $this->paragraph($Line);
|
241 |
+
|
242 |
+
$CurrentBlock['identified'] = true;
|
243 |
+
}
|
244 |
+
}
|
245 |
+
|
246 |
+
# ~
|
247 |
+
|
248 |
+
if (isset($CurrentBlock['continuable']) and method_exists($this, 'block'.$CurrentBlock['type'].'Complete'))
|
249 |
+
{
|
250 |
+
$CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
|
251 |
+
}
|
252 |
+
|
253 |
+
# ~
|
254 |
+
|
255 |
+
$Blocks []= $CurrentBlock;
|
256 |
+
|
257 |
+
unset($Blocks[0]);
|
258 |
+
|
259 |
+
# ~
|
260 |
+
|
261 |
+
$markup = '';
|
262 |
+
|
263 |
+
foreach ($Blocks as $Block)
|
264 |
+
{
|
265 |
+
if (isset($Block['hidden']))
|
266 |
+
{
|
267 |
+
continue;
|
268 |
+
}
|
269 |
+
|
270 |
+
$markup .= "\n";
|
271 |
+
$markup .= isset($Block['markup']) ? $Block['markup'] : $this->element($Block['element']);
|
272 |
+
}
|
273 |
+
|
274 |
+
$markup .= "\n";
|
275 |
+
|
276 |
+
# ~
|
277 |
+
|
278 |
+
return $markup;
|
279 |
+
}
|
280 |
+
|
281 |
+
#
|
282 |
+
# Code
|
283 |
+
|
284 |
+
protected function blockCode($Line, $Block = null)
|
285 |
+
{
|
286 |
+
if (isset($Block) and ! isset($Block['type']) and ! isset($Block['interrupted']))
|
287 |
+
{
|
288 |
+
return;
|
289 |
+
}
|
290 |
+
|
291 |
+
if ($Line['indent'] >= 4)
|
292 |
+
{
|
293 |
+
$text = substr($Line['body'], 4);
|
294 |
+
|
295 |
+
$Block = array(
|
296 |
+
'element' => array(
|
297 |
+
'name' => 'pre',
|
298 |
+
'handler' => 'element',
|
299 |
+
'text' => array(
|
300 |
+
'name' => 'code',
|
301 |
+
'text' => $text,
|
302 |
+
),
|
303 |
+
),
|
304 |
+
);
|
305 |
+
|
306 |
+
return $Block;
|
307 |
+
}
|
308 |
+
}
|
309 |
+
|
310 |
+
protected function blockCodeContinue($Line, $Block)
|
311 |
+
{
|
312 |
+
if ($Line['indent'] >= 4)
|
313 |
+
{
|
314 |
+
if (isset($Block['interrupted']))
|
315 |
+
{
|
316 |
+
$Block['element']['text']['text'] .= "\n";
|
317 |
+
|
318 |
+
unset($Block['interrupted']);
|
319 |
+
}
|
320 |
+
|
321 |
+
$Block['element']['text']['text'] .= "\n";
|
322 |
+
|
323 |
+
$text = substr($Line['body'], 4);
|
324 |
+
|
325 |
+
$Block['element']['text']['text'] .= $text;
|
326 |
+
|
327 |
+
return $Block;
|
328 |
+
}
|
329 |
+
}
|
330 |
+
|
331 |
+
protected function blockCodeComplete($Block)
|
332 |
+
{
|
333 |
+
$text = $Block['element']['text']['text'];
|
334 |
+
|
335 |
+
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
|
336 |
+
|
337 |
+
$Block['element']['text']['text'] = $text;
|
338 |
+
|
339 |
+
return $Block;
|
340 |
+
}
|
341 |
+
|
342 |
+
#
|
343 |
+
# Comment
|
344 |
+
|
345 |
+
protected function blockComment($Line)
|
346 |
+
{
|
347 |
+
if ($this->markupEscaped)
|
348 |
+
{
|
349 |
+
return;
|
350 |
+
}
|
351 |
+
|
352 |
+
if (isset($Line['text'][3]) and $Line['text'][3] === '-' and $Line['text'][2] === '-' and $Line['text'][1] === '!')
|
353 |
+
{
|
354 |
+
$Block = array(
|
355 |
+
'markup' => $Line['body'],
|
356 |
+
);
|
357 |
+
|
358 |
+
if (preg_match('/-->$/', $Line['text']))
|
359 |
+
{
|
360 |
+
$Block['closed'] = true;
|
361 |
+
}
|
362 |
+
|
363 |
+
return $Block;
|
364 |
+
}
|
365 |
+
}
|
366 |
+
|
367 |
+
protected function blockCommentContinue($Line, array $Block)
|
368 |
+
{
|
369 |
+
if (isset($Block['closed']))
|
370 |
+
{
|
371 |
+
return;
|
372 |
+
}
|
373 |
+
|
374 |
+
$Block['markup'] .= "\n" . $Line['body'];
|
375 |
+
|
376 |
+
if (preg_match('/-->$/', $Line['text']))
|
377 |
+
{
|
378 |
+
$Block['closed'] = true;
|
379 |
+
}
|
380 |
+
|
381 |
+
return $Block;
|
382 |
+
}
|
383 |
+
|
384 |
+
#
|
385 |
+
# Fenced Code
|
386 |
+
|
387 |
+
protected function blockFencedCode($Line)
|
388 |
+
{
|
389 |
+
if (preg_match('/^['.$Line['text'][0].']{3,}[ ]*([\w-]+)?[ ]*$/', $Line['text'], $matches))
|
390 |
+
{
|
391 |
+
$Element = array(
|
392 |
+
'name' => 'code',
|
393 |
+
'text' => '',
|
394 |
+
);
|
395 |
+
|
396 |
+
if (isset($matches[1]))
|
397 |
+
{
|
398 |
+
$class = 'language-'.$matches[1];
|
399 |
+
|
400 |
+
$Element['attributes'] = array(
|
401 |
+
'class' => $class,
|
402 |
+
);
|
403 |
+
}
|
404 |
+
|
405 |
+
$Block = array(
|
406 |
+
'char' => $Line['text'][0],
|
407 |
+
'element' => array(
|
408 |
+
'name' => 'pre',
|
409 |
+
'handler' => 'element',
|
410 |
+
'text' => $Element,
|
411 |
+
),
|
412 |
+
);
|
413 |
+
|
414 |
+
return $Block;
|
415 |
+
}
|
416 |
+
}
|
417 |
+
|
418 |
+
protected function blockFencedCodeContinue($Line, $Block)
|
419 |
+
{
|
420 |
+
if (isset($Block['complete']))
|
421 |
+
{
|
422 |
+
return;
|
423 |
+
}
|
424 |
+
|
425 |
+
if (isset($Block['interrupted']))
|
426 |
+
{
|
427 |
+
$Block['element']['text']['text'] .= "\n";
|
428 |
+
|
429 |
+
unset($Block['interrupted']);
|
430 |
+
}
|
431 |
+
|
432 |
+
if (preg_match('/^'.$Block['char'].'{3,}[ ]*$/', $Line['text']))
|
433 |
+
{
|
434 |
+
$Block['element']['text']['text'] = substr($Block['element']['text']['text'], 1);
|
435 |
+
|
436 |
+
$Block['complete'] = true;
|
437 |
+
|
438 |
+
return $Block;
|
439 |
+
}
|
440 |
+
|
441 |
+
$Block['element']['text']['text'] .= "\n".$Line['body'];;
|
442 |
+
|
443 |
+
return $Block;
|
444 |
+
}
|
445 |
+
|
446 |
+
protected function blockFencedCodeComplete($Block)
|
447 |
+
{
|
448 |
+
$text = $Block['element']['text']['text'];
|
449 |
+
|
450 |
+
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
|
451 |
+
|
452 |
+
$Block['element']['text']['text'] = $text;
|
453 |
+
|
454 |
+
return $Block;
|
455 |
+
}
|
456 |
+
|
457 |
+
#
|
458 |
+
# Header
|
459 |
+
|
460 |
+
protected function blockHeader($Line)
|
461 |
+
{
|
462 |
+
if (isset($Line['text'][1]))
|
463 |
+
{
|
464 |
+
$level = 1;
|
465 |
+
|
466 |
+
while (isset($Line['text'][$level]) and $Line['text'][$level] === '#')
|
467 |
+
{
|
468 |
+
$level ++;
|
469 |
+
}
|
470 |
+
|
471 |
+
if ($level > 6)
|
472 |
+
{
|
473 |
+
return;
|
474 |
+
}
|
475 |
+
|
476 |
+
$text = trim($Line['text'], '# ');
|
477 |
+
|
478 |
+
$Block = array(
|
479 |
+
'element' => array(
|
480 |
+
'name' => 'h' . min(6, $level),
|
481 |
+
'text' => $text,
|
482 |
+
'handler' => 'line',
|
483 |
+
),
|
484 |
+
);
|
485 |
+
|
486 |
+
return $Block;
|
487 |
+
}
|
488 |
+
}
|
489 |
+
|
490 |
+
#
|
491 |
+
# List
|
492 |
+
|
493 |
+
protected function blockList($Line)
|
494 |
+
{
|
495 |
+
list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]+[.]');
|
496 |
+
|
497 |
+
if (preg_match('/^('.$pattern.'[ ]+)(.*)/', $Line['text'], $matches))
|
498 |
+
{
|
499 |
+
$Block = array(
|
500 |
+
'indent' => $Line['indent'],
|
501 |
+
'pattern' => $pattern,
|
502 |
+
'element' => array(
|
503 |
+
'name' => $name,
|
504 |
+
'handler' => 'elements',
|
505 |
+
),
|
506 |
+
);
|
507 |
+
|
508 |
+
$Block['li'] = array(
|
509 |
+
'name' => 'li',
|
510 |
+
'handler' => 'li',
|
511 |
+
'text' => array(
|
512 |
+
$matches[2],
|
513 |
+
),
|
514 |
+
);
|
515 |
+
|
516 |
+
$Block['element']['text'] []= & $Block['li'];
|
517 |
+
|
518 |
+
return $Block;
|
519 |
+
}
|
520 |
+
}
|
521 |
+
|
522 |
+
protected function blockListContinue($Line, array $Block)
|
523 |
+
{
|
524 |
+
if ($Block['indent'] === $Line['indent'] and preg_match('/^'.$Block['pattern'].'(?:[ ]+(.*)|$)/', $Line['text'], $matches))
|
525 |
+
{
|
526 |
+
if (isset($Block['interrupted']))
|
527 |
+
{
|
528 |
+
$Block['li']['text'] []= '';
|
529 |
+
|
530 |
+
unset($Block['interrupted']);
|
531 |
+
}
|
532 |
+
|
533 |
+
unset($Block['li']);
|
534 |
+
|
535 |
+
$text = isset($matches[1]) ? $matches[1] : '';
|
536 |
+
|
537 |
+
$Block['li'] = array(
|
538 |
+
'name' => 'li',
|
539 |
+
'handler' => 'li',
|
540 |
+
'text' => array(
|
541 |
+
$text,
|
542 |
+
),
|
543 |
+
);
|
544 |
+
|
545 |
+
$Block['element']['text'] []= & $Block['li'];
|
546 |
+
|
547 |
+
return $Block;
|
548 |
+
}
|
549 |
+
|
550 |
+
if ($Line['text'][0] === '[' and $this->blockReference($Line))
|
551 |
+
{
|
552 |
+
return $Block;
|
553 |
+
}
|
554 |
+
|
555 |
+
if ( ! isset($Block['interrupted']))
|
556 |
+
{
|
557 |
+
$text = preg_replace('/^[ ]{0,4}/', '', $Line['body']);
|
558 |
+
|
559 |
+
$Block['li']['text'] []= $text;
|
560 |
+
|
561 |
+
return $Block;
|
562 |
+
}
|
563 |
+
|
564 |
+
if ($Line['indent'] > 0)
|
565 |
+
{
|
566 |
+
$Block['li']['text'] []= '';
|
567 |
+
|
568 |
+
$text = preg_replace('/^[ ]{0,4}/', '', $Line['body']);
|
569 |
+
|
570 |
+
$Block['li']['text'] []= $text;
|
571 |
+
|
572 |
+
unset($Block['interrupted']);
|
573 |
+
|
574 |
+
return $Block;
|
575 |
+
}
|
576 |
+
}
|
577 |
+
|
578 |
+
#
|
579 |
+
# Quote
|
580 |
+
|
581 |
+
protected function blockQuote($Line)
|
582 |
+
{
|
583 |
+
if (preg_match('/^>[ ]?(.*)/', $Line['text'], $matches))
|
584 |
+
{
|
585 |
+
$Block = array(
|
586 |
+
'element' => array(
|
587 |
+
'name' => 'blockquote',
|
588 |
+
'handler' => 'lines',
|
589 |
+
'text' => (array) $matches[1],
|
590 |
+
),
|
591 |
+
);
|
592 |
+
|
593 |
+
return $Block;
|
594 |
+
}
|
595 |
+
}
|
596 |
+
|
597 |
+
protected function blockQuoteContinue($Line, array $Block)
|
598 |
+
{
|
599 |
+
if ($Line['text'][0] === '>' and preg_match('/^>[ ]?(.*)/', $Line['text'], $matches))
|
600 |
+
{
|
601 |
+
if (isset($Block['interrupted']))
|
602 |
+
{
|
603 |
+
$Block['element']['text'] []= '';
|
604 |
+
|
605 |
+
unset($Block['interrupted']);
|
606 |
+
}
|
607 |
+
|
608 |
+
$Block['element']['text'] []= $matches[1];
|
609 |
+
|
610 |
+
return $Block;
|
611 |
+
}
|
612 |
+
|
613 |
+
if ( ! isset($Block['interrupted']))
|
614 |
+
{
|
615 |
+
$Block['element']['text'] []= $Line['text'];
|
616 |
+
|
617 |
+
return $Block;
|
618 |
+
}
|
619 |
+
}
|
620 |
+
|
621 |
+
#
|
622 |
+
# Rule
|
623 |
+
|
624 |
+
protected function blockRule($Line)
|
625 |
+
{
|
626 |
+
if (preg_match('/^(['.$Line['text'][0].'])([ ]*\1){2,}[ ]*$/', $Line['text']))
|
627 |
+
{
|
628 |
+
$Block = array(
|
629 |
+
'element' => array(
|
630 |
+
'name' => 'hr'
|
631 |
+
),
|
632 |
+
);
|
633 |
+
|
634 |
+
return $Block;
|
635 |
+
}
|
636 |
+
}
|
637 |
+
|
638 |
+
#
|
639 |
+
# Setext
|
640 |
+
|
641 |
+
protected function blockSetextHeader($Line, array $Block = null)
|
642 |
+
{
|
643 |
+
if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
|
644 |
+
{
|
645 |
+
return;
|
646 |
+
}
|
647 |
+
|
648 |
+
if (chop($Line['text'], $Line['text'][0]) === '')
|
649 |
+
{
|
650 |
+
$Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2';
|
651 |
+
|
652 |
+
return $Block;
|
653 |
+
}
|
654 |
+
}
|
655 |
+
|
656 |
+
#
|
657 |
+
# Markup
|
658 |
+
|
659 |
+
protected function blockMarkup($Line)
|
660 |
+
{
|
661 |
+
if ($this->markupEscaped)
|
662 |
+
{
|
663 |
+
return;
|
664 |
+
}
|
665 |
+
|
666 |
+
if (preg_match('/^<(\w*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches))
|
667 |
+
{
|
668 |
+
$element = strtolower($matches[1]);
|
669 |
+
|
670 |
+
if (in_array($element, $this->textLevelElements))
|
671 |
+
{
|
672 |
+
return;
|
673 |
+
}
|
674 |
+
|
675 |
+
$Block = array(
|
676 |
+
'name' => $matches[1],
|
677 |
+
'depth' => 0,
|
678 |
+
'markup' => $Line['text'],
|
679 |
+
);
|
680 |
+
|
681 |
+
$length = strlen($matches[0]);
|
682 |
+
|
683 |
+
$remainder = substr($Line['text'], $length);
|
684 |
+
|
685 |
+
if (trim($remainder) === '')
|
686 |
+
{
|
687 |
+
if (isset($matches[2]) or in_array($matches[1], $this->voidElements))
|
688 |
+
{
|
689 |
+
$Block['closed'] = true;
|
690 |
+
|
691 |
+
$Block['void'] = true;
|
692 |
+
}
|
693 |
+
}
|
694 |
+
else
|
695 |
+
{
|
696 |
+
if (isset($matches[2]) or in_array($matches[1], $this->voidElements))
|
697 |
+
{
|
698 |
+
return;
|
699 |
+
}
|
700 |
+
|
701 |
+
if (preg_match('/<\/'.$matches[1].'>[ ]*$/i', $remainder))
|
702 |
+
{
|
703 |
+
$Block['closed'] = true;
|
704 |
+
}
|
705 |
+
}
|
706 |
+
|
707 |
+
return $Block;
|
708 |
+
}
|
709 |
+
}
|
710 |
+
|
711 |
+
protected function blockMarkupContinue($Line, array $Block)
|
712 |
+
{
|
713 |
+
if (isset($Block['closed']))
|
714 |
+
{
|
715 |
+
return;
|
716 |
+
}
|
717 |
+
|
718 |
+
if (preg_match('/^<'.$Block['name'].'(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*>/i', $Line['text'])) # open
|
719 |
+
{
|
720 |
+
$Block['depth'] ++;
|
721 |
+
}
|
722 |
+
|
723 |
+
if (preg_match('/(.*?)<\/'.$Block['name'].'>[ ]*$/i', $Line['text'], $matches)) # close
|
724 |
+
{
|
725 |
+
if ($Block['depth'] > 0)
|
726 |
+
{
|
727 |
+
$Block['depth'] --;
|
728 |
+
}
|
729 |
+
else
|
730 |
+
{
|
731 |
+
$Block['closed'] = true;
|
732 |
+
}
|
733 |
+
}
|
734 |
+
|
735 |
+
if (isset($Block['interrupted']))
|
736 |
+
{
|
737 |
+
$Block['markup'] .= "\n";
|
738 |
+
|
739 |
+
unset($Block['interrupted']);
|
740 |
+
}
|
741 |
+
|
742 |
+
$Block['markup'] .= "\n".$Line['body'];
|
743 |
+
|
744 |
+
return $Block;
|
745 |
+
}
|
746 |
+
|
747 |
+
#
|
748 |
+
# Reference
|
749 |
+
|
750 |
+
protected function blockReference($Line)
|
751 |
+
{
|
752 |
+
if (preg_match('/^\[(.+?)\]:[ ]*<?(\S+?)>?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $Line['text'], $matches))
|
753 |
+
{
|
754 |
+
$id = strtolower($matches[1]);
|
755 |
+
|
756 |
+
$Data = array(
|
757 |
+
'url' => $matches[2],
|
758 |
+
'title' => null,
|
759 |
+
);
|
760 |
+
|
761 |
+
if (isset($matches[3]))
|
762 |
+
{
|
763 |
+
$Data['title'] = $matches[3];
|
764 |
+
}
|
765 |
+
|
766 |
+
$this->DefinitionData['Reference'][$id] = $Data;
|
767 |
+
|
768 |
+
$Block = array(
|
769 |
+
'hidden' => true,
|
770 |
+
);
|
771 |
+
|
772 |
+
return $Block;
|
773 |
+
}
|
774 |
+
}
|
775 |
+
|
776 |
+
#
|
777 |
+
# Table
|
778 |
+
|
779 |
+
protected function blockTable($Line, array $Block = null)
|
780 |
+
{
|
781 |
+
if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
|
782 |
+
{
|
783 |
+
return;
|
784 |
+
}
|
785 |
+
|
786 |
+
if (strpos($Block['element']['text'], '|') !== false and chop($Line['text'], ' -:|') === '')
|
787 |
+
{
|
788 |
+
$alignments = array();
|
789 |
+
|
790 |
+
$divider = $Line['text'];
|
791 |
+
|
792 |
+
$divider = trim($divider);
|
793 |
+
$divider = trim($divider, '|');
|
794 |
+
|
795 |
+
$dividerCells = explode('|', $divider);
|
796 |
+
|
797 |
+
foreach ($dividerCells as $dividerCell)
|
798 |
+
{
|
799 |
+
$dividerCell = trim($dividerCell);
|
800 |
+
|
801 |
+
if ($dividerCell === '')
|
802 |
+
{
|
803 |
+
continue;
|
804 |
+
}
|
805 |
+
|
806 |
+
$alignment = null;
|
807 |
+
|
808 |
+
if ($dividerCell[0] === ':')
|
809 |
+
{
|
810 |
+
$alignment = 'left';
|
811 |
+
}
|
812 |
+
|
813 |
+
if (substr($dividerCell, - 1) === ':')
|
814 |
+
{
|
815 |
+
$alignment = $alignment === 'left' ? 'center' : 'right';
|
816 |
+
}
|
817 |
+
|
818 |
+
$alignments []= $alignment;
|
819 |
+
}
|
820 |
+
|
821 |
+
# ~
|
822 |
+
|
823 |
+
$HeaderElements = array();
|
824 |
+
|
825 |
+
$header = $Block['element']['text'];
|
826 |
+
|
827 |
+
$header = trim($header);
|
828 |
+
$header = trim($header, '|');
|
829 |
+
|
830 |
+
$headerCells = explode('|', $header);
|
831 |
+
|
832 |
+
foreach ($headerCells as $index => $headerCell)
|
833 |
+
{
|
834 |
+
$headerCell = trim($headerCell);
|
835 |
+
|
836 |
+
$HeaderElement = array(
|
837 |
+
'name' => 'th',
|
838 |
+
'text' => $headerCell,
|
839 |
+
'handler' => 'line',
|
840 |
+
);
|
841 |
+
|
842 |
+
if (isset($alignments[$index]))
|
843 |
+
{
|
844 |
+
$alignment = $alignments[$index];
|
845 |
+
|
846 |
+
$HeaderElement['attributes'] = array(
|
847 |
+
'style' => 'text-align: '.$alignment.';',
|
848 |
+
);
|
849 |
+
}
|
850 |
+
|
851 |
+
$HeaderElements []= $HeaderElement;
|
852 |
+
}
|
853 |
+
|
854 |
+
# ~
|
855 |
+
|
856 |
+
$Block = array(
|
857 |
+
'alignments' => $alignments,
|
858 |
+
'identified' => true,
|
859 |
+
'element' => array(
|
860 |
+
'name' => 'table',
|
861 |
+
'handler' => 'elements',
|
862 |
+
),
|
863 |
+
);
|
864 |
+
|
865 |
+
$Block['element']['text'] []= array(
|
866 |
+
'name' => 'thead',
|
867 |
+
'handler' => 'elements',
|
868 |
+
);
|
869 |
+
|
870 |
+
$Block['element']['text'] []= array(
|
871 |
+
'name' => 'tbody',
|
872 |
+
'handler' => 'elements',
|
873 |
+
'text' => array(),
|
874 |
+
);
|
875 |
+
|
876 |
+
$Block['element']['text'][0]['text'] []= array(
|
877 |
+
'name' => 'tr',
|
878 |
+
'handler' => 'elements',
|
879 |
+
'text' => $HeaderElements,
|
880 |
+
);
|
881 |
+
|
882 |
+
return $Block;
|
883 |
+
}
|
884 |
+
}
|
885 |
+
|
886 |
+
protected function blockTableContinue($Line, array $Block)
|
887 |
+
{
|
888 |
+
if (isset($Block['interrupted']))
|
889 |
+
{
|
890 |
+
return;
|
891 |
+
}
|
892 |
+
|
893 |
+
if ($Line['text'][0] === '|' or strpos($Line['text'], '|'))
|
894 |
+
{
|
895 |
+
$Elements = array();
|
896 |
+
|
897 |
+
$row = $Line['text'];
|
898 |
+
|
899 |
+
$row = trim($row);
|
900 |
+
$row = trim($row, '|');
|
901 |
+
|
902 |
+
preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]+`|`)+/', $row, $matches);
|
903 |
+
|
904 |
+
foreach ($matches[0] as $index => $cell)
|
905 |
+
{
|
906 |
+
$cell = trim($cell);
|
907 |
+
|
908 |
+
$Element = array(
|
909 |
+
'name' => 'td',
|
910 |
+
'handler' => 'line',
|
911 |
+
'text' => $cell,
|
912 |
+
);
|
913 |
+
|
914 |
+
if (isset($Block['alignments'][$index]))
|
915 |
+
{
|
916 |
+
$Element['attributes'] = array(
|
917 |
+
'style' => 'text-align: '.$Block['alignments'][$index].';',
|
918 |
+
);
|
919 |
+
}
|
920 |
+
|
921 |
+
$Elements []= $Element;
|
922 |
+
}
|
923 |
+
|
924 |
+
$Element = array(
|
925 |
+
'name' => 'tr',
|
926 |
+
'handler' => 'elements',
|
927 |
+
'text' => $Elements,
|
928 |
+
);
|
929 |
+
|
930 |
+
$Block['element']['text'][1]['text'] []= $Element;
|
931 |
+
|
932 |
+
return $Block;
|
933 |
+
}
|
934 |
+
}
|
935 |
+
|
936 |
+
#
|
937 |
+
# ~
|
938 |
+
#
|
939 |
+
|
940 |
+
protected function paragraph($Line)
|
941 |
+
{
|
942 |
+
$Block = array(
|
943 |
+
'element' => array(
|
944 |
+
'name' => 'p',
|
945 |
+
'text' => $Line['text'],
|
946 |
+
'handler' => 'line',
|
947 |
+
),
|
948 |
+
);
|
949 |
+
|
950 |
+
return $Block;
|
951 |
+
}
|
952 |
+
|
953 |
+
#
|
954 |
+
# Inline Elements
|
955 |
+
#
|
956 |
+
|
957 |
+
protected $InlineTypes = array(
|
958 |
+
'"' => array('SpecialCharacter'),
|
959 |
+
'!' => array('Image'),
|
960 |
+
'&' => array('SpecialCharacter'),
|
961 |
+
'*' => array('Emphasis'),
|
962 |
+
':' => array('Url'),
|
963 |
+
'<' => array('UrlTag', 'EmailTag', 'Markup', 'SpecialCharacter'),
|
964 |
+
'>' => array('SpecialCharacter'),
|
965 |
+
'[' => array('Link'),
|
966 |
+
'_' => array('Emphasis'),
|
967 |
+
'`' => array('Code'),
|
968 |
+
'~' => array('Strikethrough'),
|
969 |
+
'\\' => array('EscapeSequence'),
|
970 |
+
);
|
971 |
+
|
972 |
+
# ~
|
973 |
+
|
974 |
+
protected $inlineMarkerList = '!"*_&[:<>`~\\';
|
975 |
+
|
976 |
+
#
|
977 |
+
# ~
|
978 |
+
#
|
979 |
+
|
980 |
+
public function line($text)
|
981 |
+
{
|
982 |
+
$markup = '';
|
983 |
+
|
984 |
+
# $excerpt is based on the first occurrence of a marker
|
985 |
+
|
986 |
+
while ($excerpt = strpbrk($text, $this->inlineMarkerList))
|
987 |
+
{
|
988 |
+
$marker = $excerpt[0];
|
989 |
+
|
990 |
+
$markerPosition = strpos($text, $marker);
|
991 |
+
|
992 |
+
$Excerpt = array('text' => $excerpt, 'context' => $text);
|
993 |
+
|
994 |
+
foreach ($this->InlineTypes[$marker] as $inlineType)
|
995 |
+
{
|
996 |
+
$Inline = $this->{'inline'.$inlineType}($Excerpt);
|
997 |
+
|
998 |
+
if ( ! isset($Inline))
|
999 |
+
{
|
1000 |
+
continue;
|
1001 |
+
}
|
1002 |
+
|
1003 |
+
# makes sure that the inline belongs to "our" marker
|
1004 |
+
|
1005 |
+
if (isset($Inline['position']) and $Inline['position'] > $markerPosition)
|
1006 |
+
{
|
1007 |
+
continue;
|
1008 |
+
}
|
1009 |
+
|
1010 |
+
# sets a default inline position
|
1011 |
+
|
1012 |
+
if ( ! isset($Inline['position']))
|
1013 |
+
{
|
1014 |
+
$Inline['position'] = $markerPosition;
|
1015 |
+
}
|
1016 |
+
|
1017 |
+
# the text that comes before the inline
|
1018 |
+
$unmarkedText = substr($text, 0, $Inline['position']);
|
1019 |
+
|
1020 |
+
# compile the unmarked text
|
1021 |
+
$markup .= $this->unmarkedText($unmarkedText);
|
1022 |
+
|
1023 |
+
# compile the inline
|
1024 |
+
$markup .= isset($Inline['markup']) ? $Inline['markup'] : $this->element($Inline['element']);
|
1025 |
+
|
1026 |
+
# remove the examined text
|
1027 |
+
$text = substr($text, $Inline['position'] + $Inline['extent']);
|
1028 |
+
|
1029 |
+
continue 2;
|
1030 |
+
}
|
1031 |
+
|
1032 |
+
# the marker does not belong to an inline
|
1033 |
+
|
1034 |
+
$unmarkedText = substr($text, 0, $markerPosition + 1);
|
1035 |
+
|
1036 |
+
$markup .= $this->unmarkedText($unmarkedText);
|
1037 |
+
|
1038 |
+
$text = substr($text, $markerPosition + 1);
|
1039 |
+
}
|
1040 |
+
|
1041 |
+
$markup .= $this->unmarkedText($text);
|
1042 |
+
|
1043 |
+
return $markup;
|
1044 |
+
}
|
1045 |
+
|
1046 |
+
#
|
1047 |
+
# ~
|
1048 |
+
#
|
1049 |
+
|
1050 |
+
protected function inlineCode($Excerpt)
|
1051 |
+
{
|
1052 |
+
$marker = $Excerpt['text'][0];
|
1053 |
+
|
1054 |
+
if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(?<!'.$marker.')\1(?!'.$marker.')/s', $Excerpt['text'], $matches))
|
1055 |
+
{
|
1056 |
+
$text = $matches[2];
|
1057 |
+
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
|
1058 |
+
$text = preg_replace("/[ ]*\n/", ' ', $text);
|
1059 |
+
|
1060 |
+
return array(
|
1061 |
+
'extent' => strlen($matches[0]),
|
1062 |
+
'element' => array(
|
1063 |
+
'name' => 'code',
|
1064 |
+
'text' => $text,
|
1065 |
+
),
|
1066 |
+
);
|
1067 |
+
}
|
1068 |
+
}
|
1069 |
+
|
1070 |
+
protected function inlineEmailTag($Excerpt)
|
1071 |
+
{
|
1072 |
+
if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<((mailto:)?\S+?@\S+?)>/i', $Excerpt['text'], $matches))
|
1073 |
+
{
|
1074 |
+
$url = $matches[1];
|
1075 |
+
|
1076 |
+
if ( ! isset($matches[2]))
|
1077 |
+
{
|
1078 |
+
$url = 'mailto:' . $url;
|
1079 |
+
}
|
1080 |
+
|
1081 |
+
return array(
|
1082 |
+
'extent' => strlen($matches[0]),
|
1083 |
+
'element' => array(
|
1084 |
+
'name' => 'a',
|
1085 |
+
'text' => $matches[1],
|
1086 |
+
'attributes' => array(
|
1087 |
+
'href' => $url,
|
1088 |
+
),
|
1089 |
+
),
|
1090 |
+
);
|
1091 |
+
}
|
1092 |
+
}
|
1093 |
+
|
1094 |
+
protected function inlineEmphasis($Excerpt)
|
1095 |
+
{
|
1096 |
+
if ( ! isset($Excerpt['text'][1]))
|
1097 |
+
{
|
1098 |
+
return;
|
1099 |
+
}
|
1100 |
+
|
1101 |
+
$marker = $Excerpt['text'][0];
|
1102 |
+
|
1103 |
+
if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches))
|
1104 |
+
{
|
1105 |
+
$emphasis = 'strong';
|
1106 |
+
}
|
1107 |
+
elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches))
|
1108 |
+
{
|
1109 |
+
$emphasis = 'em';
|
1110 |
+
}
|
1111 |
+
else
|
1112 |
+
{
|
1113 |
+
return;
|
1114 |
+
}
|
1115 |
+
|
1116 |
+
return array(
|
1117 |
+
'extent' => strlen($matches[0]),
|
1118 |
+
'element' => array(
|
1119 |
+
'name' => $emphasis,
|
1120 |
+
'handler' => 'line',
|
1121 |
+
'text' => $matches[1],
|
1122 |
+
),
|
1123 |
+
);
|
1124 |
+
}
|
1125 |
+
|
1126 |
+
protected function inlineEscapeSequence($Excerpt)
|
1127 |
+
{
|
1128 |
+
if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters))
|
1129 |
+
{
|
1130 |
+
return array(
|
1131 |
+
'markup' => $Excerpt['text'][1],
|
1132 |
+
'extent' => 2,
|
1133 |
+
);
|
1134 |
+
}
|
1135 |
+
}
|
1136 |
+
|
1137 |
+
protected function inlineImage($Excerpt)
|
1138 |
+
{
|
1139 |
+
if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '[')
|
1140 |
+
{
|
1141 |
+
return;
|
1142 |
+
}
|
1143 |
+
|
1144 |
+
$Excerpt['text']= substr($Excerpt['text'], 1);
|
1145 |
+
|
1146 |
+
$Link = $this->inlineLink($Excerpt);
|
1147 |
+
|
1148 |
+
if ($Link === null)
|
1149 |
+
{
|
1150 |
+
return;
|
1151 |
+
}
|
1152 |
+
|
1153 |
+
$Inline = array(
|
1154 |
+
'extent' => $Link['extent'] + 1,
|
1155 |
+
'element' => array(
|
1156 |
+
'name' => 'img',
|
1157 |
+
'attributes' => array(
|
1158 |
+
'src' => $Link['element']['attributes']['href'],
|
1159 |
+
'alt' => $Link['element']['text'],
|
1160 |
+
),
|
1161 |
+
),
|
1162 |
+
);
|
1163 |
+
|
1164 |
+
$Inline['element']['attributes'] += $Link['element']['attributes'];
|
1165 |
+
|
1166 |
+
unset($Inline['element']['attributes']['href']);
|
1167 |
+
|
1168 |
+
return $Inline;
|
1169 |
+
}
|
1170 |
+
|
1171 |
+
protected function inlineLink($Excerpt)
|
1172 |
+
{
|
1173 |
+
$Element = array(
|
1174 |
+
'name' => 'a',
|
1175 |
+
'handler' => 'line',
|
1176 |
+
'text' => null,
|
1177 |
+
'attributes' => array(
|
1178 |
+
'href' => null,
|
1179 |
+
'title' => null,
|
1180 |
+
),
|
1181 |
+
);
|
1182 |
+
|
1183 |
+
$extent = 0;
|
1184 |
+
|
1185 |
+
$remainder = $Excerpt['text'];
|
1186 |
+
|
1187 |
+
if (preg_match('/\[((?:[^][]|(?R))*)\]/', $remainder, $matches))
|
1188 |
+
{
|
1189 |
+
$Element['text'] = $matches[1];
|
1190 |
+
|
1191 |
+
$extent += strlen($matches[0]);
|
1192 |
+
|
1193 |
+
$remainder = substr($remainder, $extent);
|
1194 |
+
}
|
1195 |
+
else
|
1196 |
+
{
|
1197 |
+
return;
|
1198 |
+
}
|
1199 |
+
|
1200 |
+
if (preg_match('/^[(]((?:[^ ()]|[(][^ )]+[)])+)(?:[ ]+("[^"]*"|\'[^\']*\'))?[)]/', $remainder, $matches))
|
1201 |
+
{
|
1202 |
+
$Element['attributes']['href'] = $matches[1];
|
1203 |
+
|
1204 |
+
if (isset($matches[2]))
|
1205 |
+
{
|
1206 |
+
$Element['attributes']['title'] = substr($matches[2], 1, - 1);
|
1207 |
+
}
|
1208 |
+
|
1209 |
+
$extent += strlen($matches[0]);
|
1210 |
+
}
|
1211 |
+
else
|
1212 |
+
{
|
1213 |
+
if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches))
|
1214 |
+
{
|
1215 |
+
$definition = strlen($matches[1]) ? $matches[1] : $Element['text'];
|
1216 |
+
$definition = strtolower($definition);
|
1217 |
+
|
1218 |
+
$extent += strlen($matches[0]);
|
1219 |
+
}
|
1220 |
+
else
|
1221 |
+
{
|
1222 |
+
$definition = strtolower($Element['text']);
|
1223 |
+
}
|
1224 |
+
|
1225 |
+
if ( ! isset($this->DefinitionData['Reference'][$definition]))
|
1226 |
+
{
|
1227 |
+
return;
|
1228 |
+
}
|
1229 |
+
|
1230 |
+
$Definition = $this->DefinitionData['Reference'][$definition];
|
1231 |
+
|
1232 |
+
$Element['attributes']['href'] = $Definition['url'];
|
1233 |
+
$Element['attributes']['title'] = $Definition['title'];
|
1234 |
+
}
|
1235 |
+
|
1236 |
+
$Element['attributes']['href'] = str_replace(array('&', '<'), array('&', '<'), $Element['attributes']['href']);
|
1237 |
+
|
1238 |
+
return array(
|
1239 |
+
'extent' => $extent,
|
1240 |
+
'element' => $Element,
|
1241 |
+
);
|
1242 |
+
}
|
1243 |
+
|
1244 |
+
protected function inlineMarkup($Excerpt)
|
1245 |
+
{
|
1246 |
+
if ($this->markupEscaped or strpos($Excerpt['text'], '>') === false)
|
1247 |
+
{
|
1248 |
+
return;
|
1249 |
+
}
|
1250 |
+
|
1251 |
+
if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w*[ ]*>/s', $Excerpt['text'], $matches))
|
1252 |
+
{
|
1253 |
+
return array(
|
1254 |
+
'markup' => $matches[0],
|
1255 |
+
'extent' => strlen($matches[0]),
|
1256 |
+
);
|
1257 |
+
}
|
1258 |
+
|
1259 |
+
if ($Excerpt['text'][1] === '!' and preg_match('/^<!---?[^>-](?:-?[^-])*-->/s', $Excerpt['text'], $matches))
|
1260 |
+
{
|
1261 |
+
return array(
|
1262 |
+
'markup' => $matches[0],
|
1263 |
+
'extent' => strlen($matches[0]),
|
1264 |
+
);
|
1265 |
+
}
|
1266 |
+
|
1267 |
+
if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w*(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*\/?>/s', $Excerpt['text'], $matches))
|
1268 |
+
{
|
1269 |
+
return array(
|
1270 |
+
'markup' => $matches[0],
|
1271 |
+
'extent' => strlen($matches[0]),
|
1272 |
+
);
|
1273 |
+
}
|
1274 |
+
}
|
1275 |
+
|
1276 |
+
protected function inlineSpecialCharacter($Excerpt)
|
1277 |
+
{
|
1278 |
+
if ($Excerpt['text'][0] === '&' and ! preg_match('/^&#?\w+;/', $Excerpt['text']))
|
1279 |
+
{
|
1280 |
+
return array(
|
1281 |
+
'markup' => '&',
|
1282 |
+
'extent' => 1,
|
1283 |
+
);
|
1284 |
+
}
|
1285 |
+
|
1286 |
+
$SpecialCharacter = array('>' => 'gt', '<' => 'lt', '"' => 'quot');
|
1287 |
+
|
1288 |
+
if (isset($SpecialCharacter[$Excerpt['text'][0]]))
|
1289 |
+
{
|
1290 |
+
return array(
|
1291 |
+
'markup' => '&'.$SpecialCharacter[$Excerpt['text'][0]].';',
|
1292 |
+
'extent' => 1,
|
1293 |
+
);
|
1294 |
+
}
|
1295 |
+
}
|
1296 |
+
|
1297 |
+
protected function inlineStrikethrough($Excerpt)
|
1298 |
+
{
|
1299 |
+
if ( ! isset($Excerpt['text'][1]))
|
1300 |
+
{
|
1301 |
+
return;
|
1302 |
+
}
|
1303 |
+
|
1304 |
+
if ($Excerpt['text'][1] === '~' and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches))
|
1305 |
+
{
|
1306 |
+
return array(
|
1307 |
+
'extent' => strlen($matches[0]),
|
1308 |
+
'element' => array(
|
1309 |
+
'name' => 'del',
|
1310 |
+
'text' => $matches[1],
|
1311 |
+
'handler' => 'line',
|
1312 |
+
),
|
1313 |
+
);
|
1314 |
+
}
|
1315 |
+
}
|
1316 |
+
|
1317 |
+
protected function inlineUrl($Excerpt)
|
1318 |
+
{
|
1319 |
+
if ($this->urlsLinked !== true or ! isset($Excerpt['text'][2]) or $Excerpt['text'][2] !== '/')
|
1320 |
+
{
|
1321 |
+
return;
|
1322 |
+
}
|
1323 |
+
|
1324 |
+
if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE))
|
1325 |
+
{
|
1326 |
+
$Inline = array(
|
1327 |
+
'extent' => strlen($matches[0][0]),
|
1328 |
+
'position' => $matches[0][1],
|
1329 |
+
'element' => array(
|
1330 |
+
'name' => 'a',
|
1331 |
+
'text' => $matches[0][0],
|
1332 |
+
'attributes' => array(
|
1333 |
+
'href' => $matches[0][0],
|
1334 |
+
),
|
1335 |
+
),
|
1336 |
+
);
|
1337 |
+
|
1338 |
+
return $Inline;
|
1339 |
+
}
|
1340 |
+
}
|
1341 |
+
|
1342 |
+
protected function inlineUrlTag($Excerpt)
|
1343 |
+
{
|
1344 |
+
if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w+:\/{2}[^ >]+)>/i', $Excerpt['text'], $matches))
|
1345 |
+
{
|
1346 |
+
$url = str_replace(array('&', '<'), array('&', '<'), $matches[1]);
|
1347 |
+
|
1348 |
+
return array(
|
1349 |
+
'extent' => strlen($matches[0]),
|
1350 |
+
'element' => array(
|
1351 |
+
'name' => 'a',
|
1352 |
+
'text' => $url,
|
1353 |
+
'attributes' => array(
|
1354 |
+
'href' => $url,
|
1355 |
+
),
|
1356 |
+
),
|
1357 |
+
);
|
1358 |
+
}
|
1359 |
+
}
|
1360 |
+
|
1361 |
+
# ~
|
1362 |
+
|
1363 |
+
protected function unmarkedText($text)
|
1364 |
+
{
|
1365 |
+
if ($this->breaksEnabled)
|
1366 |
+
{
|
1367 |
+
$text = preg_replace('/[ ]*\n/', "<br />\n", $text);
|
1368 |
+
}
|
1369 |
+
else
|
1370 |
+
{
|
1371 |
+
$text = preg_replace('/(?:[ ][ ]+|[ ]*\\\\)\n/', "<br />\n", $text);
|
1372 |
+
$text = str_replace(" \n", "\n", $text);
|
1373 |
+
}
|
1374 |
+
|
1375 |
+
return $text;
|
1376 |
+
}
|
1377 |
+
|
1378 |
+
#
|
1379 |
+
# Handlers
|
1380 |
+
#
|
1381 |
+
|
1382 |
+
protected function element(array $Element)
|
1383 |
+
{
|
1384 |
+
$markup = '<'.$Element['name'];
|
1385 |
+
|
1386 |
+
if (isset($Element['attributes']))
|
1387 |
+
{
|
1388 |
+
foreach ($Element['attributes'] as $name => $value)
|
1389 |
+
{
|
1390 |
+
if ($value === null)
|
1391 |
+
{
|
1392 |
+
continue;
|
1393 |
+
}
|
1394 |
+
|
1395 |
+
$markup .= ' '.$name.'="'.$value.'"';
|
1396 |
+
}
|
1397 |
+
}
|
1398 |
+
|
1399 |
+
if (isset($Element['text']))
|
1400 |
+
{
|
1401 |
+
$markup .= '>';
|
1402 |
+
|
1403 |
+
if (isset($Element['handler']))
|
1404 |
+
{
|
1405 |
+
$markup .= $this->{$Element['handler']}($Element['text']);
|
1406 |
+
}
|
1407 |
+
else
|
1408 |
+
{
|
1409 |
+
$markup .= $Element['text'];
|
1410 |
+
}
|
1411 |
+
|
1412 |
+
$markup .= '</'.$Element['name'].'>';
|
1413 |
+
}
|
1414 |
+
else
|
1415 |
+
{
|
1416 |
+
$markup .= ' />';
|
1417 |
+
}
|
1418 |
+
|
1419 |
+
return $markup;
|
1420 |
+
}
|
1421 |
+
|
1422 |
+
protected function elements(array $Elements)
|
1423 |
+
{
|
1424 |
+
$markup = '';
|
1425 |
+
|
1426 |
+
foreach ($Elements as $Element)
|
1427 |
+
{
|
1428 |
+
$markup .= "\n" . $this->element($Element);
|
1429 |
+
}
|
1430 |
+
|
1431 |
+
$markup .= "\n";
|
1432 |
+
|
1433 |
+
return $markup;
|
1434 |
+
}
|
1435 |
+
|
1436 |
+
# ~
|
1437 |
+
|
1438 |
+
protected function li($lines)
|
1439 |
+
{
|
1440 |
+
$markup = $this->lines($lines);
|
1441 |
+
|
1442 |
+
$trimmedMarkup = trim($markup);
|
1443 |
+
|
1444 |
+
if ( ! in_array('', $lines) and substr($trimmedMarkup, 0, 3) === '<p>')
|
1445 |
+
{
|
1446 |
+
$markup = $trimmedMarkup;
|
1447 |
+
$markup = substr($markup, 3);
|
1448 |
+
|
1449 |
+
$position = strpos($markup, "</p>");
|
1450 |
+
|
1451 |
+
$markup = substr_replace($markup, '', $position, 4);
|
1452 |
+
}
|
1453 |
+
|
1454 |
+
return $markup;
|
1455 |
+
}
|
1456 |
+
|
1457 |
+
#
|
1458 |
+
# Deprecated Methods
|
1459 |
+
#
|
1460 |
+
|
1461 |
+
function parse($text)
|
1462 |
+
{
|
1463 |
+
$markup = $this->text($text);
|
1464 |
+
|
1465 |
+
return $markup;
|
1466 |
+
}
|
1467 |
+
|
1468 |
+
#
|
1469 |
+
# Static Methods
|
1470 |
+
#
|
1471 |
+
|
1472 |
+
static function instance($name = 'default')
|
1473 |
+
{
|
1474 |
+
if (isset(self::$instances[$name]))
|
1475 |
+
{
|
1476 |
+
return self::$instances[$name];
|
1477 |
+
}
|
1478 |
+
|
1479 |
+
$instance = new static();
|
1480 |
+
|
1481 |
+
self::$instances[$name] = $instance;
|
1482 |
+
|
1483 |
+
return $instance;
|
1484 |
+
}
|
1485 |
+
|
1486 |
+
private static $instances = array();
|
1487 |
+
|
1488 |
+
#
|
1489 |
+
# Fields
|
1490 |
+
#
|
1491 |
+
|
1492 |
+
protected $DefinitionData;
|
1493 |
+
|
1494 |
+
#
|
1495 |
+
# Read-Only
|
1496 |
+
|
1497 |
+
protected $specialCharacters = array(
|
1498 |
+
'\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!', '|',
|
1499 |
+
);
|
1500 |
+
|
1501 |
+
protected $StrongRegex = array(
|
1502 |
+
'*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s',
|
1503 |
+
'_' => '/^__((?:\\\\_|[^_]|_[^_]*_)+?)__(?!_)/us',
|
1504 |
+
);
|
1505 |
+
|
1506 |
+
protected $EmRegex = array(
|
1507 |
+
'*' => '/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s',
|
1508 |
+
'_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us',
|
1509 |
+
);
|
1510 |
+
|
1511 |
+
protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*(?:\s*=\s*(?:[^"\'=<>`\s]+|"[^"]*"|\'[^\']*\'))?';
|
1512 |
+
|
1513 |
+
protected $voidElements = array(
|
1514 |
+
'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source',
|
1515 |
+
);
|
1516 |
+
|
1517 |
+
protected $textLevelElements = array(
|
1518 |
+
'a', 'br', 'bdo', 'abbr', 'blink', 'nextid', 'acronym', 'basefont',
|
1519 |
+
'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing',
|
1520 |
+
'i', 'rp', 'del', 'code', 'strike', 'marquee',
|
1521 |
+
'q', 'rt', 'ins', 'font', 'strong',
|
1522 |
+
's', 'tt', 'sub', 'mark',
|
1523 |
+
'u', 'xm', 'sup', 'nobr',
|
1524 |
+
'var', 'ruby',
|
1525 |
+
'wbr', 'span',
|
1526 |
+
'time',
|
1527 |
+
);
|
1528 |
}
|
framework/core/components/extensions/manager/static/extension-page.css
CHANGED
@@ -1,23 +1,23 @@
|
|
1 |
-
#fw-extension-page .fw-extension-page-title {
|
2 |
-
padding-right: 0;
|
3 |
-
}
|
4 |
-
|
5 |
-
#fw-extension-page .fw-extension-page-title .button,
|
6 |
-
#fw-extension-page .fw-extension-page-title .button-primary,
|
7 |
-
#fw-extension-page .fw-extension-page-title .button-primary:active {
|
8 |
-
vertical-align: middle;
|
9 |
-
}
|
10 |
-
|
11 |
-
#fw-extension-page .fw-flash-message + br {
|
12 |
-
display: none;
|
13 |
-
}
|
14 |
-
|
15 |
-
|
16 |
-
#fw-extension-docs.fw-postbox > .insider {
|
17 |
-
margin-top: 0 !important;
|
18 |
-
}
|
19 |
-
|
20 |
-
#fw-extension-docs hr {
|
21 |
-
margin-left: -27px;
|
22 |
-
margin-right: -27px;
|
23 |
}
|
1 |
+
#fw-extension-page .fw-extension-page-title {
|
2 |
+
padding-right: 0;
|
3 |
+
}
|
4 |
+
|
5 |
+
#fw-extension-page .fw-extension-page-title .button,
|
6 |
+
#fw-extension-page .fw-extension-page-title .button-primary,
|
7 |
+
#fw-extension-page .fw-extension-page-title .button-primary:active {
|
8 |
+
vertical-align: middle;
|
9 |
+
}
|
10 |
+
|
11 |
+
#fw-extension-page .fw-flash-message + br {
|
12 |
+
display: none;
|
13 |
+
}
|
14 |
+
|
15 |
+
|
16 |
+
#fw-extension-docs.fw-postbox > .insider {
|
17 |
+
margin-top: 0 !important;
|
18 |
+
}
|
19 |
+
|
20 |
+
#fw-extension-docs hr {
|
21 |
+
margin-left: -27px;
|
22 |
+
margin-right: -27px;
|
23 |
}
|
framework/core/components/extensions/manager/static/extension-page.js
CHANGED
@@ -1,3 +1,3 @@
|
|
1 |
-
jQuery(function($){
|
2 |
-
$('#fw-extension-docs a:fw-external').attr('target', '_blank');
|
3 |
});
|
1 |
+
jQuery(function($){
|
2 |
+
$('#fw-extension-docs a:fw-external').attr('target', '_blank');
|
3 |
});
|
framework/core/components/extensions/manager/static/extensions-page.css
CHANGED
@@ -1,236 +1,236 @@
|
|
1 |
-
.fw-extensions-list a {
|
2 |
-
text-decoration: none;
|
3 |
-
}
|
4 |
-
|
5 |
-
.fw-extensions-no-active {
|
6 |
-
margin: 75px 0;
|
7 |
-
}
|
8 |
-
|
9 |
-
.fw-extensions-no-active .fw-text-muted {
|
10 |
-
color: #9d9d9d;
|
11 |
-
}
|
12 |
-
|
13 |
-
.fw-extensions-no-active .fw-extensions-title-icon .dashicons {
|
14 |
-
color: #d3d3d3;
|
15 |
-
font-size: 46px;
|
16 |
-
width: auto;
|
17 |
-
height: auto;
|
18 |
-
line-height: 35px;
|
19 |
-
}
|
20 |
-
|
21 |
-
.fw-extensions-list .fw-extensions-list-item {
|
22 |
-
padding: 0 15px 15px 0;
|
23 |
-
vertical-align: top;
|
24 |
-
display: inline-block;
|
25 |
-
float: none;
|
26 |
-
margin: 0 -1px;
|
27 |
-
}
|
28 |
-
|
29 |
-
.fw-extensions-list .fw-extensions-list-item > .inner {
|
30 |
-
padding: 20px;
|
31 |
-
background-color: #ffffff;
|
32 |
-
border: 1px solid #dedede;
|
33 |
-
position: relative;
|
34 |
-
}
|
35 |
-
|
36 |
-
.fw-extensions-list .fw-extensions-list-item > .inner > p:last-child {
|
37 |
-
margin-bottom: 0;
|
38 |
-
}
|
39 |
-
|
40 |
-
.fw-extensions-list .fw-extensions-list-item .fw-extensions-list-item-thumbnail-wrapper {
|
41 |
-
height: 128px;
|
42 |
-
width: 128px;
|
43 |
-
text-align: center;
|
44 |
-
background: url('img/thumbnail-bg.jpg');
|
45 |
-
background-size: 128px;
|
46 |
-
}
|
47 |
-
|
48 |
-
.fw-extensions-list .fw-extensions-list-item .fw-extensions-list-item-thumbnail-wrapper img.fw-extensions-list-item-thumbnail {
|
49 |
-
display: block;
|
50 |
-
height: 100%;
|
51 |
-
}
|
52 |
-
|
53 |
-
.fw-extensions-list .fw-extensions-list-item .fw-extensions-list-item-thumbnail-wrapper span.fw-extensions-list-item-thumbnail {
|
54 |
-
display: inline;
|
55 |
-
vertical-align: middle;
|
56 |
-
color: #fff;
|
57 |
-
line-height: 128px;
|
58 |
-
font-size: 42px;
|
59 |
-
}
|
60 |
-
|
61 |
-
.fw-extensions-list .fw-extensions-list-item .fw-extensions-list-item-thumbnail-wrapper span.fw-extensions-list-item-thumbnail.dashicons {
|
62 |
-
font-size: 48px;
|
63 |
-
}
|
64 |
-
|
65 |
-
.fw-extensions-list .fw-extensions-list-item .fw-extensions-list-item-title {
|
66 |
-
margin-top: 0;
|
67 |
-
}
|
68 |
-
|
69 |
-
.fw-extensions-list .fw-extensions-list-item .fw-extensions-list-item-title:last-child {
|
70 |
-
margin-bottom: 0;
|
71 |
-
}
|
72 |
-
|
73 |
-
|
74 |
-
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table {
|
75 |
-
display: table;
|
76 |
-
width: 100%;
|
77 |
-
}
|
78 |
-
|
79 |
-
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row {
|
80 |
-
display: table-row;
|
81 |
-
}
|
82 |
-
|
83 |
-
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row > .fw-extension-list-item-table-cell {
|
84 |
-
display: table-cell;
|
85 |
-
vertical-align: top;
|
86 |
-
}
|
87 |
-
|
88 |
-
/*.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row > .fw-extension-list-item-table-cell.cell-2,
|
89 |
-
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row > .fw-extension-list-item-table-cell.cell-3 {
|
90 |
-
vertical-align: middle;
|
91 |
-
}*/
|
92 |
-
|
93 |
-
@media (max-width: 782px) {
|
94 |
-
.fw-extensions-list .fw-extensions-list-item {
|
95 |
-
padding-right: 0;
|
96 |
-
}
|
97 |
-
|
98 |
-
.fw-extensions-list .fw-extensions-list-item .fw-text-center {
|
99 |
-
text-align: left;
|
100 |
-
}
|
101 |
-
|
102 |
-
body.rtl .fw-extensions-list .fw-extensions-list-item .fw-text-center {
|
103 |
-
text-align: right;
|
104 |
-
}
|
105 |
-
|
106 |
-
.fw-extensions-list .fw-extensions-list-item .fw-extensions-list-item-title {
|
107 |
-
margin-top: 20px;
|
108 |
-
}
|
109 |
-
|
110 |
-
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table,
|
111 |
-
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row,
|
112 |
-
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row > .fw-extension-list-item-table-cell {
|
113 |
-
display: block;
|
114 |
-
}
|
115 |
-
|
116 |
-
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row > .fw-extension-list-item-table-cell.cell-3 form,
|
117 |
-
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row > .fw-extension-list-item-table-cell.cell-3 form .button {
|
118 |
-
margin-bottom: 0;
|
119 |
-
}
|
120 |
-
}
|
121 |
-
|
122 |
-
@media (min-width: 783px) {
|
123 |
-
.fw-extensions-list .fw-extensions-list-item .fw-extensions-list-item-title {
|
124 |
-
margin-top: 5px;
|
125 |
-
}
|
126 |
-
|
127 |
-
hr.fw-extensions-lists-separator {
|
128 |
-
margin: 22px 0 30px;
|
129 |
-
margin-right: 15px; /* same as .fw-extensions-list .fw-extensions-list-item padding-right */
|
130 |
-
}
|
131 |
-
|
132 |
-
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row > .fw-extension-list-item-table-cell:first-child,
|
133 |
-
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row > .fw-extension-list-item-table-cell:last-child {
|
134 |
-
width: 10px;
|
135 |
-
}
|
136 |
-
|
137 |
-
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row > .fw-extension-list-item-table-cell:not(:first-child) {
|
138 |
-
padding-left: 20px;
|
139 |
-
}
|
140 |
-
|
141 |
-
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row > .fw-extension-list-item-table-cell > p:last-child {
|
142 |
-
margin-bottom: 0;
|
143 |
-
}
|
144 |
-
|
145 |
-
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row > .fw-extension-list-item-table-cell.cell-3 {
|
146 |
-
display: block;
|
147 |
-
position: absolute;
|
148 |
-
top: 20px;
|
149 |
-
right: 20px;
|
150 |
-
width: auto;
|
151 |
-
}
|
152 |
-
|
153 |
-
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row > .fw-extension-list-item-table-cell.cell-3 form {
|
154 |
-
display: inline-block;
|
155 |
-
}
|
156 |
-
|
157 |
-
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row > .fw-extension-list-item-table-cell.cell-3 form.extension-delete-form {
|
158 |
-
padding: 0 0 4px 15px;
|
159 |
-
vertical-align: bottom;
|
160 |
-
}
|
161 |
-
|
162 |
-
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row > .fw-extension-list-item-table-cell.cell-3 form.extension-delete-form .btn-icon {
|
163 |
-
font-size: 16px;
|
164 |
-
}
|
165 |
-
}
|
166 |
-
|
167 |
-
|
168 |
-
/* disabled style */
|
169 |
-
|
170 |
-
.fw-extensions-list .fw-extensions-list-item .fw-extension-disabled {
|
171 |
-
display: none;
|
172 |
-
background: rgba(255, 255, 255, 0.5) url("img/disabled-bg.png");
|
173 |
-
border: 1px solid #ffffff;
|
174 |
-
position: absolute;
|
175 |
-
top: 0;
|
176 |
-
left: 0;
|
177 |
-
width: 100%;
|
178 |
-
height: 100%;
|
179 |
-
}
|
180 |
-
|
181 |
-
.fw-extensions-list .fw-extensions-list-item.disabled .fw-extension-disabled {
|
182 |
-
display: block;
|
183 |
-
}
|
184 |
-
|
185 |
-
.fw-extensions-list .fw-extensions-list-item .fw-extension-disabled .fw-extension-disabled-panel {
|
186 |
-
position: absolute;
|
187 |
-
left: 0;
|
188 |
-
bottom: 0;
|
189 |
-
width: 100%;
|
190 |
-
background: #ffffff;
|
191 |
-
padding: 20px;
|
192 |
-
line-height: 28px;
|
193 |
-
}
|
194 |
-
|
195 |
-
@media (max-width: 782px) {
|
196 |
-
.fw-extensions-list .fw-extensions-list-item.disabled > .inner {
|
197 |
-
min-height: 320px;
|
198 |
-
}
|
199 |
-
}
|
200 |
-
|
201 |
-
/* end: disabled style */
|
202 |
-
|
203 |
-
|
204 |
-
/* tip content */
|
205 |
-
|
206 |
-
.fw-extension-tip-content {
|
207 |
-
padding: 15px;
|
208 |
-
max-width: 380px;
|
209 |
-
}
|
210 |
-
|
211 |
-
.fw-extension-tip-content ul.fw-extension-requirements {
|
212 |
-
margin: 0;
|
213 |
-
}
|
214 |
-
|
215 |
-
.fw-extension-tip-content ul.fw-extension-requirements li:last-child {
|
216 |
-
margin-bottom: 0;
|
217 |
-
}
|
218 |
-
|
219 |
-
/* end: tip content */
|
220 |
-
|
221 |
-
|
222 |
-
/* form ajax loading */
|
223 |
-
|
224 |
-
.fw-extensions-list .fw-extensions-list-item .ajax-form-loading {
|
225 |
-
display: inline-block;
|
226 |
-
margin: -2px 0;
|
227 |
-
padding-left: 7px;
|
228 |
-
}
|
229 |
-
|
230 |
-
.fw-extensions-list .fw-extensions-list-item .ajax-form-loading img {
|
231 |
-
vertical-align: middle;
|
232 |
-
display: inline-block;
|
233 |
-
margin-top: -2px;
|
234 |
-
}
|
235 |
-
|
236 |
-
/* end: form ajax loading */
|
1 |
+
.fw-extensions-list a {
|
2 |
+
text-decoration: none;
|
3 |
+
}
|
4 |
+
|
5 |
+
.fw-extensions-no-active {
|
6 |
+
margin: 75px 0;
|
7 |
+
}
|
8 |
+
|
9 |
+
.fw-extensions-no-active .fw-text-muted {
|
10 |
+
color: #9d9d9d;
|
11 |
+
}
|
12 |
+
|
13 |
+
.fw-extensions-no-active .fw-extensions-title-icon .dashicons {
|
14 |
+
color: #d3d3d3;
|
15 |
+
font-size: 46px;
|
16 |
+
width: auto;
|
17 |
+
height: auto;
|
18 |
+
line-height: 35px;
|
19 |
+
}
|
20 |
+
|
21 |
+
.fw-extensions-list .fw-extensions-list-item {
|
22 |
+
padding: 0 15px 15px 0;
|
23 |
+
vertical-align: top;
|
24 |
+
display: inline-block;
|
25 |
+
float: none;
|
26 |
+
margin: 0 -1px;
|
27 |
+
}
|
28 |
+
|
29 |
+
.fw-extensions-list .fw-extensions-list-item > .inner {
|
30 |
+
padding: 20px;
|
31 |
+
background-color: #ffffff;
|
32 |
+
border: 1px solid #dedede;
|
33 |
+
position: relative;
|
34 |
+
}
|
35 |
+
|
36 |
+
.fw-extensions-list .fw-extensions-list-item > .inner > p:last-child {
|
37 |
+
margin-bottom: 0;
|
38 |
+
}
|
39 |
+
|
40 |
+
.fw-extensions-list .fw-extensions-list-item .fw-extensions-list-item-thumbnail-wrapper {
|
41 |
+
height: 128px;
|
42 |
+
width: 128px;
|
43 |
+
text-align: center;
|
44 |
+
background: url('img/thumbnail-bg.jpg');
|
45 |
+
background-size: 128px;
|
46 |
+
}
|
47 |
+
|
48 |
+
.fw-extensions-list .fw-extensions-list-item .fw-extensions-list-item-thumbnail-wrapper img.fw-extensions-list-item-thumbnail {
|
49 |
+
display: block;
|
50 |
+
height: 100%;
|
51 |
+
}
|
52 |
+
|
53 |
+
.fw-extensions-list .fw-extensions-list-item .fw-extensions-list-item-thumbnail-wrapper span.fw-extensions-list-item-thumbnail {
|
54 |
+
display: inline;
|
55 |
+
vertical-align: middle;
|
56 |
+
color: #fff;
|
57 |
+
line-height: 128px;
|
58 |
+
font-size: 42px;
|
59 |
+
}
|
60 |
+
|
61 |
+
.fw-extensions-list .fw-extensions-list-item .fw-extensions-list-item-thumbnail-wrapper span.fw-extensions-list-item-thumbnail.dashicons {
|
62 |
+
font-size: 48px;
|
63 |
+
}
|
64 |
+
|
65 |
+
.fw-extensions-list .fw-extensions-list-item .fw-extensions-list-item-title {
|
66 |
+
margin-top: 0;
|
67 |
+
}
|
68 |
+
|
69 |
+
.fw-extensions-list .fw-extensions-list-item .fw-extensions-list-item-title:last-child {
|
70 |
+
margin-bottom: 0;
|
71 |
+
}
|
72 |
+
|
73 |
+
|
74 |
+
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table {
|
75 |
+
display: table;
|
76 |
+
width: 100%;
|
77 |
+
}
|
78 |
+
|
79 |
+
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row {
|
80 |
+
display: table-row;
|
81 |
+
}
|
82 |
+
|
83 |
+
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row > .fw-extension-list-item-table-cell {
|
84 |
+
display: table-cell;
|
85 |
+
vertical-align: top;
|
86 |
+
}
|
87 |
+
|
88 |
+
/*.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row > .fw-extension-list-item-table-cell.cell-2,
|
89 |
+
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row > .fw-extension-list-item-table-cell.cell-3 {
|
90 |
+
vertical-align: middle;
|
91 |
+
}*/
|
92 |
+
|
93 |
+
@media (max-width: 782px) {
|
94 |
+
.fw-extensions-list .fw-extensions-list-item {
|
95 |
+
padding-right: 0;
|
96 |
+
}
|
97 |
+
|
98 |
+
.fw-extensions-list .fw-extensions-list-item .fw-text-center {
|
99 |
+
text-align: left;
|
100 |
+
}
|
101 |
+
|
102 |
+
body.rtl .fw-extensions-list .fw-extensions-list-item .fw-text-center {
|
103 |
+
text-align: right;
|
104 |
+
}
|
105 |
+
|
106 |
+
.fw-extensions-list .fw-extensions-list-item .fw-extensions-list-item-title {
|
107 |
+
margin-top: 20px;
|
108 |
+
}
|
109 |
+
|
110 |
+
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table,
|
111 |
+
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row,
|
112 |
+
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row > .fw-extension-list-item-table-cell {
|
113 |
+
display: block;
|
114 |
+
}
|
115 |
+
|
116 |
+
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row > .fw-extension-list-item-table-cell.cell-3 form,
|
117 |
+
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row > .fw-extension-list-item-table-cell.cell-3 form .button {
|
118 |
+
margin-bottom: 0;
|
119 |
+
}
|
120 |
+
}
|
121 |
+
|
122 |
+
@media (min-width: 783px) {
|
123 |
+
.fw-extensions-list .fw-extensions-list-item .fw-extensions-list-item-title {
|
124 |
+
margin-top: 5px;
|
125 |
+
}
|
126 |
+
|
127 |
+
hr.fw-extensions-lists-separator {
|
128 |
+
margin: 22px 0 30px;
|
129 |
+
margin-right: 15px; /* same as .fw-extensions-list .fw-extensions-list-item padding-right */
|
130 |
+
}
|
131 |
+
|
132 |
+
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row > .fw-extension-list-item-table-cell:first-child,
|
133 |
+
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row > .fw-extension-list-item-table-cell:last-child {
|
134 |
+
width: 10px;
|
135 |
+
}
|
136 |
+
|
137 |
+
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row > .fw-extension-list-item-table-cell:not(:first-child) {
|
138 |
+
padding-left: 20px;
|
139 |
+
}
|
140 |
+
|
141 |
+
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row > .fw-extension-list-item-table-cell > p:last-child {
|
142 |
+
margin-bottom: 0;
|
143 |
+
}
|
144 |
+
|
145 |
+
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row > .fw-extension-list-item-table-cell.cell-3 {
|
146 |
+
display: block;
|
147 |
+
position: absolute;
|
148 |
+
top: 20px;
|
149 |
+
right: 20px;
|
150 |
+
width: auto;
|
151 |
+
}
|
152 |
+
|
153 |
+
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row > .fw-extension-list-item-table-cell.cell-3 form {
|
154 |
+
display: inline-block;
|
155 |
+
}
|
156 |
+
|
157 |
+
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row > .fw-extension-list-item-table-cell.cell-3 form.extension-delete-form {
|
158 |
+
padding: 0 0 4px 15px;
|
159 |
+
vertical-align: bottom;
|
160 |
+
}
|
161 |
+
|
162 |
+
.fw-extensions-list .fw-extensions-list-item .fw-extension-list-item-table > .fw-extension-list-item-table-row > .fw-extension-list-item-table-cell.cell-3 form.extension-delete-form .btn-icon {
|
163 |
+
font-size: 16px;
|
164 |
+
}
|
165 |
+
}
|
166 |
+
|
167 |
+
|
168 |
+
/* disabled style */
|
169 |
+
|
170 |
+
.fw-extensions-list .fw-extensions-list-item .fw-extension-disabled {
|
171 |
+
display: none;
|
172 |
+
background: rgba(255, 255, 255, 0.5) url("img/disabled-bg.png");
|
173 |
+
border: 1px solid #ffffff;
|
174 |
+
position: absolute;
|
175 |
+
top: 0;
|
176 |
+
left: 0;
|
177 |
+
width: 100%;
|
178 |
+
height: 100%;
|
179 |
+
}
|
180 |
+
|
181 |
+
.fw-extensions-list .fw-extensions-list-item.disabled .fw-extension-disabled {
|
182 |
+
display: block;
|
183 |
+
}
|
184 |
+
|
185 |
+
.fw-extensions-list .fw-extensions-list-item .fw-extension-disabled .fw-extension-disabled-panel {
|
186 |
+
position: absolute;
|
187 |
+
left: 0;
|
188 |
+
bottom: 0;
|
189 |
+
width: 100%;
|
190 |
+
background: #ffffff;
|
191 |
+
padding: 20px;
|
192 |
+
line-height: 28px;
|
193 |
+
}
|
194 |
+
|
195 |
+
@media (max-width: 782px) {
|
196 |
+
.fw-extensions-list .fw-extensions-list-item.disabled > .inner {
|
197 |
+
min-height: 320px;
|
198 |
+
}
|
199 |
+
}
|
200 |
+
|
201 |
+
/* end: disabled style */
|
202 |
+
|
203 |
+
|
204 |
+
/* tip content */
|
205 |
+
|
206 |
+
.fw-extension-tip-content {
|
207 |
+
padding: 15px;
|
208 |
+
max-width: 380px;
|
209 |
+
}
|
210 |
+
|
211 |
+
.fw-extension-tip-content ul.fw-extension-requirements {
|
212 |
+
margin: 0;
|
213 |
+
}
|
214 |
+
|
215 |
+
.fw-extension-tip-content ul.fw-extension-requirements li:last-child {
|
216 |
+
margin-bottom: 0;
|
217 |
+
}
|
218 |
+
|
219 |
+
/* end: tip content */
|
220 |
+
|
221 |
+
|
222 |
+
/* form ajax loading */
|
223 |
+
|
224 |
+
.fw-extensions-list .fw-extensions-list-item .ajax-form-loading {
|
225 |
+
display: inline-block;
|
226 |
+
margin: -2px 0;
|
227 |
+
padding-left: 7px;
|
228 |
+
}
|
229 |
+
|
230 |
+
.fw-extensions-list .fw-extensions-list-item .ajax-form-loading img {
|
231 |
+
vertical-align: middle;
|
232 |
+
display: inline-block;
|
233 |
+
margin-top: -2px;
|
234 |
+
}
|
235 |
+
|
236 |
+
/* end: form ajax loading */
|
framework/core/components/extensions/manager/static/extensions-page.js
CHANGED
@@ -1,108 +1,108 @@
|
|
1 |
-
jQuery(function ($) {
|
2 |
-
fw.qtip( $('.fw-extensions-list .fw-extensions-list-item .fw-extension-tip') );
|
3 |
-
});
|
4 |
-
|
5 |
-
/**
|
6 |
-
* Install/Remove/... via popup if has direct filesystem access (no ftp credentials required)
|
7 |
-
*/
|
8 |
-
jQuery(function($){
|
9 |
-
var inst = {
|
10 |
-
isBusy: false,
|
11 |
-
eventNamespace: '.fw-extension',
|
12 |
-
$wrapper: $('.wrap'),
|
13 |
-
listenSubmit: function() {
|
14 |
-
this.$wrapper.on('submit'+ this.eventNamespace, 'form.fw-extension-ajax-form', this.onSubmit);
|
15 |
-
},
|
16 |
-
stopListeningSubmit: function() {
|
17 |
-
this.$wrapper.off('submit'+ this.eventNamespace, 'form.fw-extension-ajax-form');
|
18 |
-
},
|
19 |
-
onSubmit: function(e) {
|
20 |
-
e.preventDefault();
|
21 |
-
|
22 |
-
if (inst.isBusy) {
|
23 |
-
alert('Working... Please try again later');
|
24 |
-
return;
|
25 |
-
}
|
26 |
-
|
27 |
-
var $form = $(this);
|
28 |
-
|
29 |
-
var confirmMessage = $form.attr('data-confirm-message');
|
30 |
-
|
31 |
-
inst.isBusy = true;
|
32 |
-
inst.loading($form, true);
|
33 |
-
|
34 |
-
$.ajax({
|
35 |
-
url: ajaxurl,
|
36 |
-
type: 'POST',
|
37 |
-
data: {
|
38 |
-
action: 'fw_extensions_check_direct_fs_access'
|
39 |
-
},
|
40 |
-
dataType: 'json'
|
41 |
-
}).done(function(data){
|
42 |
-
if (data.success) {
|
43 |
-
if (confirmMessage) {
|
44 |
-
if (!confirm(confirmMessage)) {
|
45 |
-
inst.isBusy = false;
|
46 |
-
inst.loading($form, false);
|
47 |
-
}
|
48 |
-
}
|
49 |
-
|
50 |
-
$.ajax({
|
51 |
-
url: ajaxurl,
|
52 |
-
type: 'POST',
|
53 |
-
data: {
|
54 |
-
action: 'fw_extensions_'+ $form.attr('data-extension-action'),
|
55 |
-
extension: $form.attr('data-extension-name')
|
56 |
-
},
|
57 |
-
dataType: 'json'
|
58 |
-
}).done(function(r) {
|
59 |
-
if (r.success) {
|
60 |
-
window.location.reload();
|
61 |
-
} else {
|
62 |
-
var error = r.data ? r.data.pop().message : 'Error';
|
63 |
-
|
64 |
-
fw.soleModal.show(
|
65 |
-
'fw-extension-install-error',
|
66 |
-
'<p class="fw-text-danger">'+ error +'</p>'
|
67 |
-
);
|
68 |
-
}
|
69 |
-
}).fail(function(jqXHR, textStatus, errorThrown){
|
70 |
-
fw.soleModal.show(
|
71 |
-
'fw-extension-install-error',
|
72 |
-
'<p class="fw-text-danger">'+ String(errorThrown) +'</p>'
|
73 |
-
);
|
74 |
-
inst.isBusy = false;
|
75 |
-
inst.loading($form, false);
|
76 |
-
});
|
77 |
-
} else {
|
78 |
-
inst.stopListeningSubmit();
|
79 |
-
$form.submit();
|
80 |
-
}
|
81 |
-
}).fail(function(jqXHR, textStatus, errorThrown){
|
82 |
-
inst.stopListeningSubmit();
|
83 |
-
$form.submit();
|
84 |
-
});
|
85 |
-
},
|
86 |
-
loading: function($form, show) {
|
87 |
-
var $loadingContainer = $form.closest('.fw-extensions-list-item').find('.fw-extensions-list-item-title').first();
|
88 |
-
var $loading = $loadingContainer.find('.ajax-form-loading');
|
89 |
-
|
90 |
-
if (!$loading.length) {
|
91 |
-
$loadingContainer.append(
|
92 |
-
'<span class="ajax-form-loading fw-text-center fw-hidden">'+
|
93 |
-
'<img src="'+ fw.img.loadingSpinner +'" />'+
|
94 |
-
'</span>'
|
95 |
-
);
|
96 |
-
$loading = $loadingContainer.find('.ajax-form-loading');
|
97 |
-
}
|
98 |
-
|
99 |
-
if (show) {
|
100 |
-
$loading.removeClass('fw-hidden');
|
101 |
-
} else {
|
102 |
-
$loading.addClass('fw-hidden');
|
103 |
-
}
|
104 |
-
}
|
105 |
-
};
|
106 |
-
|
107 |
-
inst.listenSubmit();
|
108 |
});
|
1 |
+
jQuery(function ($) {
|
2 |
+
fw.qtip( $('.fw-extensions-list .fw-extensions-list-item .fw-extension-tip') );
|
3 |
+
});
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Install/Remove/... via popup if has direct filesystem access (no ftp credentials required)
|
7 |
+
*/
|
8 |
+
jQuery(function($){
|
9 |
+
var inst = {
|
10 |
+
isBusy: false,
|
11 |
+
eventNamespace: '.fw-extension',
|
12 |
+
$wrapper: $('.wrap'),
|
13 |
+
listenSubmit: function() {
|
14 |
+
this.$wrapper.on('submit'+ this.eventNamespace, 'form.fw-extension-ajax-form', this.onSubmit);
|
15 |
+
},
|
16 |
+
stopListeningSubmit: function() {
|
17 |
+
this.$wrapper.off('submit'+ this.eventNamespace, 'form.fw-extension-ajax-form');
|
18 |
+
},
|
19 |
+
onSubmit: function(e) {
|
20 |
+
e.preventDefault();
|
21 |
+
|
22 |
+
if (inst.isBusy) {
|
23 |
+
alert('Working... Please try again later');
|
24 |
+
return;
|
25 |
+
}
|
26 |
+
|
27 |
+
var $form = $(this);
|
28 |
+
|
29 |
+
var confirmMessage = $form.attr('data-confirm-message');
|
30 |
+
|
31 |
+
inst.isBusy = true;
|
32 |
+
inst.loading($form, true);
|
33 |
+
|
34 |
+
$.ajax({
|
35 |
+
url: ajaxurl,
|
36 |
+
type: 'POST',
|
37 |
+
data: {
|
38 |
+
action: 'fw_extensions_check_direct_fs_access'
|
39 |
+
},
|
40 |
+
dataType: 'json'
|
41 |
+
}).done(function(data){
|
42 |
+
if (data.success) {
|
43 |
+
if (confirmMessage) {
|
44 |
+
if (!confirm(confirmMessage)) {
|
45 |
+
inst.isBusy = false;
|
46 |
+
inst.loading($form, false);
|
47 |
+
}
|
48 |
+
}
|
49 |
+
|
50 |
+
$.ajax({
|
51 |
+
url: ajaxurl,
|
52 |
+
type: 'POST',
|
53 |
+
data: {
|
54 |
+
action: 'fw_extensions_'+ $form.attr('data-extension-action'),
|
55 |
+
extension: $form.attr('data-extension-name')
|
56 |
+
},
|
57 |
+
dataType: 'json'
|
58 |
+
}).done(function(r) {
|
59 |
+
if (r.success) {
|
60 |
+
window.location.reload();
|
61 |
+
} else {
|
62 |
+
var error = r.data ? r.data.pop().message : 'Error';
|
63 |
+
|
64 |
+
fw.soleModal.show(
|
65 |
+
'fw-extension-install-error',
|
66 |
+
'<p class="fw-text-danger">'+ error +'</p>'
|
67 |
+
);
|
68 |
+
}
|
69 |
+
}).fail(function(jqXHR, textStatus, errorThrown){
|
70 |
+
fw.soleModal.show(
|
71 |
+
'fw-extension-install-error',
|
72 |
+
'<p class="fw-text-danger">'+ String(errorThrown) +'</p>'
|
73 |
+
);
|
74 |
+
inst.isBusy = false;
|
75 |
+
inst.loading($form, false);
|
76 |
+
});
|
77 |
+
} else {
|
78 |
+
inst.stopListeningSubmit();
|
79 |
+
$form.submit();
|
80 |
+
}
|
81 |
+
}).fail(function(jqXHR, textStatus, errorThrown){
|
82 |
+
inst.stopListeningSubmit();
|
83 |
+
$form.submit();
|
84 |
+
});
|
85 |
+
},
|
86 |
+
loading: function($form, show) {
|
87 |
+
var $loadingContainer = $form.closest('.fw-extensions-list-item').find('.fw-extensions-list-item-title').first();
|
88 |
+
var $loading = $loadingContainer.find('.ajax-form-loading');
|
89 |
+
|
90 |
+
if (!$loading.length) {
|
91 |
+
$loadingContainer.append(
|
92 |
+
'<span class="ajax-form-loading fw-text-center fw-hidden">'+
|
93 |
+
'<img src="'+ fw.img.loadingSpinner +'" />'+
|
94 |
+
'</span>'
|
95 |
+
);
|
96 |
+
$loading = $loadingContainer.find('.ajax-form-loading');
|
97 |
+
}
|
98 |
+
|
99 |
+
if (show) {
|
100 |
+
$loading.removeClass('fw-hidden');
|
101 |
+
} else {
|
102 |
+
$loading.addClass('fw-hidden');
|
103 |
+
}
|
104 |
+
}
|
105 |
+
};
|
106 |
+
|
107 |
+
inst.listenSubmit();
|
108 |
});
|
framework/core/components/extensions/manager/static/unyson-font-icon/fonts/icomoon.svg
CHANGED
@@ -1,11 +1,11 @@
|
|
1 |
-
<?xml version="1.0" standalone="no"?>
|
2 |
-
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
3 |
-
<svg xmlns="http://www.w3.org/2000/svg">
|
4 |
-
<metadata>Generated by IcoMoon</metadata>
|
5 |
-
<defs>
|
6 |
-
<font id="icomoon" horiz-adv-x="1024">
|
7 |
-
<font-face units-per-em="1024" ascent="960" descent="-64" />
|
8 |
-
<missing-glyph horiz-adv-x="1024" />
|
9 |
-
<glyph unicode=" " d="" horiz-adv-x="512" />
|
10 |
-
<glyph unicode="" d="M402.322 641.307c8.195 177.384 154.668 318.693 334.198 318.693 184.794 0 334.597-149.702 334.597-334.367 0-5.258-0.166-10.476-0.408-15.674 0 0 0.408 0 0.408 0s0-611.265 0-611.265c0 0-156.842-62.693-156.842-62.693s-146.385 62.693-146.385 62.693c0 0 0 568.788 0 568.788s0 115.621 0 115.621c0 0-0.132 0-0.132 0-0.87 37.687-20.304 70.747-49.501 90.476-27.849-18.815-46.846-49.73-49.306-85.253 0 0-0.396 0-0.396 0s0-433.633 0-433.633zM668.399 254.693c-8.192-177.384-154.577-318.693-334-318.693-184.684 0-334.399 149.702-334.399 334.367 0 5.268 0.295 10.463 0.537 15.674 0 0-0.537 0-0.537 0s0 611.265 0 611.265c0 0 156.747 62.693 156.747 62.693s146.3-62.693 146.3-62.693c0 0 0-568.788 0-568.788s0-115.621 0-115.621c0 0 0.132 0 0.132 0 0.87-37.687 20.292-70.747 49.472-90.476 27.83 18.815 47.867 50.192 50.327 85.714 0 0-0.656-0.462-0.656-0.462s0 433.633 0 433.633z" horiz-adv-x="1071" />
|
11 |
</font></defs></svg>
|
1 |
+
<?xml version="1.0" standalone="no"?>
|
2 |
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
3 |
+
<svg xmlns="http://www.w3.org/2000/svg">
|
4 |
+
<metadata>Generated by IcoMoon</metadata>
|
5 |
+
<defs>
|
6 |
+
<font id="icomoon" horiz-adv-x="1024">
|
7 |
+
<font-face units-per-em="1024" ascent="960" descent="-64" />
|
8 |
+
<missing-glyph horiz-adv-x="1024" />
|
9 |
+
<glyph unicode=" " d="" horiz-adv-x="512" />
|
10 |
+
<glyph unicode="" d="M402.322 641.307c8.195 177.384 154.668 318.693 334.198 318.693 184.794 0 334.597-149.702 334.597-334.367 0-5.258-0.166-10.476-0.408-15.674 0 0 0.408 0 0.408 0s0-611.265 0-611.265c0 0-156.842-62.693-156.842-62.693s-146.385 62.693-146.385 62.693c0 0 0 568.788 0 568.788s0 115.621 0 115.621c0 0-0.132 0-0.132 0-0.87 37.687-20.304 70.747-49.501 90.476-27.849-18.815-46.846-49.73-49.306-85.253 0 0-0.396 0-0.396 0s0-433.633 0-433.633zM668.399 254.693c-8.192-177.384-154.577-318.693-334-318.693-184.684 0-334.399 149.702-334.399 334.367 0 5.268 0.295 10.463 0.537 15.674 0 0-0.537 0-0.537 0s0 611.265 0 611.265c0 0 156.747 62.693 156.747 62.693s146.3-62.693 146.3-62.693c0 0 0-568.788 0-568.788s0-115.621 0-115.621c0 0 0.132 0 0.132 0 0.87-37.687 20.292-70.747 49.472-90.476 27.83 18.815 47.867 50.192 50.327 85.714 0 0-0.656-0.462-0.656-0.462s0 433.633 0 433.633z" horiz-adv-x="1071" />
|
11 |
</font></defs></svg>
|
framework/core/components/extensions/manager/static/unyson-font-icon/style.css
CHANGED
@@ -1,28 +1,28 @@
|
|
1 |
-
@font-face {
|
2 |
-
font-family: 'unyson-font-icon';
|
3 |
-
src:url('fonts/icomoon.eot?iganyx');
|
4 |
-
src:url('fonts/icomoon.eot?#iefixiganyx') format('embedded-opentype'),
|
5 |
-
url('fonts/icomoon.woff?iganyx') format('woff'),
|
6 |
-
url('fonts/icomoon.ttf?iganyx') format('truetype'),
|
7 |
-
url('fonts/icomoon.svg?iganyx#icomoon') format('svg');
|
8 |
-
font-weight: normal;
|
9 |
-
font-style: normal;
|
10 |
-
}
|
11 |
-
|
12 |
-
.toplevel_page_fw-extensions > .wp-menu-image:before {
|
13 |
-
font-family: 'unyson-font-icon';
|
14 |
-
speak: none;
|
15 |
-
font-style: normal;
|
16 |
-
font-weight: normal;
|
17 |
-
font-variant: normal;
|
18 |
-
text-transform: none;
|
19 |
-
line-height: 1;
|
20 |
-
|
21 |
-
/* Better Font Rendering =========== */
|
22 |
-
-webkit-font-smoothing: antialiased;
|
23 |
-
-moz-osx-font-smoothing: grayscale;
|
24 |
-
|
25 |
-
content: "\e600";
|
26 |
-
font-size: 14px;
|
27 |
-
line-height: 20px;
|
28 |
-
}
|
1 |
+
@font-face {
|
2 |
+
font-family: 'unyson-font-icon';
|
3 |
+
src:url('fonts/icomoon.eot?iganyx');
|
4 |
+
src:url('fonts/icomoon.eot?#iefixiganyx') format('embedded-opentype'),
|
5 |
+
url('fonts/icomoon.woff?iganyx') format('woff'),
|
6 |
+
url('fonts/icomoon.ttf?iganyx') format('truetype'),
|
7 |
+
url('fonts/icomoon.svg?iganyx#icomoon') format('svg');
|
8 |
+
font-weight: normal;
|
9 |
+
font-style: normal;
|
10 |
+
}
|
11 |
+
|
12 |
+
.toplevel_page_fw-extensions > .wp-menu-image:before {
|
13 |
+
font-family: 'unyson-font-icon';
|
14 |
+
speak: none;
|
15 |
+
font-style: normal;
|
16 |
+
font-weight: normal;
|
17 |
+
font-variant: normal;
|
18 |
+
text-transform: none;
|
19 |
+
line-height: 1;
|
20 |
+
|
21 |
+
/* Better Font Rendering =========== */
|
22 |
+
-webkit-font-smoothing: antialiased;
|
23 |
+
-moz-osx-font-smoothing: grayscale;
|
24 |
+
|
25 |
+
content: "\e600";
|
26 |
+
font-size: 14px;
|
27 |
+
line-height: 20px;
|
28 |
+
}
|
framework/core/components/extensions/manager/views/delete-form.php
CHANGED
@@ -1,55 +1,55 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
/**
|
3 |
-
* @var array $extension_names
|
4 |
-
* @var array $installed_extensions
|
5 |
-
* @var array $list_page_link
|
6 |
-
*/
|
7 |
-
|
8 |
-
$count = count($extension_names);
|
9 |
-
?>
|
10 |
-
|
11 |
-
<p><?php echo _n(
|
12 |
-
'You are about to remove the following extension:',
|
13 |
-
'You are about to remove the following extensions:',
|
14 |
-
$count,
|
15 |
-
'fw'
|
16 |
-
) ?></p>
|
17 |
-
|
18 |
-
<ul class="ul-disc">
|
19 |
-
<?php foreach ($extension_names as $extension_name): ?>
|
20 |
-
<li><strong><?php echo fw()->extensions->manager->get_extension_title($extension_name); ?></strong></li>
|
21 |
-
<?php endforeach; ?>
|
22 |
-
</ul>
|
23 |
-
|
24 |
-
<p><?php
|
25 |
-
echo _n(
|
26 |
-
'Are you sure you wish to delete this extension?',
|
27 |
-
'Are you sure you wish to delete these extensions?',
|
28 |
-
$count,
|
29 |
-
'fw'
|
30 |
-
)
|
31 |
-
?></p>
|
32 |
-
|
33 |
-
<input type="submit" name="submit" id="submit" class="button" value="<?php
|
34 |
-
echo esc_attr( _n(
|
35 |
-
'Yes, Delete this extension',
|
36 |
-
'Yes, Delete these extensions',
|
37 |
-
$count,
|
38 |
-
'fw'
|
39 |
-
) )
|
40 |
-
?>">
|
41 |
-
|
42 |
-
<a class="button" href="<?php echo esc_attr($list_page_link) ?>" ><?php _e('No, Return me to the extension list', 'fw') ?></a>
|
43 |
-
|
44 |
-
<p>
|
45 |
-
<a href="#" onclick="jQuery('#files-list').toggle(); return false;"><?php _e('Click to view entire list of directories which will be deleted', 'fw') ?></a>
|
46 |
-
</p>
|
47 |
-
<div id="files-list" style="display: none;">
|
48 |
-
<ul class="code">
|
49 |
-
<?php $replace_regex = '/^'. preg_quote(fw_get_framework_directory('/extensions'), '/') .'/'; ?>
|
50 |
-
<?php foreach ($extension_names as $extension_name): ?>
|
51 |
-
<?php if (!isset($installed_extensions[$extension_name])) continue; ?>
|
52 |
-
<li><?php echo preg_replace($replace_regex, '', $installed_extensions[$extension_name]['path']) ?>/</li>
|
53 |
-
<?php endforeach; ?>
|
54 |
-
</ul>
|
55 |
</div>
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
/**
|
3 |
+
* @var array $extension_names
|
4 |
+
* @var array $installed_extensions
|
5 |
+
* @var array $list_page_link
|
6 |
+
*/
|
7 |
+
|
8 |
+
$count = count($extension_names);
|
9 |
+
?>
|
10 |
+
|
11 |
+
<p><?php echo _n(
|
12 |
+
'You are about to remove the following extension:',
|
13 |
+
'You are about to remove the following extensions:',
|
14 |
+
$count,
|
15 |
+
'fw'
|
16 |
+
) ?></p>
|
17 |
+
|
18 |
+
<ul class="ul-disc">
|
19 |
+
<?php foreach ($extension_names as $extension_name): ?>
|
20 |
+
<li><strong><?php echo fw()->extensions->manager->get_extension_title($extension_name); ?></strong></li>
|
21 |
+
<?php endforeach; ?>
|
22 |
+
</ul>
|
23 |
+
|
24 |
+
<p><?php
|
25 |
+
echo _n(
|
26 |
+
'Are you sure you wish to delete this extension?',
|
27 |
+
'Are you sure you wish to delete these extensions?',
|
28 |
+
$count,
|
29 |
+
'fw'
|
30 |
+
)
|
31 |
+
?></p>
|
32 |
+
|
33 |
+
<input type="submit" name="submit" id="submit" class="button" value="<?php
|
34 |
+
echo esc_attr( _n(
|
35 |
+
'Yes, Delete this extension',
|
36 |
+
'Yes, Delete these extensions',
|
37 |
+
$count,
|
38 |
+
'fw'
|
39 |
+
) )
|
40 |
+
?>">
|
41 |
+
|
42 |
+
<a class="button" href="<?php echo esc_attr($list_page_link) ?>" ><?php _e('No, Return me to the extension list', 'fw') ?></a>
|
43 |
+
|
44 |
+
<p>
|
45 |
+
<a href="#" onclick="jQuery('#files-list').toggle(); return false;"><?php _e('Click to view entire list of directories which will be deleted', 'fw') ?></a>
|
46 |
+
</p>
|
47 |
+
<div id="files-list" style="display: none;">
|
48 |
+
<ul class="code">
|
49 |
+
<?php $replace_regex = '/^'. preg_quote(fw_get_framework_directory('/extensions'), '/') .'/'; ?>
|
50 |
+
<?php foreach ($extension_names as $extension_name): ?>
|
51 |
+
<?php if (!isset($installed_extensions[$extension_name])) continue; ?>
|
52 |
+
<li><?php echo preg_replace($replace_regex, '', $installed_extensions[$extension_name]['path']) ?>/</li>
|
53 |
+
<?php endforeach; ?>
|
54 |
+
</ul>
|
55 |
</div>
|
framework/core/components/extensions/manager/views/extension-page-header.php
CHANGED
@@ -1,50 +1,50 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
/**
|
3 |
-
* @var string $extension_name
|
4 |
-
* @var array $extension_data
|
5 |
-
* @var string $extension_title
|
6 |
-
* @var string $link_delete
|
7 |
-
* @var string $link_extension
|
8 |
-
* @var string $tab
|
9 |
-
* @var bool $is_supported
|
10 |
-
*/
|
11 |
-
|
12 |
-
?>
|
13 |
-
<h2 class="fw-extension-page-title">
|
14 |
-
<span class="fw-pull-right">
|
15 |
-
<?php
|
16 |
-
switch ($tab) {
|
17 |
-
case 'settings':
|
18 |
-
if (!file_exists($extension_data['path'] .'/readme.md.php')) {
|
19 |
-
break;
|
20 |
-
}
|
21 |
-
if ($is_supported) {
|
22 |
-
// do not show install instructions for supported extensions
|
23 |
-
break;
|
24 |
-
}
|
25 |
-
?><a href="<?php echo esc_attr($link_extension) ?>&extension=<?php echo esc_attr($extension_name) ?>&tab=docs" class="button-primary"><?php _e('Install Instructions', 'fw') ?></a><?php
|
26 |
-
break;
|
27 |
-
case 'docs':
|
28 |
-
if (!fw()->extensions->get($extension_name) || !fw()->extensions->get($extension_name)->get_settings_options()) {
|
29 |
-
break;
|
30 |
-
}
|
31 |
-
?><a href="<?php echo esc_attr($link_extension) ?>&extension=<?php echo esc_attr($extension_name) ?>" class="button-primary"><?php _e('Settings', 'fw') ?></a><?php
|
32 |
-
break;
|
33 |
-
}
|
34 |
-
?>
|
35 |
-
</span>
|
36 |
-
|
37 |
-
<?php
|
38 |
-
switch ($tab) {
|
39 |
-
case 'settings':
|
40 |
-
echo sprintf(__('%s Settings', 'fw'), $extension_title);
|
41 |
-
break;
|
42 |
-
case 'docs':
|
43 |
-
echo sprintf(__('%s Install Instructions', 'fw'), $extension_title);
|
44 |
-
break;
|
45 |
-
default:
|
46 |
-
echo __('Unknown tab:', 'fw') . ' ' . fw_htmlspecialchars($tab);
|
47 |
-
}
|
48 |
-
?>
|
49 |
-
</h2>
|
50 |
-
<br/>
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
/**
|
3 |
+
* @var string $extension_name
|
4 |
+
* @var array $extension_data
|
5 |
+
* @var string $extension_title
|
6 |
+
* @var string $link_delete
|
7 |
+
* @var string $link_extension
|
8 |
+
* @var string $tab
|
9 |
+
* @var bool $is_supported
|
10 |
+
*/
|
11 |
+
|
12 |
+
?>
|
13 |
+
<h2 class="fw-extension-page-title">
|
14 |
+
<span class="fw-pull-right">
|
15 |
+
<?php
|
16 |
+
switch ($tab) {
|
17 |
+
case 'settings':
|
18 |
+
if (!file_exists($extension_data['path'] .'/readme.md.php')) {
|
19 |
+
break;
|
20 |
+
}
|
21 |
+
if ($is_supported) {
|
22 |
+
// do not show install instructions for supported extensions
|
23 |
+
break;
|
24 |
+
}
|
25 |
+
?><a href="<?php echo esc_attr($link_extension) ?>&extension=<?php echo esc_attr($extension_name) ?>&tab=docs" class="button-primary"><?php _e('Install Instructions', 'fw') ?></a><?php
|
26 |
+
break;
|
27 |
+
case 'docs':
|
28 |
+
if (!fw()->extensions->get($extension_name) || !fw()->extensions->get($extension_name)->get_settings_options()) {
|
29 |
+
break;
|
30 |
+
}
|
31 |
+
?><a href="<?php echo esc_attr($link_extension) ?>&extension=<?php echo esc_attr($extension_name) ?>" class="button-primary"><?php _e('Settings', 'fw') ?></a><?php
|
32 |
+
break;
|
33 |
+
}
|
34 |
+
?>
|
35 |
+
</span>
|
36 |
+
|
37 |
+
<?php
|
38 |
+
switch ($tab) {
|
39 |
+
case 'settings':
|
40 |
+
echo sprintf(__('%s Settings', 'fw'), $extension_title);
|
41 |
+
break;
|
42 |
+
case 'docs':
|
43 |
+
echo sprintf(__('%s Install Instructions', 'fw'), $extension_title);
|
44 |
+
break;
|
45 |
+
default:
|
46 |
+
echo __('Unknown tab:', 'fw') . ' ' . fw_htmlspecialchars($tab);
|
47 |
+
}
|
48 |
+
?>
|
49 |
+
</h2>
|
50 |
+
<br/>
|
framework/core/components/extensions/manager/views/extension.php
CHANGED
@@ -1,360 +1,360 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
/**
|
3 |
-
* Display extension in list on the extensions page
|
4 |
-
* @var string $name
|
5 |
-
* @var string $title
|
6 |
-
* @var string $description
|
7 |
-
* @var string $link
|
8 |
-
* @var array $lists
|
9 |
-
* @var array $nonces
|
10 |
-
* @var string $default_thumbnail
|
11 |
-
* @var bool $can_install
|
12 |
-
*/
|
13 |
-
|
14 |
-
$is_active = (bool)fw()->extensions->get($name);
|
15 |
-
|
16 |
-
if (isset($lists['installed'][$name])) {
|
17 |
-
$installed_data = &$lists['installed'][$name];
|
18 |
-
} else {
|
19 |
-
$installed_data = false;
|
20 |
-
}
|
21 |
-
|
22 |
-
if (isset($lists['available'][$name])) {
|
23 |
-
$available_data = &$lists['available'][$name];
|
24 |
-
} else {
|
25 |
-
$available_data = false;
|
26 |
-
}
|
27 |
-
|
28 |
-
{
|
29 |
-
$thumbnail = $default_thumbnail;
|
30 |
-
|
31 |
-
if (isset($lists['available'][$name])) {
|
32 |
-
$thumbnail = $lists['available'][$name]['thumbnail'];
|
33 |
-
}
|
34 |
-
|
35 |
-
if (isset($lists['installed'][$name])) {
|
36 |
-
$thumbnail = fw_akg('thumbnail', $lists['installed'][$name]['manifest'], $thumbnail);
|
37 |
-
}
|
38 |
-
}
|
39 |
-
|
40 |
-
$is_compatible =
|
41 |
-
isset($lists['supported'][$name]) // is listed in the supported extensions list in theme manifest
|
42 |
-
||
|
43 |
-
($installed_data && $installed_data['is']['theme']); // is located in the theme
|
44 |
-
|
45 |
-
$wrapper_class = 'fw-col-xs-12 fw-col-lg-6 fw-extensions-list-item';
|
46 |
-
|
47 |
-
if ($installed_data && !$is_active) {
|
48 |
-
$wrapper_class .= ' disabled';
|
49 |
-
}
|
50 |
-
|
51 |
-
if (!$installed_data && !$is_compatible) {
|
52 |
-
$wrapper_class .= ' not-compatible';
|
53 |
-
}
|
54 |
-
?>
|
55 |
-
<div class="<?php echo esc_attr($wrapper_class) ?>" id="fw-ext-<?php echo esc_attr($name) ?>">
|
56 |
-
<div class="inner">
|
57 |
-
<div class="fw-extension-list-item-table">
|
58 |
-
<div class="fw-extension-list-item-table-row">
|
59 |
-
<div class="fw-extension-list-item-table-cell cell-1">
|
60 |
-
<div class="fw-extensions-list-item-thumbnail-wrapper">
|
61 |
-
<?php echo fw_string_to_icon_html($thumbnail, array('class' => 'fw-extensions-list-item-thumbnail')); ?>
|
62 |
-
</div>
|
63 |
-
</div>
|
64 |
-
<div class="fw-extension-list-item-table-cell cell-2">
|
65 |
-
<h3 class="fw-extensions-list-item-title"<?php if ($is_active): ?> title="v<?php echo esc_attr(fw()->extensions->get($name)->manifest->get_version()) ?>"<?php endif; ?>><?php
|
66 |
-
if ($is_active && ($extension_link = fw()->extensions->get($name)->_get_link())) {
|
67 |
-
echo fw_html_tag('a', array('href' => $extension_link), $title);
|
68 |
-
} else {
|
69 |
-
echo $title;
|
70 |
-
}
|
71 |
-
?></h3>
|
72 |
-
|
73 |
-
<?php if ($description): ?>
|
74 |
-
<p class="fw-extensions-list-item-desc"><?php echo esc_html($description); ?></p>
|
75 |
-
<?php endif; ?>
|
76 |
-
|
77 |
-
<?php
|
78 |
-
if ($installed_data) {
|
79 |
-
$_links = array();
|
80 |
-
|
81 |
-
if ( $is_active && fw()->extensions->get( $name )->get_settings_options() ) {
|
82 |
-
$_links[] = '<a href="' . esc_attr( $link ) . '&sub-page=extension&extension=' . esc_attr( $name ) . '">' . __( 'Settings', 'fw' ) . '</a>';
|
83 |
-
}
|
84 |
-
|
85 |
-
if ( $is_active && file_exists( $installed_data['path'] . '/readme.md.php' ) ) {
|
86 |
-
if ( isset($lists['supported'][$name]) ) {
|
87 |
-
// no sense to teach how to install the extension if theme is already configured and the is extension marked as compatible
|
88 |
-
} else {
|
89 |
-
$_links[] = '<a href="' . esc_attr( $link ) . '&sub-page=extension&extension=' . esc_attr( $name ) . '&tab=docs">' . __( 'Install Instructions', 'fw' ) . '</a>';
|
90 |
-
}
|
91 |
-
}
|
92 |
-
|
93 |
-
if ( ! empty( $_links ) ):
|
94 |
-
?><p
|
95 |
-
class="fw-extensions-list-item-links"><?php echo implode( ' <span class="fw-text-muted">|</span> ', $_links ); ?></p><?php
|
96 |
-
endif;
|
97 |
-
|
98 |
-
unset( $_links );
|
99 |
-
}
|
100 |
-
?>
|
101 |
-
<?php if ($is_compatible): ?>
|
102 |
-
<p><em><strong><span class="dashicons dashicons-yes"></span> <?php _e('Compatible', 'fw') ?></strong> <?php _e('with your current theme', 'fw') ?></em></p>
|
103 |
-
<?php endif; ?>
|
104 |
-
</div>
|
105 |
-
<div class="fw-extension-list-item-table-cell cell-3">
|
106 |
-
<?php if ($is_active): ?>
|
107 |
-
<form action="<?php echo esc_attr($link) ?>&sub-page=deactivate&extension=<?php echo esc_attr($name) ?>" method="post">
|
108 |
-
<?php wp_nonce_field($nonces['deactivate']['action'], $nonces['deactivate']['name']); ?>
|
109 |
-
<input class="button" type="submit" value="<?php esc_attr_e('Deactivate', 'fw'); ?>"/>
|
110 |
-
</form>
|
111 |
-
<?php elseif ($installed_data): ?>
|
112 |
-
<div class="fw-text-center">
|
113 |
-
<form action="<?php echo esc_attr($link) ?>&sub-page=activate&extension=<?php echo esc_attr($name) ?>"
|
114 |
-
method="post"
|
115 |
-
class="extension-activate-form"
|
116 |
-
>
|
117 |
-
<?php wp_nonce_field($nonces['activate']['action'], $nonces['activate']['name']); ?>
|
118 |
-
<input class="button" type="submit" value="<?php esc_attr_e('Activate', 'fw'); ?>"/>
|
119 |
-
</form>
|
120 |
-
<?php
|
121 |
-
/**
|
122 |
-
* Do not show the "Delete extension" button if the extension is not in the available list.
|
123 |
-
* If you delete such extension you will not be able to install it back.
|
124 |
-
* Most often these will be extensions located in the theme.
|
125 |
-
*/
|
126 |
-
if ($can_install && $available_data):
|
127 |
-
?>
|
128 |
-
<form action="<?php echo esc_attr($link) ?>&sub-page=delete&extension=<?php echo esc_attr($name) ?>"
|
129 |
-
method="post"
|
130 |
-
class="fw-extension-ajax-form extension-delete-form"
|
131 |
-
data-confirm-message="<?php esc_attr_e('Are you sure you want to remove this extension?', 'fw') ?>"
|
132 |
-
data-extension-name="<?php echo esc_attr($name) ?>"
|
133 |
-
data-extension-action="uninstall"
|
134 |
-
>
|
135 |
-
<?php wp_nonce_field($nonces['delete']['action'], $nonces['delete']['name']); ?>
|
136 |
-
<p class="fw-visible-xs-block"></p>
|
137 |
-
<a href="#"
|
138 |
-
onclick="jQuery(this).closest('form').submit(); return false;"
|
139 |
-
data-remove-extension="<?php echo esc_attr($name) ?>"
|
140 |
-
title="<?php echo esc_attr_e('Remove', 'fw'); ?>"
|
141 |
-
><span class="btn-text fw-visible-xs-inline"><?php _e('Remove', 'fw'); ?></span><span class="btn-icon unycon unycon-trash fw-hidden-xs"></span></a>
|
142 |
-
</form>
|
143 |
-
<?php endif; ?>
|
144 |
-
</div>
|
145 |
-
<?php elseif ($can_install && $available_data): ?>
|
146 |
-
<form action="<?php echo esc_attr($link) ?>&sub-page=install&extension=<?php echo esc_attr($name) ?>"
|
147 |
-
method="post"
|
148 |
-
class="fw-extension-ajax-form"
|
149 |
-
data-extension-name="<?php echo esc_attr($name) ?>"
|
150 |
-
data-extension-action="install"
|
151 |
-
>
|
152 |
-
<?php wp_nonce_field($nonces['install']['action'], $nonces['install']['name']); ?>
|
153 |
-
<input type="submit" class="button" value="<?php esc_attr_e('Download', 'fw') ?>">
|
154 |
-
</form>
|
155 |
-
<?php endif; ?>
|
156 |
-
</div>
|
157 |
-
</div>
|
158 |
-
</div>
|
159 |
-
<?php if ($installed_data): ?>
|
160 |
-
<?php if (!$is_active): ?>
|
161 |
-
<?php if (!fw()->extensions->_get_db_active_extensions($name)): ?>
|
162 |
-
<span><!-- Is not set as active in db --></span>
|
163 |
-
<?php elseif ($installed_data['parent'] && !fw()->extensions->get($installed_data['parent'])): ?>
|
164 |
-
<?php
|
165 |
-
$parent_extension_name = $installed_data['parent'];
|
166 |
-
$parent_extension_title = fw_id_to_title($parent_extension_name);
|
167 |
-
|
168 |
-
if (isset($lists['installed'][$parent_extension_name])) {
|
169 |
-
$parent_extension_title = fw_akg('name', $lists['installed'][$parent_extension_name]['manifest'], $parent_extension_title);
|
170 |
-
} elseif (isset($lists['available'][$parent_extension_name])) {
|
171 |
-
$parent_extension_title = $lists['available'][$parent_extension_name]['name'];
|
172 |
-
}
|
173 |
-
?>
|
174 |
-
<p class="fw-text-muted"><?php echo sprintf(__('Parent extension "%s" is disabled', 'fw'), $parent_extension_title); ?></p>
|
175 |
-
<?php else: ?>
|
176 |
-
<div class="fw-extension-disabled fw-border-box-sizing">
|
177 |
-
<div class="fw-extension-disabled-panel fw-border-box-sizing">
|
178 |
-
<div class="fw-row">
|
179 |
-
<div class="fw-col-xs-12 fw-col-sm-3">
|
180 |
-
<span class="fw-text-danger">! <?php _e('Disabled', 'fw'); ?></span>
|
181 |
-
</div>
|
182 |
-
<div class="fw-col-xs-12 fw-col-sm-9 fw-text-right">
|
183 |
-
<?php
|
184 |
-
$requirements = array();
|
185 |
-
|
186 |
-
foreach (fw_akg('requirements', $installed_data['manifest'], array()) as $req_name => $req_data) {
|
187 |
-
switch ($req_name) {
|
188 |
-
case 'wordpress':
|
189 |
-
if (empty($req_data['min_version']) && empty($req_data['max_version'])) {
|
190 |
-
break;
|
191 |
-
}
|
192 |
-
|
193 |
-
global $wp_version;
|
194 |
-
|
195 |
-
if ( ! empty( $req_data['min_version'] ) ) {
|
196 |
-
if (!version_compare($req_data['min_version'], $wp_version, '<=')) {
|
197 |
-
if ($can_install) {
|
198 |
-
$requirements[] = sprintf(
|
199 |
-
__( 'You need to update WordPress to %s: %s', 'fw' ),
|
200 |
-
$req_data['min_version'],
|
201 |
-
fw_html_tag( 'a', array( 'href' => self_admin_url( 'update-core.php' ) ), __( 'Update WordPress', 'fw' ) )
|
202 |
-
);
|
203 |
-
} else {
|
204 |
-
$requirements[] = sprintf(
|
205 |
-
__( 'WordPress needs to be updated to %s', 'fw' ),
|
206 |
-
$req_data['min_version']
|
207 |
-
);
|
208 |
-
}
|
209 |
-
}
|
210 |
-
}
|
211 |
-
|
212 |
-
if ( ! empty( $req_data['max_version'] ) ) {
|
213 |
-
if (!version_compare($req_data['max_version'], $wp_version, '>=')) {
|
214 |
-
$requirements[] = sprintf(
|
215 |
-
__('Maximum supported WordPress version is %s', 'fw'),
|
216 |
-
$req_data['max_version']
|
217 |
-
);
|
218 |
-
}
|
219 |
-
}
|
220 |
-
break;
|
221 |
-
case 'framework':
|
222 |
-
if (empty($req_data['min_version']) && empty($req_data['max_version'])) {
|
223 |
-
break;
|
224 |
-
}
|
225 |
-
|
226 |
-
if ( ! empty( $req_data['min_version'] ) ) {
|
227 |
-
if (!version_compare($req_data['min_version'], fw()->manifest->get_version(), '<=')) {
|
228 |
-
if ($can_install) {
|
229 |
-
$requirements[] = sprintf(
|
230 |
-
__( 'You need to update %s to %s: %s', 'fw' ),
|
231 |
-
fw()->manifest->get_name(),
|
232 |
-
$req_data['min_version'],
|
233 |
-
fw_html_tag( 'a', array( 'href' => self_admin_url( 'update-core.php' ) ),
|
234 |
-
sprintf( __( 'Update %s', 'fw' ), fw()->manifest->get_name() )
|
235 |
-
)
|
236 |
-
);
|
237 |
-
} else {
|
238 |
-
$requirements[] = sprintf(
|
239 |
-
__( '%s needs to be updated to %s', 'fw' ),
|
240 |
-
fw()->manifest->get_name(),
|
241 |
-
$req_data['min_version']
|
242 |
-
);
|
243 |
-
}
|
244 |
-
}
|
245 |
-
}
|
246 |
-
|
247 |
-
if ( ! empty( $req_data['max_version'] ) ) {
|
248 |
-
if (!version_compare($req_data['max_version'], fw()->manifest->get_version(), '>=')) {
|
249 |
-
$requirements[] = sprintf(
|
250 |
-
__( 'Maximum supported %s version is %s', 'fw' ),
|
251 |
-
fw()->manifest->get_name(),
|
252 |
-
$req_data['max_version']
|
253 |
-
);
|
254 |
-
}
|
255 |
-
}
|
256 |
-
break;
|
257 |
-
case 'extensions':
|
258 |
-
foreach ($req_data as $req_ext => $req_ext_data) {
|
259 |
-
if ($ext = fw()->extensions->get($req_ext)) {
|
260 |
-
if (empty($req_ext_data['min_version']) && empty($req_ext_data['max_version'])) {
|
261 |
-
continue;
|
262 |
-
}
|
263 |
-
|
264 |
-
if ( ! empty( $req_ext_data['min_version'] ) ) {
|
265 |
-
if (!version_compare($req_ext_data['min_version'], $ext->manifest->get_version(), '<=')) {
|
266 |
-
if ($can_install) {
|
267 |
-
$requirements[] = sprintf(
|
268 |
-
__('You need to update the %s extension to %s: %s', 'fw'),
|
269 |
-
$ext->manifest->get_name(),
|
270 |
-
$req_ext_data['min_version'],
|
271 |
-
fw_html_tag('a', array('href' => self_admin_url('update-core.php')),
|
272 |
-
sprintf(__('Update %s', 'fw'), $ext->manifest->get_name())
|
273 |
-
)
|
274 |
-
);
|
275 |
-
} else {
|
276 |
-
$requirements[] = sprintf(
|
277 |
-
__('The %s extension needs to be updated to %s', 'fw'),
|
278 |
-
$ext->manifest->get_name(),
|
279 |
-
$req_ext_data['min_version']
|
280 |
-
);
|
281 |
-
}
|
282 |
-
}
|
283 |
-
}
|
284 |
-
|
285 |
-
if ( ! empty( $req_ext_data['max_version'] ) ) {
|
286 |
-
if (!version_compare($req_ext_data['max_version'], $ext->manifest->get_version(), '>=')) {
|
287 |
-
$requirements[] = sprintf(
|
288 |
-
__( 'Maximum supported %s extension version is %s', 'fw' ),
|
289 |
-
$ext->manifest->get_name(),
|
290 |
-
$req_ext_data['max_version']
|
291 |
-
);
|
292 |
-
}
|
293 |
-
}
|
294 |
-
} else {
|
295 |
-
$ext_title = fw_id_to_title($req_ext);
|
296 |
-
|
297 |
-
if (isset($lists['installed'][$req_ext])) {
|
298 |
-
$ext_title = fw_akg('name', $lists['installed'][$req_ext]['manifest'], $ext_title);
|
299 |
-
|
300 |
-
ob_start(); ?>
|
301 |
-
<form action="<?php echo esc_attr($link) ?>&sub-page=activate&extension=<?php echo esc_attr($req_ext) ?>" method="post" style="display: inline;">
|
302 |
-
<?php wp_nonce_field($nonces['activate']['action'], $nonces['activate']['name']); ?>
|
303 |
-
<?php echo sprintf(__( 'The %s extension is disabled', 'fw' ), $ext_title); ?>:
|
304 |
-
<a href="#" onclick="jQuery(this).closest('form').submit(); return false;"><?php echo sprintf(__('Activate %s', 'fw'), $ext_title); ?></a>
|
305 |
-
</form>
|
306 |
-
<?php
|
307 |
-
$requirements[] = ob_get_clean();
|
308 |
-
} else {
|
309 |
-
if ($can_install && isset($lists['available'][$req_ext])) {
|
310 |
-
$ext_title = $lists['available'][ $req_ext ]['name'];
|
311 |
-
|
312 |
-
$requirements[] = sprintf(
|
313 |
-
__( 'The %s extension is not installed: %s', 'fw' ),
|
314 |
-
$ext_title,
|
315 |
-
fw_html_tag( 'a', array( 'href' => $link . '&sub-page=install&extension=' . $req_ext ),
|
316 |
-
sprintf( __( 'Install %s', 'fw' ), $ext_title )
|
317 |
-
)
|
318 |
-
);
|
319 |
-
} else {
|
320 |
-
$requirements[] = sprintf(
|
321 |
-
__( 'The %s extension is not installed', 'fw' ),
|
322 |
-
$ext_title
|
323 |
-
);
|
324 |
-
}
|
325 |
-
}
|
326 |
-
}
|
327 |
-
}
|
328 |
-
break;
|
329 |
-
default:
|
330 |
-
trigger_error('Invalid requirement: '. $req_name, E_USER_WARNING);
|
331 |
-
continue;
|
332 |
-
}
|
333 |
-
}
|
334 |
-
?>
|
335 |
-
<a onclick="return false;" href="#" class="fw-extension-tip" title="<?php
|
336 |
-
echo fw_htmlspecialchars(
|
337 |
-
'<div class="fw-extension-tip-content">'.
|
338 |
-
'<ul class="fw-extension-requirements"><li>- '. implode('</li><li>- ', $requirements) .'</li></ul>'.
|
339 |
-
'</div>'
|
340 |
-
);
|
341 |
-
unset($requirements);
|
342 |
-
?>"><?php _e('View Requirements', 'fw') ?></a>
|
343 |
-
<p class="fw-visible-xs-block"></p><?php
|
344 |
-
if ($can_install):
|
345 |
-
?><a href="<?php echo esc_attr($link) ?>&sub-page=delete&extension=<?php echo esc_attr($name) ?>" class="button" ><?php _e('Remove', 'fw'); ?></a><?php
|
346 |
-
endif;
|
347 |
-
?>
|
348 |
-
</div>
|
349 |
-
</div>
|
350 |
-
</div>
|
351 |
-
</div>
|
352 |
-
<?php endif; ?>
|
353 |
-
<?php endif; ?>
|
354 |
-
<?php elseif ($available_data): ?>
|
355 |
-
<!-- -->
|
356 |
-
<?php else: ?>
|
357 |
-
<!-- -->
|
358 |
-
<?php endif; ?>
|
359 |
-
</div>
|
360 |
-
</div>
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
/**
|
3 |
+
* Display extension in list on the extensions page
|
4 |
+
* @var string $name
|
5 |
+
* @var string $title
|
6 |
+
* @var string $description
|
7 |
+
* @var string $link
|
8 |
+
* @var array $lists
|
9 |
+
* @var array $nonces
|
10 |
+
* @var string $default_thumbnail
|
11 |
+
* @var bool $can_install
|
12 |
+
*/
|
13 |
+
|
14 |
+
$is_active = (bool)fw()->extensions->get($name);
|
15 |
+
|
16 |
+
if (isset($lists['installed'][$name])) {
|
17 |
+
$installed_data = &$lists['installed'][$name];
|
18 |
+
} else {
|
19 |
+
$installed_data = false;
|
20 |
+
}
|
21 |
+
|
22 |
+
if (isset($lists['available'][$name])) {
|
23 |
+
$available_data = &$lists['available'][$name];
|
24 |
+
} else {
|
25 |
+
$available_data = false;
|
26 |
+
}
|
27 |
+
|
28 |
+
{
|
29 |
+
$thumbnail = $default_thumbnail;
|
30 |
+
|
31 |
+
if (isset($lists['available'][$name])) {
|
32 |
+
$thumbnail = $lists['available'][$name]['thumbnail'];
|
33 |
+
}
|
34 |
+
|
35 |
+
if (isset($lists['installed'][$name])) {
|
36 |
+
$thumbnail = fw_akg('thumbnail', $lists['installed'][$name]['manifest'], $thumbnail);
|
37 |
+
}
|
38 |
+
}
|
39 |
+
|
40 |
+
$is_compatible =
|
41 |
+
isset($lists['supported'][$name]) // is listed in the supported extensions list in theme manifest
|
42 |
+
||
|
43 |
+
($installed_data && $installed_data['is']['theme']); // is located in the theme
|
44 |
+
|
45 |
+
$wrapper_class = 'fw-col-xs-12 fw-col-lg-6 fw-extensions-list-item';
|
46 |
+
|
47 |
+
if ($installed_data && !$is_active) {
|
48 |
+
$wrapper_class .= ' disabled';
|
49 |
+
}
|
50 |
+
|
51 |
+
if (!$installed_data && !$is_compatible) {
|
52 |
+
$wrapper_class .= ' not-compatible';
|
53 |
+
}
|
54 |
+
?>
|
55 |
+
<div class="<?php echo esc_attr($wrapper_class) ?>" id="fw-ext-<?php echo esc_attr($name) ?>">
|
56 |
+
<div class="inner">
|
57 |
+
<div class="fw-extension-list-item-table">
|
58 |
+
<div class="fw-extension-list-item-table-row">
|
59 |
+
<div class="fw-extension-list-item-table-cell cell-1">
|
60 |
+
<div class="fw-extensions-list-item-thumbnail-wrapper">
|
61 |
+
<?php echo fw_string_to_icon_html($thumbnail, array('class' => 'fw-extensions-list-item-thumbnail')); ?>
|
62 |
+
</div>
|
63 |
+
</div>
|
64 |
+
<div class="fw-extension-list-item-table-cell cell-2">
|
65 |
+
<h3 class="fw-extensions-list-item-title"<?php if ($is_active): ?> title="v<?php echo esc_attr(fw()->extensions->get($name)->manifest->get_version()) ?>"<?php endif; ?>><?php
|
66 |
+
if ($is_active && ($extension_link = fw()->extensions->get($name)->_get_link())) {
|
67 |
+
echo fw_html_tag('a', array('href' => $extension_link), $title);
|
68 |
+
} else {
|
69 |
+
echo $title;
|
70 |
+
}
|
71 |
+
?></h3>
|
72 |
+
|
73 |
+
<?php if ($description): ?>
|
74 |
+
<p class="fw-extensions-list-item-desc"><?php echo esc_html($description); ?></p>
|
75 |
+
<?php endif; ?>
|
76 |
+
|
77 |
+
<?php
|
78 |
+
if ($installed_data) {
|
79 |
+
$_links = array();
|
80 |
+
|
81 |
+
if ( $is_active && fw()->extensions->get( $name )->get_settings_options() ) {
|
82 |
+
$_links[] = '<a href="' . esc_attr( $link ) . '&sub-page=extension&extension=' . esc_attr( $name ) . '">' . __( 'Settings', 'fw' ) . '</a>';
|
83 |
+
}
|
84 |
+
|
85 |
+
if ( $is_active && file_exists( $installed_data['path'] . '/readme.md.php' ) ) {
|
86 |
+
if ( isset($lists['supported'][$name]) ) {
|
87 |
+
// no sense to teach how to install the extension if theme is already configured and the is extension marked as compatible
|
88 |
+
} else {
|
89 |
+
$_links[] = '<a href="' . esc_attr( $link ) . '&sub-page=extension&extension=' . esc_attr( $name ) . '&tab=docs">' . __( 'Install Instructions', 'fw' ) . '</a>';
|
90 |
+
}
|
91 |
+
}
|
92 |
+
|
93 |
+
if ( ! empty( $_links ) ):
|
94 |
+
?><p
|
95 |
+
class="fw-extensions-list-item-links"><?php echo implode( ' <span class="fw-text-muted">|</span> ', $_links ); ?></p><?php
|
96 |
+
endif;
|
97 |
+
|
98 |
+
unset( $_links );
|
99 |
+
}
|
100 |
+
?>
|
101 |
+
<?php if ($is_compatible): ?>
|
102 |
+
<p><em><strong><span class="dashicons dashicons-yes"></span> <?php _e('Compatible', 'fw') ?></strong> <?php _e('with your current theme', 'fw') ?></em></p>
|
103 |
+
<?php endif; ?>
|
104 |
+
</div>
|
105 |
+
<div class="fw-extension-list-item-table-cell cell-3">
|
106 |
+
<?php if ($is_active): ?>
|
107 |
+
<form action="<?php echo esc_attr($link) ?>&sub-page=deactivate&extension=<?php echo esc_attr($name) ?>" method="post">
|
108 |
+
<?php wp_nonce_field($nonces['deactivate']['action'], $nonces['deactivate']['name']); ?>
|
109 |
+
<input class="button" type="submit" value="<?php esc_attr_e('Deactivate', 'fw'); ?>"/>
|
110 |
+
</form>
|
111 |
+
<?php elseif ($installed_data): ?>
|
112 |
+
<div class="fw-text-center">
|
113 |
+
<form action="<?php echo esc_attr($link) ?>&sub-page=activate&extension=<?php echo esc_attr($name) ?>"
|
114 |
+
method="post"
|
115 |
+
class="extension-activate-form"
|
116 |
+
>
|
117 |
+
<?php wp_nonce_field($nonces['activate']['action'], $nonces['activate']['name']); ?>
|
118 |
+
<input class="button" type="submit" value="<?php esc_attr_e('Activate', 'fw'); ?>"/>
|
119 |
+
</form>
|
120 |
+
<?php
|
121 |
+
/**
|
122 |
+
* Do not show the "Delete extension" button if the extension is not in the available list.
|
123 |
+
* If you delete such extension you will not be able to install it back.
|
124 |
+
* Most often these will be extensions located in the theme.
|
125 |
+
*/
|
126 |
+
if ($can_install && $available_data):
|
127 |
+
?>
|
128 |
+
<form action="<?php echo esc_attr($link) ?>&sub-page=delete&extension=<?php echo esc_attr($name) ?>"
|
129 |
+
method="post"
|
130 |
+
class="fw-extension-ajax-form extension-delete-form"
|
131 |
+
data-confirm-message="<?php esc_attr_e('Are you sure you want to remove this extension?', 'fw') ?>"
|
132 |
+
data-extension-name="<?php echo esc_attr($name) ?>"
|
133 |
+
data-extension-action="uninstall"
|
134 |
+
>
|
135 |
+
<?php wp_nonce_field($nonces['delete']['action'], $nonces['delete']['name']); ?>
|
136 |
+
<p class="fw-visible-xs-block"></p>
|
137 |
+
<a href="#"
|
138 |
+
onclick="jQuery(this).closest('form').submit(); return false;"
|
139 |
+
data-remove-extension="<?php echo esc_attr($name) ?>"
|
140 |
+
title="<?php echo esc_attr_e('Remove', 'fw'); ?>"
|
141 |
+
><span class="btn-text fw-visible-xs-inline"><?php _e('Remove', 'fw'); ?></span><span class="btn-icon unycon unycon-trash fw-hidden-xs"></span></a>
|
142 |
+
</form>
|
143 |
+
<?php endif; ?>
|
144 |
+
</div>
|
145 |
+
<?php elseif ($can_install && $available_data): ?>
|
146 |
+
<form action="<?php echo esc_attr($link) ?>&sub-page=install&extension=<?php echo esc_attr($name) ?>"
|
147 |
+
method="post"
|
148 |
+
class="fw-extension-ajax-form"
|
149 |
+
data-extension-name="<?php echo esc_attr($name) ?>"
|
150 |
+
data-extension-action="install"
|
151 |
+
>
|
152 |
+
<?php wp_nonce_field($nonces['install']['action'], $nonces['install']['name']); ?>
|
153 |
+
<input type="submit" class="button" value="<?php esc_attr_e('Download', 'fw') ?>">
|
154 |
+
</form>
|
155 |
+
<?php endif; ?>
|
156 |
+
</div>
|
157 |
+
</div>
|
158 |
+
</div>
|
159 |
+
<?php if ($installed_data): ?>
|
160 |
+
<?php if (!$is_active): ?>
|
161 |
+
<?php if (!fw()->extensions->_get_db_active_extensions($name)): ?>
|
162 |
+
<span><!-- Is not set as active in db --></span>
|
163 |
+
<?php elseif ($installed_data['parent'] && !fw()->extensions->get($installed_data['parent'])): ?>
|
164 |
+
<?php
|
165 |
+
$parent_extension_name = $installed_data['parent'];
|
166 |
+
$parent_extension_title = fw_id_to_title($parent_extension_name);
|
167 |
+
|
168 |
+
if (isset($lists['installed'][$parent_extension_name])) {
|
169 |
+
$parent_extension_title = fw_akg('name', $lists['installed'][$parent_extension_name]['manifest'], $parent_extension_title);
|
170 |
+
} elseif (isset($lists['available'][$parent_extension_name])) {
|
171 |
+
$parent_extension_title = $lists['available'][$parent_extension_name]['name'];
|
172 |
+
}
|
173 |
+
?>
|
174 |
+
<p class="fw-text-muted"><?php echo sprintf(__('Parent extension "%s" is disabled', 'fw'), $parent_extension_title); ?></p>
|
175 |
+
<?php else: ?>
|
176 |
+
<div class="fw-extension-disabled fw-border-box-sizing">
|
177 |
+
<div class="fw-extension-disabled-panel fw-border-box-sizing">
|
178 |
+
<div class="fw-row">
|
179 |
+
<div class="fw-col-xs-12 fw-col-sm-3">
|
180 |
+
<span class="fw-text-danger">! <?php _e('Disabled', 'fw'); ?></span>
|
181 |
+
</div>
|
182 |
+
<div class="fw-col-xs-12 fw-col-sm-9 fw-text-right">
|
183 |
+
<?php
|
184 |
+
$requirements = array();
|
185 |
+
|
186 |
+
foreach (fw_akg('requirements', $installed_data['manifest'], array()) as $req_name => $req_data) {
|
187 |
+
switch ($req_name) {
|
188 |
+
case 'wordpress':
|
189 |
+
if (empty($req_data['min_version']) && empty($req_data['max_version'])) {
|
190 |
+
break;
|
191 |
+
}
|
192 |
+
|
193 |
+
global $wp_version;
|
194 |
+
|
195 |
+
if ( ! empty( $req_data['min_version'] ) ) {
|
196 |
+
if (!version_compare($req_data['min_version'], $wp_version, '<=')) {
|
197 |
+
if ($can_install) {
|
198 |
+
$requirements[] = sprintf(
|
199 |
+
__( 'You need to update WordPress to %s: %s', 'fw' ),
|
200 |
+
$req_data['min_version'],
|
201 |
+
fw_html_tag( 'a', array( 'href' => self_admin_url( 'update-core.php' ) ), __( 'Update WordPress', 'fw' ) )
|
202 |
+
);
|
203 |
+
} else {
|
204 |
+
$requirements[] = sprintf(
|
205 |
+
__( 'WordPress needs to be updated to %s', 'fw' ),
|
206 |
+
$req_data['min_version']
|
207 |
+
);
|
208 |
+
}
|
209 |
+
}
|
210 |
+
}
|
211 |
+
|
212 |
+
if ( ! empty( $req_data['max_version'] ) ) {
|
213 |
+
if (!version_compare($req_data['max_version'], $wp_version, '>=')) {
|
214 |
+
$requirements[] = sprintf(
|
215 |
+
__('Maximum supported WordPress version is %s', 'fw'),
|
216 |
+
$req_data['max_version']
|
217 |
+
);
|
218 |
+
}
|
219 |
+
}
|
220 |
+
break;
|
221 |
+
case 'framework':
|
222 |
+
if (empty($req_data['min_version']) && empty($req_data['max_version'])) {
|
223 |
+
break;
|
224 |
+
}
|
225 |
+
|
226 |
+
if ( ! empty( $req_data['min_version'] ) ) {
|
227 |
+
if (!version_compare($req_data['min_version'], fw()->manifest->get_version(), '<=')) {
|
228 |
+
if ($can_install) {
|
229 |
+
$requirements[] = sprintf(
|
230 |
+
__( 'You need to update %s to %s: %s', 'fw' ),
|
231 |
+
fw()->manifest->get_name(),
|
232 |
+
$req_data['min_version'],
|
233 |
+
fw_html_tag( 'a', array( 'href' => self_admin_url( 'update-core.php' ) ),
|
234 |
+
sprintf( __( 'Update %s', 'fw' ), fw()->manifest->get_name() )
|
235 |
+
)
|
236 |
+
);
|
237 |
+
} else {
|
238 |
+
$requirements[] = sprintf(
|
239 |
+
__( '%s needs to be updated to %s', 'fw' ),
|
240 |
+
fw()->manifest->get_name(),
|
241 |
+
$req_data['min_version']
|
242 |
+
);
|
243 |
+
}
|
244 |
+
}
|
245 |
+
}
|
246 |
+
|
247 |
+
if ( ! empty( $req_data['max_version'] ) ) {
|
248 |
+
if (!version_compare($req_data['max_version'], fw()->manifest->get_version(), '>=')) {
|
249 |
+
$requirements[] = sprintf(
|
250 |
+
__( 'Maximum supported %s version is %s', 'fw' ),
|
251 |
+
fw()->manifest->get_name(),
|
252 |
+
$req_data['max_version']
|
253 |
+
);
|
254 |
+
}
|
255 |
+
}
|
256 |
+
break;
|
257 |
+
case 'extensions':
|
258 |
+
foreach ($req_data as $req_ext => $req_ext_data) {
|
259 |
+
if ($ext = fw()->extensions->get($req_ext)) {
|
260 |
+
if (empty($req_ext_data['min_version']) && empty($req_ext_data['max_version'])) {
|
261 |
+
continue;
|
262 |
+
}
|
263 |
+
|
264 |
+
if ( ! empty( $req_ext_data['min_version'] ) ) {
|
265 |
+
if (!version_compare($req_ext_data['min_version'], $ext->manifest->get_version(), '<=')) {
|
266 |
+
if ($can_install) {
|
267 |
+
$requirements[] = sprintf(
|
268 |
+
__('You need to update the %s extension to %s: %s', 'fw'),
|
269 |
+
$ext->manifest->get_name(),
|
270 |
+
$req_ext_data['min_version'],
|
271 |
+
fw_html_tag('a', array('href' => self_admin_url('update-core.php')),
|
272 |
+
sprintf(__('Update %s', 'fw'), $ext->manifest->get_name())
|
273 |
+
)
|
274 |
+
);
|
275 |
+
} else {
|
276 |
+
$requirements[] = sprintf(
|
277 |
+
__('The %s extension needs to be updated to %s', 'fw'),
|
278 |
+
$ext->manifest->get_name(),
|
279 |
+
$req_ext_data['min_version']
|
280 |
+
);
|
281 |
+
}
|
282 |
+
}
|
283 |
+
}
|
284 |
+
|
285 |
+
if ( ! empty( $req_ext_data['max_version'] ) ) {
|
286 |
+
if (!version_compare($req_ext_data['max_version'], $ext->manifest->get_version(), '>=')) {
|
287 |
+
$requirements[] = sprintf(
|
288 |
+
__( 'Maximum supported %s extension version is %s', 'fw' ),
|
289 |
+
$ext->manifest->get_name(),
|
290 |
+
$req_ext_data['max_version']
|
291 |
+
);
|
292 |
+
}
|
293 |
+
}
|
294 |
+
} else {
|
295 |
+
$ext_title = fw_id_to_title($req_ext);
|
296 |
+
|
297 |
+
if (isset($lists['installed'][$req_ext])) {
|
298 |
+
$ext_title = fw_akg('name', $lists['installed'][$req_ext]['manifest'], $ext_title);
|
299 |
+
|
300 |
+
ob_start(); ?>
|
301 |
+
<form action="<?php echo esc_attr($link) ?>&sub-page=activate&extension=<?php echo esc_attr($req_ext) ?>" method="post" style="display: inline;">
|
302 |
+
<?php wp_nonce_field($nonces['activate']['action'], $nonces['activate']['name']); ?>
|
303 |
+
<?php echo sprintf(__( 'The %s extension is disabled', 'fw' ), $ext_title); ?>:
|
304 |
+
<a href="#" onclick="jQuery(this).closest('form').submit(); return false;"><?php echo sprintf(__('Activate %s', 'fw'), $ext_title); ?></a>
|
305 |
+
</form>
|
306 |
+
<?php
|
307 |
+
$requirements[] = ob_get_clean();
|
308 |
+
} else {
|
309 |
+
if ($can_install && isset($lists['available'][$req_ext])) {
|
310 |
+
$ext_title = $lists['available'][ $req_ext ]['name'];
|
311 |
+
|
312 |
+
$requirements[] = sprintf(
|
313 |
+
__( 'The %s extension is not installed: %s', 'fw' ),
|
314 |
+
$ext_title,
|
315 |
+
fw_html_tag( 'a', array( 'href' => $link . '&sub-page=install&extension=' . $req_ext ),
|
316 |
+
sprintf( __( 'Install %s', 'fw' ), $ext_title )
|
317 |
+
)
|
318 |
+
);
|
319 |
+
} else {
|
320 |
+
$requirements[] = sprintf(
|
321 |
+
__( 'The %s extension is not installed', 'fw' ),
|
322 |
+
$ext_title
|
323 |
+
);
|
324 |
+
}
|
325 |
+
}
|
326 |
+
}
|
327 |
+
}
|
328 |
+
break;
|
329 |
+
default:
|
330 |
+
trigger_error('Invalid requirement: '. $req_name, E_USER_WARNING);
|
331 |
+
continue;
|
332 |
+
}
|
333 |
+
}
|
334 |
+
?>
|
335 |
+
<a onclick="return false;" href="#" class="fw-extension-tip" title="<?php
|
336 |
+
echo fw_htmlspecialchars(
|
337 |
+
'<div class="fw-extension-tip-content">'.
|
338 |
+
'<ul class="fw-extension-requirements"><li>- '. implode('</li><li>- ', $requirements) .'</li></ul>'.
|
339 |
+
'</div>'
|
340 |
+
);
|
341 |
+
unset($requirements);
|
342 |
+
?>"><?php _e('View Requirements', 'fw') ?></a>
|
343 |
+
<p class="fw-visible-xs-block"></p><?php
|
344 |
+
if ($can_install):
|
345 |
+
?><a href="<?php echo esc_attr($link) ?>&sub-page=delete&extension=<?php echo esc_attr($name) ?>" class="button" ><?php _e('Remove', 'fw'); ?></a><?php
|
346 |
+
endif;
|
347 |
+
?>
|
348 |
+
</div>
|
349 |
+
</div>
|
350 |
+
</div>
|
351 |
+
</div>
|
352 |
+
<?php endif; ?>
|
353 |
+
<?php endif; ?>
|
354 |
+
<?php elseif ($available_data): ?>
|
355 |
+
<!-- -->
|
356 |
+
<?php else: ?>
|
357 |
+
<!-- -->
|
358 |
+
<?php endif; ?>
|
359 |
+
</div>
|
360 |
+
</div>
|
framework/core/components/extensions/manager/views/extensions-page.php
CHANGED
@@ -1,218 +1,218 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
/**
|
3 |
-
* @var array $lists
|
4 |
-
* @var string $link
|
5 |
-
* @var array $nonces
|
6 |
-
* @var mixed $display_default_value
|
7 |
-
* @var string $default_thumbnail
|
8 |
-
* @var bool $can_install
|
9 |
-
*/
|
10 |
-
|
11 |
-
$dir = dirname(__FILE__);
|
12 |
-
$extension_view_path = $dir .'/extension.php';
|
13 |
-
|
14 |
-
$displayed = array();
|
15 |
-
?>
|
16 |
-
|
17 |
-
<h3><?php _e('Active Extensions', 'fw') ?></h3>
|
18 |
-
<?php
|
19 |
-
$display_active_extensions = array();
|
20 |
-
|
21 |
-
foreach ($lists['active'] as $name => &$data) {
|
22 |
-
if (true !== fw_akg('display', $data['manifest'], $display_default_value)) {
|
23 |
-
continue;
|
24 |
-
}
|
25 |
-
|
26 |
-
$display_active_extensions[$name] = &$data;
|
27 |
-
}
|
28 |
-
unset($data);
|
29 |
-
?>
|
30 |
-
<?php if (empty($display_active_extensions)): ?>
|
31 |
-
<div class="fw-extensions-no-active">
|
32 |
-
<div class="fw-text-center fw-extensions-title-icon"><span class="dashicons dashicons-screenoptions"></span></div>
|
33 |
-
<p class="fw-text-center fw-text-muted"><em><?php _e('No extensions activated yet', 'fw'); ?><br/><?php _e('Check the available extensions below', 'fw'); ?></em></p>
|
34 |
-
</div>
|
35 |
-
<?php else: ?>
|
36 |
-
<div class="fw-row fw-extensions-list">
|
37 |
-
<?php
|
38 |
-
foreach ($display_active_extensions as $name => &$data) {
|
39 |
-
fw_render_view($extension_view_path, array(
|
40 |
-
'name' => $name,
|
41 |
-
'title' => fw_ext($name)->manifest->get_name(),
|
42 |
-
'description' => fw_ext($name)->manifest->get('description'),
|
43 |
-
'link' => $link,
|
44 |
-
'lists' => &$lists,
|
45 |
-
'nonces' => $nonces,
|
46 |
-
'default_thumbnail' => $default_thumbnail,
|
47 |
-
'can_install' => $can_install,
|
48 |
-
), false);
|
49 |
-
|
50 |
-
$displayed[$name] = true;
|
51 |
-
}
|
52 |
-
unset($data);
|
53 |
-
?>
|
54 |
-
</div>
|
55 |
-
<?php endif; ?>
|
56 |
-
|
57 |
-
<div id="fw-extensions-list-available">
|
58 |
-
<hr class="fw-extensions-lists-separator"/>
|
59 |
-
<h3><?php _e('Available Extensions', 'fw') ?></h3><!-- This "available" differs from technical "available" -->
|
60 |
-
<div class="fw-row fw-extensions-list">
|
61 |
-
<?php $something_displayed = false; ?>
|
62 |
-
<?php
|
63 |
-
{
|
64 |
-
$theme_extensions = array();
|
65 |
-
|
66 |
-
foreach ($lists['disabled'] as $name => &$data) {
|
67 |
-
if (!$data['is']['theme']) {
|
68 |
-
continue;
|
69 |
-
}
|
70 |
-
|
71 |
-
$theme_extensions[$name] = array(
|
72 |
-
'name' => fw_akg('name', $data['manifest'], fw_id_to_title($name)),
|
73 |
-
'description' => fw_akg('description', $data['manifest'], '')
|
74 |
-
);
|
75 |
-
}
|
76 |
-
unset($data);
|
77 |
-
|
78 |
-
foreach ($theme_extensions + $lists['supported'] as $name => $data) {
|
79 |
-
if (isset($displayed[$name])) {
|
80 |
-
continue;
|
81 |
-
} elseif (isset($lists['installed'][$name])) {
|
82 |
-
if (true !== fw_akg('display', $lists['installed'][$name]['manifest'], $display_default_value)) {
|
83 |
-
continue;
|
84 |
-
}
|
85 |
-
} else {
|
86 |
-
if (isset($lists['available'][$name])) {
|
87 |
-
if (!$can_install) {
|
88 |
-
continue;
|
89 |
-
}
|
90 |
-
} else {
|
91 |
-
//trigger_error(sprintf(__('Supported extension "%s" is not available.', 'fw'), $name));
|
92 |
-
continue;
|
93 |
-
}
|
94 |
-
}
|
95 |
-
|
96 |
-
fw_render_view($extension_view_path, array(
|
97 |
-
'name' => $name,
|
98 |
-
'title' => $data['name'],
|
99 |
-
'description' => $data['description'],
|
100 |
-
'link' => $link,
|
101 |
-
'lists' => &$lists,
|
102 |
-
'nonces' => $nonces,
|
103 |
-
'default_thumbnail' => $default_thumbnail,
|
104 |
-
'can_install' => $can_install,
|
105 |
-
), false);
|
106 |
-
|
107 |
-
$displayed[$name] = $something_displayed = true;
|
108 |
-
}
|
109 |
-
|
110 |
-
unset($theme_extensions);
|
111 |
-
}
|
112 |
-
|
113 |
-
foreach ($lists['disabled'] as $name => &$data) {
|
114 |
-
if (isset($displayed[$name])) {
|
115 |
-
continue;
|
116 |
-
} elseif (true !== fw_akg('display', $data['manifest'], $display_default_value)) {
|
117 |
-
continue;
|
118 |
-
}
|
119 |
-
|
120 |
-
fw_render_view($extension_view_path, array(
|
121 |
-
'name' => $name,
|
122 |
-
'title' => fw_akg('name', $data['manifest'], fw_id_to_title($name)),
|
123 |
-
'description' => fw_akg('description', $data['manifest'], ''),
|
124 |
-
'link' => $link,
|
125 |
-
'lists' => &$lists,
|
126 |
-
'nonces' => $nonces,
|
127 |
-
'default_thumbnail' => $default_thumbnail,
|
128 |
-
'can_install' => $can_install,
|
129 |
-
), false);
|
130 |
-
|
131 |
-
$displayed[$name] = $something_displayed = true;
|
132 |
-
}
|
133 |
-
unset($data);
|
134 |
-
|
135 |
-
if ($can_install) {
|
136 |
-
foreach ( $lists['available'] as $name => &$data ) {
|
137 |
-
if ( isset( $displayed[ $name ] ) ) {
|
138 |
-
continue;
|
139 |
-
} elseif ( isset( $lists['installed'][ $name ] ) ) {
|
140 |
-
continue;
|
141 |
-
} elseif ( $data['display'] !== true ) {
|
142 |
-
continue;
|
143 |
-
}
|
144 |
-
|
145 |
-
/**
|
146 |
-
* fixme: remove this in the future when this extensions will look good on any theme
|
147 |
-
*/
|
148 |
-
if ( in_array( $name, array( 'styling', 'megamenu' ) ) ) {
|
149 |
-
if ( isset( $lists['supported'][ $name ] ) || ( defined( 'WP_DEBUG' ) && WP_DEBUG ) ) {
|
150 |
-
} else {
|
151 |
-
continue;
|
152 |
-
}
|
153 |
-
}
|
154 |
-
|
155 |
-
fw_render_view( $extension_view_path, array(
|
156 |
-
'name' => $name,
|
157 |
-
'title' => $data['name'],
|
158 |
-
'description' => $data['description'],
|
159 |
-
'link' => $link,
|
160 |
-
'lists' => &$lists,
|
161 |
-
'nonces' => $nonces,
|
162 |
-
'default_thumbnail' => $default_thumbnail,
|
163 |
-
'can_install' => $can_install,
|
164 |
-
), false );
|
165 |
-
|
166 |
-
$something_displayed = true;
|
167 |
-
}
|
168 |
-
unset($data);
|
169 |
-
}
|
170 |
-
?>
|
171 |
-
</div>
|
172 |
-
|
173 |
-
<?php if ($something_displayed && apply_filters('fw_extensions_page_show_other_extensions', true)): ?>
|
174 |
-
<!-- show/hide not compatible extensions -->
|
175 |
-
<p class="fw-text-center toggle-not-compat-ext-btn-wrapper"><?php
|
176 |
-
echo fw_html_tag(
|
177 |
-
'a',
|
178 |
-
array(
|
179 |
-
'href' => '#',
|
180 |
-
'onclick' => 'return false;',
|
181 |
-
'class' => 'button toggle-not-compat-ext-btn',
|
182 |
-
'style' => 'box-shadow:none;'
|
183 |
-
),
|
184 |
-
'<span class="the-show-text">'. __('Show other extensions', 'fw') .'</span>'.
|
185 |
-
'<span class="the-hide-text fw-hidden">'. __('Hide other extensions', 'fw') .'</span>'
|
186 |
-
);
|
187 |
-
?></p>
|
188 |
-
<script type="text/javascript">
|
189 |
-
jQuery(function($){
|
190 |
-
if (
|
191 |
-
!$('.fw-extensions-list .fw-extensions-list-item.not-compatible').length
|
192 |
-
||
|
193 |
-
<?php echo empty($lists['supported']) ? 'true' : 'false' ?>
|
194 |
-
) {
|
195 |
-
// disable the show/hide feature
|
196 |
-
$('#fw-extensions-list-wrapper .toggle-not-compat-ext-btn-wrapper').addClass('fw-hidden');
|
197 |
-
} else {
|
198 |
-
$('#fw-extensions-list-wrapper .fw-extensions-list .fw-extensions-list-item.not-compatible').fadeOut('fast');
|
199 |
-
|
200 |
-
$('#fw-extensions-list-wrapper .toggle-not-compat-ext-btn-wrapper').on('click', function(){
|
201 |
-
$('#fw-extensions-list-wrapper .fw-extensions-list .fw-extensions-list-item.not-compatible')[
|
202 |
-
$(this).find('.the-hide-text').hasClass('fw-hidden') ? 'fadeIn' : 'fadeOut'
|
203 |
-
]();
|
204 |
-
|
205 |
-
$(this).find('.the-show-text, .the-hide-text').toggleClass('fw-hidden');
|
206 |
-
});
|
207 |
-
}
|
208 |
-
});
|
209 |
-
</script>
|
210 |
-
<!-- end: show/hide not compatible extensions -->
|
211 |
-
<?php else: ?>
|
212 |
-
<script type="text/javascript">
|
213 |
-
jQuery(function($){
|
214 |
-
$('#fw-extensions-list-available').remove();
|
215 |
-
});
|
216 |
-
</script>
|
217 |
-
<?php endif; ?>
|
218 |
-
</div>
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
/**
|
3 |
+
* @var array $lists
|
4 |
+
* @var string $link
|
5 |
+
* @var array $nonces
|
6 |
+
* @var mixed $display_default_value
|
7 |
+
* @var string $default_thumbnail
|
8 |
+
* @var bool $can_install
|
9 |
+
*/
|
10 |
+
|
11 |
+
$dir = dirname(__FILE__);
|
12 |
+
$extension_view_path = $dir .'/extension.php';
|
13 |
+
|
14 |
+
$displayed = array();
|
15 |
+
?>
|
16 |
+
|
17 |
+
<h3><?php _e('Active Extensions', 'fw') ?></h3>
|
18 |
+
<?php
|
19 |
+
$display_active_extensions = array();
|
20 |
+
|
21 |
+
foreach ($lists['active'] as $name => &$data) {
|
22 |
+
if (true !== fw_akg('display', $data['manifest'], $display_default_value)) {
|
23 |
+
continue;
|
24 |
+
}
|
25 |
+
|
26 |
+
$display_active_extensions[$name] = &$data;
|
27 |
+
}
|
28 |
+
unset($data);
|
29 |
+
?>
|
30 |
+
<?php if (empty($display_active_extensions)): ?>
|
31 |
+
<div class="fw-extensions-no-active">
|
32 |
+
<div class="fw-text-center fw-extensions-title-icon"><span class="dashicons dashicons-screenoptions"></span></div>
|
33 |
+
<p class="fw-text-center fw-text-muted"><em><?php _e('No extensions activated yet', 'fw'); ?><br/><?php _e('Check the available extensions below', 'fw'); ?></em></p>
|
34 |
+
</div>
|
35 |
+
<?php else: ?>
|
36 |
+
<div class="fw-row fw-extensions-list">
|
37 |
+
<?php
|
38 |
+
foreach ($display_active_extensions as $name => &$data) {
|
39 |
+
fw_render_view($extension_view_path, array(
|
40 |
+
'name' => $name,
|
41 |
+
'title' => fw_ext($name)->manifest->get_name(),
|
42 |
+
'description' => fw_ext($name)->manifest->get('description'),
|
43 |
+
'link' => $link,
|
44 |
+
'lists' => &$lists,
|
45 |
+
'nonces' => $nonces,
|
46 |
+
'default_thumbnail' => $default_thumbnail,
|
47 |
+
'can_install' => $can_install,
|
48 |
+
), false);
|
49 |
+
|
50 |
+
$displayed[$name] = true;
|
51 |
+
}
|
52 |
+
unset($data);
|
53 |
+
?>
|
54 |
+
</div>
|
55 |
+
<?php endif; ?>
|
56 |
+
|
57 |
+
<div id="fw-extensions-list-available">
|
58 |
+
<hr class="fw-extensions-lists-separator"/>
|
59 |
+
<h3><?php _e('Available Extensions', 'fw') ?></h3><!-- This "available" differs from technical "available" -->
|
60 |
+
<div class="fw-row fw-extensions-list">
|
61 |
+
<?php $something_displayed = false; ?>
|
62 |
+
<?php
|
63 |
+
{
|
64 |
+
$theme_extensions = array();
|
65 |
+
|
66 |
+
foreach ($lists['disabled'] as $name => &$data) {
|
67 |
+
if (!$data['is']['theme']) {
|
68 |
+
continue;
|
69 |
+
}
|
70 |
+
|
71 |
+
$theme_extensions[$name] = array(
|
72 |
+
'name' => fw_akg('name', $data['manifest'], fw_id_to_title($name)),
|
73 |
+
'description' => fw_akg('description', $data['manifest'], '')
|
74 |
+
);
|
75 |
+
}
|
76 |
+
unset($data);
|
77 |
+
|
78 |
+
foreach ($theme_extensions + $lists['supported'] as $name => $data) {
|
79 |
+
if (isset($displayed[$name])) {
|
80 |
+
continue;
|
81 |
+
} elseif (isset($lists['installed'][$name])) {
|
82 |
+
if (true !== fw_akg('display', $lists['installed'][$name]['manifest'], $display_default_value)) {
|
83 |
+
continue;
|
84 |
+
}
|
85 |
+
} else {
|
86 |
+
if (isset($lists['available'][$name])) {
|
87 |
+
if (!$can_install) {
|
88 |
+
continue;
|
89 |
+
}
|
90 |
+
} else {
|
91 |
+
//trigger_error(sprintf(__('Supported extension "%s" is not available.', 'fw'), $name));
|
92 |
+
continue;
|
93 |
+
}
|
94 |
+
}
|
95 |
+
|
96 |
+
fw_render_view($extension_view_path, array(
|
97 |
+
'name' => $name,
|
98 |
+
'title' => $data['name'],
|
99 |
+
'description' => $data['description'],
|
100 |
+
'link' => $link,
|
101 |
+
'lists' => &$lists,
|
102 |
+
'nonces' => $nonces,
|
103 |
+
'default_thumbnail' => $default_thumbnail,
|
104 |
+
'can_install' => $can_install,
|
105 |
+
), false);
|
106 |
+
|
107 |
+
$displayed[$name] = $something_displayed = true;
|
108 |
+
}
|
109 |
+
|
110 |
+
unset($theme_extensions);
|
111 |
+
}
|
112 |
+
|
113 |
+
foreach ($lists['disabled'] as $name => &$data) {
|
114 |
+
if (isset($displayed[$name])) {
|
115 |
+
continue;
|
116 |
+
} elseif (true !== fw_akg('display', $data['manifest'], $display_default_value)) {
|
117 |
+
continue;
|
118 |
+
}
|
119 |
+
|
120 |
+
fw_render_view($extension_view_path, array(
|
121 |
+
'name' => $name,
|
122 |
+
'title' => fw_akg('name', $data['manifest'], fw_id_to_title($name)),
|
123 |
+
'description' => fw_akg('description', $data['manifest'], ''),
|
124 |
+
'link' => $link,
|
125 |
+
'lists' => &$lists,
|
126 |
+
'nonces' => $nonces,
|
127 |
+
'default_thumbnail' => $default_thumbnail,
|
128 |
+
'can_install' => $can_install,
|
129 |
+
), false);
|
130 |
+
|
131 |
+
$displayed[$name] = $something_displayed = true;
|
132 |
+
}
|
133 |
+
unset($data);
|
134 |
+
|
135 |
+
if ($can_install) {
|
136 |
+
foreach ( $lists['available'] as $name => &$data ) {
|
137 |
+
if ( isset( $displayed[ $name ] ) ) {
|
138 |
+
continue;
|
139 |
+
} elseif ( isset( $lists['installed'][ $name ] ) ) {
|
140 |
+
continue;
|
141 |
+
} elseif ( $data['display'] !== true ) {
|
142 |
+
continue;
|
143 |
+
}
|
144 |
+
|
145 |
+
/**
|
146 |
+
* fixme: remove this in the future when this extensions will look good on any theme
|
147 |
+
*/
|
148 |
+
if ( in_array( $name, array( 'styling', 'megamenu' ) ) ) {
|
149 |
+
if ( isset( $lists['supported'][ $name ] ) || ( defined( 'WP_DEBUG' ) && WP_DEBUG ) ) {
|
150 |
+
} else {
|
151 |
+
continue;
|
152 |
+
}
|
153 |
+
}
|
154 |
+
|
155 |
+
fw_render_view( $extension_view_path, array(
|
156 |
+
'name' => $name,
|
157 |
+
'title' => $data['name'],
|
158 |
+
'description' => $data['description'],
|
159 |
+
'link' => $link,
|
160 |
+
'lists' => &$lists,
|
161 |
+
'nonces' => $nonces,
|
162 |
+
'default_thumbnail' => $default_thumbnail,
|
163 |
+
'can_install' => $can_install,
|
164 |
+
), false );
|
165 |
+
|
166 |
+
$something_displayed = true;
|
167 |
+
}
|
168 |
+
unset($data);
|
169 |
+
}
|
170 |
+
?>
|
171 |
+
</div>
|
172 |
+
|
173 |
+
<?php if ($something_displayed && apply_filters('fw_extensions_page_show_other_extensions', true)): ?>
|
174 |
+
<!-- show/hide not compatible extensions -->
|
175 |
+
<p class="fw-text-center toggle-not-compat-ext-btn-wrapper"><?php
|
176 |
+
echo fw_html_tag(
|
177 |
+
'a',
|
178 |
+
array(
|
179 |
+
'href' => '#',
|
180 |
+
'onclick' => 'return false;',
|
181 |
+
'class' => 'button toggle-not-compat-ext-btn',
|
182 |
+
'style' => 'box-shadow:none;'
|
183 |
+
),
|
184 |
+
'<span class="the-show-text">'. __('Show other extensions', 'fw') .'</span>'.
|
185 |
+
'<span class="the-hide-text fw-hidden">'. __('Hide other extensions', 'fw') .'</span>'
|
186 |
+
);
|
187 |
+
?></p>
|
188 |
+
<script type="text/javascript">
|
189 |
+
jQuery(function($){
|
190 |
+
if (
|
191 |
+
!$('.fw-extensions-list .fw-extensions-list-item.not-compatible').length
|
192 |
+
||
|
193 |
+
<?php echo empty($lists['supported']) ? 'true' : 'false' ?>
|
194 |
+
) {
|
195 |
+
// disable the show/hide feature
|
196 |
+
$('#fw-extensions-list-wrapper .toggle-not-compat-ext-btn-wrapper').addClass('fw-hidden');
|
197 |
+
} else {
|
198 |
+
$('#fw-extensions-list-wrapper .fw-extensions-list .fw-extensions-list-item.not-compatible').fadeOut('fast');
|
199 |
+
|
200 |
+
$('#fw-extensions-list-wrapper .toggle-not-compat-ext-btn-wrapper').on('click', function(){
|
201 |
+
$('#fw-extensions-list-wrapper .fw-extensions-list .fw-extensions-list-item.not-compatible')[
|
202 |
+
$(this).find('.the-hide-text').hasClass('fw-hidden') ? 'fadeIn' : 'fadeOut'
|
203 |
+
]();
|
204 |
+
|
205 |
+
$(this).find('.the-show-text, .the-hide-text').toggleClass('fw-hidden');
|
206 |
+
});
|
207 |
+
}
|
208 |
+
});
|
209 |
+
</script>
|
210 |
+
<!-- end: show/hide not compatible extensions -->
|
211 |
+
<?php else: ?>
|
212 |
+
<script type="text/javascript">
|
213 |
+
jQuery(function($){
|
214 |
+
$('#fw-extensions-list-available').remove();
|
215 |
+
});
|
216 |
+
</script>
|
217 |
+
<?php endif; ?>
|
218 |
+
</div>
|
framework/core/components/extensions/manager/views/install-form.php
CHANGED
@@ -1,51 +1,51 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
/**
|
3 |
-
* @var array $extension_titles
|
4 |
-
* @var array $list_page_link
|
5 |
-
* @var bool $supported
|
6 |
-
*/
|
7 |
-
|
8 |
-
$count = count($extension_titles);
|
9 |
-
?>
|
10 |
-
|
11 |
-
<?php if ($supported): ?>
|
12 |
-
<p><?php echo _n(
|
13 |
-
'We\'ve detected that your current theme is compatible with the following extension and it is recommended that you install it to fully benefit from your theme.',
|
14 |
-
'We\'ve detected that your current theme is compatible with the following extensions and it is recommended that you install them to fully benefit from your theme.',
|
15 |
-
$count,
|
16 |
-
'fw'
|
17 |
-
) ?></p>
|
18 |
-
<?php else: ?>
|
19 |
-
<p><?php echo _n(
|
20 |
-
'You are about to install the following extension:',
|
21 |
-
'You are about to install the following extensions:',
|
22 |
-
$count,
|
23 |
-
'fw'
|
24 |
-
) ?></p>
|
25 |
-
<?php endif; ?>
|
26 |
-
|
27 |
-
<ul class="ul-disc">
|
28 |
-
<?php foreach ($extension_titles as $extension_title): ?>
|
29 |
-
<li><strong><?php echo $extension_title; ?></strong></li>
|
30 |
-
<?php endforeach; ?>
|
31 |
-
</ul>
|
32 |
-
|
33 |
-
<p><?php
|
34 |
-
echo _n(
|
35 |
-
'Are you sure you wish to install this extension?',
|
36 |
-
'Are you sure you wish to install these extensions?',
|
37 |
-
$count,
|
38 |
-
'fw'
|
39 |
-
)
|
40 |
-
?></p>
|
41 |
-
|
42 |
-
<input type="submit" name="submit" id="submit" class="button" value="<?php
|
43 |
-
echo esc_attr( _n(
|
44 |
-
'Yes, Install this extension',
|
45 |
-
'Yes, Install these extensions',
|
46 |
-
$count,
|
47 |
-
'fw'
|
48 |
-
) )
|
49 |
-
?>">
|
50 |
-
|
51 |
-
<a class="button" href="<?php echo esc_attr($list_page_link) ?>" ><?php _e('No, Return me to the extension list', 'fw') ?></a>
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
/**
|
3 |
+
* @var array $extension_titles
|
4 |
+
* @var array $list_page_link
|
5 |
+
* @var bool $supported
|
6 |
+
*/
|
7 |
+
|
8 |
+
$count = count($extension_titles);
|
9 |
+
?>
|
10 |
+
|
11 |
+
<?php if ($supported): ?>
|
12 |
+
<p><?php echo _n(
|
13 |
+
'We\'ve detected that your current theme is compatible with the following extension and it is recommended that you install it to fully benefit from your theme.',
|
14 |
+
'We\'ve detected that your current theme is compatible with the following extensions and it is recommended that you install them to fully benefit from your theme.',
|
15 |
+
$count,
|
16 |
+
'fw'
|
17 |
+
) ?></p>
|
18 |
+
<?php else: ?>
|
19 |
+
<p><?php echo _n(
|
20 |
+
'You are about to install the following extension:',
|
21 |
+
'You are about to install the following extensions:',
|
22 |
+
$count,
|
23 |
+
'fw'
|
24 |
+
) ?></p>
|
25 |
+
<?php endif; ?>
|
26 |
+
|
27 |
+
<ul class="ul-disc">
|
28 |
+
<?php foreach ($extension_titles as $extension_title): ?>
|
29 |
+
<li><strong><?php echo $extension_title; ?></strong></li>
|
30 |
+
<?php endforeach; ?>
|
31 |
+
</ul>
|
32 |
+
|
33 |
+
<p><?php
|
34 |
+
echo _n(
|
35 |
+
'Are you sure you wish to install this extension?',
|
36 |
+
'Are you sure you wish to install these extensions?',
|
37 |
+
$count,
|
38 |
+
'fw'
|
39 |
+
)
|
40 |
+
?></p>
|
41 |
+
|
42 |
+
<input type="submit" name="submit" id="submit" class="button" value="<?php
|
43 |
+
echo esc_attr( _n(
|
44 |
+
'Yes, Install this extension',
|
45 |
+
'Yes, Install these extensions',
|
46 |
+
$count,
|
47 |
+
'fw'
|
48 |
+
) )
|
49 |
+
?>">
|
50 |
+
|
51 |
+
<a class="button" href="<?php echo esc_attr($list_page_link) ?>" ><?php _e('No, Return me to the extension list', 'fw') ?></a>
|
framework/core/components/theme.php
CHANGED
@@ -1,203 +1,203 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Theme Component
|
5 |
-
* Works with framework customizations / theme directory
|
6 |
-
*/
|
7 |
-
final class _FW_Component_Theme
|
8 |
-
{
|
9 |
-
private static $cache_key = 'fw_theme';
|
10 |
-
|
11 |
-
/**
|
12 |
-
* @var FW_Theme_Manifest
|
13 |
-
*/
|
14 |
-
public $manifest;
|
15 |
-
|
16 |
-
public function __construct()
|
17 |
-
{
|
18 |
-
{
|
19 |
-
$manifest = array();
|
20 |
-
|
21 |
-
@include fw_get_template_customizations_directory('/theme/manifest.php');
|
22 |
-
|
23 |
-
$this->manifest = new FW_Theme_Manifest($manifest);
|
24 |
-
}
|
25 |
-
}
|
26 |
-
|
27 |
-
/**
|
28 |
-
* @internal
|
29 |
-
*/
|
30 |
-
public function _init()
|
31 |
-
{
|
32 |
-
add_action('admin_notices', array($this, '_action_admin_notices'));
|
33 |
-
}
|
34 |
-
|
35 |
-
/**
|
36 |
-
* @internal
|
37 |
-
*/
|
38 |
-
public function _after_components_init()
|
39 |
-
{
|
40 |
-
}
|
41 |
-
|
42 |
-
/**
|
43 |
-
* Search relative path in: child theme -> parent "theme" directory and return full path
|
44 |
-
* @param string $rel_path
|
45 |
-
* @return false|string
|
46 |
-
*/
|
47 |
-
public function locate_path($rel_path)
|
48 |
-
{
|
49 |
-
if (is_child_theme() && file_exists(fw_get_stylesheet_customizations_directory('/theme'. $rel_path))) {
|
50 |
-
return fw_get_stylesheet_customizations_directory('/theme'. $rel_path);
|
51 |
-
}
|
52 |
-
|
53 |
-
if (file_exists(fw_get_template_customizations_directory('/theme'. $rel_path))) {
|
54 |
-
return fw_get_template_customizations_directory('/theme'. $rel_path);
|
55 |
-
}
|
56 |
-
|
57 |
-
return false;
|
58 |
-
}
|
59 |
-
|
60 |
-
/**
|
61 |
-
* Return array with options from specified name/path
|
62 |
-
* @param string $name '{theme}/framework-customizations/theme/options/{$name}.php'
|
63 |
-
* @param array $variables These will be available in options file (like variables for view)
|
64 |
-
* @return array
|
65 |
-
*/
|
66 |
-
public function get_options($name, array $variables = array())
|
67 |
-
{
|
68 |
-
$path = $this->locate_path('/options/'. $name .'.php');
|
69 |
-
|
70 |
-
if (!$path) {
|
71 |
-
return array();
|
72 |
-
}
|
73 |
-
|
74 |
-
$variables = fw_get_variables_from_file($path, array('options' => array()), $variables);
|
75 |
-
|
76 |
-
return $variables['options'];
|
77 |
-
}
|
78 |
-
|
79 |
-
public function get_settings_options()
|
80 |
-
{
|
81 |
-
$cache_key = self::$cache_key .'/options/settings';
|
82 |
-
|
83 |
-
try {
|
84 |
-
return FW_Cache::get($cache_key);
|
85 |
-
} catch (FW_Cache_Not_Found_Exception $e) {
|
86 |
-
$options = apply_filters('fw_settings_options', $this->get_options('settings'));
|
87 |
-
|
88 |
-
FW_Cache::set($cache_key, $options);
|
89 |
-
|
90 |
-
return $options;
|
91 |
-
}
|
92 |
-
}
|
93 |
-
|
94 |
-
public function get_customizer_options()
|
95 |
-
{
|
96 |
-
$cache_key = self::$cache_key .'/options/customizer';
|
97 |
-
|
98 |
-
try {
|
99 |
-
return FW_Cache::get($cache_key);
|
100 |
-
} catch (FW_Cache_Not_Found_Exception $e) {
|
101 |
-
$options = apply_filters('fw_customizer_options', $this->get_options('customizer'));
|
102 |
-
|
103 |
-
FW_Cache::set($cache_key, $options);
|
104 |
-
|
105 |
-
return $options;
|
106 |
-
}
|
107 |
-
}
|
108 |
-
|
109 |
-
public function get_post_options($post_type)
|
110 |
-
{
|
111 |
-
$cache_key = self::$cache_key .'/options/posts/'. $post_type;
|
112 |
-
|
113 |
-
try {
|
114 |
-
return FW_Cache::get($cache_key);
|
115 |
-
} catch (FW_Cache_Not_Found_Exception $e) {
|
116 |
-
$options = apply_filters('fw_post_options', $this->get_options('posts/'. $post_type), $post_type);
|
117 |
-
|
118 |
-
FW_Cache::set($cache_key, $options);
|
119 |
-
|
120 |
-
return $options;
|
121 |
-
}
|
122 |
-
}
|
123 |
-
|
124 |
-
public function get_taxonomy_options($taxonomy)
|
125 |
-
{
|
126 |
-
$cache_key = self::$cache_key .'/options/taxonomies/'. $taxonomy;
|
127 |
-
|
128 |
-
try {
|
129 |
-
return FW_Cache::get($cache_key);
|
130 |
-
} catch (FW_Cache_Not_Found_Exception $e) {
|
131 |
-
$options = apply_filters('fw_taxonomy_options',
|
132 |
-
$this->get_options('taxonomies/'. $taxonomy),
|
133 |
-
$taxonomy
|
134 |
-
);
|
135 |
-
|
136 |
-
FW_Cache::set($cache_key, $options);
|
137 |
-
|
138 |
-
return $options;
|
139 |
-
}
|
140 |
-
}
|
141 |
-
|
142 |
-
/**
|
143 |
-
* Return config key value, or entire config array
|
144 |
-
* Config array is merged from child configs
|
145 |
-
* @param string|null $key Multi key format accepted: 'a/b/c'
|
146 |
-
* @param mixed $default_value
|
147 |
-
* @return mixed|null
|
148 |
-
*/
|
149 |
-
final public function get_config($key = null, $default_value = null)
|
150 |
-
{
|
151 |
-
$cache_key = self::$cache_key .'/config';
|
152 |
-
|
153 |
-
try {
|
154 |
-
$config = FW_Cache::get($cache_key);
|
155 |
-
} catch (FW_Cache_Not_Found_Exception $e) {
|
156 |
-
// default values
|
157 |
-
$config = array(
|
158 |
-
/** Toggle Theme Settings form ajax submit */
|
159 |
-
'settings_form_ajax_submit' => true,
|
160 |
-
/** Toggle Theme Settings side tabs */
|
161 |
-
'settings_form_side_tabs' => false,
|
162 |
-
/** Toggle Tabs rendered all at once, or initialized only on open/display */
|
163 |
-
'lazy_tabs' => true,
|
164 |
-
);
|
165 |
-
|
166 |
-
if (file_exists(fw_get_template_customizations_directory('/theme/config.php'))) {
|
167 |
-
$variables = fw_get_variables_from_file(fw_get_template_customizations_directory('/theme/config.php'), array('cfg' => null));
|
168 |
-
|
169 |
-
if (!empty($variables['cfg'])) {
|
170 |
-
$config = array_merge($config, $variables['cfg']);
|
171 |
-
unset($variables);
|
172 |
-
}
|
173 |
-
}
|
174 |
-
|
175 |
-
if (is_child_theme() && file_exists(fw_get_stylesheet_customizations_directory('/theme/config.php'))) {
|
176 |
-
$variables = fw_get_variables_from_file(fw_get_stylesheet_customizations_directory('/theme/config.php'), array('cfg' => null));
|
177 |
-
|
178 |
-
if (!empty($variables['cfg'])) {
|
179 |
-
$config = array_merge($config, $variables['cfg']);
|
180 |
-
unset($variables);
|
181 |
-
}
|
182 |
-
}
|
183 |
-
|
184 |
-
unset($path);
|
185 |
-
|
186 |
-
FW_Cache::set($cache_key, $config);
|
187 |
-
}
|
188 |
-
|
189 |
-
return $key === null ? $config : fw_akg($key, $config, $default_value);
|
190 |
-
}
|
191 |
-
|
192 |
-
/**
|
193 |
-
* @internal
|
194 |
-
*/
|
195 |
-
public function _action_admin_notices()
|
196 |
-
{
|
197 |
-
if ( is_admin() && !fw()->theme->manifest->check_requirements() && current_user_can('manage_options') ) {
|
198 |
-
echo '<div class="notice notice-warning"><p>';
|
199 |
-
echo __('Theme requirements not met:', 'fw') .' '. fw()->theme->manifest->get_not_met_requirement_text();
|
200 |
-
echo '</p></div>';
|
201 |
-
}
|
202 |
-
}
|
203 |
-
}
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Theme Component
|
5 |
+
* Works with framework customizations / theme directory
|
6 |
+
*/
|
7 |
+
final class _FW_Component_Theme
|
8 |
+
{
|
9 |
+
private static $cache_key = 'fw_theme';
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var FW_Theme_Manifest
|
13 |
+
*/
|
14 |
+
public $manifest;
|
15 |
+
|
16 |
+
public function __construct()
|
17 |
+
{
|
18 |
+
{
|
19 |
+
$manifest = array();
|
20 |
+
|
21 |
+
@include fw_get_template_customizations_directory('/theme/manifest.php');
|
22 |
+
|
23 |
+
$this->manifest = new FW_Theme_Manifest($manifest);
|
24 |
+
}
|
25 |
+
}
|
26 |
+
|
27 |
+
/**
|
28 |
+
* @internal
|
29 |
+
*/
|
30 |
+
public function _init()
|
31 |
+
{
|
32 |
+
add_action('admin_notices', array($this, '_action_admin_notices'));
|
33 |
+
}
|
34 |
+
|
35 |
+
/**
|
36 |
+
* @internal
|
37 |
+
*/
|
38 |
+
public function _after_components_init()
|
39 |
+
{
|
40 |
+
}
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Search relative path in: child theme -> parent "theme" directory and return full path
|
44 |
+
* @param string $rel_path
|
45 |
+
* @return false|string
|
46 |
+
*/
|
47 |
+
public function locate_path($rel_path)
|
48 |
+
{
|
49 |
+
if (is_child_theme() && file_exists(fw_get_stylesheet_customizations_directory('/theme'. $rel_path))) {
|
50 |
+
return fw_get_stylesheet_customizations_directory('/theme'. $rel_path);
|
51 |
+
}
|
52 |
+
|
53 |
+
if (file_exists(fw_get_template_customizations_directory('/theme'. $rel_path))) {
|
54 |
+
return fw_get_template_customizations_directory('/theme'. $rel_path);
|
55 |
+
}
|
56 |
+
|
57 |
+
return false;
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Return array with options from specified name/path
|
62 |
+
* @param string $name '{theme}/framework-customizations/theme/options/{$name}.php'
|
63 |
+
* @param array $variables These will be available in options file (like variables for view)
|
64 |
+
* @return array
|
65 |
+
*/
|
66 |
+
public function get_options($name, array $variables = array())
|
67 |
+
{
|
68 |
+
$path = $this->locate_path('/options/'. $name .'.php');
|
69 |
+
|
70 |
+
if (!$path) {
|
71 |
+
return array();
|
72 |
+
}
|
73 |
+
|
74 |
+
$variables = fw_get_variables_from_file($path, array('options' => array()), $variables);
|
75 |
+
|
76 |
+
return $variables['options'];
|
77 |
+
}
|
78 |
+
|
79 |
+
public function get_settings_options()
|
80 |
+
{
|
81 |
+
$cache_key = self::$cache_key .'/options/settings';
|
82 |
+
|
83 |
+
try {
|
84 |
+
return FW_Cache::get($cache_key);
|
85 |
+
} catch (FW_Cache_Not_Found_Exception $e) {
|
86 |
+
$options = apply_filters('fw_settings_options', $this->get_options('settings'));
|
87 |
+
|
88 |
+
FW_Cache::set($cache_key, $options);
|
89 |
+
|
90 |
+
return $options;
|
91 |
+
}
|
92 |
+
}
|
93 |
+
|
94 |
+
public function get_customizer_options()
|
95 |
+
{
|
96 |
+
$cache_key = self::$cache_key .'/options/customizer';
|
97 |
+
|
98 |
+
try {
|
99 |
+
return FW_Cache::get($cache_key);
|
100 |
+
} catch (FW_Cache_Not_Found_Exception $e) {
|
101 |
+
$options = apply_filters('fw_customizer_options', $this->get_options('customizer'));
|
102 |
+
|
103 |
+
FW_Cache::set($cache_key, $options);
|
104 |
+
|
105 |
+
return $options;
|
106 |
+
}
|
107 |
+
}
|
108 |
+
|
109 |
+
public function get_post_options($post_type)
|
110 |
+
{
|
111 |
+
$cache_key = self::$cache_key .'/options/posts/'. $post_type;
|
112 |
+
|
113 |
+
try {
|
114 |
+
return FW_Cache::get($cache_key);
|
115 |
+
} catch (FW_Cache_Not_Found_Exception $e) {
|
116 |
+
$options = apply_filters('fw_post_options', $this->get_options('posts/'. $post_type), $post_type);
|
117 |
+
|
118 |
+
FW_Cache::set($cache_key, $options);
|
119 |
+
|
120 |
+
return $options;
|
121 |
+
}
|
122 |
+
}
|
123 |
+
|
124 |
+
public function get_taxonomy_options($taxonomy)
|
125 |
+
{
|
126 |
+
$cache_key = self::$cache_key .'/options/taxonomies/'. $taxonomy;
|
127 |
+
|
128 |
+
try {
|
129 |
+
return FW_Cache::get($cache_key);
|
130 |
+
} catch (FW_Cache_Not_Found_Exception $e) {
|
131 |
+
$options = apply_filters('fw_taxonomy_options',
|
132 |
+
$this->get_options('taxonomies/'. $taxonomy),
|
133 |
+
$taxonomy
|
134 |
+
);
|
135 |
+
|
136 |
+
FW_Cache::set($cache_key, $options);
|
137 |
+
|
138 |
+
return $options;
|
139 |
+
}
|
140 |
+
}
|
141 |
+
|
142 |
+
/**
|
143 |
+
* Return config key value, or entire config array
|
144 |
+
* Config array is merged from child configs
|
145 |
+
* @param string|null $key Multi key format accepted: 'a/b/c'
|
146 |
+
* @param mixed $default_value
|
147 |
+
* @return mixed|null
|
148 |
+
*/
|
149 |
+
final public function get_config($key = null, $default_value = null)
|
150 |
+
{
|
151 |
+
$cache_key = self::$cache_key .'/config';
|
152 |
+
|
153 |
+
try {
|
154 |
+
$config = FW_Cache::get($cache_key);
|
155 |
+
} catch (FW_Cache_Not_Found_Exception $e) {
|
156 |
+
// default values
|
157 |
+
$config = array(
|
158 |
+
/** Toggle Theme Settings form ajax submit */
|
159 |
+
'settings_form_ajax_submit' => true,
|
160 |
+
/** Toggle Theme Settings side tabs */
|
161 |
+
'settings_form_side_tabs' => false,
|
162 |
+
/** Toggle Tabs rendered all at once, or initialized only on open/display */
|
163 |
+
'lazy_tabs' => true,
|
164 |
+
);
|
165 |
+
|
166 |
+
if (file_exists(fw_get_template_customizations_directory('/theme/config.php'))) {
|
167 |
+
$variables = fw_get_variables_from_file(fw_get_template_customizations_directory('/theme/config.php'), array('cfg' => null));
|
168 |
+
|
169 |
+
if (!empty($variables['cfg'])) {
|
170 |
+
$config = array_merge($config, $variables['cfg']);
|
171 |
+
unset($variables);
|
172 |
+
}
|
173 |
+
}
|
174 |
+
|
175 |
+
if (is_child_theme() && file_exists(fw_get_stylesheet_customizations_directory('/theme/config.php'))) {
|
176 |
+
$variables = fw_get_variables_from_file(fw_get_stylesheet_customizations_directory('/theme/config.php'), array('cfg' => null));
|
177 |
+
|
178 |
+
if (!empty($variables['cfg'])) {
|
179 |
+
$config = array_merge($config, $variables['cfg']);
|
180 |
+
unset($variables);
|
181 |
+
}
|
182 |
+
}
|
183 |
+
|
184 |
+
unset($path);
|
185 |
+
|
186 |
+
FW_Cache::set($cache_key, $config);
|
187 |
+
}
|
188 |
+
|
189 |
+
return $key === null ? $config : fw_akg($key, $config, $default_value);
|
190 |
+
}
|
191 |
+
|
192 |
+
/**
|
193 |
+
* @internal
|
194 |
+
*/
|
195 |
+
public function _action_admin_notices()
|
196 |
+
{
|
197 |
+
if ( is_admin() && !fw()->theme->manifest->check_requirements() && current_user_can('manage_options') ) {
|
198 |
+
echo '<div class="notice notice-warning"><p>';
|
199 |
+
echo __('Theme requirements not met:', 'fw') .' '. fw()->theme->manifest->get_not_met_requirement_text();
|
200 |
+
echo '</p></div>';
|
201 |
+
}
|
202 |
+
}
|
203 |
+
}
|
framework/core/extends/class-fw-container-type.php
CHANGED
@@ -1,233 +1,233 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Backend option container
|
5 |
-
*/
|
6 |
-
abstract class FW_Container_Type
|
7 |
-
{
|
8 |
-
/**
|
9 |
-
* Container's unique type, used in option array in 'type' key
|
10 |
-
* @return string
|
11 |
-
*/
|
12 |
-
abstract public function get_type();
|
13 |
-
|
14 |
-
/**
|
15 |
-
* Overwrite this method to enqueue scripts and styles
|
16 |
-
*
|
17 |
-
* @param string $id
|
18 |
-
* @param array $option
|
19 |
-
* @param array $values Options values (in db format, returned from get_value_from_input())
|
20 |
-
* @param array $data
|
21 |
-
* @param bool Return true to call this method again on the next enqueue,
|
22 |
-
* if you have some functionality in it that depends on option parameters.
|
23 |
-
* By default this method is called only once for performance reasons.
|
24 |
-
*/
|
25 |
-
abstract protected function _enqueue_static($id, $option, $values, $data);
|
26 |
-
|
27 |
-
/**
|
28 |
-
* Generate html
|
29 |
-
* @param array $containers array('option_id' => array(), ...)
|
30 |
-
* - Options arrays are merged with _get_defaults()
|
31 |
-
* - All options are 100% only current container type, no need to check if ($option['type'] === $this->get_type())
|
32 |
-
* - Are sent multiple options instead of one, because tabs (and maybe other feature containers)
|
33 |
-
* can't be rendered separately (only as a collection).
|
34 |
-
* Instead of having render_option() for those that can be rendered separately,
|
35 |
-
* and render_options() for those like tabs, was decided to make a compromise,
|
36 |
-
* only one method that always will receive an array of options,
|
37 |
-
* instead of two methods when things may become confuse and complicated.
|
38 |
-
* @param array $values Options values (in db format, returned from get_value_from_input())
|
39 |
-
* @param array $data {id_prefix => '...', name_prefix => '...'}
|
40 |
-
* @return string HTML
|
41 |
-
* @internal
|
42 |
-
*/
|
43 |
-
abstract protected function _render($containers, $values, $data);
|
44 |
-
|
45 |
-
/**
|
46 |
-
* Default option array
|
47 |
-
*
|
48 |
-
* This makes possible a container option array to have required only two parameters:
|
49 |
-
* array('type' => '...', 'options' => array(...))
|
50 |
-
* Other parameters are merged with the array returned by this method.
|
51 |
-
*
|
52 |
-
* @return array
|
53 |
-
*
|
54 |
-
* array(
|
55 |
-
* 'type' => '...',
|
56 |
-
* ...
|
57 |
-
* 'options' => array(...),
|
58 |
-
* )
|
59 |
-
* @internal
|
60 |
-
*/
|
61 |
-
abstract protected function _get_defaults();
|
62 |
-
|
63 |
-
/**
|
64 |
-
* Prevent execute enqueue multiple times
|
65 |
-
* @var bool
|
66 |
-
*/
|
67 |
-
private $static_enqueued = false;
|
68 |
-
|
69 |
-
final public function __construct()
|
70 |
-
{
|
71 |
-
// does nothing at the moment, but maybe in the future will do something
|
72 |
-
}
|
73 |
-
|
74 |
-
/**
|
75 |
-
* @param FW_Access_Key $access_key
|
76 |
-
* @internal
|
77 |
-
* This must be called right after an instance of container type has been created
|
78 |
-
* and was added to the registered array
|
79 |
-
*/
|
80 |
-
final public function _call_init($access_key)
|
81 |
-
{
|
82 |
-
if ($access_key->get_key() !== 'fw_backend') {
|
83 |
-
trigger_error('Method call not allowed', E_USER_ERROR);
|
84 |
-
}
|
85 |
-
|
86 |
-
if (method_exists($this, '_init')) {
|
87 |
-
$this->_init();
|
88 |
-
}
|
89 |
-
}
|
90 |
-
|
91 |
-
/**
|
92 |
-
* Fixes and prepare defaults
|
93 |
-
*
|
94 |
-
* @param string $id
|
95 |
-
* @param array $option
|
96 |
-
* @param array $data
|
97 |
-
* @return array
|
98 |
-
*/
|
99 |
-
private function prepare($id, &$option, &$data)
|
100 |
-
{
|
101 |
-
$data = array_merge(
|
102 |
-
array(
|
103 |
-
'id_prefix' => FW_Option_Type::get_default_id_prefix(), // attribute id prefix
|
104 |
-
'name_prefix' => FW_Option_Type::get_default_name_prefix(), // attribute name prefix
|
105 |
-
),
|
106 |
-
$data
|
107 |
-
);
|
108 |
-
|
109 |
-
$option = array_merge(
|
110 |
-
$this->get_defaults(),
|
111 |
-
$option,
|
112 |
-
array(
|
113 |
-
'type' => $this->get_type(),
|
114 |
-
)
|
115 |
-
);
|
116 |
-
|
117 |
-
if (!isset($option['attr'])) {
|
118 |
-
$option['attr'] = array();
|
119 |
-
}
|
120 |
-
|
121 |
-
if (!isset($option['title'])) {
|
122 |
-
$option['title'] = fw_id_to_title($id);
|
123 |
-
}
|
124 |
-
|
125 |
-
$option['attr']['class'] = 'fw-container fw-container-type-'. $option['type'] .(
|
126 |
-
isset($option['attr']['class'])
|
127 |
-
? ' '. $option['attr']['class']
|
128 |
-
: ''
|
129 |
-
);
|
130 |
-
}
|
131 |
-
|
132 |
-
/**
|
133 |
-
* Generate html
|
134 |
-
* @param array $options array('container_id' => array(...container option...))
|
135 |
-
* @param array $values Options values (in db format, returned from get_value_from_input())
|
136 |
-
* @param array $data {'id_prefix' => '...', 'name_prefix' => '...'}
|
137 |
-
* @return string HTML
|
138 |
-
*/
|
139 |
-
final public function render($options, $values = array(), $data = array())
|
140 |
-
{
|
141 |
-
$containers = array();
|
142 |
-
|
143 |
-
foreach ($options as $id => &$option) {
|
144 |
-
if (
|
145 |
-
!isset($option['options'])
|
146 |
-
||
|
147 |
-
!isset($option['type'])
|
148 |
-
||
|
149 |
-
$option['type'] !== $this->get_type()
|
150 |
-
) {
|
151 |
-
continue;
|
152 |
-
}
|
153 |
-
|
154 |
-
$this->prepare($id, $option, $data);
|
155 |
-
|
156 |
-
$this->enqueue_static($id, $option, $data);
|
157 |
-
|
158 |
-
$containers[$id] = &$option;
|
159 |
-
}
|
160 |
-
|
161 |
-
return $this->_render($containers, $values, $data);
|
162 |
-
}
|
163 |
-
|
164 |
-
/**
|
165 |
-
* Enqueue container type scripts and styles
|
166 |
-
*
|
167 |
-
* All parameters are optional and will be populated with defaults
|
168 |
-
*
|
169 |
-
* @param string $id
|
170 |
-
* @param array $option
|
171 |
-
* @param array $values Options values (in db format, returned from get_value_from_input())
|
172 |
-
* @param array $data
|
173 |
-
* @return bool
|
174 |
-
*/
|
175 |
-
final public function enqueue_static($id = '', $option = array(), $values = array(), $data = array())
|
176 |
-
{
|
177 |
-
if (
|
178 |
-
!doing_action('admin_enqueue_scripts')
|
179 |
-
&&
|
180 |
-
!did_action('admin_enqueue_scripts')
|
181 |
-
) {
|
182 |
-
/**
|
183 |
-
* Do not wp_enqueue/register_...() because at this point not all handles has been registered
|
184 |
-
* and maybe they are used in dependencies in handles that are going to be enqueued.
|
185 |
-
* So as a result some handles will not be equeued because of not registered dependecies.
|
186 |
-
*/
|
187 |
-
return;
|
188 |
-
}
|
189 |
-
|
190 |
-
if ($this->static_enqueued) {
|
191 |
-
return false;
|
192 |
-
}
|
193 |
-
|
194 |
-
$this->prepare($id, $option, $data);
|
195 |
-
|
196 |
-
$call_next_time = $this->_enqueue_static($id, $option, $values, $data);
|
197 |
-
|
198 |
-
$this->static_enqueued = !$call_next_time;
|
199 |
-
|
200 |
-
return $call_next_time;
|
201 |
-
}
|
202 |
-
|
203 |
-
/**
|
204 |
-
* Default option array
|
205 |
-
*
|
206 |
-
* @return array
|
207 |
-
* 'type' => '...'
|
208 |
-
* 'title' => '...'
|
209 |
-
* 'attr' => array(...)
|
210 |
-
*/
|
211 |
-
final public function get_defaults()
|
212 |
-
{
|
213 |
-
$option = $this->_get_defaults();
|
214 |
-
|
215 |
-
$option['type'] = $this->get_type();
|
216 |
-
|
217 |
-
return $option;
|
218 |
-
}
|
219 |
-
|
220 |
-
/**
|
221 |
-
* Use this method to register a new container type
|
222 |
-
* @param string|FW_Container_Type $container_type_class
|
223 |
-
*/
|
224 |
-
final public static function register($container_type_class) {
|
225 |
-
static $registration_access_key = null;
|
226 |
-
|
227 |
-
if ($registration_access_key === null) {
|
228 |
-
$registration_access_key = new FW_Access_Key('fw_container_type');
|
229 |
-
}
|
230 |
-
|
231 |
-
fw()->backend->_register_container_type($registration_access_key, $container_type_class);
|
232 |
-
}
|
233 |
}
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Backend option container
|
5 |
+
*/
|
6 |
+
abstract class FW_Container_Type
|
7 |
+
{
|
8 |
+
/**
|
9 |
+
* Container's unique type, used in option array in 'type' key
|
10 |
+
* @return string
|
11 |
+
*/
|
12 |
+
abstract public function get_type();
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Overwrite this method to enqueue scripts and styles
|
16 |
+
*
|
17 |
+
* @param string $id
|
18 |
+
* @param array $option
|
19 |
+
* @param array $values Options values (in db format, returned from get_value_from_input())
|
20 |
+
* @param array $data
|
21 |
+
* @param bool Return true to call this method again on the next enqueue,
|
22 |
+
* if you have some functionality in it that depends on option parameters.
|
23 |
+
* By default this method is called only once for performance reasons.
|
24 |
+
*/
|
25 |
+
abstract protected function _enqueue_static($id, $option, $values, $data);
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Generate html
|
29 |
+
* @param array $containers array('option_id' => array(), ...)
|
30 |
+
* - Options arrays are merged with _get_defaults()
|
31 |
+
* - All options are 100% only current container type, no need to check if ($option['type'] === $this->get_type())
|
32 |
+
* - Are sent multiple options instead of one, because tabs (and maybe other feature containers)
|
33 |
+
* can't be rendered separately (only as a collection).
|
34 |
+
* Instead of having render_option() for those that can be rendered separately,
|
35 |
+
* and render_options() for those like tabs, was decided to make a compromise,
|
36 |
+
* only one method that always will receive an array of options,
|
37 |
+
* instead of two methods when things may become confuse and complicated.
|
38 |
+
* @param array $values Options values (in db format, returned from get_value_from_input())
|
39 |
+
* @param array $data {id_prefix => '...', name_prefix => '...'}
|
40 |
+
* @return string HTML
|
41 |
+
* @internal
|
42 |
+
*/
|
43 |
+
abstract protected function _render($containers, $values, $data);
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Default option array
|
47 |
+
*
|
48 |
+
* This makes possible a container option array to have required only two parameters:
|
49 |
+
* array('type' => '...', 'options' => array(...))
|
50 |
+
* Other parameters are merged with the array returned by this method.
|
51 |
+
*
|
52 |
+
* @return array
|
53 |
+
*
|
54 |
+
* array(
|
55 |
+
* 'type' => '...',
|
56 |
+
* ...
|
57 |
+
* 'options' => array(...),
|
58 |
+
* )
|
59 |
+
* @internal
|
60 |
+
*/
|
61 |
+
abstract protected function _get_defaults();
|
62 |
+
|
63 |
+
/**
|
64 |
+
* Prevent execute enqueue multiple times
|
65 |
+
* @var bool
|
66 |
+
*/
|
67 |
+
private $static_enqueued = false;
|
68 |
+
|
69 |
+
final public function __construct()
|
70 |
+
{
|
71 |
+
// does nothing at the moment, but maybe in the future will do something
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* @param FW_Access_Key $access_key
|
76 |
+
* @internal
|
77 |
+
* This must be called right after an instance of container type has been created
|
78 |
+
* and was added to the registered array
|
79 |
+
*/
|
80 |
+
final public function _call_init($access_key)
|
81 |
+
{
|
82 |
+
if ($access_key->get_key() !== 'fw_backend') {
|
83 |
+
trigger_error('Method call not allowed', E_USER_ERROR);
|
84 |
+
}
|
85 |
+
|
86 |
+
if (method_exists($this, '_init')) {
|
87 |
+
$this->_init();
|
88 |
+
}
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
* Fixes and prepare defaults
|
93 |
+
*
|
94 |
+
* @param string $id
|
95 |
+
* @param array $option
|
96 |
+
* @param array $data
|
97 |
+
* @return array
|
98 |
+
*/
|
99 |
+
private function prepare($id, &$option, &$data)
|
100 |
+
{
|
101 |
+
$data = array_merge(
|
102 |
+
array(
|
103 |
+
'id_prefix' => FW_Option_Type::get_default_id_prefix(), // attribute id prefix
|
104 |
+
'name_prefix' => FW_Option_Type::get_default_name_prefix(), // attribute name prefix
|
105 |
+
),
|
106 |
+
$data
|
107 |
+
);
|
108 |
+
|
109 |
+
$option = array_merge(
|
110 |
+
$this->get_defaults(),
|
111 |
+
$option,
|
112 |
+
array(
|
113 |
+
'type' => $this->get_type(),
|
114 |
+
)
|
115 |
+
);
|
116 |
+
|
117 |
+
if (!isset($option['attr'])) {
|
118 |
+
$option['attr'] = array();
|
119 |
+
}
|
120 |
+
|
121 |
+
if (!isset($option['title'])) {
|
122 |
+
$option['title'] = fw_id_to_title($id);
|
123 |
+
}
|
124 |
+
|
125 |
+
$option['attr']['class'] = 'fw-container fw-container-type-'. $option['type'] .(
|
126 |
+
isset($option['attr']['class'])
|
127 |
+
? ' '. $option['attr']['class']
|
128 |
+
: ''
|
129 |
+
);
|
130 |
+
}
|
131 |
+
|
132 |
+
/**
|
133 |
+
* Generate html
|
134 |
+
* @param array $options array('container_id' => array(...container option...))
|
135 |
+
* @param array $values Options values (in db format, returned from get_value_from_input())
|
136 |
+
* @param array $data {'id_prefix' => '...', 'name_prefix' => '...'}
|
137 |
+
* @return string HTML
|
138 |
+
*/
|
139 |
+
final public function render($options, $values = array(), $data = array())
|
140 |
+
{
|
141 |
+
$containers = array();
|
142 |
+
|
143 |
+
foreach ($options as $id => &$option) {
|
144 |
+
if (
|
145 |
+
!isset($option['options'])
|
146 |
+
||
|
147 |
+
!isset($option['type'])
|
148 |
+
||
|
149 |
+
$option['type'] !== $this->get_type()
|
150 |
+
) {
|
151 |
+
continue;
|
152 |
+
}
|
153 |
+
|
154 |
+
$this->prepare($id, $option, $data);
|
155 |
+
|
156 |
+
$this->enqueue_static($id, $option, $data);
|
157 |
+
|
158 |
+
$containers[$id] = &$option;
|
159 |
+
}
|
160 |
+
|
161 |
+
return $this->_render($containers, $values, $data);
|
162 |
+
}
|
163 |
+
|
164 |
+
/**
|
165 |
+
* Enqueue container type scripts and styles
|
166 |
+
*
|
167 |
+
* All parameters are optional and will be populated with defaults
|
168 |
+
*
|
169 |
+
* @param string $id
|
170 |
+
* @param array $option
|
171 |
+
* @param array $values Options values (in db format, returned from get_value_from_input())
|
172 |
+
* @param array $data
|
173 |
+
* @return bool
|
174 |
+
*/
|
175 |
+
final public function enqueue_static($id = '', $option = array(), $values = array(), $data = array())
|
176 |
+
{
|
177 |
+
if (
|
178 |
+
!doing_action('admin_enqueue_scripts')
|
179 |
+
&&
|
180 |
+
!did_action('admin_enqueue_scripts')
|
181 |
+
) {
|
182 |
+
/**
|
183 |
+
* Do not wp_enqueue/register_...() because at this point not all handles has been registered
|
184 |
+
* and maybe they are used in dependencies in handles that are going to be enqueued.
|
185 |
+
* So as a result some handles will not be equeued because of not registered dependecies.
|
186 |
+
*/
|
187 |
+
return;
|
188 |
+
}
|
189 |
+
|
190 |
+
if ($this->static_enqueued) {
|
191 |
+
return false;
|
192 |
+
}
|
193 |
+
|
194 |
+
$this->prepare($id, $option, $data);
|
195 |
+
|
196 |
+
$call_next_time = $this->_enqueue_static($id, $option, $values, $data);
|
197 |
+
|
198 |
+
$this->static_enqueued = !$call_next_time;
|
199 |
+
|
200 |
+
return $call_next_time;
|
201 |
+
}
|
202 |
+
|
203 |
+
/**
|
204 |
+
* Default option array
|
205 |
+
*
|
206 |
+
* @return array
|
207 |
+
* 'type' => '...'
|
208 |
+
* 'title' => '...'
|
209 |
+
* 'attr' => array(...)
|
210 |
+
*/
|
211 |
+
final public function get_defaults()
|
212 |
+
{
|
213 |
+
$option = $this->_get_defaults();
|
214 |
+
|
215 |
+
$option['type'] = $this->get_type();
|
216 |
+
|
217 |
+
return $option;
|
218 |
+
}
|
219 |
+
|
220 |
+
/**
|
221 |
+
* Use this method to register a new container type
|
222 |
+
* @param string|FW_Container_Type $container_type_class
|
223 |
+
*/
|
224 |
+
final public static function register($container_type_class) {
|
225 |
+
static $registration_access_key = null;
|
226 |
+
|
227 |
+
if ($registration_access_key === null) {
|
228 |
+
$registration_access_key = new FW_Access_Key('fw_container_type');
|
229 |
+
}
|
230 |
+
|
231 |
+
fw()->backend->_register_container_type($registration_access_key, $container_type_class);
|
232 |
+
}
|
233 |
}
|
framework/core/extends/class-fw-extension.php
CHANGED
@@ -1,507 +1,507 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
|
3 |
-
/**
|
4 |
-
* All framework extensions should extend this
|
5 |
-
*/
|
6 |
-
abstract class FW_Extension
|
7 |
-
{
|
8 |
-
/**
|
9 |
-
* Called after all extensions instances was created
|
10 |
-
* @internal
|
11 |
-
*/
|
12 |
-
abstract protected function _init();
|
13 |
-
|
14 |
-
/** @var FW_Extension_Manifest */
|
15 |
-
public $manifest;
|
16 |
-
|
17 |
-
/** @var string Key used in FW_Cache to store data about extensions */
|
18 |
-
private static $cache_key = 'fw_ext';
|
19 |
-
|
20 |
-
/** @var FW_Access_Key */
|
21 |
-
private static $access_key;
|
22 |
-
|
23 |
-
/**
|
24 |
-
* Extension name, equal to directory name
|
25 |
-
* @var string
|
26 |
-
*/
|
27 |
-
private $name;
|
28 |
-
|
29 |
-
/**
|
30 |
-
* Parent extension instance
|
31 |
-
* @var FW_Extension|null
|
32 |
-
*/
|
33 |
-
private $parent;
|
34 |
-
|
35 |
-
/**
|
36 |
-
* @var string
|
37 |
-
*/
|
38 |
-
private $rel_path;
|
39 |
-
|
40 |
-
/**
|
41 |
-
* @var string
|
42 |
-
*/
|
43 |
-
private $path;
|
44 |
-
|
45 |
-
/**
|
46 |
-
* @var string
|
47 |
-
*/
|
48 |
-
private $uri;
|
49 |
-
|
50 |
-
/**
|
51 |
-
* On what directory depth is the extension
|
52 |
-
*
|
53 |
-
* 1 - Root extension
|
54 |
-
* 2 - Their children
|
55 |
-
* 3 - Sub children
|
56 |
-
* ...
|
57 |
-
*
|
58 |
-
* @var int
|
59 |
-
*/
|
60 |
-
private $depth;
|
61 |
-
|
62 |
-
/**
|
63 |
-
* Locations where the extension can look for customizations (overwrite views, options; extend config)
|
64 |
-
* @var array {'/path' => 'https://uri.to/path'}
|
65 |
-
*/
|
66 |
-
private $customizations_locations;
|
67 |
-
|
68 |
-
final public function __construct($data)
|
69 |
-
{
|
70 |
-
if (!self::$access_key) {
|
71 |
-
self::$access_key = new FW_Access_Key('extension');
|
72 |
-
}
|
73 |
-
|
74 |
-
$this->rel_path = $data['rel_path'];
|
75 |
-
$this->path = $data['path'];
|
76 |
-
$this->uri = $data['uri'];
|
77 |
-
$this->parent = $data['parent'];
|
78 |
-
$this->depth = $data['depth'];
|
79 |
-
$this->customizations_locations = $data['customizations_locations'];
|
80 |
-
|
81 |
-
{
|
82 |
-
$variables = fw_get_variables_from_file($this->path .'/manifest.php', array('manifest' => array()));
|
83 |
-
$manifest = $variables['manifest'];
|
84 |
-
unset($variables);
|
85 |
-
|
86 |
-
if (empty($manifest['name'])) {
|
87 |
-
$manifest['name'] = fw_id_to_title($this->get_name());
|
88 |
-
}
|
89 |
-
|
90 |
-
$this->manifest = new FW_Extension_Manifest($manifest);
|
91 |
-
}
|
92 |
-
}
|
93 |
-
|
94 |
-
/**
|
95 |
-
* Cache key for this extension
|
96 |
-
*
|
97 |
-
* Usage:
|
98 |
-
* FW_Cache::get( $this->get_cache_key('/some/key') )
|
99 |
-
*
|
100 |
-
* @param string $sub_key
|
101 |
-
* @return string
|
102 |
-
*/
|
103 |
-
final public function get_cache_key($sub_key = '')
|
104 |
-
{
|
105 |
-
return self::$cache_key .'/'. $this->get_name() . $sub_key;
|
106 |
-
}
|
107 |
-
|
108 |
-
/**
|
109 |
-
* @param string $name View file name (without .php) from <extension>/views directory
|
110 |
-
* @param array $view_variables Keys will be variables names within view
|
111 |
-
* @param bool $return In some cases, for memory saving reasons, you can disable the use of output buffering
|
112 |
-
* @return string HTML
|
113 |
-
*/
|
114 |
-
final protected function render_view($name, $view_variables = array(), $return = true)
|
115 |
-
{
|
116 |
-
$full_path = $this->locate_path('/views/'. $name .'.php');
|
117 |
-
|
118 |
-
if (!$full_path) {
|
119 |
-
trigger_error('Extension view not found: '. $name, E_USER_WARNING);
|
120 |
-
return;
|
121 |
-
}
|
122 |
-
|
123 |
-
return fw_render_view($full_path, $view_variables, $return);
|
124 |
-
}
|
125 |
-
|
126 |
-
/**
|
127 |
-
* @internal
|
128 |
-
* @param FW_Access_Key $access_key
|
129 |
-
* @return mixed
|
130 |
-
*/
|
131 |
-
final public function _call_init($access_key)
|
132 |
-
{
|
133 |
-
if ($access_key->get_key() !== 'fw_extensions') {
|
134 |
-
trigger_error(__METHOD__ .' denied', E_USER_ERROR);
|
135 |
-
}
|
136 |
-
|
137 |
-
return $this->_init();
|
138 |
-
}
|
139 |
-
|
140 |
-
/**
|
141 |
-
* Tree array with all sub extensions
|
142 |
-
* @return array
|
143 |
-
*/
|
144 |
-
final public function get_tree()
|
145 |
-
{
|
146 |
-
return fw()->extensions->_get_extension_tree(self::$access_key, $this->get_name());
|
147 |
-
}
|
148 |
-
|
149 |
-
/**
|
150 |
-
* @param string $rel_path '/views/test.php'
|
151 |
-
* @return false|string '/var/www/.../extensions/<extension>/views/test.php'
|
152 |
-
*/
|
153 |
-
final public function locate_path($rel_path)
|
154 |
-
{
|
155 |
-
$locations = $this->customizations_locations;
|
156 |
-
$locations[$this->get_path()] = $this->get_uri();
|
157 |
-
|
158 |
-
foreach ($locations as $path => $uri) {
|
159 |
-
if (file_exists($path . $rel_path)) {
|
160 |
-
return $path . $rel_path;
|
161 |
-
}
|
162 |
-
}
|
163 |
-
|
164 |
-
return false;
|
165 |
-
}
|
166 |
-
|
167 |
-
/**
|
168 |
-
* @param string $rel_path E.g. '/static/js/scripts.js'
|
169 |
-
* @return string URI E.g. 'http: //wordpress.com/.../extensions/<extension>/static/js/scripts.js'
|
170 |
-
*/
|
171 |
-
final public function locate_URI($rel_path)
|
172 |
-
{
|
173 |
-
$locations = $this->customizations_locations;
|
174 |
-
$locations[$this->get_path()] = $this->get_uri();
|
175 |
-
|
176 |
-
foreach ($locations as $path => $uri) {
|
177 |
-
if (file_exists($path . $rel_path)) {
|
178 |
-
return $uri . $rel_path;
|
179 |
-
}
|
180 |
-
}
|
181 |
-
|
182 |
-
return false;
|
183 |
-
}
|
184 |
-
|
185 |
-
/**
|
186 |
-
* @return FW_Extension|null if has no parent extension
|
187 |
-
*/
|
188 |
-
final public function get_parent()
|
189 |
-
{
|
190 |
-
return $this->parent;
|
191 |
-
}
|
192 |
-
|
193 |
-
/**
|
194 |
-
* @return string
|
195 |
-
*/
|
196 |
-
final public function get_name()
|
197 |
-
{
|
198 |
-
if ($this->name === null) {
|
199 |
-
$this->name = basename($this->path);
|
200 |
-
}
|
201 |
-
|
202 |
-
return $this->name;
|
203 |
-
}
|
204 |
-
|
205 |
-
/**
|
206 |
-
* @return string
|
207 |
-
* @deprecated
|
208 |
-
*/
|
209 |
-
final public function get_declared_source()
|
210 |
-
{
|
211 |
-
return 'deprecated';
|
212 |
-
}
|
213 |
-
|
214 |
-
/**
|
215 |
-
* @param string $append_rel_path E.g. '/includes/something.php'
|
216 |
-
* @return string
|
217 |
-
* @deprecated
|
218 |
-
*/
|
219 |
-
final public function get_declared_path($append_rel_path = '')
|
220 |
-
{
|
221 |
-
return $this->get_path($append_rel_path);
|
222 |
-
}
|
223 |
-
|
224 |
-
final public function get_path($append_rel_path = '')
|
225 |
-
{
|
226 |
-
return $this->path . $append_rel_path;
|
227 |
-
}
|
228 |
-
|
229 |
-
/**
|
230 |
-
* @param string $append_rel_path E.g. '/includes/foo/bar/script.js'
|
231 |
-
* @return string
|
232 |
-
* @deprecated
|
233 |
-
*/
|
234 |
-
final public function get_declared_URI($append_rel_path = '')
|
235 |
-
{
|
236 |
-
return $this->get_uri($append_rel_path);
|
237 |
-
}
|
238 |
-
|
239 |
-
/**
|
240 |
-
* @param string $append_rel_path E.g. '/includes/foo/bar/script.js'
|
241 |
-
* @return string
|
242 |
-
*/
|
243 |
-
final public function get_uri($append_rel_path = '')
|
244 |
-
{
|
245 |
-
return $this->uri . $append_rel_path;
|
246 |
-
}
|
247 |
-
|
248 |
-
/**
|
249 |
-
* @param string $child_extension_name
|
250 |
-
* @return FW_Extension|null
|
251 |
-
*/
|
252 |
-
final public function get_child($child_extension_name)
|
253 |
-
{
|
254 |
-
$active_tree = $this->get_tree();
|
255 |
-
|
256 |
-
if (isset($active_tree[$child_extension_name])) {
|
257 |
-
return fw()->extensions->get($child_extension_name);
|
258 |
-
} else {
|
259 |
-
return null;
|
260 |
-
}
|
261 |
-
}
|
262 |
-
|
263 |
-
/**
|
264 |
-
* Return all child extensions
|
265 |
-
* Only one level, not all sub levels
|
266 |
-
* @return FW_Extension[]
|
267 |
-
*/
|
268 |
-
final public function get_children()
|
269 |
-
{
|
270 |
-
$active_tree = $this->get_tree();
|
271 |
-
|
272 |
-
$result = array();
|
273 |
-
|
274 |
-
foreach ($active_tree as $extension_name => &$sub_extensions) {
|
275 |
-
$result[$extension_name] = fw()->extensions->get($extension_name);
|
276 |
-
}
|
277 |
-
unset($sub_extensions);
|
278 |
-
|
279 |
-
return $result;
|
280 |
-
}
|
281 |
-
|
282 |
-
/**
|
283 |
-
* Return config key value, or entire config array
|
284 |
-
* Config array is merged from child configs
|
285 |
-
* @param string|null $key Multi key format accepted: 'a/b/c'
|
286 |
-
* @return mixed|null
|
287 |
-
*/
|
288 |
-
final public function get_config($key = null)
|
289 |
-
{
|
290 |
-
$cache_key = $this->get_cache_key() .'/config';
|
291 |
-
|
292 |
-
try {
|
293 |
-
$config = FW_Cache::get($cache_key);
|
294 |
-
} catch (FW_Cache_Not_Found_Exception $e) {
|
295 |
-
$config = array();
|
296 |
-
|
297 |
-
$locations = $this->customizations_locations;
|
298 |
-
$locations[$this->get_path()] = $this->get_uri();
|
299 |
-
|
300 |
-
foreach (array_reverse($locations) as $path => $uri) {
|
301 |
-
$config_path = $path .'/config.php';
|
302 |
-
|
303 |
-
if (file_exists($config_path)) {
|
304 |
-
$variables = fw_get_variables_from_file($config_path, array('cfg' => null));
|
305 |
-
|
306 |
-
if (!empty($variables['cfg'])) {
|
307 |
-
$config = array_merge($config, $variables['cfg']);
|
308 |
-
unset($variables);
|
309 |
-
}
|
310 |
-
}
|
311 |
-
}
|
312 |
-
|
313 |
-
FW_Cache::set($cache_key, $config);
|
314 |
-
}
|
315 |
-
|
316 |
-
return $key === null ? $config : fw_akg($key, $config);
|
317 |
-
}
|
318 |
-
|
319 |
-
/**
|
320 |
-
* Return array with options from specified name/path
|
321 |
-
* @param string $name Examples: 'framework', 'posts/portfolio'
|
322 |
-
* @param array $variables These will be available in options file (like variables for view)
|
323 |
-
* @return array
|
324 |
-
*/
|
325 |
-
final public function get_options($name, array $variables = array())
|
326 |
-
{
|
327 |
-
$path = $this->locate_path('/options/'. $name .'.php');
|
328 |
-
|
329 |
-
if (!$path) {
|
330 |
-
return array();
|
331 |
-
}
|
332 |
-
|
333 |
-
$variables = fw_get_variables_from_file($path, array('options' => array()), $variables);
|
334 |
-
|
335 |
-
return $variables['options'];
|
336 |
-
}
|
337 |
-
|
338 |
-
final public function get_settings_options()
|
339 |
-
{
|
340 |
-
$cache_key = $this->get_cache_key() .'/settings_options';
|
341 |
-
|
342 |
-
try {
|
343 |
-
return FW_Cache::get($cache_key);
|
344 |
-
} catch (FW_Cache_Not_Found_Exception $e) {
|
345 |
-
$path = $this->get_path('/settings-options.php');
|
346 |
-
|
347 |
-
if (!file_exists($path)) {
|
348 |
-
FW_Cache::set($cache_key, array());
|
349 |
-
return array();
|
350 |
-
}
|
351 |
-
|
352 |
-
$variables = fw_get_variables_from_file($path, array('options' => array()));
|
353 |
-
|
354 |
-
FW_Cache::set($cache_key, $variables['options']);
|
355 |
-
|
356 |
-
return $variables['options'];
|
357 |
-
}
|
358 |
-
}
|
359 |
-
|
360 |
-
/**
|
361 |
-
* Get extension's settings option value from the database
|
362 |
-
*
|
363 |
-
* @param string|null $option_id
|
364 |
-
* @param null|mixed $default_value If no option found in the database, this value will be returned
|
365 |
-
* @param null|bool $get_original_value Original value is that with no translations and other changes
|
366 |
-
*
|
367 |
-
* @return mixed|null
|
368 |
-
*/
|
369 |
-
final public function get_db_settings_option( $option_id = null, $default_value = null, $get_original_value = null ) {
|
370 |
-
return fw_get_db_ext_settings_option( $this->get_name(), $option_id, $default_value, $get_original_value );
|
371 |
-
}
|
372 |
-
|
373 |
-
/**
|
374 |
-
* Set extension's setting option value in database
|
375 |
-
*
|
376 |
-
* @param string|null $option_id
|
377 |
-
* @param mixed $value
|
378 |
-
*/
|
379 |
-
final public function set_db_settings_option( $option_id = null, $value ) {
|
380 |
-
fw_set_db_ext_settings_option( $this->get_name(), $option_id, $value );
|
381 |
-
}
|
382 |
-
|
383 |
-
/**
|
384 |
-
* Get extension's data from the database
|
385 |
-
*
|
386 |
-
* @param string|null $multi_key The key of the data you want to get. null - all data
|
387 |
-
* @param null|mixed $default_value If no option found in the database, this value will be returned
|
388 |
-
* @param null|bool $get_original_value Original value is that with no translations and other changes
|
389 |
-
*
|
390 |
-
* @return mixed|null
|
391 |
-
*/
|
392 |
-
final public function get_db_data( $multi_key = null, $default_value = null, $get_original_value = null ) {
|
393 |
-
return fw_get_db_extension_data( $this->get_name(), $multi_key, $default_value, $get_original_value );
|
394 |
-
}
|
395 |
-
|
396 |
-
/**
|
397 |
-
* Set some extension's data in database
|
398 |
-
*
|
399 |
-
* @param string|null $multi_key The key of the data you want to set. null - all data
|
400 |
-
* @param mixed $value
|
401 |
-
*/
|
402 |
-
final public function set_db_data( $multi_key = null, $value ) {
|
403 |
-
fw_set_db_extension_data( $this->get_name(), $multi_key, $value );
|
404 |
-
}
|
405 |
-
|
406 |
-
/**
|
407 |
-
* Get extension's data from user meta
|
408 |
-
*
|
409 |
-
* @param int $user_id
|
410 |
-
* @param string|null $keys
|
411 |
-
*
|
412 |
-
* @return mixed|null
|
413 |
-
*/
|
414 |
-
final public function get_user_data( $user_id, $keys = null ) {
|
415 |
-
return fw_get_db_extension_user_data($user_id, $this->get_name(), $keys);
|
416 |
-
}
|
417 |
-
|
418 |
-
/**
|
419 |
-
* et some extension's data in user meta
|
420 |
-
*
|
421 |
-
* @param int $user_id
|
422 |
-
* @param mixed $value
|
423 |
-
* @param string|null $keys
|
424 |
-
*
|
425 |
-
* @return bool|int
|
426 |
-
*/
|
427 |
-
final public function set_user_data( $user_id, $value, $keys = null ) {
|
428 |
-
return fw_set_db_extension_user_data($user_id, $this->get_name(), $value, $keys);
|
429 |
-
}
|
430 |
-
|
431 |
-
final public function get_post_options($post_type)
|
432 |
-
{
|
433 |
-
return $this->get_options('posts/'. $post_type);
|
434 |
-
}
|
435 |
-
|
436 |
-
final public function get_taxonomy_options($taxonomy)
|
437 |
-
{
|
438 |
-
return $this->get_options('taxonomies/'. $taxonomy);
|
439 |
-
}
|
440 |
-
|
441 |
-
/**
|
442 |
-
* @param string $name File name without extension, located in <extension>/static/js/$name.js
|
443 |
-
* @return string URI
|
444 |
-
*/
|
445 |
-
final public function locate_js_URI($name)
|
446 |
-
{
|
447 |
-
return $this->locate_URI('/static/js/'. $name .'.js');
|
448 |
-
}
|
449 |
-
|
450 |
-
/**
|
451 |
-
* @param string $name File name without extension, located in <extension>/static/js/$name.js
|
452 |
-
* @return string URI
|
453 |
-
*/
|
454 |
-
final public function locate_css_URI($name)
|
455 |
-
{
|
456 |
-
return $this->locate_URI('/static/css/'. $name .'.css');
|
457 |
-
}
|
458 |
-
|
459 |
-
/**
|
460 |
-
* @param string $name File name without extension, located in <extension>/views/$name.php
|
461 |
-
* @return false|string
|
462 |
-
*/
|
463 |
-
final public function locate_view_path($name)
|
464 |
-
{
|
465 |
-
return $this->locate_path('/views/'. $name .'.php');
|
466 |
-
}
|
467 |
-
|
468 |
-
final public function get_depth()
|
469 |
-
{
|
470 |
-
return $this->depth;
|
471 |
-
}
|
472 |
-
|
473 |
-
final public function get_customizations_locations()
|
474 |
-
{
|
475 |
-
return $this->customizations_locations;
|
476 |
-
}
|
477 |
-
|
478 |
-
final public function get_rel_path()
|
479 |
-
{
|
480 |
-
return $this->rel_path;
|
481 |
-
}
|
482 |
-
|
483 |
-
/**
|
484 |
-
* Check if child extension is valid
|
485 |
-
*
|
486 |
-
* Used for special cases when an extension requires its child extensions to extend some special class
|
487 |
-
*
|
488 |
-
* @param FW_Extension $child_extension_instance
|
489 |
-
* @return bool
|
490 |
-
* @internal
|
491 |
-
*/
|
492 |
-
public function _child_extension_is_valid($child_extension_instance)
|
493 |
-
{
|
494 |
-
return is_subclass_of($child_extension_instance, 'FW_Extension');
|
495 |
-
}
|
496 |
-
|
497 |
-
/**
|
498 |
-
* Get link to the page created by this extension in dashboard
|
499 |
-
* (Used on the extensions page)
|
500 |
-
* @internal
|
501 |
-
* @return string
|
502 |
-
*/
|
503 |
-
public function _get_link()
|
504 |
-
{
|
505 |
-
return false;
|
506 |
-
}
|
507 |
-
}
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
|
3 |
+
/**
|
4 |
+
* All framework extensions should extend this
|
5 |
+
*/
|
6 |
+
abstract class FW_Extension
|
7 |
+
{
|
8 |
+
/**
|
9 |
+
* Called after all extensions instances was created
|
10 |
+
* @internal
|
11 |
+
*/
|
12 |
+
abstract protected function _init();
|
13 |
+
|
14 |
+
/** @var FW_Extension_Manifest */
|
15 |
+
public $manifest;
|
16 |
+
|
17 |
+
/** @var string Key used in FW_Cache to store data about extensions */
|
18 |
+
private static $cache_key = 'fw_ext';
|
19 |
+
|
20 |
+
/** @var FW_Access_Key */
|
21 |
+
private static $access_key;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Extension name, equal to directory name
|
25 |
+
* @var string
|
26 |
+
*/
|
27 |
+
private $name;
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Parent extension instance
|
31 |
+
* @var FW_Extension|null
|
32 |
+
*/
|
33 |
+
private $parent;
|
34 |
+
|
35 |
+
/**
|
36 |
+
* @var string
|
37 |
+
*/
|
38 |
+
private $rel_path;
|
39 |
+
|
40 |
+
/**
|
41 |
+
* @var string
|
42 |
+
*/
|
43 |
+
private $path;
|
44 |
+
|
45 |
+
/**
|
46 |
+
* @var string
|
47 |
+
*/
|
48 |
+
private $uri;
|
49 |
+
|
50 |
+
/**
|
51 |
+
* On what directory depth is the extension
|
52 |
+
*
|
53 |
+
* 1 - Root extension
|
54 |
+
* 2 - Their children
|
55 |
+
* 3 - Sub children
|
56 |
+
* ...
|
57 |
+
*
|
58 |
+
* @var int
|
59 |
+
*/
|
60 |
+
private $depth;
|
61 |
+
|
62 |
+
/**
|
63 |
+
* Locations where the extension can look for customizations (overwrite views, options; extend config)
|
64 |
+
* @var array {'/path' => 'https://uri.to/path'}
|
65 |
+
*/
|
66 |
+
private $customizations_locations;
|
67 |
+
|
68 |
+
final public function __construct($data)
|
69 |
+
{
|
70 |
+
if (!self::$access_key) {
|
71 |
+
self::$access_key = new FW_Access_Key('extension');
|
72 |
+
}
|
73 |
+
|
74 |
+
$this->rel_path = $data['rel_path'];
|
75 |
+
$this->path = $data['path'];
|
76 |
+
$this->uri = $data['uri'];
|
77 |
+
$this->parent = $data['parent'];
|
78 |
+
$this->depth = $data['depth'];
|
79 |
+
$this->customizations_locations = $data['customizations_locations'];
|
80 |
+
|
81 |
+
{
|
82 |
+
$variables = fw_get_variables_from_file($this->path .'/manifest.php', array('manifest' => array()));
|
83 |
+
$manifest = $variables['manifest'];
|
84 |
+
unset($variables);
|
85 |
+
|
86 |
+
if (empty($manifest['name'])) {
|
87 |
+
$manifest['name'] = fw_id_to_title($this->get_name());
|
88 |
+
}
|
89 |
+
|
90 |
+
$this->manifest = new FW_Extension_Manifest($manifest);
|
91 |
+
}
|
92 |
+
}
|
93 |
+
|
94 |
+
/**
|
95 |
+
* Cache key for this extension
|
96 |
+
*
|
97 |
+
* Usage:
|
98 |
+
* FW_Cache::get( $this->get_cache_key('/some/key') )
|
99 |
+
*
|
100 |
+
* @param string $sub_key
|
101 |
+
* @return string
|
102 |
+
*/
|
103 |
+
final public function get_cache_key($sub_key = '')
|
104 |
+
{
|
105 |
+
return self::$cache_key .'/'. $this->get_name() . $sub_key;
|
106 |
+
}
|
107 |
+
|
108 |
+
/**
|
109 |
+
* @param string $name View file name (without .php) from <extension>/views directory
|
110 |
+
* @param array $view_variables Keys will be variables names within view
|
111 |
+
* @param bool $return In some cases, for memory saving reasons, you can disable the use of output buffering
|
112 |
+
* @return string HTML
|
113 |
+
*/
|
114 |
+
final protected function render_view($name, $view_variables = array(), $return = true)
|
115 |
+
{
|
116 |
+
$full_path = $this->locate_path('/views/'. $name .'.php');
|
117 |
+
|
118 |
+
if (!$full_path) {
|
119 |
+
trigger_error('Extension view not found: '. $name, E_USER_WARNING);
|
120 |
+
return;
|
121 |
+
}
|
122 |
+
|
123 |
+
return fw_render_view($full_path, $view_variables, $return);
|
124 |
+
}
|
125 |
+
|
126 |
+
/**
|
127 |
+
* @internal
|
128 |
+
* @param FW_Access_Key $access_key
|
129 |
+
* @return mixed
|
130 |
+
*/
|
131 |
+
final public function _call_init($access_key)
|
132 |
+
{
|
133 |
+
if ($access_key->get_key() !== 'fw_extensions') {
|
134 |
+
trigger_error(__METHOD__ .' denied', E_USER_ERROR);
|
135 |
+
}
|
136 |
+
|
137 |
+
return $this->_init();
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Tree array with all sub extensions
|
142 |
+
* @return array
|
143 |
+
*/
|
144 |
+
final public function get_tree()
|
145 |
+
{
|
146 |
+
return fw()->extensions->_get_extension_tree(self::$access_key, $this->get_name());
|
147 |
+
}
|
148 |
+
|
149 |
+
/**
|
150 |
+
* @param string $rel_path '/views/test.php'
|
151 |
+
* @return false|string '/var/www/.../extensions/<extension>/views/test.php'
|
152 |
+
*/
|
153 |
+
final public function locate_path($rel_path)
|
154 |
+
{
|
155 |
+
$locations = $this->customizations_locations;
|
156 |
+
$locations[$this->get_path()] = $this->get_uri();
|
157 |
+
|
158 |
+
foreach ($locations as $path => $uri) {
|
159 |
+
if (file_exists($path . $rel_path)) {
|
160 |
+
return $path . $rel_path;
|
161 |
+
}
|
162 |
+
}
|
163 |
+
|
164 |
+
return false;
|
165 |
+
}
|
166 |
+
|
167 |
+
/**
|
168 |
+
* @param string $rel_path E.g. '/static/js/scripts.js'
|
169 |
+
* @return string URI E.g. 'http: //wordpress.com/.../extensions/<extension>/static/js/scripts.js'
|
170 |
+
*/
|
171 |
+
final public function locate_URI($rel_path)
|
172 |
+
{
|
173 |
+
$locations = $this->customizations_locations;
|
174 |
+
$locations[$this->get_path()] = $this->get_uri();
|
175 |
+
|
176 |
+
foreach ($locations as $path => $uri) {
|
177 |
+
if (file_exists($path . $rel_path)) {
|
178 |
+
return $uri . $rel_path;
|
179 |
+
}
|
180 |
+
}
|
181 |
+
|
182 |
+
return false;
|
183 |
+
}
|
184 |
+
|
185 |
+
/**
|
186 |
+
* @return FW_Extension|null if has no parent extension
|
187 |
+
*/
|
188 |
+
final public function get_parent()
|
189 |
+
{
|
190 |
+
return $this->parent;
|
191 |
+
}
|
192 |
+
|
193 |
+
/**
|
194 |
+
* @return string
|
195 |
+
*/
|
196 |
+
final public function get_name()
|
197 |
+
{
|
198 |
+
if ($this->name === null) {
|
199 |
+
$this->name = basename($this->path);
|
200 |
+
}
|
201 |
+
|
202 |
+
return $this->name;
|
203 |
+
}
|
204 |
+
|
205 |
+
/**
|
206 |
+
* @return string
|
207 |
+
* @deprecated
|
208 |
+
*/
|
209 |
+
final public function get_declared_source()
|
210 |
+
{
|
211 |
+
return 'deprecated';
|
212 |
+
}
|
213 |
+
|
214 |
+
/**
|
215 |
+
* @param string $append_rel_path E.g. '/includes/something.php'
|
216 |
+
* @return string
|
217 |
+
* @deprecated
|
218 |
+
*/
|
219 |
+
final public function get_declared_path($append_rel_path = '')
|
220 |
+
{
|
221 |
+
return $this->get_path($append_rel_path);
|
222 |
+
}
|
223 |
+
|
224 |
+
final public function get_path($append_rel_path = '')
|
225 |
+
{
|
226 |
+
return $this->path . $append_rel_path;
|
227 |
+
}
|
228 |
+
|
229 |
+
/**
|
230 |
+
* @param string $append_rel_path E.g. '/includes/foo/bar/script.js'
|
231 |
+
* @return string
|
232 |
+
* @deprecated
|
233 |
+
*/
|
234 |
+
final public function get_declared_URI($append_rel_path = '')
|
235 |
+
{
|
236 |
+
return $this->get_uri($append_rel_path);
|
237 |
+
}
|
238 |
+
|
239 |
+
/**
|
240 |
+
* @param string $append_rel_path E.g. '/includes/foo/bar/script.js'
|
241 |
+
* @return string
|
242 |
+
*/
|
243 |
+
final public function get_uri($append_rel_path = '')
|
244 |
+
{
|
245 |
+
return $this->uri . $append_rel_path;
|
246 |
+
}
|
247 |
+
|
248 |
+
/**
|
249 |
+
* @param string $child_extension_name
|
250 |
+
* @return FW_Extension|null
|
251 |
+
*/
|
252 |
+
final public function get_child($child_extension_name)
|
253 |
+
{
|
254 |
+
$active_tree = $this->get_tree();
|
255 |
+
|
256 |
+
if (isset($active_tree[$child_extension_name])) {
|
257 |
+
return fw()->extensions->get($child_extension_name);
|
258 |
+
} else {
|
259 |
+
return null;
|
260 |
+
}
|
261 |
+
}
|
262 |
+
|
263 |
+
/**
|
264 |
+
* Return all child extensions
|
265 |
+
* Only one level, not all sub levels
|
266 |
+
* @return FW_Extension[]
|
267 |
+
*/
|
268 |
+
final public function get_children()
|
269 |
+
{
|
270 |
+
$active_tree = $this->get_tree();
|
271 |
+
|
272 |
+
$result = array();
|
273 |
+
|
274 |
+
foreach ($active_tree as $extension_name => &$sub_extensions) {
|
275 |
+
$result[$extension_name] = fw()->extensions->get($extension_name);
|
276 |
+
}
|
277 |
+
unset($sub_extensions);
|
278 |
+
|
279 |
+
return $result;
|
280 |
+
}
|
281 |
+
|
282 |
+
/**
|
283 |
+
* Return config key value, or entire config array
|
284 |
+
* Config array is merged from child configs
|
285 |
+
* @param string|null $key Multi key format accepted: 'a/b/c'
|
286 |
+
* @return mixed|null
|
287 |
+
*/
|
288 |
+
final public function get_config($key = null)
|
289 |
+
{
|
290 |
+
$cache_key = $this->get_cache_key() .'/config';
|
291 |
+
|
292 |
+
try {
|
293 |
+
$config = FW_Cache::get($cache_key);
|
294 |
+
} catch (FW_Cache_Not_Found_Exception $e) {
|
295 |
+
$config = array();
|
296 |
+
|
297 |
+
$locations = $this->customizations_locations;
|
298 |
+
$locations[$this->get_path()] = $this->get_uri();
|
299 |
+
|
300 |
+
foreach (array_reverse($locations) as $path => $uri) {
|
301 |
+
$config_path = $path .'/config.php';
|
302 |
+
|
303 |
+
if (file_exists($config_path)) {
|
304 |
+
$variables = fw_get_variables_from_file($config_path, array('cfg' => null));
|
305 |
+
|
306 |
+
if (!empty($variables['cfg'])) {
|
307 |
+
$config = array_merge($config, $variables['cfg']);
|
308 |
+
unset($variables);
|
309 |
+
}
|
310 |
+
}
|
311 |
+
}
|
312 |
+
|
313 |
+
FW_Cache::set($cache_key, $config);
|
314 |
+
}
|
315 |
+
|
316 |
+
return $key === null ? $config : fw_akg($key, $config);
|
317 |
+
}
|
318 |
+
|
319 |
+
/**
|
320 |
+
* Return array with options from specified name/path
|
321 |
+
* @param string $name Examples: 'framework', 'posts/portfolio'
|
322 |
+
* @param array $variables These will be available in options file (like variables for view)
|
323 |
+
* @return array
|
324 |
+
*/
|
325 |
+
final public function get_options($name, array $variables = array())
|
326 |
+
{
|
327 |
+
$path = $this->locate_path('/options/'. $name .'.php');
|
328 |
+
|
329 |
+
if (!$path) {
|
330 |
+
return array();
|
331 |
+
}
|
332 |
+
|
333 |
+
$variables = fw_get_variables_from_file($path, array('options' => array()), $variables);
|
334 |
+
|
335 |
+
return $variables['options'];
|
336 |
+
}
|
337 |
+
|
338 |
+
final public function get_settings_options()
|
339 |
+
{
|
340 |
+
$cache_key = $this->get_cache_key() .'/settings_options';
|
341 |
+
|
342 |
+
try {
|
343 |
+
return FW_Cache::get($cache_key);
|
344 |
+
} catch (FW_Cache_Not_Found_Exception $e) {
|
345 |
+
$path = $this->get_path('/settings-options.php');
|
346 |
+
|
347 |
+
if (!file_exists($path)) {
|
348 |
+
FW_Cache::set($cache_key, array());
|
349 |
+
return array();
|
350 |
+
}
|
351 |
+
|
352 |
+
$variables = fw_get_variables_from_file($path, array('options' => array()));
|
353 |
+
|
354 |
+
FW_Cache::set($cache_key, $variables['options']);
|
355 |
+
|
356 |
+
return $variables['options'];
|
357 |
+
}
|
358 |
+
}
|
359 |
+
|
360 |
+
/**
|
361 |
+
* Get extension's settings option value from the database
|
362 |
+
*
|
363 |
+
* @param string|null $option_id
|
364 |
+
* @param null|mixed $default_value If no option found in the database, this value will be returned
|
365 |
+
* @param null|bool $get_original_value Original value is that with no translations and other changes
|
366 |
+
*
|
367 |
+
* @return mixed|null
|
368 |
+
*/
|
369 |
+
final public function get_db_settings_option( $option_id = null, $default_value = null, $get_original_value = null ) {
|
370 |
+
return fw_get_db_ext_settings_option( $this->get_name(), $option_id, $default_value, $get_original_value );
|
371 |
+
}
|
372 |
+
|
373 |
+
/**
|
374 |
+
* Set extension's setting option value in database
|
375 |
+
*
|
376 |
+
* @param string|null $option_id
|
377 |
+
* @param mixed $value
|
378 |
+
*/
|
379 |
+
final public function set_db_settings_option( $option_id = null, $value ) {
|
380 |
+
fw_set_db_ext_settings_option( $this->get_name(), $option_id, $value );
|
381 |
+
}
|
382 |
+
|
383 |
+
/**
|
384 |
+
* Get extension's data from the database
|
385 |
+
*
|
386 |
+
* @param string|null $multi_key The key of the data you want to get. null - all data
|
387 |
+
* @param null|mixed $default_value If no option found in the database, this value will be returned
|
388 |
+
* @param null|bool $get_original_value Original value is that with no translations and other changes
|
389 |
+
*
|
390 |
+
* @return mixed|null
|
391 |
+
*/
|
392 |
+
final public function get_db_data( $multi_key = null, $default_value = null, $get_original_value = null ) {
|
393 |
+
return fw_get_db_extension_data( $this->get_name(), $multi_key, $default_value, $get_original_value );
|
394 |
+
}
|
395 |
+
|
396 |
+
/**
|
397 |
+
* Set some extension's data in database
|
398 |
+
*
|
399 |
+
* @param string|null $multi_key The key of the data you want to set. null - all data
|
400 |
+
* @param mixed $value
|
401 |
+
*/
|
402 |
+
final public function set_db_data( $multi_key = null, $value ) {
|
403 |
+
fw_set_db_extension_data( $this->get_name(), $multi_key, $value );
|
404 |
+
}
|
405 |
+
|
406 |
+
/**
|
407 |
+
* Get extension's data from user meta
|
408 |
+
*
|
409 |
+
* @param int $user_id
|
410 |
+
* @param string|null $keys
|
411 |
+
*
|
412 |
+
* @return mixed|null
|
413 |
+
*/
|
414 |
+
final public function get_user_data( $user_id, $keys = null ) {
|
415 |
+
return fw_get_db_extension_user_data($user_id, $this->get_name(), $keys);
|
416 |
+
}
|
417 |
+
|
418 |
+
/**
|
419 |
+
* et some extension's data in user meta
|
420 |
+
*
|
421 |
+
* @param int $user_id
|
422 |
+
* @param mixed $value
|
423 |
+
* @param string|null $keys
|
424 |
+
*
|
425 |
+
* @return bool|int
|
426 |
+
*/
|
427 |
+
final public function set_user_data( $user_id, $value, $keys = null ) {
|
428 |
+
return fw_set_db_extension_user_data($user_id, $this->get_name(), $value, $keys);
|
429 |
+
}
|
430 |
+
|
431 |
+
final public function get_post_options($post_type)
|
432 |
+
{
|
433 |
+
return $this->get_options('posts/'. $post_type);
|
434 |
+
}
|
435 |
+
|
436 |
+
final public function get_taxonomy_options($taxonomy)
|
437 |
+
{
|
438 |
+
return $this->get_options('taxonomies/'. $taxonomy);
|
439 |
+
}
|
440 |
+
|
441 |
+
/**
|
442 |
+
* @param string $name File name without extension, located in <extension>/static/js/$name.js
|
443 |
+
* @return string URI
|
444 |
+
*/
|
445 |
+
final public function locate_js_URI($name)
|
446 |
+
{
|
447 |
+
return $this->locate_URI('/static/js/'. $name .'.js');
|
448 |
+
}
|
449 |
+
|
450 |
+
/**
|
451 |
+
* @param string $name File name without extension, located in <extension>/static/js/$name.js
|
452 |
+
* @return string URI
|
453 |
+
*/
|
454 |
+
final public function locate_css_URI($name)
|
455 |
+
{
|
456 |
+
return $this->locate_URI('/static/css/'. $name .'.css');
|
457 |
+
}
|
458 |
+
|
459 |
+
/**
|
460 |
+
* @param string $name File name without extension, located in <extension>/views/$name.php
|
461 |
+
* @return false|string
|
462 |
+
*/
|
463 |
+
final public function locate_view_path($name)
|
464 |
+
{
|
465 |
+
return $this->locate_path('/views/'. $name .'.php');
|
466 |
+
}
|
467 |
+
|
468 |
+
final public function get_depth()
|
469 |
+
{
|
470 |
+
return $this->depth;
|
471 |
+
}
|
472 |
+
|
473 |
+
final public function get_customizations_locations()
|
474 |
+
{
|
475 |
+
return $this->customizations_locations;
|
476 |
+
}
|
477 |
+
|
478 |
+
final public function get_rel_path()
|
479 |
+
{
|
480 |
+
return $this->rel_path;
|
481 |
+
}
|
482 |
+
|
483 |
+
/**
|
484 |
+
* Check if child extension is valid
|
485 |
+
*
|
486 |
+
* Used for special cases when an extension requires its child extensions to extend some special class
|
487 |
+
*
|
488 |
+
* @param FW_Extension $child_extension_instance
|
489 |
+
* @return bool
|
490 |
+
* @internal
|
491 |
+
*/
|
492 |
+
public function _child_extension_is_valid($child_extension_instance)
|
493 |
+
{
|
494 |
+
return is_subclass_of($child_extension_instance, 'FW_Extension');
|
495 |
+
}
|
496 |
+
|
497 |
+
/**
|
498 |
+
* Get link to the page created by this extension in dashboard
|
499 |
+
* (Used on the extensions page)
|
500 |
+
* @internal
|
501 |
+
* @return string
|
502 |
+
*/
|
503 |
+
public function _get_link()
|
504 |
+
{
|
505 |
+
return false;
|
506 |
+
}
|
507 |
+
}
|
framework/core/extends/class-fw-option-type.php
CHANGED
@@ -1,398 +1,398 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Backend option
|
5 |
-
*/
|
6 |
-
abstract class FW_Option_Type
|
7 |
-
{
|
8 |
-
/**
|
9 |
-
* Option's unique type, used in option array in 'type' key
|
10 |
-
* @return string
|
11 |
-
*/
|
12 |
-
abstract public function get_type();
|
13 |
-
|
14 |
-
/**
|
15 |
-
* Overwrite this method to enqueue scripts and styles
|
16 |
-
*
|
17 |
-
* This method would be abstract but was added after the framework release,
|
18 |
-
* and to prevent fatal errors from new option types created by users we can't make it abstract.
|
19 |
-
*
|
20 |
-
* @param string $id
|
21 |
-
* @param array $option
|
22 |
-
* @param array $data
|
23 |
-
* @param bool Return true to call this method again on the next enqueue,
|
24 |
-
* if you have some functionality in it that depends on option parameters.
|
25 |
-
* By default this method is called only once for performance reasons.
|
26 |
-
*/
|
27 |
-
protected function _enqueue_static($id, $option, $data) {}
|
28 |
-
|
29 |
-
/**
|
30 |
-
* Generate html
|
31 |
-
* @param string $id
|
32 |
-
* @param array $option Option array merged with _get_defaults()
|
33 |
-
* @param array $data {value => _get_value_from_input(), id_prefix => ..., name_prefix => ...}
|
34 |
-
* @return string HTML
|
35 |
-
* @internal
|
36 |
-
*/
|
37 |
-
abstract protected function _render($id, $option, $data);
|
38 |
-
|
39 |
-
/**
|
40 |
-
* Extract correct value for $option['value'] from input array
|
41 |
-
* If input value is empty, will be returned $option['value']
|
42 |
-
* @param array $option Option array merged with _get_defaults()
|
43 |
-
* @param array|string|null $input_value
|
44 |
-
* @return string|array|int|bool Correct value
|
45 |
-
* @internal
|
46 |
-
*/
|
47 |
-
abstract protected function _get_value_from_input($option, $input_value);
|
48 |
-
|
49 |
-
/**
|
50 |
-
* Default option array
|
51 |
-
*
|
52 |
-
* This makes possible an option array to have required only one parameter: array('type' => '...')
|
53 |
-
* Other parameters are merged with the array returned by this method.
|
54 |
-
*
|
55 |
-
* @return array
|
56 |
-
*
|
57 |
-
* array(
|
58 |
-
* 'value' => '',
|
59 |
-
* ...
|
60 |
-
* )
|
61 |
-
* @internal
|
62 |
-
*/
|
63 |
-
abstract protected function _get_defaults();
|
64 |
-
|
65 |
-
/**
|
66 |
-
* Prevent execute enqueue multiple times
|
67 |
-
* @var bool
|
68 |
-
*/
|
69 |
-
private $static_enqueued = false;
|
70 |
-
|
71 |
-
/**
|
72 |
-
* Used as prefix for attribute id="{prefix}{option-id}"
|
73 |
-
* @return string
|
74 |
-
*/
|
75 |
-
final public static function get_default_id_prefix()
|
76 |
-
{
|
77 |
-
return 'fw-option-';
|
78 |
-
}
|
79 |
-
|
80 |
-
/**
|
81 |
-
* Used as default prefix for attribute name="prefix[name]"
|
82 |
-
* Cannot contain [], it is used for $_POST[ self::get_default_name_prefix() ]
|
83 |
-
* @return string
|
84 |
-
*/
|
85 |
-
final public static function get_default_name_prefix()
|
86 |
-
{
|
87 |
-
return 'fw_options';
|
88 |
-
}
|
89 |
-
|
90 |
-
final public function __construct()
|
91 |
-
{
|
92 |
-
// does nothing at the moment, but maybe in the future will do something
|
93 |
-
}
|
94 |
-
|
95 |
-
/**
|
96 |
-
* @param FW_Access_Key $access_key
|
97 |
-
* @internal
|
98 |
-
* This must be called right after an instance of option type has been created
|
99 |
-
* and was added to the registered array, so it is available through
|
100 |
-
* fw()->backend->option_type($this->get_type())
|
101 |
-
*/
|
102 |
-
final public function _call_init($access_key)
|
103 |
-
{
|
104 |
-
if ($access_key->get_key() !== 'fw_backend') {
|
105 |
-
trigger_error('Method call not allowed', E_USER_ERROR);
|
106 |
-
}
|
107 |
-
|
108 |
-
if (method_exists($this, '_init')) {
|
109 |
-
$this->_init();
|
110 |
-
}
|
111 |
-
}
|
112 |
-
|
113 |
-
/**
|
114 |
-
* Fixes and prepare defaults
|
115 |
-
*
|
116 |
-
* @param string $id
|
117 |
-
* @param array $option
|
118 |
-
* @param array $data
|
119 |
-
* @return array
|
120 |
-
*/
|
121 |
-
private function prepare(&$id, &$option, &$data)
|
122 |
-
{
|
123 |
-
$data = array_merge(
|
124 |
-
array(
|
125 |
-
'id_prefix' => self::get_default_id_prefix(), // attribute id prefix
|
126 |
-
'name_prefix' => self::get_default_name_prefix(), // attribute name prefix
|
127 |
-
),
|
128 |
-
$data
|
129 |
-
);
|
130 |
-
|
131 |
-
$defaults = $this->get_defaults();
|
132 |
-
$merge_attr = !empty($option['attr']) && !empty($defaults['attr']);
|
133 |
-
|
134 |
-
$option = array_merge($defaults, $option, array(
|
135 |
-
'type' => $this->get_type()
|
136 |
-
));
|
137 |
-
|
138 |
-
if ($merge_attr) {
|
139 |
-
$option['attr'] = array_merge($defaults['attr'], $option['attr']);
|
140 |
-
}
|
141 |
-
|
142 |
-
if (!isset($data['value'])) {
|
143 |
-
// if no input value, use default
|
144 |
-
$data['value'] = $option['value'];
|
145 |
-
}
|
146 |
-
|
147 |
-
if (!isset($option['attr'])) {
|
148 |
-
$option['attr'] = array();
|
149 |
-
}
|
150 |
-
|
151 |
-
$option['attr']['name'] = $data['name_prefix'] .'['. $id .']';
|
152 |
-
$option['attr']['id'] = $data['id_prefix'] . $id;
|
153 |
-
$option['attr']['class'] = 'fw-option fw-option-type-'. $option['type'] .(
|
154 |
-
isset($option['attr']['class'])
|
155 |
-
? ' '. $option['attr']['class']
|
156 |
-
: ''
|
157 |
-
);
|
158 |
-
$option['attr']['value'] = is_array($option['value']) ? '' : $option['value'];
|
159 |
-
|
160 |
-
/**
|
161 |
-
* Remove some blacklisted attributes
|
162 |
-
* They should be added only by the render method
|
163 |
-
*/
|
164 |
-
{
|
165 |
-
unset($option['attr']['type']);
|
166 |
-
unset($option['attr']['checked']);
|
167 |
-
unset($option['attr']['selected']);
|
168 |
-
}
|
169 |
-
}
|
170 |
-
|
171 |
-
/**
|
172 |
-
* Generate option's html from option array
|
173 |
-
* @param string $id
|
174 |
-
* @param array $option
|
175 |
-
* @param array $data {value => $this->get_value_from_input()}
|
176 |
-
* @return string HTML
|
177 |
-
*/
|
178 |
-
final public function render($id, $option, $data = array())
|
179 |
-
{
|
180 |
-
$this->prepare($id, $option, $data);
|
181 |
-
|
182 |
-
$this->enqueue_static($id, $option, $data);
|
183 |
-
|
184 |
-
return $this->_render($id, $option, $data);
|
185 |
-
}
|
186 |
-
|
187 |
-
/**
|
188 |
-
* Enqueue option type scripts and styles
|
189 |
-
*
|
190 |
-
* All parameters are optional and will be populated with defaults
|
191 |
-
* @param string $id
|
192 |
-
* @param array $option
|
193 |
-
* @param array $data
|
194 |
-
* @return bool
|
195 |
-
*/
|
196 |
-
final public function enqueue_static($id = '', $option = array(), $data = array())
|
197 |
-
{
|
198 |
-
if (
|
199 |
-
!doing_action('admin_enqueue_scripts')
|
200 |
-
&&
|
201 |
-
!did_action('admin_enqueue_scripts')
|
202 |
-
) {
|
203 |
-
/**
|
204 |
-
* Do not wp_enqueue/register_...() because at this point not all handles has been registered
|
205 |
-
* and maybe they are used in dependencies in handles that are going to be enqueued.
|
206 |
-
* So as a result some handles will not be equeued because of not registered dependecies.
|
207 |
-
*/
|
208 |
-
return;
|
209 |
-
}
|
210 |
-
|
211 |
-
{
|
212 |
-
static $option_types_static_enqueued = false;
|
213 |
-
|
214 |
-
if (!$option_types_static_enqueued) {
|
215 |
-
wp_enqueue_style(
|
216 |
-
'fw-option-types',
|
217 |
-
fw_get_framework_directory_uri('/static/css/option-types.css'),
|
218 |
-
array('fw', 'qtip'),
|
219 |
-
fw()->manifest->get_version()
|
220 |
-
);
|
221 |
-
wp_enqueue_script(
|
222 |
-
'fw-option-types',
|
223 |
-
fw_get_framework_directory_uri('/static/js/option-types.js'),
|
224 |
-
array('fw-events', 'qtip'),
|
225 |
-
fw()->manifest->get_version(),
|
226 |
-
true
|
227 |
-
);
|
228 |
-
|
229 |
-
$option_types_static_enqueued = true;
|
230 |
-
}
|
231 |
-
}
|
232 |
-
|
233 |
-
if ($this->static_enqueued) {
|
234 |
-
return false;
|
235 |
-
}
|
236 |
-
|
237 |
-
$this->prepare($id, $option, $data);
|
238 |
-
|
239 |
-
$call_next_time = $this->_enqueue_static($id, $option, $data);
|
240 |
-
|
241 |
-
$this->static_enqueued = !$call_next_time;
|
242 |
-
|
243 |
-
return $call_next_time;
|
244 |
-
}
|
245 |
-
|
246 |
-
/**
|
247 |
-
* Extract correct value for $option['value'] from input array
|
248 |
-
* If input value is empty, will be returned $option['value']
|
249 |
-
* @param array $option
|
250 |
-
* @param mixed|null $input_value Option's value from $_POST or elsewhere. If is null, it means it does not exists
|
251 |
-
* @return array|string
|
252 |
-
*/
|
253 |
-
final public function get_value_from_input($option, $input_value)
|
254 |
-
{
|
255 |
-
$option = array_merge(
|
256 |
-
$this->get_defaults(),
|
257 |
-
$option,
|
258 |
-
array(
|
259 |
-
'type' => $this->get_type()
|
260 |
-
)
|
261 |
-
);
|
262 |
-
|
263 |
-
return $this->_get_value_from_input($option, $input_value);
|
264 |
-
}
|
265 |
-
|
266 |
-
/**
|
267 |
-
* Default option array
|
268 |
-
*
|
269 |
-
* This makes possible an option array to have required only one parameter: array('type' => '...')
|
270 |
-
* Other parameters are merged with array returned from this method
|
271 |
-
*
|
272 |
-
* @return array
|
273 |
-
*/
|
274 |
-
final public function get_defaults()
|
275 |
-
{
|
276 |
-
$option = $this->_get_defaults();
|
277 |
-
|
278 |
-
$option['type'] = $this->get_type();
|
279 |
-
|
280 |
-
if (!array_key_exists('value', $option)) {
|
281 |
-
FW_Flash_Messages::add(
|
282 |
-
'fw-option-type-no-default-value',
|
283 |
-
sprintf(__('Option type %s has no default value', 'fw'), $this->get_type()),
|
284 |
-
'warning'
|
285 |
-
);
|
286 |
-
|
287 |
-
$option['value'] = array();
|
288 |
-
}
|
289 |
-
|
290 |
-
return $option;
|
291 |
-
}
|
292 |
-
|
293 |
-
/**
|
294 |
-
* Exist 3 types of options widths:
|
295 |
-
* - auto (float left real width of the option (minimal) )
|
296 |
-
* - fixed (inputs, select, textarea, and others - they have same width)
|
297 |
-
* - full (100% . eg. html option should expand to maximum width)
|
298 |
-
* Options can override this method to return another value
|
299 |
-
* @return bool
|
300 |
-
* @internal
|
301 |
-
*/
|
302 |
-
public function _get_backend_width_type()
|
303 |
-
{
|
304 |
-
return 'fixed';
|
305 |
-
}
|
306 |
-
|
307 |
-
/**
|
308 |
-
* Use this method to register a new option type
|
309 |
-
* @param string|FW_Option_Type $option_type_class
|
310 |
-
*/
|
311 |
-
final public static function register($option_type_class) {
|
312 |
-
static $registration_access_key = null;
|
313 |
-
|
314 |
-
if ($registration_access_key === null) {
|
315 |
-
$registration_access_key = new FW_Access_Key('fw_option_type');
|
316 |
-
}
|
317 |
-
|
318 |
-
fw()->backend->_register_option_type($registration_access_key, $option_type_class);
|
319 |
-
}
|
320 |
-
|
321 |
-
/**
|
322 |
-
* If the option is composed of more options (added by user) which values are stored in database
|
323 |
-
* the option must call fw_db_option_storage_load() for each sub-option
|
324 |
-
* because some of them may have configured the save to be done in separate place (post meta, wp option, etc.)
|
325 |
-
* @param string $id
|
326 |
-
* @param array $option
|
327 |
-
* @param mixed $value
|
328 |
-
* @param array $params
|
329 |
-
* @return mixed
|
330 |
-
* @since 2.5.0
|
331 |
-
*/
|
332 |
-
final public function storage_load($id, array $option, $value, array $params = array()) {
|
333 |
-
if ( // do not check !empty($option['fw-storage']) because this param can be set in option defaults
|
334 |
-
$this->get_type() === $option['type']
|
335 |
-
&&
|
336 |
-
($option = array_merge($this->get_defaults(), $option))
|
337 |
-
) {
|
338 |
-
if (is_null($value)) {
|
339 |
-
$value = fw()->backend->option_type($option['type'])->get_value_from_input($option, $value);
|
340 |
-
}
|
341 |
-
|
342 |
-
return $this->_storage_load($id, $option, $value, $params);
|
343 |
-
} else {
|
344 |
-
return $value;
|
345 |
-
}
|
346 |
-
}
|
347 |
-
|
348 |
-
/**
|
349 |
-
* @see storage_load()
|
350 |
-
* @param string $id
|
351 |
-
* @param array $option
|
352 |
-
* @param mixed $value
|
353 |
-
* @param array $params
|
354 |
-
* @return mixed
|
355 |
-
* @since 2.5.0
|
356 |
-
* @internal
|
357 |
-
*/
|
358 |
-
protected function _storage_load($id, array $option, $value, array $params) {
|
359 |
-
return fw_db_option_storage_load($id, $option, $value, $params);
|
360 |
-
}
|
361 |
-
|
362 |
-
/**
|
363 |
-
* If the option is composed of more options (added by user) which values are stored in database
|
364 |
-
* the option must call fw_db_option_storage_save() for each sub-option
|
365 |
-
* because some of them may have configured the save to be done in separate place (post meta, wp option, etc.)
|
366 |
-
* @param string $id
|
367 |
-
* @param array $option
|
368 |
-
* @param mixed $value
|
369 |
-
* @param array $params
|
370 |
-
* @return mixed
|
371 |
-
* @since 2.5.0
|
372 |
-
*/
|
373 |
-
final public function storage_save($id, array $option, $value, array $params = array()) {
|
374 |
-
if ( // do not check !empty($option['fw-storage']) because this param can be set in option defaults
|
375 |
-
$this->get_type() === $option['type']
|
376 |
-
&&
|
377 |
-
($option = array_merge($this->get_defaults(), $option))
|
378 |
-
) {
|
379 |
-
return $this->_storage_save($id, $option, $value, $params);
|
380 |
-
} else {
|
381 |
-
return $value;
|
382 |
-
}
|
383 |
-
}
|
384 |
-
|
385 |
-
/**
|
386 |
-
* @see storage_save()
|
387 |
-
* @param string $id
|
388 |
-
* @param array $option
|
389 |
-
* @param mixed $value
|
390 |
-
* @param array $params
|
391 |
-
* @return mixed
|
392 |
-
* @since 2.5.0
|
393 |
-
* @internal
|
394 |
-
*/
|
395 |
-
protected function _storage_save($id, array $option, $value, array $params) {
|
396 |
-
return fw_db_option_storage_save($id, $option, $value, $params);
|
397 |
-
}
|
398 |
}
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Backend option
|
5 |
+
*/
|
6 |
+
abstract class FW_Option_Type
|
7 |
+
{
|
8 |
+
/**
|
9 |
+
* Option's unique type, used in option array in 'type' key
|
10 |
+
* @return string
|
11 |
+
*/
|
12 |
+
abstract public function get_type();
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Overwrite this method to enqueue scripts and styles
|
16 |
+
*
|
17 |
+
* This method would be abstract but was added after the framework release,
|
18 |
+
* and to prevent fatal errors from new option types created by users we can't make it abstract.
|
19 |
+
*
|
20 |
+
* @param string $id
|
21 |
+
* @param array $option
|
22 |
+
* @param array $data
|
23 |
+
* @param bool Return true to call this method again on the next enqueue,
|
24 |
+
* if you have some functionality in it that depends on option parameters.
|
25 |
+
* By default this method is called only once for performance reasons.
|
26 |
+
*/
|
27 |
+
protected function _enqueue_static($id, $option, $data) {}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Generate html
|
31 |
+
* @param string $id
|
32 |
+
* @param array $option Option array merged with _get_defaults()
|
33 |
+
* @param array $data {value => _get_value_from_input(), id_prefix => ..., name_prefix => ...}
|
34 |
+
* @return string HTML
|
35 |
+
* @internal
|
36 |
+
*/
|
37 |
+
abstract protected function _render($id, $option, $data);
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Extract correct value for $option['value'] from input array
|
41 |
+
* If input value is empty, will be returned $option['value']
|
42 |
+
* @param array $option Option array merged with _get_defaults()
|
43 |
+
* @param array|string|null $input_value
|
44 |
+
* @return string|array|int|bool Correct value
|
45 |
+
* @internal
|
46 |
+
*/
|
47 |
+
abstract protected function _get_value_from_input($option, $input_value);
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Default option array
|
51 |
+
*
|
52 |
+
* This makes possible an option array to have required only one parameter: array('type' => '...')
|
53 |
+
* Other parameters are merged with the array returned by this method.
|
54 |
+
*
|
55 |
+
* @return array
|
56 |
+
*
|
57 |
+
* array(
|
58 |
+
* 'value' => '',
|
59 |
+
* ...
|
60 |
+
* )
|
61 |
+
* @internal
|
62 |
+
*/
|
63 |
+
abstract protected function _get_defaults();
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Prevent execute enqueue multiple times
|
67 |
+
* @var bool
|
68 |
+
*/
|
69 |
+
private $static_enqueued = false;
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Used as prefix for attribute id="{prefix}{option-id}"
|
73 |
+
* @return string
|
74 |
+
*/
|
75 |
+
final public static function get_default_id_prefix()
|
76 |
+
{
|
77 |
+
return 'fw-option-';
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* Used as default prefix for attribute name="prefix[name]"
|
82 |
+
* Cannot contain [], it is used for $_POST[ self::get_default_name_prefix() ]
|
83 |
+
* @return string
|
84 |
+
*/
|
85 |
+
final public static function get_default_name_prefix()
|
86 |
+
{
|
87 |
+
return 'fw_options';
|
88 |
+
}
|
89 |
+
|
90 |
+
final public function __construct()
|
91 |
+
{
|
92 |
+
// does nothing at the moment, but maybe in the future will do something
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* @param FW_Access_Key $access_key
|
97 |
+
* @internal
|
98 |
+
* This must be called right after an instance of option type has been created
|
99 |
+
* and was added to the registered array, so it is available through
|
100 |
+
* fw()->backend->option_type($this->get_type())
|
101 |
+
*/
|
102 |
+
final public function _call_init($access_key)
|
103 |
+
{
|
104 |
+
if ($access_key->get_key() !== 'fw_backend') {
|
105 |
+
trigger_error('Method call not allowed', E_USER_ERROR);
|
106 |
+
}
|
107 |
+
|
108 |
+
if (method_exists($this, '_init')) {
|
109 |
+
$this->_init();
|
110 |
+
}
|
111 |
+
}
|
112 |
+
|
113 |
+
/**
|
114 |
+
* Fixes and prepare defaults
|
115 |
+
*
|
116 |
+
* @param string $id
|
117 |
+
* @param array $option
|
118 |
+
* @param array $data
|
119 |
+
* @return array
|
120 |
+
*/
|
121 |
+
private function prepare(&$id, &$option, &$data)
|
122 |
+
{
|
123 |
+
$data = array_merge(
|
124 |
+
array(
|
125 |
+
'id_prefix' => self::get_default_id_prefix(), // attribute id prefix
|
126 |
+
'name_prefix' => self::get_default_name_prefix(), // attribute name prefix
|
127 |
+
),
|
128 |
+
$data
|
129 |
+
);
|
130 |
+
|
131 |
+
$defaults = $this->get_defaults();
|
132 |
+
$merge_attr = !empty($option['attr']) && !empty($defaults['attr']);
|
133 |
+
|
134 |
+
$option = array_merge($defaults, $option, array(
|
135 |
+
'type' => $this->get_type()
|
136 |
+
));
|
137 |
+
|
138 |
+
if ($merge_attr) {
|
139 |
+
$option['attr'] = array_merge($defaults['attr'], $option['attr']);
|
140 |
+
}
|
141 |
+
|
142 |
+
if (!isset($data['value'])) {
|
143 |
+
// if no input value, use default
|
144 |
+
$data['value'] = $option['value'];
|
145 |
+
}
|
146 |
+
|
147 |
+
if (!isset($option['attr'])) {
|
148 |
+
$option['attr'] = array();
|
149 |
+
}
|
150 |
+
|
151 |
+
$option['attr']['name'] = $data['name_prefix'] .'['. $id .']';
|
152 |
+
$option['attr']['id'] = $data['id_prefix'] . $id;
|
153 |
+
$option['attr']['class'] = 'fw-option fw-option-type-'. $option['type'] .(
|
154 |
+
isset($option['attr']['class'])
|
155 |
+
? ' '. $option['attr']['class']
|
156 |
+
: ''
|
157 |
+
);
|
158 |
+
$option['attr']['value'] = is_array($option['value']) ? '' : $option['value'];
|
159 |
+
|
160 |
+
/**
|
161 |
+
* Remove some blacklisted attributes
|
162 |
+
* They should be added only by the render method
|
163 |
+
*/
|
164 |
+
{
|
165 |
+
unset($option['attr']['type']);
|
166 |
+
unset($option['attr']['checked']);
|
167 |
+
unset($option['attr']['selected']);
|
168 |
+
}
|
169 |
+
}
|
170 |
+
|
171 |
+
/**
|
172 |
+
* Generate option's html from option array
|
173 |
+
* @param string $id
|
174 |
+
* @param array $option
|
175 |
+
* @param array $data {value => $this->get_value_from_input()}
|
176 |
+
* @return string HTML
|
177 |
+
*/
|
178 |
+
final public function render($id, $option, $data = array())
|
179 |
+
{
|
180 |
+
$this->prepare($id, $option, $data);
|
181 |
+
|
182 |
+
$this->enqueue_static($id, $option, $data);
|
183 |
+
|
184 |
+
return $this->_render($id, $option, $data);
|
185 |
+
}
|
186 |
+
|
187 |
+
/**
|
188 |
+
* Enqueue option type scripts and styles
|
189 |
+
*
|
190 |
+
* All parameters are optional and will be populated with defaults
|
191 |
+
* @param string $id
|
192 |
+
* @param array $option
|
193 |
+
* @param array $data
|
194 |
+
* @return bool
|
195 |
+
*/
|
196 |
+
final public function enqueue_static($id = '', $option = array(), $data = array())
|
197 |
+
{
|
198 |
+
if (
|
199 |
+
!doing_action('admin_enqueue_scripts')
|
200 |
+
&&
|
201 |
+
!did_action('admin_enqueue_scripts')
|
202 |
+
) {
|
203 |
+
/**
|
204 |
+
* Do not wp_enqueue/register_...() because at this point not all handles has been registered
|
205 |
+
* and maybe they are used in dependencies in handles that are going to be enqueued.
|
206 |
+
* So as a result some handles will not be equeued because of not registered dependecies.
|
207 |
+
*/
|
208 |
+
return;
|
209 |
+
}
|
210 |
+
|
211 |
+
{
|
212 |
+
static $option_types_static_enqueued = false;
|
213 |
+
|
214 |
+
if (!$option_types_static_enqueued) {
|
215 |
+
wp_enqueue_style(
|
216 |
+
'fw-option-types',
|
217 |
+
fw_get_framework_directory_uri('/static/css/option-types.css'),
|
218 |
+
array('fw', 'qtip'),
|
219 |
+
fw()->manifest->get_version()
|
220 |
+
);
|
221 |
+
wp_enqueue_script(
|
222 |
+
'fw-option-types',
|
223 |
+
fw_get_framework_directory_uri('/static/js/option-types.js'),
|
224 |
+
array('fw-events', 'qtip'),
|
225 |
+
fw()->manifest->get_version(),
|
226 |
+
true
|
227 |
+
);
|
228 |
+
|
229 |
+
$option_types_static_enqueued = true;
|
230 |
+
}
|
231 |
+
}
|
232 |
+
|
233 |
+
if ($this->static_enqueued) {
|
234 |
+
return false;
|
235 |
+
}
|
236 |
+
|
237 |
+
$this->prepare($id, $option, $data);
|
238 |
+
|
239 |
+
$call_next_time = $this->_enqueue_static($id, $option, $data);
|
240 |
+
|
241 |
+
$this->static_enqueued = !$call_next_time;
|
242 |
+
|
243 |
+
return $call_next_time;
|
244 |
+
}
|
245 |
+
|
246 |
+
/**
|
247 |
+
* Extract correct value for $option['value'] from input array
|
248 |
+
* If input value is empty, will be returned $option['value']
|
249 |
+
* @param array $option
|
250 |
+
* @param mixed|null $input_value Option's value from $_POST or elsewhere. If is null, it means it does not exists
|
251 |
+
* @return array|string
|
252 |
+
*/
|
253 |
+
final public function get_value_from_input($option, $input_value)
|
254 |
+
{
|
255 |
+
$option = array_merge(
|
256 |
+
$this->get_defaults(),
|
257 |
+
$option,
|
258 |
+
array(
|
259 |
+
'type' => $this->get_type()
|
260 |
+
)
|
261 |
+
);
|
262 |
+
|
263 |
+
return $this->_get_value_from_input($option, $input_value);
|
264 |
+
}
|
265 |
+
|
266 |
+
/**
|
267 |
+
* Default option array
|
268 |
+
*
|
269 |
+
* This makes possible an option array to have required only one parameter: array('type' => '...')
|
270 |
+
* Other parameters are merged with array returned from this method
|
271 |
+
*
|
272 |
+
* @return array
|
273 |
+
*/
|
274 |
+
final public function get_defaults()
|
275 |
+
{
|
276 |
+
$option = $this->_get_defaults();
|
277 |
+
|
278 |
+
$option['type'] = $this->get_type();
|
279 |
+
|
280 |
+
if (!array_key_exists('value', $option)) {
|
281 |
+
FW_Flash_Messages::add(
|
282 |
+
'fw-option-type-no-default-value',
|
283 |
+
sprintf(__('Option type %s has no default value', 'fw'), $this->get_type()),
|
284 |
+
'warning'
|
285 |
+
);
|
286 |
+
|
287 |
+
$option['value'] = array();
|
288 |
+
}
|
289 |
+
|
290 |
+
return $option;
|
291 |
+
}
|
292 |
+
|
293 |
+
/**
|
294 |
+
* Exist 3 types of options widths:
|
295 |
+
* - auto (float left real width of the option (minimal) )
|
296 |
+
* - fixed (inputs, select, textarea, and others - they have same width)
|
297 |
+
* - full (100% . eg. html option should expand to maximum width)
|
298 |
+
* Options can override this method to return another value
|
299 |
+
* @return bool
|
300 |
+
* @internal
|
301 |
+
*/
|
302 |
+
public function _get_backend_width_type()
|
303 |
+
{
|
304 |
+
return 'fixed';
|
305 |
+
}
|
306 |
+
|
307 |
+
/**
|
308 |
+
* Use this method to register a new option type
|
309 |
+
* @param string|FW_Option_Type $option_type_class
|
310 |
+
*/
|
311 |
+
final public static function register($option_type_class) {
|
312 |
+
static $registration_access_key = null;
|
313 |
+
|
314 |
+
if ($registration_access_key === null) {
|
315 |
+
$registration_access_key = new FW_Access_Key('fw_option_type');
|
316 |
+
}
|
317 |
+
|
318 |
+
fw()->backend->_register_option_type($registration_access_key, $option_type_class);
|
319 |
+
}
|
320 |
+
|
321 |
+
/**
|
322 |
+
* If the option is composed of more options (added by user) which values are stored in database
|
323 |
+
* the option must call fw_db_option_storage_load() for each sub-option
|
324 |
+
* because some of them may have configured the save to be done in separate place (post meta, wp option, etc.)
|
325 |
+
* @param string $id
|
326 |
+
* @param array $option
|
327 |
+
* @param mixed $value
|
328 |
+
* @param array $params
|
329 |
+
* @return mixed
|
330 |
+
* @since 2.5.0
|
331 |
+
*/
|
332 |
+
final public function storage_load($id, array $option, $value, array $params = array()) {
|
333 |
+
if ( // do not check !empty($option['fw-storage']) because this param can be set in option defaults
|
334 |
+
$this->get_type() === $option['type']
|
335 |
+
&&
|
336 |
+
($option = array_merge($this->get_defaults(), $option))
|
337 |
+
) {
|
338 |
+
if (is_null($value)) {
|
339 |
+
$value = fw()->backend->option_type($option['type'])->get_value_from_input($option, $value);
|
340 |
+
}
|
341 |
+
|
342 |
+
return $this->_storage_load($id, $option, $value, $params);
|
343 |
+
} else {
|
344 |
+
return $value;
|
345 |
+
}
|
346 |
+
}
|
347 |
+
|
348 |
+
/**
|
349 |
+
* @see storage_load()
|
350 |
+
* @param string $id
|
351 |
+
* @param array $option
|
352 |
+
* @param mixed $value
|
353 |
+
* @param array $params
|
354 |
+
* @return mixed
|
355 |
+
* @since 2.5.0
|
356 |
+
* @internal
|
357 |
+
*/
|
358 |
+
protected function _storage_load($id, array $option, $value, array $params) {
|
359 |
+
return fw_db_option_storage_load($id, $option, $value, $params);
|
360 |
+
}
|
361 |
+
|
362 |
+
/**
|
363 |
+
* If the option is composed of more options (added by user) which values are stored in database
|
364 |
+
* the option must call fw_db_option_storage_save() for each sub-option
|
365 |
+
* because some of them may have configured the save to be done in separate place (post meta, wp option, etc.)
|
366 |
+
* @param string $id
|
367 |
+
* @param array $option
|
368 |
+
* @param mixed $value
|
369 |
+
* @param array $params
|
370 |
+
* @return mixed
|
371 |
+
* @since 2.5.0
|
372 |
+
*/
|
373 |
+
final public function storage_save($id, array $option, $value, array $params = array()) {
|
374 |
+
if ( // do not check !empty($option['fw-storage']) because this param can be set in option defaults
|
375 |
+
$this->get_type() === $option['type']
|
376 |
+
&&
|
377 |
+
($option = array_merge($this->get_defaults(), $option))
|
378 |
+
) {
|
379 |
+
return $this->_storage_save($id, $option, $value, $params);
|
380 |
+
} else {
|
381 |
+
return $value;
|
382 |
+
}
|
383 |
+
}
|
384 |
+
|
385 |
+
/**
|
386 |
+
* @see storage_save()
|
387 |
+
* @param string $id
|
388 |
+
* @param array $option
|
389 |
+
* @param mixed $value
|
390 |
+
* @param array $params
|
391 |
+
* @return mixed
|
392 |
+
* @since 2.5.0
|
393 |
+
* @internal
|
394 |
+
*/
|
395 |
+
protected function _storage_save($id, array $option, $value, array $params) {
|
396 |
+
return fw_db_option_storage_save($id, $option, $value, $params);
|
397 |
+
}
|
398 |
}
|
framework/core/extends/interface-fw-option-handler.php
CHANGED
@@ -1,12 +1,12 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
|
3 |
-
/**
|
4 |
-
* @deprecated since 2.5.0
|
5 |
-
*/
|
6 |
-
interface FW_Option_Handler
|
7 |
-
{
|
8 |
-
function get_option_value($option_id, $option, $data = array());
|
9 |
-
|
10 |
-
function save_option_value($option_id, $option, $value, $data = array());
|
11 |
-
}
|
12 |
-
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @deprecated since 2.5.0
|
5 |
+
*/
|
6 |
+
interface FW_Option_Handler
|
7 |
+
{
|
8 |
+
function get_option_value($option_id, $option, $data = array());
|
9 |
+
|
10 |
+
function save_option_value($option_id, $option, $value, $data = array());
|
11 |
+
}
|
12 |
+
|
framework/extensions/blog/class-fw-extension-blog.php
CHANGED
@@ -1,90 +1,90 @@
|
|
1 |
-
<?php if ( ! defined( 'FW' ) ) {
|
2 |
-
die( 'Forbidden' );
|
3 |
-
}
|
4 |
-
|
5 |
-
class FW_Extension_Blog extends FW_Extension {
|
6 |
-
private $post_type = 'post';
|
7 |
-
|
8 |
-
/**
|
9 |
-
* @internal
|
10 |
-
*/
|
11 |
-
public function _init() {
|
12 |
-
if ( is_admin() ) {
|
13 |
-
add_action( 'admin_menu', array( $this, '_admin_action_rename_post_menu' ) );
|
14 |
-
add_action( 'init', array( $this, '_admin_action_change_post_labels' ), 999 );
|
15 |
-
} else {
|
16 |
-
add_action( 'init', array( $this, '_theme_action_change_post_labels' ), 999 );
|
17 |
-
}
|
18 |
-
}
|
19 |
-
|
20 |
-
/**
|
21 |
-
* Changes the labels value od the posts type: post from Post to Blog Post
|
22 |
-
* @internal
|
23 |
-
*/
|
24 |
-
public function _theme_action_change_post_labels() {
|
25 |
-
global $wp_post_types;
|
26 |
-
$p = $this->post_type;
|
27 |
-
|
28 |
-
// Someone has changed this post type, always check for that!
|
29 |
-
if ( empty ( $wp_post_types[ $p ] )
|
30 |
-
or ! is_object( $wp_post_types[ $p ] )
|
31 |
-
or empty ( $wp_post_types[ $p ]->labels )
|
32 |
-
) {
|
33 |
-
return;
|
34 |
-
}
|
35 |
-
|
36 |
-
$wp_post_types[ $p ]->labels->name = __( 'Blog', 'fw' );
|
37 |
-
$wp_post_types[ $p ]->labels->singular_name = __( 'Blog', 'fw' );
|
38 |
-
$wp_post_types[ $p ]->labels->add_new = __( 'Add blog post', 'fw' );
|
39 |
-
$wp_post_types[ $p ]->labels->add_new_item = __( 'Add new blog post', 'fw' );
|
40 |
-
$wp_post_types[ $p ]->labels->all_items = __( 'All blog posts', 'fw' );
|
41 |
-
$wp_post_types[ $p ]->labels->edit_item = __( 'Edit blog post', 'fw' );
|
42 |
-
$wp_post_types[ $p ]->labels->name_admin_bar = __( 'Blog Post', 'fw' );
|
43 |
-
$wp_post_types[ $p ]->labels->menu_name = __( 'Blog Post', 'fw' );
|
44 |
-
$wp_post_types[ $p ]->labels->new_item = __( 'New blog post', 'fw' );
|
45 |
-
$wp_post_types[ $p ]->labels->not_found = __( 'No blog posts found', 'fw' );
|
46 |
-
$wp_post_types[ $p ]->labels->not_found_in_trash = __( 'No blog posts found in trash', 'fw' );
|
47 |
-
$wp_post_types[ $p ]->labels->search_items = __( 'Search blog posts', 'fw' );
|
48 |
-
$wp_post_types[ $p ]->labels->view_item = __( 'View blog post', 'fw' );
|
49 |
-
}
|
50 |
-
|
51 |
-
/**
|
52 |
-
* Changes the labels value od the posts type: post from Post to Blog Post
|
53 |
-
* @internal
|
54 |
-
*/
|
55 |
-
public function _admin_action_change_post_labels() {
|
56 |
-
global $wp_post_types, $wp_taxonomies;
|
57 |
-
$p = $this->post_type;
|
58 |
-
|
59 |
-
// Someone has changed this post type, always check for that!
|
60 |
-
if ( empty ( $wp_post_types[ $p ] )
|
61 |
-
or ! is_object( $wp_post_types[ $p ] )
|
62 |
-
or empty ( $wp_post_types[ $p ]->labels )
|
63 |
-
) {
|
64 |
-
return;
|
65 |
-
}
|
66 |
-
|
67 |
-
$wp_post_types[ $p ]->labels->name = __( 'Blog Posts', 'fw' );
|
68 |
-
|
69 |
-
if ( empty ( $wp_taxonomies['category'] )
|
70 |
-
or ! is_object( $wp_taxonomies['category'] )
|
71 |
-
or empty ( $wp_taxonomies['category']->labels )
|
72 |
-
) {
|
73 |
-
return;
|
74 |
-
}
|
75 |
-
|
76 |
-
$wp_taxonomies['category']->labels->name = __( 'Blog Categories', 'fw' );
|
77 |
-
}
|
78 |
-
|
79 |
-
/**
|
80 |
-
* Changes the name in admin menu from Post to Blog Post
|
81 |
-
* @internal
|
82 |
-
*/
|
83 |
-
public function _admin_action_rename_post_menu() {
|
84 |
-
global $menu;
|
85 |
-
|
86 |
-
if ( isset( $menu[5] ) ) {
|
87 |
-
$menu[5][0] = __( 'Blog Posts', 'fw' );
|
88 |
-
}
|
89 |
-
}
|
90 |
}
|
1 |
+
<?php if ( ! defined( 'FW' ) ) {
|
2 |
+
die( 'Forbidden' );
|
3 |
+
}
|
4 |
+
|
5 |
+
class FW_Extension_Blog extends FW_Extension {
|
6 |
+
private $post_type = 'post';
|
7 |
+
|
8 |
+
/**
|
9 |
+
* @internal
|
10 |
+
*/
|
11 |
+
public function _init() {
|
12 |
+
if ( is_admin() ) {
|
13 |
+
add_action( 'admin_menu', array( $this, '_admin_action_rename_post_menu' ) );
|
14 |
+
add_action( 'init', array( $this, '_admin_action_change_post_labels' ), 999 );
|
15 |
+
} else {
|
16 |
+
add_action( 'init', array( $this, '_theme_action_change_post_labels' ), 999 );
|
17 |
+
}
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Changes the labels value od the posts type: post from Post to Blog Post
|
22 |
+
* @internal
|
23 |
+
*/
|
24 |
+
public function _theme_action_change_post_labels() {
|
25 |
+
global $wp_post_types;
|
26 |
+
$p = $this->post_type;
|
27 |
+
|
28 |
+
// Someone has changed this post type, always check for that!
|
29 |
+
if ( empty ( $wp_post_types[ $p ] )
|
30 |
+
or ! is_object( $wp_post_types[ $p ] )
|
31 |
+
or empty ( $wp_post_types[ $p ]->labels )
|
32 |
+
) {
|
33 |
+
return;
|
34 |
+
}
|
35 |
+
|
36 |
+
$wp_post_types[ $p ]->labels->name = __( 'Blog', 'fw' );
|
37 |
+
$wp_post_types[ $p ]->labels->singular_name = __( 'Blog', 'fw' );
|
38 |
+
$wp_post_types[ $p ]->labels->add_new = __( 'Add blog post', 'fw' );
|
39 |
+
$wp_post_types[ $p ]->labels->add_new_item = __( 'Add new blog post', 'fw' );
|
40 |
+
$wp_post_types[ $p ]->labels->all_items = __( 'All blog posts', 'fw' );
|
41 |
+
$wp_post_types[ $p ]->labels->edit_item = __( 'Edit blog post', 'fw' );
|
42 |
+
$wp_post_types[ $p ]->labels->name_admin_bar = __( 'Blog Post', 'fw' );
|
43 |
+
$wp_post_types[ $p ]->labels->menu_name = __( 'Blog Post', 'fw' );
|
44 |
+
$wp_post_types[ $p ]->labels->new_item = __( 'New blog post', 'fw' );
|
45 |
+
$wp_post_types[ $p ]->labels->not_found = __( 'No blog posts found', 'fw' );
|
46 |
+
$wp_post_types[ $p ]->labels->not_found_in_trash = __( 'No blog posts found in trash', 'fw' );
|
47 |
+
$wp_post_types[ $p ]->labels->search_items = __( 'Search blog posts', 'fw' );
|
48 |
+
$wp_post_types[ $p ]->labels->view_item = __( 'View blog post', 'fw' );
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Changes the labels value od the posts type: post from Post to Blog Post
|
53 |
+
* @internal
|
54 |
+
*/
|
55 |
+
public function _admin_action_change_post_labels() {
|
56 |
+
global $wp_post_types, $wp_taxonomies;
|
57 |
+
$p = $this->post_type;
|
58 |
+
|
59 |
+
// Someone has changed this post type, always check for that!
|
60 |
+
if ( empty ( $wp_post_types[ $p ] )
|
61 |
+
or ! is_object( $wp_post_types[ $p ] )
|
62 |
+
or empty ( $wp_post_types[ $p ]->labels )
|
63 |
+
) {
|
64 |
+
return;
|
65 |
+
}
|
66 |
+
|
67 |
+
$wp_post_types[ $p ]->labels->name = __( 'Blog Posts', 'fw' );
|
68 |
+
|
69 |
+
if ( empty ( $wp_taxonomies['category'] )
|
70 |
+
or ! is_object( $wp_taxonomies['category'] )
|
71 |
+
or empty ( $wp_taxonomies['category']->labels )
|
72 |
+
) {
|
73 |
+
return;
|
74 |
+
}
|
75 |
+
|
76 |
+
$wp_taxonomies['category']->labels->name = __( 'Blog Categories', 'fw' );
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Changes the name in admin menu from Post to Blog Post
|
81 |
+
* @internal
|
82 |
+
*/
|
83 |
+
public function _admin_action_rename_post_menu() {
|
84 |
+
global $menu;
|
85 |
+
|
86 |
+
if ( isset( $menu[5] ) ) {
|
87 |
+
$menu[5][0] = __( 'Blog Posts', 'fw' );
|
88 |
+
}
|
89 |
+
}
|
90 |
}
|
framework/extensions/blog/manifest.php
CHANGED
@@ -1,13 +1,13 @@
|
|
1 |
-
<?php if ( ! defined( 'FW' ) ) {
|
2 |
-
die( 'Forbidden' );
|
3 |
-
}
|
4 |
-
|
5 |
-
$manifest = array();
|
6 |
-
|
7 |
-
$manifest['name'] = __( 'Blog Posts', 'fw' );
|
8 |
-
$manifest['description'] = __( 'Blog Posts', 'fw' );
|
9 |
-
$manifest['version'] = '1.0.1';
|
10 |
-
$manifest['display'] = false;
|
11 |
-
$manifest['standalone'] = true;
|
12 |
-
|
13 |
-
$manifest['github_update'] = 'ThemeFuse/Unyson-Blog-Extension';
|
1 |
+
<?php if ( ! defined( 'FW' ) ) {
|
2 |
+
die( 'Forbidden' );
|
3 |
+
}
|
4 |
+
|
5 |
+
$manifest = array();
|
6 |
+
|
7 |
+
$manifest['name'] = __( 'Blog Posts', 'fw' );
|
8 |
+
$manifest['description'] = __( 'Blog Posts', 'fw' );
|
9 |
+
$manifest['version'] = '1.0.1';
|
10 |
+
$manifest['display'] = false;
|
11 |
+
$manifest['standalone'] = true;
|
12 |
+
|
13 |
+
$manifest['github_update'] = 'ThemeFuse/Unyson-Blog-Extension';
|
framework/extensions/update/class-fw-extension-update.php
CHANGED
@@ -1,982 +1,982 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
|
3 |
-
require dirname(__FILE__) .'/includes/extends/class-fw-ext-update-service.php';
|
4 |
-
|
5 |
-
class FW_Extension_Update extends FW_Extension
|
6 |
-
{
|
7 |
-
/**
|
8 |
-
* {@inheritdoc}
|
9 |
-
*/
|
10 |
-
public function _child_extension_is_valid($child_extension_instance)
|
11 |
-
{
|
12 |
-
return is_subclass_of($child_extension_instance, 'FW_Ext_Update_Service');
|
13 |
-
}
|
14 |
-
|
15 |
-
/**
|
16 |
-
* File names to skip (do not delete or change) during the update process
|
17 |
-
* @var array
|
18 |
-
*/
|
19 |
-
private $skip_file_names = array('.git');
|
20 |
-
|
21 |
-
/**
|
22 |
-
* @internal
|
23 |
-
*/
|
24 |
-
protected function _init()
|
25 |
-
{
|
26 |
-
{
|
27 |
-
$has_access = (current_user_can('update_themes') || current_user_can('update_plugins'));
|
28 |
-
|
29 |
-
if ($has_access) {
|
30 |
-
if (is_multisite() && !is_network_admin()) {
|
31 |
-
// only network admin can change files that affects the entire network
|
32 |
-
$has_access = false;
|
33 |
-
}
|
34 |
-
}
|
35 |
-
|
36 |
-
if (!$has_access) {
|
37 |
-
return false; // prevent child extensions activation
|
38 |
-
}
|
39 |
-
}
|
40 |
-
|
41 |
-
$this->add_actions();
|
42 |
-
$this->add_filters();
|
43 |
-
}
|
44 |
-
|
45 |
-
private function add_actions()
|
46 |
-
{
|
47 |
-
add_action('core_upgrade_preamble', array($this, '_action_updates_page_footer'));
|
48 |
-
|
49 |
-
add_action('update-core-custom_'. 'fw-update-framework', array($this, '_action_update_framework'));
|
50 |
-
add_action('update-core-custom_'. 'fw-update-theme', array($this, '_action_update_theme'));
|
51 |
-
add_action('update-core-custom_'. 'fw-update-extensions', array($this, '_action_update_extensions'));
|
52 |
-
|
53 |
-
add_action('admin_notices', array($this, '_action_admin_notices'));
|
54 |
-
}
|
55 |
-
|
56 |
-
private function add_filters()
|
57 |
-
{
|
58 |
-
add_filter('wp_get_update_data', array($this, '_filter_update_data'), 10, 2);
|
59 |
-
}
|
60 |
-
|
61 |
-
private function get_fixed_version($version)
|
62 |
-
{
|
63 |
-
// remove from the beginning everything that is not a number: 'v1.2.3' -> '1.2.3', 'ver1.0.0' -> '1.0.0'
|
64 |
-
return preg_replace('/^[^0-9]+/i', '', $version);;
|
65 |
-
}
|
66 |
-
|
67 |
-
private function get_wp_fs_tmp_dir()
|
68 |
-
{
|
69 |
-
return FW_WP_Filesystem::real_path_to_filesystem_path(
|
70 |
-
apply_filters('fw_tmp_dir', fw_fix_path(WP_CONTENT_DIR) .'/tmp')
|
71 |
-
);
|
72 |
-
}
|
73 |
-
|
74 |
-
/**
|
75 |
-
* @internal
|
76 |
-
*/
|
77 |
-
public function _action_updates_page_footer()
|
78 |
-
{
|
79 |
-
echo $this->render_view('updates-list', array(
|
80 |
-
'updates' => $this->get_updates(!empty($_GET['force-check']))
|
81 |
-
));
|
82 |
-
}
|
83 |
-
|
84 |
-
/**
|
85 |
-
* @internal
|
86 |
-
*/
|
87 |
-
public function _filter_update_data($data, $titles)
|
88 |
-
{
|
89 |
-
$updates = $this->get_updates(!empty($_GET['force-check']));
|
90 |
-
|
91 |
-
if ($updates['framework'] && !is_wp_error($updates['framework'])) {
|
92 |
-
++$data['counts']['total'];
|
93 |
-
}
|
94 |
-
|
95 |
-
if ($updates['theme'] && !is_wp_error($updates['theme'])) {
|
96 |
-
++$data['counts']['total'];
|
97 |
-
}
|
98 |
-
|
99 |
-
if (!empty($updates['extensions'])) {
|
100 |
-
foreach ( $updates['extensions'] as $ext_name => $ext_update ) {
|
101 |
-
if ( is_wp_error( $ext_update ) ) {
|
102 |
-
continue;
|
103 |
-
}
|
104 |
-
|
105 |
-
++$data['counts']['total'];
|
106 |
-
|
107 |
-
if ($this->get_config('extensions_as_one_update')) {
|
108 |
-
// no matter how many extensions, display as one update
|
109 |
-
break;
|
110 |
-
}
|
111 |
-
}
|
112 |
-
}
|
113 |
-
|
114 |
-
return $data;
|
115 |
-
}
|
116 |
-
|
117 |
-
private function get_updates($force_check = false)
|
118 |
-
{
|
119 |
-
$cache_key = 'fw_ext_update/updates';
|
120 |
-
|
121 |
-
// use cache because this method may be called multiple times (to prevent useless requests to update servers)
|
122 |
-
|
123 |
-
try {
|
124 |
-
return FW_Cache::get($cache_key);
|
125 |
-
} catch (FW_Cache_Not_Found_Exception $e) {
|
126 |
-
$updates = array(
|
127 |
-
'framework' => $this->get_framework_update($force_check),
|
128 |
-
'theme' => $this->get_theme_update($force_check),
|
129 |
-
'extensions' => $this->get_extensions_with_updates($force_check)
|
130 |
-
);
|
131 |
-
|
132 |
-
FW_Cache::set($cache_key, $updates);
|
133 |
-
|
134 |
-
return $updates;
|
135 |
-
}
|
136 |
-
}
|
137 |
-
|
138 |
-
/**
|
139 |
-
* Collect extensions that has new versions available
|
140 |
-
* @param bool $force_check
|
141 |
-
* @return array {ext_name => update_data}
|
142 |
-
*/
|
143 |
-
private function get_extensions_with_updates($force_check = false)
|
144 |
-
{
|
145 |
-
$updates = array();
|
146 |
-
$services = $this->get_children();
|
147 |
-
$theme_ext_requirements = fw()->theme->manifest->get('requirements/extensions');
|
148 |
-
|
149 |
-
foreach (fw()->extensions->get_all() as $ext_name => $extension) {
|
150 |
-
/** @var FW_Extension $extension */
|
151 |
-
|
152 |
-
/**
|
153 |
-
* Ask each service if it knows how to update the extension
|
154 |
-
*/
|
155 |
-
foreach ($services as $service_name => $service) {
|
156 |
-
/** @var $service FW_Ext_Update_Service */
|
157 |
-
|
158 |
-
$latest_version = $service->_get_extension_latest_version($extension, $force_check);
|
159 |
-
|
160 |
-
if ($latest_version === false) {
|
161 |
-
// It said that it doesn't know how to update it
|
162 |
-
continue;
|
163 |
-
}
|
164 |
-
|
165 |
-
if (is_wp_error($latest_version)) {
|
166 |
-
$updates[$ext_name] = $latest_version;
|
167 |
-
break;
|
168 |
-
}
|
169 |
-
|
170 |
-
$fixed_latest_version = $this->get_fixed_version($latest_version);
|
171 |
-
|
172 |
-
if (!version_compare($fixed_latest_version, $extension->manifest->get_version(), '>')) {
|
173 |
-
// we already have latest version
|
174 |
-
continue;
|
175 |
-
}
|
176 |
-
|
177 |
-
if (
|
178 |
-
isset($theme_ext_requirements[$ext_name])
|
179 |
-
&&
|
180 |
-
isset($theme_ext_requirements[$ext_name]['max_version'])
|
181 |
-
&&
|
182 |
-
version_compare($fixed_latest_version, $theme_ext_requirements[$ext_name]['max_version'], '>')
|
183 |
-
) {
|
184 |
-
continue; // do not allow update if it exceeds max_version
|
185 |
-
}
|
186 |
-
|
187 |
-
$updates[$ext_name] = array(
|
188 |
-
'service' => $service_name,
|
189 |
-
'latest_version' => $latest_version,
|
190 |
-
'fixed_latest_version' => $fixed_latest_version
|
191 |
-
);
|
192 |
-
|
193 |
-
break;
|
194 |
-
}
|
195 |
-
}
|
196 |
-
|
197 |
-
return $updates;
|
198 |
-
}
|
199 |
-
|
200 |
-
/**
|
201 |
-
* @param bool $force_check
|
202 |
-
* @return array|false|WP_Error
|
203 |
-
*/
|
204 |
-
private function get_framework_update($force_check = false)
|
205 |
-
{
|
206 |
-
/**
|
207 |
-
* Ask each service if it knows how to update the framework
|
208 |
-
*/
|
209 |
-
foreach ($this->get_children() as $service_name => $service) {
|
210 |
-
/** @var $service FW_Ext_Update_Service */
|
211 |
-
|
212 |
-
$latest_version = $service->_get_framework_latest_version($force_check);
|
213 |
-
|
214 |
-
if ($latest_version === false) {
|
215 |
-
// It said that it doesn't know how to update it
|
216 |
-
continue;
|
217 |
-
}
|
218 |
-
|
219 |
-
if (is_wp_error($latest_version)) {
|
220 |
-
return $latest_version;
|
221 |
-
}
|
222 |
-
|
223 |
-
$fixed_latest_version = $this->get_fixed_version($latest_version);
|
224 |
-
|
225 |
-
if (!version_compare($fixed_latest_version, fw()->manifest->get_version(), '>')) {
|
226 |
-
// we already have latest version
|
227 |
-
continue;
|
228 |
-
}
|
229 |
-
|
230 |
-
return array(
|
231 |
-
'service' => $service_name,
|
232 |
-
'latest_version' => $latest_version,
|
233 |
-
'fixed_latest_version' => $fixed_latest_version
|
234 |
-
);
|
235 |
-
}
|
236 |
-
|
237 |
-
return false;
|
238 |
-
}
|
239 |
-
|
240 |
-
/**
|
241 |
-
* @param bool $force_check
|
242 |
-
* @return array|false|WP_Error
|
243 |
-
*/
|
244 |
-
private function get_theme_update($force_check = false)
|
245 |
-
{
|
246 |
-
/**
|
247 |
-
* Ask each service if it knows how to update the theme
|
248 |
-
*/
|
249 |
-
foreach ($this->get_children() as $service_name => $service) {
|
250 |
-
/** @var $service FW_Ext_Update_Service */
|
251 |
-
|
252 |
-
$latest_version = $service->_get_theme_latest_version($force_check);
|
253 |
-
|
254 |
-
if ($latest_version === false) {
|
255 |
-
// It said that it doesn't know how to update it
|
256 |
-
continue;
|
257 |
-
}
|
258 |
-
|
259 |
-
if (is_wp_error($latest_version)) {
|
260 |
-
return $latest_version;
|
261 |
-
}
|
262 |
-
|
263 |
-
$fixed_latest_version = $this->get_fixed_version($latest_version);
|
264 |
-
|
265 |
-
if (!version_compare($fixed_latest_version, fw()->theme->manifest->get_version(), '>')) {
|
266 |
-
// we already have latest version
|
267 |
-
continue;
|
268 |
-
}
|
269 |
-
|
270 |
-
return array(
|
271 |
-
'service' => $service_name,
|
272 |
-
'latest_version' => $latest_version,
|
273 |
-
'fixed_latest_version' => $fixed_latest_version
|
274 |
-
);
|
275 |
-
}
|
276 |
-
|
277 |
-
return false;
|
278 |
-
}
|
279 |
-
|
280 |
-
/**
|
281 |
-
* Turn on/off the maintenance mode
|
282 |
-
* @param bool $enable
|
283 |
-
*/
|
284 |
-
private function maintenance_mode($enable = false)
|
285 |
-
{
|
286 |
-
/** @var WP_Filesystem_Base $wp_filesystem */
|
287 |
-
global $wp_filesystem;
|
288 |
-
|
289 |
-
if (!$wp_filesystem || (is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code())) {
|
290 |
-
return;
|
291 |
-
}
|
292 |
-
|
293 |
-
$file_path = $wp_filesystem->abspath() . '.maintenance';
|
294 |
-
|
295 |
-
if ($wp_filesystem->exists($file_path)) {
|
296 |
-
if (!$wp_filesystem->delete($file_path)) {
|
297 |
-
trigger_error(__('Cannot delete: ', 'fw') . $file_path, E_USER_WARNING);
|
298 |
-
}
|
299 |
-
}
|
300 |
-
|
301 |
-
if ($enable) {
|
302 |
-
// Create maintenance file to signal that we are upgrading
|
303 |
-
if (!$wp_filesystem->put_contents($file_path, '<?php $upgrading = ' . time() . '; ?>', FS_CHMOD_FILE)) {
|
304 |
-
trigger_error(__('Cannot create: ', 'fw') . $file_path, E_USER_WARNING);
|
305 |
-
}
|
306 |
-
}
|
307 |
-
}
|
308 |
-
|
309 |
-
/**
|
310 |
-
* Download and install new version files
|
311 |
-
*
|
312 |
-
* global $wp_filesystem; must be initialized before calling this method
|
313 |
-
*
|
314 |
-
* @param array $data
|
315 |
-
* @param bool $merge_extensions The extensions/ directory will not be replaced entirely,
|
316 |
-
* only extensions that comes with the update will be replaced
|
317 |
-
* @return null|WP_Error
|
318 |
-
*/
|
319 |
-
private function update($data, $merge_extensions = false)
|
320 |
-
{
|
321 |
-
$required_data_keys = array(
|
322 |
-
'wp_fs_destination_dir' => true,
|
323 |
-
'download_callback' => true,
|
324 |
-
'download_callback_args' => true,
|
325 |
-
'skin' => true,
|
326 |
-
'title' => true,
|
327 |
-
);
|
328 |
-
|
329 |
-
if (count($required_data_keys) > count(array_intersect_key($required_data_keys, $data))) {
|
330 |
-
trigger_error('Some required keys are not present', E_USER_ERROR);
|
331 |
-
}
|
332 |
-
|
333 |
-
// move manually every key to variable, so IDE will understand better them
|
334 |
-
{
|
335 |
-
/**
|
336 |
-
* Replace all files in this directory with downloaded
|
337 |
-
* @var string $wp_fs_destination_dir
|
338 |
-
*/
|
339 |
-
$wp_fs_destination_dir = $data['wp_fs_destination_dir'];
|
340 |
-
|
341 |
-
/**
|
342 |
-
* Called to download new version files to $this->get_wp_fs_tmp_dir()
|
343 |
-
* @var callable $download_callback
|
344 |
-
*/
|
345 |
-
$download_callback = $data['download_callback'];
|
346 |
-
|
347 |
-
/**
|
348 |
-
* @var array
|
349 |
-
*/
|
350 |
-
$download_callback_args = $data['download_callback_args'];
|
351 |
-
|
352 |
-
/**
|
353 |
-
* @var WP_Upgrader_Skin $skin
|
354 |
-
*/
|
355 |
-
$skin = $data['skin'];
|
356 |
-
|
357 |
-
/**
|
358 |
-
* Used in text messages
|
359 |
-
* @var string $title
|
360 |
-
*/
|
361 |
-
$title = $data['title'];
|
362 |
-
|
363 |
-
unset($data);
|
364 |
-
}
|
365 |
-
|
366 |
-
/**
|
367 |
-
* @var string|WP_Error
|
368 |
-
*/
|
369 |
-
$error = false;
|
370 |
-
|
371 |
-
$tmp_download_dir = $this->get_wp_fs_tmp_dir();
|
372 |
-
|
373 |
-
do {
|
374 |
-
/** @var WP_Filesystem_Base $wp_filesystem */
|
375 |
-
global $wp_filesystem;
|
376 |
-
|
377 |
-
// create temporary directory
|
378 |
-
{
|
379 |
-
if ($wp_filesystem->exists($tmp_download_dir)) {
|
380 |
-
// just in case it already exists, clear everything, it may contain old files
|
381 |
-
if (!$wp_filesystem->rmdir($tmp_download_dir, true)) {
|
382 |
-
$error = __('Cannot remove old temporary directory: ', 'fw') . $tmp_download_dir;
|
383 |
-
break;
|
384 |
-
}
|
385 |
-
}
|
386 |
-
|
387 |
-
if (!FW_WP_Filesystem::mkdir_recursive($tmp_download_dir)) {
|
388 |
-
$error = __('Cannot create directory: ', 'fw') . $tmp_download_dir;
|
389 |
-
break;
|
390 |
-
}
|
391 |
-
}
|
392 |
-
|
393 |
-
$skin->feedback(sprintf(__('Downloading the %s...', 'fw'), $title));
|
394 |
-
{
|
395 |
-
$downloaded_dir = call_user_func_array($download_callback, $download_callback_args);
|
396 |
-
|
397 |
-
if (!$downloaded_dir) {
|
398 |
-
$error = sprintf(__('Cannot download the %s.', 'fw'), $title);
|
399 |
-
break;
|
400 |
-
} elseif (is_wp_error($downloaded_dir)) {
|
401 |
-
$error = $downloaded_dir;
|
402 |
-
break;
|
403 |
-
}
|
404 |
-
}
|
405 |
-
|
406 |
-
$this->maintenance_mode(true);
|
407 |
-
|
408 |
-
$skin->feedback(sprintf(__('Installing the %s...', 'fw'), $title));
|
409 |
-
{
|
410 |
-
// remove all files from destination directory
|
411 |
-
{
|
412 |
-
$dir_files = $wp_filesystem->dirlist($wp_fs_destination_dir, true);
|
413 |
-
if ($dir_files === false) {
|
414 |
-
$error =__('Cannot access directory: ', 'fw') . $wp_fs_destination_dir;
|
415 |
-
break;
|
416 |
-
}
|
417 |
-
|
418 |
-
foreach ($dir_files as $file) {
|
419 |
-
if (in_array($file['name'], $this->skip_file_names)) {
|
420 |
-
continue;
|
421 |
-
}
|
422 |
-
|
423 |
-
if ($merge_extensions) {
|
424 |
-
if ($file['name'] === 'extensions' && $file['type'] === 'd') {
|
425 |
-
// do not remove extensions, will be merged later
|
426 |
-
continue;
|
427 |
-
}
|
428 |
-
}
|
429 |
-
|
430 |
-
$file_path = $wp_fs_destination_dir .'/'. $file['name'];
|
431 |
-
|
432 |
-
if (!$wp_filesystem->delete($file_path, true, $file['type'])) {
|
433 |
-
$error = __('Cannot remove: ', 'fw') . $file_path;
|
434 |
-
break 2;
|
435 |
-
}
|
436 |
-
}
|
437 |
-
}
|
438 |
-
|
439 |
-
// move all files from the temporary directory to the destination directory
|
440 |
-
{
|
441 |
-
$dir_files = $wp_filesystem->dirlist($downloaded_dir, true);
|
442 |
-
if ($dir_files === false) {
|
443 |
-
$error = __('Cannot access directory: ', 'fw') . $downloaded_dir;
|
444 |
-
break;
|
445 |
-
}
|
446 |
-
|
447 |
-
foreach ($dir_files as $file) {
|
448 |
-
if (in_array($file['name'], $this->skip_file_names)) {
|
449 |
-
continue;
|
450 |
-
}
|
451 |
-
|
452 |
-
$downloaded_file_path = $downloaded_dir .'/'. $file['name'];
|
453 |
-
$destination_file_path = $wp_fs_destination_dir .'/'. $file['name'];
|
454 |
-
|
455 |
-
if ($merge_extensions) {
|
456 |
-
if ($file['name'] === 'extensions' && $file['type'] === 'd') {
|
457 |
-
// merge extensions/ after all other files was moved
|
458 |
-
$merge_extensions_data = array(
|
459 |
-
'source' => $downloaded_file_path,
|
460 |
-
'destination' => $destination_file_path,
|
461 |
-
);
|
462 |
-
continue;
|
463 |
-
}
|
464 |
-
}
|
465 |
-
|
466 |
-
if (!$wp_filesystem->move($downloaded_file_path, $destination_file_path)) {
|
467 |
-
$error = sprintf(
|
468 |
-
__('Cannot move "%s" to "%s"', 'fw'),
|
469 |
-
$downloaded_file_path, $destination_file_path
|
470 |
-
);
|
471 |
-
break 2;
|
472 |
-
}
|
473 |
-
}
|
474 |
-
|
475 |
-
if ($merge_extensions) {
|
476 |
-
if (!empty($merge_extensions_data)) {
|
477 |
-
$merge_result = $this->merge_extensions(
|
478 |
-
$merge_extensions_data['source'],
|
479 |
-
$merge_extensions_data['destination']
|
480 |
-
);
|
481 |
-
|
482 |
-
if ($merge_result === false) {
|
483 |
-
$error = sprintf(
|
484 |
-
__('Cannot merge "%s" with "%s"', 'fw'),
|
485 |
-
$downloaded_file_path, $destination_file_path
|
486 |
-
);
|
487 |
-
break;
|
488 |
-
} elseif (is_wp_error($merge_result)) {
|
489 |
-
$error = $merge_result;
|
490 |
-
break;
|
491 |
-
}
|
492 |
-
}
|
493 |
-
}
|
494 |
-
}
|
495 |
-
}
|
496 |
-
|
497 |
-
$skin->feedback(sprintf(__('The %s has been successfully updated.', 'fw'), $title));
|
498 |
-
} while(false);
|
499 |
-
|
500 |
-
$this->maintenance_mode(false);
|
501 |
-
|
502 |
-
if ($wp_filesystem->exists($tmp_download_dir)) {
|
503 |
-
if ( ! $wp_filesystem->delete( $tmp_download_dir, true, 'd' ) ) {
|
504 |
-
$error = sprintf( __( 'Cannot remove temporary directory "%s".', 'fw' ), $tmp_download_dir );
|
505 |
-
}
|
506 |
-
}
|
507 |
-
|
508 |
-
if ($error) {
|
509 |
-
if (!is_wp_error($error)) {
|
510 |
-
$error = new WP_Error( 'fw_ext_update_failed', (string)$error );
|
511 |
-
}
|
512 |
-
|
513 |
-
return $error;
|
514 |
-
}
|
515 |
-
}
|
516 |
-
|
517 |
-
/**
|
518 |
-
* Merge two extensions/ directories
|
519 |
-
* @param string $source_dir WP_Filesystem dir '/a/b/c/extensions'
|
520 |
-
* @param string $destination_dir WP_Filesystem dir '/a/b/d/extensions'
|
521 |
-
* @return bool|WP_Error
|
522 |
-
*/
|
523 |
-
private function merge_extensions($source_dir, $destination_dir)
|
524 |
-
{
|
525 |
-
/** @var WP_Filesystem_Base $wp_filesystem */
|
526 |
-
global $wp_filesystem;
|
527 |
-
|
528 |
-
$wp_error_id = 'fw_ext_update_merge_extensions';
|
529 |
-
|
530 |
-
if (!$wp_filesystem->exists($destination_dir)) {
|
531 |
-
// do a simple move if destination does not exist
|
532 |
-
if (!$wp_filesystem->move($source_dir, $destination_dir)) {
|
533 |
-
return new WP_Error($wp_error_id,
|
534 |
-
sprintf(__('Cannot move "%s" to "%s"', 'fw'), $source_dir, $destination_dir)
|
535 |
-
);
|
536 |
-
}
|
537 |
-
return true;
|
538 |
-
}
|
539 |
-
|
540 |
-
$source_ext_dirs = $wp_filesystem->dirlist($source_dir, true);
|
541 |
-
if ($source_ext_dirs === false) {
|
542 |
-
return new WP_Error($wp_error_id,
|
543 |
-
__('Cannot access directory: ', 'fw') . $source_dir
|
544 |
-
);
|
545 |
-
}
|
546 |
-
|
547 |
-
foreach ($source_ext_dirs as $ext_dir) {
|
548 |
-
if (in_array($ext_dir['name'], $this->skip_file_names)) {
|
549 |
-
continue;
|
550 |
-
}
|
551 |
-
|
552 |
-
if ($ext_dir['type'] !== 'd') {
|
553 |
-
// process only directories from the extensions/ directory
|
554 |
-
continue;
|
555 |
-
}
|
556 |
-
|
557 |
-
$source_extension_dir = $source_dir .'/'. $ext_dir['name'];
|
558 |
-
$destination_extension_dir = $destination_dir .'/'. $ext_dir['name'];
|
559 |
-
|
560 |
-
{
|
561 |
-
$source_ext_files = $wp_filesystem->dirlist($source_extension_dir, true);
|
562 |
-
if ($source_ext_files === false) {
|
563 |
-
return new WP_Error($wp_error_id,
|
564 |
-
__('Cannot access directory: ', 'fw') . $source_extension_dir
|
565 |
-
);
|
566 |
-
}
|
567 |
-
|
568 |
-
if (empty($source_ext_files)) {
|
569 |
-
/**
|
570 |
-
* Source extension directory is empty, do nothing.
|
571 |
-
* This happens when the extension is a git submodule in repository
|
572 |
-
* but in zip it comes as an empty directory.
|
573 |
-
*/
|
574 |
-
continue;
|
575 |
-
}
|
576 |
-
}
|
577 |
-
|
578 |
-
// prepare destination
|
579 |
-
{
|
580 |
-
// create if not exists
|
581 |
-
if (!$wp_filesystem->exists($destination_extension_dir)) {
|
582 |
-
if (!FW_WP_Filesystem::mkdir_recursive($destination_extension_dir)) {
|
583 |
-
return new WP_Error($wp_error_id,
|
584 |
-
__('Cannot create directory: ', 'fw') . $destination_extension_dir
|
585 |
-
);
|
586 |
-
}
|
587 |
-
}
|
588 |
-
|
589 |
-
// remove everything except the extensions/ dir
|
590 |
-
{
|
591 |
-
$dest_ext_files = $wp_filesystem->dirlist($destination_extension_dir, true);
|
592 |
-
if ($dest_ext_files === false) {
|
593 |
-
return new WP_Error($wp_error_id,
|
594 |
-
__('Cannot access directory: ', 'fw') . $destination_extension_dir
|
595 |
-
);
|
596 |
-
}
|
597 |
-
|
598 |
-
$destination_has_extensions_dir = false;
|
599 |
-
|
600 |
-
foreach ($dest_ext_files as $dest_ext_file) {
|
601 |
-
if (in_array($dest_ext_file['name'], $this->skip_file_names)) {
|
602 |
-
continue;
|
603 |
-
}
|
604 |
-
|
605 |
-
if ($dest_ext_file['name'] === 'extensions' && $dest_ext_file['type'] === 'd') {
|
606 |
-
$destination_has_extensions_dir = true;
|
607 |
-
continue;
|
608 |
-
}
|
609 |
-
|
610 |
-
$dest_ext_file_path = $destination_extension_dir .'/'. $dest_ext_file['name'];
|
611 |
-
|
612 |
-
if (!$wp_filesystem->delete($dest_ext_file_path, true, $dest_ext_file['type'])) {
|
613 |
-
return new WP_Error($wp_error_id,
|
614 |
-
__('Cannot delete: ', 'fw') . $dest_ext_file_path
|
615 |
-
);
|
616 |
-
}
|
617 |
-
}
|
618 |
-
}
|
619 |
-
}
|
620 |
-
|
621 |
-
// move files from source to destination extension directory
|
622 |
-
{
|
623 |
-
$source_has_extensions_dir = false;
|
624 |
-
|
625 |
-
foreach ($source_ext_files as $source_ext_file) {
|
626 |
-
if (in_array($source_ext_file['name'], $this->skip_file_names)) {
|
627 |
-
continue;
|
628 |
-
}
|
629 |
-
|
630 |
-
if ($source_ext_file['name'] === 'extensions' && $source_ext_file['type'] === 'd') {
|
631 |
-
$source_has_extensions_dir = true;
|
632 |
-
continue;
|
633 |
-
}
|
634 |
-
|
635 |
-
$source_ext_file_path = $source_extension_dir .'/'. $source_ext_file['name'];
|
636 |
-
$dest_ext_file_path = $destination_extension_dir .'/'. $source_ext_file['name'];
|
637 |
-
|
638 |
-
if (!$wp_filesystem->move($source_ext_file_path, $dest_ext_file_path)) {
|
639 |
-
return new WP_Error($wp_error_id,
|
640 |
-
sprintf(__('Cannot move "%s" to "%s"', 'fw'),
|
641 |
-
$source_ext_file_path, $dest_ext_file_path
|
642 |
-
)
|
643 |
-
);
|
644 |
-
}
|
645 |
-
}
|
646 |
-
}
|
647 |
-
|
648 |
-
if ($source_has_extensions_dir) {
|
649 |
-
if ($destination_has_extensions_dir) {
|
650 |
-
$merge_result = $this->merge_extensions(
|
651 |
-
$source_extension_dir .'/extensions',
|
652 |
-
$destination_extension_dir .'/extensions'
|
653 |
-
);
|
654 |
-
|
655 |
-
if ($merge_result !== true) {
|
656 |
-
return $merge_result;
|
657 |
-
}
|
658 |
-
} else {
|
659 |
-
if (!$wp_filesystem->move(
|
660 |
-
$source_extension_dir .'/extensions',
|
661 |
-
$destination_extension_dir .'/extensions'
|
662 |
-
)) {
|
663 |
-
return new WP_Error($wp_error_id,
|
664 |
-
sprintf(__('Cannot move "%s" to "%s"', 'fw'),
|
665 |
-
$source_extension_dir .'/extensions',
|
666 |
-
$destination_extension_dir .'/extensions'
|
667 |
-
)
|
668 |
-
);
|
669 |
-
}
|
670 |
-
}
|
671 |
-
}
|
672 |
-
}
|
673 |
-
|
674 |
-
return true;
|
675 |
-
}
|
676 |
-
|
677 |
-
/**
|
678 |
-
* @internal
|
679 |
-
*/
|
680 |
-
public function _action_update_framework()
|
681 |
-
{
|
682 |
-
$nonce_name = '_nonce_fw_ext_update_framework';
|
683 |
-
if (!isset($_POST[$nonce_name]) || !wp_verify_nonce($_POST[$nonce_name])) {
|
684 |
-
wp_die(__('Invalid nonce.', 'fw'));
|
685 |
-
}
|
686 |
-
|
687 |
-
{
|
688 |
-
if (!class_exists('_FW_Ext_Update_Framework_Upgrader_Skin')) {
|
689 |
-
fw_include_file_isolated(
|
690 |
-
$this->get_declared_path('/includes/classes/class--fw-ext-update-framework-upgrader-skin.php')
|
691 |
-
);
|
692 |
-
}
|
693 |
-
|
694 |
-
$skin = new _FW_Ext_Update_Framework_Upgrader_Skin(array(
|
695 |
-
'title' => __('Framework Update', 'fw'),
|
696 |
-
));
|
697 |
-
}
|
698 |
-
|
699 |
-
require_once ABSPATH .'wp-admin/admin-header.php';
|
700 |
-
|
701 |
-
$skin->header();
|
702 |
-
|
703 |
-
do {
|
704 |
-
if (!FW_WP_Filesystem::request_access(fw_get_framework_directory(), fw_current_url(), array($nonce_name))) {
|
705 |
-
break;
|
706 |
-
}
|
707 |
-
|
708 |
-
$update = $this->get_framework_update();
|
709 |
-
|
710 |
-
if ($update === false) {
|
711 |
-
$skin->error(__('Failed to get framework latest version.', 'fw'));
|
712 |
-
break;
|
713 |
-
} elseif (is_wp_error($update)) {
|
714 |
-
$skin->error($update);
|
715 |
-
break;
|
716 |
-
}
|
717 |
-
|
718 |
-
/** @var FW_Ext_Update_Service $service */
|
719 |
-
$service = $this->get_child($update['service']);
|
720 |
-
|
721 |
-
$update_result = $this->update(array(
|
722 |
-
'wp_fs_destination_dir' => FW_WP_Filesystem::real_path_to_filesystem_path(
|
723 |
-
fw_get_framework_directory()
|
724 |
-
),
|
725 |
-
'download_callback' => array($service, '_download_framework'),
|
726 |
-
'download_callback_args' => array($update['latest_version'], $this->get_wp_fs_tmp_dir()),
|
727 |
-
'skin' => $skin,
|
728 |
-
'title' => __('Framework', 'fw'),
|
729 |
-
));
|
730 |
-
|
731 |
-
if (is_wp_error($update_result)) {
|
732 |
-
$skin->error($update_result);
|
733 |
-
break;
|
734 |
-
}
|
735 |
-
|
736 |
-
$skin->set_result(true);
|
737 |
-
$skin->after();
|
738 |
-
} while(false);
|
739 |
-
|
740 |
-
$skin->footer();
|
741 |
-
|
742 |
-
require_once(ABSPATH . 'wp-admin/admin-footer.php');
|
743 |
-
}
|
744 |
-
|
745 |
-
/**
|
746 |
-
* @internal
|
747 |
-
*/
|
748 |
-
public function _action_update_theme()
|
749 |
-
{
|
750 |
-
$nonce_name = '_nonce_fw_ext_update_theme';
|
751 |
-
if (!isset($_POST[$nonce_name]) || !wp_verify_nonce($_POST[$nonce_name])) {
|
752 |
-
wp_die(__('Invalid nonce.', 'fw'));
|
753 |
-
}
|
754 |
-
|
755 |
-
{
|
756 |
-
if (!class_exists('_FW_Ext_Update_Theme_Upgrader_Skin')) {
|
757 |
-
fw_include_file_isolated(
|
758 |
-
$this->get_declared_path('/includes/classes/class--fw-ext-update-theme-upgrader-skin.php')
|
759 |
-
);
|
760 |
-
}
|
761 |
-
|
762 |
-
$skin = new _FW_Ext_Update_Theme_Upgrader_Skin(array(
|
763 |
-
'title' => __('Theme Update', 'fw'),
|
764 |
-
));
|
765 |
-
}
|
766 |
-
|
767 |
-
require_once(ABSPATH . 'wp-admin/admin-header.php');
|
768 |
-
|
769 |
-
$skin->header();
|
770 |
-
|
771 |
-
do {
|
772 |
-
if (!FW_WP_Filesystem::request_access(get_template_directory(), fw_current_url(), array($nonce_name))) {
|
773 |
-
break;
|
774 |
-
}
|
775 |
-
|
776 |
-
$update = $this->get_theme_update();
|
777 |
-
|
778 |
-
if ($update === false) {
|
779 |
-
$skin->error(__('Failed to get theme latest version.', 'fw'));
|
780 |
-
break;
|
781 |
-
} elseif (is_wp_error($update)) {
|
782 |
-
$skin->error($update);
|
783 |
-
break;
|
784 |
-
}
|
785 |
-
|
786 |
-
/** @var FW_Ext_Update_Service $service */
|
787 |
-
$service = $this->get_child($update['service']);
|
788 |
-
|
789 |
-
$update_result = $this->update(array(
|
790 |
-
'wp_fs_destination_dir' => FW_WP_Filesystem::real_path_to_filesystem_path(
|
791 |
-
get_template_directory()
|
792 |
-
),
|
793 |
-
'download_callback' => array($service, '_download_theme'),
|
794 |
-
'download_callback_args' => array($update['latest_version'], $this->get_wp_fs_tmp_dir()),
|
795 |
-
'skin' => $skin,
|
796 |
-
'title' => __('Theme', 'fw'),
|
797 |
-
));
|
798 |
-
|
799 |
-
if (is_wp_error($update_result)) {
|
800 |
-
$skin->error($update_result);
|
801 |
-
break;
|
802 |
-
}
|
803 |
-
|
804 |
-
$skin->set_result(true);
|
805 |
-
$skin->after();
|
806 |
-
} while(false);
|
807 |
-
|
808 |
-
$skin->footer();
|
809 |
-
|
810 |
-
require_once(ABSPATH . 'wp-admin/admin-footer.php');
|
811 |
-
}
|
812 |
-
|
813 |
-
/**
|
814 |
-
* @internal
|
815 |
-
*/
|
816 |
-
public function _action_update_extensions()
|
817 |
-
{
|
818 |
-
$nonce_name = '_nonce_fw_ext_update_extensions';
|
819 |
-
if (!isset($_POST[$nonce_name]) || !wp_verify_nonce($_POST[$nonce_name])) {
|
820 |
-
wp_die(__('Invalid nonce.', 'fw'));
|
821 |
-
}
|
822 |
-
|
823 |
-
$form_input_name = 'extensions';
|
824 |
-
$extensions_list = FW_Request::POST($form_input_name);
|
825 |
-
|
826 |
-
if (empty($extensions_list)) {
|
827 |
-
FW_Flash_Messages::add(
|
828 |
-
'fw_ext_update',
|
829 |
-
__('Please check the extensions you want to update.', 'fw'),
|
830 |
-
'warning'
|
831 |
-
);
|
832 |
-
wp_redirect(self_admin_url('update-core.php'));
|
833 |
-
exit;
|
834 |
-
}
|
835 |
-
|
836 |
-
// handle changes by the hack below
|
837 |
-
{
|
838 |
-
if (is_string($extensions_list)) {
|
839 |
-
$extensions_list = json_decode($extensions_list);
|
840 |
-
} else {
|
841 |
-
$extensions_list = array_keys($extensions_list);
|
842 |
-
}
|
843 |
-
}
|
844 |
-
|
845 |
-
{
|
846 |
-
if (!class_exists('_FW_Ext_Update_Extensions_Upgrader_Skin')) {
|
847 |
-
fw_include_file_isolated(
|
848 |
-
$this->get_declared_path('/includes/classes/class--fw-ext-update-extensions-upgrader-skin.php')
|
849 |
-
);
|
850 |
-
}
|
851 |
-
|
852 |
-
$skin = new _FW_Ext_Update_Extensions_Upgrader_Skin(array(
|
853 |
-
'title' => __('Extensions Update', 'fw'),
|
854 |
-
));
|
855 |
-
}
|
856 |
-
|
857 |
-
require_once(ABSPATH . 'wp-admin/admin-header.php');
|
858 |
-
|
859 |
-
$skin->header();
|
860 |
-
|
861 |
-
do {
|
862 |
-
/**
|
863 |
-
* Hack for the ftp credentials template that does not support array post values
|
864 |
-
* https://github.com/WordPress/WordPress/blob/3949a8b6cc50a021ed93798287b4ef9ea8a560d9/wp-admin/includes/file.php#L1144
|
865 |
-
*/
|
866 |
-
{
|
867 |
-
$original_post_value = $_POST[$form_input_name];
|
868 |
-
$_POST[$form_input_name] = wp_slash(json_encode($extensions_list));
|
869 |
-
}
|
870 |
-
|
871 |
-
if (!FW_WP_Filesystem::request_access(
|
872 |
-
fw_get_framework_directory('/extensions'),
|
873 |
-
fw_current_url(),
|
874 |
-
array($nonce_name, $form_input_name))
|
875 |
-
) {
|
876 |
-
{ // revert hack changes
|
877 |
-
$_POST[$form_input_name] = $original_post_value;
|
878 |
-
unset($original_post_value);
|
879 |
-
}
|
880 |
-
break;
|
881 |
-
}
|
882 |
-
|
883 |
-
{ // revert hack changes
|
884 |
-
$_POST[$form_input_name] = $original_post_value;
|
885 |
-
unset($original_post_value);
|
886 |
-
}
|
887 |
-
|
888 |
-
$updates = $this->get_extensions_with_updates();
|
889 |
-
|
890 |
-
if (empty($updates)) {
|
891 |
-
$skin->error(__('No extensions updates found.', 'fw'));
|
892 |
-
break;
|
893 |
-
}
|
894 |
-
|
895 |
-
foreach ($extensions_list as $extension_name) {
|
896 |
-
if (!($extension = fw()->extensions->get($extension_name))) {
|
897 |
-
$skin->error(
|
898 |
-
sprintf(__('Extension "%s" does not exist or is disabled.', 'fw'), $extension_name)
|
899 |
-
);
|
900 |
-
continue;
|
901 |
-
}
|
902 |
-
|
903 |
-
if (!isset($updates[$extension_name])) {
|
904 |
-
$skin->error(
|
905 |
-
sprintf(__('No update found for the "%s" extension.', 'fw'), $extension->manifest->get_name())
|
906 |
-
);
|
907 |
-
continue;
|
908 |
-
}
|
909 |
-
|
910 |
-
$update = $updates[$extension_name];
|
911 |
-
|
912 |
-
if (is_wp_error($update)) {
|
913 |
-
$skin->error($update);
|
914 |
-
continue;
|
915 |
-
}
|
916 |
-
|
917 |
-
/** @var FW_Ext_Update_Service $service */
|
918 |
-
$service = $this->get_child($update['service']);
|
919 |
-
|
920 |
-
$update_result = $this->update(array(
|
921 |
-
'wp_fs_destination_dir' => FW_WP_Filesystem::real_path_to_filesystem_path(
|
922 |
-
$extension->get_declared_path()
|
923 |
-
),
|
924 |
-
'download_callback' => array($service, '_download_extension'),
|
925 |
-
'download_callback_args' => array($extension, $update['latest_version'], $this->get_wp_fs_tmp_dir()),
|
926 |
-
'skin' => $skin,
|
927 |
-
'title' => sprintf(__('%s extension', 'fw'), $extension->manifest->get_name()),
|
928 |
-
), true);
|
929 |
-
|
930 |
-
if (is_wp_error($update_result)) {
|
931 |
-
$skin->error($update_result);
|
932 |
-
continue;
|
933 |
-
}
|
934 |
-
|
935 |
-
$skin->set_result(true);
|
936 |
-
|
937 |
-
if (!$this->get_config('extensions_as_one_update')) {
|
938 |
-
$skin->decrement_extension_update_count( $extension_name );
|
939 |
-
}
|
940 |
-
}
|
941 |
-
|
942 |
-
if ($this->get_config('extensions_as_one_update')) {
|
943 |
-
$skin->decrement_extension_update_count( $extension_name );
|
944 |
-
}
|
945 |
-
|
946 |
-
$skin->after();
|
947 |
-
} while(false);
|
948 |
-
|
949 |
-
$skin->footer();
|
950 |
-
|
951 |
-
require_once(ABSPATH . 'wp-admin/admin-footer.php');
|
952 |
-
}
|
953 |
-
|
954 |
-
public function _action_admin_notices() {
|
955 |
-
if (
|
956 |
-
get_current_screen()->parent_base === fw()->extensions->manager->get_page_slug()
|
957 |
-
&&
|
958 |
-
($updates = $this->get_updates())
|
959 |
-
&&
|
960 |
-
!empty($updates['extensions'])
|
961 |
-
) { /* ok */ } else {
|
962 |
-
return;
|
963 |
-
}
|
964 |
-
|
965 |
-
foreach ($updates['extensions'] as $ext_name => $ext_update) {
|
966 |
-
if ( is_wp_error( $ext_update ) ) {
|
967 |
-
return;
|
968 |
-
}
|
969 |
-
|
970 |
-
break;
|
971 |
-
}
|
972 |
-
|
973 |
-
echo '<div class="notice notice-warning"><p>'
|
974 |
-
. sprintf(
|
975 |
-
esc_html__('New extensions updates available. %s', 'fw'),
|
976 |
-
fw_html_tag('a', array(
|
977 |
-
'href' => self_admin_url('update-core.php') .'#fw-ext-update-extensions',
|
978 |
-
), esc_html__('Go to Updates page', 'fw'))
|
979 |
-
)
|
980 |
-
. '</p></div>';
|
981 |
-
}
|
982 |
-
}
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
|
3 |
+
require dirname(__FILE__) .'/includes/extends/class-fw-ext-update-service.php';
|
4 |
+
|
5 |
+
class FW_Extension_Update extends FW_Extension
|
6 |
+
{
|
7 |
+
/**
|
8 |
+
* {@inheritdoc}
|
9 |
+
*/
|
10 |
+
public function _child_extension_is_valid($child_extension_instance)
|
11 |
+
{
|
12 |
+
return is_subclass_of($child_extension_instance, 'FW_Ext_Update_Service');
|
13 |
+
}
|
14 |
+
|
15 |
+
/**
|
16 |
+
* File names to skip (do not delete or change) during the update process
|
17 |
+
* @var array
|
18 |
+
*/
|
19 |
+
private $skip_file_names = array('.git');
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @internal
|
23 |
+
*/
|
24 |
+
protected function _init()
|
25 |
+
{
|
26 |
+
{
|
27 |
+
$has_access = (current_user_can('update_themes') || current_user_can('update_plugins'));
|
28 |
+
|
29 |
+
if ($has_access) {
|
30 |
+
if (is_multisite() && !is_network_admin()) {
|
31 |
+
// only network admin can change files that affects the entire network
|
32 |
+
$has_access = false;
|
33 |
+
}
|
34 |
+
}
|
35 |
+
|
36 |
+
if (!$has_access) {
|
37 |
+
return false; // prevent child extensions activation
|
38 |
+
}
|
39 |
+
}
|
40 |
+
|
41 |
+
$this->add_actions();
|
42 |
+
$this->add_filters();
|
43 |
+
}
|
44 |
+
|
45 |
+
private function add_actions()
|
46 |
+
{
|
47 |
+
add_action('core_upgrade_preamble', array($this, '_action_updates_page_footer'));
|
48 |
+
|
49 |
+
add_action('update-core-custom_'. 'fw-update-framework', array($this, '_action_update_framework'));
|
50 |
+
add_action('update-core-custom_'. 'fw-update-theme', array($this, '_action_update_theme'));
|
51 |
+
add_action('update-core-custom_'. 'fw-update-extensions', array($this, '_action_update_extensions'));
|
52 |
+
|
53 |
+
add_action('admin_notices', array($this, '_action_admin_notices'));
|
54 |
+
}
|
55 |
+
|
56 |
+
private function add_filters()
|
57 |
+
{
|
58 |
+
add_filter('wp_get_update_data', array($this, '_filter_update_data'), 10, 2);
|
59 |
+
}
|
60 |
+
|
61 |
+
private function get_fixed_version($version)
|
62 |
+
{
|
63 |
+
// remove from the beginning everything that is not a number: 'v1.2.3' -> '1.2.3', 'ver1.0.0' -> '1.0.0'
|
64 |
+
return preg_replace('/^[^0-9]+/i', '', $version);;
|
65 |
+
}
|
66 |
+
|
67 |
+
private function get_wp_fs_tmp_dir()
|
68 |
+
{
|
69 |
+
return FW_WP_Filesystem::real_path_to_filesystem_path(
|
70 |
+
apply_filters('fw_tmp_dir', fw_fix_path(WP_CONTENT_DIR) .'/tmp')
|
71 |
+
);
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* @internal
|
76 |
+
*/
|
77 |
+
public function _action_updates_page_footer()
|
78 |
+
{
|
79 |
+
echo $this->render_view('updates-list', array(
|
80 |
+
'updates' => $this->get_updates(!empty($_GET['force-check']))
|
81 |
+
));
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* @internal
|
86 |
+
*/
|
87 |
+
public function _filter_update_data($data, $titles)
|
88 |
+
{
|
89 |
+
$updates = $this->get_updates(!empty($_GET['force-check']));
|
90 |
+
|
91 |
+
if ($updates['framework'] && !is_wp_error($updates['framework'])) {
|
92 |
+
++$data['counts']['total'];
|
93 |
+
}
|
94 |
+
|
95 |
+
if ($updates['theme'] && !is_wp_error($updates['theme'])) {
|
96 |
+
++$data['counts']['total'];
|
97 |
+
}
|
98 |
+
|
99 |
+
if (!empty($updates['extensions'])) {
|
100 |
+
foreach ( $updates['extensions'] as $ext_name => $ext_update ) {
|
101 |
+
if ( is_wp_error( $ext_update ) ) {
|
102 |
+
continue;
|
103 |
+
}
|
104 |
+
|
105 |
+
++$data['counts']['total'];
|
106 |
+
|
107 |
+
if ($this->get_config('extensions_as_one_update')) {
|
108 |
+
// no matter how many extensions, display as one update
|
109 |
+
break;
|
110 |
+
}
|
111 |
+
}
|
112 |
+
}
|
113 |
+
|
114 |
+
return $data;
|
115 |
+
}
|
116 |
+
|
117 |
+
private function get_updates($force_check = false)
|
118 |
+
{
|
119 |
+
$cache_key = 'fw_ext_update/updates';
|
120 |
+
|
121 |
+
// use cache because this method may be called multiple times (to prevent useless requests to update servers)
|
122 |
+
|
123 |
+
try {
|
124 |
+
return FW_Cache::get($cache_key);
|
125 |
+
} catch (FW_Cache_Not_Found_Exception $e) {
|
126 |
+
$updates = array(
|
127 |
+
'framework' => $this->get_framework_update($force_check),
|
128 |
+
'theme' => $this->get_theme_update($force_check),
|
129 |
+
'extensions' => $this->get_extensions_with_updates($force_check)
|
130 |
+
);
|
131 |
+
|
132 |
+
FW_Cache::set($cache_key, $updates);
|
133 |
+
|
134 |
+
return $updates;
|
135 |
+
}
|
136 |
+
}
|
137 |
+
|
138 |
+
/**
|
139 |
+
* Collect extensions that has new versions available
|
140 |
+
* @param bool $force_check
|
141 |
+
* @return array {ext_name => update_data}
|
142 |
+
*/
|
143 |
+
private function get_extensions_with_updates($force_check = false)
|
144 |
+
{
|
145 |
+
$updates = array();
|
146 |
+
$services = $this->get_children();
|
147 |
+
$theme_ext_requirements = fw()->theme->manifest->get('requirements/extensions');
|
148 |
+
|
149 |
+
foreach (fw()->extensions->get_all() as $ext_name => $extension) {
|
150 |
+
/** @var FW_Extension $extension */
|
151 |
+
|
152 |
+
/**
|
153 |
+
* Ask each service if it knows how to update the extension
|
154 |
+
*/
|
155 |
+
foreach ($services as $service_name => $service) {
|
156 |
+
/** @var $service FW_Ext_Update_Service */
|
157 |
+
|
158 |
+
$latest_version = $service->_get_extension_latest_version($extension, $force_check);
|
159 |
+
|
160 |
+
if ($latest_version === false) {
|
161 |
+
// It said that it doesn't know how to update it
|
162 |
+
continue;
|
163 |
+
}
|
164 |
+
|
165 |
+
if (is_wp_error($latest_version)) {
|
166 |
+
$updates[$ext_name] = $latest_version;
|
167 |
+
break;
|
168 |
+
}
|
169 |
+
|
170 |
+
$fixed_latest_version = $this->get_fixed_version($latest_version);
|
171 |
+
|
172 |
+
if (!version_compare($fixed_latest_version, $extension->manifest->get_version(), '>')) {
|
173 |
+
// we already have latest version
|
174 |
+
continue;
|
175 |
+
}
|
176 |
+
|
177 |
+
if (
|
178 |
+
isset($theme_ext_requirements[$ext_name])
|
179 |
+
&&
|
180 |
+
isset($theme_ext_requirements[$ext_name]['max_version'])
|
181 |
+
&&
|
182 |
+
version_compare($fixed_latest_version, $theme_ext_requirements[$ext_name]['max_version'], '>')
|
183 |
+
) {
|
184 |
+
continue; // do not allow update if it exceeds max_version
|
185 |
+
}
|
186 |
+
|
187 |
+
$updates[$ext_name] = array(
|
188 |
+
'service' => $service_name,
|
189 |
+
'latest_version' => $latest_version,
|
190 |
+
'fixed_latest_version' => $fixed_latest_version
|
191 |
+
);
|
192 |
+
|
193 |
+
break;
|
194 |
+
}
|
195 |
+
}
|
196 |
+
|
197 |
+
return $updates;
|
198 |
+
}
|
199 |
+
|
200 |
+
/**
|
201 |
+
* @param bool $force_check
|
202 |
+
* @return array|false|WP_Error
|
203 |
+
*/
|
204 |
+
private function get_framework_update($force_check = false)
|
205 |
+
{
|
206 |
+
/**
|
207 |
+
* Ask each service if it knows how to update the framework
|
208 |
+
*/
|
209 |
+
foreach ($this->get_children() as $service_name => $service) {
|
210 |
+
/** @var $service FW_Ext_Update_Service */
|
211 |
+
|
212 |
+
$latest_version = $service->_get_framework_latest_version($force_check);
|
213 |
+
|
214 |
+
if ($latest_version === false) {
|
215 |
+
// It said that it doesn't know how to update it
|
216 |
+
continue;
|
217 |
+
}
|
218 |
+
|
219 |
+
if (is_wp_error($latest_version)) {
|
220 |
+
return $latest_version;
|
221 |
+
}
|
222 |
+
|
223 |
+
$fixed_latest_version = $this->get_fixed_version($latest_version);
|
224 |
+
|
225 |
+
if (!version_compare($fixed_latest_version, fw()->manifest->get_version(), '>')) {
|
226 |
+
// we already have latest version
|
227 |
+
continue;
|
228 |
+
}
|
229 |
+
|
230 |
+
return array(
|
231 |
+
'service' => $service_name,
|
232 |
+
'latest_version' => $latest_version,
|
233 |
+
'fixed_latest_version' => $fixed_latest_version
|
234 |
+
);
|
235 |
+
}
|
236 |
+
|
237 |
+
return false;
|
238 |
+
}
|
239 |
+
|
240 |
+
/**
|
241 |
+
* @param bool $force_check
|
242 |
+
* @return array|false|WP_Error
|
243 |
+
*/
|
244 |
+
private function get_theme_update($force_check = false)
|
245 |
+
{
|
246 |
+
/**
|
247 |
+
* Ask each service if it knows how to update the theme
|
248 |
+
*/
|
249 |
+
foreach ($this->get_children() as $service_name => $service) {
|
250 |
+
/** @var $service FW_Ext_Update_Service */
|
251 |
+
|
252 |
+
$latest_version = $service->_get_theme_latest_version($force_check);
|
253 |
+
|
254 |
+
if ($latest_version === false) {
|
255 |
+
// It said that it doesn't know how to update it
|
256 |
+
continue;
|
257 |
+
}
|
258 |
+
|
259 |
+
if (is_wp_error($latest_version)) {
|
260 |
+
return $latest_version;
|
261 |
+
}
|
262 |
+
|
263 |
+
$fixed_latest_version = $this->get_fixed_version($latest_version);
|
264 |
+
|
265 |
+
if (!version_compare($fixed_latest_version, fw()->theme->manifest->get_version(), '>')) {
|
266 |
+
// we already have latest version
|
267 |
+
continue;
|
268 |
+
}
|
269 |
+
|
270 |
+
return array(
|
271 |
+
'service' => $service_name,
|
272 |
+
'latest_version' => $latest_version,
|
273 |
+
'fixed_latest_version' => $fixed_latest_version
|
274 |
+
);
|
275 |
+
}
|
276 |
+
|
277 |
+
return false;
|
278 |
+
}
|
279 |
+
|
280 |
+
/**
|
281 |
+
* Turn on/off the maintenance mode
|
282 |
+
* @param bool $enable
|
283 |
+
*/
|
284 |
+
private function maintenance_mode($enable = false)
|
285 |
+
{
|
286 |
+
/** @var WP_Filesystem_Base $wp_filesystem */
|
287 |
+
global $wp_filesystem;
|
288 |
+
|
289 |
+
if (!$wp_filesystem || (is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code())) {
|
290 |
+
return;
|
291 |
+
}
|
292 |
+
|
293 |
+
$file_path = $wp_filesystem->abspath() . '.maintenance';
|
294 |
+
|
295 |
+
if ($wp_filesystem->exists($file_path)) {
|
296 |
+
if (!$wp_filesystem->delete($file_path)) {
|
297 |
+
trigger_error(__('Cannot delete: ', 'fw') . $file_path, E_USER_WARNING);
|
298 |
+
}
|
299 |
+
}
|
300 |
+
|
301 |
+
if ($enable) {
|
302 |
+
// Create maintenance file to signal that we are upgrading
|
303 |
+
if (!$wp_filesystem->put_contents($file_path, '<?php $upgrading = ' . time() . '; ?>', FS_CHMOD_FILE)) {
|
304 |
+
trigger_error(__('Cannot create: ', 'fw') . $file_path, E_USER_WARNING);
|
305 |
+
}
|
306 |
+
}
|
307 |
+
}
|
308 |
+
|
309 |
+
/**
|
310 |
+
* Download and install new version files
|
311 |
+
*
|
312 |
+
* global $wp_filesystem; must be initialized before calling this method
|
313 |
+
*
|
314 |
+
* @param array $data
|
315 |
+
* @param bool $merge_extensions The extensions/ directory will not be replaced entirely,
|
316 |
+
* only extensions that comes with the update will be replaced
|
317 |
+
* @return null|WP_Error
|
318 |
+
*/
|
319 |
+
private function update($data, $merge_extensions = false)
|
320 |
+
{
|
321 |
+
$required_data_keys = array(
|
322 |
+
'wp_fs_destination_dir' => true,
|
323 |
+
'download_callback' => true,
|
324 |
+
'download_callback_args' => true,
|
325 |
+
'skin' => true,
|
326 |
+
'title' => true,
|
327 |
+
);
|
328 |
+
|
329 |
+
if (count($required_data_keys) > count(array_intersect_key($required_data_keys, $data))) {
|
330 |
+
trigger_error('Some required keys are not present', E_USER_ERROR);
|
331 |
+
}
|
332 |
+
|
333 |
+
// move manually every key to variable, so IDE will understand better them
|
334 |
+
{
|
335 |
+
/**
|
336 |
+
* Replace all files in this directory with downloaded
|
337 |
+
* @var string $wp_fs_destination_dir
|
338 |
+
*/
|
339 |
+
$wp_fs_destination_dir = $data['wp_fs_destination_dir'];
|
340 |
+
|
341 |
+
/**
|
342 |
+
* Called to download new version files to $this->get_wp_fs_tmp_dir()
|
343 |
+
* @var callable $download_callback
|
344 |
+
*/
|
345 |
+
$download_callback = $data['download_callback'];
|
346 |
+
|
347 |
+
/**
|
348 |
+
* @var array
|
349 |
+
*/
|
350 |
+
$download_callback_args = $data['download_callback_args'];
|
351 |
+
|
352 |
+
/**
|
353 |
+
* @var WP_Upgrader_Skin $skin
|
354 |
+
*/
|
355 |
+
$skin = $data['skin'];
|
356 |
+
|
357 |
+
/**
|
358 |
+
* Used in text messages
|
359 |
+
* @var string $title
|
360 |
+
*/
|
361 |
+
$title = $data['title'];
|
362 |
+
|
363 |
+
unset($data);
|
364 |
+
}
|
365 |
+
|
366 |
+
/**
|
367 |
+
* @var string|WP_Error
|
368 |
+
*/
|
369 |
+
$error = false;
|
370 |
+
|
371 |
+
$tmp_download_dir = $this->get_wp_fs_tmp_dir();
|
372 |
+
|
373 |
+
do {
|
374 |
+
/** @var WP_Filesystem_Base $wp_filesystem */
|
375 |
+
global $wp_filesystem;
|
376 |
+
|
377 |
+
// create temporary directory
|
378 |
+
{
|
379 |
+
if ($wp_filesystem->exists($tmp_download_dir)) {
|
380 |
+
// just in case it already exists, clear everything, it may contain old files
|
381 |
+
if (!$wp_filesystem->rmdir($tmp_download_dir, true)) {
|
382 |
+
$error = __('Cannot remove old temporary directory: ', 'fw') . $tmp_download_dir;
|
383 |
+
break;
|
384 |
+
}
|
385 |
+
}
|
386 |
+
|
387 |
+
if (!FW_WP_Filesystem::mkdir_recursive($tmp_download_dir)) {
|
388 |
+
$error = __('Cannot create directory: ', 'fw') . $tmp_download_dir;
|
389 |
+
break;
|
390 |
+
}
|
391 |
+
}
|
392 |
+
|
393 |
+
$skin->feedback(sprintf(__('Downloading the %s...', 'fw'), $title));
|
394 |
+
{
|
395 |
+
$downloaded_dir = call_user_func_array($download_callback, $download_callback_args);
|
396 |
+
|
397 |
+
if (!$downloaded_dir) {
|
398 |
+
$error = sprintf(__('Cannot download the %s.', 'fw'), $title);
|
399 |
+
break;
|
400 |
+
} elseif (is_wp_error($downloaded_dir)) {
|
401 |
+
$error = $downloaded_dir;
|
402 |
+
break;
|
403 |
+
}
|
404 |
+
}
|
405 |
+
|
406 |
+
$this->maintenance_mode(true);
|
407 |
+
|
408 |
+
$skin->feedback(sprintf(__('Installing the %s...', 'fw'), $title));
|
409 |
+
{
|
410 |
+
// remove all files from destination directory
|
411 |
+
{
|
412 |
+
$dir_files = $wp_filesystem->dirlist($wp_fs_destination_dir, true);
|
413 |
+
if ($dir_files === false) {
|
414 |
+
$error =__('Cannot access directory: ', 'fw') . $wp_fs_destination_dir;
|
415 |
+
break;
|
416 |
+
}
|
417 |
+
|
418 |
+
foreach ($dir_files as $file) {
|
419 |
+
if (in_array($file['name'], $this->skip_file_names)) {
|
420 |
+
continue;
|
421 |
+
}
|
422 |
+
|
423 |
+
if ($merge_extensions) {
|
424 |
+
if ($file['name'] === 'extensions' && $file['type'] === 'd') {
|
425 |
+
// do not remove extensions, will be merged later
|
426 |
+
continue;
|
427 |
+
}
|
428 |
+
}
|
429 |
+
|
430 |
+
$file_path = $wp_fs_destination_dir .'/'. $file['name'];
|
431 |
+
|
432 |
+
if (!$wp_filesystem->delete($file_path, true, $file['type'])) {
|
433 |
+
$error = __('Cannot remove: ', 'fw') . $file_path;
|
434 |
+
break 2;
|
435 |
+
}
|
436 |
+
}
|
437 |
+
}
|
438 |
+
|
439 |
+
// move all files from the temporary directory to the destination directory
|
440 |
+
{
|
441 |
+
$dir_files = $wp_filesystem->dirlist($downloaded_dir, true);
|
442 |
+
if ($dir_files === false) {
|
443 |
+
$error = __('Cannot access directory: ', 'fw') . $downloaded_dir;
|
444 |
+
break;
|
445 |
+
}
|
446 |
+
|
447 |
+
foreach ($dir_files as $file) {
|
448 |
+
if (in_array($file['name'], $this->skip_file_names)) {
|
449 |
+
continue;
|
450 |
+
}
|
451 |
+
|
452 |
+
$downloaded_file_path = $downloaded_dir .'/'. $file['name'];
|
453 |
+
$destination_file_path = $wp_fs_destination_dir .'/'. $file['name'];
|
454 |
+
|
455 |
+
if ($merge_extensions) {
|
456 |
+
if ($file['name'] === 'extensions' && $file['type'] === 'd') {
|
457 |
+
// merge extensions/ after all other files was moved
|
458 |
+
$merge_extensions_data = array(
|
459 |
+
'source' => $downloaded_file_path,
|
460 |
+
'destination' => $destination_file_path,
|
461 |
+
);
|
462 |
+
continue;
|
463 |
+
}
|
464 |
+
}
|
465 |
+
|
466 |
+
if (!$wp_filesystem->move($downloaded_file_path, $destination_file_path)) {
|
467 |
+
$error = sprintf(
|
468 |
+
__('Cannot move "%s" to "%s"', 'fw'),
|
469 |
+
$downloaded_file_path, $destination_file_path
|
470 |
+
);
|
471 |
+
break 2;
|
472 |
+
}
|
473 |
+
}
|
474 |
+
|
475 |
+
if ($merge_extensions) {
|
476 |
+
if (!empty($merge_extensions_data)) {
|
477 |
+
$merge_result = $this->merge_extensions(
|
478 |
+
$merge_extensions_data['source'],
|
479 |
+
$merge_extensions_data['destination']
|
480 |
+
);
|
481 |
+
|
482 |
+
if ($merge_result === false) {
|
483 |
+
$error = sprintf(
|
484 |
+
__('Cannot merge "%s" with "%s"', 'fw'),
|
485 |
+
$downloaded_file_path, $destination_file_path
|
486 |
+
);
|
487 |
+
break;
|
488 |
+
} elseif (is_wp_error($merge_result)) {
|
489 |
+
$error = $merge_result;
|
490 |
+
break;
|
491 |
+
}
|
492 |
+
}
|
493 |
+
}
|
494 |
+
}
|
495 |
+
}
|
496 |
+
|
497 |
+
$skin->feedback(sprintf(__('The %s has been successfully updated.', 'fw'), $title));
|
498 |
+
} while(false);
|
499 |
+
|
500 |
+
$this->maintenance_mode(false);
|
501 |
+
|
502 |
+
if ($wp_filesystem->exists($tmp_download_dir)) {
|
503 |
+
if ( ! $wp_filesystem->delete( $tmp_download_dir, true, 'd' ) ) {
|
504 |
+
$error = sprintf( __( 'Cannot remove temporary directory "%s".', 'fw' ), $tmp_download_dir );
|
505 |
+
}
|
506 |
+
}
|
507 |
+
|
508 |
+
if ($error) {
|
509 |
+
if (!is_wp_error($error)) {
|
510 |
+
$error = new WP_Error( 'fw_ext_update_failed', (string)$error );
|
511 |
+
}
|
512 |
+
|
513 |
+
return $error;
|
514 |
+
}
|
515 |
+
}
|
516 |
+
|
517 |
+
/**
|
518 |
+
* Merge two extensions/ directories
|
519 |
+
* @param string $source_dir WP_Filesystem dir '/a/b/c/extensions'
|
520 |
+
* @param string $destination_dir WP_Filesystem dir '/a/b/d/extensions'
|
521 |
+
* @return bool|WP_Error
|
522 |
+
*/
|
523 |
+
private function merge_extensions($source_dir, $destination_dir)
|
524 |
+
{
|
525 |
+
/** @var WP_Filesystem_Base $wp_filesystem */
|
526 |
+
global $wp_filesystem;
|
527 |
+
|
528 |
+
$wp_error_id = 'fw_ext_update_merge_extensions';
|
529 |
+
|
530 |
+
if (!$wp_filesystem->exists($destination_dir)) {
|
531 |
+
// do a simple move if destination does not exist
|
532 |
+
if (!$wp_filesystem->move($source_dir, $destination_dir)) {
|
533 |
+
return new WP_Error($wp_error_id,
|
534 |
+
sprintf(__('Cannot move "%s" to "%s"', 'fw'), $source_dir, $destination_dir)
|
535 |
+
);
|
536 |
+
}
|
537 |
+
return true;
|
538 |
+
}
|
539 |
+
|
540 |
+
$source_ext_dirs = $wp_filesystem->dirlist($source_dir, true);
|
541 |
+
if ($source_ext_dirs === false) {
|
542 |
+
return new WP_Error($wp_error_id,
|
543 |
+
__('Cannot access directory: ', 'fw') . $source_dir
|
544 |
+
);
|
545 |
+
}
|
546 |
+
|
547 |
+
foreach ($source_ext_dirs as $ext_dir) {
|
548 |
+
if (in_array($ext_dir['name'], $this->skip_file_names)) {
|
549 |
+
continue;
|
550 |
+
}
|
551 |
+
|
552 |
+
if ($ext_dir['type'] !== 'd') {
|
553 |
+
// process only directories from the extensions/ directory
|
554 |
+
continue;
|
555 |
+
}
|
556 |
+
|
557 |
+
$source_extension_dir = $source_dir .'/'. $ext_dir['name'];
|
558 |
+
$destination_extension_dir = $destination_dir .'/'. $ext_dir['name'];
|
559 |
+
|
560 |
+
{
|
561 |
+
$source_ext_files = $wp_filesystem->dirlist($source_extension_dir, true);
|
562 |
+
if ($source_ext_files === false) {
|
563 |
+
return new WP_Error($wp_error_id,
|
564 |
+
__('Cannot access directory: ', 'fw') . $source_extension_dir
|
565 |
+
);
|
566 |
+
}
|
567 |
+
|
568 |
+
if (empty($source_ext_files)) {
|
569 |
+
/**
|
570 |
+
* Source extension directory is empty, do nothing.
|
571 |
+
* This happens when the extension is a git submodule in repository
|
572 |
+
* but in zip it comes as an empty directory.
|
573 |
+
*/
|
574 |
+
continue;
|
575 |
+
}
|
576 |
+
}
|
577 |
+
|
578 |
+
// prepare destination
|
579 |
+
{
|
580 |
+
// create if not exists
|
581 |
+
if (!$wp_filesystem->exists($destination_extension_dir)) {
|
582 |
+
if (!FW_WP_Filesystem::mkdir_recursive($destination_extension_dir)) {
|
583 |
+
return new WP_Error($wp_error_id,
|
584 |
+
__('Cannot create directory: ', 'fw') . $destination_extension_dir
|
585 |
+
);
|
586 |
+
}
|
587 |
+
}
|
588 |
+
|
589 |
+
// remove everything except the extensions/ dir
|
590 |
+
{
|
591 |
+
$dest_ext_files = $wp_filesystem->dirlist($destination_extension_dir, true);
|
592 |
+
if ($dest_ext_files === false) {
|
593 |
+
return new WP_Error($wp_error_id,
|
594 |
+
__('Cannot access directory: ', 'fw') . $destination_extension_dir
|
595 |
+
);
|
596 |
+
}
|
597 |
+
|
598 |
+
$destination_has_extensions_dir = false;
|
599 |
+
|
600 |
+
foreach ($dest_ext_files as $dest_ext_file) {
|
601 |
+
if (in_array($dest_ext_file['name'], $this->skip_file_names)) {
|
602 |
+
continue;
|
603 |
+
}
|
604 |
+
|
605 |
+
if ($dest_ext_file['name'] === 'extensions' && $dest_ext_file['type'] === 'd') {
|
606 |
+
$destination_has_extensions_dir = true;
|
607 |
+
continue;
|
608 |
+
}
|
609 |
+
|
610 |
+
$dest_ext_file_path = $destination_extension_dir .'/'. $dest_ext_file['name'];
|
611 |
+
|
612 |
+
if (!$wp_filesystem->delete($dest_ext_file_path, true, $dest_ext_file['type'])) {
|
613 |
+
return new WP_Error($wp_error_id,
|
614 |
+
__('Cannot delete: ', 'fw') . $dest_ext_file_path
|
615 |
+
);
|
616 |
+
}
|
617 |
+
}
|
618 |
+
}
|
619 |
+
}
|
620 |
+
|
621 |
+
// move files from source to destination extension directory
|
622 |
+
{
|
623 |
+
$source_has_extensions_dir = false;
|
624 |
+
|
625 |
+
foreach ($source_ext_files as $source_ext_file) {
|
626 |
+
if (in_array($source_ext_file['name'], $this->skip_file_names)) {
|
627 |
+
continue;
|
628 |
+
}
|
629 |
+
|
630 |
+
if ($source_ext_file['name'] === 'extensions' && $source_ext_file['type'] === 'd') {
|
631 |
+
$source_has_extensions_dir = true;
|
632 |
+
continue;
|
633 |
+
}
|
634 |
+
|
635 |
+
$source_ext_file_path = $source_extension_dir .'/'. $source_ext_file['name'];
|
636 |
+
$dest_ext_file_path = $destination_extension_dir .'/'. $source_ext_file['name'];
|
637 |
+
|
638 |
+
if (!$wp_filesystem->move($source_ext_file_path, $dest_ext_file_path)) {
|
639 |
+
return new WP_Error($wp_error_id,
|
640 |
+
sprintf(__('Cannot move "%s" to "%s"', 'fw'),
|
641 |
+
$source_ext_file_path, $dest_ext_file_path
|
642 |
+
)
|
643 |
+
);
|
644 |
+
}
|
645 |
+
}
|
646 |
+
}
|
647 |
+
|
648 |
+
if ($source_has_extensions_dir) {
|
649 |
+
if ($destination_has_extensions_dir) {
|
650 |
+
$merge_result = $this->merge_extensions(
|
651 |
+
$source_extension_dir .'/extensions',
|
652 |
+
$destination_extension_dir .'/extensions'
|
653 |
+
);
|
654 |
+
|
655 |
+
if ($merge_result !== true) {
|
656 |
+
return $merge_result;
|
657 |
+
}
|
658 |
+
} else {
|
659 |
+
if (!$wp_filesystem->move(
|
660 |
+
$source_extension_dir .'/extensions',
|
661 |
+
$destination_extension_dir .'/extensions'
|
662 |
+
)) {
|
663 |
+
return new WP_Error($wp_error_id,
|
664 |
+
sprintf(__('Cannot move "%s" to "%s"', 'fw'),
|
665 |
+
$source_extension_dir .'/extensions',
|
666 |
+
$destination_extension_dir .'/extensions'
|
667 |
+
)
|
668 |
+
);
|
669 |
+
}
|
670 |
+
}
|
671 |
+
}
|
672 |
+
}
|
673 |
+
|
674 |
+
return true;
|
675 |
+
}
|
676 |
+
|
677 |
+
/**
|
678 |
+
* @internal
|
679 |
+
*/
|
680 |
+
public function _action_update_framework()
|
681 |
+
{
|
682 |
+
$nonce_name = '_nonce_fw_ext_update_framework';
|
683 |
+
if (!isset($_POST[$nonce_name]) || !wp_verify_nonce($_POST[$nonce_name])) {
|
684 |
+
wp_die(__('Invalid nonce.', 'fw'));
|
685 |
+
}
|
686 |
+
|
687 |
+
{
|
688 |
+
if (!class_exists('_FW_Ext_Update_Framework_Upgrader_Skin')) {
|
689 |
+
fw_include_file_isolated(
|
690 |
+
$this->get_declared_path('/includes/classes/class--fw-ext-update-framework-upgrader-skin.php')
|
691 |
+
);
|
692 |
+
}
|
693 |
+
|
694 |
+
$skin = new _FW_Ext_Update_Framework_Upgrader_Skin(array(
|
695 |
+
'title' => __('Framework Update', 'fw'),
|
696 |
+
));
|
697 |
+
}
|
698 |
+
|
699 |
+
require_once ABSPATH .'wp-admin/admin-header.php';
|
700 |
+
|
701 |
+
$skin->header();
|
702 |
+
|
703 |
+
do {
|
704 |
+
if (!FW_WP_Filesystem::request_access(fw_get_framework_directory(), fw_current_url(), array($nonce_name))) {
|
705 |
+
break;
|
706 |
+
}
|
707 |
+
|
708 |
+
$update = $this->get_framework_update();
|
709 |
+
|
710 |
+
if ($update === false) {
|
711 |
+
$skin->error(__('Failed to get framework latest version.', 'fw'));
|
712 |
+
break;
|
713 |
+
} elseif (is_wp_error($update)) {
|
714 |
+
$skin->error($update);
|
715 |
+
break;
|
716 |
+
}
|
717 |
+
|
718 |
+
/** @var FW_Ext_Update_Service $service */
|
719 |
+
$service = $this->get_child($update['service']);
|
720 |
+
|
721 |
+
$update_result = $this->update(array(
|
722 |
+
'wp_fs_destination_dir' => FW_WP_Filesystem::real_path_to_filesystem_path(
|
723 |
+
fw_get_framework_directory()
|
724 |
+
),
|
725 |
+
'download_callback' => array($service, '_download_framework'),
|
726 |
+
'download_callback_args' => array($update['latest_version'], $this->get_wp_fs_tmp_dir()),
|
727 |
+
'skin' => $skin,
|
728 |
+
'title' => __('Framework', 'fw'),
|
729 |
+
));
|
730 |
+
|
731 |
+
if (is_wp_error($update_result)) {
|
732 |
+
$skin->error($update_result);
|
733 |
+
break;
|
734 |
+
}
|
735 |
+
|
736 |
+
$skin->set_result(true);
|
737 |
+
$skin->after();
|
738 |
+
} while(false);
|
739 |
+
|
740 |
+
$skin->footer();
|
741 |
+
|
742 |
+
require_once(ABSPATH . 'wp-admin/admin-footer.php');
|
743 |
+
}
|
744 |
+
|
745 |
+
/**
|
746 |
+
* @internal
|
747 |
+
*/
|
748 |
+
public function _action_update_theme()
|
749 |
+
{
|
750 |
+
$nonce_name = '_nonce_fw_ext_update_theme';
|
751 |
+
if (!isset($_POST[$nonce_name]) || !wp_verify_nonce($_POST[$nonce_name])) {
|
752 |
+
wp_die(__('Invalid nonce.', 'fw'));
|
753 |
+
}
|
754 |
+
|
755 |
+
{
|
756 |
+
if (!class_exists('_FW_Ext_Update_Theme_Upgrader_Skin')) {
|
757 |
+
fw_include_file_isolated(
|
758 |
+
$this->get_declared_path('/includes/classes/class--fw-ext-update-theme-upgrader-skin.php')
|
759 |
+
);
|
760 |
+
}
|
761 |
+
|
762 |
+
$skin = new _FW_Ext_Update_Theme_Upgrader_Skin(array(
|
763 |
+
'title' => __('Theme Update', 'fw'),
|
764 |
+
));
|
765 |
+
}
|
766 |
+
|
767 |
+
require_once(ABSPATH . 'wp-admin/admin-header.php');
|
768 |
+
|
769 |
+
$skin->header();
|
770 |
+
|
771 |
+
do {
|
772 |
+
if (!FW_WP_Filesystem::request_access(get_template_directory(), fw_current_url(), array($nonce_name))) {
|
773 |
+
break;
|
774 |
+
}
|
775 |
+
|
776 |
+
$update = $this->get_theme_update();
|
777 |
+
|
778 |
+
if ($update === false) {
|
779 |
+
$skin->error(__('Failed to get theme latest version.', 'fw'));
|
780 |
+
break;
|
781 |
+
} elseif (is_wp_error($update)) {
|
782 |
+
$skin->error($update);
|
783 |
+
break;
|
784 |
+
}
|
785 |
+
|
786 |
+
/** @var FW_Ext_Update_Service $service */
|
787 |
+
$service = $this->get_child($update['service']);
|
788 |
+
|
789 |
+
$update_result = $this->update(array(
|
790 |
+
'wp_fs_destination_dir' => FW_WP_Filesystem::real_path_to_filesystem_path(
|
791 |
+
get_template_directory()
|
792 |
+
),
|
793 |
+
'download_callback' => array($service, '_download_theme'),
|
794 |
+
'download_callback_args' => array($update['latest_version'], $this->get_wp_fs_tmp_dir()),
|
795 |
+
'skin' => $skin,
|
796 |
+
'title' => __('Theme', 'fw'),
|
797 |
+
));
|
798 |
+
|
799 |
+
if (is_wp_error($update_result)) {
|
800 |
+
$skin->error($update_result);
|
801 |
+
break;
|
802 |
+
}
|
803 |
+
|
804 |
+
$skin->set_result(true);
|
805 |
+
$skin->after();
|
806 |
+
} while(false);
|
807 |
+
|
808 |
+
$skin->footer();
|
809 |
+
|
810 |
+
require_once(ABSPATH . 'wp-admin/admin-footer.php');
|
811 |
+
}
|
812 |
+
|
813 |
+
/**
|
814 |
+
* @internal
|
815 |
+
*/
|
816 |
+
public function _action_update_extensions()
|
817 |
+
{
|
818 |
+
$nonce_name = '_nonce_fw_ext_update_extensions';
|
819 |
+
if (!isset($_POST[$nonce_name]) || !wp_verify_nonce($_POST[$nonce_name])) {
|
820 |
+
wp_die(__('Invalid nonce.', 'fw'));
|
821 |
+
}
|
822 |
+
|
823 |
+
$form_input_name = 'extensions';
|
824 |
+
$extensions_list = FW_Request::POST($form_input_name);
|
825 |
+
|
826 |
+
if (empty($extensions_list)) {
|
827 |
+
FW_Flash_Messages::add(
|
828 |
+
'fw_ext_update',
|
829 |
+
__('Please check the extensions you want to update.', 'fw'),
|
830 |
+
'warning'
|
831 |
+
);
|
832 |
+
wp_redirect(self_admin_url('update-core.php'));
|
833 |
+
exit;
|
834 |
+
}
|
835 |
+
|
836 |
+
// handle changes by the hack below
|
837 |
+
{
|
838 |
+
if (is_string($extensions_list)) {
|
839 |
+
$extensions_list = json_decode($extensions_list);
|
840 |
+
} else {
|
841 |
+
$extensions_list = array_keys($extensions_list);
|
842 |
+
}
|
843 |
+
}
|
844 |
+
|
845 |
+
{
|
846 |
+
if (!class_exists('_FW_Ext_Update_Extensions_Upgrader_Skin')) {
|
847 |
+
fw_include_file_isolated(
|
848 |
+
$this->get_declared_path('/includes/classes/class--fw-ext-update-extensions-upgrader-skin.php')
|
849 |
+
);
|
850 |
+
}
|
851 |
+
|
852 |
+
$skin = new _FW_Ext_Update_Extensions_Upgrader_Skin(array(
|
853 |
+
'title' => __('Extensions Update', 'fw'),
|
854 |
+
));
|
855 |
+
}
|
856 |
+
|
857 |
+
require_once(ABSPATH . 'wp-admin/admin-header.php');
|
858 |
+
|
859 |
+
$skin->header();
|
860 |
+
|
861 |
+
do {
|
862 |
+
/**
|
863 |
+
* Hack for the ftp credentials template that does not support array post values
|
864 |
+
* https://github.com/WordPress/WordPress/blob/3949a8b6cc50a021ed93798287b4ef9ea8a560d9/wp-admin/includes/file.php#L1144
|
865 |
+
*/
|
866 |
+
{
|
867 |
+
$original_post_value = $_POST[$form_input_name];
|
868 |
+
$_POST[$form_input_name] = wp_slash(json_encode($extensions_list));
|
869 |
+
}
|
870 |
+
|
871 |
+
if (!FW_WP_Filesystem::request_access(
|
872 |
+
fw_get_framework_directory('/extensions'),
|
873 |
+
fw_current_url(),
|
874 |
+
array($nonce_name, $form_input_name))
|
875 |
+
) {
|
876 |
+
{ // revert hack changes
|
877 |
+
$_POST[$form_input_name] = $original_post_value;
|
878 |
+
unset($original_post_value);
|
879 |
+
}
|
880 |
+
break;
|
881 |
+
}
|
882 |
+
|
883 |
+
{ // revert hack changes
|
884 |
+
$_POST[$form_input_name] = $original_post_value;
|
885 |
+
unset($original_post_value);
|
886 |
+
}
|
887 |
+
|
888 |
+
$updates = $this->get_extensions_with_updates();
|
889 |
+
|
890 |
+
if (empty($updates)) {
|
891 |
+
$skin->error(__('No extensions updates found.', 'fw'));
|
892 |
+
break;
|
893 |
+
}
|
894 |
+
|
895 |
+
foreach ($extensions_list as $extension_name) {
|
896 |
+
if (!($extension = fw()->extensions->get($extension_name))) {
|
897 |
+
$skin->error(
|
898 |
+
sprintf(__('Extension "%s" does not exist or is disabled.', 'fw'), $extension_name)
|
899 |
+
);
|
900 |
+
continue;
|
901 |
+
}
|
902 |
+
|
903 |
+
if (!isset($updates[$extension_name])) {
|
904 |
+
$skin->error(
|
905 |
+
sprintf(__('No update found for the "%s" extension.', 'fw'), $extension->manifest->get_name())
|
906 |
+
);
|
907 |
+
continue;
|
908 |
+
}
|
909 |
+
|
910 |
+
$update = $updates[$extension_name];
|
911 |
+
|
912 |
+
if (is_wp_error($update)) {
|
913 |
+
$skin->error($update);
|
914 |
+
continue;
|
915 |
+
}
|
916 |
+
|
917 |
+
/** @var FW_Ext_Update_Service $service */
|
918 |
+
$service = $this->get_child($update['service']);
|
919 |
+
|
920 |
+
$update_result = $this->update(array(
|
921 |
+
'wp_fs_destination_dir' => FW_WP_Filesystem::real_path_to_filesystem_path(
|
922 |
+
$extension->get_declared_path()
|
923 |
+
),
|
924 |
+
'download_callback' => array($service, '_download_extension'),
|
925 |
+
'download_callback_args' => array($extension, $update['latest_version'], $this->get_wp_fs_tmp_dir()),
|
926 |
+
'skin' => $skin,
|
927 |
+
'title' => sprintf(__('%s extension', 'fw'), $extension->manifest->get_name()),
|
928 |
+
), true);
|
929 |
+
|
930 |
+
if (is_wp_error($update_result)) {
|
931 |
+
$skin->error($update_result);
|
932 |
+
continue;
|
933 |
+
}
|
934 |
+
|
935 |
+
$skin->set_result(true);
|
936 |
+
|
937 |
+
if (!$this->get_config('extensions_as_one_update')) {
|
938 |
+
$skin->decrement_extension_update_count( $extension_name );
|
939 |
+
}
|
940 |
+
}
|
941 |
+
|
942 |
+
if ($this->get_config('extensions_as_one_update')) {
|
943 |
+
$skin->decrement_extension_update_count( $extension_name );
|
944 |
+
}
|
945 |
+
|
946 |
+
$skin->after();
|
947 |
+
} while(false);
|
948 |
+
|
949 |
+
$skin->footer();
|
950 |
+
|
951 |
+
require_once(ABSPATH . 'wp-admin/admin-footer.php');
|
952 |
+
}
|
953 |
+
|
954 |
+
public function _action_admin_notices() {
|
955 |
+
if (
|
956 |
+
get_current_screen()->parent_base === fw()->extensions->manager->get_page_slug()
|
957 |
+
&&
|
958 |
+
($updates = $this->get_updates())
|
959 |
+
&&
|
960 |
+
!empty($updates['extensions'])
|
961 |
+
) { /* ok */ } else {
|
962 |
+
return;
|
963 |
+
}
|
964 |
+
|
965 |
+
foreach ($updates['extensions'] as $ext_name => $ext_update) {
|
966 |
+
if ( is_wp_error( $ext_update ) ) {
|
967 |
+
return;
|
968 |
+
}
|
969 |
+
|
970 |
+
break;
|
971 |
+
}
|
972 |
+
|
973 |
+
echo '<div class="notice notice-warning"><p>'
|
974 |
+
. sprintf(
|
975 |
+
esc_html__('New extensions updates available. %s', 'fw'),
|
976 |
+
fw_html_tag('a', array(
|
977 |
+
'href' => self_admin_url('update-core.php') .'#fw-ext-update-extensions',
|
978 |
+
), esc_html__('Go to Updates page', 'fw'))
|
979 |
+
)
|
980 |
+
. '</p></div>';
|
981 |
+
}
|
982 |
+
}
|
framework/extensions/update/config.php
CHANGED
@@ -1,9 +1,9 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
|
3 |
-
$cfg = array();
|
4 |
-
|
5 |
-
/**
|
6 |
-
* Do not show details about each extension update, but show it as one update
|
7 |
-
* (simplify users life)
|
8 |
-
*/
|
9 |
-
$cfg['extensions_as_one_update'] = true;
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
|
3 |
+
$cfg = array();
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Do not show details about each extension update, but show it as one update
|
7 |
+
* (simplify users life)
|
8 |
+
*/
|
9 |
+
$cfg['extensions_as_one_update'] = true;
|
framework/extensions/update/extensions/github-update/class-fw-extension-github-update.php
CHANGED
@@ -1,443 +1,443 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Github Update
|
5 |
-
*
|
6 |
-
* Add {'github_update' => 'user/repo'} to your manifest and this extension will handle it
|
7 |
-
*/
|
8 |
-
class FW_Extension_Github_Update extends FW_Ext_Update_Service
|
9 |
-
{
|
10 |
-
/**
|
11 |
-
* Handle framework, theme and extensions that has this key in manifest
|
12 |
-
* @var string
|
13 |
-
*/
|
14 |
-
private $manifest_key = 'github_update';
|
15 |
-
|
16 |
-
/**
|
17 |
-
* Check if manifest key format is correct 'user/repo'
|
18 |
-
* @var string
|
19 |
-
*/
|
20 |
-
private $manifest_key_regex = '/^([^\s\/]+)\/([^\s\/]+)$/';
|
21 |
-
|
22 |
-
/**
|
23 |
-
* How long to cache server responses
|
24 |
-
* @var int seconds
|
25 |
-
*/
|
26 |
-
private $transient_expiration = DAY_IN_SECONDS;
|
27 |
-
|
28 |
-
private $download_timeout = 300;
|
29 |
-
|
30 |
-
/**
|
31 |
-
* Used when there is internet connection problems
|
32 |
-
* To prevent site being blocked on every refresh, this fake version will be cached in the transient
|
33 |
-
* @var string
|
34 |
-
*/
|
35 |
-
private $fake_latest_version = '0.0.0';
|
36 |
-
|
37 |
-
/**
|
38 |
-
* @internal
|
39 |
-
*/
|
40 |
-
protected function _init()
|
41 |
-
{
|
42 |
-
}
|
43 |
-
|
44 |
-
/**
|
45 |
-
* @param string $append '/foo/bar'
|
46 |
-
* @return string
|
47 |
-
*/
|
48 |
-
private function get_github_api_url($append)
|
49 |
-
{
|
50 |
-
return apply_filters('fw_github_api_url', 'https://api.github.com') . $append;
|
51 |
-
}
|
52 |
-
|
53 |
-
private function fetch_latest_version($user_slash_repo)
|
54 |
-
{
|
55 |
-
/**
|
56 |
-
* If at least one request failed, do not do any other requests, to prevent site being blocked on every refresh.
|
57 |
-
* This may happen on localhost when develop your theme and you have no internet connection.
|
58 |
-
* Then this method will return a fake '0.0.0' version, it will be cached by the transient
|
59 |
-
* and will not bother you until the transient will expire, then a new request will be made.
|
60 |
-
* @var bool
|
61 |
-
*/
|
62 |
-
static $no_internet_connection = false;
|
63 |
-
|
64 |
-
if ($no_internet_connection) {
|
65 |
-
return $this->fake_latest_version;
|
66 |
-
}
|
67 |
-
|
68 |
-
$http = new WP_Http();
|
69 |
-
|
70 |
-
$response = $http->get(
|
71 |
-
$this->get_github_api_url('/repos/'. $user_slash_repo .'/releases/latest')
|
72 |
-
);
|
73 |
-
|
74 |
-
unset($http);
|
75 |
-
|
76 |
-
if (is_wp_error($response)) {
|
77 |
-
if ($response->get_error_code() === 'http_request_failed') {
|
78 |
-
$no_internet_connection = true;
|
79 |
-
}
|
80 |
-
|
81 |
-
return $response;
|
82 |
-
}
|
83 |
-
|
84 |
-
if (($response_code = intval(wp_remote_retrieve_response_code($response))) !== 200) {
|
85 |
-
if ($response_code === 403) {
|
86 |
-
$json_response = json_decode($response['body'], true);
|
87 |
-
|
88 |
-
if ($json_response) {
|
89 |
-
return new WP_Error(
|
90 |
-
'fw_ext_update_github_fetch_releases_failed',
|
91 |
-
__('Github error:', 'fw') .' '. $json_response['message']
|
92 |
-
);
|
93 |
-
}
|
94 |
-
}
|
95 |
-
|
96 |
-
if ($response_code) {
|
97 |
-
return new WP_Error(
|
98 |
-
'fw_ext_update_github_fetch_releases_failed',
|
99 |
-
sprintf(
|
100 |
-
__( 'Failed to access Github repository "%s" releases. (Response code: %d)', 'fw' ),
|
101 |
-
$user_slash_repo, $response_code
|
102 |
-
)
|
103 |
-
);
|
104 |
-
} else {
|
105 |
-
return new WP_Error(
|
106 |
-
'fw_ext_update_github_fetch_releases_failed',
|
107 |
-
sprintf(
|
108 |
-
__( 'Failed to access Github repository "%s" releases.', 'fw' ),
|
109 |
-
$user_slash_repo
|
110 |
-
)
|
111 |
-
);
|
112 |
-
}
|
113 |
-
}
|
114 |
-
|
115 |
-
$release = json_decode($response['body'], true);
|
116 |
-
|
117 |
-
unset($response);
|
118 |
-
|
119 |
-
if (empty($release)) {
|
120 |
-
return new WP_Error(
|
121 |
-
'fw_ext_update_github_fetch_no_releases',
|
122 |
-
sprintf(__('No releases found in repository "%s".', 'fw'), $user_slash_repo)
|
123 |
-
);
|
124 |
-
}
|
125 |
-
|
126 |
-
return $release['tag_name'];
|
127 |
-
}
|
128 |
-
|
129 |
-
/**
|
130 |
-
* Get repository latest release version
|
131 |
-
*
|
132 |
-
* @param string $user_slash_repo Github 'user/repo'
|
133 |
-
* @param bool $force_check Bypass cache
|
134 |
-
* @param string $title Used in messages
|
135 |
-
*
|
136 |
-
* @return string|WP_Error
|
137 |
-
*/
|
138 |
-
private function get_latest_version($user_slash_repo, $force_check, $title)
|
139 |
-
{
|
140 |
-
if (!preg_match($this->manifest_key_regex, $user_slash_repo)) {
|
141 |
-
return new WP_Error('fw_ext_update_github_manifest_invalid',
|
142 |
-
sprintf(
|
143 |
-
__('%s manifest has invalid "github_update" parameter. Please use "user/repo" format.', 'fw'),
|
144 |
-
$title
|
145 |
-
)
|
146 |
-
);
|
147 |
-
}
|
148 |
-
|
149 |
-
$transient_id = 'fw_ext_upd_gh_fw'; // the length must be 45 characters or less
|
150 |
-
|
151 |
-
if ($force_check) {
|
152 |
-
delete_site_transient($transient_id);
|
153 |
-
|
154 |
-
$cache = array();
|
155 |
-
} else {
|
156 |
-
$cache = get_site_transient($transient_id);
|
157 |
-
|
158 |
-
if ($cache === false) {
|
159 |
-
$cache = array();
|
160 |
-
} elseif (isset($cache[$user_slash_repo])) {
|
161 |
-
return $cache[$user_slash_repo];
|
162 |
-
}
|
163 |
-
}
|
164 |
-
|
165 |
-
$latest_version = $this->fetch_latest_version($user_slash_repo);
|
166 |
-
|
167 |
-
if (empty($latest_version)) {
|
168 |
-
return new WP_Error(
|
169 |
-
'fw_ext_update_github_failed_fetch_latest_version',
|
170 |
-
sprintf(
|
171 |
-
__('Failed to fetch %s latest version from github "%s".', 'fw'),
|
172 |
-
$title, $user_slash_repo
|
173 |
-
)
|
174 |
-
);
|
175 |
-
}
|
176 |
-
|
177 |
-
if (is_wp_error($latest_version)) {
|
178 |
-
/**
|
179 |
-
* Internet connection problems or Github API requests limit reached.
|
180 |
-
* Cache fake version to prevent requests to Github API on every refresh.
|
181 |
-
*/
|
182 |
-
$cache = array_merge($cache, array($user_slash_repo => $this->fake_latest_version));
|
183 |
-
|
184 |
-
/**
|
185 |
-
* Show the error to the user because it is not visible elsewhere
|
186 |
-
*/
|
187 |
-
FW_Flash_Messages::add(
|
188 |
-
'fw_ext_github_update_error',
|
189 |
-
$latest_version->get_error_message(),
|
190 |
-
'error'
|
191 |
-
);
|
192 |
-
} else {
|
193 |
-
$cache = array_merge($cache, array($user_slash_repo => $latest_version));
|
194 |
-
}
|
195 |
-
|
196 |
-
set_site_transient(
|
197 |
-
$transient_id,
|
198 |
-
$cache,
|
199 |
-
$this->transient_expiration
|
200 |
-
);
|
201 |
-
|
202 |
-
return $latest_version;
|
203 |
-
}
|
204 |
-
|
205 |
-
/**
|
206 |
-
* @param string $user_slash_repo Github 'user/repo'
|
207 |
-
* @param string $version Requested version to download
|
208 |
-
* @param string $wp_filesystem_download_directory Allocated temporary empty directory
|
209 |
-
* @param string $title Used in messages
|
210 |
-
*
|
211 |
-
* @return string|WP_Error Path to the downloaded directory
|
212 |
-
*/
|
213 |
-
private function download($user_slash_repo, $version, $wp_filesystem_download_directory, $title)
|
214 |
-
{
|
215 |
-
$http = new WP_Http();
|
216 |
-
|
217 |
-
$response = $http->get(
|
218 |
-
$this->get_github_api_url('/repos/'. $user_slash_repo .'/releases/tags/'. $version)
|
219 |
-
);
|
220 |
-
|
221 |
-
unset($http);
|
222 |
-
|
223 |
-
$response_code = intval(wp_remote_retrieve_response_code($response));
|
224 |
-
|
225 |
-
if ($response_code !== 200) {
|
226 |
-
if ($response_code === 403) {
|
227 |
-
$json_response = json_decode($response['body'], true);
|
228 |
-
|
229 |
-
if ($json_response) {
|
230 |
-
return new WP_Error(
|
231 |
-
'fw_ext_update_github_download_releases_failed',
|
232 |
-
__('Github error:', 'fw') .' '. $json_response['message']
|
233 |
-
);
|
234 |
-
}
|
235 |
-
}
|
236 |
-
|
237 |
-
if ($response_code) {
|
238 |
-
return new WP_Error(
|
239 |
-
'fw_ext_update_github_download_releases_failed',
|
240 |
-
sprintf(
|
241 |
-
__( 'Failed to access Github repository "%s" releases. (Response code: %d)', 'fw' ),
|
242 |
-
$user_slash_repo, $response_code
|
243 |
-
)
|
244 |
-
);
|
245 |
-
} else {
|
246 |
-
return new WP_Error(
|
247 |
-
'fw_ext_update_github_download_releases_failed',
|
248 |
-
sprintf(
|
249 |
-
__( 'Failed to access Github repository "%s" releases.', 'fw' ),
|
250 |
-
$user_slash_repo
|
251 |
-
)
|
252 |
-
);
|
253 |
-
}
|
254 |
-
}
|
255 |
-
|
256 |
-
$release = json_decode($response['body'], true);
|
257 |
-
|
258 |
-
unset($response);
|
259 |
-
|
260 |
-
if (empty($release)) {
|
261 |
-
return new WP_Error(
|
262 |
-
'fw_ext_update_github_download_no_release',
|
263 |
-
sprintf(
|
264 |
-
__('%s github repository "%s" does not have the "%s" release.', 'fw'),
|
265 |
-
$title, $user_slash_repo, $version
|
266 |
-
)
|
267 |
-
);
|
268 |
-
}
|
269 |
-
|
270 |
-
$http = new WP_Http();
|
271 |
-
|
272 |
-
$response = $http->request(
|
273 |
-
'https://github.com/'. $user_slash_repo .'/archive/'. $release['tag_name'] .'.zip',
|
274 |
-
array(
|
275 |
-
'timeout' => $this->download_timeout,
|
276 |
-
)
|
277 |
-
);
|
278 |
-
|
279 |
-
unset($http);
|
280 |
-
|
281 |
-
if (intval(wp_remote_retrieve_response_code($response)) !== 200) {
|
282 |
-
return new WP_Error(
|
283 |
-
'fw_ext_update_github_download_failed',
|
284 |
-
sprintf(__('Cannot download %s zip.', 'fw'), $title)
|
285 |
-
);
|
286 |
-
}
|
287 |
-
|
288 |
-
/** @var WP_Filesystem_Base $wp_filesystem */
|
289 |
-
global $wp_filesystem;
|
290 |
-
|
291 |
-
$zip_path = $wp_filesystem_download_directory .'/temp.zip';
|
292 |
-
|
293 |
-
// save zip to file
|
294 |
-
if (!$wp_filesystem->put_contents($zip_path, $response['body'])) {
|
295 |
-
return new WP_Error(
|
296 |
-
'fw_ext_update_github_save_download_failed',
|
297 |
-
sprintf(__('Cannot save %s zip.', 'fw'), $title)
|
298 |
-
);
|
299 |
-
}
|
300 |
-
|
301 |
-
unset($response);
|
302 |
-
|
303 |
-
$unzip_result = unzip_file(
|
304 |
-
FW_WP_Filesystem::filesystem_path_to_real_path($zip_path),
|
305 |
-
$wp_filesystem_download_directory
|
306 |
-
);
|
307 |
-
|
308 |
-
if (is_wp_error($unzip_result)) {
|
309 |
-
return $unzip_result;
|
310 |
-
}
|
311 |
-
|
312 |
-
// remove zip file
|
313 |
-
if (!$wp_filesystem->delete($zip_path, false, 'f')) {
|
314 |
-
return new WP_Error(
|
315 |
-
'fw_ext_update_github_remove_downloaded_zip_failed',
|
316 |
-
sprintf(__('Cannot remove %s zip.', 'fw'), $title)
|
317 |
-
);
|
318 |
-
}
|
319 |
-
|
320 |
-
$unzipped_dir_files = $wp_filesystem->dirlist($wp_filesystem_download_directory);
|
321 |
-
|
322 |
-
if (!$unzipped_dir_files) {
|
323 |
-
return new WP_Error(
|
324 |
-
'fw_ext_update_github_unzipped_dir_fail',
|
325 |
-
__('Cannot access the unzipped directory files.', 'fw')
|
326 |
-
);
|
327 |
-
}
|
328 |
-
|
329 |
-
/**
|
330 |
-
* get first found directory
|
331 |
-
* (if everything worked well, there should be only one directory)
|
332 |
-
*/
|
333 |
-
foreach ($unzipped_dir_files as $file) {
|
334 |
-
if ($file['type'] == 'd') {
|
335 |
-
return $wp_filesystem_download_directory .'/'. $file['name'];
|
336 |
-
}
|
337 |
-
}
|
338 |
-
|
339 |
-
return new WP_Error(
|
340 |
-
'fw_ext_update_github_unzipped_dir_not_found',
|
341 |
-
sprintf(__('The unzipped %s directory not found.', 'fw'), $title)
|
342 |
-
);
|
343 |
-
}
|
344 |
-
|
345 |
-
/**
|
346 |
-
* {@inheritdoc}
|
347 |
-
* @internal
|
348 |
-
*/
|
349 |
-
public function _get_framework_latest_version($force_check)
|
350 |
-
{
|
351 |
-
$user_slash_repo = fw()->manifest->get($this->manifest_key);
|
352 |
-
|
353 |
-
if (empty($user_slash_repo)) {
|
354 |
-
return false;
|
355 |
-
}
|
356 |
-
|
357 |
-
return $this->get_latest_version(
|
358 |
-
$user_slash_repo,
|
359 |
-
$force_check,
|
360 |
-
__('Framework', 'fw')
|
361 |
-
);
|
362 |
-
}
|
363 |
-
|
364 |
-
/**
|
365 |
-
* {@inheritdoc}
|
366 |
-
* @internal
|
367 |
-
*/
|
368 |
-
public function _download_framework($version, $wp_filesystem_download_directory)
|
369 |
-
{
|
370 |
-
return $this->download(
|
371 |
-
fw()->manifest->get($this->manifest_key),
|
372 |
-
$version,
|
373 |
-
$wp_filesystem_download_directory,
|
374 |
-
__('Framework', 'fw')
|
375 |
-
);
|
376 |
-
}
|
377 |
-
|
378 |
-
/**
|
379 |
-
* {@inheritdoc}
|
380 |
-
* @internal
|
381 |
-
*/
|
382 |
-
public function _get_theme_latest_version($force_check)
|
383 |
-
{
|
384 |
-
$user_slash_repo = fw()->theme->manifest->get($this->manifest_key);
|
385 |
-
|
386 |
-
if (empty($user_slash_repo)) {
|
387 |
-
return false;
|
388 |
-
}
|
389 |
-
|
390 |
-
return $this->get_latest_version(
|
391 |
-
$user_slash_repo,
|
392 |
-
$force_check,
|
393 |
-
__('Theme', 'fw')
|
394 |
-
);
|
395 |
-
}
|
396 |
-
|
397 |
-
/**
|
398 |
-
* {@inheritdoc}
|
399 |
-
* @internal
|
400 |
-
*/
|
401 |
-
public function _download_theme($version, $wp_filesystem_download_directory)
|
402 |
-
{
|
403 |
-
return $this->download(
|
404 |
-
fw()->theme->manifest->get($this->manifest_key),
|
405 |
-
$version,
|
406 |
-
$wp_filesystem_download_directory,
|
407 |
-
__('Theme', 'fw')
|
408 |
-
);
|
409 |
-
}
|
410 |
-
|
411 |
-
/**
|
412 |
-
* {@inheritdoc}
|
413 |
-
* @internal
|
414 |
-
*/
|
415 |
-
public function _get_extension_latest_version(FW_Extension $extension, $force_check)
|
416 |
-
{
|
417 |
-
$user_slash_repo = $extension->manifest->get($this->manifest_key);
|
418 |
-
|
419 |
-
if (empty($user_slash_repo)) {
|
420 |
-
return false;
|
421 |
-
}
|
422 |
-
|
423 |
-
return $this->get_latest_version(
|
424 |
-
$user_slash_repo,
|
425 |
-
$force_check,
|
426 |
-
sprintf(__('%s extension', 'fw'), $extension->manifest->get_name())
|
427 |
-
);
|
428 |
-
}
|
429 |
-
|
430 |
-
/**
|
431 |
-
* {@inheritdoc}
|
432 |
-
* @internal
|
433 |
-
*/
|
434 |
-
public function _download_extension(FW_Extension $extension, $version, $wp_filesystem_download_directory)
|
435 |
-
{
|
436 |
-
return $this->download(
|
437 |
-
$extension->manifest->get($this->manifest_key),
|
438 |
-
$version,
|
439 |
-
$wp_filesystem_download_directory,
|
440 |
-
sprintf(__('%s extension', 'fw'), $extension->manifest->get_name())
|
441 |
-
);
|
442 |
-
}
|
443 |
-
}
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Github Update
|
5 |
+
*
|
6 |
+
* Add {'github_update' => 'user/repo'} to your manifest and this extension will handle it
|
7 |
+
*/
|
8 |
+
class FW_Extension_Github_Update extends FW_Ext_Update_Service
|
9 |
+
{
|
10 |
+
/**
|
11 |
+
* Handle framework, theme and extensions that has this key in manifest
|
12 |
+
* @var string
|
13 |
+
*/
|
14 |
+
private $manifest_key = 'github_update';
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Check if manifest key format is correct 'user/repo'
|
18 |
+
* @var string
|
19 |
+
*/
|
20 |
+
private $manifest_key_regex = '/^([^\s\/]+)\/([^\s\/]+)$/';
|
21 |
+
|
22 |
+
/**
|
23 |
+
* How long to cache server responses
|
24 |
+
* @var int seconds
|
25 |
+
*/
|
26 |
+
private $transient_expiration = DAY_IN_SECONDS;
|
27 |
+
|
28 |
+
private $download_timeout = 300;
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Used when there is internet connection problems
|
32 |
+
* To prevent site being blocked on every refresh, this fake version will be cached in the transient
|
33 |
+
* @var string
|
34 |
+
*/
|
35 |
+
private $fake_latest_version = '0.0.0';
|
36 |
+
|
37 |
+
/**
|
38 |
+
* @internal
|
39 |
+
*/
|
40 |
+
protected function _init()
|
41 |
+
{
|
42 |
+
}
|
43 |
+
|
44 |
+
/**
|
45 |
+
* @param string $append '/foo/bar'
|
46 |
+
* @return string
|
47 |
+
*/
|
48 |
+
private function get_github_api_url($append)
|
49 |
+
{
|
50 |
+
return apply_filters('fw_github_api_url', 'https://api.github.com') . $append;
|
51 |
+
}
|
52 |
+
|
53 |
+
private function fetch_latest_version($user_slash_repo)
|
54 |
+
{
|
55 |
+
/**
|
56 |
+
* If at least one request failed, do not do any other requests, to prevent site being blocked on every refresh.
|
57 |
+
* This may happen on localhost when develop your theme and you have no internet connection.
|
58 |
+
* Then this method will return a fake '0.0.0' version, it will be cached by the transient
|
59 |
+
* and will not bother you until the transient will expire, then a new request will be made.
|
60 |
+
* @var bool
|
61 |
+
*/
|
62 |
+
static $no_internet_connection = false;
|
63 |
+
|
64 |
+
if ($no_internet_connection) {
|
65 |
+
return $this->fake_latest_version;
|
66 |
+
}
|
67 |
+
|
68 |
+
$http = new WP_Http();
|
69 |
+
|
70 |
+
$response = $http->get(
|
71 |
+
$this->get_github_api_url('/repos/'. $user_slash_repo .'/releases/latest')
|
72 |
+
);
|
73 |
+
|
74 |
+
unset($http);
|
75 |
+
|
76 |
+
if (is_wp_error($response)) {
|
77 |
+
if ($response->get_error_code() === 'http_request_failed') {
|
78 |
+
$no_internet_connection = true;
|
79 |
+
}
|
80 |
+
|
81 |
+
return $response;
|
82 |
+
}
|
83 |
+
|
84 |
+
if (($response_code = intval(wp_remote_retrieve_response_code($response))) !== 200) {
|
85 |
+
if ($response_code === 403) {
|
86 |
+
$json_response = json_decode($response['body'], true);
|
87 |
+
|
88 |
+
if ($json_response) {
|
89 |
+
return new WP_Error(
|
90 |
+
'fw_ext_update_github_fetch_releases_failed',
|
91 |
+
__('Github error:', 'fw') .' '. $json_response['message']
|
92 |
+
);
|
93 |
+
}
|
94 |
+
}
|
95 |
+
|
96 |
+
if ($response_code) {
|
97 |
+
return new WP_Error(
|
98 |
+
'fw_ext_update_github_fetch_releases_failed',
|
99 |
+
sprintf(
|
100 |
+
__( 'Failed to access Github repository "%s" releases. (Response code: %d)', 'fw' ),
|
101 |
+
$user_slash_repo, $response_code
|
102 |
+
)
|
103 |
+
);
|
104 |
+
} else {
|
105 |
+
return new WP_Error(
|
106 |
+
'fw_ext_update_github_fetch_releases_failed',
|
107 |
+
sprintf(
|
108 |
+
__( 'Failed to access Github repository "%s" releases.', 'fw' ),
|
109 |
+
$user_slash_repo
|
110 |
+
)
|
111 |
+
);
|
112 |
+
}
|
113 |
+
}
|
114 |
+
|
115 |
+
$release = json_decode($response['body'], true);
|
116 |
+
|
117 |
+
unset($response);
|
118 |
+
|
119 |
+
if (empty($release)) {
|
120 |
+
return new WP_Error(
|
121 |
+
'fw_ext_update_github_fetch_no_releases',
|
122 |
+
sprintf(__('No releases found in repository "%s".', 'fw'), $user_slash_repo)
|
123 |
+
);
|
124 |
+
}
|
125 |
+
|
126 |
+
return $release['tag_name'];
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* Get repository latest release version
|
131 |
+
*
|
132 |
+
* @param string $user_slash_repo Github 'user/repo'
|
133 |
+
* @param bool $force_check Bypass cache
|
134 |
+
* @param string $title Used in messages
|
135 |
+
*
|
136 |
+
* @return string|WP_Error
|
137 |
+
*/
|
138 |
+
private function get_latest_version($user_slash_repo, $force_check, $title)
|
139 |
+
{
|
140 |
+
if (!preg_match($this->manifest_key_regex, $user_slash_repo)) {
|
141 |
+
return new WP_Error('fw_ext_update_github_manifest_invalid',
|
142 |
+
sprintf(
|
143 |
+
__('%s manifest has invalid "github_update" parameter. Please use "user/repo" format.', 'fw'),
|
144 |
+
$title
|
145 |
+
)
|
146 |
+
);
|
147 |
+
}
|
148 |
+
|
149 |
+
$transient_id = 'fw_ext_upd_gh_fw'; // the length must be 45 characters or less
|
150 |
+
|
151 |
+
if ($force_check) {
|
152 |
+
delete_site_transient($transient_id);
|
153 |
+
|
154 |
+
$cache = array();
|
155 |
+
} else {
|
156 |
+
$cache = get_site_transient($transient_id);
|
157 |
+
|
158 |
+
if ($cache === false) {
|
159 |
+
$cache = array();
|
160 |
+
} elseif (isset($cache[$user_slash_repo])) {
|
161 |
+
return $cache[$user_slash_repo];
|
162 |
+
}
|
163 |
+
}
|
164 |
+
|
165 |
+
$latest_version = $this->fetch_latest_version($user_slash_repo);
|
166 |
+
|
167 |
+
if (empty($latest_version)) {
|
168 |
+
return new WP_Error(
|
169 |
+
'fw_ext_update_github_failed_fetch_latest_version',
|
170 |
+
sprintf(
|
171 |
+
__('Failed to fetch %s latest version from github "%s".', 'fw'),
|
172 |
+
$title, $user_slash_repo
|
173 |
+
)
|
174 |
+
);
|
175 |
+
}
|
176 |
+
|
177 |
+
if (is_wp_error($latest_version)) {
|
178 |
+
/**
|
179 |
+
* Internet connection problems or Github API requests limit reached.
|
180 |
+
* Cache fake version to prevent requests to Github API on every refresh.
|
181 |
+
*/
|
182 |
+
$cache = array_merge($cache, array($user_slash_repo => $this->fake_latest_version));
|
183 |
+
|
184 |
+
/**
|
185 |
+
* Show the error to the user because it is not visible elsewhere
|
186 |
+
*/
|
187 |
+
FW_Flash_Messages::add(
|
188 |
+
'fw_ext_github_update_error',
|
189 |
+
$latest_version->get_error_message(),
|
190 |
+
'error'
|
191 |
+
);
|
192 |
+
} else {
|
193 |
+
$cache = array_merge($cache, array($user_slash_repo => $latest_version));
|
194 |
+
}
|
195 |
+
|
196 |
+
set_site_transient(
|
197 |
+
$transient_id,
|
198 |
+
$cache,
|
199 |
+
$this->transient_expiration
|
200 |
+
);
|
201 |
+
|
202 |
+
return $latest_version;
|
203 |
+
}
|
204 |
+
|
205 |
+
/**
|
206 |
+
* @param string $user_slash_repo Github 'user/repo'
|
207 |
+
* @param string $version Requested version to download
|
208 |
+
* @param string $wp_filesystem_download_directory Allocated temporary empty directory
|
209 |
+
* @param string $title Used in messages
|
210 |
+
*
|
211 |
+
* @return string|WP_Error Path to the downloaded directory
|
212 |
+
*/
|
213 |
+
private function download($user_slash_repo, $version, $wp_filesystem_download_directory, $title)
|
214 |
+
{
|
215 |
+
$http = new WP_Http();
|
216 |
+
|
217 |
+
$response = $http->get(
|
218 |
+
$this->get_github_api_url('/repos/'. $user_slash_repo .'/releases/tags/'. $version)
|
219 |
+
);
|
220 |
+
|
221 |
+
unset($http);
|
222 |
+
|
223 |
+
$response_code = intval(wp_remote_retrieve_response_code($response));
|
224 |
+
|
225 |
+
if ($response_code !== 200) {
|
226 |
+
if ($response_code === 403) {
|
227 |
+
$json_response = json_decode($response['body'], true);
|
228 |
+
|
229 |
+
if ($json_response) {
|
230 |
+
return new WP_Error(
|
231 |
+
'fw_ext_update_github_download_releases_failed',
|
232 |
+
__('Github error:', 'fw') .' '. $json_response['message']
|
233 |
+
);
|
234 |
+
}
|
235 |
+
}
|
236 |
+
|
237 |
+
if ($response_code) {
|
238 |
+
return new WP_Error(
|
239 |
+
'fw_ext_update_github_download_releases_failed',
|
240 |
+
sprintf(
|
241 |
+
__( 'Failed to access Github repository "%s" releases. (Response code: %d)', 'fw' ),
|
242 |
+
$user_slash_repo, $response_code
|
243 |
+
)
|
244 |
+
);
|
245 |
+
} else {
|
246 |
+
return new WP_Error(
|
247 |
+
'fw_ext_update_github_download_releases_failed',
|
248 |
+
sprintf(
|
249 |
+
__( 'Failed to access Github repository "%s" releases.', 'fw' ),
|
250 |
+
$user_slash_repo
|
251 |
+
)
|
252 |
+
);
|
253 |
+
}
|
254 |
+
}
|
255 |
+
|
256 |
+
$release = json_decode($response['body'], true);
|
257 |
+
|
258 |
+
unset($response);
|
259 |
+
|
260 |
+
if (empty($release)) {
|
261 |
+
return new WP_Error(
|
262 |
+
'fw_ext_update_github_download_no_release',
|
263 |
+
sprintf(
|
264 |
+
__('%s github repository "%s" does not have the "%s" release.', 'fw'),
|
265 |
+
$title, $user_slash_repo, $version
|
266 |
+
)
|
267 |
+
);
|
268 |
+
}
|
269 |
+
|
270 |
+
$http = new WP_Http();
|
271 |
+
|
272 |
+
$response = $http->request(
|
273 |
+
'https://github.com/'. $user_slash_repo .'/archive/'. $release['tag_name'] .'.zip',
|
274 |
+
array(
|
275 |
+
'timeout' => $this->download_timeout,
|
276 |
+
)
|
277 |
+
);
|
278 |
+
|
279 |
+
unset($http);
|
280 |
+
|
281 |
+
if (intval(wp_remote_retrieve_response_code($response)) !== 200) {
|
282 |
+
return new WP_Error(
|
283 |
+
'fw_ext_update_github_download_failed',
|
284 |
+
sprintf(__('Cannot download %s zip.', 'fw'), $title)
|
285 |
+
);
|
286 |
+
}
|
287 |
+
|
288 |
+
/** @var WP_Filesystem_Base $wp_filesystem */
|
289 |
+
global $wp_filesystem;
|
290 |
+
|
291 |
+
$zip_path = $wp_filesystem_download_directory .'/temp.zip';
|
292 |
+
|
293 |
+
// save zip to file
|
294 |
+
if (!$wp_filesystem->put_contents($zip_path, $response['body'])) {
|
295 |
+
return new WP_Error(
|
296 |
+
'fw_ext_update_github_save_download_failed',
|
297 |
+
sprintf(__('Cannot save %s zip.', 'fw'), $title)
|
298 |
+
);
|
299 |
+
}
|
300 |
+
|
301 |
+
unset($response);
|
302 |
+
|
303 |
+
$unzip_result = unzip_file(
|
304 |
+
FW_WP_Filesystem::filesystem_path_to_real_path($zip_path),
|
305 |
+
$wp_filesystem_download_directory
|
306 |
+
);
|
307 |
+
|
308 |
+
if (is_wp_error($unzip_result)) {
|
309 |
+
return $unzip_result;
|
310 |
+
}
|
311 |
+
|
312 |
+
// remove zip file
|
313 |
+
if (!$wp_filesystem->delete($zip_path, false, 'f')) {
|
314 |
+
return new WP_Error(
|
315 |
+
'fw_ext_update_github_remove_downloaded_zip_failed',
|
316 |
+
sprintf(__('Cannot remove %s zip.', 'fw'), $title)
|
317 |
+
);
|
318 |
+
}
|
319 |
+
|
320 |
+
$unzipped_dir_files = $wp_filesystem->dirlist($wp_filesystem_download_directory);
|
321 |
+
|
322 |
+
if (!$unzipped_dir_files) {
|
323 |
+
return new WP_Error(
|
324 |
+
'fw_ext_update_github_unzipped_dir_fail',
|
325 |
+
__('Cannot access the unzipped directory files.', 'fw')
|
326 |
+
);
|
327 |
+
}
|
328 |
+
|
329 |
+
/**
|
330 |
+
* get first found directory
|
331 |
+
* (if everything worked well, there should be only one directory)
|
332 |
+
*/
|
333 |
+
foreach ($unzipped_dir_files as $file) {
|
334 |
+
if ($file['type'] == 'd') {
|
335 |
+
return $wp_filesystem_download_directory .'/'. $file['name'];
|
336 |
+
}
|
337 |
+
}
|
338 |
+
|
339 |
+
return new WP_Error(
|
340 |
+
'fw_ext_update_github_unzipped_dir_not_found',
|
341 |
+
sprintf(__('The unzipped %s directory not found.', 'fw'), $title)
|
342 |
+
);
|
343 |
+
}
|
344 |
+
|
345 |
+
/**
|
346 |
+
* {@inheritdoc}
|
347 |
+
* @internal
|
348 |
+
*/
|
349 |
+
public function _get_framework_latest_version($force_check)
|
350 |
+
{
|
351 |
+
$user_slash_repo = fw()->manifest->get($this->manifest_key);
|
352 |
+
|
353 |
+
if (empty($user_slash_repo)) {
|
354 |
+
return false;
|
355 |
+
}
|
356 |
+
|
357 |
+
return $this->get_latest_version(
|
358 |
+
$user_slash_repo,
|
359 |
+
$force_check,
|
360 |
+
__('Framework', 'fw')
|
361 |
+
);
|
362 |
+
}
|
363 |
+
|
364 |
+
/**
|
365 |
+
* {@inheritdoc}
|
366 |
+
* @internal
|
367 |
+
*/
|
368 |
+
public function _download_framework($version, $wp_filesystem_download_directory)
|
369 |
+
{
|
370 |
+
return $this->download(
|
371 |
+
fw()->manifest->get($this->manifest_key),
|
372 |
+
$version,
|
373 |
+
$wp_filesystem_download_directory,
|
374 |
+
__('Framework', 'fw')
|
375 |
+
);
|
376 |
+
}
|
377 |
+
|
378 |
+
/**
|
379 |
+
* {@inheritdoc}
|
380 |
+
* @internal
|
381 |
+
*/
|
382 |
+
public function _get_theme_latest_version($force_check)
|
383 |
+
{
|
384 |
+
$user_slash_repo = fw()->theme->manifest->get($this->manifest_key);
|
385 |
+
|
386 |
+
if (empty($user_slash_repo)) {
|
387 |
+
return false;
|
388 |
+
}
|
389 |
+
|
390 |
+
return $this->get_latest_version(
|
391 |
+
$user_slash_repo,
|
392 |
+
$force_check,
|
393 |
+
__('Theme', 'fw')
|
394 |
+
);
|
395 |
+
}
|
396 |
+
|
397 |
+
/**
|
398 |
+
* {@inheritdoc}
|
399 |
+
* @internal
|
400 |
+
*/
|
401 |
+
public function _download_theme($version, $wp_filesystem_download_directory)
|
402 |
+
{
|
403 |
+
return $this->download(
|
404 |
+
fw()->theme->manifest->get($this->manifest_key),
|
405 |
+
$version,
|
406 |
+
$wp_filesystem_download_directory,
|
407 |
+
__('Theme', 'fw')
|
408 |
+
);
|
409 |
+
}
|
410 |
+
|
411 |
+
/**
|
412 |
+
* {@inheritdoc}
|
413 |
+
* @internal
|
414 |
+
*/
|
415 |
+
public function _get_extension_latest_version(FW_Extension $extension, $force_check)
|
416 |
+
{
|
417 |
+
$user_slash_repo = $extension->manifest->get($this->manifest_key);
|
418 |
+
|
419 |
+
if (empty($user_slash_repo)) {
|
420 |
+
return false;
|
421 |
+
}
|
422 |
+
|
423 |
+
return $this->get_latest_version(
|
424 |
+
$user_slash_repo,
|
425 |
+
$force_check,
|
426 |
+
sprintf(__('%s extension', 'fw'), $extension->manifest->get_name())
|
427 |
+
);
|
428 |
+
}
|
429 |
+
|
430 |
+
/**
|
431 |
+
* {@inheritdoc}
|
432 |
+
* @internal
|
433 |
+
*/
|
434 |
+
public function _download_extension(FW_Extension $extension, $version, $wp_filesystem_download_directory)
|
435 |
+
{
|
436 |
+
return $this->download(
|
437 |
+
$extension->manifest->get($this->manifest_key),
|
438 |
+
$version,
|
439 |
+
$wp_filesystem_download_directory,
|
440 |
+
sprintf(__('%s extension', 'fw'), $extension->manifest->get_name())
|
441 |
+
);
|
442 |
+
}
|
443 |
+
}
|
framework/extensions/update/extensions/github-update/manifest.php
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
|
3 |
-
$manifest = array();
|
4 |
-
|
5 |
-
$manifest['standalone'] = true;
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
|
3 |
+
$manifest = array();
|
4 |
+
|
5 |
+
$manifest['standalone'] = true;
|
framework/extensions/update/includes/classes/class--fw-ext-update-extensions-list-table.php
CHANGED
@@ -1,128 +1,128 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Display extensions with updates on the Update Page
|
5 |
-
*/
|
6 |
-
class _FW_Ext_Update_Extensions_List_Table extends WP_List_Table
|
7 |
-
{
|
8 |
-
private $items_pre_page = 1000;
|
9 |
-
|
10 |
-
private $total_items = null;
|
11 |
-
|
12 |
-
private $_extensions = array();
|
13 |
-
|
14 |
-
private $_table_columns = array();
|
15 |
-
private $_table_columns_count = 0;
|
16 |
-
|
17 |
-
public function __construct($args)
|
18 |
-
{
|
19 |
-
parent::__construct(array(
|
20 |
-
'screen' => 'fw-ext-update-extensions-update'
|
21 |
-
));
|
22 |
-
|
23 |
-
$this->_extensions = $args['extensions'];
|
24 |
-
|
25 |
-
$this->_table_columns = array(
|
26 |
-
'cb' => '<input type="checkbox" />',
|
27 |
-
'details' => fw_html_tag(
|
28 |
-
'a',
|
29 |
-
array(
|
30 |
-
'href' => '#',
|
31 |
-
'onclick' => "jQuery(this).closest('tr').find('input[type=\"checkbox\"]:first').trigger('click'); return false;"
|
32 |
-
),
|
33 |
-
__('Select All', 'fw')
|
34 |
-
),
|
35 |
-
);
|
36 |
-
$this->_table_columns_count = count($this->_table_columns);
|
37 |
-
}
|
38 |
-
|
39 |
-
public function get_columns()
|
40 |
-
{
|
41 |
-
return $this->_table_columns;
|
42 |
-
}
|
43 |
-
|
44 |
-
public function prepare_items()
|
45 |
-
{
|
46 |
-
if ($this->total_items !== null) {
|
47 |
-
return;
|
48 |
-
}
|
49 |
-
|
50 |
-
$this->total_items = count($this->_extensions);
|
51 |
-
|
52 |
-
$this->set_pagination_args(array(
|
53 |
-
'total_items' => $this->total_items,
|
54 |
-
'per_page' => $this->items_pre_page,
|
55 |
-
));
|
56 |
-
|
57 |
-
$page_num = $this->get_pagenum();
|
58 |
-
$offset = ($page_num - 1) * $this->items_pre_page;
|
59 |
-
|
60 |
-
/**
|
61 |
-
* Prepare items for output
|
62 |
-
*/
|
63 |
-
foreach ($this->_extensions as $ext_name => $ext_update) {
|
64 |
-
$extension = fw()->extensions->get($ext_name);
|
65 |
-
|
66 |
-
if (is_wp_error($ext_update)) {
|
67 |
-
$this->items[] = array(
|
68 |
-
'cb' => '<input type="checkbox" disabled />',
|
69 |
-
'details' =>
|
70 |
-
'<p>'.
|
71 |
-
'<strong>'. fw_htmlspecialchars($extension->manifest->get_name()) .'</strong>'.
|
72 |
-
'<br/>'.
|
73 |
-
'<span class="wp-ui-text-notification">'. $ext_update->get_error_message() .'</span>'.
|
74 |
-
'</p>',
|
75 |
-
);
|
76 |
-
} else {
|
77 |
-
$this->items[] = array(
|
78 |
-
'cb' => '<input type="checkbox" name="extensions['. esc_attr($ext_name) .']" />',
|
79 |
-
'details' =>
|
80 |
-
'<p>'.
|
81 |
-
'<strong>'. fw_htmlspecialchars($extension->manifest->get_name()) .'</strong>'.
|
82 |
-
'<br/>'.
|
83 |
-
sprintf(
|
84 |
-
__('You have version %s installed. Update to %s.', 'fw'),
|
85 |
-
$extension->manifest->get_version(), fw_htmlspecialchars($ext_update['fixed_latest_version'])
|
86 |
-
).
|
87 |
-
'</p>',
|
88 |
-
);
|
89 |
-
}
|
90 |
-
}
|
91 |
-
}
|
92 |
-
|
93 |
-
public function has_items()
|
94 |
-
{
|
95 |
-
$this->prepare_items();
|
96 |
-
|
97 |
-
return $this->total_items;
|
98 |
-
}
|
99 |
-
|
100 |
-
/**
|
101 |
-
* (override parent)
|
102 |
-
*/
|
103 |
-
function single_row($item)
|
104 |
-
{
|
105 |
-
static $row_class = '';
|
106 |
-
|
107 |
-
$row_class = ( $row_class == '' ? ' class="alternate"' : '' );
|
108 |
-
|
109 |
-
echo '<tr' . $row_class . '>';
|
110 |
-
echo $this->single_row_columns( $item );
|
111 |
-
echo '</tr>';
|
112 |
-
}
|
113 |
-
|
114 |
-
protected function column_cb($item)
|
115 |
-
{
|
116 |
-
echo $item['cb'];
|
117 |
-
}
|
118 |
-
|
119 |
-
protected function column_default($item, $column_name)
|
120 |
-
{
|
121 |
-
echo $item[$column_name];
|
122 |
-
}
|
123 |
-
|
124 |
-
function no_items()
|
125 |
-
{
|
126 |
-
_e('No Extensions for update.', 'fw');
|
127 |
-
}
|
128 |
-
}
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Display extensions with updates on the Update Page
|
5 |
+
*/
|
6 |
+
class _FW_Ext_Update_Extensions_List_Table extends WP_List_Table
|
7 |
+
{
|
8 |
+
private $items_pre_page = 1000;
|
9 |
+
|
10 |
+
private $total_items = null;
|
11 |
+
|
12 |
+
private $_extensions = array();
|
13 |
+
|
14 |
+
private $_table_columns = array();
|
15 |
+
private $_table_columns_count = 0;
|
16 |
+
|
17 |
+
public function __construct($args)
|
18 |
+
{
|
19 |
+
parent::__construct(array(
|
20 |
+
'screen' => 'fw-ext-update-extensions-update'
|
21 |
+
));
|
22 |
+
|
23 |
+
$this->_extensions = $args['extensions'];
|
24 |
+
|
25 |
+
$this->_table_columns = array(
|
26 |
+
'cb' => '<input type="checkbox" />',
|
27 |
+
'details' => fw_html_tag(
|
28 |
+
'a',
|
29 |
+
array(
|
30 |
+
'href' => '#',
|
31 |
+
'onclick' => "jQuery(this).closest('tr').find('input[type=\"checkbox\"]:first').trigger('click'); return false;"
|
32 |
+
),
|
33 |
+
__('Select All', 'fw')
|
34 |
+
),
|
35 |
+
);
|
36 |
+
$this->_table_columns_count = count($this->_table_columns);
|
37 |
+
}
|
38 |
+
|
39 |
+
public function get_columns()
|
40 |
+
{
|
41 |
+
return $this->_table_columns;
|
42 |
+
}
|
43 |
+
|
44 |
+
public function prepare_items()
|
45 |
+
{
|
46 |
+
if ($this->total_items !== null) {
|
47 |
+
return;
|
48 |
+
}
|
49 |
+
|
50 |
+
$this->total_items = count($this->_extensions);
|
51 |
+
|
52 |
+
$this->set_pagination_args(array(
|
53 |
+
'total_items' => $this->total_items,
|
54 |
+
'per_page' => $this->items_pre_page,
|
55 |
+
));
|
56 |
+
|
57 |
+
$page_num = $this->get_pagenum();
|
58 |
+
$offset = ($page_num - 1) * $this->items_pre_page;
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Prepare items for output
|
62 |
+
*/
|
63 |
+
foreach ($this->_extensions as $ext_name => $ext_update) {
|
64 |
+
$extension = fw()->extensions->get($ext_name);
|
65 |
+
|
66 |
+
if (is_wp_error($ext_update)) {
|
67 |
+
$this->items[] = array(
|
68 |
+
'cb' => '<input type="checkbox" disabled />',
|
69 |
+
'details' =>
|
70 |
+
'<p>'.
|
71 |
+
'<strong>'. fw_htmlspecialchars($extension->manifest->get_name()) .'</strong>'.
|
72 |
+
'<br/>'.
|
73 |
+
'<span class="wp-ui-text-notification">'. $ext_update->get_error_message() .'</span>'.
|
74 |
+
'</p>',
|
75 |
+
);
|
76 |
+
} else {
|
77 |
+
$this->items[] = array(
|
78 |
+
'cb' => '<input type="checkbox" name="extensions['. esc_attr($ext_name) .']" />',
|
79 |
+
'details' =>
|
80 |
+
'<p>'.
|
81 |
+
'<strong>'. fw_htmlspecialchars($extension->manifest->get_name()) .'</strong>'.
|
82 |
+
'<br/>'.
|
83 |
+
sprintf(
|
84 |
+
__('You have version %s installed. Update to %s.', 'fw'),
|
85 |
+
$extension->manifest->get_version(), fw_htmlspecialchars($ext_update['fixed_latest_version'])
|
86 |
+
).
|
87 |
+
'</p>',
|
88 |
+
);
|
89 |
+
}
|
90 |
+
}
|
91 |
+
}
|
92 |
+
|
93 |
+
public function has_items()
|
94 |
+
{
|
95 |
+
$this->prepare_items();
|
96 |
+
|
97 |
+
return $this->total_items;
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* (override parent)
|
102 |
+
*/
|
103 |
+
function single_row($item)
|
104 |
+
{
|
105 |
+
static $row_class = '';
|
106 |
+
|
107 |
+
$row_class = ( $row_class == '' ? ' class="alternate"' : '' );
|
108 |
+
|
109 |
+
echo '<tr' . $row_class . '>';
|
110 |
+
echo $this->single_row_columns( $item );
|
111 |
+
echo '</tr>';
|
112 |
+
}
|
113 |
+
|
114 |
+
protected function column_cb($item)
|
115 |
+
{
|
116 |
+
echo $item['cb'];
|
117 |
+
}
|
118 |
+
|
119 |
+
protected function column_default($item, $column_name)
|
120 |
+
{
|
121 |
+
echo $item[$column_name];
|
122 |
+
}
|
123 |
+
|
124 |
+
function no_items()
|
125 |
+
{
|
126 |
+
_e('No Extensions for update.', 'fw');
|
127 |
+
}
|
128 |
+
}
|
framework/extensions/update/includes/classes/class--fw-ext-update-extensions-upgrader-skin.php
CHANGED
@@ -1,36 +1,36 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
|
3 |
-
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader-skins.php';
|
4 |
-
|
5 |
-
class _FW_Ext_Update_Extensions_Upgrader_Skin extends WP_Upgrader_Skin
|
6 |
-
{
|
7 |
-
public function after()
|
8 |
-
{
|
9 |
-
$update_actions = array(
|
10 |
-
'updates_page' => fw_html_tag(
|
11 |
-
'a',
|
12 |
-
array(
|
13 |
-
'href' => self_admin_url('update-core.php'),
|
14 |
-
'title' => __('Go to updates page', 'fw'),
|
15 |
-
'target' => '_parent',
|
16 |
-
),
|
17 |
-
__('Return to Updates page', 'fw')
|
18 |
-
)
|
19 |
-
);
|
20 |
-
|
21 |
-
/**
|
22 |
-
* Filter the list of action links available following extensions update.
|
23 |
-
* @param array $update_actions Array of plugin action links.
|
24 |
-
*/
|
25 |
-
$update_actions = apply_filters('fw_ext_update_extensions_complete_actions', $update_actions);
|
26 |
-
|
27 |
-
if (!empty($update_actions)) {
|
28 |
-
$this->feedback(implode(' | ', (array)$update_actions));
|
29 |
-
}
|
30 |
-
}
|
31 |
-
|
32 |
-
public function decrement_extension_update_count($extension_name)
|
33 |
-
{
|
34 |
-
$this->decrement_update_count('fw:extension:'. $extension_name);
|
35 |
-
}
|
36 |
-
}
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
|
3 |
+
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader-skins.php';
|
4 |
+
|
5 |
+
class _FW_Ext_Update_Extensions_Upgrader_Skin extends WP_Upgrader_Skin
|
6 |
+
{
|
7 |
+
public function after()
|
8 |
+
{
|
9 |
+
$update_actions = array(
|
10 |
+
'updates_page' => fw_html_tag(
|
11 |
+
'a',
|
12 |
+
array(
|
13 |
+
'href' => self_admin_url('update-core.php'),
|
14 |
+
'title' => __('Go to updates page', 'fw'),
|
15 |
+
'target' => '_parent',
|
16 |
+
),
|
17 |
+
__('Return to Updates page', 'fw')
|
18 |
+
)
|
19 |
+
);
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Filter the list of action links available following extensions update.
|
23 |
+
* @param array $update_actions Array of plugin action links.
|
24 |
+
*/
|
25 |
+
$update_actions = apply_filters('fw_ext_update_extensions_complete_actions', $update_actions);
|
26 |
+
|
27 |
+
if (!empty($update_actions)) {
|
28 |
+
$this->feedback(implode(' | ', (array)$update_actions));
|
29 |
+
}
|
30 |
+
}
|
31 |
+
|
32 |
+
public function decrement_extension_update_count($extension_name)
|
33 |
+
{
|
34 |
+
$this->decrement_update_count('fw:extension:'. $extension_name);
|
35 |
+
}
|
36 |
+
}
|
framework/extensions/update/includes/classes/class--fw-ext-update-framework-upgrader-skin.php
CHANGED
@@ -1,33 +1,33 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
|
3 |
-
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader-skins.php';
|
4 |
-
|
5 |
-
class _FW_Ext_Update_Framework_Upgrader_Skin extends WP_Upgrader_Skin
|
6 |
-
{
|
7 |
-
public function after()
|
8 |
-
{
|
9 |
-
$this->decrement_update_count('fw');
|
10 |
-
|
11 |
-
$update_actions = array(
|
12 |
-
'updates_page' => fw_html_tag(
|
13 |
-
'a',
|
14 |
-
array(
|
15 |
-
'href' => self_admin_url('update-core.php'),
|
16 |
-
'title' => __('Go to updates page', 'fw'),
|
17 |
-
'target' => '_parent',
|
18 |
-
),
|
19 |
-
__('Return to Updates page', 'fw')
|
20 |
-
)
|
21 |
-
);
|
22 |
-
|
23 |
-
/**
|
24 |
-
* Filter the list of action links available following framework update.
|
25 |
-
* @param array $update_actions Array of plugin action links.
|
26 |
-
*/
|
27 |
-
$update_actions = apply_filters('fw_ext_update_framework_complete_actions', $update_actions);
|
28 |
-
|
29 |
-
if (!empty($update_actions)) {
|
30 |
-
$this->feedback(implode(' | ', (array)$update_actions));
|
31 |
-
}
|
32 |
-
}
|
33 |
-
}
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
|
3 |
+
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader-skins.php';
|
4 |
+
|
5 |
+
class _FW_Ext_Update_Framework_Upgrader_Skin extends WP_Upgrader_Skin
|
6 |
+
{
|
7 |
+
public function after()
|
8 |
+
{
|
9 |
+
$this->decrement_update_count('fw');
|
10 |
+
|
11 |
+
$update_actions = array(
|
12 |
+
'updates_page' => fw_html_tag(
|
13 |
+
'a',
|
14 |
+
array(
|
15 |
+
'href' => self_admin_url('update-core.php'),
|
16 |
+
'title' => __('Go to updates page', 'fw'),
|
17 |
+
'target' => '_parent',
|
18 |
+
),
|
19 |
+
__('Return to Updates page', 'fw')
|
20 |
+
)
|
21 |
+
);
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Filter the list of action links available following framework update.
|
25 |
+
* @param array $update_actions Array of plugin action links.
|
26 |
+
*/
|
27 |
+
$update_actions = apply_filters('fw_ext_update_framework_complete_actions', $update_actions);
|
28 |
+
|
29 |
+
if (!empty($update_actions)) {
|
30 |
+
$this->feedback(implode(' | ', (array)$update_actions));
|
31 |
+
}
|
32 |
+
}
|
33 |
+
}
|
framework/extensions/update/includes/classes/class--fw-ext-update-theme-upgrader-skin.php
CHANGED
@@ -1,33 +1,33 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
|
3 |
-
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader-skins.php';
|
4 |
-
|
5 |
-
class _FW_Ext_Update_Theme_Upgrader_Skin extends WP_Upgrader_Skin
|
6 |
-
{
|
7 |
-
public function after()
|
8 |
-
{
|
9 |
-
$this->decrement_update_count('fw:theme');
|
10 |
-
|
11 |
-
$update_actions = array(
|
12 |
-
'updates_page' => fw_html_tag(
|
13 |
-
'a',
|
14 |
-
array(
|
15 |
-
'href' => self_admin_url('update-core.php'),
|
16 |
-
'title' => __('Go to updates page', 'fw'),
|
17 |
-
'target' => '_parent',
|
18 |
-
),
|
19 |
-
__('Return to Updates page', 'fw')
|
20 |
-
)
|
21 |
-
);
|
22 |
-
|
23 |
-
/**
|
24 |
-
* Filter the list of action links available following theme update.
|
25 |
-
* @param array $update_actions Array of plugin action links.
|
26 |
-
*/
|
27 |
-
$update_actions = apply_filters('fw_ext_update_theme_complete_actions', $update_actions);
|
28 |
-
|
29 |
-
if (!empty($update_actions)) {
|
30 |
-
$this->feedback(implode(' | ', (array)$update_actions));
|
31 |
-
}
|
32 |
-
}
|
33 |
-
}
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
|
3 |
+
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader-skins.php';
|
4 |
+
|
5 |
+
class _FW_Ext_Update_Theme_Upgrader_Skin extends WP_Upgrader_Skin
|
6 |
+
{
|
7 |
+
public function after()
|
8 |
+
{
|
9 |
+
$this->decrement_update_count('fw:theme');
|
10 |
+
|
11 |
+
$update_actions = array(
|
12 |
+
'updates_page' => fw_html_tag(
|
13 |
+
'a',
|
14 |
+
array(
|
15 |
+
'href' => self_admin_url('update-core.php'),
|
16 |
+
'title' => __('Go to updates page', 'fw'),
|
17 |
+
'target' => '_parent',
|
18 |
+
),
|
19 |
+
__('Return to Updates page', 'fw')
|
20 |
+
)
|
21 |
+
);
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Filter the list of action links available following theme update.
|
25 |
+
* @param array $update_actions Array of plugin action links.
|
26 |
+
*/
|
27 |
+
$update_actions = apply_filters('fw_ext_update_theme_complete_actions', $update_actions);
|
28 |
+
|
29 |
+
if (!empty($update_actions)) {
|
30 |
+
$this->feedback(implode(' | ', (array)$update_actions));
|
31 |
+
}
|
32 |
+
}
|
33 |
+
}
|
framework/extensions/update/includes/extends/class-fw-ext-update-service.php
CHANGED
@@ -1,105 +1,105 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Extend this class if you want to create a new update service
|
5 |
-
*/
|
6 |
-
abstract class FW_Ext_Update_Service extends FW_Extension
|
7 |
-
{
|
8 |
-
/**
|
9 |
-
* Return latest version of the framework if this service supports framework update
|
10 |
-
*
|
11 |
-
* @param bool $force_check Check now, do not use cache
|
12 |
-
* @return string|false|WP_Error
|
13 |
-
* false Does not know how to work with extension.
|
14 |
-
* WP_Error Knows how to work with it, but there is an error
|
15 |
-
* string Everything is ok, here is latest version
|
16 |
-
*
|
17 |
-
* @internal
|
18 |
-
*/
|
19 |
-
public function _get_framework_latest_version($force_check)
|
20 |
-
{
|
21 |
-
return false;
|
22 |
-
}
|
23 |
-
|
24 |
-
/**
|
25 |
-
* Download (and extract) framework files
|
26 |
-
*
|
27 |
-
* ! Work with global $wp_filesystem; Do not use base php filesystem functions
|
28 |
-
*
|
29 |
-
* @param $version Version to download
|
30 |
-
* @param string $wp_filesystem_download_directory Empty directory offered for download files in it
|
31 |
-
* @return string|false|WP_Error Path to WP Filesystem directory with downloaded (and extracted) files
|
32 |
-
*
|
33 |
-
* @internal
|
34 |
-
*/
|
35 |
-
public function _download_framework($version, $wp_filesystem_download_directory)
|
36 |
-
{
|
37 |
-
return false;
|
38 |
-
}
|
39 |
-
|
40 |
-
/**
|
41 |
-
* Return latest version of the theme if this service supports theme update
|
42 |
-
*
|
43 |
-
* @param bool $force_check Check now, do not use cache
|
44 |
-
* @return string|false|WP_Error
|
45 |
-
* false Does not know how to work with extension.
|
46 |
-
* WP_Error Knows how to work with it, but there is an error
|
47 |
-
* string Everything is ok, here is latest version
|
48 |
-
*
|
49 |
-
* @internal
|
50 |
-
*/
|
51 |
-
public function _get_theme_latest_version($force_check)
|
52 |
-
{
|
53 |
-
return false;
|
54 |
-
}
|
55 |
-
|
56 |
-
/**
|
57 |
-
* Download (and extract) theme files
|
58 |
-
*
|
59 |
-
* ! Work with global $wp_filesystem; Do not use base php filesystem functions
|
60 |
-
*
|
61 |
-
* @param $version Version to download
|
62 |
-
* @param string $wp_filesystem_download_directory Empty directory offered for download files in it
|
63 |
-
* @return string|false|WP_Error Path to WP Filesystem directory with downloaded (and extracted) files
|
64 |
-
*
|
65 |
-
* @internal
|
66 |
-
*/
|
67 |
-
public function _download_theme($version, $wp_filesystem_download_directory)
|
68 |
-
{
|
69 |
-
return false;
|
70 |
-
}
|
71 |
-
|
72 |
-
/**
|
73 |
-
* Return latest version of the extension if this service supports extension update
|
74 |
-
*
|
75 |
-
* @param FW_Extension $extension
|
76 |
-
* @param bool $force_check Check now, do not use cache
|
77 |
-
* @return string|false|WP_Error
|
78 |
-
* false Does not know how to work with extension.
|
79 |
-
* WP_Error Knows how to work with it, but there is an error
|
80 |
-
* string Everything is ok, here is latest version
|
81 |
-
*
|
82 |
-
* @internal
|
83 |
-
*/
|
84 |
-
public function _get_extension_latest_version(FW_Extension $extension, $force_check)
|
85 |
-
{
|
86 |
-
return false;
|
87 |
-
}
|
88 |
-
|
89 |
-
/**
|
90 |
-
* Download (and extract) extension
|
91 |
-
*
|
92 |
-
* ! Work with global $wp_filesystem; Do not use base php filesystem functions
|
93 |
-
*
|
94 |
-
* @param FW_Extension $extension
|
95 |
-
* @param $version Version to download
|
96 |
-
* @param string $wp_filesystem_download_directory Empty directory offered for download files in it
|
97 |
-
* @return string|false|WP_Error Path to WP Filesystem directory with downloaded (and extracted) files
|
98 |
-
*
|
99 |
-
* @internal
|
100 |
-
*/
|
101 |
-
public function _download_extension(FW_Extension $extension, $version, $wp_filesystem_download_directory)
|
102 |
-
{
|
103 |
-
return false;
|
104 |
-
}
|
105 |
-
}
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Extend this class if you want to create a new update service
|
5 |
+
*/
|
6 |
+
abstract class FW_Ext_Update_Service extends FW_Extension
|
7 |
+
{
|
8 |
+
/**
|
9 |
+
* Return latest version of the framework if this service supports framework update
|
10 |
+
*
|
11 |
+
* @param bool $force_check Check now, do not use cache
|
12 |
+
* @return string|false|WP_Error
|
13 |
+
* false Does not know how to work with extension.
|
14 |
+
* WP_Error Knows how to work with it, but there is an error
|
15 |
+
* string Everything is ok, here is latest version
|
16 |
+
*
|
17 |
+
* @internal
|
18 |
+
*/
|
19 |
+
public function _get_framework_latest_version($force_check)
|
20 |
+
{
|
21 |
+
return false;
|
22 |
+
}
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Download (and extract) framework files
|
26 |
+
*
|
27 |
+
* ! Work with global $wp_filesystem; Do not use base php filesystem functions
|
28 |
+
*
|
29 |
+
* @param $version Version to download
|
30 |
+
* @param string $wp_filesystem_download_directory Empty directory offered for download files in it
|
31 |
+
* @return string|false|WP_Error Path to WP Filesystem directory with downloaded (and extracted) files
|
32 |
+
*
|
33 |
+
* @internal
|
34 |
+
*/
|
35 |
+
public function _download_framework($version, $wp_filesystem_download_directory)
|
36 |
+
{
|
37 |
+
return false;
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Return latest version of the theme if this service supports theme update
|
42 |
+
*
|
43 |
+
* @param bool $force_check Check now, do not use cache
|
44 |
+
* @return string|false|WP_Error
|
45 |
+
* false Does not know how to work with extension.
|
46 |
+
* WP_Error Knows how to work with it, but there is an error
|
47 |
+
* string Everything is ok, here is latest version
|
48 |
+
*
|
49 |
+
* @internal
|
50 |
+
*/
|
51 |
+
public function _get_theme_latest_version($force_check)
|
52 |
+
{
|
53 |
+
return false;
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Download (and extract) theme files
|
58 |
+
*
|
59 |
+
* ! Work with global $wp_filesystem; Do not use base php filesystem functions
|
60 |
+
*
|
61 |
+
* @param $version Version to download
|
62 |
+
* @param string $wp_filesystem_download_directory Empty directory offered for download files in it
|
63 |
+
* @return string|false|WP_Error Path to WP Filesystem directory with downloaded (and extracted) files
|
64 |
+
*
|
65 |
+
* @internal
|
66 |
+
*/
|
67 |
+
public function _download_theme($version, $wp_filesystem_download_directory)
|
68 |
+
{
|
69 |
+
return false;
|
70 |
+
}
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Return latest version of the extension if this service supports extension update
|
74 |
+
*
|
75 |
+
* @param FW_Extension $extension
|
76 |
+
* @param bool $force_check Check now, do not use cache
|
77 |
+
* @return string|false|WP_Error
|
78 |
+
* false Does not know how to work with extension.
|
79 |
+
* WP_Error Knows how to work with it, but there is an error
|
80 |
+
* string Everything is ok, here is latest version
|
81 |
+
*
|
82 |
+
* @internal
|
83 |
+
*/
|
84 |
+
public function _get_extension_latest_version(FW_Extension $extension, $force_check)
|
85 |
+
{
|
86 |
+
return false;
|
87 |
+
}
|
88 |
+
|
89 |
+
/**
|
90 |
+
* Download (and extract) extension
|
91 |
+
*
|
92 |
+
* ! Work with global $wp_filesystem; Do not use base php filesystem functions
|
93 |
+
*
|
94 |
+
* @param FW_Extension $extension
|
95 |
+
* @param $version Version to download
|
96 |
+
* @param string $wp_filesystem_download_directory Empty directory offered for download files in it
|
97 |
+
* @return string|false|WP_Error Path to WP Filesystem directory with downloaded (and extracted) files
|
98 |
+
*
|
99 |
+
* @internal
|
100 |
+
*/
|
101 |
+
public function _download_extension(FW_Extension $extension, $version, $wp_filesystem_download_directory)
|
102 |
+
{
|
103 |
+
return false;
|
104 |
+
}
|
105 |
+
}
|
framework/extensions/update/manifest.php
CHANGED
@@ -1,10 +1,10 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
|
3 |
-
$manifest = array();
|
4 |
-
|
5 |
-
$manifest['name'] = __('Update', 'fw');
|
6 |
-
$manifest['description'] = __('Keep you framework, extensions and theme up to date.', 'fw');
|
7 |
-
$manifest['standalone'] = true;
|
8 |
-
|
9 |
-
$manifest['version'] = '1.0.10';
|
10 |
-
$manifest['github_update'] = 'ThemeFuse/Unyson-Update-Extension';
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
|
3 |
+
$manifest = array();
|
4 |
+
|
5 |
+
$manifest['name'] = __('Update', 'fw');
|
6 |
+
$manifest['description'] = __('Keep you framework, extensions and theme up to date.', 'fw');
|
7 |
+
$manifest['standalone'] = true;
|
8 |
+
|
9 |
+
$manifest['version'] = '1.0.10';
|
10 |
+
$manifest['github_update'] = 'ThemeFuse/Unyson-Update-Extension';
|
framework/extensions/update/static.php
CHANGED
@@ -1,14 +1,14 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
|
3 |
-
$extension = fw()->extensions->get('update');
|
4 |
-
|
5 |
-
if (fw_current_screen_match(array('only' => array(array('id' => 'update-core'))))) {
|
6 |
-
// Include only on update page
|
7 |
-
|
8 |
-
wp_enqueue_style(
|
9 |
-
'fw-ext-'. $extension->get_name() .'-update-page',
|
10 |
-
$extension->get_declared_URI('/static/css/admin-update-page.css'),
|
11 |
-
array(),
|
12 |
-
$extension->manifest->get_version()
|
13 |
-
);
|
14 |
-
}
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
|
3 |
+
$extension = fw()->extensions->get('update');
|
4 |
+
|
5 |
+
if (fw_current_screen_match(array('only' => array(array('id' => 'update-core'))))) {
|
6 |
+
// Include only on update page
|
7 |
+
|
8 |
+
wp_enqueue_style(
|
9 |
+
'fw-ext-'. $extension->get_name() .'-update-page',
|
10 |
+
$extension->get_declared_URI('/static/css/admin-update-page.css'),
|
11 |
+
array(),
|
12 |
+
$extension->manifest->get_version()
|
13 |
+
);
|
14 |
+
}
|
framework/extensions/update/static/css/admin-update-page.css
CHANGED
@@ -1,3 +1,3 @@
|
|
1 |
-
#fw-ext-update-extensions .tablenav {
|
2 |
-
display: none;
|
3 |
}
|
1 |
+
#fw-ext-update-extensions .tablenav {
|
2 |
+
display: none;
|
3 |
}
|
framework/extensions/update/views/updates-list.php
CHANGED
@@ -1,113 +1,113 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
/**
|
3 |
-
* @var array $updates
|
4 |
-
*/
|
5 |
-
?>
|
6 |
-
|
7 |
-
<?php if ($updates['framework'] !== false): ?>
|
8 |
-
<div id="fw-ext-update-framework">
|
9 |
-
<a name="fw-framework"></a>
|
10 |
-
<h3><?php _e('Framework', 'fw') ?></h3>
|
11 |
-
<?php if (empty($updates['framework'])): ?>
|
12 |
-
<p><?php echo sprintf(__('You have the latest version of %s.', 'fw'), fw()->manifest->get_name()) ?></p>
|
13 |
-
<?php else: ?>
|
14 |
-
<?php if (is_wp_error($updates['framework'])): ?>
|
15 |
-
<p class="wp-ui-text-notification"><?php echo $updates['framework']->get_error_message() ?></p>
|
16 |
-
<?php else: ?>
|
17 |
-
<form id="fw-ext-update-framework" method="post" action="update-core.php?action=fw-update-framework">
|
18 |
-
<p><?php
|
19 |
-
_e(sprintf('You have version %s installed. Update to %s.',
|
20 |
-
fw()->manifest->get_version(),
|
21 |
-
$updates['framework']['fixed_latest_version']
|
22 |
-
), 'fw')
|
23 |
-
?></p>
|
24 |
-
<?php wp_nonce_field(-1, '_nonce_fw_ext_update_framework'); ?>
|
25 |
-
<p><input class="button" type="submit" value="<?php echo esc_attr(__('Update Framework', 'fw')) ?>" name="update"></p>
|
26 |
-
</form>
|
27 |
-
<?php endif; ?>
|
28 |
-
<?php endif; ?>
|
29 |
-
</div>
|
30 |
-
<?php endif; ?>
|
31 |
-
|
32 |
-
<?php if ($updates['theme'] !== false): ?>
|
33 |
-
<div id="fw-ext-update-theme">
|
34 |
-
<a name="fw-theme"></a>
|
35 |
-
<h3><?php $theme = wp_get_theme(); _e(sprintf('%s Theme', $theme->parent()->get('Name')), 'fw') ?></h3>
|
36 |
-
<?php if (empty($updates['theme'])): ?>
|
37 |
-
<p><?php _e('Your theme is up to date.', 'fw') ?></p>
|
38 |
-
<?php else: ?>
|
39 |
-
<?php if (is_wp_error($updates['theme'])): ?>
|
40 |
-
<p class="wp-ui-text-notification"><?php echo $updates['theme']->get_error_message() ?></p>
|
41 |
-
<?php else: ?>
|
42 |
-
<form id="fw-ext-update-theme" method="post" action="update-core.php?action=fw-update-theme">
|
43 |
-
<p><?php
|
44 |
-
_e(sprintf('You have version %s installed. Update to %s.',
|
45 |
-
fw()->theme->manifest->get_version(),
|
46 |
-
$updates['theme']['fixed_latest_version']
|
47 |
-
), 'fw')
|
48 |
-
?></p>
|
49 |
-
<?php wp_nonce_field(-1, '_nonce_fw_ext_update_theme'); ?>
|
50 |
-
<p><input class="button" type="submit" value="<?php echo esc_attr(__('Update Theme', 'fw')) ?>" name="update"></p>
|
51 |
-
</form>
|
52 |
-
<?php endif; ?>
|
53 |
-
<?php endif; ?>
|
54 |
-
</div>
|
55 |
-
<?php endif; ?>
|
56 |
-
|
57 |
-
<?php if (true): ?>
|
58 |
-
<div id="fw-ext-update-extensions">
|
59 |
-
<a name="fw-extensions"></a>
|
60 |
-
<h3><?php echo sprintf(__('%s Extensions', 'fw'), fw()->manifest->get_name()) ?></h3>
|
61 |
-
<?php if (empty($updates['extensions'])): ?>
|
62 |
-
<p><?php echo sprintf(__('You have the latest version of %s Extensions.', 'fw'), fw()->manifest->get_name()); ?></p>
|
63 |
-
<?php else: ?>
|
64 |
-
<?php
|
65 |
-
$one_update_mode = fw()->extensions->get('update')->get_config('extensions_as_one_update');
|
66 |
-
|
67 |
-
foreach ($updates['extensions'] as $extension) {
|
68 |
-
if (is_wp_error($extension)) {
|
69 |
-
/**
|
70 |
-
* Cancel the "One update mode" and display all extensions list table with details
|
71 |
-
* if at least one extension has an error that needs to be visible
|
72 |
-
*/
|
73 |
-
$one_update_mode = false;
|
74 |
-
break;
|
75 |
-
}
|
76 |
-
}
|
77 |
-
?>
|
78 |
-
<form id="fw-ext-update-extensions" method="post" action="update-core.php?action=fw-update-extensions">
|
79 |
-
<div class="fw-ext-update-extensions-form-detailed" <?php if ($one_update_mode): ?>style="display: none;"<?php endif; ?>>
|
80 |
-
<p><input class="button" type="submit" value="<?php echo esc_attr(__('Update Extensions', 'fw')) ?>" name="update"></p>
|
81 |
-
<?php
|
82 |
-
if (!class_exists('_FW_Ext_Update_Extensions_List_Table')) {
|
83 |
-
fw_include_file_isolated(
|
84 |
-
fw()->extensions->get('update')->get_declared_path('/includes/classes/class--fw-ext-update-extensions-list-table.php')
|
85 |
-
);
|
86 |
-
}
|
87 |
-
|
88 |
-
$list_table = new _FW_Ext_Update_Extensions_List_Table(array(
|
89 |
-
'extensions' => $updates['extensions']
|
90 |
-
));
|
91 |
-
|
92 |
-
$list_table->display();
|
93 |
-
?>
|
94 |
-
<?php wp_nonce_field(-1, '_nonce_fw_ext_update_extensions'); ?>
|
95 |
-
<p><input class="button" type="submit" value="<?php echo esc_attr(__('Update Extensions', 'fw')) ?>" name="update"></p>
|
96 |
-
</div>
|
97 |
-
<?php if ($one_update_mode): ?>
|
98 |
-
<div class="fw-ext-update-extensions-form-simple">
|
99 |
-
<p style="color:#d54e21;"><?php _e('New extensions updates available.', 'fw'); ?></p>
|
100 |
-
<p><input class="button" type="submit" value="<?php echo esc_attr(__('Update Extensions', 'fw')) ?>" name="update"></p>
|
101 |
-
<script type="text/javascript">
|
102 |
-
jQuery(function($){
|
103 |
-
$('form#fw-ext-update-extensions').on('submit', function(){
|
104 |
-
$(this).find('.check-column input[type="checkbox"]').prop('checked', true);
|
105 |
-
});
|
106 |
-
});
|
107 |
-
</script>
|
108 |
-
</div>
|
109 |
-
<?php endif; ?>
|
110 |
-
</form>
|
111 |
-
<?php endif; ?>
|
112 |
-
</div>
|
113 |
-
<?php endif; ?>
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
/**
|
3 |
+
* @var array $updates
|
4 |
+
*/
|
5 |
+
?>
|
6 |
+
|
7 |
+
<?php if ($updates['framework'] !== false): ?>
|
8 |
+
<div id="fw-ext-update-framework">
|
9 |
+
<a name="fw-framework"></a>
|
10 |
+
<h3><?php _e('Framework', 'fw') ?></h3>
|
11 |
+
<?php if (empty($updates['framework'])): ?>
|
12 |
+
<p><?php echo sprintf(__('You have the latest version of %s.', 'fw'), fw()->manifest->get_name()) ?></p>
|
13 |
+
<?php else: ?>
|
14 |
+
<?php if (is_wp_error($updates['framework'])): ?>
|
15 |
+
<p class="wp-ui-text-notification"><?php echo $updates['framework']->get_error_message() ?></p>
|
16 |
+
<?php else: ?>
|
17 |
+
<form id="fw-ext-update-framework" method="post" action="update-core.php?action=fw-update-framework">
|
18 |
+
<p><?php
|
19 |
+
_e(sprintf('You have version %s installed. Update to %s.',
|
20 |
+
fw()->manifest->get_version(),
|
21 |
+
$updates['framework']['fixed_latest_version']
|
22 |
+
), 'fw')
|
23 |
+
?></p>
|
24 |
+
<?php wp_nonce_field(-1, '_nonce_fw_ext_update_framework'); ?>
|
25 |
+
<p><input class="button" type="submit" value="<?php echo esc_attr(__('Update Framework', 'fw')) ?>" name="update"></p>
|
26 |
+
</form>
|
27 |
+
<?php endif; ?>
|
28 |
+
<?php endif; ?>
|
29 |
+
</div>
|
30 |
+
<?php endif; ?>
|
31 |
+
|
32 |
+
<?php if ($updates['theme'] !== false): ?>
|
33 |
+
<div id="fw-ext-update-theme">
|
34 |
+
<a name="fw-theme"></a>
|
35 |
+
<h3><?php $theme = wp_get_theme(); _e(sprintf('%s Theme', $theme->parent()->get('Name')), 'fw') ?></h3>
|
36 |
+
<?php if (empty($updates['theme'])): ?>
|
37 |
+
<p><?php _e('Your theme is up to date.', 'fw') ?></p>
|
38 |
+
<?php else: ?>
|
39 |
+
<?php if (is_wp_error($updates['theme'])): ?>
|
40 |
+
<p class="wp-ui-text-notification"><?php echo $updates['theme']->get_error_message() ?></p>
|
41 |
+
<?php else: ?>
|
42 |
+
<form id="fw-ext-update-theme" method="post" action="update-core.php?action=fw-update-theme">
|
43 |
+
<p><?php
|
44 |
+
_e(sprintf('You have version %s installed. Update to %s.',
|
45 |
+
fw()->theme->manifest->get_version(),
|
46 |
+
$updates['theme']['fixed_latest_version']
|
47 |
+
), 'fw')
|
48 |
+
?></p>
|
49 |
+
<?php wp_nonce_field(-1, '_nonce_fw_ext_update_theme'); ?>
|
50 |
+
<p><input class="button" type="submit" value="<?php echo esc_attr(__('Update Theme', 'fw')) ?>" name="update"></p>
|
51 |
+
</form>
|
52 |
+
<?php endif; ?>
|
53 |
+
<?php endif; ?>
|
54 |
+
</div>
|
55 |
+
<?php endif; ?>
|
56 |
+
|
57 |
+
<?php if (true): ?>
|
58 |
+
<div id="fw-ext-update-extensions">
|
59 |
+
<a name="fw-extensions"></a>
|
60 |
+
<h3><?php echo sprintf(__('%s Extensions', 'fw'), fw()->manifest->get_name()) ?></h3>
|
61 |
+
<?php if (empty($updates['extensions'])): ?>
|
62 |
+
<p><?php echo sprintf(__('You have the latest version of %s Extensions.', 'fw'), fw()->manifest->get_name()); ?></p>
|
63 |
+
<?php else: ?>
|
64 |
+
<?php
|
65 |
+
$one_update_mode = fw()->extensions->get('update')->get_config('extensions_as_one_update');
|
66 |
+
|
67 |
+
foreach ($updates['extensions'] as $extension) {
|
68 |
+
if (is_wp_error($extension)) {
|
69 |
+
/**
|
70 |
+
* Cancel the "One update mode" and display all extensions list table with details
|
71 |
+
* if at least one extension has an error that needs to be visible
|
72 |
+
*/
|
73 |
+
$one_update_mode = false;
|
74 |
+
break;
|
75 |
+
}
|
76 |
+
}
|
77 |
+
?>
|
78 |
+
<form id="fw-ext-update-extensions" method="post" action="update-core.php?action=fw-update-extensions">
|
79 |
+
<div class="fw-ext-update-extensions-form-detailed" <?php if ($one_update_mode): ?>style="display: none;"<?php endif; ?>>
|
80 |
+
<p><input class="button" type="submit" value="<?php echo esc_attr(__('Update Extensions', 'fw')) ?>" name="update"></p>
|
81 |
+
<?php
|
82 |
+
if (!class_exists('_FW_Ext_Update_Extensions_List_Table')) {
|
83 |
+
fw_include_file_isolated(
|
84 |
+
fw()->extensions->get('update')->get_declared_path('/includes/classes/class--fw-ext-update-extensions-list-table.php')
|
85 |
+
);
|
86 |
+
}
|
87 |
+
|
88 |
+
$list_table = new _FW_Ext_Update_Extensions_List_Table(array(
|
89 |
+
'extensions' => $updates['extensions']
|
90 |
+
));
|
91 |
+
|
92 |
+
$list_table->display();
|
93 |
+
?>
|
94 |
+
<?php wp_nonce_field(-1, '_nonce_fw_ext_update_extensions'); ?>
|
95 |
+
<p><input class="button" type="submit" value="<?php echo esc_attr(__('Update Extensions', 'fw')) ?>" name="update"></p>
|
96 |
+
</div>
|
97 |
+
<?php if ($one_update_mode): ?>
|
98 |
+
<div class="fw-ext-update-extensions-form-simple">
|
99 |
+
<p style="color:#d54e21;"><?php _e('New extensions updates available.', 'fw'); ?></p>
|
100 |
+
<p><input class="button" type="submit" value="<?php echo esc_attr(__('Update Extensions', 'fw')) ?>" name="update"></p>
|
101 |
+
<script type="text/javascript">
|
102 |
+
jQuery(function($){
|
103 |
+
$('form#fw-ext-update-extensions').on('submit', function(){
|
104 |
+
$(this).find('.check-column input[type="checkbox"]').prop('checked', true);
|
105 |
+
});
|
106 |
+
});
|
107 |
+
</script>
|
108 |
+
</div>
|
109 |
+
<?php endif; ?>
|
110 |
+
</form>
|
111 |
+
<?php endif; ?>
|
112 |
+
</div>
|
113 |
+
<?php endif; ?>
|
framework/helpers/class-fw-access-key.php
CHANGED
@@ -1,43 +1,43 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Used in public callbacks to allow call only from known caller
|
5 |
-
*
|
6 |
-
* For e.g. Inside some class is created an instance of FW_Access_Key with unique key 'whatever',
|
7 |
-
* so nobody else can create another instance with same key, only that class owns that unique key.
|
8 |
-
* Some public callback (function or method) that wants to allow to be called only by that class,
|
9 |
-
* sets an requirement that some parameter should be an instance on FW_Access_Key and its key should be 'whatever'
|
10 |
-
*
|
11 |
-
* function my_function(FW_Access_Key $key, $another_parameter) {
|
12 |
-
* if ($key->get_key() !== 'whatever') {
|
13 |
-
* trigger_error('Call denied', E_USER_ERROR);
|
14 |
-
* }
|
15 |
-
*
|
16 |
-
* //...
|
17 |
-
* }
|
18 |
-
*/
|
19 |
-
final class FW_Access_Key
|
20 |
-
{
|
21 |
-
private static $created_keys = array();
|
22 |
-
|
23 |
-
private $key;
|
24 |
-
|
25 |
-
final public function get_key()
|
26 |
-
{
|
27 |
-
return $this->key;
|
28 |
-
}
|
29 |
-
|
30 |
-
/**
|
31 |
-
* @param string $unique_key unique
|
32 |
-
*/
|
33 |
-
final public function __construct($unique_key)
|
34 |
-
{
|
35 |
-
if (isset(self::$created_keys[$unique_key])) {
|
36 |
-
trigger_error('Key "'. $unique_key .'" already defined', E_USER_ERROR);
|
37 |
-
}
|
38 |
-
|
39 |
-
self::$created_keys[$unique_key] = true;
|
40 |
-
|
41 |
-
$this->key = $unique_key;
|
42 |
-
}
|
43 |
-
}
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Used in public callbacks to allow call only from known caller
|
5 |
+
*
|
6 |
+
* For e.g. Inside some class is created an instance of FW_Access_Key with unique key 'whatever',
|
7 |
+
* so nobody else can create another instance with same key, only that class owns that unique key.
|
8 |
+
* Some public callback (function or method) that wants to allow to be called only by that class,
|
9 |
+
* sets an requirement that some parameter should be an instance on FW_Access_Key and its key should be 'whatever'
|
10 |
+
*
|
11 |
+
* function my_function(FW_Access_Key $key, $another_parameter) {
|
12 |
+
* if ($key->get_key() !== 'whatever') {
|
13 |
+
* trigger_error('Call denied', E_USER_ERROR);
|
14 |
+
* }
|
15 |
+
*
|
16 |
+
* //...
|
17 |
+
* }
|
18 |
+
*/
|
19 |
+
final class FW_Access_Key
|
20 |
+
{
|
21 |
+
private static $created_keys = array();
|
22 |
+
|
23 |
+
private $key;
|
24 |
+
|
25 |
+
final public function get_key()
|
26 |
+
{
|
27 |
+
return $this->key;
|
28 |
+
}
|
29 |
+
|
30 |
+
/**
|
31 |
+
* @param string $unique_key unique
|
32 |
+
*/
|
33 |
+
final public function __construct($unique_key)
|
34 |
+
{
|
35 |
+
if (isset(self::$created_keys[$unique_key])) {
|
36 |
+
trigger_error('Key "'. $unique_key .'" already defined', E_USER_ERROR);
|
37 |
+
}
|
38 |
+
|
39 |
+
self::$created_keys[$unique_key] = true;
|
40 |
+
|
41 |
+
$this->key = $unique_key;
|
42 |
+
}
|
43 |
+
}
|
framework/helpers/class-fw-cache.php
CHANGED
@@ -1,303 +1,303 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Memory Cache
|
5 |
-
* Only for internal usage in other functions/methods, because it throws exceptions
|
6 |
-
*
|
7 |
-
* Recommended usage example:
|
8 |
-
* try {
|
9 |
-
* $value = FW_Cache::get('some/key');
|
10 |
-
* } catch(FW_Cache_Not_Found_Exception $e) {
|
11 |
-
* $value = get_value_from_somewhere();
|
12 |
-
*
|
13 |
-
* FW_Cache::set('some/key', $value);
|
14 |
-
*
|
15 |
-
* // (!) after set, do not do this:
|
16 |
-
* $value = FW_Cache::get('some/key');
|
17 |
-
* // because there is no guaranty that FW_Cache::set('some/key', $value); succeeded
|
18 |
-
* // trust only your $value, cache can do clean-up right after set() and remove the value you tried to set
|
19 |
-
* }
|
20 |
-
*
|
21 |
-
* // use $value ...
|
22 |
-
*/
|
23 |
-
class FW_Cache
|
24 |
-
{
|
25 |
-
/**
|
26 |
-
* The actual cache
|
27 |
-
* @var array
|
28 |
-
*/
|
29 |
-
protected static $cache = array();
|
30 |
-
|
31 |
-
/**
|
32 |
-
* If the PHP will have less that this memory, the cache will try to delete parts from its array to free memory
|
33 |
-
*
|
34 |
-
* (1024 * 1024 = 1048576 = 1 Mb) * 10
|
35 |
-
*/
|
36 |
-
protected static $min_free_memory = 10485760;
|
37 |
-
|
38 |
-
/**
|
39 |
-
* A special value that is used to detect if value was found in cache
|
40 |
-
* We can't use null|false because these can be values set by user and we can't treat them as not existing values
|
41 |
-
*/
|
42 |
-
protected static $not_found_value;
|
43 |
-
|
44 |
-
/**
|
45 |
-
* The amount of times the data was already stored in the cache.
|
46 |
-
* @var int
|
47 |
-
* @since 2.4.17
|
48 |
-
*/
|
49 |
-
protected static $hits = 0;
|
50 |
-
|
51 |
-
/**
|
52 |
-
* Amount of times the cache did not have the value in cache.
|
53 |
-
* @var int
|
54 |
-
* @since 2.4.17
|
55 |
-
*/
|
56 |
-
protected static $misses = 0;
|
57 |
-
|
58 |
-
/**
|
59 |
-
* Amount of times the cache free was called.
|
60 |
-
* @var int
|
61 |
-
* @since 2.4.17
|
62 |
-
*/
|
63 |
-
protected static $freed = 0;
|
64 |
-
|
65 |
-
protected static function get_memory_limit()
|
66 |
-
{
|
67 |
-
$memory_limit = ini_get('memory_limit');
|
68 |
-
|
69 |
-
switch (substr($memory_limit, -1)) {
|
70 |
-
case 'M': return intval($memory_limit) * 1024 * 1024;
|
71 |
-
case 'K': return intval($memory_limit) * 1024;
|
72 |
-
case 'G': return intval($memory_limit) * 1024 * 1024 * 1024;
|
73 |
-
default: return intval($memory_limit) * 1024 * 1024;
|
74 |
-
}
|
75 |
-
}
|
76 |
-
|
77 |
-
protected static function memory_exceeded()
|
78 |
-
{
|
79 |
-
return memory_get_usage(false) >= self::get_memory_limit() - self::$min_free_memory;
|
80 |
-
|
81 |
-
// about memory_get_usage(false) http://stackoverflow.com/a/16239377/1794248
|
82 |
-
}
|
83 |
-
|
84 |
-
/**
|
85 |
-
* @internal
|
86 |
-
*/
|
87 |
-
public static function _init()
|
88 |
-
{
|
89 |
-
self::$not_found_value = new FW_Cache_Not_Found_Exception();
|
90 |
-
|
91 |
-
/**
|
92 |
-
* Listen often triggered hooks to clear the memory
|
93 |
-
* instead of tick function https://github.com/ThemeFuse/Unyson/issues/1197
|
94 |
-
* @since 2.4.17
|
95 |
-
*/
|
96 |
-
foreach (array(
|
97 |
-
'query' => true,
|
98 |
-
'plugins_loaded' => true,
|
99 |
-
'wp_get_object_terms' => true,
|
100 |
-
'created_term' => true,
|
101 |
-
'wp_upgrade' => true,
|
102 |
-
'added_option' => true,
|
103 |
-
'updated_option' => true,
|
104 |
-
'deleted_option' => true,
|
105 |
-
'wp_after_admin_bar_render' => true,
|
106 |
-
'http_response' => true,
|
107 |
-
'oembed_result' => true,
|
108 |
-
'customize_post_value_set' => true,
|
109 |
-
'customize_save_after' => true,
|
110 |
-
'customize_render_panel' => true,
|
111 |
-
'customize_render_control' => true,
|
112 |
-
'customize_render_section' => true,
|
113 |
-
'role_has_cap' => true,
|
114 |
-
'user_has_cap' => true,
|
115 |
-
'theme_page_templates' => true,
|
116 |
-
'pre_get_users' => true,
|
117 |
-
'request' => true,
|
118 |
-
'send_headers' => true,
|
119 |
-
'updated_usermeta' => true,
|
120 |
-
'added_usermeta' => true,
|
121 |
-
'image_memory_limit' => true,
|
122 |
-
'upload_dir' => true,
|
123 |
-
'wp_head' => true,
|
124 |
-
'wp_footer' => true,
|
125 |
-
'wp' => true,
|
126 |
-
'wp_init' => true,
|
127 |
-
'fw_init' => true,
|
128 |
-
'init' => true,
|
129 |
-
'updated_postmeta' => true,
|
130 |
-
'deleted_postmeta' => true,
|
131 |
-
'setted_transient' => true,
|
132 |
-
'registered_post_type' => true,
|
133 |
-
'wp_count_posts' => true,
|
134 |
-
'wp_count_attachments' => true,
|
135 |
-
'after_delete_post' => true,
|
136 |
-
'post_updated' => true,
|
137 |
-
'wp_insert_post' => true,
|
138 |
-
'deleted_post' => true,
|
139 |
-
'clean_post_cache' => true,
|
140 |
-
'wp_restore_post_revision' => true,
|
141 |
-
'wp_delete_post_revision' => true,
|
142 |
-
'get_term' => true,
|
143 |
-
'edited_term_taxonomies' => true,
|
144 |
-
'deleted_term_taxonomy' => true,
|
145 |
-
'edited_terms' => true,
|
146 |
-
'created_term' => true,
|
147 |
-
'clean_term_cache' => true,
|
148 |
-
'edited_term_taxonomy' => true,
|
149 |
-
'switch_theme' => true,
|
150 |
-
'wp_get_update_data' => true,
|
151 |
-
'clean_user_cache' => true,
|
152 |
-
'process_text_diff_html' => true,
|
153 |
-
) as $hook => $tmp) {
|
154 |
-
add_filter($hook, array(__CLASS__, 'free_memory'), 9999);
|
155 |
-
}
|
156 |
-
|
157 |
-
/**
|
158 |
-
* When WP global state is changed, better to flush the cache
|
159 |
-
*/
|
160 |
-
foreach (array(
|
161 |
-
'switch_blog' => true,
|
162 |
-
) as $hook => $tmp) {
|
163 |
-
add_filter($hook, array(__CLASS__, 'clear'), 1);
|
164 |
-
}
|
165 |
-
}
|
166 |
-
|
167 |
-
/**
|
168 |
-
* This method does nothing @since 2.4.17
|
169 |
-
* but we can't delete it because it's public and maybe somebody is calling it
|
170 |
-
* @return bool
|
171 |
-
*/
|
172 |
-
public static function is_enabled()
|
173 |
-
{
|
174 |
-
return true;
|
175 |
-
}
|
176 |
-
|
177 |
-
/**
|
178 |
-
* @param mixed $dummy
|
179 |
-
* @return mixed
|
180 |
-
*/
|
181 |
-
public static function free_memory($dummy = null)
|
182 |
-
{
|
183 |
-
while (self::memory_exceeded() && !empty(self::$cache)) {
|
184 |
-
reset(self::$cache);
|
185 |
-
|
186 |
-
$key = key(self::$cache);
|
187 |
-
|
188 |
-
unset(self::$cache[$key]);
|
189 |
-
}
|
190 |
-
|
191 |
-
++self::$freed;
|
192 |
-
|
193 |
-
/**
|
194 |
-
* This method is used in add_filter() so to not break anything return filter value
|
195 |
-
*/
|
196 |
-
return $dummy;
|
197 |
-
}
|
198 |
-
|
199 |
-
/**
|
200 |
-
* @param $keys
|
201 |
-
* @param $value
|
202 |
-
* @param $keys_delimiter
|
203 |
-
*/
|
204 |
-
public static function set($keys, $value, $keys_delimiter = '/')
|
205 |
-
{
|
206 |
-
if (!self::is_enabled()) {
|
207 |
-
return;
|
208 |
-
}
|
209 |
-
|
210 |
-
self::free_memory();
|
211 |
-
|
212 |
-
fw_aks($keys, $value, self::$cache, $keys_delimiter);
|
213 |
-
|
214 |
-
self::free_memory();
|
215 |
-
}
|
216 |
-
|
217 |
-
/**
|
218 |
-
* Unset key from cache
|
219 |
-
* @param $keys
|
220 |
-
* @param $keys_delimiter
|
221 |
-
*/
|
222 |
-
public static function del($keys, $keys_delimiter = '/')
|
223 |
-
{
|
224 |
-
fw_aku($keys, self::$cache, $keys_delimiter);
|
225 |
-
|
226 |
-
self::free_memory();
|
227 |
-
}
|
228 |
-
|
229 |
-
/**
|
230 |
-
* @param $keys
|
231 |
-
* @param $keys_delimiter
|
232 |
-
* @return mixed
|
233 |
-
* @throws FW_Cache_Not_Found_Exception
|
234 |
-
*/
|
235 |
-
public static function get($keys, $keys_delimiter = '/')
|
236 |
-
{
|
237 |
-
$keys = (string)$keys;
|
238 |
-
$keys_arr = explode($keys_delimiter, $keys);
|
239 |
-
|
240 |
-
$key = $keys_arr;
|
241 |
-
$key = array_shift($key);
|
242 |
-
|
243 |
-
if ($key === '' || $key === null) {
|
244 |
-
trigger_error('First key must not be empty', E_USER_ERROR);
|
245 |
-
}
|
246 |
-
|
247 |
-
self::free_memory();
|
248 |
-
|
249 |
-
$value = fw_akg($keys, self::$cache, self::$not_found_value, $keys_delimiter);
|
250 |
-
|
251 |
-
self::free_memory();
|
252 |
-
|
253 |
-
if ($value === self::$not_found_value) {
|
254 |
-
++self::$misses;
|
255 |
-
|
256 |
-
throw new FW_Cache_Not_Found_Exception();
|
257 |
-
} else {
|
258 |
-
++self::$hits;
|
259 |
-
|
260 |
-
return $value;
|
261 |
-
}
|
262 |
-
}
|
263 |
-
|
264 |
-
/**
|
265 |
-
* Empty the cache
|
266 |
-
* @param mixed $dummy
|
267 |
-
* @return mixed
|
268 |
-
*/
|
269 |
-
public static function clear($dummy = null)
|
270 |
-
{
|
271 |
-
self::$cache = array();
|
272 |
-
|
273 |
-
/**
|
274 |
-
* This method is used in add_filter() so to not break anything return filter value
|
275 |
-
*/
|
276 |
-
return $dummy;
|
277 |
-
}
|
278 |
-
|
279 |
-
/**
|
280 |
-
* Debug information
|
281 |
-
* <?php add_action('admin_footer', function(){ FW_Cache::stats(); });
|
282 |
-
* @since 2.4.17
|
283 |
-
*/
|
284 |
-
public static function stats() {
|
285 |
-
echo '<div style="z-index: 10000; position: relative; background: #fff; padding: 15px;">';
|
286 |
-
echo '<p>';
|
287 |
-
echo '<strong>Cache Hits:</strong> '. self::$hits .'<br />';
|
288 |
-
echo '<strong>Cache Misses:</strong> '. self::$misses .'<br />';
|
289 |
-
echo '<strong>Cache Freed:</strong> '. self::$freed .'<br />';
|
290 |
-
echo '<strong>PHP Memory Peak Usage:</strong> '. fw_human_bytes(memory_get_peak_usage(false)) .'<br />';
|
291 |
-
echo '</p>';
|
292 |
-
echo '<ul>';
|
293 |
-
foreach (self::$cache as $group => $cache) {
|
294 |
-
echo "<li><strong>Group:</strong> $group - ( " . number_format( strlen( serialize( $cache ) ) / KB_IN_BYTES, 2 ) . 'k )</li>';
|
295 |
-
}
|
296 |
-
echo '</ul>';
|
297 |
-
echo '</div>';
|
298 |
-
}
|
299 |
-
}
|
300 |
-
|
301 |
-
class FW_Cache_Not_Found_Exception extends Exception {}
|
302 |
-
|
303 |
FW_Cache::_init();
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Memory Cache
|
5 |
+
* Only for internal usage in other functions/methods, because it throws exceptions
|
6 |
+
*
|
7 |
+
* Recommended usage example:
|
8 |
+
* try {
|
9 |
+
* $value = FW_Cache::get('some/key');
|
10 |
+
* } catch(FW_Cache_Not_Found_Exception $e) {
|
11 |
+
* $value = get_value_from_somewhere();
|
12 |
+
*
|
13 |
+
* FW_Cache::set('some/key', $value);
|
14 |
+
*
|
15 |
+
* // (!) after set, do not do this:
|
16 |
+
* $value = FW_Cache::get('some/key');
|
17 |
+
* // because there is no guaranty that FW_Cache::set('some/key', $value); succeeded
|
18 |
+
* // trust only your $value, cache can do clean-up right after set() and remove the value you tried to set
|
19 |
+
* }
|
20 |
+
*
|
21 |
+
* // use $value ...
|
22 |
+
*/
|
23 |
+
class FW_Cache
|
24 |
+
{
|
25 |
+
/**
|
26 |
+
* The actual cache
|
27 |
+
* @var array
|
28 |
+
*/
|
29 |
+
protected static $cache = array();
|
30 |
+
|
31 |
+
/**
|
32 |
+
* If the PHP will have less that this memory, the cache will try to delete parts from its array to free memory
|
33 |
+
*
|
34 |
+
* (1024 * 1024 = 1048576 = 1 Mb) * 10
|
35 |
+
*/
|
36 |
+
protected static $min_free_memory = 10485760;
|
37 |
+
|
38 |
+
/**
|
39 |
+
* A special value that is used to detect if value was found in cache
|
40 |
+
* We can't use null|false because these can be values set by user and we can't treat them as not existing values
|
41 |
+
*/
|
42 |
+
protected static $not_found_value;
|
43 |
+
|
44 |
+
/**
|
45 |
+
* The amount of times the data was already stored in the cache.
|
46 |
+
* @var int
|
47 |
+
* @since 2.4.17
|
48 |
+
*/
|
49 |
+
protected static $hits = 0;
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Amount of times the cache did not have the value in cache.
|
53 |
+
* @var int
|
54 |
+
* @since 2.4.17
|
55 |
+
*/
|
56 |
+
protected static $misses = 0;
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Amount of times the cache free was called.
|
60 |
+
* @var int
|
61 |
+
* @since 2.4.17
|
62 |
+
*/
|
63 |
+
protected static $freed = 0;
|
64 |
+
|
65 |
+
protected static function get_memory_limit()
|
66 |
+
{
|
67 |
+
$memory_limit = ini_get('memory_limit');
|
68 |
+
|
69 |
+
switch (substr($memory_limit, -1)) {
|
70 |
+
case 'M': return intval($memory_limit) * 1024 * 1024;
|
71 |
+
case 'K': return intval($memory_limit) * 1024;
|
72 |
+
case 'G': return intval($memory_limit) * 1024 * 1024 * 1024;
|
73 |
+
default: return intval($memory_limit) * 1024 * 1024;
|
74 |
+
}
|
75 |
+
}
|
76 |
+
|
77 |
+
protected static function memory_exceeded()
|
78 |
+
{
|
79 |
+
return memory_get_usage(false) >= self::get_memory_limit() - self::$min_free_memory;
|
80 |
+
|
81 |
+
// about memory_get_usage(false) http://stackoverflow.com/a/16239377/1794248
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* @internal
|
86 |
+
*/
|
87 |
+
public static function _init()
|
88 |
+
{
|
89 |
+
self::$not_found_value = new FW_Cache_Not_Found_Exception();
|
90 |
+
|
91 |
+
/**
|
92 |
+
* Listen often triggered hooks to clear the memory
|
93 |
+
* instead of tick function https://github.com/ThemeFuse/Unyson/issues/1197
|
94 |
+
* @since 2.4.17
|
95 |
+
*/
|
96 |
+
foreach (array(
|
97 |
+
'query' => true,
|
98 |
+
'plugins_loaded' => true,
|
99 |
+
'wp_get_object_terms' => true,
|
100 |
+
'created_term' => true,
|
101 |
+
'wp_upgrade' => true,
|
102 |
+
'added_option' => true,
|
103 |
+
'updated_option' => true,
|
104 |
+
'deleted_option' => true,
|
105 |
+
'wp_after_admin_bar_render' => true,
|
106 |
+
'http_response' => true,
|
107 |
+
'oembed_result' => true,
|
108 |
+
'customize_post_value_set' => true,
|
109 |
+
'customize_save_after' => true,
|
110 |
+
'customize_render_panel' => true,
|
111 |
+
'customize_render_control' => true,
|
112 |
+
'customize_render_section' => true,
|
113 |
+
'role_has_cap' => true,
|
114 |
+
'user_has_cap' => true,
|
115 |
+
'theme_page_templates' => true,
|
116 |
+
'pre_get_users' => true,
|
117 |
+
'request' => true,
|
118 |
+
'send_headers' => true,
|
119 |
+
'updated_usermeta' => true,
|
120 |
+
'added_usermeta' => true,
|
121 |
+
'image_memory_limit' => true,
|
122 |
+
'upload_dir' => true,
|
123 |
+
'wp_head' => true,
|
124 |
+
'wp_footer' => true,
|
125 |
+
'wp' => true,
|
126 |
+
'wp_init' => true,
|
127 |
+
'fw_init' => true,
|
128 |
+
'init' => true,
|
129 |
+
'updated_postmeta' => true,
|
130 |
+
'deleted_postmeta' => true,
|
131 |
+
'setted_transient' => true,
|
132 |
+
'registered_post_type' => true,
|
133 |
+
'wp_count_posts' => true,
|
134 |
+
'wp_count_attachments' => true,
|
135 |
+
'after_delete_post' => true,
|
136 |
+
'post_updated' => true,
|
137 |
+
'wp_insert_post' => true,
|
138 |
+
'deleted_post' => true,
|
139 |
+
'clean_post_cache' => true,
|
140 |
+
'wp_restore_post_revision' => true,
|
141 |
+
'wp_delete_post_revision' => true,
|
142 |
+
'get_term' => true,
|
143 |
+
'edited_term_taxonomies' => true,
|
144 |
+
'deleted_term_taxonomy' => true,
|
145 |
+
'edited_terms' => true,
|
146 |
+
'created_term' => true,
|
147 |
+
'clean_term_cache' => true,
|
148 |
+
'edited_term_taxonomy' => true,
|
149 |
+
'switch_theme' => true,
|
150 |
+
'wp_get_update_data' => true,
|
151 |
+
'clean_user_cache' => true,
|
152 |
+
'process_text_diff_html' => true,
|
153 |
+
) as $hook => $tmp) {
|
154 |
+
add_filter($hook, array(__CLASS__, 'free_memory'), 9999);
|
155 |
+
}
|
156 |
+
|
157 |
+
/**
|
158 |
+
* When WP global state is changed, better to flush the cache
|
159 |
+
*/
|
160 |
+
foreach (array(
|
161 |
+
'switch_blog' => true,
|
162 |
+
) as $hook => $tmp) {
|
163 |
+
add_filter($hook, array(__CLASS__, 'clear'), 1);
|
164 |
+
}
|
165 |
+
}
|
166 |
+
|
167 |
+
/**
|
168 |
+
* This method does nothing @since 2.4.17
|
169 |
+
* but we can't delete it because it's public and maybe somebody is calling it
|
170 |
+
* @return bool
|
171 |
+
*/
|
172 |
+
public static function is_enabled()
|
173 |
+
{
|
174 |
+
return true;
|
175 |
+
}
|
176 |
+
|
177 |
+
/**
|
178 |
+
* @param mixed $dummy
|
179 |
+
* @return mixed
|
180 |
+
*/
|
181 |
+
public static function free_memory($dummy = null)
|
182 |
+
{
|
183 |
+
while (self::memory_exceeded() && !empty(self::$cache)) {
|
184 |
+
reset(self::$cache);
|
185 |
+
|
186 |
+
$key = key(self::$cache);
|
187 |
+
|
188 |
+
unset(self::$cache[$key]);
|
189 |
+
}
|
190 |
+
|
191 |
+
++self::$freed;
|
192 |
+
|
193 |
+
/**
|
194 |
+
* This method is used in add_filter() so to not break anything return filter value
|
195 |
+
*/
|
196 |
+
return $dummy;
|
197 |
+
}
|
198 |
+
|
199 |
+
/**
|
200 |
+
* @param $keys
|
201 |
+
* @param $value
|
202 |
+
* @param $keys_delimiter
|
203 |
+
*/
|
204 |
+
public static function set($keys, $value, $keys_delimiter = '/')
|
205 |
+
{
|
206 |
+
if (!self::is_enabled()) {
|
207 |
+
return;
|
208 |
+
}
|
209 |
+
|
210 |
+
self::free_memory();
|
211 |
+
|
212 |
+
fw_aks($keys, $value, self::$cache, $keys_delimiter);
|
213 |
+
|
214 |
+
self::free_memory();
|
215 |
+
}
|
216 |
+
|
217 |
+
/**
|
218 |
+
* Unset key from cache
|
219 |
+
* @param $keys
|
220 |
+
* @param $keys_delimiter
|
221 |
+
*/
|
222 |
+
public static function del($keys, $keys_delimiter = '/')
|
223 |
+
{
|
224 |
+
fw_aku($keys, self::$cache, $keys_delimiter);
|
225 |
+
|
226 |
+
self::free_memory();
|
227 |
+
}
|
228 |
+
|
229 |
+
/**
|
230 |
+
* @param $keys
|
231 |
+
* @param $keys_delimiter
|
232 |
+
* @return mixed
|
233 |
+
* @throws FW_Cache_Not_Found_Exception
|
234 |
+
*/
|
235 |
+
public static function get($keys, $keys_delimiter = '/')
|
236 |
+
{
|
237 |
+
$keys = (string)$keys;
|
238 |
+
$keys_arr = explode($keys_delimiter, $keys);
|
239 |
+
|
240 |
+
$key = $keys_arr;
|
241 |
+
$key = array_shift($key);
|
242 |
+
|
243 |
+
if ($key === '' || $key === null) {
|
244 |
+
trigger_error('First key must not be empty', E_USER_ERROR);
|
245 |
+
}
|
246 |
+
|
247 |
+
self::free_memory();
|
248 |
+
|
249 |
+
$value = fw_akg($keys, self::$cache, self::$not_found_value, $keys_delimiter);
|
250 |
+
|
251 |
+
self::free_memory();
|
252 |
+
|
253 |
+
if ($value === self::$not_found_value) {
|
254 |
+
++self::$misses;
|
255 |
+
|
256 |
+
throw new FW_Cache_Not_Found_Exception();
|
257 |
+
} else {
|
258 |
+
++self::$hits;
|
259 |
+
|
260 |
+
return $value;
|
261 |
+
}
|
262 |
+
}
|
263 |
+
|
264 |
+
/**
|
265 |
+
* Empty the cache
|
266 |
+
* @param mixed $dummy
|
267 |
+
* @return mixed
|
268 |
+
*/
|
269 |
+
public static function clear($dummy = null)
|
270 |
+
{
|
271 |
+
self::$cache = array();
|
272 |
+
|
273 |
+
/**
|
274 |
+
* This method is used in add_filter() so to not break anything return filter value
|
275 |
+
*/
|
276 |
+
return $dummy;
|
277 |
+
}
|
278 |
+
|
279 |
+
/**
|
280 |
+
* Debug information
|
281 |
+
* <?php add_action('admin_footer', function(){ FW_Cache::stats(); });
|
282 |
+
* @since 2.4.17
|
283 |
+
*/
|
284 |
+
public static function stats() {
|
285 |
+
echo '<div style="z-index: 10000; position: relative; background: #fff; padding: 15px;">';
|
286 |
+
echo '<p>';
|
287 |
+
echo '<strong>Cache Hits:</strong> '. self::$hits .'<br />';
|
288 |
+
echo '<strong>Cache Misses:</strong> '. self::$misses .'<br />';
|
289 |
+
echo '<strong>Cache Freed:</strong> '. self::$freed .'<br />';
|
290 |
+
echo '<strong>PHP Memory Peak Usage:</strong> '. fw_human_bytes(memory_get_peak_usage(false)) .'<br />';
|
291 |
+
echo '</p>';
|
292 |
+
echo '<ul>';
|
293 |
+
foreach (self::$cache as $group => $cache) {
|
294 |
+
echo "<li><strong>Group:</strong> $group - ( " . number_format( strlen( serialize( $cache ) ) / KB_IN_BYTES, 2 ) . 'k )</li>';
|
295 |
+
}
|
296 |
+
echo '</ul>';
|
297 |
+
echo '</div>';
|
298 |
+
}
|
299 |
+
}
|
300 |
+
|
301 |
+
class FW_Cache_Not_Found_Exception extends Exception {}
|
302 |
+
|
303 |
FW_Cache::_init();
|
framework/helpers/class-fw-dumper.php
CHANGED
@@ -1,124 +1,124 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
// original source: https://code.google.com/p/prado3/source/browse/trunk/framework/Util/TVar_dumper.php
|
4 |
-
|
5 |
-
/**
|
6 |
-
* TVar_dumper class.
|
7 |
-
*
|
8 |
-
* TVar_dumper is intended to replace the buggy PHP function var_dump and print_r.
|
9 |
-
* It can correctly identify the recursively referenced objects in a complex
|
10 |
-
* object structure. It also has a recursive depth control to avoid indefinite
|
11 |
-
* recursive display of some peculiar variables.
|
12 |
-
*
|
13 |
-
* TVar_dumper can be used as follows,
|
14 |
-
* <code>
|
15 |
-
* echo TVar_dumper::dump($var);
|
16 |
-
* </code>
|
17 |
-
*
|
18 |
-
* @author Qiang Xue <qiang.xue@gmail.com>
|
19 |
-
* @version $Id$
|
20 |
-
* @package System.Util
|
21 |
-
* @since 3.0
|
22 |
-
*/
|
23 |
-
class FW_Dumper
|
24 |
-
{
|
25 |
-
private static $_objects;
|
26 |
-
private static $_output;
|
27 |
-
private static $_depth;
|
28 |
-
|
29 |
-
/**
|
30 |
-
* Converts a variable into a string representation.
|
31 |
-
* This method achieves the similar functionality as var_dump and print_r
|
32 |
-
* but is more robust when handling complex objects such as PRADO controls.
|
33 |
-
* @param mixed $var Variable to be dumped
|
34 |
-
* @param integer $depth Maximum depth that the dumper should go into the variable. Defaults to 10.
|
35 |
-
* @return string the string representation of the variable
|
36 |
-
*/
|
37 |
-
public static function dump($var, $depth=10)
|
38 |
-
{
|
39 |
-
self::reset_internals();
|
40 |
-
|
41 |
-
self::$_depth=$depth;
|
42 |
-
self::dump_internal($var,0);
|
43 |
-
|
44 |
-
$output = self::$_output;
|
45 |
-
|
46 |
-
self::reset_internals();
|
47 |
-
|
48 |
-
return $output;
|
49 |
-
}
|
50 |
-
|
51 |
-
private static function reset_internals()
|
52 |
-
{
|
53 |
-
self::$_output='';
|
54 |
-
self::$_objects=array();
|
55 |
-
self::$_depth=10;
|
56 |
-
}
|
57 |
-
|
58 |
-
private static function dump_internal($var,$level)
|
59 |
-
{
|
60 |
-
switch(gettype($var)) {
|
61 |
-
case 'boolean':
|
62 |
-
self::$_output.=$var?'true':'false';
|
63 |
-
break;
|
64 |
-
case 'integer':
|
65 |
-
self::$_output.="$var";
|
66 |
-
break;
|
67 |
-
case 'double':
|
68 |
-
self::$_output.="$var";
|
69 |
-
break;
|
70 |
-
case 'string':
|
71 |
-
self::$_output.="'$var'";
|
72 |
-
break;
|
73 |
-
case 'resource':
|
74 |
-
self::$_output.='{resource}';
|
75 |
-
break;
|
76 |
-
case 'NULL':
|
77 |
-
self::$_output.="null";
|
78 |
-
break;
|
79 |
-
case 'unknown type':
|
80 |
-
self::$_output.='{unknown}';
|
81 |
-
break;
|
82 |
-
case 'array':
|
83 |
-
if(self::$_depth<=$level)
|
84 |
-
self::$_output.='array(...)';
|
85 |
-
else if(empty($var))
|
86 |
-
self::$_output.='array()';
|
87 |
-
else
|
88 |
-
{
|
89 |
-
$keys=array_keys($var);
|
90 |
-
$spaces=str_repeat(' ',$level*4);
|
91 |
-
self::$_output.="array\n".$spaces.'(';
|
92 |
-
foreach($keys as $key)
|
93 |
-
{
|
94 |
-
self::$_output.="\n".$spaces." [$key] => ";
|
95 |
-
self::$_output.=self::dump_internal($var[$key],$level+1);
|
96 |
-
}
|
97 |
-
self::$_output.="\n".$spaces.')';
|
98 |
-
}
|
99 |
-
break;
|
100 |
-
case 'object':
|
101 |
-
if(($id=array_search($var,self::$_objects,true))!==false)
|
102 |
-
self::$_output.=get_class($var).'(...)';
|
103 |
-
else if(self::$_depth<=$level)
|
104 |
-
self::$_output.=get_class($var).'(...)';
|
105 |
-
else
|
106 |
-
{
|
107 |
-
$id=array_push(self::$_objects,$var);
|
108 |
-
$class_name=get_class($var);
|
109 |
-
$members=(array)$var;
|
110 |
-
$keys=array_keys($members);
|
111 |
-
$spaces=str_repeat(' ',$level*4);
|
112 |
-
self::$_output.="$class_name\n".$spaces.'(';
|
113 |
-
foreach($keys as $key)
|
114 |
-
{
|
115 |
-
$key_display=strtr(trim($key),array("\0"=>':'));
|
116 |
-
self::$_output.="\n".$spaces." [$key_display] => ";
|
117 |
-
self::$_output.=self::dump_internal($members[$key],$level+1);
|
118 |
-
}
|
119 |
-
self::$_output.="\n".$spaces.')';
|
120 |
-
}
|
121 |
-
break;
|
122 |
-
}
|
123 |
-
}
|
124 |
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// original source: https://code.google.com/p/prado3/source/browse/trunk/framework/Util/TVar_dumper.php
|
4 |
+
|
5 |
+
/**
|
6 |
+
* TVar_dumper class.
|
7 |
+
*
|
8 |
+
* TVar_dumper is intended to replace the buggy PHP function var_dump and print_r.
|
9 |
+
* It can correctly identify the recursively referenced objects in a complex
|
10 |
+
* object structure. It also has a recursive depth control to avoid indefinite
|
11 |
+
* recursive display of some peculiar variables.
|
12 |
+
*
|
13 |
+
* TVar_dumper can be used as follows,
|
14 |
+
* <code>
|
15 |
+
* echo TVar_dumper::dump($var);
|
16 |
+
* </code>
|
17 |
+
*
|
18 |
+
* @author Qiang Xue <qiang.xue@gmail.com>
|
19 |
+
* @version $Id$
|
20 |
+
* @package System.Util
|
21 |
+
* @since 3.0
|
22 |
+
*/
|
23 |
+
class FW_Dumper
|
24 |
+
{
|
25 |
+
private static $_objects;
|
26 |
+
private static $_output;
|
27 |
+
private static $_depth;
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Converts a variable into a string representation.
|
31 |
+
* This method achieves the similar functionality as var_dump and print_r
|
32 |
+
* but is more robust when handling complex objects such as PRADO controls.
|
33 |
+
* @param mixed $var Variable to be dumped
|
34 |
+
* @param integer $depth Maximum depth that the dumper should go into the variable. Defaults to 10.
|
35 |
+
* @return string the string representation of the variable
|
36 |
+
*/
|
37 |
+
public static function dump($var, $depth=10)
|
38 |
+
{
|
39 |
+
self::reset_internals();
|
40 |
+
|
41 |
+
self::$_depth=$depth;
|
42 |
+
self::dump_internal($var,0);
|
43 |
+
|
44 |
+
$output = self::$_output;
|
45 |
+
|
46 |
+
self::reset_internals();
|
47 |
+
|
48 |
+
return $output;
|
49 |
+
}
|
50 |
+
|
51 |
+
private static function reset_internals()
|
52 |
+
{
|
53 |
+
self::$_output='';
|
54 |
+
self::$_objects=array();
|
55 |
+
self::$_depth=10;
|
56 |
+
}
|
57 |
+
|
58 |
+
private static function dump_internal($var,$level)
|
59 |
+
{
|
60 |
+
switch(gettype($var)) {
|
61 |
+
case 'boolean':
|
62 |
+
self::$_output.=$var?'true':'false';
|
63 |
+
break;
|
64 |
+
case 'integer':
|
65 |
+
self::$_output.="$var";
|
66 |
+
break;
|
67 |
+
case 'double':
|
68 |
+
self::$_output.="$var";
|
69 |
+
break;
|
70 |
+
case 'string':
|
71 |
+
self::$_output.="'$var'";
|
72 |
+
break;
|
73 |
+
case 'resource':
|
74 |
+
self::$_output.='{resource}';
|
75 |
+
break;
|
76 |
+
case 'NULL':
|
77 |
+
self::$_output.="null";
|
78 |
+
break;
|
79 |
+
case 'unknown type':
|
80 |
+
self::$_output.='{unknown}';
|
81 |
+
break;
|
82 |
+
case 'array':
|
83 |
+
if(self::$_depth<=$level)
|
84 |
+
self::$_output.='array(...)';
|
85 |
+
else if(empty($var))
|
86 |
+
self::$_output.='array()';
|
87 |
+
else
|
88 |
+
{
|
89 |
+
$keys=array_keys($var);
|
90 |
+
$spaces=str_repeat(' ',$level*4);
|
91 |
+
self::$_output.="array\n".$spaces.'(';
|
92 |
+
foreach($keys as $key)
|
93 |
+
{
|
94 |
+
self::$_output.="\n".$spaces." [$key] => ";
|
95 |
+
self::$_output.=self::dump_internal($var[$key],$level+1);
|
96 |
+
}
|
97 |
+
self::$_output.="\n".$spaces.')';
|
98 |
+
}
|
99 |
+
break;
|
100 |
+
case 'object':
|
101 |
+
if(($id=array_search($var,self::$_objects,true))!==false)
|
102 |
+
self::$_output.=get_class($var).'(...)';
|
103 |
+
else if(self::$_depth<=$level)
|
104 |
+
self::$_output.=get_class($var).'(...)';
|
105 |
+
else
|
106 |
+
{
|
107 |
+
$id=array_push(self::$_objects,$var);
|
108 |
+
$class_name=get_class($var);
|
109 |
+
$members=(array)$var;
|
110 |
+
$keys=array_keys($members);
|
111 |
+
$spaces=str_repeat(' ',$level*4);
|
112 |
+
self::$_output.="$class_name\n".$spaces.'(';
|
113 |
+
foreach($keys as $key)
|
114 |
+
{
|
115 |
+
$key_display=strtr(trim($key),array("\0"=>':'));
|
116 |
+
self::$_output.="\n".$spaces." [$key_display] => ";
|
117 |
+
self::$_output.=self::dump_internal($members[$key],$level+1);
|
118 |
+
}
|
119 |
+
self::$_output.="\n".$spaces.')';
|
120 |
+
}
|
121 |
+
break;
|
122 |
+
}
|
123 |
+
}
|
124 |
}
|
framework/helpers/class-fw-flash-messages.php
CHANGED
@@ -1,312 +1,312 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Set flash messages
|
5 |
-
**
|
6 |
-
* Store messages in session (to not be lost between redirects) and remove them after they were shown to the user
|
7 |
-
*/
|
8 |
-
class FW_Flash_Messages
|
9 |
-
{
|
10 |
-
private static $available_types = array(
|
11 |
-
// 'type' => 'backend class/type' (only 2 backend types exists: error and updated)
|
12 |
-
'error' => 'error',
|
13 |
-
'warning' => 'update-nag',
|
14 |
-
'info' => 'updated',
|
15 |
-
'success' => 'updated'
|
16 |
-
);
|
17 |
-
|
18 |
-
private static $session_key = 'fw_flash_messages';
|
19 |
-
|
20 |
-
private static $frontend_printed = false;
|
21 |
-
|
22 |
-
private static function get_messages()
|
23 |
-
{
|
24 |
-
$messages = FW_Session::get(self::$session_key);
|
25 |
-
|
26 |
-
if (empty($messages) || !is_array($messages)) {
|
27 |
-
$messages = array_fill_keys(array_keys(self::$available_types), array());
|
28 |
-
}
|
29 |
-
|
30 |
-
return $messages;
|
31 |
-
}
|
32 |
-
|
33 |
-
private static function set_messages(array $messages)
|
34 |
-
{
|
35 |
-
FW_Session::set(self::$session_key, $messages);
|
36 |
-
}
|
37 |
-
|
38 |
-
/**
|
39 |
-
* Remove messages with ids from pending remove
|
40 |
-
*/
|
41 |
-
private static function process_pending_remove_ids()
|
42 |
-
{
|
43 |
-
$pending_remove = array();
|
44 |
-
|
45 |
-
foreach (self::get_messages() as $messages) {
|
46 |
-
if (empty($messages)) {
|
47 |
-
continue;
|
48 |
-
}
|
49 |
-
|
50 |
-
foreach ($messages as $message) {
|
51 |
-
if (empty($message['remove_ids'])) {
|
52 |
-
continue;
|
53 |
-
}
|
54 |
-
|
55 |
-
foreach ($message['remove_ids'] as $remove_id) {
|
56 |
-
$pending_remove[$remove_id] = true;
|
57 |
-
}
|
58 |
-
}
|
59 |
-
}
|
60 |
-
|
61 |
-
if (empty($pending_remove)) {
|
62 |
-
return;
|
63 |
-
}
|
64 |
-
|
65 |
-
$types = self::get_messages();
|
66 |
-
|
67 |
-
foreach ($types as $type => $messages) {
|
68 |
-
if (empty($messages)) {
|
69 |
-
continue;
|
70 |
-
}
|
71 |
-
|
72 |
-
foreach ($messages as $id => $message) {
|
73 |
-
if (isset($pending_remove[$id])) {
|
74 |
-
unset($types[$type][$id]);
|
75 |
-
}
|
76 |
-
}
|
77 |
-
}
|
78 |
-
|
79 |
-
self::set_messages( $types );
|
80 |
-
}
|
81 |
-
|
82 |
-
/**
|
83 |
-
* Add flash message
|
84 |
-
**
|
85 |
-
* @param string $id Unique id of the message
|
86 |
-
* @param string $message Message (can be html)
|
87 |
-
* @param string $type Type from $available_types
|
88 |
-
* @param array $removed_ids Remove flashes with this id(s)
|
89 |
-
* (For e.g. your message is success and some known error messages ids needs to be removed
|
90 |
-
* because they are not relevant anymore, your success message suppress/cancels them)
|
91 |
-
*/
|
92 |
-
public static function add($id, $message, $type = 'info', array $removed_ids = array())
|
93 |
-
{
|
94 |
-
if (!isset(self::$available_types[$type])) {
|
95 |
-
trigger_error(sprintf(__('Invalid flash message type: %s', 'tfuse'), $type), E_USER_WARNING);
|
96 |
-
$type = 'info';
|
97 |
-
}
|
98 |
-
|
99 |
-
$messages = self::get_messages();
|
100 |
-
|
101 |
-
$messages[$type][$id] = array(
|
102 |
-
'message' => $message,
|
103 |
-
'remove_ids' => $removed_ids,
|
104 |
-
);
|
105 |
-
|
106 |
-
self::set_messages($messages);
|
107 |
-
}
|
108 |
-
|
109 |
-
/**
|
110 |
-
* Use this method to print messages html in backend
|
111 |
-
* (used in action at the end of the file)
|
112 |
-
* @internal
|
113 |
-
*/
|
114 |
-
public static function _print_backend()
|
115 |
-
{
|
116 |
-
self::process_pending_remove_ids();
|
117 |
-
|
118 |
-
$html = array_fill_keys(array_keys(self::$available_types), '');
|
119 |
-
|
120 |
-
$all_messages = self::get_messages();
|
121 |
-
|
122 |
-
foreach ($all_messages as $type => $messages) {
|
123 |
-
if (!empty($messages)) {
|
124 |
-
foreach ($messages as $id => $data) {
|
125 |
-
$html[$type] .=
|
126 |
-
'<div class="'. self::$available_types[$type] .' fw-flash-message">'.
|
127 |
-
'<p data-id="'. esc_attr($id) .'">'. $data['message'] .'</p>'.
|
128 |
-
'</div>';
|
129 |
-
|
130 |
-
unset($all_messages[$type][$id]);
|
131 |
-
}
|
132 |
-
|
133 |
-
$html[$type] = '<div class="fw-flash-type-'. $type .'">'. $html[$type] .'</div>';
|
134 |
-
}
|
135 |
-
}
|
136 |
-
|
137 |
-
unset($success, $error, $info);
|
138 |
-
|
139 |
-
self::set_messages($all_messages);
|
140 |
-
|
141 |
-
echo '<div class="fw-flash-messages">'. implode("\n\n", $html) .'</div>';
|
142 |
-
}
|
143 |
-
|
144 |
-
/**
|
145 |
-
* Use this method to print messages html in frontend
|
146 |
-
* @return bool If some html was printed or not
|
147 |
-
*/
|
148 |
-
public static function _print_frontend()
|
149 |
-
{
|
150 |
-
self::process_pending_remove_ids();
|
151 |
-
|
152 |
-
$html = array_fill_keys(array_keys(self::$available_types), '');
|
153 |
-
$all_messages = self::get_messages();
|
154 |
-
|
155 |
-
$messages_exists = false;
|
156 |
-
|
157 |
-
foreach ($all_messages as $type => $messages) {
|
158 |
-
if (empty($messages)) {
|
159 |
-
continue;
|
160 |
-
}
|
161 |
-
|
162 |
-
foreach ($messages as $id => $data) {
|
163 |
-
$html[$type] .= '<li class="fw-flash-message">'. nl2br($data['message']) .'</li>';
|
164 |
-
|
165 |
-
unset($all_messages[$type][$id]);
|
166 |
-
}
|
167 |
-
|
168 |
-
$html[$type] = '<ul class="fw-flash-type-'. $type .'">'. $html[$type] .'</ul>';
|
169 |
-
|
170 |
-
$messages_exists = true;
|
171 |
-
}
|
172 |
-
|
173 |
-
self::set_messages($all_messages);
|
174 |
-
|
175 |
-
self::$frontend_printed = true;
|
176 |
-
|
177 |
-
if ($messages_exists) {
|
178 |
-
echo '<div class="fw-flash-messages">';
|
179 |
-
echo implode("\n\n", $html);
|
180 |
-
echo '</div>';
|
181 |
-
|
182 |
-
return true;
|
183 |
-
} else {
|
184 |
-
return false;
|
185 |
-
}
|
186 |
-
}
|
187 |
-
|
188 |
-
public static function _frontend_printed()
|
189 |
-
{
|
190 |
-
return self::$frontend_printed;
|
191 |
-
}
|
192 |
-
|
193 |
-
public static function _get_messages($clear = false)
|
194 |
-
{
|
195 |
-
self::process_pending_remove_ids();
|
196 |
-
|
197 |
-
$messages = self::get_messages();
|
198 |
-
|
199 |
-
if ($clear) {
|
200 |
-
self::set_messages(array());
|
201 |
-
}
|
202 |
-
|
203 |
-
return $messages;
|
204 |
-
}
|
205 |
-
}
|
206 |
-
|
207 |
-
if (is_admin()) {
|
208 |
-
/**
|
209 |
-
* Start the session before the content is sent to prevent the "headers already sent" warning
|
210 |
-
* @internal
|
211 |
-
*/
|
212 |
-
function _action_fw_flash_message_backend_prepare() {
|
213 |
-
if (!session_id()) {
|
214 |
-
session_start();
|
215 |
-
}
|
216 |
-
}
|
217 |
-
add_action('current_screen', '_action_fw_flash_message_backend_prepare', 9999);
|
218 |
-
|
219 |
-
/**
|
220 |
-
* Display flash messages in backend as notices
|
221 |
-
*/
|
222 |
-
add_action( 'admin_notices', array( 'FW_Flash_Messages', '_print_backend' ) );
|
223 |
-
} else {
|
224 |
-
/**
|
225 |
-
* Start the session before the content is sent to prevent the "headers already sent" warning
|
226 |
-
* @internal
|
227 |
-
*/
|
228 |
-
function _action_fw_flash_message_frontend_prepare() {
|
229 |
-
if (
|
230 |
-
/**
|
231 |
-
* In ajax it's not possible to call flash message after headers were sent,
|
232 |
-
* so there will be no "headers already sent" warning.
|
233 |
-
* Also in the Backups extension, are made many internal ajax request,
|
234 |
-
* each creating a new independent request that don't remember/use session cookie from previous request,
|
235 |
-
* thus on server side are created many (not used) new sessions.
|
236 |
-
*/
|
237 |
-
!(defined('DOING_AJAX') && DOING_AJAX)
|
238 |
-
&&
|
239 |
-
!session_id()
|
240 |
-
) {
|
241 |
-
session_start();
|
242 |
-
}
|
243 |
-
}
|
244 |
-
add_action('send_headers', '_action_fw_flash_message_frontend_prepare', 9999);
|
245 |
-
|
246 |
-
/**
|
247 |
-
* Print flash messages in frontend if this has not been done from theme
|
248 |
-
*/
|
249 |
-
function _action_fw_flash_message_frontend_print() {
|
250 |
-
if (FW_Flash_Messages::_frontend_printed()) {
|
251 |
-
return;
|
252 |
-
}
|
253 |
-
|
254 |
-
if (!FW_Flash_Messages::_print_frontend()) {
|
255 |
-
return;
|
256 |
-
}
|
257 |
-
|
258 |
-
?>
|
259 |
-
<script type="text/javascript">
|
260 |
-
(function(){
|
261 |
-
if (typeof jQuery === "undefined") {
|
262 |
-
return;
|
263 |
-
}
|
264 |
-
|
265 |
-
jQuery(function($){
|
266 |
-
var $container;
|
267 |
-
|
268 |
-
// Try to find the content element
|
269 |
-
{
|
270 |
-
var selector, selectors = [
|
271 |
-
'#main #content',
|
272 |
-
'#content #main',
|
273 |
-
'#main',
|
274 |
-
'#content',
|
275 |
-
'#content-container',
|
276 |
-
'#container',
|
277 |
-
'.container:first'
|
278 |
-
];
|
279 |
-
|
280 |
-
while (selector = selectors.shift()) {
|
281 |
-
$container = $(selector);
|
282 |
-
|
283 |
-
if ($container.length) {
|
284 |
-
break;
|
285 |
-
}
|
286 |
-
}
|
287 |
-
}
|
288 |
-
|
289 |
-
if (!$container.length) {
|
290 |
-
// Try to find main page H1 container
|
291 |
-
$container = $('h1:first').parent();
|
292 |
-
}
|
293 |
-
|
294 |
-
if (!$container.length) {
|
295 |
-
// If nothing found, just add to body
|
296 |
-
$container = $(document.body);
|
297 |
-
}
|
298 |
-
|
299 |
-
$(".fw-flash-messages").prependTo($container);
|
300 |
-
});
|
301 |
-
})();
|
302 |
-
</script>
|
303 |
-
<style type="text/css">
|
304 |
-
.fw-flash-messages .fw-flash-type-error { color: #f00; }
|
305 |
-
.fw-flash-messages .fw-flash-type-warning { color: #f70; }
|
306 |
-
.fw-flash-messages .fw-flash-type-success { color: #070; }
|
307 |
-
.fw-flash-messages .fw-flash-type-info { color: #07f; }
|
308 |
-
</style>
|
309 |
-
<?php
|
310 |
-
}
|
311 |
-
add_action('wp_footer', '_action_fw_flash_message_frontend_print', 9999);
|
312 |
-
}
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Set flash messages
|
5 |
+
**
|
6 |
+
* Store messages in session (to not be lost between redirects) and remove them after they were shown to the user
|
7 |
+
*/
|
8 |
+
class FW_Flash_Messages
|
9 |
+
{
|
10 |
+
private static $available_types = array(
|
11 |
+
// 'type' => 'backend class/type' (only 2 backend types exists: error and updated)
|
12 |
+
'error' => 'error',
|
13 |
+
'warning' => 'update-nag',
|
14 |
+
'info' => 'updated',
|
15 |
+
'success' => 'updated'
|
16 |
+
);
|
17 |
+
|
18 |
+
private static $session_key = 'fw_flash_messages';
|
19 |
+
|
20 |
+
private static $frontend_printed = false;
|
21 |
+
|
22 |
+
private static function get_messages()
|
23 |
+
{
|
24 |
+
$messages = FW_Session::get(self::$session_key);
|
25 |
+
|
26 |
+
if (empty($messages) || !is_array($messages)) {
|
27 |
+
$messages = array_fill_keys(array_keys(self::$available_types), array());
|
28 |
+
}
|
29 |
+
|
30 |
+
return $messages;
|
31 |
+
}
|
32 |
+
|
33 |
+
private static function set_messages(array $messages)
|
34 |
+
{
|
35 |
+
FW_Session::set(self::$session_key, $messages);
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Remove messages with ids from pending remove
|
40 |
+
*/
|
41 |
+
private static function process_pending_remove_ids()
|
42 |
+
{
|
43 |
+
$pending_remove = array();
|
44 |
+
|
45 |
+
foreach (self::get_messages() as $messages) {
|
46 |
+
if (empty($messages)) {
|
47 |
+
continue;
|
48 |
+
}
|
49 |
+
|
50 |
+
foreach ($messages as $message) {
|
51 |
+
if (empty($message['remove_ids'])) {
|
52 |
+
continue;
|
53 |
+
}
|
54 |
+
|
55 |
+
foreach ($message['remove_ids'] as $remove_id) {
|
56 |
+
$pending_remove[$remove_id] = true;
|
57 |
+
}
|
58 |
+
}
|
59 |
+
}
|
60 |
+
|
61 |
+
if (empty($pending_remove)) {
|
62 |
+
return;
|
63 |
+
}
|
64 |
+
|
65 |
+
$types = self::get_messages();
|
66 |
+
|
67 |
+
foreach ($types as $type => $messages) {
|
68 |
+
if (empty($messages)) {
|
69 |
+
continue;
|
70 |
+
}
|
71 |
+
|
72 |
+
foreach ($messages as $id => $message) {
|
73 |
+
if (isset($pending_remove[$id])) {
|
74 |
+
unset($types[$type][$id]);
|
75 |
+
}
|
76 |
+
}
|
77 |
+
}
|
78 |
+
|
79 |
+
self::set_messages( $types );
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Add flash message
|
84 |
+
**
|
85 |
+
* @param string $id Unique id of the message
|
86 |
+
* @param string $message Message (can be html)
|
87 |
+
* @param string $type Type from $available_types
|
88 |
+
* @param array $removed_ids Remove flashes with this id(s)
|
89 |
+
* (For e.g. your message is success and some known error messages ids needs to be removed
|
90 |
+
* because they are not relevant anymore, your success message suppress/cancels them)
|
91 |
+
*/
|
92 |
+
public static function add($id, $message, $type = 'info', array $removed_ids = array())
|
93 |
+
{
|
94 |
+
if (!isset(self::$available_types[$type])) {
|
95 |
+
trigger_error(sprintf(__('Invalid flash message type: %s', 'tfuse'), $type), E_USER_WARNING);
|
96 |
+
$type = 'info';
|
97 |
+
}
|
98 |
+
|
99 |
+
$messages = self::get_messages();
|
100 |
+
|
101 |
+
$messages[$type][$id] = array(
|
102 |
+
'message' => $message,
|
103 |
+
'remove_ids' => $removed_ids,
|
104 |
+
);
|
105 |
+
|
106 |
+
self::set_messages($messages);
|
107 |
+
}
|
108 |
+
|
109 |
+
/**
|
110 |
+
* Use this method to print messages html in backend
|
111 |
+
* (used in action at the end of the file)
|
112 |
+
* @internal
|
113 |
+
*/
|
114 |
+
public static function _print_backend()
|
115 |
+
{
|
116 |
+
self::process_pending_remove_ids();
|
117 |
+
|
118 |
+
$html = array_fill_keys(array_keys(self::$available_types), '');
|
119 |
+
|
120 |
+
$all_messages = self::get_messages();
|
121 |
+
|
122 |
+
foreach ($all_messages as $type => $messages) {
|
123 |
+
if (!empty($messages)) {
|
124 |
+
foreach ($messages as $id => $data) {
|
125 |
+
$html[$type] .=
|
126 |
+
'<div class="'. self::$available_types[$type] .' fw-flash-message">'.
|
127 |
+
'<p data-id="'. esc_attr($id) .'">'. $data['message'] .'</p>'.
|
128 |
+
'</div>';
|
129 |
+
|
130 |
+
unset($all_messages[$type][$id]);
|
131 |
+
}
|
132 |
+
|
133 |
+
$html[$type] = '<div class="fw-flash-type-'. $type .'">'. $html[$type] .'</div>';
|
134 |
+
}
|
135 |
+
}
|
136 |
+
|
137 |
+
unset($success, $error, $info);
|
138 |
+
|
139 |
+
self::set_messages($all_messages);
|
140 |
+
|
141 |
+
echo '<div class="fw-flash-messages">'. implode("\n\n", $html) .'</div>';
|
142 |
+
}
|
143 |
+
|
144 |
+
/**
|
145 |
+
* Use this method to print messages html in frontend
|
146 |
+
* @return bool If some html was printed or not
|
147 |
+
*/
|
148 |
+
public static function _print_frontend()
|
149 |
+
{
|
150 |
+
self::process_pending_remove_ids();
|
151 |
+
|
152 |
+
$html = array_fill_keys(array_keys(self::$available_types), '');
|
153 |
+
$all_messages = self::get_messages();
|
154 |
+
|
155 |
+
$messages_exists = false;
|
156 |
+
|
157 |
+
foreach ($all_messages as $type => $messages) {
|
158 |
+
if (empty($messages)) {
|
159 |
+
continue;
|
160 |
+
}
|
161 |
+
|
162 |
+
foreach ($messages as $id => $data) {
|
163 |
+
$html[$type] .= '<li class="fw-flash-message">'. nl2br($data['message']) .'</li>';
|
164 |
+
|
165 |
+
unset($all_messages[$type][$id]);
|
166 |
+
}
|
167 |
+
|
168 |
+
$html[$type] = '<ul class="fw-flash-type-'. $type .'">'. $html[$type] .'</ul>';
|
169 |
+
|
170 |
+
$messages_exists = true;
|
171 |
+
}
|
172 |
+
|
173 |
+
self::set_messages($all_messages);
|
174 |
+
|
175 |
+
self::$frontend_printed = true;
|
176 |
+
|
177 |
+
if ($messages_exists) {
|
178 |
+
echo '<div class="fw-flash-messages">';
|
179 |
+
echo implode("\n\n", $html);
|
180 |
+
echo '</div>';
|
181 |
+
|
182 |
+
return true;
|
183 |
+
} else {
|
184 |
+
return false;
|
185 |
+
}
|
186 |
+
}
|
187 |
+
|
188 |
+
public static function _frontend_printed()
|
189 |
+
{
|
190 |
+
return self::$frontend_printed;
|
191 |
+
}
|
192 |
+
|
193 |
+
public static function _get_messages($clear = false)
|
194 |
+
{
|
195 |
+
self::process_pending_remove_ids();
|
196 |
+
|
197 |
+
$messages = self::get_messages();
|
198 |
+
|
199 |
+
if ($clear) {
|
200 |
+
self::set_messages(array());
|
201 |
+
}
|
202 |
+
|
203 |
+
return $messages;
|
204 |
+
}
|
205 |
+
}
|
206 |
+
|
207 |
+
if (is_admin()) {
|
208 |
+
/**
|
209 |
+
* Start the session before the content is sent to prevent the "headers already sent" warning
|
210 |
+
* @internal
|
211 |
+
*/
|
212 |
+
function _action_fw_flash_message_backend_prepare() {
|
213 |
+
if (!session_id()) {
|
214 |
+
session_start();
|
215 |
+
}
|
216 |
+
}
|
217 |
+
add_action('current_screen', '_action_fw_flash_message_backend_prepare', 9999);
|
218 |
+
|
219 |
+
/**
|
220 |
+
* Display flash messages in backend as notices
|
221 |
+
*/
|
222 |
+
add_action( 'admin_notices', array( 'FW_Flash_Messages', '_print_backend' ) );
|
223 |
+
} else {
|
224 |
+
/**
|
225 |
+
* Start the session before the content is sent to prevent the "headers already sent" warning
|
226 |
+
* @internal
|
227 |
+
*/
|
228 |
+
function _action_fw_flash_message_frontend_prepare() {
|
229 |
+
if (
|
230 |
+
/**
|
231 |
+
* In ajax it's not possible to call flash message after headers were sent,
|
232 |
+
* so there will be no "headers already sent" warning.
|
233 |
+
* Also in the Backups extension, are made many internal ajax request,
|
234 |
+
* each creating a new independent request that don't remember/use session cookie from previous request,
|
235 |
+
* thus on server side are created many (not used) new sessions.
|
236 |
+
*/
|
237 |
+
!(defined('DOING_AJAX') && DOING_AJAX)
|
238 |
+
&&
|
239 |
+
!session_id()
|
240 |
+
) {
|
241 |
+
session_start();
|
242 |
+
}
|
243 |
+
}
|
244 |
+
add_action('send_headers', '_action_fw_flash_message_frontend_prepare', 9999);
|
245 |
+
|
246 |
+
/**
|
247 |
+
* Print flash messages in frontend if this has not been done from theme
|
248 |
+
*/
|
249 |
+
function _action_fw_flash_message_frontend_print() {
|
250 |
+
if (FW_Flash_Messages::_frontend_printed()) {
|
251 |
+
return;
|
252 |
+
}
|
253 |
+
|
254 |
+
if (!FW_Flash_Messages::_print_frontend()) {
|
255 |
+
return;
|
256 |
+
}
|
257 |
+
|
258 |
+
?>
|
259 |
+
<script type="text/javascript">
|
260 |
+
(function(){
|
261 |
+
if (typeof jQuery === "undefined") {
|
262 |
+
return;
|
263 |
+
}
|
264 |
+
|
265 |
+
jQuery(function($){
|
266 |
+
var $container;
|
267 |
+
|
268 |
+
// Try to find the content element
|
269 |
+
{
|
270 |
+
var selector, selectors = [
|
271 |
+
'#main #content',
|
272 |
+
'#content #main',
|
273 |
+
'#main',
|
274 |
+
'#content',
|
275 |
+
'#content-container',
|
276 |
+
'#container',
|
277 |
+
'.container:first'
|
278 |
+
];
|
279 |
+
|
280 |
+
while (selector = selectors.shift()) {
|
281 |
+
$container = $(selector);
|
282 |
+
|
283 |
+
if ($container.length) {
|
284 |
+
break;
|
285 |
+
}
|
286 |
+
}
|
287 |
+
}
|
288 |
+
|
289 |
+
if (!$container.length) {
|
290 |
+
// Try to find main page H1 container
|
291 |
+
$container = $('h1:first').parent();
|
292 |
+
}
|
293 |
+
|
294 |
+
if (!$container.length) {
|
295 |
+
// If nothing found, just add to body
|
296 |
+
$container = $(document.body);
|
297 |
+
}
|
298 |
+
|
299 |
+
$(".fw-flash-messages").prependTo($container);
|
300 |
+
});
|
301 |
+
})();
|
302 |
+
</script>
|
303 |
+
<style type="text/css">
|
304 |
+
.fw-flash-messages .fw-flash-type-error { color: #f00; }
|
305 |
+
.fw-flash-messages .fw-flash-type-warning { color: #f70; }
|
306 |
+
.fw-flash-messages .fw-flash-type-success { color: #070; }
|
307 |
+
.fw-flash-messages .fw-flash-type-info { color: #07f; }
|
308 |
+
</style>
|
309 |
+
<?php
|
310 |
+
}
|
311 |
+
add_action('wp_footer', '_action_fw_flash_message_frontend_print', 9999);
|
312 |
+
}
|
framework/helpers/class-fw-form.php
CHANGED
@@ -1,585 +1,585 @@
|
|
1 |
-
<?php if ( ! defined( 'FW' ) ) {
|
2 |
-
die( 'Forbidden' );
|
3 |
-
}
|
4 |
-
|
5 |
-
/**
|
6 |
-
* Dynamic forms
|
7 |
-
*/
|
8 |
-
class FW_Form {
|
9 |
-
/**
|
10 |
-
* Store all form ids created with this class
|
11 |
-
* @var FW_Form[] {'form_id' => instance}
|
12 |
-
*/
|
13 |
-
protected static $forms = array();
|
14 |
-
|
15 |
-
/**
|
16 |
-
* The id of the submitted form id
|
17 |
-
* @var string
|
18 |
-
*/
|
19 |
-
protected static $submitted_id;
|
20 |
-
|
21 |
-
/**
|
22 |
-
* Hidden input name that stores the form id
|
23 |
-
* @var string
|
24 |
-
*/
|
25 |
-
protected static $id_input_name = 'fwf';
|
26 |
-
|
27 |
-
/**
|
28 |
-
* Form id
|
29 |
-
* @var string
|
30 |
-
*/
|
31 |
-
protected $id;
|
32 |
-
|
33 |
-
/**
|
34 |
-
* Html attributes for <form> tag
|
35 |
-
* @var array
|
36 |
-
*/
|
37 |
-
protected $attr;
|
38 |
-
|
39 |
-
/**
|
40 |
-
* Found validation errors
|
41 |
-
* @var array
|
42 |
-
*/
|
43 |
-
protected $errors;
|
44 |
-
|
45 |
-
/**
|
46 |
-
* If the get_errors() method was called at leas once
|
47 |
-
* @var bool
|
48 |
-
*/
|
49 |
-
protected $errors_accessed = false;
|
50 |
-
|
51 |
-
/**
|
52 |
-
* If current request is the submit of this form
|
53 |
-
* @var bool
|
54 |
-
*/
|
55 |
-
protected $is_submitted;
|
56 |
-
|
57 |
-
/**
|
58 |
-
* @var bool
|
59 |
-
*/
|
60 |
-
protected $validate_and_save_called = false;
|
61 |
-
|
62 |
-
protected $callbacks = array(
|
63 |
-
'render' => false,
|
64 |
-
'validate' => false,
|
65 |
-
'save' => false
|
66 |
-
);
|
67 |
-
|
68 |
-
/**
|
69 |
-
* @param string $id Unique
|
70 |
-
* @param array $data (optional)
|
71 |
-
* array(
|
72 |
-
* 'render' => callback // The callback that will render the form's html
|
73 |
-
* 'validate' => callback // The callback that will validate user input
|
74 |
-
* 'save' => callback // The callback that will save successfully validated user input
|
75 |
-
* 'attr' => array() // Custom <form ...> attributes
|
76 |
-
* )
|
77 |
-
*/
|
78 |
-
public function __construct( $id, $data = array() ) {
|
79 |
-
if ( isset( self::$forms[ $id ] ) ) {
|
80 |
-
trigger_error( sprintf( __( 'Form with id "%s" was already defined', 'fw' ), $id ), E_USER_ERROR );
|
81 |
-
}
|
82 |
-
|
83 |
-
$this->id = $id;
|
84 |
-
|
85 |
-
self::$forms[ $this->id ] =& $this;
|
86 |
-
|
87 |
-
// prepare $this->attr
|
88 |
-
{
|
89 |
-
if ( ! isset( $data['attr'] ) || ! is_array( $data['attr'] ) ) {
|
90 |
-
$data['attr'] = array();
|
91 |
-
}
|
92 |
-
|
93 |
-
$data['attr']['data-fw-form-id'] = $this->id;
|
94 |
-
|
95 |
-
/** @deprecated */
|
96 |
-
$data['attr']['class'] = 'fw_form_' . $this->id;
|
97 |
-
|
98 |
-
if ( isset( $data['attr']['method'] ) ) {
|
99 |
-
$data['attr']['method'] = strtolower( $data['attr']['method'] );
|
100 |
-
|
101 |
-
$data['attr']['method'] = in_array( $data['attr']['method'], array( 'get', 'post' ) )
|
102 |
-
? $data['attr']['method']
|
103 |
-
: 'post';
|
104 |
-
} else {
|
105 |
-
$data['attr']['method'] = 'post';
|
106 |
-
}
|
107 |
-
|
108 |
-
if ( ! isset( $data['attr']['action'] ) ) {
|
109 |
-
$data['attr']['action'] = fw_current_url();
|
110 |
-
}
|
111 |
-
|
112 |
-
$this->attr = $data['attr'];
|
113 |
-
}
|
114 |
-
|
115 |
-
// prepare $this->callbacks
|
116 |
-
{
|
117 |
-
$this->callbacks = array(
|
118 |
-
'render' => empty( $data['render'] ) ? false : $data['render'],
|
119 |
-
'validate' => empty( $data['validate'] ) ? false : $data['validate'],
|
120 |
-
'save' => empty( $data['save'] ) ? false : $data['save'],
|
121 |
-
);
|
122 |
-
}
|
123 |
-
|
124 |
-
if ( did_action( 'wp_loaded' ) ) {
|
125 |
-
// in case if form instance was created after action
|
126 |
-
$this->_validate_and_save();
|
127 |
-
} else {
|
128 |
-
// attach to an action before 'send_headers' action, to be able to do redirects
|
129 |
-
add_action( 'wp_loaded', array( $this, '_validate_and_save' ), 101 );
|
130 |
-
}
|
131 |
-
}
|
132 |
-
|
133 |
-
protected function validate() {
|
134 |
-
if ( is_array( $this->errors ) ) {
|
135 |
-
trigger_error( __METHOD__ . ' already called', E_USER_WARNING );
|
136 |
-
|
137 |
-
return;
|
138 |
-
}
|
139 |
-
|
140 |
-
/**
|
141 |
-
* Errors array {'input[name]' => 'Error message'}
|
142 |
-
*/
|
143 |
-
$errors = array();
|
144 |
-
|
145 |
-
/**
|
146 |
-
* Call validate callback
|
147 |
-
*
|
148 |
-
* Callback must 'manually' extract input values from $_POST (or $_GET)
|
149 |
-
*/
|
150 |
-
if ( $this->callbacks['validate'] ) {
|
151 |
-
$errors = call_user_func_array( $this->callbacks['validate'], array( $errors ) );
|
152 |
-
|
153 |
-
if ( ! is_array( $errors ) ) {
|
154 |
-
|
155 |
-
$errors = array();
|
156 |
-
}
|
157 |
-
}
|
158 |
-
|
159 |
-
/**
|
160 |
-
* check nonce
|
161 |
-
*/
|
162 |
-
if ( $this->attr['method'] == 'post' ) {
|
163 |
-
$nonce_name = '_nonce_' . md5( $this->id );
|
164 |
-
|
165 |
-
if ( ! isset( $_REQUEST[ $nonce_name ] ) || wp_verify_nonce( $_REQUEST[ $nonce_name ],
|
166 |
-
'submit_fwf' ) === false
|
167 |
-
) {
|
168 |
-
$errors[ $nonce_name ] = __( 'Nonce verification failed', 'fw' );
|
169 |
-
}
|
170 |
-
}
|
171 |
-
|
172 |
-
$this->errors = $errors;
|
173 |
-
}
|
174 |
-
|
175 |
-
protected function save() {
|
176 |
-
$save_data = array(
|
177 |
-
// you can set here a url for redirect after save
|
178 |
-
'redirect' => null
|
179 |
-
);
|
180 |
-
|
181 |
-
/**
|
182 |
-
* Call save callback
|
183 |
-
*
|
184 |
-
* Callback must 'manually' extract input values from $_POST (or $_GET)
|
185 |
-
*/
|
186 |
-
if ( $this->callbacks['save'] ) {
|
187 |
-
$data = call_user_func_array( $this->callbacks['save'], array( $save_data ) );
|
188 |
-
|
189 |
-
if ( ! is_array( $data ) ) {
|
190 |
-
// fix if returned wrong data from callback
|
191 |
-
$data = $save_data;
|
192 |
-
}
|
193 |
-
|
194 |
-
$save_data = $data;
|
195 |
-
|
196 |
-
unset( $data );
|
197 |
-
}
|
198 |
-
|
199 |
-
if ( ! $this->is_ajax() ) {
|
200 |
-
if ( isset( $save_data['redirect'] ) ) {
|
201 |
-
wp_redirect( $save_data['redirect'] );
|
202 |
-
exit;
|
203 |
-
}
|
204 |
-
}
|
205 |
-
|
206 |
-
return $save_data;
|
207 |
-
}
|
208 |
-
|
209 |
-
protected function is_ajax() {
|
210 |
-
return defined( 'DOING_AJAX' ) && DOING_AJAX;
|
211 |
-
}
|
212 |
-
|
213 |
-
/**
|
214 |
-
* If current form was submitted, validate and save it
|
215 |
-
*
|
216 |
-
* Note: This callback can abort script execution if save does redirect
|
217 |
-
*
|
218 |
-
* @return bool|null
|
219 |
-
* @internal
|
220 |
-
*/
|
221 |
-
public function _validate_and_save() {
|
222 |
-
if ( $this->validate_and_save_called ) {
|
223 |
-
trigger_error( __METHOD__ . ' already called', E_USER_WARNING );
|
224 |
-
|
225 |
-
return null;
|
226 |
-
} else {
|
227 |
-
$this->validate_and_save_called = true;
|
228 |
-
}
|
229 |
-
|
230 |
-
if ( ! $this->is_submitted() ) {
|
231 |
-
return null;
|
232 |
-
}
|
233 |
-
|
234 |
-
$this->validate();
|
235 |
-
|
236 |
-
if ( $this->is_ajax() ) {
|
237 |
-
$json_data = array();
|
238 |
-
|
239 |
-
if ( $this->is_valid() ) {
|
240 |
-
$json_data['save_data'] = $this->save();
|
241 |
-
} else {
|
242 |
-
$json_data['errors'] = $this->get_errors();
|
243 |
-
}
|
244 |
-
|
245 |
-
/**
|
246 |
-
* Transform flash messages structure from
|
247 |
-
* array( 'type' => array( 'message_id' => array(...) ) )
|
248 |
-
* to
|
249 |
-
* array( 'type' => array( 'message_id' => 'Message' ) )
|
250 |
-
*/
|
251 |
-
{
|
252 |
-
$flash_messages = array();
|
253 |
-
|
254 |
-
foreach (FW_Flash_Messages::_get_messages(true) as $type => $messages) {
|
255 |
-
$flash_messages[$type] = array();
|
256 |
-
|
257 |
-
foreach ($messages as $id => $message_data) {
|
258 |
-
$flash_messages[$type][$id] = $message_data['message'];
|
259 |
-
}
|
260 |
-
}
|
261 |
-
|
262 |
-
$json_data['flash_messages'] = $flash_messages;
|
263 |
-
}
|
264 |
-
|
265 |
-
/**
|
266 |
-
* Important!
|
267 |
-
* We can't send form html in response:
|
268 |
-
*
|
269 |
-
* ob_start();
|
270 |
-
* $this->render();
|
271 |
-
* $json_data['html'] = ob_get_clean();
|
272 |
-
*
|
273 |
-
* because the render() method is not called within this class
|
274 |
-
* but by the code that created and owns the $form,
|
275 |
-
* and it's usually called with some custom data $this->render(array(...))
|
276 |
-
* that it's impossible to know here which data is that.
|
277 |
-
* If we will call $this->render(); without data, this may throw errors because
|
278 |
-
* the render callback may expect some custom data.
|
279 |
-
* Also it may be called or not, depending on the owner code inner logic.
|
280 |
-
*
|
281 |
-
* The only way to get the latest form html on ajax submit
|
282 |
-
* is to make a new ajax GET to current page and extract form html from the response.
|
283 |
-
*/
|
284 |
-
|
285 |
-
if ( $this->is_valid() ) {
|
286 |
-
wp_send_json_success($json_data);
|
287 |
-
} else {
|
288 |
-
wp_send_json_error($json_data);
|
289 |
-
}
|
290 |
-
} else {
|
291 |
-
if ( ! $this->is_valid() ) {
|
292 |
-
return false;
|
293 |
-
}
|
294 |
-
|
295 |
-
$this->save();
|
296 |
-
}
|
297 |
-
|
298 |
-
return true;
|
299 |
-
}
|
300 |
-
|
301 |
-
/**
|
302 |
-
* @return string
|
303 |
-
*/
|
304 |
-
public function get_id() {
|
305 |
-
return $this->id;
|
306 |
-
}
|
307 |
-
|
308 |
-
/**
|
309 |
-
* Get html attribute(s)
|
310 |
-
*
|
311 |
-
* @param null|string $name
|
312 |
-
*
|
313 |
-
* @return array|string
|
314 |
-
*/
|
315 |
-
public function attr( $name = null ) {
|
316 |
-
if ( $name ) {
|
317 |
-
return isset( $this->attr[ $name ] ) ? $this->attr[ $name ] : null;
|
318 |
-
} else {
|
319 |
-
return $this->attr;
|
320 |
-
}
|
321 |
-
}
|
322 |
-
|
323 |
-
/**
|
324 |
-
* Render form's html
|
325 |
-
*
|
326 |
-
* @param array $data
|
327 |
-
*/
|
328 |
-
public function render( $data = array() ) {
|
329 |
-
$render_data = array(
|
330 |
-
'submit' => array(
|
331 |
-
'value' => __( 'Submit', 'fw' ),
|
332 |
-
/**
|
333 |
-
* you can set here custom submit button html
|
334 |
-
* and the 'value' parameter will not be used
|
335 |
-
*/
|
336 |
-
'html' => null,
|
337 |
-
),
|
338 |
-
'data' => $data,
|
339 |
-
'attr' => $this->attr,
|
340 |
-
);
|
341 |
-
|
342 |
-
unset( $data );
|
343 |
-
|
344 |
-
if ( $this->callbacks['render'] ) {
|
345 |
-
ob_start();
|
346 |
-
|
347 |
-
$data = call_user_func_array( $this->callbacks['render'], array( $render_data, $this ) );
|
348 |
-
|
349 |
-
$html = ob_get_clean();
|
350 |
-
|
351 |
-
if ( empty( $data ) ) {
|
352 |
-
// fix if returned wrong data from callback
|
353 |
-
$data = $render_data;
|
354 |
-
}
|
355 |
-
|
356 |
-
$render_data = $data;
|
357 |
-
|
358 |
-
unset( $data );
|
359 |
-
}
|
360 |
-
|
361 |
-
// display form errors in frontend
|
362 |
-
do {
|
363 |
-
if (is_admin()) {
|
364 |
-
// errors in admin side are displayed by a script at the end of this file
|
365 |
-
break;
|
366 |
-
}
|
367 |
-
|
368 |
-
$submitted_form = FW_Form::get_submitted();
|
369 |
-
|
370 |
-
if ( ! $submitted_form ) {
|
371 |
-
break;
|
372 |
-
}
|
373 |
-
|
374 |
-
if ( $submitted_form->get_id() !== $this->get_id() ) {
|
375 |
-
// the submitted form is not current form
|
376 |
-
break;
|
377 |
-
}
|
378 |
-
|
379 |
-
unset($submitted_form); // not needed anymore, below will be used only with $this (because it's the same form)
|
380 |
-
|
381 |
-
if ( $this->is_valid() ) {
|
382 |
-
break;
|
383 |
-
}
|
384 |
-
|
385 |
-
/**
|
386 |
-
* Use this action to customize errors display in your theme
|
387 |
-
*/
|
388 |
-
do_action('fw_form_display_errors_frontend', $this);
|
389 |
-
|
390 |
-
if ( $this->errors_accessed() ) {
|
391 |
-
// already displayed, prevent/cancel default display
|
392 |
-
break;
|
393 |
-
}
|
394 |
-
|
395 |
-
$errors = $this->get_errors();
|
396 |
-
|
397 |
-
if (empty($errors)) {
|
398 |
-
break;
|
399 |
-
}
|
400 |
-
|
401 |
-
echo '<ul class="fw-form-errors">';
|
402 |
-
|
403 |
-
foreach ($errors as $input_name => $error_message) {
|
404 |
-
echo fw_html_tag(
|
405 |
-
'li',
|
406 |
-
array(
|
407 |
-
'data-input-name' => $input_name,
|
408 |
-
),
|
409 |
-
$error_message
|
410 |
-
);
|
411 |
-
}
|
412 |
-
|
413 |
-
echo '</ul>';
|
414 |
-
|
415 |
-
unset($errors);
|
416 |
-
} while(false);
|
417 |
-
|
418 |
-
echo '<form '. fw_attr_to_html( $render_data['attr'] ) .' >';
|
419 |
-
|
420 |
-
echo fw_html_tag('input', array(
|
421 |
-
'type' => 'hidden',
|
422 |
-
'name' => self::$id_input_name,
|
423 |
-
'value' => $this->id,
|
424 |
-
));
|
425 |
-
|
426 |
-
if ( $render_data['attr']['method'] == 'post' ) {
|
427 |
-
wp_nonce_field( 'submit_fwf', '_nonce_' . md5( $this->id ) );
|
428 |
-
}
|
429 |
-
|
430 |
-
if ( ! empty( $render_data['attr']['action'] ) && $render_data['attr']['method'] == 'get' ) {
|
431 |
-
/**
|
432 |
-
* Add query vars from the action attribute url to hidden inputs to not loose them
|
433 |
-
*/
|
434 |
-
|
435 |
-
parse_str( parse_url( $render_data['attr']['action'], PHP_URL_QUERY ), $query_vars );
|
436 |
-
|
437 |
-
if ( ! empty( $query_vars ) ) {
|
438 |
-
foreach ( $query_vars as $var_name => $var_value ) {
|
439 |
-
echo fw_html_tag('input', array(
|
440 |
-
'type' => 'hidden',
|
441 |
-
'name' => $var_name,
|
442 |
-
'value' => $var_value,
|
443 |
-
));
|
444 |
-
}
|
445 |
-
}
|
446 |
-
}
|
447 |
-
|
448 |
-
echo $html;
|
449 |
-
|
450 |
-
// In filter can be defined custom html for submit button
|
451 |
-
if ( isset( $render_data['submit']['html'] ) ) {
|
452 |
-
echo $render_data['submit']['html'];
|
453 |
-
} else {
|
454 |
-
echo fw_html_tag('input', array(
|
455 |
-
'type' => 'submit',
|
456 |
-
'value' => $render_data['submit']['value']
|
457 |
-
));
|
458 |
-
}
|
459 |
-
|
460 |
-
echo '</form>';
|
461 |
-
}
|
462 |
-
|
463 |
-
/**
|
464 |
-
* If now is a submit of this form
|
465 |
-
* @return bool
|
466 |
-
*/
|
467 |
-
public function is_submitted() {
|
468 |
-
if ( is_null( $this->is_submitted ) ) {
|
469 |
-
$method = strtoupper( $this->attr( 'method' ) );
|
470 |
-
|
471 |
-
if ( $method === 'POST' ) {
|
472 |
-
$this->is_submitted = (
|
473 |
-
isset( $_POST[ self::$id_input_name ] )
|
474 |
-
&&
|
475 |
-
FW_Request::POST( self::$id_input_name ) === $this->id
|
476 |
-
);
|
477 |
-
|
478 |
-
} elseif ( $method === 'GET' ) {
|
479 |
-
$this->is_submitted = (
|
480 |
-
isset( $_GET[ self::$id_input_name ] )
|
481 |
-
&&
|
482 |
-
FW_Request::GET( self::$id_input_name ) === $this->id
|
483 |
-
);
|
484 |
-
} else {
|
485 |
-
$this->is_submitted = false;
|
486 |
-
}
|
487 |
-
}
|
488 |
-
|
489 |
-
return $this->is_submitted;
|
490 |
-
}
|
491 |
-
|
492 |
-
/**
|
493 |
-
* @return bool
|
494 |
-
*/
|
495 |
-
public function is_valid() {
|
496 |
-
if ( ! $this->validate_and_save_called ) {
|
497 |
-
trigger_error( __METHOD__ . ' called before validation', E_USER_WARNING );
|
498 |
-
|
499 |
-
return null;
|
500 |
-
}
|
501 |
-
|
502 |
-
return empty( $this->errors );
|
503 |
-
}
|
504 |
-
|
505 |
-
/**
|
506 |
-
* Get validation errors
|
507 |
-
* @return array
|
508 |
-
*/
|
509 |
-
public function get_errors() {
|
510 |
-
if ( ! $this->validate_and_save_called ) {
|
511 |
-
trigger_error( __METHOD__ . ' called before validation', E_USER_WARNING );
|
512 |
-
|
513 |
-
return array( '~' => true );
|
514 |
-
}
|
515 |
-
|
516 |
-
$this->errors_accessed = true;
|
517 |
-
|
518 |
-
return $this->errors;
|
519 |
-
}
|
520 |
-
|
521 |
-
public function errors_accessed()
|
522 |
-
{
|
523 |
-
return $this->errors_accessed;
|
524 |
-
}
|
525 |
-
|
526 |
-
/**
|
527 |
-
* Get submitted form instance (or false if no form is currently submitted)
|
528 |
-
* @return FW_Form|false
|
529 |
-
*/
|
530 |
-
public static function get_submitted() {
|
531 |
-
if ( is_null( self::$submitted_id ) ) {
|
532 |
-
// method called first time, search for submitted form
|
533 |
-
do {
|
534 |
-
foreach ( self::$forms as $form ) {
|
535 |
-
if ( $form->is_submitted() ) {
|
536 |
-
self::$submitted_id = $form->get_id();
|
537 |
-
break 2;
|
538 |
-
}
|
539 |
-
}
|
540 |
-
|
541 |
-
self::$submitted_id = false;
|
542 |
-
} while ( false );
|
543 |
-
}
|
544 |
-
|
545 |
-
if ( is_string( self::$submitted_id ) ) {
|
546 |
-
return self::$forms[ self::$submitted_id ];
|
547 |
-
} else {
|
548 |
-
return false;
|
549 |
-
}
|
550 |
-
}
|
551 |
-
}
|
552 |
-
|
553 |
-
if ( is_admin() ) {
|
554 |
-
/**
|
555 |
-
* Display form errors in admin side
|
556 |
-
* @internal
|
557 |
-
*/
|
558 |
-
function _action_fw_form_show_errors_in_admin() {
|
559 |
-
$form = FW_Form::get_submitted();
|
560 |
-
|
561 |
-
if ( ! $form || $form->is_valid() ) {
|
562 |
-
return;
|
563 |
-
}
|
564 |
-
|
565 |
-
foreach ( $form->get_errors() as $input_name => $error_message ) {
|
566 |
-
FW_Flash_Messages::add( 'fw-form-admin-' . $input_name, $error_message, 'error' );
|
567 |
-
}
|
568 |
-
}
|
569 |
-
add_action( 'wp_loaded', '_action_fw_form_show_errors_in_admin', 111 );
|
570 |
-
} else {
|
571 |
-
/**
|
572 |
-
* to disable this use remove_action('wp_print_styles', '_action_fw_form_frontend_default_styles');
|
573 |
-
* @internal
|
574 |
-
*/
|
575 |
-
function _action_fw_form_frontend_default_styles() {
|
576 |
-
$form = FW_Form::get_submitted();
|
577 |
-
|
578 |
-
if ( ! $form || $form->is_valid() ) {
|
579 |
-
return;
|
580 |
-
}
|
581 |
-
|
582 |
-
echo '<style type="text/css">.fw-form-errors { color: #bf0000; }</style>';
|
583 |
-
}
|
584 |
-
add_action( 'wp_print_styles', '_action_fw_form_frontend_default_styles' );
|
585 |
-
}
|
1 |
+
<?php if ( ! defined( 'FW' ) ) {
|
2 |
+
die( 'Forbidden' );
|
3 |
+
}
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Dynamic forms
|
7 |
+
*/
|
8 |
+
class FW_Form {
|
9 |
+
/**
|
10 |
+
* Store all form ids created with this class
|
11 |
+
* @var FW_Form[] {'form_id' => instance}
|
12 |
+
*/
|
13 |
+
protected static $forms = array();
|
14 |
+
|
15 |
+
/**
|
16 |
+
* The id of the submitted form id
|
17 |
+
* @var string
|
18 |
+
*/
|
19 |
+
protected static $submitted_id;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Hidden input name that stores the form id
|
23 |
+
* @var string
|
24 |
+
*/
|
25 |
+
protected static $id_input_name = 'fwf';
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Form id
|
29 |
+
* @var string
|
30 |
+
*/
|
31 |
+
protected $id;
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Html attributes for <form> tag
|
35 |
+
* @var array
|
36 |
+
*/
|
37 |
+
protected $attr;
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Found validation errors
|
41 |
+
* @var array
|
42 |
+
*/
|
43 |
+
protected $errors;
|
44 |
+
|
45 |
+
/**
|
46 |
+
* If the get_errors() method was called at leas once
|
47 |
+
* @var bool
|
48 |
+
*/
|
49 |
+
protected $errors_accessed = false;
|
50 |
+
|
51 |
+
/**
|
52 |
+
* If current request is the submit of this form
|
53 |
+
* @var bool
|
54 |
+
*/
|
55 |
+
protected $is_submitted;
|
56 |
+
|
57 |
+
/**
|
58 |
+
* @var bool
|
59 |
+
*/
|
60 |
+
protected $validate_and_save_called = false;
|
61 |
+
|
62 |
+
protected $callbacks = array(
|
63 |
+
'render' => false,
|
64 |
+
'validate' => false,
|
65 |
+
'save' => false
|
66 |
+
);
|
67 |
+
|
68 |
+
/**
|
69 |
+
* @param string $id Unique
|
70 |
+
* @param array $data (optional)
|
71 |
+
* array(
|
72 |
+
* 'render' => callback // The callback that will render the form's html
|
73 |
+
* 'validate' => callback // The callback that will validate user input
|
74 |
+
* 'save' => callback // The callback that will save successfully validated user input
|
75 |
+
* 'attr' => array() // Custom <form ...> attributes
|
76 |
+
* )
|
77 |
+
*/
|
78 |
+
public function __construct( $id, $data = array() ) {
|
79 |
+
if ( isset( self::$forms[ $id ] ) ) {
|
80 |
+
trigger_error( sprintf( __( 'Form with id "%s" was already defined', 'fw' ), $id ), E_USER_ERROR );
|
81 |
+
}
|
82 |
+
|
83 |
+
$this->id = $id;
|
84 |
+
|
85 |
+
self::$forms[ $this->id ] =& $this;
|
86 |
+
|
87 |
+
// prepare $this->attr
|
88 |
+
{
|
89 |
+
if ( ! isset( $data['attr'] ) || ! is_array( $data['attr'] ) ) {
|
90 |
+
$data['attr'] = array();
|
91 |
+
}
|
92 |
+
|
93 |
+
$data['attr']['data-fw-form-id'] = $this->id;
|
94 |
+
|
95 |
+
/** @deprecated */
|
96 |
+
$data['attr']['class'] = 'fw_form_' . $this->id;
|
97 |
+
|
98 |
+
if ( isset( $data['attr']['method'] ) ) {
|
99 |
+
$data['attr']['method'] = strtolower( $data['attr']['method'] );
|
100 |
+
|
101 |
+
$data['attr']['method'] = in_array( $data['attr']['method'], array( 'get', 'post' ) )
|
102 |
+
? $data['attr']['method']
|
103 |
+
: 'post';
|
104 |
+
} else {
|
105 |
+
$data['attr']['method'] = 'post';
|
106 |
+
}
|
107 |
+
|
108 |
+
if ( ! isset( $data['attr']['action'] ) ) {
|
109 |
+
$data['attr']['action'] = fw_current_url();
|
110 |
+
}
|
111 |
+
|
112 |
+
$this->attr = $data['attr'];
|
113 |
+
}
|
114 |
+
|
115 |
+
// prepare $this->callbacks
|
116 |
+
{
|
117 |
+
$this->callbacks = array(
|
118 |
+
'render' => empty( $data['render'] ) ? false : $data['render'],
|
119 |
+
'validate' => empty( $data['validate'] ) ? false : $data['validate'],
|
120 |
+
'save' => empty( $data['save'] ) ? false : $data['save'],
|
121 |
+
);
|
122 |
+
}
|
123 |
+
|
124 |
+
if ( did_action( 'wp_loaded' ) ) {
|
125 |
+
// in case if form instance was created after action
|
126 |
+
$this->_validate_and_save();
|
127 |
+
} else {
|
128 |
+
// attach to an action before 'send_headers' action, to be able to do redirects
|
129 |
+
add_action( 'wp_loaded', array( $this, '_validate_and_save' ), 101 );
|
130 |
+
}
|
131 |
+
}
|
132 |
+
|
133 |
+
protected function validate() {
|
134 |
+
if ( is_array( $this->errors ) ) {
|
135 |
+
trigger_error( __METHOD__ . ' already called', E_USER_WARNING );
|
136 |
+
|
137 |
+
return;
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Errors array {'input[name]' => 'Error message'}
|
142 |
+
*/
|
143 |
+
$errors = array();
|
144 |
+
|
145 |
+
/**
|
146 |
+
* Call validate callback
|
147 |
+
*
|
148 |
+
* Callback must 'manually' extract input values from $_POST (or $_GET)
|
149 |
+
*/
|
150 |
+
if ( $this->callbacks['validate'] ) {
|
151 |
+
$errors = call_user_func_array( $this->callbacks['validate'], array( $errors ) );
|
152 |
+
|
153 |
+
if ( ! is_array( $errors ) ) {
|
154 |
+
|
155 |
+
$errors = array();
|
156 |
+
}
|
157 |
+
}
|
158 |
+
|
159 |
+
/**
|
160 |
+
* check nonce
|
161 |
+
*/
|
162 |
+
if ( $this->attr['method'] == 'post' ) {
|
163 |
+
$nonce_name = '_nonce_' . md5( $this->id );
|
164 |
+
|
165 |
+
if ( ! isset( $_REQUEST[ $nonce_name ] ) || wp_verify_nonce( $_REQUEST[ $nonce_name ],
|
166 |
+
'submit_fwf' ) === false
|
167 |
+
) {
|
168 |
+
$errors[ $nonce_name ] = __( 'Nonce verification failed', 'fw' );
|
169 |
+
}
|
170 |
+
}
|
171 |
+
|
172 |
+
$this->errors = $errors;
|
173 |
+
}
|
174 |
+
|
175 |
+
protected function save() {
|
176 |
+
$save_data = array(
|
177 |
+
// you can set here a url for redirect after save
|
178 |
+
'redirect' => null
|
179 |
+
);
|
180 |
+
|
181 |
+
/**
|
182 |
+
* Call save callback
|
183 |
+
*
|
184 |
+
* Callback must 'manually' extract input values from $_POST (or $_GET)
|
185 |
+
*/
|
186 |
+
if ( $this->callbacks['save'] ) {
|
187 |
+
$data = call_user_func_array( $this->callbacks['save'], array( $save_data ) );
|
188 |
+
|
189 |
+
if ( ! is_array( $data ) ) {
|
190 |
+
// fix if returned wrong data from callback
|
191 |
+
$data = $save_data;
|
192 |
+
}
|
193 |
+
|
194 |
+
$save_data = $data;
|
195 |
+
|
196 |
+
unset( $data );
|
197 |
+
}
|
198 |
+
|
199 |
+
if ( ! $this->is_ajax() ) {
|
200 |
+
if ( isset( $save_data['redirect'] ) ) {
|
201 |
+
wp_redirect( $save_data['redirect'] );
|
202 |
+
exit;
|
203 |
+
}
|
204 |
+
}
|
205 |
+
|
206 |
+
return $save_data;
|
207 |
+
}
|
208 |
+
|
209 |
+
protected function is_ajax() {
|
210 |
+
return defined( 'DOING_AJAX' ) && DOING_AJAX;
|
211 |
+
}
|
212 |
+
|
213 |
+
/**
|
214 |
+
* If current form was submitted, validate and save it
|
215 |
+
*
|
216 |
+
* Note: This callback can abort script execution if save does redirect
|
217 |
+
*
|
218 |
+
* @return bool|null
|
219 |
+
* @internal
|
220 |
+
*/
|
221 |
+
public function _validate_and_save() {
|
222 |
+
if ( $this->validate_and_save_called ) {
|
223 |
+
trigger_error( __METHOD__ . ' already called', E_USER_WARNING );
|
224 |
+
|
225 |
+
return null;
|
226 |
+
} else {
|
227 |
+
$this->validate_and_save_called = true;
|
228 |
+
}
|
229 |
+
|
230 |
+
if ( ! $this->is_submitted() ) {
|
231 |
+
return null;
|
232 |
+
}
|
233 |
+
|
234 |
+
$this->validate();
|
235 |
+
|
236 |
+
if ( $this->is_ajax() ) {
|
237 |
+
$json_data = array();
|
238 |
+
|
239 |
+
if ( $this->is_valid() ) {
|
240 |
+
$json_data['save_data'] = $this->save();
|
241 |
+
} else {
|
242 |
+
$json_data['errors'] = $this->get_errors();
|
243 |
+
}
|
244 |
+
|
245 |
+
/**
|
246 |
+
* Transform flash messages structure from
|
247 |
+
* array( 'type' => array( 'message_id' => array(...) ) )
|
248 |
+
* to
|
249 |
+
* array( 'type' => array( 'message_id' => 'Message' ) )
|
250 |
+
*/
|
251 |
+
{
|
252 |
+
$flash_messages = array();
|
253 |
+
|
254 |
+
foreach (FW_Flash_Messages::_get_messages(true) as $type => $messages) {
|
255 |
+
$flash_messages[$type] = array();
|
256 |
+
|
257 |
+
foreach ($messages as $id => $message_data) {
|
258 |
+
$flash_messages[$type][$id] = $message_data['message'];
|
259 |
+
}
|
260 |
+
}
|
261 |
+
|
262 |
+
$json_data['flash_messages'] = $flash_messages;
|
263 |
+
}
|
264 |
+
|
265 |
+
/**
|
266 |
+
* Important!
|
267 |
+
* We can't send form html in response:
|
268 |
+
*
|
269 |
+
* ob_start();
|
270 |
+
* $this->render();
|
271 |
+
* $json_data['html'] = ob_get_clean();
|
272 |
+
*
|
273 |
+
* because the render() method is not called within this class
|
274 |
+
* but by the code that created and owns the $form,
|
275 |
+
* and it's usually called with some custom data $this->render(array(...))
|
276 |
+
* that it's impossible to know here which data is that.
|
277 |
+
* If we will call $this->render(); without data, this may throw errors because
|
278 |
+
* the render callback may expect some custom data.
|
279 |
+
* Also it may be called or not, depending on the owner code inner logic.
|
280 |
+
*
|
281 |
+
* The only way to get the latest form html on ajax submit
|
282 |
+
* is to make a new ajax GET to current page and extract form html from the response.
|
283 |
+
*/
|
284 |
+
|
285 |
+
if ( $this->is_valid() ) {
|
286 |
+
wp_send_json_success($json_data);
|
287 |
+
} else {
|
288 |
+
wp_send_json_error($json_data);
|
289 |
+
}
|
290 |
+
} else {
|
291 |
+
if ( ! $this->is_valid() ) {
|
292 |
+
return false;
|
293 |
+
}
|
294 |
+
|
295 |
+
$this->save();
|
296 |
+
}
|
297 |
+
|
298 |
+
return true;
|
299 |
+
}
|
300 |
+
|
301 |
+
/**
|
302 |
+
* @return string
|
303 |
+
*/
|
304 |
+
public function get_id() {
|
305 |
+
return $this->id;
|
306 |
+
}
|
307 |
+
|
308 |
+
/**
|
309 |
+
* Get html attribute(s)
|
310 |
+
*
|
311 |
+
* @param null|string $name
|
312 |
+
*
|
313 |
+
* @return array|string
|
314 |
+
*/
|
315 |
+
public function attr( $name = null ) {
|
316 |
+
if ( $name ) {
|
317 |
+
return isset( $this->attr[ $name ] ) ? $this->attr[ $name ] : null;
|
318 |
+
} else {
|
319 |
+
return $this->attr;
|
320 |
+
}
|
321 |
+
}
|
322 |
+
|
323 |
+
/**
|
324 |
+
* Render form's html
|
325 |
+
*
|
326 |
+
* @param array $data
|
327 |
+
*/
|
328 |
+
public function render( $data = array() ) {
|
329 |
+
$render_data = array(
|
330 |
+
'submit' => array(
|
331 |
+
'value' => __( 'Submit', 'fw' ),
|
332 |
+
/**
|
333 |
+
* you can set here custom submit button html
|
334 |
+
* and the 'value' parameter will not be used
|
335 |
+
*/
|
336 |
+
'html' => null,
|
337 |
+
),
|
338 |
+
'data' => $data,
|
339 |
+
'attr' => $this->attr,
|
340 |
+
);
|
341 |
+
|
342 |
+
unset( $data );
|
343 |
+
|
344 |
+
if ( $this->callbacks['render'] ) {
|
345 |
+
ob_start();
|
346 |
+
|
347 |
+
$data = call_user_func_array( $this->callbacks['render'], array( $render_data, $this ) );
|
348 |
+
|
349 |
+
$html = ob_get_clean();
|
350 |
+
|
351 |
+
if ( empty( $data ) ) {
|
352 |
+
// fix if returned wrong data from callback
|
353 |
+
$data = $render_data;
|
354 |
+
}
|
355 |
+
|
356 |
+
$render_data = $data;
|
357 |
+
|
358 |
+
unset( $data );
|
359 |
+
}
|
360 |
+
|
361 |
+
// display form errors in frontend
|
362 |
+
do {
|
363 |
+
if (is_admin()) {
|
364 |
+
// errors in admin side are displayed by a script at the end of this file
|
365 |
+
break;
|
366 |
+
}
|
367 |
+
|
368 |
+
$submitted_form = FW_Form::get_submitted();
|
369 |
+
|
370 |
+
if ( ! $submitted_form ) {
|
371 |
+
break;
|
372 |
+
}
|
373 |
+
|
374 |
+
if ( $submitted_form->get_id() !== $this->get_id() ) {
|
375 |
+
// the submitted form is not current form
|
376 |
+
break;
|
377 |
+
}
|
378 |
+
|
379 |
+
unset($submitted_form); // not needed anymore, below will be used only with $this (because it's the same form)
|
380 |
+
|
381 |
+
if ( $this->is_valid() ) {
|
382 |
+
break;
|
383 |
+
}
|
384 |
+
|
385 |
+
/**
|
386 |
+
* Use this action to customize errors display in your theme
|
387 |
+
*/
|
388 |
+
do_action('fw_form_display_errors_frontend', $this);
|
389 |
+
|
390 |
+
if ( $this->errors_accessed() ) {
|
391 |
+
// already displayed, prevent/cancel default display
|
392 |
+
break;
|
393 |
+
}
|
394 |
+
|
395 |
+
$errors = $this->get_errors();
|
396 |
+
|
397 |
+
if (empty($errors)) {
|
398 |
+
break;
|
399 |
+
}
|
400 |
+
|
401 |
+
echo '<ul class="fw-form-errors">';
|
402 |
+
|
403 |
+
foreach ($errors as $input_name => $error_message) {
|
404 |
+
echo fw_html_tag(
|
405 |
+
'li',
|
406 |
+
array(
|
407 |
+
'data-input-name' => $input_name,
|
408 |
+
),
|
409 |
+
$error_message
|
410 |
+
);
|
411 |
+
}
|
412 |
+
|
413 |
+
echo '</ul>';
|
414 |
+
|
415 |
+
unset($errors);
|
416 |
+
} while(false);
|
417 |
+
|
418 |
+
echo '<form '. fw_attr_to_html( $render_data['attr'] ) .' >';
|
419 |
+
|
420 |
+
echo fw_html_tag('input', array(
|
421 |
+
'type' => 'hidden',
|
422 |
+
'name' => self::$id_input_name,
|
423 |
+
'value' => $this->id,
|
424 |
+
));
|
425 |
+
|
426 |
+
if ( $render_data['attr']['method'] == 'post' ) {
|
427 |
+
wp_nonce_field( 'submit_fwf', '_nonce_' . md5( $this->id ) );
|
428 |
+
}
|
429 |
+
|
430 |
+
if ( ! empty( $render_data['attr']['action'] ) && $render_data['attr']['method'] == 'get' ) {
|
431 |
+
/**
|
432 |
+
* Add query vars from the action attribute url to hidden inputs to not loose them
|
433 |
+
*/
|
434 |
+
|
435 |
+
parse_str( parse_url( $render_data['attr']['action'], PHP_URL_QUERY ), $query_vars );
|
436 |
+
|
437 |
+
if ( ! empty( $query_vars ) ) {
|
438 |
+
foreach ( $query_vars as $var_name => $var_value ) {
|
439 |
+
echo fw_html_tag('input', array(
|
440 |
+
'type' => 'hidden',
|
441 |
+
'name' => $var_name,
|
442 |
+
'value' => $var_value,
|
443 |
+
));
|
444 |
+
}
|
445 |
+
}
|
446 |
+
}
|
447 |
+
|
448 |
+
echo $html;
|
449 |
+
|
450 |
+
// In filter can be defined custom html for submit button
|
451 |
+
if ( isset( $render_data['submit']['html'] ) ) {
|
452 |
+
echo $render_data['submit']['html'];
|
453 |
+
} else {
|
454 |
+
echo fw_html_tag('input', array(
|
455 |
+
'type' => 'submit',
|
456 |
+
'value' => $render_data['submit']['value']
|
457 |
+
));
|
458 |
+
}
|
459 |
+
|
460 |
+
echo '</form>';
|
461 |
+
}
|
462 |
+
|
463 |
+
/**
|
464 |
+
* If now is a submit of this form
|
465 |
+
* @return bool
|
466 |
+
*/
|
467 |
+
public function is_submitted() {
|
468 |
+
if ( is_null( $this->is_submitted ) ) {
|
469 |
+
$method = strtoupper( $this->attr( 'method' ) );
|
470 |
+
|
471 |
+
if ( $method === 'POST' ) {
|
472 |
+
$this->is_submitted = (
|
473 |
+
isset( $_POST[ self::$id_input_name ] )
|
474 |
+
&&
|
475 |
+
FW_Request::POST( self::$id_input_name ) === $this->id
|
476 |
+
);
|
477 |
+
|
478 |
+
} elseif ( $method === 'GET' ) {
|
479 |
+
$this->is_submitted = (
|
480 |
+
isset( $_GET[ self::$id_input_name ] )
|
481 |
+
&&
|
482 |
+
FW_Request::GET( self::$id_input_name ) === $this->id
|
483 |
+
);
|
484 |
+
} else {
|
485 |
+
$this->is_submitted = false;
|
486 |
+
}
|
487 |
+
}
|
488 |
+
|
489 |
+
return $this->is_submitted;
|
490 |
+
}
|
491 |
+
|
492 |
+
/**
|
493 |
+
* @return bool
|
494 |
+
*/
|
495 |
+
public function is_valid() {
|
496 |
+
if ( ! $this->validate_and_save_called ) {
|
497 |
+
trigger_error( __METHOD__ . ' called before validation', E_USER_WARNING );
|
498 |
+
|
499 |
+
return null;
|
500 |
+
}
|
501 |
+
|
502 |
+
return empty( $this->errors );
|
503 |
+
}
|
504 |
+
|
505 |
+
/**
|
506 |
+
* Get validation errors
|
507 |
+
* @return array
|
508 |
+
*/
|
509 |
+
public function get_errors() {
|
510 |
+
if ( ! $this->validate_and_save_called ) {
|
511 |
+
trigger_error( __METHOD__ . ' called before validation', E_USER_WARNING );
|
512 |
+
|
513 |
+
return array( '~' => true );
|
514 |
+
}
|
515 |
+
|
516 |
+
$this->errors_accessed = true;
|
517 |
+
|
518 |
+
return $this->errors;
|
519 |
+
}
|
520 |
+
|
521 |
+
public function errors_accessed()
|
522 |
+
{
|
523 |
+
return $this->errors_accessed;
|
524 |
+
}
|
525 |
+
|
526 |
+
/**
|
527 |
+
* Get submitted form instance (or false if no form is currently submitted)
|
528 |
+
* @return FW_Form|false
|
529 |
+
*/
|
530 |
+
public static function get_submitted() {
|
531 |
+
if ( is_null( self::$submitted_id ) ) {
|
532 |
+
// method called first time, search for submitted form
|
533 |
+
do {
|
534 |
+
foreach ( self::$forms as $form ) {
|
535 |
+
if ( $form->is_submitted() ) {
|
536 |
+
self::$submitted_id = $form->get_id();
|
537 |
+
break 2;
|
538 |
+
}
|
539 |
+
}
|
540 |
+
|
541 |
+
self::$submitted_id = false;
|
542 |
+
} while ( false );
|
543 |
+
}
|
544 |
+
|
545 |
+
if ( is_string( self::$submitted_id ) ) {
|
546 |
+
return self::$forms[ self::$submitted_id ];
|
547 |
+
} else {
|
548 |
+
return false;
|
549 |
+
}
|
550 |
+
}
|
551 |
+
}
|
552 |
+
|
553 |
+
if ( is_admin() ) {
|
554 |
+
/**
|
555 |
+
* Display form errors in admin side
|
556 |
+
* @internal
|
557 |
+
*/
|
558 |
+
function _action_fw_form_show_errors_in_admin() {
|
559 |
+
$form = FW_Form::get_submitted();
|
560 |
+
|
561 |
+
if ( ! $form || $form->is_valid() ) {
|
562 |
+
return;
|
563 |
+
}
|
564 |
+
|
565 |
+
foreach ( $form->get_errors() as $input_name => $error_message ) {
|
566 |
+
FW_Flash_Messages::add( 'fw-form-admin-' . $input_name, $error_message, 'error' );
|
567 |
+
}
|
568 |
+
}
|
569 |
+
add_action( 'wp_loaded', '_action_fw_form_show_errors_in_admin', 111 );
|
570 |
+
} else {
|
571 |
+
/**
|
572 |
+
* to disable this use remove_action('wp_print_styles', '_action_fw_form_frontend_default_styles');
|
573 |
+
* @internal
|
574 |
+
*/
|
575 |
+
function _action_fw_form_frontend_default_styles() {
|
576 |
+
$form = FW_Form::get_submitted();
|
577 |
+
|
578 |
+
if ( ! $form || $form->is_valid() ) {
|
579 |
+
return;
|
580 |
+
}
|
581 |
+
|
582 |
+
echo '<style type="text/css">.fw-form-errors { color: #bf0000; }</style>';
|
583 |
+
}
|
584 |
+
add_action( 'wp_print_styles', '_action_fw_form_frontend_default_styles' );
|
585 |
+
}
|
framework/helpers/class-fw-request.php
CHANGED
@@ -1,90 +1,90 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
|
3 |
-
/**
|
4 |
-
* WordPress automatically adds slashes to:
|
5 |
-
* $_REQUEST
|
6 |
-
* $_POST
|
7 |
-
* $_GET
|
8 |
-
* $_COOKIE
|
9 |
-
*
|
10 |
-
* For e.g.
|
11 |
-
*
|
12 |
-
* If value is simple, get value directly:
|
13 |
-
* $foo = isset($_GET['bar']) && $_GET['bar'] == 'yes';
|
14 |
-
*
|
15 |
-
* If value can contain some user input and can have quotes or json from some option, then use this helper:
|
16 |
-
* $foo = json_decode(FW_Request::POST('bar')); // json_decode($_POST('bar')) will not work if json will contain quotes
|
17 |
-
*
|
18 |
-
* You can test that problem.
|
19 |
-
* Add somewhere this code:
|
20 |
-
fw_print(array(
|
21 |
-
$_GET['test'],
|
22 |
-
json_decode($_GET['test']),
|
23 |
-
FW_Request::GET('test'),
|
24 |
-
json_decode(FW_Request::GET('test'))
|
25 |
-
));
|
26 |
-
* and access: http://your-site.com/?test={'a':1}
|
27 |
-
*/
|
28 |
-
class FW_Request
|
29 |
-
{
|
30 |
-
protected static function prepare_key($key)
|
31 |
-
{
|
32 |
-
return (get_magic_quotes_gpc() && is_string($key) ? addslashes($key) : $key);
|
33 |
-
}
|
34 |
-
|
35 |
-
protected static function get_set_key($multikey = null, $set_value = null, &$value)
|
36 |
-
{
|
37 |
-
$multikey = self::prepare_key($multikey);
|
38 |
-
|
39 |
-
if ($set_value === null) { // get
|
40 |
-
return fw_stripslashes_deep_keys($multikey === null ? $value : fw_akg($multikey, $value));
|
41 |
-
} else { // set
|
42 |
-
fw_aks($multikey, fw_addslashes_deep_keys($set_value), $value);
|
43 |
-
}
|
44 |
-
|
45 |
-
return '';
|
46 |
-
}
|
47 |
-
|
48 |
-
public static function GET($multikey = null, $default_value = null)
|
49 |
-
{
|
50 |
-
return fw_stripslashes_deep_keys(
|
51 |
-
$multikey === null
|
52 |
-
? $_GET
|
53 |
-
: fw_akg($multikey, $_GET, $default_value)
|
54 |
-
);
|
55 |
-
}
|
56 |
-
|
57 |
-
public static function POST($multikey = null, $default_value = null)
|
58 |
-
{
|
59 |
-
return fw_stripslashes_deep_keys(
|
60 |
-
$multikey === null
|
61 |
-
? $_POST
|
62 |
-
: fw_akg($multikey, $_POST, $default_value)
|
63 |
-
);
|
64 |
-
}
|
65 |
-
|
66 |
-
public static function COOKIE($multikey = null, $set_value = null, $expire = 0, $path = null)
|
67 |
-
{
|
68 |
-
if ($set_value !== null) {
|
69 |
-
|
70 |
-
// transforms a string ( key1/key2/key3 => key1][key2][key3] )
|
71 |
-
$multikey = str_replace('/', '][', $multikey) . ']';
|
72 |
-
|
73 |
-
// removes the first closed square bracket ( key1][key2][key3] => key1[key2][key3] )
|
74 |
-
$multikey = preg_replace('/\]/', '', $multikey, 1);
|
75 |
-
|
76 |
-
return setcookie($multikey, $set_value, $expire, $path);
|
77 |
-
} else {
|
78 |
-
return self::get_set_key($multikey, $set_value, $_COOKIE);
|
79 |
-
}
|
80 |
-
}
|
81 |
-
|
82 |
-
public static function REQUEST($multikey = null, $default_value = null)
|
83 |
-
{
|
84 |
-
return fw_stripslashes_deep_keys(
|
85 |
-
$multikey === null
|
86 |
-
? $_REQUEST
|
87 |
-
: fw_akg($multikey, $_REQUEST, $default_value)
|
88 |
-
);
|
89 |
-
}
|
90 |
-
}
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
|
3 |
+
/**
|
4 |
+
* WordPress automatically adds slashes to:
|
5 |
+
* $_REQUEST
|
6 |
+
* $_POST
|
7 |
+
* $_GET
|
8 |
+
* $_COOKIE
|
9 |
+
*
|
10 |
+
* For e.g.
|
11 |
+
*
|
12 |
+
* If value is simple, get value directly:
|
13 |
+
* $foo = isset($_GET['bar']) && $_GET['bar'] == 'yes';
|
14 |
+
*
|
15 |
+
* If value can contain some user input and can have quotes or json from some option, then use this helper:
|
16 |
+
* $foo = json_decode(FW_Request::POST('bar')); // json_decode($_POST('bar')) will not work if json will contain quotes
|
17 |
+
*
|
18 |
+
* You can test that problem.
|
19 |
+
* Add somewhere this code:
|
20 |
+
fw_print(array(
|
21 |
+
$_GET['test'],
|
22 |
+
json_decode($_GET['test']),
|
23 |
+
FW_Request::GET('test'),
|
24 |
+
json_decode(FW_Request::GET('test'))
|
25 |
+
));
|
26 |
+
* and access: http://your-site.com/?test={'a':1}
|
27 |
+
*/
|
28 |
+
class FW_Request
|
29 |
+
{
|
30 |
+
protected static function prepare_key($key)
|
31 |
+
{
|
32 |
+
return (get_magic_quotes_gpc() && is_string($key) ? addslashes($key) : $key);
|
33 |
+
}
|
34 |
+
|
35 |
+
protected static function get_set_key($multikey = null, $set_value = null, &$value)
|
36 |
+
{
|
37 |
+
$multikey = self::prepare_key($multikey);
|
38 |
+
|
39 |
+
if ($set_value === null) { // get
|
40 |
+
return fw_stripslashes_deep_keys($multikey === null ? $value : fw_akg($multikey, $value));
|
41 |
+
} else { // set
|
42 |
+
fw_aks($multikey, fw_addslashes_deep_keys($set_value), $value);
|
43 |
+
}
|
44 |
+
|
45 |
+
return '';
|
46 |
+
}
|
47 |
+
|
48 |
+
public static function GET($multikey = null, $default_value = null)
|
49 |
+
{
|
50 |
+
return fw_stripslashes_deep_keys(
|
51 |
+
$multikey === null
|
52 |
+
? $_GET
|
53 |
+
: fw_akg($multikey, $_GET, $default_value)
|
54 |
+
);
|
55 |
+
}
|
56 |
+
|
57 |
+
public static function POST($multikey = null, $default_value = null)
|
58 |
+
{
|
59 |
+
return fw_stripslashes_deep_keys(
|
60 |
+
$multikey === null
|
61 |
+
? $_POST
|
62 |
+
: fw_akg($multikey, $_POST, $default_value)
|
63 |
+
);
|
64 |
+
}
|
65 |
+
|
66 |
+
public static function COOKIE($multikey = null, $set_value = null, $expire = 0, $path = null)
|
67 |
+
{
|
68 |
+
if ($set_value !== null) {
|
69 |
+
|
70 |
+
// transforms a string ( key1/key2/key3 => key1][key2][key3] )
|
71 |
+
$multikey = str_replace('/', '][', $multikey) . ']';
|
72 |
+
|
73 |
+
// removes the first closed square bracket ( key1][key2][key3] => key1[key2][key3] )
|
74 |
+
$multikey = preg_replace('/\]/', '', $multikey, 1);
|
75 |
+
|
76 |
+
return setcookie($multikey, $set_value, $expire, $path);
|
77 |
+
} else {
|
78 |
+
return self::get_set_key($multikey, $set_value, $_COOKIE);
|
79 |
+
}
|
80 |
+
}
|
81 |
+
|
82 |
+
public static function REQUEST($multikey = null, $default_value = null)
|
83 |
+
{
|
84 |
+
return fw_stripslashes_deep_keys(
|
85 |
+
$multikey === null
|
86 |
+
? $_REQUEST
|
87 |
+
: fw_akg($multikey, $_REQUEST, $default_value)
|
88 |
+
);
|
89 |
+
}
|
90 |
+
}
|
framework/helpers/class-fw-resize.php
CHANGED
@@ -1,177 +1,177 @@
|
|
1 |
-
<?php if ( ! defined( 'FW' ) ) {
|
2 |
-
die( 'Forbidden' );
|
3 |
-
}
|
4 |
-
|
5 |
-
if ( ! class_exists( 'FW_Resize' ) ) {
|
6 |
-
class FW_Resize {
|
7 |
-
/**
|
8 |
-
* The singleton instance
|
9 |
-
*/
|
10 |
-
static private $instance = null;
|
11 |
-
|
12 |
-
/**
|
13 |
-
* No initialization allowed
|
14 |
-
*/
|
15 |
-
private function __construct() {
|
16 |
-
}
|
17 |
-
|
18 |
-
/**
|
19 |
-
* No cloning allowed
|
20 |
-
*/
|
21 |
-
private function __clone() {
|
22 |
-
}
|
23 |
-
|
24 |
-
static public function getInstance() {
|
25 |
-
if ( self::$instance == null ) {
|
26 |
-
self::$instance = new self;
|
27 |
-
}
|
28 |
-
|
29 |
-
return self::$instance;
|
30 |
-
}
|
31 |
-
|
32 |
-
private function get_attachment_info( $attachment ) {
|
33 |
-
|
34 |
-
$row = $this->get_attachment( $attachment );
|
35 |
-
$path = get_attached_file( $row['ID'] );
|
36 |
-
|
37 |
-
return ( ! isset( $row ) || ! $path ) ? false : array(
|
38 |
-
'id' => intval( $row['ID'] ),
|
39 |
-
'path' => $path,
|
40 |
-
'url' => $row['guid']
|
41 |
-
);
|
42 |
-
}
|
43 |
-
|
44 |
-
private function get_attachment( $attachment ) {
|
45 |
-
/**
|
46 |
-
* @var WPDB $wpdb
|
47 |
-
*/
|
48 |
-
global $wpdb;
|
49 |
-
|
50 |
-
if ( is_numeric( $attachment ) ) {
|
51 |
-
return $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->posts WHERE ID=%d LIMIT 1", $attachment ), ARRAY_A );
|
52 |
-
} else {
|
53 |
-
|
54 |
-
$attachment = str_replace( array( 'http:', 'https:' ), '', $attachment );
|
55 |
-
|
56 |
-
return $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->posts WHERE guid LIKE %s LIMIT 1", '%' . $wpdb->esc_like($attachment) ), ARRAY_A );
|
57 |
-
}
|
58 |
-
}
|
59 |
-
|
60 |
-
public function process( $attachment, $width = false, $height = false, $crop = false ) {
|
61 |
-
|
62 |
-
$attachment_info = $this->get_attachment_info( $attachment );
|
63 |
-
|
64 |
-
if ( ! $attachment_info ) {
|
65 |
-
return new WP_Error( 'invalid_attachment', 'Invalid Attachment', $attachment );
|
66 |
-
}
|
67 |
-
|
68 |
-
$file_path = $attachment_info['path'];
|
69 |
-
|
70 |
-
$info = pathinfo( $file_path );
|
71 |
-
$dir = $info['dirname'];
|
72 |
-
$ext = ( isset( $info['extension'] ) ) ? $info['extension'] : 'jpg';
|
73 |
-
$name = wp_basename( $file_path, ".$ext" );
|
74 |
-
$name = preg_replace( '/(.+)(\-\d+x\d+)$/', '$1', $name );
|
75 |
-
|
76 |
-
{
|
77 |
-
if ( ! $width || ! $height ) {
|
78 |
-
$editor = wp_get_image_editor( $file_path );
|
79 |
-
$size = $editor->get_size();
|
80 |
-
$orig_width = $size['width'];
|
81 |
-
$orig_height = $size['height'];
|
82 |
-
if ( ! $height && $width ) {
|
83 |
-
$height = round( ( $orig_height * $width ) / $orig_width );
|
84 |
-
} elseif ( ! $width && $height ) {
|
85 |
-
$width = round( ( $orig_width * $height ) / $orig_height );
|
86 |
-
} else {
|
87 |
-
return $attachment;
|
88 |
-
}
|
89 |
-
}
|
90 |
-
}
|
91 |
-
|
92 |
-
// Suffix applied to filename
|
93 |
-
$suffix = "{$width}x{$height}";
|
94 |
-
|
95 |
-
// Get the destination file name
|
96 |
-
$destination_file_name = "{$dir}/{$name}-{$suffix}.{$ext}";
|
97 |
-
// No need to resize & create a new image if it already exists
|
98 |
-
if ( ! file_exists( $destination_file_name ) ) {
|
99 |
-
//Image Resize
|
100 |
-
$editor = (isset($editor)) ? $editor : wp_get_image_editor( $file_path );
|
101 |
-
|
102 |
-
if ( is_wp_error( $editor ) ) {
|
103 |
-
return new WP_Error( 'wp_image_editor', 'WP Image editor can\'t resize this attachment', $attachment );
|
104 |
-
}
|
105 |
-
|
106 |
-
// Get the original image size
|
107 |
-
$size = $editor->get_size();
|
108 |
-
$orig_width = $size['width'];
|
109 |
-
$orig_height = $size['height'];
|
110 |
-
|
111 |
-
$src_x = $src_y = 0;
|
112 |
-
$src_w = $orig_width;
|
113 |
-
$src_h = $orig_height;
|
114 |
-
|
115 |
-
if ( $crop ) {
|
116 |
-
|
117 |
-
$cmp_x = $orig_width / $width;
|
118 |
-
$cmp_y = $orig_height / $height;
|
119 |
-
|
120 |
-
// Calculate x or y coordinate, and width or height of source
|
121 |
-
if ( $cmp_x > $cmp_y ) {
|
122 |
-
$src_w = round( $orig_width / $cmp_x * $cmp_y );
|
123 |
-
$src_x = round( ( $orig_width - ( $orig_width / $cmp_x * $cmp_y ) ) / 2 );
|
124 |
-
} else if ( $cmp_y > $cmp_x ) {
|
125 |
-
$src_h = round( $orig_height / $cmp_y * $cmp_x );
|
126 |
-
$src_y = round( ( $orig_height - ( $orig_height / $cmp_y * $cmp_x ) ) / 2 );
|
127 |
-
}
|
128 |
-
|
129 |
-
}
|
130 |
-
|
131 |
-
$editor->crop( $src_x, $src_y, $src_w, $src_h, $width, $height );
|
132 |
-
|
133 |
-
$saved = $editor->save( $destination_file_name );
|
134 |
-
|
135 |
-
$images = wp_get_attachment_metadata( $attachment_info['id'] );
|
136 |
-
if ( ! empty( $images['resizes'] ) && is_array( $images['resizes'] ) ) {
|
137 |
-
foreach ( $images['resizes'] as $image_size => $image_path ) {
|
138 |
-
$images['resizes'][ $image_size ] = addslashes( $image_path );
|
139 |
-
}
|
140 |
-
}
|
141 |
-
$uploads_dir = wp_upload_dir();
|
142 |
-
$images['resizes'][ $suffix ] = $uploads_dir['subdir'] . '/' . $saved['file'];
|
143 |
-
wp_update_attachment_metadata( $attachment_info['id'], $images );
|
144 |
-
|
145 |
-
}
|
146 |
-
|
147 |
-
return array(
|
148 |
-
'id' => $attachment_info['id'],
|
149 |
-
'src' => str_replace( basename( $attachment_info['url'] ), basename( $destination_file_name ), $attachment_info['url'] )
|
150 |
-
);
|
151 |
-
}
|
152 |
-
}
|
153 |
-
}
|
154 |
-
|
155 |
-
if ( ! function_exists( 'fw_resize' ) ) {
|
156 |
-
function fw_resize( $url, $width = false, $height = false, $crop = false ) {
|
157 |
-
$fw_resize = FW_Resize::getInstance();
|
158 |
-
$response = $fw_resize->process( $url, $width, $height, $crop );
|
159 |
-
|
160 |
-
return ( ! is_wp_error( $response ) && ! empty( $response['src'] ) ) ? $response['src'] : $url;
|
161 |
-
}
|
162 |
-
}
|
163 |
-
|
164 |
-
if ( ! function_exists( 'fw_delete_resized_thumbnails' ) ) {
|
165 |
-
function fw_delete_resized_thumbnails( $id ) {
|
166 |
-
$images = wp_get_attachment_metadata( $id );
|
167 |
-
if ( ! empty( $images['resizes'] ) ) {
|
168 |
-
$uploads_dir = wp_upload_dir();
|
169 |
-
foreach ( $images['resizes'] as $image ) {
|
170 |
-
$file = $uploads_dir['basedir'] . '/' . $image;
|
171 |
-
@unlink( $file );
|
172 |
-
}
|
173 |
-
}
|
174 |
-
}
|
175 |
-
|
176 |
-
add_action( 'delete_attachment', 'fw_delete_resized_thumbnails' );
|
177 |
-
}
|
1 |
+
<?php if ( ! defined( 'FW' ) ) {
|
2 |
+
die( 'Forbidden' );
|
3 |
+
}
|
4 |
+
|
5 |
+
if ( ! class_exists( 'FW_Resize' ) ) {
|
6 |
+
class FW_Resize {
|
7 |
+
/**
|
8 |
+
* The singleton instance
|
9 |
+
*/
|
10 |
+
static private $instance = null;
|
11 |
+
|
12 |
+
/**
|
13 |
+
* No initialization allowed
|
14 |
+
*/
|
15 |
+
private function __construct() {
|
16 |
+
}
|
17 |
+
|
18 |
+
/**
|
19 |
+
* No cloning allowed
|
20 |
+
*/
|
21 |
+
private function __clone() {
|
22 |
+
}
|
23 |
+
|
24 |
+
static public function getInstance() {
|
25 |
+
if ( self::$instance == null ) {
|
26 |
+
self::$instance = new self;
|
27 |
+
}
|
28 |
+
|
29 |
+
return self::$instance;
|
30 |
+
}
|
31 |
+
|
32 |
+
private function get_attachment_info( $attachment ) {
|
33 |
+
|
34 |
+
$row = $this->get_attachment( $attachment );
|
35 |
+
$path = get_attached_file( $row['ID'] );
|
36 |
+
|
37 |
+
return ( ! isset( $row ) || ! $path ) ? false : array(
|
38 |
+
'id' => intval( $row['ID'] ),
|
39 |
+
'path' => $path,
|
40 |
+
'url' => $row['guid']
|
41 |
+
);
|
42 |
+
}
|
43 |
+
|
44 |
+
private function get_attachment( $attachment ) {
|
45 |
+
/**
|
46 |
+
* @var WPDB $wpdb
|
47 |
+
*/
|
48 |
+
global $wpdb;
|
49 |
+
|
50 |
+
if ( is_numeric( $attachment ) ) {
|
51 |
+
return $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->posts WHERE ID=%d LIMIT 1", $attachment ), ARRAY_A );
|
52 |
+
} else {
|
53 |
+
|
54 |
+
$attachment = str_replace( array( 'http:', 'https:' ), '', $attachment );
|
55 |
+
|
56 |
+
return $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->posts WHERE guid LIKE %s LIMIT 1", '%' . $wpdb->esc_like($attachment) ), ARRAY_A );
|
57 |
+
}
|
58 |
+
}
|
59 |
+
|
60 |
+
public function process( $attachment, $width = false, $height = false, $crop = false ) {
|
61 |
+
|
62 |
+
$attachment_info = $this->get_attachment_info( $attachment );
|
63 |
+
|
64 |
+
if ( ! $attachment_info ) {
|
65 |
+
return new WP_Error( 'invalid_attachment', 'Invalid Attachment', $attachment );
|
66 |
+
}
|
67 |
+
|
68 |
+
$file_path = $attachment_info['path'];
|
69 |
+
|
70 |
+
$info = pathinfo( $file_path );
|
71 |
+
$dir = $info['dirname'];
|
72 |
+
$ext = ( isset( $info['extension'] ) ) ? $info['extension'] : 'jpg';
|
73 |
+
$name = wp_basename( $file_path, ".$ext" );
|
74 |
+
$name = preg_replace( '/(.+)(\-\d+x\d+)$/', '$1', $name );
|
75 |
+
|
76 |
+
{
|
77 |
+
if ( ! $width || ! $height ) {
|
78 |
+
$editor = wp_get_image_editor( $file_path );
|
79 |
+
$size = $editor->get_size();
|
80 |
+
$orig_width = $size['width'];
|
81 |
+
$orig_height = $size['height'];
|
82 |
+
if ( ! $height && $width ) {
|
83 |
+
$height = round( ( $orig_height * $width ) / $orig_width );
|
84 |
+
} elseif ( ! $width && $height ) {
|
85 |
+
$width = round( ( $orig_width * $height ) / $orig_height );
|
86 |
+
} else {
|
87 |
+
return $attachment;
|
88 |
+
}
|
89 |
+
}
|
90 |
+
}
|
91 |
+
|
92 |
+
// Suffix applied to filename
|
93 |
+
$suffix = "{$width}x{$height}";
|
94 |
+
|
95 |
+
// Get the destination file name
|
96 |
+
$destination_file_name = "{$dir}/{$name}-{$suffix}.{$ext}";
|
97 |
+
// No need to resize & create a new image if it already exists
|
98 |
+
if ( ! file_exists( $destination_file_name ) ) {
|
99 |
+
//Image Resize
|
100 |
+
$editor = (isset($editor)) ? $editor : wp_get_image_editor( $file_path );
|
101 |
+
|
102 |
+
if ( is_wp_error( $editor ) ) {
|
103 |
+
return new WP_Error( 'wp_image_editor', 'WP Image editor can\'t resize this attachment', $attachment );
|
104 |
+
}
|
105 |
+
|
106 |
+
// Get the original image size
|
107 |
+
$size = $editor->get_size();
|
108 |
+
$orig_width = $size['width'];
|
109 |
+
$orig_height = $size['height'];
|
110 |
+
|
111 |
+
$src_x = $src_y = 0;
|
112 |
+
$src_w = $orig_width;
|
113 |
+
$src_h = $orig_height;
|
114 |
+
|
115 |
+
if ( $crop ) {
|
116 |
+
|
117 |
+
$cmp_x = $orig_width / $width;
|
118 |
+
$cmp_y = $orig_height / $height;
|
119 |
+
|
120 |
+
// Calculate x or y coordinate, and width or height of source
|
121 |
+
if ( $cmp_x > $cmp_y ) {
|
122 |
+
$src_w = round( $orig_width / $cmp_x * $cmp_y );
|
123 |
+
$src_x = round( ( $orig_width - ( $orig_width / $cmp_x * $cmp_y ) ) / 2 );
|
124 |
+
} else if ( $cmp_y > $cmp_x ) {
|
125 |
+
$src_h = round( $orig_height / $cmp_y * $cmp_x );
|
126 |
+
$src_y = round( ( $orig_height - ( $orig_height / $cmp_y * $cmp_x ) ) / 2 );
|
127 |
+
}
|
128 |
+
|
129 |
+
}
|
130 |
+
|
131 |
+
$editor->crop( $src_x, $src_y, $src_w, $src_h, $width, $height );
|
132 |
+
|
133 |
+
$saved = $editor->save( $destination_file_name );
|
134 |
+
|
135 |
+
$images = wp_get_attachment_metadata( $attachment_info['id'] );
|
136 |
+
if ( ! empty( $images['resizes'] ) && is_array( $images['resizes'] ) ) {
|
137 |
+
foreach ( $images['resizes'] as $image_size => $image_path ) {
|
138 |
+
$images['resizes'][ $image_size ] = addslashes( $image_path );
|
139 |
+
}
|
140 |
+
}
|
141 |
+
$uploads_dir = wp_upload_dir();
|
142 |
+
$images['resizes'][ $suffix ] = $uploads_dir['subdir'] . '/' . $saved['file'];
|
143 |
+
wp_update_attachment_metadata( $attachment_info['id'], $images );
|
144 |
+
|
145 |
+
}
|
146 |
+
|
147 |
+
return array(
|
148 |
+
'id' => $attachment_info['id'],
|
149 |
+
'src' => str_replace( basename( $attachment_info['url'] ), basename( $destination_file_name ), $attachment_info['url'] )
|
150 |
+
);
|
151 |
+
}
|
152 |
+
}
|
153 |
+
}
|
154 |
+
|
155 |
+
if ( ! function_exists( 'fw_resize' ) ) {
|
156 |
+
function fw_resize( $url, $width = false, $height = false, $crop = false ) {
|
157 |
+
$fw_resize = FW_Resize::getInstance();
|
158 |
+
$response = $fw_resize->process( $url, $width, $height, $crop );
|
159 |
+
|
160 |
+
return ( ! is_wp_error( $response ) && ! empty( $response['src'] ) ) ? $response['src'] : $url;
|
161 |
+
}
|
162 |
+
}
|
163 |
+
|
164 |
+
if ( ! function_exists( 'fw_delete_resized_thumbnails' ) ) {
|
165 |
+
function fw_delete_resized_thumbnails( $id ) {
|
166 |
+
$images = wp_get_attachment_metadata( $id );
|
167 |
+
if ( ! empty( $images['resizes'] ) ) {
|
168 |
+
$uploads_dir = wp_upload_dir();
|
169 |
+
foreach ( $images['resizes'] as $image ) {
|
170 |
+
$file = $uploads_dir['basedir'] . '/' . $image;
|
171 |
+
@unlink( $file );
|
172 |
+
}
|
173 |
+
}
|
174 |
+
}
|
175 |
+
|
176 |
+
add_action( 'delete_attachment', 'fw_delete_resized_thumbnails' );
|
177 |
+
}
|
framework/helpers/class-fw-session.php
CHANGED
@@ -1,36 +1,36 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Work with $_SESSION
|
5 |
-
*
|
6 |
-
* Advantages: Do not session_start() on every refresh, but only when it is accessed
|
7 |
-
*/
|
8 |
-
class FW_Session
|
9 |
-
{
|
10 |
-
private static function start_session()
|
11 |
-
{
|
12 |
-
if (!session_id()) {
|
13 |
-
session_start();
|
14 |
-
}
|
15 |
-
}
|
16 |
-
|
17 |
-
public static function get($key, $default_value = null)
|
18 |
-
{
|
19 |
-
self::start_session();
|
20 |
-
|
21 |
-
return fw_akg($key, $_SESSION, $default_value);
|
22 |
-
}
|
23 |
-
|
24 |
-
public static function set($key, $value)
|
25 |
-
{
|
26 |
-
self::start_session();
|
27 |
-
|
28 |
-
fw_aks($key, $value, $_SESSION);
|
29 |
-
}
|
30 |
-
|
31 |
-
public static function del( $key ) {
|
32 |
-
self::start_session();
|
33 |
-
|
34 |
-
fw_aku( $key, $_SESSION );
|
35 |
-
}
|
36 |
-
}
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Work with $_SESSION
|
5 |
+
*
|
6 |
+
* Advantages: Do not session_start() on every refresh, but only when it is accessed
|
7 |
+
*/
|
8 |
+
class FW_Session
|
9 |
+
{
|
10 |
+
private static function start_session()
|
11 |
+
{
|
12 |
+
if (!session_id()) {
|
13 |
+
session_start();
|
14 |
+
}
|
15 |
+
}
|
16 |
+
|
17 |
+
public static function get($key, $default_value = null)
|
18 |
+
{
|
19 |
+
self::start_session();
|
20 |
+
|
21 |
+
return fw_akg($key, $_SESSION, $default_value);
|
22 |
+
}
|
23 |
+
|
24 |
+
public static function set($key, $value)
|
25 |
+
{
|
26 |
+
self::start_session();
|
27 |
+
|
28 |
+
fw_aks($key, $value, $_SESSION);
|
29 |
+
}
|
30 |
+
|
31 |
+
public static function del( $key ) {
|
32 |
+
self::start_session();
|
33 |
+
|
34 |
+
fw_aku( $key, $_SESSION );
|
35 |
+
}
|
36 |
+
}
|
framework/helpers/class-fw-wp-filesystem.php
CHANGED
@@ -1,312 +1,312 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
|
3 |
-
class FW_WP_Filesystem
|
4 |
-
{
|
5 |
-
/**
|
6 |
-
* Request WP Filesystem access
|
7 |
-
* @param string $context
|
8 |
-
* @param string $url
|
9 |
-
* @param array $extra_fields
|
10 |
-
* @return null|bool // todo: Create a new method that will return WP_Error with message on failure
|
11 |
-
* null - if has no access and the input credentials form was displayed
|
12 |
-
* false - if user submitted wrong credentials
|
13 |
-
* true - if we have filesystem access
|
14 |
-
*/
|
15 |
-
final public static function request_access($context = null, $url = null, $extra_fields = array())
|
16 |
-
{
|
17 |
-
/** @var WP_Filesystem_Base $wp_filesystem */
|
18 |
-
global $wp_filesystem;
|
19 |
-
|
20 |
-
if ($wp_filesystem) {
|
21 |
-
if (
|
22 |
-
is_object($wp_filesystem)
|
23 |
-
&&
|
24 |
-
!(is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code())
|
25 |
-
) {
|
26 |
-
return true; // already initialized
|
27 |
-
}
|
28 |
-
}
|
29 |
-
|
30 |
-
if ( empty( $url ) ) {
|
31 |
-
$url = fw_current_url();
|
32 |
-
}
|
33 |
-
|
34 |
-
if ( get_filesystem_method() === 'direct' ) {
|
35 |
-
// in case if direct access is available
|
36 |
-
|
37 |
-
/* you can safely run request_filesystem_credentials() without any issues and don't need to worry about passing in a URL */
|
38 |
-
$creds = request_filesystem_credentials( site_url() . '/wp-admin/', '', false, false, null );
|
39 |
-
|
40 |
-
/* initialize the API */
|
41 |
-
if ( ! WP_Filesystem( $creds ) ) {
|
42 |
-
/* any problems and we exit */
|
43 |
-
trigger_error( __( 'Cannot connect to Filesystem directly', 'fw' ), E_USER_WARNING );
|
44 |
-
|
45 |
-
return false;
|
46 |
-
}
|
47 |
-
} else {
|
48 |
-
$creds = request_filesystem_credentials( $url, '', false, $context, $extra_fields );
|
49 |
-
|
50 |
-
if ( ! $creds ) {
|
51 |
-
// the form was printed to the user
|
52 |
-
return null;
|
53 |
-
}
|
54 |
-
|
55 |
-
/* initialize the API */
|
56 |
-
if ( ! WP_Filesystem( $creds ) ) {
|
57 |
-
/* any problems and we exit */
|
58 |
-
request_filesystem_credentials( $url, '', true, $context, $extra_fields ); // the third parameter is true to show error to the user
|
59 |
-
return false;
|
60 |
-
}
|
61 |
-
}
|
62 |
-
|
63 |
-
if (
|
64 |
-
! is_object($wp_filesystem)
|
65 |
-
||
|
66 |
-
(is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code())
|
67 |
-
) {
|
68 |
-
return false;
|
69 |
-
}
|
70 |
-
|
71 |
-
if (
|
72 |
-
$wp_filesystem->abspath()
|
73 |
-
&&
|
74 |
-
$wp_filesystem->wp_content_dir()
|
75 |
-
&&
|
76 |
-
$wp_filesystem->wp_plugins_dir()
|
77 |
-
&&
|
78 |
-
$wp_filesystem->wp_themes_dir()
|
79 |
-
&&
|
80 |
-
$wp_filesystem->find_folder($context)
|
81 |
-
) {
|
82 |
-
return true;
|
83 |
-
} else {
|
84 |
-
return false;
|
85 |
-
}
|
86 |
-
}
|
87 |
-
|
88 |
-
/**
|
89 |
-
* @return array {base_dir_real_path => base_dir_wp_filesystem_path}
|
90 |
-
*/
|
91 |
-
public static function get_base_dirs_map()
|
92 |
-
{
|
93 |
-
/** @var WP_Filesystem_Base $wp_filesystem */
|
94 |
-
global $wp_filesystem;
|
95 |
-
|
96 |
-
if (!$wp_filesystem) {
|
97 |
-
trigger_error('Filesystem is not available', E_USER_ERROR);
|
98 |
-
} elseif (is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code()) {
|
99 |
-
trigger_error('Filesystem: '. $wp_filesystem->errors->get_error_message(), E_USER_ERROR);
|
100 |
-
}
|
101 |
-
|
102 |
-
try {
|
103 |
-
$cache_key = 'fw_wp_filesystem/base_dirs_map';
|
104 |
-
|
105 |
-
return FW_Cache::get($cache_key);
|
106 |
-
} catch (FW_Cache_Not_Found_Exception $e) {
|
107 |
-
// code from $wp_filesystem->wp_themes_dir()
|
108 |
-
{
|
109 |
-
$themes_dir = get_theme_root();
|
110 |
-
|
111 |
-
// Account for relative theme roots
|
112 |
-
if ( '/themes' == $themes_dir || ! is_dir( $themes_dir ) ) {
|
113 |
-
$themes_dir = WP_CONTENT_DIR . $themes_dir;
|
114 |
-
}
|
115 |
-
}
|
116 |
-
|
117 |
-
$dirs = array(
|
118 |
-
fw_fix_path(ABSPATH) => fw_fix_path($wp_filesystem->abspath()),
|
119 |
-
fw_fix_path(WP_CONTENT_DIR) => fw_fix_path($wp_filesystem->wp_content_dir()),
|
120 |
-
fw_fix_path(WP_PLUGIN_DIR) => fw_fix_path($wp_filesystem->wp_plugins_dir()),
|
121 |
-
fw_fix_path($themes_dir) => fw_fix_path($wp_filesystem->wp_themes_dir()),
|
122 |
-
);
|
123 |
-
|
124 |
-
FW_Cache::set($cache_key, $dirs);
|
125 |
-
|
126 |
-
return $dirs;
|
127 |
-
}
|
128 |
-
}
|
129 |
-
|
130 |
-
/**
|
131 |
-
* Convert real file path to WP Filesystem path
|
132 |
-
* @param string $real_path
|
133 |
-
* @return string|false
|
134 |
-
*/
|
135 |
-
final public static function real_path_to_filesystem_path($real_path) {
|
136 |
-
/** @var WP_Filesystem_Base $wp_filesystem */
|
137 |
-
global $wp_filesystem;
|
138 |
-
|
139 |
-
if (!$wp_filesystem) {
|
140 |
-
trigger_error('Filesystem is not available', E_USER_ERROR);
|
141 |
-
} elseif (is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code()) {
|
142 |
-
trigger_error('Filesystem: '. $wp_filesystem->errors->get_error_message(), E_USER_ERROR);
|
143 |
-
}
|
144 |
-
|
145 |
-
$real_path = fw_fix_path($real_path);
|
146 |
-
|
147 |
-
foreach (self::get_base_dirs_map() as $base_real_path => $base_wp_filesystem_path) {
|
148 |
-
$prefix_regex = '/^'. preg_quote($base_real_path, '/') .'/';
|
149 |
-
|
150 |
-
// check if path is inside base path
|
151 |
-
if (!preg_match($prefix_regex, $real_path)) {
|
152 |
-
continue;
|
153 |
-
}
|
154 |
-
|
155 |
-
if ($base_real_path === '/') {
|
156 |
-
$relative_path = $real_path;
|
157 |
-
} else {
|
158 |
-
$relative_path = preg_replace($prefix_regex, '', $real_path);
|
159 |
-
}
|
160 |
-
|
161 |
-
return $base_wp_filesystem_path . $relative_path;
|
162 |
-
}
|
163 |
-
|
164 |
-
return false;
|
165 |
-
}
|
166 |
-
|
167 |
-
/**
|
168 |
-
* Convert WP Filesystem path to real file path
|
169 |
-
* @param string $wp_filesystem_path
|
170 |
-
* @return string|false
|
171 |
-
*/
|
172 |
-
final public static function filesystem_path_to_real_path($wp_filesystem_path) {
|
173 |
-
/** @var WP_Filesystem_Base $wp_filesystem */
|
174 |
-
global $wp_filesystem;
|
175 |
-
|
176 |
-
if (!$wp_filesystem) {
|
177 |
-
trigger_error('Filesystem is not available', E_USER_ERROR);
|
178 |
-
} elseif (is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code()) {
|
179 |
-
trigger_error('Filesystem: '. $wp_filesystem->errors->get_error_message(), E_USER_ERROR);
|
180 |
-
}
|
181 |
-
|
182 |
-
$wp_filesystem_path = fw_fix_path($wp_filesystem_path);
|
183 |
-
|
184 |
-
foreach (self::get_base_dirs_map() as $base_real_path => $base_wp_filesystem_path) {
|
185 |
-
$prefix_regex = '/^'. preg_quote($base_wp_filesystem_path, '/') .'/';
|
186 |
-
|
187 |
-
// check if path is inside base path
|
188 |
-
if (!preg_match($prefix_regex, $wp_filesystem_path)) {
|
189 |
-
continue;
|
190 |
-
}
|
191 |
-
|
192 |
-
if ($base_wp_filesystem_path === '/') {
|
193 |
-
$relative_path = $wp_filesystem_path;
|
194 |
-
} else {
|
195 |
-
$relative_path = preg_replace($prefix_regex, '', $wp_filesystem_path);
|
196 |
-
}
|
197 |
-
|
198 |
-
return $base_real_path . $relative_path;
|
199 |
-
}
|
200 |
-
|
201 |
-
return false;
|
202 |
-
}
|
203 |
-
|
204 |
-
/**
|
205 |
-
* Check if there is direct filesystem access, so we can make changes without asking the credentials via form
|
206 |
-
* @param string|null $context
|
207 |
-
* @return bool
|
208 |
-
*/
|
209 |
-
final public static function has_direct_access($context = null)
|
210 |
-
{
|
211 |
-
/** @var WP_Filesystem_Base $wp_filesystem */
|
212 |
-
global $wp_filesystem;
|
213 |
-
|
214 |
-
if ($wp_filesystem) {
|
215 |
-
if (is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code()) {
|
216 |
-
return false;
|
217 |
-
} else {
|
218 |
-
return $wp_filesystem->method === 'direct';
|
219 |
-
}
|
220 |
-
}
|
221 |
-
|
222 |
-
if (get_filesystem_method(array(), $context) === 'direct') {
|
223 |
-
ob_start();
|
224 |
-
{
|
225 |
-
$creds = request_filesystem_credentials(admin_url(), '', false, $context, null);
|
226 |
-
}
|
227 |
-
ob_end_clean();
|
228 |
-
|
229 |
-
if ( WP_Filesystem($creds) ) {
|
230 |
-
return true;
|
231 |
-
}
|
232 |
-
}
|
233 |
-
|
234 |
-
return false;
|
235 |
-
}
|
236 |
-
|
237 |
-
/**
|
238 |
-
* Create wp filesystem directory recursive
|
239 |
-
* @param string $wp_filesystem_dir_path
|
240 |
-
* @return bool
|
241 |
-
*/
|
242 |
-
final public static function mkdir_recursive($wp_filesystem_dir_path) {
|
243 |
-
/** @var WP_Filesystem_Base $wp_filesystem */
|
244 |
-
global $wp_filesystem;
|
245 |
-
|
246 |
-
if (!$wp_filesystem) {
|
247 |
-
trigger_error('Filesystem is not available', E_USER_ERROR);
|
248 |
-
} elseif (is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code()) {
|
249 |
-
trigger_error('Filesystem: '. $wp_filesystem->errors->get_error_message(), E_USER_ERROR);
|
250 |
-
}
|
251 |
-
|
252 |
-
$wp_filesystem_dir_path = fw_fix_path($wp_filesystem_dir_path);
|
253 |
-
|
254 |
-
$path = false;
|
255 |
-
|
256 |
-
foreach (self::get_base_dirs_map() as $base_real_path => $base_wp_filesystem_path) {
|
257 |
-
$prefix_regex = '/^'. preg_quote($base_wp_filesystem_path, '/') .'/';
|
258 |
-
|
259 |
-
// check if path is inside base path
|
260 |
-
if (!preg_match($prefix_regex, $wp_filesystem_dir_path)) {
|
261 |
-
continue;
|
262 |
-
}
|
263 |
-
|
264 |
-
$path = $base_wp_filesystem_path;
|
265 |
-
break;
|
266 |
-
}
|
267 |
-
|
268 |
-
if (!$path) {
|
269 |
-
trigger_error(
|
270 |
-
sprintf(
|
271 |
-
__('Cannot create directory "%s". It must be inside "%s"', 'fw'),
|
272 |
-
$wp_filesystem_dir_path,
|
273 |
-
implode(__('" or "', 'fw'), self::get_base_dirs_map())
|
274 |
-
),
|
275 |
-
E_USER_WARNING
|
276 |
-
);
|
277 |
-
return false;
|
278 |
-
}
|
279 |
-
|
280 |
-
if ($path === '/') {
|
281 |
-
$rel_path = $wp_filesystem_dir_path;
|
282 |
-
} else {
|
283 |
-
$rel_path = preg_replace('/^'. preg_quote($path, '/') .'/', '', $wp_filesystem_dir_path);
|
284 |
-
}
|
285 |
-
|
286 |
-
// improvement: do not check directory for existence if it's known that sure it doesn't exist
|
287 |
-
$check_if_exists = true;
|
288 |
-
|
289 |
-
foreach (explode('/', ltrim($rel_path, '/')) as $dir_name) {
|
290 |
-
$path .= '/' . $dir_name;
|
291 |
-
|
292 |
-
// When WP FS abspath is '/', $path can be '//wp-content'. Fix it '/wp-content'
|
293 |
-
$path = fw_fix_path($path);
|
294 |
-
|
295 |
-
if ($check_if_exists) {
|
296 |
-
if ($wp_filesystem->is_dir($path)) {
|
297 |
-
// do nothing if exists
|
298 |
-
continue;
|
299 |
-
} else {
|
300 |
-
// do not check anymore, next directories sure doesn't exist
|
301 |
-
$check_if_exists = false;
|
302 |
-
}
|
303 |
-
}
|
304 |
-
|
305 |
-
if (!$wp_filesystem->mkdir($path, FS_CHMOD_DIR)) {
|
306 |
-
return false;
|
307 |
-
}
|
308 |
-
}
|
309 |
-
|
310 |
-
return true;
|
311 |
-
}
|
312 |
-
}
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
|
3 |
+
class FW_WP_Filesystem
|
4 |
+
{
|
5 |
+
/**
|
6 |
+
* Request WP Filesystem access
|
7 |
+
* @param string $context
|
8 |
+
* @param string $url
|
9 |
+
* @param array $extra_fields
|
10 |
+
* @return null|bool // todo: Create a new method that will return WP_Error with message on failure
|
11 |
+
* null - if has no access and the input credentials form was displayed
|
12 |
+
* false - if user submitted wrong credentials
|
13 |
+
* true - if we have filesystem access
|
14 |
+
*/
|
15 |
+
final public static function request_access($context = null, $url = null, $extra_fields = array())
|
16 |
+
{
|
17 |
+
/** @var WP_Filesystem_Base $wp_filesystem */
|
18 |
+
global $wp_filesystem;
|
19 |
+
|
20 |
+
if ($wp_filesystem) {
|
21 |
+
if (
|
22 |
+
is_object($wp_filesystem)
|
23 |
+
&&
|
24 |
+
!(is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code())
|
25 |
+
) {
|
26 |
+
return true; // already initialized
|
27 |
+
}
|
28 |
+
}
|
29 |
+
|
30 |
+
if ( empty( $url ) ) {
|
31 |
+
$url = fw_current_url();
|
32 |
+
}
|
33 |
+
|
34 |
+
if ( get_filesystem_method() === 'direct' ) {
|
35 |
+
// in case if direct access is available
|
36 |
+
|
37 |
+
/* you can safely run request_filesystem_credentials() without any issues and don't need to worry about passing in a URL */
|
38 |
+
$creds = request_filesystem_credentials( site_url() . '/wp-admin/', '', false, false, null );
|
39 |
+
|
40 |
+
/* initialize the API */
|
41 |
+
if ( ! WP_Filesystem( $creds ) ) {
|
42 |
+
/* any problems and we exit */
|
43 |
+
trigger_error( __( 'Cannot connect to Filesystem directly', 'fw' ), E_USER_WARNING );
|
44 |
+
|
45 |
+
return false;
|
46 |
+
}
|
47 |
+
} else {
|
48 |
+
$creds = request_filesystem_credentials( $url, '', false, $context, $extra_fields );
|
49 |
+
|
50 |
+
if ( ! $creds ) {
|
51 |
+
// the form was printed to the user
|
52 |
+
return null;
|
53 |
+
}
|
54 |
+
|
55 |
+
/* initialize the API */
|
56 |
+
if ( ! WP_Filesystem( $creds ) ) {
|
57 |
+
/* any problems and we exit */
|
58 |
+
request_filesystem_credentials( $url, '', true, $context, $extra_fields ); // the third parameter is true to show error to the user
|
59 |
+
return false;
|
60 |
+
}
|
61 |
+
}
|
62 |
+
|
63 |
+
if (
|
64 |
+
! is_object($wp_filesystem)
|
65 |
+
||
|
66 |
+
(is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code())
|
67 |
+
) {
|
68 |
+
return false;
|
69 |
+
}
|
70 |
+
|
71 |
+
if (
|
72 |
+
$wp_filesystem->abspath()
|
73 |
+
&&
|
74 |
+
$wp_filesystem->wp_content_dir()
|
75 |
+
&&
|
76 |
+
$wp_filesystem->wp_plugins_dir()
|
77 |
+
&&
|
78 |
+
$wp_filesystem->wp_themes_dir()
|
79 |
+
&&
|
80 |
+
$wp_filesystem->find_folder($context)
|
81 |
+
) {
|
82 |
+
return true;
|
83 |
+
} else {
|
84 |
+
return false;
|
85 |
+
}
|
86 |
+
}
|
87 |
+
|
88 |
+
/**
|
89 |
+
* @return array {base_dir_real_path => base_dir_wp_filesystem_path}
|
90 |
+
*/
|
91 |
+
public static function get_base_dirs_map()
|
92 |
+
{
|
93 |
+
/** @var WP_Filesystem_Base $wp_filesystem */
|
94 |
+
global $wp_filesystem;
|
95 |
+
|
96 |
+
if (!$wp_filesystem) {
|
97 |
+
trigger_error('Filesystem is not available', E_USER_ERROR);
|
98 |
+
} elseif (is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code()) {
|
99 |
+
trigger_error('Filesystem: '. $wp_filesystem->errors->get_error_message(), E_USER_ERROR);
|
100 |
+
}
|
101 |
+
|
102 |
+
try {
|
103 |
+
$cache_key = 'fw_wp_filesystem/base_dirs_map';
|
104 |
+
|
105 |
+
return FW_Cache::get($cache_key);
|
106 |
+
} catch (FW_Cache_Not_Found_Exception $e) {
|
107 |
+
// code from $wp_filesystem->wp_themes_dir()
|
108 |
+
{
|
109 |
+
$themes_dir = get_theme_root();
|
110 |
+
|
111 |
+
// Account for relative theme roots
|
112 |
+
if ( '/themes' == $themes_dir || ! is_dir( $themes_dir ) ) {
|
113 |
+
$themes_dir = WP_CONTENT_DIR . $themes_dir;
|
114 |
+
}
|
115 |
+
}
|
116 |
+
|
117 |
+
$dirs = array(
|
118 |
+
fw_fix_path(ABSPATH) => fw_fix_path($wp_filesystem->abspath()),
|
119 |
+
fw_fix_path(WP_CONTENT_DIR) => fw_fix_path($wp_filesystem->wp_content_dir()),
|
120 |
+
fw_fix_path(WP_PLUGIN_DIR) => fw_fix_path($wp_filesystem->wp_plugins_dir()),
|
121 |
+
fw_fix_path($themes_dir) => fw_fix_path($wp_filesystem->wp_themes_dir()),
|
122 |
+
);
|
123 |
+
|
124 |
+
FW_Cache::set($cache_key, $dirs);
|
125 |
+
|
126 |
+
return $dirs;
|
127 |
+
}
|
128 |
+
}
|
129 |
+
|
130 |
+
/**
|
131 |
+
* Convert real file path to WP Filesystem path
|
132 |
+
* @param string $real_path
|
133 |
+
* @return string|false
|
134 |
+
*/
|
135 |
+
final public static function real_path_to_filesystem_path($real_path) {
|
136 |
+
/** @var WP_Filesystem_Base $wp_filesystem */
|
137 |
+
global $wp_filesystem;
|
138 |
+
|
139 |
+
if (!$wp_filesystem) {
|
140 |
+
trigger_error('Filesystem is not available', E_USER_ERROR);
|
141 |
+
} elseif (is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code()) {
|
142 |
+
trigger_error('Filesystem: '. $wp_filesystem->errors->get_error_message(), E_USER_ERROR);
|
143 |
+
}
|
144 |
+
|
145 |
+
$real_path = fw_fix_path($real_path);
|
146 |
+
|
147 |
+
foreach (self::get_base_dirs_map() as $base_real_path => $base_wp_filesystem_path) {
|
148 |
+
$prefix_regex = '/^'. preg_quote($base_real_path, '/') .'/';
|
149 |
+
|
150 |
+
// check if path is inside base path
|
151 |
+
if (!preg_match($prefix_regex, $real_path)) {
|
152 |
+
continue;
|
153 |
+
}
|
154 |
+
|
155 |
+
if ($base_real_path === '/') {
|
156 |
+
$relative_path = $real_path;
|
157 |
+
} else {
|
158 |
+
$relative_path = preg_replace($prefix_regex, '', $real_path);
|
159 |
+
}
|
160 |
+
|
161 |
+
return $base_wp_filesystem_path . $relative_path;
|
162 |
+
}
|
163 |
+
|
164 |
+
return false;
|
165 |
+
}
|
166 |
+
|
167 |
+
/**
|
168 |
+
* Convert WP Filesystem path to real file path
|
169 |
+
* @param string $wp_filesystem_path
|
170 |
+
* @return string|false
|
171 |
+
*/
|
172 |
+
final public static function filesystem_path_to_real_path($wp_filesystem_path) {
|
173 |
+
/** @var WP_Filesystem_Base $wp_filesystem */
|
174 |
+
global $wp_filesystem;
|
175 |
+
|
176 |
+
if (!$wp_filesystem) {
|
177 |
+
trigger_error('Filesystem is not available', E_USER_ERROR);
|
178 |
+
} elseif (is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code()) {
|
179 |
+
trigger_error('Filesystem: '. $wp_filesystem->errors->get_error_message(), E_USER_ERROR);
|
180 |
+
}
|
181 |
+
|
182 |
+
$wp_filesystem_path = fw_fix_path($wp_filesystem_path);
|
183 |
+
|
184 |
+
foreach (self::get_base_dirs_map() as $base_real_path => $base_wp_filesystem_path) {
|
185 |
+
$prefix_regex = '/^'. preg_quote($base_wp_filesystem_path, '/') .'/';
|
186 |
+
|
187 |
+
// check if path is inside base path
|
188 |
+
if (!preg_match($prefix_regex, $wp_filesystem_path)) {
|
189 |
+
continue;
|
190 |
+
}
|
191 |
+
|
192 |
+
if ($base_wp_filesystem_path === '/') {
|
193 |
+
$relative_path = $wp_filesystem_path;
|
194 |
+
} else {
|
195 |
+
$relative_path = preg_replace($prefix_regex, '', $wp_filesystem_path);
|
196 |
+
}
|
197 |
+
|
198 |
+
return $base_real_path . $relative_path;
|
199 |
+
}
|
200 |
+
|
201 |
+
return false;
|
202 |
+
}
|
203 |
+
|
204 |
+
/**
|
205 |
+
* Check if there is direct filesystem access, so we can make changes without asking the credentials via form
|
206 |
+
* @param string|null $context
|
207 |
+
* @return bool
|
208 |
+
*/
|
209 |
+
final public static function has_direct_access($context = null)
|
210 |
+
{
|
211 |
+
/** @var WP_Filesystem_Base $wp_filesystem */
|
212 |
+
global $wp_filesystem;
|
213 |
+
|
214 |
+
if ($wp_filesystem) {
|
215 |
+
if (is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code()) {
|
216 |
+
return false;
|
217 |
+
} else {
|
218 |
+
return $wp_filesystem->method === 'direct';
|
219 |
+
}
|
220 |
+
}
|
221 |
+
|
222 |
+
if (get_filesystem_method(array(), $context) === 'direct') {
|
223 |
+
ob_start();
|
224 |
+
{
|
225 |
+
$creds = request_filesystem_credentials(admin_url(), '', false, $context, null);
|
226 |
+
}
|
227 |
+
ob_end_clean();
|
228 |
+
|
229 |
+
if ( WP_Filesystem($creds) ) {
|
230 |
+
return true;
|
231 |
+
}
|
232 |
+
}
|
233 |
+
|
234 |
+
return false;
|
235 |
+
}
|
236 |
+
|
237 |
+
/**
|
238 |
+
* Create wp filesystem directory recursive
|
239 |
+
* @param string $wp_filesystem_dir_path
|
240 |
+
* @return bool
|
241 |
+
*/
|
242 |
+
final public static function mkdir_recursive($wp_filesystem_dir_path) {
|
243 |
+
/** @var WP_Filesystem_Base $wp_filesystem */
|
244 |
+
global $wp_filesystem;
|
245 |
+
|
246 |
+
if (!$wp_filesystem) {
|
247 |
+
trigger_error('Filesystem is not available', E_USER_ERROR);
|
248 |
+
} elseif (is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code()) {
|
249 |
+
trigger_error('Filesystem: '. $wp_filesystem->errors->get_error_message(), E_USER_ERROR);
|
250 |
+
}
|
251 |
+
|
252 |
+
$wp_filesystem_dir_path = fw_fix_path($wp_filesystem_dir_path);
|
253 |
+
|
254 |
+
$path = false;
|
255 |
+
|
256 |
+
foreach (self::get_base_dirs_map() as $base_real_path => $base_wp_filesystem_path) {
|
257 |
+
$prefix_regex = '/^'. preg_quote($base_wp_filesystem_path, '/') .'/';
|
258 |
+
|
259 |
+
// check if path is inside base path
|
260 |
+
if (!preg_match($prefix_regex, $wp_filesystem_dir_path)) {
|
261 |
+
continue;
|
262 |
+
}
|
263 |
+
|
264 |
+
$path = $base_wp_filesystem_path;
|
265 |
+
break;
|
266 |
+
}
|
267 |
+
|
268 |
+
if (!$path) {
|
269 |
+
trigger_error(
|
270 |
+
sprintf(
|
271 |
+
__('Cannot create directory "%s". It must be inside "%s"', 'fw'),
|
272 |
+
$wp_filesystem_dir_path,
|
273 |
+
implode(__('" or "', 'fw'), self::get_base_dirs_map())
|
274 |
+
),
|
275 |
+
E_USER_WARNING
|
276 |
+
);
|
277 |
+
return false;
|
278 |
+
}
|
279 |
+
|
280 |
+
if ($path === '/') {
|
281 |
+
$rel_path = $wp_filesystem_dir_path;
|
282 |
+
} else {
|
283 |
+
$rel_path = preg_replace('/^'. preg_quote($path, '/') .'/', '', $wp_filesystem_dir_path);
|
284 |
+
}
|
285 |
+
|
286 |
+
// improvement: do not check directory for existence if it's known that sure it doesn't exist
|
287 |
+
$check_if_exists = true;
|
288 |
+
|
289 |
+
foreach (explode('/', ltrim($rel_path, '/')) as $dir_name) {
|
290 |
+
$path .= '/' . $dir_name;
|
291 |
+
|
292 |
+
// When WP FS abspath is '/', $path can be '//wp-content'. Fix it '/wp-content'
|
293 |
+
$path = fw_fix_path($path);
|
294 |
+
|
295 |
+
if ($check_if_exists) {
|
296 |
+
if ($wp_filesystem->is_dir($path)) {
|
297 |
+
// do nothing if exists
|
298 |
+
continue;
|
299 |
+
} else {
|
300 |
+
// do not check anymore, next directories sure doesn't exist
|
301 |
+
$check_if_exists = false;
|
302 |
+
}
|
303 |
+
}
|
304 |
+
|
305 |
+
if (!$wp_filesystem->mkdir($path, FS_CHMOD_DIR)) {
|
306 |
+
return false;
|
307 |
+
}
|
308 |
+
}
|
309 |
+
|
310 |
+
return true;
|
311 |
+
}
|
312 |
+
}
|
framework/helpers/class-fw-wp-list-table.php
CHANGED
@@ -1,968 +1,968 @@
|
|
1 |
-
<?php if ( ! defined( 'FW' ) ) {
|
2 |
-
die( 'Forbidden' );
|
3 |
-
}
|
4 |
-
/**
|
5 |
-
* Base class for displaying a list of items in an ajaxified HTML table.
|
6 |
-
*
|
7 |
-
* @package WordPress
|
8 |
-
* @subpackage List_Table
|
9 |
-
* @since 3.1.0
|
10 |
-
* @access private
|
11 |
-
*/
|
12 |
-
class FW_WP_List_Table {
|
13 |
-
|
14 |
-
/**
|
15 |
-
* The current list of items
|
16 |
-
*
|
17 |
-
* @since 3.1.0
|
18 |
-
* @var array
|
19 |
-
* @access protected
|
20 |
-
*/
|
21 |
-
var $items;
|
22 |
-
|
23 |
-
/**
|
24 |
-
* Various information about the current table
|
25 |
-
*
|
26 |
-
* @since 3.1.0
|
27 |
-
* @var array
|
28 |
-
* @access private
|
29 |
-
*/
|
30 |
-
var $_args;
|
31 |
-
|
32 |
-
/**
|
33 |
-
* Various information needed for displaying the pagination
|
34 |
-
*
|
35 |
-
* @since 3.1.0
|
36 |
-
* @var array
|
37 |
-
* @access private
|
38 |
-
*/
|
39 |
-
var $_pagination_args = array();
|
40 |
-
|
41 |
-
/**
|
42 |
-
* The current screen
|
43 |
-
*
|
44 |
-
* @since 3.1.0
|
45 |
-
* @var object
|
46 |
-
* @access protected
|
47 |
-
*/
|
48 |
-
var $screen;
|
49 |
-
|
50 |
-
/**
|
51 |
-
* Cached bulk actions
|
52 |
-
*
|
53 |
-
* @since 3.1.0
|
54 |
-
* @var array
|
55 |
-
* @access private
|
56 |
-
*/
|
57 |
-
var $_actions;
|
58 |
-
|
59 |
-
/**
|
60 |
-
* Cached pagination output
|
61 |
-
*
|
62 |
-
* @since 3.1.0
|
63 |
-
* @var string
|
64 |
-
* @access private
|
65 |
-
*/
|
66 |
-
var $_pagination;
|
67 |
-
|
68 |
-
/**
|
69 |
-
* Constructor. The child class should call this constructor from its own constructor
|
70 |
-
*
|
71 |
-
* @param array $args An associative array with information about the current table
|
72 |
-
* @access protected
|
73 |
-
*/
|
74 |
-
function __construct( $args = array() ) {
|
75 |
-
$args = wp_parse_args( $args, array(
|
76 |
-
'plural' => '',
|
77 |
-
'singular' => '',
|
78 |
-
'ajax' => false,
|
79 |
-
'screen' => null,
|
80 |
-
) );
|
81 |
-
|
82 |
-
$this->screen = convert_to_screen( $args['screen'] );
|
83 |
-
|
84 |
-
add_filter( "manage_{$this->screen->id}_columns", array( $this, 'get_columns' ), 0 );
|
85 |
-
|
86 |
-
if ( !$args['plural'] )
|
87 |
-
$args['plural'] = $this->screen->base;
|
88 |
-
|
89 |
-
$args['plural'] = sanitize_key( $args['plural'] );
|
90 |
-
$args['singular'] = sanitize_key( $args['singular'] );
|
91 |
-
|
92 |
-
$this->_args = $args;
|
93 |
-
|
94 |
-
if ( $args['ajax'] ) {
|
95 |
-
// wp_enqueue_script( 'list-table' );
|
96 |
-
add_action( 'admin_footer', array( $this, '_js_vars' ) );
|
97 |
-
}
|
98 |
-
}
|
99 |
-
|
100 |
-
/**
|
101 |
-
* Checks the current user's permissions
|
102 |
-
* @uses wp_die()
|
103 |
-
*
|
104 |
-
* @since 3.1.0
|
105 |
-
* @access public
|
106 |
-
* @abstract
|
107 |
-
*/
|
108 |
-
function ajax_user_can() {
|
109 |
-
die( 'function WP_List_Table::ajax_user_can() must be over-ridden in a sub-class.' );
|
110 |
-
}
|
111 |
-
|
112 |
-
/**
|
113 |
-
* Prepares the list of items for displaying.
|
114 |
-
* @uses WP_List_Table::set_pagination_args()
|
115 |
-
*
|
116 |
-
* @since 3.1.0
|
117 |
-
* @access public
|
118 |
-
* @abstract
|
119 |
-
*/
|
120 |
-
function prepare_items() {
|
121 |
-
die( 'function WP_List_Table::prepare_items() must be over-ridden in a sub-class.' );
|
122 |
-
}
|
123 |
-
|
124 |
-
/**
|
125 |
-
* An internal method that sets all the necessary pagination arguments
|
126 |
-
*
|
127 |
-
* @param array $args An associative array with information about the pagination
|
128 |
-
* @access protected
|
129 |
-
*/
|
130 |
-
function set_pagination_args( $args ) {
|
131 |
-
$args = wp_parse_args( $args, array(
|
132 |
-
'total_items' => 0,
|
133 |
-
'total_pages' => 0,
|
134 |
-
'per_page' => 0,
|
135 |
-
) );
|
136 |
-
|
137 |
-
if ( !$args['total_pages'] && $args['per_page'] > 0 )
|
138 |
-
$args['total_pages'] = ceil( $args['total_items'] / $args['per_page'] );
|
139 |
-
|
140 |
-
// redirect if page number is invalid and headers are not already sent
|
141 |
-
if ( ! headers_sent() && ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) && $args['total_pages'] > 0 && $this->get_pagenum() > $args['total_pages'] ) {
|
142 |
-
wp_redirect( add_query_arg( 'paged', $args['total_pages'] ) );
|
143 |
-
exit;
|
144 |
-
}
|
145 |
-
|
146 |
-
$this->_pagination_args = $args;
|
147 |
-
}
|
148 |
-
|
149 |
-
/**
|
150 |
-
* Access the pagination args
|
151 |
-
*
|
152 |
-
* @since 3.1.0
|
153 |
-
* @access public
|
154 |
-
*
|
155 |
-
* @param string $key
|
156 |
-
* @return array
|
157 |
-
*/
|
158 |
-
function get_pagination_arg( $key ) {
|
159 |
-
if ( 'page' == $key )
|
160 |
-
return $this->get_pagenum();
|
161 |
-
|
162 |
-
if ( isset( $this->_pagination_args[$key] ) )
|
163 |
-
return $this->_pagination_args[$key];
|
164 |
-
}
|
165 |
-
|
166 |
-
/**
|
167 |
-
* Whether the table has items to display or not
|
168 |
-
*
|
169 |
-
* @since 3.1.0
|
170 |
-
* @access public
|
171 |
-
*
|
172 |
-
* @return bool
|
173 |
-
*/
|
174 |
-
function has_items() {
|
175 |
-
return !empty( $this->items );
|
176 |
-
}
|
177 |
-
|
178 |
-
/**
|
179 |
-
* Message to be displayed when there are no items
|
180 |
-
*
|
181 |
-
* @since 3.1.0
|
182 |
-
* @access public
|
183 |
-
*/
|
184 |
-
function no_items() {
|
185 |
-
_e( 'No items found.', 'fw' );
|
186 |
-
}
|
187 |
-
|
188 |
-
/**
|
189 |
-
* Display the search box.
|
190 |
-
*
|
191 |
-
* @since 3.1.0
|
192 |
-
* @access public
|
193 |
-
*
|
194 |
-
* @param string $text The search button text
|
195 |
-
* @param string $input_id The search input id
|
196 |
-
*/
|
197 |
-
function search_box( $text, $input_id ) {
|
198 |
-
if ( empty( $_REQUEST['s'] ) && !$this->has_items() )
|
199 |
-
return;
|
200 |
-
|
201 |
-
$input_id = $input_id . '-search-input';
|
202 |
-
|
203 |
-
if ( ! empty( $_REQUEST['orderby'] ) )
|
204 |
-
echo '<input type="hidden" name="orderby" value="' . esc_attr( $_REQUEST['orderby'] ) . '" />';
|
205 |
-
if ( ! empty( $_REQUEST['order'] ) )
|
206 |
-
echo '<input type="hidden" name="order" value="' . esc_attr( $_REQUEST['order'] ) . '" />';
|
207 |
-
if ( ! empty( $_REQUEST['post_mime_type'] ) )
|
208 |
-
echo '<input type="hidden" name="post_mime_type" value="' . esc_attr( $_REQUEST['post_mime_type'] ) . '" />';
|
209 |
-
if ( ! empty( $_REQUEST['detached'] ) )
|
210 |
-
echo '<input type="hidden" name="detached" value="' . esc_attr( $_REQUEST['detached'] ) . '" />';
|
211 |
-
?>
|
212 |
-
<p class="search-box">
|
213 |
-
<label class="screen-reader-text" for="<?php echo esc_attr($input_id) ?>"><?php echo $text; ?>:</label>
|
214 |
-
<input type="search" id="<?php echo esc_attr($input_id) ?>" name="s" value="<?php _admin_search_query(); ?>" />
|
215 |
-
<?php submit_button( $text, 'button', false, false, array('id' => 'search-submit') ); ?>
|
216 |
-
</p>
|
217 |
-
<?php
|
218 |
-
}
|
219 |
-
|
220 |
-
/**
|
221 |
-
* Get an associative array ( id => link ) with the list
|
222 |
-
* of views available on this table.
|
223 |
-
*
|
224 |
-
* @since 3.1.0
|
225 |
-
* @access protected
|
226 |
-
*
|
227 |
-
* @return array
|
228 |
-
*/
|
229 |
-
function get_views() {
|
230 |
-
return array();
|
231 |
-
}
|
232 |
-
|
233 |
-
/**
|
234 |
-
* Display the list of views available on this table.
|
235 |
-
*
|
236 |
-
* @since 3.1.0
|
237 |
-
* @access public
|
238 |
-
*/
|
239 |
-
function views() {
|
240 |
-
$views = $this->get_views();
|
241 |
-
/**
|
242 |
-
* Filter the list of available list table views.
|
243 |
-
*
|
244 |
-
* The dynamic portion of the hook name, $this->screen->id, refers
|
245 |
-
* to the ID of the current screen, usually a string.
|
246 |
-
*
|
247 |
-
* @since 3.5.0
|
248 |
-
*
|
249 |
-
* @param array $views An array of available list table views.
|
250 |
-
*/
|
251 |
-
$views = apply_filters( "views_{$this->screen->id}", $views );
|
252 |
-
|
253 |
-
if ( empty( $views ) )
|
254 |
-
return;
|
255 |
-
|
256 |
-
echo "<ul class='subsubsub'>\n";
|
257 |
-
foreach ( $views as $class => $view ) {
|
258 |
-
$views[ $class ] = "\t<li class='$class'>$view";
|
259 |
-
}
|
260 |
-
echo implode( " |</li>\n", $views ) . "</li>\n";
|
261 |
-
echo "</ul>";
|
262 |
-
}
|
263 |
-
|
264 |
-
/**
|
265 |
-
* Get an associative array ( option_name => option_title ) with the list
|
266 |
-
* of bulk actions available on this table.
|
267 |
-
*
|
268 |
-
* @since 3.1.0
|
269 |
-
* @access protected
|
270 |
-
*
|
271 |
-
* @return array
|
272 |
-
*/
|
273 |
-
function get_bulk_actions() {
|
274 |
-
return array();
|
275 |
-
}
|
276 |
-
|
277 |
-
/**
|
278 |
-
* Display the bulk actions dropdown.
|
279 |
-
*
|
280 |
-
* @since 3.1.0
|
281 |
-
* @access public
|
282 |
-
*/
|
283 |
-
function bulk_actions() {
|
284 |
-
if ( is_null( $this->_actions ) ) {
|
285 |
-
$no_new_actions = $this->_actions = $this->get_bulk_actions();
|
286 |
-
/**
|
287 |
-
* Filter the list table Bulk Actions drop-down.
|
288 |
-
*
|
289 |
-
* The dynamic portion of the hook name, $this->screen->id, refers
|
290 |
-
* to the ID of the current screen, usually a string.
|
291 |
-
*
|
292 |
-
* This filter can currently only be used to remove bulk actions.
|
293 |
-
*
|
294 |
-
* @since 3.5.0
|
295 |
-
*
|
296 |
-
* @param array $actions An array of the available bulk actions.
|
297 |
-
*/
|
298 |
-
$this->_actions = apply_filters( "bulk_actions-{$this->screen->id}", $this->_actions );
|
299 |
-
$this->_actions = array_intersect_assoc( $this->_actions, $no_new_actions );
|
300 |
-
$two = '';
|
301 |
-
} else {
|
302 |
-
$two = '2';
|
303 |
-
}
|
304 |
-
|
305 |
-
if ( empty( $this->_actions ) )
|
306 |
-
return;
|
307 |
-
|
308 |
-
echo "<select name='action$two'>\n";
|
309 |
-
echo "<option value='-1' selected='selected'>" . __( 'Bulk Actions', 'fw' ) . "</option>\n";
|
310 |
-
|
311 |
-
foreach ( $this->_actions as $name => $title ) {
|
312 |
-
$class = 'edit' == $name ? ' class="hide-if-no-js"' : '';
|
313 |
-
|
314 |
-
echo "\t<option value='$name'$class>$title</option>\n";
|
315 |
-
}
|
316 |
-
|
317 |
-
echo "</select>\n";
|
318 |
-
|
319 |
-
submit_button( __( 'Apply', 'fw' ), 'action', false, false, array( 'id' => "doaction$two" ) );
|
320 |
-
echo "\n";
|
321 |
-
}
|
322 |
-
|
323 |
-
/**
|
324 |
-
* Get the current action selected from the bulk actions dropdown.
|
325 |
-
*
|
326 |
-
* @since 3.1.0
|
327 |
-
* @access public
|
328 |
-
*
|
329 |
-
* @return string|bool The action name or False if no action was selected
|
330 |
-
*/
|
331 |
-
function current_action() {
|
332 |
-
if ( isset( $_REQUEST['action'] ) && -1 != $_REQUEST['action'] )
|
333 |
-
return $_REQUEST['action'];
|
334 |
-
|
335 |
-
if ( isset( $_REQUEST['action2'] ) && -1 != $_REQUEST['action2'] )
|
336 |
-
return $_REQUEST['action2'];
|
337 |
-
|
338 |
-
return false;
|
339 |
-
}
|
340 |
-
|
341 |
-
/**
|
342 |
-
* Generate row actions div
|
343 |
-
*
|
344 |
-
* @since 3.1.0
|
345 |
-
* @access protected
|
346 |
-
*
|
347 |
-
* @param array $actions The list of actions
|
348 |
-
* @param bool $always_visible Whether the actions should be always visible
|
349 |
-
* @return string
|
350 |
-
*/
|
351 |
-
function row_actions( $actions, $always_visible = false ) {
|
352 |
-
$action_count = count( $actions );
|
353 |
-
$i = 0;
|
354 |
-
|
355 |
-
if ( !$action_count )
|
356 |
-
return '';
|
357 |
-
|
358 |
-
$out = '<div class="' . ( $always_visible ? 'row-actions visible' : 'row-actions' ) . '">';
|
359 |
-
foreach ( $actions as $action => $link ) {
|
360 |
-
++$i;
|
361 |
-
( $i == $action_count ) ? $sep = '' : $sep = ' | ';
|
362 |
-
$out .= "<span class='$action'>$link$sep</span>";
|
363 |
-
}
|
364 |
-
$out .= '</div>';
|
365 |
-
|
366 |
-
return $out;
|
367 |
-
}
|
368 |
-
|
369 |
-
/**
|
370 |
-
* Display a monthly dropdown for filtering items
|
371 |
-
*
|
372 |
-
* @since 3.1.0
|
373 |
-
* @access protected
|
374 |
-
*/
|
375 |
-
function months_dropdown( $post_type ) {
|
376 |
-
global $wpdb, $wp_locale;
|
377 |
-
|
378 |
-
$months = $wpdb->get_results( $wpdb->prepare( "
|
379 |
-
SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month
|
380 |
-
FROM $wpdb->posts
|
381 |
-
WHERE post_type = %s
|
382 |
-
ORDER BY post_date DESC
|
383 |
-
", $post_type ) );
|
384 |
-
|
385 |
-
/**
|
386 |
-
* Filter the 'Months' drop-down results.
|
387 |
-
*
|
388 |
-
* @since 3.7.0
|
389 |
-
*
|
390 |
-
* @param object $months The months drop-down query results.
|
391 |
-
* @param string $post_type The post type.
|
392 |
-
*/
|
393 |
-
$months = apply_filters( 'months_dropdown_results', $months, $post_type );
|
394 |
-
|
395 |
-
$month_count = count( $months );
|
396 |
-
|
397 |
-
if ( !$month_count || ( 1 == $month_count && 0 == $months[0]->month ) )
|
398 |
-
return;
|
399 |
-
|
400 |
-
$m = isset( $_GET['m'] ) ? (int) $_GET['m'] : 0;
|
401 |
-
?>
|
402 |
-
<select name='m'>
|
403 |
-
<option<?php selected( $m, 0 ); ?> value='0'><?php _e( 'All dates', 'fw' ); ?></option>
|
404 |
-
<?php
|
405 |
-
foreach ( $months as $arc_row ) {
|
406 |
-
if ( 0 == $arc_row->year )
|
407 |
-
continue;
|
408 |
-
|
409 |
-
$month = zeroise( $arc_row->month, 2 );
|
410 |
-
$year = $arc_row->year;
|
411 |
-
|
412 |
-
printf( "<option %s value='%s'>%s</option>\n",
|
413 |
-
selected( $m, $year . $month, false ),
|
414 |
-
esc_attr( $arc_row->year . $month ),
|
415 |
-
/* translators: 1: month name, 2: 4-digit year */
|
416 |
-
sprintf( __( '%1$s %2$d', 'fw' ), $wp_locale->get_month( $month ), $year )
|
417 |
-
);
|
418 |
-
}
|
419 |
-
?>
|
420 |
-
</select>
|
421 |
-
<?php
|
422 |
-
}
|
423 |
-
|
424 |
-
/**
|
425 |
-
* Display a view switcher
|
426 |
-
*
|
427 |
-
* @since 3.1.0
|
428 |
-
* @access protected
|
429 |
-
*/
|
430 |
-
function view_switcher( $current_mode ) {
|
431 |
-
$modes = array(
|
432 |
-
'list' => __( 'List View', 'fw' ),
|
433 |
-
'excerpt' => __( 'Excerpt View', 'fw' )
|
434 |
-
);
|
435 |
-
|
436 |
-
?>
|
437 |
-
<input type="hidden" name="mode" value="<?php echo esc_attr( $current_mode ); ?>" />
|
438 |
-
<div class="view-switch">
|
439 |
-
<?php
|
440 |
-
foreach ( $modes as $mode => $title ) {
|
441 |
-
$class = ( $current_mode == $mode ) ? 'class="current"' : '';
|
442 |
-
echo "<a href='" . esc_url( add_query_arg( 'mode', $mode, $_SERVER['REQUEST_URI'] ) ) . "' $class><img id='view-switch-$mode' src='" . esc_url( includes_url( 'images/blank.gif' ) ) . "' width='20' height='20' title='$title' alt='$title' /></a>\n";
|
443 |
-
}
|
444 |
-
?>
|
445 |
-
</div>
|
446 |
-
<?php
|
447 |
-
}
|
448 |
-
|
449 |
-
/**
|
450 |
-
* Display a comment count bubble
|
451 |
-
*
|
452 |
-
* @since 3.1.0
|
453 |
-
* @access protected
|
454 |
-
*
|
455 |
-
* @param int $post_id
|
456 |
-
* @param int $pending_comments
|
457 |
-
*/
|
458 |
-
function comments_bubble( $post_id, $pending_comments ) {
|
459 |
-
$pending_phrase = sprintf( __( '%s pending', 'fw' ), number_format( $pending_comments ) );
|
460 |
-
|
461 |
-
if ( $pending_comments )
|
462 |
-
echo '<strong>';
|
463 |
-
|
464 |
-
echo "<a href='" . esc_url( add_query_arg( 'p', $post_id, admin_url( 'edit-comments.php' ) ) ) . "' title='" . esc_attr( $pending_phrase ) . "' class='post-com-count'><span class='comment-count'>" . number_format_i18n( get_comments_number() ) . "</span></a>";
|
465 |
-
|
466 |
-
if ( $pending_comments )
|
467 |
-
echo '</strong>';
|
468 |
-
}
|
469 |
-
|
470 |
-
/**
|
471 |
-
* Get the current page number
|
472 |
-
*
|
473 |
-
* @since 3.1.0
|
474 |
-
* @access protected
|
475 |
-
*
|
476 |
-
* @return int
|
477 |
-
*/
|
478 |
-
function get_pagenum() {
|
479 |
-
$pagenum = isset( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : 0;
|
480 |
-
|
481 |
-
if( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] )
|
482 |
-
$pagenum = $this->_pagination_args['total_pages'];
|
483 |
-
|
484 |
-
return max( 1, $pagenum );
|
485 |
-
}
|
486 |
-
|
487 |
-
/**
|
488 |
-
* Get number of items to display on a single page
|
489 |
-
*
|
490 |
-
* @since 3.1.0
|
491 |
-
* @access protected
|
492 |
-
*
|
493 |
-
* @return int
|
494 |
-
*/
|
495 |
-
function get_items_per_page( $option, $default = 20 ) {
|
496 |
-
$per_page = (int) get_user_option( $option );
|
497 |
-
if ( empty( $per_page ) || $per_page < 1 )
|
498 |
-
$per_page = $default;
|
499 |
-
|
500 |
-
/**
|
501 |
-
* Filter the number of items to be displayed on each page of the list table.
|
502 |
-
*
|
503 |
-
* The dynamic hook name, $option, refers to the per page option depending
|
504 |
-
* on the type of list table in use. Possible values may include:
|
505 |
-
* 'edit_comments_per_page', 'sites_network_per_page', 'site_themes_network_per_page',
|
506 |
-
* 'themes_netework_per_page', 'users_network_per_page', 'edit_{$post_type}', etc.
|
507 |
-
*
|
508 |
-
* @since 2.9.0
|
509 |
-
*
|
510 |
-
* @param int $per_page Number of items to be displayed. Default 20.
|
511 |
-
*/
|
512 |
-
return (int) apply_filters( $option, $per_page );
|
513 |
-
}
|
514 |
-
|
515 |
-
/**
|
516 |
-
* Display the pagination.
|
517 |
-
*
|
518 |
-
* @since 3.1.0
|
519 |
-
* @access protected
|
520 |
-
*/
|
521 |
-
function pagination( $which ) {
|
522 |
-
if ( empty( $this->_pagination_args ) )
|
523 |
-
return;
|
524 |
-
|
525 |
-
extract( $this->_pagination_args, EXTR_SKIP );
|
526 |
-
|
527 |
-
$output = '<span class="displaying-num">' . sprintf( _n( '1 item', '%s items', $total_items ), number_format_i18n( $total_items ) ) . '</span>';
|
528 |
-
|
529 |
-
$current = $this->get_pagenum();
|
530 |
-
|
531 |
-
$current_url = fw_current_url();
|
532 |
-
|
533 |
-
$current_url = remove_query_arg( array( 'hotkeys_highlight_last', 'hotkeys_highlight_first' ), $current_url );
|
534 |
-
|
535 |
-
$page_links = array();
|
536 |
-
|
537 |
-
$disable_first = $disable_last = '';
|
538 |
-
if ( $current == 1 )
|
539 |
-
$disable_first = ' disabled';
|
540 |
-
if ( $current == $total_pages )
|
541 |
-
$disable_last = ' disabled';
|
542 |
-
|
543 |
-
$page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
|
544 |
-
'first-page' . $disable_first,
|
545 |
-
esc_attr__( 'Go to the first page', 'fw' ),
|
546 |
-
esc_url( remove_query_arg( 'paged', $current_url ) ),
|
547 |
-
'«'
|
548 |
-
);
|
549 |
-
|
550 |
-
$page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
|
551 |
-
'prev-page' . $disable_first,
|
552 |
-
esc_attr__( 'Go to the previous page', 'fw' ),
|
553 |
-
esc_url( add_query_arg( 'paged', max( 1, $current-1 ), $current_url ) ),
|
554 |
-
'‹'
|
555 |
-
);
|
556 |
-
|
557 |
-
if ( 'bottom' == $which )
|
558 |
-
$html_current_page = $current;
|
559 |
-
else
|
560 |
-
$html_current_page = sprintf( "<input class='current-page' title='%s' type='text' name='paged' value='%s' size='%d' />",
|
561 |
-
esc_attr__( 'Current page', 'fw' ),
|
562 |
-
$current,
|
563 |
-
strlen( $total_pages )
|
564 |
-
);
|
565 |
-
|
566 |
-
$html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) );
|
567 |
-
$page_links[] = '<span class="paging-input">' . sprintf( _x( '%1$s of %2$s', 'paging', 'fw' ), $html_current_page, $html_total_pages ) . '</span>';
|
568 |
-
|
569 |
-
$page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
|
570 |
-
'next-page' . $disable_last,
|
571 |
-
esc_attr__( 'Go to the next page', 'fw' ),
|
572 |
-
esc_url( add_query_arg( 'paged', min( $total_pages, $current+1 ), $current_url ) ),
|
573 |
-
'›'
|
574 |
-
);
|
575 |
-
|
576 |
-
$page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
|
577 |
-
'last-page' . $disable_last,
|
578 |
-
esc_attr__( 'Go to the last page', 'fw' ),
|
579 |
-
esc_url( add_query_arg( 'paged', $total_pages, $current_url ) ),
|
580 |
-
'»'
|
581 |
-
);
|
582 |
-
|
583 |
-
$pagination_links_class = 'pagination-links';
|
584 |
-
if ( ! empty( $infinite_scroll ) )
|
585 |
-
$pagination_links_class = ' hide-if-js';
|
586 |
-
$output .= "\n<span class='$pagination_links_class'>" . join( "\n", $page_links ) . '</span>';
|
587 |
-
|
588 |
-
if ( $total_pages )
|
589 |
-
$page_class = $total_pages < 2 ? ' one-page' : '';
|
590 |
-
else
|
591 |
-
$page_class = ' no-pages';
|
592 |
-
|
593 |
-
$this->_pagination = "<div class='tablenav-pages{$page_class}'>$output</div>";
|
594 |
-
|
595 |
-
echo $this->_pagination;
|
596 |
-
}
|
597 |
-
|
598 |
-
/**
|
599 |
-
* Get a list of columns. The format is:
|
600 |
-
* 'internal-name' => 'Title'
|
601 |
-
*
|
602 |
-
* @since 3.1.0
|
603 |
-
* @access protected
|
604 |
-
* @abstract
|
605 |
-
*
|
606 |
-
* @return array
|
607 |
-
*/
|
608 |
-
function get_columns() {
|
609 |
-
die( 'function WP_List_Table::get_columns() must be over-ridden in a sub-class.' );
|
610 |
-
}
|
611 |
-
|
612 |
-
/**
|
613 |
-
* Get a list of sortable columns. The format is:
|
614 |
-
* 'internal-name' => 'orderby'
|
615 |
-
* or
|
616 |
-
* 'internal-name' => array( 'orderby', true )
|
617 |
-
*
|
618 |
-
* The second format will make the initial sorting order be descending
|
619 |
-
*
|
620 |
-
* @since 3.1.0
|
621 |
-
* @access protected
|
622 |
-
*
|
623 |
-
* @return array
|
624 |
-
*/
|
625 |
-
function get_sortable_columns() {
|
626 |
-
return array();
|
627 |
-
}
|
628 |
-
|
629 |
-
/**
|
630 |
-
* Get a list of all, hidden and sortable columns, with filter applied
|
631 |
-
*
|
632 |
-
* @since 3.1.0
|
633 |
-
* @access protected
|
634 |
-
*
|
635 |
-
* @return array
|
636 |
-
*/
|
637 |
-
function get_column_info() {
|
638 |
-
if ( isset( $this->_column_headers ) )
|
639 |
-
return $this->_column_headers;
|
640 |
-
|
641 |
-
$columns = get_column_headers( $this->screen );
|
642 |
-
$hidden = get_hidden_columns( $this->screen );
|
643 |
-
|
644 |
-
$sortable_columns = $this->get_sortable_columns();
|
645 |
-
/**
|
646 |
-
* Filter the list table sortable columns for a specific screen.
|
647 |
-
*
|
648 |
-
* The dynamic portion of the hook name, $this->screen->id, refers
|
649 |
-
* to the ID of the current screen, usually a string.
|
650 |
-
*
|
651 |
-
* @since 3.5.0
|
652 |
-
*
|
653 |
-
* @param array $sortable_columns An array of sortable columns.
|
654 |
-
*/
|
655 |
-
$_sortable = apply_filters( "manage_{$this->screen->id}_sortable_columns", $sortable_columns );
|
656 |
-
|
657 |
-
$sortable = array();
|
658 |
-
foreach ( $_sortable as $id => $data ) {
|
659 |
-
if ( empty( $data ) )
|
660 |
-
continue;
|
661 |
-
|
662 |
-
$data = (array) $data;
|
663 |
-
if ( !isset( $data[1] ) )
|
664 |
-
$data[1] = false;
|
665 |
-
|
666 |
-
$sortable[$id] = $data;
|
667 |
-
}
|
668 |
-
|
669 |
-
$this->_column_headers = array( $columns, $hidden, $sortable );
|
670 |
-
|
671 |
-
return $this->_column_headers;
|
672 |
-
}
|
673 |
-
|
674 |
-
/**
|
675 |
-
* Return number of visible columns
|
676 |
-
*
|
677 |
-
* @since 3.1.0
|
678 |
-
* @access public
|
679 |
-
*
|
680 |
-
* @return int
|
681 |
-
*/
|
682 |
-
function get_column_count() {
|
683 |
-
list ( $columns, $hidden ) = $this->get_column_info();
|
684 |
-
$hidden = array_intersect( array_keys( $columns ), array_filter( $hidden ) );
|
685 |
-
return count( $columns ) - count( $hidden );
|
686 |
-
}
|
687 |
-
|
688 |
-
/**
|
689 |
-
* Print column headers, accounting for hidden and sortable columns.
|
690 |
-
*
|
691 |
-
* @since 3.1.0
|
692 |
-
* @access protected
|
693 |
-
*
|
694 |
-
* @param bool $with_id Whether to set the id attribute or not
|
695 |
-
*/
|
696 |
-
function print_column_headers( $with_id = true ) {
|
697 |
-
list( $columns, $hidden, $sortable ) = $this->get_column_info();
|
698 |
-
|
699 |
-
$current_url = fw_current_url();
|
700 |
-
$current_url = remove_query_arg( 'paged', $current_url );
|
701 |
-
|
702 |
-
if ( isset( $_GET['orderby'] ) )
|
703 |
-
$current_orderby = $_GET['orderby'];
|
704 |
-
else
|
705 |
-
$current_orderby = '';
|
706 |
-
|
707 |
-
if ( isset( $_GET['order'] ) && 'desc' == $_GET['order'] )
|
708 |
-
$current_order = 'desc';
|
709 |
-
else
|
710 |
-
$current_order = 'asc';
|
711 |
-
|
712 |
-
if ( ! empty( $columns['cb'] ) ) {
|
713 |
-
static $cb_counter = 1;
|
714 |
-
$columns['cb'] = '<label class="screen-reader-text" for="cb-select-all-' . $cb_counter . '">' . __( 'Select All', 'fw' ) . '</label>'
|
715 |
-
. '<input id="cb-select-all-' . $cb_counter . '" type="checkbox" />';
|
716 |
-
$cb_counter++;
|
717 |
-
}
|
718 |
-
|
719 |
-
foreach ( $columns as $column_key => $column_display_name ) {
|
720 |
-
$class = array( 'manage-column', "column-$column_key" );
|
721 |
-
|
722 |
-
$style = '';
|
723 |
-
if ( in_array( $column_key, $hidden ) )
|
724 |
-
$style = 'display:none;';
|
725 |
-
|
726 |
-
$style = ' style="' . $style . '"';
|
727 |
-
|
728 |
-
if ( 'cb' == $column_key )
|
729 |
-
$class[] = 'check-column';
|
730 |
-
elseif ( in_array( $column_key, array( 'posts', 'comments', 'links' ) ) )
|
731 |
-
$class[] = 'num';
|
732 |
-
|
733 |
-
if ( isset( $sortable[$column_key] ) ) {
|
734 |
-
list( $orderby, $desc_first ) = $sortable[$column_key];
|
735 |
-
|
736 |
-
if ( $current_orderby == $orderby ) {
|
737 |
-
$order = 'asc' == $current_order ? 'desc' : 'asc';
|
738 |
-
$class[] = 'sorted';
|
739 |
-
$class[] = $current_order;
|
740 |
-
} else {
|
741 |
-
$order = $desc_first ? 'desc' : 'asc';
|
742 |
-
$class[] = 'sortable';
|
743 |
-
$class[] = $desc_first ? 'asc' : 'desc';
|
744 |
-
}
|
745 |
-
|
746 |
-
$column_display_name = '<a href="' . esc_url( add_query_arg( compact( 'orderby', 'order' ), $current_url ) ) . '"><span>' . $column_display_name . '</span><span class="sorting-indicator"></span></a>';
|
747 |
-
}
|
748 |
-
|
749 |
-
$id = $with_id ? "id='$column_key'" : '';
|
750 |
-
|
751 |
-
if ( !empty( $class ) )
|
752 |
-
$class = "class='" . join( ' ', $class ) . "'";
|
753 |
-
|
754 |
-
echo "<th scope='col' $id $class $style>$column_display_name</th>";
|
755 |
-
}
|
756 |
-
}
|
757 |
-
|
758 |
-
/**
|
759 |
-
* Display the table
|
760 |
-
*
|
761 |
-
* @since 3.1.0
|
762 |
-
* @access public
|
763 |
-
*/
|
764 |
-
function display() {
|
765 |
-
extract( $this->_args );
|
766 |
-
|
767 |
-
$this->display_tablenav( 'top' );
|
768 |
-
|
769 |
-
?>
|
770 |
-
<table class="wp-list-table <?php echo implode( ' ', $this->get_table_classes() ); ?>">
|
771 |
-
<thead>
|
772 |
-
<tr>
|
773 |
-
<?php $this->print_column_headers(); ?>
|
774 |
-
</tr>
|
775 |
-
</thead>
|
776 |
-
|
777 |
-
<tfoot>
|
778 |
-
<tr>
|
779 |
-
<?php $this->print_column_headers( false ); ?>
|
780 |
-
</tr>
|
781 |
-
</tfoot>
|
782 |
-
|
783 |
-
<tbody id="the-list"<?php if ( $singular ) echo " data-wp-lists='list:$singular'"; ?>>
|
784 |
-
<?php $this->display_rows_or_placeholder(); ?>
|
785 |
-
</tbody>
|
786 |
-
</table>
|
787 |
-
<?php
|
788 |
-
$this->display_tablenav( 'bottom' );
|
789 |
-
}
|
790 |
-
|
791 |
-
/**
|
792 |
-
* Get a list of CSS classes for the <table> tag
|
793 |
-
*
|
794 |
-
* @since 3.1.0
|
795 |
-
* @access protected
|
796 |
-
*
|
797 |
-
* @return array
|
798 |
-
*/
|
799 |
-
function get_table_classes() {
|
800 |
-
return array( 'widefat', 'fixed', $this->_args['plural'] );
|
801 |
-
}
|
802 |
-
|
803 |
-
/**
|
804 |
-
* Generate the table navigation above or below the table
|
805 |
-
*
|
806 |
-
* @since 3.1.0
|
807 |
-
* @access protected
|
808 |
-
*/
|
809 |
-
function display_tablenav( $which ) {
|
810 |
-
if ( 'top' == $which )
|
811 |
-
wp_nonce_field( 'bulk-' . $this->_args['plural'] );
|
812 |
-
?>
|
813 |
-
<div class="tablenav <?php echo esc_attr( $which ); ?>">
|
814 |
-
|
815 |
-
<div class="alignleft actions bulkactions">
|
816 |
-
<?php $this->bulk_actions(); ?>
|
817 |
-
</div>
|
818 |
-
<?php
|
819 |
-
$this->extra_tablenav( $which );
|
820 |
-
$this->pagination( $which );
|
821 |
-
?>
|
822 |
-
|
823 |
-
<br class="clear" />
|
824 |
-
</div>
|
825 |
-
<?php
|
826 |
-
}
|
827 |
-
|
828 |
-
/**
|
829 |
-
* Extra controls to be displayed between bulk actions and pagination
|
830 |
-
*
|
831 |
-
* @since 3.1.0
|
832 |
-
* @access protected
|
833 |
-
*/
|
834 |
-
function extra_tablenav( $which ) {}
|
835 |
-
|
836 |
-
/**
|
837 |
-
* Generate the <tbody> part of the table
|
838 |
-
*
|
839 |
-
* @since 3.1.0
|
840 |
-
* @access protected
|
841 |
-
*/
|
842 |
-
function display_rows_or_placeholder() {
|
843 |
-
if ( $this->has_items() ) {
|
844 |
-
$this->display_rows();
|
845 |
-
} else {
|
846 |
-
list( $columns, $hidden ) = $this->get_column_info();
|
847 |
-
echo '<tr class="no-items"><td class="colspanchange" colspan="' . $this->get_column_count() . '">';
|
848 |
-
$this->no_items();
|
849 |
-
echo '</td></tr>';
|
850 |
-
}
|
851 |
-
}
|
852 |
-
|
853 |
-
/**
|
854 |
-
* Generate the table rows
|
855 |
-
*
|
856 |
-
* @since 3.1.0
|
857 |
-
* @access protected
|
858 |
-
*/
|
859 |
-
function display_rows() {
|
860 |
-
foreach ( $this->items as $item )
|
861 |
-
$this->single_row( $item );
|
862 |
-
}
|
863 |
-
|
864 |
-
/**
|
865 |
-
* Generates content for a single row of the table
|
866 |
-
*
|
867 |
-
* @since 3.1.0
|
868 |
-
* @access protected
|
869 |
-
*
|
870 |
-
* @param object $item The current item
|
871 |
-
*/
|
872 |
-
function single_row( $item ) {
|
873 |
-
static $row_class = '';
|
874 |
-
$row_class = ( $row_class == '' ? ' class="alternate"' : '' );
|
875 |
-
|
876 |
-
echo '<tr' . $row_class . '>';
|
877 |
-
$this->single_row_columns( $item );
|
878 |
-
echo '</tr>';
|
879 |
-
}
|
880 |
-
|
881 |
-
/**
|
882 |
-
* Generates the columns for a single row of the table
|
883 |
-
*
|
884 |
-
* @since 3.1.0
|
885 |
-
* @access protected
|
886 |
-
*
|
887 |
-
* @param object $item The current item
|
888 |
-
*/
|
889 |
-
function single_row_columns( $item ) {
|
890 |
-
list( $columns, $hidden ) = $this->get_column_info();
|
891 |
-
|
892 |
-
foreach ( $columns as $column_name => $column_display_name ) {
|
893 |
-
$class = "class='$column_name column-$column_name'";
|
894 |
-
|
895 |
-
$style = '';
|
896 |
-
if ( in_array( $column_name, $hidden ) )
|
897 |
-
$style = ' style="display:none;"';
|
898 |
-
|
899 |
-
$attributes = "$class$style";
|
900 |
-
|
901 |
-
if ( 'cb' == $column_name ) {
|
902 |
-
echo '<th scope="row" class="check-column">';
|
903 |
-
echo $this->column_cb( $item );
|
904 |
-
echo '</th>';
|
905 |
-
}
|
906 |
-
elseif ( method_exists( $this, 'column_' . $column_name ) ) {
|
907 |
-
echo "<td $attributes>";
|
908 |
-
echo call_user_func( array( $this, 'column_' . $column_name ), $item );
|
909 |
-
echo "</td>";
|
910 |
-
}
|
911 |
-
else {
|
912 |
-
echo "<td $attributes>";
|
913 |
-
echo $this->column_default( $item, $column_name );
|
914 |
-
echo "</td>";
|
915 |
-
}
|
916 |
-
}
|
917 |
-
}
|
918 |
-
|
919 |
-
/**
|
920 |
-
* Handle an incoming ajax request (called from admin-ajax.php)
|
921 |
-
*
|
922 |
-
* @since 3.1.0
|
923 |
-
* @access public
|
924 |
-
*/
|
925 |
-
function ajax_response() {
|
926 |
-
$this->prepare_items();
|
927 |
-
|
928 |
-
extract( $this->_args );
|
929 |
-
extract( $this->_pagination_args, EXTR_SKIP );
|
930 |
-
|
931 |
-
ob_start();
|
932 |
-
if ( ! empty( $_REQUEST['no_placeholder'] ) )
|
933 |
-
$this->display_rows();
|
934 |
-
else
|
935 |
-
$this->display_rows_or_placeholder();
|
936 |
-
|
937 |
-
$rows = ob_get_clean();
|
938 |
-
|
939 |
-
$response = array( 'rows' => $rows );
|
940 |
-
|
941 |
-
if ( isset( $total_items ) )
|
942 |
-
$response['total_items_i18n'] = sprintf( _n( '1 item', '%s items', $total_items ), number_format_i18n( $total_items ) );
|
943 |
-
|
944 |
-
if ( isset( $total_pages ) ) {
|
945 |
-
$response['total_pages'] = $total_pages;
|
946 |
-
$response['total_pages_i18n'] = number_format_i18n( $total_pages );
|
947 |
-
}
|
948 |
-
|
949 |
-
die( json_encode( $response ) );
|
950 |
-
}
|
951 |
-
|
952 |
-
/**
|
953 |
-
* Send required variables to JavaScript land
|
954 |
-
*
|
955 |
-
* @access private
|
956 |
-
*/
|
957 |
-
function _js_vars() {
|
958 |
-
$args = array(
|
959 |
-
'class' => get_class( $this ),
|
960 |
-
'screen' => array(
|
961 |
-
'id' => $this->screen->id,
|
962 |
-
'base' => $this->screen->base,
|
963 |
-
)
|
964 |
-
);
|
965 |
-
|
966 |
-
printf( "<script type='text/javascript'>list_args = %s;</script>\n", json_encode( $args ) );
|
967 |
-
}
|
968 |
-
}
|
1 |
+
<?php if ( ! defined( 'FW' ) ) {
|
2 |
+
die( 'Forbidden' );
|
3 |
+
}
|
4 |
+
/**
|
5 |
+
* Base class for displaying a list of items in an ajaxified HTML table.
|
6 |
+
*
|
7 |
+
* @package WordPress
|
8 |
+
* @subpackage List_Table
|
9 |
+
* @since 3.1.0
|
10 |
+
* @access private
|
11 |
+
*/
|
12 |
+
class FW_WP_List_Table {
|
13 |
+
|
14 |
+
/**
|
15 |
+
* The current list of items
|
16 |
+
*
|
17 |
+
* @since 3.1.0
|
18 |
+
* @var array
|
19 |
+
* @access protected
|
20 |
+
*/
|
21 |
+
var $items;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Various information about the current table
|
25 |
+
*
|
26 |
+
* @since 3.1.0
|
27 |
+
* @var array
|
28 |
+
* @access private
|
29 |
+
*/
|
30 |
+
var $_args;
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Various information needed for displaying the pagination
|
34 |
+
*
|
35 |
+
* @since 3.1.0
|
36 |
+
* @var array
|
37 |
+
* @access private
|
38 |
+
*/
|
39 |
+
var $_pagination_args = array();
|
40 |
+
|
41 |
+
/**
|
42 |
+
* The current screen
|
43 |
+
*
|
44 |
+
* @since 3.1.0
|
45 |
+
* @var object
|
46 |
+
* @access protected
|
47 |
+
*/
|
48 |
+
var $screen;
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Cached bulk actions
|
52 |
+
*
|
53 |
+
* @since 3.1.0
|
54 |
+
* @var array
|
55 |
+
* @access private
|
56 |
+
*/
|
57 |
+
var $_actions;
|
58 |
+
|
59 |
+
/**
|
60 |
+
* Cached pagination output
|
61 |
+
*
|
62 |
+
* @since 3.1.0
|
63 |
+
* @var string
|
64 |
+
* @access private
|
65 |
+
*/
|
66 |
+
var $_pagination;
|
67 |
+
|
68 |
+
/**
|
69 |
+
* Constructor. The child class should call this constructor from its own constructor
|
70 |
+
*
|
71 |
+
* @param array $args An associative array with information about the current table
|
72 |
+
* @access protected
|
73 |
+
*/
|
74 |
+
function __construct( $args = array() ) {
|
75 |
+
$args = wp_parse_args( $args, array(
|
76 |
+
'plural' => '',
|
77 |
+
'singular' => '',
|
78 |
+
'ajax' => false,
|
79 |
+
'screen' => null,
|
80 |
+
) );
|
81 |
+
|
82 |
+
$this->screen = convert_to_screen( $args['screen'] );
|
83 |
+
|
84 |
+
add_filter( "manage_{$this->screen->id}_columns", array( $this, 'get_columns' ), 0 );
|
85 |
+
|
86 |
+
if ( !$args['plural'] )
|
87 |
+
$args['plural'] = $this->screen->base;
|
88 |
+
|
89 |
+
$args['plural'] = sanitize_key( $args['plural'] );
|
90 |
+
$args['singular'] = sanitize_key( $args['singular'] );
|
91 |
+
|
92 |
+
$this->_args = $args;
|
93 |
+
|
94 |
+
if ( $args['ajax'] ) {
|
95 |
+
// wp_enqueue_script( 'list-table' );
|
96 |
+
add_action( 'admin_footer', array( $this, '_js_vars' ) );
|
97 |
+
}
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Checks the current user's permissions
|
102 |
+
* @uses wp_die()
|
103 |
+
*
|
104 |
+
* @since 3.1.0
|
105 |
+
* @access public
|
106 |
+
* @abstract
|
107 |
+
*/
|
108 |
+
function ajax_user_can() {
|
109 |
+
die( 'function WP_List_Table::ajax_user_can() must be over-ridden in a sub-class.' );
|
110 |
+
}
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Prepares the list of items for displaying.
|
114 |
+
* @uses WP_List_Table::set_pagination_args()
|
115 |
+
*
|
116 |
+
* @since 3.1.0
|
117 |
+
* @access public
|
118 |
+
* @abstract
|
119 |
+
*/
|
120 |
+
function prepare_items() {
|
121 |
+
die( 'function WP_List_Table::prepare_items() must be over-ridden in a sub-class.' );
|
122 |
+
}
|
123 |
+
|
124 |
+
/**
|
125 |
+
* An internal method that sets all the necessary pagination arguments
|
126 |
+
*
|
127 |
+
* @param array $args An associative array with information about the pagination
|
128 |
+
* @access protected
|
129 |
+
*/
|
130 |
+
function set_pagination_args( $args ) {
|
131 |
+
$args = wp_parse_args( $args, array(
|
132 |
+
'total_items' => 0,
|
133 |
+
'total_pages' => 0,
|
134 |
+
'per_page' => 0,
|
135 |
+
) );
|
136 |
+
|
137 |
+
if ( !$args['total_pages'] && $args['per_page'] > 0 )
|
138 |
+
$args['total_pages'] = ceil( $args['total_items'] / $args['per_page'] );
|
139 |
+
|
140 |
+
// redirect if page number is invalid and headers are not already sent
|
141 |
+
if ( ! headers_sent() && ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) && $args['total_pages'] > 0 && $this->get_pagenum() > $args['total_pages'] ) {
|
142 |
+
wp_redirect( add_query_arg( 'paged', $args['total_pages'] ) );
|
143 |
+
exit;
|
144 |
+
}
|
145 |
+
|
146 |
+
$this->_pagination_args = $args;
|
147 |
+
}
|
148 |
+
|
149 |
+
/**
|
150 |
+
* Access the pagination args
|
151 |
+
*
|
152 |
+
* @since 3.1.0
|
153 |
+
* @access public
|
154 |
+
*
|
155 |
+
* @param string $key
|
156 |
+
* @return array
|
157 |
+
*/
|
158 |
+
function get_pagination_arg( $key ) {
|
159 |
+
if ( 'page' == $key )
|
160 |
+
return $this->get_pagenum();
|
161 |
+
|
162 |
+
if ( isset( $this->_pagination_args[$key] ) )
|
163 |
+
return $this->_pagination_args[$key];
|
164 |
+
}
|
165 |
+
|
166 |
+
/**
|
167 |
+
* Whether the table has items to display or not
|
168 |
+
*
|
169 |
+
* @since 3.1.0
|
170 |
+
* @access public
|
171 |
+
*
|
172 |
+
* @return bool
|
173 |
+
*/
|
174 |
+
function has_items() {
|
175 |
+
return !empty( $this->items );
|
176 |
+
}
|
177 |
+
|
178 |
+
/**
|
179 |
+
* Message to be displayed when there are no items
|
180 |
+
*
|
181 |
+
* @since 3.1.0
|
182 |
+
* @access public
|
183 |
+
*/
|
184 |
+
function no_items() {
|
185 |
+
_e( 'No items found.', 'fw' );
|
186 |
+
}
|
187 |
+
|
188 |
+
/**
|
189 |
+
* Display the search box.
|
190 |
+
*
|
191 |
+
* @since 3.1.0
|
192 |
+
* @access public
|
193 |
+
*
|
194 |
+
* @param string $text The search button text
|
195 |
+
* @param string $input_id The search input id
|
196 |
+
*/
|
197 |
+
function search_box( $text, $input_id ) {
|
198 |
+
if ( empty( $_REQUEST['s'] ) && !$this->has_items() )
|
199 |
+
return;
|
200 |
+
|
201 |
+
$input_id = $input_id . '-search-input';
|
202 |
+
|
203 |
+
if ( ! empty( $_REQUEST['orderby'] ) )
|
204 |
+
echo '<input type="hidden" name="orderby" value="' . esc_attr( $_REQUEST['orderby'] ) . '" />';
|
205 |
+
if ( ! empty( $_REQUEST['order'] ) )
|
206 |
+
echo '<input type="hidden" name="order" value="' . esc_attr( $_REQUEST['order'] ) . '" />';
|
207 |
+
if ( ! empty( $_REQUEST['post_mime_type'] ) )
|
208 |
+
echo '<input type="hidden" name="post_mime_type" value="' . esc_attr( $_REQUEST['post_mime_type'] ) . '" />';
|
209 |
+
if ( ! empty( $_REQUEST['detached'] ) )
|
210 |
+
echo '<input type="hidden" name="detached" value="' . esc_attr( $_REQUEST['detached'] ) . '" />';
|
211 |
+
?>
|
212 |
+
<p class="search-box">
|
213 |
+
<label class="screen-reader-text" for="<?php echo esc_attr($input_id) ?>"><?php echo $text; ?>:</label>
|
214 |
+
<input type="search" id="<?php echo esc_attr($input_id) ?>" name="s" value="<?php _admin_search_query(); ?>" />
|
215 |
+
<?php submit_button( $text, 'button', false, false, array('id' => 'search-submit') ); ?>
|
216 |
+
</p>
|
217 |
+
<?php
|
218 |
+
}
|
219 |
+
|
220 |
+
/**
|
221 |
+
* Get an associative array ( id => link ) with the list
|
222 |
+
* of views available on this table.
|
223 |
+
*
|
224 |
+
* @since 3.1.0
|
225 |
+
* @access protected
|
226 |
+
*
|
227 |
+
* @return array
|
228 |
+
*/
|
229 |
+
function get_views() {
|
230 |
+
return array();
|
231 |
+
}
|
232 |
+
|
233 |
+
/**
|
234 |
+
* Display the list of views available on this table.
|
235 |
+
*
|
236 |
+
* @since 3.1.0
|
237 |
+
* @access public
|
238 |
+
*/
|
239 |
+
function views() {
|
240 |
+
$views = $this->get_views();
|
241 |
+
/**
|
242 |
+
* Filter the list of available list table views.
|
243 |
+
*
|
244 |
+
* The dynamic portion of the hook name, $this->screen->id, refers
|
245 |
+
* to the ID of the current screen, usually a string.
|
246 |
+
*
|
247 |
+
* @since 3.5.0
|
248 |
+
*
|
249 |
+
* @param array $views An array of available list table views.
|
250 |
+
*/
|
251 |
+
$views = apply_filters( "views_{$this->screen->id}", $views );
|
252 |
+
|
253 |
+
if ( empty( $views ) )
|
254 |
+
return;
|
255 |
+
|
256 |
+
echo "<ul class='subsubsub'>\n";
|
257 |
+
foreach ( $views as $class => $view ) {
|
258 |
+
$views[ $class ] = "\t<li class='$class'>$view";
|
259 |
+
}
|
260 |
+
echo implode( " |</li>\n", $views ) . "</li>\n";
|
261 |
+
echo "</ul>";
|
262 |
+
}
|
263 |
+
|
264 |
+
/**
|
265 |
+
* Get an associative array ( option_name => option_title ) with the list
|
266 |
+
* of bulk actions available on this table.
|
267 |
+
*
|
268 |
+
* @since 3.1.0
|
269 |
+
* @access protected
|
270 |
+
*
|
271 |
+
* @return array
|
272 |
+
*/
|
273 |
+
function get_bulk_actions() {
|
274 |
+
return array();
|
275 |
+
}
|
276 |
+
|
277 |
+
/**
|
278 |
+
* Display the bulk actions dropdown.
|
279 |
+
*
|
280 |
+
* @since 3.1.0
|
281 |
+
* @access public
|
282 |
+
*/
|
283 |
+
function bulk_actions() {
|
284 |
+
if ( is_null( $this->_actions ) ) {
|
285 |
+
$no_new_actions = $this->_actions = $this->get_bulk_actions();
|
286 |
+
/**
|
287 |
+
* Filter the list table Bulk Actions drop-down.
|
288 |
+
*
|
289 |
+
* The dynamic portion of the hook name, $this->screen->id, refers
|
290 |
+
* to the ID of the current screen, usually a string.
|
291 |
+
*
|
292 |
+
* This filter can currently only be used to remove bulk actions.
|
293 |
+
*
|
294 |
+
* @since 3.5.0
|
295 |
+
*
|
296 |
+
* @param array $actions An array of the available bulk actions.
|
297 |
+
*/
|
298 |
+
$this->_actions = apply_filters( "bulk_actions-{$this->screen->id}", $this->_actions );
|
299 |
+
$this->_actions = array_intersect_assoc( $this->_actions, $no_new_actions );
|
300 |
+
$two = '';
|
301 |
+
} else {
|
302 |
+
$two = '2';
|
303 |
+
}
|
304 |
+
|
305 |
+
if ( empty( $this->_actions ) )
|
306 |
+
return;
|
307 |
+
|
308 |
+
echo "<select name='action$two'>\n";
|
309 |
+
echo "<option value='-1' selected='selected'>" . __( 'Bulk Actions', 'fw' ) . "</option>\n";
|
310 |
+
|
311 |
+
foreach ( $this->_actions as $name => $title ) {
|
312 |
+
$class = 'edit' == $name ? ' class="hide-if-no-js"' : '';
|
313 |
+
|
314 |
+
echo "\t<option value='$name'$class>$title</option>\n";
|
315 |
+
}
|
316 |
+
|
317 |
+
echo "</select>\n";
|
318 |
+
|
319 |
+
submit_button( __( 'Apply', 'fw' ), 'action', false, false, array( 'id' => "doaction$two" ) );
|
320 |
+
echo "\n";
|
321 |
+
}
|
322 |
+
|
323 |
+
/**
|
324 |
+
* Get the current action selected from the bulk actions dropdown.
|
325 |
+
*
|
326 |
+
* @since 3.1.0
|
327 |
+
* @access public
|
328 |
+
*
|
329 |
+
* @return string|bool The action name or False if no action was selected
|
330 |
+
*/
|
331 |
+
function current_action() {
|
332 |
+
if ( isset( $_REQUEST['action'] ) && -1 != $_REQUEST['action'] )
|
333 |
+
return $_REQUEST['action'];
|
334 |
+
|
335 |
+
if ( isset( $_REQUEST['action2'] ) && -1 != $_REQUEST['action2'] )
|
336 |
+
return $_REQUEST['action2'];
|
337 |
+
|
338 |
+
return false;
|
339 |
+
}
|
340 |
+
|
341 |
+
/**
|
342 |
+
* Generate row actions div
|
343 |
+
*
|
344 |
+
* @since 3.1.0
|
345 |
+
* @access protected
|
346 |
+
*
|
347 |
+
* @param array $actions The list of actions
|
348 |
+
* @param bool $always_visible Whether the actions should be always visible
|
349 |
+
* @return string
|
350 |
+
*/
|
351 |
+
function row_actions( $actions, $always_visible = false ) {
|
352 |
+
$action_count = count( $actions );
|
353 |
+
$i = 0;
|
354 |
+
|
355 |
+
if ( !$action_count )
|
356 |
+
return '';
|
357 |
+
|
358 |
+
$out = '<div class="' . ( $always_visible ? 'row-actions visible' : 'row-actions' ) . '">';
|
359 |
+
foreach ( $actions as $action => $link ) {
|
360 |
+
++$i;
|
361 |
+
( $i == $action_count ) ? $sep = '' : $sep = ' | ';
|
362 |
+
$out .= "<span class='$action'>$link$sep</span>";
|
363 |
+
}
|
364 |
+
$out .= '</div>';
|
365 |
+
|
366 |
+
return $out;
|
367 |
+
}
|
368 |
+
|
369 |
+
/**
|
370 |
+
* Display a monthly dropdown for filtering items
|
371 |
+
*
|
372 |
+
* @since 3.1.0
|
373 |
+
* @access protected
|
374 |
+
*/
|
375 |
+
function months_dropdown( $post_type ) {
|
376 |
+
global $wpdb, $wp_locale;
|
377 |
+
|
378 |
+
$months = $wpdb->get_results( $wpdb->prepare( "
|
379 |
+
SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month
|
380 |
+
FROM $wpdb->posts
|
381 |
+
WHERE post_type = %s
|
382 |
+
ORDER BY post_date DESC
|
383 |
+
", $post_type ) );
|
384 |
+
|
385 |
+
/**
|
386 |
+
* Filter the 'Months' drop-down results.
|
387 |
+
*
|
388 |
+
* @since 3.7.0
|
389 |
+
*
|
390 |
+
* @param object $months The months drop-down query results.
|
391 |
+
* @param string $post_type The post type.
|
392 |
+
*/
|
393 |
+
$months = apply_filters( 'months_dropdown_results', $months, $post_type );
|
394 |
+
|
395 |
+
$month_count = count( $months );
|
396 |
+
|
397 |
+
if ( !$month_count || ( 1 == $month_count && 0 == $months[0]->month ) )
|
398 |
+
return;
|
399 |
+
|
400 |
+
$m = isset( $_GET['m'] ) ? (int) $_GET['m'] : 0;
|
401 |
+
?>
|
402 |
+
<select name='m'>
|
403 |
+
<option<?php selected( $m, 0 ); ?> value='0'><?php _e( 'All dates', 'fw' ); ?></option>
|
404 |
+
<?php
|
405 |
+
foreach ( $months as $arc_row ) {
|
406 |
+
if ( 0 == $arc_row->year )
|
407 |
+
continue;
|
408 |
+
|
409 |
+
$month = zeroise( $arc_row->month, 2 );
|
410 |
+
$year = $arc_row->year;
|
411 |
+
|
412 |
+
printf( "<option %s value='%s'>%s</option>\n",
|
413 |
+
selected( $m, $year . $month, false ),
|
414 |
+
esc_attr( $arc_row->year . $month ),
|
415 |
+
/* translators: 1: month name, 2: 4-digit year */
|
416 |
+
sprintf( __( '%1$s %2$d', 'fw' ), $wp_locale->get_month( $month ), $year )
|
417 |
+
);
|
418 |
+
}
|
419 |
+
?>
|
420 |
+
</select>
|
421 |
+
<?php
|
422 |
+
}
|
423 |
+
|
424 |
+
/**
|
425 |
+
* Display a view switcher
|
426 |
+
*
|
427 |
+
* @since 3.1.0
|
428 |
+
* @access protected
|
429 |
+
*/
|
430 |
+
function view_switcher( $current_mode ) {
|
431 |
+
$modes = array(
|
432 |
+
'list' => __( 'List View', 'fw' ),
|
433 |
+
'excerpt' => __( 'Excerpt View', 'fw' )
|
434 |
+
);
|
435 |
+
|
436 |
+
?>
|
437 |
+
<input type="hidden" name="mode" value="<?php echo esc_attr( $current_mode ); ?>" />
|
438 |
+
<div class="view-switch">
|
439 |
+
<?php
|
440 |
+
foreach ( $modes as $mode => $title ) {
|
441 |
+
$class = ( $current_mode == $mode ) ? 'class="current"' : '';
|
442 |
+
echo "<a href='" . esc_url( add_query_arg( 'mode', $mode, $_SERVER['REQUEST_URI'] ) ) . "' $class><img id='view-switch-$mode' src='" . esc_url( includes_url( 'images/blank.gif' ) ) . "' width='20' height='20' title='$title' alt='$title' /></a>\n";
|
443 |
+
}
|
444 |
+
?>
|
445 |
+
</div>
|
446 |
+
<?php
|
447 |
+
}
|
448 |
+
|
449 |
+
/**
|
450 |
+
* Display a comment count bubble
|
451 |
+
*
|
452 |
+
* @since 3.1.0
|
453 |
+
* @access protected
|
454 |
+
*
|
455 |
+
* @param int $post_id
|
456 |
+
* @param int $pending_comments
|
457 |
+
*/
|
458 |
+
function comments_bubble( $post_id, $pending_comments ) {
|
459 |
+
$pending_phrase = sprintf( __( '%s pending', 'fw' ), number_format( $pending_comments ) );
|
460 |
+
|
461 |
+
if ( $pending_comments )
|
462 |
+
echo '<strong>';
|
463 |
+
|
464 |
+
echo "<a href='" . esc_url( add_query_arg( 'p', $post_id, admin_url( 'edit-comments.php' ) ) ) . "' title='" . esc_attr( $pending_phrase ) . "' class='post-com-count'><span class='comment-count'>" . number_format_i18n( get_comments_number() ) . "</span></a>";
|
465 |
+
|
466 |
+
if ( $pending_comments )
|
467 |
+
echo '</strong>';
|
468 |
+
}
|
469 |
+
|
470 |
+
/**
|
471 |
+
* Get the current page number
|
472 |
+
*
|
473 |
+
* @since 3.1.0
|
474 |
+
* @access protected
|
475 |
+
*
|
476 |
+
* @return int
|
477 |
+
*/
|
478 |
+
function get_pagenum() {
|
479 |
+
$pagenum = isset( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : 0;
|
480 |
+
|
481 |
+
if( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] )
|
482 |
+
$pagenum = $this->_pagination_args['total_pages'];
|
483 |
+
|
484 |
+
return max( 1, $pagenum );
|
485 |
+
}
|
486 |
+
|
487 |
+
/**
|
488 |
+
* Get number of items to display on a single page
|
489 |
+
*
|
490 |
+
* @since 3.1.0
|
491 |
+
* @access protected
|
492 |
+
*
|
493 |
+
* @return int
|
494 |
+
*/
|
495 |
+
function get_items_per_page( $option, $default = 20 ) {
|
496 |
+
$per_page = (int) get_user_option( $option );
|
497 |
+
if ( empty( $per_page ) || $per_page < 1 )
|
498 |
+
$per_page = $default;
|
499 |
+
|
500 |
+
/**
|
501 |
+
* Filter the number of items to be displayed on each page of the list table.
|
502 |
+
*
|
503 |
+
* The dynamic hook name, $option, refers to the per page option depending
|
504 |
+
* on the type of list table in use. Possible values may include:
|
505 |
+
* 'edit_comments_per_page', 'sites_network_per_page', 'site_themes_network_per_page',
|
506 |
+
* 'themes_netework_per_page', 'users_network_per_page', 'edit_{$post_type}', etc.
|
507 |
+
*
|
508 |
+
* @since 2.9.0
|
509 |
+
*
|
510 |
+
* @param int $per_page Number of items to be displayed. Default 20.
|
511 |
+
*/
|
512 |
+
return (int) apply_filters( $option, $per_page );
|
513 |
+
}
|
514 |
+
|
515 |
+
/**
|
516 |
+
* Display the pagination.
|
517 |
+
*
|
518 |
+
* @since 3.1.0
|
519 |
+
* @access protected
|
520 |
+
*/
|
521 |
+
function pagination( $which ) {
|
522 |
+
if ( empty( $this->_pagination_args ) )
|
523 |
+
return;
|
524 |
+
|
525 |
+
extract( $this->_pagination_args, EXTR_SKIP );
|
526 |
+
|
527 |
+
$output = '<span class="displaying-num">' . sprintf( _n( '1 item', '%s items', $total_items ), number_format_i18n( $total_items ) ) . '</span>';
|
528 |
+
|
529 |
+
$current = $this->get_pagenum();
|
530 |
+
|
531 |
+
$current_url = fw_current_url();
|
532 |
+
|
533 |
+
$current_url = remove_query_arg( array( 'hotkeys_highlight_last', 'hotkeys_highlight_first' ), $current_url );
|
534 |
+
|
535 |
+
$page_links = array();
|
536 |
+
|
537 |
+
$disable_first = $disable_last = '';
|
538 |
+
if ( $current == 1 )
|
539 |
+
$disable_first = ' disabled';
|
540 |
+
if ( $current == $total_pages )
|
541 |
+
$disable_last = ' disabled';
|
542 |
+
|
543 |
+
$page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
|
544 |
+
'first-page' . $disable_first,
|
545 |
+
esc_attr__( 'Go to the first page', 'fw' ),
|
546 |
+
esc_url( remove_query_arg( 'paged', $current_url ) ),
|
547 |
+
'«'
|
548 |
+
);
|
549 |
+
|
550 |
+
$page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
|
551 |
+
'prev-page' . $disable_first,
|
552 |
+
esc_attr__( 'Go to the previous page', 'fw' ),
|
553 |
+
esc_url( add_query_arg( 'paged', max( 1, $current-1 ), $current_url ) ),
|
554 |
+
'‹'
|
555 |
+
);
|
556 |
+
|
557 |
+
if ( 'bottom' == $which )
|
558 |
+
$html_current_page = $current;
|
559 |
+
else
|
560 |
+
$html_current_page = sprintf( "<input class='current-page' title='%s' type='text' name='paged' value='%s' size='%d' />",
|
561 |
+
esc_attr__( 'Current page', 'fw' ),
|
562 |
+
$current,
|
563 |
+
strlen( $total_pages )
|
564 |
+
);
|
565 |
+
|
566 |
+
$html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) );
|
567 |
+
$page_links[] = '<span class="paging-input">' . sprintf( _x( '%1$s of %2$s', 'paging', 'fw' ), $html_current_page, $html_total_pages ) . '</span>';
|
568 |
+
|
569 |
+
$page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
|
570 |
+
'next-page' . $disable_last,
|
571 |
+
esc_attr__( 'Go to the next page', 'fw' ),
|
572 |
+
esc_url( add_query_arg( 'paged', min( $total_pages, $current+1 ), $current_url ) ),
|
573 |
+
'›'
|
574 |
+
);
|
575 |
+
|
576 |
+
$page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
|
577 |
+
'last-page' . $disable_last,
|
578 |
+
esc_attr__( 'Go to the last page', 'fw' ),
|
579 |
+
esc_url( add_query_arg( 'paged', $total_pages, $current_url ) ),
|
580 |
+
'»'
|
581 |
+
);
|
582 |
+
|
583 |
+
$pagination_links_class = 'pagination-links';
|
584 |
+
if ( ! empty( $infinite_scroll ) )
|
585 |
+
$pagination_links_class = ' hide-if-js';
|
586 |
+
$output .= "\n<span class='$pagination_links_class'>" . join( "\n", $page_links ) . '</span>';
|
587 |
+
|
588 |
+
if ( $total_pages )
|
589 |
+
$page_class = $total_pages < 2 ? ' one-page' : '';
|
590 |
+
else
|
591 |
+
$page_class = ' no-pages';
|
592 |
+
|
593 |
+
$this->_pagination = "<div class='tablenav-pages{$page_class}'>$output</div>";
|
594 |
+
|
595 |
+
echo $this->_pagination;
|
596 |
+
}
|
597 |
+
|
598 |
+
/**
|
599 |
+
* Get a list of columns. The format is:
|
600 |
+
* 'internal-name' => 'Title'
|
601 |
+
*
|
602 |
+
* @since 3.1.0
|
603 |
+
* @access protected
|
604 |
+
* @abstract
|
605 |
+
*
|
606 |
+
* @return array
|
607 |
+
*/
|
608 |
+
function get_columns() {
|
609 |
+
die( 'function WP_List_Table::get_columns() must be over-ridden in a sub-class.' );
|
610 |
+
}
|
611 |
+
|
612 |
+
/**
|
613 |
+
* Get a list of sortable columns. The format is:
|
614 |
+
* 'internal-name' => 'orderby'
|
615 |
+
* or
|
616 |
+
* 'internal-name' => array( 'orderby', true )
|
617 |
+
*
|
618 |
+
* The second format will make the initial sorting order be descending
|
619 |
+
*
|
620 |
+
* @since 3.1.0
|
621 |
+
* @access protected
|
622 |
+
*
|
623 |
+
* @return array
|
624 |
+
*/
|
625 |
+
function get_sortable_columns() {
|
626 |
+
return array();
|
627 |
+
}
|
628 |
+
|
629 |
+
/**
|
630 |
+
* Get a list of all, hidden and sortable columns, with filter applied
|
631 |
+
*
|
632 |
+
* @since 3.1.0
|
633 |
+
* @access protected
|
634 |
+
*
|
635 |
+
* @return array
|
636 |
+
*/
|
637 |
+
function get_column_info() {
|
638 |
+
if ( isset( $this->_column_headers ) )
|
639 |
+
return $this->_column_headers;
|
640 |
+
|
641 |
+
$columns = get_column_headers( $this->screen );
|
642 |
+
$hidden = get_hidden_columns( $this->screen );
|
643 |
+
|
644 |
+
$sortable_columns = $this->get_sortable_columns();
|
645 |
+
/**
|
646 |
+
* Filter the list table sortable columns for a specific screen.
|
647 |
+
*
|
648 |
+
* The dynamic portion of the hook name, $this->screen->id, refers
|
649 |
+
* to the ID of the current screen, usually a string.
|
650 |
+
*
|
651 |
+
* @since 3.5.0
|
652 |
+
*
|
653 |
+
* @param array $sortable_columns An array of sortable columns.
|
654 |
+
*/
|
655 |
+
$_sortable = apply_filters( "manage_{$this->screen->id}_sortable_columns", $sortable_columns );
|
656 |
+
|
657 |
+
$sortable = array();
|
658 |
+
foreach ( $_sortable as $id => $data ) {
|
659 |
+
if ( empty( $data ) )
|
660 |
+
continue;
|
661 |
+
|
662 |
+
$data = (array) $data;
|
663 |
+
if ( !isset( $data[1] ) )
|
664 |
+
$data[1] = false;
|
665 |
+
|
666 |
+
$sortable[$id] = $data;
|
667 |
+
}
|
668 |
+
|
669 |
+
$this->_column_headers = array( $columns, $hidden, $sortable );
|
670 |
+
|
671 |
+
return $this->_column_headers;
|
672 |
+
}
|
673 |
+
|
674 |
+
/**
|
675 |
+
* Return number of visible columns
|
676 |
+
*
|
677 |
+
* @since 3.1.0
|
678 |
+
* @access public
|
679 |
+
*
|
680 |
+
* @return int
|
681 |
+
*/
|
682 |
+
function get_column_count() {
|
683 |
+
list ( $columns, $hidden ) = $this->get_column_info();
|
684 |
+
$hidden = array_intersect( array_keys( $columns ), array_filter( $hidden ) );
|
685 |
+
return count( $columns ) - count( $hidden );
|
686 |
+
}
|
687 |
+
|
688 |
+
/**
|
689 |
+
* Print column headers, accounting for hidden and sortable columns.
|
690 |
+
*
|
691 |
+
* @since 3.1.0
|
692 |
+
* @access protected
|
693 |
+
*
|
694 |
+
* @param bool $with_id Whether to set the id attribute or not
|
695 |
+
*/
|
696 |
+
function print_column_headers( $with_id = true ) {
|
697 |
+
list( $columns, $hidden, $sortable ) = $this->get_column_info();
|
698 |
+
|
699 |
+
$current_url = fw_current_url();
|
700 |
+
$current_url = remove_query_arg( 'paged', $current_url );
|
701 |
+
|
702 |
+
if ( isset( $_GET['orderby'] ) )
|
703 |
+
$current_orderby = $_GET['orderby'];
|
704 |
+
else
|
705 |
+
$current_orderby = '';
|
706 |
+
|
707 |
+
if ( isset( $_GET['order'] ) && 'desc' == $_GET['order'] )
|
708 |
+
$current_order = 'desc';
|
709 |
+
else
|
710 |
+
$current_order = 'asc';
|
711 |
+
|
712 |
+
if ( ! empty( $columns['cb'] ) ) {
|
713 |
+
static $cb_counter = 1;
|
714 |
+
$columns['cb'] = '<label class="screen-reader-text" for="cb-select-all-' . $cb_counter . '">' . __( 'Select All', 'fw' ) . '</label>'
|
715 |
+
. '<input id="cb-select-all-' . $cb_counter . '" type="checkbox" />';
|
716 |
+
$cb_counter++;
|
717 |
+
}
|
718 |
+
|
719 |
+
foreach ( $columns as $column_key => $column_display_name ) {
|
720 |
+
$class = array( 'manage-column', "column-$column_key" );
|
721 |
+
|
722 |
+
$style = '';
|
723 |
+
if ( in_array( $column_key, $hidden ) )
|
724 |
+
$style = 'display:none;';
|
725 |
+
|
726 |
+
$style = ' style="' . $style . '"';
|
727 |
+
|
728 |
+
if ( 'cb' == $column_key )
|
729 |
+
$class[] = 'check-column';
|
730 |
+
elseif ( in_array( $column_key, array( 'posts', 'comments', 'links' ) ) )
|
731 |
+
$class[] = 'num';
|
732 |
+
|
733 |
+
if ( isset( $sortable[$column_key] ) ) {
|
734 |
+
list( $orderby, $desc_first ) = $sortable[$column_key];
|
735 |
+
|
736 |
+
if ( $current_orderby == $orderby ) {
|
737 |
+
$order = 'asc' == $current_order ? 'desc' : 'asc';
|
738 |
+
$class[] = 'sorted';
|
739 |
+
$class[] = $current_order;
|
740 |
+
} else {
|
741 |
+
$order = $desc_first ? 'desc' : 'asc';
|
742 |
+
$class[] = 'sortable';
|
743 |
+
$class[] = $desc_first ? 'asc' : 'desc';
|
744 |
+
}
|
745 |
+
|
746 |
+
$column_display_name = '<a href="' . esc_url( add_query_arg( compact( 'orderby', 'order' ), $current_url ) ) . '"><span>' . $column_display_name . '</span><span class="sorting-indicator"></span></a>';
|
747 |
+
}
|
748 |
+
|
749 |
+
$id = $with_id ? "id='$column_key'" : '';
|
750 |
+
|
751 |
+
if ( !empty( $class ) )
|
752 |
+
$class = "class='" . join( ' ', $class ) . "'";
|
753 |
+
|
754 |
+
echo "<th scope='col' $id $class $style>$column_display_name</th>";
|
755 |
+
}
|
756 |
+
}
|
757 |
+
|
758 |
+
/**
|
759 |
+
* Display the table
|
760 |
+
*
|
761 |
+
* @since 3.1.0
|
762 |
+
* @access public
|
763 |
+
*/
|
764 |
+
function display() {
|
765 |
+
extract( $this->_args );
|
766 |
+
|
767 |
+
$this->display_tablenav( 'top' );
|
768 |
+
|
769 |
+
?>
|
770 |
+
<table class="wp-list-table <?php echo implode( ' ', $this->get_table_classes() ); ?>">
|
771 |
+
<thead>
|
772 |
+
<tr>
|
773 |
+
<?php $this->print_column_headers(); ?>
|
774 |
+
</tr>
|
775 |
+
</thead>
|
776 |
+
|
777 |
+
<tfoot>
|
778 |
+
<tr>
|
779 |
+
<?php $this->print_column_headers( false ); ?>
|
780 |
+
</tr>
|
781 |
+
</tfoot>
|
782 |
+
|
783 |
+
<tbody id="the-list"<?php if ( $singular ) echo " data-wp-lists='list:$singular'"; ?>>
|
784 |
+
<?php $this->display_rows_or_placeholder(); ?>
|
785 |
+
</tbody>
|
786 |
+
</table>
|
787 |
+
<?php
|
788 |
+
$this->display_tablenav( 'bottom' );
|
789 |
+
}
|
790 |
+
|
791 |
+
/**
|
792 |
+
* Get a list of CSS classes for the <table> tag
|
793 |
+
*
|
794 |
+
* @since 3.1.0
|
795 |
+
* @access protected
|
796 |
+
*
|
797 |
+
* @return array
|
798 |
+
*/
|
799 |
+
function get_table_classes() {
|
800 |
+
return array( 'widefat', 'fixed', $this->_args['plural'] );
|
801 |
+
}
|
802 |
+
|
803 |
+
/**
|
804 |
+
* Generate the table navigation above or below the table
|
805 |
+
*
|
806 |
+
* @since 3.1.0
|
807 |
+
* @access protected
|
808 |
+
*/
|
809 |
+
function display_tablenav( $which ) {
|
810 |
+
if ( 'top' == $which )
|
811 |
+
wp_nonce_field( 'bulk-' . $this->_args['plural'] );
|
812 |
+
?>
|
813 |
+
<div class="tablenav <?php echo esc_attr( $which ); ?>">
|
814 |
+
|
815 |
+
<div class="alignleft actions bulkactions">
|
816 |
+
<?php $this->bulk_actions(); ?>
|
817 |
+
</div>
|
818 |
+
<?php
|
819 |
+
$this->extra_tablenav( $which );
|
820 |
+
$this->pagination( $which );
|
821 |
+
?>
|
822 |
+
|
823 |
+
<br class="clear" />
|
824 |
+
</div>
|
825 |
+
<?php
|
826 |
+
}
|
827 |
+
|
828 |
+
/**
|
829 |
+
* Extra controls to be displayed between bulk actions and pagination
|
830 |
+
*
|
831 |
+
* @since 3.1.0
|
832 |
+
* @access protected
|
833 |
+
*/
|
834 |
+
function extra_tablenav( $which ) {}
|
835 |
+
|
836 |
+
/**
|
837 |
+
* Generate the <tbody> part of the table
|
838 |
+
*
|
839 |
+
* @since 3.1.0
|
840 |
+
* @access protected
|
841 |
+
*/
|
842 |
+
function display_rows_or_placeholder() {
|
843 |
+
if ( $this->has_items() ) {
|
844 |
+
$this->display_rows();
|
845 |
+
} else {
|
846 |
+
list( $columns, $hidden ) = $this->get_column_info();
|
847 |
+
echo '<tr class="no-items"><td class="colspanchange" colspan="' . $this->get_column_count() . '">';
|
848 |
+
$this->no_items();
|
849 |
+
echo '</td></tr>';
|
850 |
+
}
|
851 |
+
}
|
852 |
+
|
853 |
+
/**
|
854 |
+
* Generate the table rows
|
855 |
+
*
|
856 |
+
* @since 3.1.0
|
857 |
+
* @access protected
|
858 |
+
*/
|
859 |
+
function display_rows() {
|
860 |
+
foreach ( $this->items as $item )
|
861 |
+
$this->single_row( $item );
|
862 |
+
}
|
863 |
+
|
864 |
+
/**
|
865 |
+
* Generates content for a single row of the table
|
866 |
+
*
|
867 |
+
* @since 3.1.0
|
868 |
+
* @access protected
|
869 |
+
*
|
870 |
+
* @param object $item The current item
|
871 |
+
*/
|
872 |
+
function single_row( $item ) {
|
873 |
+
static $row_class = '';
|
874 |
+
$row_class = ( $row_class == '' ? ' class="alternate"' : '' );
|
875 |
+
|
876 |
+
echo '<tr' . $row_class . '>';
|
877 |
+
$this->single_row_columns( $item );
|
878 |
+
echo '</tr>';
|
879 |
+
}
|
880 |
+
|
881 |
+
/**
|
882 |
+
* Generates the columns for a single row of the table
|
883 |
+
*
|
884 |
+
* @since 3.1.0
|
885 |
+
* @access protected
|
886 |
+
*
|
887 |
+
* @param object $item The current item
|
888 |
+
*/
|
889 |
+
function single_row_columns( $item ) {
|
890 |
+
list( $columns, $hidden ) = $this->get_column_info();
|
891 |
+
|
892 |
+
foreach ( $columns as $column_name => $column_display_name ) {
|
893 |
+
$class = "class='$column_name column-$column_name'";
|
894 |
+
|
895 |
+
$style = '';
|
896 |
+
if ( in_array( $column_name, $hidden ) )
|
897 |
+
$style = ' style="display:none;"';
|
898 |
+
|
899 |
+
$attributes = "$class$style";
|
900 |
+
|
901 |
+
if ( 'cb' == $column_name ) {
|
902 |
+
echo '<th scope="row" class="check-column">';
|
903 |
+
echo $this->column_cb( $item );
|
904 |
+
echo '</th>';
|
905 |
+
}
|
906 |
+
elseif ( method_exists( $this, 'column_' . $column_name ) ) {
|
907 |
+
echo "<td $attributes>";
|
908 |
+
echo call_user_func( array( $this, 'column_' . $column_name ), $item );
|
909 |
+
echo "</td>";
|
910 |
+
}
|
911 |
+
else {
|
912 |
+
echo "<td $attributes>";
|
913 |
+
echo $this->column_default( $item, $column_name );
|
914 |
+
echo "</td>";
|
915 |
+
}
|
916 |
+
}
|
917 |
+
}
|
918 |
+
|
919 |
+
/**
|
920 |
+
* Handle an incoming ajax request (called from admin-ajax.php)
|
921 |
+
*
|
922 |
+
* @since 3.1.0
|
923 |
+
* @access public
|
924 |
+
*/
|
925 |
+
function ajax_response() {
|
926 |
+
$this->prepare_items();
|
927 |
+
|
928 |
+
extract( $this->_args );
|
929 |
+
extract( $this->_pagination_args, EXTR_SKIP );
|
930 |
+
|
931 |
+
ob_start();
|
932 |
+
if ( ! empty( $_REQUEST['no_placeholder'] ) )
|
933 |
+
$this->display_rows();
|
934 |
+
else
|
935 |
+
$this->display_rows_or_placeholder();
|
936 |
+
|
937 |
+
$rows = ob_get_clean();
|
938 |
+
|
939 |
+
$response = array( 'rows' => $rows );
|
940 |
+
|
941 |
+
if ( isset( $total_items ) )
|
942 |
+
$response['total_items_i18n'] = sprintf( _n( '1 item', '%s items', $total_items ), number_format_i18n( $total_items ) );
|
943 |
+
|
944 |
+
if ( isset( $total_pages ) ) {
|
945 |
+
$response['total_pages'] = $total_pages;
|
946 |
+
$response['total_pages_i18n'] = number_format_i18n( $total_pages );
|
947 |
+
}
|
948 |
+
|
949 |
+
die( json_encode( $response ) );
|
950 |
+
}
|
951 |
+
|
952 |
+
/**
|
953 |
+
* Send required variables to JavaScript land
|
954 |
+
*
|
955 |
+
* @access private
|
956 |
+
*/
|
957 |
+
function _js_vars() {
|
958 |
+
$args = array(
|
959 |
+
'class' => get_class( $this ),
|
960 |
+
'screen' => array(
|
961 |
+
'id' => $this->screen->id,
|
962 |
+
'base' => $this->screen->base,
|
963 |
+
)
|
964 |
+
);
|
965 |
+
|
966 |
+
printf( "<script type='text/javascript'>list_args = %s;</script>\n", json_encode( $args ) );
|
967 |
+
}
|
968 |
+
}
|
framework/helpers/class-fw-wp-meta.php
CHANGED
@@ -1,113 +1,113 @@
|
|
1 |
-
<?php if ( ! defined( 'FW' ) ) {
|
2 |
-
die( 'Forbidden' );
|
3 |
-
}
|
4 |
-
|
5 |
-
/**
|
6 |
-
*
|
7 |
-
* Features:
|
8 |
-
* - Works with "multi keys"
|
9 |
-
* - The value is stored in two formats: original and prepared.
|
10 |
-
* Prepared is used for frontend because it is translated (+ maybe other preparations in the future)
|
11 |
-
*/
|
12 |
-
class FW_WP_Meta {
|
13 |
-
/**
|
14 |
-
* Store all this class data in cache within this key
|
15 |
-
* @var string
|
16 |
-
*/
|
17 |
-
private static $cache_key = 'wp_meta';
|
18 |
-
|
19 |
-
/**
|
20 |
-
* @param string $meta_type
|
21 |
-
* @param int $object_id
|
22 |
-
* @param string $multi_key 'abc' or 'ab/c/def'
|
23 |
-
* @param array|string|int|bool $set_value
|
24 |
-
*/
|
25 |
-
public static function set( $meta_type, $object_id, $multi_key, $set_value ) {
|
26 |
-
if ( empty( $multi_key ) ) {
|
27 |
-
trigger_error( 'Key not specified', E_USER_WARNING );
|
28 |
-
|
29 |
-
return;
|
30 |
-
}
|
31 |
-
|
32 |
-
$multi_key = explode( '/', $multi_key );
|
33 |
-
$key = array_shift( $multi_key );
|
34 |
-
$multi_key = implode( '/', $multi_key );
|
35 |
-
|
36 |
-
/*
|
37 |
-
// Make sure meta is added to the post, not a revision.
|
38 |
-
// fixme: why make sure? but I want to set post meta for a revision, how to do that?
|
39 |
-
if ( $meta_type === 'post' && $the_post = wp_is_post_revision( $object_id ) ) {
|
40 |
-
$object_id = $the_post;
|
41 |
-
}
|
42 |
-
*/
|
43 |
-
|
44 |
-
$cache_key = self::$cache_key . '/' . $meta_type . '/' . $object_id . '/' . $key;
|
45 |
-
|
46 |
-
if ( empty( $multi_key ) && $multi_key !== '0' ) {
|
47 |
-
/** Replace entire meta */
|
48 |
-
|
49 |
-
fw_update_metadata( $meta_type, $object_id, $key, $set_value );
|
50 |
-
|
51 |
-
FW_Cache::del( self::$cache_key );
|
52 |
-
|
53 |
-
} else {
|
54 |
-
/** Change only specified key */
|
55 |
-
|
56 |
-
$values = array();
|
57 |
-
|
58 |
-
$values['original'] = self::get( $meta_type, $object_id, $key, true );
|
59 |
-
$values['prepared'] = self::get( $meta_type, $object_id, $key, false );
|
60 |
-
|
61 |
-
fw_aks( $multi_key, $set_value, $values['original'] );
|
62 |
-
fw_aks( $multi_key, fw_prepare_option_value( $set_value ), $values['prepared'] );
|
63 |
-
|
64 |
-
FW_Cache::set( $cache_key, $values );
|
65 |
-
|
66 |
-
fw_update_metadata( $meta_type, $object_id, $key, $values['original'] );
|
67 |
-
}
|
68 |
-
}
|
69 |
-
|
70 |
-
/**
|
71 |
-
* @param string $meta_type
|
72 |
-
* @param int $object_id
|
73 |
-
* @param string $multi_key 'abc' or 'ab/c/def'
|
74 |
-
* @param null|mixed $default_value If no option found in the database, this value will be returned
|
75 |
-
* @param bool|null $get_original_value Original value from db, no changes and translations
|
76 |
-
*
|
77 |
-
* @return mixed|null
|
78 |
-
*/
|
79 |
-
public static function get( $meta_type, $object_id, $multi_key, $default_value = null, $get_original_value = null ) {
|
80 |
-
if ( $get_original_value === null ) {
|
81 |
-
$get_original_value = is_admin();
|
82 |
-
}
|
83 |
-
|
84 |
-
if ( empty( $multi_key ) ) {
|
85 |
-
trigger_error( 'Key not specified', E_USER_WARNING );
|
86 |
-
|
87 |
-
return null;
|
88 |
-
}
|
89 |
-
|
90 |
-
$multi_key = explode( '/', $multi_key );
|
91 |
-
$key = array_shift( $multi_key );
|
92 |
-
$multi_key = implode( '/', $multi_key );
|
93 |
-
|
94 |
-
$cache_key = self::$cache_key . '/' . $meta_type . '/' . $object_id . '/' . $key;
|
95 |
-
|
96 |
-
try {
|
97 |
-
$values = FW_Cache::get( $cache_key );
|
98 |
-
} catch ( FW_Cache_Not_Found_Exception $e ) {
|
99 |
-
$values = array();
|
100 |
-
|
101 |
-
$values['original'] = get_metadata( $meta_type, $object_id, $key, true );
|
102 |
-
$values['prepared'] = fw_prepare_option_value( $values['original'] );
|
103 |
-
|
104 |
-
FW_Cache::set( $cache_key, $values );
|
105 |
-
}
|
106 |
-
|
107 |
-
return fw_akg(
|
108 |
-
($get_original_value ? 'original' : 'prepared') . (empty($multi_key) ? '' : '/'. $multi_key),
|
109 |
-
$values,
|
110 |
-
$default_value
|
111 |
-
);
|
112 |
-
}
|
113 |
-
}
|
1 |
+
<?php if ( ! defined( 'FW' ) ) {
|
2 |
+
die( 'Forbidden' );
|
3 |
+
}
|
4 |
+
|
5 |
+
/**
|
6 |
+
*
|
7 |
+
* Features:
|
8 |
+
* - Works with "multi keys"
|
9 |
+
* - The value is stored in two formats: original and prepared.
|
10 |
+
* Prepared is used for frontend because it is translated (+ maybe other preparations in the future)
|
11 |
+
*/
|
12 |
+
class FW_WP_Meta {
|
13 |
+
/**
|
14 |
+
* Store all this class data in cache within this key
|
15 |
+
* @var string
|
16 |
+
*/
|
17 |
+
private static $cache_key = 'wp_meta';
|
18 |
+
|
19 |
+
/**
|
20 |
+
* @param string $meta_type
|
21 |
+
* @param int $object_id
|
22 |
+
* @param string $multi_key 'abc' or 'ab/c/def'
|
23 |
+
* @param array|string|int|bool $set_value
|
24 |
+
*/
|
25 |
+
public static function set( $meta_type, $object_id, $multi_key, $set_value ) {
|
26 |
+
if ( empty( $multi_key ) ) {
|
27 |
+
trigger_error( 'Key not specified', E_USER_WARNING );
|
28 |
+
|
29 |
+
return;
|
30 |
+
}
|
31 |
+
|
32 |
+
$multi_key = explode( '/', $multi_key );
|
33 |
+
$key = array_shift( $multi_key );
|
34 |
+
$multi_key = implode( '/', $multi_key );
|
35 |
+
|
36 |
+
/*
|
37 |
+
// Make sure meta is added to the post, not a revision.
|
38 |
+
// fixme: why make sure? but I want to set post meta for a revision, how to do that?
|
39 |
+
if ( $meta_type === 'post' && $the_post = wp_is_post_revision( $object_id ) ) {
|
40 |
+
$object_id = $the_post;
|
41 |
+
}
|
42 |
+
*/
|
43 |
+
|
44 |
+
$cache_key = self::$cache_key . '/' . $meta_type . '/' . $object_id . '/' . $key;
|
45 |
+
|
46 |
+
if ( empty( $multi_key ) && $multi_key !== '0' ) {
|
47 |
+
/** Replace entire meta */
|
48 |
+
|
49 |
+
fw_update_metadata( $meta_type, $object_id, $key, $set_value );
|
50 |
+
|
51 |
+
FW_Cache::del( self::$cache_key );
|
52 |
+
|
53 |
+
} else {
|
54 |
+
/** Change only specified key */
|
55 |
+
|
56 |
+
$values = array();
|
57 |
+
|
58 |
+
$values['original'] = self::get( $meta_type, $object_id, $key, true );
|
59 |
+
$values['prepared'] = self::get( $meta_type, $object_id, $key, false );
|
60 |
+
|
61 |
+
fw_aks( $multi_key, $set_value, $values['original'] );
|
62 |
+
fw_aks( $multi_key, fw_prepare_option_value( $set_value ), $values['prepared'] );
|
63 |
+
|
64 |
+
FW_Cache::set( $cache_key, $values );
|
65 |
+
|
66 |
+
fw_update_metadata( $meta_type, $object_id, $key, $values['original'] );
|
67 |
+
}
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* @param string $meta_type
|
72 |
+
* @param int $object_id
|
73 |
+
* @param string $multi_key 'abc' or 'ab/c/def'
|
74 |
+
* @param null|mixed $default_value If no option found in the database, this value will be returned
|
75 |
+
* @param bool|null $get_original_value Original value from db, no changes and translations
|
76 |
+
*
|
77 |
+
* @return mixed|null
|
78 |
+
*/
|
79 |
+
public static function get( $meta_type, $object_id, $multi_key, $default_value = null, $get_original_value = null ) {
|
80 |
+
if ( $get_original_value === null ) {
|
81 |
+
$get_original_value = is_admin();
|
82 |
+
}
|
83 |
+
|
84 |
+
if ( empty( $multi_key ) ) {
|
85 |
+
trigger_error( 'Key not specified', E_USER_WARNING );
|
86 |
+
|
87 |
+
return null;
|
88 |
+
}
|
89 |
+
|
90 |
+
$multi_key = explode( '/', $multi_key );
|
91 |
+
$key = array_shift( $multi_key );
|
92 |
+
$multi_key = implode( '/', $multi_key );
|
93 |
+
|
94 |
+
$cache_key = self::$cache_key . '/' . $meta_type . '/' . $object_id . '/' . $key;
|
95 |
+
|
96 |
+
try {
|
97 |
+
$values = FW_Cache::get( $cache_key );
|
98 |
+
} catch ( FW_Cache_Not_Found_Exception $e ) {
|
99 |
+
$values = array();
|
100 |
+
|
101 |
+
$values['original'] = get_metadata( $meta_type, $object_id, $key, true );
|
102 |
+
$values['prepared'] = fw_prepare_option_value( $values['original'] );
|
103 |
+
|
104 |
+
FW_Cache::set( $cache_key, $values );
|
105 |
+
}
|
106 |
+
|
107 |
+
return fw_akg(
|
108 |
+
($get_original_value ? 'original' : 'prepared') . (empty($multi_key) ? '' : '/'. $multi_key),
|
109 |
+
$values,
|
110 |
+
$default_value
|
111 |
+
);
|
112 |
+
}
|
113 |
+
}
|
framework/helpers/class-fw-wp-option.php
CHANGED
@@ -1,84 +1,84 @@
|
|
1 |
-
<?php if (!defined('FW')) die('Forbidden');
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Alternative to WordPress get_option() and update_option() functions
|
5 |
-
*
|
6 |
-
* Features:
|
7 |
-
* - Works with "multi keys"
|
8 |
-
* - The value is stored in two formats: original and prepared.
|
9 |
-
* Prepared is used for frontend because it is translated (+ maybe other preparations in the future)
|
10 |
-
*/
|
11 |
-
class FW_WP_Option
|
12 |
-
{
|
13 |
-
/**
|
14 |
-
* Store all this class data in cache within this key
|
15 |
-
* @var string
|
16 |
-
*/
|
17 |
-
private static $cache_key = 'wp_option';
|
18 |
-
|
19 |
-
/**
|
20 |
-
* @param string $option_name
|
21 |
-
* @param string|null $specific_multi_key 'ab/c/def'
|
22 |
-
* @param null|mixed $default_value If no option found in the database, this value will be returned
|
23 |
-
* @param bool|null $get_original_value Original value from db, no changes and translations
|
24 |
-
* @return mixed|null
|
25 |
-
*/
|
26 |
-
public static function get($option_name, $specific_multi_key = null, $default_value = null, $get_original_value = null)
|
27 |
-
{
|
28 |
-
if ($get_original_value === null) {
|
29 |
-
$get_original_value = is_admin();
|
30 |
-
}
|
31 |
-
|
32 |
-
$cache_key = self::$cache_key .'/'. $option_name;
|
33 |
-
|
34 |
-
try {
|
35 |
-
$values = FW_Cache::get($cache_key);
|
36 |
-
} catch (FW_Cache_Not_Found_Exception $e) {
|
37 |
-
$values = array();
|
38 |
-
|
39 |
-
$values['original'] = get_option($option_name, null);
|
40 |
-
$values['prepared'] = fw_prepare_option_value($values['original']);
|
41 |
-
|
42 |
-
FW_Cache::set($cache_key, $values);
|
43 |
-
}
|
44 |
-
|
45 |
-
return fw_akg(
|
46 |
-
($get_original_value ? 'original' : 'prepared') . (empty($specific_multi_key) ? '' : '/'. $specific_multi_key),
|
47 |
-
$values,
|
48 |
-
$default_value
|
49 |
-
);
|
50 |
-
}
|
51 |
-
|
52 |
-
/**
|
53 |
-
* Alternative for update_option()
|
54 |
-
* @param string $option_name
|
55 |
-
* @param string|null $specific_multi_key
|
56 |
-
* @param array|string|int|bool $set_value
|
57 |
-
*/
|
58 |
-
public static function set($option_name, $specific_multi_key = null, $set_value)
|
59 |
-
{
|
60 |
-
$cache_key = self::$cache_key .'/'. $option_name;
|
61 |
-
|
62 |
-
if ($specific_multi_key === null) {
|
63 |
-
/** Replace entire option */
|
64 |
-
|
65 |
-
update_option($option_name, $set_value, false);
|
66 |
-
|
67 |
-
FW_Cache::del($cache_key);
|
68 |
-
} else {
|
69 |
-
/** Change only specified key */
|
70 |
-
|
71 |
-
$values = array();
|
72 |
-
|
73 |
-
$values['original'] = self::get($option_name, null, true);
|
74 |
-
$values['prepared'] = self::get($option_name, null, false);
|
75 |
-
|
76 |
-
fw_aks($specific_multi_key, $set_value, $values['original']);
|
77 |
-
fw_aks($specific_multi_key, fw_prepare_option_value($set_value), $values['prepared']);
|
78 |
-
|
79 |
-
FW_Cache::set($cache_key, $values);
|
80 |
-
|
81 |
-
update_option($option_name, $values['original'], false);
|
82 |
-
}
|
83 |
-
}
|
84 |
-
}
|
1 |
+
<?php if (!defined('FW')) die('Forbidden');
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Alternative to WordPress get_option() and update_option() functions
|
5 |
+
*
|
6 |
+
* Features:
|
7 |
+
* - Works with "multi keys"
|
8 |
+
* - The value is stored in two formats: original and prepared.
|
9 |
+
* Prepared is used for frontend because it is translated (+ maybe other preparations in the future)
|
10 |
+
*/
|
11 |
+
class FW_WP_Option
|
12 |
+
{
|
13 |
+
/**
|
14 |
+
* Store all this class data in cache within this key
|
15 |
+
* @var string
|
16 |
+
*/
|
17 |
+
private static $cache_key = 'wp_option';
|
18 |
+
|
19 |
+
/**
|
20 |
+
* @param string $option_name
|
21 |
+
* @param string|null $specific_multi_key 'ab/c/def'
|
22 |
+
* @param null|mixed $default_value If no option found in the database, this value will be returned
|
23 |
+
* @param bool|null $get_original_value Original value from db, no changes and translations
|
24 |
+
* @return mixed|null
|
25 |
+
*/
|
26 |
+
public static function get($option_name, $specific_multi_key = null, $default_value = null, $get_original_value = null)
|
27 |
+
{
|
28 |
+
if ($get_original_value === null) {
|
29 |
+
$get_original_value = is_admin();
|
30 |
+
}
|
31 |
+
|
32 |
+
$cache_key = self::$cache_key .'/'. $option_name;
|
33 |
+
|
34 |
+
try {
|
35 |
+
$values = FW_Cache::get($cache_key);
|
36 |
+
} catch (FW_Cache_Not_Found_Exception $e) {
|
37 |
+
$values = array();
|
38 |
+
|
39 |
+
$values['original'] = get_option($option_name, null);
|
40 |
+
$values['prepared'] = fw_prepare_option_value($values['original']);
|
41 |
+
|
42 |
+
FW_Cache::set($cache_key, $values);
|
43 |
+
}
|
44 |
+
|
45 |
+
return fw_akg(
|
46 |
+
($get_original_value ? 'original' : 'prepared') . (empty($specific_multi_key) ? '' : '/'. $specific_multi_key),
|
47 |
+
$values,
|
48 |
+
$default_value
|
49 |
+
);
|
50 |
+
}
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Alternative for update_option()
|
54 |
+
* @param string $option_name
|
55 |
+
* @param string|null $specific_multi_key
|
56 |
+
* @param array|string|int|bool $set_value
|
57 |
+
*/
|
58 |
+
public static function set($option_name, $specific_multi_key = null, $set_value)
|
59 |
+
{
|
60 |
+
$cache_key = self::$cache_key .'/'. $option_name;
|
61 |
+
|
62 |
+
if ($specific_multi_key === null) {
|
63 |
+
/** Replace entire option */
|
64 |
+
|
65 |
+
update_option($option_name, $set_value, false);
|
66 |
+
|
67 |
+
FW_Cache::del($cache_key);
|
68 |
+
} else {
|
69 |
+
/** Change only specified key */
|
70 |
+
|
71 |
+
$values = array();
|
72 |
+
|
73 |
+
$values['original'] = self::get($option_name, null, true);
|
74 |
+
$values['prepared'] = self::get($option_name, null, false);
|
75 |
+
|
76 |
+
fw_aks($specific_multi_key, $set_value, $values['original']);
|
77 |
+
fw_aks($specific_multi_key, fw_prepare_option_value($set_value), $values['prepared']);
|
78 |
+
|
79 |
+
FW_Cache::set($cache_key, $values);
|
80 |
+
|
81 |
+
update_option($option_name, $values['original'], false);
|
82 |
+
}
|
83 |
+
}
|
84 |
+
}
|
framework/helpers/database.php
CHANGED
@@ -1,884 +1,884 @@
|
|
1 |
-
<?php if ( ! defined( 'FW' ) ) {
|
2 |
-
die( 'Forbidden' );
|
3 |
-
}
|
4 |
-
|
5 |
-
/** Theme Settings Options */
|
6 |
-
{
|
7 |
-
/**
|
8 |
-
* Get a theme settings option value from the database
|
9 |
-
*
|
10 |
-
* @param string|null $option_id Specific option id (accepts multikey). null - all options
|
11 |
-
* @param null|mixed $default_value If no option found in the database, this value will be returned
|
12 |
-
* @param null|bool $get_original_value Original value is that with no translations and other changes
|
13 |
-
*
|
14 |
-
* @return mixed|null
|
15 |
-
*/
|
16 |
-
function fw_get_db_settings_option( $option_id = null, $default_value = null, $get_original_value = null ) {
|
17 |
-
static $merge_values_with_defaults = false;
|
18 |
-
|
19 |
-
if (empty($option_id)) {
|
20 |
-
$sub_keys = null;
|
21 |
-
} else {
|
22 |
-
$option_id = explode('/', $option_id); // 'option_id/sub/keys'
|
23 |
-
$_option_id = array_shift($option_id); // 'option_id'
|
24 |
-
$sub_keys = empty($option_id) ? null : implode('/', $option_id); // 'sub/keys'
|
25 |
-
$option_id = $_option_id;
|
26 |
-
unset($_option_id);
|
27 |
-
}
|
28 |
-
|
29 |
-
try {
|
30 |
-
/**
|
31 |
-
* Cached because values are merged with extracted default values
|
32 |
-
*/
|
33 |
-
$values = FW_Cache::get($cache_key = 'fw_settings_options/values');
|
34 |
-
} catch (FW_Cache_Not_Found_Exception $e) {
|
35 |
-
FW_Cache::set(
|
36 |
-
$cache_key,
|
37 |
-
$values = (array)FW_WP_Option::get(
|
38 |
-
'fw_theme_settings_options:'. fw()->theme->manifest->get_id(), null, array(), $get_original_value
|
39 |
-
)
|
40 |
-
);
|
41 |
-
|
42 |
-
$merge_values_with_defaults = true;
|
43 |
-
}
|
44 |
-
|
45 |
-
/**
|
46 |
-
* If db value is not found and default value is provided
|
47 |
-
* return default value before loading options file
|
48 |
-
* to prevent infinite recursion in case if this function is called in options file
|
49 |
-
*/
|
50 |
-
if ( ! is_null($default_value) ) {
|
51 |
-
if ( empty( $option_id ) ) {
|
52 |
-
if ( empty( $values ) && is_array( $default_value ) ) {
|
53 |
-
return $default_value;
|
54 |
-
}
|
55 |
-
} else {
|
56 |
-
if ( is_null( $sub_keys ) ) {
|
57 |
-
if ( ! isset( $values[ $option_id ] ) ) {
|
58 |
-
return $default_value;
|
59 |
-
}
|
60 |
-
} else {
|
61 |
-
if ( is_null( fw_akg( $sub_keys, $values[ $option_id ] ) ) ) {
|
62 |
-
return $default_value;
|
63 |
-
}
|
64 |
-
}
|
65 |
-
}
|
66 |
-
}
|
67 |
-
|
68 |
-
try {
|
69 |
-
$options = FW_Cache::get( $cache_key = 'fw_only_options/settings' );
|
70 |
-
} catch (FW_Cache_Not_Found_Exception $e) {
|
71 |
-
FW_Cache::set($cache_key, array()); // prevent recursion
|
72 |
-
FW_Cache::set(
|
73 |
-
$cache_key,
|
74 |
-
$options = fw_extract_only_options(fw()->theme->get_settings_options())
|
75 |
-
);
|
76 |
-
}
|
77 |
-
|
78 |
-
/**
|
79 |
-
* Complete missing db values with default values from options array
|
80 |
-
*/
|
81 |
-
if ($merge_values_with_defaults) {
|
82 |
-
$merge_values_with_defaults = false;
|
83 |
-
FW_Cache::set(
|
84 |
-
'fw_settings_options/values',
|
85 |
-
$values = array_merge(fw_get_options_values_from_input($options, array()), $values)
|
86 |
-
);
|
87 |
-
}
|
88 |
-
|
89 |
-
if (empty($option_id)) {
|
90 |
-
foreach ($options as $id => $option) {
|
91 |
-
$values[$id] = fw()->backend->option_type($options[$id]['type'])->storage_load(
|
92 |
-
$id, $options[$id], isset($values[$id]) ? $values[$id] : null, array()
|
93 |
-
);
|
94 |
-
}
|
95 |
-
} else {
|
96 |
-
if (isset($options[$option_id])) {
|
97 |
-
$values[ $option_id ] = fw()->backend->option_type( $options[ $option_id ]['type'] )->storage_load(
|
98 |
-
$option_id,
|
99 |
-
$options[ $option_id ],
|
100 |
-
isset($values[ $option_id ]) ? $values[ $option_id ] : null,
|
101 |
-
array()
|
102 |
-
);
|
103 |
-
}
|
104 |
-
}
|
105 |
-
|
106 |
-
if (empty($option_id)) {
|
107 |
-
return (empty($values) && is_array($default_value)) ? $default_value : $values;
|
108 |
-
} else {
|
109 |
-
if (is_null($sub_keys)) {
|
110 |
-
return isset($values[$option_id]) ? $values[$option_id] : $default_value;
|
111 |
-
} else {
|
112 |
-
return fw_akg($sub_keys, $values[$option_id], $default_value);
|
113 |
-
}
|
114 |
-
}
|
115 |
-
}
|
116 |
-
|
117 |
-
/**
|
118 |
-
* Set a theme settings option value in database
|
119 |
-
*
|
120 |
-
* @param null $option_id Specific option id (accepts multikey). null - all options
|
121 |
-
* @param mixed $value
|
122 |
-
*/
|
123 |
-
function fw_set_db_settings_option( $option_id = null, $value ) {
|
124 |
-
FW_Cache::del('fw_settings_options/values');
|
125 |
-
|
126 |
-
try {
|
127 |
-
$options = FW_Cache::get( $cache_key = 'fw_only_options/settings' );
|
128 |
-
} catch ( FW_Cache_Not_Found_Exception $e ) {
|
129 |
-
FW_Cache::set(
|
130 |
-
$cache_key,
|
131 |
-
$options = fw_extract_only_options(fw()->theme->get_settings_options())
|
132 |
-
);
|
133 |
-
}
|
134 |
-
|
135 |
-
if (empty($option_id)) {
|
136 |
-
foreach ($options as $id => $option) {
|
137 |
-
if (isset($value[$id])) {
|
138 |
-
$value[ $id ] = fw()->backend->option_type( $options[ $id ]['type'] )->storage_save(
|
139 |
-
$id, $options[ $id ], $value[$id], array()
|
140 |
-
);
|
141 |
-
}
|
142 |
-
}
|
143 |
-
} else {
|
144 |
-
if (isset($options[$option_id]) && isset($value[ $option_id ])) {
|
145 |
-
$value[ $option_id ] = fw()->backend->option_type( $options[ $option_id ]['type'] )->storage_save(
|
146 |
-
$option_id,
|
147 |
-
$options[ $option_id ],
|
148 |
-
$value[ $option_id ],
|
149 |
-
array()
|
150 |
-
);
|
151 |
-
}
|
152 |
-
}
|
153 |
-
|
154 |
-
FW_WP_Option::set(
|
155 |
-
'fw_theme_settings_options:' . fw()->theme->manifest->get_id(),
|
156 |
-
$option_id, $value
|
157 |
-
);
|
158 |
-
}
|
159 |
-
}
|
160 |
-
|
161 |
-
/** Post Options */
|
162 |
-
{
|
163 |
-
/**
|
164 |
-
* Get post option value from the database
|
165 |
-
*
|
166 |
-
* @param null|int $post_id
|
167 |
-
* @param string|null $option_id Specific option id (accepts multikey). null - all options
|
168 |
-
* @param null|mixed $default_value If no option found in the database, this value will be returned
|
169 |
-
* @param null|bool $get_original_value Original value is that with no translations and other changes
|
170 |
-
*
|
171 |
-
* @return mixed|null
|
172 |
-
*/
|
173 |
-
function fw_get_db_post_option(
|
174 |
-
$post_id = null,
|
175 |
-
$option_id = null,
|
176 |
-
$default_value = null,
|
177 |
-
$get_original_value = null
|
178 |
-
) {
|
179 |
-
$meta_key = 'fw_options';
|
180 |
-
|
181 |
-
if ( ! $post_id ) {
|
182 |
-
/** @var WP_Post $post */
|
183 |
-
global $post;
|
184 |
-
|
185 |
-
if ( ! $post ) {
|
186 |
-
return $default_value;
|
187 |
-
} else {
|
188 |
-
$post_id = $post->ID;
|
189 |
-
}
|
190 |
-
|
191 |
-
/**
|
192 |
-
* Check if is Preview and use the preview post_id instead of real/current post id
|
193 |
-
*
|
194 |
-
* Note: WordPress changes the global $post content on preview:
|
195 |
-
* 1. https://github.com/WordPress/WordPress/blob/2096b451c704715db3c4faf699a1184260deade9/wp-includes/query.php#L3573-L3583
|
196 |
-
* 2. https://github.com/WordPress/WordPress/blob/4a31dd6fe8b774d56f901a29e72dcf9523e9ce85/wp-includes/revision.php#L485-L528
|
197 |
-
*/
|
198 |
-
if ( is_preview() && is_object($preview = wp_get_post_autosave($post->ID)) ) {
|
199 |
-
$post_id = $preview->ID;
|
200 |
-
}
|
201 |
-
}
|
202 |
-
|
203 |
-
$post_type = get_post_type(
|
204 |
-
($post_revision_id = wp_is_post_revision($post_id)) ? $post_revision_id : $post_id
|
205 |
-
);
|
206 |
-
|
207 |
-
try {
|
208 |
-
$options = FW_Cache::get(
|
209 |
-
$cache_key = 'fw_post_options/only/'. $post_type
|
210 |
-
);
|
211 |
-
} catch (FW_Cache_Not_Found_Exception $e) {
|
212 |
-
FW_Cache::set($cache_key, $options = array()); // prevent recursion
|
213 |
-
|
214 |
-
if (apply_filters('fw_get_db_post_option:fw-storage-enabled',
|
215 |
-
/**
|
216 |
-
* Slider extension has too many fw_get_db_post_option()
|
217 |
-
* inside post options altering filter and it creates recursive mess.
|
218 |
-
* add_filter() was added in Slider extension
|
219 |
-
* but this hardcode can be replaced with `true`
|
220 |
-
* only after all users will install new version 1.1.15.
|
221 |
-
*/
|
222 |
-
$post_type !== 'fw-slider',
|
223 |
-
$post_type
|
224 |
-
)) {
|
225 |
-
FW_Cache::set(
|
226 |
-
$cache_key,
|
227 |
-
$options = fw_extract_only_options(
|
228 |
-
fw()->theme->get_post_options( $post_type )
|
229 |
-
)
|
230 |
-
);
|
231 |
-
}
|
232 |
-
}
|
233 |
-
|
234 |
-
if ($option_id) {
|
235 |
-
$option_id = explode('/', $option_id); // 'option_id/sub/keys'
|
236 |
-
$_option_id = array_shift($option_id); // 'option_id'
|
237 |
-
$sub_keys = empty($option_id) ? null : implode('/', $option_id); // 'sub/keys'
|
238 |
-
$option_id = $_option_id;
|
239 |
-
unset($_option_id);
|
240 |
-
|
241 |
-
$value = FW_WP_Meta::get(
|
242 |
-
'post',
|
243 |
-
$post_id,
|
244 |
-
$meta_key .'/'. $option_id,
|
245 |
-
null,
|
246 |
-
$get_original_value
|
247 |
-
);
|
248 |
-
|
249 |
-
if (isset($options[$option_id])) {
|
250 |
-
try {
|
251 |
-
$value = FW_Cache::get( $cache_key = 'fw_post_options/values/'. $post_id .'/'. $option_id );
|
252 |
-
} catch (FW_Cache_Not_Found_Exception $e) {
|
253 |
-
FW_Cache::set($cache_key, array()); // prevent recursion
|
254 |
-
FW_Cache::set(
|
255 |
-
$cache_key,
|
256 |
-
$value = fw()->backend->option_type($options[$option_id]['type'])->storage_load(
|
257 |
-
$option_id,
|
258 |
-
$options[$option_id],
|
259 |
-
$value,
|
260 |
-
array( 'post-id' => $post_id, )
|
261 |
-
)
|
262 |
-
);
|
263 |
-
}
|
264 |
-
}
|
265 |
-
|
266 |
-
if ($sub_keys) {
|
267 |
-
return fw_akg($sub_keys, $value, $default_value);
|
268 |
-
} else {
|
269 |
-
return is_null($value) ? $default_value : $value;
|
270 |
-
}
|
271 |
-
} else {
|
272 |
-
$value = FW_WP_Meta::get(
|
273 |
-
'post',
|
274 |
-
$post_id,
|
275 |
-
$meta_key,
|
276 |
-
$default_value,
|
277 |
-
$get_original_value
|
278 |
-
);
|
279 |
-
|
280 |
-
if (!is_array($value)) {
|
281 |
-
$value = array();
|
282 |
-
}
|
283 |
-
|
284 |
-
foreach ($options as $_option_id => $_option) {
|
285 |
-
try {
|
286 |
-
$value[$_option_id] = FW_Cache::get( $cache_key = 'fw_post_options/values/'. $post_id .'/'. $_option_id );
|
287 |
-
} catch (FW_Cache_Not_Found_Exception $e) {
|
288 |
-
FW_Cache::set($cache_key, array()); // prevent recursion
|
289 |
-
FW_Cache::set(
|
290 |
-
$cache_key,
|
291 |
-
$value[$_option_id] = fw()->backend->option_type($_option['type'])->storage_load(
|
292 |
-
$_option_id,
|
293 |
-
$_option,
|
294 |
-
isset($value[$_option_id]) ? $value[$_option_id] : null,
|
295 |
-
array( 'post-id' => $post_id, )
|
296 |
-
)
|
297 |
-
);
|
298 |
-
}
|
299 |
-
}
|
300 |
-
|
301 |
-
return $value;
|
302 |
-
}
|
303 |
-
}
|
304 |
-
|
305 |
-
/**
|
306 |
-
* Set post option value in database
|
307 |
-
*
|
308 |
-
* @param null|int $post_id
|
309 |
-
* @param string|null $option_id Specific option id (accepts multikey). null - all options
|
310 |
-
* @param $value
|
311 |
-
*/
|
312 |
-
function fw_set_db_post_option( $post_id = null, $option_id = null, $value ) {
|
313 |
-
FW_Cache::del('fw_post_options/values');
|
314 |
-
|
315 |
-
$meta_key = 'fw_options';
|
316 |
-
$post_id = intval($post_id);
|
317 |
-
|
318 |
-
if ( ! $post_id ) {
|
319 |
-
/** @var WP_Post $post */
|
320 |
-
global $post;
|
321 |
-
|
322 |
-
if ( ! $post ) {
|
323 |
-
return;
|
324 |
-
} else {
|
325 |
-
$post_id = $post->ID;
|
326 |
-
}
|
327 |
-
}
|
328 |
-
|
329 |
-
$post_type = get_post_type(
|
330 |
-
($post_revision_id = wp_is_post_revision($post_id)) ? $post_revision_id : $post_id
|
331 |
-
);
|
332 |
-
|
333 |
-
try {
|
334 |
-
$options = FW_Cache::get(
|
335 |
-
$cache_key = 'fw_post_options/only/'. $post_type
|
336 |
-
);
|
337 |
-
} catch (FW_Cache_Not_Found_Exception $e) {
|
338 |
-
FW_Cache::set($cache_key, $options = array()); // prevent recursion
|
339 |
-
|
340 |
-
if (apply_filters('fw_get_db_post_option:fw-storage-enabled',
|
341 |
-
/**
|
342 |
-
* Slider extension has too many fw_get_db_post_option()
|
343 |
-
* inside post options altering filter and it creates recursive mess.
|
344 |
-
* add_filter() was added in Slider extension
|
345 |
-
* but this hardcode can be replaced with `true`
|
346 |
-
* only after all users will install new version 1.1.15.
|
347 |
-
*/
|
348 |
-
$post_type !== 'fw-slider',
|
349 |
-
$post_type
|
350 |
-
)) {
|
351 |
-
FW_Cache::set(
|
352 |
-
$cache_key,
|
353 |
-
$options = fw_extract_only_options(
|
354 |
-
fw()->theme->get_post_options( $post_type )
|
355 |
-
)
|
356 |
-
);
|
357 |
-
}
|
358 |
-
}
|
359 |
-
|
360 |
-
$sub_keys = null;
|
361 |
-
|
362 |
-
if ($option_id) {
|
363 |
-
$option_id = explode('/', $option_id); // 'option_id/sub/keys'
|
364 |
-
$_option_id = array_shift($option_id); // 'option_id'
|
365 |
-
$sub_keys = empty($option_id) ? null : implode('/', $option_id); // 'sub/keys'
|
366 |
-
$option_id = $_option_id;
|
367 |
-
unset($_option_id);
|
368 |
-
|
369 |
-
$old_value = fw_get_db_post_option($post_id, $option_id);
|
370 |
-
|
371 |
-
if ($sub_keys) { // update sub_key in old_value and use the entire value
|
372 |
-
$new_value = $old_value;
|
373 |
-
fw_aks($sub_keys, $value, $new_value);
|
374 |
-
$value = $new_value;
|
375 |
-
unset($new_value);
|
376 |
-
|
377 |
-
$old_value = fw_akg($sub_keys, $old_value);
|
378 |
-
}
|
379 |
-
|
380 |
-
if (isset($options[$option_id])) {
|
381 |
-
$value = fw()->backend->option_type($options[$option_id]['type'])->storage_save(
|
382 |
-
$option_id,
|
383 |
-
$options[$option_id],
|
384 |
-
$value,
|
385 |
-
array( 'post-id' => $post_id, )
|
386 |
-
);
|
387 |
-
}
|
388 |
-
|
389 |
-
FW_WP_Meta::set( 'post', $post_id, $meta_key .'/'. $option_id, $value );
|
390 |
-
} else {
|
391 |
-
$old_value = fw_get_db_post_option($post_id);
|
392 |
-
|
393 |
-
if (!is_array($value)) {
|
394 |
-
$value = array();
|
395 |
-
}
|
396 |
-
|
397 |
-
foreach ($value as $_option_id => $_option_value) {
|
398 |
-
if (isset($options[$_option_id])) {
|
399 |
-
$value[$_option_id] = fw()->backend->option_type($options[$_option_id]['type'])->storage_save(
|
400 |
-
$_option_id,
|
401 |
-
$options[$_option_id],
|
402 |
-
$_option_value,
|
403 |
-
array( 'post-id' => $post_id, )
|
404 |
-
);
|
405 |
-
}
|
406 |
-
}
|
407 |
-
|
408 |
-
FW_WP_Meta::set( 'post', $post_id, $meta_key, $value );
|
409 |
-
}
|
410 |
-
|
411 |
-
FW_Cache::del('fw_post_options/values'); // fixes https://github.com/ThemeFuse/Unyson/issues/1538
|
412 |
-
|
413 |
-
/**
|
414 |
-
* @deprecated
|
415 |
-
*/
|
416 |
-
fw()->backend->_sync_post_separate_meta($post_id);
|
417 |
-
|
418 |
-
/**
|
419 |
-
* @since 2.2.8
|
420 |
-
*/
|
421 |
-
do_action('fw_post_options_update',
|
422 |
-
$post_id,
|
423 |
-
/**
|
424 |
-
* Option id
|
425 |
-
* First level multi-key
|
426 |
-
*
|
427 |
-
* For e.g.
|
428 |
-
*
|
429 |
-
* if $option_id is 'hello/world/7'
|
430 |
-
* this will be 'hello'
|
431 |
-
*/
|
432 |
-
$option_id,
|
433 |
-
/**
|
434 |
-
* The remaining sub-keys
|
435 |
-
*
|
436 |
-
* For e.g.
|
437 |
-
*
|
438 |
-
* if $option_id is 'hello/world/7'
|
439 |
-
* $option_id_keys will be array('world', '7')
|
440 |
-
*
|
441 |
-
* if $option_id is 'hello'
|
442 |
-
* $option_id_keys will be array()
|
443 |
-
*/
|
444 |
-
explode('/', $sub_keys),
|
445 |
-
/**
|
446 |
-
* Old post option(s) value
|
447 |
-
* @since 2.3.3
|
448 |
-
*/
|
449 |
-
$old_value
|
450 |
-
);
|
451 |
-
}
|
452 |
-
}
|
453 |
-
|
454 |
-
/** Terms Options */
|
455 |
-
{
|
456 |
-
/**
|
457 |
-
* Get term option value from the database
|
458 |
-
*
|
459 |
-
* @param int $term_id
|
460 |
-
* @param string $taxonomy
|
461 |
-
* @param string|null $option_id Specific option id (accepts multikey). null - all options
|
462 |
-
* @param null|mixed $default_value If no option found in the database, this value will be returned
|
463 |
-
* @param null|bool $get_original_value Original value is that with no translations and other changes
|
464 |
-
*
|
465 |
-
* @return mixed|null
|
466 |
-
*/
|
467 |
-
function fw_get_db_term_option( $term_id, $taxonomy, $option_id = null, $default_value = null, $get_original_value = null ) {
|
468 |
-
if ( ! taxonomy_exists( $taxonomy ) ) {
|
469 |
-
return null;
|
470 |
-
}
|
471 |
-
|
472 |
-
$option_id = 'fw_options' . ( $option_id !== null ? '/' . $option_id : '' );
|
473 |
-
|
474 |
-
return FW_WP_Meta::get( 'fw_term', $term_id, $option_id, $default_value, $get_original_value );
|
475 |
-
}
|
476 |
-
|
477 |
-
/**
|
478 |
-
* Set term option value in database
|
479 |
-
*
|
480 |
-
* @param int $term_id
|
481 |
-
* @param string $taxonomy
|
482 |
-
* @param string|null $option_id Specific option id (accepts multikey). null - all options
|
483 |
-
* @param mixed $value
|
484 |
-
*
|
485 |
-
* @return null
|
486 |
-
*/
|
487 |
-
function fw_set_db_term_option( $term_id, $taxonomy, $option_id = null, $value ) {
|
488 |
-
if ( ! taxonomy_exists( $taxonomy ) ) {
|
489 |
-
return null;
|
490 |
-
}
|
491 |
-
$option_id = 'fw_options' . ( $option_id !== null ? '/' . $option_id : '' );
|
492 |
-
|
493 |
-
FW_WP_Meta::set( 'fw_term', $term_id, $option_id, $value );
|
494 |
-
}
|
495 |
-
}
|
496 |
-
|
497 |
-
/**
|
498 |
-
* Extensions Data
|
499 |
-
*
|
500 |
-
* Used by extensions to store custom data in database.
|
501 |
-
* By using these functions, extension prevent database spam with wp options for each extension,
|
502 |
-
* because these functions store all data in one wp option.
|
503 |
-
*/
|
504 |
-
{
|
505 |
-
/**
|
506 |
-
* Get extension's data from the database
|
507 |
-
*
|
508 |
-
* @param string $extension_name
|
509 |
-
* @param string|null $multi_key The key of the data you want to get. null - all data
|
510 |
-
* @param null|mixed $default_value If no option found in the database, this value will be returned
|
511 |
-
* @param null|bool $get_original_value Original value is that with no translations and other changes
|
512 |
-
*
|
513 |
-
* @return mixed|null
|
514 |
-
*/
|
515 |
-
function fw_get_db_extension_data( $extension_name, $multi_key = null, $default_value = null, $get_original_value = null ) {
|
516 |
-
if ( ! fw()->extensions->get( $extension_name ) ) {
|
517 |
-
trigger_error( 'Invalid extension: ' . $extension_name, E_USER_WARNING );
|
518 |
-
|
519 |
-
return null;
|
520 |
-
}
|
521 |
-
|
522 |
-
if ( $multi_key ) {
|
523 |
-
$multi_key = $extension_name . '/' . $multi_key;
|
524 |
-
} else {
|
525 |
-
$multi_key = $extension_name;
|
526 |
-
}
|
527 |
-
|
528 |
-
return FW_WP_Option::get( 'fw_extensions', $multi_key, $default_value, $get_original_value );
|
529 |
-
}
|
530 |
-
|
531 |
-
/**
|
532 |
-
* Set some extension's data in database
|
533 |
-
*
|
534 |
-
* @param string $extension_name
|
535 |
-
* @param string|null $multi_key The key of the data you want to set. null - all data
|
536 |
-
* @param mixed $value
|
537 |
-
*/
|
538 |
-
function fw_set_db_extension_data( $extension_name, $multi_key = null, $value ) {
|
539 |
-
if ( ! fw()->extensions->get( $extension_name ) ) {
|
540 |
-
trigger_error( 'Invalid
|
541 |
-
|
542 |
-
return;
|
543 |
-
}
|
544 |
-
|
545 |
-
if ( $multi_key ) {
|
546 |
-
$multi_key = $extension_name . '/' . $multi_key;
|
547 |
-
} else {
|
548 |
-
$multi_key = $extension_name;
|
549 |
-
}
|
550 |
-
|
551 |
-
FW_WP_Option::set( 'fw_extensions', $multi_key, $value );
|
552 |
-
}
|
553 |
-
}
|
554 |
-
|
555 |
-
/**
|
556 |
-
* Extensions Settings Options
|
557 |
-
*/
|
558 |
-
{
|
559 |
-
/**
|
560 |
-
* Get extension's settings option value from the database
|
561 |
-
*
|
562 |
-
* @param string $extension_name
|
563 |
-
* @param string|null $option_id
|
564 |
-
* @param null|mixed $default_value If no option found in the database, this value will be returned
|
565 |
-
* @param null|bool $get_original_value Original value is that with no translations and other changes
|
566 |
-
*
|
567 |
-
* @return mixed|null
|
568 |
-
*/
|
569 |
-
function fw_get_db_ext_settings_option( $extension_name, $option_id = null, $default_value = null, $get_original_value = null ) {
|
570 |
-
if ( ! ( $extension = fw()->extensions->get( $extension_name ) ) ) {
|
571 |
-
trigger_error( 'Invalid extension: ' . $extension_name, E_USER_WARNING );
|
572 |
-
|
573 |
-
return null;
|
574 |
-
}
|
575 |
-
|
576 |
-
$value = FW_WP_Option::get( 'fw_ext_settings_options:' . $extension_name, $option_id, $default_value, $get_original_value );
|
577 |
-
|
578 |
-
if ( is_null( $value ) ) {
|
579 |
-
/**
|
580 |
-
* Maybe the options was never saved or the given option id does not exists
|
581 |
-
* Extract the default values from the options array and try to find there the option id
|
582 |
-
*/
|
583 |
-
|
584 |
-
$cache_key = 'fw_default_options_values/ext_settings:' . $extension_name;
|
585 |
-
|
586 |
-
try {
|
587 |
-
$all_options_values = FW_Cache::get( $cache_key );
|
588 |
-
} catch ( FW_Cache_Not_Found_Exception $e ) {
|
589 |
-
// extract the default values from options array
|
590 |
-
$all_options_values = fw_get_options_values_from_input(
|
591 |
-
$extension->get_settings_options(),
|
592 |
-
array()
|
593 |
-
);
|
594 |
-
|
595 |
-
FW_Cache::set( $cache_key, $all_options_values );
|
596 |
-
}
|
597 |
-
|
598 |
-
if ( empty( $option_id ) ) {
|
599 |
-
// option id not specified, return all options values
|
600 |
-
return $all_options_values;
|
601 |
-
} else {
|
602 |
-
return fw_akg( $option_id, $all_options_values, $default_value );
|
603 |
-
}
|
604 |
-
} else {
|
605 |
-
return $value;
|
606 |
-
}
|
607 |
-
}
|
608 |
-
|
609 |
-
/**
|
610 |
-
* Set extension's setting option value in database
|
611 |
-
*
|
612 |
-
* @param string $extension_name
|
613 |
-
* @param string|null $option_id
|
614 |
-
* @param mixed $value
|
615 |
-
*/
|
616 |
-
function fw_set_db_ext_settings_option( $extension_name, $option_id = null, $value ) {
|
617 |
-
if ( ! fw()->extensions->get( $extension_name ) ) {
|
618 |
-
trigger_error( 'Invalid extension: ' . $extension_name, E_USER_WARNING );
|
619 |
-
|
620 |
-
return;
|
621 |
-
}
|
622 |
-
|
623 |
-
FW_WP_Option::set( 'fw_ext_settings_options:' . $extension_name, $option_id, $value );
|
624 |
-
}
|
625 |
-
}
|
626 |
-
|
627 |
-
{
|
628 |
-
/**
|
629 |
-
* Get user meta set by specific extension
|
630 |
-
*
|
631 |
-
* @param int $user_id
|
632 |
-
* @param string $extension_name
|
633 |
-
* @param string $keys
|
634 |
-
*
|
635 |
-
* If the extension doesn't exist or is disabled, or meta key doesn't exist, returns null,
|
636 |
-
* else returns the meta key value
|
637 |
-
*
|
638 |
-
* @return mixed|null
|
639 |
-
*/
|
640 |
-
function fw_get_db_extension_user_data( $user_id, $extension_name, $keys = null ) {
|
641 |
-
if ( ! fw()->extensions->get( $extension_name ) ) {
|
642 |
-
trigger_error( 'Invalid extension: ' . $extension_name, E_USER_WARNING );
|
643 |
-
|
644 |
-
return null;
|
645 |
-
}
|
646 |
-
$data = get_user_meta( $user_id, 'fw_data', true );
|
647 |
-
|
648 |
-
if ( is_null( $keys ) ) {
|
649 |
-
return fw_akg( $extension_name, $data );
|
650 |
-
}
|
651 |
-
|
652 |
-
return fw_akg( $extension_name . '/' . $keys, $data );
|
653 |
-
}
|
654 |
-
|
655 |
-
/**
|
656 |
-
* @param int $user_id
|
657 |
-
* @param string $extension_name
|
658 |
-
* @param mixed $value
|
659 |
-
* @param string $keys
|
660 |
-
*
|
661 |
-
* In case the extension doesn't exist or is disabled, or the value is equal to previous, returns false
|
662 |
-
*
|
663 |
-
* @return bool|int
|
664 |
-
*/
|
665 |
-
function fw_set_db_extension_user_data( $user_id, $extension_name, $value, $keys = null ) {
|
666 |
-
if ( ! fw()->extensions->get( $extension_name ) ) {
|
667 |
-
trigger_error( 'Invalid extension: ' . $extension_name, E_USER_WARNING );
|
668 |
-
|
669 |
-
return false;
|
670 |
-
}
|
671 |
-
$data = get_user_meta( $user_id, 'fw_data', true );
|
672 |
-
|
673 |
-
if ( $keys == null ) {
|
674 |
-
fw_aks( $extension_name, $value, $data );
|
675 |
-
} else {
|
676 |
-
fw_aks( $extension_name . '/' . $keys, $value, $data );
|
677 |
-
}
|
678 |
-
|
679 |
-
return fw_update_user_meta( $user_id, 'fw_data', $data );
|
680 |
-
}
|
681 |
-
}
|
682 |
-
|
683 |
-
/** Customizer Framework Options */
|
684 |
-
{
|
685 |
-
/**
|
686 |
-
* Get a customizer framework option value from the database
|
687 |
-
*
|
688 |
-
* @param string|null $option_id Specific option id (accepts multikey). null - all options
|
689 |
-
* @param null|mixed $default_value If no option found in the database, this value will be returned
|
690 |
-
* // fixme: Maybe add this parameter? @ param null|bool $get_original_value Original value is that with no translations and other changes
|
691 |
-
*
|
692 |
-
* @return mixed|null
|
693 |
-
*/
|
694 |
-
function fw_get_db_customizer_option( $option_id = null, $default_value = null ) {
|
695 |
-
// note: this contains only changed controls/options
|
696 |
-
$db_values = get_theme_mod(FW_Option_Type::get_default_name_prefix(), null);
|
697 |
-
|
698 |
-
if (
|
699 |
-
!is_null($default_value)
|
700 |
-
&&
|
701 |
-
is_null($option_id ? fw_akg($option_id, $db_values) : $db_values)
|
702 |
-
) {
|
703 |
-
/**
|
704 |
-
* Default value was provided in case db value is empty.
|
705 |
-
*
|
706 |
-
* Do not extract default values from options files (below)
|
707 |
-
* maybe this function was called from options files and it will cause infinite loop,
|
708 |
-
* that's why the developer provided a default value to prevent that.
|
709 |
-
*/
|
710 |
-
return $default_value;
|
711 |
-
}
|
712 |
-
|
713 |
-
if (is_null($db_values)) {
|
714 |
-
$db_values = array();
|
715 |
-
}
|
716 |
-
|
717 |
-
if (
|
718 |
-
is_null($option_id)
|
719 |
-
||
|
720 |
-
(
|
721 |
-
($base_key = explode('/', $option_id)) // note: option_id can be a multi-key 'a/b/c'
|
722 |
-
&&
|
723 |
-
($base_key = array_shift($base_key))
|
724 |
-
&&
|
725 |
-
!array_key_exists($base_key, $db_values)
|
726 |
-
)
|
727 |
-
) {
|
728 |
-
// extract options default values
|
729 |
-
{
|
730 |
-
$cache_key = 'fw_default_options_values/customizer';
|
731 |
-
|
732 |
-
try {
|
733 |
-
$default_values = FW_Cache::get( $cache_key );
|
734 |
-
} catch ( FW_Cache_Not_Found_Exception $e ) {
|
735 |
-
// extract the default values from options array
|
736 |
-
$default_values = fw_get_options_values_from_input(
|
737 |
-
fw()->theme->get_customizer_options(),
|
738 |
-
array()
|
739 |
-
);
|
740 |
-
|
741 |
-
FW_Cache::set( $cache_key, $default_values );
|
742 |
-
}
|
743 |
-
}
|
744 |
-
|
745 |
-
$db_values = array_merge($default_values, $db_values);
|
746 |
-
}
|
747 |
-
|
748 |
-
return is_null($option_id)
|
749 |
-
? $db_values
|
750 |
-
: fw_akg($option_id, $db_values, $default_value);
|
751 |
-
}
|
752 |
-
|
753 |
-
/**
|
754 |
-
* Set a theme customizer option value in database
|
755 |
-
*
|
756 |
-
* @param null $option_id Specific option id (accepts multikey). null - all options
|
757 |
-
* @param mixed $value
|
758 |
-
*/
|
759 |
-
function fw_set_db_customizer_option( $option_id = null, $value ) {
|
760 |
-
$db_value = get_theme_mod(FW_Option_Type::get_default_name_prefix(), array());
|
761 |
-
|
762 |
-
if (is_null($option_id)) {
|
763 |
-
$db_value = $value;
|
764 |
-
} else {
|
765 |
-
fw_aks($option_id, $value, $db_value);
|
766 |
-
}
|
767 |
-
|
768 |
-
set_theme_mod(
|
769 |
-
FW_Option_Type::get_default_name_prefix(),
|
770 |
-
$db_value
|
771 |
-
);
|
772 |
-
}
|
773 |
-
}
|
774 |
-
|
775 |
-
{
|
776 |
-
/**
|
777 |
-
* @param string $id
|
778 |
-
* @param array $option
|
779 |
-
* @param mixed $value
|
780 |
-
* @param array $params
|
781 |
-
*
|
782 |
-
* @return mixed
|
783 |
-
*
|
784 |
-
* @since 2.5.0
|
785 |
-
*/
|
786 |
-
function fw_db_option_storage_save($id, array $option, $value, array $params = array()) {
|
787 |
-
if (
|
788 |
-
!empty($option['fw-storage'])
|
789 |
-
&&
|
790 |
-
($storage = is_array($option['fw-storage'])
|
791 |
-
? $option['fw-storage']
|
792 |
-
: array('type' => $option['fw-storage'])
|
793 |
-
)
|
794 |
-
&&
|
795 |
-
!empty($storage['type'])
|
796 |
-
&&
|
797 |
-
($storage_type = fw_db_option_storage_type($storage['type']))
|
798 |
-
) {
|
799 |
-
$option['fw-storage'] = $storage;
|
800 |
-
} else {
|
801 |
-
return $value;
|
802 |
-
}
|
803 |
-
|
804 |
-
/** @var FW_Option_Storage_Type $storage_type */
|
805 |
-
|
806 |
-
return $storage_type->save($id, $option, $value, $params);
|
807 |
-
}
|
808 |
-
|
809 |
-
/**
|
810 |
-
* @param string $id
|
811 |
-
* @param array $option
|
812 |
-
* @param mixed $value
|
813 |
-
* @param array $params
|
814 |
-
*
|
815 |
-
* @return mixed
|
816 |
-
*
|
817 |
-
* @since 2.5.0
|
818 |
-
*/
|
819 |
-
function fw_db_option_storage_load($id, array $option, $value, array $params = array()) {
|
820 |
-
if (
|
821 |
-
!empty($option['fw-storage'])
|
822 |
-
&&
|
823 |
-
($storage = is_array($option['fw-storage'])
|
824 |
-
? $option['fw-storage']
|
825 |
-
: array('type' => $option['fw-storage'])
|
826 |
-
)
|
827 |
-
&&
|
828 |
-
!empty($storage['type'])
|
829 |
-
&&
|
830 |
-
($storage_type = fw_db_option_storage_type($storage['type']))
|
831 |
-
) {
|
832 |
-
$option['fw-storage'] = $storage;
|
833 |
-
} else {
|
834 |
-
return $value;
|
835 |
-
}
|
836 |
-
|
837 |
-
/** @var FW_Option_Storage_Type $storage_type */
|
838 |
-
|
839 |
-
return $storage_type->load($id, $option, $value, $params);
|
840 |
-
}
|
841 |
-
|
842 |
-
/**
|
843 |
-
* @param null|string $type
|
844 |
-
* @return FW_Option_Storage_Type|FW_Option_Storage_Type[]|null
|
845 |
-
* @since 2.5.0
|
846 |
-
*/
|
847 |
-
function fw_db_option_storage_type($type = null) {
|
848 |
-
static $types = null;
|
849 |
-
|
850 |
-
if (is_null($types)) {
|
851 |
-
$dir = fw_get_framework_directory('/includes/option-storage');
|
852 |
-
|
853 |
-
if (!class_exists('FW_Option_Storage_Type')) {
|
854 |
-
require_once $dir .'/class-fw-option-storage-type.php';
|
855 |
-
}
|
856 |
-
if (!class_exists('_FW_Option_Storage_Type_Register')) {
|
857 |
-
require_once $dir .'/class--fw-option-storage-type-register.php';
|
858 |
-
}
|
859 |
-
|
860 |
-
$access_key = new FW_Access_Key('fw:option-storage-register');
|
861 |
-
$register = new _FW_Option_Storage_Type_Register($access_key->get_key());
|
862 |
-
|
863 |
-
{
|
864 |
-
require_once $dir .'/type/class-fw-option-storage-type-post-meta.php';
|
865 |
-
$register->register(new FW_Option_Storage_Type_Post_Meta());
|
866 |
-
|
867 |
-
require_once $dir .'/type/class-fw-option-storage-type-wp-option.php';
|
868 |
-
$register->register(new FW_Option_Storage_Type_WP_Option());
|
869 |
-
}
|
870 |
-
|
871 |
-
do_action('fw:option-storage-types:register', $register);
|
872 |
-
|
873 |
-
$types = $register->_get_types($access_key);
|
874 |
-
}
|
875 |
-
|
876 |
-
if (empty($type)) {
|
877 |
-
return $types;
|
878 |
-
} elseif (isset($types[$type])) {
|
879 |
-
return $types[$type];
|
880 |
-
} else {
|
881 |
-
return null;
|
882 |
-
}
|
883 |
-
}
|
884 |
-
}
|
1 |
+
<?php if ( ! defined( 'FW' ) ) {
|
2 |
+
die( 'Forbidden' );
|
3 |
+
}
|
4 |
+
|
5 |
+
/** Theme Settings Options */
|
6 |
+
{
|
7 |
+
/**
|
8 |
+
* Get a theme settings option value from the database
|
9 |
+
*
|
10 |
+
* @param string|null $option_id Specific option id (accepts multikey). null - all options
|
11 |
+
* @param null|mixed $default_value If no option found in the database, this value will be returned
|
12 |
+
* @param null|bool $get_original_value Original value is that with no translations and other changes
|
13 |
+
*
|
14 |
+
* @return mixed|null
|
15 |
+
*/
|
16 |
+
function fw_get_db_settings_option( $option_id = null, $default_value = null, $get_original_value = null ) {
|
17 |
+
static $merge_values_with_defaults = false;
|
18 |
+
|
19 |
+
if (empty($option_id)) {
|
20 |
+
$sub_keys = null;
|
21 |
+
} else {
|
22 |
+
$option_id = explode('/', $option_id); // 'option_id/sub/keys'
|
23 |
+
$_option_id = array_shift($option_id); // 'option_id'
|
24 |
+
$sub_keys = empty($option_id) ? null : implode('/', $option_id); // 'sub/keys'
|
25 |
+
$option_id = $_option_id;
|
26 |
+
unset($_option_id);
|
27 |
+
}
|
28 |
+
|
29 |
+
try {
|
30 |
+
/**
|
31 |
+
* Cached because values are merged with extracted default values
|
32 |
+
*/
|
33 |
+
$values = FW_Cache::get($cache_key = 'fw_settings_options/values');
|
34 |
+
} catch (FW_Cache_Not_Found_Exception $e) {
|
35 |
+
FW_Cache::set(
|
36 |
+
$cache_key,
|
37 |
+
$values = (array)FW_WP_Option::get(
|
38 |
+
'fw_theme_settings_options:'. fw()->theme->manifest->get_id(), null, array(), $get_original_value
|
39 |
+
)
|
40 |
+
);
|
41 |
+
|
42 |
+
$merge_values_with_defaults = true;
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* If db value is not found and default value is provided
|
47 |
+
* return default value before loading options file
|
48 |
+
* to prevent infinite recursion in case if this function is called in options file
|
49 |
+
*/
|
50 |
+
if ( ! is_null($default_value) ) {
|
51 |
+
if ( empty( $option_id ) ) {
|
52 |
+
if ( empty( $values ) && is_array( $default_value ) ) {
|
53 |
+
return $default_value;
|
54 |
+
}
|
55 |
+
} else {
|
56 |
+
if ( is_null( $sub_keys ) ) {
|
57 |
+
if ( ! isset( $values[ $option_id ] ) ) {
|
58 |
+
return $default_value;
|
59 |
+
}
|
60 |
+
} else {
|
61 |
+
if ( is_null( fw_akg( $sub_keys, $values[ $option_id ] ) ) ) {
|
62 |
+
return $default_value;
|
63 |
+
}
|
64 |
+
}
|
65 |
+
}
|
66 |
+
}
|
67 |
+
|
68 |
+
try {
|
69 |
+
$options = FW_Cache::get( $cache_key = 'fw_only_options/settings' );
|
70 |
+
} catch (FW_Cache_Not_Found_Exception $e) {
|
71 |
+
FW_Cache::set($cache_key, array()); // prevent recursion
|
72 |
+
FW_Cache::set(
|
73 |
+
$cache_key,
|
74 |
+
$options = fw_extract_only_options(fw()->theme->get_settings_options())
|
75 |
+
);
|
76 |
+
}
|
77 |
+
|
78 |
+
/**
|
79 |
+
* Complete missing db values with default values from options array
|
80 |
+
*/
|
81 |
+
if ($merge_values_with_defaults) {
|
82 |
+
$merge_values_with_defaults = false;
|
83 |
+
FW_Cache::set(
|
84 |
+
'fw_settings_options/values',
|
85 |
+
$values = array_merge(fw_get_options_values_from_input($options, array()), $values)
|
86 |
+
);
|
87 |
+
}
|
88 |
+
|
89 |
+
if (empty($option_id)) {
|
90 |
+
foreach ($options as $id => $option) {
|
91 |
+
$values[$id] = fw()->backend->option_type($options[$id]['type'])->storage_load(
|
92 |
+
$id, $options[$id], isset($values[$id]) ? $values[$id] : null, array()
|
93 |
+
);
|
94 |
+
}
|
95 |
+
} else {
|
96 |
+
if (isset($options[$option_id])) {
|
97 |
+
$values[ $option_id ] = fw()->backend->option_type( $options[ $option_id ]['type'] )->storage_load(
|
98 |
+
$option_id,
|
99 |
+
$options[ $option_id ],
|
100 |
+
isset($values[ $option_id ]) ? $values[ $option_id ] : null,
|
101 |
+
array()
|
102 |
+
);
|
103 |
+
}
|
104 |
+
}
|
105 |
+
|
106 |
+
if (empty($option_id)) {
|
107 |
+
return (empty($values) && is_array($default_value)) ? $default_value : $values;
|
108 |
+
} else {
|
109 |
+
if (is_null($sub_keys)) {
|
110 |
+
return isset($values[$option_id]) ? $values[$option_id] : $default_value;
|
111 |
+
} else {
|
112 |
+
return fw_akg($sub_keys, $values[$option_id], $default_value);
|
113 |
+
}
|
114 |
+
}
|
115 |
+
}
|
116 |
+
|
117 |
+
/**
|
118 |
+
* Set a theme settings option value in database
|
119 |
+
*
|
120 |
+
* @param null $option_id Specific option id (accepts multikey). null - all options
|
121 |
+
* @param mixed $value
|
122 |
+
*/
|
123 |
+
function fw_set_db_settings_option( $option_id = null, $value ) {
|
124 |
+
FW_Cache::del('fw_settings_options/values');
|
125 |
+
|
126 |
+
try {
|
127 |
+
$options = FW_Cache::get( $cache_key = 'fw_only_options/settings' );
|
128 |
+
} catch ( FW_Cache_Not_Found_Exception $e ) {
|
129 |
+
FW_Cache::set(
|
130 |
+
$cache_key,
|
131 |
+
$options = fw_extract_only_options(fw()->theme->get_settings_options())
|
132 |
+
);
|
133 |
+
}
|
134 |
+
|
135 |
+
if (empty($option_id)) {
|
136 |
+
foreach ($options as $id => $option) {
|
137 |
+
if (isset($value[$id])) {
|
138 |
+
$value[ $id ] = fw()->backend->option_type( $options[ $id ]['type'] )->storage_save(
|
139 |
+
$id, $options[ $id ], $value[$id], array()
|
140 |
+
);
|
141 |
+
}
|
142 |
+
}
|
143 |
+
} else {
|
144 |
+
if (isset($options[$option_id]) && isset($value[ $option_id ])) {
|
145 |
+
$value[ $option_id ] = fw()->backend->option_type( $options[ $option_id ]['type'] )->storage_save(
|
146 |
+
$option_id,
|
147 |
+
$options[ $option_id ],
|
148 |
+
$value[ $option_id ],
|
149 |
+
array()
|
150 |
+
);
|
151 |
+
}
|
152 |
+
}
|
153 |
+
|
154 |
+
FW_WP_Option::set(
|
155 |
+
'fw_theme_settings_options:' . fw()->theme->manifest->get_id(),
|
156 |
+
$option_id, $value
|
157 |
+
);
|
158 |
+
}
|
159 |
+
}
|
160 |
+
|
161 |
+
/** Post Options */
|
162 |
+
{
|
163 |
+
/**
|
164 |
+
* Get post option value from the database
|
165 |
+
*
|
166 |
+
* @param null|int $post_id
|
167 |
+
* @param string|null $option_id Specific option id (accepts multikey). null - all options
|
168 |
+
* @param null|mixed $default_value If no option found in the database, this value will be returned
|
169 |
+
* @param null|bool $get_original_value Original value is that with no translations and other changes
|
170 |
+
*
|
171 |
+
* @return mixed|null
|
172 |
+
*/
|
173 |
+
function fw_get_db_post_option(
|
174 |
+
$post_id = null,
|
175 |
+
$option_id = null,
|
176 |
+
$default_value = null,
|
177 |
+
$get_original_value = null
|
178 |
+
) {
|
179 |
+
$meta_key = 'fw_options';
|
180 |
+
|
181 |
+
if ( ! $post_id ) {
|
182 |
+
/** @var WP_Post $post */
|
183 |
+
global $post;
|
184 |
+
|
185 |
+
if ( ! $post ) {
|
186 |
+
return $default_value;
|
187 |
+
} else {
|
188 |
+
$post_id = $post->ID;
|
189 |
+
}
|
190 |
+
|
191 |
+
/**
|
192 |
+
* Check if is Preview and use the preview post_id instead of real/current post id
|
193 |
+
*
|
194 |
+
* Note: WordPress changes the global $post content on preview:
|
195 |
+
* 1. https://github.com/WordPress/WordPress/blob/2096b451c704715db3c4faf699a1184260deade9/wp-includes/query.php#L3573-L3583
|
196 |
+
* 2. https://github.com/WordPress/WordPress/blob/4a31dd6fe8b774d56f901a29e72dcf9523e9ce85/wp-includes/revision.php#L485-L528
|
197 |
+
*/
|
198 |
+
if ( is_preview() && is_object($preview = wp_get_post_autosave($post->ID)) ) {
|
199 |
+
$post_id = $preview->ID;
|
200 |
+
}
|
201 |
+
}
|
202 |
+
|
203 |
+
$post_type = get_post_type(
|
204 |
+
($post_revision_id = wp_is_post_revision($post_id)) ? $post_revision_id : $post_id
|
205 |
+
);
|
206 |
+
|
207 |
+
try {
|
208 |
+
$options = FW_Cache::get(
|
209 |
+
$cache_key = 'fw_post_options/only/'. $post_type
|
210 |
+
);
|
211 |
+
} catch (FW_Cache_Not_Found_Exception $e) {
|
212 |
+
FW_Cache::set($cache_key, $options = array()); // prevent recursion
|
213 |
+
|
214 |
+
if (apply_filters('fw_get_db_post_option:fw-storage-enabled',
|
215 |
+
/**
|
216 |
+
* Slider extension has too many fw_get_db_post_option()
|
217 |
+
* inside post options altering filter and it creates recursive mess.
|
218 |
+
* add_filter() was added in Slider extension
|
219 |
+
* but this hardcode can be replaced with `true`
|
220 |
+
* only after all users will install new version 1.1.15.
|
221 |
+
*/
|
222 |
+
$post_type !== 'fw-slider',
|
223 |
+
$post_type
|
224 |
+
)) {
|
225 |
+
FW_Cache::set(
|
226 |
+
$cache_key,
|
227 |
+
$options = fw_extract_only_options(
|
228 |
+
fw()->theme->get_post_options( $post_type )
|
229 |
+
)
|
230 |
+
);
|
231 |
+
}
|
232 |
+
}
|
233 |
+
|
234 |
+
if ($option_id) {
|
235 |
+
$option_id = explode('/', $option_id); // 'option_id/sub/keys'
|
236 |
+
$_option_id = array_shift($option_id); // 'option_id'
|
237 |
+
$sub_keys = empty($option_id) ? null : implode('/', $option_id); // 'sub/keys'
|
238 |
+
$option_id = $_option_id;
|
239 |
+
unset($_option_id);
|
240 |
+
|
241 |
+
$value = FW_WP_Meta::get(
|
242 |
+
'post',
|
243 |
+
$post_id,
|
244 |
+
$meta_key .'/'. $option_id,
|
245 |
+
null,
|
246 |
+
$get_original_value
|
247 |
+
);
|
248 |
+
|
249 |
+
if (isset($options[$option_id])) {
|
250 |
+
try {
|
251 |
+
$value = FW_Cache::get( $cache_key = 'fw_post_options/values/'. $post_id .'/'. $option_id );
|
252 |
+
} catch (FW_Cache_Not_Found_Exception $e) {
|
253 |
+
FW_Cache::set($cache_key, array()); // prevent recursion
|
254 |
+
FW_Cache::set(
|
255 |
+
$cache_key,
|
256 |
+
$value = fw()->backend->option_type($options[$option_id]['type'])->storage_load(
|
257 |
+
$option_id,
|
258 |
+
$options[$option_id],
|
259 |
+
$value,
|
260 |
+
array( 'post-id' => $post_id, )
|
261 |
+
)
|
262 |
+
);
|
263 |
+
}
|
264 |
+
}
|
265 |
+
|
266 |
+
if ($sub_keys) {
|
267 |
+
return fw_akg($sub_keys, $value, $default_value);
|
268 |
+
} else {
|
269 |
+
return is_null($value) ? $default_value : $value;
|
270 |
+
}
|
271 |
+
} else {
|
272 |
+
$value = FW_WP_Meta::get(
|
273 |
+
'post',
|
274 |
+
$post_id,
|
275 |
+
$meta_key,
|
276 |
+
$default_value,
|
277 |
+
$get_original_value
|
278 |
+
);
|
279 |
+
|
280 |
+
if (!is_array($value)) {
|
281 |
+
$value = array();
|
282 |
+
}
|
283 |
+
|
284 |
+
foreach ($options as $_option_id => $_option) {
|
285 |
+
try {
|
286 |
+
$value[$_option_id] = FW_Cache::get( $cache_key = 'fw_post_options/values/'. $post_id .'/'. $_option_id );
|
287 |
+
} catch (FW_Cache_Not_Found_Exception $e) {
|
288 |
+
FW_Cache::set($cache_key, array()); // prevent recursion
|
289 |
+
FW_Cache::set(
|
290 |
+
$cache_key,
|
291 |
+
$value[$_option_id] = fw()->backend->option_type($_option['type'])->storage_load(
|
292 |
+
$_option_id,
|
293 |
+
$_option,
|
294 |
+
isset($value[$_option_id]) ? $value[$_option_id] : null,
|
295 |
+
array( 'post-id' => $post_id, )
|
296 |
+
)
|
297 |
+
);
|
298 |
+
}
|
299 |
+
}
|
300 |
+
|
301 |
+
return $value;
|
302 |
+
}
|
303 |
+
}
|
304 |
+
|
305 |
+
/**
|
306 |
+
* Set post option value in database
|
307 |
+
*
|
308 |
+
* @param null|int $post_id
|
309 |
+
* @param string|null $option_id Specific option id (accepts multikey). null - all options
|
310 |
+
* @param $value
|
311 |
+
*/
|
312 |
+
function fw_set_db_post_option( $post_id = null, $option_id = null, $value ) {
|
313 |
+
FW_Cache::del('fw_post_options/values');
|
314 |
+
|
315 |
+
$meta_key = 'fw_options';
|
316 |
+
$post_id = intval($post_id);
|
317 |
+
|
318 |
+
if ( ! $post_id ) {
|
319 |
+
/** @var WP_Post $post */
|
320 |
+
global $post;
|
321 |
+
|
322 |
+
if ( ! $post ) {
|
323 |
+
return;
|
324 |
+
} else {
|
325 |
+
$post_id = $post->ID;
|
326 |
+
}
|
327 |
+
}
|
328 |
+
|
329 |
+
$post_type = get_post_type(
|
330 |
+
($post_revision_id = wp_is_post_revision($post_id)) ? $post_revision_id : $post_id
|
331 |
+
);
|
332 |
+
|
333 |
+
try {
|
334 |
+
$options = FW_Cache::get(
|
335 |
+
$cache_key = 'fw_post_options/only/'. $post_type
|
336 |
+
);
|
337 |
+
} catch (FW_Cache_Not_Found_Exception $e) {
|
338 |
+
FW_Cache::set($cache_key, $options = array()); // prevent recursion
|
339 |
+
|
340 |
+
if (apply_filters('fw_get_db_post_option:fw-storage-enabled',
|
341 |
+
/**
|
342 |
+
* Slider extension has too many fw_get_db_post_option()
|
343 |
+
* inside post options altering filter and it creates recursive mess.
|
344 |
+
* add_filter() was added in Slider extension
|
345 |
+
* but this hardcode can be replaced with `true`
|
346 |
+
* only after all users will install new version 1.1.15.
|
347 |
+
*/
|
348 |
+
$post_type !== 'fw-slider',
|
349 |
+
$post_type
|
350 |
+
)) {
|
351 |
+
FW_Cache::set(
|
352 |
+
$cache_key,
|
353 |
+
$options = fw_extract_only_options(
|
354 |
+
fw()->theme->get_post_options( $post_type )
|
355 |
+
)
|
356 |
+
);
|
357 |
+
}
|
358 |
+
}
|
359 |
+
|
360 |
+
$sub_keys = null;
|
361 |
+
|
362 |
+
if ($option_id) {
|
363 |
+
$option_id = explode('/', $option_id); // 'option_id/sub/keys'
|
364 |
+
$_option_id = array_shift($option_id); // 'option_id'
|
365 |
+
$sub_keys = empty($option_id) ? null : implode('/', $option_id); // 'sub/keys'
|
366 |
+
$option_id = $_option_id;
|
367 |
+
unset($_option_id);
|
368 |
+
|
369 |
+
$old_value = fw_get_db_post_option($post_id, $option_id);
|
370 |
+
|
371 |
+
if ($sub_keys) { // update sub_key in old_value and use the entire value
|
372 |
+
$new_value = $old_value;
|
373 |
+
fw_aks($sub_keys, $value, $new_value);
|
374 |
+
$value = $new_value;
|
375 |
+
unset($new_value);
|
376 |
+
|
377 |
+
$old_value = fw_akg($sub_keys, $old_value);
|
378 |
+
}
|
379 |
+
|
380 |
+
if (isset($options[$option_id])) {
|
381 |
+
$value = fw()->backend->option_type($options[$option_id]['type'])->storage_save(
|
382 |
+
$option_id,
|
383 |
+
$options[$option_id],
|
384 |
+
$value,
|
385 |
+
array( 'post-id' => $post_id, )
|
386 |
+
);
|
387 |
+
}
|
388 |
+
|
389 |
+
FW_WP_Meta::set( 'post', $post_id, $meta_key .'/'. $option_id, $value );
|
390 |
+
} else {
|
391 |
+
$old_value = fw_get_db_post_option($post_id);
|
392 |
+
|
393 |
+
if (!is_array($value)) {
|
394 |
+
$value = array();
|
395 |
+
}
|
396 |
+
|
397 |
+
foreach ($value as $_option_id => $_option_value) {
|
398 |
+
if (isset($options[$_option_id])) {
|
399 |
+
$value[$_option_id] = fw()->backend->option_type($options[$_option_id]['type'])->storage_save(
|
400 |
+
$_option_id,
|
401 |
+
$options[$_option_id],
|
402 |
+
$_option_value,
|
403 |
+
array( 'post-id' => $post_id, )
|
404 |
+
);
|
405 |
+
}
|
406 |
+
}
|
407 |
+
|
408 |
+
FW_WP_Meta::set( 'post', $post_id, $meta_key, $value );
|
409 |
+
}
|
410 |
+
|
411 |
+
FW_Cache::del('fw_post_options/values'); // fixes https://github.com/ThemeFuse/Unyson/issues/1538
|
412 |
+
|
413 |
+
/**
|
414 |
+
* @deprecated
|
415 |
+
*/
|
416 |
+
fw()->backend->_sync_post_separate_meta($post_id);
|
417 |
+
|
418 |
+
/**
|
419 |
+
* @since 2.2.8
|
420 |
+
*/
|
421 |
+
do_action('fw_post_options_update',
|
422 |
+
$post_id,
|
423 |
+
/**
|
424 |
+
* Option id
|
425 |
+
* First level multi-key
|
426 |
+
*
|
427 |
+
* For e.g.
|
428 |
+
*
|
429 |
+
* if $option_id is 'hello/world/7'
|
430 |
+
* this will be 'hello'
|
431 |
+
*/
|
432 |
+
$option_id,
|
433 |
+
/**
|
434 |
+
* The remaining sub-keys
|
435 |
+
*
|
436 |
+
* For e.g.
|
437 |
+
*
|
438 |
+
* if $option_id is 'hello/world/7'
|
439 |
+
* $option_id_keys will be array('world', '7')
|
440 |
+
*
|
441 |
+
* if $option_id is 'hello'
|
442 |
+
* $option_id_keys will be array()
|
443 |
+
*/
|
444 |
+
explode('/', $sub_keys),
|
445 |
+
/**
|
446 |
+
* Old post option(s) value
|
447 |
+
* @since 2.3.3
|
448 |
+
*/
|
449 |
+
$old_value
|
450 |
+
);
|
451 |
+
}
|
452 |
+
}
|
453 |
+
|
454 |
+
/** Terms Options */
|
455 |
+
{
|
456 |
+
/**
|
457 |
+
* Get term option value from the database
|
458 |
+
*
|
459 |
+
* @param int $term_id
|
460 |
+
* @param string $taxonomy
|
461 |
+
* @param string|null $option_id Specific option id (accepts multikey). null - all options
|
462 |
+
* @param null|mixed $default_value If no option found in the database, this value will be returned
|
463 |
+
* @param null|bool $get_original_value Original value is that with no translations and other changes
|
464 |
+
*
|
465 |
+
* @return mixed|null
|
466 |
+
*/
|
467 |
+
function fw_get_db_term_option( $term_id, $taxonomy, $option_id = null, $default_value = null, $get_original_value = null ) {
|
468 |
+
if ( ! taxonomy_exists( $taxonomy ) ) {
|
469 |
+
return null;
|
470 |
+
}
|
471 |
+
|
472 |
+
$option_id = 'fw_options' . ( $option_id !== null ? '/' . $option_id : '' );
|
473 |
+
|
474 |
+
return FW_WP_Meta::get( 'fw_term', $term_id, $option_id, $default_value, $get_original_value );
|
475 |
+
}
|
476 |
+
|
477 |
+
/**
|
478 |
+
* Set term option value in database
|
479 |
+
*
|
480 |
+
* @param int $term_id
|
481 |
+
* @param string $taxonomy
|
482 |
+
* @param string|null $option_id Specific option id (accepts multikey). null - all options
|
483 |
+
* @param mixed $value
|
484 |
+
*
|
485 |
+
* @return null
|
486 |
+
*/
|
487 |
+
function fw_set_db_term_option( $term_id, $taxonomy, $option_id = null, $value ) {
|
488 |
+
if ( ! taxonomy_exists( $taxonomy ) ) {
|
489 |
+
return null;
|
490 |
+
}
|
491 |
+
$option_id = 'fw_options' . ( $option_id !== null ? '/' . $option_id : '' );
|
492 |
+
|
493 |
+
FW_WP_Meta::set( 'fw_term', $term_id, $option_id, $value );
|
494 |
+
}
|
495 |
+
}
|
496 |
+
|
497 |
+
/**
|
498 |
+
* Extensions Data
|
499 |
+
*
|
500 |
+
* Used by extensions to store custom data in database.
|
501 |
+
* By using these functions, extension prevent database spam with wp options for each extension,
|
502 |
+
* because these functions store all data in one wp option.
|
503 |
+
*/
|
504 |
+
{
|
505 |
+
/**
|
506 |
+
* Get extension's data from the database
|
507 |
+
*
|
508 |
+
* @param string $extension_name
|
509 |
+
* @param string|null $multi_key The key of the data you want to get. null - all data
|
510 |
+
* @param null|mixed $default_value If no option found in the database, this value will be returned
|
511 |
+
* @param null|bool $get_original_value Original value is that with no translations and other changes
|
512 |
+
*
|
513 |
+
* @return mixed|null
|
514 |
+
*/
|
515 |
+
function fw_get_db_extension_data( $extension_name, $multi_key = null, $default_value = null, $get_original_value = null ) {
|
516 |
+
if ( ! fw()->extensions->get( $extension_name ) ) {
|
517 |
+
trigger_error( 'Invalid extension: ' . $extension_name, E_USER_WARNING );
|
518 |
+
|
519 |
+
return null;
|
520 |
+
}
|
521 |
+
|
522 |
+
if ( $multi_key ) {
|
523 |
+
$multi_key = $extension_name . '/' . $multi_key;
|
524 |
+
} else {
|
525 |
+
$multi_key = $extension_name;
|
526 |
+
}
|
527 |
+
|
528 |
+
return FW_WP_Option::get( 'fw_extensions', $multi_key, $default_value, $get_original_value );
|
529 |
+
}
|
530 |
+
|
531 |
+
/**
|
532 |
+
* Set some extension's data in database
|
533 |
+
*
|
534 |
+
* @param string $extension_name
|
535 |
+
* @param string|null $multi_key The key of the data you want to set. null - all data
|
536 |
+
* @param mixed $value
|
537 |
+
*/
|
538 |
+
function fw_set_db_extension_data( $extension_name, $multi_key = null, $value ) {
|
539 |
+
if ( ! fw()->extensions->get( $extension_name ) ) {
|
540 |
+
trigger_error( 'Invalid extensio
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|