WP Staging – DB & File Duplicator & Migration - Version 1.0.0

Version Description

  • Fix: Do not follow symlinks during file copy process
  • Fix: css error
  • Fix: Show "not-compatible" notice only when blog version is higher than plugin tested version.
  • Fix: undefined var $size
  • Fix: Check if $path is null before writing to remaining_files.json
  • Fix: $db_helper undefined message
  • Fix: Skip non utf8 encoded files during copying process
Download this release

Release Info

Developer ReneHermi
Plugin Icon 128x128 WP Staging – DB & File Duplicator & Migration
Version 1.0.0
Comparing to
See all releases

Version 1.0.0

Files changed (81) hide show
  1. CONTRIBUTING.md +38 -0
  2. LICENSE +340 -0
  3. README.md +29 -0
  4. assets/css/wpstg-admin.css +611 -0
  5. assets/css/wpstg-admin.min.css +1 -0
  6. assets/images/admin_dashboard.png +0 -0
  7. assets/images/loading.gif +0 -0
  8. assets/images/loading.svg +1 -0
  9. assets/images/logo_clean_small_212_25.png +0 -0
  10. assets/js/wpstg-admin.js +800 -0
  11. assets/js/wpstg-admin.min.js +1 -0
  12. assets/js/wpstg.js +0 -0
  13. assets/js/wpstg.min.js +0 -0
  14. includes/WPSTG_SL_Plugin_Updater.php +377 -0
  15. includes/admin/add-ons.php +59 -0
  16. includes/admin/admin-actions.php +31 -0
  17. includes/admin/admin-footer.php +41 -0
  18. includes/admin/admin-notices.php +258 -0
  19. includes/admin/admin-pages.php +56 -0
  20. includes/admin/plugins.php +62 -0
  21. includes/admin/settings/contextual-help.php +50 -0
  22. includes/admin/settings/display-settings.php +181 -0
  23. includes/admin/settings/register-settings.php +839 -0
  24. includes/admin/tools.php +460 -0
  25. includes/admin/upload-functions.php +190 -0
  26. includes/admin/welcome.php +73 -0
  27. includes/class-wpstg-html-elements.php +219 -0
  28. includes/class-wpstg-license-handler.php +368 -0
  29. includes/debug/browsers/WPChromePHP.class.php +25 -0
  30. includes/debug/browsers/WPFirePHP.class.php +28 -0
  31. includes/debug/browsers/api/chromephp/ChromePhp.php +440 -0
  32. includes/debug/browsers/api/chromephp/README +16 -0
  33. includes/debug/browsers/api/firephp/CHANGELOG.md +167 -0
  34. includes/debug/browsers/api/firephp/README.md +75 -0
  35. includes/debug/browsers/api/firephp/examples/oo.php +82 -0
  36. includes/debug/browsers/api/firephp/examples/oo.php4 +72 -0
  37. includes/debug/browsers/api/firephp/examples/procedural.php +79 -0
  38. includes/debug/browsers/api/firephp/examples/procedural.php4 +69 -0
  39. includes/debug/browsers/api/firephp/lib/FirePHPCore/FirePHP.class.php +1828 -0
  40. includes/debug/browsers/api/firephp/lib/FirePHPCore/FirePHP.class.php4 +1327 -0
  41. includes/debug/browsers/api/firephp/lib/FirePHPCore/fb.php +275 -0
  42. includes/debug/browsers/api/firephp/lib/FirePHPCore/fb.php4 +245 -0
  43. includes/debug/browsers/api/firephp/package.json +42 -0
  44. includes/debug/browsers/api/firephp/program.json +5 -0
  45. includes/debug/browsers/api/firephp/tests/API/newlines.php +12 -0
  46. includes/debug/browsers/api/firephp/tests/FirePHPCore/FirePHPTest.php +181 -0
  47. includes/debug/browsers/api/firephp/tests/TestHelper.php +55 -0
  48. includes/debug/browsers/api/firephp/tests/phpunit.xml +2 -0
  49. includes/debug/browsers/api/firephp/workspace/README.md +19 -0
  50. includes/debug/browsers/api/firephp/workspace/lib/project.js +5 -0
  51. includes/debug/browsers/api/firephp/workspace/package.json +28 -0
  52. includes/debug/browsers/api/firephp/workspace/program.json +78 -0
  53. includes/debug/browsers/api/firephp/workspace/scripts/build.js +164 -0
  54. includes/debug/browsers/api/firephp/workspace/scripts/publish.js +65 -0
  55. includes/debug/browsers/api/firephp/workspace/tpl/license.tpl.md +21 -0
  56. includes/debug/browsers/api/firephp/workspace/tpl/pear.package.tpl.xml +61 -0
  57. includes/debug/browsers/api/firephp/workspace/tpl/readme.tpl.md +17 -0
  58. includes/debug/classes/wpstgDebug.class.php +277 -0
  59. includes/debug/classes/wpstgDebug.interface.php +11 -0
  60. includes/install.php +160 -0
  61. includes/libraries/RolingCurlX.php +218 -0
  62. includes/libraries/browser.php +1082 -0
  63. includes/logger.php +128 -0
  64. includes/scripts.php +79 -0
  65. includes/staging-functions.php +114 -0
  66. includes/template-functions.php +1722 -0
  67. includes/wpstg-sanitize.php +158 -0
  68. languages/Gruntfile.js +105 -0
  69. languages/README.md +18 -0
  70. languages/index.php +6 -0
  71. languages/readme.txt +16 -0
  72. languages/wpstg-en_EN.mo +0 -0
  73. languages/wpstg-en_EN.po +648 -0
  74. languages/wpstg.mo +0 -0
  75. languages/wpstg.po +648 -0
  76. license.txt +281 -0
  77. optimizer/wp-staging-optimizer.php +147 -0
  78. readme.txt +203 -0
  79. templates/wpstg.min.css +0 -0
  80. uninstall.php +35 -0
  81. wp-staging.php +248 -0
CONTRIBUTING.md ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #Contribute To Quick Adsense Reloaded
2
+
3
+ Community made patches, localisations, bug reports and contributions are always welcome.
4
+
5
+ When contributing please ensure you follow the guidelines below so that we can keep on top of things.
6
+
7
+ __Please Note:__ GitHub is for bug reports and contributions only.
8
+
9
+ ## Getting Started
10
+
11
+ * Submit a ticket for your issue, assuming one does not already exist.
12
+ * Raise it on our [Issue Tracker](https://github.com/rene-hermenau/quick-adsense-reloaded/issues)
13
+ * Clearly describe the issue including steps to reproduce the bug.
14
+ * Make sure you fill in the earliest version that you know has the issue as well as the version of WordPress you're using.
15
+
16
+ ## Making Changes
17
+
18
+ * Fork the repository on GitHub
19
+ * Make the changes to your forked repository
20
+ * Ensure you stick to the [WordPress Coding Standards](https://codex.wordpress.org/WordPress_Coding_Standards)
21
+ * When committing, reference your issue (if present) and include a note about the fix
22
+ * (coming soon) If possible, and if applicable, please also add/update unit tests for your changes
23
+ * Push the changes to your fork and submit a pull request to the 'master' branch of the Quick Adsense Reloaded repository
24
+
25
+ ## Code Documentation
26
+
27
+ * We ensure that every Quick Adsense Reloaded function is documented well and follows the standards set by phpDoc
28
+ * An example function can be found [here](https://gist.github.com/rene-hermenau/8d3d7ee0633ee2f64b4b)
29
+ * Please make sure that every function is documented so that when we update our API Documentation it will complete
30
+ * If you're adding/editing a function in a class, make sure to add `@access {private|public|protected}`
31
+ * Finally, please use tabs and not spaces. The tab indent size should be 4 for all Quick Adsense Reloaded code.
32
+
33
+ At this point you're waiting on us to merge your pull request. We'll review all pull requests, and make suggestions and changes if necessary.
34
+
35
+ # Additional Resources
36
+ * [General GitHub Documentation](http://help.github.com/)
37
+ * [GitHub Pull Request documentation](http://help.github.com/send-pull-requests/)
38
+ * [PHPUnit Tests Guide](http://phpunit.de/manual/current/en/writing-tests-for-phpunit.html)
LICENSE ADDED
@@ -0,0 +1,340 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 2, June 1991
3
+
4
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
5
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6
+ Everyone is permitted to copy and distribute verbatim copies
7
+ of this license document, but changing it is not allowed.
8
+
9
+ Preamble
10
+
11
+ The licenses for most software are designed to take away your
12
+ freedom to share and change it. By contrast, the GNU General Public
13
+ License is intended to guarantee your freedom to share and change free
14
+ software--to make sure the software is free for all its users. This
15
+ General Public License applies to most of the Free Software
16
+ Foundation's software and to any other program whose authors commit to
17
+ using it. (Some other Free Software Foundation software is covered by
18
+ the GNU Lesser General Public License instead.) You can apply it to
19
+ your programs, too.
20
+
21
+ When we speak of free software, we are referring to freedom, not
22
+ price. Our General Public Licenses are designed to make sure that you
23
+ have the freedom to distribute copies of free software (and charge for
24
+ this service if you wish), that you receive source code or can get it
25
+ if you want it, that you can change the software or use pieces of it
26
+ in new free programs; and that you know you can do these things.
27
+
28
+ To protect your rights, we need to make restrictions that forbid
29
+ anyone to deny you these rights or to ask you to surrender the rights.
30
+ These restrictions translate to certain responsibilities for you if you
31
+ distribute copies of the software, or if you modify it.
32
+
33
+ For example, if you distribute copies of such a program, whether
34
+ gratis or for a fee, you must give the recipients all the rights that
35
+ you have. You must make sure that they, too, receive or can get the
36
+ source code. And you must show them these terms so they know their
37
+ rights.
38
+
39
+ We protect your rights with two steps: (1) copyright the software, and
40
+ (2) offer you this license which gives you legal permission to copy,
41
+ distribute and/or modify the software.
42
+
43
+ Also, for each author's protection and ours, we want to make certain
44
+ that everyone understands that there is no warranty for this free
45
+ software. If the software is modified by someone else and passed on, we
46
+ want its recipients to know that what they have is not the original, so
47
+ that any problems introduced by others will not reflect on the original
48
+ authors' reputations.
49
+
50
+ Finally, any free program is threatened constantly by software
51
+ patents. We wish to avoid the danger that redistributors of a free
52
+ program will individually obtain patent licenses, in effect making the
53
+ program proprietary. To prevent this, we have made it clear that any
54
+ patent must be licensed for everyone's free use or not licensed at all.
55
+
56
+ The precise terms and conditions for copying, distribution and
57
+ modification follow.
58
+
59
+ GNU GENERAL PUBLIC LICENSE
60
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61
+
62
+ 0. This License applies to any program or other work which contains
63
+ a notice placed by the copyright holder saying it may be distributed
64
+ under the terms of this General Public License. The "Program", below,
65
+ refers to any such program or work, and a "work based on the Program"
66
+ means either the Program or any derivative work under copyright law:
67
+ that is to say, a work containing the Program or a portion of it,
68
+ either verbatim or with modifications and/or translated into another
69
+ language. (Hereinafter, translation is included without limitation in
70
+ the term "modification".) Each licensee is addressed as "you".
71
+
72
+ Activities other than copying, distribution and modification are not
73
+ covered by this License; they are outside its scope. The act of
74
+ running the Program is not restricted, and the output from the Program
75
+ is covered only if its contents constitute a work based on the
76
+ Program (independent of having been made by running the Program).
77
+ Whether that is true depends on what the Program does.
78
+
79
+ 1. You may copy and distribute verbatim copies of the Program's
80
+ source code as you receive it, in any medium, provided that you
81
+ conspicuously and appropriately publish on each copy an appropriate
82
+ copyright notice and disclaimer of warranty; keep intact all the
83
+ notices that refer to this License and to the absence of any warranty;
84
+ and give any other recipients of the Program a copy of this License
85
+ along with the Program.
86
+
87
+ You may charge a fee for the physical act of transferring a copy, and
88
+ you may at your option offer warranty protection in exchange for a fee.
89
+
90
+ 2. You may modify your copy or copies of the Program or any portion
91
+ of it, thus forming a work based on the Program, and copy and
92
+ distribute such modifications or work under the terms of Section 1
93
+ above, provided that you also meet all of these conditions:
94
+
95
+ a) You must cause the modified files to carry prominent notices
96
+ stating that you changed the files and the date of any change.
97
+
98
+ b) You must cause any work that you distribute or publish, that in
99
+ whole or in part contains or is derived from the Program or any
100
+ part thereof, to be licensed as a whole at no charge to all third
101
+ parties under the terms of this License.
102
+
103
+ c) If the modified program normally reads commands interactively
104
+ when run, you must cause it, when started running for such
105
+ interactive use in the most ordinary way, to print or display an
106
+ announcement including an appropriate copyright notice and a
107
+ notice that there is no warranty (or else, saying that you provide
108
+ a warranty) and that users may redistribute the program under
109
+ these conditions, and telling the user how to view a copy of this
110
+ License. (Exception: if the Program itself is interactive but
111
+ does not normally print such an announcement, your work based on
112
+ the Program is not required to print an announcement.)
113
+
114
+ These requirements apply to the modified work as a whole. If
115
+ identifiable sections of that work are not derived from the Program,
116
+ and can be reasonably considered independent and separate works in
117
+ themselves, then this License, and its terms, do not apply to those
118
+ sections when you distribute them as separate works. But when you
119
+ distribute the same sections as part of a whole which is a work based
120
+ on the Program, the distribution of the whole must be on the terms of
121
+ this License, whose permissions for other licensees extend to the
122
+ entire whole, and thus to each and every part regardless of who wrote it.
123
+
124
+ Thus, it is not the intent of this section to claim rights or contest
125
+ your rights to work written entirely by you; rather, the intent is to
126
+ exercise the right to control the distribution of derivative or
127
+ collective works based on the Program.
128
+
129
+ In addition, mere aggregation of another work not based on the Program
130
+ with the Program (or with a work based on the Program) on a volume of
131
+ a storage or distribution medium does not bring the other work under
132
+ the scope of this License.
133
+
134
+ 3. You may copy and distribute the Program (or a work based on it,
135
+ under Section 2) in object code or executable form under the terms of
136
+ Sections 1 and 2 above provided that you also do one of the following:
137
+
138
+ a) Accompany it with the complete corresponding machine-readable
139
+ source code, which must be distributed under the terms of Sections
140
+ 1 and 2 above on a medium customarily used for software interchange; or,
141
+
142
+ b) Accompany it with a written offer, valid for at least three
143
+ years, to give any third party, for a charge no more than your
144
+ cost of physically performing source distribution, a complete
145
+ machine-readable copy of the corresponding source code, to be
146
+ distributed under the terms of Sections 1 and 2 above on a medium
147
+ customarily used for software interchange; or,
148
+
149
+ c) Accompany it with the information you received as to the offer
150
+ to distribute corresponding source code. (This alternative is
151
+ allowed only for noncommercial distribution and only if you
152
+ received the program in object code or executable form with such
153
+ an offer, in accord with Subsection b above.)
154
+
155
+ The source code for a work means the preferred form of the work for
156
+ making modifications to it. For an executable work, complete source
157
+ code means all the source code for all modules it contains, plus any
158
+ associated interface definition files, plus the scripts used to
159
+ control compilation and installation of the executable. However, as a
160
+ special exception, the source code distributed need not include
161
+ anything that is normally distributed (in either source or binary
162
+ form) with the major components (compiler, kernel, and so on) of the
163
+ operating system on which the executable runs, unless that component
164
+ itself accompanies the executable.
165
+
166
+ If distribution of executable or object code is made by offering
167
+ access to copy from a designated place, then offering equivalent
168
+ access to copy the source code from the same place counts as
169
+ distribution of the source code, even though third parties are not
170
+ compelled to copy the source along with the object code.
171
+
172
+ 4. You may not copy, modify, sublicense, or distribute the Program
173
+ except as expressly provided under this License. Any attempt
174
+ otherwise to copy, modify, sublicense or distribute the Program is
175
+ void, and will automatically terminate your rights under this License.
176
+ However, parties who have received copies, or rights, from you under
177
+ this License will not have their licenses terminated so long as such
178
+ parties remain in full compliance.
179
+
180
+ 5. You are not required to accept this License, since you have not
181
+ signed it. However, nothing else grants you permission to modify or
182
+ distribute the Program or its derivative works. These actions are
183
+ prohibited by law if you do not accept this License. Therefore, by
184
+ modifying or distributing the Program (or any work based on the
185
+ Program), you indicate your acceptance of this License to do so, and
186
+ all its terms and conditions for copying, distributing or modifying
187
+ the Program or works based on it.
188
+
189
+ 6. Each time you redistribute the Program (or any work based on the
190
+ Program), the recipient automatically receives a license from the
191
+ original licensor to copy, distribute or modify the Program subject to
192
+ these terms and conditions. You may not impose any further
193
+ restrictions on the recipients' exercise of the rights granted herein.
194
+ You are not responsible for enforcing compliance by third parties to
195
+ this License.
196
+
197
+ 7. If, as a consequence of a court judgment or allegation of patent
198
+ infringement or for any other reason (not limited to patent issues),
199
+ conditions are imposed on you (whether by court order, agreement or
200
+ otherwise) that contradict the conditions of this License, they do not
201
+ excuse you from the conditions of this License. If you cannot
202
+ distribute so as to satisfy simultaneously your obligations under this
203
+ License and any other pertinent obligations, then as a consequence you
204
+ may not distribute the Program at all. For example, if a patent
205
+ license would not permit royalty-free redistribution of the Program by
206
+ all those who receive copies directly or indirectly through you, then
207
+ the only way you could satisfy both it and this License would be to
208
+ refrain entirely from distribution of the Program.
209
+
210
+ If any portion of this section is held invalid or unenforceable under
211
+ any particular circumstance, the balance of the section is intended to
212
+ apply and the section as a whole is intended to apply in other
213
+ circumstances.
214
+
215
+ It is not the purpose of this section to induce you to infringe any
216
+ patents or other property right claims or to contest validity of any
217
+ such claims; this section has the sole purpose of protecting the
218
+ integrity of the free software distribution system, which is
219
+ implemented by public license practices. Many people have made
220
+ generous contributions to the wide range of software distributed
221
+ through that system in reliance on consistent application of that
222
+ system; it is up to the author/donor to decide if he or she is willing
223
+ to distribute software through any other system and a licensee cannot
224
+ impose that choice.
225
+
226
+ This section is intended to make thoroughly clear what is believed to
227
+ be a consequence of the rest of this License.
228
+
229
+ 8. If the distribution and/or use of the Program is restricted in
230
+ certain countries either by patents or by copyrighted interfaces, the
231
+ original copyright holder who places the Program under this License
232
+ may add an explicit geographical distribution limitation excluding
233
+ those countries, so that distribution is permitted only in or among
234
+ countries not thus excluded. In such case, this License incorporates
235
+ the limitation as if written in the body of this License.
236
+
237
+ 9. The Free Software Foundation may publish revised and/or new versions
238
+ of the General Public License from time to time. Such new versions will
239
+ be similar in spirit to the present version, but may differ in detail to
240
+ address new problems or concerns.
241
+
242
+ Each version is given a distinguishing version number. If the Program
243
+ specifies a version number of this License which applies to it and "any
244
+ later version", you have the option of following the terms and conditions
245
+ either of that version or of any later version published by the Free
246
+ Software Foundation. If the Program does not specify a version number of
247
+ this License, you may choose any version ever published by the Free Software
248
+ Foundation.
249
+
250
+ 10. If you wish to incorporate parts of the Program into other free
251
+ programs whose distribution conditions are different, write to the author
252
+ to ask for permission. For software which is copyrighted by the Free
253
+ Software Foundation, write to the Free Software Foundation; we sometimes
254
+ make exceptions for this. Our decision will be guided by the two goals
255
+ of preserving the free status of all derivatives of our free software and
256
+ of promoting the sharing and reuse of software generally.
257
+
258
+ NO WARRANTY
259
+
260
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261
+ FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263
+ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264
+ OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266
+ TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267
+ PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268
+ REPAIR OR CORRECTION.
269
+
270
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272
+ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273
+ INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274
+ OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275
+ TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276
+ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277
+ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278
+ POSSIBILITY OF SUCH DAMAGES.
279
+
280
+ END OF TERMS AND CONDITIONS
281
+
282
+ How to Apply These Terms to Your New Programs
283
+
284
+ If you develop a new program, and you want it to be of the greatest
285
+ possible use to the public, the best way to achieve this is to make it
286
+ free software which everyone can redistribute and change under these terms.
287
+
288
+ To do so, attach the following notices to the program. It is safest
289
+ to attach them to the start of each source file to most effectively
290
+ convey the exclusion of warranty; and each file should have at least
291
+ the "copyright" line and a pointer to where the full notice is found.
292
+
293
+ {description}
294
+ Copyright (C) {year} {fullname}
295
+
296
+ This program is free software; you can redistribute it and/or modify
297
+ it under the terms of the GNU General Public License as published by
298
+ the Free Software Foundation; either version 2 of the License, or
299
+ (at your option) any later version.
300
+
301
+ This program is distributed in the hope that it will be useful,
302
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
303
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304
+ GNU General Public License for more details.
305
+
306
+ You should have received a copy of the GNU General Public License along
307
+ with this program; if not, write to the Free Software Foundation, Inc.,
308
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309
+
310
+ Also add information on how to contact you by electronic and paper mail.
311
+
312
+ If the program is interactive, make it output a short notice like this
313
+ when it starts in an interactive mode:
314
+
315
+ Gnomovision version 69, Copyright (C) year name of author
316
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317
+ This is free software, and you are welcome to redistribute it
318
+ under certain conditions; type `show c' for details.
319
+
320
+ The hypothetical commands `show w' and `show c' should show the appropriate
321
+ parts of the General Public License. Of course, the commands you use may
322
+ be called something other than `show w' and `show c'; they could even be
323
+ mouse-clicks or menu items--whatever suits your program.
324
+
325
+ You should also get your employer (if you work as a programmer) or your
326
+ school, if any, to sign a "copyright disclaimer" for the program, if
327
+ necessary. Here is a sample; alter the names:
328
+
329
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
331
+
332
+ {signature of Ty Coon}, 1 April 1989
333
+ Ty Coon, President of Vice
334
+
335
+ This General Public License does not permit incorporating your program into
336
+ proprietary programs. If your program is a subroutine library, you may
337
+ consider it more useful to permit linking proprietary applications with the
338
+ library. If this is what you want to do, use the GNU Lesser General
339
+ Public License instead of this License.
340
+
README.md ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ### Welcome to the WP-Staging repository
2
+
3
+ ## Note ##
4
+
5
+ This is the latest developer version of WP-Staging for WordPress.
6
+
7
+ ## Installation ##
8
+
9
+ 1. You can clone the GitHub repository: `https://github.com/rene-hermenau/wp-staging.git`
10
+ 2. Or download it directly as a ZIP file: `https://github.com/rene-hermenau/wp-staging/archive/master.zip`
11
+ 3. Upload it via WordPress->Plugin->AddNew
12
+
13
+ This will download the latest developer copy of WP-Staging.
14
+
15
+ ## Bugs ##
16
+ If you find an issue, let us know [here](https://github.com/rene-hermenau/wp-staging/issues?state=open)!
17
+
18
+ ## Support ##
19
+ This is a developer's portal for WP-Staging
20
+
21
+ ## Contributions ##
22
+ Anyone is welcome to contribute to WP-Staging. Please read the [guidelines for contributing](https://github.com/rene-hermenau/wp-staging/blob/master/CONTRIBUTING.md) to this repository.
23
+
24
+ There are various ways you can contribute:
25
+
26
+ 1. Raise an [Issue](https://github.com/rene-hermenau/wp-staging/issues) on GitHub
27
+ 2. Send us a Pull Request with your bug fixes and/or new features
28
+ 3. Translate WP-Staging into different languages
29
+ 4. Provide feedback and suggestions on [enhancements](https://github.com/rene-hermenau/wp-staging/issues?direction=desc&labels=Enhancement&page=1&sort=created&state=open)
assets/css/wpstg-admin.css ADDED
@@ -0,0 +1,611 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * WPSTG Admin CSS
3
+ *
4
+ * @package WPSTG
5
+ * @subpackage Admin CSS
6
+ * @copyright Copyright (c) 2015, René Hermenau
7
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
8
+ */
9
+
10
+
11
+ /* CSS for Tabs */
12
+
13
+ #tab_container ul {
14
+ /*height: 200px;*/
15
+ list-style: none;
16
+ margin: 0;
17
+ padding: 0;
18
+ background: #f1f1f1;
19
+ float: left;
20
+ padding-top: 0px;
21
+ /*list-style-type: square;*/
22
+ }
23
+
24
+ #tab_container ul li:first-child.selected-tab {
25
+ border-top: none;
26
+ }
27
+
28
+ #tab_container ul li a.selected-tab {
29
+ font-weight: bold;
30
+ text-decoration: none;
31
+ }
32
+
33
+
34
+
35
+ #tab_container .row{
36
+ padding-top:10px;
37
+ padding-bottom:12px;
38
+ }
39
+
40
+
41
+ #tab_container .row label strong, #tab_container .row strong {
42
+ font-weight: bold;
43
+ }
44
+
45
+ .wpstg-tabs a {
46
+ padding:5px;
47
+ }
48
+
49
+ #tab_container > ul > li.wpstg-tabs.active {
50
+ background-color:white;
51
+ }
52
+
53
+
54
+
55
+ #wpstg_settingsgeneral_header .row:nth-child(3), #wpstg_settingsgeneral_header .row:nth-child(4){
56
+ display:none;
57
+ }
58
+
59
+ /* Layout of admin table and rows
60
+ */
61
+
62
+ #tab_container .panel-container {
63
+ background: #FFF;
64
+ padding:20px;
65
+ padding-top:0px;
66
+ overflow:auto;
67
+ }
68
+
69
+ #tab_container .form-table th {
70
+ vertical-align: top;
71
+ text-align: left;
72
+ padding: 20px 10px 20px 0;
73
+ width: 200px;
74
+ line-height: 1.3;
75
+ font-weight: bold;
76
+ font-size: 14px;
77
+ color:#484848;
78
+ width: 30%;
79
+ }
80
+
81
+ #tab_container .form-table tr {
82
+ border-bottom: 1px solid #E7E7E7;
83
+ }
84
+
85
+ #tab_container span.description{
86
+ display: block;
87
+ font-weight: 400;
88
+ font-style: normal;
89
+ font-size: 13px;
90
+ margin-top: 7px;
91
+ color:#484848;
92
+ }
93
+
94
+ #tab_container .col-title{
95
+ color:#484848;
96
+ }
97
+
98
+ @media only screen and (max-width:680px) {
99
+ #tab_container ul {
100
+ float:none;
101
+ }
102
+ #tab_container .form-table tr > th {
103
+ width:100%;
104
+ }
105
+ #tab_container span.description{
106
+ font-size: 14px;
107
+ }
108
+ #tab_container .form-table tr > th, #tab_container .form-table tr > td {
109
+ padding:10px;
110
+ }
111
+ }
112
+
113
+ #tab_container ul li {
114
+ margin-bottom:0px;
115
+ }
116
+
117
+ #tab_container ul li a {
118
+ display: block;
119
+ padding:10px 4px 10px 14px;
120
+ border-width: 1px 0;
121
+ border-style: solid;
122
+ border-top-color:white;
123
+ border-bottom-color:#e7e7e7;
124
+ text-decoration: none;
125
+ color: #0097DF;
126
+ font-weight: bold;
127
+ }
128
+ #tab_container ul li a:hover {
129
+ background-color: #e5e5e5;
130
+ color:#777777;
131
+ }
132
+
133
+
134
+ .wp-staginglogo{
135
+ display: block;
136
+ font-size:16px;
137
+ padding-top:20px;
138
+ width:220px;
139
+ float:left;
140
+ }
141
+
142
+ .wpstg-version{
143
+ display: block;
144
+ padding-top:29px
145
+ }
146
+
147
+
148
+ .wpstg_admin .nav-tab {
149
+ color: #3C3C3C;
150
+ }
151
+
152
+
153
+ #tab_container table tbody tr:nth-child(1) > th > div {
154
+ font-size: 20px;
155
+ }
156
+
157
+ .wpstg_hidden{
158
+ display: none;
159
+ }
160
+
161
+ /* End layout of admin table and rows
162
+ */
163
+ #mashtabcontainer > .mashtabs {
164
+ background-color: #ffffff;
165
+ }
166
+
167
+ #mashtabcontainer ul .active {
168
+ background-color: #00adef;
169
+ color: white;
170
+ border-bottom-color: #0098D2;
171
+ }
172
+
173
+ #mashtabcontainer ul .active:hover {
174
+ background-color: #00A4E2;
175
+ color: white;
176
+ border-bottom-color: #0098D2;
177
+ }
178
+
179
+ #mashtabcontainer ul li a {
180
+ padding: 10px 14px 10px 14px;
181
+ background-color: #f3f3f3
182
+
183
+ }
184
+
185
+ #mashtabcontainer .mashtab-container {
186
+ border: 0px solid #ececec;
187
+ }
188
+
189
+ /* Cloning workflow */
190
+ #wpstg-clonepage-wrapper {
191
+ margin-bottom: 20px;
192
+ width: 690px;
193
+ }
194
+
195
+ @media screen and (min-width:1090px){
196
+ #wpstg-clonepage-wrapper {
197
+ float: left;
198
+ margin-bottom: 20px;
199
+ width: 690px;
200
+ }
201
+ .wpstg-sidebar{
202
+ display: none;
203
+ margin-left: 700px;
204
+ margin-top: 138px;
205
+ }
206
+ }
207
+
208
+ .wpstg-sidebar{
209
+ display: none;
210
+ padding: 10px;
211
+ border: 1px solid #DFDFDF;
212
+ max-width: 250px;
213
+ height: 250px;
214
+ }
215
+
216
+ #wpstg-steps {
217
+ margin-top:30px;
218
+ }
219
+
220
+ #wpstg-steps li {
221
+ color: #444;
222
+ line-height: 20px;
223
+ padding-right:10px;
224
+ float:left;
225
+ }
226
+
227
+
228
+ .wpstg-step-num {
229
+ border: 1px solid #444;
230
+ border-radius: 3px;
231
+ display: inline-block;
232
+ width: 20px;
233
+ height: 20px;
234
+ text-align: center;
235
+ margin-right: 5px;
236
+ }
237
+
238
+ .wpstg-current-step {
239
+ font-weight: bold;
240
+ }
241
+
242
+ .wpstg-current-step .wpstg-step-num {
243
+ background: #444;
244
+ color: #eee;
245
+ }
246
+
247
+ .wpstg-clone {
248
+ border: 3px solid #ffffff;
249
+ margin-bottom: 5px;
250
+ padding: 5px 10px;
251
+ width: 300px;
252
+ position: relative;
253
+ overflow: hidden;
254
+ transition: border-color .2s ease-in-out;
255
+ background-color: #DDDDDD;
256
+ }
257
+
258
+ .wpstg-clone.active {
259
+ border-color: #1d94cf;;
260
+ }
261
+
262
+ .wpstg-clone-title {
263
+ display: inline-block;
264
+ font-size: 15px;
265
+ max-width: 130px;
266
+ text-decoration: none;
267
+ font-weight: bold;
268
+ color:#0285AE;
269
+ }
270
+
271
+ .wpstg-clone-action {
272
+ background: #ffffff;
273
+ border-left: 1px solid #ccc;
274
+ color: #bbb;
275
+ display: none;
276
+ padding: 0 5px;
277
+ float: right;
278
+ text-decoration: none;
279
+ position: relative;
280
+ transition: color .2s ease-in-out;
281
+ }
282
+
283
+ .wpstg-remove-clone:hover {
284
+ color: #ef6d6d;
285
+ }
286
+
287
+ .wpstg-clone-action:last-child {
288
+ border: none;
289
+ }
290
+
291
+ .wpstg-clone:hover .wpstg-clone-action {
292
+ display: inline-block;
293
+ }
294
+
295
+ #wpstg-show-error-details:focus,
296
+ #wpstg-workflow .wpstg-clone-action {
297
+ outline: none;
298
+ box-shadow: none;
299
+ }
300
+
301
+ .wpstg-link-btn {
302
+ background: #45a1c9;
303
+ color: #fff;
304
+ display: inline-block;
305
+ padding: 5px 10px;
306
+ text-decoration: none;
307
+ vertical-align: baseline;
308
+ transition: all .2s ease-in-out;
309
+ }
310
+
311
+ .wpstg-link-btn:hover,
312
+ .wpstg-link-btn:focus {
313
+ color: #fff;
314
+ outline: none;
315
+ box-shadow: none;
316
+ }
317
+
318
+ #wpstg-workflow .wpstg-link-btn:active {
319
+ vertical-align: baseline;
320
+ }
321
+
322
+ .wpstg-link-btn[disabled] {
323
+ background: #777 !important;
324
+ pointer-events: none;
325
+ }
326
+
327
+ #wpstg-cancel-cloning {
328
+ background: #ff3428;
329
+ border-color: #e72f24;
330
+ margin-top: 5px;
331
+ }
332
+
333
+ #wpstg-cancel-cloning.success {
334
+ background: #64dd58;
335
+ border-color: #54bd4a;
336
+ }
337
+
338
+ #wpstg-error-wrapper,
339
+ #wpstg-error-details {
340
+ display: none;
341
+ margin-top: 10px;
342
+ font-size: 13px;
343
+ }
344
+
345
+ #wpstg-show-error-details {
346
+ display: inline-block;
347
+ margin-left: 5px;
348
+ color: #555;
349
+ text-decoration: none;
350
+ transition: color .2s ease-in-out;
351
+ }
352
+
353
+ #wpstg-show-error-details:hover {
354
+ color: #1d94cf;
355
+ }
356
+
357
+ #wpstg-error-details {
358
+ border-left: 5px solid #ef6d6d;
359
+ padding: 10px;
360
+ width: 500px;
361
+ }
362
+
363
+ #wpstg-home-link,
364
+ #wpstg-try-again {
365
+ display: none;
366
+ }
367
+
368
+
369
+ #wpstg-loader {
370
+ content: url('../images/loading.gif');
371
+ margin-top:-5px;
372
+ }
373
+
374
+ #wpstg-workflow {
375
+ position: relative;
376
+ clear:both;
377
+ padding-top:20px;
378
+ margin-right:20px;
379
+ }
380
+
381
+ #wpstg-workflow.loading::after,
382
+ #wpstg-removing-clone.loading::after {
383
+ background: rgba(255, 255, 255, .7);
384
+ content: 'LOADING May take little bit longer for large sites... ';
385
+ display: block;
386
+ width: 100%;
387
+ height: 100%;
388
+ font-size: 20px;
389
+ padding-top: 100px;
390
+ text-align: center;
391
+ position: absolute;
392
+ top: 0;
393
+ left: 0;
394
+ z-index: 99;
395
+ }
396
+
397
+ #wpstg-removing-clone.loading::after {
398
+ content: 'REMOVING' !important;
399
+ }
400
+
401
+ #wpstg-existing-clones,
402
+ #wpstg-removing-clone {
403
+ position: relative;
404
+ }
405
+
406
+ .wpstg-progress-bar {
407
+ max-width: 900px;
408
+ height: 27px;
409
+ padding: 0px;
410
+ background-color: #d6d8d7;
411
+ }
412
+
413
+ .wpstg-progress {
414
+ background: #1d94cf;
415
+ width: 0;
416
+ height: 100%;
417
+ transition: width 1s;
418
+ color:white;
419
+ line-height:25px;
420
+ text-align:center;
421
+ }
422
+
423
+ #wpstg-new-clone-id.wpstg-error-input,
424
+ #wpstg-clone-path.wpstg-error-input {
425
+ border: 1px solid #ff4235;
426
+ box-shadow: 0 0 2px rgba(255, 66, 53, .8);
427
+ }
428
+
429
+ #wpstg-clone-path {
430
+ margin-left: 10px;
431
+ width: 350px;
432
+ }
433
+
434
+ .wpstg-error-msg {
435
+ color: #ff4235;
436
+ }
437
+
438
+ #wpstg-start-cloning + .wpstg-error-msg {
439
+ display: block;
440
+ margin-top: 5px;
441
+ }
442
+
443
+ .wpstg-size-info {
444
+ color: #999;
445
+ font-weight: normal;
446
+ position: relative;
447
+ left: 2px;
448
+ }
449
+
450
+ .wpstg-db-table .wpstg-size-info {
451
+ top: 2px;
452
+ }
453
+
454
+ #wpstg-workflow #wpstg-start-cloning {
455
+ display: inline-block;
456
+ margin-left: 5px;
457
+ font-size: 14px;
458
+ vertical-align: baseline;
459
+ }
460
+
461
+ /* Tabs */
462
+ .wpstg-tabs-wrapper {
463
+ max-width: 640px;
464
+ margin: 10px 0;
465
+ }
466
+
467
+ #wpstg-path-wrapper {
468
+ border-bottom: 2px dashed #ccc;
469
+ padding-bottom: 10px;
470
+ margin-bottom: 10px;
471
+ }
472
+
473
+ .wpstg-tabs-wrapper {
474
+ border: 1px solid #ddd;
475
+ border-right: none;
476
+ border-left: none;
477
+ }
478
+
479
+ .wpstg-tab-section {
480
+ border: 1px solid #ddd;
481
+ border-right: none;
482
+ border-left: none;
483
+ display: none;
484
+ padding: 20px;
485
+ }
486
+
487
+ .wpstg-tab-section::after {
488
+ display: block;
489
+ content: '';
490
+ clear: both;
491
+ }
492
+
493
+ .wpstg-tab-header {
494
+ border: 1px solid #ddd;
495
+ border-right: none;
496
+ border-left: none;
497
+ color: #444;
498
+ font-size: 16px;
499
+ font-weight: bolder;
500
+ display: block;
501
+ padding: 10px;;
502
+ text-decoration: none;
503
+ }
504
+
505
+ .wpstg-tab-triangle {
506
+ display: inline-block;
507
+ margin-right: 10px;
508
+ }
509
+
510
+ .wpstg-tab-header:focus {
511
+ color: #444;
512
+ outline: none;
513
+ box-shadow: none;
514
+ }
515
+
516
+ #wpstg-large-files {
517
+ display:none;
518
+ border: 1px dashed #ccc;
519
+ /*float: right;*/
520
+ padding: 10px 10px 10px;
521
+ margin-top:20px;
522
+ position: relative;
523
+ font-size:12px;
524
+ }
525
+
526
+ #wpstg-large-files h3 {
527
+ background: #fff;
528
+ margin: 0;
529
+ padding: 0 5px;
530
+ position: absolute;
531
+ top: -10px;
532
+ left: 5px;
533
+ }
534
+
535
+ /* tmp */
536
+ .wpstg-subdir {
537
+ display: none;
538
+ margin-left: 20px;
539
+ }
540
+
541
+ .wpstg-dir a.disabled {
542
+ color: #888;
543
+ cursor: default;
544
+ text-decoration: none;
545
+ }
546
+
547
+ .wpstg-check-subdirs {
548
+ display: inline-block;
549
+ margin-left: 10px;
550
+ }
551
+
552
+ .wpstg-notice-alert{
553
+ display:block;
554
+ background-color:#FFD0D0;
555
+ padding:20px;
556
+ border: 1px solid #fff;
557
+ max-width: 600px;
558
+ }
559
+
560
+ .wpstg-header{
561
+ font-weight: 400;
562
+ line-height: 1.6em;
563
+ font-size: 19px;
564
+ border-bottom: 1px solid #DFDFDF;
565
+ clear:both;
566
+ }
567
+
568
+
569
+ #wpstg-clone-label{
570
+ font-size: 14px;
571
+ font-weight: bold;
572
+ }
573
+
574
+ #wpstg-log-details{
575
+ display:none;
576
+ height: 300px;
577
+ overflow: scroll;
578
+ max-width: 500px;
579
+ font-size: 11px;
580
+ border: 1px solid #FFF;
581
+ background-color: black;
582
+ color: white;
583
+ padding:10px;
584
+ }
585
+
586
+ #wpstg-finished-result {
587
+ display:none;
588
+ }
589
+
590
+ #wpstg-remove-cloning {
591
+ background: #ff3428;
592
+ border-color: #e72f24;
593
+ margin-top: 5px;
594
+ }
595
+
596
+ #wpstg-success-notice{
597
+ padding: 10px;
598
+ background-color: white;
599
+ max-width: 900px;
600
+ border: 1px solid #ccc;
601
+ margin-top: 20px;
602
+ }
603
+
604
+ .wpstg_beta_notice {
605
+ margin-bottom:20px;
606
+ }
607
+
608
+ .wpstg-sysinfo {
609
+ width:400px;
610
+ height: 500px;
611
+ }
assets/css/wpstg-admin.min.css ADDED
@@ -0,0 +1 @@
 
1
+ #tab_container ul{list-style:none;margin:0;padding:0;background:#f1f1f1;float:left}#tab_container ul li:first-child.selected-tab{border-top:none}#tab_container ul li a.selected-tab{font-weight:700;text-decoration:none}#tab_container .row{padding-top:10px;padding-bottom:12px}#tab_container .row label strong,#tab_container .row strong{font-weight:700}.wpstg-tabs a{padding:5px}#tab_container>ul>li.wpstg-tabs.active{background-color:#fff}#wpstg_settingsgeneral_header .row:nth-child(3),#wpstg_settingsgeneral_header .row:nth-child(4){display:none}#tab_container .panel-container{background:#FFF;padding:0 20px 20px;overflow:auto}#tab_container .form-table th{vertical-align:top;text-align:left;padding:20px 10px 20px 0;line-height:1.3;font-weight:700;font-size:14px;color:#484848;width:30%}#wpstg-workflow .wpstg-link-btn:active,.wpstg-link-btn{vertical-align:baseline}#tab_container .form-table tr{border-bottom:1px solid #E7E7E7}#tab_container span.description{display:block;font-weight:400;font-style:normal;font-size:13px;margin-top:7px;color:#484848}#tab_container .col-title{color:#484848}@media only screen and (max-width:680px){#tab_container ul{float:none}#tab_container .form-table tr>th{width:100%}#tab_container span.description{font-size:14px}#tab_container .form-table tr>td,#tab_container .form-table tr>th{padding:10px}}#tab_container ul li{margin-bottom:0}#tab_container ul li a{display:block;padding:10px 4px 10px 14px;border-width:1px 0;border-style:solid;border-top-color:#fff;border-bottom-color:#e7e7e7;text-decoration:none;color:#0097DF;font-weight:700}#tab_container ul li a:hover{background-color:#e5e5e5;color:#777}.wp-staginglogo{display:block;font-size:16px;padding-top:20px;width:220px;float:left}.wpstg-version{display:block;padding-top:29px}.wpstg_admin .nav-tab{color:#3C3C3C}#tab_container table tbody tr:nth-child(1)>th>div{font-size:20px}.wpstg_hidden{display:none}#mashtabcontainer>.mashtabs{background-color:#fff}#mashtabcontainer ul .active{background-color:#00adef;color:#fff;border-bottom-color:#0098D2}#mashtabcontainer ul .active:hover{background-color:#00A4E2;color:#fff;border-bottom-color:#0098D2}#mashtabcontainer ul li a{padding:10px 14px;background-color:#f3f3f3}#mashtabcontainer .mashtab-container{border:0 solid #ececec}#wpstg-clonepage-wrapper{margin-bottom:20px;width:690px}@media screen and (min-width:1090px){#wpstg-clonepage-wrapper{float:left;margin-bottom:20px;width:690px}.wpstg-sidebar{display:none;margin-left:700px;margin-top:138px}}.wpstg-sidebar{display:none;padding:10px;border:1px solid #DFDFDF;max-width:250px;height:250px}#wpstg-steps{margin-top:30px}#wpstg-steps li{color:#444;line-height:20px;padding-right:10px;float:left}.wpstg-step-num{border:1px solid #444;border-radius:3px;display:inline-block;width:20px;height:20px;text-align:center;margin-right:5px}.wpstg-current-step{font-weight:700}.wpstg-current-step .wpstg-step-num{background:#444;color:#eee}.wpstg-clone{border:3px solid #fff;margin-bottom:5px;padding:5px 10px;width:300px;position:relative;overflow:hidden;transition:border-color .2s ease-in-out;background-color:#DDD}.wpstg-clone.active{border-color:#1d94cf}.wpstg-clone-title{display:inline-block;font-size:15px;max-width:130px;text-decoration:none;font-weight:700;color:#0285AE}.wpstg-clone-action{background:#fff;border-left:1px solid #ccc;color:#bbb;display:none;padding:0 5px;float:right;text-decoration:none;position:relative;transition:color .2s ease-in-out}.wpstg-clone:hover .wpstg-clone-action,.wpstg-link-btn{display:inline-block}.wpstg-remove-clone:hover{color:#ef6d6d}.wpstg-clone-action:last-child{border:none}#wpstg-show-error-details:focus,#wpstg-workflow .wpstg-clone-action{outline:0;box-shadow:none}.wpstg-link-btn{background:#45a1c9;color:#fff;padding:5px 10px;text-decoration:none;transition:all .2s ease-in-out}.wpstg-link-btn:focus,.wpstg-link-btn:hover{color:#fff;outline:0;box-shadow:none}.wpstg-link-btn[disabled]{background:#777!important;pointer-events:none}#wpstg-cancel-cloning{background:#ff3428;border-color:#e72f24;margin-top:5px}#wpstg-cancel-cloning.success{background:#64dd58;border-color:#54bd4a}#wpstg-error-details,#wpstg-error-wrapper{display:none;margin-top:10px;font-size:13px}#wpstg-show-error-details{display:inline-block;margin-left:5px;color:#555;text-decoration:none;transition:color .2s ease-in-out}#wpstg-show-error-details:hover{color:#1d94cf}#wpstg-error-details{border-left:5px solid #ef6d6d;padding:10px;width:500px}.wpstg-tab-header,.wpstg-tab-section,.wpstg-tabs-wrapper{border:1px solid #ddd;border-left:none}#wpstg-home-link,#wpstg-try-again{display:none}#wpstg-loader{content:url(../images/loading.gif);margin-top:-5px}#wpstg-workflow{position:relative;clear:both;padding-top:20px;margin-right:20px}#wpstg-removing-clone.loading::after,#wpstg-workflow.loading::after{background:rgba(255,255,255,.7);content:'LOADING May take little bit longer for large sites... ';display:block;width:100%;height:100%;font-size:20px;padding-top:100px;text-align:center;position:absolute;top:0;left:0;z-index:99}#wpstg-existing-clones,#wpstg-removing-clone,.wpstg-size-info{position:relative}#wpstg-removing-clone.loading::after{content:'REMOVING'!important}.wpstg-progress-bar{max-width:900px;height:27px;padding:0;background-color:#d6d8d7}.wpstg-progress{background:#1d94cf;width:0;height:100%;transition:width 1s;color:#fff;line-height:25px;text-align:center}#wpstg-clone-path.wpstg-error-input,#wpstg-new-clone-id.wpstg-error-input{border:1px solid #ff4235;box-shadow:0 0 2px rgba(255,66,53,.8)}#wpstg-clone-path{margin-left:10px;width:350px}.wpstg-error-msg{color:#ff4235}#wpstg-start-cloning+.wpstg-error-msg{display:block;margin-top:5px}.wpstg-size-info{color:#999;font-weight:400;left:2px}.wpstg-db-table .wpstg-size-info{top:2px}#wpstg-workflow #wpstg-start-cloning{display:inline-block;margin-left:5px;font-size:14px;vertical-align:baseline}.wpstg-tabs-wrapper{max-width:640px;margin:10px 0;border-right:none}#wpstg-path-wrapper{border-bottom:2px dashed #ccc;padding-bottom:10px;margin-bottom:10px}.wpstg-tab-section{border-right:none;display:none;padding:20px}.wpstg-tab-section::after{display:block;content:'';clear:both}.wpstg-tab-header{border-right:none;color:#444;font-size:16px;font-weight:bolder;display:block;padding:10px;text-decoration:none}.wpstg-tab-triangle{display:inline-block;margin-right:10px}.wpstg-tab-header:focus{color:#444;outline:0;box-shadow:none}#wpstg-large-files{display:none;border:1px dashed #ccc;padding:10px;margin-top:20px;position:relative;font-size:12px}#wpstg-large-files h3{background:#fff;margin:0;padding:0 5px;position:absolute;top:-10px;left:5px}.wpstg-subdir{display:none;margin-left:20px}.wpstg-dir a.disabled{color:#888;cursor:default;text-decoration:none}.wpstg-check-subdirs{display:inline-block;margin-left:10px}.wpstg-notice-alert{display:block;background-color:#FFD0D0;padding:20px;border:1px solid #fff;max-width:600px}.wpstg-header{font-weight:400;line-height:1.6em;font-size:19px;border-bottom:1px solid #DFDFDF;clear:both}#wpstg-clone-label{font-size:14px;font-weight:700}#wpstg-log-details{display:none;height:300px;overflow:scroll;max-width:500px;font-size:11px;border:1px solid #FFF;background-color:#000;color:#fff;padding:10px}#wpstg-finished-result{display:none}#wpstg-remove-cloning{background:#ff3428;border-color:#e72f24;margin-top:5px}#wpstg-success-notice{padding:10px;background-color:#fff;max-width:900px;border:1px solid #ccc;margin-top:20px}.wpstg_beta_notice{margin-bottom:20px}.wpstg-sysinfo{width:400px;height:500px}
assets/images/admin_dashboard.png ADDED
Binary file
assets/images/loading.gif ADDED
Binary file
assets/images/loading.svg ADDED
@@ -0,0 +1 @@
 
1
+ <?xml version="1.0" encoding="utf-8"?><svg width='32px' height='32px' xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" class="uil-reload"><rect x="0" y="0" width="100" height="100" fill="none" class="bk"></rect><g><path d="M50 15A35 35 0 1 0 74.787 25.213" fill="none" stroke="#444444" stroke-width="12px"></path><path d="M50 0L50 30L66 15L50 0" fill="#444444"></path><animateTransform attributeName="transform" type="rotate" from="0 50 50" to="360 50 50" dur="1.5s" repeatCount="indefinite"></animateTransform></g></svg>
assets/images/logo_clean_small_212_25.png ADDED
Binary file
assets/js/wpstg-admin.js ADDED
@@ -0,0 +1,800 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * WP Staging
3
+ * Main functions
4
+ *
5
+ * @author René Hermenau
6
+ * @author url https://wp-staging.com
7
+ * @date 17.08.2015
8
+ *
9
+ * @scince 0.9
10
+ */
11
+
12
+ // Cloning workflow
13
+ (function ($) {
14
+ $(document).ready(function () {
15
+
16
+ // Unselect/select all db tables
17
+ var ischecked = true;
18
+ $('#wpstg-workflow').on("click", ".wpstg-button-unselect", function (e) {
19
+ e.preventDefault();
20
+ if (ischecked == false) {
21
+ $(".wpstg-db-table-checkboxes").attr("checked", "checked");
22
+ $(".wpstg-button-unselect").html("Uncheck All");
23
+ ischecked = true;
24
+ } else {
25
+ $(".wpstg-db-table-checkboxes").removeAttr("checked");
26
+ $(".wpstg-button-unselect").html("Check All");
27
+ ischecked = false;
28
+ }
29
+ });
30
+
31
+ // Ajax loading spinner
32
+ var admin_url = ajaxurl.replace('/admin-ajax.php', '');
33
+ var spinner_url = admin_url + '/images/spinner';
34
+ if (2 < window.devicePixelRatio) {
35
+ spinner_url += '-2x';
36
+ }
37
+ spinner_url += '.gif';
38
+ var ajax_spinner = '<img src="' + spinner_url + '" alt="" class="ajax-spinner general-spinner" />';
39
+
40
+ var doing_plugin_compatibility_ajax = false;
41
+
42
+ // Basic functions
43
+
44
+ /**
45
+ * Check if object is JSON valid
46
+ *
47
+ * @param {string} str
48
+ * @returns {Boolean}
49
+ */
50
+ function wpstgIsJsonObj(obj) {
51
+ if (typeof obj == 'object')
52
+ {
53
+ return true;
54
+ }
55
+ }
56
+
57
+
58
+ /**
59
+ * Check if given value is an integer
60
+ * This also casts strings as potential integers as well
61
+ *
62
+ * */
63
+ function wpstgIsInt(value) {
64
+ return !isNaN(value) &&
65
+ parseInt(Number(value)) == value &&
66
+ !isNaN(parseInt(value, 10));
67
+ }
68
+
69
+ /**
70
+ * Do some checks first for the clone name.
71
+ * Check the max length of string and if clone name already exists
72
+ */
73
+ $('#wpstg-workflow').on('keyup', '#wpstg-new-clone-id', function () {
74
+ var data = {
75
+ action: 'wpstg_check_clone',
76
+ cloneID: this.value
77
+ };
78
+ $.post(ajaxurl, data, function (resp, status, xhr) {
79
+ if (resp.status !== "fail") {
80
+ $('#wpstg-new-clone-id').removeClass('wpstg-error-input');
81
+ $('#wpstg-start-cloning').removeAttr('disabled');
82
+ $('#wpstg-clone-id-error').text(resp.message);
83
+ } else {
84
+ $('#wpstg-new-clone-id').addClass('wpstg-error-input');
85
+ $('#wpstg-start-cloning').attr('disabled', 'disabled');
86
+ $('#wpstg-clone-id-error').text(resp.message);
87
+ }
88
+ }).fail(function(xhr) { // Will be executed when $.post() fails
89
+ wpstg_show_error_die('Fatal Error: This should not happen but is often caused by other plugins. Enable first the option "Optimizer" in WP Staging->Settings and try again. If this does not help, enable <a href="https://codex.wordpress.org/Debugging_in_WordPress" target="_blank">wordpress debug mode</a> to find out which plugin is causing this:<br> ' + xhr.status + ' ' + xhr.statusText);
90
+ console.log(xhr.statusText);
91
+ });
92
+ });
93
+
94
+ /**
95
+ * Check cloning path
96
+ */
97
+ $('#wpstg-workflow').on('keyup', '#wpstg-clone-path', function () {
98
+ var path = this.value;
99
+ if (path.length < 1) {
100
+ $('#wpstg-clone-path').removeClass('wpstg-error-input');
101
+ $('#wpstg-start-cloning').removeAttr('disabled');
102
+ $('#wpstg-path-error').text('');
103
+ return true;
104
+ }
105
+
106
+ var data = {
107
+ action: 'wpstg_check_path',
108
+ path: path
109
+ };
110
+
111
+ $.post(ajaxurl, data, function (resp, status, xhr) {
112
+ if (resp) {
113
+ $('#wpstg-clone-path').removeClass('wpstg-error-input');
114
+ $('#wpstg-start-cloning').removeAttr('disabled');
115
+ $('#wpstg-path-error').text('');
116
+ } else {
117
+ $('#wpstg-clone-path').addClass('wpstg-error-input');
118
+ $('#wpstg-start-cloning').attr('disabled', 'disabled');
119
+ $('#wpstg-path-error').text('Folder does not exist or is not writable!');
120
+ }
121
+ }).fail(function(xhr) { // Will be executed when $.post() fails
122
+ wpstg_show_error_die('Fatal Error: This should not happen but is most often caused by other plugins. Try first the option "Optimizer" in WP Staging->Settings and try again. If this does not help, enable <a href="https://codex.wordpress.org/Debugging_in_WordPress" target="_blank">wordpress debug mode</a> to find out which plugin is causing this:<br> ' + xhr.status + ' ' + xhr.statusText);
123
+ console.log(xhr.statusText);
124
+ });
125
+ });
126
+
127
+ /**
128
+ * Edit profile
129
+ */
130
+ $('#wpstg-workflow').on('click', '.wpstg-edit-clone', function (e) {
131
+ e.preventDefault();
132
+
133
+ var data = {
134
+ action: 'wpstg_edit_profile',
135
+ clone: $(this).data('clone'),
136
+ nonce: wpstg.nonce
137
+ };
138
+ $('#wpstg-workflow').load(ajaxurl, data);
139
+ });
140
+
141
+ /**
142
+ * Save profile
143
+ */
144
+ $('#wpstg-workflow').on('click', '#wpstg-save-profile', function (e) {
145
+ e.preventDefault();
146
+
147
+ var data = {
148
+ action: 'wpstg_save_profile',
149
+ cloneID: $(this).data('clone'),
150
+ nonce: wpstg.nonce,
151
+ dbCredentials: {
152
+ dbname: $('#wpstgdb-name').val(),
153
+ dbuser: $('#wpstgdb-user').val(),
154
+ dbpassword: $('#wpstgdb-password').val(),
155
+ dbhost: $('#wpstgdb-host').val()
156
+ }
157
+ };
158
+ wpstg_additional_data(data, false);
159
+ $.post(ajaxurl, data, function (resp, status, xhr) {
160
+ location.reload();
161
+ }).fail(function(xhr) { // Will be executed when $.post() fails
162
+ wpstg_show_error_die('Fatal Error: This should not happen but is most often caused by other plugins. Try first the option "Optimizer" in WP Staging->Settings and try again. If this does not help, enable <a href="https://codex.wordpress.org/Debugging_in_WordPress" target="_blank">wordpress debug mode</a> to find out which plugin is causing this:<br> ' + xhr.status + ' ' + xhr.statusText);
163
+ console.log(xhr.statusText);
164
+ });
165
+ });
166
+
167
+ /**
168
+ * Next step
169
+ */
170
+ $('#wpstg-workflow').on('click', '.wpstg-next-step-link', function (e) {
171
+ e.preventDefault();
172
+ if ($(this).attr('disabled'))
173
+ return false;
174
+
175
+ $('#wpstg-workflow').addClass('loading');
176
+ var data = {
177
+ action: $(this).data('action'),
178
+ nonce: wpstg.nonce
179
+ };
180
+ if (data.action == 'wpstg_cloning') {
181
+ data.cloneID = $('#wpstg-new-clone-id').val() || new Date().getTime();
182
+ wpstg_additional_data(data, false);
183
+ }
184
+
185
+ $('#wpstg-workflow').load(ajaxurl, data, function (response, status, xhr) {
186
+ if ( status == 'error') { //Unknown error
187
+ console.log(xhr.status + ' ' + xhr.statusText);
188
+ wpstg_show_error_die('Fatal Error: This should not happen but is most often caused by other plugins. Try first the option "Optimizer" in WP Staging->Settings and try again. If this does not help, enable <a href="https://codex.wordpress.org/Debugging_in_WordPress" target="_blank">wordpress debug mode</a> to find out which plugin is causing this: ' + xhr.status + ' ' + xhr.statusText);
189
+ }
190
+ $('#wpstg-workflow').removeClass('loading');
191
+ $('.wpstg-current-step').removeClass('wpstg-current-step')
192
+ .next('li').addClass('wpstg-current-step');
193
+ if (data.action == 'wpstg_cloning') {
194
+ clone_db();
195
+ }
196
+ });
197
+ });
198
+
199
+ /**
200
+ * Previous step
201
+ */
202
+ $('#wpstg-workflow').on('click', '.wpstg-prev-step-link', function (e) {
203
+ e.preventDefault();
204
+ $('#wpstg-workflow').addClass('loading');
205
+ var data = {
206
+ action: 'wpstg_overview',
207
+ nonce: wpstg.nonce
208
+ };
209
+ $('#wpstg-workflow').load(ajaxurl, data, function (response, status, xhr) {
210
+ if ( status == 'error') { //Unknown error
211
+ console.log(xhr.status + ' ' + xhr.statusText);
212
+ wpstg_show_error_die('Fatal Error: This should not happen but is most often caused by other plugins. Try first the option "Optimizer" in WP Staging->Settings and try again. If this does not help, enable <a href="https://codex.wordpress.org/Debugging_in_WordPress" target="_blank">wordpress debug mode</a> to find out which plugin is causing this: ' + xhr.status + ' ' + xhr.statusText);
213
+ }
214
+ $('#wpstg-workflow').removeClass('loading');
215
+ $('.wpstg-current-step').removeClass('wpstg-current-step')
216
+ .prev('li').addClass('wpstg-current-step');
217
+ });
218
+ });
219
+
220
+ var cloneID;
221
+ function wpstg_additional_data(data, isRemoving) {
222
+ data.uncheckedTables = [];
223
+ $('.wpstg-db-table input:not(:checked)').each(function () {
224
+ data.uncheckedTables.push(this.name);
225
+ });
226
+ data.excludedFolders = [];
227
+ $('.wpstg-dir input:not(:checked)').each(function () {
228
+ if (! $(this).parent('.wpstg-dir').parents('.wpstg-dir').children('.wpstg-expand-dirs').hasClass('disabled'))
229
+ data.excludedFolders.push(this.name);
230
+ });
231
+
232
+ cloneID = data.cloneID.toString();
233
+ }
234
+
235
+ /*
236
+ * Start scanning process
237
+ */
238
+ $('#wpstg-workflow').on('click', '.wpstg-execute-clone', function (e) {
239
+ e.preventDefault();
240
+ $('#wpstg-workflow').addClass('loading');
241
+ var data = {
242
+ action: 'wpstg_scanning',
243
+ clone: $(this).data('clone'),
244
+ nonce: wpstg.nonce
245
+ };
246
+
247
+ $('#wpstg-workflow').load(ajaxurl, data, function (response, status, xhr) {
248
+ if ( status == 'error') { //Unknown error
249
+ console.log(xhr.status + ' ' + xhr.statusText);
250
+ wpstg_show_error_die('Fatal Error: This should not happen. Please try again! <br> If restarting does not work <a href="https://wordpress.org/support/plugin/wp-staging" target="blank">get in contact with us</a> ' + xhr.status + ' ' + xhr.statusText);
251
+ }
252
+ $('#wpstg-workflow').removeClass('loading');
253
+ $('.wpstg-current-step').removeClass('wpstg-current-step')
254
+ .next('li').addClass('wpstg-current-step');
255
+ });
256
+ });
257
+
258
+ $('#wpstg-workflow').on('click', '.wpstg-remove-clone', function (e) {
259
+ e.preventDefault();
260
+ $('.wpstg-clone').removeClass('active');
261
+ $( '#wpstg-existing-clones' ).append( ajax_spinner );
262
+ var data = {
263
+ action: 'wpstg_preremove',
264
+ cloneID: $(this).data('clone')
265
+ };
266
+ $('#wpstg-removing-clone').load(ajaxurl, data, function (resp, status, xhr){
267
+ if ( status == 'error') { //Unknown error
268
+ console.log(xhr.status + ' ' + xhr.statusText);
269
+ wpstg_show_error_die('Fatal Error: This should not happen but is most often caused by other plugins. Try first the option "Optimizer" in WP Staging->Settings and try again. If this does not help, enable <a href="https://codex.wordpress.org/Debugging_in_WordPress" target="_blank">wordpress debug mode</a> to find out which plugin is causing this:<br> ' + xhr.status + ' ' + xhr.statusText);
270
+ }
271
+ if (status === 'success'){
272
+ $('#wpstg-existing-clones').children("img").remove();
273
+ }
274
+ });
275
+ });
276
+
277
+ $('#wpstg-workflow').on('click', '#wpstg-cancel-removing', function (e) {
278
+ e.preventDefault();
279
+ $('.wpstg-clone').removeClass('active');
280
+ $('#wpstg-removing-clone').html('');
281
+ });
282
+
283
+ $('#wpstg-workflow').on('click', '#wpstg-remove-clone', function (e) {
284
+ e.preventDefault();
285
+ $('#wpstg-removing-clone').addClass('loading');
286
+ var cloneID = $(this).data('clone');
287
+ var data = {
288
+ action: 'wpstg_remove_clone',
289
+ cloneID: cloneID,
290
+ nonce: wpstg.nonce
291
+ };
292
+
293
+ wpstg_additional_data(data, true);
294
+ $.post(ajaxurl, data, function (resp, status, xhr) {
295
+ console.log(xhr.status + ' ' + xhr.statusText);
296
+ if ( status == 'error') { //Unknown error
297
+ console.log(xhr.status + ' ' + xhr.statusText);
298
+ wpstg_show_error_die('Fatal Error: This should not happen but is most often caused by other plugins. Try first the option "Optimizer" in WP Staging->Settings and try again. If this does not help, enable <a href="https://codex.wordpress.org/Debugging_in_WordPress" target="_blank">wordpress debug mode</a> to find out which plugin is causing this:<br> ' + xhr.status + ' ' + xhr.statusText);
299
+ }
300
+ if (resp == 0) {
301
+ $('#wpstg-removing-clone').html('');
302
+ $('.wpstg-clone#' + cloneID).remove();
303
+ $('#wpstg-removing-clone').removeClass('loading');
304
+ var remaining_clones = $('.wpstg-clone');
305
+ if (remaining_clones.length < 1)
306
+ $('#wpstg-existing-clones h3').text('');
307
+ }
308
+ }).fail(function(xhr) { // Will be executed when $.post() fails
309
+ wpstg_show_error_die('Fatal Error: This should not happen but is most often caused by other plugins. Try first the option "Optimizer" in WP Staging->Settings and try again. If this does not help, enable <a href="https://codex.wordpress.org/Debugging_in_WordPress" target="_blank">wordpress debug mode</a> to find out which plugin is causing this:<br> ' + xhr.status + ' ' + xhr.statusText);
310
+ console.log(xhr.statusText);
311
+ });
312
+ });
313
+
314
+ /**
315
+ * Show error message and die()
316
+ * Writes error message into log file
317
+ *
318
+ * @param {string} $error notice
319
+ * @returns void
320
+ */
321
+ function wpstg_show_error_die(error) {
322
+ $('#wpstg-try-again').css('display', 'inline-block');
323
+ $('#wpstg-cancel-cloning').text('Reset');
324
+ $('#wpstg-cloning-result').text('Fail');
325
+ $('#wpstg-error-wrapper').show();
326
+ $('#wpstg-error-details').show();
327
+ $('#wpstg-error-details').html(error);
328
+ $('#wpstg-loader').hide();
329
+ isFinished = true; // die cloning process
330
+ console.log(error);
331
+ var add_error = ' Fatal Error: This should not happen! Try first the option "Optimizer" in WP Staging->Settings and try again. If this does not fix it enable <a href="https://codex.wordpress.org/Debugging_in_WordPress" target="_blank">wordpress debug mode</a> to find out what is causing this.';
332
+ var data = {
333
+ action: 'wpstg_error_processing',
334
+ wpstg_error_msg: error + add_error
335
+ };
336
+ $.post(ajaxurl, data);
337
+ }
338
+
339
+
340
+ /**
341
+ * Clone database
342
+ *
343
+ * @return void
344
+ */
345
+ var isCanceled = false;
346
+ function clone_db() {
347
+
348
+ setTimeout(function(){ // timeout of x sec - prevent security blocks and cpu overload
349
+ $('#wpstg-loader').show();
350
+ var data = {
351
+ action: 'wpstg_clone_db',
352
+ nonce: wpstg.nonce
353
+ };
354
+ $.post(ajaxurl, data, function (resp, status, xhr) {
355
+ if (isCanceled) {
356
+ cancelCloning();
357
+ return false;
358
+ }
359
+ if ( status == "error" ) { //Unknown error
360
+ wpstg_show_error_die('Fatal Error: This should not happen. Please try again! <br> If restarting does not work <a href="https://wordpress.org/support/plugin/wp-staging" target="blank">get in contact with us</a> ' . xhr.statusText);
361
+ } else if (!wpstgIsJsonObj(resp)) { //Unknown error
362
+ wpstg_show_error_die('Fatal Error: This should not happen. Please try again! <br> If restarting does not work <a href="https://wordpress.org/support/plugin/wp-staging" target="blank">get in contact with us</a> ' . xhr.statusText);
363
+ } else if (resp.percent < 0) { //Fail
364
+ wpstg_show_error_die('Fatal Error: This should never happen. Please try again! <br>If restarting does not work <a href="https://wordpress.org/support/plugin/wp-staging" target="blank">get in contact with us</a> ' . xhr.statusText);
365
+ } else if(resp.percent < 1) { //Continue cloning
366
+ var complete = Math.floor(resp.percent * 100) + '%';
367
+ $('#wpstg-db-progress').text(complete).css('width', complete);
368
+ $('#wpstg-error-wrapper').show();
369
+ if (resp.message !== '')
370
+ $('#wpstg-log-details').append(resp.message);
371
+
372
+ wpstg_logscroll_bottom();
373
+ clone_db();
374
+ } else { //Success cloning
375
+ $('#wpstg-db-progress').text('').css('width', '100%');
376
+ $('#wpstg-log-details').append(resp.message + '<br>');
377
+ wpstg_logscroll_bottom();
378
+ copy_files();
379
+ }
380
+ })
381
+ .fail(function(xhr) { // Will be executed when $.post() fails
382
+ wpstg_show_error_die(xhr.statusText);
383
+ console.log('Fatal Error: This should not happen but is often caused by other plugins. Try first the option "Optimizer" in WP Staging->Settings and try again. If this does not help, enable <a href="https://codex.wordpress.org/Debugging_in_WordPress" target="_blank">wordpress debug mode</a> to find out which plugin is causing this:<br> ' + xhr.status + ' ' + xhr.statusText);
384
+ });
385
+ },wpstg.cpu_load); // timeout of x sec - prevent security blocks and cpu overload
386
+ }
387
+
388
+
389
+
390
+
391
+ function copy_files() {
392
+ setTimeout(function(){ // timeout of x sec - prevent security blocks and cpu overload
393
+ var data = {
394
+ action: 'wpstg_copy_files',
395
+ nonce: wpstg.nonce
396
+ };
397
+ $.post(ajaxurl, data, function(resp, status, xhr) {
398
+ if (isCanceled) {
399
+ cancelCloning();
400
+ return false;
401
+ }
402
+
403
+ if (!wpstgIsJsonObj(resp)) { //Unknown error
404
+ wpstg_show_error_die('Fatal Error: This should not happen. Please try again! <br> If restarting does not work <a href="https://wordpress.org/support/plugin/wp-staging" target="blank">get in contact with us</a> ');
405
+ } else if (resp.percent < 0) { //Fail
406
+ wpstg_show_error_die('Fatal Error: This should never happen. Please try again! <br>If restarting does not work <a href="https://wordpress.org/support/plugin/wp-staging" target="blank">get in contact with us</a> '. xhr.statusText);
407
+ } else if (resp.percent < 1) { //Continue copying
408
+ var complete = Math.floor(resp.percent * 100) + '%';
409
+ $('#wpstg-files-progress').text(complete).css('width', complete);
410
+ $('#wpstg-loader').show();
411
+ if (resp.message !== '')
412
+ $('#wpstg-log-details').append(resp.message + '<br>');
413
+ wpstg_logscroll_bottom();
414
+ copy_files();
415
+ } else { //Success copying
416
+ $('#wpstg-files-progress').text('').css('width', '100%');
417
+ wpstg_logscroll_bottom();
418
+ replace_links();
419
+ }
420
+ }).fail(function(xhr) { // Will be executed when $.post() fails
421
+ wpstg_show_error_die('Fatal Error: This should not happen but is most often caused by other plugins. Try first the option "Optimizer" in WP Staging->Settings and try again. If this does not help, enable <a href="https://codex.wordpress.org/Debugging_in_WordPress" target="_blank">wordpress debug mode</a> to find out which plugin is causing this:<br> ' + xhr.status + ' ' + xhr.statusText);
422
+ console.log(xhr.statusText);
423
+ });
424
+ },wpstg.cpu_load); // timeout of x sec - prevent security blocks and cpu overload
425
+ }
426
+
427
+ var isFinished = false;
428
+ function replace_links() {
429
+ var data = {
430
+ action: 'wpstg_replace_links',
431
+ nonce: wpstg.nonce
432
+ };
433
+ $.post(ajaxurl, data, function(resp, status, xhr) {
434
+ if (isCanceled) {
435
+ cancelCloning();
436
+ return false;
437
+ }
438
+
439
+ if (!wpstgIsJsonObj(resp)) { //Unknown error
440
+ wpstg_show_error_die('Fatal Error code: 1001. This should not happen. Please try again! <br> If restarting does not work <a href="https://wordpress.org/support/plugin/wp-staging" target="blank">get in contact with us</a> ');
441
+ } else if (resp.percent < 0) { //Fail
442
+ wpstg_show_error_die('Fatal Error code: 1002. This should never happen. Please try again! <br>If restarting does not work <a href="https://wordpress.org/support/plugin/wp-staging" target="blank">get in contact with us</a> ' . xhr.statusText);
443
+ } else if (resp.percent < 1) { //Continue replacing string
444
+ var complete = Math.floor(resp.percent * 100) + '%';
445
+ $('#wpstg-links-progress').text('').css('width', complete);
446
+ $('#wpstg-loader').show();
447
+ if (resp.message !== '')
448
+ $('#wpstg-log-details').append(resp.message + '<br>');
449
+ wpstg_logscroll_bottom();
450
+ replace_links();
451
+ } else { //Success
452
+ $('#wpstg-links-progress').text('').css('width', '100%');
453
+ $('#wpstg-loader').hide();
454
+ wpstg_logscroll_bottom();
455
+ cloneID = cloneID.replace(/[^A-Za-z0-9]/g, '');
456
+ var redirectURL = $('#wpstg-clone-url').attr('href') + '/' + cloneID + '/';
457
+ setTimeout(function () {
458
+ $('#wpstg-finished-result').show();
459
+ $('#wpstg-clone-url').text('Visit staging site <span style="font-size: 10px;">(login with your admin credentials)</span>' . cloneID).attr('href', redirectURL );
460
+ $('#wpstg_staging_name').text(cloneID);
461
+ $('#wpstg-cancel-cloning').hide();
462
+ $('#wpstg-home-link').css('display', 'inline-block');
463
+ isFinished = true;
464
+ }, 1200);
465
+ if (resp.message !== '')
466
+ $('#wpstg-log-details').append(resp.message + '<br>');
467
+ wpstg_logscroll_bottom();
468
+ }
469
+
470
+ }).fail(function(xhr) { // Will be executed when $.post() fails
471
+ wpstg_show_error_die('Fatal Error: This should not happen but is sometimes caused by other plugins. Try first the option "Optimizer" in WP Staging->Settings and try again. If this does not help, enable <a href="https://codex.wordpress.org/Debugging_in_WordPress" target="_blank">wordpress debug mode</a> to find out which plugin is causing this:<br> ' + xhr.status + ' ' + xhr.statusText);
472
+ console.log(xhr.statusText);
473
+ });
474
+ };
475
+
476
+ /**
477
+ * Show error log button
478
+ */
479
+ $('#wpstg-workflow').on('click', '#wpstg-show-log-button', function (e) {
480
+ e.preventDefault();
481
+ $('#wpstg-log-details').toggle();
482
+ $('html, body').animate({
483
+ scrollTop: $("#wpstg-log-details").offset().top
484
+ }, 400);
485
+ });
486
+
487
+ /**
488
+ * Scroll the log window to the bottom
489
+ *
490
+ * @return void
491
+ */
492
+ function wpstg_logscroll_bottom(){
493
+ var $mydiv = $('#wpstg-log-details');
494
+ $mydiv.scrollTop($mydiv[0].scrollHeight);
495
+ }
496
+
497
+ /**
498
+ * Cancel Cloning process button
499
+ */
500
+ $('#wpstg-workflow').on('click', '#wpstg-cancel-cloning', function (e) {
501
+ e.preventDefault();
502
+ if (! confirm('Are you sure?'))
503
+ return false;
504
+ $('#wpstg-try-again, #wpstg-home-link').hide();
505
+ $(this).attr('disabled', 'disabled');
506
+ isCanceled = true;
507
+ $('#wpstg-cloning-result').text('Please wait...this can take up to a minute');
508
+ $('#wpstg-loader').hide();
509
+ $('#wpstg-show-log-button').hide();
510
+ $( this ).parent().append( ajax_spinner );
511
+ if (isFinished)
512
+ cancelCloning();
513
+ });
514
+
515
+ /**
516
+ * Remove Clone button
517
+ */
518
+ $('#wpstg-workflow').on('click', '#wpstg-remove-cloning', function (e) {
519
+ e.preventDefault();
520
+ if (! confirm('Are you sure you want to remove the clone site ?'))
521
+ return false;
522
+ $('#wpstg-try-again, #wpstg-home-link').hide();
523
+ $(this).attr('disabled', 'disabled');
524
+ $('#wpstg-clone-url').attr('disabled', 'disabled');
525
+ isCanceled = true;
526
+ $('#wpstg-cloning-result').text('Please wait...this can take up to a minute');
527
+ $('#wpstg-loader').hide();
528
+ $('#wpstg-success-notice').hide();
529
+ $('#wpstg-show-log-button').hide();
530
+ $('#wpstg-log-details').hide();
531
+ $( this ).parent().append( ajax_spinner );
532
+ if (isFinished)
533
+ cancelCloning();
534
+ });
535
+
536
+ /**
537
+ * Try again button
538
+ */
539
+ $('#wpstg-workflow').on('click', '#wpstg-try-again', function (e) {
540
+ e.preventDefault();
541
+ console.log('test');
542
+ $('#wpstg-workflow').addClass('loading');
543
+ var data = {
544
+ action: 'wpstg_scanning',
545
+ nonce: wpstg.nonce
546
+ };
547
+ $('#wpstg-workflow').load(ajaxurl, data, function (response, status, xhr) {
548
+ if ( status == 'error') { //Unknown error
549
+ console.log(xhr.status + ' ' + xhr.statusText);
550
+ wpstg_show_error_die('Fatal Error: This should not happen but is most often caused by other plugins. Try first the option "Optimizer" in WP Staging->Settings and try again. If this does not help, enable <a href="https://codex.wordpress.org/Debugging_in_WordPress" target="_blank">wordpress debug mode</a> to find out which plugin is causing this:<br> ' + xhr.status + ' ' + xhr.statusText);
551
+ }
552
+ $('#wpstg-workflow').removeClass('loading');
553
+ $('.wpstg-current-step').removeClass('wpstg-current-step')
554
+ .prev('li').addClass('wpstg-current-step');
555
+ });
556
+ });
557
+
558
+ /**
559
+ * Reset button
560
+ */
561
+ $('#wpstg-workflow').on('click', '#wpstg-reset-clone', function (e) {
562
+ e.preventDefault();
563
+ $(this).attr('disabled', 'disabled')
564
+ .next('.wpstg-next-step-link').hide();
565
+ $('#wpstg-loader').show();
566
+ cloneID = $(this).data('clone');
567
+ cancelCloning();
568
+ });
569
+
570
+ /**
571
+ * Cancel Cloning process
572
+ *
573
+ * @returns void
574
+ */
575
+ function cancelCloning() {
576
+ var data = {
577
+ action: 'wpstg_cancel_cloning',
578
+ nonce: wpstg.nonce,
579
+ cloneID: cloneID
580
+ };
581
+ $.post(ajaxurl, data, function (resp, status, xhr) {
582
+ if (resp == 0) {
583
+ $('#wpstg-cloning-result').text('');
584
+ $('#wpstg-cancel-cloning').text('Success').addClass('success').removeAttr('disabled');
585
+ location.reload();
586
+ }
587
+ }).fail(function(xhr) { // Will be executed when $.post() fails
588
+ wpstg_show_error_die(xhr.statusText);
589
+ console.log('Fatal Error: This should not happen but is most often caused by other plugins. Try first the option "Optimizer" in WP Staging->Settings and try again. If this does not help, enable <a href="https://codex.wordpress.org/Debugging_in_WordPress" target="_blank">wordpress debug mode</a> to find out which plugin is causing this:<br> ' + xhr.status + ' ' + xhr.statusText);
590
+ });
591
+ }
592
+
593
+ $('#wpstg-workflow').on('click', '#wpstg-home-link', function (e) {
594
+ e.preventDefault();
595
+ location.reload();
596
+ });
597
+
598
+ /**
599
+ * Tabs
600
+ */
601
+ $('#wpstg-workflow').on('click', '.wpstg-tab-header', function (e) {
602
+ e.preventDefault();
603
+ var section = $(this).data('id');
604
+ $(this).toggleClass('expand');
605
+ $(section).slideToggle();
606
+ if ($(this).hasClass('expand'))
607
+ $(this).find('.wpstg-tab-triangle').html('&#9660;');
608
+ else
609
+ $(this).find('.wpstg-tab-triangle').html('&#9658;');
610
+
611
+ });
612
+
613
+ /**
614
+ * Directory structure
615
+ */
616
+ $('#wpstg-workflow').on('click', '.wpstg-expand-dirs', function (e) {
617
+ e.preventDefault();
618
+ if (! $(this).hasClass('disabled'))
619
+ $(this).siblings('.wpstg-subdir').slideToggle();
620
+ });
621
+
622
+
623
+ $('#wpstg-workflow').on('change', '.wpstg-check-dir', function () {
624
+ var dir = $(this).parent('.wpstg-dir');
625
+ if (this.checked) {
626
+ dir.parents('.wpstg-dir').children('.wpstg-check-dir').attr('checked', 'checked');
627
+ dir.find('.wpstg-expand-dirs').removeClass('disabled');
628
+ dir.find('.wpstg-subdir .wpstg-check-dir').attr('checked', 'checked');
629
+ } else {
630
+ dir.find('.wpstg-dir .wpstg-check-dir').removeAttr('checked');
631
+ dir.find('.wpstg-expand-dirs, .wpstg-check-subdirs').addClass('disabled');
632
+ dir.find('.wpstg-check-subdirs').data('action', 'check').text('check');
633
+ dir.children('.wpstg-subdir').slideUp();
634
+ }
635
+ });
636
+
637
+ /**
638
+ * install must-use plugin
639
+ */
640
+
641
+ $( '#plugin-compatibility' ).change( function( e ) {
642
+ var install = '1';
643
+ if ( $( this ).is( ':checked' ) ) {
644
+ var answer = confirm( wpstg.mu_plugin_confirmation );
645
+
646
+ if ( !answer ) {
647
+ $( this ).prop( 'checked', false );
648
+ return;
649
+ }
650
+ } else {
651
+ install = '0';
652
+ }
653
+
654
+ $( '.plugin-compatibility-wrap' ).toggle();
655
+
656
+ $( this ).parent().append( ajax_spinner );
657
+ $( '#plugin-compatibility' ).attr( 'disabled', 'disabled' );
658
+ $( '.plugin-compatibility' ).addClass( 'disabled' );
659
+
660
+ $.ajax( {
661
+ url: ajaxurl,
662
+ type: 'POST',
663
+ dataType: 'text',
664
+ cache: false,
665
+ data: {
666
+ action: 'wpstg_muplugin_install',
667
+ install: install
668
+ },
669
+ error: function( jqXHR, textStatus, errorThrown ) {
670
+ alert( "Error: " + wpstg.plugin_compatibility_settings_problem + '\r\n\r\n' + wpstg.status + ' ' + jqXHR.status + ' ' + jqXHR.statusText + '\r\n\r\n' + wpstg.response + '\r\n' + jqXHR.responseText );
671
+ $( '.ajax-spinner' ).remove();
672
+ $( '#plugin-compatibility' ).removeAttr( 'disabled' );
673
+ $( '.plugin-compatibility' ).removeClass( 'disabled' );
674
+ },
675
+ success: function( data ) {
676
+ if ( '' !== $.trim( data ) ) {
677
+ alert( data );
678
+ } else {
679
+ $( '.plugin-compatibility' ).append( '<span class="ajax-success-msg">' + wpstg.saved + '</span>' );
680
+ $( '.ajax-success-msg' ).fadeOut( 2000, function() {
681
+ $( this ).remove();
682
+ } );
683
+ }
684
+ $( '.ajax-spinner' ).remove();
685
+ $( '#plugin-compatibility' ).removeAttr( 'disabled' );
686
+ $( '.plugin-compatibility' ).removeClass( 'disabled' );
687
+ }
688
+ } );
689
+
690
+ });
691
+
692
+ if ( $( '#plugin-compatibility' ).is( ':checked' ) ) {
693
+ $( '.plugin-compatibility-wrap' ).show();
694
+ }
695
+ $( '.plugin-compatibility-save' ).click( function() {
696
+ if ( doing_plugin_compatibility_ajax ) {
697
+ return;
698
+ }
699
+ $( this ).addClass( 'disabled' );
700
+ var select_element = $( '#selected-plugins' );
701
+ $( select_element ).attr( 'disabled', 'disabled' );
702
+ var query_limit = $( '#wpstg_settings\\[wpstg_query_limit\\]' );
703
+ var batch_size = $( '#wpstg_settings\\[wpstg_batch_size\\]' );
704
+ var disable_admin_login = $( '#wpstg_settings\\[disable_admin_login\\]' );
705
+ var uninstall_on_delete = $( '#wpstg_settings\\[uninstall_on_delete\\]' );
706
+ disable_admin_login = $( disable_admin_login ).prop('checked') ? '1' : '0';
707
+ uninstall_on_delete = $( uninstall_on_delete ).prop('checked') ? '1' : '0';
708
+
709
+ doing_plugin_compatibility_ajax = true;
710
+ $( this ).after( '<img src="' + spinner_url + '" alt="" class="plugin-compatibility-spinner general-spinner" />' );
711
+
712
+ $.ajax( {
713
+ url: ajaxurl,
714
+ type: 'POST',
715
+ dataType: 'text',
716
+ cache: false,
717
+ data: {
718
+ action: 'wpstg_disable_plugins',
719
+ blacklist_plugins: $( select_element ).val(),
720
+ query_limit: $( query_limit ).val(),
721
+ batch_size: $( batch_size ).val(),
722
+ disable_admin_login: disable_admin_login,
723
+ uninstall_on_delete: uninstall_on_delete,
724
+
725
+ },
726
+ error: function( jqXHR, textStatus, errorThrown ) {
727
+ alert( wpstg.blacklist_problem + '\r\n\r\n' + wpstg.status + ' ' + jqXHR.status + ' ' + jqXHR.statusText + '\r\n\r\n' + wpstg.response + '\r\n' + jqXHR.responseText );
728
+ $( select_element ).removeAttr( 'disabled' );
729
+ $( '.plugin-compatibility-save' ).removeClass( 'disabled' );
730
+ doing_plugin_compatibility_ajax = false;
731
+ $( '.plugin-compatibility-spinner' ).remove();
732
+ $( '.plugin-compatibility-success-msg' ).show().fadeOut( 2000 );
733
+ },
734
+ success: function( data ) {
735
+ if ( '' !== $.trim( data ) ) {
736
+ alert( data );
737
+ }
738
+ $( select_element ).removeAttr( 'disabled' );
739
+ $( '.plugin-compatibility-save' ).removeClass( 'disabled' );
740
+ doing_plugin_compatibility_ajax = false;
741
+ $( '.plugin-compatibility-spinner' ).remove();
742
+ $( '.plugin-compatibility-success-msg' ).show().fadeOut( 2000 );
743
+ }
744
+ } );
745
+ } );
746
+
747
+ // select all tables
748
+ $( '.multiselect-select-all' ).click( function() {
749
+ var multiselect = $( this ).parents( '.select-wrap' ).children( '.multiselect' );
750
+ $( 'option', multiselect ).attr( 'selected', 1 );
751
+ $( multiselect ).focus().trigger( 'change' );
752
+ } );
753
+
754
+ // deselect all tables
755
+ $( '.multiselect-deselect-all' ).click( function() {
756
+ var multiselect = $( this ).parents( '.select-wrap' ).children( '.multiselect' );
757
+ $( 'option', multiselect ).removeAttr( 'selected' );
758
+ $( multiselect ).focus().trigger( 'change' );
759
+ } );
760
+
761
+ // invert table selection
762
+ $( '.multiselect-invert-selection' ).click( function() {
763
+ var multiselect = $( this ).parents( '.select-wrap' ).children( '.multiselect' );
764
+ $( 'option', multiselect ).each( function() {
765
+ $( this ).attr( 'selected', !$( this ).attr( 'selected' ) );
766
+ } );
767
+ $( multiselect ).focus().trigger( 'change' );
768
+ } );
769
+
770
+
771
+
772
+ // Show large files
773
+ $('#wpstg-workflow').on('click', '#wpstg-show-large-files', function (e) {
774
+ e.preventDefault();
775
+ //$('#wpstg-show-large-files').on('click', ) click(function(){
776
+ $('#wpstg-large-files').toggle();
777
+ });
778
+
779
+
780
+
781
+ });
782
+ })(jQuery);
783
+
784
+ // Load twitter button async
785
+ window.twttr = (function(d, s, id) {
786
+ var js, fjs = d.getElementsByTagName(s)[0],
787
+ t = window.twttr || {};
788
+ if (d.getElementById(id)) return t;
789
+ js = d.createElement(s);
790
+ js.id = id;
791
+ js.src = "https://platform.twitter.com/widgets.js";
792
+ fjs.parentNode.insertBefore(js, fjs);
793
+
794
+ t._e = [];
795
+ t.ready = function(f) {
796
+ t._e.push(f);
797
+ };
798
+
799
+ return t;
800
+ }(document, "script", "twitter-wjs"));
assets/js/wpstg-admin.min.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(a){a(document).ready(function(){function b(a){return"object"==typeof a?!0:void 0}function c(b){b.uncheckedTables=[],a(".wpstg-db-table input:not(:checked)").each(function(){b.uncheckedTables.push(this.name)}),b.excludedFolders=[],a(".wpstg-dir input:not(:checked)").each(function(){a(this).parent(".wpstg-dir").parents(".wpstg-dir").children(".wpstg-expand-dirs").hasClass("disabled")||b.excludedFolders.push(this.name)}),o=b.cloneID.toString()}function d(b){a("#wpstg-try-again").css("display","inline-block"),a("#wpstg-cancel-cloning").text("Reset"),a("#wpstg-cloning-result").text("Fail"),a("#wpstg-error-wrapper").show(),a("#wpstg-error-details").show(),a("#wpstg-error-details").html(b),a("#wpstg-loader").hide(),q=!0,console.log(b);var c=' Fatal Error: This should not happen! Try first the option "Optimizer" in WP Staging->Settings and try again. If this does not fix it enable <a href="https://codex.wordpress.org/Debugging_in_WordPress" target="_blank">wordpress debug mode</a> to find out what is causing this.',d={action:"wpstg_error_processing",wpstg_error_msg:b+c};a.post(ajaxurl,d)}function e(){setTimeout(function(){a("#wpstg-loader").show();var c={action:"wpstg_clone_db",nonce:wpstg.nonce};a.post(ajaxurl,c,function(c,g){if(p)return i(),!1;if("error"==g)d('Fatal Error: This should not happen. Please try again! <br> If restarting does not work <a href="https://wordpress.org/support/plugin/wp-staging" target="blank">get in contact with us</a> '.xhr.statusText);else if(b(c))if(c.percent<0)d('Fatal Error: This should never happen. Please try again! <br>If restarting does not work <a href="https://wordpress.org/support/plugin/wp-staging" target="blank">get in contact with us</a> '.xhr.statusText);else if(c.percent<1){var j=Math.floor(100*c.percent)+"%";a("#wpstg-db-progress").text(j).css("width",j),a("#wpstg-error-wrapper").show(),""!==c.message&&a("#wpstg-log-details").append(c.message),h(),e()}else a("#wpstg-db-progress").text("").css("width","100%"),a("#wpstg-log-details").append(c.message+"<br>"),h(),f();else d('Fatal Error: This should not happen. Please try again! <br> If restarting does not work <a href="https://wordpress.org/support/plugin/wp-staging" target="blank">get in contact with us</a> '.xhr.statusText)}).fail(function(a){d(a.statusText),console.log('Fatal Error: This should not happen but is often caused by other plugins. Try first the option "Optimizer" in WP Staging->Settings and try again. If this does not help, enable <a href="https://codex.wordpress.org/Debugging_in_WordPress" target="_blank">wordpress debug mode</a> to find out which plugin is causing this:<br> '+a.status+" "+a.statusText)})},wpstg.cpu_load)}function f(){setTimeout(function(){var c={action:"wpstg_copy_files",nonce:wpstg.nonce};a.post(ajaxurl,c,function(c){if(p)return i(),!1;if(b(c))if(c.percent<0)d('Fatal Error: This should never happen. Please try again! <br>If restarting does not work <a href="https://wordpress.org/support/plugin/wp-staging" target="blank">get in contact with us</a> '.xhr.statusText);else if(c.percent<1){var e=Math.floor(100*c.percent)+"%";a("#wpstg-files-progress").text(e).css("width",e),a("#wpstg-loader").show(),""!==c.message&&a("#wpstg-log-details").append(c.message+"<br>"),h(),f()}else a("#wpstg-files-progress").text("").css("width","100%"),h(),g();else d('Fatal Error: This should not happen. Please try again! <br> If restarting does not work <a href="https://wordpress.org/support/plugin/wp-staging" target="blank">get in contact with us</a> ')}).fail(function(a){d('Fatal Error: This should not happen but is most often caused by other plugins. Try first the option "Optimizer" in WP Staging->Settings and try again. If this does not help, enable <a href="https://codex.wordpress.org/Debugging_in_WordPress" target="_blank">wordpress debug mode</a> to find out which plugin is causing this:<br> '+a.status+" "+a.statusText),console.log(a.statusText)})},wpstg.cpu_load)}function g(){var c={action:"wpstg_replace_links",nonce:wpstg.nonce};a.post(ajaxurl,c,function(c){if(p)return i(),!1;if(b(c))if(c.percent<0)d('Fatal Error code: 1002. This should never happen. Please try again! <br>If restarting does not work <a href="https://wordpress.org/support/plugin/wp-staging" target="blank">get in contact with us</a> '.xhr.statusText);else if(c.percent<1){var e=Math.floor(100*c.percent)+"%";a("#wpstg-links-progress").text("").css("width",e),a("#wpstg-loader").show(),""!==c.message&&a("#wpstg-log-details").append(c.message+"<br>"),h(),g()}else{a("#wpstg-links-progress").text("").css("width","100%"),a("#wpstg-loader").hide(),h(),o=o.replace(/[^A-Za-z0-9]/g,"");var f=a("#wpstg-clone-url").attr("href")+"/"+o+"/";setTimeout(function(){a("#wpstg-finished-result").show(),a("#wpstg-clone-url").text('Visit staging site <span style="font-size: 10px;">(login with your admin credentials)</span>'.cloneID).attr("href",f),a("#wpstg_staging_name").text(o),a("#wpstg-cancel-cloning").hide(),a("#wpstg-home-link").css("display","inline-block"),q=!0},1200),""!==c.message&&a("#wpstg-log-details").append(c.message+"<br>"),h()}else d('Fatal Error code: 1001. This should not happen. Please try again! <br> If restarting does not work <a href="https://wordpress.org/support/plugin/wp-staging" target="blank">get in contact with us</a> ')}).fail(function(a){d('Fatal Error: This should not happen but is sometimes caused by other plugins. Try first the option "Optimizer" in WP Staging->Settings and try again. If this does not help, enable <a href="https://codex.wordpress.org/Debugging_in_WordPress" target="_blank">wordpress debug mode</a> to find out which plugin is causing this:<br> '+a.status+" "+a.statusText),console.log(a.statusText)})}function h(){var b=a("#wpstg-log-details");b.scrollTop(b[0].scrollHeight)}function i(){var b={action:"wpstg_cancel_cloning",nonce:wpstg.nonce,cloneID:o};a.post(ajaxurl,b,function(b){0==b&&(a("#wpstg-cloning-result").text(""),a("#wpstg-cancel-cloning").text("Success").addClass("success").removeAttr("disabled"),location.reload())}).fail(function(a){d(a.statusText),console.log('Fatal Error: This should not happen but is most often caused by other plugins. Try first the option "Optimizer" in WP Staging->Settings and try again. If this does not help, enable <a href="https://codex.wordpress.org/Debugging_in_WordPress" target="_blank">wordpress debug mode</a> to find out which plugin is causing this:<br> '+a.status+" "+a.statusText)})}var j=!0;a("#wpstg-workflow").on("click",".wpstg-button-unselect",function(b){b.preventDefault(),0==j?(a(".wpstg-db-table-checkboxes").attr("checked","checked"),a(".wpstg-button-unselect").html("Uncheck All"),j=!0):(a(".wpstg-db-table-checkboxes").removeAttr("checked"),a(".wpstg-button-unselect").html("Check All"),j=!1)});var k=ajaxurl.replace("/admin-ajax.php",""),l=k+"/images/spinner";2<window.devicePixelRatio&&(l+="-2x"),l+=".gif";var m='<img src="'+l+'" alt="" class="ajax-spinner general-spinner" />',n=!1;a("#wpstg-workflow").on("keyup","#wpstg-new-clone-id",function(){var b={action:"wpstg_check_clone",cloneID:this.value};a.post(ajaxurl,b,function(b){"fail"!==b.status?(a("#wpstg-new-clone-id").removeClass("wpstg-error-input"),a("#wpstg-start-cloning").removeAttr("disabled"),a("#wpstg-clone-id-error").text(b.message)):(a("#wpstg-new-clone-id").addClass("wpstg-error-input"),a("#wpstg-start-cloning").attr("disabled","disabled"),a("#wpstg-clone-id-error").text(b.message))}).fail(function(a){d('Fatal Error: This should not happen but is often caused by other plugins. Enable first the option "Optimizer" in WP Staging->Settings and try again. If this does not help, enable <a href="https://codex.wordpress.org/Debugging_in_WordPress" target="_blank">wordpress debug mode</a> to find out which plugin is causing this:<br> '+a.status+" "+a.statusText),console.log(a.statusText)})}),a("#wpstg-workflow").on("keyup","#wpstg-clone-path",function(){var b=this.value;if(b.length<1)return a("#wpstg-clone-path").removeClass("wpstg-error-input"),a("#wpstg-start-cloning").removeAttr("disabled"),a("#wpstg-path-error").text(""),!0;var c={action:"wpstg_check_path",path:b};a.post(ajaxurl,c,function(b){b?(a("#wpstg-clone-path").removeClass("wpstg-error-input"),a("#wpstg-start-cloning").removeAttr("disabled"),a("#wpstg-path-error").text("")):(a("#wpstg-clone-path").addClass("wpstg-error-input"),a("#wpstg-start-cloning").attr("disabled","disabled"),a("#wpstg-path-error").text("Folder does not exist or is not writable!"))}).fail(function(a){d('Fatal Error: This should not happen but is most often caused by other plugins. Try first the option "Optimizer" in WP Staging->Settings and try again. If this does not help, enable <a href="https://codex.wordpress.org/Debugging_in_WordPress" target="_blank">wordpress debug mode</a> to find out which plugin is causing this:<br> '+a.status+" "+a.statusText),console.log(a.statusText)})}),a("#wpstg-workflow").on("click",".wpstg-edit-clone",function(b){b.preventDefault();var c={action:"wpstg_edit_profile",clone:a(this).data("clone"),nonce:wpstg.nonce};a("#wpstg-workflow").load(ajaxurl,c)}),a("#wpstg-workflow").on("click","#wpstg-save-profile",function(b){b.preventDefault();var e={action:"wpstg_save_profile",cloneID:a(this).data("clone"),nonce:wpstg.nonce,dbCredentials:{dbname:a("#wpstgdb-name").val(),dbuser:a("#wpstgdb-user").val(),dbpassword:a("#wpstgdb-password").val(),dbhost:a("#wpstgdb-host").val()}};c(e,!1),a.post(ajaxurl,e,function(){location.reload()}).fail(function(a){d('Fatal Error: This should not happen but is most often caused by other plugins. Try first the option "Optimizer" in WP Staging->Settings and try again. If this does not help, enable <a href="https://codex.wordpress.org/Debugging_in_WordPress" target="_blank">wordpress debug mode</a> to find out which plugin is causing this:<br> '+a.status+" "+a.statusText),console.log(a.statusText)})}),a("#wpstg-workflow").on("click",".wpstg-next-step-link",function(b){if(b.preventDefault(),a(this).attr("disabled"))return!1;a("#wpstg-workflow").addClass("loading");var f={action:a(this).data("action"),nonce:wpstg.nonce};"wpstg_cloning"==f.action&&(f.cloneID=a("#wpstg-new-clone-id").val()||(new Date).getTime(),c(f,!1)),a("#wpstg-workflow").load(ajaxurl,f,function(b,c,g){"error"==c&&(console.log(g.status+" "+g.statusText),d('Fatal Error: This should not happen but is most often caused by other plugins. Try first the option "Optimizer" in WP Staging->Settings and try again. If this does not help, enable <a href="https://codex.wordpress.org/Debugging_in_WordPress" target="_blank">wordpress debug mode</a> to find out which plugin is causing this: '+g.status+" "+g.statusText)),a("#wpstg-workflow").removeClass("loading"),a(".wpstg-current-step").removeClass("wpstg-current-step").next("li").addClass("wpstg-current-step"),"wpstg_cloning"==f.action&&e()})}),a("#wpstg-workflow").on("click",".wpstg-prev-step-link",function(b){b.preventDefault(),a("#wpstg-workflow").addClass("loading");var c={action:"wpstg_overview",nonce:wpstg.nonce};a("#wpstg-workflow").load(ajaxurl,c,function(b,c,e){"error"==c&&(console.log(e.status+" "+e.statusText),d('Fatal Error: This should not happen but is most often caused by other plugins. Try first the option "Optimizer" in WP Staging->Settings and try again. If this does not help, enable <a href="https://codex.wordpress.org/Debugging_in_WordPress" target="_blank">wordpress debug mode</a> to find out which plugin is causing this: '+e.status+" "+e.statusText)),a("#wpstg-workflow").removeClass("loading"),a(".wpstg-current-step").removeClass("wpstg-current-step").prev("li").addClass("wpstg-current-step")})});var o;a("#wpstg-workflow").on("click",".wpstg-execute-clone",function(b){b.preventDefault(),a("#wpstg-workflow").addClass("loading");var c={action:"wpstg_scanning",clone:a(this).data("clone"),nonce:wpstg.nonce};a("#wpstg-workflow").load(ajaxurl,c,function(b,c,e){"error"==c&&(console.log(e.status+" "+e.statusText),d('Fatal Error: This should not happen. Please try again! <br> If restarting does not work <a href="https://wordpress.org/support/plugin/wp-staging" target="blank">get in contact with us</a> '+e.status+" "+e.statusText)),a("#wpstg-workflow").removeClass("loading"),a(".wpstg-current-step").removeClass("wpstg-current-step").next("li").addClass("wpstg-current-step")})}),a("#wpstg-workflow").on("click",".wpstg-remove-clone",function(b){b.preventDefault(),a(".wpstg-clone").removeClass("active"),a("#wpstg-existing-clones").append(m);var c={action:"wpstg_preremove",cloneID:a(this).data("clone")};a("#wpstg-removing-clone").load(ajaxurl,c,function(b,c,e){"error"==c&&(console.log(e.status+" "+e.statusText),d('Fatal Error: This should not happen but is most often caused by other plugins. Try first the option "Optimizer" in WP Staging->Settings and try again. If this does not help, enable <a href="https://codex.wordpress.org/Debugging_in_WordPress" target="_blank">wordpress debug mode</a> to find out which plugin is causing this:<br> '+e.status+" "+e.statusText)),"success"===c&&a("#wpstg-existing-clones").children("img").remove()})}),a("#wpstg-workflow").on("click","#wpstg-cancel-removing",function(b){b.preventDefault(),a(".wpstg-clone").removeClass("active"),a("#wpstg-removing-clone").html("")}),a("#wpstg-workflow").on("click","#wpstg-remove-clone",function(b){b.preventDefault(),a("#wpstg-removing-clone").addClass("loading");var e=a(this).data("clone"),f={action:"wpstg_remove_clone",cloneID:e,nonce:wpstg.nonce};c(f,!0),a.post(ajaxurl,f,function(b,c,f){if(console.log(f.status+" "+f.statusText),"error"==c&&(console.log(f.status+" "+f.statusText),d('Fatal Error: This should not happen but is most often caused by other plugins. Try first the option "Optimizer" in WP Staging->Settings and try again. If this does not help, enable <a href="https://codex.wordpress.org/Debugging_in_WordPress" target="_blank">wordpress debug mode</a> to find out which plugin is causing this:<br> '+f.status+" "+f.statusText)),0==b){a("#wpstg-removing-clone").html(""),a(".wpstg-clone#"+e).remove(),a("#wpstg-removing-clone").removeClass("loading");var g=a(".wpstg-clone");g.length<1&&a("#wpstg-existing-clones h3").text("")}}).fail(function(a){d('Fatal Error: This should not happen but is most often caused by other plugins. Try first the option "Optimizer" in WP Staging->Settings and try again. If this does not help, enable <a href="https://codex.wordpress.org/Debugging_in_WordPress" target="_blank">wordpress debug mode</a> to find out which plugin is causing this:<br> '+a.status+" "+a.statusText),console.log(a.statusText)})});var p=!1,q=!1;a("#wpstg-workflow").on("click","#wpstg-show-log-button",function(b){b.preventDefault(),a("#wpstg-log-details").toggle(),a("html, body").animate({scrollTop:a("#wpstg-log-details").offset().top},400)}),a("#wpstg-workflow").on("click","#wpstg-cancel-cloning",function(b){return b.preventDefault(),confirm("Are you sure?")?(a("#wpstg-try-again, #wpstg-home-link").hide(),a(this).attr("disabled","disabled"),p=!0,a("#wpstg-cloning-result").text("Please wait...this can take up to a minute"),a("#wpstg-loader").hide(),a("#wpstg-show-log-button").hide(),a(this).parent().append(m),void(q&&i())):!1}),a("#wpstg-workflow").on("click","#wpstg-remove-cloning",function(b){return b.preventDefault(),confirm("Are you sure you want to remove the clone site ?")?(a("#wpstg-try-again, #wpstg-home-link").hide(),a(this).attr("disabled","disabled"),a("#wpstg-clone-url").attr("disabled","disabled"),p=!0,a("#wpstg-cloning-result").text("Please wait...this can take up to a minute"),a("#wpstg-loader").hide(),a("#wpstg-success-notice").hide(),a("#wpstg-show-log-button").hide(),a("#wpstg-log-details").hide(),a(this).parent().append(m),void(q&&i())):!1}),a("#wpstg-workflow").on("click","#wpstg-try-again",function(b){b.preventDefault(),console.log("test"),a("#wpstg-workflow").addClass("loading");var c={action:"wpstg_scanning",nonce:wpstg.nonce};a("#wpstg-workflow").load(ajaxurl,c,function(b,c,e){"error"==c&&(console.log(e.status+" "+e.statusText),d('Fatal Error: This should not happen but is most often caused by other plugins. Try first the option "Optimizer" in WP Staging->Settings and try again. If this does not help, enable <a href="https://codex.wordpress.org/Debugging_in_WordPress" target="_blank">wordpress debug mode</a> to find out which plugin is causing this:<br> '+e.status+" "+e.statusText)),a("#wpstg-workflow").removeClass("loading"),a(".wpstg-current-step").removeClass("wpstg-current-step").prev("li").addClass("wpstg-current-step")})}),a("#wpstg-workflow").on("click","#wpstg-reset-clone",function(b){b.preventDefault(),a(this).attr("disabled","disabled").next(".wpstg-next-step-link").hide(),a("#wpstg-loader").show(),o=a(this).data("clone"),i()}),a("#wpstg-workflow").on("click","#wpstg-home-link",function(a){a.preventDefault(),location.reload()}),a("#wpstg-workflow").on("click",".wpstg-tab-header",function(b){b.preventDefault();var c=a(this).data("id");a(this).toggleClass("expand"),a(c).slideToggle(),a(this).find(".wpstg-tab-triangle").html(a(this).hasClass("expand")?"&#9660;":"&#9658;")}),a("#wpstg-workflow").on("click",".wpstg-expand-dirs",function(b){b.preventDefault(),a(this).hasClass("disabled")||a(this).siblings(".wpstg-subdir").slideToggle()}),a("#wpstg-workflow").on("change",".wpstg-check-dir",function(){var b=a(this).parent(".wpstg-dir");this.checked?(b.parents(".wpstg-dir").children(".wpstg-check-dir").attr("checked","checked"),b.find(".wpstg-expand-dirs").removeClass("disabled"),b.find(".wpstg-subdir .wpstg-check-dir").attr("checked","checked")):(b.find(".wpstg-dir .wpstg-check-dir").removeAttr("checked"),b.find(".wpstg-expand-dirs, .wpstg-check-subdirs").addClass("disabled"),b.find(".wpstg-check-subdirs").data("action","check").text("check"),b.children(".wpstg-subdir").slideUp())}),a("#plugin-compatibility").change(function(){var b="1";if(a(this).is(":checked")){var c=confirm(wpstg.mu_plugin_confirmation);if(!c)return void a(this).prop("checked",!1)}else b="0";a(".plugin-compatibility-wrap").toggle(),a(this).parent().append(m),a("#plugin-compatibility").attr("disabled","disabled"),a(".plugin-compatibility").addClass("disabled"),a.ajax({url:ajaxurl,type:"POST",dataType:"text",cache:!1,data:{action:"wpstg_muplugin_install",install:b},error:function(b){alert("Error: "+wpstg.plugin_compatibility_settings_problem+"\r\n\r\n"+wpstg.status+" "+b.status+" "+b.statusText+"\r\n\r\n"+wpstg.response+"\r\n"+b.responseText),a(".ajax-spinner").remove(),a("#plugin-compatibility").removeAttr("disabled"),a(".plugin-compatibility").removeClass("disabled")},success:function(b){""!==a.trim(b)?alert(b):(a(".plugin-compatibility").append('<span class="ajax-success-msg">'+wpstg.saved+"</span>"),a(".ajax-success-msg").fadeOut(2e3,function(){a(this).remove()})),a(".ajax-spinner").remove(),a("#plugin-compatibility").removeAttr("disabled"),a(".plugin-compatibility").removeClass("disabled")}})}),a("#plugin-compatibility").is(":checked")&&a(".plugin-compatibility-wrap").show(),a(".plugin-compatibility-save").click(function(){if(!n){a(this).addClass("disabled");var b=a("#selected-plugins");a(b).attr("disabled","disabled");var c=a("#wpstg_settings\\[wpstg_query_limit\\]"),d=a("#wpstg_settings\\[wpstg_batch_size\\]"),e=a("#wpstg_settings\\[disable_admin_login\\]"),f=a("#wpstg_settings\\[uninstall_on_delete\\]");e=a(e).prop("checked")?"1":"0",f=a(f).prop("checked")?"1":"0",n=!0,a(this).after('<img src="'+l+'" alt="" class="plugin-compatibility-spinner general-spinner" />'),a.ajax({url:ajaxurl,type:"POST",dataType:"text",cache:!1,data:{action:"wpstg_disable_plugins",blacklist_plugins:a(b).val(),query_limit:a(c).val(),batch_size:a(d).val(),disable_admin_login:e,uninstall_on_delete:f},error:function(c){alert(wpstg.blacklist_problem+"\r\n\r\n"+wpstg.status+" "+c.status+" "+c.statusText+"\r\n\r\n"+wpstg.response+"\r\n"+c.responseText),a(b).removeAttr("disabled"),a(".plugin-compatibility-save").removeClass("disabled"),n=!1,a(".plugin-compatibility-spinner").remove(),a(".plugin-compatibility-success-msg").show().fadeOut(2e3)},success:function(c){""!==a.trim(c)&&alert(c),a(b).removeAttr("disabled"),a(".plugin-compatibility-save").removeClass("disabled"),n=!1,a(".plugin-compatibility-spinner").remove(),a(".plugin-compatibility-success-msg").show().fadeOut(2e3)}})}}),a(".multiselect-select-all").click(function(){var b=a(this).parents(".select-wrap").children(".multiselect");a("option",b).attr("selected",1),a(b).focus().trigger("change")}),a(".multiselect-deselect-all").click(function(){var b=a(this).parents(".select-wrap").children(".multiselect");a("option",b).removeAttr("selected"),a(b).focus().trigger("change")}),a(".multiselect-invert-selection").click(function(){var b=a(this).parents(".select-wrap").children(".multiselect");a("option",b).each(function(){a(this).attr("selected",!a(this).attr("selected"))}),a(b).focus().trigger("change")}),a("#wpstg-workflow").on("click","#wpstg-show-large-files",function(b){b.preventDefault(),a("#wpstg-large-files").toggle()})})}(jQuery),window.twttr=function(a,b,c){var d,e=a.getElementsByTagName(b)[0],f=window.twttr||{};return a.getElementById(c)?f:(d=a.createElement(b),d.id=c,d.src="https://platform.twitter.com/widgets.js",e.parentNode.insertBefore(d,e),f._e=[],f.ready=function(a){f._e.push(a)},f)}(document,"script","twitter-wjs");
assets/js/wpstg.js ADDED
File without changes
assets/js/wpstg.min.js ADDED
File without changes
includes/WPSTG_SL_Plugin_Updater.php ADDED
@@ -0,0 +1,377 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // uncomment this line for testing
4
+ //set_site_transient( 'update_plugins', null );
5
+
6
+ // Exit if accessed directly
7
+ if ( ! defined( 'ABSPATH' ) ) exit;
8
+
9
+ /**
10
+ * Allows plugins to use their own update API.
11
+ *
12
+ * @author Pippin Williamson, René Hermenau
13
+ * @version 1.6
14
+ */
15
+ class WPSTG_SL_Plugin_Updater {
16
+ private $api_url = '';
17
+ private $api_data = array();
18
+ private $name = '';
19
+ private $slug = '';
20
+ private $version = '';
21
+
22
+ /**
23
+ * Class constructor.
24
+ *
25
+ * @uses plugin_basename()
26
+ * @uses hook()
27
+ *
28
+ * @param string $_api_url The URL pointing to the custom API endpoint.
29
+ * @param string $_plugin_file Path to the plugin file.
30
+ * @param array $_api_data Optional data to send with API calls.
31
+ */
32
+ function __construct( $_api_url, $_plugin_file, $_api_data = null ) {
33
+ $this->api_url = trailingslashit( $_api_url );
34
+ $this->api_data = $_api_data;
35
+ $this->name = plugin_basename( $_plugin_file );
36
+ $this->slug = basename( $_plugin_file, '.php' );
37
+ $this->version = $_api_data['version'];
38
+ $this->item_name = sanitize_title($_api_data['item_name']); //rhe return the item name words separated by hyphens
39
+
40
+ // Set up hooks.
41
+ $this->init();
42
+ add_action( 'admin_init', array( $this, 'show_changelog' ) );
43
+ }
44
+
45
+ /**
46
+ * Set up WordPress filters to hook into WP's update process.
47
+ *
48
+ * @uses add_filter()
49
+ *
50
+ * @return void
51
+ */
52
+ public function init() {
53
+
54
+ add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ) );
55
+ add_filter( 'plugins_api', array( $this, 'plugins_api_filter' ), 10, 3 );
56
+
57
+ add_action( 'after_plugin_row_' . $this->name, array( $this, 'show_update_notification' ), 10, 2 );
58
+ }
59
+
60
+ /**
61
+ * Check for Updates at the defined API endpoint and modify the update array.
62
+ *
63
+ * This function dives into the update API just when WordPress creates its update array,
64
+ * then adds a custom API call and injects the custom plugin data retrieved from the API.
65
+ * It is reassembled from parts of the native WordPress plugin update code.
66
+ * See wp-includes/update.php line 121 for the original wp_update_plugins() function.
67
+ *
68
+ * @uses api_request()
69
+ *
70
+ * @param array $_transient_data Update array build by WordPress.
71
+ * @return array Modified update array with custom plugin data.
72
+ */
73
+ function check_update( $_transient_data ) {
74
+
75
+ global $pagenow;
76
+
77
+ if( ! is_object( $_transient_data ) ) {
78
+ $_transient_data = new stdClass;
79
+ }
80
+
81
+ if( 'plugins.php' == $pagenow && is_multisite() ) {
82
+ return $_transient_data;
83
+ }
84
+
85
+
86
+ if ( empty( $_transient_data->response ) || empty( $_transient_data->response[ $this->name ] ) ) {
87
+
88
+ $version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug) );
89
+
90
+ /*echo "<pre>check_update() line 74 in WPSTG_SL_Plugin_Updater";
91
+ echo " slug: " . $this->slug;
92
+ echo " version info: " . $version_info->slug;
93
+ echo " new version :" . $version_info->new_version;
94
+ echo " item_name: " . $this->item_name;
95
+ exit;*/
96
+
97
+ if ( false !== $version_info && is_object( $version_info ) && isset( $version_info->new_version ) ) {
98
+
99
+ if( version_compare( $this->version, $version_info->new_version, '<' ) ) {
100
+
101
+ $_transient_data->response[ $this->name ] = $version_info;
102
+ }
103
+ $_transient_data->last_checked = time();
104
+ $_transient_data->checked[ $this->name ] = $this->version;
105
+ }
106
+ }
107
+ return $_transient_data;
108
+ }
109
+
110
+
111
+ /**
112
+ * show update nofication row -- needed for multisite subsites, because WP won't tell you otherwise!
113
+ *
114
+ * @param string $file
115
+ * @param array $plugin
116
+ */
117
+ public function show_update_notification( $file, $plugin ) {
118
+
119
+ if( ! current_user_can( 'update_plugins' ) ) {
120
+ return;
121
+ }
122
+
123
+ if( ! is_multisite() ) {
124
+ return;
125
+ }
126
+
127
+ if ( $this->name != $file ) {
128
+ return;
129
+ }
130
+
131
+ // Remove our filter on the site transient
132
+ remove_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ), 10 );
133
+
134
+ $update_cache = get_site_transient( 'update_plugins' );
135
+
136
+ if ( ! is_object( $update_cache ) || empty( $update_cache->response ) || empty( $update_cache->response[ $this->name ] ) ) {
137
+
138
+ $cache_key = md5( 'wpstg_plugin_' .sanitize_key( $this->name ) . '_version_info' );
139
+ $version_info = get_transient( $cache_key );
140
+
141
+ if( false === $version_info ) {
142
+ $version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug) );
143
+ set_transient( $cache_key, $version_info, 3600 );
144
+ }
145
+
146
+
147
+ if( ! is_object( $version_info ) ) {
148
+ return;
149
+ }
150
+
151
+ if( version_compare( $this->version, $version_info->new_version, '<' ) ) {
152
+
153
+ $update_cache->response[ $this->name ] = $version_info;
154
+
155
+ }
156
+
157
+ $update_cache->last_checked = time();
158
+ $update_cache->checked[ $this->name ] = $this->version;
159
+
160
+ set_site_transient( 'update_plugins', $update_cache );
161
+
162
+ } else {
163
+
164
+ $version_info = $update_cache->response[ $this->name ];
165
+
166
+ }
167
+
168
+ // Restore our filter
169
+ add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ) );
170
+
171
+ if ( ! empty( $update_cache->response[ $this->name ] ) && version_compare( $this->version, $version_info->new_version, '<' ) ) {
172
+
173
+ // build a plugin list row, with update notification
174
+ $wp_list_table = _get_list_table( 'WP_Plugins_List_Table' );
175
+ echo '<tr class="plugin-update-tr"><td colspan="' . $wp_list_table->get_column_count() . '" class="plugin-update colspanchange"><div class="update-message">';
176
+
177
+ $changelog_link = self_admin_url( 'index.php?edd_sl_action=view_plugin_changelog&plugin=' . $this->name . '&slug=' . $this->slug . '&TB_iframe=true&width=772&height=911' );
178
+
179
+ if ( empty( $version_info->download_link ) ) {
180
+ printf(
181
+ __( 'There is a new version of %1$s available. <a target="_blank" class="thickbox" href="%2$s">View version %3$s details</a>.', 'wpstg' ),
182
+ esc_html( $version_info->name ),
183
+ esc_url( $changelog_link ),
184
+ esc_html( $version_info->new_version )
185
+ );
186
+ } else {
187
+ printf(
188
+ __( 'There is a new version of %1$s available. <a target="_blank" class="thickbox" href="%2$s">View version %3$s details</a> or <a href="%4$s">update now</a>.', 'wpstg' ),
189
+ esc_html( $version_info->name ),
190
+ esc_url( $changelog_link ),
191
+ esc_html( $version_info->new_version ),
192
+ esc_url( wp_nonce_url( self_admin_url( 'update.php?action=upgrade-plugin&plugin=' ) . $this->name, 'upgrade-plugin_' . $this->name ) )
193
+ );
194
+ }
195
+
196
+ echo '</div></td></tr>';
197
+ }
198
+ }
199
+
200
+
201
+ /**
202
+ * Updates information on the "View version x.x details" page with custom data.
203
+ *
204
+ * @uses get_transient()
205
+ * @uses api_request()
206
+ *
207
+ * @param mixed $_data
208
+ * @param string $_action
209
+ * @param object $_args
210
+ * @return object $_data
211
+ */
212
+
213
+ function plugins_api_filter( $_data, $_action = '', $_args = null ) {
214
+
215
+
216
+ if ( $_action != 'plugin_information' ) {
217
+
218
+ return $_data;
219
+
220
+ }
221
+
222
+ if ( ! isset( $_args->slug ) || ( $_args->slug != $this->slug ) ) {
223
+
224
+ return $_data;
225
+
226
+ }
227
+
228
+ $to_send = array(
229
+ 'slug' => $this->slug,
230
+ 'is_ssl' => is_ssl(),
231
+ 'fields' => array(
232
+ 'banners' => false, // These will be supported soon hopefully
233
+ 'reviews' => false
234
+ )
235
+ );
236
+ // rhe get plugin information from transient storage cache
237
+ $api_response = $this->get_plugin_transient($this->slug);
238
+ if (empty($api_response)){
239
+ $api_response = $this->api_request( 'plugin_information', $to_send );
240
+ }
241
+
242
+
243
+ if ( false !== $api_response ) {
244
+ $_data = $api_response;
245
+ }
246
+
247
+ return $_data;
248
+ }
249
+
250
+
251
+ /**
252
+ * Disable SSL verification in order to prevent download update failures
253
+ *
254
+ * @param array $args
255
+ * @param string $url
256
+ * @return object $array
257
+ */
258
+ function http_request_args( $args, $url ) {
259
+ // If it is an https request and we are performing a package download, disable ssl verification
260
+ if ( strpos( $url, 'https://' ) !== false && strpos( $url, 'edd_action=package_download' ) ) {
261
+ $args['sslverify'] = false;
262
+ }
263
+ return $args;
264
+ }
265
+
266
+ /**
267
+ * Calls the API and, if successfull, returns the object delivered by the API.
268
+ *
269
+ *
270
+ * @uses get_bloginfo()
271
+ * @uses wp_remote_post()
272
+ * @uses is_wp_error()
273
+ *
274
+ * @param string $_action The requested action.
275
+ * @param array $_data Parameters for the API action.
276
+ * @return false|object
277
+ */
278
+ private function api_request( $_action, $_data ) {
279
+
280
+ global $wp_version;
281
+
282
+ $data = array_merge( $this->api_data, $_data );
283
+
284
+ if ( $data['slug'] != $this->slug )
285
+ return;
286
+
287
+
288
+ //if ( empty( $data['license'] ) )
289
+ //return;
290
+
291
+ //if ( empty( $data['[item_name]'] ) )
292
+ //return; //rhe
293
+
294
+ if( $this->api_url == home_url() ) {
295
+ return false; // Don't allow a plugin to ping itself
296
+ }
297
+
298
+ $api_params = array(
299
+ 'edd_action' => 'get_version',
300
+ 'license' => $data['license'],
301
+ 'item_name' => $this->item_name, //rhe
302
+ 'item_id' => isset( $data['item_id'] ) ? $data['item_id'] : false,
303
+ 'slug' => $data['slug'],
304
+ 'author' => $data['author'],
305
+ 'url' => home_url()
306
+ );
307
+ /*echo "<pre>api_requests() line 277 WPSTG_SL_Plugin_Updater. api params";
308
+ echo " item name: " . $this->name;
309
+ var_dump($api_params);
310
+ exit;*/
311
+
312
+ $request = wp_remote_post( $this->api_url, array( 'timeout' => 15, 'sslverify' => false, 'body' => $api_params ) );
313
+ /*echo "<pre>api_requests() line 277 WPSTG_SL_Plugin_Updater requests:";
314
+ var_dump(wp_remote_retrieve_body($request));
315
+ exit;*/
316
+
317
+ if ( ! is_wp_error( $request ) ) {
318
+ $request = json_decode( wp_remote_retrieve_body( $request ) );
319
+ }
320
+
321
+ if ( $request && isset( $request->sections ) ) {
322
+ $request->sections = maybe_unserialize( $request->sections );
323
+ } else {
324
+ $request = false;
325
+ }
326
+
327
+ return $request;
328
+ }
329
+
330
+ public function show_changelog() {
331
+
332
+
333
+ if( empty( $_REQUEST['edd_sl_action'] ) || 'view_plugin_changelog' != $_REQUEST['edd_sl_action'] ) {
334
+ return;
335
+ }
336
+
337
+ if( empty( $_REQUEST['plugin'] ) ) {
338
+ return;
339
+ }
340
+
341
+ if( empty( $_REQUEST['slug'] ) ) {
342
+ return;
343
+ }
344
+
345
+ if( ! current_user_can( 'update_plugins' ) ) {
346
+ wp_die( __( 'You do not have permission to install plugin updates', 'wpstg' ), __( 'Error', 'wpstg' ), array( 'response' => 403 ) );
347
+ }
348
+
349
+ $response = $this->api_request( 'plugin_latest_version', array( 'slug' => $_REQUEST['slug'] ) );
350
+
351
+ if( $response && isset( $response->sections['changelog'] ) ) {
352
+ echo '<div style="background:#fff;padding:10px;">' . $response->sections['changelog'] . '</div>';
353
+ }
354
+
355
+
356
+ exit;
357
+ }
358
+
359
+ /* Get result of transient by slug
360
+ *
361
+ *
362
+ * @author René Hermenau
363
+ * @scince x.x.x
364
+ * @param $response string
365
+ */
366
+
367
+ public function get_plugin_transient($slug) {
368
+ $result = get_transient($slug . '_update_info');
369
+ $result = json_decode($result);
370
+
371
+ if ($result && isset($result->sections)) {
372
+ $result->sections = maybe_unserialize($result->sections);
373
+ }
374
+ return $result;
375
+ }
376
+
377
+ }
includes/admin/add-ons.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Admin Add-ons
4
+ *
5
+ * @package WPSTG
6
+ * @subpackage Admin/Add-ons
7
+ * @copyright Copyright (c) 2014, Rene Hermenau
8
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9
+ * @since 1.1.8
10
+ */
11
+
12
+ // Exit if accessed directly
13
+ if ( ! defined( 'ABSPATH' ) ) exit;
14
+
15
+ /**
16
+ * Add-ons
17
+ *
18
+ * Renders the add-ons content.
19
+ *
20
+ * @since 1.1.8
21
+ * @return void
22
+ */
23
+ function wpstg_add_ons_page() {
24
+ ob_start(); ?>
25
+ <div class="wrap" id="wpstg-add-ons">
26
+ <h2>
27
+ <?php _e( 'Add Ons for WP-Staging', 'wpstg' ); ?>
28
+ &nbsp;&mdash;&nbsp;<a href="https://www.wp-staging.com" class="button-primary" title="<?php _e( 'Visit Website', 'wpstg' ); ?>" target="_blank"><?php _e( 'See Details', 'wpstg' ); ?></a>
29
+ </h2>
30
+ <p><?php _e( 'These add-ons extend the functionality of WP-Staging.', 'wpstg' ); ?></p>
31
+ <?php echo wpstg_add_ons_get_feed(); ?>
32
+ </div>
33
+ <?php
34
+ echo ob_get_clean();
35
+ }
36
+
37
+ /**
38
+ * Add-ons Get Feed
39
+ *
40
+ * Gets the add-ons page feed.
41
+ *
42
+ * @since 1.1.8
43
+ * @return void
44
+ */
45
+ function wpstg_add_ons_get_feed() {
46
+ if ( false === ( $cache = get_transient( 'wp-staging_add_ons_feed' ) ) ) {
47
+ $feed = wp_remote_get( 'https://www.wp-staging.net/?feed=addons', array( 'sslverify' => false ) );
48
+ if ( ! is_wp_error( $feed ) ) {
49
+ if ( isset( $feed['body'] ) && strlen( $feed['body'] ) > 0 ) {
50
+ $cache = wp_remote_retrieve_body( $feed );
51
+ set_transient( 'wp-staging_add_ons_feed', $cache, 3600 );
52
+ }
53
+ } else {
54
+ $cache = '<div class="error"><p>' . __( 'There was an error retrieving the WP-Staging addon list from the server. Please try again later.', 'wpstg' ) . '
55
+ <br>Visit instead the WP-Staging Addon Website <a href="https://www.wp-staging.net" class="button-primary" title="WP-Staging Add ons" target="_blank"> Get Add-Ons </a></div>';
56
+ }
57
+ }
58
+ return $cache;
59
+ }
includes/admin/admin-actions.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Admin Actions
4
+ *
5
+ * @package WPSTG
6
+ * @subpackage Admin/Actions
7
+ * @copyright Copyright (c) 2015, René Hermenau
8
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9
+ * @since 1.0
10
+ */
11
+
12
+ // Exit if accessed directly
13
+ if ( ! defined( 'ABSPATH' ) ) exit;
14
+
15
+ /**
16
+ * Processes all WPSTG actions sent via POST and GET by looking for the 'wpstg-action'
17
+ * request and running do_action() to call the function
18
+ *
19
+ * @since 1.0
20
+ * @return void
21
+ */
22
+ function wpstg_process_actions() {
23
+ if ( isset( $_POST['wpstg-action'] ) ) {
24
+ do_action( 'wpstg_' . $_POST['wpstg-action'], $_POST );
25
+ }
26
+
27
+ if ( isset( $_GET['wpstg-action'] ) ) {
28
+ do_action( 'wpstg_' . $_GET['wpstg-action'], $_GET );
29
+ }
30
+ }
31
+ add_action( 'admin_init', 'wpstg_process_actions' );
includes/admin/admin-footer.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Admin Footer
4
+ *
5
+ * @package WPSTG
6
+ * @subpackage Admin/Footer
7
+ * @copyright Copyright (c) 2015, René Hermenau
8
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9
+ * @since 1.0
10
+ */
11
+
12
+ // Exit if accessed directly
13
+ if( !defined( 'ABSPATH' ) ) exit;
14
+
15
+ /**
16
+ * Add rating links to the admin dashboard
17
+ *
18
+ * @since 0.9.0
19
+ * @global string $typenow
20
+ * @param string $footer_text The existing footer text
21
+ * @return string
22
+ */
23
+ function wpstg_admin_rate_us( $footer_text ) {
24
+ global $typenow;
25
+
26
+ if ( wpstg_is_admin_page() ) {
27
+
28
+ /*$rate_text = sprintf( __( 'Thank you for using <a href="%1$s" target="_blank">WP Staging</a>! Please <a href="%2$s" target="_blank">rate WP Staging</a> on <a href="%2$s" target="_blank">WordPress.org</a> and help to support this project.<br>Something not working as expected with WP Staging? Read the <a href="https://www.wp-staging.net/faq/" target="blank">FAQ</a> and visit the WP-Staging <a href="https://wp-staging.net/support" target="blank">Support Forum</a>', 'wpstg' ),
29
+ 'https://www.wp-staging.net',
30
+ 'http://wordpress.org/support/view/plugin-reviews/wp-staging?filter=5#postform'
31
+ );*/
32
+ $rate_text = sprintf( __( 'Please <a href="%1$s" target="_blank">rate WP Staging</a> and help to support this project.<br>Something not working as expected? Visit the WP Staging <a href="https://wordpress.org/support/plugin/wp-staging" target="blank">Support Forum</a>', 'wpstg' ),
33
+ 'http://wordpress.org/support/view/plugin-reviews/wp-staging?filter=5#postform'
34
+ );
35
+
36
+ return str_replace( '</span>', '', '' ) . $rate_text . '</span>';
37
+ } else {
38
+ return $footer_text;
39
+ }
40
+ }
41
+ add_filter( 'admin_footer_text', 'wpstg_admin_rate_us' );
includes/admin/admin-notices.php ADDED
@@ -0,0 +1,258 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Admin Notices
4
+ *
5
+ * @package WPSTG
6
+ * @subpackage Admin/Notices
7
+ * @copyright Copyright (c) 2015, René Hermenau
8
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9
+ * @since 1.0
10
+ */
11
+
12
+ // Exit if accessed directly
13
+ if ( ! defined( 'ABSPATH' ) ) exit;
14
+
15
+ /**
16
+ * Admin Messages
17
+ *
18
+ * @since 0.9.0
19
+ * @global $wpstg_options Array of all the WPSTG Options
20
+ * @return void
21
+ */
22
+ function wpstg_admin_messages() {
23
+ global $wpstg_options;
24
+
25
+ if ( wpstg_is_admin_page() && !wp_is_writable( wpstg_get_upload_dir() ) ){
26
+ echo '<div class="error">';
27
+ echo '<p><strong>WP Staging File Permission error: </strong>' . wpstg_get_upload_dir() . ' is not write and/or readable. <br> Check if the folder '.wpstg_get_upload_dir().' exists! File permissions should be chmod 755 or 777.</p>';
28
+ echo '</div>';
29
+ }
30
+ if ( wpstg_is_admin_page() && !wp_is_writable( WPSTG_PLUGIN_DIR . 'logs' ) ){
31
+ echo '<div class="error">';
32
+ echo '<p><strong>WP Staging File Permission error: </strong>' . WPSTG_PLUGIN_DIR . 'logs' . ' is not write and/or readable. <br> Check if the folder '.WPSTG_PLUGIN_DIR . 'logs'.' exists! File permissions should be chmod 755 or 777.</p>';
33
+ echo '</div>';
34
+ }
35
+ $path = wpstg_get_upload_dir() . '/clone_details.json';
36
+ if ( wpstg_is_admin_page() && !wpstg_clonedetailsjson_exists() || !is_readable( $path ) ){
37
+ echo '<div class="error">';
38
+ echo '<p><strong>WP Staging File Permission error: </strong>' . $path . ' is not write and/or readable. <br> Check if the file '.$path.' exists! File permissions should be chmod 644 or 777.</p>';
39
+ echo '</div>';
40
+ }
41
+ $path = wpstg_get_upload_dir() . '/remaining_files.json';
42
+ if ( wpstg_is_admin_page() && !wpstg_remainingjson_exists() || !is_readable( $path ) ){
43
+ echo '<div class="error">';
44
+ echo '<p><strong>WP Staging File Permission error: </strong>' . $path . ' is not write and/or readable . <br> Check if the file '.$path.' exists! File permissions should be chmod 644 or 777.</p>';
45
+ echo '</div>';
46
+ }
47
+ if ( wpstg_is_admin_page() && version_compare( WPSTG_WP_COMPATIBLE, get_bloginfo('version'), '<' )){
48
+ echo '<div class="error"><p>';
49
+ echo sprintf( __('You are using a version of WP Staging which has not been tested with your WordPress version %2$s.<br>
50
+ As WP Staging is using crucial db and file functions it\'s important that you are using a WP Staging version<br>
51
+ which has been verified to be working with your WordPress version. You risk unexpected results up to data lose if you do not so.
52
+ <p>Please look at <a href="%1$s" target="_blank">%s</a> for the latest WP Staging version.', 'wpstg') ,
53
+ 'https://wordpress.org/plugins/wp-staging/',
54
+ get_bloginfo('version')
55
+ );
56
+ echo '</p></div>';
57
+ }
58
+
59
+ echo wpstg_show_beta_message();
60
+
61
+ $install_date = get_option('wpstg_installDate');
62
+ $display_date = date('Y-m-d h:i:s');
63
+ $datetime1 = new DateTime($install_date);
64
+ $datetime2 = new DateTime($display_date);
65
+ $diff_intrval = round(($datetime2->format('U') - $datetime1->format('U')) / (60*60*24));
66
+
67
+ if($diff_intrval >= 7 && get_option('wpstg_RatingDiv')=="no")
68
+ {
69
+ echo '<div class="wpstg_fivestar updated" style="box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);">
70
+ <p>Awesome, you\'ve been using <strong>WP Staging </strong> for more than 1 week. May we ask you to give it a <strong>5-star</strong> rating on Wordpress?
71
+ <p><strong>Regards,<br>René Hermenau</strong>
72
+ <ul>
73
+ <li><a href="https://wordpress.org/support/view/plugin-reviews/wp-staging" class="thankyou" target="_new" title="Ok, you deserved it" style="font-weight:bold;">Ok, you deserved it</a></li>
74
+ <li><a href="javascript:void(0);" class="wpstg_hide_rating" title="I already did" style="font-weight:bold;">I already did</a></li>
75
+ <li><a href="javascript:void(0);" class="wpstg_hide_rating" title="No, not good enough" style="font-weight:bold;">No, not good enough</a></li>
76
+ </ul>
77
+ </div>
78
+ <script>
79
+ jQuery( document ).ready(function( $ ) {
80
+ jQuery(\'.wpstg_hide_rating\').click(function(){
81
+ var data={\'action\':\'wpstg_hide_rating\'}
82
+ jQuery.ajax({
83
+ url: "'.admin_url( 'admin-ajax.php' ).'",
84
+ type: "post",
85
+ data: data,
86
+ dataType: "json",
87
+ async: !0,
88
+ success: function(e) {
89
+ if (e=="success") {
90
+ jQuery(\'.wpstg_fivestar\').slideUp(\'slow\');
91
+ }
92
+ }
93
+ });
94
+ })
95
+ jQuery(\'.wpstg_hide_beta\').click(function(){
96
+ var data={\'action\':\'wpstg_hide_beta\'}
97
+ jQuery.ajax({
98
+ url: "'.admin_url( 'admin-ajax.php' ).'",
99
+ type: "post",
100
+ data: data,
101
+ dataType: "json",
102
+ async: !0,
103
+ success: function(e) {
104
+ if (e=="success") {
105
+ jQuery(\'.wpstg_beta_notice\').slideUp(\'slow\');
106
+ }
107
+ }
108
+ });
109
+ })
110
+ });
111
+ </script>
112
+ ';
113
+ }
114
+ }
115
+ add_action( 'admin_notices', 'wpstg_admin_messages' );
116
+
117
+ /* Hide the rating div
118
+ *
119
+ * @subpackage Admin/Notices
120
+ * @copyright Copyright (c) 2015, René Hermenau
121
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
122
+ * @since 0.9.0
123
+ *
124
+ * @return json string
125
+ *
126
+ */
127
+
128
+ function wpstg_hide_rating_div(){
129
+ update_option('wpstg_RatingDiv','yes');
130
+ echo json_encode(array("success")); exit;
131
+ }
132
+ add_action('wp_ajax_wpstg_hide_rating','wpstg_hide_rating_div');
133
+
134
+ /**
135
+ * Admin Add-ons Notices
136
+ *
137
+ * @since 0.9.0
138
+ * @return void
139
+ */
140
+ function wpstg_admin_addons_notices() {
141
+ add_settings_error( 'wpstg-notices', 'wpstg-addons-feed-error', __( 'There seems to be an issue with the server. Please try again in a few minutes.', 'wpstg' ), 'error' );
142
+ settings_errors( 'wpstg-notices' );
143
+ }
144
+
145
+ /**
146
+ * Dismisses admin notices when Dismiss links are clicked
147
+ *
148
+ * @since 0.9.0
149
+ * @return void
150
+ */
151
+ function wpstg_dismiss_notices() {
152
+
153
+ if( ! is_user_logged_in() ) {
154
+ return;
155
+ }
156
+
157
+ $notice = isset( $_GET['wpstg_notice'] ) ? $_GET['wpstg_notice'] : false;
158
+
159
+ if( ! $notice )
160
+ return; // No notice, so get out of here
161
+
162
+ update_user_meta( get_current_user_id(), '_wpstg_' . $notice . '_dismissed', 1 );
163
+ wp_redirect( remove_query_arg( array( 'wpstg_action', 'wpstg_notice' ) ) ); exit;
164
+
165
+ }
166
+ add_action( 'wpstg_dismiss_notices', 'wpstg_dismiss_notices' );
167
+
168
+
169
+ /*
170
+ * Show big colored update information below the official update notification in /wp-admin/plugins
171
+ * @since 0.9.0
172
+ * @return void
173
+ *
174
+ */
175
+
176
+ function wpstg_plugin_update_message( $args ) {
177
+ $transient_name = 'wpstg_upgrade_notice_' . $args['Version'];
178
+
179
+ if ( false === ( $upgrade_notice = get_transient( $transient_name ) ) ) {
180
+
181
+ $response = wp_remote_get( 'https://plugins.svn.wordpress.org/wp-staging/trunk/readme.txt' );
182
+
183
+ if ( ! is_wp_error( $response ) && ! empty( $response['body'] ) ) {
184
+
185
+ // Output Upgrade Notice
186
+ $matches = null;
187
+ $regexp = '~==\s*Upgrade Notice\s*==\s*=\s*(.*)\s*=(.*)(=\s*' . preg_quote( WPSTG_VERSION ) . '\s*=|$)~Uis';
188
+ $upgrade_notice = '';
189
+
190
+ if ( preg_match( $regexp, $response['body'], $matches ) ) {
191
+ $version = trim( $matches[1] );
192
+ $notices = (array) preg_split('~[\r\n]+~', trim( $matches[2] ) );
193
+
194
+ if ( version_compare( WPSTG_VERSION, $version, '<' ) ) {
195
+
196
+ $upgrade_notice .= '<div class="wpstg_plugin_upgrade_notice" style="padding:10px;background-color: #479CCF;color: #FFF;">';
197
+
198
+ foreach ( $notices as $index => $line ) {
199
+ $upgrade_notice .= wp_kses_post( preg_replace( '~\[([^\]]*)\]\(([^\)]*)\)~', '<a href="${2}" style="text-decoration:underline;color:#ffffff;">${1}</a>', $line ) );
200
+ }
201
+
202
+ $upgrade_notice .= '</div> ';
203
+ }
204
+ }
205
+
206
+ set_transient( $transient_name, $upgrade_notice, DAY_IN_SECONDS );
207
+ }
208
+ }
209
+
210
+ echo wp_kses_post( $upgrade_notice );
211
+ }
212
+ add_action ( "in_plugin_update_message-wp-staging/wp-staging.php", 'wpstg_plugin_update_message' );
213
+
214
+ /**
215
+ * Show a admin notice that this software is beta
216
+ */
217
+ function wpstg_show_beta_message(){
218
+ $notice = '<div class="wpstg_beta_notice error" style="box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);">
219
+ <p>This software is beta and work in progress! <br>WP Staging is well tested and we did our best to catch every possible error we can forecast but we can not handle all possible combinations of different server, plugins and themes. <br><strong>BEFORE</strong> you create your first staging site it´s highly recommended <strong>to make a full backup of your website</strong> first!
220
+ <p><strong>This is no joke! </strong>WP Staging is using crucial database and system close functions which have the power to break your website or even to delete your entire database! WP-Staging has neever caused any errors like data loose on any of the sites we are using for testing, so in most cases everything will be running fine, but we have
221
+ to give out this warning until WP Staging is not in beta status any longer.
222
+ <p>
223
+ One of the best free plugins for an entire wordpress backup is the free one <a href="https://wordpress.org/plugins/backwpup/" target="_blank">BackWPup</a>
224
+ <p>To be more clear: <p>We are not responsible for any damages this plugin will cause to your site. <br>Do a full backup first!</p>
225
+ <ul>
226
+ <li><a href="javascript:void(0);" class="wpstg_hide_beta" title="I understand" style="font-weight:bold;color:#00a0d2;">I understand! (Do not show this again)</a></li>
227
+ </ul>
228
+ </div>
229
+ <script>
230
+ jQuery( document ).ready(function( $ ) {
231
+ jQuery(\'.wpstg_hide_beta\').click(function(){
232
+ var data={\'action\':\'wpstg_hide_beta\'}
233
+ jQuery.ajax({
234
+ url: "'.admin_url( 'admin-ajax.php' ).'",
235
+ type: "post",
236
+ data: data,
237
+ dataType: "json",
238
+ async: !0,
239
+ success: function(e) {
240
+ if (e=="success") {
241
+ jQuery(\'.wpstg_beta_notice\').slideUp(\'slow\');
242
+ }
243
+ }
244
+ });
245
+ })
246
+ });
247
+ </script>
248
+ ';
249
+
250
+ if( get_option('wpstg_hide_beta') === "no" && wpstg_is_admin_page() )
251
+ return $notice;
252
+ }
253
+
254
+ function wpstg_hide_beta_div(){
255
+ update_option('wpstg_hide_beta','yes');
256
+ echo json_encode(array("success")); exit;
257
+ }
258
+ add_action('wp_ajax_wpstg_hide_beta','wpstg_hide_beta_div');
includes/admin/admin-pages.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Admin Pages
4
+ *
5
+ * @package WPSTG
6
+ * @subpackage Admin/Pages
7
+ * @copyright Copyright (c) 2015, René Hermenau
8
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9
+ * @since 0.9.0
10
+ */
11
+
12
+ // Exit if accessed directly
13
+ if ( ! defined( 'ABSPATH' ) ) exit;
14
+
15
+ /**
16
+ * Creates the admin submenu pages under the WP-Staging menu and assigns their
17
+ * links to global variables
18
+ *
19
+ * @since 1.0
20
+ * @global $wpstg_settings_page
21
+ * @global $wpstg_add_ons_page
22
+ * @global $wpstg_tools_page
23
+ * @return void
24
+ */
25
+ function wpstg_add_options_link() {
26
+ global $wpstg_parent_page, $wpstg_add_ons_page, $wpstg_add_ons_page2, $wpstg_settings_page, $wpstg_tools_page, $wpstg_clone_page;
27
+ $wpstg_parent_page = add_menu_page( 'WP-Staging', __( 'WP Staging', 'wpstg' ), 'manage_options', 'wpstg_clone', 'wpstg_clone_page', 'dashicons-hammer' );
28
+ $wpstg_clone_page = add_submenu_page('wpstg_clone', __('WP Staging Jobs', 'wpstg'), __('Start', 'wpstg'), 'manage_options', 'wpstg_clone', 'wpstg_clone_page');
29
+ $wpstg_settings_page = add_submenu_page( 'wpstg_clone', __( 'WP Staging Settings', 'wpstg' ), __( 'Settings', 'wpstg' ), 'manage_options', 'wpstg-settings', 'wpstg_options_page' );
30
+ $wpstg_tools_page = add_submenu_page( 'wpstg_clone', __( 'WP Staging Tools', 'wpstg' ), __( 'Tools', 'wpstg' ), 'manage_options', 'wpstg-tools', 'wpstg_tools_page' );
31
+
32
+ }
33
+ add_action( 'admin_menu', 'wpstg_add_options_link', 10 );
34
+
35
+ /**
36
+ * Determines whether the current admin page is an WPSTG admin page.
37
+ *
38
+ * Only works after the `wp_loaded` hook, & most effective
39
+ * starting on `admin_menu` hook.
40
+ *
41
+ * @since 0.9.0
42
+ * @return bool True if WPSTG admin page.
43
+ */
44
+ function wpstg_is_admin_page() {
45
+ $currentpage = isset($_GET['page']) ? $_GET['page'] : '';
46
+ if ( ! is_admin() || ! did_action( 'wp_loaded' ) ) {
47
+ return false;
48
+ }
49
+
50
+ global $wpstg_parent_page, $pagenow, $typenow, $wpstg_settings_page, $wpstg_add_ons_page, $wpstg_tools_page, $wpstg_clone_page;
51
+
52
+ if ( 'wpstg-settings' == $currentpage || 'wpstg-addons' == $currentpage || 'wpstg-tools' == $currentpage || 'wpstg-clone' == $currentpage || 'wpstg_clone' == $currentpage) {
53
+ //mashdebug()->info("wpstg_is_admin_page() = true");
54
+ return true;
55
+ }
56
+ }
includes/admin/plugins.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Admin Plugins
4
+ *
5
+ * @package WPSTG
6
+ * @subpackage Admin/Plugins
7
+ * @copyright Copyright (c) 2015, René Hermenau
8
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9
+ * @since 1.0
10
+ */
11
+
12
+
13
+ // Exit if accessed directly
14
+ if ( ! defined( 'ABSPATH' ) ) exit;
15
+
16
+
17
+ /**
18
+ * Plugins row action links
19
+ *
20
+ * @author Michael Cannon <mc@aihr.us>
21
+ * @since 0.9.0
22
+ * @param array $links already defined action links
23
+ * @param string $file plugin file path and name being processed
24
+ * @return array $links
25
+ */
26
+ function wpstg_plugin_action_links( $links, $file ) {
27
+ $settings_link = '<a href="' . admin_url( 'admin.php?page=wpstg-settings' ) . '">' . esc_html__( 'General Settings', 'wpstg' ) . '</a>';
28
+ if ( $file == 'wp-staging/wp-staging.php' )
29
+ array_unshift( $links, $settings_link );
30
+
31
+ return $links;
32
+ }
33
+ add_filter( 'plugin_action_links', 'wpstg_plugin_action_links', 10, 2 );
34
+
35
+
36
+ /**
37
+ * Plugin row meta links
38
+ *
39
+ * @author Michael Cannon <mc@aihr.us>
40
+ * @since 2.0
41
+ * @param array $input already defined meta links
42
+ * @param string $file plugin file path and name being processed
43
+ * @return array $input
44
+ */
45
+ function wpstg_plugin_row_meta( $input, $file ) {
46
+ if ( $file != 'wp-staging/wp-staging.php' )
47
+ return $input;
48
+
49
+ /*$links = array(
50
+ '<a href="' . admin_url( 'options-general.php?page=wpstg-settings' ) . '">' . esc_html__( 'Getting Started', 'wpstg' ) . '</a>',
51
+ '<a href="https://www.wp-staging.net/downloads/">' . esc_html__( 'Add Ons', 'wpstg' ) . '</a>',
52
+ );*/
53
+
54
+ $links = array(
55
+ '<a href="' . admin_url( 'admin.php?page=wpstg-settings' ) . '">' . esc_html__( 'Getting Started', 'wpstg' ) . '</a>',
56
+ );
57
+
58
+ $input = array_merge( $input, $links );
59
+
60
+ return $input;
61
+ }
62
+ add_filter( 'plugin_row_meta', 'wpstg_plugin_row_meta', 10, 2 );
includes/admin/settings/contextual-help.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Contextual Help
4
+ *
5
+ * @package WPSTG
6
+ * @subpackage Admin/Settings
7
+ * @copyright Copyright (c) 2014, René Hermenau
8
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9
+ * @since 1.0
10
+ */
11
+
12
+ // Exit if accessed directly
13
+ if ( ! defined( 'ABSPATH' ) ) exit;
14
+
15
+ /**
16
+ * Settings contextual help.
17
+ *
18
+ * @access private
19
+ * @since 1.0
20
+ * @return void
21
+ */
22
+ function wpstg_settings_contextual_help() {
23
+ $screen = get_current_screen();
24
+
25
+ /*if ( $screen->id != 'wpstg-settings' )
26
+ return;
27
+ */
28
+ $screen->set_help_sidebar(
29
+ '<p><strong>' . $screen->id . sprintf( __( 'For more information:', 'wpstg' ) . '</strong></p>' .
30
+ '<p>' . sprintf( __( 'Visit the <a href="%s">documentation</a> on the WP-Staging website.', 'wpstg' ), esc_url( 'https://www.wp-staging.net/' ) ) ) . '</p>' .
31
+ '<p>' . sprintf(
32
+ __( '<a href="%s">Post an issue</a> on <a href="%s">WP-Staging</a>. View <a href="%s">extensions</a>.', 'wpstg' ),
33
+ esc_url( 'https://www.wp-staging.net/contact-support/' ),
34
+ esc_url( 'https://www.wp-staging.net' ),
35
+ esc_url( 'https://www.wp-staging.net/downloads' )
36
+ ) . '</p>'
37
+ );
38
+
39
+ $screen->add_help_tab( array(
40
+ 'id' => 'wpstg-settings-general',
41
+ 'title' => __( 'General', 'wpstg' ),
42
+ 'content' => '<p>' . __( 'This screen provides the most basic settings for configuring WP-Staging.', 'wpstg' ) . '</p>'
43
+ ) );
44
+
45
+
46
+
47
+
48
+ do_action( 'wpstg_settings_contextual_help', $screen );
49
+ }
50
+ add_action( 'load-wpstg-settings', 'wpstg_settings_contextual_help' );
includes/admin/settings/display-settings.php ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Admin Options Page
4
+ *
5
+ * @package WPSTG
6
+ * @subpackage Admin/Settings
7
+ * @copyright Copyright (c) 2015, René Hermenau
8
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9
+ * @since 1.0
10
+ */
11
+
12
+ // Exit if accessed directly
13
+ if ( ! defined( 'ABSPATH' ) ) exit;
14
+
15
+ /* Returns list elements for jQuery tab navigation
16
+ * based on header callback
17
+ *
18
+ * @since 0.9.0
19
+ * @todo Use sprintf to sanitize $field['id'] instead using str_replace() Should be faster?
20
+ * @return string
21
+ */
22
+
23
+ function wpstg_getTabHeader($page, $section){
24
+ global $wpstg_options;
25
+ global $wp_settings_fields;
26
+
27
+ if (!isset($wp_settings_fields[$page][$section]))
28
+ return;
29
+
30
+ echo '<ul>';
31
+ foreach ((array) $wp_settings_fields[$page][$section] as $field) {
32
+ $sanitizedID = str_replace('[', '', $field['id'] );
33
+ $sanitizedID = str_replace(']', '', $sanitizedID );
34
+ if (strpos($field['callback'],'header') !== false) {
35
+ echo '<li class="wpstg-tabs"><a href="#' . $sanitizedID . '">' . $field['title'] .'</a></li>';
36
+ }
37
+ }
38
+ echo '</ul>';
39
+ }
40
+
41
+
42
+ /**
43
+ * Print out the settings fields for a particular settings section
44
+ *
45
+ * Part of the Settings API. Use this in a settings page to output
46
+ * a specific section. Should normally be called by do_settings_sections()
47
+ * rather than directly.
48
+ *
49
+ * @global $wp_settings_fields Storage array of settings fields and their pages/sections
50
+ * @return string
51
+ *
52
+ * @since 2.1.2
53
+ *
54
+ * @param string $page Slug title of the admin page who's settings fields you want to show.
55
+ * @param section $section Slug title of the settings section who's fields you want to show.
56
+ *
57
+ * Copied from WP Core 4.0 /wp-admin/includes/template.php do_settings_fields()
58
+ * We use our own function to be able to create jQuery tabs with easytabs()
59
+ *
60
+ * We dont use tables here any longer. Are we stuck in the nineties?
61
+ * @todo Use sprintf to sanitize $field['id'] instead using str_replace() Should be faster?
62
+ * @todo some media queries for better responisbility
63
+ */
64
+ function wpstg_do_settings_fields($page, $section) {
65
+ global $wp_settings_fields;
66
+ $header = false;
67
+ $firstHeader = false;
68
+
69
+ if (!isset($wp_settings_fields[$page][$section]))
70
+ return;
71
+
72
+ // Check first if any callback header registered
73
+ foreach ((array) $wp_settings_fields[$page][$section] as $field) {
74
+ strpos($field['callback'],'header') !== false ? $header = true : $header = false;
75
+
76
+ if ($header === true)
77
+ break;
78
+ }
79
+
80
+ foreach ((array) $wp_settings_fields[$page][$section] as $field) {
81
+
82
+ $sanitizedID = str_replace('[', '', $field['id'] );
83
+ $sanitizedID = str_replace(']', '', $sanitizedID );
84
+
85
+ // Check if header has been created previously
86
+ if (strpos($field['callback'],'header') !== false && $firstHeader === false) {
87
+ echo '<div id="' . $sanitizedID . '">';
88
+ echo '<table class="form-table"><tbody>';
89
+ $firstHeader = true;
90
+ } elseif (strpos($field['callback'],'header') !== false && $firstHeader === true) {
91
+ // Header has been created previously so we have to close the first opened div
92
+ echo '</table></div><div id="' . $sanitizedID . '">';
93
+ echo '<table class="form-table"><tbody>';
94
+
95
+ }
96
+ echo '<tr class="row"><th class="row th">';
97
+ //echo "<pre>";
98
+ //var_dump($field);
99
+ if (!empty($field['args']['label_for']))
100
+ echo '<label for="' . esc_attr($field['args']['label_for']) . '">' . $field['title'] . '</label>';
101
+ else
102
+ echo '<div class="col-title">' . $field['title'] . '<span class="description">' . $field['args']['desc'] . '</span></div>';
103
+ echo '</th>';
104
+ echo '<td>';
105
+ call_user_func($field['callback'], $field['args']);
106
+ echo '</td></tr>';
107
+
108
+
109
+ }
110
+ echo '</tbody></table>';
111
+ if ($header === true){
112
+ echo '</div>';
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Options Page
118
+ *
119
+ * Renders the options page contents.
120
+ *
121
+ * @since 1.0
122
+ * @global $wpstg_options Array of all the WPSTG Options
123
+ * @return void
124
+ */
125
+ function wpstg_options_page() {
126
+ global $wpstg_options;
127
+
128
+ $active_tab = isset( $_GET[ 'tab' ] ) && array_key_exists( $_GET['tab'], wpstg_get_settings_tabs() ) ? $_GET[ 'tab' ] : 'general';
129
+
130
+ ob_start();
131
+ ?>
132
+ <div class="wpstg_admin">
133
+ <span class="wp-staginglogo"><img src="<?php echo WPSTG_PLUGIN_URL . 'assets/images/logo_clean_small_212_25.png';?>"></span><span class="wpstg-version"><?php echo WPSTG_VERSION . ''; ?></span>
134
+ <div class="wpstg-header">
135
+ <?php echo __('Thank you for using WP Staging.', 'wpstg');?>
136
+ <br>
137
+ <?php echo __('WP Staging is ready to create a staging site!', 'wpstg'); ?>
138
+ <br>
139
+ <iframe src="//www.facebook.com/plugins/like.php?href=https%3A%2F%2Fwordpress.org%2Fplugins%2Fwp-staging%2F&amp;width=100&amp;layout=standard&amp;action=like&amp;show_faces=false&amp;share=true&amp;height=35&amp;appId=449277011881884" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:96px; height:20px;" allowTransparency="true"></iframe>
140
+ <a class="twitter-follow-button" href="https://twitter.com/wp_staging" data-size="small" id="twitter-wjs" style="display: none;">Follow @wp_staging</a>
141
+ <a class="twitter-share-button" href="https://twitter.com/intent/tweet?text=Check%20this%20WordPress%20Staging%20plugin%20&url=https://wordpress.org/plugins/wp-staging&hashtags=wpstaging&via=wp_staging">Tweet</a>
142
+ </div>
143
+ <h2 class="nav-tab-wrapper">
144
+ <?php
145
+ foreach( wpstg_get_settings_tabs() as $tab_id => $tab_name ) {
146
+
147
+ $tab_url = esc_url(add_query_arg( array(
148
+ 'settings-updated' => false,
149
+ 'tab' => $tab_id
150
+ ) ));
151
+
152
+ $active = $active_tab == $tab_id ? ' nav-tab-active' : '';
153
+
154
+ echo '<a href="' . esc_url( $tab_url ) . '" title="' . esc_attr( $tab_name ) . '" class="nav-tab' . $active . '">';
155
+ echo esc_html( $tab_name );
156
+ echo '</a>';
157
+ }
158
+ ?>
159
+ </h2>
160
+ <div id="tab_container" class="tab_container">
161
+ <?php //wpstg_getTabHeader( 'wpstg_settings_' . $active_tab, 'wpstg_settings_' . $active_tab ); ?>
162
+ <div class="panel-container"> <!-- new //-->
163
+ <form method="post" action="options.php">
164
+ <?php
165
+ settings_fields( 'wpstg_settings' );
166
+ wpstg_do_settings_fields( 'wpstg_settings_' . $active_tab, 'wpstg_settings_' . $active_tab );
167
+ ?>
168
+ <!--</table>-->
169
+
170
+ <?php
171
+ // do not show save button on add-on page
172
+ if ($active_tab !== 'addons')
173
+ submit_button();
174
+ ?>
175
+ </form>
176
+ </div> <!-- new //-->
177
+ </div><!-- #tab_container-->
178
+ </div><!-- .wrap -->
179
+ <?php
180
+ echo ob_get_clean();
181
+ }
includes/admin/settings/register-settings.php ADDED
@@ -0,0 +1,839 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Register Settings
4
+ *
5
+ * @package WPSTG
6
+ * @subpackage Admin/Settings
7
+ * @copyright Copyright (c) 2014, René Hermenau
8
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9
+ * @since 1.0
10
+ */
11
+
12
+ // Exit if accessed directly
13
+ if ( !defined( 'ABSPATH' ) ) exit;
14
+
15
+
16
+
17
+ /**
18
+ * Get an option
19
+ *
20
+ * Looks to see if the specified setting exists, returns default if not
21
+ *
22
+ * @since 0.9.0
23
+ * @return mixed
24
+ */
25
+ function wpstg_get_option( $key = '', $default = false ) {
26
+ global $wpstg_options;
27
+ $value = ! empty( $wpstg_options[ $key ] ) ? $wpstg_options[ $key ] : $default;
28
+ $value = apply_filters( 'wpstg_get_option', $value, $key, $default );
29
+ return apply_filters( 'wpstg_get_option_' . $key, $value, $key, $default );
30
+ }
31
+
32
+ /**
33
+ * Get Settings
34
+ *
35
+ * Retrieves all plugin settings
36
+ *
37
+ * @since 1.0
38
+ * @return array WPSTG settings
39
+ */
40
+ function wpstg_get_settings() {
41
+ $settings = get_option( 'wpstg_settings' );
42
+
43
+
44
+ if( empty( $settings ) ) {
45
+ // Update old settings with new single option
46
+ $general_settings = is_array( get_option( 'wpstg_settings_general' ) ) ? get_option( 'wpstg_settings_general' ) : array();
47
+
48
+ //$settings = array_merge( $general_settings, $ext_settings, $license_settings);//, $networks, $ext_settings, $license_settings, $addons_settings);
49
+ $settings = $general_settings;
50
+
51
+ update_option( 'wpstg_settings', $settings);
52
+ }
53
+ return apply_filters( 'wpstg_get_settings', $settings );
54
+ }
55
+
56
+ /**
57
+ * Add all settings sections and fields
58
+ *
59
+ * @since 1.0
60
+ * @return void
61
+ */
62
+ function wpstg_register_settings() {
63
+
64
+ if ( false == get_option( 'wpstg_settings' ) ) {
65
+ add_option( 'wpstg_settings' );
66
+ }
67
+
68
+ foreach( wpstg_get_registered_settings() as $tab => $settings ) {
69
+
70
+ add_settings_section(
71
+ 'wpstg_settings_' . $tab,
72
+ __return_null(),
73
+ '__return_false',
74
+ 'wpstg_settings_' . $tab
75
+ );
76
+
77
+ foreach ( $settings as $option ) {
78
+
79
+ $name = isset( $option['name'] ) ? $option['name'] : '';
80
+
81
+ add_settings_field(
82
+ 'wpstg_settings[' . $option['id'] . ']',
83
+ $name,
84
+ function_exists( 'wpstg_' . $option['type'] . '_callback' ) ? 'wpstg_' . $option['type'] . '_callback' : 'wpstg_missing_callback',
85
+ 'wpstg_settings_' . $tab,
86
+ 'wpstg_settings_' . $tab,
87
+ array(
88
+ 'id' => isset( $option['id'] ) ? $option['id'] : null,
89
+ 'desc' => ! empty( $option['desc'] ) ? $option['desc'] : '',
90
+ 'name' => isset( $option['name'] ) ? $option['name'] : null,
91
+ 'section' => $tab,
92
+ 'size' => isset( $option['size'] ) ? $option['size'] : null,
93
+ 'options' => isset( $option['options'] ) ? $option['options'] : '',
94
+ 'std' => isset( $option['std'] ) ? $option['std'] : '',
95
+ 'textarea_rows' => isset( $option['textarea_rows']) ? $option['textarea_rows'] : ''
96
+ )
97
+ );
98
+ }
99
+
100
+ }
101
+
102
+ // Creates our settings in the options table
103
+ register_setting( 'wpstg_settings', 'wpstg_settings', 'wpstg_settings_sanitize' );
104
+
105
+ }
106
+ add_action('admin_init', 'wpstg_register_settings');
107
+
108
+ /**
109
+ * Retrieve the array of plugin settings
110
+ *
111
+ * @since 1.8
112
+ * @return array
113
+ */
114
+ function wpstg_get_registered_settings() {
115
+
116
+ /**
117
+ * 'Whitelisted' WPSTG settings, filters are provided for each settings
118
+ * section to allow extensions and other plugins to add their own settings
119
+ */
120
+ $wpstg_settings = array(
121
+ /** General Settings */
122
+ 'general' => apply_filters( 'wpstg_settings_general',
123
+ array(
124
+ array(
125
+ 'id' => 'wpstg_header',
126
+ 'name' => '<strong>' . __( 'General', 'wpstg' ) . '</strong>',
127
+ 'desc' => '',
128
+ 'type' => 'header',
129
+ 'size' => 'regular'
130
+ ),
131
+ array(
132
+ 'id' => 'wpstg_query_limit',
133
+ 'name' => __('DB Copy Query Limit', 'wpstg'),
134
+ 'desc' => __('Number of DB rows, that will be copied within one ajax request. The higher the value the faster the database copy process. To find out the highest possible values try a high value like 1.000 or more and decrease it until you get no more errors during copy process. <strong> Default: 100 </strong>'),
135
+ 'type' => 'number',
136
+ 'size' => 'medium',
137
+ 'std' => 100,
138
+ ),
139
+ array(
140
+ 'id' => 'wpstg_batch_size',
141
+ 'name' => __('File Copy Batch Size', 'wpstg'),
142
+ 'desc' => __('Buffer size for the file copy process in megabyte. The higher the value the faster large files will be copied. To find out the highest possible values try a high one and lower it until you get no errors during file copy process. Usually this value correlates directly with the memory consumption of php so make sure that it does not exceed any php.ini max_memory limits. <strong>Default:</strong> 2 ', 'wpstg'),
143
+ 'type' => 'number',
144
+ 'size' => 'medium',
145
+ 'std' => '2',
146
+ ),
147
+ array(
148
+ 'id' => 'wpstg_cpu_load',
149
+ 'name' => __('CPU load priority', 'wpstg'),
150
+ 'desc' => __('Using high will result in fast as possible processing but the cpu load increases and it\'s also possible that staging process gets interupted because of too many ajax requests (e.g. <strong>authorization error</strong>). Using a lower value results in lower cpu load on your server but also slower staging site creation. <strong>Default: </strong> Medium ', 'wpstg'),
151
+ 'type' => 'select',
152
+ 'size' => 'medium',
153
+ 'options' => array(
154
+ 'default' => 'Default',
155
+ 'high' => 'High (fast)',
156
+ 'medium' => 'Medium (average)',
157
+ 'low' => 'Low (slow)'
158
+
159
+ )
160
+ ),
161
+ array(
162
+ 'id' => 'wpstg_disabled_plugins',
163
+ 'name' => __('Optimizer', 'wpstg'),
164
+ 'desc' => __('Select the plugins that should be disabled during build process of the staging site. Some plugins slow down the copy process and add overhead to each request, requiring extra CPU and memory consumption. Some of them can interfere with cloning process and cause them to fail, so we recommend to disable all plugins that are not directly related to WP Staging.', 'wpstg'),
165
+ 'type' => 'install_muplugin',
166
+ 'size' => 'medium',
167
+ 'std' => '20',
168
+ ),
169
+ 'disable_admin_login' => array(
170
+ 'id' => 'disable_admin_login',
171
+ 'name' => __( 'Disable admin authorization', 'mashsb' ),
172
+ 'desc' => __( 'Use this option only if you are using a custom login page and not the default login.php. If you enable this option you are allowing everyone including searchengines to see your staging site, so you have to create a custom authentication like using .htaccess', 'mashsb' ),
173
+ 'type' => 'checkbox'
174
+ ),
175
+ 'wordpress_subdirectory' => array(
176
+ 'id' => 'wordpress_subdirectory',
177
+ 'name' => __( 'Wordpress in subdirectory', 'mashsb' ),
178
+ 'desc' => __( 'Use this option when you gave wordpress its own subdirectory. if you enable this, WP Staging will reset the index.php of the clone site to the originally one. <br> <a href="https://codex.wordpress.org/Giving_WordPress_Its_Own_Directory" target="_blank">Read more in the WordPress Codex</a>', 'mashsb' ),
179
+ 'type' => 'checkbox'
180
+ ),
181
+ /*'link_images' => array(
182
+ 'id' => 'link_images',
183
+ 'name' => __( 'Link images. Change upload ', 'mashsb' ),
184
+ 'desc' => __( 'Enable this if you want WP Staging to link images. if you enable this, WP Staging will reset the index.php of the clone site to the originally one. <br> <a href="https://codex.wordpress.org/Giving_WordPress_Its_Own_Directory" target="_blank">Read more in the WordPress Codex</a>', 'mashsb' ),
185
+ 'type' => 'checkbox'
186
+ ),*/
187
+ /*'admin_login_page' => array(
188
+ 'id' => 'admin_login_page',
189
+ 'name' => __( 'Login page', 'mashsb' ),
190
+ 'desc' => __( ' This is necessary if you are using a custom login page and not the default login.php. Fill in the page id of your custom login page, otherwise you will not be able to login to your staging website.', 'mashsb' ),
191
+ 'type' => 'text',
192
+ 'size' => 'medium'
193
+ ),*/
194
+ 'debug_mode' => array(
195
+ 'id' => 'debug_mode',
196
+ 'name' => __( 'Debug Mode', 'mashsb' ),
197
+ 'desc' => __( 'This will enable an extended debug mode which creates additional entries in <strong>wp-content/wp-staging/logs</strong>. Please enable this when we ask you to do so.', 'mashsb' ),
198
+ 'type' => 'checkbox'
199
+ ),
200
+ 'uninstall_on_delete' => array(
201
+ 'id' => 'uninstall_on_delete',
202
+ 'name' => __( 'Remove Data on Uninstall?', 'mashsb' ),
203
+ 'desc' => __( 'Check this box if you like WP Staging to completely remove all of its data when the plugin is deleted.', 'mashsb' ),
204
+ 'type' => 'checkbox'
205
+ ),
206
+
207
+ )
208
+ ),
209
+ 'licenses' => apply_filters('wpstg_settings_licenses',
210
+ array('licenses_header' => array(
211
+ 'id' => 'licenses_header',
212
+ 'name' => __( 'Activate your Add-Ons', 'wpstg' ),
213
+ 'desc' => '',
214
+ 'type' => 'header'
215
+ ),)
216
+ ),
217
+ 'extensions' => apply_filters('wpstg_settings_extension',
218
+ array()
219
+ ),
220
+ 'addons' => apply_filters('wpstg_settings_addons',
221
+ array(
222
+ 'addons' => array(
223
+ 'id' => 'addons',
224
+ 'name' => __( '', 'wpstg' ),
225
+ 'desc' => __( '', 'wpstg' ),
226
+ 'type' => 'addons'
227
+ )
228
+ )
229
+ )
230
+ );
231
+
232
+ return $wpstg_settings;
233
+ }
234
+
235
+ /**
236
+ * Settings Sanitization
237
+ *
238
+ * Adds a settings error (for the updated message)
239
+ * At some point this will validate input
240
+ *
241
+ * @since 0.9.0
242
+ *
243
+ * @param array $input The value input in the field
244
+ *
245
+ * @return string $input Sanitized value
246
+ */
247
+ function wpstg_settings_sanitize( $input = array() ) {
248
+
249
+ global $wpstg_options;
250
+
251
+ if ( empty( $_POST['_wp_http_referer'] ) ) {
252
+ return $input;
253
+ }
254
+
255
+ parse_str( $_POST['_wp_http_referer'], $referrer );
256
+
257
+ $settings = wpstg_get_registered_settings();
258
+ $tab = isset( $referrer['tab'] ) ? $referrer['tab'] : 'general';
259
+
260
+ $input = $input ? $input : array();
261
+ $input = apply_filters( 'wpstg_settings_' . $tab . '_sanitize', $input );
262
+
263
+ // Loop through each setting being saved and pass it through a sanitization filter
264
+ foreach ( $input as $key => $value ) {
265
+
266
+ // Get the setting type (checkbox, select, etc)
267
+ $type = isset( $settings[$tab][$key]['type'] ) ? $settings[$tab][$key]['type'] : false;
268
+
269
+ if ( $type ) {
270
+ // Field type specific filter
271
+ $input[$key] = apply_filters( 'wpstg_settings_sanitize_' . $type, $value, $key );
272
+ }
273
+
274
+ // General filter
275
+ $input[$key] = apply_filters( 'wpstg_settings_sanitize', $value, $key );
276
+ }
277
+
278
+ // Loop through the whitelist and unset any that are empty for the tab being saved
279
+ if ( ! empty( $settings[$tab] ) ) {
280
+ foreach ( $settings[$tab] as $key => $value ) {
281
+
282
+ // settings used to have numeric keys, now they have keys that match the option ID. This ensures both methods work
283
+ if ( is_numeric( $key ) ) {
284
+ $key = $value['id'];
285
+ }
286
+
287
+ if ( empty( $input[$key] ) ) {
288
+ unset( $wpstg_options[$key] );
289
+ }
290
+
291
+ }
292
+ }
293
+
294
+ // Merge our new settings with the existing
295
+ $output = array_merge( $wpstg_options, $input );
296
+
297
+ add_settings_error( 'wpstg-notices', '', __( 'Settings updated.', 'wpstg' ), 'updated' );
298
+
299
+ return $output;
300
+ }
301
+
302
+
303
+ /**
304
+ * Sanitize text fields
305
+ *
306
+ * @since 1.8
307
+ * @param array $input The field value
308
+ * @return string $input Sanitizied value
309
+ */
310
+ function wpstg_sanitize_text_field( $input ) {
311
+ return trim( $input );
312
+ }
313
+ add_filter( 'wpstg_settings_sanitize_text', 'wpstg_sanitize_text_field' );
314
+
315
+ /**
316
+ * Retrieve settings tabs
317
+ *
318
+ * @since 1.8
319
+ * @param array $input The field value
320
+ * @return string $input Sanitizied value
321
+ */
322
+ function wpstg_get_settings_tabs() {
323
+
324
+ $settings = wpstg_get_registered_settings();
325
+
326
+ $tabs = array();
327
+ $tabs['general'] = __( 'General', 'wpstg' );
328
+
329
+ if( ! empty( $settings['misc'] ) ) {
330
+ $tabs['misc'] = __( 'Misc', 'wpstg' );
331
+ }
332
+
333
+ if( ! empty( $settings['networks'] ) ) {
334
+ //$tabs['networks'] = __( 'Social Networks', 'wpstg' );
335
+ }
336
+
337
+ if( ! empty( $settings['extensions'] ) ) {
338
+ $tabs['extensions'] = __( 'Extensions', 'wpstg' );
339
+ }
340
+
341
+ if( ! empty( $settings['licenses'] ) ) {
342
+ //$tabs['licenses'] = __( 'Licenses', 'wpstg' );
343
+ }
344
+ //$tabs['addons'] = __( 'Add-Ons', 'wpstg' );
345
+
346
+ //$tabs['misc'] = __( 'Misc', 'wpstg' );
347
+
348
+ return apply_filters( 'wpstg_settings_tabs', $tabs );
349
+ }
350
+
351
+
352
+
353
+ /**
354
+ * Header Callback
355
+ *
356
+ * Renders the header.
357
+ *
358
+ * @since 1.0
359
+ * @param array $args Arguments passed by the setting
360
+ * @return void
361
+ */
362
+ function wpstg_header_callback( $args ) {
363
+ //echo '<hr/>';
364
+ echo '&nbsp';
365
+ }
366
+
367
+ /**
368
+ * Checkbox Callback
369
+ *
370
+ * Renders checkboxes.
371
+ *
372
+ * @since 1.0
373
+ * @param array $args Arguments passed by the setting
374
+ * @global $wpstg_options Array of all the WPSTG Options
375
+ * @return void
376
+ */
377
+ function wpstg_checkbox_callback( $args ) {
378
+ global $wpstg_options;
379
+
380
+ $checked = isset( $wpstg_options[ $args[ 'id' ] ] ) ? checked( 1, $wpstg_options[ $args[ 'id' ] ], false ) : '';
381
+ $html = '<input type="checkbox" id="wpstg_settings[' . $args['id'] . ']" name="wpstg_settings[' . $args['id'] . ']" value="1" ' . $checked . '/>';
382
+ $html .= '<label class="wpstg_hidden" for="wpstg_settings[' . $args['id'] . ']"> ' . $args['desc'] . '</label>';
383
+
384
+ echo $html;
385
+ }
386
+
387
+
388
+ /**
389
+ * Multicheck Callback
390
+ *
391
+ * Renders multiple checkboxes.
392
+ *
393
+ * @since 1.0
394
+ * @param array $args Arguments passed by the setting
395
+ * @global $wpstg_options Array of all the WPSTG Options
396
+ * @return void
397
+ */
398
+ function wpstg_multicheck_callback( $args ) {
399
+ global $wpstg_options;
400
+
401
+ if ( ! empty( $args['options'] ) ) {
402
+ foreach( $args['options'] as $key => $option ):
403
+ if( isset( $wpstg_options[$args['id']][$key] ) ) { $enabled = $option; } else { $enabled = NULL; }
404
+ echo '<input name="wpstg_settings[' . $args['id'] . '][' . $key . ']" id="wpstg_settings[' . $args['id'] . '][' . $key . ']" type="checkbox" value="' . $option . '" ' . checked($option, $enabled, false) . '/>&nbsp;';
405
+ echo '<label for="wpstg_settings[' . $args['id'] . '][' . $key . ']">' . $option . '</label><br/>';
406
+ endforeach;
407
+ echo '<p class="description wpstg_hidden">' . $args['desc'] . '</p>';
408
+ }
409
+ }
410
+
411
+ /**
412
+ * Radio Callback
413
+ *
414
+ * Renders radio boxes.
415
+ *
416
+ * @since 1.3.3
417
+ * @param array $args Arguments passed by the setting
418
+ * @global $wpstg_options Array of all the WPSTG Options
419
+ * @return void
420
+ */
421
+ function wpstg_radio_callback( $args ) {
422
+ global $wpstg_options;
423
+
424
+ foreach ( $args['options'] as $key => $option ) :
425
+ $checked = false;
426
+
427
+ if ( isset( $wpstg_options[ $args['id'] ] ) && $wpstg_options[ $args['id'] ] == $key )
428
+ $checked = true;
429
+ elseif( isset( $args['std'] ) && $args['std'] == $key && ! isset( $wpstg_options[ $args['id'] ] ) )
430
+ $checked = true;
431
+
432
+ echo '<input name="wpstg_settings[' . $args['id'] . ']"" id="wpstg_settings[' . $args['id'] . '][' . $key . ']" type="radio" value="' . $key . '" ' . checked(true, $checked, false) . '/>&nbsp;';
433
+ echo '<label for="wpstg_settings[' . $args['id'] . '][' . $key . ']">' . $option . '</label><br/>';
434
+ endforeach;
435
+
436
+ echo '<p class="description wpstg_hidden">' . $args['desc'] . '</p>';
437
+ }
438
+
439
+ /**
440
+ * Text Callback
441
+ *
442
+ * Renders text fields.
443
+ *
444
+ * @since 1.0
445
+ * @param array $args Arguments passed by the setting
446
+ * @global $wpstg_options Array of all the WPSTG Options
447
+ * @return void
448
+ */
449
+ function wpstg_text_callback( $args ) {
450
+ global $wpstg_options;
451
+
452
+ if ( isset( $wpstg_options[ $args['id'] ] ) )
453
+ $value = $wpstg_options[ $args['id'] ];
454
+ else
455
+ $value = isset( $args['std'] ) ? $args['std'] : '';
456
+
457
+ $size = ( isset( $args['size'] ) && ! is_null( $args['size'] ) ) ? $args['size'] : 'regular';
458
+ $html = '<input type="text" class="' . $size . '-text" id="wpstg_settings[' . $args['id'] . ']" name="wpstg_settings[' . $args['id'] . ']" value="' . esc_attr( stripslashes( $value ) ) . '"/>';
459
+ $html .= '<label class="wpstg_hidden" class="wpstg_hidden" for="wpstg_settings[' . $args['id'] . ']"> ' . $args['desc'] . '</label>';
460
+
461
+ echo $html;
462
+ }
463
+
464
+ /**
465
+ * Number Callback
466
+ *
467
+ * Renders number fields.
468
+ *
469
+ * @since 1.9
470
+ * @param array $args Arguments passed by the setting
471
+ * @global $wpstg_options Array of all the WPSTG Options
472
+ * @return void
473
+ */
474
+ function wpstg_number_callback( $args ) {
475
+ global $wpstg_options;
476
+
477
+ if ( isset( $wpstg_options[ $args['id'] ] ) )
478
+ $value = $wpstg_options[ $args['id'] ];
479
+ else
480
+ $value = isset( $args['std'] ) ? $args['std'] : '';
481
+
482
+ $max = isset( $args['max'] ) ? $args['max'] : 999999;
483
+ $min = isset( $args['min'] ) ? $args['min'] : 0;
484
+ $step = isset( $args['step'] ) ? $args['step'] : 1;
485
+
486
+ $size = ( isset( $args['size'] ) && ! is_null( $args['size'] ) ) ? $args['size'] : 'regular';
487
+ $html = '<input type="number" step="' . esc_attr( $step ) . '" max="' . esc_attr( $max ) . '" min="' . esc_attr( $min ) . '" class="' . $size . '-text" id="wpstg_settings[' . $args['id'] . ']" name="wpstg_settings[' . $args['id'] . ']" value="' . esc_attr( stripslashes( $value ) ) . '"/>';
488
+ $html .= '<label class="wpstg_hidden" for="wpstg_settings[' . $args['id'] . ']"> ' . $args['desc'] . '</label>';
489
+
490
+ echo $html;
491
+ }
492
+
493
+ /**
494
+ * Textarea Callback
495
+ *
496
+ * Renders textarea fields.
497
+ *
498
+ * @since 1.0
499
+ * @param array $args Arguments passed by the setting
500
+ * @global $wpstg_options Array of all the WPSTG Options
501
+ * @return void
502
+ */
503
+ function wpstg_textarea_callback( $args ) {
504
+ global $wpstg_options;
505
+
506
+ if ( isset( $wpstg_options[ $args['id'] ] ) )
507
+ $value = $wpstg_options[ $args['id'] ];
508
+ else
509
+ $value = isset( $args['std'] ) ? $args['std'] : '';
510
+
511
+ $size = ( isset( $args['size'] ) && ! is_null( $args['size'] ) ) ? $args['size'] : '40';
512
+ $html = '<textarea class="large-text wpstg-textarea" cols="50" rows="' . $size . '" id="wpstg_settings[' . $args['id'] . ']" name="wpstg_settings[' . $args['id'] . ']">' . esc_textarea( stripslashes( $value ) ) . '</textarea>';
513
+ $html .= '<label class="wpstg_hidden" for="wpstg_settings[' . $args['id'] . ']"> ' . $args['desc'] . '</label>';
514
+
515
+ echo $html;
516
+ }
517
+
518
+ /**
519
+ * Password Callback
520
+ *
521
+ * Renders password fields.
522
+ *
523
+ * @since 1.3
524
+ * @param array $args Arguments passed by the setting
525
+ * @global $wpstg_options Array of all the WPSTG Options
526
+ * @return void
527
+ */
528
+ function wpstg_password_callback( $args ) {
529
+ global $wpstg_options;
530
+
531
+ if ( isset( $wpstg_options[ $args['id'] ] ) )
532
+ $value = $wpstg_options[ $args['id'] ];
533
+ else
534
+ $value = isset( $args['std'] ) ? $args['std'] : '';
535
+
536
+ $size = ( isset( $args['size'] ) && ! is_null( $args['size'] ) ) ? $args['size'] : 'regular';
537
+ $html = '<input type="password" class="' . $size . '-text" id="wpstg_settings[' . $args['id'] . ']" name="wpstg_settings[' . $args['id'] . ']" value="' . esc_attr( $value ) . '"/>';
538
+ $html .= '<label for="wpstg_settings[' . $args['id'] . ']"> ' . $args['desc'] . '</label>';
539
+
540
+ echo $html;
541
+ }
542
+
543
+ /**
544
+ * Missing Callback
545
+ *
546
+ * If a function is missing for settings callbacks alert the user.
547
+ *
548
+ * @since 1.3.1
549
+ * @param array $args Arguments passed by the setting
550
+ * @return void
551
+ */
552
+ function wpstg_missing_callback($args) {
553
+ printf( __( 'The callback function used for the <strong>%s</strong> setting is missing.', 'wpstg' ), $args['id'] );
554
+ }
555
+
556
+ /**
557
+ * Select Callback
558
+ *
559
+ * Renders select fields.
560
+ *
561
+ * @since 1.0
562
+ * @param array $args Arguments passed by the setting
563
+ * @global $wpstg_options Array of all the WPSTG Options
564
+ * @return void
565
+ */
566
+ function wpstg_select_callback($args) {
567
+ global $wpstg_options;
568
+
569
+ if ( isset( $wpstg_options[ $args['id'] ] ) )
570
+ $value = $wpstg_options[ $args['id'] ];
571
+ else
572
+ $value = isset( $args['std'] ) ? $args['std'] : '';
573
+
574
+ $html = '<select id="wpstg_settings[' . $args['id'] . ']" name="wpstg_settings[' . $args['id'] . ']"/>';
575
+
576
+ foreach ( $args['options'] as $option => $name ) :
577
+ $selected = selected( $option, $value, false );
578
+ $html .= '<option value="' . $option . '" ' . $selected . '>' . $name . '</option>';
579
+ endforeach;
580
+
581
+ $html .= '</select>';
582
+ $html .= '<label class="wpstg_hidden" for="wpstg_settings[' . $args['id'] . ']"> ' . $args['desc'] . '</label>';
583
+
584
+ echo $html;
585
+ }
586
+
587
+ /**
588
+ * Color select Callback
589
+ *
590
+ * Renders color select fields.
591
+ *
592
+ * @since 2.1.2
593
+ * @param array $args Arguments passed by the setting
594
+ * @global $wpstg_options Array of all the WPSTG Options
595
+ * @return void
596
+ */
597
+
598
+
599
+ function wpstg_color_select_callback( $args ) {
600
+ global $wpstg_options;
601
+
602
+ if ( isset( $wpstg_options[ $args['id'] ] ) )
603
+ $value = $wpstg_options[ $args['id'] ];
604
+ else
605
+ $value = isset( $args['std'] ) ? $args['std'] : '';
606
+
607
+ $html = '<strong>#:</strong><input type="text" style="max-width:80px;border:1px solid #' . esc_attr( stripslashes( $value ) ) . ';border-right:20px solid #' . esc_attr( stripslashes( $value ) ) . ';" id="wpstg_settings[' . $args['id'] . ']" class="medium-text ' . $args['id'] . '" name="wpstg_settings[' . $args['id'] . ']" value="' . esc_attr( stripslashes( $value ) ) . '"/>';
608
+
609
+ $html .= '</select>';
610
+ $html .= '<label class="wpstg_hidden" for="wpstg_settings[' . $args['id'] . ']"> ' . $args['desc'] . '</label>';
611
+
612
+ echo $html;
613
+ }
614
+
615
+ /**
616
+ * Rich Editor Callback
617
+ *
618
+ * Renders rich editor fields.
619
+ *
620
+ * @since 1.0
621
+ * @param array $args Arguments passed by the setting
622
+ * @global $wpstg_options Array of all the WPSTG Options
623
+ * @global $wp_version WordPress Version
624
+ */
625
+ function wpstg_rich_editor_callback( $args ) {
626
+ global $wpstg_options, $wp_version;
627
+ if ( isset( $wpstg_options[ $args['id'] ] ) )
628
+ $value = $wpstg_options[ $args['id'] ];
629
+ else
630
+ $value = isset( $args['std'] ) ? $args['std'] : '';
631
+
632
+ if ( $wp_version >= 3.3 && function_exists( 'wp_editor' ) ) {
633
+ ob_start();
634
+ wp_editor( stripslashes( $value ), 'wpstg_settings_' . $args['id'], array( 'textarea_name' => 'wpstg_settings[' . $args['id'] . ']', 'textarea_rows' => $args['textarea_rows'] ) );
635
+ $html = ob_get_clean();
636
+ } else {
637
+ $html = '<textarea class="large-text wpstg-richeditor" rows="10" id="wpstg_settings[' . $args['id'] . ']" name="wpstg_settings[' . $args['id'] . ']">' . esc_textarea( stripslashes( $value ) ) . '</textarea>';
638
+ }
639
+
640
+ $html .= '<br/><label class="wpstg_hidden" for="wpstg_settings[' . $args['id'] . ']"> ' . $args['desc'] . '</label>';
641
+
642
+ echo $html;
643
+ }
644
+
645
+ /**
646
+ * Upload Callback
647
+ *
648
+ * Renders upload fields.
649
+ *
650
+ * @since 1.0
651
+ * @param array $args Arguments passed by the setting
652
+ * @global $wpstg_options Array of all the WPSTG Options
653
+ * @return void
654
+ */
655
+ function wpstg_upload_callback( $args ) {
656
+ global $wpstg_options;
657
+
658
+ if ( isset( $wpstg_options[ $args['id'] ] ) )
659
+ $value = $wpstg_options[$args['id']];
660
+ else
661
+ $value = isset($args['std']) ? $args['std'] : '';
662
+
663
+ $size = ( isset( $args['size'] ) && ! is_null( $args['size'] ) ) ? $args['size'] : 'regular';
664
+ $html = '<input type="text" class="' . $size . '-text wpstg_upload_field" id="wpstg_settings[' . $args['id'] . ']" name="wpstg_settings[' . $args['id'] . ']" value="' . esc_attr( stripslashes( $value ) ) . '"/>';
665
+ $html .= '<span>&nbsp;<input type="button" class="wpstg_settings_upload_button button-secondary" value="' . __( 'Upload File', 'wpstg' ) . '"/></span>';
666
+ $html .= '<label class="wpstg_hidden" for="wpstg_settings[' . $args['id'] . ']"> ' . $args['desc'] . '</label>';
667
+
668
+ echo $html;
669
+ }
670
+
671
+
672
+ /**
673
+ * Registers the license field callback for Software Licensing
674
+ *
675
+ * @since 1.5
676
+ * @param array $args Arguments passed by the setting
677
+ * @global $wpstg_options Array of all the WPSTG Options
678
+ * @return void
679
+ */
680
+ if ( ! function_exists( 'wpstg_license_key_callback' ) ) {
681
+ function wpstg_license_key_callback( $args ) {
682
+ global $wpstg_options;
683
+
684
+ if ( isset( $wpstg_options[ $args['id'] ] ) )
685
+ $value = $wpstg_options[ $args['id'] ];
686
+ else
687
+ $value = isset( $args['std'] ) ? $args['std'] : '';
688
+
689
+ $size = ( isset( $args['size'] ) && ! is_null( $args['size'] ) ) ? $args['size'] : 'regular';
690
+ $html = '<input type="text" class="' . $size . '-text" id="wpstg_settings[' . $args['id'] . ']" name="wpstg_settings[' . $args['id'] . ']" value="' . esc_attr( $value ) . '"/>';
691
+
692
+ if ( 'valid' == get_option( $args['options']['is_valid_license_option'] ) ) {
693
+ $html .= '<input type="submit" class="button-secondary" name="' . $args['id'] . '_deactivate" value="' . __( 'Deactivate License', 'wpstg' ) . '"/>';
694
+ $html .= '<span style="font-weight:bold;color:green;"> License key activated! </span> <p style="color:green;font-size:13px;"> You´ll get updates for this Add-On automatically!</p>';
695
+ } else {
696
+ $html .= '<span style="color:red;"> License key not activated!</span style=""><p style="font-size:13px;font-weight:bold;">You´ll get no important security and feature updates for this Add-On!</p>';
697
+ }
698
+ $html .= '<label for="wpstg_settings[' . $args['id'] . ']"> ' . $args['desc'] . '</label>';
699
+
700
+ wp_nonce_field( $args['id'] . '-nonce', $args['id'] . '-nonce' );
701
+
702
+ echo $html;
703
+ }
704
+ }
705
+
706
+
707
+
708
+ /**
709
+ * Registers the Add-Ons field callback for WP-Staging Add-Ons
710
+ *
711
+ * @since 2.0.5
712
+ * @param array $args Arguments passed by the setting
713
+ * @return html
714
+ */
715
+ function wpstg_addons_callback( $args ) {
716
+ $html = wpstg_add_ons_page();
717
+ echo $html;
718
+ }
719
+
720
+ /**
721
+ * Registers the image upload field
722
+ *
723
+ * @since 1.0
724
+ * @param array $args Arguments passed by the setting
725
+ * @global $wpstg_options Array of all the WPSTG Options
726
+ * @return void
727
+ */
728
+
729
+ function wpstg_upload_image_callback( $args ) {
730
+ global $wpstg_options;
731
+
732
+ if ( isset( $wpstg_options[ $args['id'] ] ) )
733
+ $value = $wpstg_options[ $args['id'] ];
734
+ else
735
+ $value = isset( $args['std'] ) ? $args['std'] : '';
736
+
737
+ $size = ( isset( $args['size'] ) && ! is_null( $args['size'] ) ) ? $args['size'] : 'regular';
738
+ $html = '<input type="text" class="' . $size . '-text ' . $args['id'] . '" id="wpstg_settings[' . $args['id'] . ']" name="wpstg_settings[' . $args['id'] . ']" value="' . esc_attr( $value ) . '"/>';
739
+
740
+ $html .= '<input type="submit" class="button-secondary wpstg_upload_image" name="' . $args['id'] . '_upload" value="' . __( 'Select Image', 'wpstg' ) . '"/>';
741
+
742
+ $html .= '<label class="wpstg_hidden" for="wpstg_settings[' . $args['id'] . ']"> ' . $args['desc'] . '</label>';
743
+
744
+ echo $html;
745
+ }
746
+
747
+
748
+
749
+
750
+
751
+ /**
752
+ * Hook Callback
753
+ *
754
+ * Adds a do_action() hook in place of the field
755
+ *
756
+ * @since 1.0.8.2
757
+ * @param array $args Arguments passed by the setting
758
+ * @return void
759
+ */
760
+ function wpstg_hook_callback( $args ) {
761
+ do_action( 'wpstg_' . $args['id'] );
762
+ }
763
+
764
+ /**
765
+ * Set manage_options as the cap required to save WPSTG settings pages
766
+ *
767
+ * @since 1.9
768
+ * @return string capability required
769
+ */
770
+ function wpstg_set_settings_cap() {
771
+ return 'manage_options';
772
+ }
773
+ add_filter( 'option_page_capability_wpstg_settings', 'wpstg_set_settings_cap' );
774
+
775
+
776
+
777
+
778
+ /* Permission check if logfile is writable
779
+ *
780
+ * @since 2.0.6
781
+ * @return string
782
+ */
783
+
784
+ function wpstg_log_permissions(){
785
+ global $wpstg_options;
786
+ if (!WPSTG()->logger->checkDir() ){
787
+ return '<br><strong style="color:red;">' . __('Log file directory not writable! Set FTP permission to 755 or 777 for /wp-content/plugins/wp-staging/logs/', 'wpstg') . '</strong> <br> Read here more about <a href="http://codex.wordpress.org/Changing_File_Permissions" target="_blank">file permissions</a> ';
788
+ }
789
+ }
790
+
791
+ /**
792
+ * Render template for installing a must-use plugin (MU Plugin)
793
+ */
794
+ function wpstg_install_muplugin_callback(){
795
+ $plugin_compatibility_checked = isset( $GLOBALS['wpstg_optimizer'] ) ? ' checked="checked"' : '';
796
+ ob_start();
797
+ ?>
798
+ <div class="option-section plugin-compatibility-section">
799
+ <label for="plugin-compatibility" class="plugin-compatibility bubble" style="float:left;">
800
+ <input id="plugin-compatibility" type="checkbox" name="plugin_compatibility"<?php echo $plugin_compatibility_checked; ?> autocomplete="off"<?php echo $plugin_compatibility_checked; ?> />
801
+ <?php __( 'Improve performance and reliability by not loading the following plugins for migration requests', 'wpstg' ); ?>
802
+ </label>
803
+ <a href="#" class="general-helper plugin-compatibility-helper js-action-link"></a>
804
+
805
+ <div class="plugin-compatibility-message helper-message bottom">
806
+ <?php echo __( 'Select the plugins you wish to disable during clone process', 'wpstg' ); ?></br>
807
+ </div>
808
+
809
+ <div class="indent-wrap expandable-content plugin-compatibility-wrap select-wrap" style="display:none;">
810
+ <select autocomplete="off" class="multiselect" id="selected-plugins" name="selected_plugins[]" multiple="multiple" style="min-height:400px;">
811
+ <?php
812
+ global $wpstg_options;
813
+ $blacklist = array_flip( (array) $wpstg_options['blacklist_plugins'] );
814
+ foreach ( get_plugins() as $key => $plugin ) {
815
+ if ( 0 === strpos( $key, 'wp-staging' ) ) {
816
+ continue;
817
+ }
818
+ $selected = ( isset( $blacklist[ $key ] ) ) ? ' selected' : '';
819
+ printf( '<option value="%s"%s>%s</option>', $key, $selected, $plugin['Name'] );
820
+ }
821
+ ?>
822
+ </select>
823
+ <br>
824
+ <a class="multiselect-select-all js-action-link" href="#"><?php _e( 'Select All', 'wpstg' ); ?></a>
825
+ <span class="select-deselect-divider">/</span>
826
+ <a class="multiselect-deselect-all js-action-link" href="#"><?php _e( 'Deselect All', 'wpstg' ); ?></a>
827
+ <span class="select-deselect-divider">/</span>
828
+ <a class="multiselect-invert-selection js-action-link" href="#"><?php _e( 'Invert Selection', 'wpstg' ); ?></a>
829
+ <p>
830
+ <span class="button plugin-compatibility-save"><?php _e( 'Save Changes', 'wpstg' ); ?></span>
831
+ <span class="plugin-compatibility-success-msg"><?php _ex( 'Saved', 'The settings were saved successfully', 'wpstg' ); ?></span>
832
+ </p>
833
+ </div>
834
+ </div>
835
+ <?php
836
+ $html = ob_get_contents();
837
+ ob_end_clean();
838
+ echo $html;
839
+ }
includes/admin/tools.php ADDED
@@ -0,0 +1,460 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Tools
4
+ *
5
+ * These are functions used for displaying WPSTG tools such as the import/export system.
6
+ *
7
+ * @package WPSTG
8
+ * @subpackage Admin/Tools
9
+ * @copyright Copyright (c) 2015, Pippin Williamson, René Hermenau
10
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
11
+ */
12
+
13
+ // Exit if accessed directly
14
+ if ( ! defined( 'ABSPATH' ) ) exit;
15
+
16
+ /**
17
+ * Tools
18
+ *
19
+ * Shows the tools panel which contains WPSTG-specific tools including the
20
+ * built-in import/export system.
21
+ *
22
+ * @since 0.9.0
23
+ * @author Daniel J Griffiths
24
+ * @return void
25
+ */
26
+ function wpstg_tools_page() {
27
+ $active_tab = isset( $_GET['tab'] ) ? $_GET['tab'] : 'import_export';
28
+ ?>
29
+ <div class="wrap">
30
+ <h2 class="nav-tab-wrapper">
31
+ <?php
32
+ foreach( wpstg_get_tools_tabs() as $tab_id => $tab_name ) {
33
+
34
+ $tab_url = add_query_arg( array(
35
+ 'tab' => $tab_id
36
+ ) );
37
+
38
+ $tab_url = remove_query_arg( array(
39
+ 'wpstg-message'
40
+ ), $tab_url );
41
+
42
+ $active = $active_tab == $tab_id ? ' nav-tab-active' : '';
43
+ echo '<a href="' . esc_url( $tab_url ) . '" title="' . esc_attr( $tab_name ) . '" class="nav-tab' . $active . '">' . esc_html( $tab_name ) . '</a>';
44
+
45
+ }
46
+ ?>
47
+ </h2>
48
+ <div class="metabox-holder">
49
+ <?php
50
+ do_action( 'wpstg_tools_tab_' . $active_tab );
51
+ ?>
52
+ </div><!-- .metabox-holder -->
53
+ </div><!-- .wrap -->
54
+ <?php
55
+ }
56
+
57
+
58
+ /**
59
+ * Retrieve tools tabs
60
+ *
61
+ * @since 2.1.6
62
+ * @return array
63
+ */
64
+ function wpstg_get_tools_tabs() {
65
+
66
+ $tabs = array();
67
+ $tabs['import_export'] = __( 'Import/Export', 'wpstg' );
68
+ $tabs['system_info'] = __( 'System Info', 'wpstg' );
69
+
70
+ return apply_filters( 'wpstg_tools_tabs', $tabs );
71
+ }
72
+
73
+
74
+
75
+ /**
76
+ * Display the tools import/export tab
77
+ *
78
+ * @since 2.1.6
79
+ * @return void
80
+ */
81
+ function wpstg_tools_import_export_display() {
82
+
83
+ if( ! current_user_can( 'update_plugins' ) ) {
84
+ return;
85
+ }
86
+
87
+ do_action( 'wpstg_tools_import_export_before' );
88
+ ?>
89
+ <div class="postbox">
90
+ <h3><span><?php _e( 'Export Settings', 'wpstg' ); ?></span></h3>
91
+ <div class="inside">
92
+ <p><?php _e( 'Export the WP-Staging settings for this site as a .json file. This allows you to easily import the configuration into another site.', 'wpstg' ); ?></p>
93
+
94
+ <form method="post" action="<?php echo admin_url( 'admin.php?page=wpstg-tools&amp;tab=import_export' ); ?>">
95
+ <p><input type="hidden" name="wpstg-action" value="export_settings" /></p>
96
+ <p>
97
+ <?php wp_nonce_field( 'wpstg_export_nonce', 'wpstg_export_nonce' ); ?>
98
+ <?php submit_button( __( 'Export', 'wpstg' ), 'primary', 'submit', false ); ?>
99
+ </p>
100
+ </form>
101
+ </div><!-- .inside -->
102
+ </div><!-- .postbox -->
103
+
104
+ <div class="postbox">
105
+ <h3><span><?php _e( 'Import Settings', 'wpstg' ); ?></span></h3>
106
+ <div class="inside">
107
+ <p><?php _e( 'Import the WP-Staging settings from a .json file. This file can be obtained by exporting the settings on another site using the form above.', 'wpstg' ); ?></p>
108
+ <form method="post" enctype="multipart/form-data" action="<?php echo admin_url( 'admin.php?page=wpstg-tools&amp;tab=import_export' ); ?>">
109
+ <p>
110
+ <input type="file" name="import_file"/>
111
+ </p>
112
+ <p>
113
+ <input type="hidden" name="wpstg-action" value="import_settings" />
114
+ <?php wp_nonce_field( 'wpstg_import_nonce', 'wpstg_import_nonce' ); ?>
115
+ <?php submit_button( __( 'Import', 'wpstg' ), 'secondary', 'submit', false ); ?>
116
+ </p>
117
+ </form>
118
+ </div><!-- .inside -->
119
+ </div><!-- .postbox -->
120
+ <?php
121
+ do_action( 'wpstg_tools_import_export_after' );
122
+ }
123
+ add_action( 'wpstg_tools_tab_import_export', 'wpstg_tools_import_export_display' );
124
+
125
+ /* check if function is disabled or not
126
+ *
127
+ * @returns bool
128
+ * @since 2.1.6
129
+ */
130
+ function wpstg_is_func_disabled( $function ) {
131
+ $disabled = explode( ',', ini_get( 'disable_functions' ) );
132
+ return in_array( $function, $disabled );
133
+ }
134
+
135
+ /**
136
+ * Process a settings export that generates a .json file of the WP-Staging settings
137
+ *
138
+ * @since 2.1.6
139
+ * @return void
140
+ */
141
+ function wpstg_tools_import_export_process_export() {
142
+ if( empty( $_POST['wpstg_export_nonce'] ) )
143
+ return;
144
+
145
+ if( ! wp_verify_nonce( $_POST['wpstg_export_nonce'], 'wpstg_export_nonce' ) )
146
+ return;
147
+
148
+ if( ! current_user_can( 'manage_options' ) )
149
+ return;
150
+
151
+ $settings = array();
152
+ $settings = get_option( 'wpstg_settings' );
153
+
154
+ ignore_user_abort( true );
155
+
156
+ if ( ! wpstg_is_func_disabled( 'set_time_limit' ) && ! ini_get( 'safe_mode' ) )
157
+ set_time_limit( 0 );
158
+
159
+ nocache_headers();
160
+ header( 'Content-Type: application/json; charset=utf-8' );
161
+ header( 'Content-Disposition: attachment; filename=' . apply_filters( 'wpstg_settings_export_filename', 'wpstg-settings-export-' . date( 'm-d-Y' ) ) . '.json' );
162
+ header( "Expires: 0" );
163
+
164
+ echo json_encode( $settings );
165
+ exit;
166
+ }
167
+ add_action( 'wpstg_export_settings', 'wpstg_tools_import_export_process_export' );
168
+
169
+ /**
170
+ * Get File Extension
171
+ *
172
+ * Returns the file extension of a filename.
173
+ *
174
+ * @since 1.0
175
+ * @param unknown $str File name
176
+ * @return mixed File extension
177
+ */
178
+ function wpstg_get_file_extension( $str ) {
179
+ $parts = explode( '.', $str );
180
+ return end( $parts );
181
+ }
182
+
183
+ /* Convert an object to an associative array.
184
+ * Can handle multidimensional arrays
185
+ *
186
+ * @returns array
187
+ * @since 2.1.6
188
+ */
189
+ function wpstg_object_to_array( $data ) {
190
+ if ( is_array( $data ) || is_object( $data ) ) {
191
+ $result = array();
192
+ foreach ( $data as $key => $value ) {
193
+ $result[ $key ] = wpstg_object_to_array( $value );
194
+ }
195
+ return $result;
196
+ }
197
+ return $data;
198
+ }
199
+
200
+ /**
201
+ * Process a settings import from a json file
202
+ *
203
+ * @since 2.1.6
204
+ * @return void
205
+ */
206
+ function wpstg_tools_import_export_process_import() {
207
+ if( empty( $_POST['wpstg_import_nonce'] ) )
208
+ return;
209
+
210
+ if( ! wp_verify_nonce( $_POST['wpstg_import_nonce'], 'wpstg_import_nonce' ) )
211
+ return;
212
+
213
+ if( ! current_user_can( 'update_plugins' ) )
214
+ return;
215
+
216
+ if( wpstg_get_file_extension( $_FILES['import_file']['name'] ) != 'json' ) {
217
+ wp_die( __( 'Please upload a valid .json file', 'wpstg' ) );
218
+ }
219
+
220
+ $import_file = $_FILES['import_file']['tmp_name'];
221
+
222
+ if( empty( $import_file ) ) {
223
+ wp_die( __( 'Please upload a file to import', 'wpstg' ) );
224
+ }
225
+
226
+ // Retrieve the settings from the file and convert the json object to an array
227
+ $settings = wpstg_object_to_array( json_decode( file_get_contents( $import_file ) ) );
228
+
229
+ update_option( 'wpstg_settings', $settings );
230
+
231
+ wp_safe_redirect( admin_url( 'admin.php?page=wpstg-tools&amp;wpstg-message=settings-imported' ) ); exit;
232
+
233
+ }
234
+ add_action( 'wpstg_import_settings', 'wpstg_tools_import_export_process_import' );
235
+
236
+
237
+ /**
238
+ * Display the system info tab
239
+ *
240
+ * @since 2.1.6
241
+ * @return void
242
+ * @change 2.3.1
243
+ */
244
+ function wpstg_tools_sysinfo_display() {
245
+
246
+ if( ! current_user_can( 'update_plugins' ) ) {
247
+ return;
248
+ }
249
+
250
+ ?>
251
+ <form action="<?php echo esc_url( admin_url( 'admin.php?page=wpstg-tools&amp;tab=system_info' ) ); ?>" method="post" dir="ltr">
252
+ <textarea class="wpstg-sysinfo" readonly="readonly" onclick="this.focus(); this.select()" id="system-info-textarea" name="wpstg-sysinfo" title="To copy the system info, click below then press Ctrl + C (PC) or Cmd + C (Mac)."><?php echo wpstg_tools_sysinfo_get(); ?></textarea>
253
+ <p class="submit">
254
+ <input type="hidden" name="wpstg-action" value="download_sysinfo" />
255
+ <?php submit_button( 'Download System Info File', 'primary', 'wpstg-download-sysinfo', false ); ?>
256
+ </p>
257
+ </form>
258
+ <?php
259
+ }
260
+ add_action( 'wpstg_tools_tab_system_info', 'wpstg_tools_sysinfo_display' );
261
+
262
+
263
+ /**
264
+ * Get system info
265
+ *
266
+ * @since 2.1.6
267
+ * @access public
268
+ * @global object $wpdb Used to query the database using the WordPress Database API
269
+ * @global array $wpstg_options Array of all WPSTG options
270
+ * @return string $return A string containing the info to output
271
+ */
272
+ function wpstg_tools_sysinfo_get() {
273
+ global $wpdb, $wpstg_options;
274
+
275
+ if( !class_exists( 'Browser' ) )
276
+ require_once WPSTG_PLUGIN_DIR . 'includes/libraries/browser.php';
277
+
278
+ $browser = new Browser();
279
+
280
+ // Get theme info
281
+ if( get_bloginfo( 'version' ) < '3.4' ) {
282
+ $theme_data = get_theme_data( get_stylesheet_directory() . '/style.css' );
283
+ $theme = $theme_data['Name'] . ' ' . $theme_data['Version'];
284
+ } else {
285
+ $theme_data = wp_get_theme();
286
+ $theme = $theme_data->Name . ' ' . $theme_data->Version;
287
+ }
288
+
289
+
290
+ $return = '### Begin System Info ###' . "\n\n";
291
+
292
+ // Start with the basics...
293
+ $return .= '-- Site Info' . "\n\n";
294
+ $return .= 'Site URL: ' . site_url() . "\n";
295
+ $return .= 'Home URL: ' . home_url() . "\n";
296
+ $return .= 'Multisite: ' . ( is_multisite() ? 'Yes' : 'No' ) . "\n";
297
+
298
+ $return = apply_filters( 'wpstg_sysinfo_after_site_info', $return );
299
+
300
+
301
+ // The local users' browser information, handled by the Browser class
302
+ $return .= "\n" . '-- User Browser' . "\n\n";
303
+ $return .= $browser;
304
+
305
+ $return = apply_filters( 'wpstg_sysinfo_after_user_browser', $return );
306
+
307
+ // WordPress configuration
308
+ $return .= "\n" . '-- WordPress Configuration' . "\n\n";
309
+ $return .= 'Version: ' . get_bloginfo( 'version' ) . "\n";
310
+ $return .= 'Language: ' . ( defined( 'WPLANG' ) && WPLANG ? WPLANG : 'en_US' ) . "\n";
311
+ $return .= 'Permalink Structure: ' . ( get_option( 'permalink_structure' ) ? get_option( 'permalink_structure' ) : 'Default' ) . "\n";
312
+ $return .= 'Active Theme: ' . $theme . "\n";
313
+ $return .= 'Show On Front: ' . get_option( 'show_on_front' ) . "\n";
314
+
315
+ // Only show page specs if frontpage is set to 'page'
316
+ if( get_option( 'show_on_front' ) == 'page' ) {
317
+ $front_page_id = get_option( 'page_on_front' );
318
+ $blog_page_id = get_option( 'page_for_posts' );
319
+
320
+ $return .= 'Page On Front: ' . ( $front_page_id != 0 ? get_the_title( $front_page_id ) . ' (#' . $front_page_id . ')' : 'Unset' ) . "\n";
321
+ $return .= 'Page For Posts: ' . ( $blog_page_id != 0 ? get_the_title( $blog_page_id ) . ' (#' . $blog_page_id . ')' : 'Unset' ) . "\n";
322
+ }
323
+
324
+ // Make sure wp_remote_post() is working
325
+ $request['cmd'] = '_notify-validate';
326
+
327
+ $params = array(
328
+ 'sslverify' => false,
329
+ 'timeout' => 60,
330
+ 'user-agent' => 'WPSTG/' . WPSTG_VERSION,
331
+ 'body' => $request
332
+ );
333
+
334
+ $response = wp_remote_post( 'https://www.paypal.com/cgi-bin/webscr', $params );
335
+
336
+ if( !is_wp_error( $response ) && $response['response']['code'] >= 200 && $response['response']['code'] < 300 ) {
337
+ $WP_REMOTE_POST = 'wp_remote_post() works';
338
+ } else {
339
+ $WP_REMOTE_POST = 'wp_remote_post() does not work';
340
+ }
341
+
342
+ $return .= 'Remote Post: ' . $WP_REMOTE_POST . "\n";
343
+ $return .= 'Table Prefix: ' . 'Length: ' . strlen( $wpdb->prefix ) . ' Status: ' . ( strlen( $wpdb->prefix ) > 16 ? 'ERROR: Too long' : 'Acceptable' ) . "\n";
344
+ $return .= 'WP_DEBUG: ' . ( defined( 'WP_DEBUG' ) ? WP_DEBUG ? 'Enabled' : 'Disabled' : 'Not set' ) . "\n";
345
+ $return .= 'Memory Limit: ' . WP_MEMORY_LIMIT . "\n";
346
+ $return .= 'Registered Post Stati: ' . implode( ', ', get_post_stati() ) . "\n";
347
+
348
+ $return = apply_filters( 'wpstg_sysinfo_after_wordpress_config', $return );
349
+
350
+ // WPSTG configuration
351
+ $return .= "\n" . '-- WPSTG Configuration' . "\n\n";
352
+ $return .= 'Version: ' . WPSTG_VERSION . "\n";
353
+ $return .= 'Upgraded From: ' . get_option( 'wpstg_version_upgraded_from', 'None' ) . "\n";
354
+
355
+ $return = apply_filters( 'wpstg_sysinfo_after_wpstg_config', $return );
356
+
357
+
358
+ // WordPress active plugins
359
+ $return .= "\n" . '-- WordPress Active Plugins' . "\n\n";
360
+
361
+ $plugins = get_plugins();
362
+ $active_plugins = get_option( 'active_plugins', array() );
363
+
364
+ foreach( $plugins as $plugin_path => $plugin ) {
365
+ if( !in_array( $plugin_path, $active_plugins ) )
366
+ continue;
367
+
368
+ $return .= $plugin['Name'] . ': ' . $plugin['Version'] . "\n";
369
+ }
370
+
371
+ $return = apply_filters( 'wpstg_sysinfo_after_wordpress_plugins', $return );
372
+
373
+ // WordPress inactive plugins
374
+ $return .= "\n" . '-- WordPress Inactive Plugins' . "\n\n";
375
+
376
+ foreach( $plugins as $plugin_path => $plugin ) {
377
+ if( in_array( $plugin_path, $active_plugins ) )
378
+ continue;
379
+
380
+ $return .= $plugin['Name'] . ': ' . $plugin['Version'] . "\n";
381
+ }
382
+
383
+ $return = apply_filters( 'wpstg_sysinfo_after_wordpress_plugins_inactive', $return );
384
+
385
+ if( is_multisite() ) {
386
+ // WordPress Multisite active plugins
387
+ $return .= "\n" . '-- Network Active Plugins' . "\n\n";
388
+
389
+ $plugins = wp_get_active_network_plugins();
390
+ $active_plugins = get_site_option( 'active_sitewide_plugins', array() );
391
+
392
+ foreach( $plugins as $plugin_path ) {
393
+ $plugin_base = plugin_basename( $plugin_path );
394
+
395
+ if( !array_key_exists( $plugin_base, $active_plugins ) )
396
+ continue;
397
+
398
+ $plugin = get_plugin_data( $plugin_path );
399
+ $return .= $plugin['Name'] . ': ' . $plugin['Version'] . "\n";
400
+ }
401
+
402
+ $return = apply_filters( 'wpstg_sysinfo_after_wordpress_ms_plugins', $return );
403
+ }
404
+
405
+ // Server configuration (really just versioning)
406
+ $return .= "\n" . '-- Webserver Configuration' . "\n\n";
407
+ $return .= 'PHP Version: ' . PHP_VERSION . "\n";
408
+ $return .= 'MySQL Version: ' . $wpdb->db_version() . "\n";
409
+ $return .= 'Webserver Info: ' . $_SERVER['SERVER_SOFTWARE'] . "\n";
410
+
411
+ $return = apply_filters( 'wpstg_sysinfo_after_webserver_config', $return );
412
+
413
+ // PHP configs... now we're getting to the important stuff
414
+ $return .= "\n" . '-- PHP Configuration' . "\n\n";
415
+ $return .= 'Safe Mode: ' . ( ini_get( 'safe_mode' ) ? 'Enabled' : 'Disabled' . "\n" );
416
+ $return .= 'Memory Limit: ' . ini_get( 'memory_limit' ) . "\n";
417
+ $return .= 'Upload Max Size: ' . ini_get( 'upload_max_filesize' ) . "\n";
418
+ $return .= 'Post Max Size: ' . ini_get( 'post_max_size' ) . "\n";
419
+ $return .= 'Upload Max Filesize: ' . ini_get( 'upload_max_filesize' ) . "\n";
420
+ $return .= 'Time Limit: ' . ini_get( 'max_execution_time' ) . "\n";
421
+ $return .= 'Max Input Vars: ' . ini_get( 'max_input_vars' ) . "\n";
422
+ $return .= 'Display Errors: ' . ( ini_get( 'display_errors' ) ? 'On (' . ini_get( 'display_errors' ) . ')' : 'N/A' ) . "\n";
423
+
424
+ $return = apply_filters( 'wpstg_sysinfo_after_php_config', $return );
425
+
426
+ // PHP extensions and such
427
+ $return .= "\n" . '-- PHP Extensions' . "\n\n";
428
+ $return .= 'cURL: ' . ( function_exists( 'curl_init' ) ? 'Supported' : 'Not Supported' ) . "\n";
429
+ $return .= 'fsockopen: ' . ( function_exists( 'fsockopen' ) ? 'Supported' : 'Not Supported' ) . "\n";
430
+ $return .= 'SOAP Client: ' . ( class_exists( 'SoapClient' ) ? 'Installed' : 'Not Installed' ) . "\n";
431
+ $return .= 'Suhosin: ' . ( extension_loaded( 'suhosin' ) ? 'Installed' : 'Not Installed' ) . "\n";
432
+
433
+ $return = apply_filters( 'wpstg_sysinfo_after_php_ext', $return );
434
+
435
+ $return .= "\n" . '### End System Info ###';
436
+
437
+ return $return;
438
+ }
439
+
440
+
441
+ /**
442
+ * Generates a System Info download file
443
+ *
444
+ * @since 2.0
445
+ * @return void
446
+ */
447
+ function wpstg_tools_sysinfo_download() {
448
+
449
+ if( ! current_user_can( 'update_plugins' ) )
450
+ return;
451
+
452
+ nocache_headers();
453
+
454
+ header( 'Content-Type: text/plain' );
455
+ header( 'Content-Disposition: attachment; filename="wpstg-system-info.txt"' );
456
+
457
+ echo wp_strip_all_tags( $_POST['wpstg-sysinfo'] );
458
+ wp_die();
459
+ }
460
+ add_action( 'wpstg_download_sysinfo', 'wpstg_tools_sysinfo_download' );
includes/admin/upload-functions.php ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Upload Functions
5
+ *
6
+ * @package WPSTG
7
+ * @subpackage Admin/Upload
8
+ * @copyright Copyright (c) 2015, Pippin Williamson, René Hermenau
9
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
10
+ * @since 0.9.0
11
+ */
12
+
13
+
14
+ /**
15
+ * Retrieve the absolute path to the file upload directory without the trailing slash
16
+ *
17
+ * @since 0.9.0
18
+ * @return string $path Absolute path to the WPSTG upload directory
19
+ */
20
+ function wpstg_get_upload_dir() {
21
+ $wp_upload_dir = wp_upload_dir();
22
+ wp_mkdir_p( $wp_upload_dir['basedir'] . '/wp-staging' );
23
+ $path = $wp_upload_dir['basedir'] . '/wp-staging';
24
+
25
+ return apply_filters( 'wpstg_get_upload_dir', $path );
26
+ }
27
+
28
+ /**
29
+ * Checks if the .htaccess file exists in wp-content/uploads/wp-staging
30
+ *
31
+ * @since 0.9.0
32
+ * @return bool
33
+ */
34
+ function wpstg_htaccess_exists() {
35
+ $upload_path = wpstg_get_upload_dir();
36
+
37
+ return file_exists( $upload_path . '/.htaccess' );
38
+ }
39
+
40
+ /**
41
+ * Checks if the remaining_files.json file exists in wp-content/uploads/wp-staging
42
+ *
43
+ * @since 0.9.0
44
+ * @return bool
45
+ */
46
+ function wpstg_remainingjson_exists() {
47
+ $upload_path = wpstg_get_upload_dir();
48
+
49
+ return file_exists( $upload_path . '/remaining_files.json' );
50
+ }
51
+
52
+ /**
53
+ * Checks if the clone_details.json file exists in wp-content/uploads/wp-staging
54
+ *
55
+ * @since 0.9.0
56
+ * @return bool
57
+ */
58
+ function wpstg_clonedetailsjson_exists() {
59
+ $upload_path = wpstg_get_upload_dir();
60
+
61
+ return file_exists( $upload_path . '/clone_details.json' );
62
+ }
63
+
64
+ /**
65
+ * Retrieve the .htaccess rules to wp-content/uploads/wp-staging/
66
+ *
67
+ * @since 0.9.0
68
+ *
69
+ * @param bool $method
70
+ * @return mixed|void The htaccess rules
71
+ */
72
+ function wpstg_get_htaccess_rules() {
73
+ // Prevent directory browsing and direct access to all files
74
+ $rules = "<Files \"*\">\n";
75
+ $rules .= "<IfModule mod_access.c>\n";
76
+ $rules .= "Deny from all\n";
77
+ $rules .= "</IfModule>\n";
78
+ $rules .= "<IfModule !mod_access_compat>\n";
79
+ $rules .= "<IfModule mod_authz_host.c>\n";
80
+ $rules .= "Deny from all\n";
81
+ $rules .= "</IfModule>\n";
82
+ $rules .= "</IfModule>\n";
83
+ $rules .= "<IfModule mod_access_compat>\n";
84
+ $rules .= "Deny from all\n";
85
+ $rules .= "</IfModule>\n";
86
+ $rules .= "</Files>\n";
87
+ $rules = apply_filters('wpstg_protected_directory_htaccess_rules', $rules);
88
+ return $rules;
89
+ }
90
+
91
+ /**
92
+ * Creates blank index.php and .htaccess files
93
+ * Creates /wp-content/uploads/wp-staging subfolder
94
+ *
95
+ * This function runs approximately once per month in order to ensure all folders
96
+ * have their necessary protection files
97
+ *
98
+ * @since 1.1.0
99
+ *
100
+ * @param bool $force
101
+ */
102
+
103
+ function wpstg_create_protection_files( $force = false ) {
104
+ if ( false === get_transient( 'wpstg_check_protection_files' ) || $force ) {
105
+
106
+ $upload_path = wpstg_get_upload_dir();
107
+
108
+ // Make sure the /wpstg folder is created
109
+ wp_mkdir_p( $upload_path );
110
+
111
+ // Top level .htaccess file
112
+ $rules = wpstg_get_htaccess_rules();
113
+ if ( wpstg_htaccess_exists() ) {
114
+ $contents = @file_get_contents( $upload_path . '/.htaccess' );
115
+ if ( $contents !== $rules || ! $contents ) {
116
+ // Update the .htaccess rules if they don't match
117
+ @file_put_contents( $upload_path . '/.htaccess', $rules );
118
+ }
119
+ } elseif( wp_is_writable( $upload_path ) ) {
120
+ // Create the file if it doesn't exist
121
+ @file_put_contents( $upload_path . '/.htaccess', $rules );
122
+ }
123
+
124
+ // Top level blank index.php
125
+ if ( ! file_exists( $upload_path . '/index.php' ) && wp_is_writable( $upload_path ) ) {
126
+ @file_put_contents( $upload_path . '/index.php', '<?php' . PHP_EOL . '// Silence is golden.' );
127
+ }
128
+
129
+ // Now place index.php files in all sub folders
130
+ $folders = wpstg_scan_upload_folders( $upload_path );
131
+ foreach ( $folders as $folder ) {
132
+ // Create index.php, if it doesn't exist
133
+ if ( ! file_exists( $folder . 'index.php' ) && wp_is_writable( $folder ) ) {
134
+ @file_put_contents( $folder . 'index.php', '<?php' . PHP_EOL . '// Silence is golden.' );
135
+ }
136
+ }
137
+ // Check for the files once per day
138
+ set_transient( 'wpstg_check_protection_files', true, 3600 * 24 );
139
+ }
140
+ }
141
+ add_action( 'admin_init', 'wpstg_create_protection_files' );
142
+
143
+ /**
144
+ * Scans all folders inside of /uploads/wpstg
145
+ *
146
+ * @since 0.9.0
147
+ * @return array $return List of files inside directory
148
+ */
149
+ function wpstg_scan_upload_folders( $path = '', $return = array() ) {
150
+ $path = $path == ''? dirname( __FILE__ ) : $path;
151
+ $lists = @scandir( $path );
152
+
153
+ if ( ! empty( $lists ) ) {
154
+ foreach ( $lists as $f ) {
155
+ if ( is_dir( $path . DIRECTORY_SEPARATOR . $f ) && $f != "." && $f != ".." ) {
156
+ if ( ! in_array( $path . DIRECTORY_SEPARATOR . $f, $return ) )
157
+ $return[] = trailingslashit( $path . DIRECTORY_SEPARATOR . $f );
158
+
159
+ wpstg_scan_upload_folders( $path . DIRECTORY_SEPARATOR . $f, $return);
160
+ }
161
+ }
162
+ }
163
+
164
+ return $return;
165
+ }
166
+
167
+ // For installs on pre WP 3.6
168
+ if( ! function_exists( 'wp_is_writable' ) ) {
169
+
170
+ /**
171
+ * Determine if a directory is writable.
172
+ *
173
+ * This function is used to work around certain ACL issues
174
+ * in PHP primarily affecting Windows Servers.
175
+ *
176
+ * @see win_is_writable()
177
+ *
178
+ * @since 3.6.0
179
+ *
180
+ * @param string $path
181
+ * @return bool
182
+ */
183
+ function wp_is_writable( $path ) {
184
+ if ( 'WIN' === strtoupper( substr( PHP_OS, 0, 3 ) ) )
185
+ return win_is_writable( $path );
186
+ else
187
+ return @is_writable( $path );
188
+ }
189
+ }
190
+
includes/admin/welcome.php ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Weclome Page Class
4
+ *
5
+ * @package WPSTG
6
+ * @subpackage Admin/Welcome
7
+ * @copyright Copyright (c) 2015, René Hermenau
8
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9
+ * @since 1.0
10
+ */
11
+
12
+ // Exit if accessed directly
13
+ if ( ! defined( 'ABSPATH' ) ) exit;
14
+
15
+ /**
16
+ * WPSTG_Welcome Class
17
+ *
18
+ * A general class for About and Credits page.
19
+ *
20
+ * @since 1.0
21
+ */
22
+ class WPSTG_Welcome {
23
+
24
+ /**
25
+ * @var string The capability users should have to view the page
26
+ */
27
+ public $minimum_capability = 'manage_options';
28
+
29
+ /**
30
+ * Get things started
31
+ *
32
+ * @since 1.0.1
33
+ */
34
+ public function __construct() {
35
+ add_action( 'admin_init', array( $this, 'welcome' ) );
36
+ }
37
+
38
+
39
+
40
+ /**
41
+ * Sends user to the Settings page on first activation of WPSTG as well as each
42
+ * time WPSTG is upgraded to a new version
43
+ *
44
+ * @access public
45
+ * @since 0.9.0
46
+ * @global $wpstg_options Array of all the WPSTG Options
47
+ * @return void
48
+ */
49
+ public function welcome() {
50
+ global $wpstg_options;
51
+
52
+ // Bail if no activation redirect
53
+ if ( ! get_transient( '_wpstg_activation_redirect' ) )
54
+ return;
55
+
56
+ // Delete the redirect transient
57
+ delete_transient( '_wpstg_activation_redirect' );
58
+
59
+ // Bail if activating from network, or bulk
60
+ if ( is_network_admin() || isset( $_GET['activate-multi'] ) )
61
+ return;
62
+
63
+ $upgrade = get_option( 'wpstg_version_upgraded_from' );
64
+
65
+ //@since 0.9.0
66
+ if( ! $upgrade ) { // First time install
67
+ wp_safe_redirect( admin_url( 'admin.php?page=wpstg_clone' ) ); exit;
68
+ } else { // Update
69
+ wp_safe_redirect( admin_url( 'admin.php?page=wpstg_clone' ) ); exit;
70
+ }
71
+ }
72
+ }
73
+ new WPSTG_Welcome();
includes/class-wpstg-html-elements.php ADDED
@@ -0,0 +1,219 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * HTML elements
4
+ *
5
+ * A helper class for outputting common HTML elements, such as product drop downs
6
+ *
7
+ * @package WPSTG
8
+ * @subpackage Classes/HTML
9
+ * @copyright Copyright (c) 2015, René Hermenau, Pippin Williamsen
10
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
11
+ * @since 0.9.0
12
+ */
13
+
14
+ // Exit if accessed directly
15
+ if ( ! defined( 'ABSPATH' ) ) exit;
16
+
17
+ /**
18
+ * WPSTG_HTML_Elements Class
19
+ *
20
+ * @since 1.0
21
+ */
22
+ class WPSTG_HTML_Elements {
23
+
24
+
25
+ /**
26
+ * Renders an HTML Dropdown
27
+ *
28
+ * @since 0.9.0
29
+ *
30
+ * @param array $args
31
+ *
32
+ * @return string
33
+ */
34
+ public function select( $args = array() ) {
35
+ $defaults = array(
36
+ 'options' => array(),
37
+ 'name' => null,
38
+ 'class' => '',
39
+ 'id' => '',
40
+ 'selected' => 0,
41
+ 'chosen' => false,
42
+ 'multiple' => false,
43
+ 'show_option_all' => _x( 'All', 'all dropdown items', 'wpstg' ),
44
+ 'show_option_none' => _x( 'None', 'no dropdown items', 'wpstg' )
45
+ );
46
+
47
+ $args = wp_parse_args( $args, $defaults );
48
+
49
+
50
+ if( $args['multiple'] ) {
51
+ $multiple = ' MULTIPLE';
52
+ } else {
53
+ $multiple = '';
54
+ }
55
+
56
+ if( $args['chosen'] ) {
57
+ $args['class'] .= ' wpstg-select-chosen';
58
+ }
59
+
60
+ $output = '<select name="' . esc_attr( $args[ 'name' ] ) . '" id="' . esc_attr( sanitize_key( str_replace( '-', '_', $args[ 'id' ] ) ) ) . '" class="wpstg-select ' . esc_attr( $args[ 'class'] ) . '"' . $multiple . '>';
61
+
62
+ if ( ! empty( $args[ 'options' ] ) ) {
63
+ if ( $args[ 'show_option_all' ] ) {
64
+ if( $args['multiple'] ) {
65
+ $selected = selected( true, in_array( 0, $args['selected'] ), false );
66
+ } else {
67
+ $selected = selected( $args['selected'], 0, false );
68
+ }
69
+ $output .= '<option value="all"' . $selected . '>' . esc_html( $args[ 'show_option_all' ] ) . '</option>';
70
+ }
71
+
72
+ if ( $args[ 'show_option_none' ] ) {
73
+ if( $args['multiple'] ) {
74
+ $selected = selected( true, in_array( -1, $args['selected'] ), false );
75
+ } else {
76
+ $selected = selected( $args['selected'], -1, false );
77
+ }
78
+ $output .= '<option value="-1"' . $selected . '>' . esc_html( $args[ 'show_option_none' ] ) . '</option>';
79
+ }
80
+
81
+ foreach( $args[ 'options' ] as $key => $option ) {
82
+
83
+ if( $args['multiple'] && is_array( $args['selected'] ) ) {
84
+ $selected = selected( true, in_array( $key, $args['selected'] ), false );
85
+ } else {
86
+ $selected = selected( $args['selected'], $key, false );
87
+ }
88
+
89
+ $output .= '<option value="' . esc_attr( $key ) . '"' . $selected . '>' . esc_html( $option ) . '</option>';
90
+ }
91
+ }
92
+
93
+ $output .= '</select>';
94
+
95
+ return $output;
96
+ }
97
+
98
+ /**
99
+ * Renders an HTML Checkbox
100
+ *
101
+ * @since 1.9
102
+ *
103
+ * @param array $args
104
+ *
105
+ * @return string
106
+ */
107
+ public function checkbox( $args = array() ) {
108
+ $defaults = array(
109
+ 'name' => null,
110
+ 'current' => null,
111
+ 'class' => 'wpstg-checkbox'
112
+ );
113
+
114
+ $args = wp_parse_args( $args, $defaults );
115
+
116
+ $output = '<input type="checkbox" name="' . esc_attr( $args[ 'name' ] ) . '" id="' . esc_attr( $args[ 'name' ] ) . '" class="' . $args[ 'class' ] . ' ' . esc_attr( $args[ 'name'] ) . '" ' . checked( 1, $args[ 'current' ], false ) . ' />';
117
+
118
+ return $output;
119
+ }
120
+
121
+ /**
122
+ * Renders an HTML Text field
123
+ *
124
+ * @since 1.5.2
125
+ *
126
+ * @param string $name Name attribute of the text field
127
+ * @param string $value The value to prepopulate the field with
128
+ * @param string $label
129
+ * @param string $desc
130
+ * @return string Text field
131
+ */
132
+ public function text( $args = array() ) {
133
+ // Backwards compatabliity
134
+ if ( func_num_args() > 1 ) {
135
+ $args = func_get_args();
136
+
137
+ $name = $args[0];
138
+ $value = isset( $args[1] ) ? $args[1] : '';
139
+ $label = isset( $args[2] ) ? $args[2] : '';
140
+ $desc = isset( $args[3] ) ? $args[3] : '';
141
+ }
142
+
143
+ $defaults = array(
144
+ 'name' => isset( $name ) ? $name : 'text',
145
+ 'value' => isset( $value ) ? $value : null,
146
+ 'label' => isset( $label ) ? $label : null,
147
+ 'desc' => isset( $desc ) ? $desc : null,
148
+ 'placeholder' => '',
149
+ 'class' => 'regular-text',
150
+ 'disabled' => false,
151
+ 'autocomplete' => ''
152
+ );
153
+
154
+ $args = wp_parse_args( $args, $defaults );
155
+
156
+ $disabled = '';
157
+ if( $args['disabled'] ) {
158
+ $disabled = ' disabled="disabled"';
159
+ }
160
+
161
+ $output = '<span id="wpstg-' . sanitize_key( $args[ 'name' ] ) . '-wrap">';
162
+
163
+ $output .= '<label class="wpstg-label" for="wpstg-' . sanitize_key( $args[ 'name' ] ) . '">' . esc_html( $args[ 'label' ] ) . '</label>';
164
+
165
+ if ( ! empty( $args[ 'desc' ] ) ) {
166
+ $output .= '<span class="wpstg-description">' . esc_html( $args[ 'desc' ] ) . '</span>';
167
+ }
168
+
169
+ $output .= '<input type="text" name="' . esc_attr( $args[ 'name' ] ) . '" id="' . esc_attr( $args[ 'name' ] ) . '" autocomplete="' . esc_attr( $args[ 'autocomplete' ] ) . '" value="' . esc_attr( $args[ 'value' ] ) . '" placeholder="' . esc_attr( $args[ 'placeholder' ] ) . '" class="' . $args[ 'class' ] . '"' . $disabled . '/>';
170
+
171
+ $output .= '</span>';
172
+
173
+ return $output;
174
+ }
175
+
176
+ /**
177
+ * Renders an HTML textarea
178
+ *
179
+ * @since 0.9.0
180
+ *
181
+ * @param string $name Name attribute of the textarea
182
+ * @param string $value The value to prepopulate the field with
183
+ * @param string $label
184
+ * @param string $desc
185
+ * @return string textarea
186
+ */
187
+ public function textarea( $args = array() ) {
188
+ $defaults = array(
189
+ 'name' => 'textarea',
190
+ 'value' => null,
191
+ 'label' => null,
192
+ 'desc' => null,
193
+ 'class' => 'large-text',
194
+ 'disabled' => false
195
+ );
196
+
197
+ $args = wp_parse_args( $args, $defaults );
198
+
199
+ $disabled = '';
200
+ if( $args['disabled'] ) {
201
+ $disabled = ' disabled="disabled"';
202
+ }
203
+
204
+ $output = '<span id="wpstg-' . sanitize_key( $args[ 'name' ] ) . '-wrap">';
205
+
206
+ $output .= '<label class="wpstg-label" for="wpstg-' . sanitize_key( $args[ 'name' ] ) . '">' . esc_html( $args[ 'label' ] ) . '</label>';
207
+
208
+ $output .= '<textarea name="' . esc_attr( $args[ 'name' ] ) . '" id="' . esc_attr( $args[ 'name' ] ) . '" class="' . $args[ 'class' ] . '"' . $disabled . '>' . esc_attr( $args[ 'value' ] ) . '</textarea>';
209
+
210
+ if ( ! empty( $args[ 'desc' ] ) ) {
211
+ $output .= '<span class="wpstg-description">' . esc_html( $args[ 'desc' ] ) . '</span>';
212
+ }
213
+
214
+ $output .= '</span>';
215
+
216
+ return $output;
217
+ }
218
+
219
+ }
includes/class-wpstg-license-handler.php ADDED
@@ -0,0 +1,368 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * License handler for WP-Staging Add-Ons
4
+ *
5
+ * This class should simplify the process of adding license information
6
+ * to new WPSTG extensions.
7
+ *
8
+ * @version 1.1
9
+ */
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
12
+
13
+ if ( ! class_exists( 'WPSTG_License' ) ) :
14
+
15
+ /**
16
+ * WPSTG_License Class
17
+ */
18
+ class WPSTG_License {
19
+ private $file;
20
+ private $license;
21
+ private $item_name;
22
+ private $item_shortname;
23
+ private $version;
24
+ private $author;
25
+ private $api_url = 'https://www.wp-staging.com/edd-sl-api/';
26
+
27
+ /**
28
+ * Class constructor
29
+ *
30
+ * @global array $wpstg_options
31
+ * @param string $_file
32
+ * @param string $_item_name
33
+ * @param string $_version
34
+ * @param string $_author
35
+ * @param string $_optname
36
+ * @param string $_api_url
37
+ */
38
+ function __construct( $_file, $_item_name, $_version, $_author, $_optname = null, $_api_url = null ) {
39
+ global $wpstg_options;
40
+
41
+ $this->file = $_file;
42
+ $this->item_name = $_item_name;
43
+ $this->item_shortname = 'wpstg_' . preg_replace( '/[^a-zA-Z0-9_\s]/', '', str_replace( ' ', '_', strtolower( $this->item_name ) ) );
44
+ $this->version = $_version;
45
+ $this->license = isset( $wpstg_options[ $this->item_shortname . '_license_key' ] ) ? trim( $wpstg_options[ $this->item_shortname . '_license_key' ] ) : '';
46
+ $this->author = $_author;
47
+ $this->api_url = is_null( $_api_url ) ? $this->api_url : $_api_url;
48
+
49
+ /**
50
+ * Allows for backwards compatibility with old license options,
51
+ * i.e. if the plugins had license key fields previously, the license
52
+ * handler will automatically pick these up and use those in lieu of the
53
+ * user having to reactive their license.
54
+ */
55
+ if ( ! empty( $_optname ) ) {
56
+ $opt = wpstg_get_option( $_optname, false );
57
+
58
+ if( isset( $opt ) && empty( $this->license ) ) {
59
+ $this->license = trim( $opt );
60
+ }
61
+ }
62
+
63
+ // Setup hooks
64
+ $this->includes();
65
+ $this->hooks();
66
+ //$this->auto_updater();
67
+ }
68
+
69
+ /**
70
+ * Include the updater class
71
+ *
72
+ * @access private
73
+ * @return void
74
+ */
75
+ private function includes() {
76
+ if ( ! class_exists( 'WPSTG_SL_Plugin_Updater' ) ) require_once 'WPSTG_SL_Plugin_Updater.php';
77
+ }
78
+
79
+ /**
80
+ * Setup hooks
81
+ *
82
+ * @access private
83
+ * @return void
84
+ */
85
+ private function hooks() {
86
+
87
+ // Register settings
88
+ add_filter( 'wpstg_settings_licenses', array( $this, 'settings' ), 1 );
89
+
90
+ // Activate license key on settings save
91
+ add_action( 'admin_init', array( $this, 'activate_license' ) );
92
+
93
+ // Deactivate license key
94
+ add_action( 'admin_init', array( $this, 'deactivate_license' ) );
95
+
96
+ // Updater
97
+ add_action( 'admin_init', array( $this, 'auto_updater' ), 0 );
98
+
99
+ add_action( 'admin_notices', array( $this, 'notices' ) );
100
+ }
101
+
102
+ /**
103
+ * Auto updater
104
+ *
105
+ * @access private
106
+ * @global array $wpstg_options
107
+ * @return void
108
+ */
109
+ public function auto_updater() {
110
+
111
+ //if ( 'valid' !== get_option( $this->item_shortname . '_license_active' ) )
112
+ //return;
113
+ //rhe
114
+
115
+ $test = array(
116
+ 'version' => $this->version,
117
+ 'license' => $this->license,
118
+ 'item_name' => $this->item_name,
119
+ 'author' => $this->author
120
+ );
121
+
122
+
123
+ // Setup the updater
124
+ $wpstg_updater = new WPSTG_SL_Plugin_Updater(
125
+ $this->api_url,
126
+ $this->file,
127
+ array(
128
+ 'version' => $this->version,
129
+ 'license' => $this->license,
130
+ 'item_name' => $this->item_name,
131
+ 'author' => $this->author
132
+ )
133
+ );
134
+ }
135
+
136
+
137
+ /**
138
+ * Add license field to settings
139
+ *
140
+ * @access public
141
+ * @param array $settings
142
+ * @return array
143
+ */
144
+ public function settings( $settings ) {
145
+ $wpstg_license_settings = array(
146
+ array(
147
+ 'id' => $this->item_shortname . '_license_key',
148
+ 'name' => sprintf( __( '%1$s License Key', 'wpstg' ), $this->item_name ),
149
+ 'desc' => '',
150
+ 'type' => 'license_key',
151
+ 'options' => array( 'is_valid_license_option' => $this->item_shortname . '_license_active' ),
152
+ 'size' => 'regular'
153
+ )
154
+ );
155
+
156
+ return array_merge( $settings, $wpstg_license_settings );
157
+ }
158
+
159
+
160
+ /**
161
+ * Activate the license key
162
+ *
163
+ * @access public
164
+ * @return void
165
+ */
166
+ public function activate_license() {
167
+ if ( ! isset( $_POST['wpstg_settings'] ) ) {
168
+ return;
169
+ }
170
+
171
+ if ( ! isset( $_POST['wpstg_settings'][ $this->item_shortname . '_license_key'] ) ) {
172
+ return;
173
+ }
174
+
175
+ foreach( $_POST as $key => $value ) {
176
+ if( false !== strpos( $key, 'license_key_deactivate' ) ) {
177
+ // Don't activate a key when deactivating a different key
178
+ return;
179
+ }
180
+ }
181
+
182
+ if( ! wp_verify_nonce( $_REQUEST[ $this->item_shortname . '_license_key-nonce'], $this->item_shortname . '_license_key-nonce' ) ) {
183
+
184
+ wp_die( __( 'Nonce verification failed', 'wpstg' ), __( 'Error', 'wpstg' ), array( 'response' => 403 ) );
185
+
186
+ }
187
+
188
+ if( ! current_user_can( 'manage_options' ) ) {
189
+ return;
190
+ }
191
+
192
+ if ( 'valid' === get_option( $this->item_shortname . '_license_active' ) ) {
193
+ return;
194
+ }
195
+
196
+ $license = sanitize_text_field( $_POST['wpstg_settings'][ $this->item_shortname . '_license_key'] );
197
+
198
+ if( empty( $license ) ) {
199
+ return;
200
+ }
201
+
202
+ // Data to send to the API
203
+ $api_params = array(
204
+ 'edd_action' => 'activate_license',
205
+ 'license' => $license,
206
+ 'item_name' => urlencode( $this->item_name ),
207
+ 'url' => home_url()
208
+ );
209
+
210
+ // Call the API
211
+ $response = wp_remote_post(
212
+ $this->api_url,
213
+ array(
214
+ 'timeout' => 15,
215
+ 'sslverify' => false,
216
+ 'body' => $api_params
217
+ )
218
+ );
219
+
220
+ // Make sure there are no errors
221
+ if ( is_wp_error( $response ) ) {
222
+ return;
223
+ }
224
+
225
+ // Tell WordPress to look for updates
226
+ set_site_transient( 'update_plugins', null );
227
+
228
+ // Decode license data
229
+ $license_data = json_decode( wp_remote_retrieve_body( $response ) );
230
+
231
+ update_option( $this->item_shortname . '_license_active', $license_data->license );
232
+
233
+ if( ! (bool) $license_data->success ) {
234
+ set_transient( 'wpstg_license_error', $license_data, 1000 );
235
+ } else {
236
+ delete_transient( 'wpstg_license_error' );
237
+ }
238
+ }
239
+
240
+
241
+ /**
242
+ * Deactivate the license key
243
+ *
244
+ * @access public
245
+ * @return void
246
+ */
247
+ public function deactivate_license() {
248
+
249
+ if ( ! isset( $_POST['wpstg_settings'] ) )
250
+ return;
251
+
252
+ if ( ! isset( $_POST['wpstg_settings'][ $this->item_shortname . '_license_key'] ) )
253
+ return;
254
+
255
+ if( ! wp_verify_nonce( $_REQUEST[ $this->item_shortname . '_license_key-nonce'], $this->item_shortname . '_license_key-nonce' ) ) {
256
+
257
+ wp_die( __( 'Nonce verification failed', 'wpstg' ), __( 'Error', 'wpstg' ), array( 'response' => 403 ) );
258
+
259
+ }
260
+
261
+ if( ! current_user_can( 'manage_options' ) ) {
262
+ return;
263
+ }
264
+
265
+ // Run on deactivate button press
266
+ if ( isset( $_POST[ $this->item_shortname . '_license_key_deactivate'] ) ) {
267
+
268
+ // Data to send to the API
269
+ $api_params = array(
270
+ 'edd_action' => 'deactivate_license',
271
+ 'license' => $this->license,
272
+ 'item_name' => urlencode( $this->item_name ),
273
+ 'url' => home_url()
274
+ );
275
+
276
+ // Call the API
277
+ $response = wp_remote_post(
278
+ $this->api_url,
279
+ array(
280
+ 'timeout' => 15,
281
+ 'sslverify' => false,
282
+ 'body' => $api_params
283
+ )
284
+ );
285
+
286
+ // Make sure there are no errors
287
+ if ( is_wp_error( $response ) ) {
288
+ return;
289
+ }
290
+
291
+ // Decode the license data
292
+ $license_data = json_decode( wp_remote_retrieve_body( $response ) );
293
+
294
+ delete_option( $this->item_shortname . '_license_active' );
295
+
296
+ if( ! (bool) $license_data->success ) {
297
+ set_transient( 'wpstg_license_error', $license_data, 1000 );
298
+ } else {
299
+ delete_transient( 'wpstg_license_error' );
300
+ }
301
+ }
302
+ }
303
+
304
+
305
+ /**
306
+ * Admin notices for errors
307
+ *
308
+ * @access public
309
+ * @return void
310
+ */
311
+ public function notices() {
312
+
313
+ if( ! isset( $_GET['page'] ) || 'wpstg-settings' !== $_GET['page'] ) {
314
+ return;
315
+ }
316
+
317
+ if( ! isset( $_GET['tab'] ) || 'licenses' !== $_GET['tab'] ) {
318
+ return;
319
+ }
320
+
321
+ $license_error = get_transient( 'wpstg_license_error' );
322
+
323
+ if( false === $license_error ) {
324
+ return;
325
+ }
326
+
327
+ if( ! empty( $license_error->error ) ) {
328
+
329
+ switch( $license_error->error ) {
330
+
331
+ case 'item_name_mismatch' :
332
+
333
+ $message = __( 'This license does not belong to the product you have entered it for.', 'wpstg' );
334
+ break;
335
+
336
+ case 'no_activations_left' :
337
+
338
+ $message = __( 'This license does not have any activations left', 'wpstg' );
339
+ break;
340
+
341
+ case 'expired' :
342
+
343
+ $message = __( 'This license key is expired. Please renew it.', 'wpstg' );
344
+ break;
345
+
346
+ default :
347
+
348
+ $message = sprintf( __( 'There was a problem activating your license key, please try again or contact support. Error code: %s', 'wpstg' ), $license_error->error );
349
+ break;
350
+
351
+ }
352
+
353
+ }
354
+
355
+ if( ! empty( $message ) ) {
356
+
357
+ echo '<div class="error">';
358
+ echo '<p>' . $message . '</p>';
359
+ echo '</div>';
360
+
361
+ }
362
+
363
+ delete_transient( 'wpstg_license_error' );
364
+
365
+ }
366
+ }
367
+
368
+ endif; // end class_exists check
includes/debug/browsers/WPChromePHP.class.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class WPChromePHP implements iMashDebug {
3
+
4
+ public function __construct() {
5
+ include 'api/chromephp/ChromePhp.php';
6
+ $this->api = ChromePhp::getInstance();
7
+ }
8
+
9
+
10
+ public function log( $var, $label = null ) {
11
+ $this->api->log( $label, $var );
12
+ }
13
+
14
+ public function info( $var, $label = null ) {
15
+ $this->api->info( $label, $var );
16
+ }
17
+
18
+ public function warn( $var, $label = null ) {
19
+ $this->api->warn( $label, $var );
20
+ }
21
+
22
+ public function error( $var, $label = null ) {
23
+ $this->api->error( $label, $var );
24
+ }
25
+ }
includes/debug/browsers/WPFirePHP.class.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class WPFirePHP implements iMashDebug {
3
+
4
+ private $api;
5
+
6
+ public function __construct() {
7
+ include 'api/firephp/lib/FirePHPCore/FirePHP.class.php';
8
+ $this->api = FirePHP::getInstance( true );
9
+ }
10
+
11
+ public function log( $var, $label = null ) {
12
+ $this->api->log( $var, $label );
13
+ }
14
+
15
+ public function info( $var, $label = null ) {
16
+ $this->api->info( $var, $label );
17
+ }
18
+
19
+ public function warn( $var, $label = null ) {
20
+ $this->api->warn( $var, $label );
21
+ }
22
+
23
+ public function error( $var, $label = null ) {
24
+ $this->api->error( $var, $label );
25
+ }
26
+
27
+
28
+ }
includes/debug/browsers/api/chromephp/ChromePhp.php ADDED
@@ -0,0 +1,440 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright 2012 Craig Campbell
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ /**
19
+ * Server Side Chrome PHP debugger class
20
+ *
21
+ * @package ChromePhp
22
+ * @author Craig Campbell <iamcraigcampbell@gmail.com>
23
+ */
24
+ class ChromePhp
25
+ {
26
+ /**
27
+ * @var string
28
+ */
29
+ const VERSION = '3.0';
30
+
31
+ /**
32
+ * @var string
33
+ */
34
+ const HEADER_NAME = 'X-ChromePhp-Data';
35
+
36
+ /**
37
+ * @var string
38
+ */
39
+ const BACKTRACE_LEVEL = 'backtrace_level';
40
+
41
+ /**
42
+ * @var string
43
+ */
44
+ const LOG = 'log';
45
+
46
+ /**
47
+ * @var string
48
+ */
49
+ const WARN = 'warn';
50
+
51
+ /**
52
+ * @var string
53
+ */
54
+ const ERROR = 'error';
55
+
56
+ /**
57
+ * @var string
58
+ */
59
+ const GROUP = 'group';
60
+
61
+ /**
62
+ * @var string
63
+ */
64
+ const INFO = 'info';
65
+
66
+ /**
67
+ * @var string
68
+ */
69
+ const GROUP_END = 'groupEnd';
70
+
71
+ /**
72
+ * @var string
73
+ */
74
+ const GROUP_COLLAPSED = 'groupCollapsed';
75
+
76
+ /**
77
+ * @var string
78
+ */
79
+ protected $_php_version;
80
+
81
+ /**
82
+ * @var int
83
+ */
84
+ protected $_timestamp;
85
+
86
+ /**
87
+ * @var array
88
+ */
89
+ protected $_json = array(
90
+ 'version' => self::VERSION,
91
+ 'columns' => array('label', 'log', 'backtrace', 'type'),
92
+ 'rows' => array()
93
+ );
94
+
95
+ /**
96
+ * @var array
97
+ */
98
+ protected $_backtraces = array();
99
+
100
+ /**
101
+ * @var bool
102
+ */
103
+ protected $_error_triggered = false;
104
+
105
+ /**
106
+ * @var array
107
+ */
108
+ protected $_settings = array(
109
+ self::BACKTRACE_LEVEL => 1
110
+ );
111
+
112
+ /**
113
+ * @var ChromePhp
114
+ */
115
+ protected static $_instance;
116
+
117
+ /**
118
+ * Prevent recursion when working with objects referring to each other
119
+ *
120
+ * @var array
121
+ */
122
+ protected $_processed = array();
123
+
124
+ /**
125
+ * constructor
126
+ */
127
+ private function __construct()
128
+ {
129
+ $this->_php_version = phpversion();
130
+ $this->_timestamp = $this->_php_version >= 5.1 ? $_SERVER['REQUEST_TIME'] : time();
131
+ $this->_json['request_uri'] = $_SERVER['REQUEST_URI'];
132
+ }
133
+
134
+ /**
135
+ * gets instance of this class
136
+ *
137
+ * @return ChromePhp
138
+ */
139
+ public static function getInstance()
140
+ {
141
+ if (self::$_instance === null) {
142
+ self::$_instance = new ChromePhp();
143
+ }
144
+ return self::$_instance;
145
+ }
146
+
147
+ /**
148
+ * logs a variable to the console
149
+ *
150
+ * @param string label
151
+ * @param mixed value
152
+ * @param string severity ChromePhp::LOG || ChromePhp::WARN || ChromePhp::ERROR
153
+ * @return void
154
+ */
155
+ public static function log()
156
+ {
157
+ $args = func_get_args();
158
+ $severity = count($args) == 3 ? array_pop($args) : '';
159
+
160
+ // save precious bytes
161
+ if ($severity == self::LOG) {
162
+ $severity = '';
163
+ }
164
+
165
+ return self::_log($args + array('type' => $severity));
166
+ }
167
+
168
+ /**
169
+ * logs a warning to the console
170
+ *
171
+ * @param string label
172
+ * @param mixed value
173
+ * @return void
174
+ */
175
+ public static function warn()
176
+ {
177
+ return self::_log(func_get_args() + array('type' => self::WARN));
178
+ }
179
+
180
+ /**
181
+ * logs an error to the console
182
+ *
183
+ * @param string label
184
+ * @param mixed value
185
+ * @return void
186
+ */
187
+ public static function error()
188
+ {
189
+ return self::_log(func_get_args() + array('type' => self::ERROR));
190
+ }
191
+
192
+ /**
193
+ * sends a group log
194
+ *
195
+ * @param string value
196
+ */
197
+ public static function group()
198
+ {
199
+ return self::_log(func_get_args() + array('type' => self::GROUP));
200
+ }
201
+
202
+ /**
203
+ * sends an info log
204
+ *
205
+ * @param string value
206
+ */
207
+ public static function info()
208
+ {
209
+ return self::_log(func_get_args() + array('type' => self::INFO));
210
+ }
211
+
212
+ /**
213
+ * sends a collapsed group log
214
+ *
215
+ * @param string value
216
+ */
217
+ public static function groupCollapsed()
218
+ {
219
+ return self::_log(func_get_args() + array('type' => self::GROUP_COLLAPSED));
220
+ }
221
+
222
+ /**
223
+ * ends a group log
224
+ *
225
+ * @param string value
226
+ */
227
+ public static function groupEnd()
228
+ {
229
+ return self::_log(func_get_args() + array('type' => self::GROUP_END));
230
+ }
231
+
232
+ /**
233
+ * internal logging call
234
+ *
235
+ * @param string $type
236
+ * @return void
237
+ */
238
+ protected static function _log(array $args)
239
+ {
240
+ $type = $args['type'];
241
+ unset($args['type']);
242
+
243
+ // nothing passed in, don't do anything
244
+ if (count($args) == 0 && $type != self::GROUP_END) {
245
+ return;
246
+ }
247
+
248
+ // default to single
249
+ $label = null;
250
+ $value = isset($args[0]) ? $args[0] : '';
251
+
252
+ $logger = self::getInstance();
253
+
254
+ // if there are two values passed in then the first one is the label
255
+ if (count($args) == 2) {
256
+ $label = $args[0];
257
+ $value = $args[1];
258
+ }
259
+
260
+ $logger->_processed = array();
261
+ $value = $logger->_convert($value);
262
+
263
+ $backtrace = debug_backtrace(false);
264
+ $level = $logger->getSetting(self::BACKTRACE_LEVEL);
265
+
266
+ $backtrace_message = 'unknown';
267
+ if (isset($backtrace[$level]['file']) && isset($backtrace[$level]['line'])) {
268
+ $backtrace_message = $backtrace[$level]['file'] . ' : ' . $backtrace[$level]['line'];
269
+ }
270
+
271
+ $logger->_addRow($label, $value, $backtrace_message, $type);
272
+ }
273
+
274
+ /**
275
+ * converts an object to a better format for logging
276
+ *
277
+ * @param Object
278
+ * @return array
279
+ */
280
+ protected function _convert($object)
281
+ {
282
+ // if this isn't an object then just return it
283
+ if (!is_object($object)) {
284
+ return $object;
285
+ }
286
+
287
+ //Mark this object as processed so we don't convert it twice and it
288
+ //Also avoid recursion when objects refer to each other
289
+ $this->_processed[] = $object;
290
+
291
+ $object_as_array = array();
292
+
293
+ // first add the class name
294
+ $object_as_array['___class_name'] = get_class($object);
295
+
296
+ // loop through object vars
297
+ $object_vars = get_object_vars($object);
298
+ foreach ($object_vars as $key => $value) {
299
+
300
+ // same instance as parent object
301
+ if ($value === $object || in_array($value, $this->_processed, true)) {
302
+ $value = 'recursion - parent object [' . get_class($value) . ']';
303
+ }
304
+ $object_as_array[$key] = $this->_convert($value);
305
+ }
306
+
307
+ $reflection = new ReflectionClass($object);
308
+
309
+ // loop through the properties and add those
310
+ foreach ($reflection->getProperties() as $property) {
311
+
312
+ // if one of these properties was already added above then ignore it
313
+ if (array_key_exists($property->getName(), $object_vars)) {
314
+ continue;
315
+ }
316
+ $type = $this->_getPropertyKey($property);
317
+
318
+ if ($this->_php_version >= 5.3) {
319
+ $property->setAccessible(true);
320
+ }
321
+
322
+ try {
323
+ $value = $property->getValue($object);
324
+ } catch (ReflectionException $e) {
325
+ $value = 'only PHP 5.3 can access private/protected properties';
326
+ }
327
+
328
+ // same instance as parent object
329
+ if ($value === $object || in_array($value, $this->_processed, true)) {
330
+ $value = 'recursion - parent object [' . get_class($value) . ']';
331
+ }
332
+
333
+ $object_as_array[$type] = $this->_convert($value);
334
+ }
335
+ return $object_as_array;
336
+ }
337
+
338
+ /**
339
+ * takes a reflection property and returns a nicely formatted key of the property name
340
+ *
341
+ * @param ReflectionProperty
342
+ * @return string
343
+ */
344
+ protected function _getPropertyKey(ReflectionProperty $property)
345
+ {
346
+ $static = $property->isStatic() ? ' static' : '';
347
+ if ($property->isPublic()) {
348
+ return 'public' . $static . ' ' . $property->getName();
349
+ }
350
+
351
+ if ($property->isProtected()) {
352
+ return 'protected' . $static . ' ' . $property->getName();
353
+ }
354
+
355
+ if ($property->isPrivate()) {
356
+ return 'private' . $static . ' ' . $property->getName();
357
+ }
358
+ }
359
+
360
+ /**
361
+ * adds a value to the data array
362
+ *
363
+ * @var mixed
364
+ * @return void
365
+ */
366
+ protected function _addRow($label, $log, $backtrace, $type)
367
+ {
368
+ // if this is logged on the same line for example in a loop, set it to null to save space
369
+ if (in_array($backtrace, $this->_backtraces)) {
370
+ $backtrace = null;
371
+ }
372
+
373
+ if ($backtrace !== null) {
374
+ $this->_backtraces[] = $backtrace;
375
+ }
376
+
377
+ $row = array($label, $log, $backtrace, $type);
378
+
379
+ $this->_json['rows'][] = $row;
380
+ $this->_writeHeader($this->_json);
381
+ }
382
+
383
+ protected function _writeHeader($data)
384
+ {
385
+ //ob_start();
386
+ @header(self::HEADER_NAME . ': ' . $this->_encode($data));
387
+ //echo self::HEADER_NAME . ': ' . $this->_encode($data);
388
+ //ob_end_clean();
389
+ }
390
+
391
+ /**
392
+ * encodes the data to be sent along with the request
393
+ *
394
+ * @param array $data
395
+ * @return string
396
+ */
397
+ protected function _encode($data)
398
+ {
399
+ return base64_encode(utf8_encode(json_encode($data)));
400
+ }
401
+
402
+ /**
403
+ * adds a setting
404
+ *
405
+ * @param string key
406
+ * @param mixed value
407
+ * @return void
408
+ */
409
+ public function addSetting($key, $value)
410
+ {
411
+ $this->_settings[$key] = $value;
412
+ }
413
+
414
+ /**
415
+ * add ability to set multiple settings in one call
416
+ *
417
+ * @param array $settings
418
+ * @return void
419
+ */
420
+ public function addSettings(array $settings)
421
+ {
422
+ foreach ($settings as $key => $value) {
423
+ $this->addSetting($key, $value);
424
+ }
425
+ }
426
+
427
+ /**
428
+ * gets a setting
429
+ *
430
+ * @param string key
431
+ * @return mixed
432
+ */
433
+ public function getSetting($key)
434
+ {
435
+ if (!isset($this->_settings[$key])) {
436
+ return null;
437
+ }
438
+ return $this->_settings[$key];
439
+ }
440
+ }
includes/debug/browsers/api/chromephp/README ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version 3.0
2
+ This class requires PHP 5 or later.
3
+
4
+ ChromePhp is a PHP class to go along with the ChromePhp Google Chrome extension.
5
+
6
+ This class allows you to log variables to the Chrome console.
7
+
8
+ INSTALLATION
9
+ 1. Download the Chrome extension from: https://chrome.google.com/extensions/detail/noaneddfkdjfnfdakjjmocngnfkfehhd
10
+ 2. Put ChromePhp.php somewhere in your PHP include path
11
+ 3. Click the extension icon on the browser to enable it for the current site
12
+ 3. include 'ChromePhp.php';
13
+ 4. ChromePhp::log('hello world');
14
+
15
+ More information can be found here:
16
+ http://www.chromephp.com
includes/debug/browsers/api/firephp/CHANGELOG.md ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ TODO:
3
+
4
+ * Fix code indenting in PHP 4 code
5
+ * Port maxDepth option to PHP 4 code
6
+
7
+ 2011-06-22 - Release Version: 0.4.0rc3
8
+
9
+ * Build fixes
10
+
11
+ 2011-06-20 - Release Version: 0.4.0rc1
12
+
13
+ * (Issue 163) PHP5 class_exists() throws Exception without second parameter
14
+ * (Issue 166) Non-utf8 array values replaced with null
15
+ * Cleaned up code formatting [sokolov.innokenty@gmail.com]
16
+ * Ensure JSON keys are never NULL (due to NULL key in some arrays)
17
+ * Better UTF-8 encoding detection
18
+ * Code style cleanup (qbbr)
19
+ * Changed license to MIT
20
+ * Refactored project
21
+
22
+ 2010-10-26 - Release Version: 0.3.2
23
+
24
+ 2010-10-12 - Release Version: 0.3.2rc6
25
+
26
+ * (Issue 154) getRequestHeader uses "getallheaders" even though it doesn't always exist. [25m]
27
+
28
+ 2010-10-09 - Release Version: 0.3.2rc5
29
+
30
+ * (Issue 153) FirePHP incorrectly double-encodes UTF8 when mbstring.func_overload is enabled
31
+
32
+ 2010-10-08 - Release Version: 0.3.2rc4
33
+
34
+ * Trigger upgrade message if part of FirePHP 1.0
35
+ * Removed FirePHP/Init.php inclusion logic and only load FirePHP.class.php if not already loaded
36
+
37
+ 2010-07-19 - Release Version: 0.3.2rc3
38
+
39
+ * Fixed FirePHP/Init.php inclusion logic
40
+
41
+ 2010-07-19 - Release Version: 0.3.2rc2
42
+
43
+ * (Issue 145) maxDepth option
44
+ * Changed maxObjectDepth and maxArrayDepth option defaults to 5
45
+ * Fixed code indentation
46
+
47
+ 2010-03-05 - Release Version: 0.3.2rc1
48
+
49
+ * (Issue 114) Allow options to be passed on to basic logging wrappers
50
+ * (Issue 122) Filter objectStack property of FirePHP class
51
+ * (Issue 123) registerErrorHandler(false) by default
52
+ * Added setOption() and getOption() methods
53
+ * (Issue 117) dump() method argument validation
54
+ * Started adding PHPUnit tests
55
+ * Some refactoring to support unit testing
56
+ * Deprecated setProcessorUrl() and setRendererUrl()
57
+ * Check User-Agent and X-FirePHP-Version header to detect FirePHP on client
58
+ * (Issue 135) FirePHP 0.4.3 with Firebug 1.5 changes user agent on the fly
59
+ * (Issue 112) Error Predefined Constants Not available for PHP 5.x versions
60
+
61
+ 2008-06-14 - Release Version: 0.3.1
62
+
63
+ * (Issue 108) ignore class name case in object filter
64
+
65
+ 2009-05-11 - Release Version: 0.3
66
+ 2009-05-01 - Release Version: 0.3.rc.1
67
+
68
+ * (Issue 90) PHP4 compatible version of FirePHPCore
69
+ * (Issue 98) Thrown exceptions don't send an HTTP 500 if the FirePHP exception handler is enabled
70
+ * (Issue 85) Support associative arrays in encodeTable method in FirePHP.class.php
71
+ * (Issue 66) Add a new getOptions() public method in API
72
+ * (Issue 82) Define $this->options outside of __construct
73
+ * (Issue 72) Message error if group name is null
74
+ * (Issue 68) registerErrorHandler() and registerExceptionHandler() should returns previous handlers defined
75
+ * (Issue 69) Add the missing register handler in the triumvirate (error, exception, assert)
76
+ * (Issue 75) [Error & Exception Handling] Option to not exit script execution
77
+ * (Issue 83) Exception handler can't throw exceptions
78
+ * (Issue 80) Auto/Pre collapsing groups AND Custom group row colors
79
+
80
+ 2008-11-09 - Release Version: 0.2.1
81
+
82
+ * (Issue 70) Problem when logging resources
83
+
84
+ 2008-10-21 - Release Version: 0.2.0
85
+
86
+ * Updated version to 0.2.0
87
+ * Switched to using __sleep instead of __wakeup
88
+ * Added support to exclude object members when encoding
89
+ * Add support to enable/disable logging
90
+
91
+ 2008-10-17 - Release Version: 0.2.b.8
92
+
93
+ * New implementation for is_utf8()
94
+ * (Issue 55) maxObjectDepth Option not working correctly when using TABLE and EXCEPTION Type
95
+ * Bugfix for max[Object|Array]Depth when encoding nested array/object graphs
96
+ * Bugfix for FB::setOptions()
97
+
98
+ 2008-10-16 - Release Version: 0.2.b.7
99
+
100
+ * (Issue 45) Truncate dump when string have non utf8 cars
101
+ * (Issue 52) logging will not work when firephp object gets stored in the session.
102
+
103
+ 2008-10-16 - Release Version: 0.2.b.6
104
+
105
+ * (Issue 37) Display file and line information for each log message
106
+ * (Issue 51) Limit output of object graphs
107
+ * Bugfix for encoding object members set to NULL|false|''
108
+
109
+ 2008-10-14 - Release Version: 0.2.b.5
110
+
111
+ * Updated JsonStream wildfire protocol to be more robust
112
+ * (Issue 33) PHP error notices running demos
113
+ * (Issue 48) Warning: ReflectionProperty::getValue() expects exactly 1 parameter, 0 given
114
+
115
+ 2008-10-08 - Release Version: 0.2.b.4
116
+
117
+ * Bugfix for logging objects with recursion
118
+
119
+ 2008-10-08 - Release Version: 0.2.b.3
120
+
121
+ * (Issue 43) Notice message in 0.2b2
122
+ * Added support for PHP's native json_encode() if available
123
+ * Revised object encoder to detect object recursion
124
+
125
+ 2008-10-07 - Release Version: 0.2.b.2
126
+
127
+ * (Issue 28) Need solution for logging private and protected object variables
128
+ * Added trace() and table() aliases in FirePHP class
129
+ * (Issue 41) Use PHP doc in FirePHP
130
+ * (Issue 39) Static logging method for object oriented API
131
+
132
+ 2008-10-01 - Release Version: 0.2.b.1
133
+
134
+ * Added support for error and exception handling
135
+ * Updated min PHP version for PEAR package to 5.2
136
+ * Added version constant for library
137
+ * Gave server library it's own wildfire plugin namespace
138
+ * Migrated communication protocol to Wildfire JsonStream
139
+ * Added support for console groups using "group" and "groupEnd"
140
+ * Added support for log, info, warn and error logging aliases
141
+ * (Issue 29) problem with TRACE when using with error_handler
142
+ * (Issue 33) PHP error notices running demos
143
+ * (Issue 12) undefined index php notice
144
+ * Removed closing ?> php tags
145
+ * (Issue 13) the code in the fb() function has a second return statement that will never be reached
146
+
147
+ 2008-07-30 - Release Version: 0.1.1.3
148
+
149
+ * Include __className property in JSON string if variable was an object
150
+ * Bugfix - Mis-spelt "Exception" in JSON encoding code
151
+
152
+ 2008-06-13 - Release Version: 0.1.1.1
153
+
154
+ * Bugfix - Standardize windows paths in stack traces
155
+ * Bugfix - Display correct stack trace info in windows environments
156
+ * Bugfix - Check $_SERVER['HTTP_USER_AGENT'] before returning
157
+
158
+ 2008-06-13 - Release Version: 0.1.1
159
+
160
+ * Added support for FirePHP::TRACE log style
161
+ * Changed license to New BSD License
162
+
163
+ 2008-06-06 - Release Version: 0.0.2
164
+
165
+ * Bugfix - Added usleep() to header writing loop to ensure unique index
166
+ * Bugfix - Ensure chunk_split does not generate trailing "\n" with empty data header
167
+ * Added support for FirePHP::TABLE log style
includes/debug/browsers/api/firephp/README.md ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FirePHPCore
2
+ ===========
3
+
4
+ **Status: stable**
5
+
6
+ > **FirePHP is an advanced logging system that can display PHP variables in the browser as an application is navigated.**
7
+ > All communication is out of band to the application meaning that the logging data will not interfere with the normal functioning of the application.
8
+
9
+ This project contains the *FirePHPCore* PHP server library and provides a development environment (see `./workspace/`) for working on *FirePHPCore*.
10
+
11
+
12
+ Usage
13
+ =====
14
+
15
+ See [Install/Traditional: FirePHPCore](http://docs.sourcemint.org/firephp.org/firephp/1/-docs/Configuration/Traditional) in the
16
+ [FirePHP 1.0 Documentation](http://docs.sourcemint.org/firephp.org/firephp/1/-docs/).
17
+
18
+
19
+ Testing
20
+ =======
21
+
22
+ cd tests
23
+ phpunit .
24
+
25
+
26
+ Support & Feedback
27
+ ==================
28
+
29
+ See [Support](http://docs.sourcemint.org/firephp.org/firephp/1/-docs/OpenSource#support) in the [FirePHP 1.0 Documentation](http://docs.sourcemint.org/firephp.org/firephp/1/-docs/).
30
+
31
+
32
+ Contribute
33
+ ==========
34
+
35
+ See [Contribute](http://docs.sourcemint.org/firephp.org/firephp/1/-docs/OpenSource#contribute) in the [FirePHP 1.0 Documentation](http://docs.sourcemint.org/firephp.org/firephp/1/-docs/).
36
+
37
+
38
+ Author
39
+ ======
40
+
41
+ This project is authored and maintained by [Christoph Dorn](http://www.christophdorn.com/).
42
+
43
+
44
+ Documentation License
45
+ =====================
46
+
47
+ [Creative Commons Attribution-NonCommercial-ShareAlike 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/)
48
+
49
+ Copyright (c) 2007+ [Christoph Dorn](http://www.christophdorn.com/)
50
+
51
+
52
+ Code License
53
+ ============
54
+
55
+ [MIT License](http://www.opensource.org/licenses/mit-license.php)
56
+
57
+ Copyright (c) 2007+ [Christoph Dorn](http://www.christophdorn.com/)
58
+
59
+ Permission is hereby granted, free of charge, to any person obtaining a copy
60
+ of this software and associated documentation files (the "Software"), to deal
61
+ in the Software without restriction, including without limitation the rights
62
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
63
+ copies of the Software, and to permit persons to whom the Software is
64
+ furnished to do so, subject to the following conditions:
65
+
66
+ The above copyright notice and this permission notice shall be included in
67
+ all copies or substantial portions of the Software.
68
+
69
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
70
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
71
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
72
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
73
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
74
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
75
+ THE SOFTWARE.
includes/debug/browsers/api/firephp/examples/oo.php ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Authors:
3
+ // - cadorn, Christoph Dorn <christoph@christophdorn.com>, Copyright 2007, New BSD License
4
+ // - cadorn, Christoph Dorn <christoph@christophdorn.com>, Copyright 2011, MIT License
5
+
6
+ /* *** BEGIN LICENSE BLOCK *****
7
+ *
8
+ * [MIT License](http://www.opensource.org/licenses/mit-license.php)
9
+ *
10
+ * Copyright (c) 2007+ [Christoph Dorn](http://www.christophdorn.com/)
11
+ *
12
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ * of this software and associated documentation files (the "Software"), to deal
14
+ * in the Software without restriction, including without limitation the rights
15
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ * copies of the Software, and to permit persons to whom the Software is
17
+ * furnished to do so, subject to the following conditions:
18
+ *
19
+ * The above copyright notice and this permission notice shall be included in
20
+ * all copies or substantial portions of the Software.
21
+ *
22
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28
+ * THE SOFTWARE.
29
+ *
30
+ * ***** END LICENSE BLOCK ***** */
31
+
32
+
33
+ /* NOTE: You must have the FirePHPCore library in your include path */
34
+
35
+ set_include_path(dirname(dirname(__FILE__)).'/lib'.PATH_SEPARATOR.get_include_path());
36
+
37
+
38
+ require('FirePHPCore/FirePHP.class.php');
39
+
40
+ /* NOTE: You must have Output Buffering enabled via
41
+ ob_start() or output_buffering ini directive. */
42
+
43
+
44
+ $firephp = FirePHP::getInstance(true);
45
+
46
+
47
+ $firephp->fb('Hello World'); /* Defaults to FirePHP::LOG */
48
+
49
+ $firephp->fb('Log message' ,FirePHP::LOG);
50
+ $firephp->fb('Info message' ,FirePHP::INFO);
51
+ $firephp->fb('Warn message' ,FirePHP::WARN);
52
+ $firephp->fb('Error message',FirePHP::ERROR);
53
+
54
+ $firephp->fb('Message with label','Label',FirePHP::LOG);
55
+
56
+ $firephp->fb(array('key1'=>'val1',
57
+ 'key2'=>array(array('v1','v2'),'v3')),
58
+ 'TestArray',FirePHP::LOG);
59
+
60
+ function test($Arg1) {
61
+ throw new Exception('Test Exception');
62
+ }
63
+ try {
64
+ test(array('Hello'=>'World'));
65
+ } catch(Exception $e) {
66
+ /* Log exception including stack trace & variables */
67
+ $firephp->fb($e);
68
+ }
69
+
70
+ $firephp->fb('Backtrace to here',FirePHP::TRACE);
71
+
72
+ $firephp->fb(array('2 SQL queries took 0.06 seconds',array(
73
+ array('SQL Statement','Time','Result'),
74
+ array('SELECT * FROM Foo','0.02',array('row1','row2')),
75
+ array('SELECT * FROM Bar','0.04',array('row1','row2'))
76
+ )),FirePHP::TABLE);
77
+
78
+ /* Will show only in "Server" tab for the request */
79
+ $firephp->fb(apache_request_headers(),'RequestHeaders',FirePHP::DUMP);
80
+
81
+
82
+ print 'Hello World';
includes/debug/browsers/api/firephp/examples/oo.php4 ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Authors:
3
+ // - cadorn, Christoph Dorn <christoph@christophdorn.com>, Copyright 2007, New BSD License
4
+ // - cadorn, Christoph Dorn <christoph@christophdorn.com>, Copyright 2011, MIT License
5
+
6
+ /* *** BEGIN LICENSE BLOCK *****
7
+ *
8
+ * [MIT License](http://www.opensource.org/licenses/mit-license.php)
9
+ *
10
+ * Copyright (c) 2007+ [Christoph Dorn](http://www.christophdorn.com/)
11
+ *
12
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ * of this software and associated documentation files (the "Software"), to deal
14
+ * in the Software without restriction, including without limitation the rights
15
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ * copies of the Software, and to permit persons to whom the Software is
17
+ * furnished to do so, subject to the following conditions:
18
+ *
19
+ * The above copyright notice and this permission notice shall be included in
20
+ * all copies or substantial portions of the Software.
21
+ *
22
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28
+ * THE SOFTWARE.
29
+ *
30
+ * ***** END LICENSE BLOCK ***** */
31
+
32
+
33
+ /* NOTE: You must have the FirePHPCore library in your include path */
34
+
35
+ set_include_path(dirname(dirname(__FILE__)).'/lib'.PATH_SEPARATOR.get_include_path());
36
+
37
+
38
+ require('FirePHPCore/FirePHP.class.php4');
39
+
40
+ /* NOTE: You must have Output Buffering enabled via
41
+ ob_start() or output_buffering ini directive. */
42
+
43
+
44
+ $firephp =& FirePHP::getInstance(true);
45
+
46
+
47
+ $firephp->fb('Hello World'); /* Defaults to FirePHP::LOG */
48
+
49
+ $firephp->fb('Log message' ,FirePHP_LOG);
50
+ $firephp->fb('Info message' ,FirePHP_INFO);
51
+ $firephp->fb('Warn message' ,FirePHP_WARN);
52
+ $firephp->fb('Error message',FirePHP_ERROR);
53
+
54
+ $firephp->fb('Message with label','Label',FirePHP_LOG);
55
+
56
+ $firephp->fb(array('key1'=>'val1',
57
+ 'key2'=>array(array('v1','v2'),'v3')),
58
+ 'TestArray',FirePHP_LOG);
59
+
60
+ $firephp->fb('Backtrace to here',FirePHP_TRACE);
61
+
62
+ $firephp->fb(array('2 SQL queries took 0.06 seconds',array(
63
+ array('SQL Statement','Time','Result'),
64
+ array('SELECT * FROM Foo','0.02',array('row1','row2')),
65
+ array('SELECT * FROM Bar','0.04',array('row1','row2'))
66
+ )),FirePHP_TABLE);
67
+
68
+ /* Will show only in "Server" tab for the request */
69
+ $firephp->fb(apache_request_headers(),'RequestHeaders',FirePHP_DUMP);
70
+
71
+
72
+ print 'Hello World';
includes/debug/browsers/api/firephp/examples/procedural.php ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Authors:
3
+ // - cadorn, Christoph Dorn <christoph@christophdorn.com>, Copyright 2007, New BSD License
4
+ // - cadorn, Christoph Dorn <christoph@christophdorn.com>, Copyright 2011, MIT License
5
+
6
+ /* *** BEGIN LICENSE BLOCK *****
7
+ *
8
+ * [MIT License](http://www.opensource.org/licenses/mit-license.php)
9
+ *
10
+ * Copyright (c) 2007+ [Christoph Dorn](http://www.christophdorn.com/)
11
+ *
12
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ * of this software and associated documentation files (the "Software"), to deal
14
+ * in the Software without restriction, including without limitation the rights
15
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ * copies of the Software, and to permit persons to whom the Software is
17
+ * furnished to do so, subject to the following conditions:
18
+ *
19
+ * The above copyright notice and this permission notice shall be included in
20
+ * all copies or substantial portions of the Software.
21
+ *
22
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28
+ * THE SOFTWARE.
29
+ *
30
+ * ***** END LICENSE BLOCK ***** */
31
+
32
+
33
+ /* NOTE: You must have the FirePHPCore library in your include path */
34
+
35
+ set_include_path(dirname(dirname(__FILE__)).'/lib'.PATH_SEPARATOR.get_include_path());
36
+
37
+
38
+ require('FirePHPCore/fb.php');
39
+
40
+ /* NOTE: You must have Output Buffering enabled via
41
+ ob_start() or output_buffering ini directive. */
42
+
43
+ fb('Hello World'); /* Defaults to FirePHP::LOG */
44
+
45
+ fb('Log message' ,FirePHP::LOG);
46
+ fb('Info message' ,FirePHP::INFO);
47
+ fb('Warn message' ,FirePHP::WARN);
48
+ fb('Error message',FirePHP::ERROR);
49
+
50
+ fb('Message with label','Label',FirePHP::LOG);
51
+
52
+ fb(array('key1'=>'val1',
53
+ 'key2'=>array(array('v1','v2'),'v3')),
54
+ 'TestArray',FirePHP::LOG);
55
+
56
+ function test($Arg1) {
57
+ throw new Exception('Test Exception');
58
+ }
59
+ try {
60
+ test(array('Hello'=>'World'));
61
+ } catch(Exception $e) {
62
+ /* Log exception including stack trace & variables */
63
+ fb($e);
64
+ }
65
+
66
+ fb('Backtrace to here',FirePHP::TRACE);
67
+
68
+ fb(array('2 SQL queries took 0.06 seconds',array(
69
+ array('SQL Statement','Time','Result'),
70
+ array('SELECT * FROM Foo','0.02',array('row1','row2')),
71
+ array('SELECT * FROM Bar','0.04',array('row1','row2'))
72
+ )),FirePHP::TABLE);
73
+
74
+ /* Will show only in "Server" tab for the request */
75
+ fb(apache_request_headers(),'RequestHeaders',FirePHP::DUMP);
76
+
77
+
78
+ print 'Hello World';
79
+
includes/debug/browsers/api/firephp/examples/procedural.php4 ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Authors:
3
+ // - cadorn, Christoph Dorn <christoph@christophdorn.com>, Copyright 2007, New BSD License
4
+ // - cadorn, Christoph Dorn <christoph@christophdorn.com>, Copyright 2011, MIT License
5
+
6
+ /* *** BEGIN LICENSE BLOCK *****
7
+ *
8
+ * [MIT License](http://www.opensource.org/licenses/mit-license.php)
9
+ *
10
+ * Copyright (c) 2007+ [Christoph Dorn](http://www.christophdorn.com/)
11
+ *
12
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ * of this software and associated documentation files (the "Software"), to deal
14
+ * in the Software without restriction, including without limitation the rights
15
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ * copies of the Software, and to permit persons to whom the Software is
17
+ * furnished to do so, subject to the following conditions:
18
+ *
19
+ * The above copyright notice and this permission notice shall be included in
20
+ * all copies or substantial portions of the Software.
21
+ *
22
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28
+ * THE SOFTWARE.
29
+ *
30
+ * ***** END LICENSE BLOCK ***** */
31
+
32
+
33
+ /* NOTE: You must have the FirePHPCore library in your include path */
34
+
35
+ set_include_path(dirname(dirname(__FILE__)).'/lib'.PATH_SEPARATOR.get_include_path());
36
+
37
+
38
+ require('FirePHPCore/fb.php');
39
+
40
+ /* NOTE: You must have Output Buffering enabled via
41
+ ob_start() or output_buffering ini directive. */
42
+
43
+ fb('Hello World'); /* Defaults to FirePHP::LOG */
44
+
45
+ fb('Log message' ,FirePHP_LOG);
46
+ fb('Info message' ,FirePHP_INFO);
47
+ fb('Warn message' ,FirePHP_WARN);
48
+ fb('Error message',FirePHP_ERROR);
49
+
50
+ fb('Message with label','Label',FirePHP_LOG);
51
+
52
+ fb(array('key1'=>'val1',
53
+ 'key2'=>array(array('v1','v2'),'v3')),
54
+ 'TestArray',FirePHP_LOG);
55
+
56
+ fb('Backtrace to here',FirePHP_TRACE);
57
+
58
+ fb(array('2 SQL queries took 0.06 seconds',array(
59
+ array('SQL Statement','Time','Result'),
60
+ array('SELECT * FROM Foo','0.02',array('row1','row2')),
61
+ array('SELECT * FROM Bar','0.04',array('row1','row2'))
62
+ )),FirePHP_TABLE);
63
+
64
+ /* Will show only in "Server" tab for the request */
65
+ fb(apache_request_headers(),'RequestHeaders',FirePHP_DUMP);
66
+
67
+
68
+ print 'Hello World';
69
+
includes/debug/browsers/api/firephp/lib/FirePHPCore/FirePHP.class.php ADDED
@@ -0,0 +1,1828 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Authors:
3
+ // - cadorn, Christoph Dorn <christoph@christophdorn.com>, Copyright 2007, New BSD License
4
+ // - qbbr, Sokolov Innokenty <sokolov.innokenty@gmail.com>, Copyright 2011, New BSD License
5
+ // - cadorn, Christoph Dorn <christoph@christophdorn.com>, Copyright 2011, MIT License
6
+
7
+ /**
8
+ * *** BEGIN LICENSE BLOCK *****
9
+ *
10
+ * [MIT License](http://www.opensource.org/licenses/mit-license.php)
11
+ *
12
+ * Copyright (c) 2007+ [Christoph Dorn](http://www.christophdorn.com/)
13
+ *
14
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
15
+ * of this software and associated documentation files (the "Software"), to deal
16
+ * in the Software without restriction, including without limitation the rights
17
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18
+ * copies of the Software, and to permit persons to whom the Software is
19
+ * furnished to do so, subject to the following conditions:
20
+ *
21
+ * The above copyright notice and this permission notice shall be included in
22
+ * all copies or substantial portions of the Software.
23
+ *
24
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
30
+ * THE SOFTWARE.
31
+ *
32
+ * ***** END LICENSE BLOCK *****
33
+ *
34
+ * @copyright Copyright (C) 2007+ Christoph Dorn
35
+ * @author Christoph Dorn <christoph@christophdorn.com>
36
+ * @license [MIT License](http://www.opensource.org/licenses/mit-license.php)
37
+ * @package FirePHPCore
38
+ */
39
+
40
+ /**
41
+ * @see http://code.google.com/p/firephp/issues/detail?id=112
42
+ */
43
+ if (!defined('E_STRICT')) {
44
+ define('E_STRICT', 2048);
45
+ }
46
+ if (!defined('E_RECOVERABLE_ERROR')) {
47
+ define('E_RECOVERABLE_ERROR', 4096);
48
+ }
49
+ if (!defined('E_DEPRECATED')) {
50
+ define('E_DEPRECATED', 8192);
51
+ }
52
+ if (!defined('E_USER_DEPRECATED')) {
53
+ define('E_USER_DEPRECATED', 16384);
54
+ }
55
+
56
+ /**
57
+ * Sends the given data to the FirePHP Firefox Extension.
58
+ * The data can be displayed in the Firebug Console or in the
59
+ * "Server" request tab.
60
+ *
61
+ * For more information see: http://www.firephp.org/
62
+ *
63
+ * @copyright Copyright (C) 2007+ Christoph Dorn
64
+ * @author Christoph Dorn <christoph@christophdorn.com>
65
+ * @license [MIT License](http://www.opensource.org/licenses/mit-license.php)
66
+ * @package FirePHPCore
67
+ */
68
+ class FirePHP {
69
+
70
+ /**
71
+ * FirePHP version
72
+ *
73
+ * @var string
74
+ */
75
+ const VERSION = '0.3'; // @pinf replace '0.3' with '%%VERSION%%'
76
+
77
+ /**
78
+ * Firebug LOG level
79
+ *
80
+ * Logs a message to firebug console.
81
+ *
82
+ * @var string
83
+ */
84
+ const LOG = 'LOG';
85
+
86
+ /**
87
+ * Firebug INFO level
88
+ *
89
+ * Logs a message to firebug console and displays an info icon before the message.
90
+ *
91
+ * @var string
92
+ */
93
+ const INFO = 'INFO';
94
+
95
+ /**
96
+ * Firebug WARN level
97
+ *
98
+ * Logs a message to firebug console, displays an warning icon before the message and colors the line turquoise.
99
+ *
100
+ * @var string
101
+ */
102
+ const WARN = 'WARN';
103
+
104
+ /**
105
+ * Firebug ERROR level
106
+ *
107
+ * Logs a message to firebug console, displays an error icon before the message and colors the line yellow. Also increments the firebug error count.
108
+ *
109
+ * @var string
110
+ */
111
+ const ERROR = 'ERROR';
112
+
113
+ /**
114
+ * Dumps a variable to firebug's server panel
115
+ *
116
+ * @var string
117
+ */
118
+ const DUMP = 'DUMP';
119
+
120
+ /**
121
+ * Displays a stack trace in firebug console
122
+ *
123
+ * @var string
124
+ */
125
+ const TRACE = 'TRACE';
126
+
127
+ /**
128
+ * Displays an exception in firebug console
129
+ *
130
+ * Increments the firebug error count.
131
+ *
132
+ * @var string
133
+ */
134
+ const EXCEPTION = 'EXCEPTION';
135
+
136
+ /**
137
+ * Displays an table in firebug console
138
+ *
139
+ * @var string
140
+ */
141
+ const TABLE = 'TABLE';
142
+
143
+ /**
144
+ * Starts a group in firebug console
145
+ *
146
+ * @var string
147
+ */
148
+ const GROUP_START = 'GROUP_START';
149
+
150
+ /**
151
+ * Ends a group in firebug console
152
+ *
153
+ * @var string
154
+ */
155
+ const GROUP_END = 'GROUP_END';
156
+
157
+ /**
158
+ * Singleton instance of FirePHP
159
+ *
160
+ * @var FirePHP
161
+ */
162
+ protected static $instance = null;
163
+
164
+ /**
165
+ * Flag whether we are logging from within the exception handler
166
+ *
167
+ * @var boolean
168
+ */
169
+ protected $inExceptionHandler = false;
170
+
171
+ /**
172
+ * Flag whether to throw PHP errors that have been converted to ErrorExceptions
173
+ *
174
+ * @var boolean
175
+ */
176
+ protected $throwErrorExceptions = true;
177
+
178
+ /**
179
+ * Flag whether to convert PHP assertion errors to Exceptions
180
+ *
181
+ * @var boolean
182
+ */
183
+ protected $convertAssertionErrorsToExceptions = true;
184
+
185
+ /**
186
+ * Flag whether to throw PHP assertion errors that have been converted to Exceptions
187
+ *
188
+ * @var boolean
189
+ */
190
+ protected $throwAssertionExceptions = false;
191
+
192
+ /**
193
+ * Wildfire protocol message index
194
+ *
195
+ * @var integer
196
+ */
197
+ protected $messageIndex = 1;
198
+
199
+ /**
200
+ * Options for the library
201
+ *
202
+ * @var array
203
+ */
204
+ protected $options = array('maxDepth' => 10,
205
+ 'maxObjectDepth' => 5,
206
+ 'maxArrayDepth' => 5,
207
+ 'useNativeJsonEncode' => true,
208
+ 'includeLineNumbers' => true);
209
+
210
+ /**
211
+ * Filters used to exclude object members when encoding
212
+ *
213
+ * @var array
214
+ */
215
+ protected $objectFilters = array(
216
+ 'firephp' => array('objectStack', 'instance', 'json_objectStack'),
217
+ 'firephp_test_class' => array('objectStack', 'instance', 'json_objectStack')
218
+ );
219
+
220
+ /**
221
+ * A stack of objects used to detect recursion during object encoding
222
+ *
223
+ * @var object
224
+ */
225
+ protected $objectStack = array();
226
+
227
+ /**
228
+ * Flag to enable/disable logging
229
+ *
230
+ * @var boolean
231
+ */
232
+ protected $enabled = true;
233
+
234
+ /**
235
+ * The insight console to log to if applicable
236
+ *
237
+ * @var object
238
+ */
239
+ protected $logToInsightConsole = null;
240
+
241
+ /**
242
+ * When the object gets serialized only include specific object members.
243
+ *
244
+ * @return array
245
+ */
246
+ public function __sleep()
247
+ {
248
+ return array('options', 'objectFilters', 'enabled');
249
+ }
250
+
251
+ /**
252
+ * Gets singleton instance of FirePHP
253
+ *
254
+ * @param boolean $autoCreate
255
+ * @return FirePHP
256
+ */
257
+ public static function getInstance($autoCreate = false)
258
+ {
259
+ if ($autoCreate === true && !self::$instance) {
260
+ self::init();
261
+ }
262
+ return self::$instance;
263
+ }
264
+
265
+ /**
266
+ * Creates FirePHP object and stores it for singleton access
267
+ *
268
+ * @return FirePHP
269
+ */
270
+ public static function init()
271
+ {
272
+ return self::setInstance(new self());
273
+ }
274
+
275
+ /**
276
+ * Set the instance of the FirePHP singleton
277
+ *
278
+ * @param FirePHP $instance The FirePHP object instance
279
+ * @return FirePHP
280
+ */
281
+ public static function setInstance($instance)
282
+ {
283
+ return self::$instance = $instance;
284
+ }
285
+
286
+ /**
287
+ * Set an Insight console to direct all logging calls to
288
+ *
289
+ * @param object $console The console object to log to
290
+ * @return void
291
+ */
292
+ public function setLogToInsightConsole($console)
293
+ {
294
+ if (is_string($console)) {
295
+ if (get_class($this) != 'FirePHP_Insight' && !is_subclass_of($this, 'FirePHP_Insight')) {
296
+ throw new Exception('FirePHP instance not an instance or subclass of FirePHP_Insight!');
297
+ }
298
+ $this->logToInsightConsole = $this->to('request')->console($console);
299
+ } else {
300
+ $this->logToInsightConsole = $console;
301
+ }
302
+ }
303
+
304
+ /**
305
+ * Enable and disable logging to Firebug
306
+ *
307
+ * @param boolean $enabled TRUE to enable, FALSE to disable
308
+ * @return void
309
+ */
310
+ public function setEnabled($enabled)
311
+ {
312
+ $this->enabled = $enabled;
313
+ }
314
+
315
+ /**
316
+ * Check if logging is enabled
317
+ *
318
+ * @return boolean TRUE if enabled
319
+ */
320
+ public function getEnabled()
321
+ {
322
+ return $this->enabled;
323
+ }
324
+
325
+ /**
326
+ * Specify a filter to be used when encoding an object
327
+ *
328
+ * Filters are used to exclude object members.
329
+ *
330
+ * @param string $class The class name of the object
331
+ * @param array $filter An array of members to exclude
332
+ * @return void
333
+ */
334
+ public function setObjectFilter($class, $filter)
335
+ {
336
+ $this->objectFilters[strtolower($class)] = $filter;
337
+ }
338
+
339
+ /**
340
+ * Set some options for the library
341
+ *
342
+ * Options:
343
+ * - maxDepth: The maximum depth to traverse (default: 10)
344
+ * - maxObjectDepth: The maximum depth to traverse objects (default: 5)
345
+ * - maxArrayDepth: The maximum depth to traverse arrays (default: 5)
346
+ * - useNativeJsonEncode: If true will use json_encode() (default: true)
347
+ * - includeLineNumbers: If true will include line numbers and filenames (default: true)
348
+ *
349
+ * @param array $options The options to be set
350
+ * @return void
351
+ */
352
+ public function setOptions($options)
353
+ {
354
+ $this->options = array_merge($this->options, $options);
355
+ }
356
+
357
+ /**
358
+ * Get options from the library
359
+ *
360
+ * @return array The currently set options
361
+ */
362
+ public function getOptions()
363
+ {
364
+ return $this->options;
365
+ }
366
+
367
+ /**
368
+ * Set an option for the library
369
+ *
370
+ * @param string $name
371
+ * @param mixed $value
372
+ * @return void
373
+ * @throws Exception
374
+ */
375
+ public function setOption($name, $value)
376
+ {
377
+ if (!isset($this->options[$name])) {
378
+ throw $this->newException('Unknown option: ' . $name);
379
+ }
380
+ $this->options[$name] = $value;
381
+ }
382
+
383
+ /**
384
+ * Get an option from the library
385
+ *
386
+ * @param string $name
387
+ * @return mixed
388
+ * @throws Exception
389
+ */
390
+ public function getOption($name)
391
+ {
392
+ if (!isset($this->options[$name])) {
393
+ throw $this->newException('Unknown option: ' . $name);
394
+ }
395
+ return $this->options[$name];
396
+ }
397
+
398
+ /**
399
+ * Register FirePHP as your error handler
400
+ *
401
+ * Will throw exceptions for each php error.
402
+ *
403
+ * @return mixed Returns a string containing the previously defined error handler (if any)
404
+ */
405
+ public function registerErrorHandler($throwErrorExceptions = false)
406
+ {
407
+ //NOTE: The following errors will not be caught by this error handler:
408
+ // E_ERROR, E_PARSE, E_CORE_ERROR,
409
+ // E_CORE_WARNING, E_COMPILE_ERROR,
410
+ // E_COMPILE_WARNING, E_STRICT
411
+
412
+ $this->throwErrorExceptions = $throwErrorExceptions;
413
+
414
+ return set_error_handler(array($this, 'errorHandler'));
415
+ }
416
+
417
+ /**
418
+ * FirePHP's error handler
419
+ *
420
+ * Throws exception for each php error that will occur.
421
+ *
422
+ * @param integer $errno
423
+ * @param string $errstr
424
+ * @param string $errfile
425
+ * @param integer $errline
426
+ * @param array $errcontext
427
+ */
428
+ public function errorHandler($errno, $errstr, $errfile, $errline, $errcontext)
429
+ {
430
+ // Don't throw exception if error reporting is switched off
431
+ if (error_reporting() == 0) {
432
+ return;
433
+ }
434
+ // Only throw exceptions for errors we are asking for
435
+ if (error_reporting() & $errno) {
436
+
437
+ $exception = new ErrorException($errstr, 0, $errno, $errfile, $errline);
438
+ if ($this->throwErrorExceptions) {
439
+ throw $exception;
440
+ } else {
441
+ $this->fb($exception);
442
+ }
443
+ }
444
+ }
445
+
446
+ /**
447
+ * Register FirePHP as your exception handler
448
+ *
449
+ * @return mixed Returns the name of the previously defined exception handler,
450
+ * or NULL on error.
451
+ * If no previous handler was defined, NULL is also returned.
452
+ */
453
+ public function registerExceptionHandler()
454
+ {
455
+ return set_exception_handler(array($this, 'exceptionHandler'));
456
+ }
457
+
458
+ /**
459
+ * FirePHP's exception handler
460
+ *
461
+ * Logs all exceptions to your firebug console and then stops the script.
462
+ *
463
+ * @param Exception $exception
464
+ * @throws Exception
465
+ */
466
+ function exceptionHandler($exception)
467
+ {
468
+ $this->inExceptionHandler = true;
469
+
470
+ header('HTTP/1.1 500 Internal Server Error');
471
+
472
+ try {
473
+ $this->fb($exception);
474
+ } catch (Exception $e) {
475
+ echo 'We had an exception: ' . $e;
476
+ }
477
+
478
+ $this->inExceptionHandler = false;
479
+ }
480
+
481
+ /**
482
+ * Register FirePHP driver as your assert callback
483
+ *
484
+ * @param boolean $convertAssertionErrorsToExceptions
485
+ * @param boolean $throwAssertionExceptions
486
+ * @return mixed Returns the original setting or FALSE on errors
487
+ */
488
+ public function registerAssertionHandler($convertAssertionErrorsToExceptions = true, $throwAssertionExceptions = false)
489
+ {
490
+ $this->convertAssertionErrorsToExceptions = $convertAssertionErrorsToExceptions;
491
+ $this->throwAssertionExceptions = $throwAssertionExceptions;
492
+
493
+ if ($throwAssertionExceptions && !$convertAssertionErrorsToExceptions) {
494
+ throw $this->newException('Cannot throw assertion exceptions as assertion errors are not being converted to exceptions!');
495
+ }
496
+
497
+ return assert_options(ASSERT_CALLBACK, array($this, 'assertionHandler'));
498
+ }
499
+
500
+ /**
501
+ * FirePHP's assertion handler
502
+ *
503
+ * Logs all assertions to your firebug console and then stops the script.
504
+ *
505
+ * @param string $file File source of assertion
506
+ * @param integer $line Line source of assertion
507
+ * @param mixed $code Assertion code
508
+ */
509
+ public function assertionHandler($file, $line, $code)
510
+ {
511
+ if ($this->convertAssertionErrorsToExceptions) {
512
+
513
+ $exception = new ErrorException('Assertion Failed - Code[ ' . $code . ' ]', 0, null, $file, $line);
514
+
515
+ if ($this->throwAssertionExceptions) {
516
+ throw $exception;
517
+ } else {
518
+ $this->fb($exception);
519
+ }
520
+
521
+ } else {
522
+ $this->fb($code, 'Assertion Failed', FirePHP::ERROR, array('File' => $file, 'Line' => $line));
523
+ }
524
+ }
525
+
526
+ /**
527
+ * Start a group for following messages.
528
+ *
529
+ * Options:
530
+ * Collapsed: [true|false]
531
+ * Color: [#RRGGBB|ColorName]
532
+ *
533
+ * @param string $name
534
+ * @param array $options OPTIONAL Instructions on how to log the group
535
+ * @return true
536
+ * @throws Exception
537
+ */
538
+ public function group($name, $options = null)
539
+ {
540
+
541
+ if (!$name) {
542
+ throw $this->newException('You must specify a label for the group!');
543
+ }
544
+
545
+ if ($options) {
546
+ if (!is_array($options)) {
547
+ throw $this->newException('Options must be defined as an array!');
548
+ }
549
+ if (array_key_exists('Collapsed', $options)) {
550
+ $options['Collapsed'] = ($options['Collapsed']) ? 'true' : 'false';
551
+ }
552
+ }
553
+
554
+ return $this->fb(null, $name, FirePHP::GROUP_START, $options);
555
+ }
556
+
557
+ /**
558
+ * Ends a group you have started before
559
+ *
560
+ * @return true
561
+ * @throws Exception
562
+ */
563
+ public function groupEnd()
564
+ {
565
+ return $this->fb(null, null, FirePHP::GROUP_END);
566
+ }
567
+
568
+ /**
569
+ * Log object with label to firebug console
570
+ *
571
+ * @see FirePHP::LOG
572
+ * @param mixes $object
573
+ * @param string $label
574
+ * @return true
575
+ * @throws Exception
576
+ */
577
+ public function log($object, $label = null, $options = array())
578
+ {
579
+ return $this->fb($object, $label, FirePHP::LOG, $options);
580
+ }
581
+
582
+ /**
583
+ * Log object with label to firebug console
584
+ *
585
+ * @see FirePHP::INFO
586
+ * @param mixes $object
587
+ * @param string $label
588
+ * @return true
589
+ * @throws Exception
590
+ */
591
+ public function info($object, $label = null, $options = array())
592
+ {
593
+ return $this->fb($object, $label, FirePHP::INFO, $options);
594
+ }
595
+
596
+ /**
597
+ * Log object with label to firebug console
598
+ *
599
+ * @see FirePHP::WARN
600
+ * @param mixes $object
601
+ * @param string $label
602
+ * @return true
603
+ * @throws Exception
604
+ */
605
+ public function warn($object, $label = null, $options = array())
606
+ {
607
+ return $this->fb($object, $label, FirePHP::WARN, $options);
608
+ }
609
+
610
+ /**
611
+ * Log object with label to firebug console
612
+ *
613
+ * @see FirePHP::ERROR
614
+ * @param mixes $object
615
+ * @param string $label
616
+ * @return true
617
+ * @throws Exception
618
+ */
619
+ public function error($object, $label = null, $options = array())
620
+ {
621
+ return $this->fb($object, $label, FirePHP::ERROR, $options);
622
+ }
623
+
624
+ /**
625
+ * Dumps key and variable to firebug server panel
626
+ *
627
+ * @see FirePHP::DUMP
628
+ * @param string $key
629
+ * @param mixed $variable
630
+ * @return true
631
+ * @throws Exception
632
+ */
633
+ public function dump($key, $variable, $options = array())
634
+ {
635
+ if (!is_string($key)) {
636
+ throw $this->newException('Key passed to dump() is not a string');
637
+ }
638
+ if (strlen($key) > 100) {
639
+ throw $this->newException('Key passed to dump() is longer than 100 characters');
640
+ }
641
+ if (!preg_match_all('/^[a-zA-Z0-9-_\.:]*$/', $key, $m)) {
642
+ throw $this->newException('Key passed to dump() contains invalid characters [a-zA-Z0-9-_\.:]');
643
+ }
644
+ return $this->fb($variable, $key, FirePHP::DUMP, $options);
645
+ }
646
+
647
+ /**
648
+ * Log a trace in the firebug console
649
+ *
650
+ * @see FirePHP::TRACE
651
+ * @param string $label
652
+ * @return true
653
+ * @throws Exception
654
+ */
655
+ public function trace($label)
656
+ {
657
+ return $this->fb($label, FirePHP::TRACE);
658
+ }
659
+
660
+ /**
661
+ * Log a table in the firebug console
662
+ *
663
+ * @see FirePHP::TABLE
664
+ * @param string $label
665
+ * @param string $table
666
+ * @return true
667
+ * @throws Exception
668
+ */
669
+ public function table($label, $table, $options = array())
670
+ {
671
+ return $this->fb($table, $label, FirePHP::TABLE, $options);
672
+ }
673
+
674
+ /**
675
+ * Insight API wrapper
676
+ *
677
+ * @see Insight_Helper::to()
678
+ */
679
+ public static function to()
680
+ {
681
+ $instance = self::getInstance();
682
+ if (!method_exists($instance, '_to')) {
683
+ throw new Exception('FirePHP::to() implementation not loaded');
684
+ }
685
+ $args = func_get_args();
686
+ return call_user_func_array(array($instance, '_to'), $args);
687
+ }
688
+
689
+ /**
690
+ * Insight API wrapper
691
+ *
692
+ * @see Insight_Helper::plugin()
693
+ */
694
+ public static function plugin()
695
+ {
696
+ $instance = self::getInstance();
697
+ if (!method_exists($instance, '_plugin')) {
698
+ throw new Exception('FirePHP::plugin() implementation not loaded');
699
+ }
700
+ $args = func_get_args();
701
+ return call_user_func_array(array($instance, '_plugin'), $args);
702
+ }
703
+
704
+ /**
705
+ * Check if FirePHP is installed on client
706
+ *
707
+ * @return boolean
708
+ */
709
+ public function detectClientExtension()
710
+ {
711
+ // Check if FirePHP is installed on client via User-Agent header
712
+ if (@preg_match_all('/\sFirePHP\/([\.\d]*)\s?/si', $this->getUserAgent(), $m) &&
713
+ version_compare($m[1][0], '0.0.6', '>=')) {
714
+ return true;
715
+ } else
716
+ // Check if FirePHP is installed on client via X-FirePHP-Version header
717
+ if (@preg_match_all('/^([\.\d]*)$/si', $this->getRequestHeader('X-FirePHP-Version'), $m) &&
718
+ version_compare($m[1][0], '0.0.6', '>=')) {
719
+ return true;
720
+ }
721
+ return false;
722
+ }
723
+
724
+ /**
725
+ * Log varible to Firebug
726
+ *
727
+ * @see http://www.firephp.org/Wiki/Reference/Fb
728
+ * @param mixed $object The variable to be logged
729
+ * @return boolean Return TRUE if message was added to headers, FALSE otherwise
730
+ * @throws Exception
731
+ */
732
+ public function fb($object)
733
+ {
734
+ if ($this instanceof FirePHP_Insight && method_exists($this, '_logUpgradeClientMessage')) {
735
+ if (!FirePHP_Insight::$upgradeClientMessageLogged) { // avoid infinite recursion as _logUpgradeClientMessage() logs a message
736
+ $this->_logUpgradeClientMessage();
737
+ }
738
+ }
739
+
740
+ static $insightGroupStack = array();
741
+
742
+ if (!$this->getEnabled()) {
743
+ return false;
744
+ }
745
+
746
+ if ($this->headersSent($filename, $linenum)) {
747
+ // If we are logging from within the exception handler we cannot throw another exception
748
+ if ($this->inExceptionHandler) {
749
+ // Simply echo the error out to the page
750
+ echo '<div style="border: 2px solid red; font-family: Arial; font-size: 12px; background-color: lightgray; padding: 5px;"><span style="color: red; font-weight: bold;">FirePHP ERROR:</span> Headers already sent in <b>' . $filename . '</b> on line <b>' . $linenum . '</b>. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.</div>';
751
+ } else {
752
+ throw $this->newException('Headers already sent in ' . $filename . ' on line ' . $linenum . '. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.');
753
+ }
754
+ }
755
+
756
+ $type = null;
757
+ $label = null;
758
+ $options = array();
759
+
760
+ if (func_num_args() == 1) {
761
+ } else if (func_num_args() == 2) {
762
+ switch (func_get_arg(1)) {
763
+ case self::LOG:
764
+ case self::INFO:
765
+ case self::WARN:
766
+ case self::ERROR:
767
+ case self::DUMP:
768
+ case self::TRACE:
769
+ case self::EXCEPTION:
770
+ case self::TABLE:
771
+ case self::GROUP_START:
772
+ case self::GROUP_END:
773
+ $type = func_get_arg(1);
774
+ break;
775
+ default:
776
+ $label = func_get_arg(1);
777
+ break;
778
+ }
779
+ } else if (func_num_args() == 3) {
780
+ $type = func_get_arg(2);
781
+ $label = func_get_arg(1);
782
+ } else if (func_num_args() == 4) {
783
+ $type = func_get_arg(2);
784
+ $label = func_get_arg(1);
785
+ $options = func_get_arg(3);
786
+ } else {
787
+ throw $this->newException('Wrong number of arguments to fb() function!');
788
+ }
789
+
790
+ if ($this->logToInsightConsole !== null && (get_class($this) == 'FirePHP_Insight' || is_subclass_of($this, 'FirePHP_Insight'))) {
791
+ $trace = debug_backtrace();
792
+ if (!$trace) return false;
793
+ for ($i = 0; $i < sizeof($trace); $i++) {
794
+ if (isset($trace[$i]['class'])) {
795
+ if ($trace[$i]['class'] == 'FirePHP' || $trace[$i]['class'] == 'FB') {
796
+ continue;
797
+ }
798
+ }
799
+ if (isset($trace[$i]['file'])) {
800
+ $path = $this->_standardizePath($trace[$i]['file']);
801
+ if (substr($path, -18, 18) == 'FirePHPCore/fb.php' || substr($path, -29, 29) == 'FirePHPCore/FirePHP.class.php') {
802
+ continue;
803
+ }
804
+ }
805
+ if (isset($trace[$i]['function']) && $trace[$i]['function'] == 'fb' &&
806
+ isset($trace[$i - 1]['file']) && substr($this->_standardizePath($trace[$i - 1]['file']), -18, 18) == 'FirePHPCore/fb.php') {
807
+ continue;
808
+ }
809
+ if (isset($trace[$i]['class']) && $trace[$i]['class'] == 'FB' &&
810
+ isset($trace[$i - 1]['file']) && substr($this->_standardizePath($trace[$i - 1]['file']), -18, 18) == 'FirePHPCore/fb.php') {
811
+ continue;
812
+ }
813
+ break;
814
+ }
815
+ // adjust trace offset
816
+ $msg = $this->logToInsightConsole->option('encoder.trace.offsetAdjustment', $i);
817
+
818
+ if ($object instanceof Exception) {
819
+ $type = self::EXCEPTION;
820
+ }
821
+ if ($label && $type != self::TABLE && $type != self::GROUP_START) {
822
+ $msg = $msg->label($label);
823
+ }
824
+ switch ($type) {
825
+ case self::DUMP:
826
+ case self::LOG:
827
+ return $msg->log($object);
828
+ case self::INFO:
829
+ return $msg->info($object);
830
+ case self::WARN:
831
+ return $msg->warn($object);
832
+ case self::ERROR:
833
+ return $msg->error($object);
834
+ case self::TRACE:
835
+ return $msg->trace($object);
836
+ case self::EXCEPTION:
837
+ return $this->plugin('error')->handleException($object, $msg);
838
+ case self::TABLE:
839
+ if (isset($object[0]) && !is_string($object[0]) && $label) {
840
+ $object = array($label, $object);
841
+ }
842
+ return $msg->table($object[0], array_slice($object[1], 1), $object[1][0]);
843
+ case self::GROUP_START:
844
+ $insightGroupStack[] = $msg->group(md5($label))->open();
845
+ return $msg->log($label);
846
+ case self::GROUP_END:
847
+ if (count($insightGroupStack) == 0) {
848
+ throw new Error('Too many groupEnd() as opposed to group() calls!');
849
+ }
850
+ $group = array_pop($insightGroupStack);
851
+ return $group->close();
852
+ default:
853
+ return $msg->log($object);
854
+ }
855
+ }
856
+
857
+ if (!$this->detectClientExtension()) {
858
+ return false;
859
+ }
860
+
861
+ $meta = array();
862
+ $skipFinalObjectEncode = false;
863
+
864
+ if ($object instanceof Exception) {
865
+
866
+ $meta['file'] = $this->_escapeTraceFile($object->getFile());
867
+ $meta['line'] = $object->getLine();
868
+
869
+ $trace = $object->getTrace();
870
+ if ($object instanceof ErrorException
871
+ && isset($trace[0]['function'])
872
+ && $trace[0]['function'] == 'errorHandler'
873
+ && isset($trace[0]['class'])
874
+ && $trace[0]['class'] == 'FirePHP') {
875
+
876
+ $severity = false;
877
+ switch ($object->getSeverity()) {
878
+ case E_WARNING:
879
+ $severity = 'E_WARNING';
880
+ break;
881
+
882
+ case E_NOTICE:
883
+ $severity = 'E_NOTICE';
884
+ break;
885
+
886
+ case E_USER_ERROR:
887
+ $severity = 'E_USER_ERROR';
888
+ break;
889
+
890
+ case E_USER_WARNING:
891
+ $severity = 'E_USER_WARNING';
892
+ break;
893
+
894
+ case E_USER_NOTICE:
895
+ $severity = 'E_USER_NOTICE';
896
+ break;
897
+
898
+ case E_STRICT:
899
+ $severity = 'E_STRICT';
900
+ break;
901
+
902
+ case E_RECOVERABLE_ERROR:
903
+ $severity = 'E_RECOVERABLE_ERROR';
904
+ break;
905
+
906
+ case E_DEPRECATED:
907
+ $severity = 'E_DEPRECATED';
908
+ break;
909
+
910
+ case E_USER_DEPRECATED:
911
+ $severity = 'E_USER_DEPRECATED';
912
+ break;
913
+ }
914
+
915
+ $object = array('Class' => get_class($object),
916
+ 'Message' => $severity . ': ' . $object->getMessage(),
917
+ 'File' => $this->_escapeTraceFile($object->getFile()),
918
+ 'Line' => $object->getLine(),
919
+ 'Type' => 'trigger',
920
+ 'Trace' => $this->_escapeTrace(array_splice($trace, 2)));
921
+ $skipFinalObjectEncode = true;
922
+ } else {
923
+ $object = array('Class' => get_class($object),
924
+ 'Message' => $object->getMessage(),
925
+ 'File' => $this->_escapeTraceFile($object->getFile()),
926
+ 'Line' => $object->getLine(),
927
+ 'Type' => 'throw',
928
+ 'Trace' => $this->_escapeTrace($trace));
929
+ $skipFinalObjectEncode = true;
930
+ }
931
+ $type = self::EXCEPTION;
932
+
933
+ } else if ($type == self::TRACE) {
934
+
935
+ $trace = debug_backtrace();
936
+ if (!$trace) return false;
937
+ for ($i = 0; $i < sizeof($trace); $i++) {
938
+
939
+ if (isset($trace[$i]['class'])
940
+ && isset($trace[$i]['file'])
941
+ && ($trace[$i]['class'] == 'FirePHP'
942
+ || $trace[$i]['class'] == 'FB')
943
+ && (substr($this->_standardizePath($trace[$i]['file']), -18, 18) == 'FirePHPCore/fb.php'
944
+ || substr($this->_standardizePath($trace[$i]['file']), -29, 29) == 'FirePHPCore/FirePHP.class.php')) {
945
+ /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */
946
+ } else
947
+ if (isset($trace[$i]['class'])
948
+ && isset($trace[$i+1]['file'])
949
+ && $trace[$i]['class'] == 'FirePHP'
950
+ && substr($this->_standardizePath($trace[$i + 1]['file']), -18, 18) == 'FirePHPCore/fb.php') {
951
+ /* Skip fb() */
952
+ } else
953
+ if ($trace[$i]['function'] == 'fb'
954
+ || $trace[$i]['function'] == 'trace'
955
+ || $trace[$i]['function'] == 'send') {
956
+
957
+ $object = array('Class' => isset($trace[$i]['class']) ? $trace[$i]['class'] : '',
958
+ 'Type' => isset($trace[$i]['type']) ? $trace[$i]['type'] : '',
959
+ 'Function' => isset($trace[$i]['function']) ? $trace[$i]['function'] : '',
960
+ 'Message' => $trace[$i]['args'][0],
961
+ 'File' => isset($trace[$i]['file']) ? $this->_escapeTraceFile($trace[$i]['file']) : '',
962
+ 'Line' => isset($trace[$i]['line']) ? $trace[$i]['line'] : '',
963
+ 'Args' => isset($trace[$i]['args']) ? $this->encodeObject($trace[$i]['args']) : '',
964
+ 'Trace' => $this->_escapeTrace(array_splice($trace, $i + 1)));
965
+
966
+ $skipFinalObjectEncode = true;
967
+ $meta['file'] = isset($trace[$i]['file']) ? $this->_escapeTraceFile($trace[$i]['file']) : '';
968
+ $meta['line'] = isset($trace[$i]['line']) ? $trace[$i]['line'] : '';
969
+ break;
970
+ }
971
+ }
972
+
973
+ } else
974
+ if ($type == self::TABLE) {
975
+
976
+ if (isset($object[0]) && is_string($object[0])) {
977
+ $object[1] = $this->encodeTable($object[1]);
978
+ } else {
979
+ $object = $this->encodeTable($object);
980
+ }
981
+
982
+ $skipFinalObjectEncode = true;
983
+
984
+ } else if ($type == self::GROUP_START) {
985
+
986
+ if (!$label) {
987
+ throw $this->newException('You must specify a label for the group!');
988
+ }
989
+
990
+ } else {
991
+ if ($type === null) {
992
+ $type = self::LOG;
993
+ }
994
+ }
995
+
996
+ if ($this->options['includeLineNumbers']) {
997
+ if (!isset($meta['file']) || !isset($meta['line'])) {
998
+
999
+ $trace = debug_backtrace();
1000
+ for ($i = 0; $trace && $i < sizeof($trace); $i++) {
1001
+
1002
+ if (isset($trace[$i]['class'])
1003
+ && isset($trace[$i]['file'])
1004
+ && ($trace[$i]['class'] == 'FirePHP'
1005
+ || $trace[$i]['class'] == 'FB')
1006
+ && (substr($this->_standardizePath($trace[$i]['file']), -18, 18) == 'FirePHPCore/fb.php'
1007
+ || substr($this->_standardizePath($trace[$i]['file']), -29, 29) == 'FirePHPCore/FirePHP.class.php')) {
1008
+ /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */
1009
+ } else
1010
+ if (isset($trace[$i]['class'])
1011
+ && isset($trace[$i + 1]['file'])
1012
+ && $trace[$i]['class'] == 'FirePHP'
1013
+ && substr($this->_standardizePath($trace[$i + 1]['file']), -18, 18) == 'FirePHPCore/fb.php') {
1014
+ /* Skip fb() */
1015
+ } else
1016
+ if (isset($trace[$i]['file'])
1017
+ && substr($this->_standardizePath($trace[$i]['file']), -18, 18) == 'FirePHPCore/fb.php') {
1018
+ /* Skip FB::fb() */
1019
+ } else {
1020
+ $meta['file'] = isset($trace[$i]['file']) ? $this->_escapeTraceFile($trace[$i]['file']) : '';
1021
+ $meta['line'] = isset($trace[$i]['line']) ? $trace[$i]['line'] : '';
1022
+ break;
1023
+ }
1024
+ }
1025
+ }
1026
+ } else {
1027
+ unset($meta['file']);
1028
+ unset($meta['line']);
1029
+ }
1030
+
1031
+ $this->setHeader('X-Wf-Protocol-1', 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2');
1032
+ $this->setHeader('X-Wf-1-Plugin-1', 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/' . self::VERSION);
1033
+
1034
+ $structureIndex = 1;
1035
+ if ($type == self::DUMP) {
1036
+ $structureIndex = 2;
1037
+ $this->setHeader('X-Wf-1-Structure-2', 'http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1');
1038
+ } else {
1039
+ $this->setHeader('X-Wf-1-Structure-1', 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1');
1040
+ }
1041
+
1042
+ if ($type == self::DUMP) {
1043
+ $msg = '{"' . $label . '":' . $this->jsonEncode($object, $skipFinalObjectEncode) . '}';
1044
+ } else {
1045
+ $msgMeta = $options;
1046
+ $msgMeta['Type'] = $type;
1047
+ if ($label !== null) {
1048
+ $msgMeta['Label'] = $label;
1049
+ }
1050
+ if (isset($meta['file']) && !isset($msgMeta['File'])) {
1051
+ $msgMeta['File'] = $meta['file'];
1052
+ }
1053
+ if (isset($meta['line']) && !isset($msgMeta['Line'])) {
1054
+ $msgMeta['Line'] = $meta['line'];
1055
+ }
1056
+ $msg = '[' . $this->jsonEncode($msgMeta) . ',' . $this->jsonEncode($object, $skipFinalObjectEncode) . ']';
1057
+ }
1058
+
1059
+ $parts = explode("\n", chunk_split($msg, 5000, "\n"));
1060
+
1061
+ for ($i = 0; $i < count($parts); $i++) {
1062
+
1063
+ $part = $parts[$i];
1064
+ if ($part) {
1065
+
1066
+ if (count($parts) > 2) {
1067
+ // Message needs to be split into multiple parts
1068
+ $this->setHeader('X-Wf-1-' . $structureIndex . '-' . '1-' . $this->messageIndex,
1069
+ (($i == 0) ? strlen($msg) : '')
1070
+ . '|' . $part . '|'
1071
+ . (($i < count($parts) - 2) ? '\\' : ''));
1072
+ } else {
1073
+ $this->setHeader('X-Wf-1-' . $structureIndex . '-' . '1-' . $this->messageIndex,
1074
+ strlen($part) . '|' . $part . '|');
1075
+ }
1076
+
1077
+ $this->messageIndex++;
1078
+
1079
+ if ($this->messageIndex > 99999) {
1080
+ throw $this->newException('Maximum number (99,999) of messages reached!');
1081
+ }
1082
+ }
1083
+ }
1084
+
1085
+ $this->setHeader('X-Wf-1-Index', $this->messageIndex - 1);
1086
+
1087
+ return true;
1088
+ }
1089
+
1090
+ /**
1091
+ * Standardizes path for windows systems.
1092
+ *
1093
+ * @param string $path
1094
+ * @return string
1095
+ */
1096
+ protected function _standardizePath($path)
1097
+ {
1098
+ return preg_replace('/\\\\+/', '/', $path);
1099
+ }
1100
+
1101
+ /**
1102
+ * Escape trace path for windows systems
1103
+ *
1104
+ * @param array $trace
1105
+ * @return array
1106
+ */
1107
+ protected function _escapeTrace($trace)
1108
+ {
1109
+ if (!$trace) return $trace;
1110
+ for ($i = 0; $i < sizeof($trace); $i++) {
1111
+ if (isset($trace[$i]['file'])) {
1112
+ $trace[$i]['file'] = $this->_escapeTraceFile($trace[$i]['file']);
1113
+ }
1114
+ if (isset($trace[$i]['args'])) {
1115
+ $trace[$i]['args'] = $this->encodeObject($trace[$i]['args']);
1116
+ }
1117
+ }
1118
+ return $trace;
1119
+ }
1120
+
1121
+ /**
1122
+ * Escape file information of trace for windows systems
1123
+ *
1124
+ * @param string $file
1125
+ * @return string
1126
+ */
1127
+ protected function _escapeTraceFile($file)
1128
+ {
1129
+ /* Check if we have a windows filepath */
1130
+ if (strpos($file, '\\')) {
1131
+ /* First strip down to single \ */
1132
+
1133
+ $file = preg_replace('/\\\\+/', '\\', $file);
1134
+
1135
+ return $file;
1136
+ }
1137
+ return $file;
1138
+ }
1139
+
1140
+ /**
1141
+ * Check if headers have already been sent
1142
+ *
1143
+ * @param string $filename
1144
+ * @param integer $linenum
1145
+ */
1146
+ protected function headersSent(&$filename, &$linenum)
1147
+ {
1148
+ return headers_sent($filename, $linenum);
1149
+ }
1150
+
1151
+ /**
1152
+ * Send header
1153
+ *
1154
+ * @param string $name
1155
+ * @param string $value
1156
+ */
1157
+ protected function setHeader($name, $value)
1158
+ {
1159
+ return header($name . ': ' . $value);
1160
+ }
1161
+
1162
+ /**
1163
+ * Get user agent
1164
+ *
1165
+ * @return string|false
1166
+ */
1167
+ protected function getUserAgent()
1168
+ {
1169
+ if (!isset($_SERVER['HTTP_USER_AGENT'])) return false;
1170
+ return $_SERVER['HTTP_USER_AGENT'];
1171
+ }
1172
+
1173
+ /**
1174
+ * Get all request headers
1175
+ *
1176
+ * @return array
1177
+ */
1178
+ public static function getAllRequestHeaders()
1179
+ {
1180
+ static $_cachedHeaders = false;
1181
+ if ($_cachedHeaders !== false) {
1182
+ return $_cachedHeaders;
1183
+ }
1184
+ $headers = array();
1185
+ if (function_exists('getallheaders')) {
1186
+ foreach (getallheaders() as $name => $value) {
1187
+ $headers[strtolower($name)] = $value;
1188
+ }
1189
+ } else {
1190
+ foreach ($_SERVER as $name => $value) {
1191
+ if (substr($name, 0, 5) == 'HTTP_') {
1192
+ $headers[strtolower(str_replace(' ', '-', str_replace('_', ' ', substr($name, 5))))] = $value;
1193
+ }
1194
+ }
1195
+ }
1196
+ return $_cachedHeaders = $headers;
1197
+ }
1198
+
1199
+ /**
1200
+ * Get a request header
1201
+ *
1202
+ * @return string|false
1203
+ */
1204
+ protected function getRequestHeader($name)
1205
+ {
1206
+ $headers = self::getAllRequestHeaders();
1207
+ if (isset($headers[strtolower($name)])) {
1208
+ return $headers[strtolower($name)];
1209
+ }
1210
+ return false;
1211
+ }
1212
+
1213
+ /**
1214
+ * Returns a new exception
1215
+ *
1216
+ * @param string $message
1217
+ * @return Exception
1218
+ */
1219
+ protected function newException($message)
1220
+ {
1221
+ return new Exception($message);
1222
+ }
1223
+
1224
+ /**
1225
+ * Encode an object into a JSON string
1226
+ *
1227
+ * Uses PHP's jeson_encode() if available
1228
+ *
1229
+ * @param object $object The object to be encoded
1230
+ * @param boolean $skipObjectEncode
1231
+ * @return string The JSON string
1232
+ */
1233
+ public function jsonEncode($object, $skipObjectEncode = false)
1234
+ {
1235
+ if (!$skipObjectEncode) {
1236
+ $object = $this->encodeObject($object);
1237
+ }
1238
+
1239
+ if (function_exists('json_encode')
1240
+ && $this->options['useNativeJsonEncode'] != false) {
1241
+
1242
+ return json_encode($object);
1243
+ } else {
1244
+ return $this->json_encode($object);
1245
+ }
1246
+ }
1247
+
1248
+ /**
1249
+ * Encodes a table by encoding each row and column with encodeObject()
1250
+ *
1251
+ * @param array $table The table to be encoded
1252
+ * @return array
1253
+ */
1254
+ protected function encodeTable($table)
1255
+ {
1256
+ if (!$table) return $table;
1257
+
1258
+ $newTable = array();
1259
+ foreach ($table as $row) {
1260
+
1261
+ if (is_array($row)) {
1262
+ $newRow = array();
1263
+
1264
+ foreach ($row as $item) {
1265
+ $newRow[] = $this->encodeObject($item);
1266
+ }
1267
+
1268
+ $newTable[] = $newRow;
1269
+ }
1270
+ }
1271
+
1272
+ return $newTable;
1273
+ }
1274
+
1275
+ /**
1276
+ * Encodes an object including members with
1277
+ * protected and private visibility
1278
+ *
1279
+ * @param object $object The object to be encoded
1280
+ * @param integer $Depth The current traversal depth
1281
+ * @return array All members of the object
1282
+ */
1283
+ protected function encodeObject($object, $objectDepth = 1, $arrayDepth = 1, $maxDepth = 1)
1284
+ {
1285
+ if ($maxDepth > $this->options['maxDepth']) {
1286
+ return '** Max Depth (' . $this->options['maxDepth'] . ') **';
1287
+ }
1288
+
1289
+ $return = array();
1290
+
1291
+ if (is_resource($object)) {
1292
+
1293
+ return '** ' . (string) $object . ' **';
1294
+
1295
+ } else if (is_object($object)) {
1296
+
1297
+ if ($objectDepth > $this->options['maxObjectDepth']) {
1298
+ return '** Max Object Depth (' . $this->options['maxObjectDepth'] . ') **';
1299
+ }
1300
+
1301
+ foreach ($this->objectStack as $refVal) {
1302
+ if ($refVal === $object) {
1303
+ return '** Recursion (' . get_class($object) . ') **';
1304
+ }
1305
+ }
1306
+ array_push($this->objectStack, $object);
1307
+
1308
+ $return['__className'] = $class = get_class($object);
1309
+ $classLower = strtolower($class);
1310
+
1311
+ $reflectionClass = new ReflectionClass($class);
1312
+ $properties = array();
1313
+ foreach ($reflectionClass->getProperties() as $property) {
1314
+ $properties[$property->getName()] = $property;
1315
+ }
1316
+
1317
+ $members = (array)$object;
1318
+
1319
+ foreach ($properties as $plainName => $property) {
1320
+
1321
+ $name = $rawName = $plainName;
1322
+ if ($property->isStatic()) {
1323
+ $name = 'static:' . $name;
1324
+ }
1325
+ if ($property->isPublic()) {
1326
+ $name = 'public:' . $name;
1327
+ } else if ($property->isPrivate()) {
1328
+ $name = 'private:' . $name;
1329
+ $rawName = "\0" . $class . "\0" . $rawName;
1330
+ } else if ($property->isProtected()) {
1331
+ $name = 'protected:' . $name;
1332
+ $rawName = "\0" . '*' . "\0" . $rawName;
1333
+ }
1334
+
1335
+ if (!(isset($this->objectFilters[$classLower])
1336
+ && is_array($this->objectFilters[$classLower])
1337
+ && in_array($plainName, $this->objectFilters[$classLower]))) {
1338
+
1339
+ if (array_key_exists($rawName, $members) && !$property->isStatic()) {
1340
+ $return[$name] = $this->encodeObject($members[$rawName], $objectDepth + 1, 1, $maxDepth + 1);
1341
+ } else {
1342
+ if (method_exists($property, 'setAccessible')) {
1343
+ $property->setAccessible(true);
1344
+ $return[$name] = $this->encodeObject($property->getValue($object), $objectDepth + 1, 1, $maxDepth + 1);
1345
+ } else
1346
+ if ($property->isPublic()) {
1347
+ $return[$name] = $this->encodeObject($property->getValue($object), $objectDepth + 1, 1, $maxDepth + 1);
1348
+ } else {
1349
+ $return[$name] = '** Need PHP 5.3 to get value **';
1350
+ }
1351
+ }
1352
+ } else {
1353
+ $return[$name] = '** Excluded by Filter **';
1354
+ }
1355
+ }
1356
+
1357
+ // Include all members that are not defined in the class
1358
+ // but exist in the object
1359
+ foreach ($members as $rawName => $value) {
1360
+
1361
+ $name = $rawName;
1362
+
1363
+ if ($name{0} == "\0") {
1364
+ $parts = explode("\0", $name);
1365
+ $name = $parts[2];
1366
+ }
1367
+
1368
+ $plainName = $name;
1369
+
1370
+ if (!isset($properties[$name])) {
1371
+ $name = 'undeclared:' . $name;
1372
+
1373
+ if (!(isset($this->objectFilters[$classLower])
1374
+ && is_array($this->objectFilters[$classLower])
1375
+ && in_array($plainName, $this->objectFilters[$classLower]))) {
1376
+
1377
+ $return[$name] = $this->encodeObject($value, $objectDepth + 1, 1, $maxDepth + 1);
1378
+ } else {
1379
+ $return[$name] = '** Excluded by Filter **';
1380
+ }
1381
+ }
1382
+ }
1383
+
1384
+ array_pop($this->objectStack);
1385
+
1386
+ } elseif (is_array($object)) {
1387
+
1388
+ if ($arrayDepth > $this->options['maxArrayDepth']) {
1389
+ return '** Max Array Depth (' . $this->options['maxArrayDepth'] . ') **';
1390
+ }
1391
+
1392
+ foreach ($object as $key => $val) {
1393
+
1394
+ // Encoding the $GLOBALS PHP array causes an infinite loop
1395
+ // if the recursion is not reset here as it contains
1396
+ // a reference to itself. This is the only way I have come up
1397
+ // with to stop infinite recursion in this case.
1398
+ if ($key == 'GLOBALS'
1399
+ && is_array($val)
1400
+ && array_key_exists('GLOBALS', $val)) {
1401
+ $val['GLOBALS'] = '** Recursion (GLOBALS) **';
1402
+ }
1403
+
1404
+ if (!$this->is_utf8($key)) {
1405
+ $key = utf8_encode($key);
1406
+ }
1407
+
1408
+ $return[$key] = $this->encodeObject($val, 1, $arrayDepth + 1, $maxDepth + 1);
1409
+ }
1410
+ } else {
1411
+ if ($this->is_utf8($object)) {
1412
+ return $object;
1413
+ } else {
1414
+ return utf8_encode($object);
1415
+ }
1416
+ }
1417
+ return $return;
1418
+ }
1419
+
1420
+ /**
1421
+ * Returns true if $string is valid UTF-8 and false otherwise.
1422
+ *
1423
+ * @param mixed $str String to be tested
1424
+ * @return boolean
1425
+ */
1426
+ protected function is_utf8($str)
1427
+ {
1428
+ if (function_exists('mb_detect_encoding')) {
1429
+ return (
1430
+ mb_detect_encoding($str, 'UTF-8', true) == 'UTF-8' &&
1431
+ ($str === null || $this->jsonEncode($str, true) !== 'null')
1432
+ );
1433
+ }
1434
+ $c = 0;
1435
+ $b = 0;
1436
+ $bits = 0;
1437
+ $len = strlen($str);
1438
+ for ($i = 0; $i < $len; $i++) {
1439
+ $c = ord($str[$i]);
1440
+ if ($c > 128) {
1441
+ if (($c >= 254)) return false;
1442
+ elseif ($c >= 252) $bits = 6;
1443
+ elseif ($c >= 248) $bits = 5;
1444
+ elseif ($c >= 240) $bits = 4;
1445
+ elseif ($c >= 224) $bits = 3;
1446
+ elseif ($c >= 192) $bits = 2;
1447
+ else return false;
1448
+ if (($i + $bits) > $len) return false;
1449
+ while($bits > 1) {
1450
+ $i++;
1451
+ $b = ord($str[$i]);
1452
+ if ($b < 128 || $b > 191) return false;
1453
+ $bits--;
1454
+ }
1455
+ }
1456
+ }
1457
+ return ($str === null || $this->jsonEncode($str, true) !== 'null');
1458
+ }
1459
+
1460
+ /**
1461
+ * Converts to and from JSON format.
1462
+ *
1463
+ * JSON (JavaScript Object Notation) is a lightweight data-interchange
1464
+ * format. It is easy for humans to read and write. It is easy for machines
1465
+ * to parse and generate. It is based on a subset of the JavaScript
1466
+ * Programming Language, Standard ECMA-262 3rd Edition - December 1999.
1467
+ * This feature can also be found in Python. JSON is a text format that is
1468
+ * completely language independent but uses conventions that are familiar
1469
+ * to programmers of the C-family of languages, including C, C++, C#, Java,
1470
+ * JavaScript, Perl, TCL, and many others. These properties make JSON an
1471
+ * ideal data-interchange language.
1472
+ *
1473
+ * This package provides a simple encoder and decoder for JSON notation. It
1474
+ * is intended for use with client-side Javascript applications that make
1475
+ * use of HTTPRequest to perform server communication functions - data can
1476
+ * be encoded into JSON notation for use in a client-side javascript, or
1477
+ * decoded from incoming Javascript requests. JSON format is native to
1478
+ * Javascript, and can be directly eval()'ed with no further parsing
1479
+ * overhead
1480
+ *
1481
+ * All strings should be in ASCII or UTF-8 format!
1482
+ *
1483
+ * LICENSE: Redistribution and use in source and binary forms, with or
1484
+ * without modification, are permitted provided that the following
1485
+ * conditions are met: Redistributions of source code must retain the
1486
+ * above copyright notice, this list of conditions and the following
1487
+ * disclaimer. Redistributions in binary form must reproduce the above
1488
+ * copyright notice, this list of conditions and the following disclaimer
1489
+ * in the documentation and/or other materials provided with the
1490
+ * distribution.
1491
+ *
1492
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
1493
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
1494
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
1495
+ * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
1496
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
1497
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
1498
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
1499
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
1500
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
1501
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
1502
+ * DAMAGE.
1503
+ *
1504
+ * @category
1505
+ * @package Services_JSON
1506
+ * @author Michal Migurski <mike-json@teczno.com>
1507
+ * @author Matt Knapp <mdknapp[at]gmail[dot]com>
1508
+ * @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
1509
+ * @author Christoph Dorn <christoph@christophdorn.com>
1510
+ * @copyright 2005 Michal Migurski
1511
+ * @version CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $
1512
+ * @license http://www.opensource.org/licenses/bsd-license.php
1513
+ * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
1514
+ */
1515
+
1516
+
1517
+ /**
1518
+ * Keep a list of objects as we descend into the array so we can detect recursion.
1519
+ */
1520
+ private $json_objectStack = array();
1521
+
1522
+
1523
+ /**
1524
+ * convert a string from one UTF-8 char to one UTF-16 char
1525
+ *
1526
+ * Normally should be handled by mb_convert_encoding, but
1527
+ * provides a slower PHP-only method for installations
1528
+ * that lack the multibye string extension.
1529
+ *
1530
+ * @param string $utf8 UTF-8 character
1531
+ * @return string UTF-16 character
1532
+ * @access private
1533
+ */
1534
+ private function json_utf82utf16($utf8)
1535
+ {
1536
+ // oh please oh please oh please oh please oh please
1537
+ if (function_exists('mb_convert_encoding')) {
1538
+ return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
1539
+ }
1540
+
1541
+ switch (strlen($utf8)) {
1542
+ case 1:
1543
+ // this case should never be reached, because we are in ASCII range
1544
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1545
+ return $utf8;
1546
+
1547
+ case 2:
1548
+ // return a UTF-16 character from a 2-byte UTF-8 char
1549
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1550
+ return chr(0x07 & (ord($utf8{0}) >> 2))
1551
+ . chr((0xC0 & (ord($utf8{0}) << 6))
1552
+ | (0x3F & ord($utf8{1})));
1553
+
1554
+ case 3:
1555
+ // return a UTF-16 character from a 3-byte UTF-8 char
1556
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1557
+ return chr((0xF0 & (ord($utf8{0}) << 4))
1558
+ | (0x0F & (ord($utf8{1}) >> 2)))
1559
+ . chr((0xC0 & (ord($utf8{1}) << 6))
1560
+ | (0x7F & ord($utf8{2})));
1561
+ }
1562
+
1563
+ // ignoring UTF-32 for now, sorry
1564
+ return '';
1565
+ }
1566
+
1567
+ /**
1568
+ * encodes an arbitrary variable into JSON format
1569
+ *
1570
+ * @param mixed $var any number, boolean, string, array, or object to be encoded.
1571
+ * see argument 1 to Services_JSON() above for array-parsing behavior.
1572
+ * if var is a strng, note that encode() always expects it
1573
+ * to be in ASCII or UTF-8 format!
1574
+ *
1575
+ * @return mixed JSON string representation of input var or an error if a problem occurs
1576
+ * @access public
1577
+ */
1578
+ private function json_encode($var)
1579
+ {
1580
+ if (is_object($var)) {
1581
+ if (in_array($var, $this->json_objectStack)) {
1582
+ return '"** Recursion **"';
1583
+ }
1584
+ }
1585
+
1586
+ switch (gettype($var)) {
1587
+ case 'boolean':
1588
+ return $var ? 'true' : 'false';
1589
+
1590
+ case 'NULL':
1591
+ return 'null';
1592
+
1593
+ case 'integer':
1594
+ return (int) $var;
1595
+
1596
+ case 'double':
1597
+ case 'float':
1598
+ return (float) $var;
1599
+
1600
+ case 'string':
1601
+ // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
1602
+ $ascii = '';
1603
+ $strlen_var = strlen($var);
1604
+
1605
+ /*
1606
+ * Iterate over every character in the string,
1607
+ * escaping with a slash or encoding to UTF-8 where necessary
1608
+ */
1609
+ for ($c = 0; $c < $strlen_var; ++$c) {
1610
+
1611
+ $ord_var_c = ord($var{$c});
1612
+
1613
+ switch (true) {
1614
+ case $ord_var_c == 0x08:
1615
+ $ascii .= '\b';
1616
+ break;
1617
+ case $ord_var_c == 0x09:
1618
+ $ascii .= '\t';
1619
+ break;
1620
+ case $ord_var_c == 0x0A:
1621
+ $ascii .= '\n';
1622
+ break;
1623
+ case $ord_var_c == 0x0C:
1624
+ $ascii .= '\f';
1625
+ break;
1626
+ case $ord_var_c == 0x0D:
1627
+ $ascii .= '\r';
1628
+ break;
1629
+
1630
+ case $ord_var_c == 0x22:
1631
+ case $ord_var_c == 0x2F:
1632
+ case $ord_var_c == 0x5C:
1633
+ // double quote, slash, slosh
1634
+ $ascii .= '\\' . $var{$c};
1635
+ break;
1636
+
1637
+ case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
1638
+ // characters U-00000000 - U-0000007F (same as ASCII)
1639
+ $ascii .= $var{$c};
1640
+ break;
1641
+
1642
+ case (($ord_var_c & 0xE0) == 0xC0):
1643
+ // characters U-00000080 - U-000007FF, mask 110XXXXX
1644
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1645
+ $char = pack('C*', $ord_var_c, ord($var{$c + 1}));
1646
+ $c += 1;
1647
+ $utf16 = $this->json_utf82utf16($char);
1648
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
1649
+ break;
1650
+
1651
+ case (($ord_var_c & 0xF0) == 0xE0):
1652
+ // characters U-00000800 - U-0000FFFF, mask 1110XXXX
1653
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1654
+ $char = pack('C*', $ord_var_c,
1655
+ ord($var{$c + 1}),
1656
+ ord($var{$c + 2}));
1657
+ $c += 2;
1658
+ $utf16 = $this->json_utf82utf16($char);
1659
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
1660
+ break;
1661
+
1662
+ case (($ord_var_c & 0xF8) == 0xF0):
1663
+ // characters U-00010000 - U-001FFFFF, mask 11110XXX
1664
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1665
+ $char = pack('C*', $ord_var_c,
1666
+ ord($var{$c + 1}),
1667
+ ord($var{$c + 2}),
1668
+ ord($var{$c + 3}));
1669
+ $c += 3;
1670
+ $utf16 = $this->json_utf82utf16($char);
1671
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
1672
+ break;
1673
+
1674
+ case (($ord_var_c & 0xFC) == 0xF8):
1675
+ // characters U-00200000 - U-03FFFFFF, mask 111110XX
1676
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1677
+ $char = pack('C*', $ord_var_c,
1678
+ ord($var{$c + 1}),
1679
+ ord($var{$c + 2}),
1680
+ ord($var{$c + 3}),
1681
+ ord($var{$c + 4}));
1682
+ $c += 4;
1683
+ $utf16 = $this->json_utf82utf16($char);
1684
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
1685
+ break;
1686
+
1687
+ case (($ord_var_c & 0xFE) == 0xFC):
1688
+ // characters U-04000000 - U-7FFFFFFF, mask 1111110X
1689
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1690
+ $char = pack('C*', $ord_var_c,
1691
+ ord($var{$c + 1}),
1692
+ ord($var{$c + 2}),
1693
+ ord($var{$c + 3}),
1694
+ ord($var{$c + 4}),
1695
+ ord($var{$c + 5}));
1696
+ $c += 5;
1697
+ $utf16 = $this->json_utf82utf16($char);
1698
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
1699
+ break;
1700
+ }
1701
+ }
1702
+
1703
+ return '"' . $ascii . '"';
1704
+
1705
+ case 'array':
1706
+ /*
1707
+ * As per JSON spec if any array key is not an integer
1708
+ * we must treat the the whole array as an object. We
1709
+ * also try to catch a sparsely populated associative
1710
+ * array with numeric keys here because some JS engines
1711
+ * will create an array with empty indexes up to
1712
+ * max_index which can cause memory issues and because
1713
+ * the keys, which may be relevant, will be remapped
1714
+ * otherwise.
1715
+ *
1716
+ * As per the ECMA and JSON specification an object may
1717
+ * have any string as a property. Unfortunately due to
1718
+ * a hole in the ECMA specification if the key is a
1719
+ * ECMA reserved word or starts with a digit the
1720
+ * parameter is only accessible using ECMAScript's
1721
+ * bracket notation.
1722
+ */
1723
+
1724
+ // treat as a JSON object
1725
+ if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
1726
+
1727
+ $this->json_objectStack[] = $var;
1728
+
1729
+ $properties = array_map(array($this, 'json_name_value'),
1730
+ array_keys($var),
1731
+ array_values($var));
1732
+
1733
+ array_pop($this->json_objectStack);
1734
+
1735
+ foreach ($properties as $property) {
1736
+ if ($property instanceof Exception) {
1737
+ return $property;
1738
+ }
1739
+ }
1740
+
1741
+ return '{' . join(',', $properties) . '}';
1742
+ }
1743
+
1744
+ $this->json_objectStack[] = $var;
1745
+
1746
+ // treat it like a regular array
1747
+ $elements = array_map(array($this, 'json_encode'), $var);
1748
+
1749
+ array_pop($this->json_objectStack);
1750
+
1751
+ foreach ($elements as $element) {
1752
+ if ($element instanceof Exception) {
1753
+ return $element;
1754
+ }
1755
+ }
1756
+
1757
+ return '[' . join(',', $elements) . ']';
1758
+
1759
+ case 'object':
1760
+ $vars = self::encodeObject($var);
1761
+
1762
+ $this->json_objectStack[] = $var;
1763
+
1764
+ $properties = array_map(array($this, 'json_name_value'),
1765
+ array_keys($vars),
1766
+ array_values($vars));
1767
+
1768
+ array_pop($this->json_objectStack);
1769
+
1770
+ foreach ($properties as $property) {
1771
+ if ($property instanceof Exception) {
1772
+ return $property;
1773
+ }
1774
+ }
1775
+
1776
+ return '{' . join(',', $properties) . '}';
1777
+
1778
+ default:
1779
+ return null;
1780
+ }
1781
+ }
1782
+
1783
+ /**
1784
+ * array-walking function for use in generating JSON-formatted name-value pairs
1785
+ *
1786
+ * @param string $name name of key to use
1787
+ * @param mixed $value reference to an array element to be encoded
1788
+ *
1789
+ * @return string JSON-formatted name-value pair, like '"name":value'
1790
+ * @access private
1791
+ */
1792
+ private function json_name_value($name, $value)
1793
+ {
1794
+ // Encoding the $GLOBALS PHP array causes an infinite loop
1795
+ // if the recursion is not reset here as it contains
1796
+ // a reference to itself. This is the only way I have come up
1797
+ // with to stop infinite recursion in this case.
1798
+ if ($name == 'GLOBALS'
1799
+ && is_array($value)
1800
+ && array_key_exists('GLOBALS', $value)) {
1801
+ $value['GLOBALS'] = '** Recursion **';
1802
+ }
1803
+
1804
+ $encodedValue = $this->json_encode($value);
1805
+
1806
+ if ($encodedValue instanceof Exception) {
1807
+ return $encodedValue;
1808
+ }
1809
+
1810
+ return $this->json_encode(strval($name)) . ':' . $encodedValue;
1811
+ }
1812
+
1813
+ /**
1814
+ * @deprecated
1815
+ */
1816
+ public function setProcessorUrl($URL)
1817
+ {
1818
+ trigger_error('The FirePHP::setProcessorUrl() method is no longer supported', E_USER_DEPRECATED);
1819
+ }
1820
+
1821
+ /**
1822
+ * @deprecated
1823
+ */
1824
+ public function setRendererUrl($URL)
1825
+ {
1826
+ trigger_error('The FirePHP::setRendererUrl() method is no longer supported', E_USER_DEPRECATED);
1827
+ }
1828
+ }
includes/debug/browsers/api/firephp/lib/FirePHPCore/FirePHP.class.php4 ADDED
@@ -0,0 +1,1327 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Authors:
3
+ // - cadorn, Christoph Dorn <christoph@christophdorn.com>, Copyright 2007, New BSD License
4
+ // - qbbr, Michael Day <manveru.alma@gmail.com>, Copyright 2008, New BSD License
5
+ // - cadorn, Christoph Dorn <christoph@christophdorn.com>, Copyright 2011, MIT License
6
+
7
+ /**
8
+ * *** BEGIN LICENSE BLOCK *****
9
+ *
10
+ * [MIT License](http://www.opensource.org/licenses/mit-license.php)
11
+ *
12
+ * Copyright (c) 2007+ [Christoph Dorn](http://www.christophdorn.com/)
13
+ *
14
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
15
+ * of this software and associated documentation files (the "Software"), to deal
16
+ * in the Software without restriction, including without limitation the rights
17
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18
+ * copies of the Software, and to permit persons to whom the Software is
19
+ * furnished to do so, subject to the following conditions:
20
+ *
21
+ * The above copyright notice and this permission notice shall be included in
22
+ * all copies or substantial portions of the Software.
23
+ *
24
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
30
+ * THE SOFTWARE.
31
+ *
32
+ * ***** END LICENSE BLOCK *****
33
+ *
34
+ * This verion of FirePHPCore is for use with PHP4. If you do not require PHP4
35
+ * compatibility, it is suggested you use FirePHPCore.class.php instead.
36
+ *
37
+ * @copyright Copyright (C) 2007+ Christoph Dorn
38
+ * @author Christoph Dorn <christoph@christophdorn.com>
39
+ * @author Michael Day <manveru.alma@gmail.com>
40
+ * @license [MIT License](http://www.opensource.org/licenses/mit-license.php)
41
+ * @package FirePHPCore
42
+ */
43
+
44
+ /**
45
+ * FirePHP version
46
+ *
47
+ * @var string
48
+ */
49
+ define('FirePHP_VERSION', '0.3'); // @pinf replace '0.3' with '%%VERSION%%'
50
+
51
+ /**
52
+ * Firebug LOG level
53
+ *
54
+ * Logs a message to firebug console
55
+ *
56
+ * @var string
57
+ */
58
+ define('FirePHP_LOG', 'LOG');
59
+
60
+ /**
61
+ * Firebug INFO level
62
+ *
63
+ * Logs a message to firebug console and displays an info icon before the message
64
+ *
65
+ * @var string
66
+ */
67
+ define('FirePHP_INFO', 'INFO');
68
+
69
+ /**
70
+ * Firebug WARN level
71
+ *
72
+ * Logs a message to firebug console, displays a warning icon before the message and colors the line turquoise
73
+ *
74
+ * @var string
75
+ */
76
+ define('FirePHP_WARN', 'WARN');
77
+
78
+ /**
79
+ * Firebug ERROR level
80
+ *
81
+ * Logs a message to firebug console, displays an error icon before the message and colors the line yellow. Also increments the firebug error count.
82
+ *
83
+ * @var string
84
+ */
85
+ define('FirePHP_ERROR', 'ERROR');
86
+
87
+ /**
88
+ * Dumps a variable to firebug's server panel
89
+ *
90
+ * @var string
91
+ */
92
+ define('FirePHP_DUMP', 'DUMP');
93
+
94
+ /**
95
+ * Displays a stack trace in firebug console
96
+ *
97
+ * @var string
98
+ */
99
+ define('FirePHP_TRACE', 'TRACE');
100
+
101
+ /**
102
+ * Displays a table in firebug console
103
+ *
104
+ * @var string
105
+ */
106
+ define('FirePHP_TABLE', 'TABLE');
107
+
108
+ /**
109
+ * Starts a group in firebug console
110
+ *
111
+ * @var string
112
+ */
113
+ define('FirePHP_GROUP_START', 'GROUP_START');
114
+
115
+ /**
116
+ * Ends a group in firebug console
117
+ *
118
+ * @var string
119
+ */
120
+ define('FirePHP_GROUP_END', 'GROUP_END');
121
+
122
+ /**
123
+ * Sends the given data to the FirePHP Firefox Extension.
124
+ * The data can be displayed in the Firebug Console or in the
125
+ * "Server" request tab.
126
+ *
127
+ * For more information see: http://www.firephp.org/
128
+ *
129
+ * @copyright Copyright (C) 2007+ Christoph Dorn
130
+ * @author Christoph Dorn <christoph@christophdorn.com>
131
+ * @author Michael Day <manveru.alma@gmail.com>
132
+ * @license [MIT License](http://www.opensource.org/licenses/mit-license.php)
133
+ * @package FirePHPCore
134
+ */
135
+ class FirePHP {
136
+ /**
137
+ * Wildfire protocol message index
138
+ *
139
+ * @var int
140
+ */
141
+ var $messageIndex = 1;
142
+
143
+ /**
144
+ * Options for the library
145
+ *
146
+ * @var array
147
+ */
148
+ var $options = array('maxObjectDepth' => 5,
149
+ 'maxArrayDepth' => 5,
150
+ 'useNativeJsonEncode' => true,
151
+ 'includeLineNumbers' => true);
152
+
153
+ /**
154
+ * Filters used to exclude object members when encoding
155
+ *
156
+ * @var array
157
+ */
158
+ var $objectFilters = array();
159
+
160
+ /**
161
+ * A stack of objects used to detect recursion during object encoding
162
+ *
163
+ * @var object
164
+ */
165
+ var $objectStack = array();
166
+
167
+ /**
168
+ * Flag to enable/disable logging
169
+ *
170
+ * @var boolean
171
+ */
172
+ var $enabled = true;
173
+
174
+ /**
175
+ * The object constructor
176
+ */
177
+ function FirePHP() {
178
+ }
179
+
180
+
181
+ /**
182
+ * When the object gets serialized only include specific object members.
183
+ *
184
+ * @return array
185
+ */
186
+ function __sleep() {
187
+ return array('options','objectFilters','enabled');
188
+ }
189
+
190
+ /**
191
+ * Gets singleton instance of FirePHP
192
+ *
193
+ * @param boolean $AutoCreate
194
+ * @return FirePHP
195
+ */
196
+ function &getInstance($AutoCreate=false) {
197
+ global $FirePHP_Instance;
198
+
199
+ if($AutoCreate===true && !$FirePHP_Instance) {
200
+ $FirePHP_Instance = new FirePHP();
201
+ }
202
+
203
+ return $FirePHP_Instance;
204
+ }
205
+
206
+ /**
207
+ * Enable and disable logging to Firebug
208
+ *
209
+ * @param boolean $Enabled TRUE to enable, FALSE to disable
210
+ * @return void
211
+ */
212
+ function setEnabled($Enabled) {
213
+ $this->enabled = $Enabled;
214
+ }
215
+
216
+ /**
217
+ * Check if logging is enabled
218
+ *
219
+ * @return boolean TRUE if enabled
220
+ */
221
+ function getEnabled() {
222
+ return $this->enabled;
223
+ }
224
+
225
+ /**
226
+ * Specify a filter to be used when encoding an object
227
+ *
228
+ * Filters are used to exclude object members.
229
+ *
230
+ * @param string $Class The class name of the object
231
+ * @param array $Filter An array of members to exclude
232
+ * @return void
233
+ */
234
+ function setObjectFilter($Class, $Filter) {
235
+ $this->objectFilters[strtolower($Class)] = $Filter;
236
+ }
237
+
238
+ /**
239
+ * Set some options for the library
240
+ *
241
+ * Options:
242
+ * - maxObjectDepth: The maximum depth to traverse objects (default: 5)
243
+ * - maxArrayDepth: The maximum depth to traverse arrays (default: 5)
244
+ * - useNativeJsonEncode: If true will use json_encode() (default: true)
245
+ * - includeLineNumbers: If true will include line numbers and filenames (default: true)
246
+ *
247
+ * @param array $Options The options to be set
248
+ * @return void
249
+ */
250
+ function setOptions($Options) {
251
+ $this->options = array_merge($this->options,$Options);
252
+ }
253
+
254
+ /**
255
+ * Get options from the library
256
+ *
257
+ * @return array The currently set options
258
+ */
259
+ function getOptions() {
260
+ return $this->options;
261
+ }
262
+
263
+ /**
264
+ * Register FirePHP as your error handler
265
+ *
266
+ * Will use FirePHP to log each php error.
267
+ *
268
+ * @return mixed Returns a string containing the previously defined error handler (if any)
269
+ */
270
+ function registerErrorHandler()
271
+ {
272
+ //NOTE: The following errors will not be caught by this error handler:
273
+ // E_ERROR, E_PARSE, E_CORE_ERROR,
274
+ // E_CORE_WARNING, E_COMPILE_ERROR,
275
+ // E_COMPILE_WARNING, E_STRICT
276
+
277
+ return set_error_handler(array($this,'errorHandler'));
278
+ }
279
+
280
+ /**
281
+ * FirePHP's error handler
282
+ *
283
+ * Logs each php error that will occur.
284
+ *
285
+ * @param int $errno
286
+ * @param string $errstr
287
+ * @param string $errfile
288
+ * @param int $errline
289
+ * @param array $errcontext
290
+ */
291
+ function errorHandler($errno, $errstr, $errfile, $errline, $errcontext)
292
+ {
293
+ global $FirePHP_Instance;
294
+ // Don't log error if error reporting is switched off
295
+ if (error_reporting() == 0) {
296
+ return;
297
+ }
298
+ // Only log error for errors we are asking for
299
+ if (error_reporting() & $errno) {
300
+ $FirePHP_Instance->group($errstr);
301
+ $FirePHP_Instance->error("{$errfile}, line $errline");
302
+ $FirePHP_Instance->groupEnd();
303
+ }
304
+ }
305
+
306
+ /**
307
+ * Register FirePHP driver as your assert callback
308
+ *
309
+ * @return mixed Returns the original setting
310
+ */
311
+ function registerAssertionHandler()
312
+ {
313
+ return assert_options(ASSERT_CALLBACK, array($this, 'assertionHandler'));
314
+ }
315
+
316
+ /**
317
+ * FirePHP's assertion handler
318
+ *
319
+ * Logs all assertions to your firebug console and then stops the script.
320
+ *
321
+ * @param string $file File source of assertion
322
+ * @param int $line Line source of assertion
323
+ * @param mixed $code Assertion code
324
+ */
325
+ function assertionHandler($file, $line, $code)
326
+ {
327
+ $this->fb($code, 'Assertion Failed', FirePHP_ERROR, array('File'=>$file,'Line'=>$line));
328
+ }
329
+
330
+ /**
331
+ * Set custom processor url for FirePHP
332
+ *
333
+ * @param string $URL
334
+ */
335
+ function setProcessorUrl($URL)
336
+ {
337
+ $this->setHeader('X-FirePHP-ProcessorURL', $URL);
338
+ }
339
+
340
+ /**
341
+ * Set custom renderer url for FirePHP
342
+ *
343
+ * @param string $URL
344
+ */
345
+ function setRendererUrl($URL)
346
+ {
347
+ $this->setHeader('X-FirePHP-RendererURL', $URL);
348
+ }
349
+
350
+ /**
351
+ * Start a group for following messages.
352
+ *
353
+ * Options:
354
+ * Collapsed: [true|false]
355
+ * Color: [#RRGGBB|ColorName]
356
+ *
357
+ * @param string $Name
358
+ * @param array $Options OPTIONAL Instructions on how to log the group
359
+ * @return true
360
+ * @throws Exception
361
+ */
362
+ function group($Name, $Options=null) {
363
+
364
+ if(!$Name) {
365
+ trigger_error('You must specify a label for the group!');
366
+ }
367
+
368
+ if($Options) {
369
+ if(!is_array($Options)) {
370
+ trigger_error('Options must be defined as an array!');
371
+ }
372
+ if(array_key_exists('Collapsed', $Options)) {
373
+ $Options['Collapsed'] = ($Options['Collapsed'])?'true':'false';
374
+ }
375
+ }
376
+
377
+ return $this->fb(null, $Name, FirePHP_GROUP_START, $Options);
378
+ }
379
+
380
+ /**
381
+ * Ends a group you have started before
382
+ *
383
+ * @return true
384
+ * @throws Exception
385
+ */
386
+ function groupEnd() {
387
+ return $this->fb(null, null, FirePHP_GROUP_END);
388
+ }
389
+
390
+ /**
391
+ * Log object with label to firebug console
392
+ *
393
+ * @see FirePHP::LOG
394
+ * @param mixes $Object
395
+ * @param string $Label
396
+ * @return true
397
+ * @throws Exception
398
+ */
399
+ function log($Object, $Label=null) {
400
+ return $this->fb($Object, $Label, FirePHP_LOG);
401
+ }
402
+
403
+ /**
404
+ * Log object with label to firebug console
405
+ *
406
+ * @see FirePHP::INFO
407
+ * @param mixes $Object
408
+ * @param string $Label
409
+ * @return true
410
+ * @throws Exception
411
+ */
412
+ function info($Object, $Label=null) {
413
+ return $this->fb($Object, $Label, FirePHP_INFO);
414
+ }
415
+
416
+ /**
417
+ * Log object with label to firebug console
418
+ *
419
+ * @see FirePHP::WARN
420
+ * @param mixes $Object
421
+ * @param string $Label
422
+ * @return true
423
+ * @throws Exception
424
+ */
425
+ function warn($Object, $Label=null) {
426
+ return $this->fb($Object, $Label, FirePHP_WARN);
427
+ }
428
+
429
+ /**
430
+ * Log object with label to firebug console
431
+ *
432
+ * @see FirePHP::ERROR
433
+ * @param mixes $Object
434
+ * @param string $Label
435
+ * @return true
436
+ * @throws Exception
437
+ */
438
+ function error($Object, $Label=null) {
439
+ return $this->fb($Object, $Label, FirePHP_ERROR);
440
+ }
441
+
442
+ /**
443
+ * Dumps key and variable to firebug server panel
444
+ *
445
+ * @see FirePHP::DUMP
446
+ * @param string $Key
447
+ * @param mixed $Variable
448
+ * @return true
449
+ * @throws Exception
450
+ */
451
+ function dump($Key, $Variable) {
452
+ return $this->fb($Variable, $Key, FirePHP_DUMP);
453
+ }
454
+
455
+ /**
456
+ * Log a trace in the firebug console
457
+ *
458
+ * @see FirePHP::TRACE
459
+ * @param string $Label
460
+ * @return true
461
+ * @throws Exception
462
+ */
463
+ function trace($Label) {
464
+ return $this->fb($Label, FirePHP_TRACE);
465
+ }
466
+
467
+ /**
468
+ * Log a table in the firebug console
469
+ *
470
+ * @see FirePHP::TABLE
471
+ * @param string $Label
472
+ * @param string $Table
473
+ * @return true
474
+ * @throws Exception
475
+ */
476
+ function table($Label, $Table) {
477
+ return $this->fb($Table, $Label, FirePHP_TABLE);
478
+ }
479
+
480
+ /**
481
+ * Check if FirePHP is installed on client
482
+ *
483
+ * @return boolean
484
+ */
485
+ function detectClientExtension() {
486
+ // Check if FirePHP is installed on client via User-Agent header
487
+ if(@preg_match_all('/\sFirePHP\/([\.\d]*)\s?/si',$this->getUserAgent(),$m) &&
488
+ version_compare($m[1][0],'0.0.6','>=')) {
489
+ return true;
490
+ } else
491
+ // Check if FirePHP is installed on client via X-FirePHP-Version header
492
+ if(@preg_match_all('/^([\.\d]*)$/si',$this->getRequestHeader("X-FirePHP-Version"),$m) &&
493
+ version_compare($m[1][0],'0.0.6','>=')) {
494
+ return true;
495
+ }
496
+ return false;
497
+ }
498
+
499
+ /**
500
+ * Log varible to Firebug
501
+ *
502
+ * @see http://www.firephp.org/Wiki/Reference/Fb
503
+ * @param mixed $Object The variable to be logged
504
+ * @return true Return TRUE if message was added to headers, FALSE otherwise
505
+ * @throws Exception
506
+ */
507
+ function fb($Object) {
508
+
509
+ if(!$this->enabled) {
510
+ return false;
511
+ }
512
+
513
+ if (headers_sent($filename, $linenum)) {
514
+ trigger_error('Headers already sent in '.$filename.' on line '.$linenum.'. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.');
515
+ }
516
+
517
+ $Type = null;
518
+ $Label = null;
519
+ $Options = array();
520
+
521
+ if(func_num_args()==1) {
522
+ } else
523
+ if(func_num_args()==2) {
524
+ switch(func_get_arg(1)) {
525
+ case FirePHP_LOG:
526
+ case FirePHP_INFO:
527
+ case FirePHP_WARN:
528
+ case FirePHP_ERROR:
529
+ case FirePHP_DUMP:
530
+ case FirePHP_TRACE:
531
+ case FirePHP_TABLE:
532
+ case FirePHP_GROUP_START:
533
+ case FirePHP_GROUP_END:
534
+ $Type = func_get_arg(1);
535
+ break;
536
+ default:
537
+ $Label = func_get_arg(1);
538
+ break;
539
+ }
540
+ } else
541
+ if(func_num_args()==3) {
542
+ $Type = func_get_arg(2);
543
+ $Label = func_get_arg(1);
544
+ } else
545
+ if(func_num_args()==4) {
546
+ $Type = func_get_arg(2);
547
+ $Label = func_get_arg(1);
548
+ $Options = func_get_arg(3);
549
+ } else {
550
+ trigger_error('Wrong number of arguments to fb() function!');
551
+ }
552
+
553
+
554
+ if(!$this->detectClientExtension()) {
555
+ return false;
556
+ }
557
+
558
+ $meta = array();
559
+ $skipFinalObjectEncode = false;
560
+
561
+ if($Type==FirePHP_TRACE) {
562
+
563
+ $trace = debug_backtrace();
564
+ if(!$trace) return false;
565
+ for( $i=0 ; $i<sizeof($trace) ; $i++ ) {
566
+
567
+ if(isset($trace[$i]['class'])
568
+ && isset($trace[$i]['file'])
569
+ && ($trace[$i]['class']=='FirePHP'
570
+ || $trace[$i]['class']=='FB')
571
+ && (substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php'
572
+ || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) {
573
+ /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */
574
+ } else
575
+ if(isset($trace[$i]['class'])
576
+ && isset($trace[$i+1]['file'])
577
+ && $trace[$i]['class']=='FirePHP'
578
+ && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') {
579
+ /* Skip fb() */
580
+ } else
581
+ if($trace[$i]['function']=='fb'
582
+ || $trace[$i]['function']=='trace'
583
+ || $trace[$i]['function']=='send') {
584
+ $Object = array('Class'=>isset($trace[$i]['class'])?$trace[$i]['class']:'',
585
+ 'Type'=>isset($trace[$i]['type'])?$trace[$i]['type']:'',
586
+ 'Function'=>isset($trace[$i]['function'])?$trace[$i]['function']:'',
587
+ 'Message'=>$trace[$i]['args'][0],
588
+ 'File'=>isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'',
589
+ 'Line'=>isset($trace[$i]['line'])?$trace[$i]['line']:'',
590
+ 'Args'=>isset($trace[$i]['args'])?$this->encodeObject($trace[$i]['args']):'',
591
+ 'Trace'=>$this->_escapeTrace(array_splice($trace,$i+1)));
592
+
593
+ $skipFinalObjectEncode = true;
594
+ $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'';
595
+ $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:'';
596
+ break;
597
+ }
598
+ }
599
+
600
+ } else
601
+ if($Type==FirePHP_TABLE) {
602
+
603
+ if(isset($Object[0]) && is_string($Object[0])) {
604
+ $Object[1] = $this->encodeTable($Object[1]);
605
+ } else {
606
+ $Object = $this->encodeTable($Object);
607
+ }
608
+
609
+ $skipFinalObjectEncode = true;
610
+
611
+ } else
612
+ if($Type==FirePHP_GROUP_START) {
613
+
614
+ if(!$Label) {
615
+ trigger_error('You must specify a label for the group!');
616
+ }
617
+ } else {
618
+ if($Type===null) {
619
+ $Type = FirePHP_LOG;
620
+ }
621
+ }
622
+
623
+ if($this->options['includeLineNumbers']) {
624
+ if(!isset($meta['file']) || !isset($meta['line'])) {
625
+
626
+ $trace = debug_backtrace();
627
+ for( $i=0 ; $trace && $i<sizeof($trace) ; $i++ ) {
628
+
629
+ if(isset($trace[$i]['class'])
630
+ && isset($trace[$i]['file'])
631
+ && ($trace[$i]['class']=='FirePHP'
632
+ || $trace[$i]['class']=='FB')
633
+ && (substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php'
634
+ || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) {
635
+ /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */
636
+ } else
637
+ if(isset($trace[$i]['class'])
638
+ && isset($trace[$i+1]['file'])
639
+ && $trace[$i]['class']=='FirePHP'
640
+ && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') {
641
+ /* Skip fb() */
642
+ } else
643
+ if(isset($trace[$i]['file'])
644
+ && substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php') {
645
+ /* Skip FB::fb() */
646
+ } else {
647
+ $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'';
648
+ $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:'';
649
+ break;
650
+ }
651
+ }
652
+
653
+ }
654
+ } else {
655
+ unset($meta['file']);
656
+ unset($meta['line']);
657
+ }
658
+
659
+ $this->setHeader('X-Wf-Protocol-1','http://meta.wildfirehq.org/Protocol/JsonStream/0.2');
660
+ $this->setHeader('X-Wf-1-Plugin-1','http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/'.FirePHP_VERSION);
661
+
662
+ $structure_index = 1;
663
+ if($Type==FirePHP_DUMP) {
664
+ $structure_index = 2;
665
+ $this->setHeader('X-Wf-1-Structure-2','http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1');
666
+ } else {
667
+ $this->setHeader('X-Wf-1-Structure-1','http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1');
668
+ }
669
+
670
+ if($Type==FirePHP_DUMP) {
671
+ $msg = '{"'.$Label.'":'.$this->jsonEncode($Object, $skipFinalObjectEncode).'}';
672
+ } else {
673
+ $msg_meta = $Options;
674
+ $msg_meta['Type'] = $Type;
675
+ if($Label!==null) {
676
+ $msg_meta['Label'] = $Label;
677
+ }
678
+ if(isset($meta['file']) && !isset($msg_meta['File'])) {
679
+ $msg_meta['File'] = $meta['file'];
680
+ }
681
+ if(isset($meta['line']) && !isset($msg_meta['Line'])) {
682
+ $msg_meta['Line'] = $meta['line'];
683
+ }
684
+ $msg = '['.$this->jsonEncode($msg_meta).','.$this->jsonEncode($Object, $skipFinalObjectEncode).']';
685
+ }
686
+
687
+ $parts = explode("\n",chunk_split($msg, 5000, "\n"));
688
+
689
+ for( $i=0 ; $i<count($parts) ; $i++) {
690
+
691
+ $part = $parts[$i];
692
+ if ($part) {
693
+
694
+ if(count($parts)>2) {
695
+ // Message needs to be split into multiple parts
696
+ $this->setHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex,
697
+ (($i==0)?strlen($msg):'')
698
+ . '|' . $part . '|'
699
+ . (($i<count($parts)-2)?'\\':''));
700
+ } else {
701
+ $this->setHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex,
702
+ strlen($part) . '|' . $part . '|');
703
+ }
704
+
705
+ $this->messageIndex++;
706
+
707
+ if ($this->messageIndex > 99999) {
708
+ trigger_error('Maximum number (99,999) of messages reached!');
709
+ }
710
+ }
711
+ }
712
+
713
+ $this->setHeader('X-Wf-1-Index',$this->messageIndex-1);
714
+
715
+ return true;
716
+ }
717
+
718
+
719
+ /**
720
+ * Standardizes path for windows systems.
721
+ *
722
+ * @param string $Path
723
+ * @return string
724
+ */
725
+ function _standardizePath($Path) {
726
+ return preg_replace('/\\\\+/','/',$Path);
727
+ }
728
+
729
+ /**
730
+ * Escape trace path for windows systems
731
+ *
732
+ * @param array $Trace
733
+ * @return array
734
+ */
735
+ function _escapeTrace($Trace) {
736
+ if(!$Trace) return $Trace;
737
+ for( $i=0 ; $i<sizeof($Trace) ; $i++ ) {
738
+ if(isset($Trace[$i]['file'])) {
739
+ $Trace[$i]['file'] = $this->_escapeTraceFile($Trace[$i]['file']);
740
+ }
741
+ if(isset($Trace[$i]['args'])) {
742
+ $Trace[$i]['args'] = $this->encodeObject($Trace[$i]['args']);
743
+ }
744
+ }
745
+ return $Trace;
746
+ }
747
+
748
+ /**
749
+ * Escape file information of trace for windows systems
750
+ *
751
+ * @param string $File
752
+ * @return string
753
+ */
754
+ function _escapeTraceFile($File) {
755
+ /* Check if we have a windows filepath */
756
+ if(strpos($File,'\\')) {
757
+ /* First strip down to single \ */
758
+
759
+ $file = preg_replace('/\\\\+/','\\',$File);
760
+
761
+ return $file;
762
+ }
763
+ return $File;
764
+ }
765
+
766
+ /**
767
+ * Send header
768
+ *
769
+ * @param string $Name
770
+ * @param string_type $Value
771
+ */
772
+ function setHeader($Name, $Value) {
773
+ return header($Name.': '.$Value);
774
+ }
775
+
776
+ /**
777
+ * Get user agent
778
+ *
779
+ * @return string|false
780
+ */
781
+ function getUserAgent() {
782
+ if(!isset($_SERVER['HTTP_USER_AGENT'])) return false;
783
+ return $_SERVER['HTTP_USER_AGENT'];
784
+ }
785
+
786
+ /**
787
+ * Get all request headers
788
+ *
789
+ * @return array
790
+ */
791
+ function getAllRequestHeaders() {
792
+ $headers = array();
793
+ if(function_exists('getallheaders')) {
794
+ foreach( getallheaders() as $name => $value ) {
795
+ $headers[strtolower($name)] = $value;
796
+ }
797
+ } else {
798
+ foreach($_SERVER as $name => $value) {
799
+ if(substr($name, 0, 5) == 'HTTP_') {
800
+ $headers[strtolower(str_replace(' ', '-', str_replace('_', ' ', substr($name, 5))))] = $value;
801
+ }
802
+ }
803
+ }
804
+ return $headers;
805
+ }
806
+
807
+ /**
808
+ * Get a request header
809
+ *
810
+ * @return string|false
811
+ */
812
+ function getRequestHeader($Name)
813
+ {
814
+ $headers = $this->getAllRequestHeaders();
815
+ if (isset($headers[strtolower($Name)])) {
816
+ return $headers[strtolower($Name)];
817
+ }
818
+ return false;
819
+ }
820
+
821
+ /**
822
+ * Encode an object into a JSON string
823
+ *
824
+ * Uses PHP's jeson_encode() if available
825
+ *
826
+ * @param object $Object The object to be encoded
827
+ * @return string The JSON string
828
+ */
829
+ function jsonEncode($Object, $skipObjectEncode=false)
830
+ {
831
+ if(!$skipObjectEncode) {
832
+ $Object = $this->encodeObject($Object);
833
+ }
834
+
835
+ if(function_exists('json_encode')
836
+ && $this->options['useNativeJsonEncode']!=false) {
837
+
838
+ return json_encode($Object);
839
+ } else {
840
+ return $this->json_encode($Object);
841
+ }
842
+ }
843
+
844
+ /**
845
+ * Encodes a table by encoding each row and column with encodeObject()
846
+ *
847
+ * @param array $Table The table to be encoded
848
+ * @return array
849
+ */
850
+ function encodeTable($Table) {
851
+
852
+ if(!$Table) return $Table;
853
+
854
+ $new_table = array();
855
+ foreach($Table as $row) {
856
+
857
+ if(is_array($row)) {
858
+ $new_row = array();
859
+
860
+ foreach($row as $item) {
861
+ $new_row[] = $this->encodeObject($item);
862
+ }
863
+
864
+ $new_table[] = $new_row;
865
+ }
866
+ }
867
+
868
+ return $new_table;
869
+ }
870
+
871
+ /**
872
+ * Encodes an object
873
+ *
874
+ * @param Object $Object The object to be encoded
875
+ * @param int $Depth The current traversal depth
876
+ * @return array All members of the object
877
+ */
878
+ function encodeObject($Object, $ObjectDepth = 1, $ArrayDepth = 1)
879
+ {
880
+ $return = array();
881
+
882
+ if (is_resource($Object)) {
883
+
884
+ return '** '.(string)$Object.' **';
885
+
886
+ } else
887
+ if (is_object($Object)) {
888
+
889
+ if ($ObjectDepth > $this->options['maxObjectDepth']) {
890
+ return '** Max Object Depth ('.$this->options['maxObjectDepth'].') **';
891
+ }
892
+
893
+ foreach ($this->objectStack as $refVal) {
894
+ if ($refVal === $Object) {
895
+ return '** Recursion ('.get_class($Object).') **';
896
+ }
897
+ }
898
+ array_push($this->objectStack, $Object);
899
+
900
+ $return['__className'] = $class = get_class($Object);
901
+ $class_lower = strtolower($class);
902
+
903
+ $members = (array)$Object;
904
+
905
+ // Include all members that are not defined in the class
906
+ // but exist in the object
907
+ foreach( $members as $raw_name => $value ) {
908
+
909
+ $name = $raw_name;
910
+
911
+ if ($name{0} == "\0") {
912
+ $parts = explode("\0", $name);
913
+ $name = $parts[2];
914
+ }
915
+
916
+ if(!isset($properties[$name])) {
917
+ $name = 'undeclared:'.$name;
918
+
919
+ if(!(isset($this->objectFilters[$class_lower])
920
+ && is_array($this->objectFilters[$class_lower])
921
+ && in_array($raw_name,$this->objectFilters[$class_lower]))) {
922
+
923
+ $return[$name] = $this->encodeObject($value, $ObjectDepth + 1, 1);
924
+ } else {
925
+ $return[$name] = '** Excluded by Filter **';
926
+ }
927
+ }
928
+ }
929
+
930
+ array_pop($this->objectStack);
931
+
932
+ } elseif (is_array($Object)) {
933
+
934
+ if ($ArrayDepth > $this->options['maxArrayDepth']) {
935
+ return '** Max Array Depth ('.$this->options['maxArrayDepth'].') **';
936
+ }
937
+
938
+ foreach ($Object as $key => $val) {
939
+
940
+ // Encoding the $GLOBALS PHP array causes an infinite loop
941
+ // if the recursion is not reset here as it contains
942
+ // a reference to itself. This is the only way I have come up
943
+ // with to stop infinite recursion in this case.
944
+ if($key=='GLOBALS'
945
+ && is_array($val)
946
+ && array_key_exists('GLOBALS',$val)) {
947
+ $val['GLOBALS'] = '** Recursion (GLOBALS) **';
948
+ }
949
+
950
+ $return[$key] = $this->encodeObject($val, 1, $ArrayDepth + 1);
951
+ }
952
+ } else {
953
+ if($this->is_utf8($Object)) {
954
+ return $Object;
955
+ } else {
956
+ return utf8_encode($Object);
957
+ }
958
+ }
959
+ return $return;
960
+
961
+ }
962
+
963
+ /**
964
+ * Returns true if $string is valid UTF-8 and false otherwise.
965
+ *
966
+ * @param mixed $str String to be tested
967
+ * @return boolean
968
+ */
969
+ function is_utf8($str) {
970
+ $c=0; $b=0;
971
+ $bits=0;
972
+ $len=strlen($str);
973
+ for($i=0; $i<$len; $i++){
974
+ $c=ord($str[$i]);
975
+ if($c > 128){
976
+ if(($c >= 254)) return false;
977
+ elseif($c >= 252) $bits=6;
978
+ elseif($c >= 248) $bits=5;
979
+ elseif($c >= 240) $bits=4;
980
+ elseif($c >= 224) $bits=3;
981
+ elseif($c >= 192) $bits=2;
982
+ else return false;
983
+ if(($i+$bits) > $len) return false;
984
+ while($bits > 1){
985
+ $i++;
986
+ $b=ord($str[$i]);
987
+ if($b < 128 || $b > 191) return false;
988
+ $bits--;
989
+ }
990
+ }
991
+ }
992
+ return true;
993
+ }
994
+
995
+ /**
996
+ * Converts to and from JSON format.
997
+ *
998
+ * JSON (JavaScript Object Notation) is a lightweight data-interchange
999
+ * format. It is easy for humans to read and write. It is easy for machines
1000
+ * to parse and generate. It is based on a subset of the JavaScript
1001
+ * Programming Language, Standard ECMA-262 3rd Edition - December 1999.
1002
+ * This feature can also be found in Python. JSON is a text format that is
1003
+ * completely language independent but uses conventions that are familiar
1004
+ * to programmers of the C-family of languages, including C, C++, C#, Java,
1005
+ * JavaScript, Perl, TCL, and many others. These properties make JSON an
1006
+ * ideal data-interchange language.
1007
+ *
1008
+ * This package provides a simple encoder and decoder for JSON notation. It
1009
+ * is intended for use with client-side Javascript applications that make
1010
+ * use of HTTPRequest to perform server communication functions - data can
1011
+ * be encoded into JSON notation for use in a client-side javascript, or
1012
+ * decoded from incoming Javascript requests. JSON format is native to
1013
+ * Javascript, and can be directly eval()'ed with no further parsing
1014
+ * overhead
1015
+ *
1016
+ * All strings should be in ASCII or UTF-8 format!
1017
+ *
1018
+ * LICENSE: Redistribution and use in source and binary forms, with or
1019
+ * without modification, are permitted provided that the following
1020
+ * conditions are met: Redistributions of source code must retain the
1021
+ * above copyright notice, this list of conditions and the following
1022
+ * disclaimer. Redistributions in binary form must reproduce the above
1023
+ * copyright notice, this list of conditions and the following disclaimer
1024
+ * in the documentation and/or other materials provided with the
1025
+ * distribution.
1026
+ *
1027
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
1028
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
1029
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
1030
+ * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
1031
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
1032
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
1033
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
1034
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
1035
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
1036
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
1037
+ * DAMAGE.
1038
+ *
1039
+ * @category
1040
+ * @package Services_JSON
1041
+ * @author Michal Migurski <mike-json@teczno.com>
1042
+ * @author Matt Knapp <mdknapp[at]gmail[dot]com>
1043
+ * @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
1044
+ * @author Christoph Dorn <christoph@christophdorn.com>
1045
+ * @copyright 2005 Michal Migurski
1046
+ * @version CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $
1047
+ * @license http://www.opensource.org/licenses/bsd-license.php
1048
+ * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
1049
+ */
1050
+
1051
+
1052
+ /**
1053
+ * Keep a list of objects as we descend into the array so we can detect recursion.
1054
+ */
1055
+ var $json_objectStack = array();
1056
+
1057
+
1058
+ /**
1059
+ * convert a string from one UTF-8 char to one UTF-16 char
1060
+ *
1061
+ * Normally should be handled by mb_convert_encoding, but
1062
+ * provides a slower PHP-only method for installations
1063
+ * that lack the multibye string extension.
1064
+ *
1065
+ * @param string $utf8 UTF-8 character
1066
+ * @return string UTF-16 character
1067
+ * @access private
1068
+ */
1069
+ function json_utf82utf16($utf8)
1070
+ {
1071
+ // oh please oh please oh please oh please oh please
1072
+ if(function_exists('mb_convert_encoding')) {
1073
+ return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
1074
+ }
1075
+
1076
+ switch(strlen($utf8)) {
1077
+ case 1:
1078
+ // this case should never be reached, because we are in ASCII range
1079
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1080
+ return $utf8;
1081
+
1082
+ case 2:
1083
+ // return a UTF-16 character from a 2-byte UTF-8 char
1084
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1085
+ return chr(0x07 & (ord($utf8{0}) >> 2))
1086
+ . chr((0xC0 & (ord($utf8{0}) << 6))
1087
+ | (0x3F & ord($utf8{1})));
1088
+
1089
+ case 3:
1090
+ // return a UTF-16 character from a 3-byte UTF-8 char
1091
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1092
+ return chr((0xF0 & (ord($utf8{0}) << 4))
1093
+ | (0x0F & (ord($utf8{1}) >> 2)))
1094
+ . chr((0xC0 & (ord($utf8{1}) << 6))
1095
+ | (0x7F & ord($utf8{2})));
1096
+ }
1097
+
1098
+ // ignoring UTF-32 for now, sorry
1099
+ return '';
1100
+ }
1101
+
1102
+ /**
1103
+ * encodes an arbitrary variable into JSON format
1104
+ *
1105
+ * @param mixed $var any number, boolean, string, array, or object to be encoded.
1106
+ * see argument 1 to Services_JSON() above for array-parsing behavior.
1107
+ * if var is a strng, note that encode() always expects it
1108
+ * to be in ASCII or UTF-8 format!
1109
+ *
1110
+ * @return mixed JSON string representation of input var or an error if a problem occurs
1111
+ * @access public
1112
+ */
1113
+ function json_encode($var)
1114
+ {
1115
+
1116
+ if(is_object($var)) {
1117
+ if(in_array($var,$this->json_objectStack)) {
1118
+ return '"** Recursion **"';
1119
+ }
1120
+ }
1121
+
1122
+ switch (gettype($var)) {
1123
+ case 'boolean':
1124
+ return $var ? 'true' : 'false';
1125
+
1126
+ case 'NULL':
1127
+ return 'null';
1128
+
1129
+ case 'integer':
1130
+ return (int) $var;
1131
+
1132
+ case 'double':
1133
+ case 'float':
1134
+ return (float) $var;
1135
+
1136
+ case 'string':
1137
+ // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
1138
+ $ascii = '';
1139
+ $strlen_var = strlen($var);
1140
+
1141
+ /*
1142
+ * Iterate over every character in the string,
1143
+ * escaping with a slash or encoding to UTF-8 where necessary
1144
+ */
1145
+ for ($c = 0; $c < $strlen_var; ++$c) {
1146
+
1147
+ $ord_var_c = ord($var{$c});
1148
+
1149
+ switch (true) {
1150
+ case $ord_var_c == 0x08:
1151
+ $ascii .= '\b';
1152
+ break;
1153
+ case $ord_var_c == 0x09:
1154
+ $ascii .= '\t';
1155
+ break;
1156
+ case $ord_var_c == 0x0A:
1157
+ $ascii .= '\n';
1158
+ break;
1159
+ case $ord_var_c == 0x0C:
1160
+ $ascii .= '\f';
1161
+ break;
1162
+ case $ord_var_c == 0x0D:
1163
+ $ascii .= '\r';
1164
+ break;
1165
+
1166
+ case $ord_var_c == 0x22:
1167
+ case $ord_var_c == 0x2F:
1168
+ case $ord_var_c == 0x5C:
1169
+ // double quote, slash, slosh
1170
+ $ascii .= '\\'.$var{$c};
1171
+ break;
1172
+
1173
+ case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
1174
+ // characters U-00000000 - U-0000007F (same as ASCII)
1175
+ $ascii .= $var{$c};
1176
+ break;
1177
+
1178
+ case (($ord_var_c & 0xE0) == 0xC0):
1179
+ // characters U-00000080 - U-000007FF, mask 110XXXXX
1180
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1181
+ $char = pack('C*', $ord_var_c, ord($var{$c + 1}));
1182
+ $c += 1;
1183
+ $utf16 = $this->json_utf82utf16($char);
1184
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
1185
+ break;
1186
+
1187
+ case (($ord_var_c & 0xF0) == 0xE0):
1188
+ // characters U-00000800 - U-0000FFFF, mask 1110XXXX
1189
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1190
+ $char = pack('C*', $ord_var_c,
1191
+ ord($var{$c + 1}),
1192
+ ord($var{$c + 2}));
1193
+ $c += 2;
1194
+ $utf16 = $this->json_utf82utf16($char);
1195
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
1196
+ break;
1197
+
1198
+ case (($ord_var_c & 0xF8) == 0xF0):
1199
+ // characters U-00010000 - U-001FFFFF, mask 11110XXX
1200
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1201
+ $char = pack('C*', $ord_var_c,
1202
+ ord($var{$c + 1}),
1203
+ ord($var{$c + 2}),
1204
+ ord($var{$c + 3}));
1205
+ $c += 3;
1206
+ $utf16 = $this->json_utf82utf16($char);
1207
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
1208
+ break;
1209
+
1210
+ case (($ord_var_c & 0xFC) == 0xF8):
1211
+ // characters U-00200000 - U-03FFFFFF, mask 111110XX
1212
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1213
+ $char = pack('C*', $ord_var_c,
1214
+ ord($var{$c + 1}),
1215
+ ord($var{$c + 2}),
1216
+ ord($var{$c + 3}),
1217
+ ord($var{$c + 4}));
1218
+ $c += 4;
1219
+ $utf16 = $this->json_utf82utf16($char);
1220
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
1221
+ break;
1222
+
1223
+ case (($ord_var_c & 0xFE) == 0xFC):
1224
+ // characters U-04000000 - U-7FFFFFFF, mask 1111110X
1225
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1226
+ $char = pack('C*', $ord_var_c,
1227
+ ord($var{$c + 1}),
1228
+ ord($var{$c + 2}),
1229
+ ord($var{$c + 3}),
1230
+ ord($var{$c + 4}),
1231
+ ord($var{$c + 5}));
1232
+ $c += 5;
1233
+ $utf16 = $this->json_utf82utf16($char);
1234
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
1235
+ break;
1236
+ }
1237
+ }
1238
+
1239
+ return '"'.$ascii.'"';
1240
+
1241
+ case 'array':
1242
+ /*
1243
+ * As per JSON spec if any array key is not an integer
1244
+ * we must treat the the whole array as an object. We
1245
+ * also try to catch a sparsely populated associative
1246
+ * array with numeric keys here because some JS engines
1247
+ * will create an array with empty indexes up to
1248
+ * max_index which can cause memory issues and because
1249
+ * the keys, which may be relevant, will be remapped
1250
+ * otherwise.
1251
+ *
1252
+ * As per the ECMA and JSON specification an object may
1253
+ * have any string as a property. Unfortunately due to
1254
+ * a hole in the ECMA specification if the key is a
1255
+ * ECMA reserved word or starts with a digit the
1256
+ * parameter is only accessible using ECMAScript's
1257
+ * bracket notation.
1258
+ */
1259
+
1260
+ // treat as a JSON object
1261
+ if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
1262
+
1263
+ $this->json_objectStack[] = $var;
1264
+
1265
+ $properties = array_map(array($this, 'json_name_value'),
1266
+ array_keys($var),
1267
+ array_values($var));
1268
+
1269
+ array_pop($this->json_objectStack);
1270
+
1271
+ return '{' . join(',', $properties) . '}';
1272
+ }
1273
+
1274
+ $this->json_objectStack[] = $var;
1275
+
1276
+ // treat it like a regular array
1277
+ $elements = array_map(array($this, 'json_encode'), $var);
1278
+
1279
+ array_pop($this->json_objectStack);
1280
+
1281
+ return '[' . join(',', $elements) . ']';
1282
+
1283
+ case 'object':
1284
+ $vars = FirePHP::encodeObject($var);
1285
+
1286
+ $this->json_objectStack[] = $var;
1287
+
1288
+ $properties = array_map(array($this, 'json_name_value'),
1289
+ array_keys($vars),
1290
+ array_values($vars));
1291
+
1292
+ array_pop($this->json_objectStack);
1293
+
1294
+ return '{' . join(',', $properties) . '}';
1295
+
1296
+ default:
1297
+ return null;
1298
+ }
1299
+ }
1300
+
1301
+ /**
1302
+ * array-walking function for use in generating JSON-formatted name-value pairs
1303
+ *
1304
+ * @param string $name name of key to use
1305
+ * @param mixed $value reference to an array element to be encoded
1306
+ *
1307
+ * @return string JSON-formatted name-value pair, like '"name":value'
1308
+ * @access private
1309
+ */
1310
+ function json_name_value($name, $value)
1311
+ {
1312
+ // Encoding the $GLOBALS PHP array causes an infinite loop
1313
+ // if the recursion is not reset here as it contains
1314
+ // a reference to itself. This is the only way I have come up
1315
+ // with to stop infinite recursion in this case.
1316
+ if($name=='GLOBALS'
1317
+ && is_array($value)
1318
+ && array_key_exists('GLOBALS',$value)) {
1319
+ $value['GLOBALS'] = '** Recursion **';
1320
+ }
1321
+
1322
+ $encoded_value = $this->json_encode($value);
1323
+
1324
+ return $this->json_encode(strval($name)) . ':' . $encoded_value;
1325
+ }
1326
+ }
1327
+
includes/debug/browsers/api/firephp/lib/FirePHPCore/fb.php ADDED
@@ -0,0 +1,275 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Authors:
3
+ // - cadorn, Christoph Dorn <christoph@christophdorn.com>, Copyright 2007, New BSD License
4
+ // - qbbr, Sokolov Innokenty <sokolov.innokenty@gmail.com>, Copyright 2011, New BSD License
5
+ // - cadorn, Christoph Dorn <christoph@christophdorn.com>, Copyright 2011, MIT License
6
+
7
+ /**
8
+ * ***** BEGIN LICENSE BLOCK *****
9
+ *
10
+ * [MIT License](http://www.opensource.org/licenses/mit-license.php)
11
+ *
12
+ * Copyright (c) 2007+ [Christoph Dorn](http://www.christophdorn.com/)
13
+ *
14
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
15
+ * of this software and associated documentation files (the "Software"), to deal
16
+ * in the Software without restriction, including without limitation the rights
17
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18
+ * copies of the Software, and to permit persons to whom the Software is
19
+ * furnished to do so, subject to the following conditions:
20
+ *
21
+ * The above copyright notice and this permission notice shall be included in
22
+ * all copies or substantial portions of the Software.
23
+ *
24
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
30
+ * THE SOFTWARE.
31
+ *
32
+ * ***** END LICENSE BLOCK *****
33
+ *
34
+ * @copyright Copyright (C) 2007+ Christoph Dorn
35
+ * @author Christoph Dorn <christoph@christophdorn.com>
36
+ * @license [MIT License](http://www.opensource.org/licenses/mit-license.php)
37
+ * @package FirePHPCore
38
+ */
39
+
40
+ if (!class_exists('FirePHP', false)) {
41
+ require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'FirePHP.class.php';
42
+ }
43
+
44
+ /**
45
+ * Sends the given data to the FirePHP Firefox Extension.
46
+ * The data can be displayed in the Firebug Console or in the
47
+ * "Server" request tab.
48
+ *
49
+ * @see http://www.firephp.org/Wiki/Reference/Fb
50
+ * @param mixed $Object
51
+ * @return true
52
+ * @throws Exception
53
+ */
54
+ function fb()
55
+ {
56
+ $instance = FirePHP::getInstance(true);
57
+
58
+ $args = func_get_args();
59
+ return call_user_func_array(array($instance, 'fb'), $args);
60
+ }
61
+
62
+
63
+ class FB
64
+ {
65
+ /**
66
+ * Set an Insight console to direct all logging calls to
67
+ *
68
+ * @param object $console The console object to log to
69
+ * @return void
70
+ */
71
+ public static function setLogToInsightConsole($console)
72
+ {
73
+ FirePHP::getInstance(true)->setLogToInsightConsole($console);
74
+ }
75
+
76
+ /**
77
+ * Enable and disable logging to Firebug
78
+ *
79
+ * @see FirePHP->setEnabled()
80
+ * @param boolean $enabled TRUE to enable, FALSE to disable
81
+ * @return void
82
+ */
83
+ public static function setEnabled($enabled)
84
+ {
85
+ FirePHP::getInstance(true)->setEnabled($enabled);
86
+ }
87
+
88
+ /**
89
+ * Check if logging is enabled
90
+ *
91
+ * @see FirePHP->getEnabled()
92
+ * @return boolean TRUE if enabled
93
+ */
94
+ public static function getEnabled()
95
+ {
96
+ return FirePHP::getInstance(true)->getEnabled();
97
+ }
98
+
99
+ /**
100
+ * Specify a filter to be used when encoding an object
101
+ *
102
+ * Filters are used to exclude object members.
103
+ *
104
+ * @see FirePHP->setObjectFilter()
105
+ * @param string $class The class name of the object
106
+ * @param array $filter An array or members to exclude
107
+ * @return void
108
+ */
109
+ public static function setObjectFilter($class, $filter)
110
+ {
111
+ FirePHP::getInstance(true)->setObjectFilter($class, $filter);
112
+ }
113
+
114
+ /**
115
+ * Set some options for the library
116
+ *
117
+ * @see FirePHP->setOptions()
118
+ * @param array $options The options to be set
119
+ * @return void
120
+ */
121
+ public static function setOptions($options)
122
+ {
123
+ FirePHP::getInstance(true)->setOptions($options);
124
+ }
125
+
126
+ /**
127
+ * Get options for the library
128
+ *
129
+ * @see FirePHP->getOptions()
130
+ * @return array The options
131
+ */
132
+ public static function getOptions()
133
+ {
134
+ return FirePHP::getInstance(true)->getOptions();
135
+ }
136
+
137
+ /**
138
+ * Log object to firebug
139
+ *
140
+ * @see http://www.firephp.org/Wiki/Reference/Fb
141
+ * @param mixed $object
142
+ * @return true
143
+ * @throws Exception
144
+ */
145
+ public static function send()
146
+ {
147
+ $args = func_get_args();
148
+ return call_user_func_array(array(FirePHP::getInstance(true), 'fb'), $args);
149
+ }
150
+
151
+ /**
152
+ * Start a group for following messages
153
+ *
154
+ * Options:
155
+ * Collapsed: [true|false]
156
+ * Color: [#RRGGBB|ColorName]
157
+ *
158
+ * @param string $name
159
+ * @param array $options OPTIONAL Instructions on how to log the group
160
+ * @return true
161
+ */
162
+ public static function group($name, $options=null)
163
+ {
164
+ return FirePHP::getInstance(true)->group($name, $options);
165
+ }
166
+
167
+ /**
168
+ * Ends a group you have started before
169
+ *
170
+ * @return true
171
+ * @throws Exception
172
+ */
173
+ public static function groupEnd()
174
+ {
175
+ return self::send(null, null, FirePHP::GROUP_END);
176
+ }
177
+
178
+ /**
179
+ * Log object with label to firebug console
180
+ *
181
+ * @see FirePHP::LOG
182
+ * @param mixes $object
183
+ * @param string $label
184
+ * @return true
185
+ * @throws Exception
186
+ */
187
+ public static function log($object, $label=null)
188
+ {
189
+ return self::send($object, $label, FirePHP::LOG);
190
+ }
191
+
192
+ /**
193
+ * Log object with label to firebug console
194
+ *
195
+ * @see FirePHP::INFO
196
+ * @param mixes $object
197
+ * @param string $label
198
+ * @return true
199
+ * @throws Exception
200
+ */
201
+ public static function info($object, $label=null)
202
+ {
203
+ return self::send($object, $label, FirePHP::INFO);
204
+ }
205
+
206
+ /**
207
+ * Log object with label to firebug console
208
+ *
209
+ * @see FirePHP::WARN
210
+ * @param mixes $object
211
+ * @param string $label
212
+ * @return true
213
+ * @throws Exception
214
+ */
215
+ public static function warn($object, $label=null)
216
+ {
217
+ return self::send($object, $label, FirePHP::WARN);
218
+ }
219
+
220
+ /**
221
+ * Log object with label to firebug console
222
+ *
223
+ * @see FirePHP::ERROR
224
+ * @param mixes $object
225
+ * @param string $label
226
+ * @return true
227
+ * @throws Exception
228
+ */
229
+ public static function error($object, $label=null)
230
+ {
231
+ return self::send($object, $label, FirePHP::ERROR);
232
+ }
233
+
234
+ /**
235
+ * Dumps key and variable to firebug server panel
236
+ *
237
+ * @see FirePHP::DUMP
238
+ * @param string $key
239
+ * @param mixed $variable
240
+ * @return true
241
+ * @throws Exception
242
+ */
243
+ public static function dump($key, $variable)
244
+ {
245
+ return self::send($variable, $key, FirePHP::DUMP);
246
+ }
247
+
248
+ /**
249
+ * Log a trace in the firebug console
250
+ *
251
+ * @see FirePHP::TRACE
252
+ * @param string $label
253
+ * @return true
254
+ * @throws Exception
255
+ */
256
+ public static function trace($label)
257
+ {
258
+ return self::send($label, FirePHP::TRACE);
259
+ }
260
+
261
+ /**
262
+ * Log a table in the firebug console
263
+ *
264
+ * @see FirePHP::TABLE
265
+ * @param string $label
266
+ * @param string $table
267
+ * @return true
268
+ * @throws Exception
269
+ */
270
+ public static function table($label, $table)
271
+ {
272
+ return self::send($table, $label, FirePHP::TABLE);
273
+ }
274
+
275
+ }
includes/debug/browsers/api/firephp/lib/FirePHPCore/fb.php4 ADDED
@@ -0,0 +1,245 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Authors:
3
+ // - cadorn, Christoph Dorn <christoph@christophdorn.com>, Copyright 2007, New BSD License
4
+ // - qbbr, Michael Day <manveru.alma@gmail.com>, Copyright 2008, New BSD License
5
+ // - cadorn, Christoph Dorn <christoph@christophdorn.com>, Copyright 2011, MIT License
6
+
7
+ /* ***** BEGIN LICENSE BLOCK *****
8
+ *
9
+ * [MIT License](http://www.opensource.org/licenses/mit-license.php)
10
+ *
11
+ * Copyright (c) 2007+ [Christoph Dorn](http://www.christophdorn.com/)
12
+ *
13
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ * of this software and associated documentation files (the "Software"), to deal
15
+ * in the Software without restriction, including without limitation the rights
16
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
+ * copies of the Software, and to permit persons to whom the Software is
18
+ * furnished to do so, subject to the following conditions:
19
+ *
20
+ * The above copyright notice and this permission notice shall be included in
21
+ * all copies or substantial portions of the Software.
22
+ *
23
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29
+ * THE SOFTWARE.
30
+ *
31
+ * ***** END LICENSE BLOCK *****
32
+ *
33
+ * @copyright Copyright (C) 2007+ Christoph Dorn
34
+ * @author Christoph Dorn <christoph@christophdorn.com>
35
+ * @author Michael Day <manveru.alma@gmail.com>
36
+ * @license [MIT License](http://www.opensource.org/licenses/mit-license.php)
37
+ * @package FirePHPCore
38
+ */
39
+
40
+ require_once dirname(__FILE__).'/FirePHP.class.php4';
41
+
42
+ /**
43
+ * Sends the given data to the FirePHP Firefox Extension.
44
+ * The data can be displayed in the Firebug Console or in the
45
+ * "Server" request tab.
46
+ *
47
+ * @see http://www.firephp.org/Wiki/Reference/Fb
48
+ * @param mixed $Object
49
+ * @return true
50
+ * @throws Exception
51
+ */
52
+ function fb()
53
+ {
54
+ $instance =& FirePHP::getInstance(true);
55
+
56
+ $args = func_get_args();
57
+ return call_user_func_array(array(&$instance,'fb'),$args);
58
+ }
59
+
60
+
61
+ class FB
62
+ {
63
+ /**
64
+ * Enable and disable logging to Firebug
65
+ *
66
+ * @see FirePHP->setEnabled()
67
+ * @param boolean $Enabled TRUE to enable, FALSE to disable
68
+ * @return void
69
+ */
70
+ function setEnabled($Enabled) {
71
+ $instance =& FirePHP::getInstance(true);
72
+ $instance->setEnabled($Enabled);
73
+ }
74
+
75
+ /**
76
+ * Check if logging is enabled
77
+ *
78
+ * @see FirePHP->getEnabled()
79
+ * @return boolean TRUE if enabled
80
+ */
81
+ function getEnabled() {
82
+ $instance =& FirePHP::getInstance(true);
83
+ return $instance->getEnabled();
84
+ }
85
+
86
+ /**
87
+ * Specify a filter to be used when encoding an object
88
+ *
89
+ * Filters are used to exclude object members.
90
+ *
91
+ * @see FirePHP->setObjectFilter()
92
+ * @param string $Class The class name of the object
93
+ * @param array $Filter An array or members to exclude
94
+ * @return void
95
+ */
96
+ function setObjectFilter($Class, $Filter) {
97
+ $instance =& FirePHP::getInstance(true);
98
+ $instance->setObjectFilter($Class, $Filter);
99
+ }
100
+
101
+ /**
102
+ * Set some options for the library
103
+ *
104
+ * @see FirePHP->setOptions()
105
+ * @param array $Options The options to be set
106
+ * @return void
107
+ */
108
+ function setOptions($Options) {
109
+ $instance =& FirePHP::getInstance(true);
110
+ $instance->setOptions($Options);
111
+ }
112
+
113
+ /**
114
+ * Get options for the library
115
+ *
116
+ * @see FirePHP->getOptions()
117
+ * @return array The options
118
+ */
119
+ function getOptions() {
120
+ $instance =& FirePHP::getInstance(true);
121
+ return $instance->getOptions();
122
+ }
123
+
124
+ /**
125
+ * Log object to firebug
126
+ *
127
+ * @see http://www.firephp.org/Wiki/Reference/Fb
128
+ * @param mixed $Object
129
+ * @return true
130
+ */
131
+ function send()
132
+ {
133
+ $instance =& FirePHP::getInstance(true);
134
+ $args = func_get_args();
135
+ return call_user_func_array(array(&$instance,'fb'),$args);
136
+ }
137
+
138
+ /**
139
+ * Start a group for following messages
140
+ *
141
+ * Options:
142
+ * Collapsed: [true|false]
143
+ * Color: [#RRGGBB|ColorName]
144
+ *
145
+ * @param string $Name
146
+ * @param array $Options OPTIONAL Instructions on how to log the group
147
+ * @return true
148
+ */
149
+ function group($Name, $Options=null) {
150
+ $instance =& FirePHP::getInstance(true);
151
+ return $instance->group($Name, $Options);
152
+ }
153
+
154
+ /**
155
+ * Ends a group you have started before
156
+ *
157
+ * @return true
158
+ */
159
+ function groupEnd() {
160
+ return FB::send(null, null, FirePHP_GROUP_END);
161
+ }
162
+
163
+ /**
164
+ * Log object with label to firebug console
165
+ *
166
+ * @see FirePHP::LOG
167
+ * @param mixes $Object
168
+ * @param string $Label
169
+ * @return true
170
+ */
171
+ function log($Object, $Label=null) {
172
+ return FB::send($Object, $Label, FirePHP_LOG);
173
+ }
174
+
175
+ /**
176
+ * Log object with label to firebug console
177
+ *
178
+ * @see FirePHP::INFO
179
+ * @param mixes $Object
180
+ * @param string $Label
181
+ * @return true
182
+ */
183
+ function info($Object, $Label=null) {
184
+ return FB::send($Object, $Label, FirePHP_INFO);
185
+ }
186
+
187
+ /**
188
+ * Log object with label to firebug console
189
+ *
190
+ * @see FirePHP::WARN
191
+ * @param mixes $Object
192
+ * @param string $Label
193
+ * @return true
194
+ */
195
+ function warn($Object, $Label=null) {
196
+ return FB::send($Object, $Label, FirePHP_WARN);
197
+ }
198
+
199
+ /**
200
+ * Log object with label to firebug console
201
+ *
202
+ * @see FirePHP::ERROR
203
+ * @param mixes $Object
204
+ * @param string $Label
205
+ * @return true
206
+ */
207
+ function error($Object, $Label=null) {
208
+ return FB::send($Object, $Label, FirePHP_ERROR);
209
+ }
210
+
211
+ /**
212
+ * Dumps key and variable to firebug server panel
213
+ *
214
+ * @see FirePHP::DUMP
215
+ * @param string $Key
216
+ * @param mixed $Variable
217
+ * @return true
218
+ */
219
+ function dump($Key, $Variable) {
220
+ return FB::send($Variable, $Key, FirePHP_DUMP);
221
+ }
222
+
223
+ /**
224
+ * Log a trace in the firebug console
225
+ *
226
+ * @see FirePHP::TRACE
227
+ * @param string $Label
228
+ * @return true
229
+ */
230
+ function trace($Label) {
231
+ return FB::send($Label, FirePHP_TRACE);
232
+ }
233
+
234
+ /**
235
+ * Log a table in the firebug console
236
+ *
237
+ * @see FirePHP::TABLE
238
+ * @param string $Label
239
+ * @param string $Table
240
+ * @return true
241
+ */
242
+ function table($Label, $Table) {
243
+ return FB::send($Table, $Label, FirePHP_TABLE);
244
+ }
245
+ }
includes/debug/browsers/api/firephp/package.json ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "uid": "https://github.com/firephp/firephp-core/",
3
+ "name": "firephp-core",
4
+ "label": "FirePHP Server Library",
5
+ "repositories": [
6
+ {
7
+ "type": "git",
8
+ "url": "git://github.com/firephp/firephp-core.git"
9
+ }
10
+ ],
11
+ "maintainers": [
12
+ {
13
+ "name": "Christoph Dorn",
14
+ "email": "christoph@christophdorn.com",
15
+ "web": "http://www.christophdorn.com/",
16
+ "alias": {
17
+ "github": "cadorn"
18
+ }
19
+ }
20
+ ],
21
+ "contributors": [
22
+ {
23
+ "name": "Christoph Dorn",
24
+ "email": "christoph@christophdorn.com",
25
+ "web": "http://www.christophdorn.com/",
26
+ "alias": {
27
+ "github": "cadorn"
28
+ }
29
+ },
30
+ {
31
+ "name": "Michael Day",
32
+ "email": "manveru.alma@gmail.com"
33
+ },
34
+ {
35
+ "name": "Sokolov Innokenty",
36
+ "email": "sokolov.innokenty@gmail.com",
37
+ "alias": {
38
+ "github": "qbbr"
39
+ }
40
+ }
41
+ ]
42
+ }
includes/debug/browsers/api/firephp/program.json ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ {
2
+ "extends": {
3
+ "location": "./workspace/program.json"
4
+ }
5
+ }
includes/debug/browsers/api/firephp/tests/API/newlines.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ set_include_path(dirname(dirname(dirname(__FILE__))).'/lib'.PATH_SEPARATOR.get_include_path());
4
+ require('FirePHPCore/fb.php');
5
+
6
+
7
+ fb('Hello\nWorld');
8
+ fb(array('Hello\nWorld'));
9
+ fb(array('Table cell with newline',array(
10
+ array('Header'),
11
+ array('Hello\nWorld'),
12
+ )),FirePHP::TABLE);
includes/debug/browsers/api/firephp/tests/FirePHPCore/FirePHPTest.php ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class FirePHPCore_FirePHPTest extends PHPUnit_Framework_TestCase
4
+ {
5
+ /**
6
+ * @issue http://code.google.com/p/firephp/issues/detail?id=117
7
+ */
8
+ public function testDumpArguments()
9
+ {
10
+ $firephp = new FirePHP_Test_Class();
11
+
12
+ $firephp->dump("key", "value");
13
+ $headers = $firephp->_getHeaders();
14
+ $this->assertEquals('15|{"key":"value"}|', $headers['X-Wf-1-2-1-1']);
15
+ $firephp->_clearHeaders();
16
+
17
+ $caught = false;
18
+ try {
19
+ $firephp->dump(array(), "value");
20
+ } catch(Exception $e) {
21
+ // Key passed to dump() is not a string
22
+ $caught = true;
23
+ }
24
+ if(!$caught) $this->fail('No exception thrown');
25
+
26
+ $caught = false;
27
+ try {
28
+ $firephp->dump("key \n\r value", "value");
29
+ } catch(Exception $e) {
30
+ // Key passed to dump() contains invalid characters [a-zA-Z0-9-_\.:]
31
+ $caught = true;
32
+ }
33
+ if(!$caught) $this->fail('No exception thrown');
34
+
35
+ $caught = false;
36
+ try {
37
+ $firephp->dump("keykeykeykkeykeykeykkeykeykeykkeykeykeykkeykeykeykkeykeykeykkeykeykeykkeykeykeykkeykeykeykkeykeykeyk1", "value");
38
+ } catch(Exception $e) {
39
+ // Key passed to dump() is longer than 100 characters
40
+ $caught = true;
41
+ }
42
+ if(!$caught) $this->fail('No exception thrown');
43
+ }
44
+
45
+ /**
46
+ * @issue http://code.google.com/p/firephp/issues/detail?id=123
47
+ */
48
+ public function testRegisterErrorHandler()
49
+ {
50
+ $firephp = new FirePHP_Test_Class();
51
+ $firephp->setOption("maxObjectDepth", 1);
52
+ $firephp->setOption("maxArrayDepth", 1);
53
+
54
+ $firephp->registerErrorHandler();
55
+ trigger_error("Hello World");
56
+ $headers = $firephp->_getHeaders();
57
+ if(!isset($headers["X-Wf-1-1-1-1"])) {
58
+ $this->fail("Error not in headers");
59
+ }
60
+ }
61
+
62
+ /**
63
+ * @issue http://code.google.com/p/firephp/issues/detail?id=122
64
+ */
65
+ public function testFirePHPClassInstanceLogging()
66
+ {
67
+ $firephp = new FirePHP_Test_Class();
68
+
69
+ $firephp->log($firephp);
70
+ $headers = $firephp->_getHeaders();
71
+ if(!preg_match_all('/"protected:objectStack":"\\*\\* Excluded by Filter \\*\\*"/', $headers['X-Wf-1-1-1-1'], $m)) {
72
+ $this->fail("objectStack member contains value");
73
+ }
74
+ if(!preg_match_all('/"protected:static:instance":"\\*\\* Excluded by Filter \\*\\*"/', $headers['X-Wf-1-1-1-1'], $m)) {
75
+ $this->fail("instance member should not be logged");
76
+ }
77
+ if(!preg_match_all('/"undeclared:json_objectStack":"\\*\\* Excluded by Filter \\*\\*"/', $headers['X-Wf-1-1-1-1'], $m)) {
78
+ $this->fail("json_objectStack member should not be logged");
79
+ }
80
+ }
81
+
82
+ /**
83
+ * @issue http://code.google.com/p/firephp/issues/detail?id=114
84
+ */
85
+ public function testCustomFileLineOptions()
86
+ {
87
+ $firephp = new FirePHP_Test_Class();
88
+
89
+ $firephp->log("message", "label", array("File"=>"/file/path", "Line"=>"1"));
90
+ $firephp->info("message", "label", array("File"=>"/file/path", "Line"=>"1"));
91
+ $firephp->warn("message", "label", array("File"=>"/file/path", "Line"=>"1"));
92
+ $firephp->error("message", "label", array("File"=>"/file/path", "Line"=>"1"));
93
+ $firephp->dump("key", "value", array("File"=>"/file/path", "Line"=>"1"));
94
+ $firephp->table("label", array(array("header"),array("cell")), array("File"=>"/file/path", "Line"=>"1"));
95
+
96
+ $headers = $firephp->_getHeaders();
97
+
98
+ $this->assertEquals('75|[{"File":"\/file\/path","Line":"1","Type":"LOG","Label":"label"},"message"]|', $headers['X-Wf-1-1-1-1']);
99
+ $this->assertEquals('76|[{"File":"\/file\/path","Line":"1","Type":"INFO","Label":"label"},"message"]|', $headers['X-Wf-1-1-1-2']);
100
+ $this->assertEquals('76|[{"File":"\/file\/path","Line":"1","Type":"WARN","Label":"label"},"message"]|', $headers['X-Wf-1-1-1-3']);
101
+ $this->assertEquals('77|[{"File":"\/file\/path","Line":"1","Type":"ERROR","Label":"label"},"message"]|', $headers['X-Wf-1-1-1-4']);
102
+ $this->assertEquals('15|{"key":"value"}|', $headers['X-Wf-1-2-1-5']);
103
+ $this->assertEquals('89|[{"File":"\/file\/path","Line":"1","Type":"TABLE","Label":"label"},[["header"],["cell"]]]|', $headers['X-Wf-1-1-1-6']);
104
+ }
105
+
106
+ public function testRecursiveEncode()
107
+ {
108
+ $firephp = new FirePHP_Test_Class();
109
+
110
+ $obj = new FirePHPCore_FirePHPTest__TestObject();
111
+ $obj->child = $obj;
112
+
113
+ $firephp->log($obj, "label", array("File"=>"/file/path", "Line"=>"1"));
114
+ $headers = $firephp->_getHeaders();
115
+ $this->assertEquals('215|[{"File":"\/file\/path","Line":"1","Type":"LOG","Label":"label"},{"__className":"FirePHPCore_FirePHPTest__TestObject","public:var":"value","undeclared:child":"** Recursion (FirePHPCore_FirePHPTest__TestObject) **"}]|', $headers['X-Wf-1-1-1-1']);
116
+ }
117
+
118
+ public function testOptions()
119
+ {
120
+ $firephp = new FirePHP_Test_Class();
121
+
122
+ // defaults
123
+ $this->assertEquals(5, $firephp->getOption("maxObjectDepth"));
124
+ $this->assertEquals(5, $firephp->getOption("maxArrayDepth"));
125
+ $this->assertEquals(true, $firephp->getOption("useNativeJsonEncode"));
126
+ $this->assertEquals(true, $firephp->getOption("includeLineNumbers"));
127
+
128
+ // modify
129
+ $firephp->setOption("maxObjectDepth", 1);
130
+ $this->assertEquals(1, $firephp->getOption("maxObjectDepth"));
131
+
132
+ // invalid
133
+ $caught = false;
134
+ try {
135
+ $firephp->setOption("invalidName", 1);
136
+ } catch(Exception $e) {
137
+ $caught = true;
138
+ }
139
+ if(!$caught) $this->fail('No exception thrown');
140
+
141
+ $caught = false;
142
+ try {
143
+ $firephp->getOption("invalidName");
144
+ } catch(Exception $e) {
145
+ $caught = true;
146
+ }
147
+ if(!$caught) $this->fail('No exception thrown');
148
+ }
149
+
150
+ public function testDeprecatedMethods()
151
+ {
152
+ $firephp = new FirePHP_Test_Class();
153
+
154
+ $caught = false;
155
+ try {
156
+ $firephp->setProcessorUrl('URL');
157
+ } catch(Exception $e) {
158
+ $caught = true;
159
+ $this->assertEquals(E_USER_DEPRECATED, $e->getCode());
160
+ $this->assertEquals('The FirePHP::setProcessorUrl() method is no longer supported', $e->getMessage());
161
+ }
162
+ if(!$caught) $this->fail('No deprecation error thrown');
163
+
164
+ $caught = false;
165
+ try {
166
+ $firephp->setRendererUrl('URL');
167
+ } catch(Exception $e) {
168
+ $caught = true;
169
+ $this->assertEquals(E_USER_DEPRECATED, $e->getCode());
170
+ $this->assertEquals('The FirePHP::setRendererUrl() method is no longer supported', $e->getMessage());
171
+ }
172
+ if(!$caught) $this->fail('No deprecation error thrown');
173
+ }
174
+
175
+ }
176
+
177
+
178
+ class FirePHPCore_FirePHPTest__TestObject
179
+ {
180
+ public $var = "value";
181
+ }
includes/debug/browsers/api/firephp/tests/TestHelper.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ function __autoload__($class)
4
+ {
5
+ if (strpos($class, 'FirePHPCore') !== 0 && $class != 'FirePHP') {
6
+ return;
7
+ }
8
+
9
+ $basePath = dirname(dirname(__FILE__)) . '/lib';
10
+ if (!file_exists($basePath)) {
11
+ $basePath = dirname(dirname(dirname(dirname(__FILE__)))) . '/lib';
12
+ }
13
+
14
+ if ($class == 'FirePHP') {
15
+ $class = 'FirePHPCore/FirePHP.class';
16
+ }
17
+
18
+ // find relative
19
+ if (file_exists($file = $basePath . '/' . str_replace('_', '/', $class) . '.php')) {
20
+ require_once($file);
21
+ }
22
+ }
23
+
24
+ spl_autoload_register('__autoload__');
25
+
26
+ class FirePHP_Test_Class extends FirePHP {
27
+
28
+ private $_headers = array();
29
+
30
+
31
+ public function _getHeaders() {
32
+ return $this->_headers;
33
+ }
34
+ public function _clearHeaders() {
35
+ $this->_headers = array();
36
+ }
37
+
38
+
39
+ // ######################
40
+ // # Subclassed Methods #
41
+ // ######################
42
+
43
+ protected function setHeader($Name, $Value) {
44
+ $this->_headers[$Name] = $Value;
45
+ }
46
+
47
+ protected function headersSent(&$Filename, &$Linenum) {
48
+ return false;
49
+ }
50
+
51
+ public function detectClientExtension() {
52
+ return true;
53
+ }
54
+
55
+ }
includes/debug/browsers/api/firephp/tests/phpunit.xml ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <phpunit bootstrap="TestHelper.php">
2
+ </phpunit>
includes/debug/browsers/api/firephp/workspace/README.md ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ The [PINF JavaScript Loader](https://github.com/pinf/loader-js) is used to provide a development environment and package releases for this project.
3
+
4
+ **NOTE:** It is assumed you have the _PINF JavaScript Loader_ mapped to the `commonjs` command and are using the `node` platform by default as explained [here](https://github.com/pinf/loader-js/blob/master/docs/Setup.md).
5
+
6
+
7
+ Publishing
8
+ ==========
9
+
10
+ git tag v...
11
+
12
+ commonjs -v --script build .
13
+
14
+ commonjs -v --script publish .
15
+
16
+
17
+ TODO: Auto-upload to PEAR channel server at http://pear.firephp.org/
18
+
19
+ NOTE: For PEAR RC releases: Change release stability to "beta" and capitalize "RC" in release version in package.xml
includes/debug/browsers/api/firephp/workspace/lib/project.js ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+
2
+ exports.main = function(options)
3
+ {
4
+
5
+ }
includes/debug/browsers/api/firephp/workspace/package.json ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "firephp-core",
3
+ "engine": [
4
+ "node"
5
+ ],
6
+ "main": "lib/project.js",
7
+ "scripts": {
8
+ "build": {
9
+ "location": "./",
10
+ "module": "/scripts/build.js"
11
+ },
12
+ "publish": {
13
+ "location": "./",
14
+ "module": "/scripts/publish.js"
15
+ }
16
+ },
17
+ "mappings": {
18
+ "nodejs": {
19
+ "id": "nodejs.org/"
20
+ },
21
+ "pinf": {
22
+ "id": "pinf.org/loader/"
23
+ },
24
+ "modules": {
25
+ "id": "github.com/pinf/modules-js/"
26
+ }
27
+ }
28
+ }
includes/debug/browsers/api/firephp/workspace/program.json ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "boot": "workspace",
3
+ "engine": [
4
+ "node"
5
+ ],
6
+ "packages": {
7
+ "workspace": {
8
+ "locator": {
9
+ "location": "./"
10
+ }
11
+ },
12
+ "nodejs.org/": {
13
+ "provider": "nodejs.org/"
14
+ },
15
+ "pinf.org/loader/": {
16
+ "provider": "pinf.org/loader/"
17
+ },
18
+ "github.com/pinf/modules-js/": {
19
+ "locator": {
20
+ "archive": "https://github.com/pinf/modules-js/zipball/master"
21
+ }
22
+ },
23
+ "github.com/kriskowal/q/": {
24
+ "locator": {
25
+ "archive": "https://github.com/kriskowal/q/zipball/v0.3.0"
26
+ },
27
+ "descriptor": {
28
+ "uid": "https://github.com/kriskowal/q/",
29
+ "dependencies": [
30
+ {
31
+ "id": "github.com/pinf/modules-js/"
32
+ }
33
+ ]
34
+ }
35
+ },
36
+ "private-registry.appspot.com/cadorn.com/github/com.cadorn.baby/projects/sourcemint/packages/client-js/": {
37
+ "locator": {
38
+ "archive": "https://github.com/cadorn/com.cadorn.baby/zipball/master",
39
+ "path": "projects/sourcemint/packages/client-js"
40
+ }
41
+ },
42
+ "github.com/cadorn/aws-lib/": {
43
+ "locator": {
44
+ "archive": "https://github.com/cadorn/aws-lib/zipball/master"
45
+ },
46
+ "descriptor": {
47
+ "uid": "https://github.com/cadorn/aws-lib/",
48
+ "native": true,
49
+ "dependencies": [
50
+ {
51
+ "id": "registry.npmjs.org/sax/"
52
+ },
53
+ {
54
+ "id": "registry.npmjs.org/xml2js/"
55
+ }
56
+ ]
57
+ }
58
+ },
59
+ "registry.npmjs.org/sax/": {
60
+ "locator": {
61
+ "archive": "http://registry.npmjs.org/sax/-/sax-0.1.2.tgz"
62
+ },
63
+ "descriptor": {
64
+ "uid": "http://registry.npmjs.org/sax/",
65
+ "native": true
66
+ }
67
+ },
68
+ "registry.npmjs.org/xml2js/": {
69
+ "locator": {
70
+ "archive": "http://registry.npmjs.org/xml2js/-/xml2js-0.1.6.tgz"
71
+ },
72
+ "descriptor": {
73
+ "uid": "http://registry.npmjs.org/xml2js/",
74
+ "native": true
75
+ }
76
+ }
77
+ }
78
+ }
includes/debug/browsers/api/firephp/workspace/scripts/build.js ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ var FILE = require("modules/file"),
3
+ Q = require("modules/q"),
4
+ SYSTEM = require("modules/system"),
5
+ UTIL = require("modules/util"),
6
+ JSON = require("modules/json");
7
+
8
+
9
+ var pkgPath = FILE.dirname(FILE.dirname(FILE.dirname(module.id))),
10
+ buildPath = pkgPath + "/build",
11
+ tplPath = pkgPath + "/workspace/tpl",
12
+ version = false;
13
+
14
+ exports.getBuildPath = function()
15
+ {
16
+ return buildPath;
17
+ }
18
+
19
+ exports.main = function()
20
+ {
21
+
22
+ SYSTEM.exec("rm -Rf " + buildPath, function()
23
+ {
24
+ FILE.mkdirs(buildPath, 0775);
25
+
26
+ SYSTEM.exec("git tag", function(stdout)
27
+ {
28
+ version = UTIL.trim(stdout).split("\n").pop().match(/^v(.*)$/)[1];
29
+
30
+ // TODO: Compare against version in `../../program.json ~ version` (ensure =)
31
+
32
+ module.print("\0cyan(Building version: " + version + "\0)\n");
33
+
34
+ buildZipArchive(function()
35
+ {
36
+ buildPEARArchive(function()
37
+ {
38
+ done();
39
+ });
40
+ });
41
+ });
42
+ });
43
+
44
+ function done()
45
+ {
46
+ module.print("\0green(Done\0)\n");
47
+ }
48
+ }
49
+
50
+ function buildZipArchive(callback)
51
+ {
52
+ var targetBasePath = buildPath + "/FirePHPCore-" + version;
53
+
54
+ FILE.mkdirs(targetBasePath, 0775);
55
+
56
+ SYSTEM.exec("rsync -r --copy-links --exclude \"- .DS_Store\" --exclude \"- .git/\" --exclude \"- .tmp_*\" " + pkgPath + "/lib " + targetBasePath, function()
57
+ {
58
+ replaceVariablesInFile(targetBasePath + "/lib/FirePHPCore/FirePHP.class.php");
59
+ replaceVariablesInFile(targetBasePath + "/lib/FirePHPCore/FirePHP.class.php4");
60
+
61
+ SYSTEM.exec("cp -Rf " + pkgPath + "/examples " + targetBasePath, function()
62
+ {
63
+ next1();
64
+ });
65
+ });
66
+
67
+ function next1()
68
+ {
69
+ var content = FILE.read(tplPath + "/readme.tpl.md");
70
+ content = content.replace(/%%VERSION%%/g, version);
71
+ FILE.write(targetBasePath + "/README.md", content);
72
+
73
+ var content = FILE.read(tplPath + "/license.tpl.md");
74
+ FILE.write(targetBasePath + "/LICENSE.md", content);
75
+
76
+ FILE.write(buildPath + "/info.json", JSON.encode({
77
+ version: version
78
+ }));
79
+
80
+ next2();
81
+ }
82
+
83
+ function next2()
84
+ {
85
+ SYSTEM.exec("cd " + buildPath + " ; zip -vr FirePHPCore-" + version + ".zip FirePHPCore-" + version, function(stdout)
86
+ {
87
+ console.log(stdout);
88
+
89
+ callback();
90
+ });
91
+ }
92
+ }
93
+
94
+ function buildPEARArchive(callback)
95
+ {
96
+ var targetBasePath = buildPath + "/pear";
97
+
98
+ FILE.mkdirs(targetBasePath, 0775);
99
+
100
+ SYSTEM.exec("rsync -r --copy-links --exclude \"- .DS_Store\" --exclude \"- .git/\" --exclude \"- .tmp_*\" " + pkgPath + "/lib/FirePHPCore/* " + targetBasePath, function()
101
+ {
102
+ replaceVariablesInFile(targetBasePath + "/FirePHP.class.php");
103
+ replaceVariablesInFile(targetBasePath + "/FirePHP.class.php4");
104
+
105
+ next1();
106
+ });
107
+
108
+ function next1()
109
+ {
110
+ var content = FILE.read(tplPath + "/pear.package.tpl.xml");
111
+
112
+ var date = new Date();
113
+ content = content.replace(/%%DATE%%/g, date.getFullYear() + "-" + UTIL.padBegin(date.getMonth()+1, 2, "0") + "-" + date.getDate());
114
+ content = content.replace(/%%VERSION%%/g, version);
115
+ content = content.replace(/%%STABILITY%%/g, "stable");
116
+
117
+ FILE.write(targetBasePath + "/package.xml", content);
118
+
119
+ next2();
120
+ }
121
+
122
+ function next2()
123
+ {
124
+ SYSTEM.exec("pear channel-discover pear.firephp.org", function(stdout)
125
+ {
126
+ console.log(stdout);
127
+
128
+ SYSTEM.exec("cd " + targetBasePath + "; pear package package.xml", function(stdout)
129
+ {
130
+ console.log(stdout);
131
+
132
+ callback();
133
+ });
134
+ });
135
+ }
136
+ }
137
+
138
+ function replaceVariablesInFile(path)
139
+ {
140
+ var content = FILE.read(path);
141
+
142
+ // @pinf replace '0.3' with '%%VERSION%%'
143
+ var re1 = /\n(.*)\/\/\s*@pinf\s(.*)\n/g;
144
+ var match1;
145
+ while (match1 = re1.exec(content)) {
146
+ var rule = match1[2].match(/^replace (.*?) with (.*)$/);
147
+ if(rule) {
148
+ // replace variables in rule
149
+ var re2 = /%%([^%]*)%%/g;
150
+ var match2;
151
+ while (match2 = re2.exec(rule[2])) {
152
+ var value;
153
+ if(match2[1]=="VERSION") {
154
+ value = version;
155
+ }
156
+ rule[2] = rule[2].replace(match2[0], value);
157
+ }
158
+ match1[1] = match1[1].replace(rule[1], rule[2]);
159
+ content = content.replace(match1[0], "\n"+match1[1]+"\n");
160
+ }
161
+ }
162
+
163
+ FILE.write(path, content);
164
+ }
includes/debug/browsers/api/firephp/workspace/scripts/publish.js ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ var PINF_LOADER = require("pinf/loader"),
3
+ SANDBOX = PINF_LOADER.getSandbox(),
4
+ FILE = require("modules/file"),
5
+ Q = require("modules/q"),
6
+ SYSTEM = require("modules/system"),
7
+ BUILD = require("./build"),
8
+ JSON = require("modules/json"),
9
+ SOURCEMINT_CLIENT = false;
10
+
11
+ exports.main = function()
12
+ {
13
+ module.load({
14
+ id: "private-registry.appspot.com/cadorn.com/github/com.cadorn.baby/projects/sourcemint/packages/client-js/",
15
+ descriptor: {
16
+ main: "lib/client.js"
17
+ }
18
+ }, function(id)
19
+ {
20
+ SOURCEMINT_CLIENT = require(id);
21
+
22
+ publish();
23
+ });
24
+ }
25
+
26
+ function publish()
27
+ {
28
+ var buildPath = BUILD.getBuildPath(),
29
+ info = JSON.decode(FILE.read(buildPath + "/info.json")),
30
+ descriptor = JSON.decode(FILE.read(FILE.dirname(FILE.dirname(FILE.dirname(module.id))) + "/package.json"));
31
+
32
+ var bundles = {};
33
+ bundles["firephp-core.zip"] = {
34
+ "type": "zip",
35
+ "options": {
36
+ "archivePath": buildPath + "/FirePHPCore-" + info.version + ".zip",
37
+ }
38
+ };
39
+
40
+ var packages = [
41
+ {
42
+ "uid": descriptor.uid,
43
+ "stream": "stable",
44
+ "version": info.version,
45
+ "bundles": bundles
46
+ }
47
+ ];
48
+
49
+ try
50
+ {
51
+ Q.when(SOURCEMINT_CLIENT.publish(packages), function(info)
52
+ {
53
+ module.print("\0green(Published:\n");
54
+ console.log(info);
55
+ module.print("\0)");
56
+ }, function(e)
57
+ {
58
+ throw e;
59
+ });
60
+ }
61
+ catch(e)
62
+ {
63
+ console.error("Error: " + e);
64
+ }
65
+ }
includes/debug/browsers/api/firephp/workspace/tpl/license.tpl.md ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [MIT License](http://www.opensource.org/licenses/mit-license.php)
2
+
3
+ Copyright (c) 2007+ [Christoph Dorn](http://www.christophdorn.com/)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all 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,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
includes/debug/browsers/api/firephp/workspace/tpl/pear.package.tpl.xml ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <package packagerversion="1.7.1"
3
+ version="2.0"
4
+ xmlns="http://pear.php.net/dtd/package-2.0"
5
+ xmlns:tasks="http://pear.php.net/dtd/tasks-1.0"
6
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
7
+ xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
8
+
9
+ <name>FirePHPCore</name>
10
+ <channel>pear.firephp.org</channel>
11
+ <summary>Log variables from PHP to the browser (Firebug Console)</summary>
12
+ <description>Handles all communication between the PHP code on the server and the client. Also implements all core FirePHP features.</description>
13
+
14
+ <lead>
15
+ <name>Christoph Dorn</name>
16
+ <user>cadorn</user>
17
+ <email>christoph@christophdorn.com</email>
18
+ <active>yes</active>
19
+ </lead>
20
+
21
+ <date>%%DATE%%</date>
22
+ <version>
23
+ <release>%%VERSION%%</release>
24
+ <api>0.3</api>
25
+ </version>
26
+
27
+ <stability>
28
+ <release>%%STABILITY%%</release>
29
+ <api>stable</api>
30
+ </stability>
31
+
32
+ <license>MIT</license>
33
+
34
+ <notes>No Notes</notes>
35
+
36
+ <contents>
37
+ <dir name="/" baseinstalldir="FirePHPCore">
38
+
39
+ <file name="fb.php" role="php"/>
40
+ <file name="fb.php4" role="php"/>
41
+
42
+ <file name="FirePHP.class.php" role="php"/>
43
+ <file name="FirePHP.class.php4" role="php"/>
44
+
45
+ </dir>
46
+ </contents>
47
+
48
+ <dependencies>
49
+ <required>
50
+ <php>
51
+ <min>4.0</min>
52
+ </php>
53
+ <pearinstaller>
54
+ <min>1.4.5</min>
55
+ </pearinstaller>
56
+ </required>
57
+ </dependencies>
58
+
59
+ <phprelease />
60
+
61
+ </package>
includes/debug/browsers/api/firephp/workspace/tpl/readme.tpl.md ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FirePHPCore Server Library
2
+ ==========================
3
+
4
+ Status: stable
5
+
6
+ Version: [%%VERSION%%](https://github.com/firephp/firephp-core/tree/v%%VERSION%%)
7
+
8
+ This archive contains the *FirePHPCore* PHP server library.
9
+
10
+ Links
11
+ -----
12
+
13
+ * Documentation: http://docs.sourcemint.org/firephp.org/firephp/1/-docs/
14
+ * Install: http://docs.sourcemint.org/firephp.org/firephp/1/-docs/Configuration/Traditional
15
+ * Support: http://docs.sourcemint.org/firephp.org/firephp/1/-docs/OpenSource#support
16
+ * Author: [Christoph Dorn](http://www.christophdorn.com/)
17
+ * License: [MIT License](http://www.opensource.org/licenses/mit-license.php)
includes/debug/classes/wpstgDebug.class.php ADDED
@@ -0,0 +1,277 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /* wpstgDebug
4
+ *
5
+ * Based on WP Log in browser by MZAWeb
6
+ * http://wordpress.org/plugins/wp-log-in-browser
7
+ *
8
+ * @package WPSTG
9
+ * @subpackage Classes/debug
10
+ * @copyright Copyright (c) 2015, René Hermenau
11
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
12
+ * @since 0.9.0
13
+ * @author MZAWeb
14
+ * @author Rene Hermenau
15
+ *
16
+ */
17
+
18
+ if ( class_exists( "wpstgDebug" ) )
19
+ return;
20
+
21
+ class wpstgDebug implements iWPstgDebug {
22
+
23
+ private static $instance;
24
+ private $path;
25
+
26
+ private $interfaces = array();
27
+ private static $timers = array();
28
+ private static $memory = array();
29
+
30
+ public function __construct() {
31
+
32
+ $this->path = trailingslashit( dirname( dirname( __FILE__ ) ) );
33
+
34
+ $this->_get_interfaces();
35
+
36
+ add_action( 'init', array( $this, 'init' ) );
37
+ add_action( 'shutdown', array( $this, 'shutdown' ) );
38
+
39
+ }
40
+
41
+ /************* API **************/
42
+
43
+ /**
44
+ *
45
+ * @param mixed $var
46
+ * @param string $label
47
+ *
48
+ * @return wpstgDebug
49
+ */
50
+ public function log( $var, $label = null ) {
51
+ $this->_run( 'log', array( $var, $label ) );
52
+ return $this;
53
+ }
54
+
55
+ /**
56
+ *
57
+ * @param mixed $var
58
+ * @param string $label
59
+ *
60
+ * @return wpstgDebug
61
+ */
62
+ public function info( $var, $label = null ) {
63
+ $this->_run( 'info', array( $var, $label ) );
64
+ return $this;
65
+ }
66
+
67
+ /**
68
+ * @param mixed $var
69
+ * @param string $label
70
+ *
71
+ * @return wpstgDebug
72
+ */
73
+ public function warn( $var, $label = null ) {
74
+ $this->_run( 'warn', array( $var, $label ) );
75
+ return $this;
76
+ }
77
+
78
+ /**
79
+ * @param mixed $var
80
+ * @param string $label
81
+ *
82
+ * @return wpstgDebug
83
+ */
84
+ public function error( $var, $label = null ) {
85
+ $this->_run( 'error', array( $var, $label ) );
86
+ return $this;
87
+ }
88
+
89
+ /**
90
+ * @static
91
+ *
92
+ * @param string $key
93
+ * @param bool $log
94
+ *
95
+ * @return bool|float
96
+ *
97
+ */
98
+ public function timer( $key, $log = false ) {
99
+ if ( !isset( self::$timers[$key] ) ) {
100
+ $time = microtime();
101
+ $time = explode( ' ', $time );
102
+ $time = $time[1] + $time[0];
103
+ self::$timers[$key] = $time;
104
+ return false;
105
+
106
+ } else {
107
+ $time = microtime();
108
+ $time = explode( ' ', $time );
109
+ $time = $time[1] + $time[0];
110
+ $finish = $time;
111
+ $total_time = round( ( $finish - self::$timers[$key] ), 4 );
112
+
113
+ if ( $log )
114
+ $this->log( $total_time, $key );
115
+
116
+ unset( self::$timers[$key] );
117
+
118
+ return $total_time;
119
+ }
120
+ }
121
+
122
+
123
+ /**
124
+ * @static
125
+ *
126
+ * @param string $key
127
+ * @param bool $log
128
+ *
129
+ * @return bool|float
130
+ *
131
+ */
132
+ public function memory( $key, $log = false ) {
133
+ if ( !isset( self::$memory[$key] ) ) {
134
+ $memory = memory_get_usage();
135
+ self::$memory[$key] = $memory;
136
+
137
+ return false;
138
+ } else {
139
+ $memory = memory_get_usage();
140
+ $total_memory = $memory - self::$memory[$key];
141
+
142
+ if ( $log )
143
+ $this->log( $total_memory, $key );
144
+
145
+ unset( self::$memory[$key] );
146
+
147
+ return $total_memory;
148
+ }
149
+ }
150
+
151
+ /************* API **************/
152
+
153
+ public function init() {
154
+ ob_start();
155
+ }
156
+
157
+ public function shutdown() {
158
+ if ( ob_get_level() )
159
+ ob_end_flush();
160
+ }
161
+
162
+ /**
163
+ * @return bool
164
+ */
165
+ private function _should_run() {
166
+ global $wpstg_options;
167
+ //$enabled = apply_filters( 'wplinb-enabled', true );
168
+ $match_wp_debug = apply_filters( 'wplinb-match-wp-debug', false );
169
+ $enabled = isset($wpstg_options['debug_mode']) ? $wpstg_options['debug_mode'] : false;
170
+
171
+ if ( !$enabled )
172
+ return false;
173
+
174
+ if ( !$match_wp_debug )
175
+ return true;
176
+
177
+ if ( WP_DEBUG )
178
+ return true;
179
+
180
+ return false;
181
+
182
+ }
183
+
184
+ /**
185
+ * @param string $command
186
+ * @param array $params
187
+ */
188
+ private function _run( $command, $params = array() ) {
189
+
190
+ if ( !$this->_should_run() )
191
+ return;
192
+
193
+ if ( empty( $this->interfaces ) )
194
+ return;
195
+
196
+ foreach ( $this->interfaces as $interface ) {
197
+ try {
198
+ call_user_func_array( array( $interface, $command ), $params );
199
+ } catch ( Exception $e ) {
200
+
201
+ }
202
+ }
203
+ }
204
+
205
+ /**
206
+ *
207
+ */
208
+ private function _get_interfaces() {
209
+ // This will come from the admin config
210
+ $selected_interfaces = array( 'FirePHP', 'ChromePHP' );
211
+
212
+ $selected_interfaces = apply_filters( 'wplinb-selected-interfaces', $selected_interfaces );
213
+
214
+ foreach ( (array)$selected_interfaces as $interface_name ) {
215
+ $interface = $this->_get_interface( $interface_name );
216
+ if ( !empty( $interface ) )
217
+ $this->interfaces[] = $interface;
218
+
219
+ }
220
+ }
221
+
222
+ /**
223
+ * @param $interface_name
224
+ *
225
+ * @return mixed|null|WPChromePHP|WPFirePHP
226
+ */
227
+ private function _get_interface( $interface_name ) {
228
+
229
+ switch ( $interface_name ) {
230
+
231
+ case 'FirePHP':
232
+ include $this->path . 'browsers/WPFirePHP.class.php';
233
+ $interface = new WPFirePHP();
234
+ break;
235
+
236
+ case 'ChromePHP':
237
+ include $this->path . 'browsers/WPChromePHP.class.php';
238
+ $interface = new WPChromePHP();
239
+ break;
240
+
241
+ default:
242
+ $interface = apply_filters( 'wplinb-get-interface', null, $interface_name );
243
+
244
+ if ( $interface && !in_array( 'iWPstgDebug', class_implements( $interface ) ) )
245
+ $interface = null;
246
+
247
+ break;
248
+ }
249
+
250
+ return $interface;
251
+ }
252
+
253
+ /**
254
+ * @static
255
+ * @return wpstgDebug
256
+ *
257
+ * Return an instance of this class. Singleton.
258
+ */
259
+ public static function instance() {
260
+ if ( !isset( self::$instance ) ) {
261
+ $className = __CLASS__;
262
+ self::$instance = new $className;
263
+ }
264
+ return self::$instance;
265
+ }
266
+ }
267
+
268
+ if ( !function_exists( 'wpstgdebug' ) ) {
269
+ /**
270
+ * Returns a wpstgDebug instance.
271
+ *
272
+ * @return wpstgDebug
273
+ */
274
+ function wpstgdebug() {
275
+ return wpstgDebug::instance();
276
+ }
277
+ }
includes/debug/classes/wpstgDebug.interface.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ interface iWPstgDebug {
3
+
4
+ public function log( $var, $label = null );
5
+
6
+ public function info( $var, $label = null );
7
+
8
+ public function warn( $var, $label = null );
9
+
10
+ public function error( $var, $label = null );
11
+ }
includes/install.php ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Install Function
4
+ *
5
+ * @package WPSTG
6
+ * @subpackage Functions/Install
7
+ * @copyright Copyright (c) 2015, René Hermenau
8
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9
+ * @since 2.0
10
+ */
11
+
12
+ // Exit if accessed directly
13
+ if ( ! defined( 'ABSPATH' ) ) exit;
14
+
15
+ /* Install Multisite
16
+ * check first if multisite is enabled
17
+ * @since 0.9.0
18
+ *
19
+ */
20
+
21
+ register_activation_hook( WPSTG_PLUGIN_FILE, 'wpstg_install_multisite' );
22
+
23
+ function wpstg_install_multisite($networkwide) {
24
+ global $wpdb;
25
+
26
+ if (function_exists('is_multisite') && is_multisite()) {
27
+ // check if it is a network activation - if so, run the activation function for each blog id
28
+ if ($networkwide) {
29
+ $old_blog = $wpdb->blogid;
30
+ // Get all blog ids
31
+ $blogids = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs");
32
+ foreach ($blogids as $blog_id) {
33
+ switch_to_blog($blog_id);
34
+ wpstg_install();
35
+ }
36
+ switch_to_blog($old_blog);
37
+ return;
38
+ }
39
+ }
40
+ wpstg_install();
41
+ }
42
+
43
+ /**
44
+ * Install
45
+ *
46
+ * Runs on plugin install to populates the settings fields for those plugin
47
+ * pages. After successful install, the user is redirected to the WPSTG Welcome
48
+ * screen.
49
+ *
50
+ * @since 0.9.0
51
+ * @global $wpdb
52
+ * @global $wpstg_options
53
+ * @global $wp_version
54
+ * @return void
55
+ */
56
+
57
+
58
+
59
+ function wpstg_install() {
60
+ global $wpdb, $wpstg_options, $wp_version;
61
+
62
+
63
+ // Add Upgraded from Option
64
+ $current_version = get_option( 'wpstg_version' );
65
+ if ( $current_version ) {
66
+ update_option( 'wpstg_version_upgraded_from', $current_version );
67
+ }
68
+
69
+ // Update the current version
70
+ update_option( 'wpstg_version', WPSTG_VERSION );
71
+ // Add plugin installation date and variable for rating div
72
+ add_option('wpstg_installDate',date('Y-m-d h:i:s'));
73
+ add_option('wpstg_RatingDiv','no');
74
+ // Add First-time variables
75
+ add_option('wpstg_firsttime','true');
76
+ add_option('wpstg_is_staging_site','false');
77
+ // Show beta notice
78
+ add_option('wpstg_hide_beta','no');
79
+
80
+ // Create empty config files in /wp-content/uploads/wp-staging
81
+ wpstg_create_remaining_files();
82
+ wpstg_create_clonedetails_files();
83
+
84
+
85
+
86
+ /* Setup some default options
87
+ * Store our initial social networks in separate option row.
88
+ * For easier modification and to prevent some trouble
89
+ */
90
+
91
+ // Bail if activating from network, or bulk
92
+ if ( is_network_admin() || isset( $_GET['activate-multi'] ) ) {
93
+ return;
94
+ }
95
+
96
+ // Add the transient to redirect / not for multisites
97
+ set_transient( '_wpstg_activation_redirect', true, 30 );
98
+
99
+ }
100
+
101
+ /**
102
+ * Post-installation
103
+ *
104
+ * Runs just after plugin installation and exposes the
105
+ * wpstg_after_install hook.
106
+ *
107
+ * @since 2.0
108
+ * @return void
109
+ */
110
+ function wpstg_after_install() {
111
+
112
+ if ( ! is_admin() ) {
113
+ return;
114
+ }
115
+
116
+
117
+ $activation_pages = get_transient( '_wpstg_activation_pages' );
118
+
119
+ // Exit if not in admin or the transient doesn't exist
120
+ if ( false === $activation_pages ) {
121
+ return;
122
+ }
123
+
124
+ // Delete the transient
125
+ delete_transient( '_wpstg_activation_pages' );
126
+
127
+ do_action( 'wpstg_after_install', $activation_pages );
128
+ }
129
+ add_action( 'admin_init', 'wpstg_after_install' );
130
+
131
+
132
+ /**
133
+ * Create json remaining_files.json after activation of the plugin
134
+ *
135
+ * @return bool
136
+ */
137
+ function wpstg_create_remaining_files() {
138
+ $path = wpstg_get_upload_dir();
139
+ if (wp_is_writable($path)) {
140
+ $file = 'remaining_files.json';
141
+ file_put_contents($path . '/' . $file, null);
142
+ }else {
143
+ WPSTG()->logger->info($path . '/' . $file . ' is not writeable! ');
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Create json cloning_details.json after activation of the plugin
149
+ *
150
+ * @return bool
151
+ */
152
+ function wpstg_create_clonedetails_files() {
153
+ $path = wpstg_get_upload_dir();
154
+ if (wp_is_writable($path)) {
155
+ $file = 'clone_details.json';
156
+ file_put_contents($path . '/' . $file, null);
157
+ }else {
158
+ WPSTG()->logger->info($path . '/' . $file . ' is not writeable! ');
159
+ }
160
+ }
includes/libraries/RolingCurlX.php ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * Based on RollingCurlX https://github.com/marcushat/rollingcurlx
4
+ *
5
+ * Modified for WP-Staging by René Hermenau to be compatible with php 5.3 and older versions
6
+ *
7
+ * @scince 2.2.7
8
+ */
9
+ Class RollingCurlX {
10
+ private $_maxConcurrent = 0; //max. number of simultaneous connections allowed
11
+ private $_options = array(); //shared cURL options
12
+ private $_headers = array(); //shared cURL request headers
13
+ private $_callback = NULL; //default callback
14
+ private $_timeout = 1; //timeout used for curl_multi_select() function in seconds. Lower this to zero increases total performance.
15
+ private $requests = array(); //request_queue
16
+
17
+ function __construct($max_concurrent = 10) {
18
+ $this->setMaxConcurrent($max_concurrent);
19
+ }
20
+
21
+ public function setMaxConcurrent($max_requests) {
22
+ if($max_requests > 0) {
23
+ $this->_maxConcurrent = $max_requests;
24
+ }
25
+ }
26
+
27
+ public function setOptions(array $options) {
28
+ $this->_options = $options;
29
+ }
30
+
31
+ public function setHeaders(array $headers) {
32
+ if(is_array($headers) && count($headers)) {
33
+ $this->_headers = $headers;
34
+ }
35
+ }
36
+
37
+
38
+ //public function setCallback(callable $callback) {
39
+ public function setCallback($callback) {
40
+ $this->_callback = $callback;
41
+ }
42
+
43
+ /* Bug on php 5.3 Do not use this
44
+
45
+ */
46
+ /*public function setTimeout($timeout) { //in milliseconds
47
+ if($timeout > 0) {
48
+ $this->_timeout = $timeout/1000; //to seconds
49
+ }
50
+ }*/
51
+
52
+ //Add a request to the request queue
53
+ public function addRequest(
54
+ $url,
55
+ array $post_data = NULL,
56
+ //callable $callback = NULL, //individual callback rhe
57
+ //$callback = NULL, //individual callback
58
+ array $callback = NULL,
59
+ $user_data = NULL,
60
+ array $options = NULL, //individual cURL options
61
+ array $headers = NULL //individual cURL request headers
62
+ ) { //Add to request queue
63
+ $this->requests[] = array(
64
+ 'url' => $url,
65
+ 'post_data' => ($post_data) ? $post_data : NULL,
66
+ 'callback' => ($callback) ? $callback : $this->_callback,
67
+ 'user_data' => ($user_data) ? $user_data : NULL,
68
+ 'options' => ($options) ? $options : NULL,
69
+ 'headers' => ($headers) ? $headers : NULL
70
+ );
71
+ return count($this->requests) - 1; //return request number/index
72
+ }
73
+
74
+ //Reset request queue
75
+ public function reset() {
76
+ $this->requests = array();
77
+ }
78
+
79
+ //Execute the request queue
80
+ public function execute() {
81
+ if(count($this->requests) < $this->_maxConcurrent) {
82
+ $this->_maxConcurrent = count($this->requests);
83
+ }
84
+ //the request map that maps the request queue to request curl handles
85
+ $requests_map = array();
86
+ $multi_handle = curl_multi_init();
87
+ //start processing the initial request queue
88
+ for($i = 0; $i < $this->_maxConcurrent; $i++) {
89
+ $ch = curl_init();
90
+
91
+ $request =& $this->requests[$i];
92
+ $this->addTimer($request);
93
+
94
+ curl_setopt_array($ch, $this->buildOptions($request));
95
+ curl_multi_add_handle($multi_handle, $ch);
96
+
97
+
98
+ //add curl handle of a request to the request map
99
+ $key = (string) $ch;
100
+ $requests_map[$key] = $i;
101
+ }
102
+ do{
103
+ while(($mh_status = curl_multi_exec($multi_handle, $active)) == CURLM_CALL_MULTI_PERFORM);
104
+ if($mh_status != CURLM_OK) {
105
+ break;
106
+ }
107
+
108
+ //a request is just completed, find out which one
109
+ while($completed = curl_multi_info_read($multi_handle)) {
110
+ $ch = $completed['handle'];
111
+ $request_info = curl_getinfo($ch);
112
+ if(curl_errno($ch) !== 0 || intval($request_info['http_code']) !== 200) { //if server responded with http error
113
+ $response = false;
114
+ } else { //sucessful response
115
+ $response = curl_multi_getcontent($ch);
116
+ }
117
+
118
+ //get request info
119
+ $key = (string) $ch;
120
+ $request =& $this->requests[$requests_map[$key]]; //map handler to request index to get request info
121
+ $url = $request['url'];
122
+ $callback = $request['callback'];
123
+ $user_data = $request['user_data'];
124
+ $options = $request['options'];
125
+ $this->stopTimer($request); //record request time
126
+ $time = $request['time'];
127
+
128
+ if($response && (isset($this->_options[CURLOPT_HEADER]) || isset($options[CURLOPT_HEADER]))) {
129
+ $k = intval($request_info['header_size']);
130
+ $request_info['response_header'] = substr($response, 0, $k);
131
+ $response = substr($response, $k);
132
+ }
133
+
134
+ //remove completed request and its curl handle
135
+ unset($requests_map[$key]);
136
+ curl_multi_remove_handle($multi_handle, $ch);
137
+
138
+ //call the callback function and pass request info and user data to it
139
+ if($callback) {
140
+ call_user_func($callback, $response, $url, $request_info, $user_data, $time);
141
+ }
142
+ $request = NULL; //free up memory now just incase response was large
143
+
144
+ //add/start a new request to the request queue
145
+ if($i < count($this->requests) && isset($this->requests[$i])) { //if requests left
146
+ $ch = curl_init();
147
+
148
+ $request =& $this->requests[$i];
149
+ $this->addTimer($request);
150
+
151
+ curl_setopt_array($ch, $this->buildOptions($request));
152
+ curl_multi_add_handle($multi_handle, $ch);
153
+
154
+ //add curl handle of a new request to the request map
155
+ $key = (string) $ch;
156
+ $requests_map[$key] = $i;
157
+ $i++;
158
+ }
159
+ }
160
+ if($active) {
161
+ if(curl_multi_select($multi_handle, $this->_timeout) === -1) { //wait for activity on any connection
162
+ usleep(5);
163
+ }
164
+
165
+ }
166
+ } while ($active || count($requests_map)); //End do-while
167
+
168
+ $this->reset();
169
+ curl_multi_close($multi_handle);
170
+ }
171
+
172
+
173
+
174
+ //Build individual cURL options for a request
175
+ private function buildOptions(array $request) {
176
+ $url = $request['url'];
177
+ $post_data = $request['post_data'];
178
+ $individual_opts = $request['options'];
179
+ $individual_headers = $request['headers'];
180
+
181
+ $options = ($individual_opts) ? $individual_opts + $this->_options : $this->_options; //merge shared and individual request options
182
+ $headers = ($individual_headers) ? $individual_headers + $this->_headers : $this->_headers; //merge shared and individual request headers
183
+
184
+ //the below will overide the corresponding default or individual options
185
+ $options[CURLOPT_RETURNTRANSFER] = true;
186
+ //$options[CURLOPT_TIMEOUT] = $this->_timeout; //timeout in ms. bug in php 5.3
187
+ $options[CURLOPT_TIMEOUT] = 5; //timeout in seconds;
188
+ if($url) {
189
+ $options[CURLOPT_URL] = $url;
190
+ }
191
+
192
+ if($headers) {
193
+ $options[CURLOPT_HTTPHEADER] = $headers;
194
+ }
195
+
196
+ // enable POST method and set POST parameters
197
+ if($post_data) {
198
+ $options[CURLOPT_POST] = 1;
199
+ $options[CURLOPT_POSTFIELDS] = is_array($post_data)? http_build_query($post_data) : $post_data;
200
+ }
201
+ return $options;
202
+ }
203
+
204
+
205
+
206
+ private function addTimer(array &$request) { //adds timer object to request
207
+ $request['timer'] = microtime(true);
208
+ $request['time'] = false; //default if not overridden by time later
209
+ }
210
+
211
+ private function stopTimer(array &$request) {
212
+ $start_time = $request['timer'];
213
+ $end_time = microtime(true);
214
+ $elapsed_time = rtrim(sprintf('%.20F', ($end_time - $start_time)), '0') . 'secs'; //convert float to string
215
+ $request['time'] = $elapsed_time*1000; //
216
+ unset($request['timer']);
217
+ }
218
+ }
includes/libraries/browser.php ADDED
@@ -0,0 +1,1082 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Modified to remove var
4
+ * Chris Christoff on 12/26/2012
5
+ * Changes: Changes vars to publics
6
+ *
7
+ * Modified to work for EDD by
8
+ * Chris Christoff on 12/23/2012
9
+ * Changes: Removed the browser string return and added spacing. Also removed return HTML formatting.
10
+ *
11
+ * Modified to add formatted User Agent string for EDD System Info by
12
+ * Chris Christoff on 12/23/2012
13
+ * Changes: Split user string and add formatting so we can print a nicely
14
+ * formatted user agent string on the EDD System Info
15
+ *
16
+ * File: Browser.php
17
+ * Author: Chris Schuld (http://chrisschuld.com/)
18
+ * Last Modified: August 20th, 2010
19
+ * @version 1.9
20
+ * @package PegasusPHP
21
+ *
22
+ * Copyright (C) 2008-2010 Chris Schuld (chris@chrisschuld.com)
23
+ *
24
+ * This program is free software; you can redistribute it and/or
25
+ * modify it under the terms of the GNU General Public License as
26
+ * published by the Free Software Foundation; either version 2 of
27
+ * the License, or (at your option) any later version.
28
+ *
29
+ * This program is distributed in the hope that it will be useful,
30
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
31
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32
+ * GNU General Public License for more details at:
33
+ * http://www.gnu.org/copyleft/gpl.html
34
+ *
35
+ *
36
+ * Typical Usage:
37
+ *
38
+ * $browser = new Browser();
39
+ * if( $browser->getBrowser() == Browser::BROWSER_FIREFOX && $browser->getVersion() >= 2 ) {
40
+ * echo 'You have FireFox version 2 or greater';
41
+ * }
42
+ *
43
+ * User Agents Sampled from: http://www.useragentstring.com/
44
+ *
45
+ * This implementation is based on the original work from Gary White
46
+ * http://apptools.com/phptools/browser/
47
+ *
48
+ * UPDATES:
49
+ *
50
+ * 2010-08-20 (v1.9):
51
+ * + Added MSN Explorer Browser (legacy)
52
+ * + Added Bing/MSN Robot (Thanks Rob MacDonald)
53
+ * + Added the Android Platform (PLATFORM_ANDROID)
54
+ * + Fixed issue with Android 1.6/2.2 (Thanks Tom Hirashima)
55
+ *
56
+ * 2010-04-27 (v1.8):
57
+ * + Added iPad Support
58
+ *
59
+ * 2010-03-07 (v1.7):
60
+ * + *MAJOR* Rebuild (preg_match and other "slow" routine removal(s))
61
+ * + Almost allof Gary's original code has been replaced
62
+ * + Large PHPUNIT testing environment created to validate new releases and additions
63
+ * + Added FreeBSD Platform
64
+ * + Added OpenBSD Platform
65
+ * + Added NetBSD Platform
66
+ * + Added SunOS Platform
67
+ * + Added OpenSolaris Platform
68
+ * + Added support of the Iceweazel Browser
69
+ * + Added isChromeFrame() call to check if chromeframe is in use
70
+ * + Moved the Opera check in front of the Firefox check due to legacy Opera User Agents
71
+ * + Added the __toString() method (Thanks Deano)
72
+ *
73
+ * 2009-11-15:
74
+ * + Updated the checkes for Firefox
75
+ * + Added the NOKIA platform
76
+ * + Added Checks for the NOKIA brower(s)
77
+ *
78
+ * 2009-11-08:
79
+ * + PHP 5.3 Support
80
+ * + Added support for BlackBerry OS and BlackBerry browser
81
+ * + Added support for the Opera Mini browser
82
+ * + Added additional documenation
83
+ * + Added support for isRobot() and isMobile()
84
+ * + Added support for Opera version 10
85
+ * + Added support for deprecated Netscape Navigator version 9
86
+ * + Added support for IceCat
87
+ * + Added support for Shiretoko
88
+ *
89
+ * 2010-04-27 (v1.8):
90
+ * + Added iPad Support
91
+ *
92
+ * 2009-08-18:
93
+ * + Updated to support PHP 5.3 - removed all deprecated function calls
94
+ * + Updated to remove all double quotes (") -- converted to single quotes (')
95
+ *
96
+ * 2009-04-27:
97
+ * + Updated the IE check to remove a typo and bug (thanks John)
98
+ *
99
+ * 2009-04-22:
100
+ * + Added detection for GoogleBot
101
+ * + Added detection for the W3C Validator.
102
+ * + Added detection for Yahoo! Slurp
103
+ *
104
+ * 2009-03-14:
105
+ * + Added detection for iPods.
106
+ * + Added Platform detection for iPhones
107
+ * + Added Platform detection for iPods
108
+ *
109
+ * 2009-02-16: (Rick Hale)
110
+ * + Added version detection for Android phones.
111
+ *
112
+ * 2008-12-09:
113
+ * + Removed unused constant
114
+ *
115
+ * 2008-11-07:
116
+ * + Added Google's Chrome to the detection list
117
+ * + Added isBrowser(string) to the list of functions special thanks to
118
+ * Daniel 'mavrick' Lang for the function concept (http://mavrick.id.au)
119
+ *
120
+ *
121
+ * Gary White noted: "Since browser detection is so unreliable, I am
122
+ * no longer maintaining this script. You are free to use and or
123
+ * modify/update it as you want, however the author assumes no
124
+ * responsibility for the accuracy of the detected values."
125
+ *
126
+ * Anyone experienced with Gary's script might be interested in these notes:
127
+ *
128
+ * Added class constants
129
+ * Added detection and version detection for Google's Chrome
130
+ * Updated the version detection for Amaya
131
+ * Updated the version detection for Firefox
132
+ * Updated the version detection for Lynx
133
+ * Updated the version detection for WebTV
134
+ * Updated the version detection for NetPositive
135
+ * Updated the version detection for IE
136
+ * Updated the version detection for OmniWeb
137
+ * Updated the version detection for iCab
138
+ * Updated the version detection for Safari
139
+ * Updated Safari to remove mobile devices (iPhone)
140
+ * Added detection for iPhone
141
+ * Added detection for robots
142
+ * Added detection for mobile devices
143
+ * Added detection for BlackBerry
144
+ * Removed Netscape checks (matches heavily with firefox & mozilla)
145
+ *
146
+ */
147
+
148
+ class Browser {
149
+ public $_agent = '';
150
+ public $_browser_name = '';
151
+ public $_version = '';
152
+ public $_platform = '';
153
+ public $_os = '';
154
+ public $_is_aol = false;
155
+ public $_is_mobile = false;
156
+ public $_is_robot = false;
157
+ public $_aol_version = '';
158
+
159
+ public $BROWSER_UNKNOWN = 'unknown';
160
+ public $VERSION_UNKNOWN = 'unknown';
161
+
162
+ public $BROWSER_OPERA = 'Opera'; // Http://www.opera.com/
163
+ public $BROWSER_OPERA_MINI = 'Opera Mini'; // Http://www.opera.com/mini/
164
+ public $BROWSER_WEBTV = 'WebTV'; // Http://www.webtv.net/pc/
165
+ public $BROWSER_IE = 'Internet Explorer'; // Http://www.microsoft.com/ie/
166
+ public $BROWSER_POCKET_IE = 'Pocket Internet Explorer'; // Http://en.wikipedia.org/wiki/Internet_Explorer_Mobile
167
+ public $BROWSER_KONQUEROR = 'Konqueror'; // Http://www.konqueror.org/
168
+ public $BROWSER_ICAB = 'iCab'; // Http://www.icab.de/
169
+ public $BROWSER_OMNIWEB = 'OmniWeb'; // Http://www.omnigroup.com/applications/omniweb/
170
+ public $BROWSER_FIREBIRD = 'Firebird'; // Http://www.ibphoenix.com/
171
+ public $BROWSER_FIREFOX = 'Firefox'; // Http://www.mozilla.com/en-US/firefox/firefox.html
172
+ public $BROWSER_ICEWEASEL = 'Iceweasel'; // Http://www.geticeweasel.org/
173
+ public $BROWSER_SHIRETOKO = 'Shiretoko'; // Http://wiki.mozilla.org/Projects/shiretoko
174
+ public $BROWSER_MOZILLA = 'Mozilla'; // Http://www.mozilla.com/en-US/
175
+ public $BROWSER_AMAYA = 'Amaya'; // Http://www.w3.org/Amaya/
176
+ public $BROWSER_LYNX = 'Lynx'; // Http://en.wikipedia.org/wiki/Lynx
177
+ public $BROWSER_SAFARI = 'Safari'; // Http://apple.com
178
+ public $BROWSER_IPHONE = 'iPhone'; // Http://apple.com
179
+ public $BROWSER_IPOD = 'iPod'; // Http://apple.com
180
+ public $BROWSER_IPAD = 'iPad'; // Http://apple.com
181
+ public $BROWSER_CHROME = 'Chrome'; // Http://www.google.com/chrome
182
+ public $BROWSER_ANDROID = 'Android'; // Http://www.android.com/
183
+ public $BROWSER_GOOGLEBOT = 'GoogleBot'; // Http://en.wikipedia.org/wiki/Googlebot
184
+ public $BROWSER_SLURP = 'Yahoo! Slurp'; // Http://en.wikipedia.org/wiki/Yahoo!_Slurp
185
+ public $BROWSER_W3CVALIDATOR = 'W3C Validator'; // Http://validator.w3.org/
186
+ public $BROWSER_BLACKBERRY = 'BlackBerry'; // Http://www.blackberry.com/
187
+ public $BROWSER_ICECAT = 'IceCat'; // Http://en.wikipedia.org/wiki/GNU_IceCat
188
+ public $BROWSER_NOKIA_S60 = 'Nokia S60 OSS Browser'; // Http://en.wikipedia.org/wiki/Web_Browser_for_S60
189
+ public $BROWSER_NOKIA = 'Nokia Browser'; // * all other WAP-based browsers on the Nokia Platform
190
+ public $BROWSER_MSN = 'MSN Browser'; // Http://explorer.msn.com/
191
+ public $BROWSER_MSNBOT = 'MSN Bot'; // Http://search.msn.com/msnbot.htm
192
+ // Http://en.wikipedia.org/wiki/Msnbot (used for Bing as well)
193
+
194
+ public $BROWSER_NETSCAPE_NAVIGATOR = 'Netscape Navigator'; // Http://browser.netscape.com/ (DEPRECATED)
195
+ public $BROWSER_GALEON = 'Galeon'; // Http://galeon.sourceforge.net/ (DEPRECATED)
196
+ public $BROWSER_NETPOSITIVE = 'NetPositive'; // Http://en.wikipedia.org/wiki/NetPositive (DEPRECATED)
197
+ public $BROWSER_PHOENIX = 'Phoenix'; // Http://en.wikipedia.org/wiki/History_of_Mozilla_Firefox (DEPRECATED)
198
+
199
+ public $PLATFORM_UNKNOWN = 'unknown';
200
+ public $PLATFORM_WINDOWS = 'Windows';
201
+ public $PLATFORM_WINDOWS_CE = 'Windows CE';
202
+ public $PLATFORM_APPLE = 'Apple';
203
+ public $PLATFORM_LINUX = 'Linux';
204
+ public $PLATFORM_OS2 = 'OS/2';
205
+ public $PLATFORM_BEOS = 'BeOS';
206
+ public $PLATFORM_IPHONE = 'iPhone';
207
+ public $PLATFORM_IPOD = 'iPod';
208
+ public $PLATFORM_IPAD = 'iPad';
209
+ public $PLATFORM_BLACKBERRY = 'BlackBerry';
210
+ public $PLATFORM_NOKIA = 'Nokia';
211
+ public $PLATFORM_FREEBSD = 'FreeBSD';
212
+ public $PLATFORM_OPENBSD = 'OpenBSD';
213
+ public $PLATFORM_NETBSD = 'NetBSD';
214
+ public $PLATFORM_SUNOS = 'SunOS';
215
+ public $PLATFORM_OPENSOLARIS = 'OpenSolaris';
216
+ public $PLATFORM_ANDROID = 'Android';
217
+
218
+ public $OPERATING_SYSTEM_UNKNOWN = 'unknown';
219
+
220
+ function Browser($useragent="") {
221
+ $this->reset();
222
+ if( $useragent != "" ) {
223
+ $this->setUserAgent($useragent);
224
+ }
225
+ else {
226
+ $this->determine();
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Reset all properties
232
+ */
233
+ function reset() {
234
+ $this->_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : "";
235
+ $this->_browser_name = $this->BROWSER_UNKNOWN;
236
+ $this->_version = $this->VERSION_UNKNOWN;
237
+ $this->_platform = $this->PLATFORM_UNKNOWN;
238
+ $this->_os = $this->OPERATING_SYSTEM_UNKNOWN;
239
+ $this->_is_aol = false;
240
+ $this->_is_mobile = false;
241
+ $this->_is_robot = false;
242
+ $this->_aol_version = $this->VERSION_UNKNOWN;
243
+ }
244
+
245
+ /**
246
+ * Check to see if the specific browser is valid
247
+ * @param string $browserName
248
+ * @return True if the browser is the specified browser
249
+ */
250
+ function isBrowser($browserName) { return( 0 == strcasecmp($this->_browser_name, trim($browserName))); }
251
+
252
+ /**
253
+ * The name of the browser. All return types are from the class contants
254
+ * @return string Name of the browser
255
+ */
256
+ function getBrowser() { return $this->_browser_name; }
257
+ /**
258
+ * Set the name of the browser
259
+ * @param $browser The name of the Browser
260
+ */
261
+ function setBrowser($browser) { return $this->_browser_name = $browser; }
262
+ /**
263
+ * The name of the platform. All return types are from the class contants
264
+ * @return string Name of the browser
265
+ */
266
+ function getPlatform() { return $this->_platform; }
267
+ /**
268
+ * Set the name of the platform
269
+ * @param $platform The name of the Platform
270
+ */
271
+ function setPlatform($platform) { return $this->_platform = $platform; }
272
+ /**
273
+ * The version of the browser.
274
+ * @return string Version of the browser (will only contain alpha-numeric characters and a period)
275
+ */
276
+ function getVersion() { return $this->_version; }
277
+ /**
278
+ * Set the version of the browser
279
+ * @param $version The version of the Browser
280
+ */
281
+ function setVersion($version) { $this->_version = preg_replace('/[^0-9,.,a-z,A-Z-]/','',$version); }
282
+ /**
283
+ * The version of AOL.
284
+ * @return string Version of AOL (will only contain alpha-numeric characters and a period)
285
+ */
286
+ function getAolVersion() { return $this->_aol_version; }
287
+ /**
288
+ * Set the version of AOL
289
+ * @param $version The version of AOL
290
+ */
291
+ function setAolVersion($version) { $this->_aol_version = preg_replace('/[^0-9,.,a-z,A-Z]/','',$version); }
292
+ /**
293
+ * Is the browser from AOL?
294
+ * @return boolean True if the browser is from AOL otherwise false
295
+ */
296
+ function isAol() { return $this->_is_aol; }
297
+ /**
298
+ * Is the browser from a mobile device?
299
+ * @return boolean True if the browser is from a mobile device otherwise false
300
+ */
301
+ function isMobile() { return $this->_is_mobile; }
302
+ /**
303
+ * Is the browser from a robot (ex Slurp,GoogleBot)?
304
+ * @return boolean True if the browser is from a robot otherwise false
305
+ */
306
+ function isRobot() { return $this->_is_robot; }
307
+ /**
308
+ * Set the browser to be from AOL
309
+ * @param $isAol
310
+ */
311
+ function setAol($isAol) { $this->_is_aol = $isAol; }
312
+ /**
313
+ * Set the Browser to be mobile
314
+ * @param boolean $value is the browser a mobile brower or not
315
+ */
316
+ function setMobile($value=true) { $this->_is_mobile = $value; }
317
+ /**
318
+ * Set the Browser to be a robot
319
+ * @param boolean $value is the browser a robot or not
320
+ */
321
+ function setRobot($value=true) { $this->_is_robot = $value; }
322
+ /**
323
+ * Get the user agent value in use to determine the browser
324
+ * @return string The user agent from the HTTP header
325
+ */
326
+ function getUserAgent() { return $this->_agent; }
327
+ /**
328
+ * Set the user agent value (the construction will use the HTTP header value - this will overwrite it)
329
+ * @param $agent_string The value for the User Agent
330
+ */
331
+ function setUserAgent($agent_string) {
332
+ $this->reset();
333
+ $this->_agent = $agent_string;
334
+ $this->determine();
335
+ }
336
+ /**
337
+ * Used to determine if the browser is actually "chromeframe"
338
+ * @since 1.7
339
+ * @return boolean True if the browser is using chromeframe
340
+ */
341
+ function isChromeFrame() {
342
+ return( strpos($this->_agent,"chromeframe") !== false );
343
+ }
344
+ /**
345
+ * Returns a formatted string with a summary of the details of the browser.
346
+ * @return string formatted string with a summary of the browser
347
+ */
348
+ function __toString() {
349
+ $text1 = $this->getUserAgent(); //grabs the UA (user agent) string
350
+ $UAline1 = substr($text1, 0, 32); //the first line we print should only be the first 32 characters of the UA string
351
+ $text2 = $this->getUserAgent();//now we grab it again and save it to a string
352
+ $towrapUA = str_replace($UAline1, '', $text2);//the rest of the printoff (other than first line) is equivolent
353
+ // To the whole string minus the part we printed off. IE
354
+ // User Agent: thefirst32charactersfromUAline1
355
+ // the rest of it is now stored in
356
+ // $text2 to be printed off
357
+ // But we need to add spaces before each line that is split other than line 1
358
+ $space = '';
359
+ for($i = 0; $i < 25; $i++) {
360
+ $space .= ' ';
361
+ }
362
+ // Now we split the remaining string of UA ($text2) into lines that are prefixed by spaces for formatting
363
+ $wordwrapped = chunk_split($towrapUA, 32, "\n $space");
364
+ return "Platform: {$this->getPlatform()} \n".
365
+ "Browser Name: {$this->getBrowser()} \n" .
366
+ "Browser Version: {$this->getVersion()} \n" .
367
+ "User Agent String: $UAline1 \n\t\t\t " .
368
+ "$wordwrapped";
369
+ }
370
+ /**
371
+ * Protected routine to calculate and determine what the browser is in use (including platform)
372
+ */
373
+ function determine() {
374
+ $this->checkPlatform();
375
+ $this->checkBrowsers();
376
+ $this->checkForAol();
377
+ }
378
+ /**
379
+ * Protected routine to determine the browser type
380
+ * @return boolean True if the browser was detected otherwise false
381
+ */
382
+ function checkBrowsers() {
383
+ return (
384
+ // Well-known, well-used
385
+ // Special Notes:
386
+ // (1) Opera must be checked before FireFox due to the odd
387
+ // user agents used in some older versions of Opera
388
+ // (2) WebTV is strapped onto Internet Explorer so we must
389
+ // check for WebTV before IE
390
+ // (3) (deprecated) Galeon is based on Firefox and needs to be
391
+ // tested before Firefox is tested
392
+ // (4) OmniWeb is based on Safari so OmniWeb check must occur
393
+ // before Safari
394
+ // (5) Netscape 9+ is based on Firefox so Netscape checks
395
+ // before FireFox are necessary
396
+ $this->checkBrowserWebTv() ||
397
+ $this->checkBrowserInternetExplorer() ||
398
+ $this->checkBrowserOpera() ||
399
+ $this->checkBrowserGaleon() ||
400
+ $this->checkBrowserNetscapeNavigator9Plus() ||
401
+ $this->checkBrowserFirefox() ||
402
+ $this->checkBrowserChrome() ||
403
+ $this->checkBrowserOmniWeb() ||
404
+
405
+ // Common mobile
406
+ $this->checkBrowserAndroid() ||
407
+ $this->checkBrowseriPad() ||
408
+ $this->checkBrowseriPod() ||
409
+ $this->checkBrowseriPhone() ||
410
+ $this->checkBrowserBlackBerry() ||
411
+ $this->checkBrowserNokia() ||
412
+
413
+ // Common bots
414
+ $this->checkBrowserGoogleBot() ||
415
+ $this->checkBrowserMSNBot() ||
416
+ $this->checkBrowserSlurp() ||
417
+
418
+ // WebKit base check (post mobile and others)
419
+ $this->checkBrowserSafari() ||
420
+
421
+ // Everyone else
422
+ $this->checkBrowserNetPositive() ||
423
+ $this->checkBrowserFirebird() ||
424
+ $this->checkBrowserKonqueror() ||
425
+ $this->checkBrowserIcab() ||
426
+ $this->checkBrowserPhoenix() ||
427
+ $this->checkBrowserAmaya() ||
428
+ $this->checkBrowserLynx() ||
429
+
430
+ $this->checkBrowserShiretoko() ||
431
+ $this->checkBrowserIceCat() ||
432
+ $this->checkBrowserW3CValidator() ||
433
+ $this->checkBrowserMozilla() /* Mozilla is such an open standard that you must check it last */
434
+ );
435
+ }
436
+
437
+ /**
438
+ * Determine if the user is using a BlackBerry (last updated 1.7)
439
+ * @return boolean True if the browser is the BlackBerry browser otherwise false
440
+ */
441
+ function checkBrowserBlackBerry() {
442
+ if( stripos($this->_agent,'blackberry') !== false ) {
443
+ $aresult = explode("/",stristr($this->_agent,"BlackBerry"));
444
+ $aversion = explode(' ',$aresult[1]);
445
+ $this->setVersion($aversion[0]);
446
+ $this->_browser_name = $this->BROWSER_BLACKBERRY;
447
+ $this->setMobile(true);
448
+ return true;
449
+ }
450
+ return false;
451
+ }
452
+
453
+ /**
454
+ * Determine if the user is using an AOL User Agent (last updated 1.7)
455
+ * @return boolean True if the browser is from AOL otherwise false
456
+ */
457
+ function checkForAol() {
458
+ $this->setAol(false);
459
+ $this->setAolVersion($this->VERSION_UNKNOWN);
460
+
461
+ if( stripos($this->_agent,'aol') !== false ) {
462
+ $aversion = explode(' ',stristr($this->_agent, 'AOL'));
463
+ $this->setAol(true);
464
+ $this->setAolVersion(preg_replace('/[^0-9\.a-z]/i', '', $aversion[1]));
465
+ return true;
466
+ }
467
+ return false;
468
+ }
469
+
470
+ /**
471
+ * Determine if the browser is the GoogleBot or not (last updated 1.7)
472
+ * @return boolean True if the browser is the GoogletBot otherwise false
473
+ */
474
+ function checkBrowserGoogleBot() {
475
+ if( stripos($this->_agent,'googlebot') !== false ) {
476
+ $aresult = explode('/',stristr($this->_agent,'googlebot'));
477
+ $aversion = explode(' ',$aresult[1]);
478
+ $this->setVersion(str_replace(';','',$aversion[0]));
479
+ $this->_browser_name = $this->BROWSER_GOOGLEBOT;
480
+ $this->setRobot(true);
481
+ return true;
482
+ }
483
+ return false;
484
+ }
485
+
486
+ /**
487
+ * Determine if the browser is the MSNBot or not (last updated 1.9)
488
+ * @return boolean True if the browser is the MSNBot otherwise false
489
+ */
490
+ function checkBrowserMSNBot() {
491
+ if( stripos($this->_agent,"msnbot") !== false ) {
492
+ $aresult = explode("/",stristr($this->_agent,"msnbot"));
493
+ $aversion = explode(" ",$aresult[1]);
494
+ $this->setVersion(str_replace(";","",$aversion[0]));
495
+ $this->_browser_name = $this->BROWSER_MSNBOT;
496
+ $this->setRobot(true);
497
+ return true;
498
+ }
499
+ return false;
500
+ }
501
+
502
+ /**
503
+ * Determine if the browser is the W3C Validator or not (last updated 1.7)
504
+ * @return boolean True if the browser is the W3C Validator otherwise false
505
+ */
506
+ function checkBrowserW3CValidator() {
507
+ if( stripos($this->_agent,'W3C-checklink') !== false ) {
508
+ $aresult = explode('/',stristr($this->_agent,'W3C-checklink'));
509
+ $aversion = explode(' ',$aresult[1]);
510
+ $this->setVersion($aversion[0]);
511
+ $this->_browser_name = $this->BROWSER_W3CVALIDATOR;
512
+ return true;
513
+ }
514
+ else if( stripos($this->_agent,'W3C_Validator') !== false ) {
515
+ // Some of the Validator versions do not delineate w/ a slash - add it back in
516
+ $ua = str_replace("W3C_Validator ", "W3C_Validator/", $this->_agent);
517
+ $aresult = explode('/',stristr($ua,'W3C_Validator'));
518
+ $aversion = explode(' ',$aresult[1]);
519
+ $this->setVersion($aversion[0]);
520
+ $this->_browser_name = $this->BROWSER_W3CVALIDATOR;
521
+ return true;
522
+ }
523
+ return false;
524
+ }
525
+
526
+ /**
527
+ * Determine if the browser is the Yahoo! Slurp Robot or not (last updated 1.7)
528
+ * @return boolean True if the browser is the Yahoo! Slurp Robot otherwise false
529
+ */
530
+ function checkBrowserSlurp() {
531
+ if( stripos($this->_agent,'slurp') !== false ) {
532
+ $aresult = explode('/',stristr($this->_agent,'Slurp'));
533
+ $aversion = explode(' ',$aresult[1]);
534
+ $this->setVersion($aversion[0]);
535
+ $this->_browser_name = $this->BROWSER_SLURP;
536
+ $this->setRobot(true);
537
+ $this->setMobile(false);
538
+ return true;
539
+ }
540
+ return false;
541
+ }
542
+
543
+ /**
544
+ * Determine if the browser is Internet Explorer or not (last updated 1.7)
545
+ * @return boolean True if the browser is Internet Explorer otherwise false
546
+ */
547
+ function checkBrowserInternetExplorer() {
548
+
549
+ // Test for v1 - v1.5 IE
550
+ if( stripos($this->_agent,'microsoft internet explorer') !== false ) {
551
+ $this->setBrowser($this->BROWSER_IE);
552
+ $this->setVersion('1.0');
553
+ $aresult = stristr($this->_agent, '/');
554
+ if( preg_match('/308|425|426|474|0b1/i', $aresult) ) {
555
+ $this->setVersion('1.5');
556
+ }
557
+ return true;
558
+ }
559
+ // Test for versions > 1.5
560
+ else if( stripos($this->_agent,'msie') !== false && stripos($this->_agent,'opera') === false ) {
561
+ // See if the browser is the odd MSN Explorer
562
+ if( stripos($this->_agent,'msnb') !== false ) {
563
+ $aresult = explode(' ',stristr(str_replace(';','; ',$this->_agent),'MSN'));
564
+ $this->setBrowser( $this->BROWSER_MSN );
565
+ $this->setVersion(str_replace(array('(',')',';'),'',$aresult[1]));
566
+ return true;
567
+ }
568
+ $aresult = explode(' ',stristr(str_replace(';','; ',$this->_agent),'msie'));
569
+ $this->setBrowser( $this->BROWSER_IE );
570
+ $this->setVersion(str_replace(array('(',')',';'),'',$aresult[1]));
571
+ return true;
572
+ }
573
+ // Test for Pocket IE
574
+ else if( stripos($this->_agent,'mspie') !== false || stripos($this->_agent,'pocket') !== false ) {
575
+ $aresult = explode(' ',stristr($this->_agent,'mspie'));
576
+ $this->setPlatform( $this->PLATFORM_WINDOWS_CE );
577
+ $this->setBrowser( $this->BROWSER_POCKET_IE );
578
+ $this->setMobile(true);
579
+
580
+ if( stripos($this->_agent,'mspie') !== false ) {
581
+ $this->setVersion($aresult[1]);
582
+ }
583
+ else {
584
+ $aversion = explode('/',$this->_agent);
585
+ $this->setVersion($aversion[1]);
586
+ }
587
+ return true;
588
+ }
589
+ return false;
590
+ }
591
+
592
+ /**
593
+ * Determine if the browser is Opera or not (last updated 1.7)
594
+ * @return boolean True if the browser is Opera otherwise false
595
+ */
596
+ function checkBrowserOpera() {
597
+ if( stripos($this->_agent,'opera mini') !== false ) {
598
+ $resultant = stristr($this->_agent, 'opera mini');
599
+ if( preg_match('/\//',$resultant) ) {
600
+ $aresult = explode('/',$resultant);
601
+ $aversion = explode(' ',$aresult[1]);
602
+ $this->setVersion($aversion[0]);
603
+ }
604
+ else {
605
+ $aversion = explode(' ',stristr($resultant,'opera mini'));
606
+ $this->setVersion($aversion[1]);
607
+ }
608
+ $this->_browser_name = $this->BROWSER_OPERA_MINI;
609
+ $this->setMobile(true);
610
+ return true;
611
+ }
612
+ else if( stripos($this->_agent,'opera') !== false ) {
613
+ $resultant = stristr($this->_agent, 'opera');
614
+ if( preg_match('/Version\/(10.*)$/',$resultant,$matches) ) {
615
+ $this->setVersion($matches[1]);
616
+ }
617
+ else if( preg_match('/\//',$resultant) ) {
618
+ $aresult = explode('/',str_replace("("," ",$resultant));
619
+ $aversion = explode(' ',$aresult[1]);
620
+ $this->setVersion($aversion[0]);
621
+ }
622
+ else {
623
+ $aversion = explode(' ',stristr($resultant,'opera'));
624
+ $this->setVersion(isset($aversion[1])?$aversion[1]:"");
625
+ }
626
+ $this->_browser_name = $this->BROWSER_OPERA;
627
+ return true;
628
+ }
629
+ return false;
630
+ }
631
+
632
+ /**
633
+ * Determine if the browser is Chrome or not (last updated 1.7)
634
+ * @return boolean True if the browser is Chrome otherwise false
635
+ */
636
+ function checkBrowserChrome() {
637
+ if( stripos($this->_agent,'Chrome') !== false ) {
638
+ $aresult = explode('/',stristr($this->_agent,'Chrome'));
639
+ $aversion = explode(' ',$aresult[1]);
640
+ $this->setVersion($aversion[0]);
641
+ $this->setBrowser($this->BROWSER_CHROME);
642
+ return true;
643
+ }
644
+ return false;
645
+ }
646
+
647
+
648
+ /**
649
+ * Determine if the browser is WebTv or not (last updated 1.7)
650
+ * @return boolean True if the browser is WebTv otherwise false
651
+ */
652
+ function checkBrowserWebTv() {
653
+ if( stripos($this->_agent,'webtv') !== false ) {
654
+ $aresult = explode('/',stristr($this->_agent,'webtv'));
655
+ $aversion = explode(' ',$aresult[1]);
656
+ $this->setVersion($aversion[0]);
657
+ $this->setBrowser($this->BROWSER_WEBTV);
658
+ return true;
659
+ }
660
+ return false;
661
+ }
662
+
663
+ /**
664
+ * Determine if the browser is NetPositive or not (last updated 1.7)
665
+ * @return boolean True if the browser is NetPositive otherwise false
666
+ */
667
+ function checkBrowserNetPositive() {
668
+ if( stripos($this->_agent,'NetPositive') !== false ) {
669
+ $aresult = explode('/',stristr($this->_agent,'NetPositive'));
670
+ $aversion = explode(' ',$aresult[1]);
671
+ $this->setVersion(str_replace(array('(',')',';'),'',$aversion[0]));
672
+ $this->setBrowser($this->BROWSER_NETPOSITIVE);
673
+ return true;
674
+ }
675
+ return false;
676
+ }
677
+
678
+ /**
679
+ * Determine if the browser is Galeon or not (last updated 1.7)
680
+ * @return boolean True if the browser is Galeon otherwise false
681
+ */
682
+ function checkBrowserGaleon() {
683
+ if( stripos($this->_agent,'galeon') !== false ) {
684
+ $aresult = explode(' ',stristr($this->_agent,'galeon'));
685
+ $aversion = explode('/',$aresult[0]);
686
+ $this->setVersion($aversion[1]);
687
+ $this->setBrowser($this->BROWSER_GALEON);
688
+ return true;
689
+ }
690
+ return false;
691
+ }
692
+
693
+ /**
694
+ * Determine if the browser is Konqueror or not (last updated 1.7)
695
+ * @return boolean True if the browser is Konqueror otherwise false
696
+ */
697
+ function checkBrowserKonqueror() {
698
+ if( stripos($this->_agent,'Konqueror') !== false ) {
699
+ $aresult = explode(' ',stristr($this->_agent,'Konqueror'));
700
+ $aversion = explode('/',$aresult[0]);
701
+ $this->setVersion($aversion[1]);
702
+ $this->setBrowser($this->BROWSER_KONQUEROR);
703
+ return true;
704
+ }
705
+ return false;
706
+ }
707
+
708
+ /**
709
+ * Determine if the browser is iCab or not (last updated 1.7)
710
+ * @return boolean True if the browser is iCab otherwise false
711
+ */
712
+ function checkBrowserIcab() {
713
+ if( stripos($this->_agent,'icab') !== false ) {
714
+ $aversion = explode(' ',stristr(str_replace('/',' ',$this->_agent),'icab'));
715
+ $this->setVersion($aversion[1]);
716
+ $this->setBrowser($this->BROWSER_ICAB);
717
+ return true;
718
+ }
719
+ return false;
720
+ }
721
+
722
+ /**
723
+ * Determine if the browser is OmniWeb or not (last updated 1.7)
724
+ * @return boolean True if the browser is OmniWeb otherwise false
725
+ */
726
+ function checkBrowserOmniWeb() {
727
+ if( stripos($this->_agent,'omniweb') !== false ) {
728
+ $aresult = explode('/',stristr($this->_agent,'omniweb'));
729
+ $aversion = explode(' ',isset($aresult[1])?$aresult[1]:"");
730
+ $this->setVersion($aversion[0]);
731
+ $this->setBrowser($this->BROWSER_OMNIWEB);
732
+ return true;
733
+ }
734
+ return false;
735
+ }
736
+
737
+ /**
738
+ * Determine if the browser is Phoenix or not (last updated 1.7)
739
+ * @return boolean True if the browser is Phoenix otherwise false
740
+ */
741
+ function checkBrowserPhoenix() {
742
+ if( stripos($this->_agent,'Phoenix') !== false ) {
743
+ $aversion = explode('/',stristr($this->_agent,'Phoenix'));
744
+ $this->setVersion($aversion[1]);
745
+ $this->setBrowser($this->BROWSER_PHOENIX);
746
+ return true;
747
+ }
748
+ return false;
749
+ }
750
+
751
+ /**
752
+ * Determine if the browser is Firebird or not (last updated 1.7)
753
+ * @return boolean True if the browser is Firebird otherwise false
754
+ */
755
+ function checkBrowserFirebird() {
756
+ if( stripos($this->_agent,'Firebird') !== false ) {
757
+ $aversion = explode('/',stristr($this->_agent,'Firebird'));
758
+ $this->setVersion($aversion[1]);
759
+ $this->setBrowser($this->BROWSER_FIREBIRD);
760
+ return true;
761
+ }
762
+ return false;
763
+ }
764
+
765
+ /**
766
+ * Determine if the browser is Netscape Navigator 9+ or not (last updated 1.7)
767
+ * NOTE: (http://browser.netscape.com/ - Official support ended on March 1st, 2008)
768
+ * @return boolean True if the browser is Netscape Navigator 9+ otherwise false
769
+ */
770
+ function checkBrowserNetscapeNavigator9Plus() {
771
+ if( stripos($this->_agent,'Firefox') !== false && preg_match('/Navigator\/([^ ]*)/i',$this->_agent,$matches) ) {
772
+ $this->setVersion($matches[1]);
773
+ $this->setBrowser($this->BROWSER_NETSCAPE_NAVIGATOR);
774
+ return true;
775
+ }
776
+ else if( stripos($this->_agent,'Firefox') === false && preg_match('/Netscape6?\/([^ ]*)/i',$this->_agent,$matches) ) {
777
+ $this->setVersion($matches[1]);
778
+ $this->setBrowser($this->BROWSER_NETSCAPE_NAVIGATOR);
779
+ return true;
780
+ }
781
+ return false;
782
+ }
783
+
784
+ /**
785
+ * Determine if the browser is Shiretoko or not (https://wiki.mozilla.org/Projects/shiretoko) (last updated 1.7)
786
+ * @return boolean True if the browser is Shiretoko otherwise false
787
+ */
788
+ function checkBrowserShiretoko() {
789
+ if( stripos($this->_agent,'Mozilla') !== false && preg_match('/Shiretoko\/([^ ]*)/i',$this->_agent,$matches) ) {
790
+ $this->setVersion($matches[1]);
791
+ $this->setBrowser($this->BROWSER_SHIRETOKO);
792
+ return true;
793
+ }
794
+ return false;
795
+ }
796
+
797
+ /**
798
+ * Determine if the browser is Ice Cat or not (http://en.wikipedia.org/wiki/GNU_IceCat) (last updated 1.7)
799
+ * @return boolean True if the browser is Ice Cat otherwise false
800
+ */
801
+ function checkBrowserIceCat() {
802
+ if( stripos($this->_agent,'Mozilla') !== false && preg_match('/IceCat\/([^ ]*)/i',$this->_agent,$matches) ) {
803
+ $this->setVersion($matches[1]);
804
+ $this->setBrowser($this->BROWSER_ICECAT);
805
+ return true;
806
+ }
807
+ return false;
808
+ }
809
+
810
+ /**
811
+ * Determine if the browser is Nokia or not (last updated 1.7)
812
+ * @return boolean True if the browser is Nokia otherwise false
813
+ */
814
+ function checkBrowserNokia() {
815
+ if( preg_match("/Nokia([^\/]+)\/([^ SP]+)/i",$this->_agent,$matches) ) {
816
+ $this->setVersion($matches[2]);
817
+ if( stripos($this->_agent,'Series60') !== false || strpos($this->_agent,'S60') !== false ) {
818
+ $this->setBrowser($this->BROWSER_NOKIA_S60);
819
+ }
820
+ else {
821
+ $this->setBrowser( $this->BROWSER_NOKIA );
822
+ }
823
+ $this->setMobile(true);
824
+ return true;
825
+ }
826
+ return false;
827
+ }
828
+
829
+ /**
830
+ * Determine if the browser is Firefox or not (last updated 1.7)
831
+ * @return boolean True if the browser is Firefox otherwise false
832
+ */
833
+ function checkBrowserFirefox() {
834
+ if( stripos($this->_agent,'safari') === false ) {
835
+ if( preg_match("/Firefox[\/ \(]([^ ;\)]+)/i",$this->_agent,$matches) ) {
836
+ $this->setVersion($matches[1]);
837
+ $this->setBrowser($this->BROWSER_FIREFOX);
838
+ return true;
839
+ }
840
+ else if( preg_match("/Firefox$/i",$this->_agent,$matches) ) {
841
+ $this->setVersion("");
842
+ $this->setBrowser($this->BROWSER_FIREFOX);
843
+ return true;
844
+ }
845
+ }
846
+ return false;
847
+ }
848
+
849
+ /**
850
+ * Determine if the browser is Firefox or not (last updated 1.7)
851
+ * @return boolean True if the browser is Firefox otherwise false
852
+ */
853
+ function checkBrowserIceweasel() {
854
+ if( stripos($this->_agent,'Iceweasel') !== false ) {
855
+ $aresult = explode('/',stristr($this->_agent,'Iceweasel'));
856
+ $aversion = explode(' ',$aresult[1]);
857
+ $this->setVersion($aversion[0]);
858
+ $this->setBrowser($this->BROWSER_ICEWEASEL);
859
+ return true;
860
+ }
861
+ return false;
862
+ }
863
+ /**
864
+ * Determine if the browser is Mozilla or not (last updated 1.7)
865
+ * @return boolean True if the browser is Mozilla otherwise false
866
+ */
867
+ function checkBrowserMozilla() {
868
+ if( stripos($this->_agent,'mozilla') !== false && preg_match('/rv:[0-9].[0-9][a-b]?/i',$this->_agent) && stripos($this->_agent,'netscape') === false) {
869
+ $aversion = explode(' ',stristr($this->_agent,'rv:'));
870
+ preg_match('/rv:[0-9].[0-9][a-b]?/i',$this->_agent,$aversion);
871
+ $this->setVersion(str_replace('rv:','',$aversion[0]));
872
+ $this->setBrowser($this->BROWSER_MOZILLA);
873
+ return true;
874
+ }
875
+ else if( stripos($this->_agent,'mozilla') !== false && preg_match('/rv:[0-9]\.[0-9]/i',$this->_agent) && stripos($this->_agent,'netscape') === false ) {
876
+ $aversion = explode('',stristr($this->_agent,'rv:'));
877
+ $this->setVersion(str_replace('rv:','',$aversion[0]));
878
+ $this->setBrowser($this->BROWSER_MOZILLA);
879
+ return true;
880
+ }
881
+ else if( stripos($this->_agent,'mozilla') !== false && preg_match('/mozilla\/([^ ]*)/i',$this->_agent,$matches) && stripos($this->_agent,'netscape') === false ) {
882
+ $this->setVersion($matches[1]);
883
+ $this->setBrowser($this->BROWSER_MOZILLA);
884
+ return true;
885
+ }
886
+ return false;
887
+ }
888
+
889
+ /**
890
+ * Determine if the browser is Lynx or not (last updated 1.7)
891
+ * @return boolean True if the browser is Lynx otherwise false
892
+ */
893
+ function checkBrowserLynx() {
894
+ if( stripos($this->_agent,'lynx') !== false ) {
895
+ $aresult = explode('/',stristr($this->_agent,'Lynx'));
896
+ $aversion = explode(' ',(isset($aresult[1])?$aresult[1]:""));
897
+ $this->setVersion($aversion[0]);
898
+ $this->setBrowser($this->BROWSER_LYNX);
899
+ return true;
900
+ }
901
+ return false;
902
+ }
903
+
904
+ /**
905
+ * Determine if the browser is Amaya or not (last updated 1.7)
906
+ * @return boolean True if the browser is Amaya otherwise false
907
+ */
908
+ function checkBrowserAmaya() {
909
+ if( stripos($this->_agent,'amaya') !== false ) {
910
+ $aresult = explode('/',stristr($this->_agent,'Amaya'));
911
+ $aversion = explode(' ',$aresult[1]);
912
+ $this->setVersion($aversion[0]);
913
+ $this->setBrowser($this->BROWSER_AMAYA);
914
+ return true;
915
+ }
916
+ return false;
917
+ }
918
+
919
+ /**
920
+ * Determine if the browser is Safari or not (last updated 1.7)
921
+ * @return boolean True if the browser is Safari otherwise false
922
+ */
923
+ function checkBrowserSafari() {
924
+ if( stripos($this->_agent,'Safari') !== false && stripos($this->_agent,'iPhone') === false && stripos($this->_agent,'iPod') === false ) {
925
+ $aresult = explode('/',stristr($this->_agent,'Version'));
926
+ if( isset($aresult[1]) ) {
927
+ $aversion = explode(' ',$aresult[1]);
928
+ $this->setVersion($aversion[0]);
929
+ }
930
+ else {
931
+ $this->setVersion($this->VERSION_UNKNOWN);
932
+ }
933
+ $this->setBrowser($this->BROWSER_SAFARI);
934
+ return true;
935
+ }
936
+ return false;
937
+ }
938
+
939
+ /**
940
+ * Determine if the browser is iPhone or not (last updated 1.7)
941
+ * @return boolean True if the browser is iPhone otherwise false
942
+ */
943
+ function checkBrowseriPhone() {
944
+ if( stripos($this->_agent,'iPhone') !== false ) {
945
+ $aresult = explode('/',stristr($this->_agent,'Version'));
946
+ if( isset($aresult[1]) ) {
947
+ $aversion = explode(' ',$aresult[1]);
948
+ $this->setVersion($aversion[0]);
949
+ }
950
+ else {
951
+ $this->setVersion($this->VERSION_UNKNOWN);
952
+ }
953
+ $this->setMobile(true);
954
+ $this->setBrowser($this->BROWSER_IPHONE);
955
+ return true;
956
+ }
957
+ return false;
958
+ }
959
+
960
+ /**
961
+ * Determine if the browser is iPod or not (last updated 1.7)
962
+ * @return boolean True if the browser is iPod otherwise false
963
+ */
964
+ function checkBrowseriPad() {
965
+ if( stripos($this->_agent,'iPad') !== false ) {
966
+ $aresult = explode('/',stristr($this->_agent,'Version'));
967
+ if( isset($aresult[1]) ) {
968
+ $aversion = explode(' ',$aresult[1]);
969
+ $this->setVersion($aversion[0]);
970
+ }
971
+ else {
972
+ $this->setVersion($this->VERSION_UNKNOWN);
973
+ }
974
+ $this->setMobile(true);
975
+ $this->setBrowser($this->BROWSER_IPAD);
976
+ return true;
977
+ }
978
+ return false;
979
+ }
980
+
981
+ /**
982
+ * Determine if the browser is iPod or not (last updated 1.7)
983
+ * @return boolean True if the browser is iPod otherwise false
984
+ */
985
+ function checkBrowseriPod() {
986
+ if( stripos($this->_agent,'iPod') !== false ) {
987
+ $aresult = explode('/',stristr($this->_agent,'Version'));
988
+ if( isset($aresult[1]) ) {
989
+ $aversion = explode(' ',$aresult[1]);
990
+ $this->setVersion($aversion[0]);
991
+ }
992
+ else {
993
+ $this->setVersion($this->VERSION_UNKNOWN);
994
+ }
995
+ $this->setMobile(true);
996
+ $this->setBrowser($this->BROWSER_IPOD);
997
+ return true;
998
+ }
999
+ return false;
1000
+ }
1001
+
1002
+ /**
1003
+ * Determine if the browser is Android or not (last updated 1.7)
1004
+ * @return boolean True if the browser is Android otherwise false
1005
+ */
1006
+ function checkBrowserAndroid() {
1007
+ if( stripos($this->_agent,'Android') !== false ) {
1008
+ $aresult = explode(' ',stristr($this->_agent,'Android'));
1009
+ if( isset($aresult[1]) ) {
1010
+ $aversion = explode(' ',$aresult[1]);
1011
+ $this->setVersion($aversion[0]);
1012
+ }
1013
+ else {
1014
+ $this->setVersion($this->VERSION_UNKNOWN);
1015
+ }
1016
+ $this->setMobile(true);
1017
+ $this->setBrowser($this->BROWSER_ANDROID);
1018
+ return true;
1019
+ }
1020
+ return false;
1021
+ }
1022
+
1023
+ /**
1024
+ * Determine the user's platform (last updated 1.7)
1025
+ */
1026
+ function checkPlatform() {
1027
+ if( stripos($this->_agent, 'windows') !== false ) {
1028
+ $this->_platform = $this->PLATFORM_WINDOWS;
1029
+ }
1030
+ else if( stripos($this->_agent, 'iPad') !== false ) {
1031
+ $this->_platform = $this->PLATFORM_IPAD;
1032
+ }
1033
+ else if( stripos($this->_agent, 'iPod') !== false ) {
1034
+ $this->_platform = $this->PLATFORM_IPOD;
1035
+ }
1036
+ else if( stripos($this->_agent, 'iPhone') !== false ) {
1037
+ $this->_platform = $this->PLATFORM_IPHONE;
1038
+ }
1039
+ elseif( stripos($this->_agent, 'mac') !== false ) {
1040
+ $this->_platform = $this->PLATFORM_APPLE;
1041
+ }
1042
+ elseif( stripos($this->_agent, 'android') !== false ) {
1043
+ $this->_platform = $this->PLATFORM_ANDROID;
1044
+ }
1045
+ elseif( stripos($this->_agent, 'linux') !== false ) {
1046
+ $this->_platform = $this->PLATFORM_LINUX;
1047
+ }
1048
+ else if( stripos($this->_agent, 'Nokia') !== false ) {
1049
+ $this->_platform = $this->PLATFORM_NOKIA;
1050
+ }
1051
+ else if( stripos($this->_agent, 'BlackBerry') !== false ) {
1052
+ $this->_platform = $this->PLATFORM_BLACKBERRY;
1053
+ }
1054
+ elseif( stripos($this->_agent,'FreeBSD') !== false ) {
1055
+ $this->_platform = $this->PLATFORM_FREEBSD;
1056
+ }
1057
+ elseif( stripos($this->_agent,'OpenBSD') !== false ) {
1058
+ $this->_platform = $this->PLATFORM_OPENBSD;
1059
+ }
1060
+ elseif( stripos($this->_agent,'NetBSD') !== false ) {
1061
+ $this->_platform = $this->PLATFORM_NETBSD;
1062
+ }
1063
+ elseif( stripos($this->_agent, 'OpenSolaris') !== false ) {
1064
+ $this->_platform = $this->PLATFORM_OPENSOLARIS;
1065
+ }
1066
+ elseif( stripos($this->_agent, 'SunOS') !== false ) {
1067
+ $this->_platform = $this->PLATFORM_SUNOS;
1068
+ }
1069
+ elseif( stripos($this->_agent, 'OS\/2') !== false ) {
1070
+ $this->_platform = $this->PLATFORM_OS2;
1071
+ }
1072
+ elseif( stripos($this->_agent, 'BeOS') !== false ) {
1073
+ $this->_platform = $this->PLATFORM_BEOS;
1074
+ }
1075
+ elseif( stripos($this->_agent, 'win') !== false ) {
1076
+ $this->_platform = $this->PLATFORM_WINDOWS;
1077
+ }
1078
+
1079
+ }
1080
+ }
1081
+
1082
+ ?>
includes/logger.php ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Simple Logger Class
4
+ *
5
+ * @author Josh Nesbitt <josh@josh-nesbitt.net>
6
+ *
7
+ * By default will write to path/to/logger/ + log/filename.log
8
+ * Author url: https://raw.githubusercontent.com/joshnesbitt/logger/master/lib/logger.php
9
+ *
10
+ **/
11
+ class wpstgLogger {
12
+ var $file, $path, $level, $stream;
13
+ const INFO = 4;
14
+ const DEBUG = 3;
15
+ const WARN = 2;
16
+ const ERROR = 1;
17
+ const FATAL = 0;
18
+
19
+ function __construct($file, $level)
20
+ {
21
+ $this->file = $file;
22
+ $this->level = $level;
23
+ $this->path = str_replace('\\', '/', WPSTG_PLUGIN_DIR . "logs/$this->file");
24
+ $this->folder = str_replace('\\', '/', WPSTG_PLUGIN_DIR . "logs");
25
+ $this->start();
26
+ }
27
+
28
+ function info($string)
29
+ {
30
+ return $this->check_level(self::INFO) ? true : $this->log($string);
31
+ }
32
+
33
+ function warn($string)
34
+ {
35
+ return $this->check_level(self::WARN) ? true : $this->log($string);
36
+ }
37
+
38
+ function debug($string)
39
+ {
40
+ return $this->check_level(self::DEBUG) ? true : $this->log($string);
41
+ }
42
+
43
+ function error($string)
44
+ {
45
+ return $this->check_level(self::ERROR) ? true : $this->log($string);
46
+ }
47
+
48
+ function fatal($string)
49
+ {
50
+ return $this->check_level(self::FATAL) ? true : $this->log($string);
51
+ }
52
+
53
+ function clear()
54
+ {
55
+ $this->close();
56
+ $this->open("w");
57
+ $this->close();
58
+ $this->open();
59
+ }
60
+
61
+ private function check_level($level)
62
+ {
63
+ return $this->level < $level;
64
+ }
65
+
66
+ /* Start logging
67
+ */
68
+ private function log($string)
69
+ {
70
+ global $wpstg_options;
71
+ //$enabled = isset($wpstg_options['debug_mode']) ? $wpstg_options['debug_mode'] : false;
72
+ //if ($enabled)
73
+ $this->write("[". date('l jS F Y : h:i:sa') . "] ". $string . "\r\n");
74
+
75
+ return false;
76
+ }
77
+
78
+ private function write($string)
79
+ {
80
+ return fwrite($this->stream, $string);
81
+ }
82
+
83
+ /* Check if wp-staging debug mode is enabled
84
+ *
85
+ * @return bool
86
+ */
87
+ private function start()
88
+ {
89
+ global $wpstg_options;
90
+ //$enabled = isset($wpstg_options['debug_mode']) ? $wpstg_options['debug_mode'] : false;
91
+ //if ($enabled)
92
+ return $this->open();
93
+
94
+ return false;
95
+ }
96
+
97
+ /* Check if directory is writable
98
+ *
99
+ * @return bool
100
+ */
101
+
102
+ function checkDir(){
103
+ $writable = is_writable($this->folder);
104
+ if ($writable)
105
+ return true;
106
+
107
+ return false;
108
+ }
109
+
110
+ /* Open the log file
111
+ *
112
+ * @return stream
113
+ */
114
+ private function open($mode="a")
115
+ {
116
+ if ($this->checkDir())
117
+ return $this->stream = fopen($this->path, $mode);
118
+ //or die("Cannot write to file '$this->path', please ensure '$this->path' is writable.");
119
+ }
120
+
121
+ private function close()
122
+ {
123
+ return fclose($this->stream);
124
+ }
125
+
126
+ }
127
+
128
+ ?>
includes/scripts.php ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Scripts
4
+ *
5
+ * @package WPSTG
6
+ * @subpackage Functions
7
+ * @copyright Copyright (c) 2015, René Hermenau
8
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9
+ * @since 1.0
10
+ */
11
+
12
+ // Exit if accessed directly
13
+ if ( ! defined( 'ABSPATH' ) ) exit;
14
+
15
+
16
+ function wpstg_get_cpu_load_sett(){
17
+ global $wpstg_options;
18
+
19
+ $get_cpu_load = isset($wpstg_options['wpstg_cpu_load']) ? $wpstg_options['wpstg_cpu_load'] : 'default';
20
+
21
+ if ($get_cpu_load === 'default')
22
+ $cpu_load = 1000;
23
+
24
+ if ($get_cpu_load === 'high')
25
+ $cpu_load = 0;
26
+
27
+ if ($get_cpu_load === 'medium')
28
+ $cpu_load = 1000;
29
+
30
+ if ($get_cpu_load === 'low')
31
+ $cpu_load = 3000;
32
+
33
+ return $cpu_load;
34
+
35
+ }
36
+
37
+ /**
38
+ * Load Admin Scripts
39
+ *
40
+ * Enqueues the required admin scripts.
41
+ *
42
+ * @since 1.0
43
+ * @global $post
44
+ * @param string $hook Page hook
45
+ * @return void
46
+ */
47
+
48
+ function wpstg_load_admin_scripts( $hook ) {
49
+ if ( ! apply_filters( 'wpstg_load_admin_scripts', wpstg_is_admin_page(), $hook ) ) {
50
+ return;
51
+ }
52
+ global $wp_version, $wpstg_options;
53
+
54
+
55
+ $js_dir = WPSTG_PLUGIN_URL . 'assets/js/';
56
+ $css_dir = WPSTG_PLUGIN_URL . 'assets/css/';
57
+
58
+ // Use minified libraries if SCRIPT_DEBUG is turned off
59
+ //$suffix = '';//( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
60
+ $suffix = isset($wpstg_options['debug_mode']) ? '.min' : '';
61
+
62
+
63
+
64
+ // These have to be global
65
+ wp_enqueue_script( 'wpstg-admin-script', $js_dir . 'wpstg-admin' . $suffix . '.js', array( 'jquery' ), WPSTG_VERSION, false );
66
+ wp_enqueue_style( 'wpstg-admin', $css_dir . 'wpstg-admin' . $suffix . '.css', WPSTG_VERSION );
67
+ wp_localize_script( 'wpstg-admin-script', 'wpstg', array(
68
+ 'nonce' => wp_create_nonce( 'wpstg_ajax_nonce' ),
69
+ 'mu_plugin_confirmation' => __( "If confirmed we will install an additional WordPress 'Must Use' plugin. This plugin will allow us to control which plugins are loaded during WP Staging specific operations. Do you wish to continue?", 'wpstg' ),
70
+ 'plugin_compatibility_settings_problem' => __( 'A problem occurred when trying to change the plugin compatibility setting.', 'wpstg' ),
71
+ 'saved' => __( 'Saved', 'The settings were saved successfully', 'wpstg' ),
72
+ 'status' => __( 'Status', 'Current request status', 'wpstg' ),
73
+ 'response' => __( 'Response', 'The message the server responded with', 'wpstg' ),
74
+ 'blacklist_problem' => __( 'A problem occurred when trying to add plugins to backlist.', 'wpstg' ),
75
+ 'cpu_load' => wpstg_get_cpu_load_sett(),
76
+
77
+ ));
78
+ }
79
+ add_action( 'admin_enqueue_scripts', 'wpstg_load_admin_scripts', 100 );
includes/staging-functions.php ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Staging functions
4
+ *
5
+ * @package WPSTG
6
+ * @subpackage includes/staging-functions
7
+ * @copyright Copyright (c) 2015, Rene Hermenau
8
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9
+ * @since 0.9.0
10
+ */
11
+
12
+ // Exit if accessed directly
13
+ if ( ! defined( 'ABSPATH' ) ) exit;
14
+
15
+ /**
16
+ * Check if website is a clone or not.
17
+ * If it' s a clone website we allow access to the frontend only for administrators.
18
+ *
19
+ * At init() stage most of WP is loaded, and the user is authenticated.
20
+ * Get the wpstg_options via get_option because our global $wpstg_option is only available on admin pages
21
+ *
22
+ * @return string wp_die()
23
+ */
24
+ function wpstg_staging_permissions(){
25
+ $wpstg_options = get_option( 'wpstg_settings' );
26
+ $wpstg_disable_admin_login = isset($wpstg_options['disable_admin_login']) ? $wpstg_options['disable_admin_login'] : 0;
27
+
28
+ if ( wpstg_is_staging_site() && $wpstg_disable_admin_login === 0){
29
+ if ( !current_user_can( 'administrator' ) && !wpstg_is_login_page() && !is_admin() )
30
+ //wp_die( sprintf ( __('Access denied. <a href="%1$s" target="_blank">Login</a> first','wpstg'), './wp-admin/' ) );
31
+ wp_die( sprintf ( __('Access denied. <a href="%1$s" target="_blank">Login</a> first','wpstg'), wp_login_url() ) );
32
+ }
33
+ wpstg_reset_permalinks();
34
+ }
35
+ add_action( 'init', 'wpstg_staging_permissions' );
36
+
37
+ /**
38
+ * Inject custom header for staging website
39
+ *
40
+ * @deprecated since version 0.2
41
+ */
42
+ /*function wpstg_inject_header(){
43
+ if ( !wpstg_is_staging_site() ) {
44
+ ?>
45
+ <script type="text/javascript">
46
+ jQuery(document).ready( function($) {
47
+ var struct='<div id="wpstg_staging_header" style="display:block;position:fixed;background-color:#c12161;color:#fff;height:32px;top:0;left:0;width:100%;">Staging website!</div>';
48
+ function
49
+ jQuery('body').append(struct);
50
+ })
51
+ </script>
52
+ <?php
53
+ }
54
+ }*/
55
+ //add_action('wp_head','wpstg_inject_header');
56
+
57
+ /**
58
+ * Change admin_bar site_name
59
+ *
60
+ * @global object $wp_admin_bar
61
+ * @return void
62
+ */
63
+ function wpstg_change_adminbar_name() {
64
+ global $wp_admin_bar;
65
+ if (wpstg_is_staging_site()) {
66
+ // Main Title
67
+ $wp_admin_bar->add_menu(array(
68
+ 'id' => 'site-name',
69
+ 'title' => is_admin() ? ('STAGING - ' . get_bloginfo( 'name' ) ) : ( 'STAGING ' . get_bloginfo( 'name' ) . ' Dashboard' ),
70
+ 'href' => is_admin() ? home_url('/') : admin_url(),
71
+ ));
72
+ }
73
+ }
74
+ add_filter('wp_before_admin_bar_render', 'wpstg_change_adminbar_name');
75
+
76
+ /**
77
+ * Check if current wordpress instance is the main site or a clone
78
+ *
79
+ * @global array $wpstg_options options
80
+ * @return bool true if current website is a staging website
81
+ */
82
+ function wpstg_is_staging_site(){
83
+ $is_staging_site = get_option('wpstg_is_staging_site') ? get_option('wpstg_is_staging_site') : 'false';
84
+ if ($is_staging_site === 'true'){
85
+ return true;
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Reset permalink structure of the clone to default index.php/p=123
91
+ * This is used once
92
+ * @global array $wpstg_options options
93
+ */
94
+ function wpstg_reset_permalinks(){
95
+ global $wp_rewrite;
96
+ $permalink_structure = null;
97
+ $already_executed = get_option('wpstg_rmpermalinks_executed') ? get_option('wpstg_rmpermalinks_executed') : 'false';
98
+ if (wpstg_is_staging_site() && $already_executed !== 'true' ){
99
+ $wp_rewrite->set_permalink_structure( $permalink_structure );
100
+ flush_rewrite_rules();
101
+ update_option('wpstg_rmpermalinks_executed', 'true');
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Check if current page is a login page
107
+ *
108
+ * @return bool true if page is login page
109
+ */
110
+ function wpstg_is_login_page() {
111
+ global $wpstg_options;
112
+ //$wpstg_login_page = isset($wpstg_options['admin_login_page']) ? $wpstg_options['admin_login_page'] : '';
113
+ return in_array( $GLOBALS['pagenow'], array('wp-login.php') );
114
+ }
includes/template-functions.php ADDED
@@ -0,0 +1,1722 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Template Functions
4
+ *
5
+ * @package WPSTG
6
+ * @subpackage Functions/Templates
7
+ * @copyright Copyright (c) 2015, René Hermenau
8
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9
+ * @since 0.9.0
10
+ */
11
+
12
+ // Exit if accessed directly
13
+ if ( ! defined( 'ABSPATH' ) ) exit;
14
+
15
+ //error_reporting(-1);
16
+ //ini_set('display_errors', 'On');
17
+
18
+ /**
19
+ * Main Page
20
+ *
21
+ * Renders the main WP-Staging page contents.
22
+ *
23
+ * @since 1.0
24
+ * @return void
25
+ */
26
+
27
+ /* Global vars
28
+ *
29
+ */
30
+
31
+ $state_data = '';
32
+ $start_time = microtime(true);
33
+
34
+ function wpstg_clone_page() {
35
+ ob_start();
36
+ ?>
37
+ <div id="wpstg-clonepage-wrapper">
38
+ <span class="wp-staginglogo"><img src="<?php echo WPSTG_PLUGIN_URL . 'assets/images/logo_clean_small_212_25.png';?>"></span><span class="wpstg-version"><?php echo WPSTG_VERSION . ''; ?></span>
39
+ <div class="wpstg-header">
40
+ <?php echo __('Thank you for using WP Staging', 'wpstg');?>
41
+ <br>
42
+ <?php echo __('WP Staging is ready to create a staging site!', 'wpstg'); ?>
43
+ <br>
44
+ <iframe src="//www.facebook.com/plugins/like.php?href=https%3A%2F%2Fwordpress.org%2Fplugins%2Fwp-staging%2F&amp;width=100&amp;layout=button&amp;action=like&amp;show_faces=false&amp;share=true&amp;height=35&amp;appId=449277011881884" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:96px; height:20px;" allowTransparency="true"></iframe>
45
+ <a class="twitter-follow-button" href="https://twitter.com/wp_staging" data-size="small" id="twitter-wjs" style="display: none;">Follow @wp_staging</a>
46
+ <a class="twitter-share-button" href="https://twitter.com/intent/tweet?text=Check%20this%20WordPress%20Staging%20plugin%20&url=https://wordpress.org/plugins/wp-staging&hashtags=wpstaging&via=wp_staging">Tweet</a>
47
+ </div>
48
+ <?php do_action('wpstg_notifications');?>
49
+ <?php if (is_multisite()) {
50
+ echo '<span class="wpstg-notice-alert" style="margin-top:20px;">' . __('WordPress Multisite is currently not supported! <a href="https://wp-staging.com/contact">Get in contact with us</a> and ask for it.', 'wpstg') . '</span>';
51
+ exit;
52
+ }?>
53
+ <ul id="wpstg-steps">
54
+ <li class="wpstg-current-step"><span class="wpstg-step-num">1</span><?php echo __('Overview', 'wpstg');?></li>
55
+ <li><span class="wpstg-step-num">2</span><?php echo __('Scanning', 'wpstg');?></li>
56
+ <li><span class="wpstg-step-num">3</span><?php echo __('Cloning', 'wpstg');?></li>
57
+ <li><span href="#" id="wpstg-loader" style="display:none;"></span></li>
58
+ </ul> <!-- #wpstg-steps -->
59
+ <div id="wpstg-workflow">
60
+ <?php echo wpstg_overview(false); ?>
61
+ </div> <!-- #wpstg-workflow -->
62
+ <div id="wpstg-error-wrapper">
63
+ <div id="wpstg-error-details"></div>
64
+ </div>
65
+ </div> <!-- #wpstg-clonepage-wrapper -->
66
+ <?php
67
+ echo wpstg_get_sidebar();
68
+ echo ob_get_clean();
69
+ }
70
+
71
+ /**
72
+ * Renders the sidebar
73
+ *
74
+ * @return string
75
+ */
76
+ function wpstg_get_sidebar(){
77
+ $html = '<div class="wpstg-sidebar">side bar here</div>';
78
+ return $html;
79
+ }
80
+
81
+ /**
82
+ * 1st step: Overview
83
+ * Renders the overview page content
84
+ *
85
+ * @global type $wpstg_clone_details
86
+ */
87
+ function wpstg_overview() {
88
+ global $wpstg_clone_details;
89
+ $wpstg_clone_details = wpstg_get_options();
90
+ $existing_clones = get_option('wpstg_existing_clones', array());
91
+
92
+ ?>
93
+ <?php if (isset($wpstg_clone_details['current_clone'])) : ?>
94
+ Current clone: <?php echo $wpstg_clone_details['current_clone']; ?>
95
+ <a href="#" id="wpstg-reset-clone" class="wpstg-link-btn button-primary" data-clone="<?php echo $wpstg_clone_details['current_clone']; ?>">Reset</a>
96
+ <a href="#" class="wpstg-next-step-link wpstg-link-btn button-primary" data-action="wpstg_scanning">Continue</a>
97
+ <?php else : ?>
98
+ <a href="#" id="wpstg-new-clone" class="wpstg-next-step-link wpstg-link-btn button-primary" data-action="wpstg_scanning"><?php echo __('Create new staging site', 'wpstg'); ?></a>
99
+ <?php endif; ?>
100
+ <br>
101
+ <div id="wpstg-existing-clones">
102
+ <?php if (!empty($existing_clones)) : ?>
103
+ <h3><?php _e('Available Staging Sites:', 'wpstg'); ?></h3>
104
+ <?php foreach ($existing_clones as $clone) : ?>
105
+ <div class="wpstg-clone" id="<?php echo $clone; ?>">
106
+ <a href="<?php echo get_home_url() . "/$clone/wp-login.php"; ?>" class="wpstg-clone-title" target="_blank"><?php echo $clone; ?></a>
107
+ <a href="<?php echo get_home_url() . "/$clone/wp-login.php"; ?>" class="wpstg-open-clone wpstg-clone-action" target="_blank"><?php _e('Open', 'wpstg'); ?></a>
108
+ <a href="#" class="wpstg-execute-clone wpstg-clone-action" data-clone="<?php echo $clone; ?>"><?php _e('Edit', 'wpstg'); ?></a>
109
+ <a href="#" class="wpstg-remove-clone wpstg-clone-action" data-clone="<?php echo $clone; ?>"><?php _e('Delete', 'wpstg'); ?></a>
110
+ <!--<a href="#" class="wpstg-edit-clone wpstg-clone-action" data-clone="<?php //echo $clone; ?>"><?php //_e('Edit', 'wpstg'); ?></a>-->
111
+ </div> <!-- .wpstg-clone -->
112
+ <?php endforeach; ?>
113
+ <?php endif; ?>
114
+ </div> <!-- #wpstg-existing-clones -->
115
+ <div id="wpstg-removing-clone">
116
+
117
+ </div> <!-- #wpstg-removing-clone -->
118
+ <?php
119
+ if (check_ajax_referer('wpstg_ajax_nonce', 'nonce', false))
120
+ wp_die();
121
+ }
122
+ add_action('wp_ajax_wpstg_overview', 'wpstg_overview');
123
+
124
+
125
+ /**
126
+ * 2nd step: Scanning
127
+ * Collect database and file data for clone
128
+ *
129
+ * @global object $wpdb
130
+ * @global array $wpstg_clone_details
131
+ * @global array $all_files
132
+ */
133
+ function wpstg_scanning() {
134
+ global $wpdb, $wpstg_clone_details, $all_files;
135
+ check_ajax_referer( 'wpstg_ajax_nonce', 'nonce' );
136
+ $wpstg_clone_details = wpstg_get_options();
137
+
138
+ $unchecked_tables = array();
139
+ $excluded_folders = array();
140
+ $clone_path = 'value="' . get_home_path() . '"';
141
+
142
+ $disabled = isset($wpstg_clone_details['current_clone']) ? 'disabled' : false;
143
+ $clone = isset($_POST['clone']) ? $_POST['clone']
144
+ : (isset($wpstg_clone_details['current_clone']) ? $wpstg_clone_details['current_clone'] : false);
145
+
146
+ if ($clone) {
147
+ //$wpstg_profile = wpstg_get_profile($clone);
148
+ //$unchecked_tables = $wpstg_profile['unchecked_tables'];
149
+ //$excluded_folders = $wpstg_profile['excluded_folders'];
150
+ //$excluded_folders[] = get_home_path() . $wpstg_profile['name'];
151
+ //$clone_path = 'value = "' . $wpstg_profile['path'] . '"';
152
+ }
153
+
154
+
155
+ //Scan DB
156
+ $tables = $wpdb->get_results("show table status like '" . $wpdb->prefix . "_%'");
157
+ $wpstg_clone_details['all_tables'] = $wpdb->get_col("show tables like '" . $wpdb->prefix . "%'");
158
+
159
+ //Scan Files
160
+ $wpstg_clone_details['total_size'] = 0;
161
+ unset($wpstg_clone_details['large_files']);
162
+ //$folders = wpstg_scan_files(rtrim(get_home_path(), '/'));
163
+ //$folders = wpstg_scan_files(get_home_path());
164
+ $folders = wpstg_scan_files(wpstg_get_clone_root_path());
165
+
166
+ array_pop($folders);
167
+
168
+ $path = wpstg_get_upload_dir() . '/remaining_files.json';
169
+ file_put_contents($path, json_encode($all_files));
170
+
171
+ wpstg_save_options();
172
+
173
+ $clone_id = '';
174
+ if (isset($wpstg_clone_details['current_clone']))
175
+ $clone_id = 'value="' . $wpstg_clone_details['current_clone'] . '" disabled';
176
+
177
+ //$free_space = function_exists('disk_free_space') ? disk_free_space(get_home_path()) : '';
178
+ //$overflow = $free_space < $wpstg_clone_details['total_size'] ? true : false;
179
+ ?>
180
+ <label id="wpstg-clone-label" for="wpstg-new-clone">
181
+ <?php echo __('Name your new site, e.g. staging, dev (keep it short):', 'wpstg');?>
182
+ <input type="text" id="wpstg-new-clone-id" value="<?php echo $clone; ?>" <?php echo $disabled; ?>>
183
+ </label>
184
+ <span class="wpstg-error-msg" id="wpstg-clone-id-error">
185
+ <?php
186
+ echo wpstg_check_diskspace($wpstg_clone_details['total_size']);
187
+ ?>
188
+ </span>
189
+ <div class="wpstg-tabs-wrapper">
190
+ <a href="#" class="wpstg-tab-header active" data-id="#wpstg-scanning-db">
191
+ <span class="wpstg-tab-triangle">&#9658;</span>
192
+ <?php echo __('DB Tables', 'wpstg'); ?>
193
+ </a>
194
+ <div class="wpstg-tab-section" id="wpstg-scanning-db">
195
+ <?php
196
+ do_action('wpstg_scanning_db');
197
+ echo '<h4 style="margin:0px;">' . __('Select the tables to copy. (If the copy process was previously interrupted, succesfull copied tables are greyed out and copy process will skip these ones)', 'wpstg') . '<h4>';
198
+ wpstg_show_tables($tables, $unchecked_tables); ?>
199
+ </div> <!-- #wpstg-scanning-db -->
200
+
201
+ <a href="#" class="wpstg-tab-header" data-id="#wpstg-scanning-files">
202
+ <span class="wpstg-tab-triangle">&#9658;</span>
203
+ <?php echo __('Files', 'wpstg'); ?>
204
+ </a>
205
+ <div class="wpstg-tab-section" id="wpstg-scanning-files">
206
+
207
+ <?php
208
+ echo '<h4 style="margin:0px;">' . __('Exclude folders (Click on it for expanding)', 'wpstg') . '<h4>';
209
+ wpstg_directory_structure($folders, null, false, false, $excluded_folders);
210
+ wpstg_show_large_files();
211
+ echo '<p><span id=wpstg-file-summary>' . __('Files will be copied into subfolder of: ','wpstg') . wpstg_get_clone_root_path() . '</span>';
212
+ ?>
213
+ </div> <!-- #wpstg-scanning-files -->
214
+
215
+ <a href="#" class="wpstg-tab-header" data-id="#wpstg-advanced-settings">
216
+ <span class="wpstg-tab-triangle">&#9658;</span>
217
+ <?php echo __('Advanced Options', 'wpstg'); ?>
218
+ </a>
219
+ <div class="wpstg-tab-section" id="wpstg-advanced-settings">
220
+ <?php echo wpstg_advanced_settings(); ?>
221
+ </div> <!-- #wpstg-advanced-settings -->
222
+
223
+
224
+ </div>
225
+ <a href="#" class="wpstg-prev-step-link wpstg-link-btn button-primary"><?php _e('Back', 'wpstg'); ?></a>
226
+ <a href="#" id="wpstg-start-cloning" class="wpstg-next-step-link wpstg-link-btn button-primary" data-action="wpstg_cloning"><?php echo wpstg_return_button_title();?></a>
227
+ <?php
228
+ wp_die();
229
+ }
230
+ add_action('wp_ajax_wpstg_scanning', 'wpstg_scanning');
231
+
232
+ /**
233
+ * Display db tables
234
+ *
235
+ * @param array $tables
236
+ * @param array $unchecked_tables
237
+ * @global array $wpstg_clone_details
238
+ */
239
+ function wpstg_show_tables($tables, $unchecked_tables = array()) {
240
+ global $wpstg_clone_details;
241
+
242
+ $cloned_tables = isset($wpstg_clone_details['cloned_tables']) ? $wpstg_clone_details['cloned_tables'] : array();
243
+ foreach ($tables as $table) { ?>
244
+ <div class="wpstg-db-table">
245
+ <label>
246
+ <?php
247
+ $attributes = in_array($table->Name, $unchecked_tables) ? '' : 'checked';
248
+ $attributes .= in_array($table->Name, $cloned_tables) ? ' disabled' : '';
249
+ ?>
250
+ <input class="wpstg-db-table-checkboxes" type="checkbox" name="<?php echo $table->Name; ?>" <?php echo $attributes; ?>>
251
+ <?php echo $table->Name; ?>
252
+ </label>
253
+ <span class="wpstg-size-info">
254
+ <?php echo wpstg_short_size($table->Data_length + $table->Index_length); ?>
255
+ </span>
256
+ </div>
257
+ <?php } ?>
258
+ <div><a href="#" class="wpstg-button-unselect">Unselect all tables</a></div>
259
+ <?php
260
+ }
261
+
262
+
263
+ /**
264
+ * Scan all files and create directory structure
265
+ *
266
+ * @global array $all_files
267
+ * @global array $wpstg_clone_details
268
+ * @global $wpstg_options $wpstg_options
269
+ * @param string $path
270
+ * @param array $folders
271
+ * @return array
272
+ */
273
+ function wpstg_scan_files($path, &$folders = array()) {
274
+ global $all_files, $wpstg_clone_details, $wpstg_options;
275
+
276
+ $batch_size = isset($wpstg_options['wpstg_batch_size']) ? $wpstg_options['wpstg_batch_size'] : 20;
277
+ $batch_size *= 1024*1024;
278
+ $wpstg_clone_details['large_files'] = isset($wpstg_clone_details['large_files']) ? $wpstg_clone_details['large_files'] : array();
279
+ $clone = isset($wpstg_clone_details['current_clone']) ? $wpstg_clone_details['current_clone'] : null;
280
+
281
+ if (is_dir($path)) {
282
+ $dir = dir($path);
283
+ $dirsize = 0;
284
+ while ( method_exists($dir,'read') && false !== ($entry = $dir->read()) ) { // works
285
+ if ($entry == '.' || $entry == '..' || $entry == $clone)
286
+ continue;
287
+ //if ( is_file($path . $entry) && !is_null($path) && !empty($path) && !is_null($path) ) {
288
+ //if (is_file($path . $entry) && is_readable($path . $entry) && !is_null($path) && $path != 'null' && $path != '' && !empty($path)) {
289
+ if (is_file($path . $entry) && is_readable($path . $entry)) {
290
+ $all_files[] = utf8_encode($path . $entry);
291
+ //$all_files[] = $path . $entry;
292
+ //$all_files[] = $path . $entry;
293
+ $dirsize += filesize( $path . $entry);
294
+ if ($batch_size < $size = filesize($path . $entry ))
295
+ $wpstg_clone_details['large_files'][] = $path . $entry;
296
+ $wpstg_clone_details['total_size'] += $size;
297
+ continue;
298
+ }
299
+
300
+ $tmp_path = str_replace('//', '/' ,$path . '/' . $entry .'//'); // Make sure that directory contains ending slash / but never double slashes //
301
+ $tmp = wpstg_scan_files($tmp_path, $folders[$entry]);
302
+
303
+ $dirsize += $tmp['size'];
304
+ }
305
+ $folders['size'] = $dirsize;
306
+ }
307
+ return $folders;
308
+ }
309
+
310
+ /**
311
+ * Get a list of all files which will be copied
312
+ *
313
+ * @param string $folder
314
+ * @param array $files
315
+ * @param string $total_size
316
+ * @return array files
317
+ */
318
+
319
+ function wpstg_get_files($folder, &$files = array(), &$total_size) {
320
+ if (! is_dir($folder))
321
+ return array();
322
+ $dir = dir($folder);
323
+
324
+ while (false !== $entry = $dir->read()) {
325
+ if ($entry == '.' || $entry == '..')
326
+ continue;
327
+ if (is_file("$folder/$entry")) {
328
+ $files[] = "$folder/$entry";
329
+ $total_size -= filesize("$folder/$entry");
330
+ } else
331
+ wpstg_get_files("$folder/$entry", $files, $total_size);
332
+
333
+ }
334
+ return $files;
335
+ }
336
+
337
+ /**
338
+ * Display directory structure with checkboxes
339
+ *
340
+ * @param array $folders list of folder names
341
+ * @param string $path base path for the directory structure
342
+ * @param bool $not_checked checkbox value
343
+ * @param bool $is_removing
344
+ * @param array $excluded_folders
345
+ */
346
+ function wpstg_directory_structure($folders, $path = null, $not_checked = false, $is_removing = false, $excluded_folders = array()) {
347
+ /** @var array $existing_clones */
348
+ $existing_clones = get_option('wpstg_existing_clones', array());
349
+ $path = $path === null ? rtrim(get_home_path(), '/') : $path;
350
+
351
+ foreach ($folders as $name => $folder) {
352
+ $size = array_pop($folder);
353
+ if ($is_removing) {
354
+ $not_checked_tmp = false;
355
+ } else {
356
+ if (empty ($excluded_folders))
357
+ $not_checked_tmp = $not_checked ? $not_checked : in_array($name, $existing_clones);
358
+ else
359
+ $not_checked_tmp = in_array("$path/$name", $excluded_folders);
360
+ }
361
+ ?>
362
+ <div class="wpstg-dir">
363
+ <input type="checkbox" class="wpstg-check-dir" <?php echo $not_checked_tmp ? '' : 'checked'; ?> name="<?php echo "$path/$name"; ?>">
364
+ <a href="#" class="wpstg-expand-dirs <?php echo $not_checked_tmp ? 'disabled' : ''; ?>"><?php echo $name;?></a>
365
+ <span class="wpstg-size-info"><?php echo wpstg_short_size($size); ?></span>
366
+ <?php if (!empty($folder)) : ?>
367
+ <div class="wpstg-dir wpstg-subdir">
368
+ <?php wpstg_directory_structure($folder, "$path/$name", $not_checked_tmp, $is_removing, $excluded_folders); ?>
369
+ </div>
370
+ <?php endif; ?>
371
+ </div>
372
+ <?php }
373
+ }
374
+
375
+ /**
376
+ * Convert byte into human readable numbers
377
+ *
378
+ * @param integer $size
379
+ * @return string
380
+ */
381
+ function wpstg_short_size($size) {
382
+ if (1 < $out = $size / 1000000000)
383
+ return round($out, 2) . ' Gb';
384
+ else if (1 < $out = $size / 1000000)
385
+ return round($out, 2) . ' Mb';
386
+ else if (1 < $out = $size / 1000)
387
+ return round($out, 2) . ' Kb';
388
+ return $size . ' bytes';
389
+ }
390
+
391
+ /**
392
+ * Display list of large files
393
+ */
394
+ function wpstg_show_large_files() {
395
+ global $wpstg_clone_details;
396
+
397
+ $large_files = isset($wpstg_clone_details['large_files']) ? $wpstg_clone_details['large_files'] : array();
398
+ if (! empty($large_files)) : ?>
399
+ <br>
400
+ <span style="font-weight:bold;">That might require your attention:</span>
401
+ <br>
402
+ <span id="wpstg-show-large-files"><a href="#">Show large files</a></span>
403
+
404
+ <div id="wpstg-large-files">
405
+ <h4 style="margin-top: 0px;">
406
+ <?php echo __('We have detected the following large files which could neeed some investigation. Often this files are backup files or other temporary files which must not necessarily copied for creating a staging site:','wpstg');?>
407
+ </h4>
408
+
409
+ <?php foreach ($large_files as $file) : ?>
410
+ <div class="wpstg-large-file"><li><?php echo $file; ?></li></div>
411
+ <?php endforeach; ?>
412
+ </div> <!-- #wpstg-large-file -->
413
+
414
+ <?php endif;
415
+ }
416
+
417
+ /**
418
+ * Display advanced settings
419
+ */
420
+ function wpstg_advanced_settings() {
421
+ ?>
422
+ Comming soon
423
+ <?php
424
+ }
425
+
426
+ /**
427
+ * Sanitize staging name
428
+ * Lowercase alphanumeric characters, dashes and underscores are allowed.
429
+ * Uppercase characters will be converted to lowercase.
430
+ *
431
+ * @param string
432
+ * @return string
433
+ */
434
+
435
+ function wpstg_sanitize_key($key){
436
+ return sanitize_key( $key );
437
+ }
438
+
439
+ /**
440
+ * Check the staging name input
441
+ *
442
+ * - Check if name does not contain more than 17 characters
443
+ * - Check is staging name does not already exists
444
+ * - Sanitize staging name
445
+ *
446
+ * @return json message | name | percent | running time | status
447
+ */
448
+ function wpstg_check_clone() {
449
+ global $wpstg_clone_details;
450
+ $wpstg_clone_details = wpstg_get_options();
451
+ //$cur_clone = preg_replace('/[^A-Za-z0-9]/', '', $_POST['cloneID']);
452
+ $cur_clone = wpstg_sanitize_key($_POST['cloneID']);
453
+ $existing_clones = get_option('wpstg_existing_clones', array());
454
+ strlen($_POST['cloneID']) >= 17 ? $max_length = true : $max_length = false;
455
+ //wp_die(!in_array($cur_clone, $existing_clones));
456
+ if ( $max_length )
457
+ wpstg_return_json('wpstg_check_clone', 'fail', 'Clone name must not be longer than 16 characters', 1, wpstg_get_runtime());
458
+
459
+ if ( in_array($cur_clone, $existing_clones) )
460
+ wpstg_return_json('wpstg_check_clone', 'fail', 'Clone with same name already exists', 1, wpstg_get_runtime());
461
+
462
+ if ( !in_array($cur_clone, $existing_clones ) && $max_length !== true )
463
+ wpstg_return_json('wpstg_check_clone', 'success', '', 1, wpstg_get_runtime());
464
+ }
465
+ add_action('wp_ajax_wpstg_check_clone', 'wpstg_check_clone');
466
+
467
+ /**
468
+ * 3rd step: Cloning
469
+ *
470
+ * @global array $wpstg_clone_details clone related data
471
+ */
472
+ function wpstg_cloning() {
473
+ global $wpstg_clone_details;
474
+ check_ajax_referer( 'wpstg_ajax_nonce', 'nonce' );
475
+ $wpstg_clone_details = wpstg_get_options();
476
+
477
+ $clone = preg_replace('/[^A-Za-z0-9]/', '', $_POST['cloneID']);
478
+ $wpstg_clone_details['current_clone'] = isset($wpstg_clone_details['current_clone']) ? $wpstg_clone_details['current_clone'] : $clone;
479
+
480
+ $wpstg_clone_details['cloned_tables'] = isset($wpstg_clone_details['cloned_tables']) ? $wpstg_clone_details['cloned_tables'] : array();
481
+ if (isset($_POST['uncheckedTables']))
482
+ $wpstg_clone_details['cloned_tables'] = array_merge($wpstg_clone_details['cloned_tables'], $_POST['uncheckedTables']);
483
+ if (isset($_POST['excludedFolders'])) {
484
+ $path = wpstg_get_upload_dir() . '/remaining_files.json';
485
+ $all_files = json_decode(file_get_contents($path), true);
486
+
487
+ $excluded_files = array();
488
+ //$excluded_files = array (null, 'null'); //rhe 25.09.2015
489
+ foreach ($_POST['excludedFolders'] as $folder) {
490
+ $tmp_array = array();
491
+
492
+ $excluded_files = array_merge($excluded_files, wpstg_get_files($folder, $tmp_array, $wpstg_clone_details['total_size']));
493
+ }
494
+ $excluded_files = array_map("utf8_encode", $excluded_files ); // convert to utf 8 because $all_files is also utf8
495
+ $remaining_files = array_diff($all_files, $excluded_files);
496
+ file_put_contents($path, json_encode(array_values($remaining_files)));
497
+ }
498
+
499
+ $wpstg_clone_details['db_progress'] = isset($wpstg_clone_details['db_progress']) ? $wpstg_clone_details['db_progress'] : 0;
500
+ $wpstg_clone_details['files_progress'] = isset($wpstg_clone_details['files_progress']) ? $wpstg_clone_details['files_progress'] : 0;
501
+ $wpstg_clone_details['links_progress'] = isset($wpstg_clone_details['links_progress']) ? $wpstg_clone_details['links_progress'] : 0;
502
+
503
+ wpstg_save_options();
504
+
505
+ $unchecked_tables = isset($_POST['uncheckedTables']) ? $_POST['uncheckedTables'] : array();
506
+ $excluded_folders = isset($_POST['excludedFolders']) ? $_POST['excludedFolders'] : array();
507
+ //$clone_path = isset($_POST['path']) ? $_POST['path'] : get_home_path();
508
+
509
+ //wpstg_initiate_profile($clone, $unchecked_tables, $excluded_folders, $clone_path);
510
+ do_action('wpstg_start_cloning', $clone);
511
+ ?>
512
+ <div class="wpstg-cloning-section"> <?php echo __('Copy database tables', 'wpstg');?>
513
+ <div class="wpstg-progress-bar">
514
+ <div class="wpstg-progress" id="wpstg-db-progress" style="width: <?php echo 100 * $wpstg_clone_details['db_progress']; ?>%;"></div>
515
+ </div>
516
+ </div>
517
+ <div class="wpstg-cloning-section"><?php echo __('Copy files', 'wpstg');?>
518
+ <div class="wpstg-progress-bar">
519
+ <div class="wpstg-progress" id="wpstg-files-progress" style="width: <?php echo 100 * $wpstg_clone_details['files_progress']; ?>%;"></div>
520
+ </div>
521
+ </div>
522
+ <div class="wpstg-cloning-section"><?php echo __('Replace Links', 'wpstg');?>
523
+ <div class="wpstg-progress-bar">
524
+ <div class="wpstg-progress" id="wpstg-links-progress" style="width: <?php echo 100 * $wpstg_clone_details['links_progress']; ?>%"></div>
525
+ </div>
526
+ </div>
527
+ <!--<a href="<?php //echo get_home_url();?>" id="wpstg-clone-url" target="_blank"></a>-->
528
+ <a href="#" id="wpstg-cancel-cloning" class="wpstg-link-btn button-primary"><?php echo __('Cancel', 'wpstg');?></a>
529
+ <!--<a href="#" id="wpstg-home-link" class="wpstg-link-btn button-primary"><?php //echo __('Home', 'wpstg');?></a>
530
+ <a href="#" id="wpstg-try-again" class="wpstg-link-btn button-primary"><?php //echo __('Try Again', 'wpstg');?></a>-->
531
+
532
+ <a href="#" id="wpstg-show-log-button" class="button" style="margin-top: 5px;"><?php _e('Display working log','wpstg'); ?></a>
533
+ <div><span id="wpstg-cloning-result"></span></div>
534
+ <div id="wpstg-finished-result"><h3>Congratulations: </h3>
535
+ <?php echo __('WP Staging succesfully created a staging site in a subfolder of your main site in: <strong> ', 'wpstg') . get_home_url(); ?>/<span id="wpstg_staging_name"></span></strong>
536
+ <br><br>
537
+ <?php echo __('Now, you have several options: ', 'wpstg'); ?>
538
+ <br>
539
+ <a href="<?php echo get_home_url();?>" id="wpstg-clone-url" target="_blank" class="wpstg-link-btn button-primary">Open staging site <span style="font-size: 10px;">(login with your admin credentials)</span></a>
540
+ <a href="" class="wpstg-link-btn button-primary" id="wpstg-remove-cloning"><?php echo __('Remove', 'wpstg');?></a>
541
+ <a href="" class="wpstg-link-btn button-primary" id="wpstg-home-link"><?php echo __('Start again', 'wpstg');?></a>
542
+ <div id="wpstg-success-notice">
543
+ <h3 style="margin-top:0px;"><?php _e('Important notes:' ,'wpstg'); ?></h3>
544
+ <ul>
545
+ <li> <strong>1. Permalinks on your <span style="font-style:italic;">staging site</span> will be disabled for technical reasons! </strong><br>Usually this is no problem for a staging website and you do not have to use permalinks!<br>
546
+ <p>If you really need permalinks on your staging site you have to do several modifications to your .htaccess (Apache) or *.conf (Nginx). <br>WP Staging can not do this automatically.
547
+ <p><strong>Read more:</strong>
548
+ <a href="http://stackoverflow.com/questions/5564881/htaccess-to-rewrite-wordpress-subdirectory-with-permalinks-to-root" target="_blank">Changes .htaccess </a> | <a href="http://robido.com/nginx/nginx-wordpress-subdirectory-configuration-example/" target="_blank">Changes nginx conf</a>
549
+ </li>
550
+ <li> <strong>2. Verify that you are REALLY working on your staging site and NOT on your production site if you are uncertain! </strong>
551
+ <br>Your main and your staging site are both reachable under the same domain so<br> it´s easy to become confused. <p>
552
+ To assist you we changed the name of the dashboard link to <strong style="font-style:italic;">"Staging - <span class="wpstg-clone-name"><?php echo get_bloginfo('name');?></span>"</strong>.
553
+ <br>You will notice this new name in the admin bar:
554
+ <br><br>
555
+ <img src="<?php echo WPSTG_PLUGIN_URL . '/assets/images/admin_dashboard.png'; ?>">
556
+ </li>
557
+ </ul>
558
+ </div>
559
+ </div>
560
+ <div id="wpstg-error-wrapper">
561
+ <div id="wpstg-error-details"></div>
562
+ </div>
563
+ <div id="wpstg-log-details"></div>
564
+ <?php
565
+ wp_die();
566
+ }
567
+ add_action('wp_ajax_wpstg_cloning', 'wpstg_cloning');
568
+
569
+ /**
570
+ * Create a clone profile with start settings
571
+ *
572
+ * @param $clone
573
+ * @param $unchecked_tables
574
+ * @param $excluded_folders
575
+ */
576
+ function wpstg_initiate_profile($clone, $unchecked_tables, $excluded_folders, $path) {
577
+ $wpstg_profiles = get_option('wpstg_profiles', array());
578
+
579
+ $profile = array(
580
+ 'name' => $clone,
581
+ 'unchecked_tables' => $unchecked_tables,
582
+ 'excluded_folders' => $excluded_folders,
583
+ 'path' => $path,
584
+ );
585
+ $wpstg_profiles[$clone] = $profile;
586
+
587
+ update_option('wpstg_profiles', $wpstg_profiles);
588
+ }
589
+
590
+ function wpstg_get_profile($clone) {
591
+ $wpstg_profiles = get_option('wpstg_profiles', array());
592
+
593
+ return isset($wpstg_profiles[$clone]) ? $wpstg_profiles[$clone] : false;
594
+ }
595
+
596
+ /**
597
+ * Helper function to get database object for cloning into external database
598
+ *
599
+ * @global object $wpdb database object
600
+ * @global array $wpstg_clone_details clone related settings
601
+ */
602
+ function wpstg_clone_db() {
603
+ global $wpdb, $wpstg_clone_details;
604
+ $wpstg_clone_details = wpstg_get_options();
605
+
606
+ // $clone = $profile = wpstg_get_profile
607
+
608
+ $db_helper = apply_filters('wpstg_db_helper', $wpdb, $wpstg_clone_details['current_clone']);
609
+ if ($db_helper->dbname != $wpdb->dbname)
610
+ wpstg_clone_db_external($db_helper);
611
+ else
612
+ wpstg_clone_db_internal();
613
+ }
614
+ add_action('wp_ajax_wpstg_clone_db', 'wpstg_clone_db');
615
+
616
+ /**
617
+ * Clone into internal database
618
+ *
619
+ * @global object $wpdb database object
620
+ * @global array $wpstg_clone_details clone data
621
+ * @global array $wpstg_options global options
622
+ */
623
+ function wpstg_clone_db_internal() {
624
+ global $wpdb, $wpstg_clone_details, $wpstg_options;
625
+ check_ajax_referer( 'wpstg_ajax_nonce', 'nonce' );
626
+ $wpstg_clone_details = wpstg_get_options();
627
+
628
+ // Start timer
629
+ wpstg_get_runtime();
630
+
631
+ // Use only for debugging
632
+ //usleep(40000000);
633
+
634
+ $progress = isset($wpstg_clone_details['db_progress']) ? $wpstg_clone_details['db_progress'] : 0;
635
+ if ($progress >= 1)
636
+ wpstg_return_json('wpstg_clone_db_internal', 'success', 'DB successfull copied', 1, wpstg_get_runtime());
637
+
638
+
639
+ $limit = isset($wpstg_options['wpstg_query_limit']) ? $wpstg_options['wpstg_query_limit'] : 100;
640
+ $rows_count = 0;
641
+ $log_data = '';
642
+
643
+ while (true) {
644
+ $table = isset($wpstg_clone_details['current_table']) ? $wpstg_clone_details['current_table'] : null;
645
+ $is_new = false;
646
+
647
+ if ($table === null) {
648
+ $tables = $wpstg_clone_details['all_tables'];
649
+ $cloned_tables = !empty($wpstg_clone_details['cloned_tables']) ? $wpstg_clone_details['cloned_tables'] : array(); //already cloned tables
650
+ $tables = array_diff($tables, $cloned_tables);
651
+ if (empty($tables)) { //exit condition
652
+ $wpstg_clone_details['db_progress'] = 1;
653
+ wpstg_save_options();
654
+ WPSTG()->logger->info('DB has been cloned successfully');
655
+ wpstg_return_json('wpstg_clone_db_internal', 'success', 'DB has been cloned successfully', 1, wpstg_get_runtime());
656
+ }
657
+ $table = reset($tables);
658
+ $is_new = true;
659
+ }
660
+
661
+ $new_table = $wpstg_clone_details['current_clone'] . '_' . $table;
662
+ $offset = isset($wpstg_clone_details['offsets'][$table]) ? $wpstg_clone_details['offsets'][$table] : 0;
663
+ $is_cloned = true;
664
+
665
+ if ($is_new) {
666
+ $existing_table = $wpdb->get_var(
667
+ $wpdb->prepare(
668
+ 'show tables like %s',
669
+ $new_table
670
+ )
671
+ );
672
+ if ($existing_table == $new_table)
673
+ $wpdb->query("drop table $new_table");
674
+
675
+ $is_cloned = $wpdb->query("create table `$new_table` like `$table`");
676
+ wpstg_debug_log("Debug QUERY: create table $new_table like $table");
677
+ $wpstg_clone_details['current_table'] = $table;
678
+ }
679
+ if ($is_cloned) {
680
+ /*$limit -= $rows_count;
681
+ if ($limit < 1)
682
+ break; rhe */
683
+ $inserted_rows = $wpdb->query(
684
+ "insert `$new_table` select * from `$table` limit $offset, $limit"
685
+ );
686
+ wpstg_debug_log("Debug QUERY: insert $new_table select * from $table limit $offset, $limit");
687
+ if ($inserted_rows !== false) {
688
+ $wpstg_clone_details['offsets'][$table] = $offset + $inserted_rows;
689
+ $rows_count += $inserted_rows;
690
+ if ($inserted_rows < $limit) {
691
+ $wpstg_clone_details['cloned_tables'][] = $table;
692
+ $all_tables_count = count($wpstg_clone_details['all_tables']);
693
+ $cloned_tables_count = count($wpstg_clone_details['cloned_tables']);
694
+ $wpstg_clone_details['db_progress'] = $cloned_tables_count / $all_tables_count;
695
+ $log_data .= '[' . date('d-m-Y H:i:s') . '] Copy database table: ' . $wpstg_clone_details['current_table'] . '<br>';
696
+ unset($wpstg_clone_details['current_table']);
697
+ wpstg_save_options();
698
+ }
699
+
700
+ if ($rows_count > $limit) {
701
+ $all_tables_count = count($wpstg_clone_details['all_tables']);
702
+ $cloned_tables_count = count($wpstg_clone_details['cloned_tables']);
703
+ $wpstg_clone_details['db_progress'] = $cloned_tables_count / $all_tables_count;
704
+ wpstg_save_options();
705
+ $log_data .= '[' . date('d-m-Y H:i:s') . '] Copy database table: ' . $table . '<br>';
706
+ WPSTG()->logger->info( 'Query limit exceeded. Starting new query batch! Table: ' . $table);
707
+ wpstg_return_json('wpstg_clone_db_internal', 'success', wpstg_get_log_data($progress) . $log_data, $wpstg_clone_details['db_progress'], wpstg_get_runtime());
708
+ }
709
+ } else {
710
+ WPSTG()->logger->info('Table ' . $new_table . ' has been created, BUT inserting rows failed. This happens sometimes when a table had been updated during staging process. Exclude this table from copying and try again. Offset: ' . $offset . '');
711
+ wpstg_save_options();
712
+ wpstg_return_json('wpstg_clone_db_internal', 'fail', 'Table ' . $new_table . ' has been created, BUT inserting rows failed. This happens sometimes when a table had been updated during staging process. Exclude this table from copying and try again. Offset: ' . $offset, $wpstg_clone_details['db_progress'] . ' ', wpstg_get_runtime());
713
+
714
+ }
715
+ } else {
716
+ WPSTG()->logger->info('Creating table ' . $table . ' has been failed.');
717
+ wpstg_save_options();
718
+ wpstg_return_json('wpstg_clone_db_internal', 'fail', 'Copying table ' . $table . ' has been failed.', $wpstg_clone_details['db_progress'], wpstg_get_runtime());
719
+ }
720
+ $log_data .= '[' . date('d-m-Y H:i:s') . '] Copy database table: ' . $table . ' DB rows: ' . $offset . '<br>';
721
+ } //end while
722
+ wpstg_save_options();
723
+ wpstg_return_json('wpstg_clone_db_internal', 'success', wpstg_get_log_data($progress) . $log_data, $progress, wpstg_get_runtime());
724
+ }
725
+ /**
726
+ *
727
+ * Clone into separate database
728
+ *
729
+ * @global object $wpdb database object
730
+ * @global array $wpstg_clone_details clone related data
731
+ * @global array $wpstg_options global settings
732
+ *
733
+ * @param object $db_helper new database object
734
+ */
735
+ function wpstg_clone_db_external($db_helper) {
736
+ global $wpdb, $wpstg_clone_details, $wpstg_options;
737
+ check_ajax_referer( 'wpstg_ajax_nonce', 'nonce' );
738
+ $one = 1;
739
+
740
+ $progress = isset($wpstg_clone_details['db_progress']) ? $wpstg_clone_details['db_progress'] : 0;
741
+ if ($progress >= 1)
742
+ wp_die(1);
743
+
744
+ $limit = isset($wpstg_options['wpstg_query_limit']) ? $wpstg_options['wpstg_query_limit'] : 1000;
745
+ $rows_count = 0;
746
+
747
+ while (true) {
748
+ $table = isset($wpstg_clone_details['current_table']) ? $wpstg_clone_details['current_table'] : null;
749
+ $is_new = false;
750
+
751
+ if ($table === null) {
752
+ $tables = $wpstg_clone_details['all_tables'];
753
+ $cloned_tables = !empty($wpstg_clone_details['cloned_tables']) ? $wpstg_clone_details['cloned_tables'] : array(); //already cloned tables
754
+ $tables = array_diff($tables, $cloned_tables);
755
+ if (empty($tables)) { //exit condition
756
+ $wpstg_clone_details['db_progress'] = 1;
757
+ wpstg_save_options();
758
+ WPSTG()->logger->info('DB has been cloned successfully');
759
+ wp_die(1);
760
+ }
761
+ $table = reset($tables);
762
+ $is_new = true;
763
+ }
764
+
765
+ $new_table = $wpstg_clone_details['current_clone'] . '_' . $table;
766
+ $offset = isset($wpstg_clone_details['offsets'][$table]) ? $wpstg_clone_details['offsets'][$table] : 0;
767
+ $is_cloned = true;
768
+
769
+ if ($is_new) {
770
+ $existing_table = $db_helper->get_var(
771
+ $db_helper->prepare(
772
+ 'show tables like %s',
773
+ $new_table
774
+ )
775
+ );
776
+ if ($existing_table == $new_table)
777
+ $db_helper->query("drop table $new_table");
778
+
779
+ $tmp_result = $wpdb->get_row("show create table $table", ARRAY_N);
780
+ $create_sql = str_replace($tmp_result[0], $new_table, $tmp_result[1], $one);
781
+ $is_cloned = $db_helper->query($create_sql);
782
+ $wpstg_clone_details['current_table'] = $table;
783
+ }
784
+ if ($is_cloned) {
785
+ $limit -= $rows_count;
786
+ if ($limit < 1)
787
+ break;
788
+
789
+ $selected_rows = $wpdb->get_results("select * from $table limit $offset, $limit", ARRAY_N);
790
+ $inserted_rows = 0;
791
+ foreach ($selected_rows as $row) {
792
+ $row = esc_sql($row);
793
+ $values = implode("', '", $row);
794
+ $query = "insert into $new_table values('$values')";
795
+ $inserted_rows += $db_helper->query($query);
796
+ }
797
+
798
+ if ($inserted_rows !== false) {
799
+ $wpstg_clone_details['offsets'][$table] = $offset + $inserted_rows;
800
+ $rows_count += $inserted_rows;
801
+ if ($inserted_rows < $limit) {
802
+ $wpstg_clone_details['cloned_tables'][] = $table;
803
+ $all_tables_count = count($wpstg_clone_details['all_tables']);
804
+ $cloned_tables_count = count($wpstg_clone_details['cloned_tables']);
805
+ $wpstg_clone_details['db_progress'] = $cloned_tables_count / $all_tables_count;
806
+ unset($wpstg_clone_details['current_table']);
807
+ wpstg_save_options();
808
+ }
809
+ if ($rows_count > $limit) {
810
+ $all_tables_count = count($wpstg_clone_details['all_tables']);
811
+ $cloned_tables_count = count($wpstg_clone_details['cloned_tables']);
812
+ $wpstg_clone_details['db_progress'] = $cloned_tables_count / $all_tables_count;
813
+ wpstg_save_options();
814
+ WPSTG()->logger->info('Query limit is exceeded. Current Table: ' . $table);
815
+ wp_die($wpstg_clone_details['db_progress']);
816
+ }
817
+ } else {
818
+ WPSTG()->logger->info('Table ' . $new_table . ' has been created, BUT inserting rows failed. Offset: ' . $offset);
819
+ wpstg_save_options();
820
+ //wp_die(-1);
821
+ wp_die('Table ' . $new_table . ' has been created, BUT inserting rows failed. Offset: ' . $offset);
822
+ }
823
+ } else {
824
+ WPSTG()->logger->info('Creating table ' . $table . ' has been failed.');
825
+ wpstg_save_options();
826
+ //wp_die(-1);
827
+ wp_die('Creating table ' . $table . ' has been failed.');
828
+ }
829
+ } //end while
830
+ wpstg_save_options();
831
+ wp_die($progress);
832
+ }
833
+
834
+ /**
835
+ * Get clone root path. Thats the absolute path to wordpress,
836
+ * no matter if wordpress is installed in a subdirectory or not.
837
+ * This path is everytime the same and is used to determine the clone destination
838
+ *
839
+ * @global $wpstg_options global settings
840
+ * @return string clone root path
841
+ */
842
+ function wpstg_get_clone_root_path() {
843
+ $home_path = ABSPATH;
844
+ return str_replace('\\', '/', $home_path);
845
+ }
846
+
847
+ /**
848
+ * Copy selected files files into staging subfolder
849
+ *
850
+ * @global $wpstg_clone_details
851
+ * @global $wpstg_options
852
+ * @global $batch
853
+ */
854
+ function wpstg_copy_files() {
855
+ global $wpstg_clone_details, $wpstg_options, $batch;
856
+ check_ajax_referer( 'wpstg_ajax_nonce', 'nonce' );
857
+ $wpstg_clone_details = wpstg_get_options();
858
+
859
+ // Start timer
860
+ wpstg_get_runtime();
861
+
862
+ // Use only for debugging
863
+ //usleep(40000000);
864
+
865
+ $clone_root_path = get_home_path() . $wpstg_clone_details['current_clone'];
866
+ //$clone_root_path = wpstg_get_clone_root_path() . $wpstg_clone_details['current_clone'];
867
+ $sourcepath = wpstg_get_upload_dir() . '/remaining_files.json';
868
+ $files = json_decode(file_get_contents($sourcepath), true);
869
+ $start_index = isset($wpstg_clone_details['file_index']) ? $wpstg_clone_details['file_index'] : 0;
870
+ $wpstg_clone_details['files_progress'] = isset($wpstg_clone_details['files_progress']) ? $wpstg_clone_details['files_progress'] : 0;
871
+ $batch_size = isset($wpstg_options['wpstg_batch_size']) ? $wpstg_options['wpstg_batch_size'] : 2;
872
+ $batch_size *= 1024*1024;
873
+ $batch = 0;
874
+ $log_data = '';
875
+ $size = 0;
876
+
877
+ if (!is_dir($clone_root_path))
878
+ mkdir($clone_root_path);
879
+
880
+ for ($i = $start_index; $i < count($files); $i++) {
881
+ $new_file = wpstg_create_directories($files[$i], wpstg_get_clone_root_path(), $clone_root_path);
882
+
883
+ if ( file_exists($files[$i] ) ){
884
+ $size = filesize($files[$i]);
885
+ }
886
+
887
+ if ( is_file($files[$i]) && file_exists($files[$i]) && $size > $batch_size ) { // is_file() checks if its a symlink or real file
888
+ //if ( is_file($files[$i] && file_exists($files[$i] && $size > $batch_size) ) ) { // is_file() checks if its a symlink or real file
889
+ if (wpstg_copy_large_file($files[$i], $new_file, $batch_size)) {
890
+ //WPSTG()->logger->info('Copy LARGE file: ' . $files[$i] . '. Batch size: ' . wpstg_short_size($batch + $size) . ' (' . ($batch + $size) . ' bytes)');
891
+ $wpstg_clone_details['file_index'] = $i + 1;
892
+ //$part = ($batch + $size) / $wpstg_clone_details['total_size'];
893
+ $part = $batch / $wpstg_clone_details['total_size'];
894
+ $wpstg_clone_details['files_progress'] += $part;
895
+ wpstg_save_options();
896
+ wpstg_return_json('wpstg_copy_files', 'success', '<br> [' . date('d-m-Y H:i:s') . '] Copy LARGE file: ' . $files[$i] . '. Batch size: ' . wpstg_short_size($batch + $size) . ' (' . ($batch + $size) . ' bytes)', $wpstg_clone_details['files_progress'], wpstg_get_runtime());
897
+ } else {
898
+ WPSTG()->logger->info('Copying LARGE file has been failed and will be skipped: ' . $files[$i]);
899
+ $wpstg_clone_details['file_index'] = $i + 1; //increment it because we want to skip this file when it can not be copied successfully
900
+ $part = $batch / $wpstg_clone_details['total_size'];
901
+ $wpstg_clone_details['files_progress'] += $part;
902
+ wpstg_save_options();
903
+ wpstg_return_json('wpstg_copy_files', 'fail', '<br> [' . date('d-m-Y H:i:s') . '] <span style="color:red;">Fail: </span> Copying LARGE file has been failed and will be skipped: ' . $files[$i], $wpstg_clone_details['files_progress'], wpstg_get_runtime());
904
+ }
905
+ }
906
+ if ( $batch_size > $batch + $size ) {
907
+ //if ( is_readable( $files[$i] ) && is_file($files[$i]) && copy($files[$i], $new_file)) {
908
+ if ( is_readable( $files[$i] ) && copy($files[$i], $new_file)) {
909
+ $batch += $size;
910
+ WPSTG()->logger->info('Copy file no: ' . $i . ' Total files:' . count($files) .' File: ' . $files[$i] . ' to ' . $new_file);
911
+ //wpstg_return_json('wpstg_copy_files', 'success', '[' . date('d-m-Y H:i:s') . '] Copy file no: ' . $i . ' Total files:' . count($files) .' File: ' . $files[$i] . ' to ' . $new_file, $wpstg_clone_details['files_progress'], wpstg_get_runtime());
912
+ } else {
913
+ WPSTG()->logger->info('Copying file has been failed and will be skipped: ' . $files[$i]);
914
+ $wpstg_clone_details['file_index'] = $i + 1; //increment it because we want to skip this file when it can not be copied successfully
915
+ $part = $batch / $wpstg_clone_details['total_size'];
916
+ $wpstg_clone_details['files_progress'] += $part;
917
+ wpstg_save_options();
918
+ //wp_die(-1);
919
+ //wp_die('Copying file has been failed: ' . $files[$i]);
920
+ wpstg_return_json('wpstg_copy_files', 'fail', '[' . date('d-m-Y H:i:s') . '] <span style="color:red;">Fail: </span> Copying file has been failed and will be skipped: ' . $files[$i], $wpstg_clone_details['files_progress'], wpstg_get_runtime());
921
+ }
922
+ } else {
923
+ WPSTG()->logger->info('Batch size: ' . wpstg_short_size($batch) . ' (' . $batch . ' bytes)' . '. Current File: ' . $files[$i]);
924
+ $wpstg_clone_details['file_index'] = $i;
925
+ $part = $batch / $wpstg_clone_details['total_size'];
926
+ $wpstg_clone_details['files_progress'] += $part;
927
+ wpstg_save_options();
928
+
929
+ wpstg_return_json('wpstg_copy_files', 'success', '[' . date('d-m-Y H:i:s') . '] File copy in progress... ' . round($wpstg_clone_details['files_progress'] * 100, 1) . '%', $wpstg_clone_details['files_progress'], wpstg_get_runtime());
930
+ }
931
+ } // for loop
932
+
933
+ $wpstg_clone_details['files_progress'] = 1;
934
+ wpstg_save_options();
935
+ //wp_die(1);
936
+ wpstg_return_json('wpstg_copy_files', 'success', '[' . date('d-m-Y H:i:s') . '] File copy succeeded. Percent 100%', $wpstg_clone_details['files_progress'], wpstg_get_runtime());
937
+ }
938
+ add_action('wp_ajax_wpstg_copy_files', 'wpstg_copy_files');
939
+
940
+
941
+ /**
942
+ * Create target directory during copy process and returns path to the copied file
943
+ *
944
+ * @param string $source Source file including full path e.g. var/www/htdocs/mainsite/index.php
945
+ * @param string $home_root_path path of the main wordpress installation e.g. var/www/htdocs/mainsite/
946
+ * @param string $clone_root_path main path of the clone e.g. var/www/htdocs/mainsite/myclone/
947
+ *
948
+ * @return string full target path e.g. var/www/htdocs/mainsite/myclone/index.php
949
+ *
950
+ *
951
+ */
952
+ function wpstg_create_directories($source, $home_root_path, $clone_root_path) {
953
+ $path = substr($source, strlen($home_root_path)); // remove the source part from home_root_path
954
+ $folders = explode('/', $path); // convert path into array
955
+ array_pop($folders); // remove the file
956
+
957
+
958
+ $new_folder = $clone_root_path;
959
+ //$new_folder = wpstg_get_clone_root_path();
960
+ foreach ($folders as $folder) {
961
+ $new_folder .= '/' . $folder;
962
+ if (!is_dir($new_folder))
963
+ mkdir($new_folder);
964
+ }
965
+
966
+ $destination = $clone_root_path . "/" . $path;
967
+ //$destination = $new_folder . "/" . $path;
968
+ return $destination;
969
+ }
970
+
971
+
972
+
973
+ /**
974
+ *
975
+ * Copy large files in chunks
976
+ * Set return value to true, no matter if copying has been successfull or not.
977
+ * Unsuccessfull copying will be skipped for large files.
978
+ *
979
+ * @param string $src source path
980
+ * @param string $dst destination path
981
+ * @param integer $buffersize // Not used at the moment
982
+ * @return boolean
983
+ */
984
+ function wpstg_copy_large_file($src, $dst, $buffersize) {
985
+ $src = fopen($src, 'r');
986
+ $dest = fopen($dst, 'w');
987
+
988
+ // Try first method:
989
+ while (! feof($src)){
990
+ if (false === fwrite($dest, fread($src, $buffersize))){
991
+ $error = true;
992
+ }
993
+ }
994
+ // Try second method if first one failed
995
+ if (isset($error) && ($error === true)){
996
+ while(!feof($src)){
997
+ stream_copy_to_stream($src, $dest, 1024 );
998
+ }
999
+ fclose($src);
1000
+ fclose($dest);
1001
+ return true;
1002
+ }
1003
+ fclose($src);
1004
+ fclose($dest);
1005
+ return true;
1006
+ }
1007
+
1008
+
1009
+
1010
+ /**
1011
+ * Replace all urls in data
1012
+ *
1013
+ * @global object $wpdb
1014
+ * @global array $wpstg_clone_details
1015
+ */
1016
+
1017
+ function wpstg_replace_links() {
1018
+ global $wpdb, $wpstg_clone_details;
1019
+ check_ajax_referer( 'wpstg_ajax_nonce', 'nonce' );
1020
+ $wpstg_clone_details = wpstg_get_options();
1021
+
1022
+ $db_helper = apply_filters('wpstg_db_helper', $wpdb, $wpstg_clone_details['current_clone']);
1023
+ $new_prefix = $wpstg_clone_details['current_clone'] . '_' . $wpdb->prefix;
1024
+ $wpstg_clone_details['links_progress'] = isset($wpstg_clone_details['links_progress']) ? $wpstg_clone_details['links_progress'] : 0;
1025
+ //replace site url in options
1026
+ if ($wpstg_clone_details['links_progress'] < 0.1) {
1027
+ $result = $db_helper->query(
1028
+ $db_helper->prepare(
1029
+ 'update ' . $new_prefix . 'options set option_value = %s where option_name = \'siteurl\' or option_name = \'home\'',
1030
+ get_home_url() . '/' . $wpstg_clone_details['current_clone']
1031
+ )
1032
+ );
1033
+ if (!$result) {
1034
+ $wpstg_clone_details['links_progress'] = 0.33;
1035
+ $error = isset($db_helper->dbh->error) ? $db_helper->dbh->error : '';
1036
+ WPSTG()->logger->info('Replacing site url has been failed. ' . $error);
1037
+ //wp_die($db_helper->dbh->error);
1038
+ wpstg_save_options();
1039
+ wpstg_return_json('wpstg_replace_links', 'fail', '[' . date('d-m-Y H:i:s') . '] <span style="color:red;">Fail: </span>Replacing site url has been failed. DB Error: ' . $error, $wpstg_clone_details['links_progress'], wpstg_get_runtime());
1040
+ } else {
1041
+ $wpstg_clone_details['links_progress'] = 0.33;
1042
+ WPSTG()->logger->info('Replacing siteurl has been done succesfully');
1043
+ wpstg_save_options();
1044
+ wpstg_return_json('wpstg_replace_links', 'success', '[' . date('d-m-Y H:i:s') . '] Replacing "siteurl" has been done succesfully', $wpstg_clone_details['links_progress'], wpstg_get_runtime());
1045
+ }
1046
+ }
1047
+ //Update wpstg_is_staging_site in clone options table
1048
+ if ($wpstg_clone_details['links_progress'] < 0.34) {
1049
+ $result = $db_helper->query(
1050
+ $db_helper->prepare(
1051
+ 'update ' . $new_prefix . 'options set option_value = %s where option_name = \'wpstg_is_staging_site\'',
1052
+ 'true'
1053
+ )
1054
+ );
1055
+ if (!$result) {
1056
+ $wpstg_clone_details['links_progress'] = 0.43;
1057
+ WPSTG()->logger->info('Updating db table ' . $new_prefix . 'options where option_name = wpstg_is_staging_site has been failed');
1058
+ //wp_die(-1);
1059
+ //wp_die('Updating option[wpstg_is_staging_site] has been failed');
1060
+ wpstg_save_options();
1061
+ wpstg_return_json('wpstg_replace_links', 'fail', '[' . date('d-m-Y H:i:s') . '] <span style="color:red;">Fail: </span> Updating db' . $new_prefix . 'options where option_name = wpstg_is_staging_site has been failed', $wpstg_clone_details['links_progress'], wpstg_get_runtime());
1062
+ } else {
1063
+ $wpstg_clone_details['links_progress'] = 0.43;
1064
+ WPSTG()->logger->info('Updating option [wpstg_is_staging_site] has been done succesfully');
1065
+ wpstg_save_options();
1066
+ wpstg_return_json('wpstg_replace_links', 'success', '[' . date('d-m-Y H:i:s') . '] Updating option [wpstg_is_staging_site] has been done succesfully', $wpstg_clone_details['links_progress'], wpstg_get_runtime());
1067
+ }
1068
+ }
1069
+
1070
+ //Update rewrite_rules in clone options table
1071
+ if ($wpstg_clone_details['links_progress'] < 0.44) {
1072
+ $result = $db_helper->query(
1073
+ $db_helper->prepare(
1074
+ 'update ' . $new_prefix . 'options set option_value = %s where option_name = \'rewrite_rules\'',
1075
+ ''
1076
+ )
1077
+ );
1078
+ if (!$result) {
1079
+ WPSTG()->logger->info('Updating option[rewrite_rules] not successfull, probably because the main site is not using permalinks');
1080
+ $wpstg_clone_details['links_progress'] = 0.45;
1081
+ // Do not die here. This db option is not available on sites with permalinks disabled, so we want to continue
1082
+ //wp_die(-1);
1083
+ wpstg_save_options();
1084
+ wpstg_return_json('wpstg_replace_links', 'fail', '[' . date('d-m-Y H:i:s') . '] Updating option[rewrite_rules] was not possible. Will be skipped! Usually this is no problem and happens only when main site has no permalinks enabled!', $wpstg_clone_details['links_progress'], wpstg_get_runtime());
1085
+ } else {
1086
+ $wpstg_clone_details['links_progress'] = 0.45;
1087
+ WPSTG()->logger->info('Updating option [rewrite_rules] has been done succesfully');
1088
+ wpstg_save_options();
1089
+ wpstg_return_json('wpstg_replace_links', 'success', '[' . date('d-m-Y H:i:s') . '] Updating option [rewrite_rules] has been done succesfully', $wpstg_clone_details['links_progress'], wpstg_get_runtime());
1090
+
1091
+ }
1092
+ }
1093
+
1094
+ //replace table prefix in meta_keys
1095
+ if ($wpstg_clone_details['links_progress'] < 0.50) {
1096
+ $result_options = $db_helper->query(
1097
+ $db_helper->prepare(
1098
+ 'update ' . $new_prefix . 'usermeta set meta_key = replace(meta_key, %s, %s) where meta_key like %s',
1099
+ $wpdb->prefix,
1100
+ $new_prefix,
1101
+ $wpdb->prefix . '_%'
1102
+ )
1103
+ );
1104
+ $result_usermeta = $db_helper->query(
1105
+ $db_helper->prepare(
1106
+ 'update ' . $new_prefix . 'options set option_name = replace(option_name, %s, %s) where option_name like %s',
1107
+ $wpdb->prefix,
1108
+ $new_prefix,
1109
+ $wpdb->prefix . '_%'
1110
+ )
1111
+ );
1112
+ if (!$result_options || !$result_usermeta) {
1113
+ $wpstg_clone_details['links_progress'] = 0.66;
1114
+ WPSTG()->logger->error('Updating table ' . $new_prefix . ' has been failed.');
1115
+ wpstg_return_json('wpstg_replace_links', 'fail', '[' . date('d-m-Y H:i:s') . ']<span style="color:red;">Fatal error!</span> Updating table ' . $new_prefix . ' has been failed.', $wpstg_clone_details['links_progress'], wpstg_get_runtime());
1116
+ //wp_die(.51);
1117
+ //wp_die('Updating table ' . $new_prefix . ' has been failed.');
1118
+ } else {
1119
+ $wpstg_clone_details['links_progress'] = 0.66;
1120
+ WPSTG()->logger->error('Updating db prefix "' . $wpdb->prefix . '" to "' . $new_prefix . '" has been done succesfully.');
1121
+ wpstg_save_options();
1122
+ wpstg_return_json('wpstg_replace_links', 'success', '[' . date('d-m-Y H:i:s') . '] Updating prefix name ' . $wpdb->prefix . ' to ' . $new_prefix . ' has been done succesfully.', $wpstg_clone_details['links_progress'], wpstg_get_runtime());
1123
+ }
1124
+ }
1125
+
1126
+ //replace $table_prefix in wp-config.php
1127
+ if ($wpstg_clone_details['links_progress'] < 0.67) {
1128
+ $wpstg_clone_details['links_progress'] = 0.67;
1129
+ $path = get_home_path() . $wpstg_clone_details['current_clone'] . '/wp-config.php';
1130
+ $content = file_get_contents($path);
1131
+ if ($content) {
1132
+ $content = str_replace('$table_prefix', '$table_prefix = \'' . $new_prefix . '\';//', $content); // replace table prefix
1133
+ $content = str_replace(get_home_url(), wpstg_get_staging_url(), $content); // replace any url
1134
+ if ($db_helper->dbname != $wpdb->dbname) {
1135
+ $content = str_replace('define(\'DB_NAME\'', 'define(\'DB_NAME\', \'' . $db_helper->dbname . '\');//', $content);
1136
+ $content = str_replace('define(\'DB_USER\'', 'define(\'DB_USER\', \'' . $db_helper->dbuser . '\');//', $content);
1137
+ $content = str_replace('define(\'DB_PASSWORD\'', 'define(\'DB_PASSWORD\', \'' . $db_helper->dbpassword . '\');//', $content);
1138
+ $content = str_replace('define(\'DB_HOST\'', 'define(\'DB_HOST\', \'' . $db_helper->dbhost . '\');//', $content);
1139
+ }
1140
+ if (FALSE === file_put_contents($path, $content)) {
1141
+ wpstg_save_options(); // we have to die hier @to do write function
1142
+ WPSTG()->logger->info($path . ' is not writable');
1143
+ wpstg_return_json('wpstg_replace_links', 'fail', '[' . date('d-m-Y H:i:s') . '] <span style="color:red;">Fatal error: </span>Can not modify ' . $path . ' !', $wpstg_clone_details['links_progress'], wpstg_get_runtime());
1144
+ }else{
1145
+ wpstg_save_options();
1146
+ wpstg_return_json('wpstg_replace_links', 'success', '[' . date('d-m-Y H:i:s') . '] ' . $path . ' has been successfully modified!', $wpstg_clone_details['links_progress'], wpstg_get_runtime());
1147
+ }
1148
+ } else {
1149
+ WPSTG()->logger->info($path . ' is not readable.');
1150
+ wpstg_save_options();
1151
+ wpstg_return_json('wpstg_replace_links', 'fail', '[' . date('d-m-Y H:i:s') . '] <span style="color:red;">Fail: </span>' . $path . ' is not writable', $wpstg_clone_details['links_progress'], wpstg_get_runtime());
1152
+ }
1153
+ }
1154
+
1155
+
1156
+ // Replace path in index.php
1157
+ wpstg_reset_index_php(0.71);
1158
+
1159
+ $existing_clones = get_option('wpstg_existing_clones', array());
1160
+ if (false === array_search($wpstg_clone_details['current_clone'], $existing_clones)) {
1161
+ $existing_clones[] = $wpstg_clone_details['current_clone'];
1162
+ update_option('wpstg_existing_clones', $existing_clones);
1163
+ }
1164
+
1165
+ wpstg_clear_options();
1166
+
1167
+ wpstg_return_json('wpstg_replace_links', 'success', '[' . date('d-m-Y H:i:s') . '] All string replacements have been done successfully', 1, wpstg_get_runtime());
1168
+ }
1169
+ add_action('wp_ajax_wpstg_replace_links', 'wpstg_replace_links');
1170
+
1171
+ /**
1172
+ * Reset index.php to original file
1173
+ * Check first if main wordpress is used in subfolder and index.php in parent directory
1174
+ *
1175
+ * See: https://codex.wordpress.org/Giving_WordPress_Its_Own_Directory
1176
+ *
1177
+ * @param $progress decimal value as progress indicatior. 1 = 100% | 0.9 = 90% and so on
1178
+ *
1179
+ * Return JSON
1180
+ */
1181
+ function wpstg_reset_index_php($progress){
1182
+ global $wpstg_options, $wpstg_clone_details;
1183
+
1184
+ if ( isset( $wpstg_options['wordpress_subdirectory'] ) && $wpstg_options['wordpress_subdirectory'] === "1" && $wpstg_clone_details['links_progress'] < $progress) {
1185
+ $wpstg_clone_details['links_progress'] = $progress;
1186
+ $path = get_home_path() . $wpstg_clone_details['current_clone'] . '/index.php';
1187
+ $content = file_get_contents($path);
1188
+
1189
+ if ($content) {
1190
+
1191
+ $pattern = "/(require(.*)wp-blog-header.php' \);)/";
1192
+ if ( !preg_match($pattern, $content, $matches) )
1193
+ wpstg_return_json('wpstg_wp_in_subdirectory', 'fail', '[' . date('d-m-Y H:i:s') . '] <span style="color:red;">Fatal error: </span>wp-blog-header.php not included in ' . $path . ' !', $progress, wpstg_get_runtime());
1194
+
1195
+ $pattern2 = "/require(.*) dirname(.*) __FILE__ (.*) \. '(.*)wp-blog-header.php'(.*);/";
1196
+ $replace = "require( dirname( __FILE__ ) . '/wp-blog-header.php' ); // " . $matches[0] . " // Changed by WP-Staging";
1197
+ $content = preg_replace($pattern2, $replace, $content);
1198
+
1199
+ if (FALSE === file_put_contents($path, $content)) {
1200
+ wpstg_save_options(); // we should throw fatal error here to die @todo create function()
1201
+ WPSTG()->logger->info($path . ' is not writable');
1202
+ wpstg_return_json('wpstg_wp_in_subdirectory', 'fail', '[' . date('d-m-Y H:i:s') . '] <span style="color:red;">Fatal error: </span>Can not modify ' . $path . ' !', $progress, wpstg_get_runtime());
1203
+ } else {
1204
+ wpstg_save_options();
1205
+ wpstg_return_json('wpstg_replace_links', 'success', '[' . date('d-m-Y H:i:s') . '] ' . $path . ' has been successfully modified!', $progress, wpstg_get_runtime());
1206
+ }
1207
+ } else {
1208
+ WPSTG()->logger->info($path . ' is not readable.');
1209
+ wpstg_save_options();
1210
+ wpstg_return_json('wpstg_replace_links', 'fail', '[' . date('d-m-Y H:i:s') . '] <span style="color:red;">Fail: </span>' . $path . ' is not writable', $progress, wpstg_get_runtime());
1211
+ }
1212
+ }
1213
+ }
1214
+
1215
+ /**
1216
+ * Clear all task related data in *.json files
1217
+ *
1218
+ * @return void
1219
+ */
1220
+ function wpstg_clear_options() {
1221
+ $path = wpstg_get_upload_dir() . '/clone_details.json';
1222
+ if (wp_is_writable($path)) {
1223
+ file_put_contents($path, '');
1224
+ }
1225
+ WPSTG()->logger->info(wpstg_get_upload_dir() . '/clone_details.json has been purged successfully');
1226
+
1227
+ $path = wpstg_get_upload_dir() . '/remaining_files.json';
1228
+ if (wp_is_writable($path)) {
1229
+ file_put_contents($path, '');
1230
+ }
1231
+ WPSTG()->logger->info(wpstg_get_upload_dir() . '/remaining_files.json has been purged successfully');
1232
+ }
1233
+
1234
+ /** Check the files before removing
1235
+ *
1236
+ * @global object $wpdb
1237
+ */
1238
+ function wpstg_preremove_clone() {
1239
+ global $wpdb;
1240
+
1241
+ $clone = $_POST['cloneID'];
1242
+ $prefix = $clone . '_' . $wpdb->prefix;
1243
+ $db_helper = apply_filters('wpstg_db_helper', $wpdb, $clone);
1244
+ $tables = $db_helper->get_results("show table status like '" . $prefix . "_%'");
1245
+
1246
+ $path = get_home_path() . $clone;
1247
+ $folders[$clone] = wpstg_check_removing_files($path);
1248
+ ?>
1249
+ <h4 class="wpstg-notice-alert">
1250
+ <?php _e('Attention: Check carefully if this DB tables and files are safe to delete for the staging site','wpstg'); ?>
1251
+ <span style="background-color:#575757;color:#fff;"><?php echo $clone; ?></span>
1252
+ <?php _e('Usually the preselected data can be deleted without any risk, but in case something goes wrong you better check it first.','wpstg'); ?>
1253
+ </h4>
1254
+ <div class="wpstg-tabs-wrapper">
1255
+ <a href="#" class="wpstg-tab-header active" data-id="#wpstg-scanning-db">
1256
+ <span class="wpstg-tab-triangle">&#9658;</span>
1257
+ <?php echo __('DB tables to remove', 'wpstg');?>
1258
+ </a>
1259
+ <div class="wpstg-tab-section" id="wpstg-scanning-db">
1260
+ <h4 style="margin:0px;">
1261
+ <?php
1262
+ _e('Select the tables for removal:', 'wpstg');
1263
+ wpstg_show_tables($tables);
1264
+ ?>
1265
+ <h4>
1266
+ </div> <!-- #wpstg-scanning-db -->
1267
+
1268
+ <a href="#" class="wpstg-tab-header" data-id="#wpstg-scanning-files">
1269
+ <span class="wpstg-tab-triangle">&#9658;</span>
1270
+ <?php echo __('Files to remove', 'wpstg');?>
1271
+ </a>
1272
+ <div class="wpstg-tab-section" id="wpstg-scanning-files">
1273
+ <h4 style="margin:0px;">
1274
+ <?php
1275
+ _e('Select the folders for removal. Click on a folder name to expand it:', 'wpstg');
1276
+ wpstg_directory_structure($folders, null, false, true);
1277
+ ?>
1278
+ <h4>
1279
+ </div> <!-- #wpstg-scanning-files -->
1280
+ </div>
1281
+ <a href="#" class="wpstg-link-btn button-primary" id="wpstg-cancel-removing"><?php _e('Cancel', 'wpstg');?></a>
1282
+ <a href="#" class="wpstg-link-btn button-primary" id="wpstg-remove-clone" data-clone="<?php echo $clone; ?>"><?php echo __('Remove', 'wpstg');?></a>
1283
+ <?php
1284
+ wp_die();
1285
+ }
1286
+ add_action('wp_ajax_wpstg_preremove', 'wpstg_preremove_clone');
1287
+
1288
+ /**
1289
+ * Check the folders for removing
1290
+ *
1291
+ * @param string $path
1292
+ * @param array $folders
1293
+ *
1294
+ * @return array
1295
+ */
1296
+ function wpstg_check_removing_files($path, &$folders = array()) {
1297
+ if (is_dir($path)) {
1298
+ $dirsize = 0;
1299
+ $dir = dir($path);
1300
+ while (false !== $entry = $dir->read()) {
1301
+ if ($entry == '.' || $entry == '..')
1302
+ continue;
1303
+ if (is_file("$path/$entry")) {
1304
+ $dirsize += filesize("$path/$entry");
1305
+ continue;
1306
+ }
1307
+
1308
+ $tmp = wpstg_check_removing_files("$path/$entry", $folders[$entry]);
1309
+ $dirsize += $tmp['size'];
1310
+ }
1311
+ $folders['size'] = $dirsize;
1312
+ }
1313
+ return $folders;
1314
+ }
1315
+
1316
+ /**
1317
+ * Removes current clone
1318
+ *
1319
+ * @global object $wpdb
1320
+ * @global array $wpstg_clone_details
1321
+ * @param bool $isAjax
1322
+ */
1323
+ function wpstg_remove_clone($isAjax = true) {
1324
+ global $wpdb, $wpstg_clone_details;
1325
+ check_ajax_referer( 'wpstg_ajax_nonce', 'nonce' );
1326
+ $wpstg_clone_details = wpstg_get_options();
1327
+
1328
+ $clone = $_POST['cloneID'];
1329
+
1330
+ if (empty ($clone) || $clone === '' ) {
1331
+ WPSTG()->logger->info('cloneID does not exist or is empty');
1332
+ //wp_die(-1);
1333
+ wp_die('cloneID does not exist or is empty');
1334
+ }
1335
+
1336
+ //drop clone tables
1337
+ $db_helper = apply_filters('wpstg_db_helper', $wpdb, $clone);
1338
+ $tables = $db_helper->get_col( $wpdb->prepare('show tables like %s', $clone . '_%'));
1339
+ $unchecked_tables = isset($_POST['uncheckedTables']) ? $_POST['uncheckedTables'] : array();
1340
+ $tables = array_diff($tables, $unchecked_tables);
1341
+ foreach ($tables as $table) {
1342
+ if (! wpstg_is_root_table($table, $wpdb->prefix))
1343
+ $result = $db_helper->query("drop table `$table`");
1344
+ if (! $result) {
1345
+ WPSTG()->logger->info('Droping table ' . $table . ' has been failed.');
1346
+ //wp_die(-1);
1347
+ wp_die('Droping table ' . $table . ' has been failed.');
1348
+ }
1349
+ WPSTG()->logger->info('Droping table ' . $table . ' was successfull');
1350
+ }
1351
+
1352
+ //remove clone folder
1353
+ $excluded_folders = isset($_POST['excludedFolders']) ? $_POST['excludedFolders'] : array();
1354
+ $result = wpstgDeleteDirectory(get_home_path() . $clone, $excluded_folders);
1355
+ if (! $result) {
1356
+ WPSTG()->logger->info('Removing clone folder '.get_home_path() . $clone.' has been failed.');
1357
+ wp_die(-1);
1358
+ }
1359
+ WPSTG()->logger->info('Clone folder '.get_home_path() . $clone.' has been removed successfully.');
1360
+ $existing_clones = get_option('wpstg_existing_clones', array());
1361
+ $key = array_search($clone, $existing_clones);
1362
+
1363
+ if ($key !== false && $key !== NULL) {
1364
+ unset($existing_clones[$key]);
1365
+ update_option('wpstg_existing_clones', $existing_clones);
1366
+ }
1367
+
1368
+ do_action('wpstg_remove_clone', $clone);
1369
+ if ($isAjax) {
1370
+ WPSTG()->logger->info('Clone( ' . $clone . ' ) has been removed successfully.');
1371
+ wp_die(0);
1372
+ }
1373
+ }
1374
+ add_action('wp_ajax_wpstg_remove_clone', 'wpstg_remove_clone');
1375
+
1376
+
1377
+
1378
+ /**
1379
+ * Delete a specific directory
1380
+ *
1381
+ * @param array $dir
1382
+ * @param array $excluded_dirs
1383
+ * @return boolean
1384
+ */
1385
+ function wpstgDeleteDirectory($dir, $excluded_dirs) {
1386
+ if (!file_exists($dir))
1387
+ return true;
1388
+
1389
+ if (!is_dir($dir))
1390
+ return unlink($dir);
1391
+
1392
+ foreach (scandir($dir) as $item) {
1393
+ if ($item == '.' || $item == '..' || in_array("$dir/$item", $excluded_dirs))
1394
+ continue;
1395
+
1396
+ if (!wpstgDeleteDirectory("$dir/$item", $excluded_dirs))
1397
+ return false;
1398
+ }
1399
+
1400
+ $need_rm = true;
1401
+ foreach ($excluded_dirs as $ex_dir)
1402
+ if (strpos($ex_dir, $dir) !== false) {
1403
+ $need_rm = false;
1404
+ break;
1405
+ }
1406
+
1407
+ return $need_rm ? rmdir($dir) : true;
1408
+ }
1409
+
1410
+ /** Cancel cloning process
1411
+ * Remove current clone and purge json files containing task related data
1412
+ *
1413
+ * @return void
1414
+ */
1415
+
1416
+ function wpstg_cancel_cloning() {
1417
+ wpstg_remove_clone(false);
1418
+ wpstg_clear_options();
1419
+ wp_die(0);
1420
+ }
1421
+ add_action('wp_ajax_wpstg_cancel_cloning', 'wpstg_cancel_cloning');
1422
+
1423
+
1424
+ /**
1425
+ * Check if table name starts with prefix which belongs to the wordpress root table
1426
+ *
1427
+ * @param string $haystack
1428
+ * @param string $needle
1429
+ * @return bool true if table name starts with prefix of the root table
1430
+ */
1431
+ function wpstg_is_root_table($haystack, $needle) {
1432
+ return strpos($haystack, $needle) === 0;
1433
+ }
1434
+
1435
+ /** Get global clone details options
1436
+ *
1437
+ * @return string JSON that includes the cloning relevant data
1438
+ */
1439
+ function wpstg_get_options() {
1440
+ $path = wpstg_get_upload_dir() . '/clone_details.json';
1441
+ $content = @file_get_contents($path);
1442
+ if ($content) {
1443
+ return json_decode($content, true);
1444
+ }else{
1445
+ return json_decode('', true);
1446
+ }
1447
+ }
1448
+
1449
+ /**
1450
+ * Save global clone details options
1451
+ * and create clone_details.json
1452
+ *
1453
+ * @return void
1454
+ */
1455
+ function wpstg_save_options() {
1456
+ global $wpstg_clone_details;
1457
+ $path = wpstg_get_upload_dir();
1458
+
1459
+ if (wp_is_writable($path)) {
1460
+ $file = 'clone_details.json';
1461
+ file_put_contents($path . '/' . $file, json_encode($wpstg_clone_details));
1462
+ }else {
1463
+ WPSTG()->logger->info($path . '/' . $file . ' is not writeable! ');
1464
+ }
1465
+ }
1466
+
1467
+ /**
1468
+ * Write unexpected errors into the log file
1469
+ */
1470
+ function wpstg_error_processing() {
1471
+ $msg = sanitize_text_field($_POST['wpstg_error_msg']);
1472
+ if (! empty($msg))
1473
+ WPSTG()->logger->info($msg);
1474
+ wp_die();
1475
+ }
1476
+ add_action('wp_ajax_wpstg_error_processing', 'wpstg_error_processing');
1477
+
1478
+
1479
+ /**
1480
+ * Install must-use plugin that disables other plugins when wp staging ajax requests are made.
1481
+ *
1482
+ * @return void;
1483
+ */
1484
+ function wpstg_ajax_muplugin_install() {
1485
+ global $state_data;
1486
+
1487
+ $key_rules = array(
1488
+ 'action' => 'key',
1489
+ 'install' => 'numeric',
1490
+ );
1491
+ wpstg_set_post_data($key_rules);
1492
+
1493
+ $mu_dir = ( defined('WPMU_PLUGIN_DIR') && defined('WPMU_PLUGIN_URL') ) ? WPMU_PLUGIN_DIR : trailingslashit(WP_CONTENT_DIR) . 'mu-plugins';
1494
+ $source = trailingslashit(WPSTG_PLUGIN_DIR) . 'optimizer/wp-staging-optimizer.php';
1495
+ $dest = trailingslashit($mu_dir) . 'wp-staging-optimizer.php';
1496
+
1497
+ if ('1' === trim($state_data['install'])) { // install MU plugin
1498
+ if (!wp_mkdir_p($mu_dir)) {
1499
+ printf(esc_html__('The following directory could not be created: %s', 'wpstg'), $mu_dir);
1500
+ exit;
1501
+ }
1502
+
1503
+ if (!copy($source, $dest)) {
1504
+ printf(esc_html__('Could not copy the compatibility plugin from %1$s to %2$s', 'wpstg'), $source, $dest);
1505
+ exit;
1506
+ }
1507
+ } else { // uninstall MU plugin
1508
+ // TODO: Use WP_Filesystem API.
1509
+ if (file_exists($dest) && !unlink($dest)) {
1510
+ printf(esc_html__('Could not remove the compatibility plugin from %s', 'wpstg'), $dest);
1511
+ exit;
1512
+ }
1513
+ }
1514
+ exit;
1515
+ }
1516
+
1517
+ add_action('wp_ajax_wpstg_muplugin_install', 'wpstg_ajax_muplugin_install');
1518
+
1519
+ /**
1520
+ * Handler for saving the options and for updating
1521
+ * the plugins that are not loaded during a request (Optimizing Mode).
1522
+ */
1523
+ function wpstg_ajax_disable_plugins() {
1524
+ global $state_data, $wpstg_options;
1525
+
1526
+ $key_rules = array(
1527
+ 'action' => 'key',
1528
+ 'blacklist_plugins' => 'array',
1529
+ 'batch_size' => 'int',
1530
+ 'query_limit' => 'int',
1531
+ 'disable_admin_login' => 'int',
1532
+ 'uninstall_on_delete' => 'int'
1533
+
1534
+ );
1535
+
1536
+ wpstg_set_post_data($key_rules);
1537
+
1538
+ $wpstg_options['blacklist_plugins'] = (array) $state_data['blacklist_plugins'];
1539
+ $wpstg_options['wpstg_batch_size'] = (int) $state_data['batch_size'];
1540
+ $wpstg_options['wpstg_query_limit'] = (int) $state_data['query_limit'];
1541
+ $wpstg_options['disable_admin_login'] = (int) $state_data['disable_admin_login'];
1542
+ $wpstg_options['uninstall_on_delete'] = (int) $state_data['uninstall_on_delete'];
1543
+
1544
+ update_option('wpstg_settings', $wpstg_options);
1545
+ exit;
1546
+ }
1547
+ add_action('wp_ajax_wpstg_disable_plugins', 'wpstg_ajax_disable_plugins');
1548
+
1549
+ /**
1550
+ * Sets $state_data from $_POST, potentially un-slashed and sanitized.
1551
+ *
1552
+ * @param array $key_rules An optional associative array of expected keys and their sanitization rule(s).
1553
+ * @param string $context The method that is specifying the sanitization rules. Defaults to calling method.
1554
+ */
1555
+ function wpstg_set_post_data($key_rules = array(), $context = '') {
1556
+ global $state_data;
1557
+
1558
+ if (is_null($state_data)) {
1559
+ $state_data = wp_unslash($_POST);
1560
+ } else {
1561
+ return;
1562
+ }
1563
+
1564
+ // Sanitize the new state data.
1565
+ if (!empty($key_rules)) {
1566
+ $context = empty($context) ? wpstg_get_caller_function() : trim($context);
1567
+ $state_data = WPSTG_Sanitize::sanitize_data($state_data, $key_rules, $context);
1568
+
1569
+ if (false === $state_data) {
1570
+ exit;
1571
+ }
1572
+ }
1573
+ }
1574
+
1575
+ /**
1576
+ * Returns the function name that called the function using this function.
1577
+ *
1578
+ * @return string function name
1579
+ */
1580
+ function wpstg_get_caller_function() {
1581
+ list(,, $caller ) = debug_backtrace(false);
1582
+
1583
+ if (!empty($caller['function'])) {
1584
+ $caller = $caller['function'];
1585
+ } else {
1586
+ $caller = '';
1587
+ }
1588
+
1589
+ return $caller;
1590
+ }
1591
+
1592
+ /**
1593
+ *
1594
+ *
1595
+ *
1596
+ * @param string $function php function name
1597
+ * @param string $status | success | fail
1598
+ * @param string $message error message or successfull notice
1599
+ * @param integer step
1600
+ *
1601
+ * @result json
1602
+ */
1603
+ function wpstg_return_json($name, $status, $message, $percent, $running_time){
1604
+ $result = array(
1605
+ 'name' => $name,
1606
+ 'status' => $status,
1607
+ 'message' => $message,
1608
+ 'percent' => $percent,
1609
+ 'running_time' => $running_time
1610
+ );
1611
+ wp_send_json( $result );
1612
+ }
1613
+
1614
+ /**
1615
+ * Get runtime
1616
+ *
1617
+ * @global type $start_time
1618
+ * @return integer
1619
+ */
1620
+ function wpstg_get_runtime(){
1621
+ global $start_time;
1622
+ $now_time = microtime(true);
1623
+ $now = $now_time - $start_time;
1624
+ return $now;
1625
+ }
1626
+
1627
+ /**
1628
+ * Get header of working log
1629
+ *
1630
+ * @param integer $progress percent of progress
1631
+ * @param string $message define optional logging message
1632
+ * @return string
1633
+ */
1634
+ function wpstg_get_log_data($progress){
1635
+ if ($progress === 0) {
1636
+ return $log_data_header = '###########################################<br>'
1637
+ . '&nbsp;&nbsp;&nbsp; WP Staging working log <br>'
1638
+ . '&nbsp;&nbsp;&nbsp; You find all log files in: <br>'
1639
+ . '&nbsp;&nbsp;&nbsp; wp-content/plugins/wp-staging/logs <br>'
1640
+ . '###########################################<br>';
1641
+ } else {
1642
+ return '';
1643
+ }
1644
+ }
1645
+
1646
+ /*
1647
+ * This checks if a job has been started previously
1648
+ *
1649
+ * @return bool
1650
+ */
1651
+ function wpstg_check_job_exists(){
1652
+ global $wpstg_clone_details;
1653
+ if ( isset($wpstg_clone_details['db_progress']) ) {
1654
+ return true;
1655
+ }
1656
+ }
1657
+
1658
+ /**
1659
+ * Return the button title depending if the
1660
+ *
1661
+ * @return string
1662
+ */
1663
+ function wpstg_return_button_title(){
1664
+ If ( !wpstg_check_job_exists() ) {
1665
+ return __('Start Cloning', 'wpstg');
1666
+ } else {
1667
+ return __('Resume Cloning', 'wpstg');
1668
+ }
1669
+ }
1670
+ /**
1671
+ * Check if there is enough disk space left for cloning site
1672
+ *
1673
+ * @return string
1674
+ */
1675
+ /*function wpstg_check_diskspace_($clone_size){
1676
+ $free_space = function_exists('disk_free_space') ? disk_free_space(get_home_path()) : 'undefined';
1677
+ $overflow = $free_space < $clone_size ? true : false;
1678
+
1679
+ if (function_exists('disk_free_space') ) {
1680
+ return $overflow ? __('Probably not enough free disk space to create a staging site. You can continue but its likely that the copying process will fail.', 'wpstg') : '';
1681
+ }
1682
+ //return __('Can not check if there is enough disk space left for the staging website. This is not really a bad thing so you can still continue.', 'wpstg');
1683
+ }*/
1684
+
1685
+ function wpstg_check_diskspace($clone_size) {
1686
+ if ( !function_exists('disk_free_space') )
1687
+ return '';
1688
+
1689
+ $overflow = @disk_free_space(get_home_path());
1690
+ if ( $overflow == false)
1691
+ return '';
1692
+
1693
+ $overflow = $overflow < $clone_size ? true : false;
1694
+ return $overflow ? '<br>' . __('Probably not enough free disk space to create a staging site. <br> You can continue but its likely that the copying process will fail.', 'wpstg') : '';
1695
+ }
1696
+
1697
+ /**
1698
+ * Write extended debug messsages into logfiles
1699
+ *
1700
+ * @param string $error
1701
+ */
1702
+ function wpstg_debug_log($error){
1703
+ global $wpstg_options;
1704
+
1705
+ if ( isset($wpstg_options['debug_mode']) )
1706
+ WPSTG()->logger->info($error);
1707
+ }
1708
+
1709
+ /**
1710
+ * Get URL of staging site
1711
+ *
1712
+ * @global type $wpstg_clone_details
1713
+ * @return mixed bool | string false when there is not staging url defined
1714
+ */
1715
+ function wpstg_get_staging_url(){
1716
+ global $wpstg_clone_details;
1717
+
1718
+ if ( !empty($wpstg_clone_details['current_clone']))
1719
+ return get_home_url() . '/' . $wpstg_clone_details['current_clone'];
1720
+
1721
+ return false;
1722
+ }
includes/wpstg-sanitize.php ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WPSTG_Sanitize {
4
+ /**
5
+ * Sanitize and validate data.
6
+ *
7
+ * @param string|array $data The data to the sanitized.
8
+ * @param string|array $key_rules The keys in the data (if data is an array) and the sanitization rule(s) to apply for each key.
9
+ * @param string $context Additional context data for messages etc.
10
+ *
11
+ * @return mixed The sanitized data, the data if no key rules supplied or `false` if an unrecognized rule supplied.
12
+ */
13
+ static function sanitize_data( $data, $key_rules, $context ) {
14
+ if ( empty( $data ) || empty( $key_rules ) ) {
15
+ return $data;
16
+ }
17
+
18
+ return WPSTG_Sanitize::_sanitize_data( $data, $key_rules, $context );
19
+ }
20
+
21
+ /**
22
+ * Sanitize and validate data.
23
+ *
24
+ * @param string|array $data The data to the sanitized.
25
+ * @param string|array $key_rules The keys in the data (if data is an array) and the sanitization rule(s) to apply for each key.
26
+ * @param string $context Additional context data for messages etc.
27
+ * @param int $recursion_level How deep in the recursion are we? Optional, defaults to 0.
28
+ *
29
+ * @return mixed The sanitized data, the data if no key rules supplied or `false` if an unrecognized rule supplied.
30
+ */
31
+ private static function _sanitize_data( $data, $key_rules, $context, $recursion_level = 0 ) {
32
+ if ( empty( $data ) || empty( $key_rules ) ) {
33
+ return $data;
34
+ }
35
+
36
+ if ( 0 === $recursion_level && is_array( $data ) ) {
37
+ // We always expect associative arrays.
38
+ if ( ! is_array( $key_rules ) ) {
39
+ wp_die( sprintf( __( '%1$s was not expecting data to be an array.', 'wpstg' ), $context ) );
40
+
41
+ return false;
42
+ }
43
+ foreach ( $data as $key => $value ) {
44
+ // If a key does not have a rule it's not ours and can be removed.
45
+ // We should not fail if there is extra data as plugins like Polylang add their own data to each ajax request.
46
+ if ( ! array_key_exists( $key, $key_rules ) ) {
47
+ unset( $data[ $key ] );
48
+ continue;
49
+ }
50
+ $data[ $key ] = WPSTG_Sanitize::_sanitize_data( $value, $key_rules[ $key ], $context, ( $recursion_level + 1 ) );
51
+ }
52
+ } elseif ( is_array( $key_rules ) ) {
53
+ foreach ( $key_rules as $rule ) {
54
+ $data = WPSTG_Sanitize::_sanitize_data( $data, $rule, $context, ( $recursion_level + 1 ) );
55
+ }
56
+ } else {
57
+ // Neither $data or $key_rules are a first level array so can be analysed.
58
+ if ( 'array' == $key_rules ) {
59
+ if ( ! is_array( $data ) ) {
60
+ wp_die( sprintf( __( '%1$s was expecting an array but got something else: "%2$s"', 'wpstg' ), $context, $data ) );
61
+
62
+ return false;
63
+ }
64
+ } elseif ( 'string' == $key_rules ) {
65
+ if ( ! is_string( $data ) ) {
66
+ wp_die( sprintf( __( '%1$s was expecting a string but got something else: "%2$s"', 'wpstg' ), $context, $data ) );
67
+
68
+ return false;
69
+ }
70
+ } elseif ( 'key' == $key_rules ) {
71
+ $key_name = sanitize_key( $data );
72
+ if ( $key_name !== $data ) {
73
+ wp_die( sprintf( __( '%1$s was expecting a valid key but got something else: "%2$s"', 'wpstg' ), $context, $data ) );
74
+
75
+ return false;
76
+ }
77
+ $data = $key_name;
78
+ } elseif ( 'text' == $key_rules ) {
79
+ $text = sanitize_text_field( $data );
80
+ if ( $text !== $data ) {
81
+ wp_die( sprintf( __( '%1$s was expecting text but got something else: "%2$s"', 'wpstg' ), $context, $data ) );
82
+
83
+ return false;
84
+ }
85
+ $data = $text;
86
+ } elseif ( 'serialized' == $key_rules ) {
87
+ if ( ! is_string( $data ) || ! is_serialized( $data ) ) {
88
+ wp_die( sprintf( __( '%1$s was expecting serialized data but got something else: "%2$s"', 'wpstg' ), $context, $data ) );
89
+
90
+ return false;
91
+ }
92
+ } elseif ( 'numeric' == $key_rules ) {
93
+ if ( ! is_numeric( $data ) ) {
94
+ wp_die( sprintf( __( '%1$s was expecting a valid numeric but got something else: "%2$s"', 'wpstg' ), $context, $data ) );
95
+
96
+ return false;
97
+ }
98
+ } elseif ( 'int' == $key_rules ) {
99
+ // As we are sanitizing form data, even integers are within a string.
100
+ if ( ! is_numeric( $data ) || (int) $data != $data ) {
101
+ wp_die( sprintf( __( '%1$s was expecting an integer but got something else: "%2$s"', 'wpstg' ), $context, $data ) );
102
+
103
+ return false;
104
+ }
105
+ $data = (int) $data;
106
+ } elseif ( 'positive_int' == $key_rules ) {
107
+ if ( ! is_numeric( $data ) || (int) $data != $data || 0 > $data ) {
108
+ wp_die( sprintf( __( '%1$s was expecting a positive number (int) but got something else: "%2$s"', 'wpstg' ), $context, $data ) );
109
+
110
+ return false;
111
+ }
112
+ $data = floor( $data );
113
+ } elseif ( 'negative_int' == $key_rules ) {
114
+ if ( ! is_numeric( $data ) || (int) $data != $data || 0 < $data ) {
115
+ wp_die( sprintf( __( '%1$s was expecting a negative number (int) but got something else: "%2$s"', 'wpstg' ), $context, $data ) );
116
+
117
+ return false;
118
+ }
119
+ $data = ceil( $data );
120
+ } elseif ( 'zero_int' == $key_rules ) {
121
+ if ( ! is_numeric( $data ) || (int) $data != $data || 0 !== $data ) {
122
+ wp_die( sprintf( __( '%1$s was expecting 0 (int) but got something else: "%2$s"', 'wpstg' ), $context, $data ) );
123
+
124
+ return false;
125
+ }
126
+ $data = 0;
127
+ } elseif ( 'empty' == $key_rules ) {
128
+ if ( ! empty( $data ) ) {
129
+ wp_die( sprintf( __( '%1$s was expecting an empty value but got something else: "%2$s"', 'wpstg' ), $context, $data ) );
130
+
131
+ return false;
132
+ }
133
+ } elseif ( 'url' == $key_rules ) {
134
+ $url = esc_url_raw( $data );
135
+ if ( empty( $url ) ) {
136
+ wp_die( sprintf( __( '%1$s was expecting a URL but got something else: "%2$s"', 'wpstg' ), $context, $data ) );
137
+
138
+ return false;
139
+ }
140
+ $data = $url;
141
+ } elseif ( 'bool' == $key_rules ) {
142
+ $bool = sanitize_key( $data );
143
+ if ( empty( $bool ) || ! in_array( $bool, array( 'true', 'false' ) ) ) {
144
+ wp_die( sprintf( __( '%1$s was expecting a bool but got something else: "%2$s"', 'wpstg' ), $context, $data ) );
145
+
146
+ return false;
147
+ }
148
+ $data = $bool;
149
+ } else {
150
+ wp_die( sprintf( __( 'Unknown sanitization rule "%1$s" supplied by %2$s', 'wpstg' ), $key_rules, $context ) );
151
+
152
+ return false;
153
+ }
154
+ }
155
+
156
+ return $data;
157
+ }
158
+ }
languages/Gruntfile.js ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* local path for wp-staging git repository
2
+ cd "s:\github\wp-staging"
3
+ *
4
+ */
5
+ module.exports = function(grunt) {
6
+
7
+ // Project configuration.
8
+ grunt.initConfig({
9
+
10
+ pkg: grunt.file.readJSON( 'package.json' ),
11
+ paths : {
12
+ // Base destination dir
13
+ base : '../../wordpress-svn/staging/tags/<%= pkg.version %>',
14
+ basetrunk : '../../wordpress-svn/staging/svn/trunk/',
15
+ basezip: '../../wordpress-svn/staging/'
16
+ },
17
+
18
+ // Tasks here
19
+ // Bump version numbers
20
+ version: {
21
+ css: {
22
+ options: {
23
+ prefix: 'Version\\:\\s'
24
+ },
25
+ src: [ 'style.css' ]
26
+ },
27
+ php: {
28
+ options: {
29
+ prefix: '\@version\\s+'
30
+ },
31
+ src: [ 'functions.php', '<%= pkg.name %>.php' ]
32
+ }
33
+ },
34
+ // minify js
35
+ uglify: {
36
+ build: {
37
+ files:[
38
+ {'assets/js/wpstg-admin.min.js' : 'assets/js/wpstg-admin.js'},
39
+ {'assets/js/wpstg.min.js' : 'assets/js/wpstg.js'},
40
+ ]
41
+ }
42
+ },
43
+ // Copy to build folder
44
+ copy: {
45
+ build: {
46
+ files: [
47
+ {expand: true, src: ['**', '!node_modules/**', '!Gruntfile.js', '!package.json', '!nbproject/**', '!grunt/**'],
48
+ dest: '<%= paths.base %>'},
49
+
50
+ {expand: true, src: ['**', '!node_modules/**', '!Gruntfile.js', '!package.json', '!nbproject/**', '!grunt/**'],
51
+ dest: '<%= paths.basetrunk %>'},
52
+ ]
53
+ },
54
+ },
55
+
56
+ // Clean the build folder
57
+ clean: {
58
+ options: {
59
+ force: true
60
+ },
61
+ build: {
62
+ files:[
63
+ {src: ['<%= paths.base %>']},
64
+ {src: ['<%= paths.basetrunk %>']},
65
+ ]
66
+
67
+ }
68
+ },
69
+ // Minify CSS files into NAME-OF-FILE.min.css
70
+ cssmin: {
71
+ build: {
72
+ files:[
73
+ {'assets/css/wpstg-admin.min.css' : 'assets/css/wpstg-admin.css'},
74
+ {'templates/wpstg.min.css' : 'templates/wpstg.min.css'},
75
+ ]
76
+ }
77
+ },
78
+ // Compress the build folder into an upload-ready zip file
79
+ compress: {
80
+ build: {
81
+ options: {
82
+ archive: '<%= paths.basezip %>/<%= pkg.name %>.zip'
83
+ },
84
+ cwd: '<%= paths.base %>',
85
+ src: ['**/*']
86
+ //dest: '../../',
87
+ //expand: true
88
+ }
89
+ }
90
+
91
+
92
+ });
93
+
94
+ // Load all grunt plugins here
95
+ // [...]
96
+ //require('load-grunt-config')(grunt);
97
+ require('load-grunt-tasks')(grunt);
98
+
99
+ // Display task timing
100
+ require('time-grunt')(grunt);
101
+
102
+ // Build task
103
+ //grunt.registerTask( 'build', [ 'compress:build' ]);
104
+ grunt.registerTask( 'build', [ 'clean:build', 'uglify:build', 'cssmin:build', 'copy:build', 'compress:build' ]);
105
+ };
languages/README.md ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Staging I18n #
2
+
3
+ To help translate, review, or improve a translation, write Rene Hermenau at info@wp-staging.net
4
+
5
+ More info at https://www.wp-staging.net/
6
+
7
+ # Quick Start #
8
+
9
+ In staging/languages/ you find a file named wpstg.po
10
+
11
+ - Open it with the free editor: http://poedit.net/
12
+
13
+ - Translate the strings and save it. Poedit automatically creates the file wpstg.mo
14
+
15
+ - Rename this to your language specific translation. E.g. for italy the file is called wpstg-it_IT.mo and put it into the folder /wp-content/languages/staging/
16
+
17
+ If you like to send us new mo files we will put it into this plugin package.
18
+
languages/index.php ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Do not put custom translations here. They will be deleted on Staging updates.
4
+ *
5
+ * Keep custom WPSTG translations in /wp-content/languages/wpstg/
6
+ */
languages/readme.txt ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ********************************************************
2
+
3
+ Staging I18n
4
+ ============================
5
+
6
+ Do not put custom translations here. They will be deleted
7
+ on Staging updates.
8
+
9
+ Keep custom WPSTG translations in /wp-content/languages/staging/
10
+
11
+ You want to translate, help, or improve a translation?
12
+ Write: info@wp-staging.net
13
+
14
+ More info at https://www.wp-staging.net/
15
+
16
+ ********************************************************
languages/wpstg-en_EN.mo ADDED
Binary file
languages/wpstg-en_EN.po ADDED
@@ -0,0 +1,648 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2015 WP Staging - Create a staging clone site for testing & developing
2
+ # This file is distributed under the same license as the WP Staging - Create a staging clone site for testing & developing package.
3
+ msgid ""
4
+ msgstr ""
5
+ "Project-Id-Version: WP Staging - Create a staging clone site for testing & "
6
+ "developing 0.9.0\n"
7
+ "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/wp-staging\n"
8
+ "POT-Creation-Date: 2015-08-05 09:53:10+00:00\n"
9
+ "MIME-Version: 1.0\n"
10
+ "Content-Type: text/plain; charset=UTF-8\n"
11
+ "Content-Transfer-Encoding: 8bit\n"
12
+ "PO-Revision-Date: 2015-08-05 11:55+0100\n"
13
+ "Last-Translator: Rene Hermenau <support@wp-staging.com>\n"
14
+ "Language-Team: LANGUAGE <LL@li.org>\n"
15
+ "X-Generator: Poedit 1.5.6\n"
16
+
17
+ #: includes/WPSTG_SL_Plugin_Updater.php:181
18
+ msgid ""
19
+ "There is a new version of %1$s available. <a target=\"_blank\" class="
20
+ "\"thickbox\" href=\"%2$s\">View version %3$s details</a>."
21
+ msgstr ""
22
+
23
+ #: includes/WPSTG_SL_Plugin_Updater.php:188
24
+ msgid ""
25
+ "There is a new version of %1$s available. <a target=\"_blank\" class="
26
+ "\"thickbox\" href=\"%2$s\">View version %3$s details</a> or <a href=\"%4$s"
27
+ "\">update now</a>."
28
+ msgstr ""
29
+
30
+ #: includes/WPSTG_SL_Plugin_Updater.php:346
31
+ msgid "You do not have permission to install plugin updates"
32
+ msgstr ""
33
+
34
+ #: includes/WPSTG_SL_Plugin_Updater.php:346
35
+ #: includes/class-wpstg-license-handler.php:184
36
+ #: includes/class-wpstg-license-handler.php:257
37
+ msgid "Error"
38
+ msgstr ""
39
+
40
+ #: includes/admin/add-ons.php:27
41
+ msgid "Add Ons for WP-Staging"
42
+ msgstr ""
43
+
44
+ #: includes/admin/add-ons.php:28
45
+ msgid "Visit Website"
46
+ msgstr ""
47
+
48
+ #: includes/admin/add-ons.php:28
49
+ msgid "See Details"
50
+ msgstr ""
51
+
52
+ #: includes/admin/add-ons.php:30
53
+ msgid "These add-ons extend the functionality of WP-Staging."
54
+ msgstr ""
55
+
56
+ #: includes/admin/add-ons.php:54
57
+ msgid ""
58
+ "There was an error retrieving the WP-Staging addon list from the server. "
59
+ "Please try again later."
60
+ msgstr ""
61
+
62
+ #: includes/admin/admin-footer.php:32
63
+ msgid ""
64
+ "Please <a href=\"%1$s\" target=\"_blank\">rate WP Staging</a> and help to "
65
+ "support this project.<br>Something not working as expected? Visit the WP "
66
+ "Staging <a href=\"https://wordpress.org/support/plugin/wp-staging\" target="
67
+ "\"blank\">Support Forum</a>"
68
+ msgstr ""
69
+
70
+ #: includes/admin/admin-notices.php:44
71
+ msgid ""
72
+ "You are using an outdated version of WP Staging which has not been tested "
73
+ "with your WordPress version %2$s.<br> \r\n"
74
+ " As WP Staging is using crucial db and file functions it's "
75
+ "important that you are using a WP Staging version<br> \r\n"
76
+ " which has been verified to be working with your WordPress "
77
+ "version. You risk unexpected results up to data lose if you do not so.\r\n"
78
+ " <p>Please look at <a href=\"%1$s\" target=\"_blank\">%s</a> for "
79
+ "the latest WP Staging version."
80
+ msgstr ""
81
+
82
+ #: includes/admin/admin-notices.php:135
83
+ msgid ""
84
+ "There seems to be an issue with the server. Please try again in a few "
85
+ "minutes."
86
+ msgstr ""
87
+
88
+ #: includes/admin/admin-pages.php:27
89
+ msgid "WP Staging"
90
+ msgstr ""
91
+
92
+ #: includes/admin/admin-pages.php:28
93
+ msgid "WP Staging Jobs"
94
+ msgstr ""
95
+
96
+ #: includes/admin/admin-pages.php:28
97
+ msgid "Start"
98
+ msgstr ""
99
+
100
+ #: includes/admin/admin-pages.php:29
101
+ msgid "WP Staging Settings"
102
+ msgstr ""
103
+
104
+ #: includes/admin/admin-pages.php:29
105
+ msgid "Settings"
106
+ msgstr ""
107
+
108
+ #: includes/admin/admin-pages.php:30
109
+ msgid "WP Staging Tools"
110
+ msgstr ""
111
+
112
+ #: includes/admin/admin-pages.php:30
113
+ msgid "Tools"
114
+ msgstr ""
115
+
116
+ #: includes/admin/plugins.php:27
117
+ msgid "General Settings"
118
+ msgstr ""
119
+
120
+ #: includes/admin/plugins.php:55
121
+ msgid "Getting Started"
122
+ msgstr ""
123
+
124
+ #: includes/admin/settings/contextual-help.php:29
125
+ msgid "For more information:"
126
+ msgstr ""
127
+
128
+ #: includes/admin/settings/contextual-help.php:30
129
+ msgid "Visit the <a href=\"%s\">documentation</a> on the WP-Staging website."
130
+ msgstr ""
131
+
132
+ #: includes/admin/settings/contextual-help.php:32
133
+ msgid ""
134
+ "<a href=\"%s\">Post an issue</a> on <a href=\"%s\">WP-Staging</a>. View <a "
135
+ "href=\"%s\">extensions</a>."
136
+ msgstr ""
137
+
138
+ #: includes/admin/settings/contextual-help.php:41
139
+ #: includes/admin/settings/register-settings.php:126
140
+ #: includes/admin/settings/register-settings.php:301
141
+ msgid "General"
142
+ msgstr ""
143
+
144
+ #: includes/admin/settings/contextual-help.php:42
145
+ msgid ""
146
+ "This screen provides the most basic settings for configuring WP-Staging."
147
+ msgstr ""
148
+
149
+ #: includes/admin/settings/display-settings.php:135
150
+ #: includes/template-functions.php:40
151
+ msgid "Thank you for using WP Staging"
152
+ msgstr ""
153
+
154
+ #: includes/admin/settings/display-settings.php:137
155
+ #: includes/template-functions.php:42
156
+ msgid "WP Staging is ready to create a staging site!"
157
+ msgstr ""
158
+
159
+ #: includes/admin/settings/register-settings.php:133
160
+ msgid "DB Copy Query Limit"
161
+ msgstr ""
162
+
163
+ #: includes/admin/settings/register-settings.php:134
164
+ msgid ""
165
+ "Number of DB rows, that will be copied within one ajax request. The higher "
166
+ "the value the faster the database copy process. To find out the highest "
167
+ "possible values try a high value like 1.000 or more and decrease it until "
168
+ "you get no more errors during copy process. <strong> Default: 100 </strong>"
169
+ msgstr ""
170
+
171
+ #: includes/admin/settings/register-settings.php:141
172
+ msgid "File Copy Batch Size"
173
+ msgstr ""
174
+
175
+ #: includes/admin/settings/register-settings.php:142
176
+ msgid ""
177
+ "Buffer size for the file copy process in megabyte. The higher the value the "
178
+ "faster large files will be copied. To find out the highest possible values "
179
+ "try a high one and lower it until you get no errors during file copy "
180
+ "process. Usually this value correlates directly with the memory consumption "
181
+ "of php so make sure that it does not exceed any php.ini max_memory limits. "
182
+ "<strong>Default:</strong> 2 "
183
+ msgstr ""
184
+
185
+ #: includes/admin/settings/register-settings.php:149
186
+ msgid "Optimizer"
187
+ msgstr ""
188
+
189
+ #: includes/admin/settings/register-settings.php:150
190
+ msgid ""
191
+ "Select the plugins that should be disabled during build process of the "
192
+ "staging site. Some plugins slow down the copy process and add overhead to "
193
+ "each request, requiring extra CPU and memory consumption. Some of them can "
194
+ "interfere with cloning process and cause them to fail, so we recommend to "
195
+ "disable all plugins that are not directly related to WP Staging."
196
+ msgstr ""
197
+
198
+ #: includes/admin/settings/register-settings.php:157
199
+ msgid "Don´t force admin login"
200
+ msgstr ""
201
+
202
+ #: includes/admin/settings/register-settings.php:158
203
+ msgid ""
204
+ "Use this option only if you are using a custom login page and not the "
205
+ "default login.php. If you enable this option you are allowing everyone "
206
+ "including searchengines to see your staging site, so you have to create a "
207
+ "custom authentication like using .htaccess"
208
+ msgstr ""
209
+
210
+ #: includes/admin/settings/register-settings.php:170
211
+ msgid "Remove Data on Uninstall?"
212
+ msgstr ""
213
+
214
+ #: includes/admin/settings/register-settings.php:171
215
+ msgid ""
216
+ "Check this box if you like WP Staging to completely remove all of its data "
217
+ "when the plugin is deleted."
218
+ msgstr ""
219
+
220
+ #: includes/admin/settings/register-settings.php:186
221
+ msgid "Activate your Add-Ons"
222
+ msgstr ""
223
+
224
+ #: includes/admin/settings/register-settings.php:271
225
+ msgid "Settings updated."
226
+ msgstr ""
227
+
228
+ #: includes/admin/settings/register-settings.php:304
229
+ msgid "Misc"
230
+ msgstr ""
231
+
232
+ #: includes/admin/settings/register-settings.php:312
233
+ msgid "Extensions"
234
+ msgstr ""
235
+
236
+ #: includes/admin/settings/register-settings.php:527
237
+ msgid ""
238
+ "The callback function used for the <strong>%s</strong> setting is missing."
239
+ msgstr ""
240
+
241
+ #: includes/admin/settings/register-settings.php:639
242
+ msgid "Upload File"
243
+ msgstr ""
244
+
245
+ #: includes/admin/settings/register-settings.php:667
246
+ msgid "Deactivate License"
247
+ msgstr ""
248
+
249
+ #: includes/admin/settings/register-settings.php:714
250
+ msgid "Select Image"
251
+ msgstr ""
252
+
253
+ #: includes/admin/settings/register-settings.php:761
254
+ msgid ""
255
+ "Log file directory not writable! Set FTP permission to 755 or 777 for /wp-"
256
+ "content/plugins/wp-staging/logs/"
257
+ msgstr ""
258
+
259
+ #: includes/admin/settings/register-settings.php:775
260
+ msgid ""
261
+ "Improve performance and reliability by not loading the following plugins for "
262
+ "migration requests"
263
+ msgstr ""
264
+
265
+ #: includes/admin/settings/register-settings.php:780
266
+ msgid "Select the plugins you wish to disable during clone process"
267
+ msgstr ""
268
+
269
+ #: includes/admin/settings/register-settings.php:798
270
+ msgid "Select All"
271
+ msgstr ""
272
+
273
+ #: includes/admin/settings/register-settings.php:800
274
+ msgid "Deselect All"
275
+ msgstr ""
276
+
277
+ #: includes/admin/settings/register-settings.php:802
278
+ msgid "Invert Selection"
279
+ msgstr ""
280
+
281
+ #: includes/admin/settings/register-settings.php:804
282
+ msgid "Save Changes"
283
+ msgstr ""
284
+
285
+ #: includes/admin/settings/register-settings.php:805
286
+ msgctxt "The settings were saved successfully"
287
+ msgid "Saved"
288
+ msgstr ""
289
+
290
+ #: includes/admin/tools.php:67
291
+ msgid "Import/Export"
292
+ msgstr ""
293
+
294
+ #: includes/admin/tools.php:68
295
+ msgid "System Info"
296
+ msgstr ""
297
+
298
+ #: includes/admin/tools.php:90
299
+ msgid "Export Settings"
300
+ msgstr ""
301
+
302
+ #: includes/admin/tools.php:92
303
+ msgid ""
304
+ "Export the WP-Staging settings for this site as a .json file. This allows "
305
+ "you to easily import the configuration into another site."
306
+ msgstr ""
307
+
308
+ #: includes/admin/tools.php:98
309
+ msgid "Export"
310
+ msgstr ""
311
+
312
+ #: includes/admin/tools.php:105
313
+ msgid "Import Settings"
314
+ msgstr ""
315
+
316
+ #: includes/admin/tools.php:107
317
+ msgid ""
318
+ "Import the WP-Staging settings from a .json file. This file can be obtained "
319
+ "by exporting the settings on another site using the form above."
320
+ msgstr ""
321
+
322
+ #: includes/admin/tools.php:115
323
+ msgid "Import"
324
+ msgstr ""
325
+
326
+ #: includes/admin/tools.php:217
327
+ msgid "Please upload a valid .json file"
328
+ msgstr ""
329
+
330
+ #: includes/admin/tools.php:223
331
+ msgid "Please upload a file to import"
332
+ msgstr ""
333
+
334
+ #: includes/class-wpstg-html-elements.php:43
335
+ msgctxt "all dropdown items"
336
+ msgid "All"
337
+ msgstr ""
338
+
339
+ #: includes/class-wpstg-html-elements.php:44
340
+ msgctxt "no dropdown items"
341
+ msgid "None"
342
+ msgstr ""
343
+
344
+ #: includes/class-wpstg-license-handler.php:148
345
+ msgid "%1$s License Key"
346
+ msgstr ""
347
+
348
+ #: includes/class-wpstg-license-handler.php:184
349
+ #: includes/class-wpstg-license-handler.php:257
350
+ msgid "Nonce verification failed"
351
+ msgstr ""
352
+
353
+ #: includes/class-wpstg-license-handler.php:333
354
+ msgid "This license does not belong to the product you have entered it for."
355
+ msgstr ""
356
+
357
+ #: includes/class-wpstg-license-handler.php:338
358
+ msgid "This license does not have any activations left"
359
+ msgstr ""
360
+
361
+ #: includes/class-wpstg-license-handler.php:343
362
+ msgid "This license key is expired. Please renew it."
363
+ msgstr ""
364
+
365
+ #: includes/class-wpstg-license-handler.php:348
366
+ msgid ""
367
+ "There was a problem activating your license key, please try again or contact "
368
+ "support. Error code: %s"
369
+ msgstr ""
370
+
371
+ #: includes/scripts.php:45
372
+ msgid ""
373
+ "If confirmed we will install an additional WordPress 'Must Use' plugin. This "
374
+ "plugin will allow us to control which plugins are loaded during WP Staging "
375
+ "specific operations. Do you wish to continue?"
376
+ msgstr ""
377
+
378
+ #: includes/scripts.php:46
379
+ msgid ""
380
+ "A problem occurred when trying to change the plugin compatibility setting."
381
+ msgstr ""
382
+
383
+ #: includes/scripts.php:47
384
+ msgid "Saved"
385
+ msgstr ""
386
+
387
+ #: includes/scripts.php:48
388
+ msgid "Status"
389
+ msgstr ""
390
+
391
+ #: includes/scripts.php:49
392
+ msgid "Response"
393
+ msgstr ""
394
+
395
+ #: includes/scripts.php:50
396
+ msgid "A problem occurred when trying to add plugins to backlist."
397
+ msgstr ""
398
+
399
+ #: includes/staging-functions.php:31
400
+ msgid "Access denied. <a href=\"%1$s\" target=\"_blank\">Login</a> first"
401
+ msgstr ""
402
+
403
+ #: includes/template-functions.php:50
404
+ msgid ""
405
+ "WordPress Multisite is currently not supported! <a href=\"https://wp-staging."
406
+ "com/contact\">Get in contact with us</a> and ask for it."
407
+ msgstr ""
408
+
409
+ #: includes/template-functions.php:54
410
+ msgid "Overview"
411
+ msgstr ""
412
+
413
+ #: includes/template-functions.php:55
414
+ msgid "Scanning"
415
+ msgstr ""
416
+
417
+ #: includes/template-functions.php:56
418
+ msgid "Cloning"
419
+ msgstr ""
420
+
421
+ #: includes/template-functions.php:87
422
+ msgid "Create new staging site"
423
+ msgstr ""
424
+
425
+ #: includes/template-functions.php:92
426
+ msgid "Available Staging Sites:"
427
+ msgstr ""
428
+
429
+ #: includes/template-functions.php:96
430
+ msgid "Open"
431
+ msgstr ""
432
+
433
+ #: includes/template-functions.php:97
434
+ msgid "Edit"
435
+ msgstr ""
436
+
437
+ #: includes/template-functions.php:98
438
+ msgid "Delete"
439
+ msgstr ""
440
+
441
+ #: includes/template-functions.php:169
442
+ msgid "Name your new site, e.g. staging, development:"
443
+ msgstr ""
444
+
445
+ #: includes/template-functions.php:181
446
+ msgid "DB Tables"
447
+ msgstr ""
448
+
449
+ #: includes/template-functions.php:186
450
+ msgid ""
451
+ "Select the tables to copy. (If the copy process was previously interrupted, "
452
+ "succesfull copied tables are greyed out and copy process will skip these "
453
+ "ones)"
454
+ msgstr ""
455
+
456
+ #: includes/template-functions.php:192
457
+ msgid "Files"
458
+ msgstr ""
459
+
460
+ #: includes/template-functions.php:197
461
+ msgid "Select the folders to copy:"
462
+ msgstr ""
463
+
464
+ #: includes/template-functions.php:205
465
+ msgid "Advanced Options"
466
+ msgstr ""
467
+
468
+ #: includes/template-functions.php:211
469
+ msgid "Back"
470
+ msgstr ""
471
+
472
+ #: includes/template-functions.php:380
473
+ msgid ""
474
+ "We have detected the following large files which could neeed some "
475
+ "investigation. Often this files are backup files or other temporary files "
476
+ "which must not necessarily copied for creating a staging site:"
477
+ msgstr ""
478
+
479
+ #: includes/template-functions.php:468
480
+ msgid "Copy database tables"
481
+ msgstr ""
482
+
483
+ #: includes/template-functions.php:473
484
+ msgid "Copy files"
485
+ msgstr ""
486
+
487
+ #: includes/template-functions.php:478
488
+ msgid "Replace Links"
489
+ msgstr ""
490
+
491
+ #: includes/template-functions.php:484 includes/template-functions.php:1180
492
+ msgid "Cancel"
493
+ msgstr ""
494
+
495
+ #: includes/template-functions.php:488
496
+ msgid "Display working log"
497
+ msgstr ""
498
+
499
+ #: includes/template-functions.php:494 includes/template-functions.php:1181
500
+ msgid "Remove"
501
+ msgstr ""
502
+
503
+ #: includes/template-functions.php:495
504
+ msgid "Start again"
505
+ msgstr ""
506
+
507
+ #: includes/template-functions.php:497
508
+ msgid "Important notes:"
509
+ msgstr ""
510
+
511
+ #: includes/template-functions.php:1149
512
+ msgid ""
513
+ "Attention: Check carefully if this DB tables and files are safe to delete "
514
+ "for the staging site"
515
+ msgstr ""
516
+
517
+ #: includes/template-functions.php:1151
518
+ msgid ""
519
+ "Usually the preselected data can be deleted without any risk, but in case "
520
+ "something goes wrong you better check it first."
521
+ msgstr ""
522
+
523
+ #: includes/template-functions.php:1156
524
+ msgid "DB tables to remove"
525
+ msgstr ""
526
+
527
+ #: includes/template-functions.php:1161
528
+ msgid "Select the tables for removal:"
529
+ msgstr ""
530
+
531
+ #: includes/template-functions.php:1169
532
+ msgid "Files to remove"
533
+ msgstr ""
534
+
535
+ #: includes/template-functions.php:1174
536
+ msgid "Select the folders for removal. Click on a folder name to expand it:"
537
+ msgstr ""
538
+
539
+ #: includes/template-functions.php:1394
540
+ msgid "The following directory could not be created: %s"
541
+ msgstr ""
542
+
543
+ #: includes/template-functions.php:1399
544
+ msgid "Could not copy the compatibility plugin from %1$s to %2$s"
545
+ msgstr ""
546
+
547
+ #: includes/template-functions.php:1405
548
+ msgid "Could not remove the compatibility plugin from %s"
549
+ msgstr ""
550
+
551
+ #: includes/template-functions.php:1560
552
+ msgid "Start Cloning"
553
+ msgstr ""
554
+
555
+ #: includes/template-functions.php:1562
556
+ msgid "Resume Cloning"
557
+ msgstr ""
558
+
559
+ #: includes/template-functions.php:1575
560
+ msgid ""
561
+ "Probably not enough free disk space to create a staging site. You can "
562
+ "continue but its likely that the copying process will fail."
563
+ msgstr ""
564
+
565
+ #: includes/wpstg-sanitize.php:39
566
+ msgid "%1$s was not expecting data to be an array."
567
+ msgstr ""
568
+
569
+ #: includes/wpstg-sanitize.php:60
570
+ msgid "%1$s was expecting an array but got something else: \"%2$s\""
571
+ msgstr ""
572
+
573
+ #: includes/wpstg-sanitize.php:66
574
+ msgid "%1$s was expecting a string but got something else: \"%2$s\""
575
+ msgstr ""
576
+
577
+ #: includes/wpstg-sanitize.php:73
578
+ msgid "%1$s was expecting a valid key but got something else: \"%2$s\""
579
+ msgstr ""
580
+
581
+ #: includes/wpstg-sanitize.php:81
582
+ msgid "%1$s was expecting text but got something else: \"%2$s\""
583
+ msgstr ""
584
+
585
+ #: includes/wpstg-sanitize.php:88
586
+ msgid "%1$s was expecting serialized data but got something else: \"%2$s\""
587
+ msgstr ""
588
+
589
+ #: includes/wpstg-sanitize.php:94
590
+ msgid "%1$s was expecting a valid numeric but got something else: \"%2$s\""
591
+ msgstr ""
592
+
593
+ #: includes/wpstg-sanitize.php:101
594
+ msgid "%1$s was expecting an integer but got something else: \"%2$s\""
595
+ msgstr ""
596
+
597
+ #: includes/wpstg-sanitize.php:108
598
+ msgid ""
599
+ "%1$s was expecting a positive number (int) but got something else: \"%2$s\""
600
+ msgstr ""
601
+
602
+ #: includes/wpstg-sanitize.php:115
603
+ msgid ""
604
+ "%1$s was expecting a negative number (int) but got something else: \"%2$s\""
605
+ msgstr ""
606
+
607
+ #: includes/wpstg-sanitize.php:122
608
+ msgid "%1$s was expecting 0 (int) but got something else: \"%2$s\""
609
+ msgstr ""
610
+
611
+ #: includes/wpstg-sanitize.php:129
612
+ msgid "%1$s was expecting an empty value but got something else: \"%2$s\""
613
+ msgstr ""
614
+
615
+ #: includes/wpstg-sanitize.php:136
616
+ msgid "%1$s was expecting a URL but got something else: \"%2$s\""
617
+ msgstr ""
618
+
619
+ #: includes/wpstg-sanitize.php:144
620
+ msgid "%1$s was expecting a bool but got something else: \"%2$s\""
621
+ msgstr ""
622
+
623
+ #: includes/wpstg-sanitize.php:150
624
+ msgid "Unknown sanitization rule \"%1$s\" supplied by %2$s"
625
+ msgstr ""
626
+
627
+ #: wp-staging.php:114 wp-staging.php:126
628
+ msgid "Cheatin&#8217; huh?"
629
+ msgstr ""
630
+
631
+ #. Plugin Name of the plugin/theme
632
+ msgid "WP Staging - Create a staging clone site for testing & developing"
633
+ msgstr ""
634
+
635
+ #. #-#-#-#-# plugin.pot (WP Staging - Create a staging clone site for testing & developing 0.9.0) #-#-#-#-#
636
+ #. Plugin URI of the plugin/theme
637
+ #. #-#-#-#-# plugin.pot (WP Staging - Create a staging clone site for testing & developing 0.9.0) #-#-#-#-#
638
+ #. Author URI of the plugin/theme
639
+ msgid "wordpress.org/plugins/wp-staging"
640
+ msgstr ""
641
+
642
+ #. Description of the plugin/theme
643
+ msgid "WP-Staging - Create a staging clone site for testing & developing"
644
+ msgstr ""
645
+
646
+ #. Author of the plugin/theme
647
+ msgid "René Hermenau"
648
+ msgstr ""
languages/wpstg.mo ADDED
Binary file
languages/wpstg.po ADDED
@@ -0,0 +1,648 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2015 WP Staging - Create a staging clone site for testing & developing
2
+ # This file is distributed under the same license as the WP Staging - Create a staging clone site for testing & developing package.
3
+ msgid ""
4
+ msgstr ""
5
+ "Project-Id-Version: WP Staging - Create a staging clone site for testing & "
6
+ "developing 0.9.0\n"
7
+ "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/wp-staging\n"
8
+ "POT-Creation-Date: 2015-08-05 09:53:10+00:00\n"
9
+ "MIME-Version: 1.0\n"
10
+ "Content-Type: text/plain; charset=UTF-8\n"
11
+ "Content-Transfer-Encoding: 8bit\n"
12
+ "PO-Revision-Date: 2015-08-05 11:54+0100\n"
13
+ "Last-Translator: Rene Hermenau <support@wp-staging.com>\n"
14
+ "Language-Team: LANGUAGE <LL@li.org>\n"
15
+ "X-Generator: Poedit 1.5.6\n"
16
+
17
+ #: includes/WPSTG_SL_Plugin_Updater.php:181
18
+ msgid ""
19
+ "There is a new version of %1$s available. <a target=\"_blank\" class="
20
+ "\"thickbox\" href=\"%2$s\">View version %3$s details</a>."
21
+ msgstr ""
22
+
23
+ #: includes/WPSTG_SL_Plugin_Updater.php:188
24
+ msgid ""
25
+ "There is a new version of %1$s available. <a target=\"_blank\" class="
26
+ "\"thickbox\" href=\"%2$s\">View version %3$s details</a> or <a href=\"%4$s"
27
+ "\">update now</a>."
28
+ msgstr ""
29
+
30
+ #: includes/WPSTG_SL_Plugin_Updater.php:346
31
+ msgid "You do not have permission to install plugin updates"
32
+ msgstr ""
33
+
34
+ #: includes/WPSTG_SL_Plugin_Updater.php:346
35
+ #: includes/class-wpstg-license-handler.php:184
36
+ #: includes/class-wpstg-license-handler.php:257
37
+ msgid "Error"
38
+ msgstr ""
39
+
40
+ #: includes/admin/add-ons.php:27
41
+ msgid "Add Ons for WP-Staging"
42
+ msgstr ""
43
+
44
+ #: includes/admin/add-ons.php:28
45
+ msgid "Visit Website"
46
+ msgstr ""
47
+
48
+ #: includes/admin/add-ons.php:28
49
+ msgid "See Details"
50
+ msgstr ""
51
+
52
+ #: includes/admin/add-ons.php:30
53
+ msgid "These add-ons extend the functionality of WP-Staging."
54
+ msgstr ""
55
+
56
+ #: includes/admin/add-ons.php:54
57
+ msgid ""
58
+ "There was an error retrieving the WP-Staging addon list from the server. "
59
+ "Please try again later."
60
+ msgstr ""
61
+
62
+ #: includes/admin/admin-footer.php:32
63
+ msgid ""
64
+ "Please <a href=\"%1$s\" target=\"_blank\">rate WP Staging</a> and help to "
65
+ "support this project.<br>Something not working as expected? Visit the WP "
66
+ "Staging <a href=\"https://wordpress.org/support/plugin/wp-staging\" target="
67
+ "\"blank\">Support Forum</a>"
68
+ msgstr ""
69
+
70
+ #: includes/admin/admin-notices.php:44
71
+ msgid ""
72
+ "You are using an outdated version of WP Staging which has not been tested "
73
+ "with your WordPress version %2$s.<br> \r\n"
74
+ " As WP Staging is using crucial db and file functions it's "
75
+ "important that you are using a WP Staging version<br> \r\n"
76
+ " which has been verified to be working with your WordPress "
77
+ "version. You risk unexpected results up to data lose if you do not so.\r\n"
78
+ " <p>Please look at <a href=\"%1$s\" target=\"_blank\">%s</a> for "
79
+ "the latest WP Staging version."
80
+ msgstr ""
81
+
82
+ #: includes/admin/admin-notices.php:135
83
+ msgid ""
84
+ "There seems to be an issue with the server. Please try again in a few "
85
+ "minutes."
86
+ msgstr ""
87
+
88
+ #: includes/admin/admin-pages.php:27
89
+ msgid "WP Staging"
90
+ msgstr ""
91
+
92
+ #: includes/admin/admin-pages.php:28
93
+ msgid "WP Staging Jobs"
94
+ msgstr ""
95
+
96
+ #: includes/admin/admin-pages.php:28
97
+ msgid "Start"
98
+ msgstr ""
99
+
100
+ #: includes/admin/admin-pages.php:29
101
+ msgid "WP Staging Settings"
102
+ msgstr ""
103
+
104
+ #: includes/admin/admin-pages.php:29
105
+ msgid "Settings"
106
+ msgstr ""
107
+
108
+ #: includes/admin/admin-pages.php:30
109
+ msgid "WP Staging Tools"
110
+ msgstr ""
111
+
112
+ #: includes/admin/admin-pages.php:30
113
+ msgid "Tools"
114
+ msgstr ""
115
+
116
+ #: includes/admin/plugins.php:27
117
+ msgid "General Settings"
118
+ msgstr ""
119
+
120
+ #: includes/admin/plugins.php:55
121
+ msgid "Getting Started"
122
+ msgstr ""
123
+
124
+ #: includes/admin/settings/contextual-help.php:29
125
+ msgid "For more information:"
126
+ msgstr ""
127
+
128
+ #: includes/admin/settings/contextual-help.php:30
129
+ msgid "Visit the <a href=\"%s\">documentation</a> on the WP-Staging website."
130
+ msgstr ""
131
+
132
+ #: includes/admin/settings/contextual-help.php:32
133
+ msgid ""
134
+ "<a href=\"%s\">Post an issue</a> on <a href=\"%s\">WP-Staging</a>. View <a "
135
+ "href=\"%s\">extensions</a>."
136
+ msgstr ""
137
+
138
+ #: includes/admin/settings/contextual-help.php:41
139
+ #: includes/admin/settings/register-settings.php:126
140
+ #: includes/admin/settings/register-settings.php:301
141
+ msgid "General"
142
+ msgstr ""
143
+
144
+ #: includes/admin/settings/contextual-help.php:42
145
+ msgid ""
146
+ "This screen provides the most basic settings for configuring WP-Staging."
147
+ msgstr ""
148
+
149
+ #: includes/admin/settings/display-settings.php:135
150
+ #: includes/template-functions.php:40
151
+ msgid "Thank you for using WP Staging"
152
+ msgstr ""
153
+
154
+ #: includes/admin/settings/display-settings.php:137
155
+ #: includes/template-functions.php:42
156
+ msgid "WP Staging is ready to create a staging site!"
157
+ msgstr ""
158
+
159
+ #: includes/admin/settings/register-settings.php:133
160
+ msgid "DB Copy Query Limit"
161
+ msgstr ""
162
+
163
+ #: includes/admin/settings/register-settings.php:134
164
+ msgid ""
165
+ "Number of DB rows, that will be copied within one ajax request. The higher "
166
+ "the value the faster the database copy process. To find out the highest "
167
+ "possible values try a high value like 1.000 or more and decrease it until "
168
+ "you get no more errors during copy process. <strong> Default: 100 </strong>"
169
+ msgstr ""
170
+
171
+ #: includes/admin/settings/register-settings.php:141
172
+ msgid "File Copy Batch Size"
173
+ msgstr ""
174
+
175
+ #: includes/admin/settings/register-settings.php:142
176
+ msgid ""
177
+ "Buffer size for the file copy process in megabyte. The higher the value the "
178
+ "faster large files will be copied. To find out the highest possible values "
179
+ "try a high one and lower it until you get no errors during file copy "
180
+ "process. Usually this value correlates directly with the memory consumption "
181
+ "of php so make sure that it does not exceed any php.ini max_memory limits. "
182
+ "<strong>Default:</strong> 2 "
183
+ msgstr ""
184
+
185
+ #: includes/admin/settings/register-settings.php:149
186
+ msgid "Optimizer"
187
+ msgstr ""
188
+
189
+ #: includes/admin/settings/register-settings.php:150
190
+ msgid ""
191
+ "Select the plugins that should be disabled during build process of the "
192
+ "staging site. Some plugins slow down the copy process and add overhead to "
193
+ "each request, requiring extra CPU and memory consumption. Some of them can "
194
+ "interfere with cloning process and cause them to fail, so we recommend to "
195
+ "disable all plugins that are not directly related to WP Staging."
196
+ msgstr ""
197
+
198
+ #: includes/admin/settings/register-settings.php:157
199
+ msgid "Don´t force admin login"
200
+ msgstr ""
201
+
202
+ #: includes/admin/settings/register-settings.php:158
203
+ msgid ""
204
+ "Use this option only if you are using a custom login page and not the "
205
+ "default login.php. If you enable this option you are allowing everyone "
206
+ "including searchengines to see your staging site, so you have to create a "
207
+ "custom authentication like using .htaccess"
208
+ msgstr ""
209
+
210
+ #: includes/admin/settings/register-settings.php:170
211
+ msgid "Remove Data on Uninstall?"
212
+ msgstr ""
213
+
214
+ #: includes/admin/settings/register-settings.php:171
215
+ msgid ""
216
+ "Check this box if you like WP Staging to completely remove all of its data "
217
+ "when the plugin is deleted."
218
+ msgstr ""
219
+
220
+ #: includes/admin/settings/register-settings.php:186
221
+ msgid "Activate your Add-Ons"
222
+ msgstr ""
223
+
224
+ #: includes/admin/settings/register-settings.php:271
225
+ msgid "Settings updated."
226
+ msgstr ""
227
+
228
+ #: includes/admin/settings/register-settings.php:304
229
+ msgid "Misc"
230
+ msgstr ""
231
+
232
+ #: includes/admin/settings/register-settings.php:312
233
+ msgid "Extensions"
234
+ msgstr ""
235
+
236
+ #: includes/admin/settings/register-settings.php:527
237
+ msgid ""
238
+ "The callback function used for the <strong>%s</strong> setting is missing."
239
+ msgstr ""
240
+
241
+ #: includes/admin/settings/register-settings.php:639
242
+ msgid "Upload File"
243
+ msgstr ""
244
+
245
+ #: includes/admin/settings/register-settings.php:667
246
+ msgid "Deactivate License"
247
+ msgstr ""
248
+
249
+ #: includes/admin/settings/register-settings.php:714
250
+ msgid "Select Image"
251
+ msgstr ""
252
+
253
+ #: includes/admin/settings/register-settings.php:761
254
+ msgid ""
255
+ "Log file directory not writable! Set FTP permission to 755 or 777 for /wp-"
256
+ "content/plugins/wp-staging/logs/"
257
+ msgstr ""
258
+
259
+ #: includes/admin/settings/register-settings.php:775
260
+ msgid ""
261
+ "Improve performance and reliability by not loading the following plugins for "
262
+ "migration requests"
263
+ msgstr ""
264
+
265
+ #: includes/admin/settings/register-settings.php:780
266
+ msgid "Select the plugins you wish to disable during clone process"
267
+ msgstr ""
268
+
269
+ #: includes/admin/settings/register-settings.php:798
270
+ msgid "Select All"
271
+ msgstr ""
272
+
273
+ #: includes/admin/settings/register-settings.php:800
274
+ msgid "Deselect All"
275
+ msgstr ""
276
+
277
+ #: includes/admin/settings/register-settings.php:802
278
+ msgid "Invert Selection"
279
+ msgstr ""
280
+
281
+ #: includes/admin/settings/register-settings.php:804
282
+ msgid "Save Changes"
283
+ msgstr ""
284
+
285
+ #: includes/admin/settings/register-settings.php:805
286
+ msgctxt "The settings were saved successfully"
287
+ msgid "Saved"
288
+ msgstr ""
289
+
290
+ #: includes/admin/tools.php:67
291
+ msgid "Import/Export"
292
+ msgstr ""
293
+
294
+ #: includes/admin/tools.php:68
295
+ msgid "System Info"
296
+ msgstr ""
297
+
298
+ #: includes/admin/tools.php:90
299
+ msgid "Export Settings"
300
+ msgstr ""
301
+
302
+ #: includes/admin/tools.php:92
303
+ msgid ""
304
+ "Export the WP-Staging settings for this site as a .json file. This allows "
305
+ "you to easily import the configuration into another site."
306
+ msgstr ""
307
+
308
+ #: includes/admin/tools.php:98
309
+ msgid "Export"
310
+ msgstr ""
311
+
312
+ #: includes/admin/tools.php:105
313
+ msgid "Import Settings"
314
+ msgstr ""
315
+
316
+ #: includes/admin/tools.php:107
317
+ msgid ""
318
+ "Import the WP-Staging settings from a .json file. This file can be obtained "
319
+ "by exporting the settings on another site using the form above."
320
+ msgstr ""
321
+
322
+ #: includes/admin/tools.php:115
323
+ msgid "Import"
324
+ msgstr ""
325
+
326
+ #: includes/admin/tools.php:217
327
+ msgid "Please upload a valid .json file"
328
+ msgstr ""
329
+
330
+ #: includes/admin/tools.php:223
331
+ msgid "Please upload a file to import"
332
+ msgstr ""
333
+
334
+ #: includes/class-wpstg-html-elements.php:43
335
+ msgctxt "all dropdown items"
336
+ msgid "All"
337
+ msgstr ""
338
+
339
+ #: includes/class-wpstg-html-elements.php:44
340
+ msgctxt "no dropdown items"
341
+ msgid "None"
342
+ msgstr ""
343
+
344
+ #: includes/class-wpstg-license-handler.php:148
345
+ msgid "%1$s License Key"
346
+ msgstr ""
347
+
348
+ #: includes/class-wpstg-license-handler.php:184
349
+ #: includes/class-wpstg-license-handler.php:257
350
+ msgid "Nonce verification failed"
351
+ msgstr ""
352
+
353
+ #: includes/class-wpstg-license-handler.php:333
354
+ msgid "This license does not belong to the product you have entered it for."
355
+ msgstr ""
356
+
357
+ #: includes/class-wpstg-license-handler.php:338
358
+ msgid "This license does not have any activations left"
359
+ msgstr ""
360
+
361
+ #: includes/class-wpstg-license-handler.php:343
362
+ msgid "This license key is expired. Please renew it."
363
+ msgstr ""
364
+
365
+ #: includes/class-wpstg-license-handler.php:348
366
+ msgid ""
367
+ "There was a problem activating your license key, please try again or contact "
368
+ "support. Error code: %s"
369
+ msgstr ""
370
+
371
+ #: includes/scripts.php:45
372
+ msgid ""
373
+ "If confirmed we will install an additional WordPress 'Must Use' plugin. This "
374
+ "plugin will allow us to control which plugins are loaded during WP Staging "
375
+ "specific operations. Do you wish to continue?"
376
+ msgstr ""
377
+
378
+ #: includes/scripts.php:46
379
+ msgid ""
380
+ "A problem occurred when trying to change the plugin compatibility setting."
381
+ msgstr ""
382
+
383
+ #: includes/scripts.php:47
384
+ msgid "Saved"
385
+ msgstr ""
386
+
387
+ #: includes/scripts.php:48
388
+ msgid "Status"
389
+ msgstr ""
390
+
391
+ #: includes/scripts.php:49
392
+ msgid "Response"
393
+ msgstr ""
394
+
395
+ #: includes/scripts.php:50
396
+ msgid "A problem occurred when trying to add plugins to backlist."
397
+ msgstr ""
398
+
399
+ #: includes/staging-functions.php:31
400
+ msgid "Access denied. <a href=\"%1$s\" target=\"_blank\">Login</a> first"
401
+ msgstr ""
402
+
403
+ #: includes/template-functions.php:50
404
+ msgid ""
405
+ "WordPress Multisite is currently not supported! <a href=\"https://wp-staging."
406
+ "com/contact\">Get in contact with us</a> and ask for it."
407
+ msgstr ""
408
+
409
+ #: includes/template-functions.php:54
410
+ msgid "Overview"
411
+ msgstr ""
412
+
413
+ #: includes/template-functions.php:55
414
+ msgid "Scanning"
415
+ msgstr ""
416
+
417
+ #: includes/template-functions.php:56
418
+ msgid "Cloning"
419
+ msgstr ""
420
+
421
+ #: includes/template-functions.php:87
422
+ msgid "Create new staging site"
423
+ msgstr ""
424
+
425
+ #: includes/template-functions.php:92
426
+ msgid "Available Staging Sites:"
427
+ msgstr ""
428
+
429
+ #: includes/template-functions.php:96
430
+ msgid "Open"
431
+ msgstr ""
432
+
433
+ #: includes/template-functions.php:97
434
+ msgid "Edit"
435
+ msgstr ""
436
+
437
+ #: includes/template-functions.php:98
438
+ msgid "Delete"
439
+ msgstr ""
440
+
441
+ #: includes/template-functions.php:169
442
+ msgid "Name your new site, e.g. staging, development:"
443
+ msgstr ""
444
+
445
+ #: includes/template-functions.php:181
446
+ msgid "DB Tables"
447
+ msgstr ""
448
+
449
+ #: includes/template-functions.php:186
450
+ msgid ""
451
+ "Select the tables to copy. (If the copy process was previously interrupted, "
452
+ "succesfull copied tables are greyed out and copy process will skip these "
453
+ "ones)"
454
+ msgstr ""
455
+
456
+ #: includes/template-functions.php:192
457
+ msgid "Files"
458
+ msgstr ""
459
+
460
+ #: includes/template-functions.php:197
461
+ msgid "Select the folders to copy:"
462
+ msgstr ""
463
+
464
+ #: includes/template-functions.php:205
465
+ msgid "Advanced Options"
466
+ msgstr ""
467
+
468
+ #: includes/template-functions.php:211
469
+ msgid "Back"
470
+ msgstr ""
471
+
472
+ #: includes/template-functions.php:380
473
+ msgid ""
474
+ "We have detected the following large files which could neeed some "
475
+ "investigation. Often this files are backup files or other temporary files "
476
+ "which must not necessarily copied for creating a staging site:"
477
+ msgstr ""
478
+
479
+ #: includes/template-functions.php:468
480
+ msgid "Copy database tables"
481
+ msgstr ""
482
+
483
+ #: includes/template-functions.php:473
484
+ msgid "Copy files"
485
+ msgstr ""
486
+
487
+ #: includes/template-functions.php:478
488
+ msgid "Replace Links"
489
+ msgstr ""
490
+
491
+ #: includes/template-functions.php:484 includes/template-functions.php:1180
492
+ msgid "Cancel"
493
+ msgstr ""
494
+
495
+ #: includes/template-functions.php:488
496
+ msgid "Display working log"
497
+ msgstr ""
498
+
499
+ #: includes/template-functions.php:494 includes/template-functions.php:1181
500
+ msgid "Remove"
501
+ msgstr ""
502
+
503
+ #: includes/template-functions.php:495
504
+ msgid "Start again"
505
+ msgstr ""
506
+
507
+ #: includes/template-functions.php:497
508
+ msgid "Important notes:"
509
+ msgstr ""
510
+
511
+ #: includes/template-functions.php:1149
512
+ msgid ""
513
+ "Attention: Check carefully if this DB tables and files are safe to delete "
514
+ "for the staging site"
515
+ msgstr ""
516
+
517
+ #: includes/template-functions.php:1151
518
+ msgid ""
519
+ "Usually the preselected data can be deleted without any risk, but in case "
520
+ "something goes wrong you better check it first."
521
+ msgstr ""
522
+
523
+ #: includes/template-functions.php:1156
524
+ msgid "DB tables to remove"
525
+ msgstr ""
526
+
527
+ #: includes/template-functions.php:1161
528
+ msgid "Select the tables for removal:"
529
+ msgstr ""
530
+
531
+ #: includes/template-functions.php:1169
532
+ msgid "Files to remove"
533
+ msgstr ""
534
+
535
+ #: includes/template-functions.php:1174
536
+ msgid "Select the folders for removal. Click on a folder name to expand it:"
537
+ msgstr ""
538
+
539
+ #: includes/template-functions.php:1394
540
+ msgid "The following directory could not be created: %s"
541
+ msgstr ""
542
+
543
+ #: includes/template-functions.php:1399
544
+ msgid "Could not copy the compatibility plugin from %1$s to %2$s"
545
+ msgstr ""
546
+
547
+ #: includes/template-functions.php:1405
548
+ msgid "Could not remove the compatibility plugin from %s"
549
+ msgstr ""
550
+
551
+ #: includes/template-functions.php:1560
552
+ msgid "Start Cloning"
553
+ msgstr ""
554
+
555
+ #: includes/template-functions.php:1562
556
+ msgid "Resume Cloning"
557
+ msgstr ""
558
+
559
+ #: includes/template-functions.php:1575
560
+ msgid ""
561
+ "Probably not enough free disk space to create a staging site. You can "
562
+ "continue but its likely that the copying process will fail."
563
+ msgstr ""
564
+
565
+ #: includes/wpstg-sanitize.php:39
566
+ msgid "%1$s was not expecting data to be an array."
567
+ msgstr ""
568
+
569
+ #: includes/wpstg-sanitize.php:60
570
+ msgid "%1$s was expecting an array but got something else: \"%2$s\""
571
+ msgstr ""
572
+
573
+ #: includes/wpstg-sanitize.php:66
574
+ msgid "%1$s was expecting a string but got something else: \"%2$s\""
575
+ msgstr ""
576
+
577
+ #: includes/wpstg-sanitize.php:73
578
+ msgid "%1$s was expecting a valid key but got something else: \"%2$s\""
579
+ msgstr ""
580
+
581
+ #: includes/wpstg-sanitize.php:81
582
+ msgid "%1$s was expecting text but got something else: \"%2$s\""
583
+ msgstr ""
584
+
585
+ #: includes/wpstg-sanitize.php:88
586
+ msgid "%1$s was expecting serialized data but got something else: \"%2$s\""
587
+ msgstr ""
588
+
589
+ #: includes/wpstg-sanitize.php:94
590
+ msgid "%1$s was expecting a valid numeric but got something else: \"%2$s\""
591
+ msgstr ""
592
+
593
+ #: includes/wpstg-sanitize.php:101
594
+ msgid "%1$s was expecting an integer but got something else: \"%2$s\""
595
+ msgstr ""
596
+
597
+ #: includes/wpstg-sanitize.php:108
598
+ msgid ""
599
+ "%1$s was expecting a positive number (int) but got something else: \"%2$s\""
600
+ msgstr ""
601
+
602
+ #: includes/wpstg-sanitize.php:115
603
+ msgid ""
604
+ "%1$s was expecting a negative number (int) but got something else: \"%2$s\""
605
+ msgstr ""
606
+
607
+ #: includes/wpstg-sanitize.php:122
608
+ msgid "%1$s was expecting 0 (int) but got something else: \"%2$s\""
609
+ msgstr ""
610
+
611
+ #: includes/wpstg-sanitize.php:129
612
+ msgid "%1$s was expecting an empty value but got something else: \"%2$s\""
613
+ msgstr ""
614
+
615
+ #: includes/wpstg-sanitize.php:136
616
+ msgid "%1$s was expecting a URL but got something else: \"%2$s\""
617
+ msgstr ""
618
+
619
+ #: includes/wpstg-sanitize.php:144
620
+ msgid "%1$s was expecting a bool but got something else: \"%2$s\""
621
+ msgstr ""
622
+
623
+ #: includes/wpstg-sanitize.php:150
624
+ msgid "Unknown sanitization rule \"%1$s\" supplied by %2$s"
625
+ msgstr ""
626
+
627
+ #: wp-staging.php:114 wp-staging.php:126
628
+ msgid "Cheatin&#8217; huh?"
629
+ msgstr ""
630
+
631
+ #. Plugin Name of the plugin/theme
632
+ msgid "WP Staging - Create a staging clone site for testing & developing"
633
+ msgstr ""
634
+
635
+ #. #-#-#-#-# plugin.pot (WP Staging - Create a staging clone site for testing & developing 0.9.0) #-#-#-#-#
636
+ #. Plugin URI of the plugin/theme
637
+ #. #-#-#-#-# plugin.pot (WP Staging - Create a staging clone site for testing & developing 0.9.0) #-#-#-#-#
638
+ #. Author URI of the plugin/theme
639
+ msgid "wordpress.org/plugins/wp-staging"
640
+ msgstr ""
641
+
642
+ #. Description of the plugin/theme
643
+ msgid "WP-Staging - Create a staging clone site for testing & developing"
644
+ msgstr ""
645
+
646
+ #. Author of the plugin/theme
647
+ msgid "René Hermenau"
648
+ msgstr ""
license.txt ADDED
@@ -0,0 +1,281 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 2, June 1991
3
+
4
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
5
+ 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
6
+
7
+ Everyone is permitted to copy and distribute verbatim copies
8
+ of this license document, but changing it is not allowed.
9
+
10
+ Preamble
11
+
12
+ The licenses for most software are designed to take away your
13
+ freedom to share and change it. By contrast, the GNU General Public
14
+ License is intended to guarantee your freedom to share and change free
15
+ software--to make sure the software is free for all its users. This
16
+ General Public License applies to most of the Free Software
17
+ Foundation's software and to any other program whose authors commit to
18
+ using it. (Some other Free Software Foundation software is covered by
19
+ the GNU Library General Public License instead.) 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
+ this service if you wish), that you receive source code or can get it
26
+ if you want it, that you can change the software or use pieces of it
27
+ in new free programs; and that you know you can do these things.
28
+
29
+ To protect your rights, we need to make restrictions that forbid
30
+ anyone to deny you these rights or to ask you to surrender the rights.
31
+ These restrictions translate to certain responsibilities for you if you
32
+ distribute copies of the software, or if you modify it.
33
+
34
+ For example, if you distribute copies of such a program, whether
35
+ gratis or for a fee, you must give the recipients all the rights that
36
+ you have. You must make sure that they, too, receive or can get the
37
+ source code. And you must show them these terms so they know their
38
+ rights.
39
+
40
+ We protect your rights with two steps: (1) copyright the software, and
41
+ (2) offer you this license which gives you legal permission to copy,
42
+ distribute and/or modify the software.
43
+
44
+ Also, for each author's protection and ours, we want to make certain
45
+ that everyone understands that there is no warranty for this free
46
+ software. If the software is modified by someone else and passed on, we
47
+ want its recipients to know that what they have is not the original, so
48
+ that any problems introduced by others will not reflect on the original
49
+ authors' reputations.
50
+
51
+ Finally, any free program is threatened constantly by software
52
+ patents. We wish to avoid the danger that redistributors of a free
53
+ program will individually obtain patent licenses, in effect making the
54
+ program proprietary. To prevent this, we have made it clear that any
55
+ patent must be licensed for everyone's free use or not licensed at all.
56
+
57
+ The precise terms and conditions for copying, distribution and
58
+ modification follow.
59
+
60
+ GNU GENERAL PUBLIC LICENSE
61
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
62
+
63
+ 0. This License applies to any program or other work which contains
64
+ a notice placed by the copyright holder saying it may be distributed
65
+ under the terms of this General Public License. The "Program", below,
66
+ refers to any such program or work, and a "work based on the Program"
67
+ means either the Program or any derivative work under copyright law:
68
+ that is to say, a work containing the Program or a portion of it,
69
+ either verbatim or with modifications and/or translated into another
70
+ language. (Hereinafter, translation is included without limitation in
71
+ the term "modification".) Each licensee is addressed as "you".
72
+
73
+ Activities other than copying, distribution and modification are not
74
+ covered by this License; they are outside its scope. The act of
75
+ running the Program is not restricted, and the output from the Program
76
+ is covered only if its contents constitute a work based on the
77
+ Program (independent of having been made by running the Program).
78
+ Whether that is true depends on what the Program does.
79
+
80
+ 1. You may copy and distribute verbatim copies of the Program's
81
+ source code as you receive it, in any medium, provided that you
82
+ conspicuously and appropriately publish on each copy an appropriate
83
+ copyright notice and disclaimer of warranty; keep intact all the
84
+ notices that refer to this License and to the absence of any warranty;
85
+ and give any other recipients of the Program a copy of this License
86
+ along with the Program.
87
+
88
+ You may charge a fee for the physical act of transferring a copy, and
89
+ you may at your option offer warranty protection in exchange for a fee.
90
+
91
+ 2. You may modify your copy or copies of the Program or any portion
92
+ of it, thus forming a work based on the Program, and copy and
93
+ distribute such modifications or work under the terms of Section 1
94
+ above, provided that you also meet all of these conditions:
95
+
96
+ a) You must cause the modified files to carry prominent notices
97
+ stating that you changed the files and the date of any change.
98
+
99
+ b) You must cause any work that you distribute or publish, that in
100
+ whole or in part contains or is derived from the Program or any
101
+ part thereof, to be licensed as a whole at no charge to all third
102
+ parties under the terms of this License.
103
+
104
+ c) If the modified program normally reads commands interactively
105
+ when run, you must cause it, when started running for such
106
+ interactive use in the most ordinary way, to print or display an
107
+ announcement including an appropriate copyright notice and a
108
+ notice that there is no warranty (or else, saying that you provide
109
+ a warranty) and that users may redistribute the program under
110
+ these conditions, and telling the user how to view a copy of this
111
+ License. (Exception: if the Program itself is interactive but
112
+ does not normally print such an announcement, your work based on
113
+ the Program is not required to print an announcement.)
114
+
115
+ These requirements apply to the modified work as a whole. If
116
+ identifiable sections of that work are not derived from the Program,
117
+ and can be reasonably considered independent and separate works in
118
+ themselves, then this License, and its terms, do not apply to those
119
+ sections when you distribute them as separate works. But when you
120
+ distribute the same sections as part of a whole which is a work based
121
+ on the Program, the distribution of the whole must be on the terms of
122
+ this License, whose permissions for other licensees extend to the
123
+ entire whole, and thus to each and every part regardless of who wrote it.
124
+ Thus, it is not the intent of this section to claim rights or contest
125
+ your rights to work written entirely by you; rather, the intent is to
126
+ exercise the right to control the distribution of derivative or
127
+ collective works based on the Program.
128
+
129
+ In addition, mere aggregation of another work not based on the Program
130
+ with the Program (or with a work based on the Program) on a volume of
131
+ a storage or distribution medium does not bring the other work under
132
+ the scope of this License.
133
+
134
+ 3. You may copy and distribute the Program (or a work based on it,
135
+ under Section 2) in object code or executable form under the terms of
136
+ Sections 1 and 2 above provided that you also do one of the following:
137
+
138
+ a) Accompany it with the complete corresponding machine-readable
139
+ source code, which must be distributed under the terms of Sections
140
+ 1 and 2 above on a medium customarily used for software interchange; or,
141
+
142
+ b) Accompany it with a written offer, valid for at least three
143
+ years, to give any third party, for a charge no more than your
144
+ cost of physically performing source distribution, a complete
145
+ machine-readable copy of the corresponding source code, to be
146
+ distributed under the terms of Sections 1 and 2 above on a medium
147
+ customarily used for software interchange; or,
148
+
149
+ c) Accompany it with the information you received as to the offer
150
+ to distribute corresponding source code. (This alternative is
151
+ allowed only for noncommercial distribution and only if you
152
+ received the program in object code or executable form with such
153
+ an offer, in accord with Subsection b above.)
154
+
155
+ The source code for a work means the preferred form of the work for
156
+ making modifications to it. For an executable work, complete source
157
+ code means all the source code for all modules it contains, plus any
158
+ associated interface definition files, plus the scripts used to
159
+ control compilation and installation of the executable. However, as a
160
+ special exception, the source code distributed need not include
161
+ anything that is normally distributed (in either source or binary
162
+ form) with the major components (compiler, kernel, and so on) of the
163
+ operating system on which the executable runs, unless that component
164
+ itself accompanies the executable.
165
+
166
+ If distribution of executable or object code is made by offering
167
+ access to copy from a designated place, then offering equivalent
168
+ access to copy the source code from the same place counts as
169
+ distribution of the source code, even though third parties are not
170
+ compelled to copy the source along with the object code.
171
+
172
+ 4. You may not copy, modify, sublicense, or distribute the Program
173
+ except as expressly provided under this License. Any attempt
174
+ otherwise to copy, modify, sublicense or distribute the Program is
175
+ void, and will automatically terminate your rights under this License.
176
+ However, parties who have received copies, or rights, from you under
177
+ this License will not have their licenses terminated so long as such
178
+ parties remain in full compliance.
179
+
180
+ 5. You are not required to accept this License, since you have not
181
+ signed it. However, nothing else grants you permission to modify or
182
+ distribute the Program or its derivative works. These actions are
183
+ prohibited by law if you do not accept this License. Therefore, by
184
+ modifying or distributing the Program (or any work based on the
185
+ Program), you indicate your acceptance of this License to do so, and
186
+ all its terms and conditions for copying, distributing or modifying
187
+ the Program or works based on it.
188
+
189
+ 6. Each time you redistribute the Program (or any work based on the
190
+ Program), the recipient automatically receives a license from the
191
+ original licensor to copy, distribute or modify the Program subject to
192
+ these terms and conditions. You may not impose any further
193
+ restrictions on the recipients' exercise of the rights granted herein.
194
+ You are not responsible for enforcing compliance by third parties to
195
+ this License.
196
+
197
+ 7. If, as a consequence of a court judgment or allegation of patent
198
+ infringement or for any other reason (not limited to patent issues),
199
+ conditions are imposed on you (whether by court order, agreement or
200
+ otherwise) that contradict the conditions of this License, they do not
201
+ excuse you from the conditions of this License. If you cannot
202
+ distribute so as to satisfy simultaneously your obligations under this
203
+ License and any other pertinent obligations, then as a consequence you
204
+ may not distribute the Program at all. For example, if a patent
205
+ license would not permit royalty-free redistribution of the Program by
206
+ all those who receive copies directly or indirectly through you, then
207
+ the only way you could satisfy both it and this License would be to
208
+ refrain entirely from distribution of the Program.
209
+
210
+ If any portion of this section is held invalid or unenforceable under
211
+ any particular circumstance, the balance of the section is intended to
212
+ apply and the section as a whole is intended to apply in other
213
+ circumstances.
214
+
215
+ It is not the purpose of this section to induce you to infringe any
216
+ patents or other property right claims or to contest validity of any
217
+ such claims; this section has the sole purpose of protecting the
218
+ integrity of the free software distribution system, which is
219
+ implemented by public license practices. Many people have made
220
+ generous contributions to the wide range of software distributed
221
+ through that system in reliance on consistent application of that
222
+ system; it is up to the author/donor to decide if he or she is willing
223
+ to distribute software through any other system and a licensee cannot
224
+ impose that choice.
225
+
226
+ This section is intended to make thoroughly clear what is believed to
227
+ be a consequence of the rest of this License.
228
+
229
+ 8. If the distribution and/or use of the Program is restricted in
230
+ certain countries either by patents or by copyrighted interfaces, the
231
+ original copyright holder who places the Program under this License
232
+ may add an explicit geographical distribution limitation excluding
233
+ those countries, so that distribution is permitted only in or among
234
+ countries not thus excluded. In such case, this License incorporates
235
+ the limitation as if written in the body of this License.
236
+
237
+ 9. The Free Software Foundation may publish revised and/or new versions
238
+ of the General Public License from time to time. Such new versions will
239
+ be similar in spirit to the present version, but may differ in detail to
240
+ address new problems or concerns.
241
+
242
+ Each version is given a distinguishing version number. If the Program
243
+ specifies a version number of this License which applies to it and "any
244
+ later version", you have the option of following the terms and conditions
245
+ either of that version or of any later version published by the Free
246
+ Software Foundation. If the Program does not specify a version number of
247
+ this License, you may choose any version ever published by the Free Software
248
+ Foundation.
249
+
250
+ 10. If you wish to incorporate parts of the Program into other free
251
+ programs whose distribution conditions are different, write to the author
252
+ to ask for permission. For software which is copyrighted by the Free
253
+ Software Foundation, write to the Free Software Foundation; we sometimes
254
+ make exceptions for this. Our decision will be guided by the two goals
255
+ of preserving the free status of all derivatives of our free software and
256
+ of promoting the sharing and reuse of software generally.
257
+
258
+ NO WARRANTY
259
+
260
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261
+ FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263
+ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264
+ OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266
+ TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267
+ PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268
+ REPAIR OR CORRECTION.
269
+
270
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272
+ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273
+ INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274
+ OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275
+ TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276
+ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277
+ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278
+ POSSIBILITY OF SUCH DAMAGES.
279
+
280
+ END OF TERMS AND CONDITIONS
281
+
optimizer/wp-staging-optimizer.php ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Plugin Name: WP Staging Optimizer
4
+ Plugin URI: https://wp-staging.com
5
+ Description: Prevents 3rd party plugins from being loaded during WP Staging specific operations
6
+ Author: René Hermenau
7
+ Version: 1.0
8
+ Author URI: https://wp-staging.com
9
+ Credit: Original version is made by Delicious Brains (WP Migrate DB). Thank you guys!
10
+ */
11
+
12
+ $GLOBALS['wpstg_optimizer'] = true;
13
+
14
+ /**
15
+ * Remove TGM Plugin Activation 'force_activation' admin_init action hook if present.
16
+ *
17
+ * This is to stop excluded plugins being deactivated after a migration, when a theme uses TGMPA to require a plugin to be always active.
18
+ */
19
+ function wpstg_tgmpa_compatibility() {
20
+ $remove_function = false;
21
+
22
+ // run on wpstg page
23
+ if ( isset( $_GET['page'] ) && 'wpstg_clone' == $_GET['page'] ) {
24
+ $remove_function = true;
25
+ }
26
+ // run on wpstg ajax requests
27
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX && isset( $_POST['action'] ) && false !== strpos( $_POST['action'], 'wpstg' ) ) {
28
+ $remove_function = true;
29
+ }
30
+
31
+ if ( $remove_function ) {
32
+ global $wp_filter;
33
+ $admin_init_functions = $wp_filter['admin_init'];
34
+ foreach ( $admin_init_functions as $priority => $functions ) {
35
+ foreach ( $functions as $key => $function ) {
36
+ // searching for function this way as can't rely on the calling class being named TGM_Plugin_Activation
37
+ if ( false !== strpos( $key, 'force_activation' ) ) {
38
+ unset( $wp_filter['admin_init'][ $priority ][ $key ] );
39
+
40
+ return;
41
+ }
42
+ }
43
+ }
44
+ }
45
+ }
46
+
47
+ add_action( 'admin_init', 'wpstg_tgmpa_compatibility', 1 );
48
+
49
+ /**
50
+ * remove blog-active plugins
51
+ *
52
+ * @param array $plugins numerically keyed array of plugin names
53
+ *
54
+ * @return array
55
+ */
56
+ function wpstg_exclude_plugins( $plugins ) {
57
+ if ( ! is_array( $plugins ) || empty( $plugins ) ) {
58
+ return $plugins;
59
+ }
60
+
61
+ if ( ! wpstg_is_compatibility_mode_request() ) {
62
+ return $plugins;
63
+ }
64
+
65
+ $blacklist_plugins = wpstg_get_blacklist_plugins();
66
+
67
+ if ( ! empty( $blacklist_plugins ) ) {
68
+ foreach ( $plugins as $key => $plugin ) {
69
+ if ( false !== strpos( $plugin, 'wp-staging' ) || ! isset( $blacklist_plugins[ $plugin ] ) ) {
70
+ continue;
71
+ }
72
+ unset( $plugins[ $key ] );
73
+ }
74
+ }
75
+
76
+ return $plugins;
77
+
78
+ }
79
+
80
+ add_filter( 'option_active_plugins', 'wpstg_exclude_plugins' );
81
+
82
+ /**
83
+ * remove network-active plugins
84
+ *
85
+ * @param array $plugins array of plugins keyed by name (name=>timestamp pairs)
86
+ *
87
+ * @return array
88
+ */
89
+ function wpstg_exclude_site_plugins( $plugins ) {
90
+ if ( ! is_array( $plugins ) || empty( $plugins ) ) {
91
+ return $plugins;
92
+ }
93
+
94
+ if ( ! wpstg_is_compatibility_mode_request() ) {
95
+ return $plugins;
96
+ }
97
+
98
+ $blacklist_plugins = wpstg_get_blacklist_plugins();
99
+
100
+ if ( ! empty( $blacklist_plugins ) ) {
101
+ foreach ( array_keys( $plugins ) as $plugin ) {
102
+ if ( false !== strpos( $plugin, 'wp-staging' ) || ! isset( $blacklist_plugins[ $plugin ] ) ) {
103
+ continue;
104
+ }
105
+ unset( $plugins[ $plugin ] );
106
+ }
107
+ }
108
+
109
+ return $plugins;
110
+ }
111
+
112
+ add_filter( 'site_option_active_sitewide_plugins', 'wpstg_exclude_site_plugins' );
113
+
114
+ /**
115
+ * Should the current request be processed by Compatibility Mode?
116
+ *
117
+ * @return bool
118
+ */
119
+ function wpstg_is_compatibility_mode_request() {
120
+ if ( ! defined( 'DOING_AJAX' ) ||
121
+ ! DOING_AJAX ||
122
+ ! isset( $_POST['action'] ) ||
123
+ false === strpos( $_POST['action'], 'wpstg' )
124
+ ) {
125
+ return false;
126
+ }
127
+ return true;
128
+ }
129
+
130
+ /**
131
+ * Returns an array of plugin slugs to be blacklisted.
132
+ *
133
+ * @return array
134
+ */
135
+ function wpstg_get_blacklist_plugins() {
136
+ $blacklist_plugins = array();
137
+
138
+ //@todo use this later for multisites
139
+ //$wpstg_options = get_site_option( 'wpstg_settings' );
140
+ $wpstg_options = get_option( 'wpstg_settings' );
141
+
142
+ if ( ! empty( $wpstg_options['blacklist_plugins'] ) ) {
143
+ $blacklist_plugins = array_flip( $wpstg_options['blacklist_plugins'] );
144
+ }
145
+
146
+ return $blacklist_plugins;
147
+ }
readme.txt ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === WP Staging - DB & file duplicator & migration ===
2
+
3
+ Author URL: https://wordpress.org/plugins/wp-staging
4
+ Plugin URL: https://wordpress.org/plugins/wp-staging
5
+ Contributors: ReneHermi, WP-Staging
6
+ Donate link: https://wordpress.org/plugins/wp-staging
7
+ License: GPLv2 or later
8
+ License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
+ Tags: staging, migration, wordpress migration, wordpress staging, development, migrate, cloning, clone, database export, database find replace, database serialization, duplication, duplicator, duplicate, site duplicate, duplicate posts, db backup, file backup, backup, db migration, db restore, website backup, website staging, website deploy, staging, admin, page, content, plugin, media, backup, test, test site, testing, sandbox, widget, post, plugin, admin, posts, sidebar, shortcode, google, administration, ajax, api, authentication, blog, code, comments, contact, dashboard, multisite, theme
10
+ Requires at least: 3.6+
11
+ Tested up to: 4.4.1
12
+ Stable tag: 1.0.0
13
+
14
+ A duplicator plugin! Clone, duplicate and migrate live sites to independent staging and development sites that are available only to administrators.
15
+
16
+ == Description ==
17
+
18
+ <strong>This cloning and staging plugin is well tested but still work in progress! <br>
19
+ If you find a bug please open a ticket in the [support request](https://wordpress.org/support/plugin/wp-staging/ "support forum"). Every issue will be fixed asap!
20
+ </strong>
21
+ <br /><br />
22
+ <strong>Note: </strong> This plugin is not able to push back your changes to the live site at the moment! This is a feature i am already working on.
23
+ <br /> <br />
24
+
25
+
26
+ <blockquote>
27
+ <h4> WP Staging for WordPress Migration </h4>
28
+ This duplicator plugin allows you to create an staging or development environment in seconds* <br /> <br />
29
+ It creates a file clone of your website into a subfolder of your current WordPress installation with an entire copy of your database.
30
+ This sounds pretty simple and yes it is! All the hard time consumptive database and file copy stuff including url replacements is done in the background.
31
+ <br /> <br />
32
+ I created this plugin because all other solutions are way too complex, overloaded with dozens of options or having server requirements which are not available on most shared hosting solutions.
33
+ All these reasons prevent user from testing new plugins and updates first before installing them on their live website, so its time to release a plugin which has the potential to be merged into everyone´s wordpress workflow.
34
+ <br /> <br />
35
+ <p><small><em>* Time of creation depends on size of your database and file size</em></small></p>
36
+ </blockquote>
37
+
38
+ WP Staging helps you to prevent your website from being broken or unavailable because of installing untested plugin updates!
39
+
40
+ [youtube https://www.youtube.com/watch?v=Ye3fC6cdB3A]
41
+
42
+ = Main Features =
43
+
44
+ * <strong>Easy: </strong> Staging migration applicable for everyone. No configuration needed!
45
+ * <strong>Fast: </strong> Migration process lasts only a few seconds or minutes, depending on the site's size and server I/O power
46
+ * <strong>Safe: </strong> Access to staging site is granted for administrators only.
47
+ <br /><br />
48
+ <strong>More safe:</strong>
49
+ <br>
50
+ * Admin bar reflects that you are working on a staging site
51
+ * Extensive logging if duplication and migration process fails.
52
+
53
+ = What does not work or is not tested when running wordpress migration? =
54
+
55
+ * Wordpress migration of wordpress multisites (not tested)
56
+ * WordPress duplicating process on windows server (not tested but will probably work)
57
+ Edit: Duplication on windows server seems to be working well: [Read more](https://wordpress.org/support/topic/wont-copy-files?replies=5 "Read more")
58
+
59
+
60
+ <strong>Change your workflow of updating themes and plugins data:</strong>
61
+
62
+ 1. Use WP Staging for migration of a production website to a clone site for staging purposes
63
+ 2. Customize theme, configuration and plugins or install new plugins
64
+ 3. Test everything on your staging site first
65
+ 4. Everything running as expected? You are on the save side for migration of all these modifications to your production site!
66
+
67
+
68
+ <h3> Why should i use a staging website? </h3>
69
+
70
+ Plugin updates and theme customizations should be tested on a staging platform first. Its recommended to have the staging platform on the same server where the production website is located.
71
+ When you run a plugin update or plan to install a new one, it is a necessary task to check first the modifications on a clone of your production website.
72
+ This makes sure that any modifications is working on your website without throwing unexpected errors or preventing your site from loading. (Better known as the wordpress blank page error)
73
+
74
+ Testing a plugin update before installing it in live environment isn´t done very often by most user because existing staging solutions are too complex and need a lot of time to create a
75
+ up-to-date copy of your website.
76
+
77
+ Some people are also afraid of installing plugins updates because they follow the rule "never touch a running system" with having in mind that untested updates are increasing the risk of breaking their site.
78
+ I totally understand this and i am guilty as well here, but unfortunately this leads to one of the main reasons why WordPress installations are often outdated, not updated at all and unsecure due to this non-update behavior.
79
+
80
+ <strong> I think its time to change this, so i created "WP Staging" for WordPress migration of staging sites</strong>
81
+
82
+ <h3> Can´t i just use my local wordpress development copy for testing like xampp / lampp? </h3>
83
+
84
+ Nope! If your local hardware and software environment is not a 100% exact clone of your production server there is NO guarantee that every aspect
85
+ of your local copy is working on your live website exactely as you would expect it.
86
+ There are some obvious things like differences in the config of php and the server you are running but even such non obvious settings like the amount of ram or the
87
+ the cpu performance can lead to unexpected results on your production website.
88
+ There are dozens of other possible cause of failure which can not be handled well when you are testing your changes on a local staging platform.
89
+
90
+ This is were WP Staging steps in... Site cloning and staging site creation simplified!
91
+
92
+ <h3>I just want to migrate the database from one installation to another</h3>
93
+ If you want to migrate your local database to a already existing production site you can use a tool like WP Migrate DB.
94
+ WP Staging is only for creating a staging site with latest data from your production site. So it goes the opposite way of WP Migrate DB.
95
+ Both tools are excellent cooperating eachother.
96
+
97
+ <h3>What are the benefits compared to a plugin like Duplicator?</h3>
98
+ At first, i love the [Duplicator plugin](https://wordpress.org/plugins/duplicator/ "Duplicator plugin"). Duplicator is a great tool for migrating from development site to production one or from production site to development one.
99
+ The downside is that Duplicator needs adjustments, manually interventions and prerequirements for this. Duplicator also needs some skills to be able to create a development / staging site, where WP Staging does not need more than a click from you.
100
+ However, Duplicator is best placed to be a tool for first-time creation of your production site. This is something where it is very handy and powerful.
101
+
102
+ So, if you have created a local or webhosted development site and you need to migrate this site the first time to your production domain than you are doing nothing wrong with using
103
+ the Duplicator plugin! If you need all you latest production data like posts, updated plugins, theme data and styles in a testing environment than i recommend to use WP Staging instead!
104
+
105
+ = I need you feedback =
106
+ This plugin has been done in hundreds of hours to work on even the smallest shared webhosting package but i am limited in testing this only on a handful of different server so i need your help:
107
+ Please open a [support request](https://wordpress.org/support/plugin/wp-staging/ "support request") and describe your problem exactely. In wp-content/wp-staging/logs you find extended logfiles. Have a look at them and let me know the error-thrown lines.
108
+
109
+
110
+ = Important =
111
+
112
+ Per default the staging site will have permalinks disabled because the staging site will be cloned into a subfolder and regular permalinks are not working
113
+ without doing changes to your .htaccess or nginx.conf.
114
+ In the majority of cases this is abolutely fine for a staging platform and you still will be able to test new plugins and do some theme changes on your staging platform.
115
+ If you need the same permalink stucture on your staging platform as you have in your prodcution website you have to create a custom .htaccess for apache webserver
116
+ or to adjust your nginx.conf.
117
+
118
+
119
+ = How to install and setup? =
120
+ Install it via the admin dashboard and to 'Plugins', click 'Add New' and search the plugins for 'Staging'. Install the plugin with 'Install Now'.
121
+ After installation goto the settings page 'Staging' and do your adjustments there.
122
+
123
+
124
+ == Frequently Asked Questions ==
125
+
126
+
127
+ == Official Site ==
128
+
129
+
130
+ == Installation ==
131
+ 1. Download the file "wp-staging" , unzip and place it in your wp-content/plugins/wp-staging folder. You can alternatively upload and install it via the WordPress plugin backend.
132
+ 2. Activate the plugin through the 'Plugins' menu in WordPress.
133
+ 3. Start Plugins->Staging
134
+
135
+ == Screenshots ==
136
+
137
+ 1. Step 1. Create new WordPress staging site
138
+ 2. Step 2. Scanning your website for files and database tables
139
+ 3. Step 3. Wordpress Staging site creation in progress
140
+ 4. Finish!
141
+
142
+ == Changelog ==
143
+
144
+ = 1.0.0 =
145
+ * Fix: Do not follow symlinks during file copy process
146
+ * Fix: css error
147
+ * Fix: Show "not-compatible" notice only when blog version is higher than plugin tested version.
148
+ * Fix: undefined var $size
149
+ * Fix: Check if $path is null before writing to remaining_files.json
150
+ * Fix: $db_helper undefined message
151
+ * Fix: Skip non utf8 encoded files during copying process
152
+
153
+ = 0.9.9 =
154
+ * Fix: Use back ticks for table names to prevent copy errors when table names are containing hyphens or similar special characters
155
+ * New: Load option to reduce cpu load and to lower the risk of killed ajax calls because of security flooding mechanism (Prevent 405 errors: not allowed)
156
+ * Tweak: Load non minified js file when WPSTG debug mode is enabled
157
+
158
+ = 0.9.8 =
159
+ * New: Tested up to WP 4.4
160
+ * New: New debug mode in settings
161
+ * Tweak: Check if url's in staging's wp-config.php needs a replacement and do so.
162
+ * Fix: Prevent fatal error and end of copying process. Make sure files are writable before trying to copy them
163
+
164
+ = 0.9.7 =
165
+ * Fix: Change backend link to https://wordpress.org/plugins/wp-staging/ when using an outdated version of the plugin
166
+ * New: Tested up to WP 4.3.1
167
+
168
+ = 0.9.6 =
169
+ * New: Show notice when there is not enough disk space for a clone
170
+ * Fix: PHP Error on 32bit systems: "disk_free_space(): Value too large for defined data type"
171
+ * Fix: Copying process of larges files gets interupted sometimes due undefined variable
172
+ * Fix: Define width and height for the system info export formular
173
+ * Fix: Cannot redeclare deleteDirectory()
174
+
175
+ = 0.9.5 =
176
+ * Fix: Option for cloning sites which are moved into a subdirectory was not working on several systems
177
+ * New: WordPress Migration tested up to WP 4.3
178
+
179
+ = 0.9.4 =
180
+ * Fix: Large files are copied partly
181
+ * Fix: js error xhr.statusText not defined
182
+ * New: Option for cloning sites which are moved into a subdirectory. Read more: https://codex.wordpress.org/Giving_WordPress_Its_Own_Directory
183
+ * New: Create an alternative copy method for large files
184
+ * New: Add new author WP-Staging to the readme.txt and to the wordpress repository
185
+
186
+ = 0.9.3 =
187
+ * Fix: Rating container is not shown because of wrong wordpress option name
188
+ * Tweak: Change color of the rating links
189
+
190
+ = 0.9.2 =
191
+ * Fix: A conflict with the plugin WP Migrate DB (Pro)
192
+ * Fix: Limit the staging name to maximum of 16 characters for migration process
193
+
194
+ = 0.9.1 =
195
+ * Fix: Change search and replace function for table wp_options when running migration. This prevented on some sites the moving of serialized theme data
196
+
197
+ = 0.9 =
198
+ * New: Release
199
+
200
+ == Upgrade Notice ==
201
+
202
+ = 0.9.7 =
203
+ 0.9.7 <strong> * New: Tested up to wp 4.3.1</strong>
templates/wpstg.min.css ADDED
File without changes
uninstall.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Uninstall WP-Staging
4
+ *
5
+ * @package WPSTG
6
+ * @subpackage Uninstall
7
+ * @copyright Copyright (c) 2015, René Hermenau
8
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9
+ * @since 0.9.0
10
+ */
11
+
12
+ // Exit if accessed directly
13
+ if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) exit;
14
+
15
+ // Load WPSTG file
16
+ include_once( 'wp-staging.php' );
17
+
18
+ global $wpdb, $wpstg_options;
19
+
20
+ /**
21
+ * Delete all the Plugin Options
22
+ *
23
+ */
24
+ if( wpstg_get_option( 'uninstall_on_delete' ) ) {
25
+ delete_option('wpstg_version_upgraded_from');
26
+ delete_option('wpstg_version');
27
+ delete_option('wpstg_installDate');
28
+ delete_option('wpstg_RatingDiv');
29
+ delete_option('wpstg_firsttime');
30
+ delete_option('wpstg_is_staging_site');
31
+ delete_option('wpstg_hide_beta');
32
+ delete_option('wpstg_settings');
33
+ delete_option( 'wpstg_existing_clones' );
34
+ }
35
+
wp-staging.php ADDED
@@ -0,0 +1,248 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Plugin Name: WP Staging - Create a staging clone site for testing & developing
4
+ * Plugin URI: wordpress.org/plugins/wp-staging
5
+ * Description: WP-Staging - Create a staging clone site for testing & developing
6
+ * Author: WP-Staging, René Hermenau
7
+ * Author URI: https://wordpress.org/plugins/wp-staging
8
+ * Version: 0.9.9
9
+ * Text Domain: wpstg
10
+ * Domain Path: languages
11
+
12
+ *
13
+ * WP-Staging is free software: you can redistribute it and/or modify
14
+ * it under the terms of the GNU General Public License as published by
15
+ * the Free Software Foundation, either version 2 of the License, or
16
+ * any later version.
17
+ *
18
+ * WP-Staging is distributed in the hope that it will be useful,
19
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
+ * GNU General Public License for more details.
22
+ *
23
+ * You should have received a copy of the GNU General Public License
24
+ * along with Staging. If not, see <http://www.gnu.org/licenses/>.
25
+ *
26
+ * @package WPSTG
27
+ * @category Core
28
+ * @author René Hermenau
29
+ * @version 0.9.1
30
+ */
31
+ // Exit if accessed directly
32
+ if (!defined('ABSPATH'))
33
+ exit;
34
+
35
+ // Plugin version
36
+ if (!defined('WPSTG_VERSION')) {
37
+ define('WPSTG_VERSION', '0.9.9');
38
+ }
39
+ // Plugin version
40
+ if (!defined('WPSTG_WP_COMPATIBLE')) {
41
+ define('WPSTG_WP_COMPATIBLE', '4.4.1');
42
+ }
43
+
44
+ if (!class_exists('wpstaging')) :
45
+
46
+ /**
47
+ * Main wpstg Class
48
+ *
49
+ * @since 0.9.0
50
+ */
51
+ final class wpstaging {
52
+ /** Singleton ************************************************************ */
53
+
54
+ /**
55
+ * @var WP-Staging The one and only WP-Staging
56
+ * @since 1.0
57
+ */
58
+ private static $instance;
59
+
60
+ /**
61
+ * WPSTG HTML Element Helper Object
62
+ *
63
+ * @var object
64
+ * @since 2.0.0
65
+ */
66
+ //public $html;
67
+
68
+ /* WPSTG LOGGER Class
69
+ *
70
+ */
71
+ public $logger;
72
+
73
+
74
+
75
+ /**
76
+ * Main WP-Staging Instance
77
+ *
78
+ * Insures that only one instance of wp-staging exists in memory at any one
79
+ * time. Also prevents needing to define globals all over the place.
80
+ *
81
+ * @since 1.0
82
+ * @static
83
+ * @staticvar array $instance
84
+ * @uses wp-staging::setup_constants() Setup the constants needed
85
+ * @uses wp-staging::includes() Include the required files
86
+ * @uses wp-staging::load_textdomain() load the language files
87
+ * @see WPSTG()
88
+ * @return The one true wp-staging
89
+ */
90
+ public static function instance() {
91
+ if (!isset(self::$instance) && !( self::$instance instanceof wpstaging )) {
92
+ self::$instance = new wpstaging;
93
+ self::$instance->setup_constants();
94
+ self::$instance->includes();
95
+ self::$instance->load_textdomain();
96
+ //self::$instance->html = new WPSTG_HTML_Elements();
97
+ self::$instance->logger = new wpstgLogger("wpstglog_" . date("Y-m-d") . ".log", wpstgLogger::INFO);
98
+ }
99
+ return self::$instance;
100
+ }
101
+
102
+ /**
103
+ * Throw error on object clone
104
+ *
105
+ * The whole idea of the singleton design pattern is that there is a single
106
+ * object therefore, we don't want the object to be cloned.
107
+ *
108
+ * @since 1.0
109
+ * @access protected
110
+ * @return void
111
+ */
112
+ public function __clone() {
113
+ // Cloning instances of the class is forbidden
114
+ _doing_it_wrong(__FUNCTION__, __('Cheatin&#8217; huh?', 'WPSTG'), '1.0');
115
+ }
116
+
117
+ /**
118
+ * Disable unserializing of the class
119
+ *
120
+ * @since 1.0
121
+ * @access protected
122
+ * @return void
123
+ */
124
+ public function __wakeup() {
125
+ // Unserializing instances of the class is forbidden
126
+ _doing_it_wrong(__FUNCTION__, __('Cheatin&#8217; huh?', 'WPSTG'), '1.0');
127
+ }
128
+
129
+ /**
130
+ * Setup plugin constants
131
+ *
132
+ * @access private
133
+ * @since 1.0
134
+ * @return void
135
+ */
136
+ private function setup_constants() {
137
+ global $wpdb;
138
+
139
+ // Plugin Folder Path
140
+ if (!defined('WPSTG_PLUGIN_DIR')) {
141
+ define('WPSTG_PLUGIN_DIR', plugin_dir_path(__FILE__));
142
+ }
143
+
144
+ // Plugin Folder URL
145
+ if (!defined('WPSTG_PLUGIN_URL')) {
146
+ define('WPSTG_PLUGIN_URL', plugin_dir_url(__FILE__));
147
+ }
148
+
149
+ // Plugin Root File
150
+ if (!defined('WPSTG_PLUGIN_FILE')) {
151
+ define('WPSTG_PLUGIN_FILE', __FILE__);
152
+ }
153
+
154
+ // Plugin database
155
+ // Plugin Root File
156
+ if (!defined('WPSTG_TABLE')) {
157
+ define('WPSTG_TABLE', $wpdb->prefix . 'wp-staging');
158
+ }
159
+ }
160
+
161
+ /**
162
+ * Include required files
163
+ *
164
+ * @access private
165
+ * @since 1.0
166
+ * @return void
167
+ */
168
+ private function includes() {
169
+ global $wpstg_options;
170
+ require_once WPSTG_PLUGIN_DIR . 'includes/logger.php';
171
+ require_once WPSTG_PLUGIN_DIR . 'includes/staging-functions.php';
172
+ if (is_admin() || ( defined('WP_CLI') && WP_CLI )) {
173
+ require_once WPSTG_PLUGIN_DIR . 'includes/admin/settings/register-settings.php';
174
+ $wpstg_options = wpstg_get_settings(); // Load it on top of all
175
+ require_once WPSTG_PLUGIN_DIR . 'includes/admin/admin-actions.php';
176
+ require_once WPSTG_PLUGIN_DIR . 'includes/admin/admin-notices.php';
177
+ require_once WPSTG_PLUGIN_DIR . 'includes/admin/admin-footer.php';
178
+ require_once WPSTG_PLUGIN_DIR . 'includes/admin/admin-pages.php';
179
+ require_once WPSTG_PLUGIN_DIR . 'includes/admin/plugins.php';
180
+ require_once WPSTG_PLUGIN_DIR . 'includes/admin/welcome.php';
181
+ require_once WPSTG_PLUGIN_DIR . 'includes/admin/settings/display-settings.php';
182
+ require_once WPSTG_PLUGIN_DIR . 'includes/admin/settings/contextual-help.php';
183
+ require_once WPSTG_PLUGIN_DIR . 'includes/install.php';
184
+ require_once WPSTG_PLUGIN_DIR . 'includes/admin/tools.php';
185
+ require_once WPSTG_PLUGIN_DIR . 'includes/admin/upload-functions.php';
186
+ require_once WPSTG_PLUGIN_DIR . 'includes/scripts.php';
187
+ require_once WPSTG_PLUGIN_DIR . 'includes/class-wpstg-license-handler.php';
188
+ require_once WPSTG_PLUGIN_DIR . 'includes/debug/classes/wpstgDebug.interface.php';
189
+ require_once WPSTG_PLUGIN_DIR . 'includes/debug/classes/wpstgDebug.class.php';
190
+ require_once WPSTG_PLUGIN_DIR . 'includes/wpstg-sanitize.php';
191
+ require_once WPSTG_PLUGIN_DIR . 'includes/template-functions.php';
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Loads the plugin language files
197
+ *
198
+ * @access public
199
+ * @since 1.0
200
+ * @return void
201
+ */
202
+ public function load_textdomain() {
203
+ // Set filter for plugin's languages directory
204
+ $wpstg_lang_dir = dirname(plugin_basename(WPSTG_PLUGIN_FILE)) . '/languages/';
205
+ $wpstg_lang_dir = apply_filters('wpstg_languages_directory', $wpstg_lang_dir);
206
+
207
+ // Traditional WordPress plugin locale filter
208
+ $locale = apply_filters('plugin_locale', get_locale(), 'wpstg');
209
+ $mofile = sprintf('%1$s-%2$s.mo', 'wpstg', $locale);
210
+
211
+ // Setup paths to current locale file
212
+ $mofile_local = $wpstg_lang_dir . $mofile;
213
+ $mofile_global = WP_LANG_DIR . '/wpstg/' . $mofile;
214
+
215
+ if (file_exists($mofile_global)) {
216
+ // Look in global /wp-content/languages/WPSTG folder
217
+ load_textdomain('wpstg', $mofile_global);
218
+ } elseif (file_exists($mofile_local)) {
219
+ // Look in local /wp-content/plugins/wp-staging/languages/ folder
220
+ load_textdomain('wpstg', $mofile_local);
221
+ } else {
222
+ // Load the default language files
223
+ load_plugin_textdomain('wpstg', false, $wpstg_lang_dir);
224
+ }
225
+ }
226
+
227
+ }
228
+
229
+ endif; // End if class_exists check
230
+
231
+ /**
232
+ * The main function responsible for returning the one true wpstaging
233
+ * Instance to functions everywhere.
234
+ *
235
+ * Use this function like you would a global variable, except without needing
236
+ * to declare the global.
237
+ *
238
+ * Example: <?php $WPSTG = WPSTG(); ?>
239
+ *
240
+ * @since 0.9.0
241
+ * @return object The one true wpstaging Instance
242
+ */
243
+ function WPSTG() {
244
+ return wpstaging::instance();
245
+ }
246
+
247
+ // Get WPSTG Running
248
+ WPSTG();