Total Upkeep – WordPress Backup Plugin plus Restore & Migrate by BoldGrid - Version 1.6.1

Version Description

Release Date: May 24th, 2018

  • Update: Ran PHPCBF to beautify PHP code.
  • Update: $_POST sanitization
  • Update: Cron system updated to avoid calling core files directly
Download this release

Release Info

Developer boldgrid
Plugin Icon 128x128 Total Upkeep – WordPress Backup Plugin plus Restore & Migrate by BoldGrid
Version 1.6.1
Comparing to
See all releases

Version 1.6.1

Files changed (195) hide show
  1. LICENSE.txt +339 -0
  2. admin/class-boldgrid-backup-admin-archive-actions.php +189 -0
  3. admin/class-boldgrid-backup-admin-archive-browser.php +285 -0
  4. admin/class-boldgrid-backup-admin-archive-details.php +176 -0
  5. admin/class-boldgrid-backup-admin-archive-fail.php +178 -0
  6. admin/class-boldgrid-backup-admin-archive-log.php +199 -0
  7. admin/class-boldgrid-backup-admin-archive.php +359 -0
  8. admin/class-boldgrid-backup-admin-archives-all.php +217 -0
  9. admin/class-boldgrid-backup-admin-archives.php +239 -0
  10. admin/class-boldgrid-backup-admin-auto-rollback.php +1033 -0
  11. admin/class-boldgrid-backup-admin-backup-dir.php +391 -0
  12. admin/class-boldgrid-backup-admin-compressor.php +76 -0
  13. admin/class-boldgrid-backup-admin-compressors.php +133 -0
  14. admin/class-boldgrid-backup-admin-config.php +466 -0
  15. admin/class-boldgrid-backup-admin-core-files.php +98 -0
  16. admin/class-boldgrid-backup-admin-core.php +2722 -0
  17. admin/class-boldgrid-backup-admin-cron.php +994 -0
  18. admin/class-boldgrid-backup-admin-crypt.php +98 -0
  19. admin/class-boldgrid-backup-admin-db-dump.php +199 -0
  20. admin/class-boldgrid-backup-admin-db-get.php +89 -0
  21. admin/class-boldgrid-backup-admin-db-import.php +167 -0
  22. admin/class-boldgrid-backup-admin-db-omit.php +312 -0
  23. admin/class-boldgrid-backup-admin-email.php +191 -0
  24. admin/class-boldgrid-backup-admin-filelist.php +138 -0
  25. admin/class-boldgrid-backup-admin-folder-exclusion.php +492 -0
  26. admin/class-boldgrid-backup-admin-go-pro.php +128 -0
  27. admin/class-boldgrid-backup-admin-home-dir.php +67 -0
  28. admin/class-boldgrid-backup-admin-in-progress.php +274 -0
  29. admin/class-boldgrid-backup-admin-jobs.php +295 -0
  30. admin/class-boldgrid-backup-admin-notice.php +230 -0
  31. admin/class-boldgrid-backup-admin-remote.php +100 -0
  32. admin/class-boldgrid-backup-admin-restore-git.php +45 -0
  33. admin/class-boldgrid-backup-admin-restore-helper.php +296 -0
  34. admin/class-boldgrid-backup-admin-scheduler.php +139 -0
  35. admin/class-boldgrid-backup-admin-settings.php +806 -0
  36. admin/class-boldgrid-backup-admin-test.php +729 -0
  37. admin/class-boldgrid-backup-admin-time.php +412 -0
  38. admin/class-boldgrid-backup-admin-tools.php +54 -0
  39. admin/class-boldgrid-backup-admin-upload.php +366 -0
  40. admin/class-boldgrid-backup-admin-utility.php +866 -0
  41. admin/class-boldgrid-backup-admin-wp-cron.php +360 -0
  42. admin/class-boldgrid-backup-admin-xhprof.php +130 -0
  43. admin/class-boldgrid-backup-admin.php +199 -0
  44. admin/compressor/pcl_zip.php +546 -0
  45. admin/compressor/php_zip.php +212 -0
  46. admin/css/boldgrid-backup-admin-customizer.css +6 -0
  47. admin/css/boldgrid-backup-admin-folder-exclude.css +105 -0
  48. admin/css/boldgrid-backup-admin-ftp-settings.css +8 -0
  49. admin/css/boldgrid-backup-admin-hide-all.css +24 -0
  50. admin/css/boldgrid-backup-admin-home.css +16 -0
  51. admin/css/boldgrid-backup-admin-new-thickbox-style.css +26 -0
  52. admin/css/boldgrid-backup-admin-settings.css +9 -0
  53. admin/css/boldgrid-backup-admin-test.css +46 -0
  54. admin/css/boldgrid-backup-admin-zip-browser.css +32 -0
  55. admin/css/boldgrid-backup-admin.css +129 -0
  56. admin/index.php +2 -0
  57. admin/js/boldgrid-backup-admin-archive-actions.js +164 -0
  58. admin/js/boldgrid-backup-admin-archive-details.js +163 -0
  59. admin/js/boldgrid-backup-admin-backup-now.js +236 -0
  60. admin/js/boldgrid-backup-admin-customizer.js +125 -0
  61. admin/js/boldgrid-backup-admin-folder-exclude.js +413 -0
  62. admin/js/boldgrid-backup-admin-ftp-settings.js +69 -0
  63. admin/js/boldgrid-backup-admin-home.js +196 -0
  64. admin/js/boldgrid-backup-admin-rollback.js +440 -0
  65. admin/js/boldgrid-backup-admin-settings.js +311 -0
  66. admin/js/boldgrid-backup-admin-table-include.js +112 -0
  67. admin/js/boldgrid-backup-admin-update-selectors.js +148 -0
  68. admin/js/boldgrid-backup-admin-zip-browser.js +285 -0
  69. admin/js/boldgrid-backup-admin.js +269 -0
  70. admin/partials/archive-details/browser-entry.php +49 -0
  71. admin/partials/archive-details/browser.php +31 -0
  72. admin/partials/archive-details/db.php +50 -0
  73. admin/partials/archive-details/details.php +95 -0
  74. admin/partials/archive-details/not-found.php +16 -0
  75. admin/partials/archive-details/only-remote.php +32 -0
  76. admin/partials/archive-details/remote-storage.php +80 -0
  77. admin/partials/archives/add-new.php +91 -0
  78. admin/partials/archives/note-pre-backup.php +55 -0
  79. admin/partials/boldgrid-backup-admin-archive-details.php +240 -0
  80. admin/partials/boldgrid-backup-admin-backup-button.php +40 -0
  81. admin/partials/boldgrid-backup-admin-backup-modal.php +36 -0
  82. admin/partials/boldgrid-backup-admin-backup.php +161 -0
  83. admin/partials/boldgrid-backup-admin-home.php +74 -0
  84. admin/partials/boldgrid-backup-admin-mail-restore.php +78 -0
  85. admin/partials/boldgrid-backup-admin-nav.php +60 -0
  86. admin/partials/boldgrid-backup-admin-settings.php +146 -0
  87. admin/partials/boldgrid-backup-admin-test.php +377 -0
  88. admin/partials/boldgrid-backup-admin-tools.php +53 -0
  89. admin/partials/remote/ftp.php +88 -0
  90. admin/partials/settings/auto-updates.php +155 -0
  91. admin/partials/settings/backup-directory.php +57 -0
  92. admin/partials/settings/compressor.php +72 -0
  93. admin/partials/settings/connect-key.php +68 -0
  94. admin/partials/settings/days-of-week.php +52 -0
  95. admin/partials/settings/db.php +83 -0
  96. admin/partials/settings/folders.php +262 -0
  97. admin/partials/settings/notifications.php +56 -0
  98. admin/partials/settings/premium-message.php +57 -0
  99. admin/partials/settings/retention.php +58 -0
  100. admin/partials/settings/scheduler.php +53 -0
  101. admin/partials/settings/storage-location.php +59 -0
  102. admin/partials/settings/storage.php +89 -0
  103. admin/partials/settings/time-of-day.php +79 -0
  104. admin/partials/tools/local-remote.php +130 -0
  105. admin/remote/ftp-hooks.php +287 -0
  106. admin/remote/ftp-page.php +190 -0
  107. admin/remote/ftp.php +858 -0
  108. admin/remote/sftp.php +553 -0
  109. admin/storage/local.php +100 -0
  110. boldgrid-backup-cron.php +72 -0
  111. boldgrid-backup.php +146 -0
  112. cron/run_jobs.php +61 -0
  113. includes/class-boldgrid-backup-activator.php +46 -0
  114. includes/class-boldgrid-backup-deactivator.php +35 -0
  115. includes/class-boldgrid-backup-i18n.php +37 -0
  116. includes/class-boldgrid-backup-loader.php +132 -0
  117. includes/class-boldgrid-backup.php +430 -0
  118. includes/config/.gitignore +1 -0
  119. includes/config/config.plugin.php +43 -0
  120. includes/config/config.sample.php +24 -0
  121. includes/config/index.php +2 -0
  122. includes/index.php +2 -0
  123. index.php +2 -0
  124. languages/boldgrid-backup.pot +0 -0
  125. readme.txt +276 -0
  126. uninstall.php +25 -0
  127. vendor/autoload.php +7 -0
  128. vendor/boldgrid/library/.eslintrc.js +22 -0
  129. vendor/boldgrid/library/.gitignore +7 -0
  130. vendor/boldgrid/library/.prettierrc +6 -0
  131. vendor/boldgrid/library/.travis.yml +54 -0
  132. vendor/boldgrid/library/LICENSE +339 -0
  133. vendor/boldgrid/library/README.md +73 -0
  134. vendor/boldgrid/library/bin/install-wp-tests.sh +78 -0
  135. vendor/boldgrid/library/composer.json +33 -0
  136. vendor/boldgrid/library/package-lock.json +1907 -0
  137. vendor/boldgrid/library/package.json +13 -0
  138. vendor/boldgrid/library/phpunit.xml +19 -0
  139. vendor/boldgrid/library/src/Library/Api/Availability.php +118 -0
  140. vendor/boldgrid/library/src/Library/Api/Call.php +270 -0
  141. vendor/boldgrid/library/src/Library/Configs.php +95 -0
  142. vendor/boldgrid/library/src/Library/Filter.php +186 -0
  143. vendor/boldgrid/library/src/Library/Key.php +303 -0
  144. vendor/boldgrid/library/src/Library/Key/Validate.php +169 -0
  145. vendor/boldgrid/library/src/Library/License.php +373 -0
  146. vendor/boldgrid/library/src/Library/Notice.php +235 -0
  147. vendor/boldgrid/library/src/Library/Notice/ClaimPremiumKey.php +190 -0
  148. vendor/boldgrid/library/src/Library/Notice/KeyPrompt.php +244 -0
  149. vendor/boldgrid/library/src/Library/Plugin/Checker.php +141 -0
  150. vendor/boldgrid/library/src/Library/Registration.php +74 -0
  151. vendor/boldgrid/library/src/Library/ReleaseChannel.php +141 -0
  152. vendor/boldgrid/library/src/Library/Reseller.php +63 -0
  153. vendor/boldgrid/library/src/Library/Start.php +128 -0
  154. vendor/boldgrid/library/src/Library/Ui.php +183 -0
  155. vendor/boldgrid/library/src/Library/Update.php +62 -0
  156. vendor/boldgrid/library/src/Library/Util/Plugin.php +71 -0
  157. vendor/boldgrid/library/src/Library/Views/ClaimPremiumKey.php +19 -0
  158. vendor/boldgrid/library/src/Library/Views/ConnectionIssue.php +15 -0
  159. vendor/boldgrid/library/src/Library/Views/InvalidLicense.php +5 -0
  160. vendor/boldgrid/library/src/Library/Views/KeyPrompt.php +103 -0
  161. vendor/boldgrid/library/src/Util/Load.php +212 -0
  162. vendor/boldgrid/library/src/Util/Option.php +124 -0
  163. vendor/boldgrid/library/src/Util/Plugin.php +52 -0
  164. vendor/boldgrid/library/src/Util/Registration.php +114 -0
  165. vendor/boldgrid/library/src/Util/Registration/Plugin.php +38 -0
  166. vendor/boldgrid/library/src/Util/Registration/RegistrationInterface.php +57 -0
  167. vendor/boldgrid/library/src/Util/Registration/Theme.php +38 -0
  168. vendor/boldgrid/library/src/Util/Version.php +107 -0
  169. vendor/boldgrid/library/src/assets/css/api-notice.css +98 -0
  170. vendor/boldgrid/library/src/assets/css/ui.css +133 -0
  171. vendor/boldgrid/library/src/assets/js/api-notice.js +231 -0
  172. vendor/boldgrid/library/src/assets/js/license.js +35 -0
  173. vendor/boldgrid/library/src/assets/js/notice.js +33 -0
  174. vendor/boldgrid/library/src/assets/js/sticky.js +288 -0
  175. vendor/boldgrid/library/src/assets/js/ui.js +68 -0
  176. vendor/boldgrid/library/src/library.global.php +82 -0
  177. vendor/boldgrid/library/tests/Library/Util/test-plugin.php +42 -0
  178. vendor/boldgrid/library/tests/Util/test-option.php +38 -0
  179. vendor/boldgrid/library/tests/bootstrap.php +19 -0
  180. vendor/boldgrid/library/tests/test-reseller.php +45 -0
  181. vendor/boldgrid/library/yarn.lock +1260 -0
  182. vendor/cbschuld/browser.php +1 -0
  183. vendor/composer/ClassLoader.php +445 -0
  184. vendor/composer/LICENSE +21 -0
  185. vendor/composer/autoload_classmap.php +9 -0
  186. vendor/composer/autoload_files.php +11 -0
  187. vendor/composer/autoload_namespaces.php +9 -0
  188. vendor/composer/autoload_psr4.php +12 -0
  189. vendor/composer/autoload_real.php +70 -0
  190. vendor/composer/autoload_static.php +52 -0
  191. vendor/composer/installed.json +248 -0
  192. vendor/ifsnop/mysqldump-php/.gitignore +9 -0
  193. vendor/ifsnop/mysqldump-php/.scrutinizer.yml +43 -0
  194. vendor/ifsnop/mysqldump-php/.travis.yml +49 -0
  195. vendor/ifsnop/mysqldump-php/LICENSE +629 -0
LICENSE.txt ADDED
@@ -0,0 +1,339 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 2, June 1991
3
+
4
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6
+ Everyone is permitted to copy and distribute verbatim copies
7
+ of this license document, but changing it is not allowed.
8
+
9
+ Preamble
10
+
11
+ The licenses for most software are designed to take away your
12
+ freedom to share and change it. By contrast, the GNU General Public
13
+ License is intended to guarantee your freedom to share and change free
14
+ software--to make sure the software is free for all its users. This
15
+ General Public License applies to most of the Free Software
16
+ Foundation's software and to any other program whose authors commit to
17
+ using it. (Some other Free Software Foundation software is covered by
18
+ the GNU Lesser General Public License instead.) You can apply it to
19
+ your programs, too.
20
+
21
+ When we speak of free software, we are referring to freedom, not
22
+ price. Our General Public Licenses are designed to make sure that you
23
+ have the freedom to distribute copies of free software (and charge for
24
+ this service if you wish), that you receive source code or can get it
25
+ if you want it, that you can change the software or use pieces of it
26
+ in new free programs; and that you know you can do these things.
27
+
28
+ To protect your rights, we need to make restrictions that forbid
29
+ anyone to deny you these rights or to ask you to surrender the rights.
30
+ These restrictions translate to certain responsibilities for you if you
31
+ distribute copies of the software, or if you modify it.
32
+
33
+ For example, if you distribute copies of such a program, whether
34
+ gratis or for a fee, you must give the recipients all the rights that
35
+ you have. You must make sure that they, too, receive or can get the
36
+ source code. And you must show them these terms so they know their
37
+ rights.
38
+
39
+ We protect your rights with two steps: (1) copyright the software, and
40
+ (2) offer you this license which gives you legal permission to copy,
41
+ distribute and/or modify the software.
42
+
43
+ Also, for each author's protection and ours, we want to make certain
44
+ that everyone understands that there is no warranty for this free
45
+ software. If the software is modified by someone else and passed on, we
46
+ want its recipients to know that what they have is not the original, so
47
+ that any problems introduced by others will not reflect on the original
48
+ authors' reputations.
49
+
50
+ Finally, any free program is threatened constantly by software
51
+ patents. We wish to avoid the danger that redistributors of a free
52
+ program will individually obtain patent licenses, in effect making the
53
+ program proprietary. To prevent this, we have made it clear that any
54
+ patent must be licensed for everyone's free use or not licensed at all.
55
+
56
+ The precise terms and conditions for copying, distribution and
57
+ modification follow.
58
+
59
+ GNU GENERAL PUBLIC LICENSE
60
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61
+
62
+ 0. This License applies to any program or other work which contains
63
+ a notice placed by the copyright holder saying it may be distributed
64
+ under the terms of this General Public License. The "Program", below,
65
+ refers to any such program or work, and a "work based on the Program"
66
+ means either the Program or any derivative work under copyright law:
67
+ that is to say, a work containing the Program or a portion of it,
68
+ either verbatim or with modifications and/or translated into another
69
+ language. (Hereinafter, translation is included without limitation in
70
+ the term "modification".) Each licensee is addressed as "you".
71
+
72
+ Activities other than copying, distribution and modification are not
73
+ covered by this License; they are outside its scope. The act of
74
+ running the Program is not restricted, and the output from the Program
75
+ is covered only if its contents constitute a work based on the
76
+ Program (independent of having been made by running the Program).
77
+ Whether that is true depends on what the Program does.
78
+
79
+ 1. You may copy and distribute verbatim copies of the Program's
80
+ source code as you receive it, in any medium, provided that you
81
+ conspicuously and appropriately publish on each copy an appropriate
82
+ copyright notice and disclaimer of warranty; keep intact all the
83
+ notices that refer to this License and to the absence of any warranty;
84
+ and give any other recipients of the Program a copy of this License
85
+ along with the Program.
86
+
87
+ You may charge a fee for the physical act of transferring a copy, and
88
+ you may at your option offer warranty protection in exchange for a fee.
89
+
90
+ 2. You may modify your copy or copies of the Program or any portion
91
+ of it, thus forming a work based on the Program, and copy and
92
+ distribute such modifications or work under the terms of Section 1
93
+ above, provided that you also meet all of these conditions:
94
+
95
+ a) You must cause the modified files to carry prominent notices
96
+ stating that you changed the files and the date of any change.
97
+
98
+ b) You must cause any work that you distribute or publish, that in
99
+ whole or in part contains or is derived from the Program or any
100
+ part thereof, to be licensed as a whole at no charge to all third
101
+ parties under the terms of this License.
102
+
103
+ c) If the modified program normally reads commands interactively
104
+ when run, you must cause it, when started running for such
105
+ interactive use in the most ordinary way, to print or display an
106
+ announcement including an appropriate copyright notice and a
107
+ notice that there is no warranty (or else, saying that you provide
108
+ a warranty) and that users may redistribute the program under
109
+ these conditions, and telling the user how to view a copy of this
110
+ License. (Exception: if the Program itself is interactive but
111
+ does not normally print such an announcement, your work based on
112
+ the Program is not required to print an announcement.)
113
+
114
+ These requirements apply to the modified work as a whole. If
115
+ identifiable sections of that work are not derived from the Program,
116
+ and can be reasonably considered independent and separate works in
117
+ themselves, then this License, and its terms, do not apply to those
118
+ sections when you distribute them as separate works. But when you
119
+ distribute the same sections as part of a whole which is a work based
120
+ on the Program, the distribution of the whole must be on the terms of
121
+ this License, whose permissions for other licensees extend to the
122
+ entire whole, and thus to each and every part regardless of who wrote it.
123
+
124
+ Thus, it is not the intent of this section to claim rights or contest
125
+ your rights to work written entirely by you; rather, the intent is to
126
+ exercise the right to control the distribution of derivative or
127
+ collective works based on the Program.
128
+
129
+ In addition, mere aggregation of another work not based on the Program
130
+ with the Program (or with a work based on the Program) on a volume of
131
+ a storage or distribution medium does not bring the other work under
132
+ the scope of this License.
133
+
134
+ 3. You may copy and distribute the Program (or a work based on it,
135
+ under Section 2) in object code or executable form under the terms of
136
+ Sections 1 and 2 above provided that you also do one of the following:
137
+
138
+ a) Accompany it with the complete corresponding machine-readable
139
+ source code, which must be distributed under the terms of Sections
140
+ 1 and 2 above on a medium customarily used for software interchange; or,
141
+
142
+ b) Accompany it with a written offer, valid for at least three
143
+ years, to give any third party, for a charge no more than your
144
+ cost of physically performing source distribution, a complete
145
+ machine-readable copy of the corresponding source code, to be
146
+ distributed under the terms of Sections 1 and 2 above on a medium
147
+ customarily used for software interchange; or,
148
+
149
+ c) Accompany it with the information you received as to the offer
150
+ to distribute corresponding source code. (This alternative is
151
+ allowed only for noncommercial distribution and only if you
152
+ received the program in object code or executable form with such
153
+ an offer, in accord with Subsection b above.)
154
+
155
+ The source code for a work means the preferred form of the work for
156
+ making modifications to it. For an executable work, complete source
157
+ code means all the source code for all modules it contains, plus any
158
+ associated interface definition files, plus the scripts used to
159
+ control compilation and installation of the executable. However, as a
160
+ special exception, the source code distributed need not include
161
+ anything that is normally distributed (in either source or binary
162
+ form) with the major components (compiler, kernel, and so on) of the
163
+ operating system on which the executable runs, unless that component
164
+ itself accompanies the executable.
165
+
166
+ If distribution of executable or object code is made by offering
167
+ access to copy from a designated place, then offering equivalent
168
+ access to copy the source code from the same place counts as
169
+ distribution of the source code, even though third parties are not
170
+ compelled to copy the source along with the object code.
171
+
172
+ 4. You may not copy, modify, sublicense, or distribute the Program
173
+ except as expressly provided under this License. Any attempt
174
+ otherwise to copy, modify, sublicense or distribute the Program is
175
+ void, and will automatically terminate your rights under this License.
176
+ However, parties who have received copies, or rights, from you under
177
+ this License will not have their licenses terminated so long as such
178
+ parties remain in full compliance.
179
+
180
+ 5. You are not required to accept this License, since you have not
181
+ signed it. However, nothing else grants you permission to modify or
182
+ distribute the Program or its derivative works. These actions are
183
+ prohibited by law if you do not accept this License. Therefore, by
184
+ modifying or distributing the Program (or any work based on the
185
+ Program), you indicate your acceptance of this License to do so, and
186
+ all its terms and conditions for copying, distributing or modifying
187
+ the Program or works based on it.
188
+
189
+ 6. Each time you redistribute the Program (or any work based on the
190
+ Program), the recipient automatically receives a license from the
191
+ original licensor to copy, distribute or modify the Program subject to
192
+ these terms and conditions. You may not impose any further
193
+ restrictions on the recipients' exercise of the rights granted herein.
194
+ You are not responsible for enforcing compliance by third parties to
195
+ this License.
196
+
197
+ 7. If, as a consequence of a court judgment or allegation of patent
198
+ infringement or for any other reason (not limited to patent issues),
199
+ conditions are imposed on you (whether by court order, agreement or
200
+ otherwise) that contradict the conditions of this License, they do not
201
+ excuse you from the conditions of this License. If you cannot
202
+ distribute so as to satisfy simultaneously your obligations under this
203
+ License and any other pertinent obligations, then as a consequence you
204
+ may not distribute the Program at all. For example, if a patent
205
+ license would not permit royalty-free redistribution of the Program by
206
+ all those who receive copies directly or indirectly through you, then
207
+ the only way you could satisfy both it and this License would be to
208
+ refrain entirely from distribution of the Program.
209
+
210
+ If any portion of this section is held invalid or unenforceable under
211
+ any particular circumstance, the balance of the section is intended to
212
+ apply and the section as a whole is intended to apply in other
213
+ circumstances.
214
+
215
+ It is not the purpose of this section to induce you to infringe any
216
+ patents or other property right claims or to contest validity of any
217
+ such claims; this section has the sole purpose of protecting the
218
+ integrity of the free software distribution system, which is
219
+ implemented by public license practices. Many people have made
220
+ generous contributions to the wide range of software distributed
221
+ through that system in reliance on consistent application of that
222
+ system; it is up to the author/donor to decide if he or she is willing
223
+ to distribute software through any other system and a licensee cannot
224
+ impose that choice.
225
+
226
+ This section is intended to make thoroughly clear what is believed to
227
+ be a consequence of the rest of this License.
228
+
229
+ 8. If the distribution and/or use of the Program is restricted in
230
+ certain countries either by patents or by copyrighted interfaces, the
231
+ original copyright holder who places the Program under this License
232
+ may add an explicit geographical distribution limitation excluding
233
+ those countries, so that distribution is permitted only in or among
234
+ countries not thus excluded. In such case, this License incorporates
235
+ the limitation as if written in the body of this License.
236
+
237
+ 9. The Free Software Foundation may publish revised and/or new versions
238
+ of the General Public License from time to time. Such new versions will
239
+ be similar in spirit to the present version, but may differ in detail to
240
+ address new problems or concerns.
241
+
242
+ Each version is given a distinguishing version number. If the Program
243
+ specifies a version number of this License which applies to it and "any
244
+ later version", you have the option of following the terms and conditions
245
+ either of that version or of any later version published by the Free
246
+ Software Foundation. If the Program does not specify a version number of
247
+ this License, you may choose any version ever published by the Free Software
248
+ Foundation.
249
+
250
+ 10. If you wish to incorporate parts of the Program into other free
251
+ programs whose distribution conditions are different, write to the author
252
+ to ask for permission. For software which is copyrighted by the Free
253
+ Software Foundation, write to the Free Software Foundation; we sometimes
254
+ make exceptions for this. Our decision will be guided by the two goals
255
+ of preserving the free status of all derivatives of our free software and
256
+ of promoting the sharing and reuse of software generally.
257
+
258
+ NO WARRANTY
259
+
260
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261
+ FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263
+ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264
+ OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266
+ TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267
+ PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268
+ REPAIR OR CORRECTION.
269
+
270
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272
+ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273
+ INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274
+ OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275
+ TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276
+ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277
+ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278
+ POSSIBILITY OF SUCH DAMAGES.
279
+
280
+ END OF TERMS AND CONDITIONS
281
+
282
+ How to Apply These Terms to Your New Programs
283
+
284
+ If you develop a new program, and you want it to be of the greatest
285
+ possible use to the public, the best way to achieve this is to make it
286
+ free software which everyone can redistribute and change under these terms.
287
+
288
+ To do so, attach the following notices to the program. It is safest
289
+ to attach them to the start of each source file to most effectively
290
+ convey the exclusion of warranty; and each file should have at least
291
+ the "copyright" line and a pointer to where the full notice is found.
292
+
293
+ <one line to give the program's name and a brief idea of what it does.>
294
+ Copyright (C) <year> <name of author>
295
+
296
+ This program is free software; you can redistribute it and/or modify
297
+ it under the terms of the GNU General Public License as published by
298
+ the Free Software Foundation; either version 2 of the License, or
299
+ (at your option) any later version.
300
+
301
+ This program is distributed in the hope that it will be useful,
302
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
303
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304
+ GNU General Public License for more details.
305
+
306
+ You should have received a copy of the GNU General Public License along
307
+ with this program; if not, write to the Free Software Foundation, Inc.,
308
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309
+
310
+ Also add information on how to contact you by electronic and paper mail.
311
+
312
+ If the program is interactive, make it output a short notice like this
313
+ when it starts in an interactive mode:
314
+
315
+ Gnomovision version 69, Copyright (C) year name of author
316
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317
+ This is free software, and you are welcome to redistribute it
318
+ under certain conditions; type `show c' for details.
319
+
320
+ The hypothetical commands `show w' and `show c' should show the appropriate
321
+ parts of the General Public License. Of course, the commands you use may
322
+ be called something other than `show w' and `show c'; they could even be
323
+ mouse-clicks or menu items--whatever suits your program.
324
+
325
+ You should also get your employer (if you work as a programmer) or your
326
+ school, if any, to sign a "copyright disclaimer" for the program, if
327
+ necessary. Here is a sample; alter the names:
328
+
329
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
331
+
332
+ <signature of Ty Coon>, 1 April 1989
333
+ Ty Coon, President of Vice
334
+
335
+ This General Public License does not permit incorporating your program into
336
+ proprietary programs. If your program is a subroutine library, you may
337
+ consider it more useful to permit linking proprietary applications with the
338
+ library. If this is what you want to do, use the GNU Lesser General
339
+ Public License instead of this License.
admin/class-boldgrid-backup-admin-archive-actions.php ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Archive Actions class.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.4
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup Admin Archive Actions Class.
17
+ *
18
+ * @since 1.5.4
19
+ */
20
+ class Boldgrid_Backup_Admin_Archive_Actions {
21
+
22
+ /**
23
+ * The core class object.
24
+ *
25
+ * @since 1.5.4
26
+ * @access private
27
+ * @var Boldgrid_Backup_Admin_Core
28
+ */
29
+ private $core;
30
+
31
+ /**
32
+ * Constructor.
33
+ *
34
+ * @since 1.5.4
35
+ *
36
+ * @param Boldgrid_Backup_Admin_Core $core Core class object.
37
+ */
38
+ public function __construct( $core ) {
39
+ $this->core = $core;
40
+ }
41
+
42
+ /**
43
+ * Enqueue scripts.
44
+ *
45
+ * @since 1.5.4
46
+ */
47
+ public function enqueue_scripts() {
48
+ $access_type = get_filesystem_method();
49
+ $archive_nonce = wp_create_nonce( 'archive_auth' );
50
+ $delete_confirm_text = esc_html__(
51
+ 'Please confirm the deletion of the archive file:' . PHP_EOL,
52
+ 'boldgrid-backup'
53
+ );
54
+ $restore_confirm_text = esc_html__(
55
+ 'Please confirm the restoration of this WordPress installation from the archive file:' .
56
+ PHP_EOL . '"%s"' . PHP_EOL . PHP_EOL .
57
+ 'Please be aware that you may get logged-out if your session token does not exist in the database restored.',
58
+ 'boldgrid-backup'
59
+ );
60
+
61
+ $handle = 'boldgrid-backup-admin-archive-actions';
62
+ wp_register_script( $handle,
63
+ plugin_dir_url( __FILE__ ) . 'js/' . $handle . '.js',
64
+ array( 'jquery' ),
65
+ BOLDGRID_BACKUP_VERSION,
66
+ false
67
+ );
68
+ $translation = array(
69
+ 'accessType' => $access_type,
70
+ 'archiveNonce' => $archive_nonce,
71
+ 'deleteConfirmText' => $delete_confirm_text,
72
+ 'restoreConfirmText' => $restore_confirm_text,
73
+ );
74
+ wp_localize_script( $handle, 'BoldGridBackupAdminArchiveActions', $translation );
75
+ wp_enqueue_script( $handle );
76
+ }
77
+
78
+ /**
79
+ * Return a link to delete an archive.
80
+ *
81
+ * @since 1.5.4
82
+ *
83
+ * @param string $filename
84
+ * @return string
85
+ */
86
+ public function get_delete_link( $filename ) {
87
+ $archive = $this->core->archive->get_by_name( $filename );
88
+
89
+ if ( empty( $archive ) ) {
90
+ $link = '';
91
+ } else {
92
+ $link = sprintf( '
93
+ <form method="post" id="delete-action" >
94
+ <input type="hidden" name="delete_now" value="1" />
95
+ <input type="hidden" name="archive_key" value="%2$s" />
96
+ <input type="hidden" name="archive_filename" value="%3$s" />
97
+ %4$s
98
+ <a href="" class="submitdelete" data-key="%2$s" data-filename="%3$s">%5$s</a>
99
+ <span class="spinner"></span>
100
+ </form>',
101
+ /* 1 */ get_admin_url( null, 'admin.php?page=boldgrid-backup-archive-details' ),
102
+ /* 2 */ $archive['key'],
103
+ /* 3 */ $archive['filename'],
104
+ /* 4 */ wp_nonce_field( 'archive_auth', 'archive_auth', true, false ),
105
+ /* 5 */ __( 'Delete backup', 'boldgrid-backup' )
106
+ );
107
+ }
108
+
109
+ return $link;
110
+ }
111
+
112
+ /**
113
+ * Return a link to download an archive.
114
+ *
115
+ * @since 1.5.4
116
+ *
117
+ * @param string $filename
118
+ * @return string
119
+ */
120
+ public function get_download_button( $filename ) {
121
+ $archive = $this->core->archive->get_by_name( $filename );
122
+
123
+ if ( empty( $archive ) ) {
124
+ $button = '';
125
+ } else {
126
+ $button = sprintf( '
127
+ <a
128
+ id="backup-archive-download-%1$s"
129
+ class="button button-primary action-download"
130
+ href="#"
131
+ data-key="%1$s"
132
+ data-filepath="%2$s"
133
+ data-filename="%3$s">
134
+ %4$s
135
+ </a>',
136
+ /* 1 */ $archive['key'],
137
+ /* 2 */ $archive['filepath'],
138
+ /* 3 */ $archive['filename'],
139
+ /* 4 */ __( 'Download to Local Machine', 'boldgrid-backup' )
140
+ );
141
+ }
142
+
143
+ return $button;
144
+ }
145
+
146
+ /**
147
+ * Return a link to restore an archive.
148
+ *
149
+ * @since 1.5.4
150
+ *
151
+ * @param string $filename
152
+ * @param array $args
153
+ * @return string
154
+ */
155
+ public function get_restore_button( $filename, $args = array() ) {
156
+ $defaults = array(
157
+ 'button_text' => __( 'Restore' ),
158
+ );
159
+
160
+ $args = wp_parse_args( $args, $defaults );
161
+
162
+ $archive = $this->core->archive->get_by_name( $filename );
163
+
164
+ if ( empty( $archive ) ) {
165
+ $button = '';
166
+ } else {
167
+ $button = sprintf('
168
+ <a
169
+ data-restore-now="1"
170
+ data-archive-key="%2$s"
171
+ data-archive-filename="%3$s"
172
+ data-nonce="%4$s"
173
+ class="button restore-now"
174
+ href="">
175
+ %5$s
176
+ </a>
177
+ %6$s',
178
+ /* 1 */ get_admin_url( null, 'admin.php?page=boldgrid-backup' ),
179
+ /* 2 */ $archive['key'],
180
+ /* 3 */ $filename,
181
+ /* 4 */ wp_create_nonce( 'boldgrid_backup_restore_archive' ),
182
+ /* 5 */ $args['button_text'],
183
+ /* 6 */ $this->core->lang['spinner']
184
+ );
185
+ }
186
+
187
+ return $button;
188
+ }
189
+ }
admin/class-boldgrid-backup-admin-archive-browser.php ADDED
@@ -0,0 +1,285 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Archive Browser class.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.2
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup Admin Archive Browser Class.
17
+ *
18
+ * @since 1.5.2
19
+ */
20
+ class Boldgrid_Backup_Admin_Archive_Browser {
21
+
22
+ /**
23
+ * The core class object.
24
+ *
25
+ * @since 1.5.2
26
+ * @access private
27
+ * @var Boldgrid_Backup_Admin_Core
28
+ */
29
+ private $core;
30
+
31
+ /**
32
+ * Constructor.
33
+ *
34
+ * @since 1.5.2
35
+ *
36
+ * @param Boldgrid_Backup_Admin_Core $core Core class object.
37
+ */
38
+ public function __construct( $core ) {
39
+ $this->core = $core;
40
+ }
41
+
42
+ /**
43
+ * Authorize an ajax request.
44
+ *
45
+ * Many of the ajax handlers in this method require the same
46
+ * current_user_can() and check_ajax_referer() checks.
47
+ *
48
+ * @since 1.5.4
49
+ */
50
+ public function authorize() {
51
+ if ( ! current_user_can( 'update_plugins' ) ) {
52
+ wp_send_json_error( __( 'Permission denied.', 'boldgrid-backup' ) );
53
+ }
54
+
55
+ if ( ! check_ajax_referer( 'boldgrid_backup_remote_storage_upload', 'security', false ) ) {
56
+ wp_send_json_error( __( 'Invalid nonce.', 'boldgrid-backup' ) );
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Render and return html markup for a .sql file.
62
+ *
63
+ * When a user clicks to "View details" of a database dump, this
64
+ * method will create a table showing all the tables in that backup.
65
+ *
66
+ * @since 1.5.4
67
+ *
68
+ * @param string $filepath Zip file.
69
+ * @param string $file Sql file name.
70
+ * @return string
71
+ */
72
+ public function get_sql_details( $filepath, $file ) {
73
+ $tables_with_records = $this->core->db_dump->get_insert_count( $filepath, $file );
74
+ $prefixed_tables = $this->core->db_get->prefixed_count();
75
+
76
+ $in_backup = __( '# Records in this backup', 'boldgrid-backup' );
77
+ $in_current = __( '# Records in current database', 'boldgrid-backup' );
78
+
79
+ $return = sprintf( '
80
+ <table class="wp-list-table fixed striped widefat">
81
+ <thead>
82
+ <tr>
83
+ <th>Table</th>
84
+ <th>%1$s</th>
85
+ <th class="bulk-action-notice">
86
+ %2$s
87
+ <span class="toggle-indicator"></span>
88
+ </th>
89
+ </tr>
90
+ </thead>
91
+ <tbody>',
92
+ /* 1 */ $in_backup,
93
+ /* 2 */ $in_current
94
+ );
95
+
96
+ foreach ( $prefixed_tables as $table => $record_count ) {
97
+ $return .= sprintf(
98
+ '<tr>
99
+ <td>%1$s</td>
100
+ <td>%2$s</td>
101
+ <td>%3$s</td>
102
+ </tr>',
103
+ esc_html( $table ),
104
+ isset( $tables_with_records[ $table ] ) ? $tables_with_records[ $table ] : '0',
105
+ esc_html( $record_count )
106
+ );
107
+ }
108
+
109
+ $return .= '</tbody></table>';
110
+
111
+ return $return;
112
+ }
113
+
114
+ /**
115
+ * Allow the user to browse an archive file.
116
+ *
117
+ * Returns a formatted table to the browser.
118
+ *
119
+ * @since 1.5.3
120
+ */
121
+ public function wp_ajax_browse_archive() {
122
+ $error = __( 'Unable to get contents of archive file:', 'boldgrid-backup' );
123
+
124
+ $this->authorize();
125
+
126
+ $filename = ! empty( $_POST['filename'] ) ? sanitize_file_name( $_POST['filename'] ) : false;
127
+ $filepath = $this->core->backup_dir->get_path_to( $filename );
128
+ if ( empty( $filename ) || ! $this->core->wp_filesystem->exists( $filepath ) ) {
129
+ wp_send_json_error( $error . ' ' . __( 'Invalid archive filename.', 'boldgrid-backup' ) );
130
+ }
131
+
132
+ $this->core->archive->init( $filepath );
133
+
134
+ $dump_file = $this->core->get_dump_file( $filepath );
135
+
136
+ // An array of files not to show in the archive browser.
137
+ $no_show = array(
138
+ /*
139
+ * If this is our database dump file, skip over it. We have another
140
+ * section of the archive details page that will help with restoring
141
+ * a dump file.
142
+ */
143
+ basename( $dump_file ),
144
+ basename( $this->core->archive->log_filepath ),
145
+ );
146
+
147
+ $dir = ! empty( $_POST['dir'] ) ? trim( strip_tags( $_POST['dir'] ) ) : null;
148
+
149
+ $zip = new Boldgrid_Backup_Admin_Compressor_Pcl_Zip( $this->core );
150
+
151
+ $contents = $zip->browse( $filepath, $dir );
152
+
153
+ $tr = '';
154
+ $empty_directory = '<tr><td colspan="3">' . __( 'Empty directory', 'boldgrid-backup' ) . '</td></tr>';
155
+
156
+ $table = sprintf(
157
+ '<table class="wp-list-table fixed striped remote-storage widefat">
158
+ <thead>
159
+ <tr>
160
+ <th>%1$s</th>
161
+ <th>%2$s</th>
162
+ <th class="bulk-action-notice">
163
+ %3$s
164
+ <span class="toggle-indicator"></span>
165
+ </th>
166
+ </tr>
167
+ </thead>
168
+ <tbody>
169
+ ',
170
+ __( 'Name', 'boldgrid-backup' ),
171
+ __( 'Size', 'boldgrid-backup' ),
172
+ __( 'Last Modified', 'boldgrid-backup' )
173
+ );
174
+
175
+ foreach ( $contents as $file ) {
176
+ if ( in_array( basename( $file['filename'] ), $no_show, true ) ) {
177
+ continue;
178
+ }
179
+
180
+ $tr .= include BOLDGRID_BACKUP_PATH . '/admin/partials/archive-details/browser-entry.php';
181
+ }
182
+
183
+ $table .= empty( $tr ) ? $empty_directory : $tr;
184
+
185
+ $table .= '</tbody></table>';
186
+
187
+ wp_send_json_success( $table );
188
+ }
189
+
190
+ /**
191
+ * Show available actions for a single file.
192
+ *
193
+ * When the user clicks on a single file in a backup archive, show them
194
+ * what options they have available.
195
+ *
196
+ * @since 1.5.3
197
+ */
198
+ public function wp_ajax_file_actions() {
199
+ $this->authorize();
200
+
201
+ $filename = ! empty( $_POST['filename'] ) ? sanitize_file_name( $_POST['filename'] ) : false;
202
+ $filepath = $this->core->backup_dir->get_path_to( $filename );
203
+ $file = ! empty( $_POST['file'] ) ? trim( strip_tags( $_POST['file'] ) ) : false;
204
+ if ( empty( $filepath ) || empty( $file ) ) {
205
+ wp_send_json_error( __( 'Invalid file / filepath.', 'boldgrid-backup' ) );
206
+ }
207
+
208
+ // Here's the default message.
209
+ $upgrade_message = __( 'With BoldGrid Backup Premium, you can view and restore files from here.', 'boldgrid-backup' );
210
+
211
+ /**
212
+ * Allow other plugins to add functionality.
213
+ *
214
+ * @since 1.5.3
215
+ *
216
+ * @param string $upgrade_message
217
+ * @param string $file Example: wp-admin/import.php
218
+ */
219
+ $upgrade_message = apply_filters( 'boldgrid_backup_file_actions', $upgrade_message, $file );
220
+
221
+ wp_send_json_success( $upgrade_message );
222
+ }
223
+
224
+ /**
225
+ * Restore a database dump.
226
+ *
227
+ * This handles an ajax call for restoring a dump from the archive details
228
+ * page.
229
+ *
230
+ * @since 1.5.4
231
+ */
232
+ public function wp_ajax_restore_db() {
233
+ $this->authorize();
234
+
235
+ $filename = ! empty( $_POST['filename'] ) ? sanitize_file_name( $_POST['filename'] ) : false;
236
+ $filepath = $this->core->backup_dir->get_path_to( $filename );
237
+ $file = ! empty( $_POST['file'] ) ? trim( strip_tags( $_POST['file'] ) ) : false;
238
+ if ( empty( $filepath ) || empty( $file ) ) {
239
+ wp_send_json_error( __( 'Invalid file / filepath.', 'boldgrid-backup' ) );
240
+ }
241
+
242
+ $importer = new Boldgrid_Backup_Admin_Db_Import( $this->core );
243
+ $success = $importer->import_from_archive( $filepath, $file );
244
+
245
+ if ( ! $success ) {
246
+ $this->core->notice->add_user_notice(
247
+ sprintf( __( 'Error, unable to import database %1$s from %2$s.', 'boldgrid-backup' ), $file, $filepath ),
248
+ $this->core->notice->lang['dis_error']
249
+ );
250
+ } else {
251
+ $this->core->notice->add_user_notice(
252
+ sprintf( __( 'Success! Database %1$s imported from %2$s.', 'boldgrid-backup' ), $file, $filepath ),
253
+ $this->core->notice->lang['dis_success']
254
+ );
255
+ }
256
+ }
257
+
258
+ /**
259
+ * View the details of a database.
260
+ *
261
+ * This method handles the ajax call of "View details" for a database on the
262
+ * archive details page.
263
+ *
264
+ * @since 1.5.4
265
+ */
266
+ public function wp_ajax_view_db() {
267
+ $this->authorize();
268
+
269
+ $filename = ! empty( $_POST['filename'] ) ? sanitize_file_name( $_POST['filename'] ) : false;
270
+ $filepath = $this->core->backup_dir->get_path_to( $filename );
271
+ $file = ! empty( $_POST['file'] ) ? trim( strip_tags( $_POST['file'] ) ) : false;
272
+ if ( empty( $filename ) || empty( $filepath ) || empty( $file ) ) {
273
+ wp_send_json_error( __( 'Invalid file / filepath.', 'boldgrid-backup' ) );
274
+ }
275
+
276
+ $table = $this->get_sql_details( $filepath, $file );
277
+
278
+ if ( empty( $table ) ) {
279
+ $error = $this->core->notice->get_notice_markup( 'notice notice-error is-dismissible', __( 'Error, unable to get details from this database backup.', 'boldgrid-backup' ) );
280
+ wp_send_json_error( $error );
281
+ } else {
282
+ wp_send_json_success( $table );
283
+ }
284
+ }
285
+ }
admin/class-boldgrid-backup-admin-archive-details.php ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Archive Details class.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.1
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup Admin Archive Details Class.
17
+ *
18
+ * @since 1.5.1
19
+ */
20
+ class Boldgrid_Backup_Admin_Archive_Details {
21
+
22
+ /**
23
+ * The core class object.
24
+ *
25
+ * @since 1.5.1
26
+ * @access private
27
+ * @var Boldgrid_Backup_Admin_Core
28
+ */
29
+ private $core;
30
+
31
+ /**
32
+ * An array of remote storage locations.
33
+ *
34
+ * @since 1.5.4
35
+ * @access public
36
+ * @var array
37
+ */
38
+ public $remote_storage_li = array();
39
+
40
+ /**
41
+ * Constructor.
42
+ *
43
+ * @since 1.5.1
44
+ *
45
+ * @param Boldgrid_Backup_Admin_Core $core Core class object.
46
+ */
47
+ public function __construct( $core ) {
48
+ $this->core = $core;
49
+ }
50
+
51
+ /**
52
+ * Get a link for an archive's details page.
53
+ *
54
+ * @since 1.6.0
55
+ *
56
+ * @param string $filename
57
+ * @return string
58
+ */
59
+ public function get_url( $filename ) {
60
+ return get_admin_url( null, 'admin.php?page=boldgrid-backup-archive-details&filename=' . $filename );
61
+ }
62
+
63
+ /**
64
+ * Enqueue scripts.
65
+ *
66
+ * @since 1.5.4
67
+ */
68
+ public function enqueue_scripts() {
69
+ wp_enqueue_style(
70
+ 'boldgrid-backup-admin-zip-browser',
71
+ plugin_dir_url( __FILE__ ) . 'css/boldgrid-backup-admin-zip-browser.css',
72
+ array(),
73
+ BOLDGRID_BACKUP_VERSION,
74
+ 'all'
75
+ );
76
+
77
+ wp_register_script(
78
+ 'boldgrid-backup-admin-archive-details',
79
+ plugin_dir_url( __FILE__ ) . 'js/boldgrid-backup-admin-archive-details.js',
80
+ array( 'jquery' ),
81
+ BOLDGRID_BACKUP_VERSION
82
+ );
83
+ $translations = array(
84
+ 'uploading' => __( 'Uploading', 'boldgrid-backup' ),
85
+ 'uploaded' => __( 'Uploaded', 'boldgrid-backup' ),
86
+ 'failUpload' => __( 'Unable to upload backup file.', 'boldgrid-backup' ),
87
+ );
88
+ wp_localize_script( 'boldgrid-backup-admin-archive-details', 'boldgrid_backup_archive_details', $translations );
89
+ wp_enqueue_script( 'boldgrid-backup-admin-archive-details' );
90
+
91
+ wp_register_script(
92
+ 'boldgrid-backup-admin-zip-browser',
93
+ plugin_dir_url( __FILE__ ) . 'js/boldgrid-backup-admin-zip-browser.js',
94
+ array( 'jquery' ),
95
+ BOLDGRID_BACKUP_VERSION
96
+ );
97
+ $unknown_error = __( 'An unknown error has occurred.', 'boldgrid-backup' );
98
+ $translations = array(
99
+ 'loading' => __( 'Loading', 'boldgrid-backup' ),
100
+ 'home' => __( 'Home', 'boldgrid-backup' ),
101
+ 'restoring' => __( 'Restoring', 'boldgrid-backup' ),
102
+ 'confirmDbRestore' => __( 'Are you sure you want to restore this database backup?', 'boldgrid-backup' ),
103
+ 'unknownBrowseError' => __( 'An unknown error has occurred when trying to get a listing of the files in this archive.', 'boldgrid-backup' ),
104
+ 'unknownError' => $unknown_error,
105
+ 'unknownErrorNotice' => sprintf( '<div class="%1$s"><p>%2$s</p></div>', $this->core->notice->lang['dis_error'], $unknown_error ),
106
+ );
107
+ wp_localize_script( 'boldgrid-backup-admin-zip-browser', 'boldgrid_backup_zip_browser', $translations );
108
+ wp_enqueue_script( 'boldgrid-backup-admin-zip-browser' );
109
+
110
+ wp_enqueue_style( 'bglib-ui-css' );
111
+
112
+ /**
113
+ * Allow other plugins to enqueue scripts on this page.
114
+ *
115
+ * @since 1.5.3
116
+ */
117
+ do_action( 'boldgrid_backup_enqueue_archive_details' );
118
+ }
119
+
120
+ /**
121
+ * Render the details page of an archive.
122
+ *
123
+ * @since 1.5.1
124
+ */
125
+ public function render_archive() {
126
+ if ( ! empty( $_POST['delete_now'] ) ) {
127
+ $this->core->delete_archive_file();
128
+ }
129
+
130
+ $this->enqueue_scripts();
131
+ $this->core->archive_actions->enqueue_scripts();
132
+
133
+ $archive_found = false;
134
+
135
+ $filename = ! empty( $_GET['filename'] ) ? sanitize_file_name( $_GET['filename'] ) : false;
136
+ if ( ! $filename ) {
137
+ echo __( 'No archive specified.', 'boldgrid-backup' );
138
+ return;
139
+ }
140
+
141
+ // Get our archive.
142
+ $archive = $this->core->archive->get_by_name( $filename );
143
+ if ( $archive ) {
144
+ $log = $this->core->archive_log->get_by_zip( $archive['filepath'] );
145
+ $archive = array_merge( $log, $archive );
146
+ $archive_found = true;
147
+ $dump_file = $this->core->get_dump_file( $archive['filepath'] );
148
+ } else {
149
+ $archive = array(
150
+ 'filename' => $filename,
151
+ 'filepath' => $this->core->backup_dir->get_path_to( $archive['filename'] ),
152
+ );
153
+ }
154
+
155
+ // Initialize the archive. We will need it in our included template below.
156
+ $this->core->archive->init( $archive['filepath'] );
157
+
158
+ include BOLDGRID_BACKUP_PATH . '/admin/partials/boldgrid-backup-admin-archive-details.php';
159
+ }
160
+
161
+ /**
162
+ * Validate the nonce on the Backup Archive Details page.
163
+ *
164
+ * On the backup archive page, there is a nonce used by several different
165
+ * methods, boldgrid_backup_remote_storage_upload. This method is an easy
166
+ * way to validate the nonce.
167
+ *
168
+ * The nonce can be added to an ajax request's data via:
169
+ * 'security' : $( '#_wpnonce' ).val()
170
+ *
171
+ * @since 1.5.4
172
+ */
173
+ public function validate_nonce() {
174
+ return check_ajax_referer( 'boldgrid_backup_remote_storage_upload', 'security', false );
175
+ }
176
+ }
admin/class-boldgrid-backup-admin-archive-fail.php ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Backup Admin Archive Fail.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.2
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup Admin Archive Fail Class.
17
+ *
18
+ * @since 1.5.1
19
+ */
20
+ class Boldgrid_Backup_Admin_Archive_Fail {
21
+ /**
22
+ * The core class object.
23
+ *
24
+ * @since 1.5.2
25
+ * @access private
26
+ * @var Boldgrid_Backup_Admin_Core
27
+ */
28
+ private $core;
29
+
30
+ /**
31
+ * A string that will hold memory for emergency purposes.
32
+ *
33
+ * @since 1.5.2
34
+ * @access public
35
+ * @var string
36
+ * @see archive_files_init
37
+ */
38
+ public $memory = '';
39
+
40
+ /**
41
+ * Generic lang string stating unable to backup.
42
+ *
43
+ * @since 1.5.4
44
+ * @access public
45
+ * @var string
46
+ */
47
+ public $unable_to_backup;
48
+
49
+ /**
50
+ * Constructor.
51
+ *
52
+ * @since 1.5.2
53
+ *
54
+ * @param Boldgrid_Backup_Admin_Core $core Core class object.
55
+ */
56
+ public function __construct( $core ) {
57
+ $this->core = $core;
58
+
59
+ $this->unable_to_backup = __( 'We were unable to create a backup of your website due to the following:', 'boldgrid-backup' );
60
+ }
61
+
62
+ /**
63
+ * Add actions to "boldgrid_backup_archive_files_init".
64
+ *
65
+ * The "boldgrid_backup_archive_files_init" action is done as the first
66
+ * thing within the archive files method.
67
+ *
68
+ * @since 1.5.2
69
+ */
70
+ public function archive_files_init() {
71
+ add_action( 'shutdown', array( $this, 'shutdown' ) );
72
+
73
+ /*
74
+ * Store ~0.35 MB of memory for use in an emergency.
75
+ *
76
+ * If there's a fatal error, our shutdown method will need enough
77
+ * memory to complete a few tasks. 0.25 MB seems sufficient, but we'll
78
+ * save 0.35 to be on the safe side.
79
+ *
80
+ * Tests have shown the following memory limits are sufficient enough
81
+ * for executing our shutdown function:
82
+ * # YES 1 MB
83
+ * # YES .5 MB
84
+ * # YES .25 MB
85
+ * # NO .10 MB shutdown function fails early on when calling
86
+ * error_get_last().
87
+ */
88
+ $mb = 1000000;
89
+ $this->memory = str_repeat( '0', ( 0.35 * $mb ) );
90
+ }
91
+
92
+ /**
93
+ * Send an email if a backup failed during cron.
94
+ *
95
+ * @since 1.5.2
96
+ *
97
+ * @param array $data Array of data, containing a message to send via email.
98
+ * @return bool
99
+ */
100
+ public function cron_fail_email( $data ) {
101
+ $subject = __( 'Backup failed for', 'boldgrid-backup' ) . ' ' . get_site_url();
102
+ return $this->core->email->send( $subject, $data['message'] );
103
+ }
104
+
105
+ /**
106
+ * Create a "backup failed" email and schedule it to be sent via jobs.
107
+ *
108
+ * @since 1.5.4
109
+ *
110
+ * @param string $message Error message.
111
+ */
112
+ public function schedule_fail_email( $message ) {
113
+ $message = sprintf(
114
+ $this->unable_to_backup . "\n\n%1\$s",
115
+ strip_tags( $message )
116
+ );
117
+
118
+ $email_body = $this->core->email->fill_generic_template( $message, false );
119
+
120
+ $args = array(
121
+ 'action' => 'boldgrid_backup_cron_fail_email',
122
+ 'action_data' => array(
123
+ 'message' => $email_body,
124
+ ),
125
+ 'action_title' => __( 'Send warning email because backup failed', 'boldgrid-backup' ),
126
+ );
127
+
128
+ $this->core->jobs->add( $args );
129
+ }
130
+
131
+ /**
132
+ * Hook into shutdown.
133
+ *
134
+ * @since 1.5.2
135
+ */
136
+ public function shutdown() {
137
+
138
+ // Free up memory so we have enough to complete this method.
139
+ $this->memory = null;
140
+
141
+ // If we had a fatal error, tell the system we're no longer backing up.
142
+ $this->core->in_progress->end();
143
+
144
+ /*
145
+ * If an archive fails, there may be a rogue db dump sitting out there.
146
+ * If it exists, delete it, it should be in the archive file.
147
+ */
148
+ if ( $this->core->wp_filesystem->exists( $this->core->db_dump_filepath ) ) {
149
+ $this->core->wp_filesystem->delete( $this->core->db_dump_filepath );
150
+ }
151
+
152
+ $last_error = error_get_last();
153
+
154
+ /*
155
+ * If there's no error or this is not fatal, abort.
156
+ *
157
+ * @see http://php.net/manual/en/errorfunc.constants.php
158
+ */
159
+ if ( empty( $last_error ) || 1 !== $last_error['type'] ) {
160
+ return;
161
+ }
162
+
163
+ $error_message = sprintf(
164
+ '<strong>%1$s</strong>: %2$s in %3$s on line %4$s',
165
+ __( 'Fatal error', 'boldgrid-backup' ),
166
+ $last_error['message'],
167
+ $last_error['file'],
168
+ $last_error['line']
169
+ );
170
+
171
+ $this->schedule_fail_email( $error_message );
172
+
173
+ if ( ! $this->core->doing_cron ) {
174
+ $data['errorText'] = $this->unable_to_backup . '<br />' . $error_message;
175
+ wp_send_json_error( $data );
176
+ }
177
+ }
178
+ }
admin/class-boldgrid-backup-admin-archive-log.php ADDED
@@ -0,0 +1,199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Archive Log class.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.1
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup Admin Archive Log Class.
17
+ *
18
+ * @since 1.5.1
19
+ */
20
+ class Boldgrid_Backup_Admin_Archive_Log {
21
+
22
+ /**
23
+ * The core class object.
24
+ *
25
+ * @since 1.5.1
26
+ * @access private
27
+ * @var Boldgrid_Backup_Admin_Core
28
+ */
29
+ private $core;
30
+
31
+ /**
32
+ * Constructor.
33
+ *
34
+ * @since 1.5.1
35
+ *
36
+ * @param Boldgrid_Backup_Admin_Core $core Core class object.
37
+ */
38
+ public function __construct( $core ) {
39
+ $this->core = $core;
40
+ }
41
+
42
+ /**
43
+ * Delete a log file.
44
+ *
45
+ * @since 1.5.1
46
+ *
47
+ * @param string $zip_filepath
48
+ * @return bool
49
+ */
50
+ public function delete_by_zip( $zip_filepath ) {
51
+ $log_filepath = $this->path_from_zip( $zip_filepath );
52
+
53
+ $exists = $this->core->wp_filesystem->exists( $log_filepath );
54
+ if ( ! $exists ) {
55
+ return true;
56
+ }
57
+
58
+ return $this->core->wp_filesystem->delete( $log_filepath );
59
+ }
60
+
61
+ /**
62
+ * Get the contents of a log file.
63
+ *
64
+ * @since 1.5.1
65
+ *
66
+ * @param string $zip_filepath
67
+ * @return array
68
+ */
69
+ public function get_by_zip( $zip_filepath ) {
70
+ $log_filepath = $this->path_from_zip( $zip_filepath );
71
+
72
+ $exists = $this->core->wp_filesystem->exists( $log_filepath );
73
+ if ( ! $exists ) {
74
+ return array();
75
+ }
76
+
77
+ $file_contents = $this->core->wp_filesystem->get_contents( $log_filepath );
78
+ if ( ! $file_contents ) {
79
+ return array();
80
+ }
81
+
82
+ return json_decode( $file_contents, true );
83
+ }
84
+
85
+ /**
86
+ * Create the path to a log file based on a zip file.
87
+ *
88
+ * Pass in c:\abc.zip and get c:\abc.log
89
+ *
90
+ * @since 1.5.1
91
+ *
92
+ * @param string $zip_filepath
93
+ * @return bool
94
+ */
95
+ public function path_from_zip( $zip_filepath ) {
96
+ return pathinfo( $zip_filepath, PATHINFO_DIRNAME ) . DIRECTORY_SEPARATOR . pathinfo( $zip_filepath, PATHINFO_FILENAME ) . '.log';
97
+ }
98
+
99
+ /**
100
+ * Take action after an archive has been restored.
101
+ *
102
+ * This method hooks into the boldgrid_backup_post_restore action.
103
+ *
104
+ * @since 1.6.0
105
+ *
106
+ * @param array $info
107
+ */
108
+ public function post_restore( $info ) {
109
+ $path_backup_dir = $this->path_from_zip( $info['filepath'] );
110
+ $path_abspath = ABSPATH . basename( $path_backup_dir );
111
+
112
+ // If this backup did not restore a log file to ABSPATH, then we can abort.
113
+ if ( ! $this->core->wp_filesystem->exists( $path_abspath ) ) {
114
+ return;
115
+ }
116
+
117
+ // Move the abspath log file to the backup dir.
118
+ $this->core->wp_filesystem->move( $path_abspath, $path_backup_dir, true );
119
+
120
+ // We don't need the log file in the ABSPATH, remove it.
121
+ $this->core->wp_filesystem->delete( $path_abspath );
122
+ }
123
+
124
+ /**
125
+ * Restore a log file by a zip's filepath.
126
+ *
127
+ * For example, if we just downloaded backup.zip from FTP, this method will
128
+ * extract backup.log from backup.zip if it exists. This was, we have all
129
+ * of the meta data about the backup.
130
+ *
131
+ * @since 1.5.4
132
+ *
133
+ * @param string $filepath
134
+ */
135
+ public function restore_by_zip( $filepath ) {
136
+ $log_filepath = $this->path_from_zip( $filepath );
137
+ $log_filename = basename( $log_filepath );
138
+
139
+ if ( $this->core->wp_filesystem->exists( $log_filepath ) ) {
140
+ return true;
141
+ }
142
+
143
+ // Extract the log file to ABSPATH.
144
+ $zip = new Boldgrid_Backup_Admin_Compressor_Pcl_Zip( $this->core );
145
+ $status = $zip->extract_one( $filepath, $log_filename );
146
+ if ( ! $status ) {
147
+ return false;
148
+ }
149
+
150
+ // Move the log file from the ABSPATH to the backup dir.
151
+ $old_path = ABSPATH . $log_filename;
152
+ $new_path = $this->core->backup_dir->get_path_to( $log_filename );
153
+ return $this->core->wp_filesystem->move( $old_path, $new_path );
154
+ }
155
+
156
+ /**
157
+ * Write info file.
158
+ *
159
+ * @since 1.5.1
160
+ *
161
+ * @param $info array
162
+ * @return bool
163
+ */
164
+ public function write( $info ) {
165
+ if ( empty( $info['filepath'] ) ) {
166
+ return false;
167
+ }
168
+
169
+ $log_filepath = $this->path_from_zip( $info['filepath'] );
170
+
171
+ $touched = $this->core->wp_filesystem->touch( $log_filepath );
172
+ if ( ! $touched ) {
173
+ return false;
174
+ }
175
+
176
+ $written = $this->core->wp_filesystem->put_contents( $log_filepath, json_encode( $info ) );
177
+ if ( ! $written ) {
178
+ return false;
179
+ }
180
+
181
+ // Add the log file to the archive file, as of 1.5.4.
182
+ $archive = new PclZip( $info['filepath'] );
183
+ if ( 0 === $archive ) {
184
+ return false;
185
+ }
186
+ /*
187
+ * The log file is being added to the root of the archive. If the user
188
+ * restores the archive, the log will be restored to the ABSPATH. The
189
+ * $this->post_restore() method will move the log file to the backup
190
+ * directory and delete it from the ABSPATH.
191
+ */
192
+ $status = $archive->add( $log_filepath, PCLZIP_OPT_REMOVE_ALL_PATH );
193
+ if ( 0 === $status ) {
194
+ return false;
195
+ }
196
+
197
+ return true;
198
+ }
199
+ }
admin/class-boldgrid-backup-admin-archive.php ADDED
@@ -0,0 +1,359 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Archive class.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.3
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup Admin Archive Browser Class.
17
+ *
18
+ * @since 1.5.3
19
+ */
20
+ class Boldgrid_Backup_Admin_Archive {
21
+
22
+ /**
23
+ * The core class object.
24
+ *
25
+ * @since 1.5.3
26
+ * @access private
27
+ * @var Boldgrid_Backup_Admin_Core
28
+ */
29
+ private $core;
30
+
31
+ /**
32
+ * Compressor used when creating archive.
33
+ *
34
+ * @since 1.6.0
35
+ * @access public
36
+ * @var string
37
+ */
38
+ public $compressor = null;
39
+
40
+ /**
41
+ * Filename of this archive.
42
+ *
43
+ * @since 1.6.0
44
+ * @access public
45
+ * @var string
46
+ */
47
+ public $filename = null;
48
+
49
+ /**
50
+ * Full filepath to the archive.
51
+ *
52
+ * Set in the init method.
53
+ *
54
+ * @since 1.5.3
55
+ * @access public
56
+ * @var string
57
+ */
58
+ public $filepath = null;
59
+
60
+ /**
61
+ * The contents of the archive's log file.
62
+ *
63
+ * @since 1.6.0
64
+ * @access public
65
+ * @var array
66
+ */
67
+ public $log = array();
68
+
69
+ /**
70
+ * The filename of this archive's log file.
71
+ *
72
+ * @since 1.6.0
73
+ * @access public
74
+ * @var string
75
+ */
76
+ public $log_filename = null;
77
+
78
+ /**
79
+ * The filepath to this archive's log file.
80
+ *
81
+ * @since 1.6.0
82
+ * @access public
83
+ * @var string
84
+ */
85
+ public $log_filepath = null;
86
+
87
+ /**
88
+ * URL to the details page of this backup.
89
+ *
90
+ * This property is available after calling init().
91
+ *
92
+ * @since 1.6.0
93
+ * @access protected
94
+ * @var string
95
+ */
96
+ public $view_details_url = '';
97
+
98
+ /**
99
+ * Constructor.
100
+ *
101
+ * @since 1.5.3
102
+ *
103
+ * @param Boldgrid_Backup_Admin_Core $core Core class object.
104
+ */
105
+ public function __construct( $core ) {
106
+ $this->core = $core;
107
+ }
108
+
109
+ /**
110
+ * Delete an archive file.
111
+ *
112
+ * @since 1.5.3
113
+ *
114
+ * @param string $filepath Absolute path to a backup file.
115
+ * @return bool
116
+ */
117
+ public function delete( $filepath ) {
118
+ $deleted = $this->core->wp_filesystem->delete( $filepath, false, 'f' );
119
+
120
+ $this->core->archive_log->delete_by_zip( $filepath );
121
+
122
+ return $deleted;
123
+ }
124
+
125
+ /**
126
+ * Get an archive by name.
127
+ *
128
+ * Please see @return for more information on what an archive actually is.
129
+ *
130
+ * @since 1.5.4
131
+ *
132
+ * @param string $filename
133
+ * @return array {
134
+ * Details about an archive.
135
+ *
136
+ * @type string $filepath /home/user/boldgrid_backup/file.zip
137
+ * @type string $filename file.zip
138
+ * @type string $filedate 1/2/2018 1:21 PM
139
+ * @type int $filesize 99152247
140
+ * @type int $lastmodunix 1514917311
141
+ * @type int $key 0
142
+ * }
143
+ */
144
+ public function get_by_name( $filename ) {
145
+ $return_archive = false;
146
+
147
+ $archives = $this->core->get_archive_list();
148
+
149
+ foreach ( $archives as $key => $archive ) {
150
+ if ( $archive['filename'] === $filename ) {
151
+ $archive['key'] = $key;
152
+ $return_archive = $archive;
153
+ break;
154
+ }
155
+ }
156
+
157
+ return $return_archive;
158
+ }
159
+
160
+ /**
161
+ * Get one file from an archive.
162
+ *
163
+ * @since 1.5.3
164
+ *
165
+ * @param string $file The file to get.
166
+ * @param bool $meta_only Whether to include the content of the file.
167
+ * @return array
168
+ */
169
+ public function get_file( $file, $meta_only = false ) {
170
+ if ( empty( $this->filepath ) || ! $this->is_archive( $this->filepath ) ) {
171
+ return false;
172
+ }
173
+
174
+ $zip = new Boldgrid_Backup_Admin_Compressor_Pcl_Zip( $this->core );
175
+
176
+ $file_contents = $zip->get_file( $this->filepath, $file );
177
+
178
+ // If we only want the meta data, unset the content of the file.
179
+ if ( $meta_only && ! empty( $file_contents[0]['content'] ) ) {
180
+ unset( $file_contents[0]['content'] );
181
+ }
182
+
183
+ return $file_contents;
184
+ }
185
+
186
+ /**
187
+ * Init.
188
+ *
189
+ * @since 1.6.0
190
+ *
191
+ * @param string $filepath
192
+ */
193
+ public function init( $filepath ) {
194
+
195
+ $filepath = strip_tags( $filepath );
196
+
197
+ if ( ! empty( $this->filepath ) && $filepath === $this->filepath ) {
198
+ return;
199
+ }
200
+
201
+ $this->reset();
202
+
203
+ $zip = new Boldgrid_Backup_Admin_Compressor_Pcl_Zip( $this->core );
204
+
205
+ $this->filepath = $filepath;
206
+ $this->filename = basename( $this->filepath );
207
+
208
+ $this->log_filepath = $this->core->archive_log->path_from_zip( $this->filepath );
209
+ $this->log_filename = basename( $this->log_filepath );
210
+
211
+ // If the archive's log file does not exist, extract it.
212
+ $have_log = $this->core->wp_filesystem->exists( $this->log_filepath );
213
+ if ( ! $have_log ) {
214
+ $have_log = $this->core->archive_log->restore_by_zip( $this->filepath );
215
+ }
216
+
217
+ // Init our log.
218
+ if ( $have_log ) {
219
+ $this->log = $this->core->archive_log->get_by_zip( $this->filepath );
220
+ }
221
+
222
+ /*
223
+ * Init our compressor.
224
+ *
225
+ * If there is no log file, this archive was created with version < 1.6
226
+ * and the only compressor was ZipArchive.
227
+ */
228
+ $this->compressor = ! empty( $this->log['compressor'] ) ? $this->log['compressor'] : 'php_zip';
229
+
230
+ $this->view_details_url = admin_url( 'admin.php?page=boldgrid-backup-archive-details&filename=' . $this->filename );
231
+
232
+ unset( $zip );
233
+ }
234
+
235
+ /**
236
+ * Determine if a zip file is in our archive.
237
+ *
238
+ * @since 1.5.3
239
+ *
240
+ * @param string $filepath
241
+ * @return bool
242
+ */
243
+ public function is_archive( $filepath ) {
244
+ $archives = $this->core->get_archive_list();
245
+
246
+ if ( empty( $archives ) ) {
247
+ return false;
248
+ }
249
+
250
+ foreach ( $archives as $archive ) {
251
+ if ( $filepath === $archive['filepath'] ) {
252
+ return true;
253
+ }
254
+ }
255
+
256
+ return false;
257
+ }
258
+
259
+ /**
260
+ * Determine if a backup belongs to this site.
261
+ *
262
+ * This method takes into account a site's $backup_identifier and compares
263
+ * it to a backup's filename.
264
+ *
265
+ * @since 1.6.0
266
+ *
267
+ * @param string $filename
268
+ * @return bool
269
+ */
270
+ public function is_site_archive( $filename ) {
271
+ $backup_identifier = $this->core->get_backup_identifier();
272
+
273
+ // End in zip.
274
+ $extension = pathinfo( $filename, PATHINFO_EXTENSION );
275
+ if ( 'zip' !== $extension ) {
276
+ return false;
277
+ }
278
+
279
+ // Include the backup identifier.
280
+ if ( false === strpos( $filename, $backup_identifier ) ) {
281
+ return false;
282
+ }
283
+
284
+ // Begin with 'boldgrid-backup-'.
285
+ if ( 0 !== strpos( $filename, 'boldgrid-backup-' ) ) {
286
+ return false;
287
+ }
288
+
289
+ return true;
290
+ }
291
+
292
+ /**
293
+ * Determine whether or not this archive is stored on the web server (local).
294
+ *
295
+ * This is similar to self::is_stored_remotely. While that method is an
296
+ * expensive operation, this one is not. However, for consistency, this too
297
+ * will be a method rather than a class property.
298
+ *
299
+ * @since 1.6.0
300
+ *
301
+ * @return bool
302
+ */
303
+ public function is_stored_locally() {
304
+ $this->core->archives_all->init();
305
+
306
+ return isset( $this->core->archives_all->archives[ $this->filename ]['on_web_server'] ) &&
307
+ true === $this->core->archives_all->archives[ $this->filename ]['on_web_server'];
308
+ }
309
+
310
+ /**
311
+ * Determine whether or not this archive is stored remotely somewhere.
312
+ *
313
+ * This is an expensive operation, so we are not using this as a class
314
+ * property / initializing during init.
315
+ *
316
+ * @since 1.6.0
317
+ *
318
+ * @return bool
319
+ */
320
+ public function is_stored_remotely() {
321
+ $this->core->archives_all->init();
322
+
323
+ return isset( $this->core->archives_all->archives[ $this->filename ]['on_remote_server'] ) &&
324
+ true === $this->core->archives_all->archives[ $this->filename ]['on_remote_server'];
325
+ }
326
+
327
+ /**
328
+ * Reset this class.
329
+ *
330
+ * @since 1.6.0
331
+ */
332
+ public function reset() {
333
+ $this->filename = null;
334
+ $this->filepath = null;
335
+ $this->log_filepath = null;
336
+ $this->log_filename = null;
337
+ $this->log = array();
338
+ $this->compressor = null;
339
+ }
340
+
341
+ /**
342
+ * Update an archive's timestamp based on the time in the log.
343
+ *
344
+ * For example, if the archive was created at 10am and you uploaded it to
345
+ * an FTP server at 12pm, the FTP server may set the timestamp to 12pm. Then
346
+ * you download from FTP to web server at 2pm, and the archive's timestamp
347
+ * is now 2pm. This is all confusing. This method will get the archive's
348
+ * timestamp from the log and configure the last modified appropriately.
349
+ */
350
+ public function update_timestamp() {
351
+
352
+ // If we don't have what we need, abort.
353
+ if ( empty( $this->filepath ) || empty( $this->log['lastmodunix'] ) ) {
354
+ return false;
355
+ }
356
+
357
+ return $this->core->wp_filesystem->touch( $this->filepath, $this->log['lastmodunix'] );
358
+ }
359
+ }
admin/class-boldgrid-backup-admin-archives-all.php ADDED
@@ -0,0 +1,217 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Archives All class.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.4
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup Admin Archives All Class.
17
+ *
18
+ * @since 1.5.4
19
+ */
20
+ class Boldgrid_Backup_Admin_Archives_All {
21
+
22
+ /**
23
+ * An array of all archives.
24
+ *
25
+ * @since 1.5.4
26
+ * @access public
27
+ * @var array
28
+ */
29
+ public $all = array();
30
+
31
+ /**
32
+ * The core class object.
33
+ *
34
+ * @since 1.5.4
35
+ * @access private
36
+ * @var Boldgrid_Backup_Admin_Core
37
+ */
38
+ private $core;
39
+
40
+ /**
41
+ * Whether or not we have initialized all backups.
42
+ *
43
+ * @since 1.5.4
44
+ * @access public
45
+ * @var bool
46
+ */
47
+ public $is_init = false;
48
+
49
+ /**
50
+ * Local server title, such as "Web server".
51
+ *
52
+ * @since 1.5.4
53
+ * @access public
54
+ * @var string
55
+ */
56
+ public $local_title;
57
+
58
+ /**
59
+ * An array of data about remote locations and how many backups at each.
60
+ *
61
+ * @since 1.5.4
62
+ * @access public
63
+ * @var array
64
+ */
65
+ public $location_count = array();
66
+
67
+ /**
68
+ * Location types.
69
+ *
70
+ * @since 1.6.0
71
+ * @access public
72
+ * @var array
73
+ */
74
+ public $location_types = array(
75
+ 'on_web_server',
76
+ 'on_remote_server',
77
+ );
78
+
79
+ /**
80
+ * An array of meta information about each archive.
81
+ *
82
+ * This array is initialized during init(). Each key of this array is an
83
+ * archive filename.
84
+ *
85
+ * @since 1.6.0
86
+ * @access public
87
+ * @var array
88
+ */
89
+ public $archives = array();
90
+
91
+ /**
92
+ * Constructor.
93
+ *
94
+ * @since 1.5.4
95
+ *
96
+ * @param Boldgrid_Backup_Admin_Core $core Core class object.
97
+ */
98
+ public function __construct( $core ) {
99
+ $this->core = $core;
100
+
101
+ $this->local_title = __( 'Web Server', 'BoldGrid Backup' );
102
+ }
103
+
104
+ /**
105
+ * Add a backup to the list of all backups.
106
+ *
107
+ * @since 1.5.4
108
+ *
109
+ * @param array $backup
110
+ */
111
+ public function add( $backup ) {
112
+ $in_all = false;
113
+
114
+ $is_remote = ! empty( $backup['locations'][0]['on_remote_server'] ) && true === $backup['locations'][0]['on_remote_server'];
115
+ if ( $is_remote ) {
116
+ $this->archives[ $backup['filename'] ]['on_remote_server'] = true;
117
+ }
118
+
119
+ // Loop through all of our existing backups to see if this one exists.
120
+ foreach ( $this->all as &$all_backup ) {
121
+ if ( $backup['filename'] === $all_backup['filename'] ) {
122
+
123
+ // Once we found our backup, flag that we found it.
124
+ $in_all = true;
125
+ $all_backup['locations'][] = $backup['locations'][0];
126
+ }
127
+ }
128
+
129
+ // If we didn't find it, add it to the list.
130
+ if ( ! $in_all ) {
131
+ $this->all[] = $backup;
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Determine if an archive has a location type.
137
+ *
138
+ * @since 1.6.0
139
+ *
140
+ * @param array $archive
141
+ * @param string $location_type
142
+ * @return bool
143
+ */
144
+ public function has_location_type( $archive, $location_type ) {
145
+ foreach ( $archive['locations'] as $location ) {
146
+ if ( isset( $location[ $location_type ] ) && true === $location[ $location_type ] ) {
147
+ return true;
148
+ }
149
+ }
150
+
151
+ return false;
152
+ }
153
+
154
+ /**
155
+ * Init the $location_count property.
156
+ *
157
+ * @since 1.5.4
158
+ */
159
+ public function init_location_count() {
160
+
161
+ $this->location_count['all'] = count( $this->all );
162
+
163
+ foreach ( $this->all as $archive ) {
164
+
165
+ if ( empty( $archive['locations'] ) ) {
166
+ continue;
167
+ }
168
+
169
+ foreach ( $this->core->archives_all->location_types as $location_type ) {
170
+ if ( ! $this->has_location_type( $archive, $location_type ) ) {
171
+ continue;
172
+ }
173
+
174
+ if ( empty( $this->location_count[ $location_type ] ) ) {
175
+ $this->location_count[ $location_type ] = 0;
176
+ }
177
+
178
+ $this->location_count[ $location_type ]++;
179
+ }
180
+ }
181
+ }
182
+
183
+ /**
184
+ * Init and get a list of all backups (local and remote).
185
+ *
186
+ * @since 1.5.4
187
+ */
188
+ public function init() {
189
+ if ( $this->is_init ) {
190
+ return;
191
+ }
192
+
193
+ $archives = $this->core->get_archive_list();
194
+
195
+ foreach ( $archives as $archive ) {
196
+ $this->all[] = array(
197
+ 'filename' => $archive['filename'],
198
+ 'last_modified' => $archive['lastmodunix'],
199
+ 'size' => $archive['filesize'],
200
+ 'locations' => array(
201
+ array(
202
+ 'title' => $this->local_title,
203
+ 'on_web_server' => true,
204
+ ),
205
+ ),
206
+ );
207
+
208
+ $this->archives[ $archive['filename'] ]['on_web_server'] = true;
209
+ }
210
+
211
+ do_action( 'boldgrid_backup_get_all' );
212
+
213
+ $this->init_location_count();
214
+
215
+ $this->is_init = true;
216
+ }
217
+ }
admin/class-boldgrid-backup-admin-archives.php ADDED
@@ -0,0 +1,239 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Archives class.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.4
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup Admin Archives Class.
17
+ *
18
+ * @since 1.5.4
19
+ */
20
+ class Boldgrid_Backup_Admin_Archives {
21
+
22
+ /**
23
+ * The core class object.
24
+ *
25
+ * @since 1.5.4
26
+ * @access private
27
+ * @var Boldgrid_Backup_Admin_Core
28
+ */
29
+ private $core;
30
+
31
+ /**
32
+ * Constructor.
33
+ *
34
+ * @since 1.5.4
35
+ *
36
+ * @param Boldgrid_Backup_Admin_Core $core Core class object.
37
+ */
38
+ public function __construct( $core ) {
39
+ $this->core = $core;
40
+ }
41
+
42
+ /**
43
+ * Get the location type from a location.
44
+ *
45
+ * @since 1.6.0
46
+ *
47
+ * @param $location array {
48
+ * A location.
49
+ *
50
+ * @type string $title Such as "Web Server".
51
+ * @type bool $location
52
+ * }
53
+ * @return mixed
54
+ */
55
+ public function get_location_type( $location ) {
56
+ foreach ( $this->core->archives_all->location_types as $type ) {
57
+ if ( isset( $location[ $type ] ) && true === $location[ $type ] ) {
58
+ return $type;
59
+ }
60
+ }
61
+
62
+ return false;
63
+ }
64
+
65
+ /**
66
+ * Get a location type's title.
67
+ *
68
+ * @since 1.6.0
69
+ *
70
+ * @param string $type
71
+ * @return string
72
+ */
73
+ public function get_location_type_title( $type ) {
74
+ if ( 'all' === $type ) {
75
+ $title = $this->core->lang['All'];
76
+ } elseif ( 'on_web_server' === $type ) {
77
+ $title = $this->core->lang['Web_Server'];
78
+ } else {
79
+ $title = $this->core->lang['Remote'];
80
+ }
81
+
82
+ return $title;
83
+ }
84
+
85
+ /**
86
+ * Create the list of locations.
87
+ *
88
+ * This method returns a list of locations (html markup), which will be
89
+ * located under the backup, such as "Web Server, SFTP".
90
+ *
91
+ * @since 1.6.0
92
+ *
93
+ * @return string
94
+ */
95
+ public function get_locations( $archive ) {
96
+ $locations = array();
97
+
98
+ foreach ( $archive['locations'] as $location ) {
99
+
100
+ $location_type = $this->get_location_type( $location );
101
+
102
+ $data_attr = sprintf( 'data-%1$s="true"', $location_type );
103
+
104
+ $title_attr = ! empty( $location['title_attr'] ) ? sprintf( 'title="%1$s"', esc_attr( $location['title_attr'] ) ) : '';
105
+
106
+ $locations[] = sprintf(
107
+ '<span %2$s %3$s>%1$s</span>',
108
+ esc_html( $location['title'] ),
109
+ $data_attr,
110
+ $title_attr
111
+ );
112
+ }
113
+
114
+ $locations = implode( ', ', $locations );
115
+
116
+ return $locations;
117
+ }
118
+
119
+ /**
120
+ * Get a "mine count" of backup files.
121
+ *
122
+ * Returns a string such as:
123
+ * All (5) | Web Server (4) | Remote (2)
124
+ *
125
+ * @since 1.5.4
126
+ *
127
+ * @return string
128
+ */
129
+ public function get_mine_count() {
130
+ $this->core->archives_all->init();
131
+
132
+ // An array of locations, each array item simliar to: <a>All<a/> (5)
133
+ $locations = array();
134
+
135
+ foreach ( $this->core->archives_all->location_count as $location => $count ) {
136
+
137
+ // The first locaion, "All", should have the "current" class.
138
+ $current = 'all' === $location ? 'current' : '';
139
+
140
+ $title = $this->get_location_type_title( $location );
141
+
142
+ $locations[] = sprintf('
143
+ %3$s %1$s %4$s (%2$s)
144
+ ',
145
+ /* 1 */ $title,
146
+ /* 2 */ $count,
147
+ /* 3 */ sprintf( '<a href="" class="mine %1$s" data-count-type="%2$s">', $current, $location ),
148
+ /* 4 */ '</a>'
149
+ );
150
+ }
151
+
152
+ // The last location, not really a "location", is the help icon.
153
+ $locations[] = '<span class="dashicons dashicons-editor-help" data-id="mine-count"></span>';
154
+
155
+ $markup = '<p class="subsubsub">' . implode( ' | ', $locations ) . '</p>';
156
+
157
+ // Create help text to go along with help icon.
158
+ $markup .= sprintf('
159
+ <p class="help" data-id="mine-count">
160
+ %1$s
161
+ </p>',
162
+ __( 'This list shows on which computers your backup archives are being stored. They can be saved to more than one location. Please <a href="admin.php?page=boldgrid-backup-tools&section=section_locations">click here</a> for more information on what <strong>Web Server</strong> and other terms mean.', 'boldgrid-backup' )
163
+ );
164
+
165
+ return $markup;
166
+ }
167
+
168
+ /**
169
+ * Get a table containing a list of all backups.
170
+ *
171
+ * This table is displayed on the Backup Archives page.
172
+ *
173
+ * @since 1.5.4
174
+ *
175
+ * @return string
176
+ */
177
+ public function get_table() {
178
+ $this->core->archives_all->init();
179
+ $backup = __( 'Backup', 'boldgrid-backup' );
180
+ $view_details = __( 'View details', 'boldgrid-backup' );
181
+
182
+ $table = $this->get_mine_count();
183
+
184
+ $table .= sprintf( '
185
+ <table class="wp-list-table widefat fixed striped pages">
186
+ <thead>
187
+ <td>%1$s</td>
188
+ <td>%2$s</td>
189
+ <td></td>
190
+ <tbody id="backup-archive-list-body">',
191
+ __( 'Date', 'boldgrid-backup' ),
192
+ __( 'Size', 'boldgrid-backup' )
193
+ );
194
+
195
+ foreach ( $this->core->archives_all->all as $archive ) {
196
+ $locations = $this->get_locations( $archive );
197
+
198
+ // dirlist -> lastmodunix -> mtime (last_modified in unix time).
199
+ $this->core->time->init( $archive['last_modified'], 'utc' );
200
+
201
+ $table .= sprintf( '
202
+ <tr>
203
+ <td>
204
+ %2$s
205
+ <p class="description">%6$s</p>
206
+ </td>
207
+ <td>
208
+ %3$s
209
+ </td>
210
+ <td>
211
+ <a
212
+ class="button"
213
+ href="admin.php?page=boldgrid-backup-archive-details&filename=%4$s"
214
+ >%5$s</a>
215
+ </td>
216
+ </tr>
217
+ ',
218
+ /* 1 */ $backup,
219
+ /* 2 */ $this->core->time->get_span(),
220
+ /* 3 */ Boldgrid_Backup_Admin_Utility::bytes_to_human( $archive['size'] ),
221
+ /* 4 */ $archive['filename'],
222
+ /* 5 */ $view_details,
223
+ /* 6 */ $locations
224
+ );
225
+ }
226
+ $table .= '</tbody>
227
+ </table>
228
+ ';
229
+
230
+ if ( empty( $this->core->archives_all->all ) ) {
231
+ $table = sprintf( '
232
+ <p>%1$s</p>',
233
+ __( 'You currently do not have any backups.', 'boldgrid-backup' )
234
+ );
235
+ }
236
+
237
+ return $table;
238
+ }
239
+ }
admin/class-boldgrid-backup-admin-auto-rollback.php ADDED
@@ -0,0 +1,1033 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Boldgrid Backup Admin Auto Rollback.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.2
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * Boldgrid Backup Admin Auto Rollback class.
17
+ *
18
+ * We hook into "the upgrader_process_complete" (run when the download process
19
+ * for a plugin install or update finishes). If the user has enabled auto
20
+ * rollback and we have data in the boldgrid_backup_pending_rollback site
21
+ * option, then we add the cron jobs for 5 minutes later to auto rollback.
22
+ *
23
+ * Auto Rollback works with the following site options:
24
+ *
25
+ * boldgrid_backup_pending_rollback When we manually create a backup, if we
26
+ * $_POST['is_updating'] === 'true', then the
27
+ * results of this backup file are saved in
28
+ * this option.
29
+ *
30
+ * To cancel an auto rollback, this option
31
+ * needs to be deleted (and subsequent crons
32
+ * cleared).
33
+ *
34
+ * array (
35
+ * compressor "pcl_zip"
36
+ * db_duration "0.16"
37
+ * dryrun false
38
+ * duration "20.07"
39
+ * filepath "/home/user/boldgrid-backup/backup.zip"
40
+ * filesize 262562329
41
+ * lastmodunix 1505912200
42
+ * mail_success true
43
+ * mode "backup"
44
+ * save true
45
+ * );
46
+ *
47
+ * @since 1.5.1
48
+ */
49
+ class Boldgrid_Backup_Admin_Auto_Rollback {
50
+
51
+ /**
52
+ * The core class object.
53
+ *
54
+ * @since 1.5.1
55
+ * @access private
56
+ * @var Boldgrid_Backup_Admin_Core
57
+ */
58
+ private $core;
59
+
60
+ /**
61
+ * Whether or not we are on an update page.
62
+ *
63
+ * An update page is a page that allows the user to update either WP, a plugin,
64
+ * or a theme. Defined in the constructor.
65
+ *
66
+ * Used by the backup now button to determine if the backup being made is
67
+ * for update protection.
68
+ *
69
+ * @since 1.6.0
70
+ * @access protected
71
+ * @var bool
72
+ */
73
+ public $on_update_page = false;
74
+
75
+ /**
76
+ * The amount of time before an auto rollback occurs.
77
+ *
78
+ * For example, allow 10 minutes for testing.
79
+ *
80
+ * @since 1.5.3
81
+ * @access public
82
+ * @var string
83
+ */
84
+ public $testing_time = '+15 minutes';
85
+
86
+ /**
87
+ * An array of pagenow's in which the user has the option to update either
88
+ * WP, a plugin, or a theme.
89
+ *
90
+ * @since 1.6.0
91
+ * @access protected
92
+ * @var array
93
+ */
94
+ protected $update_pages = array(
95
+ 'customize.php',
96
+ 'plugins.php',
97
+ 'plugin-install.php',
98
+ 'themes.php',
99
+ 'update-core.php',
100
+ );
101
+
102
+ /**
103
+ * Whether or not we are in the middle of upgrading core.
104
+ *
105
+ * When we've clicked "Update Now" on Dashboards > Updates, we are redirected
106
+ * to wp-admin/update-core.php?action=do-core-upgrade
107
+ *
108
+ * @since 1.6.0
109
+ * @access public
110
+ * @var bool
111
+ */
112
+ public $updating_core = false;
113
+
114
+ /**
115
+ * Constructor.
116
+ *
117
+ * @since 1.5.1
118
+ *
119
+ * @param Boldgrid_Backup_Admin_Core $core
120
+ */
121
+ public function __construct( $core ) {
122
+ $this->core = $core;
123
+
124
+ $this->updating_core = 'update-core.php' === $this->core->pagenow &&
125
+ ! empty( $_GET['action'] ) && 'do-core-upgrade' === $_GET['action'];
126
+
127
+ $this->on_update_page = in_array( $this->core->pagenow, $this->update_pages, true );
128
+ }
129
+
130
+ /**
131
+ * Add cron to permorm auto rollback.
132
+ *
133
+ * This method includes the checks for (1) The user enabling auto_rollback
134
+ * and (2) we have a recent backup labeled as the one to restore for the
135
+ * rollback.
136
+ *
137
+ * Based on scheduler, cron is either system cron or wp cron.
138
+ *
139
+ * @since 1.5.1
140
+ */
141
+ public function add_cron() {
142
+ $settings = $this->core->settings->get_settings();
143
+
144
+ // If auto-rollback is not enabled, then abort.
145
+ if ( 1 !== $settings['auto_rollback'] ) {
146
+ $this->core->settings->delete_rollback_option();
147
+ return;
148
+ }
149
+
150
+ // If a backup was not made prior to an update (from an update page), then abort.
151
+ $pending_rollback = get_site_option( 'boldgrid_backup_pending_rollback' );
152
+ if ( empty( $pending_rollback ) ) {
153
+ return;
154
+ }
155
+
156
+ $archives = $this->core->get_archive_list();
157
+ $archive_count = count( $archives );
158
+
159
+ // If there are no archives, then abort.
160
+ if ( $archive_count <= 0 ) {
161
+ $this->core->settings->delete_rollback_option();
162
+ return;
163
+ }
164
+
165
+ $scheduler = $this->core->scheduler->get();
166
+
167
+ switch ( $scheduler ) {
168
+ case 'cron':
169
+ $this->core->cron->add_restore_cron();
170
+ break;
171
+ case 'wp-cron':
172
+ $this->core->wp_cron->add_restore_cron();
173
+ break;
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Cancel rollback.
179
+ *
180
+ * Prior to @1.5.3 this method was in the core class.
181
+ *
182
+ * @since 1.0.1
183
+ */
184
+ public function cancel() {
185
+ // Remove any cron jobs for restore actions.
186
+ $this->core->cron->delete_cron_entries( 'restore' );
187
+
188
+ // Remove WP option boldgrid_backup_pending_rollback.
189
+ $this->core->settings->delete_rollback_option();
190
+ }
191
+
192
+ /**
193
+ * Enqueue backup scripts.
194
+ *
195
+ * Backup scripts are those needed to handle any "Backup" buttons.
196
+ *
197
+ * @since 1.6.0
198
+ */
199
+ public function enqueue_backup_scripts() {
200
+ $handle = 'boldgrid-backup-admin-backup-now';
201
+
202
+ wp_register_script(
203
+ $handle,
204
+ plugin_dir_url( __FILE__ ) . 'js/boldgrid-backup-admin-backup-now.js',
205
+ array( 'jquery' ),
206
+ BOLDGRID_BACKUP_VERSION,
207
+ false
208
+ );
209
+
210
+ $access_type = get_filesystem_method();
211
+ $archive_nonce = wp_create_nonce( 'archive_auth' );
212
+ $localize_script_data = array(
213
+ 'archiveNonce' => $archive_nonce,
214
+ 'accessType' => $access_type,
215
+ 'updateProtectionActivated' => $this->core->elements['update_protection_activated'],
216
+ 'backupCreated' => $this->core->lang['backup_created'],
217
+ 'errorText' => esc_html__(
218
+ 'There was an error processing your request. Please reload the page and try again.',
219
+ 'boldgrid-backup'
220
+ ),
221
+ );
222
+ wp_localize_script( $handle, 'localizeScriptData', $localize_script_data );
223
+
224
+ wp_enqueue_script( $handle );
225
+ }
226
+
227
+ /**
228
+ * Enqueue scripts within the customizer.
229
+ *
230
+ * Currently this includes adding Update Protection to the themes area where
231
+ * users can upgrade themes.
232
+ *
233
+ * @since 1.6.0
234
+ */
235
+ public function enqueue_customize_controls() {
236
+ $handle = 'boldgrid-backup-admin-customizer';
237
+
238
+ wp_enqueue_style(
239
+ $handle,
240
+ plugin_dir_url( __FILE__ ) . 'css/boldgrid-backup-admin-customizer.css', array(),
241
+ BOLDGRID_BACKUP_VERSION,
242
+ 'all'
243
+ );
244
+
245
+ wp_register_script(
246
+ $handle,
247
+ plugin_dir_url( __FILE__ ) . 'js/boldgrid-backup-admin-customizer.js',
248
+ array( 'jquery' ),
249
+ BOLDGRID_BACKUP_VERSION,
250
+ false
251
+ );
252
+
253
+ $translations = array(
254
+ 'update_data' => wp_get_update_data(),
255
+ 'in_progress_notice' => $this->core->in_progress->get_notice_markup(),
256
+ 'nonce' => wp_create_nonce( 'boldgrid_backup_customizer' ),
257
+ 'is_rollback_enabled' => $this->is_enabled(),
258
+ );
259
+ wp_localize_script( $handle, 'boldgridBackupCustomizer', $translations );
260
+ wp_enqueue_script( $handle );
261
+
262
+ $this->enqueue_backup_scripts();
263
+
264
+ $this->enqueue_rollback_scripts();
265
+
266
+ $this->enqueue_update_selectors();
267
+ }
268
+
269
+ /**
270
+ * Enqueue scripts needed for the home page (the archives page).
271
+ *
272
+ * @since 1.6.0
273
+ */
274
+ public function enqueue_home_scripts() {
275
+ wp_enqueue_script(
276
+ 'boldgrid-backup-admin-home',
277
+ plugin_dir_url( __FILE__ ) . 'js/boldgrid-backup-admin-home.js',
278
+ array( 'jquery' ),
279
+ BOLDGRID_BACKUP_VERSION,
280
+ false
281
+ );
282
+ }
283
+
284
+ /**
285
+ * Enqueue scripts required for the rollback functionality.
286
+ *
287
+ * The $deadline param may not always be passed in because there may not be
288
+ * a deadline yet. Here's such a scenario:
289
+ * # We're showing the "backup now for upgrade protection" notice.
290
+ * # The user creates a backup.
291
+ * # The user does an ajaxy theme update.
292
+ * # After the update, we show the countdown via ajax.
293
+ * These scripts were already enqueued on page load for the possiblity that
294
+ * the user ajaxy updates. If they do, the countdown notice will be shown
295
+ * and the button clicks need to be handled.
296
+ *
297
+ * @since 1.6.0
298
+ *
299
+ * @param int $deadline Auto rollback deadline (unix timestamp).
300
+ */
301
+ public function enqueue_rollback_scripts( $deadline = null ) {
302
+ $handle = 'boldgrid-backup-admin-rollback';
303
+
304
+ wp_register_script(
305
+ $handle,
306
+ plugin_dir_url( __FILE__ ) . 'js/boldgrid-backup-admin-rollback.js',
307
+ array( 'jquery' ),
308
+ BOLDGRID_BACKUP_VERSION,
309
+ false
310
+ );
311
+
312
+ if ( ! empty( $deadline ) ) {
313
+ $localize_script_data = array(
314
+ // Include the time (in ISO 8601 format).
315
+ 'rolloutDeadline' => date( 'c', $deadline ),
316
+ );
317
+ wp_localize_script( $handle, 'boldgrid_backup_admin_rollback', $localize_script_data );
318
+ }
319
+
320
+ wp_enqueue_script( $handle );
321
+
322
+ /*
323
+ * If there is a countdown showing in the customizer, there's also a
324
+ * rollback button, which is handled by the archive_actions class.
325
+ */
326
+ $this->core->archive_actions->enqueue_scripts();
327
+ }
328
+
329
+ /**
330
+ * Enqueue update-selectors script.
331
+ *
332
+ * The update-selectors script is intended to help dynamically disable /
333
+ * enable any "update" buttons or links on a page.
334
+ *
335
+ * For example, if you are in the middle of making a backup, you probably
336
+ * shouldn't be performing any updates.
337
+ *
338
+ * @since 1.6.0
339
+ */
340
+ public function enqueue_update_selectors() {
341
+ if ( $this->on_update_page ) {
342
+ $handle = 'boldgrid-backup-admin-update-selectors';
343
+
344
+ wp_register_script(
345
+ $handle,
346
+ plugin_dir_url( __FILE__ ) . 'js/boldgrid-backup-admin-update-selectors.js',
347
+ array( 'jquery' ),
348
+ BOLDGRID_BACKUP_VERSION,
349
+ false
350
+ );
351
+
352
+ $localize_script_data = array(
353
+ // Generally used as the title attr of a disable update button.
354
+ 'backupInProgress' => __( 'Your website is currently being backed up. You can perform updates when the backup is complete.', 'boldgrid-backup' ),
355
+ 'waitClass' => 'bgbu-wait',
356
+ );
357
+
358
+ wp_localize_script( $handle, 'boldgrid_backup_admin_update_selectors', $localize_script_data );
359
+
360
+ wp_enqueue_script( $handle );
361
+ }
362
+ }
363
+
364
+ /**
365
+ * Show an admin notice if there is a pending rollback.
366
+ *
367
+ * Prior to @1.5.3 this method was in the core class.
368
+ *
369
+ * This method is called in the admin_notices hook.
370
+ *
371
+ * @since 1.0
372
+ *
373
+ * @return null
374
+ */
375
+ public function notice_countdown_show() {
376
+
377
+ // Process GET / POST info.
378
+ $action = ! empty( $_GET['action'] ) ? sanitize_key( $_GET['action'] ) : null;
379
+ $restore_now = ! empty( $_POST['restore_now'] );
380
+
381
+ $pending_rollback = get_site_option( 'boldgrid_backup_pending_rollback' );
382
+ $deadline = ! empty( $pending_rollback['deadline'] ) ? $pending_rollback['deadline'] : null;
383
+ $deadline_passed = ! empty( $deadline ) && $deadline <= time();
384
+
385
+ if ( $this->on_update_page ) {
386
+ $this->enqueue_rollback_scripts();
387
+ }
388
+
389
+ /*
390
+ * Updated and pending.
391
+ *
392
+ * The initial implementation of this variable is not very well
393
+ * documented. As of 1.6.0, this is the best interpretation.
394
+ *
395
+ * It appears that this var is meant to tell us when we're on a page in
396
+ * which a plugin/theme is being updated, such as:
397
+ * wp-admin/update-core.php?action=do-plugin-upgrade
398
+ * ... and we have a backup that is pending restoration.
399
+ *
400
+ * When you update a plugin via
401
+ * wp-admin/update-core.php?action=do-plugin-upgrade
402
+ * the $deadline won't be set until the iframe doing the upgrade completes.
403
+ * Example iframe: update.php?action=update-selected&plugins=plugin.php&_wpnonce=1234
404
+ *
405
+ * As long as we know we're on a page upgrading a plugin AND we have a
406
+ * pending rollback, we'll show the countdown (even though we don't have
407
+ * a deadline). Once the iframe loads, we'll read the deadline from the
408
+ * iframe and update the countdown.
409
+ *
410
+ * @todo Clean up the above comment in the future once determined this
411
+ * is accurate.
412
+ */
413
+ $updated_and_pending = 'update-core.php' === $this->core->pagenow && ! empty( $action ) && ! empty( $pending_rollback );
414
+
415
+ // If we're restoring a file, we don't need to show any notices.
416
+ if ( $restore_now ) {
417
+ return;
418
+ }
419
+
420
+ // If there is not a pending rollback, then abort.
421
+ if ( empty( $deadline ) && ! $updated_and_pending ) {
422
+ return;
423
+ }
424
+
425
+ $archives = $this->core->get_archive_list();
426
+ $archive_count = count( $archives );
427
+
428
+ /*
429
+ * If the deadline has passed or no backup archives to restore, then
430
+ * remove the pending rollback information and cron.
431
+ */
432
+ if ( $deadline_passed || 0 === $archive_count ) {
433
+ $this->cancel();
434
+ return;
435
+ }
436
+
437
+ /*
438
+ * Abort if we are updating core.
439
+ *
440
+ * The update of core is different than the updating of plugins or themes
441
+ * because instead of sitting on this page after the upgrade, the user
442
+ * is redirected to about.php.
443
+ *
444
+ * Because we're redirecting, there's no need to show the countdown on
445
+ * this page.
446
+ */
447
+ if ( $this->updating_core ) {
448
+ return;
449
+ }
450
+
451
+ wp_enqueue_style(
452
+ 'boldgrid-backup-admin-home',
453
+ plugin_dir_url( __FILE__ ) . 'css/boldgrid-backup-admin-home.css', array(),
454
+ BOLDGRID_BACKUP_VERSION,
455
+ 'all'
456
+ );
457
+
458
+ $this->enqueue_rollback_scripts( $deadline );
459
+
460
+ /*
461
+ * Create and display our notice.
462
+ *
463
+ * The boldgrid-backup-countdown class was added as of 1.6.0 so we can
464
+ * uniquely identify this notice.
465
+ */
466
+ $notice_markup = $this->notice_countdown_get();
467
+ do_action( 'boldgrid_backup_notice', $notice_markup, 'notice notice-warning is-dismissible boldgrid-backup-countdown' );
468
+
469
+ return;
470
+ }
471
+
472
+ /**
473
+ * Generate markup for the rollback notice.
474
+ *
475
+ * Prior to @1.5.3 this method was in the core class.
476
+ *
477
+ * @since 1.2
478
+ * @access private
479
+ *
480
+ * @param array $args {
481
+ * An array of arguments.
482
+ *
483
+ * @type int $restore_key Key index used for restoration.
484
+ * @type string $restore_filename Filename of the backup archive to be restored.
485
+ * }
486
+ * @return string The resulting markup.
487
+ */
488
+ public function notice_countdown_get( $args = array() ) {
489
+
490
+ // By default we will restore the newest backup.
491
+ if ( empty( $args ) ) {
492
+ $key = 0;
493
+ $archives = $this->core->get_archive_list();
494
+ $args = array(
495
+ 'restore_key' => $key,
496
+ 'restore_filename' => $archives[ $key ]['filename'],
497
+ );
498
+ }
499
+
500
+ $pending_rollback = get_site_option( 'boldgrid_backup_pending_rollback' );
501
+ $deadline = ! empty( $pending_rollback['deadline'] ) ? sprintf( '(<em>%1$s</em>)', date( 'g:i a', $this->core->utility->time( $pending_rollback['deadline'] ) ) ) : '';
502
+
503
+ $update_trigger = $this->notice_trigger_get();
504
+ $update_trigger = ! empty( $update_trigger ) ? sprintf( '<p>%1$s</p>', $update_trigger ) : '';
505
+
506
+ $nonce = wp_nonce_field( 'boldgrid_rollback_notice', 'cancel_rollback_auth', true, false );
507
+
508
+ $button_args = array(
509
+ 'button_text' => __( 'Rollback Site Now', 'boldgrid-backup' ),
510
+ );
511
+ $restore_button = $this->core->archive_actions->get_restore_button( $args['restore_filename'], $button_args );
512
+
513
+ $iso_time = ! empty( $pending_rollback['deadline'] ) ? date( 'c', $pending_rollback['deadline'] ) : null;
514
+ $rollback_deadline = sprintf( '<input type="hidden" id="rollback-deadline" value="%1$s" />', $iso_time );
515
+
516
+ $notice_markup = '
517
+ <div id="cancel-rollback-section">
518
+ <h2 class="header-notice">' . $this->core->lang['heading_update_protection'] . '</h2>
519
+
520
+ <p>
521
+ ' . $this->core->lang['icon_warning'] . ' ' . __( 'There is a pending automatic rollback using the most recent backup archive.', 'boldgrid-backup' ) . '
522
+ </p>
523
+
524
+ ' . $update_trigger . '
525
+
526
+ <p>
527
+ ' . __( 'Now is the time to test your website to ensure the upgrade did not break anything. If the upgrade did cause problems, you can click <strong>Rollback Site Now</strong> to restore your site to just before the update. If the update was a success, click <strong>Cancel Rollback</strong> so a backup is not automatically restored at the end of the countdown.', 'boldgrid-backup' ) . '
528
+ </p>
529
+
530
+ <p>
531
+ ' . sprintf( __( '<strong>Update Protection</strong> for <em>future updates</em> can be configured on your <a href="%1$s">Settings</a> page.', 'boldgrid-backup' ), admin_url( 'admin.php?page=boldgrid-backup-settings&section=section_updates' ) ) . '
532
+ </p>
533
+
534
+ <p>
535
+ <strong>' . __( 'Countdown', 'boldgrid-backup' ) . '</strong>:
536
+ <span id="rollback-countdown-timer">
537
+ <span class="spinner inline"></span>
538
+ </span>
539
+ ' . $deadline . '
540
+ </p>
541
+
542
+ <form action="#" id="cancel-rollback-form" method="POST">
543
+ ' . $nonce . '
544
+ <p>
545
+ <a id="cancel-rollback-button" class="button button-primary">' . __( 'Cancel Rollback', 'boldgrid-backup' ) . '</a> <span class="spinner"></span>
546
+ </p>
547
+ </form>
548
+ </div>
549
+
550
+ <p>' . $restore_button . '</p>
551
+
552
+ <div id="cancel-rollback-results"></div>
553
+ ' . $rollback_deadline;
554
+
555
+ return $notice_markup;
556
+ }
557
+
558
+ /**
559
+ * Get the pending rollback deadline (in unix seconds).
560
+ *
561
+ * @since 1.2
562
+ *
563
+ * @return int The pending rollback deadline in unix seconds, or zero if not present.
564
+ */
565
+ public function get_deadline() {
566
+ // Get pending rollback information.
567
+ $pending_rollback = get_site_option( 'boldgrid_backup_pending_rollback' );
568
+
569
+ // Return pending rollback deadline, or 0 if not present.
570
+ if ( empty( $pending_rollback['deadline'] ) ) {
571
+ return 0;
572
+ } else {
573
+ return $pending_rollback['deadline'];
574
+ }
575
+ }
576
+
577
+ /**
578
+ * Return a bool indicating whether or not auto_rollback is enabled.
579
+ *
580
+ * @since 1.6.0
581
+ *
582
+ * @return bool
583
+ */
584
+ public function is_enabled() {
585
+ $settings = $this->core->settings->get_settings();
586
+
587
+ return isset( $settings['auto_rollback'] ) && 1 === $settings['auto_rollback'];
588
+ }
589
+
590
+ /**
591
+ * Create markup to show what was updated.
592
+ *
593
+ * @since 1.5.3
594
+ *
595
+ * @return mixed String on success, false on failure.
596
+ */
597
+ public function notice_trigger_get() {
598
+ $pending_rollback = get_site_option( 'boldgrid_backup_pending_rollback' );
599
+ $notice = false;
600
+ $li = array();
601
+
602
+ if ( empty( $pending_rollback['update_trigger'] ) ) {
603
+ return false;
604
+ }
605
+
606
+ $trigger = $pending_rollback['update_trigger'];
607
+
608
+ if ( 'update' !== $trigger['action'] ) {
609
+ return false;
610
+ }
611
+
612
+ switch ( $trigger['type'] ) {
613
+ case 'core':
614
+ $wordpress_version = get_bloginfo( 'version' );
615
+ $notice = sprintf( __( 'WordPress was recently updated to version %1$s.', 'boldgrid-backup' ), $wordpress_version );
616
+ break;
617
+ case 'theme':
618
+ foreach ( $trigger['themes'] as $theme ) {
619
+ $data = wp_get_theme( $theme );
620
+ $li[] = sprintf( '<strong>%1$s</strong> to version %2$s', $data->get( 'Name' ), $data->get( 'Version' ) );
621
+ }
622
+ $notice = __( 'The following theme(s) were recently updated:', 'boldgrid-backup' ) . '<br />';
623
+ $notice .= implode( '<br />', $li );
624
+ break;
625
+ case 'plugin':
626
+ foreach ( $trigger['plugins'] as $plugin ) {
627
+ $data = $this->core->utility->get_plugin_data( $plugin );
628
+ $li[] = sprintf( '<strong>%1$s</strong> to version %2$s', $data['Name'], $data['Version'] );
629
+ }
630
+ $notice = __( 'The following plugin(s) were recently updated:', 'boldgrid-backup' ) . '<br />';
631
+ $notice .= implode( '<br />', $li );
632
+ break;
633
+ }
634
+
635
+ return $notice;
636
+ }
637
+
638
+ /**
639
+ * Generate markup for "You should make a backup for updating".
640
+ *
641
+ * @since 1.5.3
642
+ *
643
+ * @return string
644
+ */
645
+ public function notice_backup_get() {
646
+ $notice_text = sprintf( '<h2 class="header-notice">%1$s</h2>', __( 'BoldGrid Backup - Update Protection', 'boldgrid-backup' ) );
647
+
648
+ $notice_text .= '<p>';
649
+
650
+ switch ( $this->core->pagenow ) {
651
+ case 'update-core.php':
652
+ $notice_text .= __( 'On this page you are able to update WordPress, Plugins, and Themes.' ) . ' ';
653
+ break;
654
+ case 'plugins.php':
655
+ $notice_text .= __( 'On this page you are able to update plugins.' ) . ' ';
656
+ break;
657
+ }
658
+
659
+ $notice_text .= __( 'It is recommended to backup your site before performing updates. If you perform a backup here, before performing updates, then an automatic rollback is possible.', 'boldgrid-backup' );
660
+
661
+ $notice_text .= '</p>';
662
+
663
+ $notice_text .= sprintf(
664
+ '<p id="protection_enabled">%1$s %2$s</p>',
665
+ $this->core->lang['icon_warning'],
666
+ __( 'Update protection not available until you click <strong>Backup Site Now</strong> and a backup is created.', 'boldgrid-backup' )
667
+ );
668
+
669
+ return $notice_text;
670
+ }
671
+
672
+ /**
673
+ * Show an admin notice on the WordPress Updates page.
674
+ *
675
+ * Prior to @1.5.3 this method was in the core class.
676
+ *
677
+ * @since 1.0
678
+ *
679
+ * @global string $pagenow
680
+ */
681
+ public function notice_backup_show() {
682
+
683
+ /*
684
+ * This method is hooked into admin_notices. If we don't have auto_rollback
685
+ * enabled, then we can abort right now.
686
+ */
687
+ if ( ! $this->is_enabled() ) {
688
+ return;
689
+ }
690
+
691
+ $display = false;
692
+
693
+ $configs = array(
694
+ array(
695
+ 'pagenow' => 'plugins.php',
696
+ 'check' => 'plugins',
697
+ ),
698
+ array(
699
+ 'pagenow' => 'themes.php',
700
+ 'check' => 'themes',
701
+ ),
702
+ array(
703
+ 'pagenow' => 'update-core.php',
704
+ 'check' => 'total',
705
+ ),
706
+ );
707
+
708
+ /**
709
+ * Allow other plugins to filter the pages the backup notice shows on.
710
+ *
711
+ * @since 1.6.0
712
+ *
713
+ * @param array $configs
714
+ */
715
+ $configs = apply_filters( 'boldgrid_backup_notice_show_configs', $configs );
716
+
717
+ /*
718
+ * Based on our $configs, determine if we need to show a notice.
719
+ *
720
+ * The nested if's below are designed to save resources and only call
721
+ * wp_get_update_data() if we are on a $pagenow that should show the
722
+ * notice.
723
+ */
724
+ foreach ( $configs as $config ) {
725
+ if ( $this->core->pagenow === $config['pagenow'] ) {
726
+ $update_data = ! isset( $update_data ) ? wp_get_update_data() : $update_data;
727
+ if ( $update_data['counts'][ $config['check'] ] ) {
728
+ $display = true;
729
+ break;
730
+ }
731
+ }
732
+ }
733
+
734
+ if ( ! $display || $this->updating_core ) {
735
+ return;
736
+ }
737
+
738
+ // Get pending rollback information.
739
+ $pending_rollback = get_site_option( 'boldgrid_backup_pending_rollback' );
740
+
741
+ // If we're in the middle of a countdown, abort.
742
+ if ( ! empty( $pending_rollback['deadline'] ) ) {
743
+ return;
744
+ }
745
+
746
+ // If there is a pending rollback, then abort.
747
+ if ( ! empty( $pending_rollback['lastmodunix'] ) ) {
748
+ $this->notice_activated_show();
749
+ return;
750
+ }
751
+
752
+ $this->enqueue_backup_scripts();
753
+
754
+ /*
755
+ * Show admin notice.
756
+ *
757
+ * The boldgrid-backup-protect-now class was added to the notice as of
758
+ * 1.6.0 so that we can uniquely identify this notice on the page.
759
+ */
760
+ $backup_button = include BOLDGRID_BACKUP_PATH . '/admin/partials/boldgrid-backup-admin-backup-button.php';
761
+ $notice = $this->notice_backup_get();
762
+ do_action( 'boldgrid_backup_notice', $notice . $backup_button, 'notice notice-warning is-dismissible boldgrid-backup-protect-now' );
763
+ }
764
+
765
+ /**
766
+ * Callback function for the hook "upgrader_process_complete".
767
+ *
768
+ * Prior to @1.5.3 this method was in the core class.
769
+ *
770
+ * @since 1.2
771
+ *
772
+ * @link https://developer.wordpress.org/reference/hooks/upgrader_process_complete/
773
+ * @see Boldgrid_Backup_Admin_Cron::add_restore_cron().
774
+ *
775
+ * @param object $upgrader_object Plugin_Upgrader Object
776
+ * @param array $options See https://pastebin.com/ah4E048B
777
+ */
778
+ public function notice_deadline_show( $upgrader_object, $options ) {
779
+
780
+ /*
781
+ * This method is ran both when a plugin/theme/WP is updated, and when
782
+ * a plugin is simply uploaded. As of 1.6.0, this plugin does not offer
783
+ * update protection for plugin uploads and activation, only for updates.
784
+ *
785
+ * @todo Allow update protection for plugin activation.
786
+ */
787
+ if ( empty( $options['action'] ) || 'update' !== $options['action'] ) {
788
+ return;
789
+ }
790
+
791
+ // Add/update restoration cron job.
792
+ $this->add_cron();
793
+
794
+ $this->set_update_trigger( $options );
795
+
796
+ // If not on an admin page, then abort.
797
+ if ( ! is_admin() ) {
798
+ return;
799
+ }
800
+
801
+ // Get pending rollback deadline.
802
+ $deadline = $this->get_deadline();
803
+
804
+ // If there is not a pending rollback, then abort.
805
+ if ( empty( $deadline ) ) {
806
+ return;
807
+ }
808
+
809
+ // Get the ISO time (in ISO 8601 format).
810
+ $iso_time = date( 'c', $deadline );
811
+
812
+ // Print a hidden div with the time, so that JavaScript can read it.
813
+ printf( '<div class="hidden" id="rollback-deadline">%1$s</div>', $iso_time );
814
+ }
815
+
816
+ /**
817
+ * Save the update trigger.
818
+ *
819
+ * @since 1.5.3
820
+ *
821
+ * @param array $options https://pastebin.com/ah4E048B
822
+ */
823
+ public function set_update_trigger( $options ) {
824
+ if ( empty( $options ) || ! is_array( $options ) ) {
825
+ return;
826
+ }
827
+
828
+ $pending_rollback = get_site_option( 'boldgrid_backup_pending_rollback' );
829
+
830
+ if ( empty( $pending_rollback ) || ! is_array( $pending_rollback ) ) {
831
+ return;
832
+ }
833
+
834
+ $pending_rollback['update_trigger'] = $options;
835
+
836
+ update_site_option( 'boldgrid_backup_pending_rollback', $pending_rollback );
837
+ }
838
+
839
+ /**
840
+ * Get our 'activated' notice.
841
+ *
842
+ * This is the notice that says you're protected, go ahead and update.
843
+ *
844
+ * @since 1.6.0
845
+ *
846
+ * @return array
847
+ */
848
+ public function notice_activated_get() {
849
+
850
+ $pending_rollback = get_site_option( 'boldgrid_backup_pending_rollback' );
851
+
852
+ $theme_message = __( 'If you update a theme on this page, an auto rollback will occur if anything goes wrong.', 'boldgrid-backup' );
853
+
854
+ $message = '<h2 class="header-notice">' . $this->core->lang['heading_update_protection'] . '</h2>';
855
+
856
+ $message .= sprintf( '<p>%1$s</p>', $this->core->elements['update_protection_activated'] );
857
+
858
+ $message .= '<p>';
859
+
860
+ $message .= sprintf(
861
+ __( 'You last made a backup %1$s ago.', 'boldgrid-backup' ),
862
+ human_time_diff( $pending_rollback['lastmodunix'], time() )
863
+ ) . ' ';
864
+
865
+ switch ( $this->core->pagenow ) {
866
+ case 'update-core.php':
867
+ $message .= __( 'If you update WordPress, any plugins, or any themes on this page, an auto rollback will occur if anything goes wrong.', 'boldgrid-backup' );
868
+ break;
869
+ case 'plugins.php':
870
+ $message .= __( 'If you update a plugin on this page, an auto rollback will occur if anything goes wrong.', 'boldgrid-backup' );
871
+ break;
872
+ case 'themes.php':
873
+ $message .= $theme_message;
874
+ break;
875
+ }
876
+
877
+ // Customize our message for the "update theme" feature within the customizer.
878
+ $path = parse_url( wp_get_referer(), PHP_URL_PATH );
879
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX && 'customize.php' === substr( $path, -1 * strlen( 'customize.php' ) ) ) {
880
+ $message .= $theme_message;
881
+ }
882
+
883
+ $message .= '</p>';
884
+
885
+ $message = array(
886
+ 'html' => $message,
887
+ 'class' => 'notice notice-success is-dismissible boldgrid-backup-protected',
888
+ );
889
+
890
+ return $message;
891
+ }
892
+
893
+ /**
894
+ * Show a message that the user has backup protection.
895
+ *
896
+ * If we have a pending $pending_rollback['lastmodunix'], tell the user,
897
+ * "You're protected" rather than, "Create a backup and protect yourself".
898
+ *
899
+ * This method is called via:
900
+ * action:admin_notices > self::notice_backup_show().
901
+ *
902
+ * @since 1.5.3
903
+ */
904
+ public function notice_activated_show() {
905
+
906
+ /*
907
+ * If we're in the middle of upgrading something, such as:
908
+ * update-core.php?action=do-theme-upgrade
909
+ * Then there's no need to show a message.
910
+ */
911
+ if ( ! empty( $_GET['action'] ) ) {
912
+ return;
913
+ }
914
+
915
+ $message = $this->notice_activated_get();
916
+
917
+ do_action( 'boldgrid_backup_notice', $message['html'], $message['class'] );
918
+ }
919
+
920
+ /**
921
+ * Callback function for canceling a pending rollback.
922
+ *
923
+ * Prior to @1.5.3 this method was in the core class.
924
+ *
925
+ * @since 1.0
926
+ */
927
+ public function wp_ajax_cancel() {
928
+ // Check user capabilities.
929
+ if ( ! current_user_can( 'update_plugins' ) ) {
930
+ wp_die(
931
+ '<div class="error"><p>' .
932
+ esc_html__( 'Security violation (not authorized).', 'boldgrid-backup' ) .
933
+ '</p></div>'
934
+ );
935
+ }
936
+
937
+ // Verify nonce, or die with an error message.
938
+ if ( ! isset( $_POST['cancel_rollback_auth'] ) ||
939
+ 1 !== check_ajax_referer( 'boldgrid_rollback_notice', 'cancel_rollback_auth', false ) ) {
940
+ wp_die(
941
+ '<div class="error"><p>' .
942
+ esc_html__( 'Security violation (invalid nonce).', 'boldgrid-backup' ) .
943
+ '</p></div>'
944
+ );
945
+ }
946
+
947
+ // Clear rollback information.
948
+ $this->cancel();
949
+
950
+ // Echo a success message.
951
+ echo '<p>Automatic rollback has been canceled.</p>';
952
+
953
+ // End nicely.
954
+ wp_die();
955
+ }
956
+
957
+ /**
958
+ * Callback for getting the rollback deadline.
959
+ *
960
+ * Prior to @1.5.3 this method was in the core class.
961
+ *
962
+ * @since 1.2.1
963
+ */
964
+ public function wp_ajax_get_deadline() {
965
+ // Check user capabilities.
966
+ if ( ! current_user_can( 'update_plugins' ) ) {
967
+ wp_die();
968
+ }
969
+
970
+ // Get the rollback deadline.
971
+ $deadline = $this->get_deadline();
972
+
973
+ // If there is no deadline, then die.
974
+ if ( empty( $deadline ) ) {
975
+ wp_die();
976
+ }
977
+
978
+ // Convert the deadline to ISO time (in ISO 8601 format).
979
+ $iso_time = date( 'c', $deadline );
980
+ wp_die( $iso_time );
981
+ }
982
+
983
+ /**
984
+ * Get the countdown method via an ajax call.
985
+ *
986
+ * Useful when plugins are updated via ajaxy and we need to show the
987
+ * countdown without refreshing the page.
988
+ *
989
+ * @since 1.6.0
990
+ */
991
+ public function wp_ajax_get_countdown_notice() {
992
+ if ( ! current_user_can( 'update_plugins' ) ) {
993
+ wp_send_json_error();
994
+ }
995
+
996
+ $pending_rollback = get_site_option( 'boldgrid_backup_pending_rollback' );
997
+ if ( empty( $pending_rollback ) ) {
998
+ wp_send_json_error();
999
+ }
1000
+
1001
+ $notice = $this->notice_countdown_get();
1002
+ $notice = '<div class="notice notice-warning is-dismissible boldgrid-backup-countdown">' . $notice . '</div>';
1003
+
1004
+ wp_send_json_success( $notice );
1005
+ }
1006
+
1007
+ /**
1008
+ * Get our "protect" notice.
1009
+ *
1010
+ * This will return either the "get protected" or "you are protected" notice.
1011
+ *
1012
+ * @since 1.6.0
1013
+ */
1014
+ public function wp_ajax_get_protect_notice() {
1015
+ if ( ! current_user_can( 'update_plugins' ) ) {
1016
+ wp_send_json_error();
1017
+ }
1018
+
1019
+ $pending_rollback = get_site_option( 'boldgrid_backup_pending_rollback' );
1020
+ if ( ! empty( $pending_rollback ) ) {
1021
+ // You're protected, go ahead and update.
1022
+ $message = $this->notice_activated_get();
1023
+ $notice = sprintf( '<div class="%1$s">%2$s</div>', $message['class'], $message['html'] );
1024
+ } else {
1025
+ // You're not protected, make a backup first.
1026
+ $notice = $this->notice_backup_get();
1027
+ $backup_button = include BOLDGRID_BACKUP_PATH . '/admin/partials/boldgrid-backup-admin-backup-button.php';
1028
+ $notice = '<div class="notice notice-warning is-dismissible boldgrid-backup-protect-now">' . $notice . $backup_button . '</div>';
1029
+ }
1030
+
1031
+ wp_send_json_success( $notice );
1032
+ }
1033
+ }
admin/class-boldgrid-backup-admin-backup-dir.php ADDED
@@ -0,0 +1,391 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Boldgrid Backup Admin Backup Dir.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.1
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * Boldgrid Backup Admin Backup Dir class.
17
+ *
18
+ * @since 1.5.1
19
+ */
20
+ class Boldgrid_Backup_Admin_Backup_Dir {
21
+
22
+ /**
23
+ * Backup directory.
24
+ *
25
+ * @since 1.5.1
26
+ * @var string
27
+ */
28
+ public $backup_directory;
29
+
30
+ /**
31
+ * The core class object.
32
+ *
33
+ * @since 1.5.1
34
+ * @access private
35
+ * @var Boldgrid_Backup_Admin_Core
36
+ */
37
+ private $core;
38
+
39
+ /**
40
+ * An array of errors.
41
+ *
42
+ * @since 1.5.1
43
+ * @access public
44
+ * @var array
45
+ */
46
+ public $errors = array();
47
+
48
+ /**
49
+ * The backup directory with the absolute path removed.
50
+ *
51
+ * @since 1.5.1
52
+ * @access public
53
+ * @var string
54
+ */
55
+ public $without_abspath;
56
+
57
+ /**
58
+ * Constructor.
59
+ *
60
+ * @since 1.5.1
61
+ *
62
+ * @param Boldgrid_Backup_Admin_Core $core
63
+ */
64
+ public function __construct( $core ) {
65
+ $this->core = $core;
66
+ }
67
+
68
+ /**
69
+ * Create our backup directory and necessary files.
70
+ *
71
+ * @since 1.5.1
72
+ *
73
+ * @param string $backup_dir
74
+ * @return mixed False on failure, trailingslashed $backup_dir on success.
75
+ */
76
+ public function create( $backup_dir ) {
77
+ $check_permissions = __( 'Please ensure your backup directory exists and has the proper read, write, and modify permissions.', 'boldgrid-backup' );
78
+
79
+ $cannot_create = __( 'Unable to create necessary file: %1$s<br />%2$s', 'boldgrid-backup' );
80
+ $cannot_write = __( 'Unable to write to necessary file: %1$s<br />%2$s', 'boldgrid-backup' );
81
+
82
+ $backup_dir = untrailingslashit( $backup_dir );
83
+
84
+ $htaccess_path = $backup_dir . DIRECTORY_SEPARATOR . '.htaccess';
85
+ $index_html_path = $backup_dir . DIRECTORY_SEPARATOR . 'index.html';
86
+ $index_php_path = $backup_dir . DIRECTORY_SEPARATOR . 'index.php';
87
+
88
+ $files = array(
89
+ array(
90
+ 'type' => 'dir',
91
+ 'path' => $backup_dir,
92
+ 'chmod' => 0700,
93
+ ),
94
+ array(
95
+ 'type' => 'file',
96
+ 'path' => $htaccess_path,
97
+ 'contents' => "<IfModule mod_access_compat.c>\nOrder Allow,Deny\nDeny from all\n</IfModule>\nOptions -Indexes\n",
98
+ ),
99
+ array(
100
+ 'type' => 'file',
101
+ 'path' => $index_html_path,
102
+ ),
103
+ array(
104
+ 'type' => 'file',
105
+ 'path' => $index_php_path,
106
+ ),
107
+ );
108
+
109
+ /**
110
+ * Allow other plugins to modify our config.
111
+ *
112
+ * @since 1.5.3
113
+ *
114
+ * @param array $files
115
+ * @param string $backup_dir
116
+ */
117
+ $files = apply_filters( 'boldgrid_backup_create_dir_config', $files, $backup_dir );
118
+
119
+ foreach ( $files as $file ) {
120
+ switch ( $file['type'] ) {
121
+ case 'dir':
122
+ if ( ! $this->core->wp_filesystem->exists( $file['path'] ) ) {
123
+ $chmod = ! empty( $file['chmod'] ) ? $file['chmod'] : false;
124
+ $created = $this->core->wp_filesystem->mkdir( $file['path'], $chmod );
125
+ if ( ! $created ) {
126
+ $this->errors[] = sprintf( $cannot_create, $file['path'], $check_permissions );
127
+ return false;
128
+ }
129
+ }
130
+ break;
131
+ case 'file':
132
+ if ( ! $this->core->wp_filesystem->exists( $file['path'] ) ) {
133
+ $created = $this->core->wp_filesystem->touch( $file['path'] );
134
+ if ( ! $created ) {
135
+ $this->errors[] = sprintf( $cannot_create, $file['path'], $check_permissions );
136
+ return false;
137
+ }
138
+
139
+ if ( ! empty( $file['contents'] ) ) {
140
+ $written = $this->core->wp_filesystem->put_contents( $file['path'], $file['contents'] );
141
+ if ( ! $written ) {
142
+ $this->errors[] = sprintf( $cannot_write, $file['path'], $check_permissions );
143
+ return false;
144
+ }
145
+ }
146
+ }
147
+ break;
148
+ }
149
+ }
150
+
151
+ return $backup_dir;
152
+ }
153
+
154
+ /**
155
+ * Get and return the backup directory path.
156
+ *
157
+ * @since 1.0
158
+ *
159
+ * @return string|bool The backup directory path, or FALSE on error.
160
+ */
161
+ public function get() {
162
+
163
+ // If we've already set the backup directory, return it.
164
+ if ( ! empty( $this->backup_directory ) ) {
165
+ return $this->backup_directory;
166
+ }
167
+
168
+ // If we have it in the settings, then use it.
169
+ $settings = $this->core->settings->get_settings();
170
+ if ( ! empty( $settings['backup_directory'] ) ) {
171
+ $this->set( $settings['backup_directory'] );
172
+ return $this->backup_directory;
173
+ }
174
+
175
+ return $this->guess_and_set();
176
+ }
177
+
178
+ /**
179
+ * Get an array of possible backup directories.
180
+ *
181
+ * @since 1.5.1
182
+ * @return array
183
+ */
184
+ public function get_possible_dirs() {
185
+ $dirs = array();
186
+
187
+ // Standard value, the user's home directory.
188
+ $dirs[] = $this->core->config->get_home_directory();
189
+
190
+ if ( $this->core->test->is_windows() ) {
191
+ // C:\Users\user\AppData\Local
192
+ $dirs[] = $this->core->config->get_home_directory() . DIRECTORY_SEPARATOR . 'AppData' . DIRECTORY_SEPARATOR . 'Local';
193
+
194
+ if ( ! empty( $_SERVER['DOCUMENT_ROOT'] ) ) {
195
+ /*
196
+ * App_Data (Windows / Plesk).
197
+ *
198
+ * The App_Data folder is used as a data storage for the web
199
+ * application. It can store files such as .mdf, .mdb, and XML. It
200
+ * manages all of your application's data centrally. It is
201
+ * accessible from anywhere in your web application. The real
202
+ * advantage of the App_Data folder is that, any file you place
203
+ * there won't be downloadable.
204
+ */
205
+ $app_data = $_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . 'App_Data';
206
+ $dirs[] = str_replace( '\\\\', '\\', $app_data );
207
+ }
208
+ }
209
+
210
+ // As a last resort, we will store backups in the /wp-content folder.
211
+ $dirs[] = WP_CONTENT_DIR;
212
+
213
+ return $dirs;
214
+ }
215
+
216
+ /**
217
+ * Get the full path to a file in the backup dir.
218
+ *
219
+ * Returns backup_dir/$file.
220
+ *
221
+ * @since 1.5.4
222
+ *
223
+ * @param string $file
224
+ * @return string
225
+ */
226
+ public function get_path_to( $file ) {
227
+ $backup_dir = $this->get();
228
+ return Boldgrid_Backup_Admin_Utility::trailingslashit( $backup_dir ) . $file;
229
+ }
230
+
231
+ /**
232
+ * Set our backup directory.
233
+ *
234
+ * Based on the environment, determine where best backup dir should be and
235
+ * then create it.
236
+ *
237
+ * @since 1.5.2
238
+ *
239
+ * @return mixed Backup directory on success, false on failure.
240
+ */
241
+ public function guess_and_set() {
242
+ $possible_dirs = $this->get_possible_dirs();
243
+
244
+ foreach ( $possible_dirs as $possible_dir ) {
245
+
246
+ $possible_dir = untrailingslashit( $possible_dir );
247
+
248
+ // Ensure /parent_directory exists.
249
+ if ( ! $this->core->wp_filesystem->exists( $possible_dir ) ) {
250
+ continue;
251
+ }
252
+
253
+ /*
254
+ * Create the directory and all applicable files needed within for security,
255
+ * such as .htaccess / etc.
256
+ */
257
+ $possible_dir .= DIRECTORY_SEPARATOR . 'boldgrid_backup';
258
+ $backup_directory = $this->create( $possible_dir );
259
+ if ( ! $backup_directory ) {
260
+ continue;
261
+ }
262
+
263
+ // Validate read/write/modify/ect. permissions of directory.
264
+ $valid = $this->is_valid( $backup_directory );
265
+ if ( ! $valid ) {
266
+ continue;
267
+ }
268
+
269
+ // If we've gotten this far, we've got our new backup directory.
270
+ break;
271
+ }
272
+
273
+ if ( empty( $backup_directory ) ) {
274
+ return false;
275
+ }
276
+
277
+ $this->set( $backup_directory );
278
+
279
+ $settings['backup_directory'] = $backup_directory;
280
+ $this->core->settings->save( $settings );
281
+
282
+ return $this->backup_directory;
283
+ }
284
+
285
+ /**
286
+ * Determine if a file is in the backup directory.
287
+ *
288
+ * Pass in a filepath (relative to ABSPATH) and this method will determine
289
+ * if it's within the backup directory.
290
+ *
291
+ * Example $this->without_abspath:
292
+ * * wp-content/boldgrid-backup/
293
+ *
294
+ * Example $file(s):
295
+ * * (no) .htaccess
296
+ * * (no) wp-admin/admin.php
297
+ * * (yes) wp-content/boldgrid-backup/boldgrid-backup-domain-000-000-000.zip
298
+ *
299
+ * @since 1.5.1
300
+ *
301
+ * @param string $file
302
+ * @param bool $use_abspath Bool determining whether or not to use the
303
+ * backup directory in its absolute path.
304
+ * @return bool
305
+ */
306
+ public function file_in_dir( $file, $use_abspath = false ) {
307
+ if ( ! $use_abspath ) {
308
+ return false !== strpos( $file, $this->without_abspath );
309
+ } else {
310
+ return false !== strpos( $file, $this->backup_directory );
311
+ }
312
+ }
313
+
314
+ /**
315
+ * Validate backup directory.
316
+ *
317
+ * Make sure it exists, it's writable, etc.
318
+ *
319
+ * @param string $backup_dir
320
+ * @return bool
321
+ */
322
+ public function is_valid( $backup_dir ) {
323
+
324
+ if ( empty( $backup_dir ) ) {
325
+ return false;
326
+ }
327
+
328
+ $perms = $this->core->test->extensive_dir_test( $backup_dir );
329
+
330
+ if ( ! $perms['exists'] ) {
331
+ $this->errors[] = sprintf( __( 'Backup Directory does not exists: %1$s', 'boldgrid-backup' ), $backup_dir );
332
+ }
333
+
334
+ if ( ! $perms['read'] ) {
335
+ $this->errors[] = sprintf( __( 'Backup Directory does not have read permission: %1$s', 'boldgrid-backup' ), $backup_dir );
336
+ }
337
+
338
+ if ( ! $perms['rename'] ) {
339
+ $this->errors[] = sprintf( __( 'Backup Directory does not have permission to rename files: %1$s', 'boldgrid-backup' ), $backup_dir );
340
+ }
341
+
342
+ if ( ! $perms['delete'] ) {
343
+ $this->errors[] = sprintf( __( 'Backup Directory does not have permission to delete files: %1$s', 'boldgrid-backup' ), $backup_dir );
344
+ }
345
+
346
+ if ( ! $perms['dirlist'] ) {
347
+ $this->errors[] = sprintf( __( 'Backup Directory does not have permission to retrieve directory listing: %1$s', 'boldgrid-backup' ), $backup_dir );
348
+ }
349
+
350
+ /*
351
+ * Do not allow the ABSPATH (/home/user/wordpress) to be within the
352
+ * backup directory (/home/user).
353
+ *
354
+ * In the above example, we will create /home/user/.htaccess to prevent
355
+ * browsing of backups, and this would prevent all traffic to the ABSPATH.
356
+ */
357
+ $backup_dir = Boldgrid_Backup_Admin_Utility::trailingslashit( $backup_dir );
358
+ $abspath_in_dir = 0 === strpos( ABSPATH, $backup_dir );
359
+ if ( $abspath_in_dir ) {
360
+ $this->errors[] = sprintf(
361
+ __( 'Your <strong>WordPress directory</strong> <em>%1$s</em> cannot be within your <strong>backup directory</strong> %2$s.', 'boldgrid-backup' ),
362
+ ABSPATH,
363
+ $backup_dir
364
+ );
365
+ }
366
+
367
+ return $perms['exists'] && $perms['read'] && $perms['write'] && $perms['rename'] && $perms['delete'] && $perms['dirlist'] && ! $abspath_in_dir;
368
+ }
369
+
370
+ /**
371
+ * Even in a Windows environment, wp_filesystem->dirlist retrieves paths
372
+ * with a / instead of \. Fix $without_abspath so we can properly check if
373
+ * files are in the backup directory.
374
+ */
375
+ public function set( $backup_directory ) {
376
+
377
+ if ( empty( $backup_directory ) ) {
378
+ return false;
379
+ }
380
+
381
+ $created = $this->create( $backup_directory );
382
+ if ( ! $created ) {
383
+ return false;
384
+ }
385
+
386
+ $this->backup_directory = $backup_directory;
387
+
388
+ $this->without_abspath = str_replace( ABSPATH, '', $this->backup_directory );
389
+ $this->without_abspath = str_replace( '\\', '/', $this->without_abspath );
390
+ }
391
+ }
admin/class-boldgrid-backup-admin-compressor.php ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Compressor.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.1
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * Base Compressor class for which other compressors extend.
17
+ *
18
+ * @since 1.5.1
19
+ */
20
+ class Boldgrid_Backup_Admin_Compressor {
21
+
22
+ /**
23
+ * An instance of Boldgrid_Backup_Admin_Core.
24
+ *
25
+ * @since 1.5.1
26
+ * @var Boldgrid_Backup_Admin_Core object
27
+ */
28
+ public $core;
29
+
30
+ /**
31
+ * A reference to the global wp_filesystem.
32
+ *
33
+ * @since 1.5.1
34
+ * @var WP Filesystem
35
+ */
36
+ public $wp_filesystem;
37
+
38
+ /**
39
+ * Constructor.
40
+ *
41
+ * @since 1.5.1
42
+ *
43
+ * @global $wp_filesystem
44
+ *
45
+ * @param Boldgrid_Backup_Admin_Core $core
46
+ */
47
+ public function __construct( $core ) {
48
+ global $wp_filesystem;
49
+ $this->wp_filesystem = $wp_filesystem;
50
+
51
+ $this->core = $core;
52
+ }
53
+
54
+ /**
55
+ * Archive files.
56
+ *
57
+ * Default behaviour is to do nothing. Subclass is expected to override.
58
+ *
59
+ * @since 1.5.1
60
+ *
61
+ * @param array $filelist See Boldgrid_Backup_Admin_Filelist::get_total_size
62
+ * @param array $info {
63
+ * An array of data about the backup archive we are generating.
64
+ *
65
+ * @type string mode backup
66
+ * @type bool dryrun
67
+ * @type string compressor php_zip
68
+ * @type ing filesize 0
69
+ * @type bool save 1
70
+ * @type int total_size 0
71
+ * }
72
+ */
73
+ public function archive_files( $filelist, &$info ) {
74
+ return false;
75
+ }
76
+ }
admin/class-boldgrid-backup-admin-compressors.php ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Compressors.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.1
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup Admin Compressors class.
17
+ *
18
+ * @since 1.5.1
19
+ */
20
+ class Boldgrid_Backup_Admin_Compressors {
21
+
22
+ /**
23
+ * The core class object.
24
+ *
25
+ * @since 1.5.1
26
+ * @access private
27
+ * @var Boldgrid_Backup_Admin_Core
28
+ */
29
+ private $core;
30
+
31
+ /**
32
+ * The default compressors.
33
+ *
34
+ * WordPress ships out of the box with pcl_zip.
35
+ *
36
+ * @since 1.5.1
37
+ * @access public
38
+ * @var string
39
+ */
40
+ public $default = 'pcl_zip';
41
+
42
+ /**
43
+ * Constructor.
44
+ *
45
+ * @since 1.5.1
46
+ *
47
+ * @param Boldgrid_Backup_Admin_Core $core Core class object.
48
+ */
49
+ public function __construct( $core ) {
50
+ $this->core = $core;
51
+
52
+ /*
53
+ * If ZipArchive is available, make it the default. Tests show it is
54
+ * superior to PclZip.
55
+ */
56
+ if ( class_exists( 'Boldgrid_Backup_Admin_Compressor_Php_Zip' ) && Boldgrid_Backup_Admin_Compressor_Php_Zip::is_available() ) {
57
+ $this->default = 'php_zip';
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Get the compressor type we will use, such as 'php_zip'.
63
+ *
64
+ * @since 1.5.1
65
+ *
66
+ * @return string
67
+ */
68
+ public function get() {
69
+ $settings = $this->core->settings->get_settings();
70
+ $available_compressors = $this->get_available();
71
+
72
+ /*
73
+ * If we have a compressor saved in our settings and it is an
74
+ * available compressor, then use it.
75
+ */
76
+ if ( ! empty( $settings['compressor'] ) && in_array( $settings['compressor'], $available_compressors, true ) ) {
77
+ return $settings['compressor'];
78
+ }
79
+
80
+ // Otherwise, return the default.
81
+ return $this->default;
82
+ }
83
+
84
+ /**
85
+ * Get all available compressors.
86
+ *
87
+ * @since 1.5.1
88
+ *
89
+ * @return array
90
+ */
91
+ public function get_available() {
92
+ return $this->core->config->get_available_compressors();
93
+ }
94
+
95
+ /**
96
+ * Set php_zip (ZipArchive) as our compressor/extractor.
97
+ *
98
+ * @since 1.5.2
99
+ *
100
+ * @return bool True on success.
101
+ */
102
+ public function set_php_zip() {
103
+ if ( Boldgrid_Backup_Admin_Compressor_Php_Zip::is_available() ) {
104
+ $settings = $this->core->settings->get_settings();
105
+ $settings['compressor'] = 'php_zip';
106
+ $settings['extractor'] = 'php_zip';
107
+ return $this->core->settings->save( $settings );
108
+ }
109
+
110
+ return false;
111
+ }
112
+
113
+ /**
114
+ * Hook into WordPress' filter: unzip_file_use_ziparchive
115
+ *
116
+ * @since 1.5.1
117
+ *
118
+ * @return bool
119
+ */
120
+ public function unzip_file_use_ziparchive() {
121
+ $settings = $this->core->settings->get_settings();
122
+
123
+ /*
124
+ * By default WordPress is set to use ZipArchive by default. Only use
125
+ * PclZip if we explicitly set it.
126
+ */
127
+ if ( ! empty( $settings['extractor'] ) && 'pcl_zip' === $settings['extractor'] ) {
128
+ return false;
129
+ }
130
+
131
+ return true;
132
+ }
133
+ }
admin/class-boldgrid-backup-admin-config.php ADDED
@@ -0,0 +1,466 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The admin-specific configuration class for the plugin
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.0
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup admin configuration class.
17
+ *
18
+ * @since 1.0
19
+ */
20
+ class Boldgrid_Backup_Admin_Config {
21
+ /**
22
+ * The core class object.
23
+ *
24
+ * @since 1.0
25
+ * @access private
26
+ * @var Boldgrid_Backup_Admin_Core
27
+ */
28
+ private $core;
29
+
30
+ /**
31
+ * User home directory.
32
+ *
33
+ * @since 1.0
34
+ * @access private
35
+ * @var string
36
+ */
37
+ private $home_dir;
38
+
39
+ /**
40
+ * Backup directory.
41
+ *
42
+ * @since 1.0
43
+ * @access private
44
+ * @var string
45
+ */
46
+ private $backup_directory;
47
+
48
+ /**
49
+ * Available compressors.
50
+ *
51
+ * @since 1.0
52
+ * @access private
53
+ * @var array
54
+ */
55
+ private $available_compressors = array();
56
+
57
+ /**
58
+ * The default retention.
59
+ *
60
+ * @since 1.3.1
61
+ * @access private
62
+ * @var int
63
+ */
64
+ private $default_retention = 5;
65
+
66
+ /**
67
+ * This is the premium version of the plugin.
68
+ *
69
+ * @since 1.3.1
70
+ * @access private
71
+ * @var bool
72
+ */
73
+ private $is_premium = false;
74
+
75
+ /**
76
+ * Whether or not the premium plugin is activated.
77
+ *
78
+ * @since 1.5.4
79
+ * @access public
80
+ * @var bool
81
+ */
82
+ public $is_premium_active = false;
83
+
84
+ /**
85
+ * Whether or not we have a premium license and the premium extension is
86
+ * installed.
87
+ *
88
+ * @since 1.5.4
89
+ * @access public
90
+ * @var bool
91
+ */
92
+ public $is_premium_done = false;
93
+
94
+ /**
95
+ * Whether or not the premium extension is installed (we didn't say activated,
96
+ * just installed, the files exist on the server).
97
+ *
98
+ * @since 1.5.4
99
+ * @access public
100
+ * @var bool
101
+ */
102
+ public $is_premium_installed = false;
103
+
104
+ /**
105
+ * Language.
106
+ *
107
+ * @since 1.3.1
108
+ * @access public
109
+ * @var array
110
+ */
111
+ public $lang = array();
112
+
113
+ /**
114
+ * License, an instance of \Boldgrid\Library\Library\License.
115
+ *
116
+ * @since 1.6.0
117
+ * @access protected
118
+ * @var \Boldgrid\Library\Library\License
119
+ */
120
+ protected $license;
121
+
122
+ /**
123
+ * Constructor.
124
+ *
125
+ * @since 1.0
126
+ *
127
+ * @param Boldgrid_Backup_Admin_Core $core Core class object.
128
+ */
129
+ public function __construct( $core ) {
130
+ // Save the Boldgrid_Backup_Admin_Core object as a class property.
131
+ if ( is_object( $core ) ) {
132
+ $this->core = $core;
133
+ }
134
+
135
+ if ( class_exists( '\Boldgrid\Library\Library\License' ) ) {
136
+ $this->license = new \Boldgrid\Library\Library\License();
137
+ $this->is_premium = $this->license->isPremium( 'boldgrid-backup' );
138
+ }
139
+
140
+ $this->set_lang();
141
+ }
142
+
143
+ /**
144
+ * Get the user home directory.
145
+ *
146
+ * @since 1.0
147
+ *
148
+ * @return string The path to the user home directory.
149
+ */
150
+ public function get_home_directory() {
151
+ // If home directory was already set, then return it.
152
+ if ( ! empty( $this->home_dir ) ) {
153
+ return $this->home_dir;
154
+ }
155
+
156
+ if ( $this->core->test->is_windows() && $this->core->test->is_plesk() ) {
157
+ /*
158
+ * Plesk's File Manager labels C:\Inetpub\vhosts\domain.com as the
159
+ * "Home directory". If we find we cannot read that directory, then
160
+ * we'll use the document root as the home directory.
161
+ */
162
+ $home_dir = dirname( $_SERVER['DOCUMENT_ROOT'] );
163
+ if ( ! $this->core->wp_filesystem->is_readable( $home_dir ) ) {
164
+ $home_dir = $_SERVER['DOCUMENT_ROOT'];
165
+ }
166
+ } elseif ( $this->core->test->is_windows() ) {
167
+ // Windows.
168
+ $home_drive = ( ! empty( $_SERVER['HOMEDRIVE'] ) ? $_SERVER['HOMEDRIVE'] : null );
169
+ $home_path = ( ! empty( $_SERVER['HOMEPATH'] ) ? $_SERVER['HOMEPATH'] : null );
170
+
171
+ if ( ! ( empty( $home_drive ) || empty( $home_path ) ) ) {
172
+ $home_dir = $home_drive . $home_path;
173
+ }
174
+
175
+ // If still unknown, then try getenv USERPROFILE.
176
+ if ( empty( $home_dir ) ) {
177
+ $home_dir = getenv( 'USERPROFILE' );
178
+ }
179
+ } else {
180
+ // Linux. Try posix_getpwuid and posix_getuid.
181
+ if ( function_exists( 'posix_getuid' ) && function_exists( 'posix_getpwuid' ) ) {
182
+ $user = posix_getpwuid( posix_getuid() );
183
+ $home_dir = ( ! empty( $user['dir'] ) ? $user['dir'] : null );
184
+ }
185
+
186
+ if ( empty( $home_dir ) ) {
187
+ $home_dir = ( ! empty( $_SERVER['HOME'] ) ? $_SERVER['HOME'] : null );
188
+ }
189
+
190
+ // If still unknown, then try environmental variables.
191
+ if ( empty( $home_dir ) ) {
192
+ $home_dir = getenv( 'HOME' );
193
+ }
194
+ }
195
+
196
+ // Could not find the user home directory, so use the WordPress root directory.
197
+ if ( empty( $home_dir ) ) {
198
+ $home_dir = ABSPATH;
199
+ }
200
+
201
+ // Use rtrim the $home_dir to strip any trailing slashes.
202
+ $home_dir = rtrim( $home_dir, '\\/' );
203
+ $home_dir = str_replace( '\\\\', '\\', $home_dir );
204
+
205
+ // Record the home directory.
206
+ $this->home_dir = $home_dir;
207
+
208
+ // Return the directory path.
209
+ return $home_dir;
210
+ }
211
+
212
+ /**
213
+ * Get the mode (last 3 characters of the octal number) of the home directory.
214
+ *
215
+ * @global WP_Filesystem $wp_filesystem The WordPress Filesystem API global object.
216
+ *
217
+ * @return string The mode of the home directory.
218
+ */
219
+ public function get_home_mode() {
220
+ // Get the user home directory.
221
+ $home_dir = $this->get_home_directory();
222
+
223
+ // Connect to the WordPress Filesystem API.
224
+ global $wp_filesystem;
225
+
226
+ // Get the mode of the directory.
227
+ $home_dir_mode = $wp_filesystem->getchmod( $home_dir );
228
+
229
+ return $home_dir_mode;
230
+ }
231
+
232
+ /**
233
+ * Get is_premium.
234
+ *
235
+ * @since 1.3.1
236
+ *
237
+ * @return bool
238
+ */
239
+ public function get_is_premium() {
240
+ return $this->is_premium;
241
+ }
242
+
243
+ /**
244
+ * Get our license string, such as "Free" or "Premium".
245
+ *
246
+ * @since 1.6.0
247
+ */
248
+ public function get_license_string() {
249
+ if ( ! isset( $this->license ) ) {
250
+ return __( 'Unknown', 'boldgrid-backup' );
251
+ } else {
252
+ return $this->license->getLicenseString();
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Get default_retention.
258
+ *
259
+ * @since 1.3.1
260
+ *
261
+ * @return int
262
+ */
263
+ public function get_default_retention() {
264
+ return $this->default_retention;
265
+ }
266
+
267
+
268
+ /**
269
+ * Set lang.
270
+ *
271
+ * @since 1.3.1
272
+ */
273
+ public function set_lang() {
274
+ $this->lang = array(
275
+ 'website_size' => esc_html__( 'Website Size:', 'boldgrid-backup' ),
276
+ 'database_size' => esc_html__( 'Database Size:', 'boldgrid-backup' ),
277
+ 'of' => esc_html__( 'of', 'boldgrid-backup' ),
278
+ 'xmark' => '&#10007;',
279
+ );
280
+ }
281
+
282
+ /**
283
+ * Custom upload directory callback.
284
+ *
285
+ * @since 1.2.2
286
+ *
287
+ * @see Boldgrid_Backup_Admin_Backup_Dir::get()
288
+ *
289
+ * @param array $upload Upload data array.
290
+ * @return array
291
+ */
292
+ public function custom_upload_dir( $upload ) {
293
+ // Get the backup directory path.
294
+ $backup_directory = $this->core->backup_dir->get();
295
+
296
+ // Get the subdirectory name.
297
+ $subdir = explode( '/', $backup_directory );
298
+ $subdir = $subdir[ count( $subdir ) - 1 ];
299
+
300
+ $upload['subdir'] = $subdir;
301
+ $upload['path'] = $backup_directory;
302
+ $upload['url'] = null;
303
+
304
+ return $upload;
305
+ }
306
+
307
+ /**
308
+ * Get the WordPress admin email address.
309
+ *
310
+ * @since 1.0.1
311
+ *
312
+ * @return string|bool The admin email address, or FALSE on error.
313
+ */
314
+ public function get_admin_email() {
315
+ // Initialize $admin_email.
316
+ $admin_email = null;
317
+
318
+ // Get the site email address.
319
+ // Try get_bloginfo.
320
+ if ( function_exists( 'get_bloginfo' ) ) {
321
+ $admin_email = get_bloginfo( 'admin_email' );
322
+ }
323
+
324
+ // If the email address is still needed, then try wp_get_current_user.
325
+ if ( empty( $admin_email ) && function_exists( 'wp_get_current_user' ) ) {
326
+ // Get the current user information.
327
+ $current_user = wp_get_current_user();
328
+
329
+ // Check if user information was retrieved, abort if not.
330
+ if ( ! $current_user ) {
331
+ return false;
332
+ }
333
+
334
+ // Get the current user email address.
335
+ $admin_email = $current_user->user_email;
336
+ }
337
+
338
+ // If there is no email address found, then abort.
339
+ if ( empty( $admin_email ) ) {
340
+ return false;
341
+ }
342
+
343
+ // Return the admin email address.
344
+ return $admin_email;
345
+ }
346
+
347
+ /**
348
+ * Add an archive compressor to the available list.
349
+ *
350
+ * @since 1.0
351
+ * @access private
352
+ *
353
+ * @param string $compressor A name of a compressor.
354
+ * @return null
355
+ */
356
+ private function add_compressor( $compressor = null ) {
357
+ if ( ! empty( $compressor ) &&
358
+ ! in_array( $compressor, $this->available_compressors, true ) ) {
359
+ $this->available_compressors[] = $compressor;
360
+ }
361
+
362
+ return;
363
+ }
364
+
365
+ /**
366
+ * Actions to take during the admin_init hook.
367
+ *
368
+ * @since 1.5.4
369
+ */
370
+ public function admin_init() {
371
+ $relative_path = 'boldgrid-backup-premium/boldgrid-backup-premium.php';
372
+ $abs_path = dirname( BOLDGRID_BACKUP_PATH ) . '/' . $relative_path;
373
+
374
+ // Function is_plugin_active only available in and after admin_init.
375
+ $this->is_premium_active = is_plugin_active( $relative_path );
376
+
377
+ $this->is_premium_installed = $this->core->wp_filesystem->exists( $abs_path );
378
+
379
+ $this->is_premium_done = $this->is_premium && $this->is_premium_active;
380
+ }
381
+
382
+ /**
383
+ * Is a specific archive compressor available?
384
+ *
385
+ * @since 1.0
386
+ *
387
+ * @param string $compressor A string to identify a compressor.
388
+ * @return bool
389
+ */
390
+ public function is_compressor_available( $compressor = null ) {
391
+ // If input parameter is empty, then fail.
392
+ if ( empty( $compressor ) || empty( $this->available_compressors ) ) {
393
+ return false;
394
+ }
395
+
396
+ // Check the array to see if the specified compressor is available.
397
+ $is_available = in_array( $compressor, $this->available_compressors, true );
398
+
399
+ return $is_available;
400
+ }
401
+
402
+ /**
403
+ * Get available compressors.
404
+ *
405
+ * Test for available archive compressors, add them to the array in a preferred order, and
406
+ * return the array.
407
+ *
408
+ * @since 1.0
409
+ *
410
+ * @return array
411
+ */
412
+ public function get_available_compressors() {
413
+ // If at least one compressor is already configured, then return TRUE.
414
+ if ( ! empty( $this->available_compressors ) ) {
415
+ return $this->available_compressors;
416
+ }
417
+
418
+ if ( ! class_exists( 'PclZip' ) ) {
419
+ require_once( ABSPATH . '/wp-admin/includes/class-pclzip.php' );
420
+ }
421
+
422
+ // Initialize $this->available_compressors to an empty array.
423
+ $this->available_compressors = array();
424
+
425
+ // PHP zip (ZipArchive).
426
+ if ( Boldgrid_Backup_Admin_Compressor_Php_Zip::is_available() ) {
427
+ $this->add_compressor( 'php_zip' );
428
+ }
429
+
430
+ // PclZip
431
+ if ( class_exists( 'PclZip' ) ) {
432
+ $this->add_compressor( 'pcl_zip' );
433
+ }
434
+
435
+ // PHP bz2 (Bzip2).
436
+ if ( extension_loaded( 'bz2' ) && function_exists( 'bzcompress' ) ) {
437
+ $this->add_compressor( 'php_bz2' );
438
+ }
439
+
440
+ // PHP zlib (Zlib).
441
+ if ( extension_loaded( 'zlib' ) && function_exists( 'gzwrite' ) ) {
442
+ $this->add_compressor( 'php_zlib' );
443
+ }
444
+
445
+ // PHP lzf (LZF).
446
+ if ( function_exists( 'lzf_compress' ) ) {
447
+ $this->add_compressor( 'php_lzf' );
448
+ }
449
+
450
+ if ( $this->core->test->is_windows() ) {
451
+ return $this->available_compressors;
452
+ }
453
+
454
+ // System tar.
455
+ if ( $this->core->execute_command( '/bin/tar --version' ) ) {
456
+ $this->add_compressor( 'system_tar' );
457
+ }
458
+
459
+ // System zip.
460
+ if ( $this->core->execute_command( '/usr/bin/zip -v ' ) ) {
461
+ $this->add_compressor( 'system_zip' );
462
+ }
463
+
464
+ return $this->available_compressors;
465
+ }
466
+ }
admin/class-boldgrid-backup-admin-core-files.php ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Core files.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.4
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup Admin Core Files Class.
17
+ *
18
+ * @since 1.5.4
19
+ */
20
+ class Boldgrid_Backup_Admin_Core_Files {
21
+
22
+ /**
23
+ * The core class object.
24
+ *
25
+ * @since 1.5.4
26
+ * @access private
27
+ * @var Boldgrid_Backup_Admin_Core
28
+ */
29
+ private $core;
30
+
31
+ /**
32
+ * An array of core files within WordPress.
33
+ *
34
+ * Exceptions:
35
+ * # The wp-content folder is not included.
36
+ * # .htaccess.bgb is included, but it is not a core file.
37
+ *
38
+ * @since 1.5.4
39
+ * @access public
40
+ * @var array
41
+ */
42
+ public $files = array(
43
+ '.htaccess',
44
+ '.htaccess.bgb',
45
+ 'index.php',
46
+ 'license.txt',
47
+ 'readme.html',
48
+ 'wp-activate.php',
49
+ 'wp-admin',
50
+ 'wp-blog-header.php',
51
+ 'wp-comments-post.php',
52
+ 'wp-config.php',
53
+ 'wp-cron.php',
54
+ 'wp-includes',
55
+ 'wp-links-opml.php',
56
+ 'wp-load.php',
57
+ 'wp-login.php',
58
+ 'wp-mail.php',
59
+ 'wp-settings.php',
60
+ 'wp-signup.php',
61
+ 'wp-trackback.php',
62
+ 'xmlrpc.php',
63
+ );
64
+
65
+ /**
66
+ * Constructor.
67
+ *
68
+ * @since 1.5.4
69
+ *
70
+ * @param Boldgrid_Backup_Admin_Core $core Core class object.
71
+ */
72
+ public function __construct( $core ) {
73
+ $this->core = $core;
74
+ }
75
+
76
+ /**
77
+ * Determine if a given $file (relative to ABSPATH) is a core file.
78
+ *
79
+ * @since 1.5.4
80
+ *
81
+ * @param string $file
82
+ * @return bool
83
+ */
84
+ public function is_core_file( $file ) {
85
+ if ( ! is_string( $file ) || empty( $file ) ) {
86
+ return false;
87
+ }
88
+
89
+ foreach ( $this->files as $core_file ) {
90
+ if ( 0 === strpos( $file, $core_file ) ) {
91
+ return true;
92
+ }
93
+ }
94
+
95
+ return false;
96
+ }
97
+
98
+ }
admin/class-boldgrid-backup-admin-core.php ADDED
@@ -0,0 +1,2722 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The admin-specific core functionality of the plugin
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.0
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup admin core class.
17
+ *
18
+ * @since 1.0
19
+ */
20
+ class Boldgrid_Backup_Admin_Core {
21
+ /**
22
+ * Auto Rollback class.
23
+ *
24
+ * @since 1.5.2
25
+ * @access public
26
+ * @var Boldgrid_Backup_Admin_Auto_Rollback
27
+ */
28
+ public $auto_rollback;
29
+
30
+ /**
31
+ * The settings class object.
32
+ *
33
+ * @since 1.0
34
+ * @access public
35
+ * @var Boldgrid_Backup_Admin_Settings
36
+ */
37
+ public $settings;
38
+
39
+ /**
40
+ * The configuration class object.
41
+ *
42
+ * @since 1.0
43
+ * @access public
44
+ * @var Boldgrid_Backup_Admin_Config
45
+ */
46
+ public $config;
47
+
48
+ /**
49
+ * Plugin configs.
50
+ *
51
+ * @since 1.3.4
52
+ * @access plublic
53
+ * @var array
54
+ */
55
+ public $configs;
56
+
57
+ /**
58
+ * Core Files class.
59
+ *
60
+ * @since 1.5.4
61
+ * @access public
62
+ * @var Boldgrid_Backup_Admin_Core_Files
63
+ */
64
+ public $core_files;
65
+
66
+ /**
67
+ * Whether or not we're in ajax.
68
+ *
69
+ * When we're on the archives page and click "Backup Site Now", when
70
+ * this->archive_files runs, it reports doing_ajax as true.
71
+ *
72
+ * @since 1.5.2
73
+ * @access public
74
+ * @var bool
75
+ */
76
+ public $doing_ajax;
77
+
78
+ /**
79
+ * Whether or not we're doing cron.
80
+ *
81
+ * When we're generating a backup, both via cron and wpcron, doing_cron is
82
+ * true.
83
+ *
84
+ * @since 1.5.1
85
+ * @access public
86
+ * @var bool
87
+ */
88
+ public $doing_cron;
89
+
90
+ /**
91
+ * Whether or not we're doing wp_cron.
92
+ *
93
+ * In WordPress' wp-cron.php DOING_CRON is defined as true. In several of
94
+ * our files, we define DOING_CRON as well. When we want to tell the
95
+ * difference between (1)wp-cron.php and (2)cron / cli, it's difficult. This
96
+ * property is soley to know if we're in wp-cron.php
97
+ *
98
+ * @todo Within our plugins, DOING_CRON should be cleaned up.
99
+ *
100
+ * @since 1.5.4
101
+ */
102
+ public $doing_wp_cron;
103
+
104
+ /**
105
+ * Email.
106
+ *
107
+ * @since 1.5.2
108
+ * @access public
109
+ * @var Boldgrid_Backup_Admin_Email
110
+ */
111
+ public $email;
112
+
113
+ /**
114
+ * Elements.
115
+ *
116
+ * Common elements used throughout admin pages. Usually a combination of
117
+ * language strings.
118
+ *
119
+ * @since 1.5.3
120
+ * @access public
121
+ * @var Boldgrid_Backup_Admin_Email
122
+ */
123
+ public $elements = array();
124
+
125
+ /**
126
+ * The functionality test class object.
127
+ *
128
+ * @since 1.0
129
+ * @access public
130
+ * @var Boldgrid_Backup_Admin_Test
131
+ */
132
+ public $test;
133
+
134
+ /**
135
+ * The Time class object.
136
+ *
137
+ * @since 1.6.0
138
+ * @access public
139
+ * @var Boldgrid_Backup_Admin_Time
140
+ */
141
+ public $time;
142
+
143
+ /**
144
+ * An instance of Boldgrid_Backup_Admin_Tools.
145
+ *
146
+ * @since 1.5.4
147
+ * @access public
148
+ * @var Boldgrid_Backup_Admin_Tools
149
+ */
150
+ public $tools;
151
+
152
+ /**
153
+ * An instance of Boldgrid_Backup_Admin_Utility.
154
+ *
155
+ * @since 1.5.3
156
+ * @access public
157
+ * @var Boldgrid_Backup_Admin_Utility
158
+ */
159
+ public $utility;
160
+
161
+ /**
162
+ * The admin notice class object.
163
+ *
164
+ * @since 1.0
165
+ * @access public
166
+ * @var Boldgrid_Backup_Admin_Notice
167
+ */
168
+ public $notice;
169
+
170
+ /**
171
+ * WordPress' global pagenow.
172
+ *
173
+ * @since 1.6.0
174
+ * @access public
175
+ * @var string
176
+ */
177
+ public $pagenow;
178
+
179
+ /**
180
+ * A bool indicating at the time of archivign files whether or not the
181
+ * current_filter() was pre_auto_update (the action ran immediately before
182
+ * WordPress does an auto upgrade).
183
+ *
184
+ * @since 1.6.0
185
+ * @access public
186
+ * @var bool
187
+ */
188
+ public $pre_auto_update = false;
189
+
190
+ /**
191
+ * The admin cron class object.
192
+ *
193
+ * @since 1.0
194
+ * @access public
195
+ * @var Boldgrid_Backup_Admin_Cron
196
+ */
197
+ public $cron;
198
+
199
+ /**
200
+ * The admin xhprof class object.
201
+ *
202
+ * @since 1.2
203
+ * @access public
204
+ * @var Boldgrid_Backup_Admin_Xhprof
205
+ */
206
+ public $xhprof;
207
+
208
+ /**
209
+ * WP Cron class.
210
+ *
211
+ * @since 1.5.1
212
+ * @access public
213
+ * @var Boldgrid_Backup_Admin_WP_Cron
214
+ */
215
+ public $wp_cron;
216
+
217
+ /**
218
+ * An instance of the filesystem.
219
+ *
220
+ * @since 1.5.1
221
+ * @access public
222
+ * @var WP_Filesystem
223
+ */
224
+ public $wp_filesystem;
225
+
226
+ /**
227
+ * An instance of the Boldgrid_Backup_Admin_Archive class.
228
+ *
229
+ * @since 1.5.3
230
+ * @access public
231
+ * @var Boldgrid_Backup_Admin_Archive
232
+ */
233
+ public $archive;
234
+
235
+ /**
236
+ * An instance of the Boldgrid_Backup_Admin_Archives class.
237
+ *
238
+ * @since 1.5.4
239
+ * @access public
240
+ * @var Boldgrid_Backup_Admin_Archives
241
+ */
242
+ public $archives;
243
+
244
+ /**
245
+ * An instance of the Boldgrid_Backup_Admin_Archives_All class.
246
+ *
247
+ * @since 1.5.4
248
+ * @access public
249
+ * @var Boldgrid_Backup_Admin_Archives_All
250
+ */
251
+ public $archives_all;
252
+
253
+ /**
254
+ * An instance of the Boldgrid_Backup_Admin_Archive_Actions class.
255
+ *
256
+ * @since 1.5.4
257
+ * @access public
258
+ * @var Boldgrid_Backup_Admin_Archive_Actions
259
+ */
260
+ public $archive_actions;
261
+
262
+ /**
263
+ * An instance of the Boldgrid_Backup_Admin_Archive_Browser class.
264
+ *
265
+ * @since 1.5.2
266
+ * @access public
267
+ * @var Boldgrid_Backup_Admin_Archive_Browser
268
+ */
269
+ public $archive_browser;
270
+
271
+ /**
272
+ * An instance of the Archive Log class.
273
+ *
274
+ * @since 1.5.1
275
+ * @access public
276
+ * @var Boldgrid_Backup_Admin_Archive_Log
277
+ */
278
+ public $archive_log;
279
+
280
+ /**
281
+ * An instance of the Archive Details class.
282
+ *
283
+ * @since 1.5.1
284
+ * @access public
285
+ * @var Boldgrid_Backup_Admin_Archive_Details
286
+ */
287
+ public $archive_details;
288
+
289
+ /**
290
+ * An instance of the Archive Fail class.
291
+ *
292
+ * @since 1.5.2
293
+ * @access public
294
+ * @var Boldgrid_Backup_Admin_Archive_Fail
295
+ */
296
+ public $archive_fail;
297
+
298
+ /**
299
+ * Available execution functions.
300
+ *
301
+ * @since 1.0
302
+ * @access private
303
+ * @var array
304
+ */
305
+ private $available_exec_functions = null;
306
+
307
+ /**
308
+ * Db Dump.
309
+ *
310
+ * @since 1.5.3
311
+ * @access public
312
+ * @var Boldgrid_Backup_Admin_Db_Dump
313
+ */
314
+ public $db_dump;
315
+
316
+ /**
317
+ * Database backup file path.
318
+ *
319
+ * @since 1.0
320
+ * @access public
321
+ * @var string
322
+ */
323
+ public $db_dump_filepath = '';
324
+
325
+ /**
326
+ * Db Get.
327
+ *
328
+ * @since 1.5.3
329
+ * @access public
330
+ * @var Boldgrid_Backup_Admin_Db_Dump
331
+ */
332
+ public $db_get;
333
+
334
+ /**
335
+ * An instance of Boldgrid_Backup_Admin_Db_Omit.
336
+ *
337
+ * @since 1.5.3
338
+ * @access public
339
+ * @var Boldgrid_Backup_Admin_Db_Omit
340
+ */
341
+ public $db_omit;
342
+
343
+ /**
344
+ * An instance of the Boldgrid Backup Admin Filelist Class.
345
+ *
346
+ * @since 1.5.1
347
+ * @var Boldgrid_Backup_Admin_Filelist object
348
+ */
349
+ public $filelist;
350
+
351
+ /**
352
+ * Base directory for the get_filelist method.
353
+ *
354
+ * @since 1.0
355
+ * @access private
356
+ * @var string
357
+ */
358
+ private $filelist_basedir = null;
359
+
360
+ /**
361
+ * An instance of the Boldgrid_Backup_Admin_Folder_Exclusion class.
362
+ *
363
+ * @since 1.5.4
364
+ * @var Boldgrid_Backup_Admin_Folder_Exclusion
365
+ */
366
+ public $folder_exclusion;
367
+
368
+ /**
369
+ * An instance of the Boldgrid_Backup_Admin_Ftp class.
370
+ *
371
+ * @since 1.5.4
372
+ * @var Boldgrid_Backup_Admin_Ftp
373
+ */
374
+ public $ftp;
375
+
376
+ /**
377
+ * An instance of the Boldgrid_Backup_Admin_Go_Pro class.
378
+ *
379
+ * @since 1.5.4
380
+ * @var Boldgrid_Backup_Admin_Go_Pro
381
+ */
382
+ public $go_pro;
383
+
384
+ /**
385
+ * An instance of the Boldgrid Backup Admin Backup Dir class.
386
+ *
387
+ * @since 1.5.1
388
+ * @var Boldgrid_Backup_Admin_Backup_Dir object.
389
+ */
390
+ public $backup_dir;
391
+
392
+ /**
393
+ * A unique identifier for backups of this WordPress installation.
394
+ *
395
+ * @since 1.0.1
396
+ * @access private
397
+ * @var string
398
+ */
399
+ private $backup_identifier = null;
400
+
401
+ /**
402
+ * Value indicating we are in the Backup Site Now callback and the user is
403
+ * choosing a full backup.
404
+ *
405
+ * @since 1.5.4
406
+ * @access public
407
+ * @var bool
408
+ */
409
+ public $is_backup_full = false;
410
+
411
+ /**
412
+ * Value indicating we are in the Backup Site Now callback.
413
+ *
414
+ * @since 1.5.4
415
+ * @access public
416
+ * @var bool
417
+ */
418
+ public $is_backup_now = false;
419
+
420
+ /**
421
+ * An instance of the Boldgrid Backup Admin Home Dir class.
422
+ *
423
+ * @since 1.5.1
424
+ * @var Boldgrid_Backup_Admin_Home_Dir object.
425
+ */
426
+ public $home_dir;
427
+
428
+ /**
429
+ * An instance of the In Progress class.
430
+ *
431
+ * @since 1.5.4
432
+ * @var Boldgrid_Backup_Admin_In_Progress object.
433
+ */
434
+ public $in_progress;
435
+
436
+ /**
437
+ * Value indicating whether or not we're creating a backup for update
438
+ * protection.
439
+ *
440
+ * @since 1.5.4
441
+ * @var bool
442
+ */
443
+ public $is_archiving_update_protection = false;
444
+
445
+ /**
446
+ * Common elements.
447
+ *
448
+ * @since 1.5.3
449
+ * @var array
450
+ */
451
+ public $lang = array();
452
+
453
+ /**
454
+ * Local storage.
455
+ *
456
+ * @since 1.5.2
457
+ * @access public
458
+ * @var Boldgrid_Backup_Admin_Storage_Local
459
+ */
460
+ public $local;
461
+
462
+ /**
463
+ * The Restore Helper class.
464
+ *
465
+ * @since 1.6.1
466
+ * @access public
467
+ * @var Boldgrid_Backup_Admin_Restore_Helper
468
+ */
469
+ public $restore_helper;
470
+
471
+ /**
472
+ * The scheduler class object.
473
+ *
474
+ * @since 1.5.1
475
+ * @access public
476
+ * @var Boldgrid_Backup_Admin_Scheduler
477
+ */
478
+ public $scheduler;
479
+
480
+ /**
481
+ * Constructor.
482
+ *
483
+ * @since 1.0
484
+ *
485
+ * @global $wp_filesystem.
486
+ */
487
+ public function __construct() {
488
+ global $wp_filesystem;
489
+ global $pagenow;
490
+
491
+ $this->doing_cron = ( defined( 'DOING_CRON' ) && DOING_CRON ) || isset( $_GET['doing_wp_cron'] );
492
+ $this->doing_ajax = is_admin() && defined( 'DOING_AJAX' ) && DOING_AJAX;
493
+ $this->doing_wp_cron = ! empty( $_SERVER['SCRIPT_FILENAME'] ) && $_SERVER['SCRIPT_FILENAME'] === trailingslashit( ABSPATH ) . 'wp-cron.php';
494
+
495
+ $this->wp_filesystem = $wp_filesystem;
496
+ $this->pagenow = $pagenow;
497
+
498
+ // Instantiate Boldgrid_Backup_Admin_Settings.
499
+ $this->settings = new Boldgrid_Backup_Admin_Settings( $this );
500
+
501
+ // Instantiate Boldgrid_Backup_Admin_Config.
502
+ $this->config = new Boldgrid_Backup_Admin_Config( $this );
503
+
504
+ // Instantiate Boldgrid_Backup_Admin_Test.
505
+ $this->test = new Boldgrid_Backup_Admin_Test( $this );
506
+
507
+ // Instantiate Boldgrid_Backup_Admin_Notice.
508
+ $this->notice = new Boldgrid_Backup_Admin_Notice( $this );
509
+
510
+ // Instantiate Boldgrid_Backup_Admin_Cron.
511
+ $this->cron = new Boldgrid_Backup_Admin_Cron( $this );
512
+
513
+ // Instantiate Boldgrid_Backup_Admin_Upload.
514
+ $this->upload = new Boldgrid_Backup_Admin_Upload( $this );
515
+
516
+ // Instantiate Boldgrid_Backup_Admin_Xhprof.
517
+ // Starts profiling and saves a report, if enabled in the "config.local.php" file.
518
+ $this->xhprof = new Boldgrid_Backup_Admin_Xhprof();
519
+
520
+ $this->restore_helper = new Boldgrid_Backup_Admin_Restore_Helper();
521
+
522
+ $this->restore_git = new Boldgrid_Backup_Admin_Restore_Git();
523
+
524
+ $this->filelist = new Boldgrid_Backup_Admin_Filelist( $this );
525
+
526
+ $this->backup_dir = new Boldgrid_Backup_Admin_Backup_Dir( $this );
527
+
528
+ $this->home_dir = new Boldgrid_Backup_Admin_Home_Dir( $this );
529
+
530
+ $this->compressors = new Boldgrid_Backup_Admin_Compressors( $this );
531
+
532
+ $this->archive_browser = new Boldgrid_Backup_Admin_Archive_Browser( $this );
533
+
534
+ $this->archive = new Boldgrid_Backup_Admin_Archive( $this );
535
+
536
+ $this->archive_actions = new Boldgrid_Backup_Admin_Archive_Actions( $this );
537
+
538
+ $this->archives = new Boldgrid_Backup_Admin_Archives( $this );
539
+
540
+ $this->archives_all = new Boldgrid_Backup_Admin_Archives_All( $this );
541
+
542
+ $this->archive_log = new Boldgrid_Backup_Admin_Archive_Log( $this );
543
+
544
+ $this->archive_details = new Boldgrid_Backup_Admin_Archive_Details( $this );
545
+
546
+ $this->archive_fail = new Boldgrid_Backup_Admin_Archive_Fail( $this );
547
+
548
+ $this->wp_cron = new Boldgrid_Backup_Admin_WP_Cron( $this );
549
+
550
+ $this->scheduler = new Boldgrid_Backup_Admin_Scheduler( $this );
551
+
552
+ $this->auto_rollback = new Boldgrid_Backup_Admin_Auto_Rollback( $this );
553
+
554
+ $this->remote = new Boldgrid_Backup_Admin_Remote( $this );
555
+
556
+ $this->jobs = new Boldgrid_Backup_Admin_Jobs( $this );
557
+
558
+ $this->local = new Boldgrid_Backup_Admin_Storage_Local( $this );
559
+
560
+ $this->email = new Boldgrid_Backup_Admin_Email( $this );
561
+
562
+ $this->db_omit = new Boldgrid_Backup_Admin_Db_Omit( $this );
563
+
564
+ $this->db_dump = new Boldgrid_Backup_Admin_Db_Dump( $this );
565
+
566
+ $this->db_get = new Boldgrid_Backup_Admin_Db_get( $this );
567
+
568
+ $this->utility = new Boldgrid_Backup_Admin_Utility();
569
+
570
+ $this->folder_exclusion = new Boldgrid_Backup_Admin_Folder_Exclusion( $this );
571
+
572
+ $this->core_files = new Boldgrid_Backup_Admin_Core_Files( $this );
573
+
574
+ $this->in_progress = new Boldgrid_Backup_Admin_In_Progress( $this );
575
+
576
+ $this->ftp = new Boldgrid_Backup_Admin_Ftp( $this );
577
+
578
+ $this->go_pro = new Boldgrid_Backup_Admin_Go_Pro( $this );
579
+
580
+ $this->tools = new Boldgrid_Backup_Admin_Tools( $this );
581
+
582
+ $this->time = new Boldgrid_Backup_Admin_Time( $this );
583
+
584
+ // Ensure there is a backup identifier.
585
+ $this->get_backup_identifier();
586
+
587
+ $this->configs = Boldgrid_Backup_Admin::get_configs();
588
+
589
+ $this->set_lang();
590
+
591
+ // Need to construct class so necessary filters are added.
592
+ if ( class_exists( '\Boldgrid\Library\Library\Ui' ) ) {
593
+ $ui = new \Boldgrid\Library\Library\Ui();
594
+ }
595
+ }
596
+
597
+ /**
598
+ * Get the unique identifier for backups of this WordPress installation.
599
+ *
600
+ * @since 1.0.1
601
+ *
602
+ * @return string A unique identifier for backups.
603
+ */
604
+ public function get_backup_identifier() {
605
+ // If the id was already stored, then return it.
606
+ if ( ! empty( $this->backup_identifier ) ) {
607
+ return $this->backup_identifier;
608
+ }
609
+
610
+ // Check wp_options for the id.
611
+ $backup_identifier = get_site_option( 'boldgrid_backup_id' );
612
+
613
+ // If the id was already stored in WP options, then save and return it.
614
+ if ( ! empty( $backup_identifier ) ) {
615
+ $this->backup_identifier = $backup_identifier;
616
+
617
+ return $backup_identifier;
618
+ }
619
+
620
+ // Generate a new backup id.
621
+ $admin_email = $this->config->get_admin_email();
622
+
623
+ $unique_string = site_url() . ' <' . $admin_email . '>';
624
+
625
+ $backup_identifier = hash( 'crc32', hash( 'sha512', $unique_string ) );
626
+
627
+ // If something went wrong with hashing, then just use a random string to make the id.
628
+ if ( empty( $backup_identifier ) ) {
629
+ $random_string = '';
630
+
631
+ for ( $i = 0; $i <= 32; $i ++ ) {
632
+ $random_string .= chr( mt_rand( 40, 126 ) );
633
+ }
634
+
635
+ $backup_identifier = hash( 'crc32', $random_string );
636
+ }
637
+
638
+ // Save and return the id.
639
+ $this->backup_identifier = $backup_identifier;
640
+
641
+ update_site_option( 'boldgrid_backup_id', $backup_identifier );
642
+
643
+ return $backup_identifier;
644
+ }
645
+
646
+ /**
647
+ * Initialize the premium version of the plugin.
648
+ *
649
+ * @since 1.5.2
650
+ */
651
+ public function init_premium() {
652
+ $premium_class = 'Boldgrid_Backup_Premium';
653
+
654
+ /*
655
+ * Only initialize premium if both the plugin exists, is activated, and
656
+ * we have a premium key.
657
+ */
658
+ if ( ! class_exists( $premium_class ) || ! $this->config->get_is_premium() ) {
659
+ return;
660
+ }
661
+
662
+ $this->premium = new $premium_class( $this );
663
+ $this->premium->run();
664
+ }
665
+
666
+ /**
667
+ * Get the available execution functions.
668
+ *
669
+ * @since 1.0
670
+ *
671
+ * @return array An array of function names.
672
+ */
673
+ public function get_execution_functions() {
674
+ // If the array already has elements, then return the array.
675
+ if ( $this->available_exec_functions ) {
676
+ return $this->available_exec_functions;
677
+ }
678
+
679
+ // If PHP is in safe mode, then return an empty array.
680
+ if ( $this->test->is_php_safemode() ) {
681
+ return array();
682
+ }
683
+
684
+ // Get the PHP disable_functions list.
685
+ $disabled = explode( ',', ini_get( 'disable_functions' ) );
686
+
687
+ // Make an array of execution functions.
688
+ $exec_functions = array(
689
+ 'popen',
690
+ 'proc_open',
691
+ 'exec',
692
+ 'shell_exec',
693
+ 'passthru',
694
+ 'system',
695
+ );
696
+
697
+ // Iterate through the array and remove disabled functions.
698
+ foreach ( $exec_functions as $exec_function ) {
699
+ if ( in_array( $exec_function, $disabled, true ) ) {
700
+ unset( $exec_functions[ $exec_function ] );
701
+ }
702
+ }
703
+
704
+ // Save the array of execution functions.
705
+ $this->available_exec_functions = $exec_functions;
706
+
707
+ return $exec_functions;
708
+ }
709
+
710
+ /**
711
+ * Execute a system command using an array of execution functions.
712
+ *
713
+ * @since 1.0
714
+ *
715
+ * @param string $command A command string to be executed.
716
+ * @param array $available_exec_functions An array of available execution functions.
717
+ * @param bool $success or failure of the operation, passed back to the caller.
718
+ * @param int $return_var If present, the return_var, passed back to the caller.
719
+ * @return string|bool Returns the command output or FALSE on error.
720
+ */
721
+ public function execute_command( $command, $available_exec_functions = array(), &$success = false, &$return_var = 0 ) {
722
+ // If no command was passed, then fail.
723
+ if ( empty( $command ) ) {
724
+ return false;
725
+ }
726
+
727
+ // If there are no supplied execution functions, then retrieve available ones.
728
+ if ( empty( $available_exec_functions ) ) {
729
+ $available_exec_functions = $this->get_execution_functions();
730
+ }
731
+
732
+ // Disable stderr.
733
+ if ( ! $this->test->is_windows() && false === strpos( $command, '2>/dev/null' ) ) {
734
+ $command .= ' 2>/dev/null';
735
+ }
736
+
737
+ // Initialize $success.
738
+ $success = false;
739
+
740
+ // Test getting output using available execution functions, until one is successful.
741
+ foreach ( $available_exec_functions as $exec_function ) {
742
+ switch ( $exec_function ) {
743
+ case 'exec' :
744
+ exec( $command, $out, $return_var );
745
+
746
+ // If the exit status is int(0), then it was successful.
747
+ if ( 0 === $return_var ) {
748
+ $output = implode( PHP_EOL, $out );
749
+
750
+ $success = true;
751
+
752
+ break 2;
753
+ } else {
754
+ $output = false;
755
+ }
756
+
757
+ break 2;
758
+
759
+ case 'passthru' :
760
+ // If output buffering is enabled, then use passthru.
761
+ if ( ob_start() ) {
762
+ passthru( $command, $return_var );
763
+
764
+ // Get current buffer contents and delete current output buffer.
765
+ $output = ob_get_clean();
766
+
767
+ // If the exit status is int(0), then it was successful.
768
+ if ( 0 === $return_var ) {
769
+ $success = true;
770
+
771
+ break 2;
772
+ } else {
773
+ $output = false;
774
+ }
775
+ }
776
+
777
+ break 2;
778
+
779
+ case 'popen' :
780
+ $handle = popen( $command, 'r' );
781
+
782
+ $output = fread( $handle, 4096 );
783
+
784
+ /*
785
+ * If handle is a valid resource, then check for success.
786
+ */
787
+ if ( false !== $handle ) {
788
+ // Close the process handle and get the return status.
789
+ $return_var = pclose( $handle );
790
+
791
+ // If the exit status is int(0), then it was successful.
792
+ if ( 0 === $return_var ) {
793
+ $success = true;
794
+
795
+ break 2;
796
+ } else {
797
+ // Bad exit status code (non-zero).
798
+ $output = false;
799
+ }
800
+ } else {
801
+ // Failed to create a process handle.
802
+ $output = false;
803
+ }
804
+
805
+ break 2;
806
+
807
+ case 'proc_open' :
808
+ // Create the descriptor spec array.
809
+ $descriptorspec = array(
810
+ 0 => array(
811
+ 'pipe',
812
+ 'r',
813
+ ),
814
+ 1 => array(
815
+ 'pipe',
816
+ 'w',
817
+ ),
818
+ 2 => array(
819
+ 'pipe',
820
+ 'w',
821
+ ),
822
+ );
823
+
824
+ // Open a process handle.
825
+ $handle = proc_open( $command, $descriptorspec, $pipes );
826
+
827
+ if ( is_resource( $handle ) ) {
828
+ // Close unused pipes[0].
829
+ fclose( $pipes[0] );
830
+
831
+ // Read output from pipes[1].
832
+ $output = stream_get_contents( $pipes[1] );
833
+
834
+ // Close pipes[1].
835
+ fclose( $pipes[1] );
836
+
837
+ // Close unused pipes[0].
838
+ fclose( $pipes[2] );
839
+
840
+ // Close the process handle and get the return status.
841
+ $return_var = proc_close( $handle );
842
+
843
+ // If the exit status is int(0), then it was successful.
844
+ if ( 0 === $return_var ) {
845
+ $success = true;
846
+
847
+ break 2;
848
+ } else {
849
+ $output = false;
850
+ }
851
+ }
852
+
853
+ break 2;
854
+
855
+ case 'shell_exec' :
856
+ $output = shell_exec( $command );
857
+
858
+ if ( false === strpos( $output, 'command not found' ) ) {
859
+ $success = true;
860
+
861
+ break 2;
862
+ } else {
863
+ $output = false;
864
+ }
865
+
866
+ break 2;
867
+
868
+ case 'system' :
869
+ // If output buffering is enabled, then use system.
870
+ if ( ob_start() ) {
871
+ system( $command, $return_var );
872
+
873
+ // Get current buffer contents and delete current output buffer.
874
+ $output = ob_get_clean();
875
+
876
+ // If the exit status is int(0), then it was successful.
877
+ if ( 0 === $return_var ) {
878
+ $success = true;
879
+
880
+ break 2;
881
+ } else {
882
+ $output = false;
883
+ }
884
+ }
885
+
886
+ break 2;
887
+
888
+ default :
889
+ break;
890
+ }
891
+ }
892
+
893
+ // If there is output, then trim it.
894
+ if ( ! empty( $output ) ) {
895
+ $output = trim( $output );
896
+ }
897
+
898
+ // If the command was not successful, then return FALSE.
899
+ if ( ! $success ) {
900
+ return false;
901
+ }
902
+
903
+ // Success.
904
+ return $output;
905
+ }
906
+
907
+ /**
908
+ * Add menu items.
909
+ *
910
+ * @since 1.0
911
+ *
912
+ * @global array $submenu
913
+ *
914
+ * @return null
915
+ */
916
+ public function add_menu_items() {
917
+ global $submenu;
918
+
919
+ $lang = array(
920
+ 'backup_archive' => __( 'Backup Archive', 'boldgrid-backup' ),
921
+ 'boldgrid_backup' => __( 'BoldGrid Backup', 'boldgrid-backup' ),
922
+ 'get_premium' => __( 'Get Premium', 'boldgrid-bacukp' ),
923
+ 'preflight_check' => __( 'Preflight Check', 'boldgrid-backup' ),
924
+ 'settings' => __( 'Settings', 'boldgrid-backup' ),
925
+ 'tools' => __( 'Tools', 'boldgrid-backup' ),
926
+ );
927
+
928
+ // The main slug all sub menu items are children of.
929
+ $main_slug = 'boldgrid-backup-settings';
930
+
931
+ // The callable function for the settings page.
932
+ $settings_page = array(
933
+ $this->settings,
934
+ 'page_backup_settings',
935
+ );
936
+
937
+ // The capability required for these menu items to be displayed to the user.
938
+ $capability = 'administrator';
939
+
940
+ add_menu_page(
941
+ $lang['boldgrid_backup'],
942
+ $lang['boldgrid_backup'],
943
+ $capability,
944
+ $main_slug,
945
+ $settings_page,
946
+ 'none'
947
+ );
948
+
949
+ /*
950
+ * Add "Settings", formally known as "Backup Settings".
951
+ *
952
+ * @link http://wordpress.stackexchange.com/questions/66498/add-menu-page-with-different-name-for-first-submenu-item
953
+ */
954
+ add_submenu_page(
955
+ $main_slug,
956
+ $lang['boldgrid_backup'] . ' ' . $lang['settings'],
957
+ $lang['settings'],
958
+ $capability,
959
+ $main_slug,
960
+ $settings_page
961
+ );
962
+
963
+ // Add "Backup Archive", formally known as "BoldGrid Backup".
964
+ add_submenu_page(
965
+ $main_slug,
966
+ 'BoldGrid ' . $lang['backup_archive'],
967
+ $lang['backup_archive'],
968
+ $capability,
969
+ 'boldgrid-backup',
970
+ array(
971
+ $this,
972
+ 'page_archives',
973
+ )
974
+ );
975
+
976
+ // Add "Preflight Check" page, formally know as "Functionality Test".
977
+ add_submenu_page(
978
+ $main_slug,
979
+ $lang['boldgrid_backup'] . ' ' . $lang['preflight_check'],
980
+ $lang['preflight_check'],
981
+ $capability,
982
+ 'boldgrid-backup-test',
983
+ array(
984
+ $this,
985
+ 'page_backup_test',
986
+ )
987
+ );
988
+
989
+ add_submenu_page(
990
+ null,
991
+ 'BoldGrid ' . $lang['backup_archive'],
992
+ $lang['backup_archive'],
993
+ $capability,
994
+ 'boldgrid-backup-archive-details',
995
+ array(
996
+ $this->archive_details,
997
+ 'render_archive',
998
+ )
999
+ );
1000
+
1001
+ // Add "Preflight Check" page, formally know as "Functionality Test".
1002
+ add_submenu_page(
1003
+ $main_slug,
1004
+ $lang['boldgrid_backup'] . ' ' . $lang['tools'],
1005
+ $lang['tools'],
1006
+ $capability,
1007
+ 'boldgrid-backup-tools',
1008
+ array(
1009
+ $this->tools,
1010
+ 'page',
1011
+ )
1012
+ );
1013
+
1014
+ /*
1015
+ * Add our "Get Premium" link to the navigation.
1016
+ *
1017
+ * Leave this as the last menu item.
1018
+ */
1019
+ if ( ! $this->config->get_is_premium() ) {
1020
+ $menu_slug = 'boldgrid-backup-get-premium';
1021
+
1022
+ add_submenu_page(
1023
+ $main_slug,
1024
+ $lang['get_premium'],
1025
+ '<span class="dashicons dashicons-dashboard"></span> <span class="get-premium">' . $lang['get_premium'] . '</span>',
1026
+ $capability,
1027
+ $menu_slug
1028
+ );
1029
+
1030
+ // Change the url (2 is key of the menu item's slug / url).
1031
+ foreach ( $submenu[ $main_slug ] as &$item ) {
1032
+ if ( $menu_slug === $item[2] ) {
1033
+ $item[2] = Boldgrid_Backup_Admin_Go_Pro::$url;
1034
+ }
1035
+ }
1036
+ }
1037
+
1038
+ return;
1039
+ }
1040
+
1041
+ /**
1042
+ * Register / enqueue scripts.
1043
+ *
1044
+ * This method is being called during the "admin_enqueue_scripts" hook.
1045
+ *
1046
+ * @since 1.5.2
1047
+ */
1048
+ public function admin_enqueue_scripts() {
1049
+ wp_register_style(
1050
+ 'boldgrid-backup-admin-new-thickbox-style',
1051
+ plugin_dir_url( __FILE__ ) . 'css/boldgrid-backup-admin-new-thickbox-style.css',
1052
+ array(),
1053
+ BOLDGRID_BACKUP_VERSION
1054
+ );
1055
+
1056
+ wp_register_style(
1057
+ 'boldgrid-backup-admin-hide-all',
1058
+ plugin_dir_url( __FILE__ ) . 'css/boldgrid-backup-admin-hide-all.css',
1059
+ array(),
1060
+ BOLDGRID_BACKUP_VERSION
1061
+ );
1062
+ }
1063
+
1064
+ /**
1065
+ * Backup the WordPress database.
1066
+ *
1067
+ * @since 1.0
1068
+ * @access private
1069
+ *
1070
+ * @global WP_Filesystem $wp_filesystem The WordPress Filesystem API global object.
1071
+ *
1072
+ * @return bool Status of the operation.
1073
+ */
1074
+ private function backup_database() {
1075
+
1076
+ /*
1077
+ * If we're omitting all the tables, we can skip trying to backup the
1078
+ * database.
1079
+ */
1080
+ if ( $this->db_omit->is_omit_all() ) {
1081
+ return true;
1082
+ }
1083
+
1084
+ // Check if functional.
1085
+ if ( ! $this->test->run_functionality_tests() ) {
1086
+ // Display an error notice.
1087
+ $this->notice->functionality_fail_notice();
1088
+ return array( 'error' => __( 'Unable to create backup, functionality test failed.', 'boldgrid_backup' ) );
1089
+ }
1090
+
1091
+ // Get the backup directory path.
1092
+ $backup_directory = $this->backup_dir->get();
1093
+
1094
+ // Connect to the WordPress Filesystem API.
1095
+ global $wp_filesystem;
1096
+
1097
+ // Check if the backup directory is writable.
1098
+ if ( ! $wp_filesystem->is_writable( $backup_directory ) ) {
1099
+ return array( 'error' => sprintf( __( 'The backup directory is not writable: %1$s.', 'boldgrid-backup' ), $backup_directory ) );
1100
+ }
1101
+
1102
+ // Create a file path for the dump file.
1103
+ $db_dump_filepath = $backup_directory . DIRECTORY_SEPARATOR . DB_NAME . '.' . date( 'Ymd-His' ) . '.sql';
1104
+
1105
+ // Save the file path.
1106
+ $this->db_dump_filepath = $db_dump_filepath;
1107
+
1108
+ $this->set_time_limit();
1109
+
1110
+ // Create a dump of our database.
1111
+ $status = $this->db_dump->dump( $db_dump_filepath );
1112
+ if ( ! empty( $status['error'] ) ) {
1113
+ return array( 'error' => $status['error'] );
1114
+ }
1115
+
1116
+ // Ensure file is written and is over 100 bytes.
1117
+ $exists = $this->test->exists( $db_dump_filepath );
1118
+ if ( ! $exists ) {
1119
+ return array( 'error' => sprintf( __( 'mysqldump file does not exist: %1$s', 'boldgrid-backup' ), $db_dump_filepath ) );
1120
+ }
1121
+ $dump_file_size = $this->wp_filesystem->size( $db_dump_filepath );
1122
+ if ( 100 > $dump_file_size ) {
1123
+ return array( 'error' => sprintf( __( 'mysqldump file was not written to: %1$s', 'boldgrid-backup' ), $db_dump_filepath ) );
1124
+ }
1125
+
1126
+ // Limit file permissions to the dump file.
1127
+ $wp_filesystem->chmod( $db_dump_filepath, 0600 );
1128
+
1129
+ // Return success.
1130
+ return true;
1131
+ }
1132
+
1133
+ /**
1134
+ * Restore the WordPress database from a dump file.
1135
+ *
1136
+ * @since 1.0
1137
+ * @access private
1138
+ *
1139
+ * @see Boldgrid_Backup_Admin_Test::run_functionality_tests()
1140
+ * @see Boldgrid_Backup_Admin_Backup_Dir::get()
1141
+ * @see Boldgrid_Backup_Admin_Core::execute_command()
1142
+ * @see Boldgrid_Backup_Admin_Utility::update_siteurl()
1143
+ * @global WP_Filesystem $wp_filesystem The WordPress Filesystem API global object.
1144
+ * @global wpdb $wpdb The WordPress database class object.
1145
+ *
1146
+ * @param string $db_dump_filepath File path to the mysql dump file.
1147
+ * @param string $db_prefix The database prefix to use, if restoring and it changed.
1148
+ * @return bool Status of the operation.
1149
+ */
1150
+ private function restore_database( $db_dump_filepath, $db_prefix = null ) {
1151
+ // Check input.
1152
+ if ( empty( $db_dump_filepath ) ) {
1153
+ // Display an error notice.
1154
+ do_action(
1155
+ 'boldgrid_backup_notice',
1156
+ esc_html__( 'The database dump file was not found.', 'boldgrid-backup' ),
1157
+ 'notice notice-error is-dismissible'
1158
+ );
1159
+
1160
+ return false;
1161
+ }
1162
+
1163
+ // Check if functional.
1164
+ if ( ! $this->test->run_functionality_tests() ) {
1165
+ // Display an error notice.
1166
+ $this->notice->functionality_fail_notice();
1167
+
1168
+ return false;
1169
+ }
1170
+
1171
+ // Connect to the WordPress Filesystem API.
1172
+ global $wp_filesystem;
1173
+
1174
+ // Save the file path.
1175
+ $this->db_dump_filepath = $db_dump_filepath;
1176
+
1177
+ // Get the WP Options for "siteurl" and "home", to restore later.
1178
+ $wp_siteurl = get_option( 'siteurl' );
1179
+ $wp_home = get_option( 'home' );
1180
+
1181
+ $this->set_time_limit();
1182
+
1183
+ $importer = new Boldgrid_Backup_Admin_Db_Import();
1184
+ $status = $importer->import( $db_dump_filepath );
1185
+
1186
+ if ( ! empty( $status['error'] ) ) {
1187
+ do_action( 'boldgrid_backup_notice', $status['error'], 'notice notice-error is-dismissible' );
1188
+ return false;
1189
+ }
1190
+
1191
+ // Set the database prefix, if supplied/changed.
1192
+ if ( ! empty( $db_prefix ) ) {
1193
+ // Connect to the WordPress database via $wpdb.
1194
+ global $wpdb;
1195
+
1196
+ // Set the database table prefix.
1197
+ $wpdb->set_prefix( $db_prefix );
1198
+ }
1199
+
1200
+ // Clear the WordPress cache.
1201
+ wp_cache_flush();
1202
+
1203
+ // Get the restored "siteurl" and "home".
1204
+ $restored_wp_siteurl = get_option( 'siteurl' );
1205
+ $restored_wp_home = get_option( 'home' );
1206
+
1207
+ // If changed, then update the siteurl in the database.
1208
+ if ( $restored_wp_siteurl !== $wp_siteurl ) {
1209
+ $update_siteurl_success =
1210
+ Boldgrid_Backup_Admin_Utility::update_siteurl( $restored_wp_siteurl, $wp_siteurl );
1211
+
1212
+ if ( ! $update_siteurl_success ) {
1213
+ // Display an error notice.
1214
+ do_action(
1215
+ 'boldgrid_backup_notice',
1216
+ esc_html__(
1217
+ 'The WordPress siteurl has changed. There was an issue changing it back. You will have to fix the siteurl manually in the database, or use an override in your wp-config.php file.',
1218
+ 'boldgrid-backup'
1219
+ ),
1220
+ 'notice notice-error is-dismissible'
1221
+ );
1222
+ }
1223
+ }
1224
+
1225
+ // If changed, then restore the WP Option for "home".
1226
+ if ( $restored_wp_home !== $wp_home ) {
1227
+ // Ensure there are no trailing slashes in siteurl.
1228
+ $wp_home = untrailingslashit( $wp_home );
1229
+
1230
+ update_option( 'home', $wp_home );
1231
+ }
1232
+
1233
+ // Return success.
1234
+ return true;
1235
+ }
1236
+
1237
+ /**
1238
+ * Get a single-dimension filelist array from a directory path.
1239
+ *
1240
+ * @since 1.0
1241
+ *
1242
+ * @param string $dirpath A directory path.
1243
+ * @return array A single-dimension filelist array for use in this class.
1244
+ */
1245
+ public function get_filelist( $dirpath ) {
1246
+
1247
+ // If this is a node_modules folder, do not iterate through it.
1248
+ if ( false !== strpos( $dirpath, '/node_modules' ) ) {
1249
+ return array();
1250
+ }
1251
+
1252
+ // Connect to the WordPress Filesystem API.
1253
+ global $wp_filesystem;
1254
+
1255
+ // Validate input.
1256
+ if ( empty( $dirpath ) || ! $wp_filesystem->is_readable( $dirpath ) ) {
1257
+ return array();
1258
+ }
1259
+
1260
+ // Remove any training slash in dirpath.
1261
+ $dirpath = untrailingslashit( $dirpath );
1262
+
1263
+ // Mark the base directory, if not set (the first run).
1264
+ if ( empty( $this->filelist_basedir ) ) {
1265
+ $this->filelist_basedir = $dirpath;
1266
+ }
1267
+
1268
+ // Get the non-recursive directory listing for the specified path.
1269
+ $dirlist = $wp_filesystem->dirlist( $dirpath, true, false );
1270
+
1271
+ // Initialize $filelist.
1272
+ $filelist = array();
1273
+
1274
+ /*
1275
+ * Add empty directory.
1276
+ *
1277
+ * If this is an empty directory, add the directory itself to the
1278
+ * $filelist.
1279
+ *
1280
+ * Previously we used Boldgrid_Backup_Admin_Compressor_Php_Zip::add_dirs
1281
+ * to add all empty directories, but that method is no longer needed.
1282
+ */
1283
+ if ( empty( $dirlist ) ) {
1284
+ $filelist[] = array(
1285
+ $dirpath,
1286
+ str_replace( ABSPATH, '', $dirpath ),
1287
+ 0,
1288
+ /*
1289
+ * @since 1.5.4, this 4th key represetnts 'type', as in a file
1290
+ * or a directory.
1291
+ */
1292
+ 'd',
1293
+ );
1294
+ }
1295
+
1296
+ // Sort the dirlist array by filename.
1297
+ uasort( $dirlist,
1298
+ function ( $a, $b ) {
1299
+ if ( $a['name'] < $b['name'] ) {
1300
+ return - 1;
1301
+ }
1302
+
1303
+ if ( $a['name'] > $b['name'] ) {
1304
+ return 1;
1305
+ }
1306
+
1307
+ return 0;
1308
+ }
1309
+ );
1310
+
1311
+ // Perform conversion.
1312
+ foreach ( $dirlist as $fileinfo ) {
1313
+ // If item is a directory, then recurse, merge, and continue.
1314
+ if ( 'd' === $fileinfo['type'] ) {
1315
+ $filelist_add = $this->get_filelist( $dirpath . '/' . $fileinfo['name'] );
1316
+
1317
+ $filelist = array_merge( $filelist, $filelist_add );
1318
+
1319
+ continue;
1320
+ }
1321
+
1322
+ // Get the file path.
1323
+ $filepath = $dirpath . '/' . $fileinfo['name'];
1324
+
1325
+ // The relative path inside the ZIP file.
1326
+ $relative_path = substr( $filepath, strlen( $this->filelist_basedir ) + 1 );
1327
+
1328
+ // For files, add to the filelist array.
1329
+ $filelist[] = array(
1330
+ $filepath,
1331
+ $relative_path,
1332
+ $fileinfo['size'],
1333
+ );
1334
+ }
1335
+
1336
+ // Return the array.
1337
+ return $filelist;
1338
+ }
1339
+
1340
+ /**
1341
+ * Get a recursive file list of the WordPress installation root directory.
1342
+ *
1343
+ * This is a recursive function, which uses the class property filelist_basedir.
1344
+ *
1345
+ * @since 1.0
1346
+ *
1347
+ * @see Boldgrid_Backup_Admin_Core::get_filelist().
1348
+ *
1349
+ * @param string $dirpath A directory path, defaults to ABSPATH.
1350
+ * @return array An array of absolute file paths, relative paths, and file sizes.
1351
+ * Example: https://pastebin.com/QiquHdcC
1352
+ */
1353
+ public function get_filtered_filelist( $dirpath = ABSPATH ) {
1354
+
1355
+ // Validate input.
1356
+ if ( empty( $dirpath ) || ! $this->wp_filesystem->is_readable( $dirpath ) ) {
1357
+ return array();
1358
+ }
1359
+
1360
+ // Get the recursive directory listing for the specified path.
1361
+ $filelist = $this->get_filelist( $dirpath );
1362
+
1363
+ // If no files were found, then return an empty array.
1364
+ if ( empty( $filelist ) ) {
1365
+ return array();
1366
+ }
1367
+
1368
+ // Initialize $new_filelist.
1369
+ $new_filelist = array();
1370
+
1371
+ // Filter the filelist array.
1372
+ foreach ( $filelist as $fileinfo ) {
1373
+
1374
+ // @todo The user needs a way to specifiy what to skip in the backups.
1375
+ $is_node_modules = false !== strpos( $fileinfo[1], '/node_modules/' );
1376
+ $is_backup_directory = $this->backup_dir->file_in_dir( $fileinfo[1] );
1377
+
1378
+ if ( $is_node_modules || $is_backup_directory ) {
1379
+ continue;
1380
+ }
1381
+
1382
+ if ( ! $this->folder_exclusion->allow_file( $fileinfo[1] ) ) {
1383
+ continue;
1384
+ }
1385
+
1386
+ $new_filelist[] = $fileinfo;
1387
+ }
1388
+
1389
+ // Replace filelist.
1390
+ $filelist = $new_filelist;
1391
+
1392
+ // Clear filelist_basedir.
1393
+ $this->filelist_basedir = null;
1394
+
1395
+ // Return the filelist array.
1396
+ return $filelist;
1397
+ }
1398
+
1399
+ /**
1400
+ * Generate an new archive file path.
1401
+ *
1402
+ * @since 1.0
1403
+ * @access private
1404
+ *
1405
+ * @param string $extension An optional file extension.
1406
+ * @return string|bool An archive file path, or FALSE on error.
1407
+ */
1408
+ public function generate_archive_path( $extension = null ) {
1409
+ // Get the backup directory path.
1410
+ $backup_directory = $this->backup_dir->get();
1411
+
1412
+ // Connect to the WordPress Filesystem API.
1413
+ global $wp_filesystem;
1414
+
1415
+ // Check if the backup directory is writable.
1416
+ if ( ! $wp_filesystem->is_writable( $backup_directory ) ) {
1417
+ return false;
1418
+ }
1419
+
1420
+ // Get backup identifier.
1421
+ $backup_identifier = $this->get_backup_identifier();
1422
+
1423
+ // Create a site identifier.
1424
+ $site_id = Boldgrid_Backup_Admin_Utility::create_site_id();
1425
+
1426
+ $filename = sprintf( 'boldgrid-backup-%1$s-%2$s-%3$s',
1427
+ $site_id,
1428
+ $backup_identifier,
1429
+ date( 'Ymd-His' )
1430
+ );
1431
+ $filename = sanitize_file_name( $filename );
1432
+
1433
+ // Create a file path with no extension (added later).
1434
+ $filepath = $backup_directory . DIRECTORY_SEPARATOR . $filename;
1435
+
1436
+ // If specified, add an extension.
1437
+ if ( ! empty( $extension ) ) {
1438
+ // Trim the input extension.
1439
+ $extension = trim( $extension, ' .' );
1440
+
1441
+ $filepath .= '.' . $extension;
1442
+ }
1443
+
1444
+ return $filepath;
1445
+ }
1446
+
1447
+ /**
1448
+ * Create an archive file containing the WordPress files.
1449
+ *
1450
+ * @since 1.0
1451
+ *
1452
+ * @see Boldgrid_Backup_Admin_Core::backup_database().
1453
+ *
1454
+ * @param bool $save A switch to save the archive file. Default is FALSE.
1455
+ * @param bool $dryrun An optional switch to perform a dry run test.
1456
+ * @return array An array of archive file information.
1457
+ */
1458
+ public function archive_files( $save = false, $dryrun = false ) {
1459
+ $this->pre_auto_update = 'pre_auto_update' === current_filter();
1460
+
1461
+ /**
1462
+ * Actions to take before any archiving begins.
1463
+ *
1464
+ * @since 1.5.2
1465
+ */
1466
+ do_action( 'boldgrid_backup_archive_files_init' );
1467
+
1468
+ if ( $save && ! $dryrun ) {
1469
+ $this->in_progress->set();
1470
+ }
1471
+
1472
+ $is_scheduled_backup = $this->doing_cron && ! $this->pre_auto_update;
1473
+
1474
+ /*
1475
+ * If this is a scheduled backup and no location is selected to save the
1476
+ * backup to, abort.
1477
+ *
1478
+ * While we could prevent he user from setting this up in the first place,
1479
+ * at the moment the settings page saves all settings. So, if the user
1480
+ * wanted to change their retention settings but did not want to schedule
1481
+ * backups, validating storage locations would be problematic.
1482
+ */
1483
+ if ( $is_scheduled_backup && ! $this->remote->any_enabled() ) {
1484
+ $error = __( 'No backup locations selected! While we could create a backup archive, you have not selected where the backup archive should be saved to. Please choose a storage location in your BoldGrid Backup Settings to save this backup archive to.', 'boldgrid-backup' );
1485
+ $this->archive_fail->schedule_fail_email( $error );
1486
+ return array(
1487
+ 'error' => $error,
1488
+ );
1489
+ }
1490
+
1491
+ // Check if functional.
1492
+ if ( ! $this->test->run_functionality_tests() ) {
1493
+ // Display an error notice, if not already on the test page.
1494
+ if ( ! isset( $_GET['page'] ) || 'boldgrid-backup-test' !== $_GET['page'] ) {
1495
+ // Display an error notice.
1496
+ $this->notice->functionality_fail_notice();
1497
+ }
1498
+
1499
+ return array(
1500
+ 'error' => 'Functionality tests fail.',
1501
+ );
1502
+ }
1503
+
1504
+ // Close any PHP session, so that another session can open during the backup operation.
1505
+ session_write_close();
1506
+
1507
+ // Initialize return array and add "compressor" and "save" keys.
1508
+ $info = array(
1509
+ 'mode' => 'backup',
1510
+ 'dryrun' => $dryrun,
1511
+ 'compressor' => null,
1512
+ 'filesize' => 0,
1513
+ 'save' => $save,
1514
+ 'total_size' => 0,
1515
+ /*
1516
+ * As of 1.6.0, the folder include and exclude settings below are
1517
+ * for informational purposes only. This array cannot be filtered to
1518
+ * adjust which folders are actually included / excluded.
1519
+ */
1520
+ 'folder_include' => $this->folder_exclusion->from_settings( 'include' ),
1521
+ 'folder_exclude' => $this->folder_exclusion->from_settings( 'exclude' ),
1522
+ 'table_exclude' => $this->db_omit->get_excluded_tables(),
1523
+ );
1524
+
1525
+ // Determine how this backup was triggered.
1526
+ if ( $this->pre_auto_update ) {
1527
+ $info['trigger'] = __( 'Auto update', 'boldgrid-bakcup' );
1528
+ } elseif ( $this->doing_ajax && is_user_logged_in() ) {
1529
+ $current_user = wp_get_current_user();
1530
+ $info['trigger'] = $current_user->user_login . ' (' . $current_user->user_email . ')';
1531
+ } elseif ( $this->doing_wp_cron ) {
1532
+ $info['trigger'] = 'WP cron';
1533
+ } elseif ( $this->doing_cron ) {
1534
+ $info['trigger'] = 'Cron';
1535
+ } else {
1536
+ $info['trigger'] = __( 'Unknown', 'boldgrid-backup' );
1537
+ }
1538
+
1539
+ $info['compressor'] = $this->compressors->get();
1540
+
1541
+ // If there is no available compressor, then fail.
1542
+ if ( null === $info['compressor'] ) {
1543
+ return array(
1544
+ 'error' => 'No available compressor.',
1545
+ );
1546
+ }
1547
+
1548
+ // Enforce retention setting.
1549
+ $this->enforce_retention();
1550
+
1551
+ // Prevent this script from dying.
1552
+ ignore_user_abort( true );
1553
+
1554
+ // Start timer.
1555
+ $time_start = microtime( true );
1556
+
1557
+ // Backup the database, if saving an archive file and not a dry run.
1558
+ if ( $save && ! $dryrun ) {
1559
+ $status = $this->backup_database();
1560
+
1561
+ if ( false === $status || ! empty( $status['error'] ) ) {
1562
+ return array(
1563
+ 'error' => ! empty( $status['error'] ) ? $status['error'] : __( 'An unknown error occurred when backing up the database.', 'boldgrid-backup' ),
1564
+ );
1565
+ }
1566
+ }
1567
+
1568
+ // Keep track of how long the site was paused for / the time to backup the database.
1569
+ $db_time_stop = microtime( true );
1570
+
1571
+ // Get the file list.
1572
+ $filelist = $this->get_filtered_filelist( ABSPATH );
1573
+
1574
+ // Initialize total_size.
1575
+ $info['total_size'] = 0;
1576
+
1577
+ // If not saving, then just return info.
1578
+ if ( ! $save ) {
1579
+ foreach ( $filelist as $fileinfo ) {
1580
+ // Add the file size to the total.
1581
+ $info['total_size'] += $fileinfo[2];
1582
+ }
1583
+
1584
+ return $info;
1585
+ }
1586
+
1587
+ // Get the backup directory path.
1588
+ $backup_directory = $this->backup_dir->get();
1589
+
1590
+ // Check if the backup directory is writable.
1591
+ if ( ! $this->wp_filesystem->is_writable( $backup_directory ) ) {
1592
+ return false;
1593
+ }
1594
+
1595
+ // Add the database dump file to the beginning of file list.
1596
+ if ( ! empty( $this->db_dump_filepath ) ) {
1597
+ $db_file_array = array(
1598
+ $this->db_dump_filepath,
1599
+ substr( $this->db_dump_filepath, strlen( $backup_directory ) + 1 ),
1600
+ $this->wp_filesystem->size( $this->db_dump_filepath ),
1601
+ );
1602
+
1603
+ array_unshift( $filelist, $db_file_array );
1604
+ }
1605
+
1606
+ $this->set_time_limit();
1607
+
1608
+ /**
1609
+ * Allow the filtering of our $info before generating a backup.
1610
+ *
1611
+ * @since 1.5.1
1612
+ *
1613
+ * @param array $info See Boldgrid_Backup_Admin_Compressor_Php_Zip::archive_files.
1614
+ */
1615
+ $info = apply_filters( 'boldgrid_backup_pre_archive_info', $info );
1616
+
1617
+ /*
1618
+ * Use the chosen compressor to build an archive.
1619
+ * If the is no available compressor, then return an error.
1620
+ */
1621
+ switch ( $info['compressor'] ) {
1622
+ case 'php_zip' :
1623
+ $compressor = new Boldgrid_Backup_Admin_Compressor_Php_Zip( $this );
1624
+ $status = $compressor->archive_files( $filelist, $info );
1625
+ break;
1626
+ case 'pcl_zip' :
1627
+ $compressor = new Boldgrid_Backup_Admin_Compressor_Pcl_Zip( $this );
1628
+ $status = $compressor->archive_files( $filelist, $info );
1629
+ break;
1630
+ case 'php_bz2' :
1631
+ // Generate a new archive file path.
1632
+ $info['filepath'] = $this->generate_archive_path( 'b2z' );
1633
+ break;
1634
+ case 'php_zlib' :
1635
+ // Generate a new archive file path.
1636
+ $info['filepath'] = $this->generate_archive_path( 'zlib' );
1637
+ break;
1638
+ case 'php_lzf' :
1639
+ // Generate a new archive file path.
1640
+ $info['filepath'] = $this->generate_archive_path( 'lzf' );
1641
+ break;
1642
+ case 'system_tar' :
1643
+ // Generate a new archive file path.
1644
+ $info['filepath'] = $this->generate_archive_path( 'tar.gz' );
1645
+ break;
1646
+ case 'system_zip' :
1647
+ // Generate a new archive file path.
1648
+ $info['filepath'] = $this->generate_archive_path( 'zip' );
1649
+ break;
1650
+ default :
1651
+ $status = array(
1652
+ 'error' => 'No available compressor',
1653
+ );
1654
+ break;
1655
+ }
1656
+
1657
+ $info['total_size'] += $this->filelist->get_total_size( $filelist );
1658
+
1659
+ if ( true === $status && ! $this->wp_filesystem->exists( $info['filepath'] ) ) {
1660
+ $status = array(
1661
+ 'error' => 'The archive file "' . $info['filepath'] . '" was not written.',
1662
+ );
1663
+ }
1664
+
1665
+ if ( ! empty( $status['error'] ) ) {
1666
+ return $status;
1667
+ }
1668
+
1669
+ $info['lastmodunix'] = $this->wp_filesystem->mtime( $info['filepath'] );
1670
+
1671
+ if ( $save && ! $dryrun ) {
1672
+ // Modify the archive file permissions to help protect from public access.
1673
+ $this->wp_filesystem->chmod( $info['filepath'], 0600 );
1674
+
1675
+ // Add some statistics to the return.
1676
+ $info['filesize'] = $this->wp_filesystem->size( $info['filepath'] );
1677
+
1678
+ // Delete the temporary database dump file.
1679
+ $this->wp_filesystem->delete( $this->db_dump_filepath, false, 'f' );
1680
+ }
1681
+
1682
+ // Stop timer.
1683
+ $time_stop = microtime( true );
1684
+
1685
+ // Calculate duration.
1686
+ $info['duration'] = number_format( ( $time_stop - $time_start ), 2, '.', '' );
1687
+ $info['db_duration'] = number_format( ( $db_time_stop - $time_start ), 2, '.', '' );
1688
+
1689
+ /**
1690
+ * Actions to take after a backup has been created.
1691
+ *
1692
+ * At priority 100, we delete the local backup file if the user does
1693
+ * not want to keep it.
1694
+ *
1695
+ * At priority 200, we send an email to the user with a summary of the
1696
+ * backup and the jobs.
1697
+ *
1698
+ * @since 1.5.2
1699
+ *
1700
+ * @param array $info{
1701
+ * An array of info about the backup just created.
1702
+ *
1703
+ * @type string $mode backup
1704
+ * @type bool $dryrun
1705
+ * @type string $compressor pcl_zip
1706
+ * @type int $filesize 30992482
1707
+ * @type bool $save
1708
+ * @type int $total_size
1709
+ * @type string $filepath C:\file.zip
1710
+ * @type int $lastmodunix 1506602959
1711
+ * @type int $duration 57.08
1712
+ * @type int $db_duration 0.35
1713
+ * @type bool $mail_success
1714
+ * }
1715
+ */
1716
+ do_action( 'boldgrid_backup_post_archive_files', $info );
1717
+
1718
+ // Send an email.
1719
+ if ( $this->email->user_wants_notification( 'backup' ) && $this->doing_ajax ) {
1720
+ $email_parts = $this->email->post_archive_parts( $info );
1721
+ $email_body = $email_parts['body']['main'] . $email_parts['body']['signature'];
1722
+ $info['mail_success'] = $this->email->send( $email_parts['subject'], $email_body );
1723
+ }
1724
+
1725
+ // If not a dry-run test, update the last backup option and enforce retention.
1726
+ if ( ! $dryrun ) {
1727
+ // Update WP option for "boldgrid_backup_last_backup".
1728
+ update_site_option( 'boldgrid_backup_last_backup', time() );
1729
+
1730
+ $this->archive_log->write( $info );
1731
+
1732
+ // Enforce retention setting.
1733
+ $this->enforce_retention();
1734
+
1735
+ update_option( 'boldgrid_backup_latest_backup', $info );
1736
+ }
1737
+
1738
+ // Return the array of archive information.
1739
+ return $info;
1740
+ }
1741
+
1742
+ /**
1743
+ * Get information for the list of archive file(s) (in descending order by date modified).
1744
+ *
1745
+ * @since 1.0
1746
+ *
1747
+ * @global WP_Filesystem $wp_filesystem The WordPress Filesystem API global object.
1748
+ *
1749
+ * @param string $download_filename A filename to match to get info.
1750
+ * @param string $backup_directory Specify a directory to look within for backups.
1751
+ * @return array {
1752
+ * A numbered array of arrays containing the following indexes.
1753
+ * @type string $filepath Archive file path.
1754
+ * @type string $filename Archive filename.
1755
+ * @type string $filedate Localized file modification date.
1756
+ * @type int $filesize The archive file size in bytes.
1757
+ * @type int $lastmodunix The archive file modification time in unix seconds.
1758
+ * }
1759
+ */
1760
+ public function get_archive_list( $download_filename = null, $backup_directory = null ) {
1761
+ // Connect to the WordPress Filesystem API.
1762
+ global $wp_filesystem;
1763
+
1764
+ // Initialize $archive_files array.
1765
+ $archive_files = array();
1766
+
1767
+ // Get the backup directory.
1768
+ if ( is_null( $backup_directory ) ) {
1769
+ $backup_directory = $this->backup_dir->get();
1770
+ }
1771
+
1772
+ // If the backup directory is not configured, then return an empty array.
1773
+ if ( ! $backup_directory ) {
1774
+ return array();
1775
+ }
1776
+
1777
+ // Find all backups.
1778
+ $dirlist = $wp_filesystem->dirlist( $backup_directory, false, false );
1779
+
1780
+ // If no files were found, then return an empty array.
1781
+ if ( empty( $dirlist ) ) {
1782
+ return array();
1783
+ }
1784
+
1785
+ // Sort the dirlist array by "lastmodunix" descending.
1786
+ uasort( $dirlist,
1787
+ function ( $a, $b ) {
1788
+ if ( $a['lastmodunix'] < $b['lastmodunix'] ) {
1789
+ return 1;
1790
+ }
1791
+
1792
+ if ( $a['lastmodunix'] > $b['lastmodunix'] ) {
1793
+ return - 1;
1794
+ }
1795
+
1796
+ return 0;
1797
+ }
1798
+ );
1799
+
1800
+ // Initialize $index.
1801
+ $index = -1;
1802
+
1803
+ // Filter the array.
1804
+ foreach ( $dirlist as $fileinfo ) {
1805
+ if ( $this->archive->is_site_archive( $fileinfo['name'] ) ) {
1806
+ // Increment the index.
1807
+ $index++;
1808
+
1809
+ // If looking for one match, skip an iteration if not the matching filename.
1810
+ if ( ! empty( $download_filename ) && $download_filename !== $fileinfo['name'] ) {
1811
+ continue;
1812
+ }
1813
+
1814
+ // Create the return array.
1815
+ // @todo Should we use the data and time from the filename, or rely on lastmodunix?
1816
+ $archive_files[ $index ] = array(
1817
+ 'filepath' => $backup_directory . '/' . $fileinfo['name'],
1818
+ 'filename' => $fileinfo['name'],
1819
+ 'filedate' => get_date_from_gmt(
1820
+ date( 'Y-m-d H:i:s', $fileinfo['lastmodunix'] ), 'n/j/Y g:i A'
1821
+ ),
1822
+ 'filesize' => $fileinfo['size'],
1823
+ 'lastmodunix' => $fileinfo['lastmodunix'],
1824
+ );
1825
+
1826
+ // If looking for info on one file and we found the match, then break the loop.
1827
+ if ( ! empty( $download_filename ) ) {
1828
+ break;
1829
+ }
1830
+ }
1831
+ }
1832
+
1833
+ // Return the array.
1834
+ return $archive_files;
1835
+ }
1836
+
1837
+ /**
1838
+ * Delete an archive file.
1839
+ *
1840
+ * @since 1.0
1841
+ * @access private
1842
+ *
1843
+ * @return bool Whether or not the archive file was deleted.
1844
+ */
1845
+ public function delete_archive_file() {
1846
+
1847
+ // If a deletion was not requested, then abort.
1848
+ if ( empty( $_POST['delete_now'] ) ) {
1849
+ return false;
1850
+ }
1851
+
1852
+ // Initialize $delete_ok.
1853
+ $delete_ok = true;
1854
+
1855
+ // Verify nonce, or die.
1856
+ check_admin_referer( 'archive_auth', 'archive_auth' );
1857
+
1858
+ // Validate archive_key.
1859
+ if ( isset( $_POST['archive_key'] ) && is_numeric( $_POST['archive_key'] ) ) {
1860
+ $archive_key = (int) $_POST['archive_key'];
1861
+ } else {
1862
+ $delete_ok = false;
1863
+
1864
+ do_action(
1865
+ 'boldgrid_backup_notice',
1866
+ esc_html__( 'Invalid key for the selected archive file.', 'boldgrid-backup' ),
1867
+ 'notice notice-error is-dismissible'
1868
+ );
1869
+
1870
+ $archive_key = null;
1871
+ }
1872
+
1873
+ // Validate archive_filename.
1874
+ if ( ! empty( $_POST['archive_filename'] ) ) {
1875
+ $archive_filename = sanitize_file_name( $_POST['archive_filename'] );
1876
+ } else {
1877
+ // Fail with a notice.
1878
+ do_action(
1879
+ 'boldgrid_backup_notice',
1880
+ esc_html__( 'Invalid filename for the selected archive file.', 'boldgrid-backup' ),
1881
+ 'notice notice-error is-dismissible'
1882
+ );
1883
+
1884
+ return false;
1885
+ }
1886
+
1887
+ // If there are errors, then abort.
1888
+ if ( ! $delete_ok ) {
1889
+ return false;
1890
+ }
1891
+
1892
+ // Get archive list.
1893
+ $archives = $this->get_archive_list( $archive_filename );
1894
+
1895
+ // If no files were found, then show a notice.
1896
+ if ( empty( $archives ) ) {
1897
+ // Fail with a notice.
1898
+ do_action(
1899
+ 'boldgrid_backup_notice',
1900
+ esc_html__( 'No archive files were found.', 'boldgrid-backup' ),
1901
+ 'notice notice-error is-dismissible'
1902
+ );
1903
+
1904
+ return false;
1905
+ }
1906
+
1907
+ // Locate the filename by key number.
1908
+ $filename = (
1909
+ ! empty( $archives[ $archive_key ]['filename'] ) ?
1910
+ $archives[ $archive_key ]['filename'] : null
1911
+ );
1912
+
1913
+ // Verify specified filename.
1914
+ if ( $archive_filename !== $filename ) {
1915
+ // Fail with a notice.
1916
+ do_action( 'boldgrid_backup_notice',
1917
+ esc_html__( 'The selected archive file was not found.', 'boldgrid-backup' ),
1918
+ 'notice notice-error is-dismissible'
1919
+ );
1920
+
1921
+ return false;
1922
+ }
1923
+
1924
+ // Get the file path to delete.
1925
+ $filepath = (
1926
+ ! empty( $archives[ $archive_key ]['filepath'] ) ?
1927
+ $archives[ $archive_key ]['filepath'] : null
1928
+ );
1929
+
1930
+ $delete_ok = $this->archive->delete( $filepath );
1931
+
1932
+ // Display notice of deletion status.
1933
+ if ( ! $delete_ok ) {
1934
+ do_action(
1935
+ 'boldgrid_backup_notice',
1936
+ esc_html__( 'Error deleting the selected archive file.', 'boldgrid-backup' ),
1937
+ 'notice notice-error is-dismissible'
1938
+ );
1939
+ }
1940
+
1941
+ /**
1942
+ * Take action after a user deletes a backup.
1943
+ *
1944
+ * @since 1.5.3
1945
+ *
1946
+ * @param string $filepath
1947
+ * @param bool $delete_ok
1948
+ */
1949
+ do_action( 'boldgrid_backup_user_deleted_backup', $filepath, $delete_ok );
1950
+
1951
+ // Return deletion status.
1952
+ return $delete_ok;
1953
+ }
1954
+
1955
+ /**
1956
+ * Get the newest database dump file path from a restored archive.
1957
+ *
1958
+ * @since 1.0
1959
+ *
1960
+ * @global WP_Filesystem $wp_filesystem The WordPress Filesystem API global object.
1961
+ *
1962
+ * @param string $filepath The full filepath to the file .zip archive.
1963
+ * @return string File path to the database dump file.
1964
+ */
1965
+ public function get_dump_file( $filepath ) {
1966
+
1967
+ if ( empty( $filepath ) || ! $this->wp_filesystem->exists( $filepath ) ) {
1968
+ return '';
1969
+ }
1970
+
1971
+ /*
1972
+ * Get the sql file to restore.
1973
+ *
1974
+ * These few lines below are new to the get_dump_file method.
1975
+ * Historically we searched the ABSPATH for a filename matching
1976
+ * ########-######.sql and returned the first one we found. Well, what
1977
+ * if there were more than one matching file, which .sql file do we
1978
+ * restore?
1979
+ *
1980
+ * $pcl_zip->get_sqls Searches the zip file and finds a list of all
1981
+ * .sql files that match the pattern we're looking for and returns them.
1982
+ * We should only get 1 back, and that is the appropriate dump file.
1983
+ *
1984
+ * These few lines should do the trick. If not, the original code is
1985
+ * still in place to take the initial approach and find a dump file to
1986
+ * restore (the first applicable one it can find).
1987
+ */
1988
+ $pcl_zip = new Boldgrid_Backup_Admin_Compressor_Pcl_Zip( $this );
1989
+ $sqls = $pcl_zip->get_sqls( $filepath );
1990
+ if ( 1 === count( $sqls ) ) {
1991
+ return ABSPATH . $sqls[0];
1992
+ }
1993
+
1994
+ // Initialize $db_dump_filepath.
1995
+ $db_dump_filepath = '';
1996
+
1997
+ // Find all backups.
1998
+ $dirlist = $this->wp_filesystem->dirlist( ABSPATH, false, false );
1999
+
2000
+ // If no files were found, then return an empty array.
2001
+ if ( empty( $dirlist ) ) {
2002
+ return '';
2003
+ }
2004
+
2005
+ // Sort the dirlist array by "name" descending.
2006
+ uasort( $dirlist,
2007
+ function ( $a, $b ) {
2008
+ if ( $a['name'] < $b['name'] ) {
2009
+ return 1;
2010
+ }
2011
+
2012
+ if ( $a['name'] > $b['name'] ) {
2013
+ return - 1;
2014
+ }
2015
+
2016
+ return 0;
2017
+ }
2018
+ );
2019
+
2020
+ // Find the first occurrence of a MySQL dump file.
2021
+ // Format: *.########-######.sql
2022
+ // An example filename: joec_wrdp2.20160919-162431.sql
2023
+ foreach ( $dirlist as $fileinfo ) {
2024
+ if ( 1 === preg_match( '/\.[\d]+-[\d]+\.sql$/',$fileinfo['name'] ) ) {
2025
+ $db_dump_filepath = ABSPATH . $fileinfo['name'];
2026
+ break;
2027
+ }
2028
+ }
2029
+
2030
+ // Return the array.
2031
+ return $db_dump_filepath;
2032
+ }
2033
+
2034
+ /**
2035
+ * Restore from a specified archive file.
2036
+ *
2037
+ * @since 1.0
2038
+ *
2039
+ * @see https://codex.wordpress.org/Function_Reference/flush_rewrite_rules
2040
+ *
2041
+ * @param bool $dryrun An optional switch to perform a dry run test.
2042
+ * @return array An array of archive file information.
2043
+ */
2044
+ public function restore_archive_file( $dryrun = false ) {
2045
+ $restore_ok = true;
2046
+
2047
+ // If a restoration was not requested, then abort.
2048
+ if ( empty( $_POST['restore_now'] ) ) {
2049
+ return array(
2050
+ 'error' => esc_html__( 'Invalid restore_now value.', 'boldgrid-backup' ),
2051
+ );
2052
+ }
2053
+
2054
+ // Check if functional.
2055
+ if ( ! $this->test->run_functionality_tests() ) {
2056
+ return array(
2057
+ 'error' => esc_html__( 'Functionality tests fail.', 'boldgrid-backup' ),
2058
+ );
2059
+ }
2060
+
2061
+ // Initialize variables.
2062
+ $archive_key = null;
2063
+ $archive_filename = null;
2064
+
2065
+ // Validate archive_key.
2066
+ if ( isset( $_POST['archive_key'] ) && is_numeric( $_POST['archive_key'] ) ) {
2067
+ $archive_key = (int) $_POST['archive_key'];
2068
+ } else {
2069
+ return array(
2070
+ 'error' => esc_html__( 'Invalid key for the selected archive file.', 'boldgrid-backup' ),
2071
+ );
2072
+ }
2073
+
2074
+ // Validate archive_filename.
2075
+ if ( ! empty( $_POST['archive_filename'] ) ) {
2076
+ $archive_filename = sanitize_file_name( $_POST['archive_filename'] );
2077
+ } else {
2078
+ return array(
2079
+ 'error' => esc_html__( 'Invalid filename for the selected archive file.', 'boldgrid-backup' ),
2080
+ );
2081
+ }
2082
+
2083
+ // Close any PHP session, so that another session can open during this restore operation.
2084
+ session_write_close();
2085
+
2086
+ $archives = $this->get_archive_list( $archive_filename );
2087
+ if ( empty( $archives ) ) {
2088
+ return array(
2089
+ 'error' => esc_html__( 'No archive files were found.', 'boldgrid-backup' ),
2090
+ );
2091
+ }
2092
+
2093
+ $filename = ! empty( $archives[ $archive_key ]['filename'] ) ? $archives[ $archive_key ]['filename'] : null;
2094
+
2095
+ if ( $archive_filename !== $filename ) {
2096
+ return array(
2097
+ 'error' => esc_html__( 'The selected archive file was not found.', 'boldgrid-backup' ),
2098
+ );
2099
+ }
2100
+
2101
+ $filepath = ! empty( $archives[ $archive_key ]['filepath'] ) ? $archives[ $archive_key ]['filepath'] : null;
2102
+
2103
+ if ( ! empty( $filepath ) && $this->wp_filesystem->exists( $filepath ) ) {
2104
+ $filesize = $this->wp_filesystem->size( $filepath );
2105
+ } else {
2106
+ return array(
2107
+ 'error' => esc_html__( 'The selected archive file is empty.', 'boldgrid-backup' ),
2108
+ );
2109
+ }
2110
+
2111
+ // Populate $info.
2112
+ $info = array(
2113
+ 'mode' => 'restore',
2114
+ 'dryrun' => $dryrun,
2115
+ 'filename' => $archive_filename,
2116
+ 'filepath' => $filepath,
2117
+ 'filesize' => $filesize,
2118
+ 'archive_key' => $archive_key,
2119
+ 'restore_ok' => $restore_ok,
2120
+ );
2121
+
2122
+ // Prevent this script from dying.
2123
+ ignore_user_abort( true );
2124
+
2125
+ $this->set_time_limit();
2126
+
2127
+ /**
2128
+ * Action to take before restoring an archive.
2129
+ *
2130
+ * @since 1.5.1
2131
+ *
2132
+ * @param array $info
2133
+ */
2134
+ do_action( 'boldgrid_backup_pre_restore', $info );
2135
+
2136
+ $this->restore_helper->set_writable_permissions( $info['filepath'] );
2137
+
2138
+ $unzip_status = ! $dryrun ? unzip_file( $info['filepath'], ABSPATH ) : null;
2139
+
2140
+ if ( is_wp_error( $unzip_status ) ) {
2141
+ $error = false;
2142
+
2143
+ /**
2144
+ * Take action when a restoration fails.
2145
+ *
2146
+ * Those actions may return a custom error message, such as:
2147
+ * "Your restoration failed, but we did XYZ. Please try again".
2148
+ *
2149
+ * @param WP_Error $unzip_status
2150
+ */
2151
+ $error = apply_filters( 'boldgrid_backup_restore_fail', $unzip_status );
2152
+
2153
+ if ( empty( $error ) ) {
2154
+ $message = $unzip_status->get_error_message();
2155
+ $data = $unzip_status->get_error_data();
2156
+ $error = sprintf( '%1$s%2$s', $message, is_string( $data ) && ! empty( $data ) ? ': ' . $data : '' );
2157
+ }
2158
+
2159
+ return array(
2160
+ 'error' => $error,
2161
+ );
2162
+ }
2163
+
2164
+ /**
2165
+ * Action to take after restoring an archive.
2166
+ *
2167
+ * @since 1.5.1
2168
+ *
2169
+ * @param array $info
2170
+ */
2171
+ do_action( 'boldgrid_backup_post_restore', $info );
2172
+
2173
+ /*
2174
+ * Restore database.
2175
+ *
2176
+ * As of 1.5.4, we are checking to see if the backup archive contains a
2177
+ * database dump before running the below conditional. Not all archives
2178
+ * will contain a database dump, so we may be able to skip this step.
2179
+ */
2180
+ $db_dump_filepath = $this->get_dump_file( $filepath );
2181
+ if ( ! $dryrun && ! empty( $db_dump_filepath ) ) {
2182
+ $db_prefix = null;
2183
+
2184
+ // Get the database table prefix from the new "wp-config.php" file, if exists.
2185
+ if ( $this->wp_filesystem->exists( ABSPATH . 'wp-config.php' ) ) {
2186
+ $wpcfg_contents = $this->wp_filesystem->get_contents( ABSPATH . 'wp-config.php' );
2187
+ }
2188
+
2189
+ if ( ! empty( $wpcfg_contents ) ) {
2190
+ preg_match( '#\$table_prefix.*?=.*?' . "'" . '(.*?)' . "'" . ';#', $wpcfg_contents, $matches );
2191
+
2192
+ if ( ! empty( $matches[1] ) ) {
2193
+ $db_prefix = $matches[1];
2194
+ }
2195
+ }
2196
+
2197
+ // Restore the database and then delete the dump.
2198
+ $restore_ok = $this->restore_database( $db_dump_filepath, $db_prefix );
2199
+ $this->wp_filesystem->delete( $db_dump_filepath, false, 'f' );
2200
+
2201
+ // Display notice of deletion status.
2202
+ if ( ! $restore_ok ) {
2203
+ return array(
2204
+ 'error' => esc_html__( 'Could not restore database.', 'boldgrid-backup' ),
2205
+ );
2206
+ }
2207
+ }
2208
+
2209
+ // Clear rollback information and restoration cron jobs that may be present.
2210
+ $this->auto_rollback->cancel();
2211
+
2212
+ // Get settings.
2213
+ $settings = $this->settings->get_settings();
2214
+
2215
+ // If enabled, send email notification for restoration completed.
2216
+ if ( ! empty( $settings['notifications']['restore'] ) ) {
2217
+ // Include the mail template.
2218
+ include BOLDGRID_BACKUP_PATH . '/admin/partials/boldgrid-backup-admin-mail-restore.php';
2219
+
2220
+ // Send the notification.
2221
+ // Parameters come from the included mail template file.
2222
+ $info['mail_success'] = $this->email->send( $subject, $body );
2223
+ }
2224
+
2225
+ // Update status.
2226
+ $info['restore_ok'] = $restore_ok;
2227
+
2228
+ // Check backup directory.
2229
+ $info['backup_directory_set'] = $this->backup_dir->get();
2230
+
2231
+ // Return info array.
2232
+ return $info;
2233
+ }
2234
+
2235
+ /**
2236
+ * Menu callback to display the Backup home page.
2237
+ *
2238
+ * @since 1.0
2239
+ *
2240
+ * @see Boldgrid_Backup_Admin_Upload::upload_archive_file().
2241
+ * @global string $pagenow
2242
+ *
2243
+ * @return null
2244
+ */
2245
+ public function page_archives() {
2246
+ global $pagenow;
2247
+
2248
+ // Thickbox used for Backup Site Now modal.
2249
+ add_thickbox();
2250
+ wp_enqueue_style( 'boldgrid-backup-admin-new-thickbox-style' );
2251
+
2252
+ wp_enqueue_style( 'bglib-ui-css' );
2253
+
2254
+ // Run the functionality tests.
2255
+ $is_functional = $this->test->run_functionality_tests();
2256
+
2257
+ // If tests fail, then show an admin notice and abort.
2258
+ if ( ! $is_functional ) {
2259
+ $this->notice->functionality_fail_notice();
2260
+
2261
+ return;
2262
+ }
2263
+
2264
+ $this->auto_rollback->enqueue_home_scripts();
2265
+ $this->auto_rollback->enqueue_backup_scripts();
2266
+
2267
+ $this->folder_exclusion->enqueue_scripts();
2268
+ $this->db_omit->enqueue_scripts();
2269
+
2270
+ // If uploading an archive file.
2271
+ if ( ! empty( $_FILES['file'] ) ) {
2272
+ $this->upload->upload_archive_file();
2273
+ }
2274
+
2275
+ // Get archive list.
2276
+ $archives = $this->get_archive_list();
2277
+
2278
+ // Get the archives file count.
2279
+ $archives_count = count( $archives );
2280
+
2281
+ // Get the total size for all archives.
2282
+ $archives_size = 0;
2283
+
2284
+ foreach ( $archives as $archive ) {
2285
+ $archives_size += $archive['filesize'];
2286
+ }
2287
+
2288
+ // Get backup identifier.
2289
+ $backup_identifier = $this->get_backup_identifier();
2290
+
2291
+ $settings = $this->settings->get_settings();
2292
+
2293
+ // If the directory path is not in the settings, then add it for the form.
2294
+ if ( empty( $settings['backup_directory'] ) ) {
2295
+ $settings['backup_directory'] = $this->backup_dir->get();
2296
+ }
2297
+
2298
+ $table = $this->archives->get_table();
2299
+
2300
+ // Include the home page template.
2301
+ include BOLDGRID_BACKUP_PATH . '/admin/partials/boldgrid-backup-admin-home.php';
2302
+
2303
+ return;
2304
+ }
2305
+
2306
+ /**
2307
+ * Callback function for creating a backup archive file now via AJAX.
2308
+ *
2309
+ * @since 1.0
2310
+ *
2311
+ * @see Boldgrid_Backup_Admin_Core::archive_files()
2312
+ */
2313
+ public function boldgrid_backup_now_callback() {
2314
+
2315
+ // Verify nonce.
2316
+ if ( ! isset( $_POST['backup_auth'] ) || 1 !== check_ajax_referer( 'boldgrid_backup_now', 'backup_auth', false ) ) {
2317
+ $this->notice->add_user_notice(
2318
+ '<p>' . esc_html__( 'Security violation (invalid nonce).', 'boldgrid-backup' ) . '</p>',
2319
+ 'error'
2320
+ );
2321
+ wp_die();
2322
+ }
2323
+
2324
+ // Check user capabilities.
2325
+ if ( ! current_user_can( 'update_plugins' ) ) {
2326
+ $this->notice->add_user_notice(
2327
+ '<p>' . esc_html__( 'Security violation (not authorized).', 'boldgrid-backup' ) . '</p>',
2328
+ 'error'
2329
+ );
2330
+ wp_die();
2331
+ }
2332
+
2333
+ $this->is_backup_now = true;
2334
+
2335
+ $key = 'folder_exclusion_type';
2336
+ $this->is_backup_full = isset( $_POST[ $key ] ) && 'full' === $_POST[ $key ];
2337
+
2338
+ $this->is_archiving_update_protection = ! empty( $_POST['is_updating'] ) &&
2339
+ 'true' === $_POST['is_updating'];
2340
+
2341
+ $archive_info = $this->archive_files( true );
2342
+
2343
+ if ( ! $this->is_archiving_update_protection ) {
2344
+ $message = include BOLDGRID_BACKUP_PATH . '/admin/partials/boldgrid-backup-admin-backup.php';
2345
+ $this->notice->add_user_notice( $message['message'], $message['class'] );
2346
+ wp_send_json_success( array(
2347
+ 'callback' => 'reload',
2348
+ ));
2349
+ } else {
2350
+ update_site_option( 'boldgrid_backup_pending_rollback', $archive_info );
2351
+ wp_send_json_success( array(
2352
+ 'callback' => 'updateProtectionEnabled',
2353
+ ));
2354
+ }
2355
+ }
2356
+
2357
+ /**
2358
+ * Callback function for downloading an archive file via AJAX.
2359
+ *
2360
+ * This callback function should only be called if the WP_Filesystem method is "direct", or
2361
+ * a message should be displayed with the path to download using an alternate method.
2362
+ *
2363
+ * @since 1.0
2364
+ */
2365
+ public function download_archive_file_callback() {
2366
+ // Verify nonce, or die.
2367
+ check_ajax_referer( 'archive_auth', 'wpnonce' );
2368
+
2369
+ // Check user capabilities.
2370
+ if ( ! current_user_can( 'update_plugins' ) ) {
2371
+ esc_html_e( 'Security violation (not authorized).', 'boldgrid-backup' );
2372
+ wp_die();
2373
+ }
2374
+
2375
+ // Validate download_key.
2376
+ if ( isset( $_POST['download_key'] ) && is_numeric( $_POST['download_key'] ) ) {
2377
+ $download_key = (int) $_POST['download_key'];
2378
+ } else {
2379
+ esc_html_e( 'INVALID DOWNLOAD KEY', 'boldgrid-backup' );
2380
+ wp_die();
2381
+ }
2382
+
2383
+ // Validate download_filename.
2384
+ if ( ! empty( $_POST['download_filename'] ) ) {
2385
+ $download_filename = sanitize_file_name( $_POST['download_filename'] );
2386
+ } else {
2387
+ esc_html_e( 'INVALID DOWNLOAD FILENAME', 'boldgrid-backup' );
2388
+ wp_die();
2389
+ }
2390
+
2391
+ // Get the current wp_filesystem access method.
2392
+ $access_type = get_filesystem_method();
2393
+
2394
+ // Check WP_Filesystem method; ensure it is "direct".
2395
+ if ( 'direct' !== $access_type ) {
2396
+ esc_html_e( 'WP_Filesystem method is not "direct"', 'boldgrid-backup' );
2397
+ wp_die();
2398
+ }
2399
+
2400
+ // Get archive list.
2401
+ $archives = $this->get_archive_list( $download_filename );
2402
+
2403
+ // If no files were found, then abort.
2404
+ if ( empty( $archives ) ) {
2405
+ esc_html_e( 'NO BACKUP ARCHIVES FOUND', 'boldgrid-backup' );
2406
+ wp_die();
2407
+ }
2408
+
2409
+ // Locate the filename by key number.
2410
+ $filename = (
2411
+ ! empty( $archives[ $download_key ]['filename'] ) ?
2412
+ $archives[ $download_key ]['filename'] : null
2413
+ );
2414
+
2415
+ // Verify filename.
2416
+ if ( $download_filename !== $filename ) {
2417
+ esc_html_e( 'FILE NOT FOUND', 'boldgrid-backup' );
2418
+ wp_die();
2419
+ }
2420
+
2421
+ $filepath = $archives[ $download_key ]['filepath'];
2422
+
2423
+ $filesize = $archives[ $download_key ]['filesize'];
2424
+
2425
+ // Send header.
2426
+ header( 'Content-Disposition: attachment; filename="' . $filename . '"' );
2427
+ header( 'Content-Transfer-Encoding: binary' );
2428
+ header( 'Content-Type: binary/octet-stream' );
2429
+ header( 'Content-Length: ' . $filesize );
2430
+
2431
+ // Check and flush output buffer if needed.
2432
+ if ( 0 !== ob_get_level() ) {
2433
+ ob_end_flush();
2434
+ }
2435
+
2436
+ // Close any PHP session, so another session can open during the download.
2437
+ session_write_close();
2438
+
2439
+ // Send the file. Not finding a replacement in $wp_filesystem.
2440
+ readfile( $filepath );
2441
+
2442
+ // Exit.
2443
+ wp_die();
2444
+ }
2445
+
2446
+ /**
2447
+ * Menu callback to display the Backup functionality test page.
2448
+ *
2449
+ * @since 1.0
2450
+ *
2451
+ * @global string $wp_version The WordPress version string.
2452
+ * @global wpdb $wpdb The WordPress database class object.
2453
+ *
2454
+ * @return null
2455
+ */
2456
+ public function page_backup_test() {
2457
+ // Perform functionality tests.
2458
+ $is_functional = $this->test->run_functionality_tests();
2459
+
2460
+ // Get the user home directory.
2461
+ $home_dir = $this->config->get_home_directory();
2462
+
2463
+ // Get the mode of the directory.
2464
+ $home_dir_mode = $this->config->get_home_mode();
2465
+
2466
+ // Check if home directory is writable.
2467
+ $home_dir_writable = $this->test->is_homedir_writable();
2468
+
2469
+ // Get the backup directory path.
2470
+ $backup_directory = $this->backup_dir->get();
2471
+
2472
+ $possible_backup_dirs = $this->backup_dir->get_possible_dirs();
2473
+
2474
+ // Get the WordPress version.
2475
+ global $wp_version;
2476
+
2477
+ // Connect to the WordPress database via $wpdb.
2478
+ global $wpdb;
2479
+
2480
+ // Get the database size.
2481
+ $db_size = $this->test->get_database_size();
2482
+
2483
+ // Get the database character set.
2484
+ $db_charset = $wpdb->charset;
2485
+
2486
+ // Get the database collation.
2487
+ $db_collate = $wpdb->collate;
2488
+
2489
+ $disk_space = $this->test->get_disk_space();
2490
+
2491
+ // Get our crons and ready them for display.
2492
+ $our_crons = $this->cron->get_our_crons();
2493
+ foreach ( $our_crons as &$cron ) {
2494
+ $cron = esc_html( $cron );
2495
+ }
2496
+ $our_wp_crons = $this->wp_cron->get_our_crons();
2497
+ foreach ( $our_wp_crons as &$cron ) {
2498
+ $cron = esc_html( $cron );
2499
+ }
2500
+
2501
+ // Enqueue CSS for the test page.
2502
+ wp_enqueue_style( 'boldgrid-backup-admin-test',
2503
+ plugin_dir_url( __FILE__ ) . 'css/boldgrid-backup-admin-test.css', array(),
2504
+ BOLDGRID_BACKUP_VERSION, 'all'
2505
+ );
2506
+
2507
+ // Load template view.
2508
+ include BOLDGRID_BACKUP_PATH . '/admin/partials/boldgrid-backup-admin-test.php';
2509
+
2510
+ return;
2511
+ }
2512
+
2513
+ /**
2514
+ * Set lang strings.
2515
+ *
2516
+ * @since 1.5.3
2517
+ */
2518
+ public function set_lang() {
2519
+ $this->lang = array(
2520
+ // Mine count, total number of backups.
2521
+ 'All' => __( 'All', 'boldgrid-backup' ),
2522
+ 'backup_created' => __( 'Backup created successfully!', 'boldgrid-backup' ),
2523
+ 'Checking_credentials' => __( 'Checking credentials', 'boldgrid-backup' ),
2524
+ 'checkmark' => '&#10003;',
2525
+ 'icon_success' => '<span class="dashicons dashicons-yes green"></span> ',
2526
+ 'icon_warning' => '<span class="dashicons dashicons-warning yellow"></span> ',
2527
+ 'heading_update_protection' => __( 'BoldGrid Backup - Update Protection', 'boldgrid-backup' ),
2528
+ // Mine count, number of backups on remote storage providers.
2529
+ 'Remote' => __( 'Remote', 'boldgrid-backup' ),
2530
+ 'spinner' => '<span class="spinner"></span>',
2531
+ 'spinner_inline' => '<span class="spinner inline"></span>',
2532
+ 'want_to' => __( 'Want to store your backups on Amazon S3, restore individual files with just a click, and have access to more tools? Get <strong>BoldGrid Backup Premium</strong>!', 'boldgrid-backup' ),
2533
+ // Mine count, number of backups on local web server.
2534
+ 'Web_Server' => __( 'Web Server', 'boldgrid-backup' ),
2535
+ );
2536
+
2537
+ $this->elements = array(
2538
+ 'update_protection_activated' => sprintf( '%1$s %2$s', $this->lang['icon_success'], __( 'Update protection activated!', 'boldgrid-backup' ) ),
2539
+ // Use on long loading pages. Javascript will remove this on page load.
2540
+ 'long_checking_creds' => sprintf( '<div class="bgbu-remove-load">%1$s %2$s</div>', $this->lang['Checking_credentials'], $this->lang['spinner_inline'] ),
2541
+ );
2542
+ }
2543
+
2544
+ /**
2545
+ * Set the PHP timeout limit to at least 15 minutes.
2546
+ *
2547
+ * Various places within this class use to set the timeout limit to 300 seconds. This timeout
2548
+ * limit has been increased to 900 seconds and moved into its own method.
2549
+ *
2550
+ * @since 1.3.5
2551
+ *
2552
+ * @param int $time_limit Limit in seconds.
2553
+ */
2554
+ public function set_time_limit( $time_limit = 900 ) {
2555
+ set_time_limit( ( ( $max_execution_time = ini_get( 'max_execution_time' ) ) > $time_limit ?
2556
+ $max_execution_time :
2557
+ $time_limit
2558
+ ) );
2559
+ }
2560
+
2561
+ /**
2562
+ * Handle ajax request to restore a file.
2563
+ *
2564
+ * @since 1.5.4
2565
+ */
2566
+ public function wp_ajax_restore() {
2567
+ $error = __( 'Unable to restore backup: ', 'boldgrid-backup' );
2568
+ $redirect_url = admin_url( 'admin.php?page=boldgrid-backup' );
2569
+
2570
+ // Validate user role.
2571
+ if ( ! current_user_can( 'update_plugins' ) ) {
2572
+ $this->notice->add_user_notice(
2573
+ '<p>' . $error . esc_html__( 'Security violation (not authorized).', 'boldgrid-backup' ) . '</p>',
2574
+ 'error'
2575
+ );
2576
+ wp_send_json_error();
2577
+ }
2578
+
2579
+ // Validate nonce.
2580
+ if ( ! check_ajax_referer( 'boldgrid_backup_restore_archive', 'archive_auth', false ) ) {
2581
+ $this->notice->add_user_notice(
2582
+ '<p>' . $error . esc_html__( 'Security violation (invalid nonce).', 'boldgrid-backup' ) . '</p>',
2583
+ 'error'
2584
+ );
2585
+ wp_send_json_error();
2586
+ }
2587
+
2588
+ $archive_info = $this->restore_archive_file();
2589
+
2590
+ /*
2591
+ * Generate success message and add as a user notice.
2592
+ *
2593
+ * Historically our success / fail message was taken from this file:
2594
+ * admin/partials/boldgrid-backup-admin-backup.php'
2595
+ *
2596
+ * However, we cannot guarantee:
2597
+ * # What is in that file right now because we could have restored a
2598
+ * backup from version 1.5 or 1.6.
2599
+ * # What the file is doing (either returning info or echoing it).
2600
+ */
2601
+ $is_success = ! empty( $archive_info ) && empty( $archive_info['error'] );
2602
+ if ( $is_success ) {
2603
+ $message = array(
2604
+ 'message' => esc_html__( 'The selected archive file has been successfully restored.', 'boldgrid-backup' ),
2605
+ 'class' => 'notice notice-success is-dismissible',
2606
+ 'header' => __( 'BoldGrid Backup - Restoration complete' ),
2607
+ );
2608
+ } else {
2609
+ $message = array(
2610
+ 'message' => ! empty( $archive_info['error'] ) ? $archive_info['error'] : __( 'Unknown error when attempting to restore archive.', 'bolcgrid-backup' ),
2611
+ 'class' => 'notice notice-error is-dismissible',
2612
+ 'header' => __( 'BoldGrid Backup - Restoration failed' ),
2613
+ );
2614
+ }
2615
+ $this->notice->add_user_notice( $message['message'], $message['class'], $message['header'] );
2616
+
2617
+ wp_send_json_success( array(
2618
+ 'redirect_url' => $redirect_url,
2619
+ ));
2620
+ }
2621
+
2622
+ /**
2623
+ * Creating a backup archive file now, before an auto-update occurs.
2624
+ *
2625
+ * This method is hooked into the pre_auto_update action, which fires
2626
+ * immediately prior to an auto-update.
2627
+ *
2628
+ * @since 1.0
2629
+ *
2630
+ * @link https://developer.wordpress.org/reference/hooks/pre_auto_update/
2631
+ * @see Boldgrid_Backup_Admin_Core::archive_files()
2632
+ *
2633
+ * @param string $type The type of update being checked: 'core', 'theme', 'plugin', or 'translation'.
2634
+ * @return null
2635
+ */
2636
+ public function boldgrid_backup_now_auto( $type ) {
2637
+ // Get backup settings.
2638
+ $settings = $this->settings->get_settings();
2639
+
2640
+ // Abort if auto-backup is not enabled.
2641
+ if ( empty( $settings['auto_backup'] ) ) {
2642
+ return;
2643
+ }
2644
+
2645
+ // Get the last backup time (unix seconds).
2646
+ $last_backup_time = get_option( 'boldgrid_backup_last_backup' );
2647
+
2648
+ // If the last backup was done in the last hour, then abort.
2649
+ if ( $last_backup_time && ( time() - $last_backup_time ) <= HOUR_IN_SECONDS ) {
2650
+ return;
2651
+ }
2652
+
2653
+ // Perform the backup operation.
2654
+ $archive_info = $this->archive_files( true );
2655
+
2656
+ return;
2657
+ }
2658
+
2659
+ /**
2660
+ * Enforce backup archive retention setting.
2661
+ *
2662
+ * @since 1.0
2663
+ *
2664
+ * @see Boldgrid_Backup_Admin_Settings::get_settings()
2665
+ *
2666
+ * @return null
2667
+ */
2668
+ public function enforce_retention() {
2669
+ // Get backup settings.
2670
+ $settings = $this->settings->get_settings();
2671
+
2672
+ // Get archive list.
2673
+ $archives = $this->get_archive_list();
2674
+
2675
+ // Get the archives file count.
2676
+ $archives_count = count( $archives );
2677
+
2678
+ // If the archive count is not beyond the set retention count, then return.
2679
+ if ( empty( $settings['retention_count'] ) || $archives_count <= $settings['retention_count'] ) {
2680
+ return;
2681
+ }
2682
+
2683
+ // Connect to the WordPress Filesystem API.
2684
+ global $wp_filesystem;
2685
+
2686
+ // Initialize $counter.
2687
+ $counter = $archives_count - 1;
2688
+
2689
+ // Delete old backups.
2690
+ while ( $archives_count > $settings['retention_count'] ) {
2691
+ // Get the file path to delete.
2692
+ $filepath = (
2693
+ ! empty( $archives[ $counter ]['filepath'] ) ?
2694
+ $archives[ $counter ]['filepath'] : null
2695
+ );
2696
+
2697
+ // Delete the specified archive file.
2698
+ $deleted = $this->archive->delete( $filepath );
2699
+ if ( ! $deleted ) {
2700
+ // Something went wrong.
2701
+ break;
2702
+ }
2703
+
2704
+ /**
2705
+ * Take action after a backup has been deleted due to retention.
2706
+ *
2707
+ * @since 1.5.3
2708
+ *
2709
+ * @param string $filepath
2710
+ */
2711
+ do_action( 'boldgrid_backup_retention_deleted', $filepath );
2712
+
2713
+ // Decrease the archive count.
2714
+ $archives_count --;
2715
+
2716
+ // Increment the counter.
2717
+ $counter --;
2718
+ }
2719
+
2720
+ return;
2721
+ }
2722
+ }
admin/class-boldgrid-backup-admin-cron.php ADDED
@@ -0,0 +1,994 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The admin-specific cron functionality of the plugin
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.2
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup admin cron class.
17
+ *
18
+ * @since 1.2
19
+ */
20
+ class Boldgrid_Backup_Admin_Cron {
21
+ /**
22
+ * The core class object.
23
+ *
24
+ * @since 1.2
25
+ * @access private
26
+ * @var Boldgrid_Backup_Admin_Core
27
+ */
28
+ private $core;
29
+
30
+ /**
31
+ * Path to run_jobs.php
32
+ *
33
+ * @since 1.5.2
34
+ * @var string
35
+ */
36
+ public $run_jobs = 'cron/run_jobs.php';
37
+
38
+ /**
39
+ * A cron secret used to validate unauthenticated crontab jobs.
40
+ *
41
+ * @since 1.6.1-rc.1
42
+ * @access private
43
+ * @var string
44
+ */
45
+ private $cron_secret = null;
46
+
47
+ /**
48
+ * Linux crontab entry version string.
49
+ *
50
+ * The version represents the plugin version string when the crontab entry format was changed.
51
+ *
52
+ * @var string
53
+ */
54
+ public $crontab_version = '1.6.1';
55
+
56
+ /**
57
+ * Constructor.
58
+ *
59
+ * @since 1.2
60
+ *
61
+ * @param Boldgrid_Backup_Admin_Core $core Core class object.
62
+ */
63
+ public function __construct( $core ) {
64
+ // Save the Boldgrid_Backup_Admin_Core object as a class property.
65
+ $this->core = $core;
66
+ }
67
+
68
+ /**
69
+ * Add cron entry for backups from stored settings.
70
+ *
71
+ * @since 1.2
72
+ *
73
+ * @see Boldgrid_Backup_Admin_Cron::delete_cron_entries().
74
+ * @see Boldgrid_Backup_Admin_Cron::update_cron().
75
+ * @see BoldGrid_Backup_Admin_Core::get_backup_identifier()
76
+ * @see BoldGrid_Backup_Admin_Cron::get_cron_secret()
77
+ *
78
+ * @param array $settings
79
+ * @return bool Success.
80
+ */
81
+ public function add_cron_entry( $settings = array() ) {
82
+ if ( empty( $settings ) ) {
83
+ $settings = $this->core->settings->get_settings();
84
+ }
85
+
86
+ // Delete existing backup cron jobs.
87
+ $cron_status = $this->delete_cron_entries();
88
+
89
+ // Initialize $days_scheduled_list.
90
+ $days_scheduled_list = '';
91
+
92
+ // Create an array of days index names.
93
+ $days = array(
94
+ 'dow_sunday' => 0,
95
+ 'dow_monday' => 1,
96
+ 'dow_tuesday' => 2,
97
+ 'dow_wednesday' => 3,
98
+ 'dow_thursday' => 4,
99
+ 'dow_friday' => 5,
100
+ 'dow_saturday' => 6,
101
+ );
102
+
103
+ // Add scheduled days to the list.
104
+ foreach ( $days as $index => $int ) {
105
+ if ( isset( $settings['schedule'][ $index ] ) &&
106
+ 1 === $settings['schedule'][ $index ] ) {
107
+ $days_scheduled_list .= $int . ',';
108
+ }
109
+ }
110
+
111
+ // If no days are scheduled, then abort.
112
+ if ( empty( $days_scheduled_list ) ) {
113
+ return true;
114
+ }
115
+
116
+ // Strip trailing comma.
117
+ $days_scheduled_list = rtrim( $days_scheduled_list, ',' );
118
+
119
+ // Convert our WordPress time to Server time.
120
+ $date = $this->core->time->get_settings_date( $settings );
121
+ if ( false === $date ) {
122
+ return false;
123
+ }
124
+ $server_timezone = $this->core->time->get_server_timezone();
125
+ if ( false === $server_timezone ) {
126
+ return false;
127
+ }
128
+ $date->setTimezone( $server_timezone );
129
+
130
+ // Build cron job line in crontab format.
131
+ $entry = $date->format( 'i G' ) . ' * * ';
132
+
133
+ $entry .= $days_scheduled_list . ' php -qf "' . dirname( dirname( __FILE__ ) ) .
134
+ '/boldgrid-backup-cron.php" mode=backup siteurl=' . get_site_url() . ' id=' .
135
+ $this->core->get_backup_identifier() . ' secret=' . $this->get_cron_secret();
136
+
137
+ // If not Windows, then also silence the cron job.
138
+ if ( ! $this->core->test->is_windows() ) {
139
+ $entry .= ' > /dev/null 2>&1';
140
+ }
141
+
142
+ // Update cron.
143
+ $status = $this->update_cron( $entry );
144
+
145
+ return $status;
146
+ }
147
+
148
+ /**
149
+ * Add all cron jobs.
150
+ *
151
+ * This method first clears all crons, then adds all necessary crons based
152
+ * upon our settings.
153
+ *
154
+ * This method is useful for when:
155
+ * # User saves settings on settings page and crons need to be updated.
156
+ * # User reactivates plugin and all crons need to be added again.
157
+ *
158
+ * @since 1.6.0
159
+ *
160
+ * @param array $settings
161
+ * @return bool
162
+ */
163
+ public function add_all_crons( $settings ) {
164
+ $success = false;
165
+
166
+ $scheduler = ! empty( $settings['scheduler'] ) ? $settings['scheduler'] : null;
167
+ $schedule = ! empty( $settings['schedule'] ) ? $settings['schedule'] : null;
168
+
169
+ if ( 'cron' === $scheduler && $this->core->scheduler->is_available( $scheduler ) && ! empty( $schedule ) ) {
170
+ $this->core->scheduler->clear_all_schedules();
171
+
172
+ $scheduled = $this->add_cron_entry( $settings );
173
+ $jobs_scheduled = $this->schedule_jobs();
174
+
175
+ $success = $scheduled && $jobs_scheduled;
176
+
177
+ if ( $success ) {
178
+ $settings['crontab_version'] = $this->crontab_version;
179
+ $settings['cron_secret'] = $this->get_cron_secret();
180
+ update_site_option( 'boldgrid_backup_settings', $settings );
181
+ }
182
+ }
183
+
184
+ return $success;
185
+ }
186
+
187
+ /**
188
+ * Add a cron job to restore (rollback) using the last backup.
189
+ *
190
+ * @since 1.2
191
+ *
192
+ * @see Boldgrid_Backup_Admin_Core::get_archive_list()
193
+ * @see Boldgrid_Backup_Admin_Core::execute_command()
194
+ * @see Boldgrid_Backup_Admin_Cron::delete_cron_entries().
195
+ * @see Boldgrid_Backup_Admin_Cron::update_cron().
196
+ * @see Boldgrid_Backup_Admin_Test::is_windows()
197
+ * @see BoldGrid_Backup_Admin_Core::get_backup_identifier()
198
+ * @see BoldGrid_Backup_Admin_Cron::get_cron_secret()
199
+ *
200
+ * @return null
201
+ */
202
+ public function add_restore_cron() {
203
+ $pending_rollback = get_site_option( 'boldgrid_backup_pending_rollback' );
204
+ $archives = $this->core->get_archive_list();
205
+
206
+ // Use the first key to get info on the most recent archive.
207
+ $archive_key = 0;
208
+
209
+ $archive = $archives[ $archive_key ];
210
+
211
+ $archive_filename = $archive['filename'];
212
+
213
+ // Remove existing restore cron jobs.
214
+ $this->delete_cron_entries( 'restore' );
215
+
216
+ // Get the unix time for 5 minutes from now.
217
+ $time_5_minutes_later = strtotime( $this->core->auto_rollback->testing_time );
218
+
219
+ // Get the system's localized current time (HH:MM:SS), 5 minutes in the future.
220
+ $system_time = $this->core->execute_command(
221
+ 'date "+%H|%M|%S|%a %d %b %Y %I:%M:00 %p %Z" -d "' . $this->core->auto_rollback->testing_time . '"'
222
+ );
223
+
224
+ // Split the time into hour, minute, and second.
225
+ if ( ! empty( $system_time ) ) {
226
+ list( $hour, $minute, $second, $system_time_iso ) = explode( '|', $system_time );
227
+ }
228
+
229
+ // Validate hour; use system hour, or the date code for hour ("G").
230
+ if ( ! isset( $hour ) ) {
231
+ $hour = 'G';
232
+ }
233
+
234
+ // Validate hour; use system hour, or the date code for minute ("i").
235
+ if ( ! isset( $minute ) ) {
236
+ $minute = 'i';
237
+ }
238
+
239
+ // Mark the deadline.
240
+ if ( ! empty( $system_time_iso ) ) {
241
+ $deadline = strtotime( $system_time_iso );
242
+ } else {
243
+ $deadline = $time_5_minutes_later;
244
+ }
245
+
246
+ // Build cron job line in crontab format.
247
+ $entry = date( $minute . ' ' . $hour, $deadline ) . ' * * ' . date( 'w' ) . ' php -qf "' .
248
+ dirname( dirname( __FILE__ ) ) . '/boldgrid-backup-cron.php" mode=restore siteurl=' .
249
+ get_site_url() . ' id=' . $this->core->get_backup_identifier() . ' secret=' .
250
+ $this->get_cron_secret() . ' archive_key=' . $archive_key .
251
+ ' archive_filename=' . $archive_filename;
252
+
253
+ // If not Windows, then also silence the cron job.
254
+ if ( ! $this->core->test->is_windows() ) {
255
+ $entry .= ' > /dev/null 2>&1';
256
+ }
257
+
258
+ // Update cron.
259
+ $status = $this->update_cron( $entry );
260
+
261
+ // If cron job was added, then update the boldgrid_backup_pending_rollback option with time.
262
+ if ( $status ) {
263
+ $pending_rollback['deadline'] = $deadline;
264
+
265
+ update_site_option( 'boldgrid_backup_pending_rollback', $pending_rollback );
266
+ }
267
+
268
+ return;
269
+ }
270
+
271
+ /**
272
+ * Read an entry from the system user crontab or wp-cron.
273
+ *
274
+ * @since 1.2
275
+ *
276
+ * @param string $mode The mode of the cron job; either "backup" or "restore".
277
+ * @return array An array containing the backup schedule.
278
+ */
279
+ public function read_cron_entry( $mode = 'backup' ) {
280
+ // Validate mode.
281
+ if ( 'backup' !== $mode && 'restore' !== $mode ) {
282
+ return array();
283
+ }
284
+
285
+ // Set a search pattern to match for our cron jobs.
286
+ $pattern = dirname( dirname( __FILE__ ) ) . '/boldgrid-backup-cron.php" mode=' . $mode;
287
+
288
+ // Get our cron jobs.
289
+ $crontab_exploded = $this->get_all();
290
+ if ( empty( $crontab_exploded ) ) {
291
+ return array();
292
+ }
293
+
294
+ // If there's no cron jobs matching our pattern, abort.
295
+ $crontab = implode( '', $crontab_exploded );
296
+ if ( false === strpos( $crontab, $pattern ) ) {
297
+ return array();
298
+ }
299
+
300
+ // Initialize $entry.
301
+ $entry = '';
302
+
303
+ foreach ( $crontab_exploded as $line ) {
304
+ if ( false !== strpos( $line, $pattern ) ) {
305
+ // Found a matching entry.
306
+ $entry = trim( $line );
307
+
308
+ break;
309
+ }
310
+ }
311
+
312
+ $schedule = $this->get_schedule( $entry );
313
+
314
+ return $schedule;
315
+ }
316
+
317
+ /**
318
+ * Schedule "run_jobs".
319
+ *
320
+ * This hook will run every 5 minutes and run one job at a time, such as
321
+ * upload to a remote storage provider.
322
+ *
323
+ * This method is usually ran after saving the BoldGrid Backup settings. If
324
+ * after save cron is our scheduler, then we need to make sure we have
325
+ * the "run_jobs" wp-cron scheduled.
326
+ *
327
+ * @since 1.5.2
328
+ *
329
+ * @see BoldGrid_Backup_Admin_Core::get_backup_identifier()
330
+ * @see BoldGrid_Backup_Admin_Cron::get_cron_secret()
331
+ */
332
+ public function schedule_jobs() {
333
+ $entry = sprintf(
334
+ '*/5 * * * * php -qf "%1$s/%2$s" siteurl=%3$s id=%4$s secret=%5$s > /dev/null 2>&1',
335
+ dirname( dirname( __FILE__ ) ),
336
+ $this->run_jobs,
337
+ get_site_url(),
338
+ $this->core->get_backup_identifier(),
339
+ $this->get_cron_secret()
340
+ );
341
+
342
+ return $this->update_cron( $entry );
343
+ }
344
+
345
+ /**
346
+ * Update or add an entry to the system user crontab or wp-cron.
347
+ *
348
+ * @since 1.2
349
+ *
350
+ * @global WP_Filesystem $wp_filesystem The WordPress Filesystem API global object.
351
+ *
352
+ * @param string $entry A cron entry.
353
+ * @return bool Success.
354
+ */
355
+ public function update_cron( $entry ) {
356
+
357
+ // If no entry was passed, then abort.
358
+ if ( empty( $entry ) ) {
359
+ return false;
360
+ }
361
+
362
+ if ( $this->entry_exists( $entry ) ) {
363
+ return true;
364
+ }
365
+
366
+ // Check if the backup directory is configured.
367
+ if ( ! $this->core->backup_dir->get() ) {
368
+ return false;
369
+ }
370
+
371
+ $crontab = $this->get_all( true );
372
+
373
+ $crontab .= "\n" . $entry . "\n";
374
+
375
+ $crontab_written = $this->write_crontab( $crontab );
376
+
377
+ return $crontab_written && $this->entry_exists( $entry );
378
+ }
379
+
380
+ /**
381
+ * Write the crontab.
382
+ *
383
+ * @since 1.6.0
384
+ *
385
+ * @param string $crontab A string of crons, similar to raw output of crontab -l.
386
+ * @return bool
387
+ */
388
+ public function write_crontab( $crontab ) {
389
+ // Strip extra line breaks.
390
+ $crontab = str_replace( "\n\n", "\n", $crontab );
391
+
392
+ // Trim the crontab.
393
+ $crontab = trim( $crontab );
394
+
395
+ // Add a line break at the end of the file.
396
+ $crontab .= "\n";
397
+
398
+ // Get the backup directory path.
399
+ $backup_directory = $this->core->backup_dir->get();
400
+
401
+ // Check if the backup directory is writable.
402
+ if ( ! $this->core->wp_filesystem->is_writable( $backup_directory ) ) {
403
+ return false;
404
+ }
405
+
406
+ // Save the temp crontab to file.
407
+ $temp_crontab_path = $backup_directory . '/crontab.' . microtime( true ) . '.tmp';
408
+
409
+ $this->core->wp_filesystem->put_contents( $temp_crontab_path, $crontab, 0600 );
410
+
411
+ // Check if the defaults file was written.
412
+ if ( ! $this->core->wp_filesystem->exists( $temp_crontab_path ) ) {
413
+ return false;
414
+ }
415
+
416
+ // Write crontab.
417
+ $command = 'crontab ' . $temp_crontab_path;
418
+
419
+ $crontab = $this->core->execute_command( $command, null, $success );
420
+
421
+ // Remove temp crontab file.
422
+ $deleted = $this->core->wp_filesystem->delete( $temp_crontab_path, false, 'f' );
423
+
424
+ return $success && $deleted;
425
+ }
426
+
427
+ /**
428
+ * Delete boldgrid-backup cron entries from the system user crontab.
429
+ *
430
+ * @since 1.2
431
+ *
432
+ * @global WP_Filesystem $wp_filesystem The WordPress Filesystem API global object.
433
+ *
434
+ * @param string $mode Please see in-method comments when the $pattern is
435
+ * configured.
436
+ * @return bool Success.
437
+ */
438
+ public function delete_cron_entries( $mode = '' ) {
439
+ // Check if crontab is available.
440
+ $is_crontab_available = $this->core->test->is_crontab_available();
441
+
442
+ // If crontab is not available, then abort.
443
+ if ( ! $is_crontab_available ) {
444
+ return false;
445
+ }
446
+
447
+ // Check if the backup directory is configured.
448
+ if ( ! $this->core->backup_dir->get() ) {
449
+ return false;
450
+ }
451
+
452
+ /*
453
+ * Configure our pattern.
454
+ *
455
+ * When this method was initially written, $mode was either
456
+ * empty (defaulting to "backup") or "restore", hence the first two
457
+ * conditionals below.
458
+ *
459
+ * As of @1.5.2, you can pass any other string to this method, such as
460
+ * "cron/run_jobs.php", so that the pattern will become
461
+ * /home/user/public_html/wp-content/plugins/boldgrid-backup/cron/run_jobs.php
462
+ *
463
+ * As of @1.6.0 you can pass true as the $mode so that nothing else is
464
+ * added to the pattern and ALL crons for this site will be removed.
465
+ */
466
+ $pattern = BOLDGRID_BACKUP_PATH . '/';
467
+ if ( '' === $mode ) {
468
+ $pattern .= 'boldgrid-backup-cron.php" mode=';
469
+ } elseif ( 'restore' === $mode ) {
470
+ $pattern .= 'boldgrid-backup-cron.php" mode=restore';
471
+ } elseif ( true !== $mode ) {
472
+ $pattern .= $mode;
473
+ }
474
+
475
+ // Use either crontab or wp-cron.
476
+ if ( $is_crontab_available ) {
477
+ // Use crontab.
478
+ // Read crontab.
479
+ $command = 'crontab -l';
480
+
481
+ $crontab = $this->core->execute_command( $command, null, $success );
482
+
483
+ // If the command to retrieve crontab failed, then abort.
484
+ if ( ! $success ) {
485
+ return false;
486
+ }
487
+
488
+ // If no entries exist, then return success.
489
+ if ( false === strpos( $crontab, $pattern ) ) {
490
+ return true;
491
+ }
492
+
493
+ // Remove lines matching the pattern.
494
+ $crontab_exploded = explode( "\n", $crontab );
495
+
496
+ $crontab = '';
497
+
498
+ foreach ( $crontab_exploded as $line ) {
499
+ if ( false === strpos( $line, $pattern ) ) {
500
+ $line = trim( $line );
501
+ $crontab .= $line . "\n";
502
+ }
503
+ }
504
+
505
+ // Get the backup directory path.
506
+ $backup_directory = $this->core->backup_dir->get();
507
+
508
+ // Connect to the WordPress Filesystem API.
509
+ global $wp_filesystem;
510
+
511
+ // Check if the backup directory is writable.
512
+ if ( ! $wp_filesystem->is_writable( $backup_directory ) ) {
513
+ return false;
514
+ }
515
+
516
+ // Save the temp crontab to file.
517
+ $temp_crontab_path = $backup_directory . '/crontab.' . microtime( true ) . '.tmp';
518
+
519
+ // Save a temporary file for crontab.
520
+ $wp_filesystem->put_contents( $temp_crontab_path, $crontab, 0600 );
521
+
522
+ // Check if the defaults file was written.
523
+ if ( ! $wp_filesystem->exists( $temp_crontab_path ) ) {
524
+ return false;
525
+ }
526
+
527
+ // Write crontab.
528
+ $command = 'crontab ' . $temp_crontab_path;
529
+
530
+ $crontab = $this->core->execute_command( $command, null, $success );
531
+
532
+ // Remove temp crontab file.
533
+ $wp_filesystem->delete( $temp_crontab_path, false, 'f' );
534
+ }
535
+
536
+ return true;
537
+ }
538
+
539
+ /**
540
+ * Delete one entry from the crontab.
541
+ *
542
+ * @since 1.6.0
543
+ *
544
+ * @param string $entry
545
+ * @return bool True if the entry does not exist or was deleted successfully.
546
+ */
547
+ public function entry_delete( $entry ) {
548
+ if ( ! $this->entry_exists( $entry ) ) {
549
+ return true;
550
+ }
551
+
552
+ $all_entries = $this->get_all();
553
+
554
+ if ( ( $key = array_search( $entry, $all_entries ) ) !== false ) {
555
+ unset( $all_entries[ $key ] );
556
+ }
557
+
558
+ $all_entries = implode( "\n", $all_entries );
559
+
560
+ return $this->write_crontab( $all_entries ) && ! $this->entry_exists( $entry );
561
+ }
562
+
563
+ /**
564
+ * Determine if an entry exists in the crontab.
565
+ *
566
+ * @since 1.6.0
567
+ *
568
+ * @param string $entry
569
+ * @return bool
570
+ */
571
+ public function entry_exists( $entry ) {
572
+ $all_entries = $this->get_all();
573
+
574
+ return false !== array_search( $entry, $all_entries );
575
+ }
576
+
577
+ /**
578
+ * Get all entries in cron.
579
+ *
580
+ * @since 1.5.2
581
+ *
582
+ * @param bool $raw Return a string of crons when true, an array when false.
583
+ * @return mixed
584
+ */
585
+ public function get_all( $raw = false ) {
586
+
587
+ /*
588
+ * Cron is not available on Windows.
589
+ *
590
+ * It would be clean to call is_crontab_available(), but that method
591
+ * uses this method, and would result in an infinite loop.
592
+ */
593
+ if ( $this->core->test->is_windows() ) {
594
+ return false;
595
+ }
596
+
597
+ $command = 'crontab -l';
598
+ $crontab = $this->core->execute_command( $command, null, $success );
599
+
600
+ if ( ! $success ) {
601
+ return false;
602
+ }
603
+
604
+ return $raw ? $crontab : explode( "\n", $crontab );
605
+ }
606
+
607
+ /**
608
+ * Get all of our cron jobs.
609
+ *
610
+ * Similar to self::get_all, except only returns crons belonging to this
611
+ * installation.
612
+ *
613
+ * @since 1.5.2
614
+ *
615
+ * @return array
616
+ */
617
+ public function get_our_crons() {
618
+ $our = array();
619
+ $all = $this->get_all();
620
+
621
+ if ( empty( $all ) ) {
622
+ return $our;
623
+ }
624
+
625
+ foreach ( $all as $cron ) {
626
+ if ( false !== strpos( $cron, BOLDGRID_BACKUP_PATH ) ) {
627
+ $our[] = $cron;
628
+ }
629
+ }
630
+
631
+ return $our;
632
+ }
633
+
634
+ /**
635
+ * Read a line from the cron and return the schedule.
636
+ *
637
+ * @since 1.5.2
638
+ *
639
+ * @param string $cron_line An entry from cron.
640
+ * @return array Please see the $schedule initialized early in this method.
641
+ */
642
+ public function get_schedule( $cron_line ) {
643
+ // Initialize $schedule.
644
+ $schedule = array(
645
+ 'dow_sunday' => 0,
646
+ 'dow_monday' => 0,
647
+ 'dow_tuesday' => 0,
648
+ 'dow_wednesday' => 0,
649
+ 'dow_thursday' => 0,
650
+ 'dow_friday' => 0,
651
+ 'dow_saturday' => 0,
652
+ 'tod_h' => null,
653
+ 'tod_m' => null,
654
+ 'tod_a' => null,
655
+ );
656
+
657
+ if ( empty( $cron_line ) ) {
658
+ return $schedule;
659
+ }
660
+
661
+ // Parse cron schedule.
662
+ preg_match_all( '/([0-9*]+)(,([0-9*])+)*? /', $cron_line, $matches );
663
+
664
+ // Minute.
665
+ if ( isset( $matches[1][0] ) && is_numeric( $matches[1][0] ) ) {
666
+ $schedule['tod_m'] = intval( $matches[1][0] );
667
+ } else {
668
+ return array();
669
+ }
670
+
671
+ // Hour.
672
+ if ( isset( $matches[1][1] ) && is_numeric( $matches[1][1] ) ) {
673
+ $schedule['tod_h'] = intval( $matches[1][1] );
674
+ } else {
675
+ return array();
676
+ }
677
+
678
+ // Convert from 24H to 12H time format.
679
+ $unix_time = strtotime( $schedule['tod_h'] . ':' . $schedule['tod_m'] );
680
+
681
+ $schedule['tod_h'] = intval( date( 'g', $unix_time ) );
682
+ $schedule['tod_a'] = date( 'A', $unix_time );
683
+
684
+ // Days of the week.
685
+ if ( isset( $matches[0][4] ) ) {
686
+ $days = explode( ',', $matches[0][4] );
687
+ foreach ( $days as $day ) {
688
+ switch ( $day ) {
689
+ case 0 :
690
+ $schedule['dow_sunday'] = 1;
691
+ break;
692
+ case 1 :
693
+ $schedule['dow_monday'] = 1;
694
+ break;
695
+ case 2 :
696
+ $schedule['dow_tuesday'] = 1;
697
+ break;
698
+ case 3 :
699
+ $schedule['dow_wednesday'] = 1;
700
+ break;
701
+ case 4 :
702
+ $schedule['dow_thursday'] = 1;
703
+ break;
704
+ case 5 :
705
+ $schedule['dow_friday'] = 1;
706
+ break;
707
+ case 6 :
708
+ $schedule['dow_saturday'] = 1;
709
+ break;
710
+ default :
711
+ break;
712
+ }
713
+ }
714
+ }
715
+
716
+ return $schedule;
717
+ }
718
+
719
+ /**
720
+ * Print cron report.
721
+ *
722
+ * @since 1.2
723
+ *
724
+ * @param array $archive_info An array of archive file information.
725
+ */
726
+ public function print_cron_report( $archive_info ) {
727
+ // Validate mode.
728
+ if ( empty( $archive_info['mode'] ) ) {
729
+ esc_html_e( 'Error: A mode was not specified.', 'boldgrid-backup' );
730
+ wp_die();
731
+ }
732
+
733
+ $valid_modes = array(
734
+ 'backup',
735
+ 'restore',
736
+ );
737
+
738
+ if ( ! in_array( $archive_info['mode'], $valid_modes, true ) ) {
739
+ printf(
740
+ esc_html__( 'Error: Invalid mode "%s".', 'boldgrid-backup' ),
741
+ $archive_info['mode']
742
+ );
743
+ wp_die();
744
+ }
745
+
746
+ // Create action name.
747
+ switch ( $archive_info['mode'] ) {
748
+ case 'backup' :
749
+ $action_name = 'creating';
750
+ break;
751
+
752
+ case 'restore' :
753
+ $action_name = 'restoring';
754
+ break;
755
+
756
+ default :
757
+ $action_name = 'handling';
758
+ break;
759
+ }
760
+
761
+ // Print report.
762
+ if ( ! empty( $archive_info['error'] ) ) {
763
+ // Error.
764
+ printf(
765
+ esc_html__( 'There was an error $s backup archive file.', 'boldgrid-backup' ),
766
+ $action_name
767
+ );
768
+
769
+ echo PHP_EOL;
770
+
771
+ printf(
772
+ esc_html__( 'Error: %s', 'boldgrid-backup' ),
773
+ $archive_info['error']
774
+ );
775
+
776
+ echo PHP_EOL;
777
+
778
+ if ( isset( $archive_info['error_message'] ) ) {
779
+ printf(
780
+ esc_html__( 'Error Message: %s', 'boldgrid-backup' ),
781
+ $archive_info['error_message']
782
+ );
783
+ }
784
+
785
+ if ( isset( $archive_info['error_code'] ) ) {
786
+ printf(
787
+ ' (%s)',
788
+ $archive_info['error_code']
789
+ );
790
+ }
791
+
792
+ echo PHP_EOL;
793
+ } elseif ( ! empty( $archive_info['filesize'] ) || ! empty( $archive_info['dryrun'] ) ) {
794
+ // Dry run.
795
+ if ( ! empty( $archive_info['filepath'] ) ) {
796
+ printf(
797
+ esc_html__( 'File Path: %s', 'boldgrid-backup' ),
798
+ $archive_info['filepath']
799
+ );
800
+
801
+ echo PHP_EOL;
802
+ }
803
+
804
+ if ( ! empty( $archive_info['filesize'] ) ) {
805
+ printf(
806
+ esc_html__( 'File Size: %s', 'boldgrid-backup' ),
807
+ Boldgrid_Backup_Admin_Utility::bytes_to_human( $archive_info['filesize'] )
808
+ );
809
+
810
+ echo PHP_EOL;
811
+ }
812
+
813
+ if ( ! empty( $archive_info['total_size'] ) ) {
814
+ printf(
815
+ esc_html__( 'Total size: %s', 'boldgrid-backup' ),
816
+ Boldgrid_Backup_Admin_Utility::bytes_to_human( $archive_info['total_size'] )
817
+ );
818
+
819
+ echo PHP_EOL;
820
+ }
821
+
822
+ if ( ! empty( $archive_info['compressor'] ) ) {
823
+ printf(
824
+ esc_html__( 'Compressor: %s', 'boldgrid-backup' ),
825
+ $archive_info['compressor']
826
+ );
827
+
828
+ echo PHP_EOL;
829
+ }
830
+
831
+ // Show how long the website was paused for.
832
+ if ( isset( $archive_info['db_duration'] ) ) {
833
+ printf( $this->core->configs['lang']['est_pause'], $archive_info['db_duration'] );
834
+ echo PHP_EOL;
835
+ }
836
+
837
+ if ( isset( $archive_info['duration'] ) ) {
838
+ printf(
839
+ esc_html__( 'Duration: %s seconds', 'boldgrid-backup' ),
840
+ $archive_info['duration']
841
+ );
842
+
843
+ echo PHP_EOL;
844
+ }
845
+ } else {
846
+ // Unknown error.
847
+ printf(
848
+ esc_html__(
849
+ 'There was an unknown error %s a backup archive file.',
850
+ 'boldgrid-backup'
851
+ ),
852
+ $action_name
853
+ );
854
+
855
+ echo PHP_EOL;
856
+ }
857
+ }
858
+
859
+ /**
860
+ * Get the cron secret used to validate unauthenticated crontab jobs.
861
+ *
862
+ * @since 1.6.1-rc.1
863
+ *
864
+ * @see BoldGrid_Backup_Admin_Settings::get_settings()
865
+ *
866
+ * @return string
867
+ */
868
+ public function get_cron_secret() {
869
+ if ( empty( $this->cron_secret ) ) {
870
+ $settings = $this->core->settings->get_settings( true );
871
+
872
+ if ( empty( $settings['cron_secret'] ) ) {
873
+ $settings['cron_secret'] = hash( 'sha256', openssl_random_pseudo_bytes( 21 ) );
874
+
875
+ update_site_option( 'boldgrid_backup_settings', $settings );
876
+ }
877
+
878
+ $this->cron_secret = $settings['cron_secret'];
879
+ }
880
+
881
+ return $this->cron_secret;
882
+ }
883
+
884
+ /**
885
+ * Validate an unauthenticated wp_ajax_nopriv_ call by backup id and cron secret.
886
+ *
887
+ * @since 1.6.1-rc.1
888
+ *
889
+ * @uses $_GET['id']
890
+ * @uses $_GET['secret']
891
+ *
892
+ * @see current_user_can()
893
+ * @see BoldGrid_Backup_Admin_Core::get_backup_identifier()
894
+ * @see BoldGrid_Backup_Admin_Cron::get_cron_secret()
895
+ *
896
+ * @return bool
897
+ */
898
+ public function is_valid_call() {
899
+ $backup_id_match = ! empty( $_GET['id'] ) &&
900
+ $this->core->get_backup_identifier() === $_GET['id'];
901
+
902
+ $cron_secret_match = ! empty( $_GET['secret'] ) &&
903
+ $this->get_cron_secret() === $_GET['secret'];
904
+
905
+ return current_user_can( 'update_plugins' ) || ( $backup_id_match && $cron_secret_match );
906
+ }
907
+
908
+ /**
909
+ * Upgrade crontab entries, if not already upgraded.
910
+ *
911
+ * @since 1.6.1-rc.1
912
+ *
913
+ * @see BoldGrid_Backup_Admin_Settings::get_settings()
914
+ * @see BoldGrid_Backup_Admin_Cron::add_all_crons()
915
+ *
916
+ * @return bool Returns TRUE only if an upgrade was performed.
917
+ */
918
+ public function upgrade_crontab_entries() {
919
+ $upgraded = false;
920
+ $settings = $this->core->settings->get_settings( true );
921
+
922
+ if ( empty( $settings['crontab_version'] ) ||
923
+ $this->crontab_version !== $settings['crontab_version'] ) {
924
+ // Delete and recreate the crontab entries.
925
+ $upgraded = $this->add_all_crons( $settings );
926
+
927
+ if ( $upgraded ) {
928
+ /**
929
+ * Action when the crontab entry upgrade is successfully completed.
930
+ *
931
+ * @since 1.6.1-rc.1
932
+ *
933
+ * @param string The new crontab entry version.
934
+ */
935
+ do_action(
936
+ 'boldgrid_backup_upgrade_crontab_entries_complete',
937
+ $this->crontab_version
938
+ );
939
+ }
940
+ }
941
+
942
+ return $upgraded;
943
+ }
944
+
945
+ /**
946
+ * Hook into "wp_ajax_nopriv_boldgrid_backup_run_backup" and generate backup.
947
+ *
948
+ * @since 1.6.1-rc.1
949
+ *
950
+ * @see Boldgrid_Backup_Admin_Cron::is_valid_call()
951
+ *
952
+ * @return array An array of archive file information.
953
+ */
954
+ public function backup() {
955
+ if ( ! $this->is_valid_call() ) {
956
+ wp_die(
957
+ __( 'Error: Invalid request.' ),
958
+ 'boldgrid-backup'
959
+ );
960
+ }
961
+
962
+ $archive_info = $this->core->archive_files( true );
963
+
964
+ return $archive_info;
965
+ }
966
+
967
+ /**
968
+ * Hook into "wp_ajax_nopriv_boldgrid_backup_run_restore" and restores from backup.
969
+ *
970
+ * @since 1.6.1-rc.1
971
+ *
972
+ * @see Boldgrid_Backup_Admin_Cron::is_valid_call()
973
+ *
974
+ * @return array An array of archive file information.
975
+ */
976
+ public function restore() {
977
+ if ( ! $this->is_valid_call() ) {
978
+ wp_die(
979
+ __( 'Error: Invalid request.' ),
980
+ 'boldgrid-backup'
981
+ );
982
+ }
983
+
984
+ $archive_info = array(
985
+ 'error' => __( 'Could not perform restoration from cron job.', 'boldgrid-backup' ),
986
+ );
987
+
988
+ if ( $this->core->restore_helper->prepare_restore() ) {
989
+ $archive_info = $this->core->restore_archive_file();
990
+ }
991
+
992
+ return $archive_info;
993
+ }
994
+ }
admin/class-boldgrid-backup-admin-crypt.php ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Crypt class.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.6.0
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup Admin Crypt Class.
17
+ *
18
+ * @since 1.6.0
19
+ */
20
+ class Boldgrid_Backup_Admin_Crypt {
21
+ /**
22
+ * Encrypt and decrypt.
23
+ *
24
+ * @author Nazmul Ahsan <n.mukto@gmail.com>
25
+ * @link http://nazmulahsan.me/simple-two-way-function-encrypt-decrypt-string/
26
+ *
27
+ * @param string $string String to be encrypted/decrypted.
28
+ * @param string $action e for encrypt, d for decrypt.
29
+ * @return string
30
+ */
31
+ public static function crypt( $string, $action = 'e' ) {
32
+ /*
33
+ * We are only encrypting strings and numbers. User beware, encrypt a
34
+ * number, it will be a string when decrypted.
35
+ */
36
+ if ( ! is_string( $string ) && ! is_numeric( $string ) ) {
37
+ return $string;
38
+ }
39
+
40
+ $output = false;
41
+ $encrypt_method = 'AES-256-CBC';
42
+ $key = hash( 'sha256', AUTH_KEY );
43
+ $iv = substr( hash( 'sha256', SECURE_AUTH_KEY ), 0, 16 );
44
+
45
+ if ( 'e' === $action ) {
46
+ $output = base64_encode( openssl_encrypt( $string, $encrypt_method, $key, 0, $iv ) );
47
+ } elseif ( 'd' === $action ) {
48
+ $output = openssl_decrypt( base64_decode( $string ), $encrypt_method, $key, 0, $iv );
49
+ }
50
+
51
+ return $output;
52
+ }
53
+
54
+ /**
55
+ * When getting the settings option, decrypt it.
56
+ *
57
+ * @since 1.6.0
58
+ *
59
+ * @param mixed $value Value of option.
60
+ * @param string $option Name of option.
61
+ * @return mixed
62
+ */
63
+ public static function option_settings( $value, $option ) {
64
+ // Decrypt all remote (ftp / S3) credentials.
65
+ if ( ! empty( $value['remote'] ) ) {
66
+ foreach ( $value['remote'] as $remote_type => &$remote_settings ) {
67
+ foreach ( $remote_settings as &$remote_setting ) {
68
+ $remote_setting = self::crypt( $remote_setting, 'd' );
69
+ }
70
+ }
71
+ }
72
+
73
+ return $value;
74
+ }
75
+
76
+ /**
77
+ * When updating the settings option, encrypt it.
78
+ *
79
+ * @since 1.6.0
80
+ *
81
+ * @param mixed $value Value of option.
82
+ * @param mixed $old_value Old value of option.
83
+ * @param string $option Option name.
84
+ * @return mixed
85
+ */
86
+ public static function pre_update_settings( $value, $old_value, $option ) {
87
+ // Encrypt all remote (ftp / S3) credentials.
88
+ if ( ! empty( $value['remote'] ) ) {
89
+ foreach ( $value['remote'] as $remote_type => &$remote_settings ) {
90
+ foreach ( $remote_settings as &$remote_setting ) {
91
+ $remote_setting = self::crypt( $remote_setting );
92
+ }
93
+ }
94
+ }
95
+
96
+ return $value;
97
+ }
98
+ }
admin/class-boldgrid-backup-admin-db-dump.php ADDED
@@ -0,0 +1,199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Database Dump.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.1
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ use Ifsnop\Mysqldump as IMysqldump;
16
+
17
+ /**
18
+ * BoldGrid Backup Admin Database Dump class.
19
+ *
20
+ * @since 1.5.1
21
+ */
22
+ class Boldgrid_Backup_Admin_Db_Dump {
23
+
24
+ /**
25
+ * The core class object.
26
+ *
27
+ * @since 1.5.3
28
+ * @access private
29
+ * @var Boldgrid_Backup_Admin_Core
30
+ */
31
+ private $core;
32
+
33
+ /**
34
+ * Constructor.
35
+ *
36
+ * @since 1.5.3
37
+ *
38
+ * @param Boldgrid_Backup_Admin_Core $core Core class object.
39
+ */
40
+ public function __construct( $core ) {
41
+ $this->core = $core;
42
+ }
43
+
44
+ /**
45
+ * Create a MySQL dump of the database.
46
+ *
47
+ * @since 1.5.1
48
+ *
49
+ * @param string $file The filepath to our file.
50
+ * @return bool True on success.
51
+ */
52
+ public function dump( $file ) {
53
+ $include_tables = $this->core->db_omit->get_filtered_tables();
54
+ if ( empty( $include_tables ) ) {
55
+ return array( 'error' => esc_html__( 'No tables selected to backup.', 'boldgrid-backup' ) );
56
+ }
57
+
58
+ /**
59
+ * Take action before a database is dumped.
60
+ *
61
+ * @since 1.6.0
62
+ */
63
+ do_action( 'boldgrid_backup_pre_dump' );
64
+
65
+ // Some hosts may configure the DB_HOST as localhost:3306. Strip out the port.
66
+ $db_host = explode( ':', DB_HOST );
67
+
68
+ try {
69
+ $dump = new IMysqldump\Mysqldump(
70
+ sprintf( 'mysql:host=%1$s;dbname=%2$s', $db_host[0], DB_NAME ),
71
+ DB_USER,
72
+ DB_PASSWORD,
73
+ array(
74
+ 'include-tables' => $include_tables,
75
+ 'add-drop-table' => true,
76
+ 'no-autocommit' => false,
77
+ )
78
+ );
79
+ $dump->start( $file );
80
+ } catch (\Exception $e) {
81
+ return array( 'error' => $e->getMessage() );
82
+ }
83
+
84
+ /**
85
+ * Take action after a database is dumped.
86
+ *
87
+ * @since 1.6.0
88
+ */
89
+ do_action( 'boldgrid_backup_post_dump' );
90
+
91
+ return true;
92
+ }
93
+
94
+ /**
95
+ * Get data on all tables and the number of records in the backup file.
96
+ *
97
+ * @since 1.5.3
98
+ *
99
+ * @param string $filepath Path to zip file.
100
+ * @param string $file Path to file in zip.
101
+ * @return array
102
+ */
103
+ public function get_insert_count( $filepath, $file ) {
104
+ $return = array();
105
+
106
+ $tables = $this->get_insert_tables( $filepath, $file );
107
+
108
+ $file_contents = $this->core->archive->get_file( $file );
109
+
110
+ foreach ( $tables as $table ) {
111
+ /*
112
+ * Grab the exact insert statement.
113
+ *
114
+ * We have to use preg_match_all because some tables may have
115
+ * multiple INSERT INTO commands.
116
+ */
117
+ $expression = sprintf( '/INSERT INTO \`%1$s\` VALUES(.*);/', $table );
118
+ preg_match_all( $expression, $file_contents[0]['content'], $matches );
119
+
120
+ if ( empty( $matches[1] ) ) {
121
+ $return[ $table ] = 0;
122
+ continue;
123
+ }
124
+
125
+ $count = 0;
126
+
127
+ foreach ( $matches[1] as $line ) {
128
+ /*
129
+ * Ultimately what we're trying to do below is get the number of
130
+ * records in the INSERT statement.
131
+ *
132
+ * We cannot simply count the number of items between ()'s, as there
133
+ * could be a text entry like this ('()'),(), that would trigger a
134
+ * false positive.
135
+ *
136
+ * What we're doing is converting:
137
+ * 'some text goes \' () here ' to
138
+ * 'text'
139
+ * ... so we can simply count the number of ('s to find the number
140
+ * of records.
141
+ */
142
+ $insert_command = str_replace( '\\\'', '', $line );
143
+ $exploded = explode( '\'', $insert_command );
144
+ foreach ( $exploded as $k => $v ) {
145
+ // Odd numbers are what was between quotes.
146
+ if ( 0 !== $k % 2 ) {
147
+ $exploded[ $k ] = '';
148
+ }
149
+ }
150
+ $insert_command = implode( '\'', $exploded );
151
+
152
+ $count += substr_count( $insert_command, '(' );
153
+ }
154
+
155
+ $return[ $table ] = $count;
156
+ }
157
+
158
+ return $return;
159
+ }
160
+
161
+ /**
162
+ * Get a list of all tables a .sql file has insert commands for.
163
+ *
164
+ * @since 1.5.3
165
+ *
166
+ * @param string $filepath Path to zip file.
167
+ * @param string $file Path to file in zip.
168
+ * @return array
169
+ */
170
+ public function get_insert_tables( $filepath, $file ) {
171
+ $this->core->archive->init( $filepath );
172
+ $file_contents = $this->core->archive->get_file( $file );
173
+
174
+ /*
175
+ * Get a list of all tables within the dump that we are inserting
176
+ * records into.
177
+ *
178
+ * We initially did this taking a preg_match_all approach, but it fails
179
+ * for really long tables / fails with really long strings.
180
+ * # preg_match_all('/INSERT INTO \`(.*)\` VALUES(.*)/', $file_contents[0]['content'], $matches );
181
+ * # $tables = ! empty( $matches[1] ) ? $matches[1] : array();
182
+ *
183
+ * @link https://stackoverflow.com/questions/3021316/preg-match-and-long-strings
184
+ *
185
+ * For now, we'll go with the handy explode technique.
186
+ */
187
+ $tables = array();
188
+ $exploded = explode( 'INSERT INTO `', $file_contents[0]['content'] );
189
+ unset( $exploded[0] );
190
+ foreach ( $exploded as $table ) {
191
+ $tables[] = strstr( $table, '`', true );
192
+ }
193
+
194
+ // Tables may have more than one INSERT INTO statement.
195
+ $tables = array_unique( $tables );
196
+
197
+ return $tables;
198
+ }
199
+ }
admin/class-boldgrid-backup-admin-db-get.php ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Db Get class.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.3
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup Admin Db Get Class.
17
+ *
18
+ * @since 1.5.3
19
+ */
20
+ class Boldgrid_Backup_Admin_Db_Get {
21
+
22
+ /**
23
+ * The core class object.
24
+ *
25
+ * @since 1.5.3
26
+ * @access private
27
+ * @var Boldgrid_Backup_Admin_Core
28
+ */
29
+ private $core;
30
+
31
+ /**
32
+ * Constructor.
33
+ *
34
+ * @since 1.5.3
35
+ *
36
+ * @param Boldgrid_Backup_Admin_Core $core Core class object.
37
+ */
38
+ public function __construct( $core ) {
39
+ $this->core = $core;
40
+ }
41
+
42
+ /**
43
+ * Get a list of all tables based on system prefix.
44
+ *
45
+ * @since 1.5.3
46
+ *
47
+ * @global $wpdb;
48
+ *
49
+ * @return array
50
+ */
51
+ public function prefixed() {
52
+ global $wpdb;
53
+ $prefix_tables = array();
54
+
55
+ $sql = sprintf( 'SHOW TABLES LIKE "%1$s%%"', $wpdb->prefix );
56
+ $results = $wpdb->get_results( $sql, ARRAY_N );
57
+
58
+ foreach ( $results as $k => $v ) {
59
+ $prefix_tables[] = $v[0];
60
+ }
61
+
62
+ return $prefix_tables;
63
+ }
64
+
65
+ /**
66
+ * Get a list of all prefixed tables and the number of rows in each.
67
+ *
68
+ * This is similar to self::prefixed, except this method returns the number
69
+ * of rows in each table.
70
+ *
71
+ * @since 1.5.3
72
+ *
73
+ * @return array
74
+ */
75
+ public function prefixed_count() {
76
+ global $wpdb;
77
+ $return = array();
78
+
79
+ $tables = $this->prefixed();
80
+
81
+ foreach ( $tables as $table ) {
82
+ $sql = sprintf( 'SELECT COUNT(*) FROM %1$s;', $table );
83
+ $num = $wpdb->get_var( $sql );
84
+ $return[ $table ] = $num;
85
+ }
86
+
87
+ return $return;
88
+ }
89
+ }
admin/class-boldgrid-backup-admin-db-import.php ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Database Import.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.1
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+ /**
15
+ * BoldGrid Backup Admin Database Import class.
16
+ *
17
+ * @since 1.5.1
18
+ */
19
+ class Boldgrid_Backup_Admin_Db_Import {
20
+
21
+ /**
22
+ * The core class object.
23
+ *
24
+ * @since 1.5.4
25
+ * @access private
26
+ * @var Boldgrid_Backup_Admin_Core
27
+ */
28
+ private $core;
29
+
30
+ /**
31
+ * Errors.
32
+ *
33
+ * @since 1.5.4
34
+ * @var array
35
+ */
36
+ public $errors = array();
37
+
38
+ /**
39
+ * Constructor.
40
+ *
41
+ * @since 1.5.4
42
+ *
43
+ * @param Boldgrid_Backup_Admin_Core $core
44
+ */
45
+ public function __construct( $core = false ) {
46
+
47
+ // We don't always require $core for this class.
48
+ if ( $core ) {
49
+ $this->core = $core;
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Import a mysqldump.
55
+ *
56
+ * @since 1.5.1
57
+ *
58
+ * @param string $file The filepath to our file.
59
+ * @return bool True on success.
60
+ */
61
+ public function import( $file ) {
62
+ $lines = file( $file );
63
+ if ( false === $lines ) {
64
+ return array( 'error' => sprintf( __( 'Unable to open mysqldump, %1$s.', 'boldgrid-backup' ), $file ) );
65
+ }
66
+
67
+ $success = $this->import_lines( $lines );
68
+
69
+ return $success;
70
+ }
71
+
72
+ /**
73
+ * Import a database dump from an archive.
74
+ *
75
+ * Pass in "file.zip" and "backup.sql" and we'll find "backup.sql" in the
76
+ * "file.zip" file and restore it.
77
+ *
78
+ * @since 1.5.4
79
+ *
80
+ * @param string $archive_filepath
81
+ * @param string $file
82
+ * @return bool
83
+ */
84
+ public function import_from_archive( $archive_filepath, $file ) {
85
+ $this->core->archive->init( $archive_filepath );
86
+ $file_from_archive = $this->core->archive->get_file( $file );
87
+
88
+ $sql = ! empty( $file_from_archive[0]['content'] ) ? $file_from_archive[0]['content'] : null;
89
+ if ( empty( $sql ) ) {
90
+ $this->errors[] = __( 'Unable to get contents of file.', 'boldgrid-backup' );
91
+ return false;
92
+ }
93
+
94
+ $success = $this->import_string( $sql );
95
+
96
+ return $success;
97
+ }
98
+
99
+ /**
100
+ * Import lines (queries).
101
+ *
102
+ * This method accepts an array of $lines, and loops through each $line and
103
+ * imports it.
104
+ *
105
+ * These lines usually come from either a .sql file, or a string is parsed
106
+ * into separate lines.
107
+ *
108
+ * The functionality in this method use to be in the main import method,
109
+ * however it was broken away to make more reusable.
110
+ *
111
+ * @since 1.5.4
112
+ *
113
+ * @param array $lines
114
+ * @return bool
115
+ */
116
+ public function import_lines( $lines ) {
117
+ if ( empty( $lines ) ) {
118
+ return false;
119
+ }
120
+
121
+ $db = new PDO( sprintf( 'mysql:host=%1$s;dbname=%2$s;', DB_HOST, DB_NAME ), DB_USER, DB_PASSWORD );
122
+
123
+ $templine = '';
124
+
125
+ foreach ( $lines as $line ) {
126
+ // Skip comments and empty lines.
127
+ if ( substr( $line, 0, 2 ) === '--' || empty( $line ) ) {
128
+ continue;
129
+ }
130
+
131
+ $templine .= $line;
132
+
133
+ // Check if this is the end of the query.
134
+ if ( substr( trim( $line ), -1, 1 ) === ';' ) {
135
+ $affected_rows = $db->exec( $templine );
136
+ if ( false === $affected_rows ) {
137
+ return false;
138
+ }
139
+
140
+ $templine = '';
141
+ }
142
+ }
143
+
144
+ return true;
145
+ }
146
+
147
+ /**
148
+ * Import a string into a database.
149
+ *
150
+ * Generally this method is used when we grab a .sql file from within a .zip
151
+ * file and import it. Instead of saving the .sql file then importing, it
152
+ * comes straight from the .zip file as a string to here.
153
+ *
154
+ * @since 1.5.4
155
+ *
156
+ * @param string $string
157
+ * @return bool
158
+ */
159
+ public function import_string( $string ) {
160
+ $lines = preg_split( "/\\r\\n|\\r|\\n/", $string );
161
+
162
+ $success = $this->import_lines( $lines );
163
+
164
+ return $success;
165
+ }
166
+ }
167
+
admin/class-boldgrid-backup-admin-db-omit.php ADDED
@@ -0,0 +1,312 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Database Omit class.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.3
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup Admin Db Omit Class.
17
+ *
18
+ * @since 1.5.3
19
+ */
20
+ class Boldgrid_Backup_Admin_Db_Omit {
21
+
22
+ /**
23
+ * The core class object.
24
+ *
25
+ * @since 1.5.3
26
+ * @access private
27
+ * @var Boldgrid_Backup_Admin_Core
28
+ */
29
+ private $core;
30
+
31
+ /**
32
+ * Default type.
33
+ *
34
+ * Usually 'full' or 'custom' backup.
35
+ *
36
+ * @since 1.5.4
37
+ * @access public
38
+ * @var string
39
+ */
40
+ public $default_type = 'full';
41
+
42
+ /**
43
+ * Valid types.
44
+ *
45
+ * Usually 'full' or 'custom' backup.
46
+ *
47
+ * @since 1.5.4
48
+ * @access public
49
+ * @var array
50
+ */
51
+ public $valid_types = array( 'full', 'custom' );
52
+
53
+ /**
54
+ * Constructor.
55
+ *
56
+ * @since 1.5.3
57
+ *
58
+ * @param Boldgrid_Backup_Admin_Core $core Core class object.
59
+ */
60
+ public function __construct( $core ) {
61
+ $this->core = $core;
62
+ }
63
+
64
+ /**
65
+ * Generate a section for email alerts including information about tables
66
+ * excluded.
67
+ *
68
+ * @since 1.5.4
69
+ *
70
+ * @param array $info
71
+ * @return string
72
+ */
73
+ public function email_part( $info ) {
74
+ $body = '';
75
+
76
+ // Include database tables excluded.
77
+ if ( isset( $info['table_exclude'] ) ) {
78
+ $body .= "\n" . __( 'DATABASE SETTINGS', 'boldgrid-backup' ) . "\n";
79
+
80
+ $tables_excluded = empty( $info['table_exclude'] ) ? __( 'None', 'boldgrid-backup' ) : implode( ',', $info['table_exclude'] );
81
+ $body .= sprintf( esc_html__( 'Tables Excluded: %1$s', 'boldgrid-backup' ), $tables_excluded ) . "\n";
82
+ }
83
+
84
+ return $body;
85
+ }
86
+
87
+ /**
88
+ * Enqueue scripts.
89
+ *
90
+ * @since 1.5.4
91
+ */
92
+ public function enqueue_scripts() {
93
+ $handle = 'boldgrid-backup-admin-table-include';
94
+ wp_register_script( $handle,
95
+ plugin_dir_url( __FILE__ ) . 'js/boldgrid-backup-admin-table-include.js',
96
+ array( 'jquery' ),
97
+ BOLDGRID_BACKUP_VERSION,
98
+ false
99
+ );
100
+ $translation = array();
101
+ wp_localize_script( $handle, 'BoldGridBackupAdminTableInclude', $translation );
102
+ wp_enqueue_script( $handle );
103
+
104
+ // Enqueue CSS for folder exclude functionality.
105
+ // wp_enqueue_style(
106
+ // $handle,
107
+ // plugin_dir_url( __FILE__ ) . 'css/boldgrid-backup-admin-folder-exclude.css', array(),
108
+ // BOLDGRID_BACKUP_VERSION
109
+ // );
110
+ }
111
+
112
+ /**
113
+ * Get our exluded tables list.
114
+ *
115
+ * This method determines what tables to exclude based upon several
116
+ * different scenarios. For example, if we are doing a backup for update
117
+ * protection, we will not exclude any tables. If we are doing a backup via
118
+ * a scheduled cron, the exlucded tables will be calculated based upon our
119
+ * excluded_tables_type (full or custom).
120
+ *
121
+ * @since 1.5.3
122
+ *
123
+ * @return array
124
+ */
125
+ public function get_excluded_tables() {
126
+
127
+ /*
128
+ * Determine if the user is doing "backup site now" and they selected to
129
+ * backup all tables (full backup).
130
+ */
131
+ $backup_now_full = $this->core->is_backup_now && 'full' === $this->get_post_type();
132
+
133
+ if ( $this->core->is_archiving_update_protection || $backup_now_full || $this->core->pre_auto_update ) {
134
+ $excluded_tables = array();
135
+ } elseif ( $this->core->is_backup_now && isset( $_POST['include_tables'] ) ) {
136
+ $excluded_tables = $this->get_from_post();
137
+ } else {
138
+ $settings = $this->core->settings->get_settings();
139
+
140
+ $type = $this->get_settings_type( $settings );
141
+
142
+ $excluded_tables = 'full' === $type ? array() : $this->get_settings_excluded( $settings );
143
+ }
144
+
145
+ $excluded_tables = is_array( $excluded_tables ) ? $excluded_tables : array();
146
+
147
+ return $excluded_tables;
148
+ }
149
+
150
+ /**
151
+ * Get the tables we need to backup.
152
+ *
153
+ * We first get all the prefixed tables, and then we remove the tables from
154
+ * that list that the user specifically set not to backup.
155
+ *
156
+ * @since 1.5.3
157
+ *
158
+ * @return array
159
+ */
160
+ public function get_filtered_tables() {
161
+ $prefixed_tables = $this->core->db_get->prefixed();
162
+
163
+ // If we're creating a backup for update protection, backup all tables.
164
+ if ( $this->core->is_archiving_update_protection || $this->core->pre_auto_update ) {
165
+ return $prefixed_tables;
166
+ }
167
+
168
+ $exclude_tables = $this->get_excluded_tables();
169
+
170
+ foreach ( $prefixed_tables as $key => $table ) {
171
+ if ( in_array( $table, $exclude_tables ) ) {
172
+ unset( $prefixed_tables[ $key ] );
173
+ }
174
+ }
175
+
176
+ return $prefixed_tables;
177
+ }
178
+
179
+ /**
180
+ * From post, get an array of tables to exclude.
181
+ *
182
+ * We are submitting via post "include_tables", however we use this data to
183
+ * then calculate "exclude_tables".
184
+ *
185
+ * @since 1.5.4
186
+ *
187
+ * @return array
188
+ */
189
+ public function get_from_post() {
190
+ $exclude_tables = array();
191
+
192
+ $include_tables = ! empty( $_POST['include_tables'] ) ?
193
+ array_map( 'sanitize_text_field', $_POST['include_tables'] ) : array();
194
+
195
+ $all_tables = $this->core->db_get->prefixed();
196
+
197
+ /*
198
+ * Loop through every table we have.
199
+ *
200
+ * If the table we want to
201
+ */
202
+ foreach ( $all_tables as $table ) {
203
+ if ( ! in_array( $table, $include_tables ) ) {
204
+ $exclude_tables[] = $table;
205
+ }
206
+ }
207
+
208
+ return $exclude_tables;
209
+ }
210
+
211
+ /**
212
+ * Get value of 'table_inclusion_type' from $_POST.
213
+ *
214
+ * @since 1.5.4
215
+ *
216
+ * @return string
217
+ */
218
+ public function get_post_type() {
219
+ $key = 'table_inclusion_type';
220
+
221
+ return ! empty( $_POST[ $key ] ) && in_array( $_POST[ $key ], $this->valid_types, true ) ?
222
+ sanitize_key( $_POST[ $key ] ) : null;
223
+ }
224
+
225
+ /**
226
+ * From settings, get our exclude_tables.
227
+ *
228
+ * If no exclude_tables are set, return an empty array.
229
+ *
230
+ * @since 1.5.4
231
+ *
232
+ * @param array $settings
233
+ * @return bool
234
+ */
235
+ public function get_settings_excluded( $settings = array() ) {
236
+ if ( empty( $settings ) ) {
237
+ $settings = $this->core->settings->get_settings();
238
+ }
239
+
240
+ // Get the actual value stored in the settings. Set to an empty array if non existing.
241
+ $key = 'exclude_tables';
242
+
243
+ return ! isset( $settings[ $key ] ) || ! is_array( $settings[ $key ] ) ?
244
+ array() : array_map( 'sanitize_text_field', $settings[ $key ] );
245
+ }
246
+
247
+ /**
248
+ * From settings, get our type.
249
+ *
250
+ * In addition to getting the 'type' from settings, we can also get the type
251
+ * from $_POST. Please see this->get_post_type.
252
+ *
253
+ * Return null if we do not have a type saved in the settings.
254
+ *
255
+ * @since 1.5.4
256
+ *
257
+ * @param array $settings
258
+ * @return bool
259
+ */
260
+ public function get_settings_type( $settings = array() ) {
261
+ if ( empty( $settings ) ) {
262
+ $settings = $this->core->settings->get_settings();
263
+ }
264
+
265
+ $key = 'exclude_tables_type';
266
+
267
+ return ! empty( $settings[ $key ] ) && in_array( $settings[ $key ], $this->valid_types, true ) ? $settings[ $key ] : null;
268
+ }
269
+
270
+ /**
271
+ * Format an array of tables.
272
+ *
273
+ * Creates checkboxes for each table.
274
+ *
275
+ * @since 1.5.3
276
+ *
277
+ * @return string
278
+ */
279
+ public function format_prefixed_tables() {
280
+ $exclude_tables = $this->get_excluded_tables();
281
+
282
+ $tables = $this->core->db_get->prefixed();
283
+ $return = '';
284
+
285
+ foreach ( $tables as $table ) {
286
+ $checked = in_array( $table, $exclude_tables ) ? '' : 'checked';
287
+ $return .= sprintf(
288
+ '<div title="%1$s"><input value="%1$s" name="include_tables[]" type="checkbox" %2$s /> %1$s</div>',
289
+ esc_html( $table ),
290
+ $checked
291
+ );
292
+ }
293
+
294
+ return $return;
295
+ }
296
+
297
+ /**
298
+ * Determine if we are omitting all tables from the backup.
299
+ *
300
+ * @since 1.5.3
301
+ *
302
+ * @return bool
303
+ */
304
+ public function is_omit_all() {
305
+ $exclude_tables = $this->get_excluded_tables();
306
+ $prefixed_tables = $this->core->db_get->prefixed();
307
+
308
+ $diff = array_diff( $prefixed_tables, $exclude_tables );
309
+
310
+ return empty( $diff );
311
+ }
312
+ }
admin/class-boldgrid-backup-admin-email.php ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Email.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.2
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * Email.
17
+ *
18
+ * @since 1.5.2
19
+ */
20
+ class Boldgrid_Backup_Admin_Email {
21
+
22
+ /**
23
+ * An array of ads.
24
+ *
25
+ * @since 1.5.4
26
+ * @access private
27
+ * @var array
28
+ */
29
+ private $ads = array();
30
+
31
+ /**
32
+ * The core class object.
33
+ *
34
+ * @since 1.5.2
35
+ * @access private
36
+ * @var Boldgrid_Backup_Admin_Core
37
+ */
38
+ private $core;
39
+
40
+ /**
41
+ * Constructor.
42
+ *
43
+ * @since 1.5.2
44
+ *
45
+ * @param Boldgrid_Backup_Admin_Core $core
46
+ */
47
+ public function __construct( $core ) {
48
+ $this->core = $core;
49
+ }
50
+
51
+ /**
52
+ * Create a generic email body.
53
+ *
54
+ * @since 1.5.2
55
+ *
56
+ * @param string $message
57
+ * @param bool $add_ad Allow ads to be added to the email. In some cases,
58
+ * like when we have bad news (something failed), we
59
+ * may not want to ask the user to upgrade (bad timing).
60
+ * @return string
61
+ */
62
+ public function fill_generic_template( $message, $add_ad = true ) {
63
+ $this->init_ads();
64
+
65
+ $email_body = __( 'Hello', 'boldgrid-backup' ) . ",\n\n";
66
+
67
+ $email_body .= trim( $message ) . "\n\n";
68
+
69
+ if ( $add_ad ) {
70
+ $email_body .= $this->ads['generic'];
71
+ }
72
+
73
+ $email_body .= __( 'Best regards', 'boldgrid-backup' ) . ",\n\n";
74
+ $email_body .= __( 'The BoldGrid Backup plugin', 'boldgrid-backup' ) . "\n\n";
75
+
76
+ return $email_body;
77
+ }
78
+
79
+ /**
80
+ * Init our ads.
81
+ *
82
+ * @since 1.5.4
83
+ */
84
+ public function init_ads() {
85
+ $this->ads = array(
86
+ 'generic' => $this->core->config->get_is_premium() ? '' : sprintf(
87
+ __( 'Want to store your backups on Amazon S3, restore individual files with just a click, and have access to more tools? Get BoldGrid Backup Premium! - %1$s', 'boldgrid-backup' ),
88
+ Boldgrid_Backup_Admin_Go_Pro::$url
89
+ ) . "\n\n",
90
+ );
91
+ }
92
+
93
+ /**
94
+ * Get the email to send after a backup has been generated.
95
+ *
96
+ * @since 1.5.2
97
+ *
98
+ * @param array $info
99
+ * @return array
100
+ */
101
+ public function post_archive_parts( $info ) {
102
+ $this->init_ads();
103
+
104
+ $parts = array();
105
+
106
+ $site_id = Boldgrid_Backup_Admin_Utility::create_site_id();
107
+
108
+ $parts['subject'] = sprintf( __( 'Backup completed for %s', 'boldgrid-backup' ), $site_id );
109
+
110
+ $parts['body']['main'] = esc_html__( 'Hello', 'boldgrid-backup' ) . ",\n\n";
111
+ if ( $info['dryrun'] ) {
112
+ $body['main'] .= esc_html__( 'THIS OPERATION WAS A DRY-RUN TEST', 'boldgrid-backup' ) . ".\n\n";
113
+ }
114
+ $parts['body']['main'] .= sprintf( esc_html__( 'A backup archive has been created for %s', 'boldgrid-backup' ), $site_id ) . ".\n\n";
115
+ $parts['body']['main'] .= esc_html__( 'Backup details', 'boldgrid-backup' ) . ":\n";
116
+ $parts['body']['main'] .= sprintf( $this->core->configs['lang']['est_pause'], $info['db_duration'] ) . "\n";
117
+ $parts['body']['main'] .= sprintf( esc_html__( 'Duration: %s seconds', 'boldgrid-backup' ), $info['duration'] ) . "\n";
118
+ $parts['body']['main'] .= sprintf( esc_html__( 'Total size: %s', 'boldgrid-backup' ), Boldgrid_Backup_Admin_Utility::bytes_to_human( $info['total_size'] ) ) . "\n";
119
+ $parts['body']['main'] .= sprintf( esc_html__( 'Archive file path: %s', 'boldgrid-backup' ), $info['filepath'] ) . "\n";
120
+ $parts['body']['main'] .= sprintf( esc_html__( 'Archive file size: %s', 'boldgrid-backup' ), Boldgrid_Backup_Admin_Utility::bytes_to_human( $info['filesize'] ) ) . "\n";
121
+ $parts['body']['main'] .= sprintf( esc_html__( 'Compressor used: %s', 'boldgrid-backup' ), $info['compressor'] ) . "\n";
122
+
123
+ if ( ! empty( $info['trigger'] ) ) {
124
+ $parts['body']['main'] .= sprintf( esc_html__( 'Backup triggered by: %1$s', 'boldgrid-backup' ), $info['trigger'] ) . "\n";
125
+ }
126
+
127
+ $parts['body']['main'] .= $this->core->folder_exclusion->email_part( $info );
128
+
129
+ $parts['body']['main'] .= $this->core->db_omit->email_part( $info );
130
+
131
+ $parts['body']['main'] .= "\n";
132
+
133
+ $parts['body']['signature'] = esc_html__( 'You can manage notifications in your WordPress admin panel, under BoldGrid Backup Settings', 'boldgrid-backup' ) . ".\n\n";
134
+ $parts['body']['signature'] .= sprintf( esc_html__( 'For help with restoring a BoldGrid Backup archive file, please visit: %s', 'boldgrid-backup' ), esc_url( $this->core->configs['urls']['restore'] ) ) . "\n\n";
135
+
136
+ $parts['body']['signature'] .= $this->ads['generic'];
137
+
138
+ $parts['body']['signature'] .= esc_html__( 'Best regards', 'boldgrid-backup' ) . ",\n\n";
139
+ $parts['body']['signature'] .= esc_html__( 'The BoldGrid Backup plugin', 'boldgrid-backup' ) . "\n\n";
140
+
141
+ return $parts;
142
+ }
143
+
144
+ /**
145
+ * Send a notification email to the admin email address.
146
+ *
147
+ * @since 1.5.2
148
+ * @access public
149
+ *
150
+ * @param string $subject The email subject.
151
+ * @param string $body The email body.
152
+ * @return bool Whether or not the notification email was sent.
153
+ */
154
+ public function send( $subject, $body ) {
155
+ // Abort if subject or body is empty.
156
+ if ( empty( $subject ) || empty( $body ) ) {
157
+ return false;
158
+ }
159
+
160
+ // Get settings, for the notification email address.
161
+ $settings = $this->core->settings->get_settings();
162
+
163
+ $admin_email = $settings['notification_email'];
164
+
165
+ // Get the site title.
166
+ $site_title = get_bloginfo( 'name' );
167
+
168
+ // Configure mail headers.
169
+ $headers = 'From: ' . $site_title . ' <' . $admin_email . '>' . "\r\n" . 'X-Mailer: PHP/' .
170
+ phpversion() . "\r\n";
171
+
172
+ // Send mail.
173
+ $status = wp_mail( $admin_email, $subject, $body, $headers );
174
+
175
+ // Return status.
176
+ return $status;
177
+ }
178
+
179
+ /**
180
+ * Determine if the user wants a certain notification.
181
+ *
182
+ * @since 1.5.2
183
+ *
184
+ * @param string $task Such as 'backup' or 'restore'.
185
+ */
186
+ public function user_wants_notification( $task ) {
187
+ $settings = $this->core->settings->get_settings();
188
+
189
+ return ! empty( $settings['notifications'][ $task ] );
190
+ }
191
+ }
admin/class-boldgrid-backup-admin-filelist.php ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Boldgrid Backup Admin Filelist.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.1
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup Admin Filelist Class.
17
+ *
18
+ * @since 1.5.1
19
+ */
20
+ class Boldgrid_Backup_Admin_Filelist {
21
+
22
+ /**
23
+ * The core class object.
24
+ *
25
+ * @since 1.5.4
26
+ * @access private
27
+ * @var Boldgrid_Backup_Admin_Core
28
+ */
29
+ private $core;
30
+
31
+ /**
32
+ * The filelist filter array.
33
+ *
34
+ * This existed in the core class as of 1.0, moved to this class as of 1.5.4
35
+ *
36
+ * This array primarily exists to help get the total size of your website.
37
+ * We loop through this list and calculate the disk space of each item.
38
+ *
39
+ * @since 1.5.4
40
+ * @access public
41
+ * @var array
42
+ */
43
+ public $filelist_filter = array(
44
+ '.htaccess',
45
+ '.htaccess.bgb',
46
+ 'index.php',
47
+ 'license.txt',
48
+ 'readme.html',
49
+ 'wp-activate.php',
50
+ 'wp-admin',
51
+ 'wp-blog-header.php',
52
+ 'wp-comments-post.php',
53
+ 'wp-config.php',
54
+ 'wp-content',
55
+ 'wp-cron.php',
56
+ 'wp-includes',
57
+ 'wp-links-opml.php',
58
+ 'wp-load.php',
59
+ 'wp-login.php',
60
+ 'wp-mail.php',
61
+ 'wp-settings.php',
62
+ 'wp-signup.php',
63
+ 'wp-trackback.php',
64
+ 'xmlrpc.php',
65
+ );
66
+
67
+ /**
68
+ * Constructor.
69
+ *
70
+ * @since 1.5.4
71
+ *
72
+ * @param Boldgrid_Backup_Admin_Core $core Core class object.
73
+ */
74
+ public function __construct( $core ) {
75
+ $this->core = $core;
76
+ }
77
+
78
+ /**
79
+ * Get the total size of WordPress core and the wp-content directory.
80
+ *
81
+ * @since 1.5.4
82
+ *
83
+ * @return int
84
+ */
85
+ public function get_size() {
86
+
87
+ /*
88
+ * Include wp-includes/ms-functions.php.
89
+ *
90
+ * This method uses WordPress' recurse_dirsize function, which is loaded on multisite
91
+ * installations. If the recurse_dirsize function does not exist, include the necessary
92
+ * file.
93
+ */
94
+ if ( ! function_exists( 'recurse_dirsize' ) ) {
95
+ require_once ABSPATH . 'wp-includes/ms-functions.php';
96
+ }
97
+
98
+ $size = 0;
99
+
100
+ foreach ( $this->filelist_filter as $file ) {
101
+ $file_path = ABSPATH . $file;
102
+
103
+ if ( is_dir( $file_path ) ) {
104
+ $this_size = recurse_dirsize( $file_path );
105
+ } else {
106
+ $this_size = $this->core->wp_filesystem->size( $file_path );
107
+ }
108
+
109
+ $size += $this_size;
110
+ }
111
+
112
+ return $size;
113
+ }
114
+
115
+ /**
116
+ * Get the total size of a $filelist.
117
+ *
118
+ * @since 1.5.1
119
+ *
120
+ * @param array $filelist {
121
+ * An array files and data about those files.
122
+ *
123
+ * @type string 0 The path of a file. /home/user/public_html/readme.html
124
+ * @type string 1 The filename. readme.html
125
+ * @type int 2 The size of the file. 7413
126
+ * }
127
+ * @return int
128
+ */
129
+ public function get_total_size( $filelist ) {
130
+ $total_size = 0;
131
+
132
+ foreach ( $filelist as $fileinfo ) {
133
+ $total_size += $fileinfo[2];
134
+ }
135
+
136
+ return $total_size;
137
+ }
138
+ }
admin/class-boldgrid-backup-admin-folder-exclusion.php ADDED
@@ -0,0 +1,492 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Folder Exclusion class.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.4
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup Admin Folder Exclusion Class.
17
+ *
18
+ * @since 1.5.4
19
+ */
20
+ class Boldgrid_Backup_Admin_Folder_Exclusion {
21
+
22
+ /**
23
+ * The default exclude value.
24
+ *
25
+ * @since 1.5.4
26
+ * @var string
27
+ */
28
+ public $default_exclude = '.git,node_modules';
29
+
30
+ /**
31
+ * The default include value.
32
+ *
33
+ * @since 1.5.4
34
+ * @var string
35
+ */
36
+ public $default_include = 'WPCORE,/wp-content';
37
+
38
+ /**
39
+ * By default, backup all files and folders (use default settings).
40
+ *
41
+ * @since 1.5.4
42
+ * @var string
43
+ */
44
+ public $default_type = 'full';
45
+
46
+ /**
47
+ * Our exclude value.
48
+ *
49
+ * @since 1.5.4
50
+ * @var string|null
51
+ */
52
+ public $exclude = null;
53
+
54
+ /**
55
+ * Our include value.
56
+ *
57
+ * @since 1.5.4
58
+ * @var string|null
59
+ */
60
+ public $include = null;
61
+
62
+ /**
63
+ * Whether or not we're in the ajax preview.
64
+ *
65
+ * @since 1.5.4
66
+ * @var bool
67
+ */
68
+ public $in_ajax_preview = false;
69
+
70
+ /**
71
+ * Determine the type of backup we are performing.
72
+ *
73
+ * Usually it will be 'full' or 'custom'.
74
+ *
75
+ * @since 1.5.4
76
+ * @var null|string
77
+ */
78
+ public $type = null;
79
+
80
+ /**
81
+ * Allowable types.
82
+ *
83
+ * @since 1.5.4
84
+ * @var array
85
+ */
86
+ public $types = array( 'include', 'exclude', 'type' );
87
+
88
+ /**
89
+ * Valid backup types.
90
+ *
91
+ * @since 1.5.4
92
+ * @var array
93
+ */
94
+ public $valid_types = array( 'full', 'custom' );
95
+
96
+ /**
97
+ * The core class object.
98
+ *
99
+ * @since 1.5.4
100
+ * @access private
101
+ * @var Boldgrid_Backup_Admin_Core
102
+ */
103
+ private $core;
104
+
105
+ /**
106
+ * Constructor.
107
+ *
108
+ * @since 1.5.4
109
+ *
110
+ * @param Boldgrid_Backup_Admin_Core $core Core class object.
111
+ */
112
+ public function __construct( $core ) {
113
+ $this->core = $core;
114
+
115
+ /**
116
+ * Allow the filtering of the default include and exclude settings.
117
+ *
118
+ * As of 1.6.0, the backup plugin is not yet "properly" designed to
119
+ * allow for these settings to be filtered. Yes, these filters below are
120
+ * being ran, however they're being ran in the constructor. The only
121
+ * plugins we can guarantee that will be able to hook into these filters
122
+ * are mu-plugins. One possible scenario this would be used is an mu-plugin
123
+ * hooking into these filters to ensure "mu-plugins" are excluded from
124
+ * backups.
125
+ *
126
+ * @since 1.6.0
127
+ *
128
+ * @param string $this->default_include Default include values.
129
+ */
130
+ $this->default_include = apply_filters( 'boldgrid_backup_default_folder_include', $this->default_include );
131
+ $this->default_exclude = apply_filters( 'boldgrid_backup_default_folder_exclude', $this->default_exclude );
132
+ }
133
+
134
+ /**
135
+ * Determine if we should allow a file in the backup.
136
+ *
137
+ * @since 1.5.4
138
+ *
139
+ * @param string $file
140
+ * @return bool
141
+ */
142
+ public function allow_file( $file ) {
143
+ // If this file is in our backup directory, do not allow it.
144
+ if ( $this->core->backup_dir->file_in_dir( ABSPATH . $file, true ) ) {
145
+ return false;
146
+ }
147
+
148
+ // Get comma-delimited lists from user input or settings. Sanitizing is done below.
149
+ $include = $this->in_ajax_preview ? $_POST['include'] : $this->from_settings( 'include' );
150
+ $exclude = $this->in_ajax_preview ? $_POST['exclude'] : $this->from_settings( 'exclude' );
151
+
152
+ // Convert comma-delimited strings to arrays, and sanitize (also trim whitespace).
153
+ $includes = array_map( 'sanitize_text_field', explode( ',', $include ) );
154
+ $excludes = array_map( 'sanitize_text_field', explode( ',', $exclude ) );
155
+
156
+ // Default values, include everything and exclude nothing.
157
+ $is_match_include = false;
158
+ $is_match_exclude = false;
159
+
160
+ foreach ( $includes as $include ) {
161
+ if ( $this->is_match( $include, $file ) ) {
162
+ $is_match_include = true;
163
+ }
164
+ }
165
+
166
+ // If we're not including this file, we don't need to check excludes.
167
+ if ( ! $is_match_include ) {
168
+ return false;
169
+ }
170
+
171
+ // If the user left "excludes" blank, then we're not excluding anything.
172
+ if ( empty( $exclude ) ) {
173
+ return true;
174
+ }
175
+
176
+ foreach ( $excludes as $exclude ) {
177
+ if ( $this->is_match( $exclude, $file ) ) {
178
+ $is_match_exclude = true;
179
+ }
180
+ }
181
+
182
+ return ! $is_match_exclude;
183
+ }
184
+
185
+ /**
186
+ * Generate a section for email alerts including information about files and
187
+ * folders excluded.
188
+ *
189
+ * @since 1.5.4
190
+ *
191
+ * @param array $info
192
+ * @return string
193
+ */
194
+ public function email_part( $info ) {
195
+ $body = '';
196
+
197
+ $has_folder_included = isset( $info['folder_include'] );
198
+ $has_folder_excluded = isset( $info['folder_exclude'] );
199
+
200
+ if ( $has_folder_included || $has_folder_excluded ) {
201
+ $body .= "\n" . __( 'FILE AND FOLDER SETTINGS', 'boldgrid-backup' ) . "\n";
202
+ }
203
+
204
+ if ( $has_folder_included ) {
205
+ $body .= sprintf( esc_html__( 'Included: %1$s', 'boldgrid-backup' ), $info['folder_include'] ) . "\n";
206
+ }
207
+
208
+ if ( $has_folder_excluded ) {
209
+ $body .= sprintf( esc_html__( 'Excluded: %1$s', 'boldgrid-backup' ), $info['folder_exclude'] ) . "\n";
210
+ }
211
+
212
+ return $body;
213
+ }
214
+
215
+ /**
216
+ * Create our regex pattern.
217
+ *
218
+ * If the user enters wp-* for their include / exclude value, then we need
219
+ * to convert that into a propper regex pattern.
220
+ *
221
+ * When we look for matches, we want to keep it specific to one folder.
222
+ * For example, if we're given wp-adm*n, the expectation is that we want
223
+ * to find everything within the wp-admin folder.
224
+ *
225
+ * To prevent this false positive:
226
+ * wp-admin/images/media-button.png
227
+ * wp-adm************************ng
228
+ * ... we will set the wildcard to match everything except a directory
229
+ * separator.
230
+ *
231
+ * @since 1.5.4
232
+ *
233
+ * @param string $value
234
+ * @return string
235
+ */
236
+ public function create_pattern( $value ) {
237
+ $first_char = substr( $value, 0, 1 );
238
+
239
+ // Clean up our value. Forward slashes will be hanlded uniquely.
240
+ $value = trim( $value, ' /' );
241
+
242
+ // Escape everything except the wildcard.
243
+ $value = preg_quote( $value );
244
+ $value = str_replace( '\*', '*', $value );
245
+
246
+ /*
247
+ * If the first character is a /, then assume we're requiring the $file
248
+ * to be directly in the ABSPATH, and so the $file should beging with
249
+ * ^$value.
250
+ *
251
+ * Otherwise assume our $file should begin with ^value or procede a
252
+ * forward slash /$value.
253
+ */
254
+ $pattern = '/' === $first_char ? '#^' : '#(^|/)';
255
+
256
+ /*
257
+ * To keep wildcards within a path part, ensure the wildcard matches
258
+ * anything except a /. This ensure that wp-adm* matches wp-adm*n and
259
+ * not wp-admin/file.png.
260
+ */
261
+ $pattern .= str_replace( '*', '[^/]*', $value );
262
+
263
+ /*
264
+ * Our $file should either end with our $$value, or our $value should be
265
+ * followed by a forward slash $value/.
266
+ */
267
+ $pattern .= '($|/)#';
268
+
269
+ return $pattern;
270
+ }
271
+
272
+ /**
273
+ * Enqueue scripts.
274
+ *
275
+ * @since 1.5.4
276
+ */
277
+ public function enqueue_scripts() {
278
+ $handle = 'boldgrid-backup-admin-folder-exclude';
279
+ wp_register_script( $handle,
280
+ plugin_dir_url( __FILE__ ) . 'js/boldgrid-backup-admin-folder-exclude.js',
281
+ array( 'jquery' ),
282
+ BOLDGRID_BACKUP_VERSION,
283
+ false
284
+ );
285
+ $translation = array(
286
+ 'default_include' => $this->default_include,
287
+ 'default_exclude' => $this->default_exclude,
288
+ 'items' => __( 'items', 'boldgrid-backup' ),
289
+ 'no_results' => __( 'No results', 'boldgrid-backup' ),
290
+ 'of' => __( 'of', 'boldgrid-backup' ),
291
+ );
292
+ wp_localize_script( $handle, 'BoldGridBackupAdminFolderExclude', $translation );
293
+ wp_enqueue_script( $handle );
294
+
295
+ // Enqueue CSS for folder exclude functionality.
296
+ wp_enqueue_style(
297
+ $handle,
298
+ plugin_dir_url( __FILE__ ) . 'css/boldgrid-backup-admin-folder-exclude.css', array(),
299
+ BOLDGRID_BACKUP_VERSION
300
+ );
301
+
302
+ wp_enqueue_script( 'jquery-effects-core' );
303
+ wp_enqueue_script( 'jquery-effects-bounce' );
304
+ }
305
+
306
+ /**
307
+ * Get our include or exclude value from the settings.
308
+ *
309
+ * @since 1.5.4
310
+ *
311
+ * @param string $type Either 'include' or 'exclude'.
312
+ * @param array $settings
313
+ * @return string
314
+ */
315
+ public function from_settings( $type, $settings = false ) {
316
+ if ( ! in_array( $type, $this->types, true ) ) {
317
+ return false;
318
+ }
319
+
320
+ $key = 'folder_exclusion_' . $type;
321
+ $default = 'default_' . $type;
322
+
323
+ /*
324
+ * Determine if we need to do a full backup.
325
+ *
326
+ * Scenarios include:
327
+ * # We are in the middle of creating a backup file for update protection.
328
+ * # We are creating a 'full' backup.
329
+ * # We are creating a backup immediately before a WordPress auto update.
330
+ */
331
+ if ( $this->core->is_archiving_update_protection || $this->core->is_backup_full || $this->core->pre_auto_update ) {
332
+ return $this->$default;
333
+ }
334
+
335
+ /*
336
+ * If we are backing up a site now (not for update protection) and
337
+ * we've posted folder settings, use those.
338
+ */
339
+ if ( $this->core->is_backup_now && isset( $_POST[ $key ] ) ) {
340
+ $this->$type = $this->from_post( $type );
341
+ return $this->$type;
342
+ }
343
+
344
+ if ( ! is_null( $this->$type ) ) {
345
+ return $this->$type;
346
+ }
347
+
348
+ if ( $this->core->settings->is_saving_settings ) {
349
+ $this->$type = $this->from_post( $type );
350
+ /*
351
+ * Is there value for this in the settings?
352
+ *
353
+ * Initially, we checked to make sure $settings[$key] wasn't empty and
354
+ * it was a string. Now, we'll simply see if it is set. This will allow
355
+ * for the user to enter nothing in the exclude field.
356
+ */
357
+ } elseif ( isset( $settings[ $key ] ) ) {
358
+ $this->$type = $settings[ $key ];
359
+ } elseif ( ! $settings ) {
360
+ $settings = $this->core->settings->get_settings();
361
+ if ( ! empty( $settings[ $key ] ) && is_string( $settings[ $key ] ) ) {
362
+ $this->$type = $settings[ $key ];
363
+ }
364
+ }
365
+
366
+ if ( is_null( $this->$type ) ) {
367
+ $this->$type = $this->$default;
368
+ }
369
+
370
+ return $this->$type;
371
+ }
372
+
373
+ /**
374
+ * Determine if a include/exclude value matches a file.
375
+ *
376
+ * For example, if I pass in "wp-content" as a $value and
377
+ * wp-content/file.php as a $file, it should match. If I pass in "joec" as a
378
+ * $value and wp-content/file.php as a $file, it should not match.
379
+ *
380
+ * @param string $value
381
+ * @param string $file Filepath relative to ABSPATH, such as
382
+ * wp-content/plugins/boldgrid-backup/boldgrid-backup.php
383
+ * @return bool
384
+ */
385
+ public function is_match( $value, $file ) {
386
+ if ( '*' === $value ) {
387
+ return true;
388
+ }
389
+
390
+ // Handle filtering of core WordPress files.
391
+ if ( 'WPCORE' === $value ) {
392
+ return $this->core->core_files->is_core_file( $file );
393
+ }
394
+
395
+ /*
396
+ * Convert a Windows filepath to Linux. In this method we're going to
397
+ * assume that / is the directory separator.
398
+ */
399
+ $file = str_replace( '\\', '/', $file );
400
+
401
+ $pattern = $this->create_pattern( $value );
402
+
403
+ preg_match( $pattern, $file, $matches );
404
+
405
+ return ! empty( $matches );
406
+ }
407
+
408
+ /**
409
+ *
410
+ */
411
+ public function is_using_defaults() {
412
+ $type = $this->from_settings( 'type' );
413
+
414
+ return 'full' === $type;
415
+ }
416
+
417
+ /**
418
+ * Get our include / exclude settings from $_POST.
419
+ *
420
+ * @since 1.5.4
421
+ *
422
+ * @param string $type Either include or exclude.
423
+ * @return string
424
+ */
425
+ public function from_post( $type ) {
426
+ if ( ! in_array( $type, $this->types, true ) ) {
427
+ return false;
428
+ }
429
+
430
+ $key = 'folder_exclusion_' . $type;
431
+
432
+ switch ( $type ) {
433
+ case 'include':
434
+ /*
435
+ * If you submit an empty "include" setting, it will be
436
+ * interpreted as include all, *.
437
+ */
438
+ $value = ! empty( $_POST[ $key ] ) ? $_POST[ $key ] : $this->default_include;
439
+ break;
440
+ case 'exclude':
441
+ /*
442
+ * You are allowed to submit a blank "exclude" setting. It means
443
+ * you do not want to exclude anything.
444
+ */
445
+ $value = empty( $_POST[ $key ] ) ? '' : $_POST[ $key ];
446
+ break;
447
+ case 'type':
448
+ $value = ! empty( $_POST[ $key ] ) &&
449
+ in_array( $_POST[ $key ], $this->valid_types, true ) ?
450
+ $_POST[ $key ] : $this->default_type;
451
+ break;
452
+ }
453
+
454
+ $value = sanitize_text_field( $value );
455
+
456
+ return $value;
457
+ }
458
+
459
+ /**
460
+ * Handle the ajax request to preview the filters.
461
+ *
462
+ * @since 1.5.4
463
+ */
464
+ public function wp_ajax_preview() {
465
+ if ( ! check_ajax_referer( 'folder_exclusion_preview', 'security', false ) ) {
466
+ wp_send_json_error( __( 'Invalid nonce.', 'boldgrid-backup' ) );
467
+ }
468
+
469
+ $include = isset( $_POST['include'] ) ? sanitize_text_field( $_POST['include'] ) : null;
470
+ $exclude = isset( $_POST['exclude'] ) ? sanitize_text_field( $_POST['exclude'] ) : null;
471
+
472
+ if ( is_null( $include ) || is_null( $exclude ) ) {
473
+ wp_send_json_error( __( 'Invalid include / exclude values.', 'boldgrid-backup' ) );
474
+ }
475
+
476
+ $this->in_ajax_preview = true;
477
+
478
+ $filelist = $this->core->get_filtered_filelist();
479
+
480
+ if ( empty( $filelist ) ) {
481
+ wp_send_json_error( __( 'No files match your criteria.', 'boldgrid-backup' ) );
482
+ }
483
+
484
+ $markup = array();
485
+
486
+ foreach ( $filelist as $file ) {
487
+ $markup[] = $file[1];
488
+ }
489
+
490
+ wp_send_json_success( $markup );
491
+ }
492
+ }
admin/class-boldgrid-backup-admin-go-pro.php ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Go Pro class.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.4
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup Admin Go Pro Class.
17
+ *
18
+ * @since 1.5.4
19
+ */
20
+ class Boldgrid_Backup_Admin_Go_Pro {
21
+
22
+ /**
23
+ * The core class object.
24
+ *
25
+ * @since 1.5.4
26
+ * @access private
27
+ * @var Boldgrid_Backup_Admin_Core
28
+ */
29
+ private $core;
30
+
31
+ /**
32
+ * Generic upgrade link.
33
+ *
34
+ * @since 1.5.4
35
+ * @access public
36
+ * @var string
37
+ */
38
+ public static $url = 'https://boldgrid.com/update-backup';
39
+
40
+ /**
41
+ * Constructor.
42
+ *
43
+ * @since 1.5.4
44
+ *
45
+ * @param Boldgrid_Backup_Admin_Core $core Core class object.
46
+ */
47
+ public function __construct( $core ) {
48
+ $this->core = $core;
49
+ }
50
+
51
+ /**
52
+ * Display "setup" admin notices.
53
+ *
54
+ * This method is currently used to display admin notices to help guide the
55
+ * user to getting a premium key and getting / activating the premium extension.
56
+ *
57
+ * @since 1.5.4
58
+ */
59
+ public function admin_notice_setup() {
60
+
61
+ // If the premium plugin is installed and all is good, abort!
62
+ if ( $this->core->config->is_premium_done ) {
63
+ return;
64
+ }
65
+
66
+ // Check user role.
67
+ if ( ! current_user_can( 'update_plugins' ) ) {
68
+ return;
69
+ }
70
+
71
+ if ( ! class_exists( '\Boldgrid\Library\Library\Notice' ) ) {
72
+ return;
73
+ }
74
+
75
+ $is_premium = $this->core->config->get_is_premium();
76
+
77
+ $notices = array(
78
+ array(
79
+ 'id' => 'boldgrid_backup_activate_premium',
80
+ 'show' => $is_premium && $this->core->config->is_premium_installed,
81
+ 'message' => '<p>' . sprintf(
82
+ __( 'You have a <strong>Premium BoldGrid Connect Key</strong> and you have the <strong>BoldGrid Backup Premium Extension installed</strong>. Please go to your <a href="%1$s">plugins page</a> and activate your premium extension!', 'boldgrid-backup' ),
83
+ admin_url( 'plugins.php' )
84
+ ) . '</p>',
85
+ ),
86
+ array(
87
+ 'id' => 'boldgrid_backup_upgrade_premium',
88
+ 'show' => ! $is_premium && $this->core->config->is_premium_active,
89
+ 'message' => '<p>' . sprintf(
90
+ __( 'Thank you for activating the <strong>BoldGrid Backup Premium Extension</strong>! Before you can begin using all of the premium features, please visit <a href="%1$s" target="_blank">BoldGrid Central</a> and upgrade your BoldGrid Connect Key.', 'boldgrid-backup' ),
91
+ self::$url
92
+ ) . '</p>',
93
+ ),
94
+ array(
95
+ 'id' => 'boldgrid_backup_download_premium',
96
+ 'show' => $is_premium && ! $this->core->config->is_premium_installed,
97
+ 'message' => '<p>' . sprintf(
98
+ __( 'Hello there! We see that you have a <strong>Premium BoldGrid Connect Key</strong> and you have the <strong>BoldGrid Backup Plugin</strong> activated! Be sure to download the <strong>BoldGrid Backup Premium Extension</strong> from <a href="%1$s">BoldGrid Central</a> to gain access to more features!', 'boldgrid-backup' ),
99
+ 'https://www.boldgrid.com/central'
100
+ ) . '</p>',
101
+ ),
102
+ );
103
+
104
+ foreach ( $notices as $notice ) {
105
+ if ( $notice['show'] ) {
106
+ \Boldgrid\Library\Library\Notice::show( $notice['message'], $notice['id'] );
107
+ break;
108
+ }
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Get a "Get Premium" button.
114
+ *
115
+ * @since 1.5.4
116
+ *
117
+ * @param string $url
118
+ * @param string $text
119
+ * @return string
120
+ */
121
+ public function get_premium_button( $url = 'https://boldgrid.com/update-backup', $text = 'Get Premium' ) {
122
+ return sprintf( '
123
+ <a href="%1$s" class="button button-success" target="_blank">%2$s</a>',
124
+ esc_url( $url ),
125
+ $text
126
+ );
127
+ }
128
+ }
admin/class-boldgrid-backup-admin-home-dir.php ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Boldgrid Backup Admin Home Dir.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.1
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * Boldgrid Backup Admin Home Dir class.
17
+ *
18
+ * @since 1.5.1
19
+ */
20
+ class Boldgrid_Backup_Admin_Home_Dir {
21
+
22
+ /**
23
+ * The core class object.
24
+ *
25
+ * @since 1.5.1
26
+ * @access private
27
+ * @var Boldgrid_Backup_Admin_Core
28
+ */
29
+ private $core;
30
+
31
+ /**
32
+ * Constructor.
33
+ *
34
+ * @since 1.5.1
35
+ *
36
+ * @param Boldgrid_Backup_Admin_Core $core
37
+ */
38
+ public function __construct( $core ) {
39
+ $this->core = $core;
40
+ }
41
+
42
+ /**
43
+ * Get home dir used to calculate disk space.
44
+ *
45
+ * We'll assume that the user's home dir is the appropriate directory to use
46
+ * when calculating available disk space. If this dir has permissions that
47
+ * make it difficult to calculate disk space, such as
48
+ * C:\WINDOWS\system32\config\systemprofile then try a few other dirs.
49
+ *
50
+ * @since 1.5.1
51
+ */
52
+ public function get_for_disk() {
53
+ $possible_dirs[] = $this->core->config->get_home_directory();
54
+ $possible_dirs[] = ABSPATH;
55
+
56
+ foreach ( $possible_dirs as $dir ) {
57
+
58
+ $dir = Boldgrid_Backup_Admin_Utility::trailingslashit( $dir );
59
+
60
+ if ( ! empty( $dir ) && $this->core->wp_filesystem->is_dir( $dir ) && $this->core->wp_filesystem->is_readable( $dir ) ) {
61
+ return $dir;
62
+ }
63
+ }
64
+
65
+ return false;
66
+ }
67
+ }
admin/class-boldgrid-backup-admin-in-progress.php ADDED
@@ -0,0 +1,274 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * In Progress class.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.4
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup Admin In Progress Class.
17
+ *
18
+ * @since 1.5.4
19
+ */
20
+ class Boldgrid_Backup_Admin_In_Progress {
21
+
22
+ /**
23
+ * The core class object.
24
+ *
25
+ * @since 1.5.4
26
+ * @access private
27
+ * @var Boldgrid_Backup_Admin_Core
28
+ */
29
+ private $core;
30
+
31
+ /**
32
+ * A unix timestamp indicating when a backup was started.
33
+ *
34
+ * Currently this property is only used before and after a database is dumped,
35
+ * see $this->pre_dump() and $this->post_dump().
36
+ *
37
+ * @since 1.6.0
38
+ * @access protected
39
+ * @var int
40
+ */
41
+ protected $in_progress;
42
+
43
+ /**
44
+ * Constructor.
45
+ *
46
+ * @since 1.5.4
47
+ *
48
+ * @param Boldgrid_Backup_Admin_Core $core Core class object.
49
+ */
50
+ public function __construct( $core ) {
51
+ $this->core = $core;
52
+ }
53
+
54
+ /**
55
+ * Add a notice telling the user there's a backup in progress.
56
+ *
57
+ * @since 1.5.4
58
+ *
59
+ * @param array $notices
60
+ * @return array
61
+ */
62
+ public function add_notice( $notices ) {
63
+ $in_progress = $this->get();
64
+
65
+ if ( empty( $in_progress ) ) {
66
+ return $notices;
67
+ }
68
+
69
+ /*
70
+ * If we are in the middle of a backup, we'll want to increase the rate
71
+ * of the heartbeat so that we can more quickly update the user when
72
+ * the backup has completed.
73
+ */
74
+ wp_enqueue_script( 'heartbeat' );
75
+
76
+ $elapsed = time() - $in_progress;
77
+ $limit = 15 * MINUTE_IN_SECONDS;
78
+
79
+ $notice = $this->get_notice();
80
+ if ( false === $notice ) {
81
+ return $notices;
82
+ }
83
+
84
+ /*
85
+ * @todo If the backup takes longer than 15 minutes, the user needs more
86
+ * help with troubleshooting.
87
+ */
88
+ if ( $elapsed > $limit ) {
89
+ $notice['message'] .= __( ' Most backups usually finish before this amount of time, so we will stop displaying this notice.', 'boldgrid-backup' );
90
+ $this->end();
91
+ }
92
+
93
+ $notices[] = $notice;
94
+
95
+ return $notices;
96
+ }
97
+
98
+ /**
99
+ * Stop.
100
+ *
101
+ * Specify that we are no longer backing up a website.
102
+ *
103
+ * @since 1.5.4
104
+ */
105
+ public function end() {
106
+ $settings = $this->core->settings->get_settings( true );
107
+
108
+ if ( ! empty( $settings['in_progress'] ) ) {
109
+ unset( $settings['in_progress'] );
110
+ }
111
+
112
+ $this->core->settings->save( $settings );
113
+ }
114
+
115
+ /**
116
+ * Get the in progress value.
117
+ *
118
+ * The value is the time we started the backup.
119
+ *
120
+ * @since 1.5.4
121
+ *
122
+ * @return int
123
+ */
124
+ public function get() {
125
+ $settings = $this->core->settings->get_settings( true );
126
+
127
+ $in_progress = ! empty( $settings['in_progress'] ) ? $settings['in_progress'] : null;
128
+
129
+ return $in_progress;
130
+ }
131
+
132
+ /**
133
+ * Get our in progress notice.
134
+ *
135
+ * @since 1.6.0
136
+ *
137
+ * @return mixed Array on success, false when there's no backup in progress.
138
+ */
139
+ public function get_notice() {
140
+ $in_progress = $this->get();
141
+
142
+ if ( empty( $in_progress ) ) {
143
+ return false;
144
+ }
145
+
146
+ $notice = array(
147
+ 'class' => 'notice notice-warning boldgrid-backup-in-progress',
148
+ 'message' => sprintf( __( 'BoldGrid Backup began archiving your website %1$s ago.', 'boldgrid-backup' ), human_time_diff( $in_progress, time() ) ),
149
+ 'heading' => __( 'BoldGrid Backup - Backup in progress', 'boldgrid-backup' ),
150
+ );
151
+
152
+ return $notice;
153
+ }
154
+
155
+ /**
156
+ * Get our notice markup.
157
+ *
158
+ * @since 1.6.0
159
+ *
160
+ * @return mixed Returns a string (html markup), a WordPress admin notice or
161
+ * false if we don't have a notice.
162
+ */
163
+ public function get_notice_markup() {
164
+ $notice = $this->get_notice();
165
+ $markup = false;
166
+
167
+ if ( $notice ) {
168
+ $markup = $this->core->notice->get_notice_markup( $notice['class'], $notice['message'], $notice['heading'] );
169
+ }
170
+
171
+ return $markup;
172
+ }
173
+
174
+ /**
175
+ * Take action when the heartbeat is received.
176
+ *
177
+ * Include data in the heartbeat to let the user know if their backup is
178
+ * still in progress, or it has finished.
179
+ *
180
+ * @since 1.6.0
181
+ *
182
+ * @param array $response
183
+ * @param array $data
184
+ * @return array
185
+ */
186
+ public function heartbeat_received( $response, $data ) {
187
+ $key = 'boldgrid_backup_in_progress';
188
+
189
+ if ( empty( $data[ $key ] ) ) {
190
+ return $response;
191
+ }
192
+
193
+ // An int specifiying when the current "in progress" backup started.
194
+ $response[ $key ] = $this->get();
195
+
196
+ // Our "backup complete!" admin notice.
197
+ $response['boldgrid_backup_complete'] = $this->core->notice->get_backup_complete();
198
+
199
+ return $response;
200
+ }
201
+
202
+ /**
203
+ * Action to take before a database is dumped.
204
+ *
205
+ * @since 1.6.0
206
+ */
207
+ public function post_dump() {
208
+
209
+ /*
210
+ * After the database has been dumped, restore the flag stating a backup
211
+ * is still in progress.
212
+ *
213
+ * @see documentation in $this->pre_dump().
214
+ */
215
+ if ( ! empty( $this->in_progress ) ) {
216
+ $this->set( $this->in_progress );
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Action to take after a database is dumped.
222
+ *
223
+ * @since 1.6.0
224
+ */
225
+ public function pre_dump() {
226
+
227
+ /*
228
+ * Cancel any "Backup in progress" statuses.
229
+ *
230
+ * Avoid this issue:
231
+ * Before we begin creating a backup, we set a flag stating there is a
232
+ * "Backup in progress". So, when we create a backup of the database,
233
+ * that flag is in the backup. When we restore a backup, that flag will
234
+ * be restored, even if we're not in the middle of making a backup, thus
235
+ * giving us a false positive.
236
+ */
237
+ $this->in_progress = $this->get();
238
+ $this->end();
239
+ }
240
+
241
+ /**
242
+ * Set that we are in progress of backing up a website.
243
+ *
244
+ * @since 1.5.4
245
+ *
246
+ * @param int $time A unix timestamp indicating the time a backup started.
247
+ */
248
+ public function set( $time = null ) {
249
+ $settings = $this->core->settings->get_settings( true );
250
+
251
+ $settings['in_progress'] = ! empty( $time ) ? $time : time();
252
+
253
+ $this->core->settings->save( $settings );
254
+ }
255
+
256
+ /**
257
+ * Via ajax, get our "in progress" admin notice.
258
+ *
259
+ * @since 1.6.0
260
+ */
261
+ public function wp_ajax_get_progress_notice() {
262
+ if ( ! current_user_can( 'update_plugins' ) ) {
263
+ wp_send_json_error( __( 'Permission denied.', 'boldgrid-backup' ) );
264
+ }
265
+
266
+ if ( ! check_ajax_referer( 'boldgrid_backup_customizer', 'nonce', false ) ) {
267
+ wp_send_json_error( __( 'Invalid nonce.', 'boldgrid-backup' ) );
268
+ }
269
+
270
+ $in_progress_markup = $this->get_notice_markup();
271
+
272
+ wp_send_json_success( $in_progress_markup );
273
+ }
274
+ }
admin/class-boldgrid-backup-admin-jobs.php ADDED
@@ -0,0 +1,295 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Boldgrid Backup Admin Jobs.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.2
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * Boldgrid Backup Admin Jobs class.
17
+ *
18
+ * Option $boldgrid_backup_jobs array {
19
+ * An array of jobs that need to be ran.
20
+ *
21
+ * array {
22
+ *
23
+ * @type string $status pending|running|complete|fail
24
+ * @type string $filepath Full filepath to the backup file.
25
+ * @type int $start_time
26
+ * @type int $end_time
27
+ * @type string $action The action to run.
28
+ * @type array $action_data An array of data to send to the action.
29
+ * }
30
+ * }
31
+ *
32
+ * @since 1.5.2
33
+ */
34
+ class Boldgrid_Backup_Admin_Jobs {
35
+
36
+ /**
37
+ * The core class object.
38
+ *
39
+ * @since 1.5.2
40
+ * @access private
41
+ * @var Boldgrid_Backup_Admin_Core
42
+ */
43
+ private $core;
44
+
45
+ /**
46
+ * An array of jobs.
47
+ *
48
+ * @since 1.5.2
49
+ * @var mixed $jobs null|array Null by default to show it has not been
50
+ * initialized.
51
+ */
52
+ public $jobs = null;
53
+
54
+ /**
55
+ * The option name used to store jobs.
56
+ *
57
+ * @since 1.5.2
58
+ * @var string $option
59
+ */
60
+ public $option = 'boldgrid_backup_jobs';
61
+
62
+ /**
63
+ * Constructor.
64
+ *
65
+ * @since 1.5.2
66
+ *
67
+ * @param Boldgrid_Backup_Admin_Core $core
68
+ */
69
+ public function __construct( $core ) {
70
+ $this->core = $core;
71
+ }
72
+
73
+ /**
74
+ * Add a job to the queue.
75
+ *
76
+ * @since 1.5.2
77
+ *
78
+ * @param array $args An array of args for our job. Please see doc block of
79
+ * this class for more information.
80
+ */
81
+ public function add( $args ) {
82
+ if ( empty( $args['action'] ) ) {
83
+ return false;
84
+ }
85
+
86
+ $args['status'] = 'pending';
87
+
88
+ $this->set_jobs();
89
+ $this->jobs[] = $args;
90
+ $this->save_jobs();
91
+ }
92
+
93
+ /**
94
+ * Delete all jobs before and including $delete_key.
95
+ *
96
+ * We may have 100 jobs lined up. Job #5 may say "delete me and all those
97
+ * that came before me". Job #5 does that by calling this method.
98
+ *
99
+ * @since 1.5.2
100
+ *
101
+ * @param int $delete_key
102
+ */
103
+ public function delete_all_prior( $delete_key ) {
104
+
105
+ if ( ! is_numeric( $delete_key ) ) {
106
+ return;
107
+ }
108
+
109
+ $this->set_jobs();
110
+
111
+ foreach ( $this->jobs as $key => $job ) {
112
+ if ( $key <= $delete_key ) {
113
+ unset( $this->jobs[ $key ] );
114
+ }
115
+ }
116
+
117
+ $this->jobs = array_values( $this->jobs );
118
+ $this->save_jobs();
119
+ }
120
+
121
+ /**
122
+ * Determine if we have any jobs currently running.
123
+ *
124
+ * @since 1.5.2
125
+ *
126
+ * @return bool True when we have a job currently running.
127
+ */
128
+ public function is_running() {
129
+ $this->set_jobs();
130
+
131
+ foreach ( $this->jobs as $job ) {
132
+ if ( 'running' === $job['status'] ) {
133
+ return true;
134
+ }
135
+ }
136
+
137
+ return false;
138
+ }
139
+
140
+ /**
141
+ * Add "send an email" to the jobs queue.
142
+ *
143
+ * This method is added at priorty 200 to the
144
+ * boldgrid_backup_post_archive_files action. It should be the last thing
145
+ * that the jobs queue does, send an email confirmation.
146
+ *
147
+ * @since 1.5.2
148
+ *
149
+ * @param array $info
150
+ */
151
+ public function post_archive_files( $info ) {
152
+ /*
153
+ * We only want to add this to the jobs queue if we're in the middle of
154
+ * an automatic backup. If the user simply clicked on "Backup site now",
155
+ * we don't want to email the user, we'll already be doing that.
156
+ */
157
+ if ( ! $this->core->doing_cron ) {
158
+ return;
159
+ }
160
+
161
+ if ( ! $this->core->email->user_wants_notification( 'backup' ) ) {
162
+ return;
163
+ }
164
+
165
+ $args = array(
166
+ 'filepath' => $info['filepath'],
167
+ 'action' => 'boldgrid_backup_post_jobs_email',
168
+ 'action_data' => $info,
169
+ 'post_action' => 'delete_all_prior',
170
+ );
171
+
172
+ $this->add( $args );
173
+ }
174
+
175
+ /**
176
+ * Send an email after all jobs have been ran.
177
+ */
178
+ public function post_jobs_email( $info ) {
179
+ $post_jobs = 0;
180
+
181
+ $job_summary = array();
182
+
183
+ $job_summary[] = __( 'The following tasks were ran after creating the backup:', 'boldgrid-backup' );
184
+
185
+ $email_parts = $this->core->email->post_archive_parts( $info );
186
+
187
+ $this->set_jobs();
188
+
189
+ foreach ( $this->jobs as $key => $job ) {
190
+
191
+ if ( 'boldgrid_backup_post_jobs_email' === $job['action'] ) {
192
+ unset( $this->jobs[ $key ] );
193
+ break;
194
+ }
195
+
196
+ $job_summary[] = sprintf(
197
+ '%1$s: %2$s%3$s* %4$s:%10$s%5$s%3$s* %6$s:%10$s%7$s%3$s* %8$s:%10$s%10$s%9$s',
198
+ __( 'Task', 'boldgrid-backup' ),
199
+ $job['action_title'],
200
+ "\n",
201
+ __( 'status', 'boldgrid-backup' ),
202
+ $job['status'],
203
+ __( 'start', 'boldgrid-backup' ),
204
+ date( 'Y.m.d h:i:s a', $job['start_time'] ),
205
+ __( 'end', 'boldgrid-backup' ),
206
+ date( 'Y.m.d h:i:s a', $job['end_time'] ),
207
+ "\t"
208
+ );
209
+ $post_jobs++;
210
+
211
+ unset( $this->jobs[ $key ] );
212
+ }
213
+
214
+ $this->save_jobs();
215
+
216
+ if ( $post_jobs > 0 ) {
217
+ $email_parts['body']['main'] .= implode( "\n\n", $job_summary ) . "\n\n";
218
+ }
219
+
220
+ $body = implode( '', $email_parts['body'] );
221
+
222
+ return $this->core->email->send( $email_parts['subject'], $body );
223
+ }
224
+
225
+ /**
226
+ * Run the next job in the queue.
227
+ *
228
+ * This is the main method of this class. When cron or wp-cron runs, it will
229
+ * call this method, which will handle the rest.
230
+ *
231
+ * @since 1.5.2
232
+ *
233
+ * @see Boldgrid_Backup_Admin_Cron::is_valid_call()
234
+ *
235
+ * @return null
236
+ */
237
+ public function run() {
238
+ $this->set_jobs();
239
+
240
+ // If not logged-in, then require a matching "id".
241
+ if ( ! $this->core->cron->is_valid_call() ) {
242
+ wp_die( __( 'Error: Invalid request.' ), 'boldgrid-backup' );
243
+ }
244
+
245
+ // If there are no jobs or already running, then abort.
246
+ if ( empty( $this->jobs ) || $this->is_running() ) {
247
+ wp_die();
248
+ }
249
+
250
+ foreach ( $this->jobs as $key => &$job ) {
251
+ if ( 'pending' !== $job['status'] ) {
252
+ continue;
253
+ }
254
+
255
+ $job['start_time'] = time();
256
+ $this->save_jobs();
257
+
258
+ $status = apply_filters( $job['action'], $job['action_data'] );
259
+
260
+ $job['end_time'] = time();
261
+ $job['status'] = $status ? 'success' : 'fail';
262
+ $this->save_jobs();
263
+
264
+ break;
265
+ }
266
+
267
+ if ( ! empty( $job['post_action'] ) && 'delete_all_prior' === $job['post_action'] ) {
268
+ $this->delete_all_prior( $key );
269
+ }
270
+
271
+ wp_die();
272
+ }
273
+
274
+ /**
275
+ * Save jobs.
276
+ *
277
+ * @since 1.5.2
278
+ */
279
+ public function save_jobs() {
280
+ update_site_option( $this->option, $this->jobs );
281
+ }
282
+
283
+ /**
284
+ * Set jobs.
285
+ *
286
+ * @since 1.5.2
287
+ */
288
+ public function set_jobs() {
289
+ if ( ! is_null( $this->jobs ) ) {
290
+ return;
291
+ }
292
+
293
+ $this->jobs = get_site_option( $this->option, array() );
294
+ }
295
+ }
admin/class-boldgrid-backup-admin-notice.php ADDED
@@ -0,0 +1,230 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The admin-specific notice methods for the plugin
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.2
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup admin notice class.
17
+ *
18
+ * @since 1.2
19
+ */
20
+ class Boldgrid_Backup_Admin_Notice {
21
+
22
+ /**
23
+ * The core class object.
24
+ *
25
+ * @since 1.5.4
26
+ * @access private
27
+ * @var Boldgrid_Backup_Admin_Core
28
+ */
29
+ private $core;
30
+
31
+ /**
32
+ * Common strings used in notices.
33
+ *
34
+ * @since 1.5.4
35
+ * @var array
36
+ */
37
+ public $lang = array(
38
+ 'dis_error' => 'notice notice-error is-dismissible',
39
+ 'dis_success' => 'notice notice-success is-dismissible',
40
+ );
41
+
42
+ /**
43
+ * Constructor.
44
+ *
45
+ * @since 1.5.4
46
+ *
47
+ * @param Boldgrid_Backup_Admin_Core $core Core class object.
48
+ */
49
+ public function __construct( $core ) {
50
+ $this->core = $core;
51
+ }
52
+
53
+ /**
54
+ * Add a notice for a user.
55
+ *
56
+ * @since 1.5.4
57
+ *
58
+ * @param string $message
59
+ * @param string $class
60
+ * @param string $heading
61
+ */
62
+ public function add_user_notice( $message, $class, $heading = null ) {
63
+ $option = $this->get_user_option();
64
+
65
+ $notices = get_option( $option, array() );
66
+
67
+ $message = $this->add_container( $message );
68
+
69
+ $notices[] = array(
70
+ 'message' => $message,
71
+ 'class' => $class,
72
+ 'heading' => $heading,
73
+ );
74
+
75
+ update_option( $option, $notices );
76
+ }
77
+
78
+ /**
79
+ * An array of messages we've already printed to the current page.
80
+ *
81
+ * @since 1.5.1
82
+ * @var array
83
+ */
84
+ public $displayed_messages = array();
85
+
86
+ /**
87
+ * Display notices for user.
88
+ *
89
+ * @since 1.5.4
90
+ */
91
+ public function display_user_notice() {
92
+ $option = $this->get_user_option();
93
+
94
+ $notices = get_option( $option, array() );
95
+
96
+ $notices = $this->core->in_progress->add_notice( $notices );
97
+
98
+ if ( empty( $notices ) ) {
99
+ return;
100
+ }
101
+
102
+ foreach ( $notices as $notice ) {
103
+ printf( '
104
+ <div class="%1$s is-dismissible">
105
+ %3$s
106
+ %2$s
107
+ </div>',
108
+ /* 1 */ $notice['class'],
109
+ /* 2 */ $this->add_container( $notice['message'] ),
110
+ /* 3 */ ! empty( $notice['heading'] ) ? sprintf( '<h2 class="header-notice">%1$s</h2>', $notice['heading'] ) : ''
111
+ );
112
+ }
113
+
114
+ delete_option( $option );
115
+ }
116
+
117
+ /**
118
+ * Print an admin notice.
119
+ *
120
+ * @since 1.2
121
+ *
122
+ * @param string $message A message to display in the admin notice.
123
+ * @param string $class The class string for the div.
124
+ */
125
+ public function boldgrid_backup_notice( $message, $class = 'notice notice-error is-dismissible' ) {
126
+ if ( in_array( $message, $this->displayed_messages, true ) ) {
127
+ return;
128
+ }
129
+
130
+ $markup = $this->get_notice_markup( $class, $message );
131
+
132
+ echo $markup;
133
+
134
+ $this->displayed_messages[] = $message;
135
+ }
136
+
137
+ /**
138
+ * Get our "backup complete!" admin notice.
139
+ *
140
+ * @since 1.6.0
141
+ *
142
+ * @return mixed String (html markup) of admin notice on success, false on failure.
143
+ */
144
+ public function get_backup_complete() {
145
+
146
+ // Assume that this "backup complete!" notice is for the last backup made.
147
+ $archive_info = get_option( 'boldgrid_backup_latest_backup' );
148
+ if ( empty( $archive_info ) ) {
149
+ return false;
150
+ }
151
+
152
+ $message = include BOLDGRID_BACKUP_PATH . '/admin/partials/boldgrid-backup-admin-backup.php';
153
+
154
+ $markup = $this->get_notice_markup( $message['class'], $message['message'] );
155
+
156
+ return $markup;
157
+ }
158
+
159
+ /**
160
+ * Get the entire html markup for a notice, including the .notice container.
161
+ *
162
+ * @param string $class
163
+ * @param string $message
164
+ * @param string $heading
165
+ * @return string
166
+ */
167
+ public function get_notice_markup( $class, $message, $heading = null ) {
168
+ return sprintf( '
169
+ <div class="%1$s">
170
+ %2$s
171
+ %3$s
172
+ </div>',
173
+ /* 1 */ $class,
174
+ /* 2 */ ! empty( $heading ) ? sprintf( '<h2 class="header-notice">%1$s</h2>', $heading ) : '',
175
+ /* 3 */ $this->add_container( $message )
176
+ );
177
+ }
178
+
179
+ /**
180
+ * Queue a notice for a failed functionality test.
181
+ *
182
+ * @since 1.2.3
183
+ *
184
+ * @param bool $use_link Link to the Functionality Tests page. Default is TRUE.
185
+ */
186
+ public function functionality_fail_notice( $use_link = true ) {
187
+ if ( $use_link ) {
188
+ $message = sprintf(
189
+ esc_html__(
190
+ 'Functionality test has failed. You can go to %1$sFunctionality Test%2$s to view a report.',
191
+ 'boldgrid-backup'
192
+ ),
193
+ '<a href="' . admin_url( 'admin.php?page=boldgrid-backup-test' ) . '">',
194
+ '</a>'
195
+ );
196
+ } else {
197
+ $message = esc_html__( 'Functionality test has failed.', 'boldgrid-backup' );
198
+ }
199
+
200
+ do_action( 'boldgrid_backup_notice', $message, 'notice notice-error is-dismissible' );
201
+ }
202
+
203
+ /**
204
+ * Get user_notices option name for current user.
205
+ *
206
+ * @since 1.5.4
207
+ */
208
+ public function get_user_option() {
209
+ $user_id = get_current_user_id();
210
+ return 'boldgrid_backup_user_notices_' . $user_id;
211
+ }
212
+
213
+ /**
214
+ * Ensure a message is within a container and return it.
215
+ *
216
+ * If it is not within a p or div, wrap it in a p tag.
217
+ *
218
+ * @since 1.5.4
219
+ *
220
+ * @param string $message
221
+ * @return string
222
+ */
223
+ public function add_container( $message ) {
224
+ $in_container = false !== strpos( $message, '<p' ) || false !== strpos( $message, '<div' );
225
+
226
+ $message = ! $in_container ? '<p>' . $message . '</p>' : $message;
227
+
228
+ return $message;
229
+ }
230
+ }
admin/class-boldgrid-backup-admin-remote.php ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Boldgrid Backup Admin Remote.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.2
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * Boldgrid Backup Admin Remote class.
17
+ *
18
+ * @since 1.5.2
19
+ */
20
+ class Boldgrid_Backup_Admin_Remote {
21
+
22
+ /**
23
+ * The core class object.
24
+ *
25
+ * @since 1.5.2
26
+ * @access private
27
+ * @var Boldgrid_Backup_Admin_Core
28
+ */
29
+ private $core;
30
+
31
+ /**
32
+ * Constructor.
33
+ *
34
+ * @since 1.5.2
35
+ *
36
+ * @param Boldgrid_Backup_Admin_Core $core
37
+ */
38
+ public function __construct( $core ) {
39
+ $this->core = $core;
40
+ }
41
+
42
+ /**
43
+ * Determine if any storage locations are enabled.
44
+ *
45
+ * @since 1.5.4
46
+ *
47
+ * @return bool
48
+ */
49
+ public function any_enabled() {
50
+ $settings = $this->core->settings->get_settings();
51
+
52
+ if ( empty( $settings ) || empty( $settings['remote'] ) ) {
53
+ return false;
54
+ }
55
+
56
+ foreach ( $settings['remote'] as $remote ) {
57
+ if ( isset( $remote['enabled'] ) && true === $remote['enabled'] ) {
58
+ return true;
59
+ }
60
+ }
61
+
62
+ return false;
63
+ }
64
+
65
+ /**
66
+ * Return whether or not a remote storage provider is enabled.
67
+ *
68
+ * @since 1.5.2
69
+ *
70
+ * @param string $id amazon_s3
71
+ * @return bool
72
+ */
73
+ public function is_enabled( $id ) {
74
+ $settings = $this->core->settings->get_settings();
75
+
76
+ return ! empty( $settings['remote'][ $id ]['enabled'] ) && true === $settings['remote'][ $id ]['enabled'] ;
77
+ }
78
+
79
+ /**
80
+ * Take action after a backup has been downloaded remotely.
81
+ *
82
+ * @since 1.5.4
83
+ */
84
+ public function post_download( $filepath ) {
85
+
86
+ /*
87
+ * Restore the log file from the archive so we can get all the juicy
88
+ * meta data about the archive.
89
+ */
90
+ $this->core->archive_log->restore_by_zip( $filepath );
91
+
92
+ /*
93
+ * Now that we have the log, update the archive's timestamp based upon
94
+ * time last modified time in the log.
95
+ */
96
+ $this->core->archive->reset();
97
+ $this->core->archive->init( $filepath );
98
+ $this->core->archive->update_timestamp();
99
+ }
100
+ }
admin/class-boldgrid-backup-admin-restore-git.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Backup Admin Restore Git.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.1
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup Admin Restore Git.
17
+ *
18
+ * @since 1.5.1
19
+ */
20
+ class Boldgrid_Backup_Admin_Restore_Git {
21
+
22
+ /**
23
+ * Chmod .git/objects so that we can restore without file permission issues.
24
+ *
25
+ * @since 1.5.1
26
+ *
27
+ * @global $wp_filesystem
28
+ *
29
+ * @param string $dir
30
+ * @return string
31
+ */
32
+ public function chmod_objects( $dir ) {
33
+ global $wp_filesystem;
34
+
35
+ $message = sprintf( __( 'A file permissions error was encountered when attempting to restore files in "%1$s".', 'boldgrid-backup' ), ABSPATH . $dir );
36
+
37
+ $chmodded = $wp_filesystem->chmod( ABSPATH . $dir, FS_CHMOD_FILE, true );
38
+
39
+ if ( $chmodded ) {
40
+ return $message . ' ' . __( 'We updated file permissions, and suggest that you attempt the restoration again.', 'boldgrid-backup' );
41
+ } else {
42
+ return $message . ' ' . __( 'We attempted to updated file permissions, but that does not appear to have worked. Before attempting to restore again, please manually review these file permissions.', 'boldgrid-backup' );
43
+ }
44
+ }
45
+ }
admin/class-boldgrid-backup-admin-restore-helper.php ADDED
@@ -0,0 +1,296 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Backup Admin Restore Helper Class.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.1
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup Admin Restore Helper Class.
17
+ *
18
+ * @since 1.5.1
19
+ */
20
+ class Boldgrid_Backup_Admin_Restore_Helper {
21
+
22
+ /**
23
+ * Whether or not we are doing cron.
24
+ *
25
+ * @since 1.5.1
26
+ * @var bool
27
+ */
28
+ public $doing_cron;
29
+
30
+ /**
31
+ * A config of files to monitor before / after a restoration.
32
+ *
33
+ * @since 1.5.1
34
+ * @var array
35
+ */
36
+ public $monitor_files = array(
37
+ 'htaccess' => array(
38
+ // Filename, relative to ABSPATH.
39
+ 'filename' => '.htaccess',
40
+ // Whether or not to make a copy of the file before restoration.
41
+ 'copy' => true,
42
+ // Whether or not to keep the copy after restoration.
43
+ 'keep_copy' => true,
44
+ // Whether or not the file has been copied.
45
+ 'copied' => false,
46
+ ),
47
+ 'wpconfig' => array(
48
+ 'filename' => 'wp-config.php',
49
+ 'copy' => true,
50
+ 'keep_copy' => false,
51
+ 'copied' => false,
52
+ ),
53
+ );
54
+
55
+ /**
56
+ * Constructor.
57
+ *
58
+ * @since 1.5.1
59
+ */
60
+ public function __construct() {
61
+ $this->doing_cron = defined( 'DOING_CRON' ) && DOING_CRON;
62
+ }
63
+
64
+ /**
65
+ * Action to take when a .htaccess file has been restored.
66
+ *
67
+ * @since 1.5.1
68
+ */
69
+ public function post_restore_htaccess() {
70
+ add_action( 'shutdown', 'flush_rewrite_rules' );
71
+ }
72
+
73
+ /**
74
+ * Action to take when the wp-config.php file has been restored.
75
+ *
76
+ * @since 1.5.1
77
+ */
78
+ public function post_restore_wpconfig() {
79
+ $result = Boldgrid_Backup_Admin_Utility::fix_wpconfig();
80
+
81
+ if ( ! $result ) {
82
+ $message = esc_html__( 'Could not update the WordPress configuration file.', 'boldgrid-backup' );
83
+
84
+ error_log( __METHOD__ . ': ' . $message );
85
+
86
+ if ( ! $this->doing_cron ) {
87
+ do_action( 'boldgrid_backup_notice', $message, 'notice notice-error is-dismissible' );
88
+ }
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Action to take after restoring an archive.
94
+ *
95
+ * @since 1.5.1
96
+ *
97
+ * @global wp_filesystem
98
+ *
99
+ * @param array $info
100
+ */
101
+ public function post_restore( $info ) {
102
+ if ( $info['dryrun'] ) {
103
+ return;
104
+ }
105
+
106
+ global $wp_filesystem;
107
+
108
+ foreach ( $this->monitor_files as $key => $file ) {
109
+ $original = ABSPATH . $file['filename'];
110
+ $new = $original . '.bgb';
111
+
112
+ // Determine if the file was restored from backup.
113
+ $file_restored = false;
114
+ if ( $file['copied'] && sha1_file( $original ) !== sha1_file( $new ) ) {
115
+ $file_restored = true;
116
+ } elseif ( $file['copy'] && ! $file['copied'] && $wp_filesystem->exists( $original ) ) {
117
+ $file_restored = true;
118
+ }
119
+
120
+ if ( $file_restored ) {
121
+ /**
122
+ * Action to take after a specific file has been restored.
123
+ *
124
+ * @since 1.5.1
125
+ */
126
+ do_action( 'boldgrid_backup_post_restore_' . $key );
127
+ }
128
+
129
+ if ( $file['copy'] && $file['copied'] && ! $file['keep_copy'] ) {
130
+ $wp_filesystem->delete( $new );
131
+ }
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Action to take before restoring an archive.
137
+ *
138
+ * @since 1.5.1
139
+ *
140
+ * @global wp_filesystem
141
+ *
142
+ * @param array $info
143
+ */
144
+ public function pre_restore( $info ) {
145
+ if ( $info['dryrun'] ) {
146
+ return;
147
+ }
148
+
149
+ global $wp_filesystem;
150
+
151
+ foreach ( $this->monitor_files as $key => $file ) {
152
+ $original = ABSPATH . $file['filename'];
153
+ $new = $original . '.bgb';
154
+
155
+ if ( $file['copy'] && $wp_filesystem->exists( $original ) ) {
156
+ $wp_filesystem->copy( $original, $new, true, 0644 );
157
+ $this->monitor_files[ $key ]['copied'] = true;
158
+ }
159
+ }
160
+
161
+ // Only register this action when we know we're doing a restore.
162
+ add_action( 'shutdown', array( $this, 'shutdown' ) );
163
+ }
164
+
165
+ /**
166
+ * Prepare for a restoration via cron job.
167
+ *
168
+ * @since 1.6.1
169
+ *
170
+ * @return bool
171
+ */
172
+ public function prepare_restore() {
173
+ $pending_rollback = get_site_option( 'boldgrid_backup_pending_rollback' );
174
+
175
+ if ( empty( $pending_rollback ) ) {
176
+ return false;
177
+ }
178
+
179
+ /*
180
+ * Set POST variables.
181
+ *
182
+ * The archive_key and the archive_filename must match.
183
+ */
184
+ $_POST['restore_now'] = 1;
185
+ $_POST['archive_key'] = 0;
186
+ $_POST['archive_filename'] = basename( $pending_rollback['filepath'] );
187
+
188
+ return true;
189
+ }
190
+
191
+ /**
192
+ * Update permissions so an archive is safe to restore.
193
+ *
194
+ * The most common failure thus for when extracting an archive is file
195
+ * permissions related. If WordPress cannot restore a file because the current
196
+ * file's permissions don't allow editing, then the restoration both (1) fails
197
+ * and (2) gives us a half restored site.
198
+ *
199
+ * This method loops through all files in the archive and updates the actual
200
+ * file's permissions in an attempt to avoid file permission issues.
201
+ *
202
+ * @since 1.6.0
203
+ *
204
+ * @param string $archive_filepath Full path to an archive file.
205
+ */
206
+ public function set_writable_permissions( $archive_filepath ) {
207
+ global $wp_filesystem;
208
+
209
+ $zip = new ZipArchive();
210
+
211
+ if ( $zip->open( $archive_filepath ) ) {
212
+ for ( $i = 0; $i < $zip->numFiles; $i++ ) {
213
+ $data = $zip->statIndex( $i );
214
+
215
+ if ( empty( $data['name'] ) ) {
216
+ continue;
217
+ }
218
+
219
+ $wp_filesystem->chmod( ABSPATH . $data['name'] );
220
+ }
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Action to take during shutdown hook.
226
+ *
227
+ * This method was written because many of the calls in
228
+ * wp-admin/includes/class-pclzip.php are suppressed using @. If you are
229
+ * restoring a relatively large file and you reach a memory limit, the
230
+ * fatal error will never be apparent because of the @ suppression.abstract
231
+ *
232
+ * This method allows us to show any fatal errors.
233
+ *
234
+ * @since 1.5.1
235
+ */
236
+ public function shutdown() {
237
+ if ( $this->doing_cron ) {
238
+ return;
239
+ }
240
+
241
+ $last_error = error_get_last();
242
+
243
+ /*
244
+ * If there's no error or this is not fatal, abort.
245
+ *
246
+ * @see http://php.net/manual/en/errorfunc.constants.php
247
+ */
248
+ if ( empty( $last_error ) || 1 !== $last_error['type'] ) {
249
+ return;
250
+ }
251
+
252
+ $message = sprintf(
253
+ '<strong>%1$s</strong>: %2$s in %3$s on line %4$s',
254
+ __( 'Fatal error', 'boldgrid-backup' ),
255
+ $last_error['message'],
256
+ $last_error['file'],
257
+ $last_error['line']
258
+ );
259
+
260
+ do_action( 'boldgrid_backup_notice', $message, 'notice notice-error is-dismissible' );
261
+
262
+ echo '
263
+ <script type="text/javascript">
264
+ jQuery( ".restoration-in-progress" ).hide();
265
+ </script>
266
+ ';
267
+ }
268
+
269
+ /**
270
+ * If a restoration fails, take action.
271
+ *
272
+ * @since 1.5.1
273
+ *
274
+ * @param WP_Error $error
275
+ * @return mixed False if no action is taken, otherwise a string containing
276
+ * a description of the action.
277
+ */
278
+ public function restore_fail( $error ) {
279
+ global $wp_filesystem;
280
+
281
+ $message = $error->get_error_message();
282
+ $data = $error->get_error_data();
283
+
284
+ if ( __( 'Could not copy file.' ) === $message ) {
285
+
286
+ // Take action if we are having trouble restoring .git/objects/.
287
+ preg_match( '/(.*\.git\/objects\/).*/', $data, $matches );
288
+ if ( ! empty( $matches[1] ) ) {
289
+ $new_error = false;
290
+ return apply_filters( 'boldgrid_backup_cannnot_restore_git_objects', $matches[1] );
291
+ }
292
+ }
293
+
294
+ return false;
295
+ }
296
+ }
admin/class-boldgrid-backup-admin-scheduler.php ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Scheduler.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.1
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup Admin Scheduler class.
17
+ *
18
+ * @since 1.5.1
19
+ */
20
+ class Boldgrid_Backup_Admin_Scheduler {
21
+
22
+ /**
23
+ * Available schedulers.
24
+ *
25
+ * @since 1.5.1
26
+ * @access public
27
+ * @var array
28
+ */
29
+ public $available = array();
30
+
31
+ /**
32
+ * The core class object.
33
+ *
34
+ * @since 1.5.1
35
+ * @access private
36
+ * @var Boldgrid_Backup_Admin_Core
37
+ */
38
+ private $core;
39
+
40
+ /**
41
+ * Constructor.
42
+ *
43
+ * @since 1.5.1
44
+ *
45
+ * @param Boldgrid_Backup_Admin_Core $core Core class object.
46
+ */
47
+ public function __construct( $core ) {
48
+ $this->core = $core;
49
+ }
50
+
51
+ /**
52
+ * Clear all schedules.
53
+ *
54
+ * @since 1.5.1
55
+ */
56
+ public function clear_all_schedules() {
57
+ $this->core->wp_cron->clear_schedules();
58
+
59
+ $this->core->cron->delete_cron_entries();
60
+ $this->core->cron->delete_cron_entries( $this->core->cron->run_jobs );
61
+ }
62
+
63
+ /**
64
+ * Get our scheduler.
65
+ *
66
+ * @since 1.5.1
67
+ * @return mixed
68
+ */
69
+ public function get() {
70
+ $settings = $this->core->settings->get_settings();
71
+
72
+ $available = $this->get_available();
73
+
74
+ if ( ! empty( $settings['scheduler'] ) ) {
75
+ return $settings['scheduler'];
76
+ } elseif ( array_key_exists( 'cron', $available ) ) {
77
+ return 'cron';
78
+ } elseif ( array_key_exists( 'wp-cron', $available ) ) {
79
+ return 'wp-cron';
80
+ } else {
81
+ return false;
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Get available schedulers.
87
+ *
88
+ * @since 1.5.1
89
+ *
90
+ * @return array {
91
+ * An array of available schedulers.
92
+ *
93
+ * cron array {
94
+ * @type string $title Cron
95
+ * }
96
+ * wp-cron array {
97
+ * @type string $title WP Cron
98
+ * }
99
+ */
100
+ public function get_available() {
101
+ if ( ! empty( $this->available ) ) {
102
+ return $this->available;
103
+ }
104
+
105
+ $is_crontab_available = $this->core->test->is_crontab_available();
106
+
107
+ // We schedule crontab jobs requiring the PHP setting "allow_url_fopen" be enabled.
108
+ if ( $is_crontab_available && ini_get( 'allow_url_fopen' ) ) {
109
+ $this->available['cron'] = array(
110
+ 'title' => 'Cron',
111
+ );
112
+ }
113
+
114
+ $is_wpcron_available = $this->core->test->wp_cron_enabled();
115
+
116
+ if ( $is_wpcron_available ) {
117
+ $this->available['wp-cron'] = array(
118
+ 'title' => 'WP Cron',
119
+ );
120
+ }
121
+
122
+ return $this->available;
123
+ }
124
+
125
+ /**
126
+ * Check if a scheduler is available.
127
+ *
128
+ * @since 1.5.1
129
+ *
130
+ * @param string
131
+ * @return bool
132
+ */
133
+ public function is_available( $scheduler ) {
134
+ $available = $this->get_available();
135
+
136
+ return array_key_exists( $scheduler, $available );
137
+ }
138
+
139
+ }
admin/class-boldgrid-backup-admin-settings.php ADDED
@@ -0,0 +1,806 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The admin-specific utilities methods for the plugin
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.0
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup admin settings class.
17
+ *
18
+ * @since 1.0
19
+ */
20
+ class Boldgrid_Backup_Admin_Settings {
21
+ /**
22
+ * The core class object.
23
+ *
24
+ * @since 1.0
25
+ * @access private
26
+ * @var Boldgrid_Backup_Admin_Core
27
+ */
28
+ private $core;
29
+
30
+ /**
31
+ * Whether or not we're in the middle of saving settings from $_POST.
32
+ *
33
+ * @since 1.5.4
34
+ * @access public
35
+ * @var bool
36
+ */
37
+ public $is_saving_settings = false;
38
+
39
+ /**
40
+ * Constructor.
41
+ *
42
+ * @since 1.0
43
+ *
44
+ * @param Boldgrid_Backup_Admin_Core $core Core class object.
45
+ */
46
+ public function __construct( $core ) {
47
+ // Save the Boldgrid_Backup_Admin_Core object as a class property.
48
+ $this->core = $core;
49
+
50
+ $this->is_saving_settings = isset( $_POST['save_time'] );
51
+ }
52
+
53
+ /**
54
+ * How many days of the week are being saved?
55
+ *
56
+ * This method counts the number of $_POST keys that begin with "dow_". The count returned by
57
+ * this method is used to help enforce restrictions on the free version of the plugin.
58
+ *
59
+ * @since 1.3.1
60
+ *
61
+ * @return int
62
+ */
63
+ public function get_dow_count() {
64
+ $count = 0;
65
+
66
+ if ( ! isset( $_POST ) || ! is_array( $_POST ) ) {
67
+ return 0;
68
+ }
69
+
70
+ // Loop through each $_POST value and check if the key begins with dow_.
71
+ foreach ( $_POST as $k => $v ) {
72
+ if ( substr( $k, 0, 4 ) === 'dow_' ) {
73
+ $count++;
74
+ }
75
+ }
76
+
77
+ return $count;
78
+ }
79
+
80
+ /**
81
+ * Get settings using defaults.
82
+ *
83
+ * @since 1.0
84
+ *
85
+ * @param bool $raw Return the raw settings. This would happen very early
86
+ * on in this method, before all of the other checks
87
+ * happen.
88
+ * @return array An array of settings.
89
+ */
90
+ public function get_settings( $raw = false ) {
91
+ // Get settings.
92
+ $settings = get_site_option( 'boldgrid_backup_settings', array() );
93
+
94
+ if ( $raw ) {
95
+ return $settings;
96
+ }
97
+
98
+ // Configure a random minute. 5:4am will fail, but 5:04am will pass.
99
+ $random_minute = mt_rand( 1, 59 );
100
+ $random_minute = 1 === strlen( $random_minute ) ? '0' . $random_minute : $random_minute;
101
+
102
+ // Parse settings.
103
+ if ( ! empty( $settings['schedule'] ) ) {
104
+ // Update schedule format.
105
+ // Days of the week.
106
+ $settings['schedule']['dow_sunday'] = (
107
+ ! empty( $settings['schedule']['dow_sunday'] ) ? 1 : 0
108
+ );
109
+ $settings['schedule']['dow_monday'] = (
110
+ ! empty( $settings['schedule']['dow_monday'] ) ? 1 : 0
111
+ );
112
+ $settings['schedule']['dow_tuesday'] = (
113
+ ! empty( $settings['schedule']['dow_tuesday'] ) ? 1 : 0
114
+ );
115
+ $settings['schedule']['dow_wednesday'] = (
116
+ ! empty( $settings['schedule']['dow_wednesday'] ) ? 1 : 0
117
+ );
118
+ $settings['schedule']['dow_thursday'] = (
119
+ ! empty( $settings['schedule']['dow_thursday'] ) ? 1 : 0
120
+ );
121
+ $settings['schedule']['dow_friday'] = (
122
+ ! empty( $settings['schedule']['dow_friday'] ) ? 1 : 0
123
+ );
124
+ $settings['schedule']['dow_saturday'] = (
125
+ ! empty( $settings['schedule']['dow_saturday'] ) ? 1 : 0
126
+ );
127
+
128
+ // Time of day.
129
+ $settings['schedule']['tod_h'] = (
130
+ ! empty( $settings['schedule']['tod_h'] ) ?
131
+ $settings['schedule']['tod_h'] : mt_rand( 1, 5 )
132
+ );
133
+ $settings['schedule']['tod_m'] = (
134
+ ! empty( $settings['schedule']['tod_m'] ) ?
135
+ $settings['schedule']['tod_m'] : $random_minute
136
+ );
137
+ $settings['schedule']['tod_a'] = (
138
+ ! empty( $settings['schedule']['tod_a'] ) ?
139
+ $settings['schedule']['tod_a'] : 'AM'
140
+ );
141
+
142
+ // Notification settings.
143
+ $settings['notifications']['backup'] = (
144
+ ! isset( $settings['notifications']['backup'] ) ||
145
+ ! empty( $settings['notifications']['backup'] ) ? 1 : 0
146
+ );
147
+ $settings['notifications']['restore'] = (
148
+ ! isset( $settings['notifications']['restore'] ) ||
149
+ ! empty( $settings['notifications']['restore'] ) ? 1 : 0
150
+ );
151
+
152
+ // Notification email address.
153
+ if ( empty( $settings['notification_email'] ) ) {
154
+ $settings['notification_email'] = $this->core->config->get_admin_email();
155
+ }
156
+
157
+ // Other settings.
158
+ $settings['auto_backup'] = (
159
+ ! isset( $settings['auto_backup'] ) || ! empty( $settings['auto_backup'] ) ? 1 : 0
160
+ );
161
+ $settings['auto_rollback'] = (
162
+ ! isset( $settings['auto_rollback'] ) || ! empty( $settings['auto_rollback'] ) ?
163
+ 1 : 0
164
+ );
165
+ } else {
166
+ // Define defaults.
167
+ // Days of the week.
168
+ $settings['schedule']['dow_sunday'] = 0;
169
+ $settings['schedule']['dow_monday'] = 0;
170
+ $settings['schedule']['dow_tuesday'] = 0;
171
+ $settings['schedule']['dow_wednesday'] = 0;
172
+ $settings['schedule']['dow_thursday'] = 0;
173
+ $settings['schedule']['dow_friday'] = 0;
174
+ $settings['schedule']['dow_saturday'] = 0;
175
+
176
+ // Time of day.
177
+ $settings['schedule']['tod_h'] = mt_rand( 1, 5 );
178
+ $settings['schedule']['tod_m'] = $random_minute;
179
+ $settings['schedule']['tod_a'] = 'AM';
180
+
181
+ // Other settings.
182
+ $settings['retention_count'] = 5;
183
+ $settings['notification_email'] = $this->core->config->get_admin_email();
184
+ $settings['notifications']['backup'] = 1;
185
+ $settings['notifications']['restore'] = 1;
186
+ $settings['auto_backup'] = 1;
187
+ $settings['auto_rollback'] = 1;
188
+ }
189
+
190
+ /*
191
+ * If a cron schedule was found, then merge the settings.
192
+ *
193
+ * @todo As of 1.6.0, this feature is on hold. We need to take into
194
+ * account timezones, and possibly tell the user their settings don't
195
+ * actually match what's in the crontab.
196
+ */
197
+ /*
198
+ // If not updating the settings, then check cron for schedule.
199
+ if ( ! isset( $_POST['save_time'] ) ) {
200
+ $cron_schedule = $this->core->cron->read_cron_entry();
201
+ }
202
+
203
+ // If a cron schedule was found, then merge the settings.
204
+ if ( ! empty( $cron_schedule ) ) {
205
+ $settings['schedule'] = array_merge( $settings['schedule'], $cron_schedule );
206
+ }
207
+ */
208
+
209
+ $boldgrid_settings = get_site_option( 'boldgrid_settings' );
210
+
211
+ $settings['plugin_autoupdate'] = (
212
+ ! empty( $boldgrid_settings['plugin_autoupdate'] ) ? 1 : 0
213
+ );
214
+
215
+ $settings['theme_autoupdate'] = (
216
+ ! empty( $boldgrid_settings['theme_autoupdate'] ) ? 1 : 0
217
+ );
218
+
219
+ if ( empty( $settings['remote'] ) ) {
220
+ $settings['remote'] = array();
221
+ }
222
+
223
+ // For consistency, untrailingslashit the backup dir.
224
+ if ( isset( $settings['backup_directory'] ) ) {
225
+ $settings['backup_directory'] = untrailingslashit( $settings['backup_directory'] );
226
+ }
227
+
228
+ if ( empty( $settings['exclude_tables'] ) ) {
229
+ $settings['exclude_tables'] = array();
230
+ }
231
+
232
+ // Configure default folder_exclusion settings.
233
+ $settings['folder_exclusion_include'] = $this->core->folder_exclusion->from_settings( 'include', $settings );
234
+ $settings['folder_exclusion_exclude'] = $this->core->folder_exclusion->from_settings( 'exclude', $settings );
235
+
236
+ // Return the settings array.
237
+ return $settings;
238
+ }
239
+
240
+ /**
241
+ * Move backups from one directory to another.
242
+ *
243
+ * @since 1.3.2
244
+ *
245
+ * @param string $old_dir
246
+ * @param string $new_dir
247
+ * @return bool True on success / no backups needed to be moved.
248
+ */
249
+ private function move_backups( $old_dir, $new_dir ) {
250
+ $fail_count = 0;
251
+
252
+ $old_dir = Boldgrid_Backup_Admin_Utility::trailingslashit( $old_dir );
253
+ $new_dir = Boldgrid_Backup_Admin_Utility::trailingslashit( $new_dir );
254
+
255
+ $archives = $this->core->get_archive_list( null, $old_dir );
256
+
257
+ ignore_user_abort( true );
258
+
259
+ // Loop through each archive and move it.
260
+ foreach ( $archives as $archive ) {
261
+ $source = $archive['filepath'];
262
+ $destination = $new_dir . $archive['filename'];
263
+
264
+ $success = @$this->core->wp_filesystem->move( $source, $destination );
265
+
266
+ if ( ! $success ) {
267
+ $fail_count++;
268
+ }
269
+ }
270
+
271
+ return 0 === $fail_count;
272
+ }
273
+
274
+ /**
275
+ * Update settings.
276
+ *
277
+ * @since 1.0
278
+ * @access private
279
+ *
280
+ * @see Boldgrid_Backup_Admin_Cron::add_cron_entry()
281
+ * @see Boldgrid_Backup_Admin_Cron::get_cron_secret()
282
+ *
283
+ * @return bool Update success.
284
+ */
285
+ private function update_settings() {
286
+ $update_errors = array();
287
+
288
+ // Verify nonce.
289
+ check_admin_referer( 'boldgrid-backup-settings', 'settings_auth' );
290
+
291
+ // Get the retention count.
292
+ if ( isset( $_POST['retention_count'] ) ) {
293
+ $retention_count = intval( $_POST['retention_count'] );
294
+ } else {
295
+ $retention_count = $this->core->config->get_default_retention();
296
+ }
297
+
298
+ // Check for settings update.
299
+ if ( ! empty( $_POST['save_time'] ) ) {
300
+ // Get settings.
301
+ $settings = $this->get_settings();
302
+
303
+ // Initialize $update_error.
304
+ $update_error = false;
305
+
306
+ // Initialize $days_scheduled.
307
+ $days_scheduled = array();
308
+
309
+ // Validate input for schedule.
310
+ $indices = array(
311
+ 'dow_sunday',
312
+ 'dow_monday',
313
+ 'dow_tuesday',
314
+ 'dow_wednesday',
315
+ 'dow_thursday',
316
+ 'dow_friday',
317
+ 'dow_saturday',
318
+ 'tod_h',
319
+ 'tod_m',
320
+ 'tod_a',
321
+ );
322
+
323
+ foreach ( $indices as $index ) {
324
+ // Determine input type.
325
+ if ( 0 === strpos( $index, 'dow_' ) ) {
326
+ $type = 'day';
327
+ } elseif ( 'tod_h' === $index ) {
328
+ $type = 'h';
329
+ } elseif ( 'tod_m' === $index ) {
330
+ $type = 'm';
331
+ } elseif ( 'tod_a' === $index ) {
332
+ $type = 'a';
333
+ } else {
334
+ // Unknown type.
335
+ $type = '?';
336
+ }
337
+
338
+ if ( ! empty( $_POST[ $index ] ) ) {
339
+ // Validate by type.
340
+ switch ( $type ) {
341
+ case 'day' :
342
+ // Convert to integer.
343
+ $_POST[ $index ] = (int) $_POST[ $index ];
344
+
345
+ // If day was scheduled, then track it.
346
+ if ( 1 === $_POST[ $index ] ) {
347
+ $days_scheduled[] = date( 'w', strtotime( str_replace( 'dow_', '', $index ) ) );
348
+ }
349
+
350
+ break;
351
+ case 'h' :
352
+ if ( $_POST[ $index ] < 1 || $_POST[ $index ] > 12 ) {
353
+ // Error in input.
354
+ $update_error = true;
355
+ break 2;
356
+ }
357
+
358
+ // Convert to integer.
359
+ $_POST[ $index ] = (int) $_POST[ $index ];
360
+
361
+ break;
362
+ case 'm' :
363
+ if ( $_POST[ $index ] < 0 || $_POST[ $index ] > 59 ) {
364
+ // Error in input.
365
+ $update_error = true;
366
+ break 2;
367
+ }
368
+
369
+ // Convert to integer.
370
+ $_POST[ $index ] = (int) $_POST[ $index ];
371
+
372
+ // Pad left with 0.
373
+ $_POST[ $index ] = str_pad( $_POST[ $index ], 2, '0', STR_PAD_LEFT );
374
+
375
+ break;
376
+ case 'a' :
377
+ if ( 'AM' !== $_POST[ $index ] && 'PM' !== $_POST[ $index ] ) {
378
+ // Error in input; unknown type.
379
+ $update_error = true;
380
+ break 2;
381
+ }
382
+
383
+ break;
384
+ default :
385
+ // Error in input; unknown type.
386
+ $update_error = true;
387
+ break 2;
388
+ }
389
+
390
+ // Update the setting value provided.
391
+ $settings['schedule'][ $index ] = $_POST[ $index ];
392
+ } elseif ( 'day' === $type ) {
393
+ // Unassigned days.
394
+ $settings['schedule'][ $index ] = 0;
395
+ } else {
396
+ // Error in input.
397
+ $update_error = true;
398
+
399
+ break;
400
+ }
401
+ }
402
+
403
+ // Validate input for other settings.
404
+ $settings['retention_count'] = (
405
+ isset( $_POST['retention_count'] ) ? (int) $_POST['retention_count'] : 5
406
+ );
407
+
408
+ $settings['notifications']['backup'] = (
409
+ ( isset( $_POST['notify_backup'] ) && '1' === $_POST['notify_backup'] ) ? 1 : 0
410
+ );
411
+
412
+ $settings['notifications']['restore'] = (
413
+ ( isset( $_POST['notify_restore'] ) && '1' === $_POST['notify_restore'] ) ? 1 : 0
414
+ );
415
+
416
+ $settings['auto_backup'] = (
417
+ ( ! isset( $_POST['auto_backup'] ) || '1' === $_POST['auto_backup'] ) ? 1 : 0
418
+ );
419
+
420
+ $settings['auto_rollback'] = (
421
+ ( ! isset( $_POST['auto_rollback'] ) || '1' === $_POST['auto_rollback'] ) ? 1 : 0
422
+ );
423
+
424
+ // Update notification email address, if changed.
425
+ if ( isset( $settings['notification_email'] ) &&
426
+ sanitize_email( $_POST['notification_email'] ) !== $settings['notification_email'] ) {
427
+ $settings['notification_email'] = sanitize_email( $_POST['notification_email'] );
428
+ }
429
+
430
+ $boldgrid_settings['plugin_autoupdate'] = (
431
+ ( isset( $_POST['plugin_autoupdate'] ) && '1' === $_POST['plugin_autoupdate'] ) ?
432
+ 1 : 0
433
+ );
434
+
435
+ $boldgrid_settings['theme_autoupdate'] = (
436
+ ( isset( $_POST['theme_autoupdate'] ) && '1' === $_POST['theme_autoupdate'] ) ?
437
+ 1 : 0
438
+ );
439
+
440
+ unset( $settings['plugin_autoupdate'], $settings['theme_autoupdate'] );
441
+
442
+ // Get the current backup directory path.
443
+ $backup_dir_changed = false;
444
+ $original_backup_directory = ! empty( $settings['backup_directory'] ) ? $settings['backup_directory'] : false;
445
+
446
+ if ( ! empty( $_POST['backup_directory'] ) ) {
447
+ $post_backup_directory = trim( $_POST['backup_directory'] );
448
+ $post_backup_directory = untrailingslashit( $post_backup_directory );
449
+ $post_backup_directory = str_replace( '\\\\', '\\', $post_backup_directory );
450
+ }
451
+
452
+ /*
453
+ * Create the backup directory.
454
+ *
455
+ * Allow the user to submit a blank backup directory if they want
456
+ * to set the backup directory to the default.
457
+ */
458
+ if ( empty( $_POST['backup_directory'] ) ) {
459
+ // The get method validates and creates the backup directory.
460
+ $backup_directory = $this->core->backup_dir->guess_and_set();
461
+
462
+ $backup_dir_changed = $original_backup_directory !== $backup_directory;
463
+ } elseif ( $post_backup_directory !== $original_backup_directory ) {
464
+ $backup_directory = $post_backup_directory;
465
+
466
+ /*
467
+ * Create the backup directory.
468
+ *
469
+ * Even if the backup directory already exists, we still want to
470
+ * run the create method so that the necessary .htaccess and other
471
+ * files are created to protect the directory.
472
+ */
473
+ $backup_directory = $this->core->backup_dir->create( $backup_directory );
474
+
475
+ // Make sure that the backup directory has proper permissions.
476
+ $valid = $this->core->backup_dir->is_valid( $backup_directory );
477
+ if ( ! $valid ) {
478
+ $backup_directory = false;
479
+ }
480
+
481
+ $backup_dir_changed = true;
482
+ }
483
+
484
+ if ( $backup_dir_changed ) {
485
+ if ( false === $backup_directory ) {
486
+ $update_error = true;
487
+ $backup_dir_changed = false;
488
+ $update_errors = array_merge( $update_errors, $this->core->backup_dir->errors );
489
+ } else {
490
+ $settings['backup_directory'] = $backup_directory;
491
+ }
492
+ }
493
+
494
+ // Move backups to the new directory.
495
+ if ( $backup_dir_changed && isset( $_POST['move-backups'] ) && 'on' === $_POST['move-backups'] ) {
496
+ $backups_moved = $this->move_backups( $original_backup_directory, $backup_directory );
497
+
498
+ if ( ! $backups_moved ) {
499
+ $update_error = true;
500
+ $update_errors[] = sprintf( __( 'Unable to move backups from %1$s to %2$s', 'boldgrid-backup' ), $original_backup_directory, $backup_directory );
501
+ }
502
+ }
503
+
504
+ /*
505
+ * Save compressor settings.
506
+ *
507
+ * @since 1.5.1
508
+ */
509
+ if ( ! empty( $_POST['compressor'] ) ) {
510
+ $available_compressors = $this->core->compressors->get_available();
511
+ $selected_compressor = $_POST['compressor'];
512
+ if ( in_array( $selected_compressor, $available_compressors, true ) ) {
513
+ $settings['compressor'] = $selected_compressor;
514
+ } else {
515
+ $update_error = true;
516
+ $update_errors[] = __( 'The compressor you seleted is unavailable. Please select another.', 'boldgrid-backup' );
517
+ }
518
+ }
519
+
520
+ /*
521
+ * Save extractor settings.
522
+ *
523
+ * @since 1.5.1
524
+ */
525
+ if ( ! empty( $_POST['extractor'] ) ) {
526
+ $selected_extractor = $_POST['extractor'];
527
+ if ( in_array( $selected_extractor, $available_compressors, true ) ) {
528
+ $settings['extractor'] = $selected_extractor;
529
+ } else {
530
+ $update_error = true;
531
+ $update_errors[] = __( 'The extractor you seleted is unavailable. Please select another.', 'boldgrid-backup' );
532
+ }
533
+ }
534
+
535
+ /*
536
+ * Change the scheduler.
537
+ *
538
+ * If the scheduler is indeed changed, clear all prior backup
539
+ * schedules.
540
+ *
541
+ * @since 1.5.1
542
+ */
543
+ $original_scheduler = ! empty( $settings['scheduler'] ) ? $settings['scheduler'] : false;
544
+ $schedulers_available = $this->core->scheduler->get_available();
545
+ $scheduler_changed = ! empty( $_POST['scheduler'] ) && $original_scheduler !== $_POST['scheduler'];
546
+ if ( $scheduler_changed && array_key_exists( $_POST['scheduler'], $schedulers_available ) ) {
547
+ $settings['scheduler'] = $_POST['scheduler'];
548
+ }
549
+
550
+ /*
551
+ * Save WP Cron / Crons.
552
+ *
553
+ * @since 1.5.1
554
+ */
555
+ $scheduler = ! empty( $settings['scheduler'] ) ? $settings['scheduler'] : null;
556
+ if ( 'wp-cron' === $scheduler ) {
557
+ $crons_added = $this->core->wp_cron->add_all_crons( $settings );
558
+ } elseif ( 'cron' === $scheduler ) {
559
+ $crons_added = $this->core->cron->add_all_crons( $settings );
560
+ $settings['crontab_version'] = $this->core->cron->crontab_version;
561
+ $settings['cron_secret'] = $this->core->cron->get_cron_secret();
562
+ }
563
+ // Take action if we tried and failed to add crons.
564
+ if ( isset( $crons_added ) && ! $crons_added ) {
565
+ $update_error = true;
566
+ $update_errors[] = esc_html__( 'An error occurred when modifying cron jobs. Please try again.', 'boldgrid-backup' );
567
+ }
568
+
569
+ /*
570
+ * Save storage locations.
571
+ *
572
+ * @since 1.5.2
573
+ */
574
+ $storage_locations = ! empty( $settings['remote'] ) ? $settings['remote'] : array();
575
+
576
+ // Start off by disabling each storage location.
577
+ foreach ( $storage_locations as $remote_key => $storage_location ) {
578
+ $settings['remote'][ $remote_key ]['enabled'] = false;
579
+ }
580
+
581
+ // Get the storage location array from POST, then sanitize below.
582
+ $storage_locations_save = ! empty( $_POST['storage_location'] ) ?
583
+ $_POST['storage_location'] : array();
584
+
585
+ // Then enable it only if submitted. Values are not used, only key/index.
586
+ foreach ( $storage_locations_save as $storage_location => $storage_location_enabled ) {
587
+ $storage_location = sanitize_key( $storage_location );
588
+
589
+ /*
590
+ * Unless this is the local environment, don't enable a storage location if it has
591
+ * not yet been setup. Why enable FTP if FTP hasn't been setup yet.
592
+ */
593
+ if ( 'local' === $storage_location || isset( $settings['remote'][ $storage_location ] ) ) {
594
+ $settings['remote'][ $storage_location ]['enabled'] = true;
595
+ }
596
+ }
597
+
598
+ /*
599
+ * Save tables to include.
600
+ *
601
+ * @since 1.5.3
602
+ */
603
+ $settings['exclude_tables'] = $this->core->db_omit->get_from_post();
604
+ $settings['exclude_tables_type'] = $this->core->db_omit->get_post_type();
605
+
606
+ /*
607
+ * Save folder exclusion settings.
608
+ *
609
+ * @since 1.5.4
610
+ */
611
+ $settings['folder_exclusion_include'] = $this->core->folder_exclusion->from_post( 'include' );
612
+ $settings['folder_exclusion_exclude'] = $this->core->folder_exclusion->from_post( 'exclude' );
613
+ $settings['folder_exclusion_type'] = $this->core->folder_exclusion->from_post( 'type' );
614
+
615
+ // If no errors, then save the settings.
616
+ if ( ! $update_error ) {
617
+ $settings['updated'] = time();
618
+ update_site_option( 'boldgrid_backup_settings', $settings );
619
+ $this->update_boldgrid_settings( $boldgrid_settings );
620
+ }
621
+ }
622
+
623
+ // If there was no error, then show success notice.
624
+ if ( ! $update_error ) {
625
+ // Success.
626
+ do_action(
627
+ 'boldgrid_backup_notice',
628
+ esc_html__( 'Settings saved.', 'boldgrid-backup' ),
629
+ 'updated settings-error notice is-dismissible'
630
+ );
631
+ } elseif ( empty( $update_errors ) ) {
632
+ $failure_message = esc_html__( 'Invalid settings submitted. Please try again.', 'boldgrid-backup' );
633
+ } else {
634
+ $failure_message = sprintf( '<strong>%1$s</strong><br /><br />%2$s', __( 'We were unable to save your settings for the following reason(s):', 'boldgrid-backup' ), implode( '<br /><br />', $update_errors ) );
635
+ }
636
+
637
+ if ( isset( $failure_message ) ) {
638
+ do_action( 'boldgrid_backup_notice', $failure_message );
639
+ }
640
+
641
+ if ( ! $update_error ) {
642
+ /**
643
+ * Take action when settings have been updated.
644
+ *
645
+ * @since 1.5.3
646
+ */
647
+ do_action( 'boldgrid_backup_settings_updated' );
648
+ }
649
+
650
+ // Return success.
651
+ return ! $update_error;
652
+ }
653
+
654
+ /**
655
+ * Delete the boldgrid_backup_pending_rollback option.
656
+ *
657
+ * @since 1.0.1
658
+ */
659
+ public function delete_rollback_option() {
660
+ delete_site_option( 'boldgrid_backup_pending_rollback' );
661
+ }
662
+
663
+ /**
664
+ * Menu callback to display the Backup schedule page.
665
+ *
666
+ * @since 1.0
667
+ *
668
+ * @return null
669
+ */
670
+ public function page_backup_settings() {
671
+ add_thickbox();
672
+ wp_enqueue_style( 'boldgrid-backup-admin-new-thickbox-style' );
673
+
674
+ wp_enqueue_style( 'bglib-ui-css' );
675
+ wp_enqueue_script( 'bglib-ui-js' );
676
+ wp_enqueue_script( 'bglib-sticky' );
677
+
678
+ wp_enqueue_script( 'bglib-license' );
679
+
680
+ if ( ! $this->is_saving_settings ) {
681
+ $is_functional = $this->core->test->run_functionality_tests();
682
+ }
683
+
684
+ // If tests fail, then show an admin notice and abort.
685
+ if ( isset( $is_functional ) && ! $is_functional ) {
686
+ do_action(
687
+ 'boldgrid_backup_notice',
688
+ sprintf(
689
+ esc_html__(
690
+ 'Functionality test has failed. You can go to %1$sFunctionality Test%1$s to view a report.',
691
+ 'boldgrid-backup'
692
+ ),
693
+ '<a href="' . admin_url( 'admin.php?page=boldgrid-backup-test' ) . '">',
694
+ '</a>'
695
+ ),
696
+ 'notice notice-error is-dismissible'
697
+ );
698
+ }
699
+
700
+ // Display warning on resource usage and backups.
701
+ do_action(
702
+ 'boldgrid_backup_notice',
703
+ esc_html__(
704
+ 'Warning: Making backups uses resources. When the system is backing up, it will slow down your site for visitors. Furthermore, when the database itself is being copied, your site must “pause” temporarily to preserve data integrity. For most sites, the pause is typically a few seconds and is not noticed by visitors. Large sites take longer though. Please keep the number of backups you have stored and how often you make those backups to a minimum.',
705
+ 'boldgrid-backup'
706
+ ),
707
+ 'notice notice-warning is-dismissible'
708
+ );
709
+
710
+ // Get BoldGrid reseller settings.
711
+ $boldgrid_reseller = get_option( 'boldgrid_reseller' );
712
+
713
+ // If not part of a reseller, then show the unofficial host notice.
714
+ if ( empty( $boldgrid_reseller ) ) {
715
+ do_action(
716
+ 'boldgrid_backup_notice',
717
+ esc_html__(
718
+ 'Please note that your web hosting provider may have a policy against these types of backups. Please verify with your provider or choose a BoldGrid Official Host.',
719
+ 'boldgrid-backup'
720
+ ),
721
+ 'notice notice-warning is-dismissible'
722
+ );
723
+ }
724
+
725
+ // Check for settings update.
726
+ if ( $this->is_saving_settings ) {
727
+ // Verify nonce.
728
+ check_admin_referer( 'boldgrid-backup-settings', 'settings_auth' );
729
+
730
+ $this->update_settings();
731
+ }
732
+
733
+ // Enqueue CSS for the settings page.
734
+ wp_enqueue_style( 'boldgrid-backup-admin-settings',
735
+ plugin_dir_url( __FILE__ ) . 'css/boldgrid-backup-admin-settings.css', array(),
736
+ BOLDGRID_BACKUP_VERSION, 'all'
737
+ );
738
+
739
+ // Enqueue the JS for the settings page.
740
+ wp_enqueue_script( 'boldgrid-backup-admin-settings',
741
+ plugin_dir_url( __FILE__ ) . 'js/boldgrid-backup-admin-settings.js',
742
+ array( 'jquery' ),
743
+ BOLDGRID_BACKUP_VERSION,
744
+ false
745
+ );
746
+
747
+ $this->core->folder_exclusion->enqueue_scripts();
748
+ $this->core->db_omit->enqueue_scripts();
749
+
750
+ $settings = $this->get_settings();
751
+
752
+ // If the directory path is not in the settings, then add it for the form.
753
+ if ( empty( $settings['backup_directory'] ) ) {
754
+ $settings['backup_directory'] = $this->core->backup_dir->get();
755
+ }
756
+
757
+ $available_compressors = $this->core->compressors->get_available();
758
+
759
+ // Include the page template.
760
+ include BOLDGRID_BACKUP_PATH . '/admin/partials/boldgrid-backup-admin-settings.php';
761
+
762
+ return;
763
+ }
764
+
765
+ /**
766
+ * Save our settings.
767
+ *
768
+ * @since 1.5.2
769
+ *
770
+ * @param array $settings
771
+ * @return bool True on success.
772
+ */
773
+ public function save( $settings ) {
774
+
775
+ // For consistency, untrailingslashit the backup dir.
776
+ if ( isset( $settings['backup_directory'] ) ) {
777
+ $settings['backup_directory'] = untrailingslashit( $settings['backup_directory'] );
778
+ }
779
+
780
+ return update_site_option( 'boldgrid_backup_settings', $settings );
781
+ }
782
+
783
+ /**
784
+ * Update BoldGrid general settings.
785
+ *
786
+ * @since 1.3.11
787
+ *
788
+ * @param array $settings Array of BoldGrid settings.
789
+ * @return bool
790
+ */
791
+ public function update_boldgrid_settings( array $settings ) {
792
+ $boldgrid_settings = get_site_option( 'boldgrid_settings' );
793
+
794
+ $boldgrid_settings['plugin_autoupdate'] = (
795
+ ( isset( $settings['plugin_autoupdate'] ) && 1 === $settings['plugin_autoupdate'] ) ?
796
+ 1 : 0
797
+ );
798
+
799
+ $boldgrid_settings['theme_autoupdate'] = (
800
+ ( isset( $settings['theme_autoupdate'] ) && 1 === $settings['theme_autoupdate'] ) ?
801
+ 1 : 0
802
+ );
803
+
804
+ return update_site_option( 'boldgrid_settings', $boldgrid_settings );
805
+ }
806
+ }
admin/class-boldgrid-backup-admin-test.php ADDED
@@ -0,0 +1,729 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The admin-specific test functionality of the plugin
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.0
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup admin test class.
17
+ *
18
+ * @since 1.0
19
+ */
20
+ class Boldgrid_Backup_Admin_Test {
21
+
22
+ /**
23
+ * Base test filename.
24
+ *
25
+ * When we create test files, this is the prefix for all of them.
26
+ *
27
+ * @since 1.5.1
28
+ * @access public
29
+ * @var string
30
+ */
31
+ public $test_prefix = 'boldgrid-backup-test-file-';
32
+
33
+ /**
34
+ * The core class object.
35
+ *
36
+ * @since 1.0
37
+ * @access private
38
+ * @var Boldgrid_Backup_Admin_Core
39
+ */
40
+ private $core;
41
+
42
+ /**
43
+ * Is running Windows?
44
+ *
45
+ * @since 1.0
46
+ * @access private
47
+ * @var bool
48
+ */
49
+ private $is_windows = null;
50
+
51
+ /**
52
+ * Is the home directory writable?
53
+ *
54
+ * @since 1.2
55
+ * @access private
56
+ * @var bool
57
+ */
58
+ private $is_homedir_writable = null;
59
+
60
+ /**
61
+ * Is the WordPress installation root directory (ABSPATH) writable?
62
+ *
63
+ * @since 1.0
64
+ * @access private
65
+ * @var bool
66
+ */
67
+ private $is_abspath_writable = null;
68
+
69
+ /**
70
+ * Is crontab available?
71
+ *
72
+ * @since 1.0
73
+ * @access private
74
+ * @var bool
75
+ */
76
+ private $is_crontab_available = null;
77
+
78
+ /**
79
+ * Is WP-CRON enabled?
80
+ *
81
+ * @since 1.0
82
+ * @access private
83
+ * @var bool
84
+ */
85
+ private $wp_cron_enabled = null;
86
+
87
+ /**
88
+ * Is PHP in safe mode?
89
+ *
90
+ * @since 1.0
91
+ * @access private
92
+ * @var bool
93
+ */
94
+ private $is_php_safemode = null;
95
+
96
+ /**
97
+ * Functionality tests completed?
98
+ *
99
+ * @since 1.0
100
+ * @access private
101
+ * @var bool
102
+ */
103
+ private $functionality_tested = false;
104
+
105
+ /**
106
+ * Is functional?
107
+ *
108
+ * @since 1.0
109
+ * @access private
110
+ * @var bool
111
+ */
112
+ private $is_functional = null;
113
+
114
+ /**
115
+ * Transient time (in seconds) for disk / db size data.
116
+ *
117
+ * Default value is 300 seconds (5 minutes).
118
+ *
119
+ * @since 1.3.1
120
+ * @access public
121
+ * @var int
122
+ */
123
+ public $transient_time = 300;
124
+
125
+ /**
126
+ * Constructor.
127
+ *
128
+ * @since 1.0
129
+ *
130
+ * @param Boldgrid_Backup_Admin_Core $core Core class object.
131
+ */
132
+ public function __construct( $core ) {
133
+ // Save the Boldgrid_Backup_Admin_Core object as a class property.
134
+ $this->core = $core;
135
+ }
136
+
137
+ /**
138
+ * Wrapper for wp_filesystem exists.
139
+ *
140
+ * @since 1.5.1
141
+ *
142
+ * @param string $path
143
+ * @return bool
144
+ */
145
+ public function exists( $path ) {
146
+ $exists = $this->core->wp_filesystem->exists( $path );
147
+
148
+ /*
149
+ * Initial testing shows the wp_filesystem on Windows, at least when
150
+ * running in conjunction with IIS, does not always report accurately.
151
+ */
152
+ if ( ! $exists && $this->is_windows() ) {
153
+ $exists = file_exists( $path );
154
+ }
155
+
156
+ return $exists;
157
+ }
158
+
159
+ /**
160
+ * Extensive directory test.
161
+ *
162
+ * Pass in a directory, and we'll check to see if we can read / write / etc.
163
+ *
164
+ * @since 1.5.1
165
+ *
166
+ * @param string $dir
167
+ * @return array
168
+ */
169
+ public function extensive_dir_test( $dir ) {
170
+ $dir = Boldgrid_Backup_Admin_Utility::trailingslashit( $dir );
171
+ $random_filename = $dir . $this->test_prefix . mt_rand();
172
+ $txt_filename = $random_filename . '.txt';
173
+ $info_filename = $random_filename . '.rtf';
174
+ $str = __( 'This is a test file from BoldGrid Backup. You can delete this file.', 'boldgrid-backup' );
175
+
176
+ $data['exists'] = $this->core->wp_filesystem->exists( $dir );
177
+ $data['read'] = $this->core->wp_filesystem->is_readable( $dir );
178
+ $data['write'] = $this->core->wp_filesystem->is_writable( $dir );
179
+
180
+ // Can we get a directory listing?
181
+ $dirlist = $this->core->wp_filesystem->dirlist( $dir );
182
+ $data['dirlist'] = is_array( $dirlist );
183
+
184
+ // Determine if we have permission to rename a file.
185
+ $touched = $this->core->wp_filesystem->touch( $txt_filename );
186
+ $this->core->wp_filesystem->put_contents( $txt_filename, $str );
187
+ $data['rename'] = $touched && $this->core->wp_filesystem->move( $txt_filename, $info_filename );
188
+
189
+ // Delete the temp files.
190
+ $this->core->wp_filesystem->delete( $txt_filename );
191
+ $data['delete'] = $data['write'] && $this->core->wp_filesystem->delete( $info_filename );
192
+
193
+ /*
194
+ * IIS Users, we've tried hard enough to delete old test files for you.
195
+ * If we can't get the dir listing below, you'll need to delete all the
196
+ * files in $dir starting that beging with'boldgrid-backup-test-file-'.
197
+ */
198
+ $this->delete_test_files( $dir );
199
+
200
+ return $data;
201
+ }
202
+
203
+ /**
204
+ * Recursively search for a folder.
205
+ *
206
+ * @since 1.5.2
207
+ *
208
+ * @param string $folder_name
209
+ * @param string $starting_dir
210
+ * @return bool True if folder name found.
211
+ */
212
+ public function find_folder( $folder_name, $starting_dir = ABSPATH ) {
213
+ $starting_dir = trailingslashit( $starting_dir );
214
+
215
+ $files = $this->core->wp_filesystem->dirlist( $starting_dir );
216
+ $files = is_array( $files ) ? $files : array();
217
+
218
+ foreach ( $files as $file ) {
219
+
220
+ if ( 'd' !== $file['type'] ) {
221
+ continue;
222
+ }
223
+
224
+ $full_path = $starting_dir . $file['name'];
225
+
226
+ if ( $file['name'] === $folder_name ) {
227
+ return $full_path;
228
+ }
229
+
230
+ $folder_found = $this->find_folder( $folder_name, $full_path );
231
+ if ( false !== $folder_found ) {
232
+ return $folder_found;
233
+ }
234
+ }
235
+
236
+ return false;
237
+ }
238
+
239
+ /**
240
+ * Delete test files.
241
+ *
242
+ * When given a $dir, we'll scan and delete all files that begin with
243
+ * $this->test_prefix.
244
+ *
245
+ * @since 1.5.1
246
+ *
247
+ * @param string $dir
248
+ * @return bool
249
+ */
250
+ public function delete_test_files( $dir ) {
251
+ $dir = Boldgrid_Backup_Admin_Utility::trailingslashit( $dir );
252
+
253
+ $files = $this->core->wp_filesystem->dirlist( $dir );
254
+
255
+ if ( ! is_array( $files ) ) {
256
+ return false;
257
+ }
258
+
259
+ foreach ( $files as $file ) {
260
+ $filename = $file['name'];
261
+
262
+ if ( 0 === strpos( $filename, $this->test_prefix ) ) {
263
+ $this->core->wp_filesystem->delete( $dir . $filename );
264
+ }
265
+ }
266
+
267
+ return true;
268
+ }
269
+
270
+ /**
271
+ * Check if using Windows.
272
+ *
273
+ * @since 1.0
274
+ *
275
+ * @return bool TRUE is using Windows.
276
+ */
277
+ public function is_windows() {
278
+ // If was already checked, then return result from the class property.
279
+ if ( null !== $this->is_windows ) {
280
+ return $this->is_windows;
281
+ }
282
+
283
+ // Check if using Windows or Linux, and set as a class property.
284
+ $this->is_windows = ( 'win' === strtolower( substr( PHP_OS, 0, 3 ) ) );
285
+
286
+ // Return result.
287
+ return $this->is_windows;
288
+ }
289
+
290
+ /**
291
+ * Determine if a dir is writable.
292
+ *
293
+ * @since 1.5.1
294
+ *
295
+ * @param $dir string
296
+ * @return bool
297
+ */
298
+ public function is_writable( $dir ) {
299
+ if ( true === $this->core->wp_filesystem->is_writable( $dir ) ) {
300
+ return true;
301
+ }
302
+
303
+ /*
304
+ * Test if a dir is writable by attempting to write a tmp file.
305
+ *
306
+ * On plesk, wp_filesystem->is_writable was returning false in a Windows
307
+ * environment. When attempting to actually write to the $dir though, it
308
+ * was successful.
309
+ */
310
+ $random_filename = trailingslashit( $dir ) . mt_rand() . '.txt';
311
+ $this->core->wp_filesystem->touch( $random_filename );
312
+ $exists = $this->core->wp_filesystem->exists( $random_filename );
313
+
314
+ if ( ! $exists ) {
315
+ return false;
316
+ }
317
+
318
+ $this->core->wp_filesystem->delete( $random_filename );
319
+ return true;
320
+ }
321
+
322
+ /**
323
+ * Display a warning if the user's account has a node_modules folder.
324
+ *
325
+ * This can cause a timeout when checking the size of the WordPress directory.
326
+ * # It is not possible to catch a "max execution time" fatal error.
327
+ * # It is not possible to skip certain folders when using recurse_dirsize
328
+ * to calculate a directory size.
329
+ *
330
+ * @since 1.5.2
331
+ *
332
+ * @return bool True when a node_modules folder is found.
333
+ */
334
+ public function node_modules_warning() {
335
+ /*
336
+ * Initial test of find_folder call shows it took 0.02 seconds to run in
337
+ * a setup with ~15,000 files.
338
+ */
339
+ $node_modules_folder = $this->find_folder( 'node_modules' );
340
+
341
+ if ( false === $node_modules_folder ) {
342
+ return false;
343
+ }
344
+
345
+ if ( $this->core->doing_ajax || $this->core->doing_cron ) {
346
+ return true;
347
+ }
348
+
349
+ $folders_found = __( 'The following node_modules folder was found in your account:', 'boldgrid-backup' );
350
+ $possible_issues = __( 'Due to possible issues node_modules folders can cause when calculating disk space, your WordPress directory size was not calculated.', 'boldgrid-backup' );
351
+ $ignore_warning = __( 'To ignore this warning and try again, please <a href="%1$s">click here</a>', 'boldgrid-backup' );
352
+
353
+ $warning = sprintf(
354
+ '<strong>%1$s</strong><br />
355
+ <em>%2$s</em><br />
356
+ %3$s %4$s',
357
+ $folders_found,
358
+ $node_modules_folder,
359
+ $possible_issues,
360
+ sprintf( $ignore_warning, '?page=boldgrid-backup-test&skip_node_modules=1' )
361
+ );
362
+
363
+ do_action( 'boldgrid_backup_notice', $warning );
364
+
365
+ return true;
366
+ }
367
+
368
+ /**
369
+ * Is crontab available?
370
+ *
371
+ * Once the success is determined, the result is stored in a class property.
372
+ *
373
+ * @since 1.0
374
+ *
375
+ * @return bool
376
+ */
377
+ public function is_crontab_available() {
378
+ // If this test was already completed, then just return the result.
379
+ if ( null !== $this->is_crontab_available ) {
380
+ return $this->is_crontab_available;
381
+ }
382
+
383
+ if ( $this->is_windows() ) {
384
+ $this->is_crontab_available = false;
385
+ return $this->is_crontab_available;
386
+ }
387
+
388
+ $test_entry = '# BoldGrid Backup Test Entry ' . time() . ' (You can delete this line).';
389
+
390
+ /*
391
+ * To determine if crontab is available, we will BOTH write and remove
392
+ * a test entry from the crontab.
393
+ */
394
+ $entry_added = $this->core->cron->update_cron( $test_entry );
395
+ $entry_deleted = $this->core->cron->entry_delete( $test_entry );
396
+ $this->is_crontab_available = $entry_added && $entry_deleted;
397
+
398
+ return $this->is_crontab_available;
399
+ }
400
+
401
+ /**
402
+ * Is WP-CRON enabled?
403
+ *
404
+ * @since 1.0
405
+ *
406
+ * @return bool
407
+ */
408
+ public function wp_cron_enabled() {
409
+ // If this test was already completed, then just return the result.
410
+ if ( null !== $this->wp_cron_enabled ) {
411
+ return $this->wp_cron_enabled;
412
+ }
413
+
414
+ // Get the WP-CRON array.
415
+ $wp_cron_array = array();
416
+
417
+ if ( function_exists( '_get_cron_array' ) ) {
418
+ $wp_cron_array = _get_cron_array();
419
+ }
420
+
421
+ // Check for the DISABLE_WP_CRON constant and value.
422
+ $disable_wp_cron = false;
423
+
424
+ if ( defined( 'DISABLE_WP_CRON' ) ) {
425
+ $disable_wp_cron = DISABLE_WP_CRON;
426
+ }
427
+
428
+ $this->wp_cron_enabled = ( ! empty( $wp_cron_array ) && ! $disable_wp_cron );
429
+
430
+ return $this->wp_cron_enabled;
431
+ }
432
+
433
+ /**
434
+ * Is PHP running in safe mode?
435
+ *
436
+ * @since 1.0
437
+ *
438
+ * @return bool
439
+ */
440
+ public function is_php_safemode() {
441
+ // If this test was already completed, then just return the result.
442
+ if ( null !== $this->is_php_safemode ) {
443
+ return $this->is_php_safemode;
444
+ }
445
+
446
+ // Check if PHP is in safe mode.
447
+ $this->is_php_safemode = (bool) ini_get( 'safe_mode' );
448
+
449
+ // Return result.
450
+ return $this->is_php_safemode;
451
+ }
452
+
453
+ /**
454
+ * Determine if this is a plesk environment.
455
+ *
456
+ * @since 1.5.1
457
+ *
458
+ * @return bool
459
+ */
460
+ public function is_plesk() {
461
+ foreach ( $_SERVER as $k => $v ) {
462
+ if ( 'plesk_' === substr( $k, 0, strlen( 'plesk_' ) ) ) {
463
+ return true;
464
+ }
465
+ }
466
+
467
+ return false;
468
+ }
469
+
470
+ /**
471
+ * Perform functionality tests.
472
+ *
473
+ * @since 1.0
474
+ *
475
+ * @global WP_Filesystem $wp_filesystem The WordPress Filesystem API global object.
476
+ *
477
+ * @return bool
478
+ */
479
+ public function run_functionality_tests() {
480
+ // If functionality tests were already performed, then just return status.
481
+ if ( $this->functionality_tested && null !== $this->is_functional ) {
482
+ return $this->is_functional;
483
+ }
484
+
485
+ // Connect to the WordPress Filesystem API.
486
+ global $wp_filesystem;
487
+
488
+ // If not writable, then mark as not functional.
489
+ if ( ! $this->get_is_abspath_writable() ) {
490
+ $this->is_functional = false;
491
+ }
492
+
493
+ // Configure the backup directory path, or mark as not functional.
494
+ if ( ! $this->core->backup_dir->get() ) {
495
+ $this->is_functional = false;
496
+ }
497
+
498
+ // Get available compressors.
499
+ $available_compressors = $this->core->config->get_available_compressors();
500
+
501
+ // Test for available compressors, and add them to the array, or mark as not functional.
502
+ if ( empty( $available_compressors ) ) {
503
+ $this->is_functional = false;
504
+ }
505
+
506
+ if ( 'php_zip' === $this->core->compressors->get() ) {
507
+ $php_zip = new Boldgrid_Backup_Admin_Compressor_Php_Zip( $this->core );
508
+ if ( ! $php_zip->test( false ) ) {
509
+ $this->is_functional = false;
510
+ }
511
+ }
512
+
513
+ if ( 'pcl_zip' === $this->core->compressors->get() ) {
514
+ $pcl_zip = new Boldgrid_Backup_Admin_Compressor_Pcl_Zip( $this->core );
515
+ if ( ! $pcl_zip->test( false ) ) {
516
+ $this->is_functional = false;
517
+ }
518
+ }
519
+
520
+ // Test for PHP safe mode.
521
+ if ( $this->is_php_safemode() ) {
522
+ $this->is_functional = false;
523
+ }
524
+
525
+ // Save result, if not previously saved.
526
+ if ( null === $this->is_functional ) {
527
+ $this->is_functional = true;
528
+ }
529
+
530
+ // Mark as completed.
531
+ $this->functionality_tested = true;
532
+
533
+ return $this->is_functional;
534
+ }
535
+
536
+ /**
537
+ * Disk space report.
538
+ *
539
+ * @since 1.0
540
+ *
541
+ * @global WP_Filesystem $wp_filesystem The WordPress Filesystem API global object.
542
+ *
543
+ * @param bool $get_wp_size Whether of not to include the size of the WordPress directory.
544
+ * @return array An array containing disk space (total, used, available, WordPress directory).
545
+ */
546
+ public function get_disk_space( $get_wp_size = true ) {
547
+ // Connect to the WordPress Filesystem API.
548
+ global $wp_filesystem;
549
+
550
+ // Get the home directory.
551
+ $home_dir = $this->core->home_dir->get_for_disk();
552
+
553
+ // If the home directory is not defined, not a directory or not readable, then return 0.00.
554
+ if ( empty( $home_dir ) || ! $wp_filesystem->is_dir( $home_dir ) ||
555
+ ! $wp_filesystem->is_readable( $home_dir ) ) {
556
+ return array(
557
+ 0.00,
558
+ 0.00,
559
+ 0.00,
560
+ false,
561
+ );
562
+ }
563
+
564
+ // Get filesystem disk space information.
565
+ $disk_total_space = disk_total_space( $home_dir );
566
+ $disk_free_space = disk_free_space( $home_dir );
567
+ $disk_used_space = $disk_total_space - $disk_free_space;
568
+
569
+ // Initialize $wp_root_size.
570
+ $wp_root_size = false;
571
+
572
+ // Get the size of the filtered WordPress installation root directory (ABSPATH).
573
+ if ( $get_wp_size ) {
574
+ $wp_root_size = $this->get_wp_size();
575
+ }
576
+
577
+ // Return the disk information array.
578
+ return array(
579
+ $disk_total_space,
580
+ $disk_used_space,
581
+ $disk_free_space,
582
+ $wp_root_size,
583
+ );
584
+ }
585
+
586
+ /**
587
+ * Get the WordPress total file size.
588
+ *
589
+ * @since 1.0
590
+ * @access private
591
+ *
592
+ * @see get_filtered_filelist
593
+ *
594
+ * @return int|bool The total size for the WordPress file system in bytes, or FALSE on error.
595
+ */
596
+ private function get_wp_size() {
597
+ // Save time, use transients.
598
+ if ( false !== ( $transient = get_transient( 'boldgrid_backup_wp_size' ) ) ) {
599
+ return $transient;
600
+ }
601
+
602
+ // Avoid timeout caused when node_modules exist. Return 0 bytes.
603
+ if ( empty( $_GET['skip_node_modules'] ) ) {
604
+ $node_modules_found = $this->node_modules_warning();
605
+ if ( true === $node_modules_found ) {
606
+ return 0;
607
+ }
608
+ }
609
+
610
+ // Perform functionality tests.
611
+ $is_functional = $this->run_functionality_tests();
612
+
613
+ // If plugin is not functional, then return FALSE.
614
+ if ( ! $is_functional ) {
615
+ return false;
616
+ }
617
+
618
+ $size = $this->core->filelist->get_size();
619
+
620
+ // Save time, use transients.
621
+ set_transient( 'boldgrid_backup_wp_size', $size, $this->transient_time );
622
+
623
+ // Return the result.
624
+ return $size;
625
+ }
626
+
627
+ /**
628
+ * Get database size.
629
+ *
630
+ * @since 1.0
631
+ *
632
+ * @global wpdb $wpdb The WordPress database class object.
633
+ *
634
+ * @return int The total size of the database (in bytes).
635
+ */
636
+ public function get_database_size() {
637
+ // Save some time, get transient.
638
+ if ( false !== ( $transient = get_transient( 'boldgrid_backup_db_size' ) ) ) {
639
+ return $transient;
640
+ }
641
+
642
+ // Connect to the WordPress database via $wpdb.
643
+ global $wpdb;
644
+
645
+ // Build query.
646
+ $query = $wpdb->prepare(
647
+ 'SELECT SUM(`data_length` + `index_length`) FROM `information_schema`.`TABLES` WHERE `TABLE_SCHEMA`=%s AND `TABLE_NAME` LIKE %s GROUP BY `TABLE_SCHEMA`;',
648
+ DB_NAME, $wpdb->get_blog_prefix( is_multisite() ) . '%'
649
+ );
650
+
651
+ // Check query.
652
+ if ( empty( $query ) ) {
653
+ return 0;
654
+ }
655
+
656
+ // Get the result.
657
+ $result = $wpdb->get_row( $query, ARRAY_N );
658
+
659
+ // If there was an error or nothing returned, then fail.
660
+ if ( empty( $result ) ) {
661
+ return 0;
662
+ }
663
+
664
+ // Save some time, set transient.
665
+ set_transient( 'boldgrid_backup_db_size', $result[0], $this->transient_time );
666
+
667
+ // Return result.
668
+ return $result[0];
669
+ }
670
+
671
+ /**
672
+ * Get and return a boolean for whether or not the ABSPATH is writable.
673
+ *
674
+ * @since 1.0
675
+ *
676
+ * @global WP_Filesystem $wp_filesystem The WordPress Filesystem API global object.
677
+ *
678
+ * @return bool
679
+ */
680
+ public function get_is_abspath_writable() {
681
+ if ( null !== $this->is_abspath_writable ) {
682
+ return $this->is_abspath_writable;
683
+ }
684
+
685
+ // Connect to the WordPress Filesystem API.
686
+ global $wp_filesystem;
687
+
688
+ // Determine if ABSPATH is writable.
689
+ $this->is_abspath_writable = $this->is_writable( ABSPATH );
690
+
691
+ // Return the result.
692
+ return $this->is_abspath_writable;
693
+ }
694
+
695
+ /**
696
+ * Get and return a boolean for whether or not the home directory is writable.
697
+ *
698
+ * @since 1.2
699
+ *
700
+ * @return bool
701
+ */
702
+ public function is_homedir_writable() {
703
+ if ( null !== $this->is_homedir_writable ) {
704
+ return $this->is_homedir_writable;
705
+ }
706
+
707
+ // Get the user home directory.
708
+ $home_dir = $this->core->config->get_home_directory();
709
+
710
+ // Check if home directory is writable.
711
+ $this->is_homedir_writable = $this->is_writable( $home_dir );
712
+
713
+ // Return the result.
714
+ return $this->is_homedir_writable;
715
+ }
716
+
717
+ /**
718
+ * Determine if we are on an IIS server.
719
+ *
720
+ * @since 1.5.1
721
+ *
722
+ * @return bool
723
+ */
724
+ public function is_iis() {
725
+ return $this->is_windows() &&
726
+ ! empty( $_SERVER['SERVER_SOFTWARE'] ) &&
727
+ false !== strpos( $_SERVER['SERVER_SOFTWARE'], 'IIS' );
728
+ }
729
+ }
admin/class-boldgrid-backup-admin-time.php ADDED
@@ -0,0 +1,412 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Time class.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.6.0
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup Admin Time Class.
17
+ *
18
+ * @since 1.6.0
19
+ */
20
+ class Boldgrid_Backup_Admin_Time {
21
+
22
+ /**
23
+ * The core class object.
24
+ *
25
+ * @since 1.6.0
26
+ * @access private
27
+ * @var Boldgrid_Backup_Admin_Core
28
+ */
29
+ private $core;
30
+
31
+ /**
32
+ * UTC Time.
33
+ *
34
+ * @since 1.6.0
35
+ * @access public
36
+ * @var int
37
+ */
38
+ public $utc_time;
39
+
40
+ /**
41
+ * Local time (local to a user).
42
+ *
43
+ * @since 1.6.0
44
+ * @access public
45
+ * @var int
46
+ */
47
+ public $local_time;
48
+
49
+ /**
50
+ * Local timezon (local to a user).
51
+ *
52
+ * @since 1.6.0
53
+ * @access public
54
+ * @var string
55
+ */
56
+ public $local_timezone;
57
+
58
+ /**
59
+ * Server offset.
60
+ *
61
+ * @since 1.6.0
62
+ * @access public
63
+ * @var int
64
+ */
65
+ public $server_offset = null;
66
+
67
+ /**
68
+ * Constructor.
69
+ *
70
+ * @since 1.6.0
71
+ *
72
+ * @param Boldgrid_Backup_Admin_Core $core Core class object.
73
+ */
74
+ public function __construct( $core ) {
75
+ $this->core = $core;
76
+ }
77
+
78
+ /**
79
+ * Get a server's UTC offset.
80
+ *
81
+ * @since 1.6.0
82
+ *
83
+ * @return int
84
+ */
85
+ public function get_server_offset() {
86
+ if ( is_null( $this->server_offset ) ) {
87
+ $this->server_offset = $this->core->execute_command( 'date +%:::z' );
88
+ }
89
+
90
+ return $this->server_offset;
91
+ }
92
+
93
+ /**
94
+ * Get our server's timezone as a DateTimeZone object.
95
+ *
96
+ * @since 1.6.0
97
+ *
98
+ * @return mixed DateTimeZone object on success, false on failure.
99
+ */
100
+ public function get_server_timezone() {
101
+
102
+ /*
103
+ * Determine how we are going to get the server's timezone.
104
+ *
105
+ * PHP time zone buffoonery (or my own).
106
+ *
107
+ * php -r "new DateTimeZone( '-0400' );"
108
+ * ------------- --------------
109
+ * - PHP 5.3.3 - - PHP 5.6.30 -
110
+ * ------------- --------------
111
+ * DateTimeZone::__construct(): Works as expected.
112
+ * Unknown or bad timezone (-0400)
113
+ *
114
+ * php -r '$tz = new DateTimeZone( "EDT" ); echo $tz->getName();';
115
+ * ------------- --------------
116
+ * - PHP 5.3.3 - - PHP 5.6.30 -
117
+ * ------------- --------------
118
+ * America/New_York EDT
119
+ *
120
+ * php -r '$tz = new DateTimeZone( "EDT" ); echo $tz->getName();';
121
+ * ------------- --------------
122
+ * - PHP 5.3.3 - - PHP 5.6.30 -
123
+ * ------------- --------------
124
+ * DateTimeZone Object DateTimeZone Object
125
+ * ( (
126
+ * ) [timezone_type] => 2
127
+ * [timezone] => EDT
128
+ * )
129
+ */
130
+ $timezone = new DateTimeZone( 'EDT' );
131
+ if ( 'EDT' === $timezone->getName() ) {
132
+ // Example, get -0400.
133
+ $timezone = $this->core->execute_command( 'date +%z' );
134
+ } else {
135
+ // Example, get EDT.
136
+ $timezone = $this->core->execute_command( 'date +%Z' );
137
+ }
138
+
139
+ try {
140
+ $timezone = new DateTimeZone( $timezone );
141
+ return $timezone;
142
+ } catch ( Exception $e ) {
143
+ return false;
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Get a DateTime object based on time set in $settings.
149
+ *
150
+ * On our Settings page, the user can enter a time to schedule automated
151
+ * backups. This method takes that time (hour, minute, ampm) and returns it
152
+ * as a DateTime object.
153
+ *
154
+ * @since 1.6.0
155
+ *
156
+ * @param array $settings
157
+ * @param array $tz_info
158
+ * @return DateTime
159
+ */
160
+ public function get_settings_date( $settings = array(), $tz_info = array() ) {
161
+
162
+ // Abort right away if bad data sent in.
163
+ if ( ! is_array( $settings ) || ! is_array( $tz_info ) ) {
164
+ return false;
165
+ }
166
+
167
+ $settings = empty( $settings ) ? $this->core->settings->get_settings() : $settings;
168
+
169
+ $tz_info = empty( $tz_info ) ? $this->get_timezone_info() : $tz_info;
170
+ $is_utc = ! empty( $tz_info['abbr'] ) && 'UTC' === substr( $tz_info['abbr'], 0, 3 );
171
+
172
+ $time_string = $settings['schedule']['tod_h'] . ':' . $settings['schedule']['tod_m'] . ' ' . $settings['schedule']['tod_a'];
173
+
174
+ /*
175
+ * DateTime Timezone types.
176
+ *
177
+ * I didn't comprehend this from:
178
+ * http://php.net/manual/en/datetime.construct.php
179
+ * ... However according to
180
+ * https://stackoverflow.com/questions/17694894/different-timezone-types-on-datetime-object
181
+ * ... DateTime objects with timezones can be constructed in the following ways:
182
+ * # Type 1: A UTC offset new DateTime( "17 July 2013 -0300" );
183
+ * # Type 2: A timezone abbreviation new DateTime( "17 July 2013 GMT" );
184
+ * # Type 3: A timezone identifier new DateTime( "17 July 2013", new DateTimeZone( "Europe/London" ) );
185
+ */
186
+ try {
187
+ if ( $is_utc ) {
188
+ $date = new DateTime( $time_string, new DateTimeZone( 'UTC' ) );
189
+
190
+ // If we have a gmt_offset (example: -4.5), use that, otherwise parse the -4 from UTC-4.
191
+ $offset = ! empty( $tz_info['gmt_offset'] ) ? $tz_info['gmt_offset'] : substr( $tz_info['abbr'], 3 );
192
+
193
+ /*
194
+ * The user may have set UTC-4, but the $date above is simply UTC.
195
+ * Modify the $date to actually get UTC. So if the user wants
196
+ * 10:20 am UTC-4 we'll give them 2:20 pm UTC.
197
+ */
198
+ $date->modify( ( -1 * $offset * HOUR_IN_SECONDS ) . ' second' );
199
+ } else {
200
+ $date = new DateTime( $time_string, new DateTimeZone( $tz_info['name'] ) );
201
+ }
202
+ return $date;
203
+ } catch ( Exception $e ) {
204
+ return false;
205
+ }
206
+ }
207
+
208
+ /**
209
+ * Format GMT Offset.
210
+ *
211
+ * This method required by this->get_timezone_info().
212
+ *
213
+ * @since 1.6.0
214
+ *
215
+ * @param float $offset Offset in hours.
216
+ * @return string Formatted offset.
217
+ */
218
+ public function format_gmt_offset( $offset ) {
219
+ if ( 0 <= $offset ) {
220
+ $formatted_offset = '+' . (string) $offset;
221
+ } else {
222
+ $formatted_offset = (string) $offset;
223
+ }
224
+ $formatted_offset = str_replace(
225
+ array( '.25', '.5', '.75' ),
226
+ array( ':15', ':30', ':45' ),
227
+ $formatted_offset
228
+ );
229
+ return $formatted_offset;
230
+ }
231
+
232
+ /**
233
+ * Get a <span> of our time.
234
+ *
235
+ * @since 1.6.0
236
+ *
237
+ * @param string $format
238
+ * @return string
239
+ */
240
+ public function get_span( $format = 'M j, Y h:i a' ) {
241
+ if ( empty( $this->local_time ) ) {
242
+ return '';
243
+ }
244
+
245
+ return sprintf(
246
+ '<span title="%1$s">%2$s</span>',
247
+ $this->local_timezone,
248
+ date( $format, $this->local_time )
249
+ );
250
+ }
251
+
252
+ /**
253
+ * Get timezone info.
254
+ *
255
+ * This is a core WordPress function, copied here. It requires
256
+ * the format_gmt_offset() method, which is copied here as well.
257
+ *
258
+ * The core implementation of this method is used within the Customizer to
259
+ * show a user their timezone when scheduling changes for a future date.
260
+ *
261
+ * In our efforts to use the same formatting as WordPress, we attempted to
262
+ * use this method, however ran into a blocker. This method is defined in the
263
+ * WP_Customize_Date_Time_Control class, which extends the WP_Customize_Control
264
+ * class. The WP_Customize_Control class was designed for use within the
265
+ * Customizer, and cannot really be instantiated outside of it.
266
+ *
267
+ * Example: WordPress set to UTC-4
268
+ * Array
269
+ * (
270
+ * [abbr] => UTC-4
271
+ * [description] => Timezone is UTC-4.
272
+ * [markup_timezone] => UTC-4
273
+ * [markup_change] => Change timezone
274
+ * )
275
+ *
276
+ * Example: WordPress set to NewYork
277
+ * Array
278
+ * (
279
+ * [abbr] => EDT
280
+ * [name] => America/New_York
281
+ * [description] => Timezone is America/New York (EDT), currently UTC-4.
282
+ * [markup_timezone] => EDT
283
+ * [markup_change] => Change timezone
284
+ * )
285
+ *
286
+ * Example: WordPress set to UTC-4.5
287
+ * Array
288
+ * (
289
+ * [gmt_offset] => -4.5
290
+ * [abbr] => UTC-4.5
291
+ * [description] => Timezone is UTC-4.5.
292
+ * [markup_timezone] => UTC-4.5
293
+ * [markup_change] => Change timezone
294
+ * )
295
+ *
296
+ * @since 1.6.0
297
+ *
298
+ * @return array
299
+ */
300
+ public function get_timezone_info() {
301
+ $tz_string = get_option( 'timezone_string' );
302
+ $timezone_info = array();
303
+
304
+ if ( $tz_string ) {
305
+ try {
306
+ $tz = new DateTimezone( $tz_string );
307
+ } catch ( Exception $e ) {
308
+ $tz = '';
309
+ }
310
+
311
+ if ( $tz ) {
312
+ $now = new DateTime( 'now', $tz );
313
+ $formatted_gmt_offset = sprintf( 'UTC%s', $this->format_gmt_offset( $tz->getOffset( $now ) / 3600 ) );
314
+ $tz_name = str_replace( '_', ' ', $tz->getName() );
315
+ $timezone_info['abbr'] = $now->format( 'T' );
316
+
317
+ // This set of code is not in core.
318
+ $timezone_info['name'] = $tz->getName();
319
+
320
+ /* translators: 1: timezone name, 2: timezone abbreviation, 3: gmt offset */
321
+ $timezone_info['description'] = sprintf( __( 'Timezone is %1$s (%2$s), currently %3$s.' ), $tz_name, $timezone_info['abbr'], $formatted_gmt_offset );
322
+ } else {
323
+ $timezone_info['description'] = '';
324
+ }
325
+ } else {
326
+
327
+ // This set of code is not in core.
328
+ $gmt_offset = get_option( 'gmt_offset', 0 );
329
+ if ( ! empty( $gmt_offset ) ) {
330
+ $timezone_info['gmt_offset'] = $gmt_offset;
331
+ }
332
+
333
+ // Not sure why WordPress is doing this. If it is -4.5, show me -4.5 and not -4.
334
+ // $formatted_gmt_offset = $this->format_gmt_offset( intval( $gmt_offset ) );
335
+ $formatted_gmt_offset = $gmt_offset;
336
+ $timezone_info['abbr'] = sprintf( 'UTC%s', $formatted_gmt_offset );
337
+
338
+ /* translators: %s: UTC offset */
339
+ $timezone_info['description'] = sprintf( __( 'Timezone is %s.' ), $timezone_info['abbr'] );
340
+ }
341
+
342
+ // This set of code is not in core.
343
+ $timezone_info['markup_timezone'] = sprintf( '<span title="%1$s">%2$s</span>', esc_attr( $timezone_info['description'] ), $timezone_info['abbr'] );
344
+ $timezone_info['markup_change'] = sprintf(
345
+ '<a href="%1$s" title="%3$s">%2$s</a>',
346
+ admin_url( 'options-general.php' ),
347
+ __( 'Change timezone', 'boldgrid-backup' ),
348
+ esc_attr( __( 'WordPress timezone settings can be adjusted within Settings &raquo; General', 'boldgrid-backup' ) )
349
+ );
350
+
351
+ return $timezone_info;
352
+ }
353
+
354
+ /**
355
+ * Init a time.
356
+ *
357
+ * @since 1.6.0
358
+ *
359
+ * @param int $time In seconds.
360
+ * @param string $type The type of type ( utc, local, -5)
361
+ */
362
+ public function init( $time, $type = 'utc' ) {
363
+ if ( empty( $time ) ) {
364
+ $this->reset();
365
+ return;
366
+ }
367
+
368
+ $gmt_offset = get_option( 'gmt_offset' );
369
+ $valid_gmt_offset = ! empty( $gmt_offset ) || is_numeric( $gmt_offset );
370
+
371
+ $this->local_timezone = 'UTC';
372
+
373
+ /*
374
+ * Determine our UTC time.
375
+ *
376
+ * By default, we'll assume the $time passed in is UTC, such as a file's
377
+ * mtime.
378
+ *
379
+ * If the time we have s from a php_zip archive, then the time is the
380
+ * server's local timezone. If we can get the server's timezone offset,
381
+ * we can then calculate UTC and the User's local time (set in WordPress).
382
+ */
383
+ $this->utc_time = $time;
384
+ if ( 'php_zip' === $type || 'local' === $type ) {
385
+ $server_offset = $this->get_server_offset();
386
+
387
+ // We can calculate UTC time with the server's timezone.
388
+ if ( is_numeric( $server_offset ) ) {
389
+ $this->utc_time = $time + ( -1 * $server_offset * HOUR_IN_SECONDS );
390
+ }
391
+ }
392
+
393
+ // Once we have the UTC time (above), we can calculate the user's local time.
394
+ if ( $valid_gmt_offset ) {
395
+ $this->local_time = $this->utc_time + ( $gmt_offset * HOUR_IN_SECONDS );
396
+ $this->local_timezone .= ' ' . $gmt_offset;
397
+ } else {
398
+ $this->local_time = $this->utc_time;
399
+ }
400
+ }
401
+
402
+ /**
403
+ * Reset the class.
404
+ *
405
+ * @since 1.6.0
406
+ */
407
+ public function reset() {
408
+ $this->local_time = null;
409
+ $this->local_timezone = null;
410
+ $this->utc_time = null;
411
+ }
412
+ }
admin/class-boldgrid-backup-admin-tools.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Tools class.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.4
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup Admin Tools Class.
17
+ *
18
+ * @since 1.5.4
19
+ */
20
+ class Boldgrid_Backup_Admin_Tools {
21
+
22
+ /**
23
+ * The core class object.
24
+ *
25
+ * @since 1.5.4
26
+ * @access private
27
+ * @var Boldgrid_Backup_Admin_Core
28
+ */
29
+ private $core;
30
+
31
+ /**
32
+ * Constructor.
33
+ *
34
+ * @since 1.5.4
35
+ *
36
+ * @param Boldgrid_Backup_Admin_Core $core Core class object.
37
+ */
38
+ public function __construct( $core ) {
39
+ $this->core = $core;
40
+ }
41
+
42
+ /**
43
+ * Render the tools page.
44
+ *
45
+ * @since 1.5.4
46
+ */
47
+ public function page() {
48
+ wp_enqueue_style( 'bglib-ui-css' );
49
+ wp_enqueue_script( 'bglib-ui-js' );
50
+ wp_enqueue_script( 'bglib-sticky' );
51
+
52
+ include BOLDGRID_BACKUP_PATH . '/admin/partials/boldgrid-backup-admin-tools.php';
53
+ }
54
+ }
admin/class-boldgrid-backup-admin-upload.php ADDED
@@ -0,0 +1,366 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The admin-specific core functionality of the plugin
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.2.2
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup admin upload class.
17
+ *
18
+ * @since 1.2.2
19
+ */
20
+ class Boldgrid_Backup_Admin_Upload {
21
+ /**
22
+ * The core class object.
23
+ *
24
+ * @since 1.2.2
25
+ * @access private
26
+ * @var Boldgrid_Backup_Admin_Core
27
+ */
28
+ private $core;
29
+
30
+ /**
31
+ * Constructor.
32
+ *
33
+ * @since 1.2.2
34
+ *
35
+ * @param Boldgrid_Backup_Admin_Core $core Core class object.
36
+ */
37
+ public function __construct( $core ) {
38
+ // Save the Boldgrid_Backup_Admin_Core object as a class property.
39
+ $this->core = $core;
40
+ }
41
+
42
+ /**
43
+ * Verify upload archive access and validate input.
44
+ *
45
+ * @since 1.2.2
46
+ *
47
+ * @see current_user_can() in wp-includes/capabilities.php
48
+ * @see wp_verify_nonce() in wp-includes/pluggable.php
49
+ * @see Boldgrid_Backup_Admin_Utility::translate_upload_error()
50
+ * @see Boldgrid_Backup_Admin_Backup_Dir::get()
51
+ *
52
+ * @return bool
53
+ */
54
+ public function verify_upload_access() {
55
+ // Verify capability.
56
+ if ( ! current_user_can( 'upload_files' ) ) {
57
+ // Display an error notice.
58
+ do_action(
59
+ 'boldgrid_backup_notice',
60
+ esc_html__( 'Security violation (not authorized).', 'boldgrid-backup' ),
61
+ 'notice notice-error is-dismissible'
62
+ );
63
+
64
+ return false;
65
+ }
66
+
67
+ // Verify the WordPress nonce.
68
+ if ( empty( $_POST['_wpnonce'] ) || ! wp_verify_nonce( $_POST['_wpnonce'], 'upload_archive_file' ) ) {
69
+ // Display an error notice.
70
+ do_action(
71
+ 'boldgrid_backup_notice',
72
+ esc_html__( 'Security violation (invalid nonce).', 'boldgrid-backup' ),
73
+ 'notice notice-error is-dismissible'
74
+ );
75
+
76
+ return false;
77
+ }
78
+
79
+ // Abort if upload was not sent from our form.
80
+ if ( empty( $_POST['uploading'] ) ) {
81
+ // Display an error notice.
82
+ do_action(
83
+ 'boldgrid_backup_notice',
84
+ esc_html__( 'Upload file was not send from the proper form.', 'boldgrid-backup' ),
85
+ 'notice notice-error is-dismissible'
86
+ );
87
+
88
+ return false;
89
+ }
90
+
91
+ // Abort if there is no file upload.
92
+ if ( empty( $_FILES['file'] ) ) {
93
+ // Display an error notice.
94
+ do_action(
95
+ 'boldgrid_backup_notice',
96
+ esc_html__( 'File upload error. Please try again.', 'boldgrid-backup' ),
97
+ 'notice notice-error is-dismissible'
98
+ );
99
+
100
+ return false;
101
+ }
102
+
103
+ // Abort if the files was not uploaded via HTTP POST.
104
+ if ( ! is_uploaded_file( $_FILES['file']['tmp_name'] ) ) {
105
+ // Display an error notice.
106
+ do_action(
107
+ 'boldgrid_backup_notice',
108
+ esc_html__( 'File upload error. Please try again.', 'boldgrid-backup' ),
109
+ 'notice notice-error is-dismissible'
110
+ );
111
+
112
+ return false;
113
+ }
114
+
115
+ // Abort with a notice if there was an upload error.
116
+ if ( $_FILES['file']['error'] ) {
117
+ // Display an error notice.
118
+ do_action(
119
+ 'boldgrid_backup_notice',
120
+ Boldgrid_Backup_Admin_Utility::translate_upload_error( $_FILES['file']['error'] ),
121
+ 'notice notice-error is-dismissible'
122
+ );
123
+
124
+ return false;
125
+ }
126
+
127
+ // Get the backup directory.
128
+ $backup_directory = $this->core->backup_dir->get();
129
+
130
+ // Abort if the backup directory is not configured.
131
+ if ( empty( $backup_directory ) ) {
132
+ // Display an error notice.
133
+ do_action(
134
+ 'boldgrid_backup_notice',
135
+ esc_html__( 'The backup directory is not configured.', 'boldgrid-backup' ),
136
+ 'notice notice-error is-dismissible'
137
+ );
138
+
139
+ return false;
140
+ }
141
+
142
+ return true;
143
+ }
144
+
145
+ /**
146
+ * Bump upload limits.
147
+ *
148
+ * Set PHP INI runtime settings to allow an upload up to the specified amount.
149
+ * Default is 1G.
150
+ *
151
+ * @since 1.2.2
152
+ *
153
+ * @see Boldgrid_Backup_Admin_Utility::bump_upload_limit()
154
+ * @see Boldgrid_Backup_Admin_Utility::bump_memory_limit()
155
+ * @see Boldgrid_Backup_Admin_Utility::bump_max_execution()
156
+ *
157
+ * @param string $limit A php.ini style string.
158
+ */
159
+ public function bump_upload_limits( $limit = '1G' ) {
160
+ Boldgrid_Backup_Admin_Utility::bump_upload_limit( $limit );
161
+ Boldgrid_Backup_Admin_Utility::bump_memory_limit( $limit );
162
+ Boldgrid_Backup_Admin_Utility::bump_max_execution( '0' );
163
+ }
164
+
165
+ /**
166
+ * Check filetype and extension.
167
+ *
168
+ * @since 1.2.2
169
+ *
170
+ * @see wp_check_filetype_and_ext() in wp-includes/functions.php
171
+ *
172
+ * @return bool
173
+ */
174
+ public function check_filetype_ext() {
175
+ // Validate input.
176
+ if ( empty( $_FILES['file'] ) ) {
177
+ return false;
178
+ }
179
+
180
+ // Get the upload file basename.
181
+ $file_basename = basename( $_FILES['file']['name'] );
182
+
183
+ // Validate the filename and mime type.
184
+ $validate = wp_check_filetype_and_ext( $_FILES['file']['tmp_name'], $file_basename );
185
+
186
+ // Abort if the file is an incorrect extension.
187
+ // Currently only "zip"; others to be added in the future.
188
+ // @todo Write a method to get the allowed file extensions, based on available compressors.
189
+ $allowed_file_ext = array(
190
+ 'zip'
191
+ );
192
+
193
+ if ( ! in_array( $validate['ext'], $allowed_file_ext, true ) ) {
194
+ // Display an error notice.
195
+ do_action(
196
+ 'boldgrid_backup_notice',
197
+ sprintf(
198
+ esc_html__(
199
+ 'Upload file extension type %s is not allowed.',
200
+ 'boldgrid-backup'
201
+ ),
202
+ ( ! empty( $validate['ext'] ) ? '"' . $validate['ext'] . '"' : '' )
203
+ ),
204
+ 'notice notice-error is-dismissible'
205
+ );
206
+
207
+ return false;
208
+ }
209
+
210
+ return true;
211
+ }
212
+
213
+ /**
214
+ * Get file save path and update the base filename.
215
+ *
216
+ * @since 1.2.2
217
+ *
218
+ * @see Boldgrid_Backup_Admin_Config::get_backup_identifier()
219
+ * @see Boldgrid_Backup_Admin_Backup_Dir::get()
220
+ *
221
+ * @return string The file save path.
222
+ */
223
+ public function get_save_path() {
224
+ // Get the upload file basename.
225
+ $file_basename = basename( $_FILES['file']['name'] );
226
+
227
+ // Get backup identifier.
228
+ $backup_identifier = $this->core->get_backup_identifier();
229
+
230
+ // Get the backup directory.
231
+ $backup_directory = $this->core->backup_dir->get();
232
+
233
+ // Create an array of strings to remove from the filename.
234
+ $remove_strings = array(
235
+ 'boldgrid-backup-',
236
+ $backup_identifier,
237
+ 'uploaded-',
238
+ );
239
+
240
+ // Remove references from filename.
241
+ foreach ( $remove_strings as $remove_string ) {
242
+ $file_basename = str_replace( $remove_string, '', $file_basename );
243
+ }
244
+
245
+ // Reformat the filename.
246
+ $file_basename = 'boldgrid-backup-' . $backup_identifier . '-uploaded-' . $file_basename;
247
+
248
+ // Remove extra dashes.
249
+ $file_basename = preg_replace( '#-+#', '-', $file_basename );
250
+
251
+ // Create the file save path.
252
+ $file_save_path = $backup_directory . DIRECTORY_SEPARATOR . $file_basename;
253
+
254
+ // Update the base filename.
255
+ $_FILES['file']['name'] = $file_basename;
256
+
257
+ return $file_save_path;
258
+ }
259
+
260
+ /**
261
+ * Handle upload.
262
+ *
263
+ * @since 1.2.2
264
+ *
265
+ * @see wp_handle_upload() in wp-admin/includes/file.php
266
+ *
267
+ * @return array The results of wp_handle_upload().
268
+ */
269
+ public function handle_upload() {
270
+ // Ensure that "wp-admin/includes/file.php" is loaded for wp_handle_upload().
271
+ if ( ! function_exists( 'wp_handle_upload' ) ) {
272
+ require_once ABSPATH . 'wp-admin/includes/file.php';
273
+ }
274
+
275
+ // Use wp_handle_upload() (with overrides and custom options), to perform the actual upload.
276
+ $upload_overrides = array(
277
+ 'test_form' => false,
278
+ );
279
+
280
+ // Add a filter to specify a custom upload directory (the backup directory).
281
+ add_filter( 'upload_dir',
282
+ array(
283
+ $this->core->config,
284
+ 'custom_upload_dir',
285
+ )
286
+ );
287
+
288
+ $movefile = wp_handle_upload( $_FILES['file'], $upload_overrides );
289
+
290
+ // Remove the temporary filter for a custom upload directory.
291
+ remove_filter( 'upload_dir',
292
+ array(
293
+ $this->core->config,
294
+ 'custom_upload_dir',
295
+ )
296
+ );
297
+
298
+ return $movefile;
299
+ }
300
+
301
+ /**
302
+ * Upload archive file.
303
+ *
304
+ * The index used for $_FILES should be "file".
305
+ *
306
+ * @since 1.2.2
307
+ *
308
+ * @see Boldgrid_Backup_Admin_Upload::verify_upload_access()
309
+ * @see Boldgrid_Backup_Admin_Upload::bump_limits()
310
+ *
311
+ * @return bool Success of the operation.
312
+ */
313
+ public function upload_archive_file() {
314
+ // Verify upload archive access and validate input.
315
+ if ( ! $this->verify_upload_access() ) {
316
+ return false;
317
+ }
318
+
319
+ // Close any PHP session, so another session can open during the upload.
320
+ session_write_close();
321
+
322
+ // Set PHP INI runtime settings to allow an upload up to 1G.
323
+ $this->bump_upload_limits( '1G' );
324
+
325
+ // Validate the filename and mime type.
326
+ if ( ! $this->check_filetype_ext() ) {
327
+ return false;
328
+ }
329
+
330
+ // Create the file save path, and update the destination base filename..
331
+ $file_save_path = $this->get_save_path();
332
+
333
+ // Handle the upload.
334
+ $movefile = $this->handle_upload();
335
+
336
+ // Determine success and produce and admin notice.
337
+ if ( $movefile && ! isset( $movefile['error'] ) ) {
338
+ // Modify the archive file permissions to help protect from public access.
339
+ Boldgrid_Backup_Admin_Utility::chmod( $file_save_path, 0600 );
340
+
341
+ // Display an success notice.
342
+ do_action(
343
+ 'boldgrid_backup_notice',
344
+ esc_html__( 'Upload successful.', 'boldgrid-backup' ),
345
+ 'notice notice-success is-dismissible'
346
+ );
347
+
348
+ // Enforce retention setting.
349
+ $this->core->enforce_retention();
350
+
351
+ return true;
352
+ } else {
353
+ // Display an error notice.
354
+ do_action(
355
+ 'boldgrid_backup_notice',
356
+ sprintf(
357
+ esc_html__( 'Upload has failed; %s.', 'boldgrid-backup' ),
358
+ $movefile['error']
359
+ ),
360
+ 'notice notice-error is-dismissible'
361
+ );
362
+
363
+ return false;
364
+ }
365
+ }
366
+ }
admin/class-boldgrid-backup-admin-utility.php ADDED
@@ -0,0 +1,866 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The admin-specific utility methods for the plugin
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.0
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup admin utility class.
17
+ *
18
+ * @since 1.0
19
+ */
20
+ class Boldgrid_Backup_Admin_Utility {
21
+ /**
22
+ * Convert bytes to a human-readable measure.
23
+ *
24
+ * @since 1.0
25
+ *
26
+ * @static
27
+ *
28
+ * @param int $bytes Number of bytes.
29
+ * @param int $decimals Number of decimal places.
30
+ * @return string
31
+ */
32
+ public static function bytes_to_human( $bytes = 0, $decimals = 2 ) {
33
+ // If $bytes is not a number, then fail.
34
+ if ( ! is_numeric( $bytes ) ) {
35
+ return 'INVALID';
36
+ }
37
+
38
+ // Ensure the $decimals is an integer.
39
+ $decimals = (int) $decimals;
40
+
41
+ $type = array(
42
+ 'B',
43
+ 'KB',
44
+ 'MB',
45
+ 'GB',
46
+ 'TB',
47
+ 'PB',
48
+ 'EB',
49
+ 'ZB',
50
+ 'YB',
51
+ );
52
+
53
+ $index = 0;
54
+
55
+ while ( $bytes >= 1024 ) {
56
+ $bytes /= 1024;
57
+ $index ++;
58
+ }
59
+
60
+ $return = number_format( $bytes, $decimals, '.', '' ) . ' ' . $type[ $index ];
61
+
62
+ $return = str_replace( '.00', '', $return );
63
+
64
+ return $return;
65
+ }
66
+
67
+ /**
68
+ * Create a site identifier.
69
+ *
70
+ * @since 1.0
71
+ *
72
+ * @static
73
+ *
74
+ * @return string The site identifier.
75
+ */
76
+ public static function create_site_id() {
77
+ // Get the siteurl.
78
+ if ( is_multisite() ) {
79
+ // Use the siteurl from blog id 1.
80
+ $siteurl = get_site_url( 1 );
81
+ } else {
82
+ // Get the current siteurl.
83
+ $siteurl = get_site_url();
84
+ }
85
+
86
+ // Make an identifier.
87
+ $site_id = explode( '/', $siteurl );
88
+ unset( $site_id[0] );
89
+ unset( $site_id[1] );
90
+ $site_id = implode( '_', $site_id );
91
+
92
+ return $site_id;
93
+ }
94
+
95
+ /**
96
+ * Custom error handler.
97
+ *
98
+ * Catches everything (including warnings) and throws an excpetion.
99
+ *
100
+ * Can be used in this manner:
101
+ * set_error_handler( array( 'Boldgrid_Backup_Admin_Utility', 'handle_error' ) );
102
+ * try{
103
+ * // Try something
104
+ * } catch( Exception $e ) {
105
+ $e->getMessage();
106
+ * }
107
+ * restore_error_handler();
108
+ *
109
+ * @since 1.6.0
110
+ *
111
+ * @param int $errno Error number. (can be a PHP Error level constant)
112
+ * @param string $errstr Error description.
113
+ * @param string $errfile File in which the error occurs.
114
+ * @param int $errline Line number where the error is situated.
115
+ */
116
+ public static function handle_error( $errno, $errstr, $errfile = false, $errline = false ) {
117
+
118
+ // A set of errors to ignore.
119
+ $skips = array(
120
+
121
+ /*
122
+ * Ignore mcrypt errors (DEPRECATED as of PHP 7.1.0).
123
+ *
124
+ * When using phpseclib for sftp, we're catching these warnings even
125
+ * though the author used @suppression with their mcrypt calls. There's
126
+ * a lot of information online about these errors within phpseclib,
127
+ * but I'll reference the following:
128
+ *
129
+ * https://github.com/phpseclib/phpseclib/issues/1028
130
+ * # mcrypt is only used if it's available. If mcrypt is not available
131
+ * either a pure-PHP implementation is used or OpenSSL is used. The
132
+ * prioritization is as follows: OpenSSL > mcrypt > pure-PHP. mcrypt
133
+ * and OpenSSL are loads faster than the pure-PHP implementation.
134
+ * # So mcrypt offers a 45x speedup over the internal mode. OpenSSL
135
+ * offers a 6.5x speedup over mcrypt.
136
+ *
137
+ * https://github.com/phpseclib/phpseclib/issues/1229
138
+ * # phpseclib (all branches) are unit tested on PHP 7.2:
139
+ * https://travis-ci.org/phpseclib/phpseclib
140
+ * They all pass in spite of using mcrypt. idk if you've ever used
141
+ * Travis CI / phpunit but an E_DEPRECATED notice will result in a
142
+ * failing unit test and yet the unit tests are all passing.
143
+ */
144
+ 'Function mcrypt_list_algorithms() is deprecated',
145
+ 'Function mcrypt_module_open() is deprecated',
146
+ 'Function mcrypt_generic_init() is deprecated',
147
+ 'Function mcrypt_generic() is deprecated',
148
+ 'Function mdecrypt_generic() is deprecated',
149
+ );
150
+
151
+ if ( in_array( $errstr, $skips, true ) ) {
152
+ return;
153
+ }
154
+
155
+ throw new ErrorException( $errstr, 0, $errno, $errfile, $errline );
156
+ }
157
+
158
+ /**
159
+ * Translate a ZipArchive error code into a human-readable message.
160
+ *
161
+ * @since 1.0
162
+ *
163
+ * @static
164
+ *
165
+ * @param int $error_code An error code from a ZipArchive constant.
166
+ * @return string An error message.
167
+ */
168
+ public static function translate_zip_error( $error_code = null ) {
169
+ switch ( $error_code ) {
170
+ case ZipArchive::ER_EXISTS :
171
+ $message = esc_html__( 'File already exists', 'boldgrid-backup' );
172
+ break;
173
+ case ZipArchive::ER_INCONS :
174
+ $message = esc_html__( 'Zip archive inconsistent', 'boldgrid-backup' );
175
+ break;
176
+ case ZipArchive::ER_INVAL :
177
+ $message = esc_html__( 'Invalid argument', 'boldgrid-backup' );
178
+ break;
179
+ case ZipArchive::ER_MEMORY :
180
+ $message = esc_html__( 'Malloc failure', 'boldgrid-backup' );
181
+ break;
182
+ case ZipArchive::ER_NOENT :
183
+ $message = esc_html__( 'No such file', 'boldgrid-backup' );
184
+ break;
185
+ case ZipArchive::ER_NOZIP :
186
+ $message = esc_html__( 'Not a zip archive', 'boldgrid-backup' );
187
+ break;
188
+ case ZipArchive::ER_OPEN :
189
+ $message = esc_html__( 'Cannot open file', 'boldgrid-backup' );
190
+ break;
191
+ case ZipArchive::ER_READ :
192
+ $message = esc_html__( 'Read error', 'boldgrid-backup' );
193
+ break;
194
+ case ZipArchive::ER_SEEK :
195
+ $message = esc_html__( 'Seek error', 'boldgrid-backup' );
196
+ break;
197
+ default :
198
+ $message = esc_html__( 'No error code was passed', 'boldgrid-backup' );
199
+ break;
200
+ }
201
+
202
+ return $message;
203
+ }
204
+
205
+ /**
206
+ * Translate a file upload error code into a human-readable message.
207
+ *
208
+ * @since 1.2.2
209
+ *
210
+ * @static
211
+ *
212
+ * @see http://php.net/manual/en/features.file-upload.errors.php
213
+ *
214
+ * @param int $error_code An error code from a file upload error constant.
215
+ * @return string An error message.
216
+ */
217
+ public static function translate_upload_error( $error_code ) {
218
+ switch ( $error_code ) {
219
+ case UPLOAD_ERR_INI_SIZE:
220
+ $message = esc_html__(
221
+ 'The uploaded file exceeds the upload_max_filesize directive in php.ini',
222
+ 'boldgrid-backup'
223
+ );
224
+ break;
225
+
226
+ case UPLOAD_ERR_FORM_SIZE:
227
+ $message = esc_html__(
228
+ 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form',
229
+ 'boldgrid-backup'
230
+ );
231
+ break;
232
+
233
+ case UPLOAD_ERR_PARTIAL:
234
+ $message = esc_html__(
235
+ 'The uploaded file was only partially uploaded',
236
+ 'boldgrid-backup'
237
+ );
238
+ break;
239
+
240
+ case UPLOAD_ERR_NO_FILE:
241
+ $message = esc_html__(
242
+ 'No file was uploaded',
243
+ 'boldgrid-backup'
244
+ );
245
+
246
+ break;
247
+
248
+ case UPLOAD_ERR_NO_TMP_DIR:
249
+ $message = esc_html__(
250
+ 'Missing a temporary folder',
251
+ 'boldgrid-backup'
252
+ );
253
+ break;
254
+
255
+ case UPLOAD_ERR_CANT_WRITE:
256
+ $message = esc_html__(
257
+ 'Failed to write file to disk',
258
+ 'boldgrid-backup'
259
+ );
260
+ break;
261
+
262
+ case UPLOAD_ERR_EXTENSION:
263
+ $message = esc_html__(
264
+ 'File upload stopped by extension',
265
+ 'boldgrid-backup'
266
+ );
267
+ break;
268
+
269
+ default:
270
+ $message = esc_html(
271
+ 'Unknown upload error',
272
+ 'boldgrid-backup'
273
+ );
274
+ break;
275
+ }
276
+ return $message;
277
+ }
278
+
279
+ /**
280
+ * Make a directory or file writable, if exists.
281
+ *
282
+ * @since 1.0
283
+ *
284
+ * @global WP_Filesystem $wp_filesystem The WordPress Filesystem API global object.
285
+ *
286
+ * @static
287
+ *
288
+ * @param string $filepath A path to a directory or file.
289
+ * @return bool Success.
290
+ */
291
+ public static function make_writable( $filepath ) {
292
+ // Validate file path string.
293
+ $filepath = realpath( $filepath );
294
+
295
+ if ( empty( $filepath ) ) {
296
+ return true;
297
+ }
298
+
299
+ // Connect to the WordPress Filesystem API.
300
+ global $wp_filesystem;
301
+
302
+ // If path exists and is not writable, then make writable.
303
+ if ( $wp_filesystem->exists( $filepath ) ) {
304
+ if ( ! $wp_filesystem->is_writable( $filepath ) ) {
305
+ if ( $wp_filesystem->is_dir( $filepath ) ) {
306
+ // Is a directory.
307
+ if ( ! $wp_filesystem->chmod( $filepath, 0755 ) ) {
308
+ // Error chmod 755 a directory.
309
+ error_log(
310
+ __METHOD__ . ': Error using chmod 0755 on directory "' . $filepath . '".'
311
+ );
312
+
313
+ return false;
314
+ }
315
+ } else {
316
+ // Is a file.
317
+ if ( ! $wp_filesystem->chmod( $filepath, 0644 ) ) {
318
+ // Error chmod 644 a file.
319
+ error_log(
320
+ __METHOD__ . ': Error using chmod 0644 on file "' . $filepath . '".'
321
+ );
322
+
323
+ return false;
324
+ }
325
+ }
326
+ }
327
+ }
328
+
329
+ return true;
330
+ }
331
+
332
+ /**
333
+ * Increase the PHP max execution time.
334
+ *
335
+ * @since 1.2.2
336
+ *
337
+ * @static
338
+ *
339
+ * @link http://php.net/manual/en/info.configuration.php#ini.max-execution-time
340
+ *
341
+ * @param string $max_execution_time A php.ini style max_execution_time.
342
+ * @return bool Success of the operation.
343
+ */
344
+ public static function bump_max_execution( $max_execution_time ) {
345
+ // Abort if in safe mode or max_execution_time is not changable.
346
+ if ( ini_get( 'safe_mode' ) || ! wp_is_ini_value_changeable( 'max_execution_time' ) ) {
347
+ return false;
348
+ }
349
+
350
+ // Validate input max_execution_time.
351
+ if ( ! is_numeric( $max_execution_time ) || $max_execution_time < 0 ) {
352
+ return false;
353
+ }
354
+
355
+ // Get the current max execution time set for PHP.
356
+ $current_max = ini_get( 'max_execution_time' );
357
+
358
+ // If the current max execution time is less than specified, then try to increase it.
359
+ // PHP default is "30".
360
+ if ( $current_max < $max_execution_time ) {
361
+ set_time_limit( $max_execution_time );
362
+
363
+ if ( false === ini_set( 'max_execution_time', $max_execution_time ) ) {
364
+ return false;
365
+ }
366
+ }
367
+
368
+ return true;
369
+ }
370
+
371
+ /**
372
+ * Get plugin data.
373
+ *
374
+ * This is a wrapper function for WordPress' get_plugin_data function,
375
+ * which requires the full path to a plugin. This method only requires
376
+ * folder/file.php
377
+ *
378
+ * @since 1.5.3
379
+ *
380
+ * @param string $plugin boldgrid-backup/boldgrid-backup.php
381
+ * @return array
382
+ */
383
+ public function get_plugin_data( $plugin ) {
384
+ $path = dirname( BOLDGRID_BACKUP_PATH ) . DIRECTORY_SEPARATOR . $plugin;
385
+ $data = get_plugin_data( $path );
386
+ return $data;
387
+ }
388
+
389
+ /**
390
+ * Get the file upload limit.
391
+ *
392
+ * @since 1.2.2
393
+ *
394
+ * @static
395
+ *
396
+ * @see wp_convert_hr_to_bytes() in wp-includes/load.php
397
+ * @link http://php.net/manual/en/ini.core.php#ini.post-max-size
398
+ * @link http://php.net/manual/en/ini.core.php#ini.upload-max-filesize
399
+ *
400
+ * @return int The upload/post limit in bytes.
401
+ */
402
+ public static function get_upload_limit() {
403
+ // Get PHP setting value for post_max_size.
404
+ // PHP default is "8M".
405
+ $post_max_size = wp_convert_hr_to_bytes( ini_get( 'post_max_size' ) );
406
+
407
+ // Get PHP setting value for upload_max_filesize.
408
+ // PHP default is "2M".
409
+ $upload_max_filesize = wp_convert_hr_to_bytes( ini_get( 'upload_max_filesize' ) );
410
+
411
+ // Determine the minimum value.
412
+ $min = min( $post_max_size, $upload_max_filesize );
413
+
414
+ // Return the resulting minimum value (int in bytes).
415
+ return $min;
416
+ }
417
+
418
+ /**
419
+ * Increase the PHP memory limit.
420
+ *
421
+ * @since 1.2.2
422
+ *
423
+ * @static
424
+ *
425
+ * @see wp_is_ini_value_changeable() in wp-includes/default-constants.php
426
+ * @see wp_convert_hr_to_bytes() in wp-includes/load.php
427
+ * @link http://php.net/manual/en/ini.core.php#ini.memory-limit
428
+ *
429
+ * @param string $memory_limit A php.ini style memory_limit string.
430
+ * @return bool Success of the operation.
431
+ */
432
+ public static function bump_memory_limit( $memory_limit ) {
433
+ // Abort if in safe mode or memory_limit is not changable.
434
+ if ( ini_get( 'safe_mode' ) || ! wp_is_ini_value_changeable( 'memory_limit' ) ) {
435
+ return false;
436
+ }
437
+
438
+ // Convert memory limit string to an integer in bytes.
439
+ $memory_limit_int = wp_convert_hr_to_bytes( $memory_limit );
440
+
441
+ // Get the current upload max filesize set for PHP.
442
+ $current_limit_int = wp_convert_hr_to_bytes( ini_get( 'memory_limit' ) );
443
+
444
+ // Apply a WordPress filter to help ensure the setting.
445
+ apply_filters( 'admin_memory_limit', $memory_limit_int );
446
+
447
+ // If the current memory limit is less than specified, then try to increase it.
448
+ // PHP default is "128M".
449
+ if ( $current_limit_int < $memory_limit_int ) {
450
+ if ( false === ini_set( 'memory_limit', $memory_limit_int ) ) {
451
+ return false;
452
+ }
453
+ }
454
+
455
+ return true;
456
+ }
457
+
458
+ /**
459
+ * Attempt to increase the PHP max upload size.
460
+ *
461
+ * The upload_max_filesize is set as "PHP_INI_PERDIR";
462
+ * The entry can be set in "php.ini", ".htaccess", "httpd.conf" or ".user.ini".
463
+ * We can attempt to set it to a higher limit via a filter, as WordPress may have previously
464
+ * reduced it.
465
+ *
466
+ * @since 1.2.2
467
+ *
468
+ * @static
469
+ *
470
+ * @see wp_convert_hr_to_bytes() in wp-includes/load.php
471
+ * @link http://php.net/manual/en/ini.sect.safe-mode.php#ini.safe-mode
472
+ * @link http://php.net/manual/en/ini.core.php#ini.file-uploads
473
+ * @link http://php.net/manual/en/ini.core.php#ini.max-file-uploads
474
+ *
475
+ * @param string $max_filesize A php.ini style upload_max_filesize string.
476
+ * @return bool Success of the operation.
477
+ */
478
+ public static function bump_upload_limit( $max_filesize ) {
479
+ // Abort if in safe mode.
480
+ if ( ini_get( 'safe_mode' ) ) {
481
+ return false;
482
+ }
483
+
484
+ // Abort if file_uploads is "0" (disabled).
485
+ // PHP default is "1" (enabled).
486
+ if ( ! ini_get( 'file_uploads' ) ) {
487
+ return false;
488
+ }
489
+
490
+ // Abort if max_file_uploads is "0" (disabled).
491
+ // PHP default is "20".
492
+ if ( ! ini_get( 'max_file_uploads' ) ) {
493
+ return false;
494
+ }
495
+
496
+ // Convert upload max filesize string to an integer in bytes.
497
+ $max_filesize_int = wp_convert_hr_to_bytes( $max_filesize );
498
+
499
+ // Apply a WordPress filter to help ensure the setting.
500
+ apply_filters( 'upload_size_limit', $max_filesize_int, $max_filesize_int, $max_filesize_int );
501
+
502
+ return true;
503
+ }
504
+
505
+ /**
506
+ * Check if a file is a ZIP archive file.
507
+ *
508
+ * @since 1.2.2
509
+ *
510
+ * @static
511
+ *
512
+ * @see get_filesystem_method() in wp-admin/includes/file.php
513
+ *
514
+ * @param string $file A file path to be checked.
515
+ * @return bool
516
+ */
517
+ public static function is_zip_file( $file ) {
518
+ // Validate input filename.
519
+ if ( empty( $file ) ) {
520
+ return false;
521
+ }
522
+
523
+ // Create a ZipArchive object.
524
+ $zip = new ZipArchive;
525
+
526
+ // Check the ZIP file for consistency.
527
+ $status = $zip->open( $file, ZipArchive::CHECKCONS );
528
+
529
+ // Close the ZIP file.
530
+ $zip->close();
531
+
532
+ // Check the result.
533
+ $result = ( true === $status );
534
+
535
+ // Return the result.
536
+ return $result;
537
+ }
538
+
539
+ /**
540
+ * Check if a specific file exists in a ZIP archive.
541
+ *
542
+ * @since 1.2.2
543
+ *
544
+ * @static
545
+ *
546
+ * @link http://php.net/manual/en/class.ziparchive.php
547
+ *
548
+ * @param string $zip_file Path to a ZIP file.
549
+ * @param string $locate_file A filename or path to be located.
550
+ * @param bool $is_path Is the input file a path.
551
+ * @return bool
552
+ */
553
+ public static function zip_file_exists( $zip_file, $locate_file, $is_path = false ) {
554
+ // Validate input parameters.
555
+ if ( empty( $zip_file ) || empty( $locate_file ) ) {
556
+ return false;
557
+ }
558
+
559
+ // Create a ZipArchive object.
560
+ $zip = new ZipArchive;
561
+
562
+ // Check the ZIP file for consistency.
563
+ $status = $zip->open( $zip_file, ZipArchive::CHECKCONS );
564
+
565
+ if ( true !== $status ) {
566
+ // Invalid ZIP file.
567
+ return false;
568
+ }
569
+
570
+ // Locate the filename or path.
571
+ if ( $is_path ) {
572
+ $index = $zip->locateName( $locate_file );
573
+ } else {
574
+ $index = $zip->locateName( $locate_file, ZipArchive::FL_NODIR );
575
+ }
576
+
577
+ // Close the ZIP file.
578
+ $zip->close();
579
+
580
+ // Return the result.
581
+ return (bool) $index;
582
+ }
583
+
584
+ /**
585
+ * Chmod a directory or file.
586
+ *
587
+ * @since 1.2.2
588
+ *
589
+ * @global WP_Filesystem $wp_filesystem The WordPress Filesystem API global object.
590
+ *
591
+ * @param string $file Path to a directory or file.
592
+ * @param int $mode (Optional) The permissions as octal number, usually 0644 for files, 0755 for dirs.
593
+ * @return bool
594
+ */
595
+ public static function chmod( $file, $mode = false ) {
596
+ // Connect to the WordPress Filesystem API.
597
+ global $wp_filesystem;
598
+
599
+ // Modify the file permissions.
600
+ $result = $wp_filesystem->chmod( $file, $mode );
601
+
602
+ // Return the result.
603
+ return $result;
604
+ }
605
+
606
+ /**
607
+ * Fix wp-config.php file.
608
+ *
609
+ * If restoring "wp-config.php", then ensure that the credentials remain intact.
610
+ *
611
+ * @since 1.2.2
612
+ *
613
+ * @see http://us1.php.net/manual/en/function.preg-replace.php#103985
614
+ * @global WP_Filesystem $wp_filesystem The WordPress Filesystem API global object.
615
+ *
616
+ * @static
617
+ *
618
+ * @return bool
619
+ */
620
+ public static function fix_wpconfig() {
621
+ // Connect to the WordPress Filesystem API.
622
+ global $wp_filesystem;
623
+
624
+ // Set the file path.
625
+ $file = ABSPATH . 'wp-config.php';
626
+
627
+ // Abort if the file does not exist.
628
+ if ( ! $wp_filesystem->exists( $file ) ) {
629
+ return false;
630
+ }
631
+
632
+ // Get the file contents.
633
+ $file_contents = $wp_filesystem->get_contents( $file );
634
+
635
+ // Create an array containing the definition names to replace.
636
+ $definitions = array(
637
+ 'DB_NAME',
638
+ 'DB_USER',
639
+ 'DB_PASSWORD',
640
+ 'DB_HOST',
641
+ 'AUTH_KEY',
642
+ 'SECURE_AUTH_KEY',
643
+ 'LOGGED_IN_KEY',
644
+ 'NONCE_KEY',
645
+ 'AUTH_SALT',
646
+ 'SECURE_AUTH_SALT',
647
+ 'LOGGED_IN_SALT',
648
+ 'NONCE_SALT',
649
+ );
650
+
651
+ // Replace the definitions.
652
+ foreach ( $definitions as $definition ) {
653
+ // If the definition does not exist, then skip it.
654
+ if ( ! defined( $definition ) ) {
655
+ continue;
656
+ }
657
+
658
+ // Replace $n ($0-$99) backreferences before preg_replace.
659
+ // @see http://us1.php.net/manual/en/function.preg-replace.php#103985 .
660
+ $value = preg_replace( '/\$(\d)/', '\\\$$1', constant( $definition ) );
661
+
662
+ // Replace definition.
663
+ $file_contents = preg_replace(
664
+ '#define.*?' . $definition . '.*;#',
665
+ "define('" . $definition . "', '" . $value . "');",
666
+ $file_contents,
667
+ 1
668
+ );
669
+
670
+ // If there was a failure, then abort.
671
+ if ( null === $file_contents ) {
672
+ return false;
673
+ }
674
+ }
675
+
676
+ // Write the changes to file.
677
+ $wp_filesystem->put_contents( $file, $file_contents, 0600 );
678
+
679
+ return true;
680
+ }
681
+
682
+ /**
683
+ * Replace the siteurl in the WordPress database.
684
+ *
685
+ * @since 1.2.3
686
+ *
687
+ * @see Boldgrid_Backup_Admin_Utility::str_replace_recursive()
688
+ * @global wpdb $wpdb The WordPress database class object.
689
+ *
690
+ * @static
691
+ *
692
+ * @param string $old_siteurl The old/restored siteurl to find and be replaced.
693
+ * @param string $new_siteurl The siteurl to replace the old siteurl.
694
+ * @return bool
695
+ */
696
+ public static function update_siteurl( $old_siteurl, $new_siteurl ) {
697
+ // Define filter options.
698
+ $filter_options = FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_HOST_REQUIRED;
699
+
700
+ // Validate the old siteurl.
701
+ if ( false === filter_var( $old_siteurl, FILTER_VALIDATE_URL, $filter_options ) ) {
702
+ return false;
703
+ }
704
+
705
+ // Validate the new siteurl.
706
+ if ( false === filter_var( $new_siteurl, FILTER_VALIDATE_URL, $filter_options ) ) {
707
+ return false;
708
+ }
709
+
710
+ // Ensure there are no trailing slashes in siteurl.
711
+ $old_siteurl = untrailingslashit( $old_siteurl );
712
+ $new_siteurl = untrailingslashit( $new_siteurl );
713
+
714
+ // Update the WP otion "siteurl".
715
+ update_option( 'siteurl', $new_siteurl );
716
+
717
+ // Connect to the WordPress database via $wpdb.
718
+ global $wpdb;
719
+
720
+ // Get the database prefix (blog id 1 or 0 gets the base prefix).
721
+ $db_prefix = $wpdb->get_blog_prefix( 1 );
722
+
723
+ // Replace the URL in wp_posts.
724
+ $wpdb->query(
725
+ $wpdb->prepare(
726
+ 'UPDATE `%1$sposts` SET `post_content` = REPLACE( `post_content`, \'%2$s\', \'%3$s\' ) WHERE `post_content` LIKE \'%%%2$s%%\';',
727
+ array(
728
+ $db_prefix,
729
+ $old_siteurl,
730
+ $new_siteurl,
731
+ )
732
+ )
733
+ );
734
+
735
+ // Check if the upload_url_path needs to be updated.
736
+ $upload_url_path = get_option( 'upload_url_path' );
737
+
738
+ if ( ! empty( $upload_url_path ) ) {
739
+ $upload_url_path = str_replace( $old_siteurl, $new_siteurl, $upload_url_path );
740
+
741
+ update_option( 'upload_url_path', $upload_url_path );
742
+ }
743
+
744
+ // Find old siteurl references in WP options.
745
+ // Match old_siteurl with and without escaped URL, for example, JSON data escapes slashes.
746
+ $matched_options = $wpdb->get_results(
747
+ $wpdb->prepare(
748
+ 'SELECT `option_name` FROM `%1$soptions` WHERE `option_value` LIKE \'%%%2$s%%\' OR `option_value` LIKE \'%%%3$s%%\';',
749
+ array(
750
+ $db_prefix,
751
+ $old_siteurl,
752
+ addslashes( $old_siteurl ),
753
+ )
754
+ ),
755
+ ARRAY_N
756
+ );
757
+
758
+ // If there are no matches options, then return.
759
+ if ( ! $matched_options ) {
760
+ return true;
761
+ }
762
+
763
+ // Replace the siteurl in matched options.
764
+ foreach ( $matched_options as $option_name ) {
765
+ $option_value = get_option( $option_name[0] );
766
+
767
+ // Replace siteurl.
768
+ $option_value = Boldgrid_Backup_Admin_Utility::str_replace_recursive(
769
+ $old_siteurl,
770
+ $new_siteurl,
771
+ $option_value
772
+ );
773
+
774
+ // Replace siteurl escaped with slashes.
775
+ $option_value = Boldgrid_Backup_Admin_Utility::str_replace_recursive(
776
+ addslashes( $old_siteurl ),
777
+ addslashes( $new_siteurl ),
778
+ $option_value
779
+ );
780
+
781
+ update_option( $option_name[0], $option_value );
782
+ }
783
+
784
+ return true;
785
+ }
786
+
787
+ /**
788
+ * Replace string(s) in a string or recurively in an array or object.
789
+ *
790
+ * @since 1.2.3
791
+ *
792
+ * @static
793
+ *
794
+ * @param string $search Search string.
795
+ * @param string $replace Replace string.
796
+ * @param mixed $subject Input subject (array|object|string).
797
+ * @return mixed The input subject with recursive string replacements.
798
+ */
799
+ public static function str_replace_recursive( $search, $replace, $subject ) {
800
+ if ( is_string( $subject ) ) {
801
+ $subject = str_replace( $search, $replace, $subject );
802
+ } elseif ( is_array( $subject ) ) {
803
+ foreach ( $subject as $index => $element ) {
804
+ // Recurse.
805
+ $subject[ $index ] = Boldgrid_Backup_Admin_Utility::str_replace_recursive(
806
+ $search,
807
+ $replace,
808
+ $element
809
+ );
810
+ }
811
+ } elseif ( is_object( $subject ) ) {
812
+ foreach ( $subject as $index => $element ) {
813
+ // Recurse.
814
+ $subject->$index = Boldgrid_Backup_Admin_Utility::str_replace_recursive(
815
+ $search,
816
+ $replace,
817
+ $element
818
+ );
819
+ }
820
+ }
821
+
822
+ return $subject;
823
+ }
824
+
825
+ /**
826
+ * Convert a unix time to user's local time.
827
+ *
828
+ * @since 1.5.3
829
+ *
830
+ * @param int $time
831
+ * @return int
832
+ */
833
+ public function time( $time ) {
834
+ $gmt_offset = get_option( 'gmt_offset' );
835
+
836
+ if ( empty( $gmt_offset ) || ! is_numeric( $gmt_offset ) ) {
837
+ return $time;
838
+ }
839
+
840
+ return $time + ( $gmt_offset * HOUR_IN_SECONDS );
841
+ }
842
+
843
+ /**
844
+ * Alternative to WordPress' trailingslashit function.
845
+ *
846
+ * WordPress' native function does not take into account Windows / the
847
+ * DIRECTORY_SEPARATOR.
848
+ *
849
+ * @since 1.5.1
850
+ *
851
+ * @param string
852
+ * @return string
853
+ */
854
+ public static function trailingslashit( $string ) {
855
+ switch ( DIRECTORY_SEPARATOR ) {
856
+ case '/':
857
+ $string = str_replace( '\\', '/', $string );
858
+ break;
859
+ case '\\':
860
+ $string = str_replace( '/', '\\', $string );
861
+ break;
862
+ }
863
+
864
+ return untrailingslashit( $string ) . DIRECTORY_SEPARATOR;
865
+ }
866
+ }
admin/class-boldgrid-backup-admin-wp-cron.php ADDED
@@ -0,0 +1,360 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WP Cron.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.1
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * WP Cron.
17
+ *
18
+ * @since 1.5.1
19
+ */
20
+ class Boldgrid_Backup_Admin_WP_Cron {
21
+
22
+ /**
23
+ * $days of the weeky.
24
+ *
25
+ * @since 1.5.1
26
+ * @access publc
27
+ */
28
+ public $days = array(
29
+ 'sunday',
30
+ 'monday',
31
+ 'tuesday',
32
+ 'wednesday',
33
+ 'thursday',
34
+ 'friday',
35
+ 'saturday',
36
+ );
37
+
38
+ /**
39
+ * The core class object.
40
+ *
41
+ * @since 1.5.1
42
+ * @access private
43
+ * @var Boldgrid_Backup_Admin_Core
44
+ */
45
+ private $core;
46
+
47
+ /**
48
+ * Hooks.
49
+ *
50
+ * @since 1.5.1
51
+ * @access public
52
+ * @var array
53
+ */
54
+ public $hooks = array(
55
+ 'backup' => 'boldgrid_backup_wp_cron_backup',
56
+ 'restore' => 'boldgrid_backup_wp_cron_restore',
57
+ 'run_jobs' => 'boldgrid_backup_wp_cron_run_jobs',
58
+ );
59
+
60
+ /**
61
+ * Schedules.
62
+ *
63
+ * @since 1.5.1
64
+ * @access public
65
+ * @var array
66
+ */
67
+ public $schedules = array();
68
+
69
+ /**
70
+ * Constructor.
71
+ *
72
+ * @since 1.5.1
73
+ *
74
+ * @param Boldgrid_Backup_Admin_Core $core Core class object.
75
+ */
76
+ public function __construct( $core ) {
77
+ $this->core = $core;
78
+
79
+ $this->schedules = array(
80
+ 'every-5-minutes' => array(
81
+ 'interval' => 5 * MINUTE_IN_SECONDS,
82
+ 'display' => __( 'Every 5 minutes', 'boldgrid-backup' ),
83
+ ),
84
+ 'weekly' => array(
85
+ 'interval' => 7 * DAY_IN_SECONDS,
86
+ 'display' => __( 'Weekly', 'boldgrid-backup' ),
87
+ ),
88
+ /*
89
+ * It does not appear that crons can be added for a one time event.
90
+ * Add a "never" schedule. Let someone in 1,000 years have the fun
91
+ * of their site being restored out of nowhere wha ha ha!
92
+ */
93
+ 'never' => array(
94
+ 'interval' => 1000 * YEAR_IN_SECONDS,
95
+ 'display' => __( 'Never', 'boldgrid-backup' ),
96
+ ),
97
+ );
98
+ }
99
+
100
+ /**
101
+ * Add all cron jobs.
102
+ *
103
+ * This method first clears all crons, then adds all necessary crons based
104
+ * upon our settings.
105
+ *
106
+ * This method is useful for when:
107
+ * # User saves settings on settings page and crons need to be updated.
108
+ * # User reactivates plugin and all crons need to be added again.
109
+ *
110
+ * @since 1.6.0
111
+ *
112
+ * @param array $settings
113
+ * @return bool
114
+ */
115
+ public function add_all_crons( $settings = array() ) {
116
+ $scheduler = ! empty( $settings['scheduler'] ) ? $settings['scheduler'] : null;
117
+ $schedule = ! empty( $settings['schedule'] ) ? $settings['schedule'] : null;
118
+
119
+ if ( 'wp-cron' === $scheduler && $this->core->scheduler->is_available( $scheduler ) && ! empty( $schedule ) ) {
120
+ $this->core->scheduler->clear_all_schedules();
121
+
122
+ $scheduled = $this->schedule( $settings, $this->hooks['backup'] );
123
+ $jobs_scheduled = $this->schedule_jobs();
124
+
125
+ return $scheduled && $jobs_scheduled;
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Add cron to perform auto rollback.
131
+ *
132
+ * @since 1.5.1
133
+ */
134
+ public function add_restore_cron() {
135
+ $pending_rollback = get_site_option( 'boldgrid_backup_pending_rollback' );
136
+
137
+ // Get the archive to restore.
138
+ $archives = $this->core->get_archive_list();
139
+ $archive_key = 0;
140
+ $archive = $archives[ $archive_key ];
141
+ $archive_filename = $archive['filename'];
142
+
143
+ // Remove existing restore cron jobs.
144
+ $this->clear_schedules( array( $this->hooks['restore'] ) );
145
+
146
+ // Cron has a 15 minute window for auto restoriations, and so will we (WP Cron).
147
+ $auto_restore_time = strtotime( '+15 MINUTES' );
148
+
149
+ $event_added = wp_schedule_event( $auto_restore_time, 'never', $this->hooks['restore'] );
150
+
151
+ // If cron job was added, then update the boldgrid_backup_pending_rollback option with time.
152
+ if ( false !== $event_added ) {
153
+ $pending_rollback['deadline'] = $auto_restore_time;
154
+ update_site_option( 'boldgrid_backup_pending_rollback', $pending_rollback );
155
+ }
156
+
157
+ return;
158
+ }
159
+
160
+ /**
161
+ * Clear schedules.
162
+ *
163
+ * @since 1.5.1
164
+ *
165
+ * @param array An array of hooks to clear.
166
+ */
167
+ public function clear_schedules( $hooks = array() ) {
168
+ if ( empty( $hooks ) ) {
169
+ $hooks = $this->hooks;
170
+ }
171
+
172
+ foreach ( $hooks as $hook ) {
173
+ wp_clear_scheduled_hook( $hook );
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Hook into "cron_schedules" filter and add weekly.
179
+ *
180
+ * @since 1.5.1
181
+ *
182
+ * @param array $schedules
183
+ * @return array
184
+ */
185
+ public function cron_schedules( $schedules ) {
186
+ foreach ( $this->schedules as $key => $schedule ) {
187
+ if ( in_array( $key, $schedules, true ) ) {
188
+ continue;
189
+ }
190
+
191
+ $schedules[ $key ] = $schedule;
192
+ }
193
+
194
+ return $schedules;
195
+ }
196
+
197
+ /**
198
+ * Get the next time for a wp cron.
199
+ *
200
+ * For example, if today is Monday 1:10pm and you pass in Monday 1:11pm, it
201
+ * should return 1 minute from now. If you pass in Monday 1:09pm, that time
202
+ * has already passed, so return next Monday at 1:09pm.
203
+ *
204
+ * @param $d string Day of the week.
205
+ * @param $h int Hour.
206
+ * @param $m int Minute.
207
+ * @param $p string Period (am/pm).
208
+ * @return A timestamp on success.
209
+ */
210
+ public function get_next_time( $d, $h, $m, $p ) {
211
+ $schedule_time = strtotime( sprintf( 'this %1$s %2$s:%3$s %4$s', $d, $h, $m, $p ) );
212
+
213
+ if ( time() > $schedule_time ) {
214
+ $schedule_time = strtotime( sprintf( 'next %1$s %2$s:%3$s %4$s', $d, $h, $m, $p ) );
215
+ }
216
+
217
+ return $schedule_time;
218
+ }
219
+
220
+ /**
221
+ * Get all of our wp crons.
222
+ *
223
+ * @since 1.5.2
224
+ *
225
+ * @return array
226
+ */
227
+ public function get_our_crons() {
228
+ $ours = array();
229
+
230
+ $crons = _get_cron_array();
231
+ $crons = is_array( $crons ) ? $crons : array();
232
+
233
+ foreach ( $crons as $time => $cron ) {
234
+ $action = key( $cron );
235
+
236
+ if ( empty( $action ) || 0 !== strpos( $action, 'boldgrid_backup_' ) ) {
237
+ continue;
238
+ }
239
+
240
+ $action_key = key( $cron[ $action ] );
241
+
242
+ $ours[] = sprintf( '%1$s (%2$s %3$s %4$s)', $action, $cron[ $action ][ $action_key ]['schedule'], __( 'starting','boldgrid-backup' ), date( 'Y.m.d h:i:s a e', $time ) );
243
+ }
244
+
245
+ return $ours;
246
+ }
247
+
248
+ /**
249
+ * Restore via wp cron.
250
+ *
251
+ * @since 1.5.2
252
+ *
253
+ * @return mixed null|false
254
+ */
255
+ public function restore() {
256
+ $archive_info = array(
257
+ 'error' => __( 'Could not perform restoration from WP Cron task.', 'boldgrid-backup' ),
258
+ );
259
+
260
+ if ( $this->core->restore_helper->prepare_restore() ) {
261
+ $archive_info = $this->core->restore_archive_file();
262
+ }
263
+
264
+ // Remove existing restore cron jobs.
265
+ $this->clear_schedules( array( $this->hooks['restore'] ) );
266
+
267
+ return $archive_info;
268
+ }
269
+
270
+ /**
271
+ * Schedule a wp cron.
272
+ *
273
+ * @since 1.5.1
274
+ *
275
+ * @param array $settings
276
+ * @param string $hook
277
+ * @return bool
278
+ */
279
+ public function schedule( $settings, $hook ) {
280
+
281
+ /*
282
+ * WP Cron works off of UTC. Get our "local" time from our $settings and
283
+ * convert it to UTC.
284
+ *
285
+ * It's important that we pass in our $settings to get_settings_date().
286
+ * Let's say the original time was 4am and we changed it to 5am.
287
+ * # If we don't pass in $settings, which is the new time we're in the
288
+ * middle of saving right now (5am)...
289
+ * # Then it will get the settings from options, which is still 4am (as
290
+ * it hasn't been saved yet).
291
+ */
292
+ $date = $this->core->time->get_settings_date( $settings );
293
+ $new_timezone = new DateTimeZone( 'UTC' );
294
+ $date->setTimezone( $new_timezone );
295
+
296
+ // Get hour, minute, and period.
297
+ $h = $date->format( 'g' );
298
+ $m = $date->format( 'i' );
299
+ $p = $date->format( 'A' );
300
+
301
+ $success = true;
302
+
303
+ foreach ( $this->days as $day ) {
304
+ if ( 1 !== $settings['schedule'][ 'dow_' . $day ] ) {
305
+ continue;
306
+ }
307
+
308
+ $schedule_time = $this->get_next_time( $day, $h, $m, $p );
309
+
310
+ /*
311
+ * Schedule our event and track our success.
312
+ *
313
+ * As we may be scheduling multiple events via this loop, the
314
+ * $success of this method is based on whether or not all items
315
+ * are successfully scheduled. If we have 5 days to schedule and
316
+ * only 4 are scheduled successfully, this method returns false.
317
+ */
318
+ $scheduled = wp_schedule_event( $schedule_time, 'weekly', $hook );
319
+ if ( false === $scheduled ) {
320
+ $success = false;
321
+ }
322
+ }
323
+
324
+ return $success;
325
+ }
326
+
327
+ /**
328
+ * Schedule the "run_jobs" hook.
329
+ *
330
+ * This hook will run every 5 minutes and run one job at a time, such as
331
+ * upload to a remote storage provider.
332
+ *
333
+ * This method is usually ran after saving the BoldGrid Backup settings. If
334
+ * after save wp-cron is our scheduler, then we need to make sure we have
335
+ * the "run_jobs" wp-cron scheduled.
336
+ *
337
+ * @since 1.5.2
338
+ *
339
+ * @return bool
340
+ */
341
+ public function schedule_jobs() {
342
+ $success = true;
343
+
344
+ if ( ! wp_next_scheduled( $this->hooks['run_jobs'] ) ) {
345
+ $scheduled = wp_schedule_event( time(), 'every-5-minutes', $this->hooks['run_jobs'] );
346
+ $success = false !== $scheduled;
347
+ }
348
+
349
+ return $success;
350
+ }
351
+
352
+ /**
353
+ * Hook into "boldgrid_backup_wp_cron_backup" and generate backup.
354
+ *
355
+ * @since 1.5.1
356
+ */
357
+ public function backup() {
358
+ $archive_info = $this->core->archive_files( true );
359
+ }
360
+ }
admin/class-boldgrid-backup-admin-xhprof.php ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The admin-specific PHP profiling methods for the plugin
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.2
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup admin xhprof class.
17
+ *
18
+ * @since 1.2
19
+ */
20
+ class Boldgrid_Backup_Admin_Xhprof {
21
+ /**
22
+ * Is XHProf active?
23
+ *
24
+ * @since 1.2
25
+ * @access private
26
+ * @var bool
27
+ */
28
+ private $xhprof_active = false;
29
+
30
+ /**
31
+ * Constructor.
32
+ *
33
+ * @since 1.2
34
+ */
35
+ public function __construct() {
36
+ // Set the configuration array.
37
+ Boldgrid_Backup_Admin::get_configs();
38
+
39
+ // Try to enable XHProf.
40
+ $this->xhprof_active = $this->xhprof_enable();
41
+
42
+ // If XHprof was enabled, then register a shutdown action to disable XHProf and
43
+ // save the run report data to file.
44
+ if ( $this->xhprof_active ) {
45
+ add_action( 'shutdown',
46
+ array(
47
+ $this,
48
+ 'xhprof_disable',
49
+ ), 10, 0
50
+ );
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Enable XHProf.
56
+ *
57
+ * @since 1.2
58
+ *
59
+ * @return bool Success; whether or not XHProf was enabled.
60
+ */
61
+ private function xhprof_enable() {
62
+ // If the action is "heartbeat", then abort.
63
+ if ( ! empty( $_POST['action'] ) && 'heartbeat' === $_POST['action'] ) {
64
+ return false;
65
+ }
66
+
67
+ // Get configs.
68
+ $configs = Boldgrid_Backup_Admin::get_configs();
69
+
70
+ // If available and enabled, then start XHProf.
71
+ if ( ! empty( $configs['xhprof'] ) && extension_loaded( 'xhprof' ) ) {
72
+ xhprof_enable( XHPROF_FLAGS_CPU | XHPROF_FLAGS_MEMORY );
73
+
74
+ return true;
75
+ }
76
+
77
+ // Unsuccessful.
78
+ return false;
79
+ }
80
+
81
+ /**
82
+ * Disable XHProf, saving report and error logging the report URL.
83
+ *
84
+ * @since 1.2
85
+ *
86
+ * @return null
87
+ */
88
+ public function xhprof_disable() {
89
+ // If XHProf is not active, then abort.
90
+ if ( ! $this->xhprof_active ) {
91
+ return;
92
+ }
93
+
94
+ // Get configs.
95
+ $configs = Boldgrid_Backup_Admin::get_configs();
96
+
97
+ // Save report to the log.
98
+ if ( ! empty( $configs['xhprof'] ) && extension_loaded( 'xhprof' ) ) {
99
+ // Disable XHProf and collect the data return array.
100
+ $xhprof_data = xhprof_disable();
101
+
102
+ // If there is no data, then abort.
103
+ if ( empty( $xhprof_data ) ) {
104
+ return;
105
+ }
106
+
107
+ // Configure the utils path.
108
+ $xhprof_utils_path = '/usr/share/pear/xhprof_lib/utils';
109
+
110
+ // If the utility libraries exists, then load them.
111
+ if ( file_exists( $xhprof_utils_path . '/xhprof_lib.php' ) &&
112
+ file_exists( $xhprof_utils_path . '/xhprof_runs.php' ) ) {
113
+ require_once $xhprof_utils_path . '/xhprof_lib.php';
114
+ require_once $xhprof_utils_path . '/xhprof_runs.php';
115
+
116
+ // Save the run data to file.
117
+ $xhprof_runs = new XHProfRuns_Default();
118
+ $run_id = $xhprof_runs->save_run( $xhprof_data, 'xhprof_boldgrid_backup' );
119
+
120
+ // Write the report URL to the error log.
121
+ error_log(
122
+ __METHOD__ . ': https://' . $_SERVER['HTTP_HOST'] .
123
+ '/xhprof/index.php?run=' . $run_id . '&source=xhprof_boldgrid_backup'
124
+ );
125
+ }
126
+ }
127
+
128
+ return;
129
+ }
130
+ }
admin/class-boldgrid-backup-admin.php ADDED
@@ -0,0 +1,199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The admin-specific functionality of the plugin
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.0
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ */
11
+
12
+ /**
13
+ * The admin-specific functionality of the plugin.
14
+ *
15
+ * Defines the plugin name, version, and two examples hooks for how to
16
+ * enqueue the admin-specific stylesheet and JavaScript.
17
+ *
18
+ * @package Boldgrid_Backup
19
+ * @subpackage Boldgrid_Backup/admin
20
+ * @author BoldGrid.com <wpb@boldgrid.com>
21
+ */
22
+ class Boldgrid_Backup_Admin {
23
+ /**
24
+ * The ID of this plugin.
25
+ *
26
+ * @since 1.0
27
+ * @access private
28
+ * @var string $plugin_name
29
+ */
30
+ private $plugin_name;
31
+
32
+ /**
33
+ * The version of this plugin.
34
+ *
35
+ * @since 1.0
36
+ * @access private
37
+ * @var string $version
38
+ */
39
+ private $version;
40
+
41
+ /**
42
+ * Boldgrid_Backup_Admin_Config class object.
43
+ *
44
+ * @since 1.3.6
45
+ *
46
+ * @var Boldgrid_Backup_Admin_Config
47
+ */
48
+ private $config;
49
+
50
+ /**
51
+ * Configuration array.
52
+ *
53
+ * @since 1.3.5
54
+ *
55
+ * @var array
56
+ * @staticvar
57
+ */
58
+ private static $configs;
59
+
60
+ /**
61
+ * Initialize the class and set its properties.
62
+ *
63
+ * @since 1.0
64
+ *
65
+ * @global WP_Filesystem $wp_filesystem The WordPress Filesystem API global object.
66
+ *
67
+ * @param string $plugin_name The name of this plugin.
68
+ * @param string $version The version of this plugin.
69
+ */
70
+ public function __construct( $plugin_name, $version ) {
71
+ $this->plugin_name = $plugin_name;
72
+ $this->version = $version;
73
+
74
+ // Connect to the WordPress Filesystem API.
75
+ global $wp_filesystem;
76
+
77
+ // Ensure the WP_Filesystem was initialized.
78
+ if ( empty( $wp_filesystem ) ) {
79
+ require_once ABSPATH . '/wp-admin/includes/file.php';
80
+ WP_Filesystem();
81
+ }
82
+
83
+ $this->config = new Boldgrid_Backup_Admin_Config( null );
84
+ }
85
+
86
+ /**
87
+ * Register the stylesheets for the admin area.
88
+ *
89
+ * @since 1.0
90
+ */
91
+ public function enqueue_styles() {
92
+ /*
93
+ * An instance of this class should be passed to the run() function
94
+ * defined in Boldgrid_Backup_Loader as all of the hooks are defined
95
+ * in that particular class.
96
+ *
97
+ * The Boldgrid_Backup_Loader will then create the relationship
98
+ * between the defined hooks and the functions defined in this
99
+ * class.
100
+ */
101
+ wp_enqueue_style( $this->plugin_name,
102
+ plugin_dir_url( __FILE__ ) . 'css/boldgrid-backup-admin.css', array(), $this->version );
103
+
104
+ // Enqueue JS.
105
+ wp_register_script( 'boldgrid-backup-admin',
106
+ plugin_dir_url( __FILE__ ) . 'js/boldgrid-backup-admin.js',
107
+ array( 'jquery' ),
108
+ BOLDGRID_BACKUP_VERSION,
109
+ false
110
+ );
111
+
112
+ $spinner = '<span class="spinner inline"></span> ';
113
+ $dots = ' ...';
114
+ $translation = array(
115
+ 'is_premium' => ( true === $this->config->get_is_premium() ? 'true' : 'false' ),
116
+ 'lang' => $this->config->lang,
117
+ 'spinner_loading' => $spinner . __( 'Loading', 'boldgrid-backup' ) . $dots,
118
+ 'get_premium_url' => Boldgrid_Backup_Admin_Go_Pro::$url,
119
+ );
120
+
121
+ wp_localize_script( 'boldgrid-backup-admin', 'BoldGridBackupAdmin', $translation );
122
+
123
+ wp_enqueue_script( 'boldgrid-backup-admin' );
124
+
125
+ // Used by admin.js to highlight / bounce elements.
126
+ wp_enqueue_script( 'jquery-effects-core' );
127
+ wp_enqueue_script( 'jquery-effects-bounce' );
128
+
129
+ // Enqueue CSS for the home page.
130
+ if ( isset( $_REQUEST['page'] ) && 'boldgrid-backup' === $_REQUEST['page'] ) {
131
+ wp_enqueue_style( 'boldgrid-backup-admin-home',
132
+ plugin_dir_url( __FILE__ ) . 'css/boldgrid-backup-admin-home.css', array(),
133
+ BOLDGRID_BACKUP_VERSION
134
+ );
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Get configuration settings.
140
+ *
141
+ * @since 1.3.5
142
+ *
143
+ * @static
144
+ *
145
+ * @return array An array of configuration settings.
146
+ */
147
+ public static function get_configs() {
148
+ // If the configuration array was already created, then return it.
149
+ if ( ! empty( self::$configs ) ) {
150
+ return self::$configs;
151
+ }
152
+
153
+ // Set the config directory.
154
+ $config_dir = BOLDGRID_BACKUP_PATH . '/includes/config';
155
+
156
+ // Set the config file paths.
157
+ $global_config_path = $config_dir . '/config.plugin.php';
158
+ $local_config_path = $config_dir . '/config.local.php';
159
+
160
+ // Initialize $global_configs array.
161
+ $global_configs = array();
162
+
163
+ // If a global config file exists, read the global configuration settings.
164
+ if ( file_exists( $global_config_path ) ) {
165
+ $global_configs = require $global_config_path;
166
+ }
167
+
168
+ // Initialize $local_configs array.
169
+ $local_configs = array();
170
+
171
+ // If a local configuration file exists, then read the settings.
172
+ if ( file_exists( $local_config_path ) ) {
173
+ $local_configs = require $local_config_path;
174
+ }
175
+
176
+ // If an api key hash stored in the database, then set it as the global api_key.
177
+ $api_key_from_database = get_option( 'boldgrid_api_key' );
178
+
179
+ if ( ! empty( $api_key_from_database ) ) {
180
+ $global_configs['api_key'] = $api_key_from_database;
181
+ }
182
+
183
+ // Get the WordPress site url and set it in the global configs array.
184
+ $global_configs['site_url'] = get_site_url();
185
+
186
+ // Merge global and local configuration settings.
187
+ if ( ! empty( $local_configs ) ) {
188
+ $configs = array_merge( $global_configs, $local_configs );
189
+ } else {
190
+ $configs = $global_configs;
191
+ }
192
+
193
+ // Set the configuration array in the class property.
194
+ self::$configs = $configs;
195
+
196
+ // Return the configuration array.
197
+ return $configs;
198
+ }
199
+ }
admin/compressor/pcl_zip.php ADDED
@@ -0,0 +1,546 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Pcl Zip Compressor.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.1
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup Admin Compressor Pclzip Class.
17
+ *
18
+ * @since 1.5.1
19
+ */
20
+ class Boldgrid_Backup_Admin_Compressor_Pcl_Zip extends Boldgrid_Backup_Admin_Compressor {
21
+
22
+ /**
23
+ * An array of errors.
24
+ *
25
+ * @since 1.5.4
26
+ * @access public
27
+ * @var array
28
+ */
29
+ public $errors = array();
30
+
31
+ /**
32
+ * The status of our test result.
33
+ *
34
+ * @since 1.5.1
35
+ * @access public
36
+ * @var mixed|bool|null
37
+ */
38
+ public static $test_result = null;
39
+
40
+ /**
41
+ * An array of any errors received while testing.
42
+ *
43
+ * @since 1.5.1
44
+ * @access public
45
+ * @var array
46
+ */
47
+ public $test_errors = array();
48
+
49
+ /**
50
+ * Constructor.
51
+ *
52
+ * @since 1.5.1
53
+ *
54
+ * @param Boldgrid_Backup_Admin_Core $core
55
+ */
56
+ public function __construct( $core ) {
57
+ if ( ! class_exists( 'PclZip' ) ) {
58
+ require_once( ABSPATH . '/wp-admin/includes/class-pclzip.php' );
59
+ }
60
+
61
+ parent::__construct( $core );
62
+ }
63
+
64
+ /**
65
+ * Add empty directories to current directory we're browsing.
66
+ *
67
+ * This method is used by this->browse, and it's whole reason for existence
68
+ * is because ZipArchive gives you the ability to simply add a directory to
69
+ * the archive, but PclZip does not.
70
+ *
71
+ * If PclZip archive only includes one file, such as
72
+ * wp-content/plugins/boldgrid/index.php, we need to "artificially" create
73
+ * these directories for the zip browser:
74
+ * # wp-content/
75
+ * # wp-content/plugins/
76
+ * # wp-content/plugins/boldgrid/
77
+ *
78
+ * @since 1.5.4
79
+ *
80
+ * @param array $list
81
+ * @param array $contents
82
+ * @param array $filenames
83
+ * @param string $in_dir
84
+ * @return array An updated $contents.
85
+ */
86
+ public function browse_add_dirs( $list, $contents, $filenames, $in_dir ) {
87
+ foreach ( $list as $key => $file ) {
88
+
89
+ // These variables are very similar, both exist for readability.
90
+ $top_dir = null;
91
+ $next_dir = null;
92
+
93
+ if ( '.' === $in_dir ) {
94
+ $top_dir = explode( '/', $file['filename'] );
95
+ $top_dir = $top_dir[0];
96
+
97
+ if ( empty( $top_dir ) || in_array( $top_dir, $filenames ) ) {
98
+ continue;
99
+ }
100
+ } else {
101
+
102
+ /*
103
+ * Determine if file is in directory.
104
+ *
105
+ * For example, We want to know if file wp-admin/user/about is
106
+ * in wp-content/
107
+ */
108
+ $file_in_dir = 0 === strpos( $file['filename'], $in_dir . '/' );
109
+ if ( ! $file_in_dir ) {
110
+ continue;
111
+ }
112
+
113
+ /*
114
+ * Calcular our next directory.
115
+ *
116
+ * If we're looking for all folders within wp-content/plugins
117
+ * and we're given a filename of
118
+ * wp-content/plugins/boldgrid/index.php, then we can say that
119
+ * wp-content/plugins/boldgrid exists in wp-content/plugins.
120
+ */
121
+ $next_dir = str_replace( $in_dir . '/', '', $file['filename'] );
122
+ $next_dir = explode( '/', $next_dir );
123
+ $next_dir = $in_dir . '/' . $next_dir[0];
124
+
125
+ if ( $next_dir === $file['filename'] || in_array( $next_dir, $filenames ) ) {
126
+ continue;
127
+ }
128
+ }
129
+
130
+ $dir = ! empty( $top_dir ) ? $top_dir : $next_dir;
131
+ $sudo_file = array(
132
+ 'filename' => $dir,
133
+ 'folder' => true,
134
+ );
135
+ $contents[] = $sudo_file;
136
+ $filenames[] = $dir;
137
+ }
138
+
139
+ return $contents;
140
+ }
141
+
142
+ /**
143
+ * Archive files.
144
+ *
145
+ * @since 1.5.1
146
+ *
147
+ * @param array $filelist See Boldgrid_Backup_Admin_Filelist::get_total_size
148
+ * @param array $info {
149
+ * An array of data about the backup archive we are generating.
150
+ *
151
+ * @type string mode backup
152
+ * @type bool dryrun
153
+ * @type string compressor php_zip
154
+ * @type ing filesize 0
155
+ * @type bool save 1
156
+ * @type int total_size 0
157
+ * }
158
+ */
159
+ public function archive_files( $filelist, &$info ) {
160
+ $info['filepath'] = $this->core->generate_archive_path( 'zip' );
161
+
162
+ if ( $info['dryrun'] ) {
163
+ return true;
164
+ }
165
+
166
+ $cwd = $this->wp_filesystem->cwd();
167
+
168
+ $archive = new PclZip( $info['filepath'] );
169
+ if ( 0 === $archive ) {
170
+ return array(
171
+ 'error' => sprintf( 'Cannot create ZIP archive file %1$s. %2$s.', $info['filepath'], $archive->errorInfo() ),
172
+ );
173
+ }
174
+
175
+ /*
176
+ * Create our $new_filelist.
177
+ *
178
+ * We can pass $archive->add() an array of files to archive. $filelist
179
+ * is an array of arrays, so we need to convert to simply an array of
180
+ * strings (filenames to archive).
181
+ */
182
+ $new_filelist = array();
183
+ foreach ( $filelist as $key => $file ) {
184
+
185
+ // Don't add the database dump at this time, it will be added later.
186
+ if ( ! empty( $this->core->db_dump_filepath ) && $file[0] === $this->core->db_dump_filepath ) {
187
+ continue;
188
+ }
189
+
190
+ $new_filelist[] = $file[0];
191
+ }
192
+
193
+ $status = $archive->add( $new_filelist,
194
+ PCLZIP_OPT_REMOVE_PATH, ABSPATH
195
+ );
196
+
197
+ if ( 0 === $status ) {
198
+ $error_info = $archive->errorInfo();
199
+
200
+ $custom_error = $this->parse_error_info( $error_info );
201
+
202
+ if ( false === $custom_error ) {
203
+ return array(
204
+ 'error' => sprintf( 'Cannot add files to ZIP archive file: %1$s', $archive->errorInfo() ),
205
+ );
206
+ } else {
207
+ return array(
208
+ 'error' => $custom_error,
209
+ );
210
+ }
211
+ }
212
+
213
+ /*
214
+ * Add our database dump to the zip.
215
+ *
216
+ * The check for ! empty is here because the user may have opted not
217
+ * to backup their database.
218
+ */
219
+ if ( ! empty( $this->core->db_dump_filepath ) ) {
220
+ $status = $archive->add( $this->core->db_dump_filepath, PCLZIP_OPT_REMOVE_ALL_PATH );
221
+ if ( 0 === $status ) {
222
+ return array(
223
+ 'error' => sprintf( 'Cannot add database dump to ZIP archive file: %1$s', $archive->errorInfo() ),
224
+ );
225
+ }
226
+ }
227
+
228
+ $this->wp_filesystem->chdir( $cwd );
229
+
230
+ return true;
231
+ }
232
+
233
+ /**
234
+ * Get the contents of a zip file.
235
+ *
236
+ * @param string $filepath
237
+ * @param string $in_dir
238
+ * @return array
239
+ */
240
+ public function browse( $filepath, $in_dir = '.' ) {
241
+ $in_dir = untrailingslashit( $in_dir );
242
+
243
+ /*
244
+ * Keep track of the contents of the directory we're trying to browse.
245
+ *
246
+ * This variable is different than the below $filenames variable because
247
+ * $contents contains an array arrays (info ABOUT each filename) while
248
+ * $filenames just contains either the file or folder name.
249
+ */
250
+ $contents = array();
251
+
252
+ /*
253
+ * Keep track of just file and folder names added to the $contents. For
254
+ * example:
255
+ *
256
+ * [0] wp-content (folder)
257
+ * [1] wp-content/index.php (file)
258
+ */
259
+ $filenames = array();
260
+
261
+ $zip = new PclZip( $filepath );
262
+
263
+ $list = $zip->listContent();
264
+ if ( empty( $list ) ) {
265
+ return $contents;
266
+ }
267
+
268
+ /*
269
+ * Each $file is an array. Several example $file's can be seen here:
270
+ * https://pastebin.com/bjQZYcAt
271
+ */
272
+ foreach ( $list as $key => $file ) {
273
+
274
+ /*
275
+ * Calculate the parent directory this file / folder belongs to.
276
+ *
277
+ * Examples:
278
+ * * readme.html = .
279
+ * * wp-admin/press-this.php = wp-admin
280
+ * * wp-admin/js/user-profile.min.js = wp-admin/js
281
+ */
282
+ $parent_dir = dirname( $file['filename'] );
283
+
284
+ if ( $parent_dir !== $in_dir ) {
285
+ continue;
286
+ }
287
+
288
+ $contents[] = $file;
289
+ $filenames[] = rtrim( $file['filename'], '/' );
290
+ }
291
+
292
+ $contents = $this->browse_add_dirs( $list, $contents, $filenames, $in_dir );
293
+
294
+ return $contents;
295
+ }
296
+
297
+ /**
298
+ * Extract one file from an archive.
299
+ *
300
+ * @since 1.5.3
301
+ *
302
+ * @param string $filepath /home/user/boldgrid_backup/archive.zip
303
+ * @param string $file wp-content/index.php
304
+ * @return bool
305
+ */
306
+ public function extract_one( $filepath, $file ) {
307
+ if ( ! $this->core->archive->is_archive( $filepath ) ) {
308
+ $this->errors[] = __( 'Not an archive.', 'boldgrid-backup' );
309
+ return false;
310
+ }
311
+
312
+ if ( empty( $file ) ) {
313
+ $this->errors[] = __( 'Empty file.', 'boldgrid-backup' );
314
+ return false;
315
+ }
316
+
317
+ $file_contents = $this->get_file( $filepath, $file );
318
+
319
+ // Make sure the file's dir exists, write the file, and adjust the timestamp.
320
+ $file_abspath = ABSPATH . $file;
321
+ wp_mkdir_p( dirname( $file_abspath ) );
322
+ $written = $this->core->wp_filesystem->put_contents( $file_abspath, $file_contents[0]['content'] );
323
+ if ( ! $written ) {
324
+ $this->errors[] = __( 'Not written.', 'boldgrid-backup' );
325
+ return false;
326
+ }
327
+
328
+ return $this->core->wp_filesystem->touch( ABSPATH . $file, $file_contents[0]['mtime'] );
329
+ }
330
+
331
+ /**
332
+ * Extract 1 file from a zip archive.
333
+ *
334
+ * @since 1.5.3
335
+ *
336
+ * @param string $filepath /home/user/boldgrid_backup/archive.zip
337
+ * @param string $file wp-content/index.php
338
+ * @return mixed False on failure, array on success {
339
+ * Accessed via $file_contentws[0].
340
+ *
341
+ * @type string $filename wp-content/index.php
342
+ * @type string $stored_filename wp-content/index.php
343
+ * @type int $size 28
344
+ * @type int $compressed_size 30
345
+ * @type int $mtime 1505997200
346
+ * @type string $comment
347
+ * @type bool $folder
348
+ * @type int $index 25054
349
+ * @type string $status ok
350
+ * @type int $crc 4212199498
351
+ * @type string $content
352
+ * }
353
+ */
354
+ public function get_file( $filepath, $file ) {
355
+ if ( ! $this->core->archive->is_archive( $filepath ) ) {
356
+ return false;
357
+ }
358
+
359
+ if ( empty( $file ) ) {
360
+ return false;
361
+ }
362
+
363
+ $zip = new PclZip( $filepath );
364
+
365
+ $list = $zip->listContent();
366
+ if ( empty( $list ) ) {
367
+ return false;
368
+ }
369
+
370
+ $file_index = false;
371
+
372
+ foreach ( $list as $index => $filedata ) {
373
+ if ( $file === $filedata['filename'] ) {
374
+ $file_index = $index;
375
+ }
376
+ }
377
+
378
+ /*
379
+ * We use to check if(! $file_index) however sometimes the file we want
380
+ * is at the 0 index.
381
+ */
382
+ if ( false === $file_index ) {
383
+ return false;
384
+ }
385
+
386
+ $file_contents = $zip->extractByIndex( $file_index, PCLZIP_OPT_EXTRACT_AS_STRING );
387
+
388
+ /*
389
+ * Ensure the mtime is UTC.
390
+ *
391
+ * mtime can vary based upon how the archive was initially created (php_zip
392
+ * or pcl_zip). Make sure it is in UTC.
393
+ */
394
+ $this->core->archive->init( $filepath );
395
+ $this->core->time->init( $file_contents[0]['mtime'], $this->core->archive->compressor );
396
+ $file_contents[0]['mtime'] = $this->core->time->utc_time;
397
+
398
+ return $file_contents;
399
+ }
400
+
401
+ /**
402
+ * Get a list of all sql dumps in an archive's root.
403
+ *
404
+ * When restoring an archive, this method is helpful in determining which
405
+ * sql dump to restore. We're expecting only 1 to be found.
406
+ *
407
+ * @since 1.5.2
408
+ *
409
+ * @param string $filepath Full path to zip file.
410
+ * @return array An array of sql dumps found in the root.
411
+ */
412
+ public function get_sqls( $filepath ) {
413
+ $sqls = array();
414
+
415
+ $zip = new PclZip( $filepath );
416
+
417
+ $list = $zip->listContent();
418
+
419
+ if ( empty( $list ) ) {
420
+ return $sqls;
421
+ }
422
+
423
+ foreach ( $list as $key => $file ) {
424
+ $filename = $file['filename'];
425
+
426
+ // If it's not in the root, skip it.
427
+ if ( false !== strpos( $filename, '/' ) || false !== strpos( $filename, '\\' ) ) {
428
+ continue;
429
+ }
430
+
431
+ // If it's not in this format, skip it - Format: *.########-######.sql
432
+ if ( 1 !== preg_match( '/\.[\d]+-[\d]+\.sql$/', $filename ) ) {
433
+ continue;
434
+ }
435
+
436
+ $sqls[] = $filename;
437
+ }
438
+
439
+ return $sqls;
440
+ }
441
+
442
+ /**
443
+ * Parse the error message and take appropriate action.
444
+ *
445
+ * @since 1.5.2
446
+ *
447
+ * @param string $error_info
448
+ * @return mixed False when no messages should be displayed, String when
449
+ * returning a message to the user.
450
+ */
451
+ public function parse_error_info( $error_info ) {
452
+ $parts = explode( '\'', $error_info );
453
+ $force_php_zip = false;
454
+ $messages = array();
455
+
456
+ // Does not exist [code -4].
457
+ if ( ! empty( $parts[2] ) && false !== strpos( $parts[2], 'code -4' ) ) {
458
+ $path = ABSPATH . $parts[1];
459
+
460
+ // Check for broken symlink.
461
+ if ( is_link( $path ) && ! $this->core->wp_filesystem->exists( $path ) ) {
462
+ $force_php_zip = true;
463
+ $messages[] = sprintf( __( 'PclZip encountered the following broken symlink and is unable to create a backup:<br />%1$s', 'boldgrid-backup' ), $parts[1] );
464
+ }
465
+ }
466
+
467
+ /*
468
+ * If we have flagged that ZipArchive should be used instead of PclZip,
469
+ * then update the settings.
470
+ */
471
+ if ( $force_php_zip ) {
472
+ $php_zip_set = $this->core->compressors->set_php_zip();
473
+
474
+ if ( $php_zip_set ) {
475
+ $messages[] = __( 'We have changed your compressor from PclZip to ZipArchive. Please try to create a backup again.' );
476
+ }
477
+ }
478
+
479
+ return empty( $messages ) ? false : implode( '<br />', $messages );
480
+ }
481
+
482
+ /**
483
+ * Test the functionality of php_zip.
484
+ *
485
+ * @since 1.5
486
+ *
487
+ * @param bool $display_errors
488
+ * @return bool
489
+ */
490
+ public function test( $display_errors = true ) {
491
+ if ( null !== self::$test_result ) {
492
+ return self::$test_result;
493
+ }
494
+
495
+ $backup_dir = $this->core->backup_dir->get();
496
+
497
+ // Strings to help with creating test files.
498
+ $test_file_contents = $str = __( 'This is a test file from BoldGrid Backup. You can delete this file.', 'boldgrid-backup' );
499
+ $safe_to_delete = __( 'safe-to-delete', 'boldgrid-backup' );
500
+ $test_zip_file = $this->core->test->test_prefix . '-zip';
501
+ $test_filename = sprintf( '%1$s%5$s%2$s-%3$s-%4$s', $backup_dir, $test_zip_file, mt_rand(), $safe_to_delete, DIRECTORY_SEPARATOR );
502
+ $zip_filepath = $test_filename . '.zip';
503
+ $random_filename = $test_filename . '.txt';
504
+
505
+ $cannot_touch_file = __( 'PclZip test failed. We were unable to create the following test file:<br />
506
+ %1$s.<br />
507
+ Please ensure your backup directory has read, write, and modify permissions.',
508
+ 'boldgrid-backup'
509
+ );
510
+
511
+ $cannot_put_contents = __( 'PclZip test failed. We were able to create the following test file, but we were unable to modify it. were unable to modify it:<br />
512
+ %1$s<br />
513
+ Please ensure your backup directory has read, write, and modify permissions.',
514
+ 'boldgrid-backup'
515
+ );
516
+
517
+ $touched = $this->core->wp_filesystem->touch( $random_filename );
518
+ if ( ! $touched ) {
519
+ $this->test_errors[] = sprintf( $cannot_touch_file, $random_filename );
520
+ self::$test_result = false;
521
+ return false;
522
+ }
523
+
524
+ $contents_put = $this->core->wp_filesystem->put_contents( $random_filename, $test_file_contents );
525
+ if ( ! $contents_put ) {
526
+ $this->test_errors[] = sprintf( $cannot_put_contents, $random_filename );
527
+ self::$test_result = false;
528
+ return false;
529
+ }
530
+
531
+ $archive = new PclZip( $zip_filepath );
532
+ if ( 0 === $archive ) {
533
+ $this->test_errors[] = sprintf( 'Cannot create ZIP archive file %1$s. %2$s.', $info['filepath'], $archive->errorInfo() );
534
+ }
535
+
536
+ $status = $archive->add( $random_filename );
537
+ if ( 0 === $status ) {
538
+ $this->test_errors[] = sprintf( 'Cannot add files to PclZip archive file: %1$s', $archive->errorInfo() );
539
+ }
540
+
541
+ $this->core->test->delete_test_files( $backup_dir );
542
+
543
+ self::$test_result = true;
544
+ return true;
545
+ }
546
+ }
admin/compressor/php_zip.php ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * PHP Zip Compressor.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.1
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * BoldGrid Backup Admin Compressor PHP Zip Class.
17
+ *
18
+ * @since 1.5.1
19
+ */
20
+ class Boldgrid_Backup_Admin_Compressor_Php_Zip extends Boldgrid_Backup_Admin_Compressor {
21
+
22
+ /**
23
+ * An array of directories we've added to the zip.
24
+ *
25
+ * @since 1.5.4
26
+ * @access public
27
+ * @var array
28
+ */
29
+ public $dirs = array();
30
+
31
+ /**
32
+ * The status of our test result.
33
+ *
34
+ * @since 1.5.1
35
+ * @access public
36
+ * @var mixed|bool|null
37
+ */
38
+ public static $test_result = null;
39
+
40
+ /**
41
+ * An array of any errors received while testing.
42
+ *
43
+ * @since 1.5.1
44
+ * @access public
45
+ * @var array
46
+ */
47
+ public $test_errors = array();
48
+
49
+ /**
50
+ * An instance of ZipArchive.
51
+ *
52
+ * @since 1.5.1
53
+ * @var ZipArchive
54
+ */
55
+ public $zip;
56
+
57
+ /**
58
+ * Constructor.
59
+ *
60
+ * @since 1.5.1
61
+ *
62
+ * @param Boldgrid_Backup_Admin_Core $core
63
+ */
64
+ public function __construct( $core ) {
65
+ parent::__construct( $core );
66
+ }
67
+
68
+ /**
69
+ * Add a file's directories to the zip.
70
+ *
71
+ * When you add a file, the parent directories are not always explicitly
72
+ * created. For example, if you add wp-content/themes/pavilion/index.php the
73
+ * wp-content directory (and so forth) is not explicity added to the zip.
74
+ *
75
+ * @since 1.5.4
76
+ *
77
+ * @param string $file
78
+ */
79
+ public function add_dir( $file ) {
80
+ $add_directory = '';
81
+ $dirs = explode( DIRECTORY_SEPARATOR, dirname( $file ) );
82
+
83
+ foreach ( $dirs as $key => $dir ) {
84
+ if ( 0 === $key ) {
85
+ $add_directory = $dir;
86
+ } else {
87
+ $add_directory .= '/' . $dir;
88
+ }
89
+
90
+ if ( ! in_array( $add_directory, $this->dirs, true ) ) {
91
+ $this->zip->addEmptyDir( $add_directory );
92
+ $this->dirs[] = $add_directory;
93
+ }
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Archive files.
99
+ *
100
+ * @since 1.5.1
101
+ *
102
+ * @param array $filelist See Boldgrid_Backup_Admin_Filelist::get_total_size
103
+ * @param array $info {
104
+ * An array of data about the backup archive we are generating.
105
+ *
106
+ * @type string mode backup
107
+ * @type bool dryrun
108
+ * @type string compressor php_zip
109
+ * @type ing filesize 0
110
+ * @type bool save 1
111
+ * @type int total_size 0
112
+ * }
113
+ */
114
+ public function archive_files( $filelist, &$info ) {
115
+ $info['filepath'] = $this->core->generate_archive_path( 'zip' );
116
+
117
+ if ( $info['dryrun'] ) {
118
+ return true;
119
+ }
120
+
121
+ $this->zip = new ZipArchive();
122
+
123
+ $status = $this->zip->open( $info['filepath'], ZipArchive::CREATE );
124
+
125
+ if ( ! $status ) {
126
+ return array(
127
+ 'error' => 'Cannot open ZIP archive file "' . $info['filepath'] . '".',
128
+ 'error_code' => $status,
129
+ 'error_message' => Boldgrid_Backup_Admin_Utility::translate_zip_error( $status ),
130
+ );
131
+ }
132
+
133
+ foreach ( $filelist as $fileinfo ) {
134
+ $is_dir = ! empty( $fileinfo[3] ) && 'd' === $fileinfo[3];
135
+
136
+ if ( $is_dir ) {
137
+ $this->zip->addEmptyDir( $fileinfo[1] );
138
+ } else {
139
+ $this->zip->addFile( $fileinfo[0], $fileinfo[1] );
140
+ $this->add_dir( $fileinfo[1] );
141
+ }
142
+ }
143
+
144
+ if ( ! $this->zip->close() ) {
145
+ return array(
146
+ 'error' => 'Cannot close ZIP archive file "' . $info['filepath'] . '".',
147
+ );
148
+ }
149
+
150
+ return true;
151
+ }
152
+
153
+ /**
154
+ * Determine if ZipArchive is available.
155
+ *
156
+ * @since 1.5.2
157
+ */
158
+ public static function is_available() {
159
+ return extension_loaded( 'zip' ) && class_exists( 'ZipArchive' );
160
+ }
161
+
162
+ /**
163
+ * Test the functionality of php_zip.
164
+ *
165
+ * @since 1.5
166
+ *
167
+ * @return bool
168
+ */
169
+ public function test() {
170
+ if ( null !== self::$test_result ) {
171
+ return self::$test_result;
172
+ }
173
+
174
+ $backup_dir = $this->core->backup_dir->get();
175
+
176
+ $test_file_contents = $str = __( 'This is a test file from BoldGrid Backup. You can delete this file.', 'boldgrid-backup' );
177
+ $cannot_open_zip = __( 'Unable to create zip file: %1$s', 'boldgrid-backup' );
178
+ $cannot_close_zip = __( 'When testing ZipArchive functionality, we are able to create a zip file and add files to it, but we were unable to close the zip file.<br /><strong>Please be sure the following backup directory has modify permissions</strong>:<br />%1$s', 'boldgrid-backup' );
179
+ $safe_to_delete = __( 'safe-to-delete', 'boldgrid-backup' );
180
+ $test_zip_file = $test_zip_file = $this->core->test->test_prefix . '-zip';
181
+ $test_filename = sprintf( '%1$s%5$s%2$s-%3$s-%4$s', $backup_dir, $test_zip_file, mt_rand(), $safe_to_delete, DIRECTORY_SEPARATOR );
182
+
183
+ $zip_filepath = $test_filename . '.zip';
184
+ $random_filename = $test_filename . '.txt';
185
+
186
+ $zip = new ZipArchive();
187
+ $status = $zip->open( $zip_filepath, ZipArchive::CREATE );
188
+ if ( ! $status ) {
189
+ $this->test_errors[] = sprintf( $cannot_open_zip, $zip_filepath );
190
+ self::$test_result = false;
191
+ return false;
192
+ }
193
+
194
+ $this->core->wp_filesystem->touch( $random_filename );
195
+ $this->core->wp_filesystem->put_contents( $random_filename, $test_file_contents );
196
+
197
+ $zip->addFile( $random_filename, 'test.txt' );
198
+
199
+ $zip_closed = @$zip->close();
200
+
201
+ $this->core->test->delete_test_files( $backup_dir );
202
+
203
+ if ( ! $zip_closed ) {
204
+ $this->test_errors[] = sprintf( $cannot_close_zip, $backup_dir );
205
+ self::$test_result = false;
206
+ return false;
207
+ }
208
+
209
+ self::$test_result = true;
210
+ return true;
211
+ }
212
+ }
admin/css/boldgrid-backup-admin-customizer.css ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ .control-panel-themes .customize-themes-notifications div.boldgrid-backup-protect-now,
2
+ .control-panel-themes .customize-themes-notifications div.boldgrid-backup-in-progress,
3
+ .control-panel-themes .customize-themes-notifications div.boldgrid-backup-complete,
4
+ .control-panel-themes .customize-themes-notifications div.boldgrid-backup-protected {
5
+ margin: 0px 25px 25px 25px;
6
+ }
admin/css/boldgrid-backup-admin-folder-exclude.css ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * This section contains code for the "Exclude Folder" interface on the Settings
3
+ * page.
4
+ */
5
+ .folder_exclude_help tr th {
6
+ vertical-align: top;
7
+ width: 70px;
8
+ }
9
+
10
+ .folder_exclude_help tr th,
11
+ .folder_exclude_help tr td {
12
+ padding: 5px 5px;
13
+ }
14
+
15
+ .folder_exclude_help .example {
16
+ font-style: italic;
17
+ color: #696969;
18
+ }
19
+
20
+ .form-table th h2 {
21
+ display: inline;
22
+ }
23
+ #folder_exclusion_filter {
24
+ margin: 3px 0px 0px 0px;
25
+ }
26
+ #exclude_folders_preview ul {
27
+ max-height: 300px;
28
+ overflow: auto;
29
+ background: #fff;
30
+ padding: 5px;
31
+ border: 1px solid #ddd;
32
+ clear: both;
33
+ }
34
+
35
+ /**
36
+ * This section contains the css for the "Exclude Folder" interface within a
37
+ * modal, such as the "Backup Site Now" modal.
38
+ */
39
+ #TB_ajaxContent {
40
+ background: #f1f1f1;
41
+ }
42
+ #TB_ajaxContent .form-table {
43
+ margin-top: 0px;
44
+ }
45
+ #TB_ajaxContent .form-table tr:first-of-type th,
46
+ #TB_ajaxContent .form-table tr:first-of-type td {
47
+ padding-top: 10px;
48
+ vertical-align:top;
49
+ }
50
+ #TB_ajaxContent .form-table td {
51
+ padding-bottom: 0px;
52
+ }
53
+ #TB_ajaxContent .form-table td p:first-of-type {
54
+ padding-top: 0px;
55
+ margin-top: 0px;
56
+ }
57
+ #TB_ajaxContent #exclude_folders_preview {
58
+ max-width: 420px;
59
+ }
60
+ #TB_ajaxContent .form-table th {
61
+ width: 100px;
62
+ }
63
+
64
+ /**
65
+ * This section includes code for the "include tables" database setting.
66
+ */
67
+ .include-tables,
68
+ #tables_to_include .tables {
69
+ margin-top: 15px;
70
+ }
71
+ .include-tables div {
72
+ float: left;
73
+ width: 300px;
74
+ white-space: nowrap;
75
+ overflow: hidden;
76
+ margin-right: 10px;
77
+ }
78
+
79
+ /**
80
+ * This section contains misc. code for the "Backup Site Now" modal.
81
+ */
82
+ .setting-section {
83
+ background:#efefef;
84
+ border:1px solid #ddd;
85
+ padding:0px 15px;
86
+ margin-bottom: 15px;
87
+ }
88
+ .setting-section.last {
89
+ margin-bottom: 65px;
90
+ }
91
+ #TB_ajaxContent .plugin-card-bottom {
92
+ position: absolute;
93
+ bottom: 0px;
94
+ left: 0px;
95
+ right: 0px;
96
+ padding: 0px 10px;
97
+ text-align: right;
98
+ }
99
+ .plugin-card-bottom #you_may_leave {
100
+ float: left;
101
+ margin-top: 5px;
102
+ }
103
+ .plugin-card-bottom .notice {
104
+ margin-bottom: 10px;
105
+ }
admin/css/boldgrid-backup-admin-ftp-settings.css ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ table [type="text"],
2
+ table [type="password"] {
3
+ width: 100%;
4
+ }
5
+
6
+ [type="number"] {
7
+ width: 75px;
8
+ }
admin/css/boldgrid-backup-admin-hide-all.css ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Hide all but content.
3
+ *
4
+ * This set of styles hides everything on the page except for what the plugin
5
+ * displays. This helps make the page look better in an iframe / modal.
6
+ */
7
+ #adminmenumain,
8
+ #wpadminbar,
9
+ #wpfooter {
10
+ display: none;
11
+ }
12
+ #wpwrap {
13
+ position: absolute;
14
+ top: 0px;
15
+ left: 0px;
16
+ }
17
+ #wpwrap #wpcontent {
18
+ margin: 0px; !important;
19
+ padding-left: 10px; !important;
20
+ padding-right: 10px; !important;
21
+ }
22
+ #wpbody-content {
23
+ padding-bottom: 0px;
24
+ }
admin/css/boldgrid-backup-admin-home.css ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #upload-archive-section .spinner {
2
+ display: none;
3
+ float: none !important;
4
+ }
5
+
6
+ .upload-plugin .help {
7
+ background: none;
8
+ padding-top: 0px;
9
+ padding-bottom: 0px;
10
+ border: 0px;
11
+ }
12
+
13
+ /* Disable floating of mine count so help section displays nicely. */
14
+ .subsubsub {
15
+ float: none;
16
+ }
admin/css/boldgrid-backup-admin-new-thickbox-style.css ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Thickbox styles.
3
+ *
4
+ * Thickbox appears when user clicks "View details" for a backup. These styles
5
+ * make the standard thickbox look more like the modal of a plugin's
6
+ * "View details" link.
7
+ */
8
+ body #TB_window,
9
+ #TB_window #TB_title {
10
+ background-color: rgba(0, 0, 0, 0);
11
+ border: 0px;
12
+ -webkit-box-shadow: none;
13
+ -moz-box-shadow: none;
14
+ box-shadow: none;
15
+ }
16
+ #TB_closeAjaxWindow #TB_closeWindowButton {
17
+ right: -29px;
18
+ top: 30px;
19
+ }
20
+ #TB_closeAjaxWindow .tb-close-icon:before {
21
+ font: normal 30px/30px dashicons;
22
+ color: #eee;
23
+ }
24
+ #TB_ajaxContent {
25
+ background: #fff;
26
+ }
admin/css/boldgrid-backup-admin-settings.css ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ /* If a day of the week checkbox is disabled, modify its hover cursor. */
2
+ .schedule-dow input[type='checkbox'][disabled]:hover {
3
+ cursor: not-allowed;
4
+ }
5
+
6
+ .form-table tr td p {
7
+ padding: 5px 0px;
8
+ margin: 0px;
9
+ }
admin/css/boldgrid-backup-admin-test.css ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .functionality-test-section table {
2
+ border-collapse: collapse;
3
+ }
4
+
5
+ .functionality-test-section table tr {
6
+ border: 1px solid #ddd;
7
+ }
8
+
9
+ .functionality-test-section table tr td {
10
+ padding: 5px 10px;
11
+ }
12
+
13
+ .functionality-test-section table tr td h2 {
14
+ margin-bottom: 5px;
15
+ margin-top: 30px;
16
+ }
17
+
18
+ /* The first tr will be the status (pass/fail) */
19
+ .functionality-test-section table tr:first-of-type {
20
+ background: none;
21
+ border: none;
22
+ font-weight: bold;
23
+ }
24
+
25
+ /* Highlight errors and warnings. */
26
+ .wp-list-table .error {
27
+ color: #dc3232;
28
+ font-weight: bold;
29
+ }
30
+ .wp-list-table .warning {
31
+ color: #ffba00;
32
+ font-weight: bold;
33
+ }
34
+ .wp-list-table .success {
35
+ color: green;
36
+ font-weight: bold;
37
+ }
38
+
39
+ .functionality-test-section tr.heading {
40
+ border: 0px;
41
+ background: transparent;
42
+ }
43
+
44
+ .functionality-test-section pre {
45
+ white-space: pre-line;
46
+ }
admin/css/boldgrid-backup-admin-zip-browser.css ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .listing .dashicons.dashicons-portfolio {
2
+ color: #c4963d;
3
+ }
4
+
5
+ .listing .dashicons.dashicons-media-default {
6
+ color: #0073aa;
7
+ }
8
+
9
+ .listing a,
10
+ a.restore-db,
11
+ a.view-db,
12
+ a.load-browser,
13
+ .breadcrumbs a {
14
+ cursor: pointer;
15
+ }
16
+
17
+ .breadcrumbs .dashicons.dashicons-admin-home {
18
+ vertical-align: text-bottom;
19
+ }
20
+
21
+ .file-actions > td {
22
+ padding-left:35px;
23
+ }
24
+
25
+ /* Mimic postbox toggle arrows. These generally are in the last thead th. */
26
+ .bulk-action-notice .toggle-indicator {
27
+ float: right;
28
+ cursor: pointer;
29
+ }
30
+ .bulk-action-notice .toggle-indicator.closed:before {
31
+ content: "\f140";
32
+ }
admin/css/boldgrid-backup-admin.css ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [id^=toplevel_page_boldgrid-backup] .wp-menu-image::before {
2
+ content: "\f178";
3
+ }
4
+
5
+ .dashicons-editor-help {
6
+ display: inline-block;
7
+ position: relative;
8
+ cursor: pointer;
9
+ }
10
+
11
+ .help[data-id] {
12
+ display: none;
13
+ }
14
+ th .help[data-id] {
15
+ font-weight: normal;
16
+ }
17
+
18
+ /* Create an inline spinner. */
19
+ .spinner.inline {
20
+ float: none;
21
+ visibility: visible;
22
+ vertical-align: bottom;
23
+ margin-left: 0px;
24
+ }
25
+ .spinner.inline.middle {
26
+ vertical-align: middle;
27
+ }
28
+ .spinner.hidden {
29
+ display: none;
30
+ }
31
+
32
+ /* When the user clicks to rollback site, show the spinner. */
33
+ #restore-now-section .spinner.is-active {
34
+ display: inline-block;
35
+ }
36
+
37
+ /* Prevent clicks on disabled links. */
38
+ a[disabled] {
39
+ pointer-events: none;
40
+ }
41
+
42
+ .bgbu-wait {
43
+ cursor: wait !important;
44
+ }
45
+
46
+ .dashicons-warning.red {
47
+ color: #dc3232;
48
+ }
49
+
50
+ .dashicons-warning.yellow {
51
+ color: #ffb900;
52
+ }
53
+
54
+ .dashicons.green {
55
+ color: #46b450;
56
+ }
57
+
58
+ div .dashicons {
59
+ vertical-align: bottom;
60
+ }
61
+
62
+ /* Dashicon in "mine count". */
63
+ .subsubsub .dashicons {
64
+ vertical-align: text-top;
65
+ }
66
+
67
+ /* The table of size data. */
68
+ #size-data table tr td {
69
+ vertical-align: top;
70
+ }
71
+
72
+ .total-size {
73
+ background: #ddd;
74
+ width: 325px;
75
+ }
76
+
77
+ .usage {
78
+ height: 5px;
79
+ max-width: 100% !important;
80
+ }
81
+
82
+ .usage.size-error {
83
+ background: #dc3232;
84
+ }
85
+
86
+ .usage.size-warning {
87
+ background: #ffb900;
88
+ }
89
+
90
+ .usage.size-success {
91
+ background: #46b450;
92
+ }
93
+
94
+ /* Format a table within a table. */
95
+ .form-table table td {
96
+ padding: 0px 10px 0px 0px;
97
+ }
98
+
99
+ /* Clean up headers we place in any admin notices. */
100
+ .notice .header-notice {
101
+ margin-top: 10px;
102
+ }
103
+
104
+ hr.separator {
105
+ margin: 30px 0px;
106
+ }
107
+ hr.separator-small {
108
+ margin: 20px 0px;
109
+ }
110
+
111
+ /* Get Premium link in the left nav. */
112
+ .toplevel_page_boldgrid-backup-settings .get-premium,
113
+ .toplevel_page_boldgrid-backup-settings .dashicons-dashboard {
114
+ color: #ffb900;
115
+ }
116
+
117
+ /* Needed style for bouncing help icon in mine count. */
118
+ .subsubsub .ui-effects-wrapper {
119
+ display: inline-block;
120
+ }
121
+
122
+ /* WordPress by default has both the h2 and h3 tags set to the same font size. */
123
+ h3 {
124
+ font-size: 1.2em;
125
+ }
126
+
127
+ input:invalid {
128
+ border: 1px solid red;
129
+ }
admin/index.php ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <?php
2
+ // Silence is golden.
admin/js/boldgrid-backup-admin-archive-actions.js ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BoldGrid Backup Admin Archive Actions.
3
+ *
4
+ * @summary JavaScript to handle archive actions.
5
+ *
6
+ * @since 1.5.4
7
+ */
8
+
9
+ /* global ajaxurl,BoldGridBackupAdminArchiveActions,jQuery */
10
+
11
+ var BOLDGRID = BOLDGRID || {};
12
+ BOLDGRID.BACKUP = BOLDGRID.BACKUP || {};
13
+
14
+ BOLDGRID.BACKUP.ACTIONS = function( $ ) {
15
+ var $body,
16
+ $wpbbody,
17
+ self = this,
18
+ lang = BoldGridBackupAdminArchiveActions;
19
+
20
+ /**
21
+ * @summary Confirm to delete a selected backup archive file.
22
+ *
23
+ * This function was originally in admin-home.js as of 1.0, but moved here
24
+ * as of 1.5.4.
25
+ *
26
+ * @since 1.5.4
27
+ */
28
+ self.onClickDelete = function( e ) {
29
+ var confirmResponse,
30
+ $form = $( this ).closest( 'form' ),
31
+ archiveFilename = $form.find( '[name="archive_filename"]' ).val(),
32
+ $spinner = $form.find( '.spinner' );
33
+
34
+ confirmResponse = confirm( lang.deleteConfirmText + ' "' + archiveFilename + '"' );
35
+
36
+ if ( ! confirmResponse ) {
37
+ return false;
38
+ }
39
+
40
+ $spinner.addClass( 'inline' );
41
+ $wpbody.bgbuDisableActions();
42
+
43
+ $form.submit();
44
+ return false;
45
+ };
46
+
47
+ /**
48
+ * @summary Download a selected backup archive file.
49
+ *
50
+ * This function was originally in admin-home.js as of 1.0, but moved here
51
+ * as of 1.5.4.
52
+ *
53
+ * @since 1.5.4
54
+ */
55
+ self.downloadArchive = function( e ) {
56
+ var downloadKey,
57
+ downloadFilename,
58
+ downloadFilepath,
59
+ data,
60
+ form,
61
+ $formDom,
62
+ $this = $( this );
63
+
64
+ downloadKey = $this.data( 'key' );
65
+ downloadFilename = $this.data( 'filename' );
66
+ downloadFilepath = $this.data( 'filepath' );
67
+
68
+ // If the wp_filesystem method is not "direct", then show a message and return.
69
+ if ( 'direct' !== lang.accessType ) {
70
+ alert(
71
+ 'Wordpress filesystem access method is not direct; it is set to \'' +
72
+ lang.accessType +
73
+ '\'.\n\nYou can download the archive file using another method, such as FTP.\n\n' +
74
+ 'The backup archive file path is: ' +
75
+ downloadFilepath
76
+ );
77
+
78
+ e.preventDefault();
79
+ return;
80
+ }
81
+
82
+ data = {
83
+ action: 'download_archive_file',
84
+ download_key: downloadKey,
85
+ download_filename: downloadFilename,
86
+ wpnonce: lang.archiveNonce
87
+ };
88
+
89
+ // Create a hidden form to request the download.
90
+ form =
91
+ '<form id=\'download-now-form\' class=\'hidden\' method=\'POST\' action=\'' +
92
+ ajaxurl +
93
+ '\' target=\'_blank\'>';
94
+ Object.keys( data ).forEach( function( key ) {
95
+ form += '<input type=\'hidden\' name=\'' + key + '\' value=\'' + data[key] + '\' />';
96
+ } );
97
+ form += '</form>';
98
+
99
+ $formDom = $( form );
100
+
101
+ $formDom.appendTo( 'body' ).submit();
102
+
103
+ e.preventDefault();
104
+ };
105
+
106
+ /**
107
+ * @summary Confirm to restore a selected backup archive file.
108
+ *
109
+ * This function was originally in admin-home.js as of 1.0, but moved here
110
+ * as of 1.5.4.
111
+ *
112
+ * @since 1.5.4
113
+ */
114
+ self.restoreArchiveConfirm = function() {
115
+ var confirmResponse,
116
+ restoreConfirmText,
117
+ $this = $( this ),
118
+ filename = $this.attr( 'data-archive-filename' ),
119
+ data = {
120
+ action: 'boldgrid_backup_restore_archive',
121
+ restore_now: $this.attr( 'data-restore-now' ),
122
+ archive_key: $this.attr( 'data-archive-key' ),
123
+ archive_filename: filename,
124
+ archive_auth: $this.attr( 'data-nonce' )
125
+ },
126
+ $spinner = $this.next( '.spinner' );
127
+
128
+ restoreConfirmText = lang.restoreConfirmText.replace( '%s', filename );
129
+ confirmResponse = confirm( restoreConfirmText );
130
+
131
+ if ( true === confirmResponse ) {
132
+ $spinner.addClass( 'inline' );
133
+ $wpbody.bgbuDisableActions();
134
+
135
+ $.post( ajaxurl, data, function( response ) {
136
+ var redirectUrl =
137
+ response.data !== undefined && response.data.redirect_url !== undefined ?
138
+ response.data.redirect_url :
139
+ false;
140
+
141
+ if ( redirectUrl ) {
142
+ window.location.href = redirectUrl;
143
+ } else {
144
+ location.reload();
145
+ }
146
+ } ).error( function() {
147
+ location.reload();
148
+ } );
149
+ }
150
+
151
+ return false;
152
+ };
153
+
154
+ $( function() {
155
+ $body = $( 'body' );
156
+ $wpbody = $body.find( '#wpbody' );
157
+
158
+ $body.on( 'click', '.action-download', self.downloadArchive );
159
+ $body.on( 'click', '.restore-now', self.restoreArchiveConfirm );
160
+ $body.on( 'click', '#delete-action a', self.onClickDelete );
161
+ } );
162
+ };
163
+
164
+ new BOLDGRID.BACKUP.ACTIONS( jQuery );
admin/js/boldgrid-backup-admin-archive-details.js ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Summary
3
+ *
4
+ * @summary JS for all admin backup pages.
5
+ *
6
+ * @since 1.3.3
7
+ */
8
+
9
+ /* global ajaxurl,BoldGridBackupAdmin,jQuery */
10
+
11
+ var BoldGrid = BoldGrid || {};
12
+
13
+ BoldGrid.ArchiveDetails = function( $ ) {
14
+ var self = this,
15
+ $body = $( 'body' ),
16
+ $contentWrap = $( '#wp-content-wrap' ),
17
+ $downloadFirst = $body.find( '#download_first' ),
18
+ $editorTabs = $body.find( '.wp-editor-tabs button' ),
19
+ adminLang = BoldGridBackupAdmin.lang,
20
+ lang = boldgrid_backup_archive_details;
21
+
22
+ /**
23
+ * @summary Handle the click of the Upload button.
24
+ */
25
+ self.onClickUpload = function() {
26
+ var $a = $( this ),
27
+ provider = $a.attr( 'data-provider-id' ),
28
+ data = {
29
+ action: 'boldgrid_backup_remote_storage_upload_' + provider,
30
+ filename: $( '#filename' ).val(),
31
+ security: $( '#_wpnonce' ).val()
32
+ },
33
+ failUpload;
34
+
35
+ /*
36
+ * @summary Action to take when an upload fails.
37
+ *
38
+ * @param {Object} response Our ajax response.
39
+ */
40
+ failUpload = function( response ) {
41
+ var defaultMessage = adminLang.xmark + ' ' + lang.failUpload,
42
+ dataNotEmpty =
43
+ response !== undefined && response.data !== undefined && '' !== response.data,
44
+ message = dataNotEmpty ? defaultMessage + ' ' + response.data : defaultMessage;
45
+
46
+ $a.parent().html( message );
47
+ };
48
+
49
+ $a
50
+ .attr( 'disabled', 'disabled' )
51
+ .text( lang.uploading + '...' )
52
+ .after( ' <span class="spinner inline"></span>' );
53
+
54
+ $.post( ajaxurl, data, function( response ) {
55
+ $a.next( '.spinner' ).remove();
56
+
57
+ if ( response.success !== undefined && true === response.success ) {
58
+ $a.after( '&#10003; ' + lang.uploaded ).remove();
59
+ }
60
+
61
+ if ( response.success === undefined || true !== response.success ) {
62
+ failUpload( response );
63
+ }
64
+ } ).error( function( response ) {
65
+ failUpload();
66
+ } );
67
+
68
+ return false;
69
+ };
70
+
71
+ /**
72
+ * @summary Action to take when a user clicks download.
73
+ *
74
+ * @since 1.5.4
75
+ */
76
+ self.onClickDownload = function() {
77
+ var $button = $( this ),
78
+ provider = $button.attr( 'data-provider-id' ),
79
+ data = {
80
+ action: 'boldgrid_backup_remote_storage_download_' + provider,
81
+ filename: $( '#filename' ).val(),
82
+ security: $( '#_wpnonce' ).val()
83
+ },
84
+ $spinner = $button.next( '.spinner' ),
85
+ $wpbody = $body.find( '#wpbody' );
86
+
87
+ $spinner.addClass( 'inline' );
88
+
89
+ $wpbody.bgbuDisableActions();
90
+
91
+ $.post( ajaxurl, data, function( response ) {
92
+ location.reload();
93
+ } ).error( function() {
94
+ location.reload();
95
+ } );
96
+ };
97
+
98
+ /**
99
+ * @summary Action to take when the user clicks the "download remote" button.
100
+ *
101
+ * This method downloads the first remote archive it finds.
102
+ *
103
+ * @since 1.5.4
104
+ */
105
+ self.onClickDownloadFirst = function() {
106
+ var $downloadToServer = $body.find( '.download-to-server' ),
107
+ $spinner = $( this ).next( '.spinner' );
108
+
109
+ $spinner.addClass( 'inline' );
110
+
111
+ $downloadToServer
112
+ .first()
113
+ .click()
114
+
115
+ // Remvoe the spinner so we don't have two spinners going at same time.
116
+ .next( '.spinner' )
117
+ .remove();
118
+ };
119
+
120
+ /**
121
+ * @summary Action to take when a tab is clicked on.
122
+ *
123
+ * These are the "Files & Folders" and "Database" tabs.
124
+ *
125
+ * @since 1.5.4
126
+ */
127
+ self.onClickTab = function() {
128
+ var $dbElements = $( '[data-view-type="db"]' ),
129
+ $fileElements = $( '[data-view-type="file"]' ),
130
+ view;
131
+
132
+ $contentWrap.toggleClass( 'html-active tmce-active' );
133
+
134
+ view = $contentWrap.hasClass( 'html-active' ) ? 'db' : 'file';
135
+
136
+ switch ( view ) {
137
+ case 'file':
138
+ $dbElements.hide();
139
+ $fileElements.show();
140
+
141
+ break;
142
+ case 'db':
143
+ BoldGrid.ZipBrowser.onClickViewDb();
144
+
145
+ $dbElements.show();
146
+ $fileElements.hide();
147
+
148
+ break;
149
+ }
150
+ };
151
+
152
+ /**
153
+ * Init.
154
+ */
155
+ $( function() {
156
+ $body.on( 'click', '.remote-storage a.upload', self.onClickUpload );
157
+ $body.on( 'click', '.remote-storage .download-to-server', self.onClickDownload );
158
+ $editorTabs.on( 'click', self.onClickTab );
159
+ $downloadFirst.on( 'click', self.onClickDownloadFirst );
160
+ } );
161
+ };
162
+
163
+ BoldGrid.ArchiveDetails( jQuery );
admin/js/boldgrid-backup-admin-backup-now.js ADDED
@@ -0,0 +1,236 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BoldGrid Backup Admin Backup Now.
3
+ *
4
+ * JavaScript for handling "backup" buttons. This code was initially contained
5
+ * within the home.js, but separated out as of 1.6.0 for reusability.
6
+ *
7
+ * @since 1.6.0
8
+ *
9
+ * @param $ The jQuery object.
10
+ */
11
+
12
+ /* global ajaxurl,jQuery,localizeScriptData */
13
+
14
+ var BOLDGRID = BOLDGRID || {};
15
+ BOLDGRID.BACKUP = BOLDGRID.BACKUP || {};
16
+
17
+ BOLDGRID.BACKUP.BackupNow = function( $ ) {
18
+ 'use strict';
19
+
20
+ var self = this,
21
+ lang = localizeScriptData,
22
+ $backupNowType = $( '[name="folder_exclusion_type"]' ),
23
+ $tablesType = $( '[name="table_inclusion_type"]' );
24
+
25
+ $( function() {
26
+ $( 'body' ).on( 'click', '#backup-site-now', self.backupNow );
27
+
28
+ $( 'body' ).on( 'boldgrid_backup_complete', self.updateProtectionEnabled );
29
+ } );
30
+
31
+ /**
32
+ * Perform a backup now.
33
+ *
34
+ * @since 1.0
35
+ */
36
+ self.backupNow = function( e ) {
37
+
38
+ // Declare variables.
39
+ var $this,
40
+ $backupSiteSection,
41
+ $backupSiteResults,
42
+ backupNonce,
43
+ wpHttpReferer,
44
+ isUpdating,
45
+ errorCallback,
46
+ successCallback,
47
+ data,
48
+ markup,
49
+ $folderExclude = $( '[name="folder_exclusion_exclude"]' ),
50
+ $folderInclude = $( '[name="folder_exclusion_include"]' ),
51
+ $tableInclude = $( '[name="include_tables[]"]' ),
52
+ includeTables = [],
53
+ type = 'full',
54
+ tablesType = null;
55
+
56
+ /*
57
+ * If we are in a Backup Site Now modal and there is a "type" value set,
58
+ * grab it.
59
+ */
60
+ if ( 1 === $backupNowType.filter( ':checked' ).length ) {
61
+ type = $backupNowType.filter( ':checked' ).val();
62
+ }
63
+
64
+ if ( 1 === $tablesType.filter( ':checked' ).length ) {
65
+ tablesType = $tablesType.filter( ':checked' ).val();
66
+ }
67
+
68
+ // Assign the current jQuery object.
69
+ $this = $( this );
70
+
71
+ // Disable the Backup Site Now link button.
72
+ $this.attr( 'disabled', 'disabled' ).css( 'pointer-events', 'none' );
73
+
74
+ // Create a context selector for the Backup Site Now section.
75
+ $backupSiteSection = $( '#backup-site-now-section' );
76
+
77
+ // Create a context selector for the Backup Site Now results.
78
+ $backupSiteResults = $( '#backup-site-now-results' );
79
+
80
+ $( '#TB_ajaxContent' )
81
+ .find( 'input' )
82
+ .attr( 'disabled', true )
83
+ .end()
84
+ .find( 'button' )
85
+ .attr( 'disabled', true )
86
+ .end();
87
+
88
+ $( '#you_may_leave' ).fadeIn();
89
+
90
+ // Get the wpnonce and referer values.
91
+ backupNonce = $backupSiteSection.find( '#backup_auth' ).val();
92
+
93
+ wpHttpReferer = $backupSiteSection.find( '[name="_wp_http_referer"]' ).val();
94
+
95
+ // Get the backup archive file key.
96
+ isUpdating = $this.data( 'updating' );
97
+
98
+ $backupSiteSection.find( '.spinner' ).addClass( 'inline' );
99
+
100
+ /**
101
+ * @summary backupNow error callback.
102
+ *
103
+ * @since 1.0
104
+ *
105
+ * @param object jqXHR
106
+ * @param string textStatus
107
+ * @param string errorThrown
108
+ */
109
+ errorCallback = function( jqXHR, textStatus, errorThrown ) {
110
+ var data,
111
+ errorText = lang.errorText;
112
+
113
+ /*
114
+ * As of 1.5.2, we are hooking into the shutdown and checking for
115
+ * errors. If a fatal error is found, we will return that, rather
116
+ * than the generic errorText defined above.
117
+ */
118
+ if ( jqXHR.responseText !== undefined && '{' === jqXHR.responseText.charAt( 0 ) ) {
119
+ data = JSON.parse( jqXHR.responseText );
120
+
121
+ if ( data !== undefined && data.data !== undefined && data.data.errorText !== undefined ) {
122
+ errorText = data.data.errorText;
123
+ }
124
+ }
125
+
126
+ // Show error message.
127
+ markup = '<div class="notice notice-error"><p>' + errorText + '</p></div>';
128
+
129
+ $backupSiteResults.html( markup );
130
+ };
131
+
132
+ /**
133
+ * @summary backupNow success callback.
134
+ *
135
+ * @since 1.5.3
136
+ */
137
+ successCallback = function( response ) {
138
+ var data = JSON.parse( response ),
139
+ success = data.success !== undefined && true === data.success,
140
+ callback =
141
+ success && data.data !== undefined && data.data.callback !== undefined ?
142
+ data.data.callback :
143
+ null;
144
+
145
+ switch ( callback ) {
146
+ case 'updateProtectionEnabled':
147
+ self.updateProtectionEnabled();
148
+ break;
149
+ case 'reload':
150
+ location.reload();
151
+ break;
152
+ }
153
+ };
154
+
155
+ // Generate the data array.
156
+ data = {
157
+ action: 'boldgrid_backup_now',
158
+ backup_auth: backupNonce,
159
+ _wp_http_referer: wpHttpReferer,
160
+ is_updating: isUpdating,
161
+ backup_now: '1',
162
+ folder_exclusion_type: type
163
+ };
164
+
165
+ /*
166
+ * The next few conditionals are used in the Backup Site Now modal. If we
167
+ * are doing a customized backup, send appropriate "include / exclude"
168
+ * settings for "folder / database".
169
+ */
170
+ if ( 'custom' === type && 1 === $folderInclude.length ) {
171
+ data.folder_exclusion_include = $folderInclude.val();
172
+ }
173
+
174
+ if ( 'custom' === type && 1 === $folderExclude.length ) {
175
+ data.folder_exclusion_exclude = $folderExclude.val();
176
+ }
177
+
178
+ if ( tablesType ) {
179
+ data.table_inclusion_type = tablesType;
180
+ }
181
+
182
+ if ( 'custom' === tablesType && $tableInclude.length ) {
183
+ $tableInclude.filter( ':checked' ).each( function() {
184
+ includeTables.push( $( this ).val() );
185
+ } );
186
+ data.include_tables = includeTables;
187
+ }
188
+
189
+ if ( undefined !== BOLDGRID.BACKUP.UpdateSelectors ) {
190
+ BOLDGRID.BACKUP.UpdateSelectors.disable();
191
+ }
192
+
193
+ // Make the call.
194
+ $.ajax( {
195
+ url: ajaxurl,
196
+ data: data,
197
+ type: 'post',
198
+ dataType: 'text',
199
+ success: successCallback,
200
+ error: errorCallback,
201
+ complete: function() {
202
+
203
+ // Hide the spinner.
204
+ $backupSiteSection.find( '.spinner' ).removeClass( 'is-active' );
205
+
206
+ if ( undefined !== BOLDGRID.BACKUP.UpdateSelectors ) {
207
+ BOLDGRID.BACKUP.UpdateSelectors.enable();
208
+ }
209
+ }
210
+ } );
211
+
212
+ // Prevent default browser action.
213
+ e.preventDefault();
214
+ };
215
+
216
+ /**
217
+ * @summary Show notice after backup and upgrade protection now enabled.
218
+ *
219
+ * This updates the current notice rather than generates a new one.
220
+ *
221
+ * @since 1.5.3
222
+ */
223
+ self.updateProtectionEnabled = function() {
224
+ var $notice = $( '#backup-site-now-results' ).closest( '.notice' ),
225
+ $status = $notice.find( '#protection_enabled' ),
226
+ $backupNow = $( '#backup-site-now-section' );
227
+
228
+ $notice.removeClass( 'notice-warning' ).addClass( 'notice-success' );
229
+
230
+ $status.html( lang.updateProtectionActivated );
231
+
232
+ $backupNow.html( '<p>' + lang.backupCreated + '</p>' );
233
+ };
234
+ };
235
+
236
+ BOLDGRID.BACKUP.BackupNow( jQuery );
admin/js/boldgrid-backup-admin-customizer.js ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BoldGrid Backup Admin Customizer.
3
+ *
4
+ * Javascript for the Custoimzer. Used for backup protection before updating
5
+ * themes.
6
+ *
7
+ * @since 1.6.0
8
+ */
9
+
10
+ /* global ajaxurl,boldgridBackupCustomizer,jQuery,wp */
11
+
12
+ var BOLDGRID = BOLDGRID || {};
13
+ BOLDGRID.BACKUP = BOLDGRID.BACKUP || {};
14
+
15
+ BOLDGRID.BACKUP.CUSTOMIZER = function( $ ) {
16
+ var self = this;
17
+
18
+ self.protectNoticeShow = false;
19
+ self.progressNoticeShow = false;
20
+
21
+ /**
22
+ * @summary If applicable, show a "Backup in progress" notice when the user
23
+ * clicks on themes.
24
+ *
25
+ * @since 1.6.0
26
+ */
27
+ self.showProgressNotice = function() {
28
+ wp.customize.section( 'installed_themes', function( section ) {
29
+
30
+ // Actions to take when installed_themes section is opened.
31
+ section.expanded.bind( function() {
32
+ if ( ! self.progressNoticeShow ) {
33
+ var data = {
34
+ action: 'boldgrid_backup_get_progress_notice',
35
+ nonce: boldgridBackupCustomizer.nonce
36
+ },
37
+ successCallback;
38
+
39
+ /**
40
+ * @summary Success callback.
41
+ *
42
+ * @since 1.6.0
43
+ */
44
+ successCallback = function( response ) {
45
+ if (
46
+ response.success !== undefined &&
47
+ true === response.success &&
48
+ false !== response.data
49
+ ) {
50
+ $( '.customize-themes-notifications' ).prepend( response.data );
51
+ $( 'body' ).trigger( 'boldgrid_backup_progress_notice_added' );
52
+
53
+ self.protectNoticeShow = true;
54
+ }
55
+ };
56
+
57
+ // Make a call to see if we have an "in progress" notice to show.
58
+ $.ajax( {
59
+ url: ajaxurl,
60
+ data: data,
61
+ type: 'post',
62
+ dataType: 'json',
63
+ success: successCallback
64
+ } );
65
+ }
66
+ } );
67
+ } );
68
+ };
69
+
70
+ /**
71
+ * Show the "protect" notice.
72
+ *
73
+ * @since 1.6.0
74
+ */
75
+ self.showProtectNotice = function() {
76
+ wp.customize.section( 'installed_themes', function( section ) {
77
+
78
+ // Actions to take when installed_themes section is opened.
79
+ section.expanded.bind( function() {
80
+
81
+ // Show the "protect now" notice.
82
+ if ( ! self.protectNoticeShow ) {
83
+ var data = {
84
+ action: 'boldgrid_backup_get_protect_notice',
85
+ update_protection: true
86
+ };
87
+
88
+ $.post( ajaxurl, data, function( response ) {
89
+ if ( response.success !== undefined && true === response.success ) {
90
+ $( '.customize-themes-notifications' ).append( response.data );
91
+ self.protectNoticeShow = true;
92
+ }
93
+ } );
94
+ }
95
+ } );
96
+ } );
97
+ };
98
+
99
+ $( function() {
100
+ var deadline;
101
+
102
+ if ( boldgridBackupCustomizer.is_rollback_enabled ) {
103
+ deadline = BOLDGRID.BACKUP.RollbackTimer.getUpdatedDeadline();
104
+ }
105
+
106
+ self.showProgressNotice();
107
+
108
+ // Wait until we have the deadline.
109
+ $( 'body' ).on( 'boldgrid-backup-have-deadline', function() {
110
+ var haveDeadline =
111
+ deadline !== undefined &&
112
+ deadline.responseText !== undefined &&
113
+ '' !== deadline.responseText,
114
+ haveUpdatesAvailable = 0 < boldgridBackupCustomizer.update_data.counts.themes;
115
+
116
+ if ( haveDeadline ) {
117
+ BOLDGRID.BACKUP.RollbackTimer.show();
118
+ } else if ( haveUpdatesAvailable ) {
119
+ self.showProtectNotice();
120
+ }
121
+ } );
122
+ } );
123
+ };
124
+
125
+ BOLDGRID.BACKUP.CUSTOMIZER( jQuery );
admin/js/boldgrid-backup-admin-folder-exclude.js ADDED
@@ -0,0 +1,413 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BoldGrid Backup Folder Exclude.
3
+ *
4
+ * @summary JavaScript for handling Folder Exclude settings..
5
+ *
6
+ * @since 1.5.4
7
+ *
8
+ * @param $ The jQuery object.
9
+ */
10
+
11
+ /* global BoldGridBackupAdmin,BoldGridBackupAdminFolderExclude,ajaxurl,jQuery */
12
+
13
+ var BoldGrid = BoldGrid || {};
14
+
15
+ BoldGrid.FolderExclude = function( $ ) {
16
+ 'use strict';
17
+
18
+ var self = this,
19
+ exclusionList = null,
20
+ filteredList = [],
21
+ lang = BoldGridBackupAdminFolderExclude,
22
+ $container = $( '#folder_exclusion' ),
23
+ $excludeFoldersPreview = $container.find( '#exclude_folders_preview' ),
24
+ $inputInclude = $container.find( '[name="folder_exclusion_include"]' ),
25
+ $inputExclude = $container.find( '[name="folder_exclusion_exclude"]' ),
26
+ $status = $container.find( '.status' ),
27
+ $filter = $container.find( '#folder_exclusion_filter' ),
28
+ $ul = $excludeFoldersPreview.find( 'ul' ),
29
+ $type = $container.find( '[name="folder_exclusion_type"]' ),
30
+ $trs = $container.find( '.form-table tbody tr' );
31
+
32
+ /**
33
+ *
34
+ */
35
+ self.bounceHelp = function() {
36
+ var $icon = $container.find( '.dashicons-editor-help' );
37
+
38
+ $icon.bgbuDrawAttention();
39
+ };
40
+
41
+ /**
42
+ *
43
+ */
44
+ self.isUsingDefaults = function() {
45
+ return (
46
+ $inputInclude.val().trim() === lang.default_include &&
47
+ $inputExclude.val().trim() === lang.default_exclude
48
+ );
49
+ };
50
+
51
+ /**
52
+ *
53
+ */
54
+ self.onClickConfigure = function() {
55
+ var $a = $( this ),
56
+ $table = $a.closest( 'table' ),
57
+ $icon = $a.siblings( '.dashicons' ),
58
+ status;
59
+
60
+ status = self.isUsingDefaults() ? 'yes' : 'warning';
61
+
62
+ $icon.toggle();
63
+
64
+ $table
65
+ .children( 'tbody' )
66
+ .children( 'tr:not(:first)' )
67
+ .toggle();
68
+
69
+ return false;
70
+ };
71
+
72
+ /**
73
+ * @summary Handle the click of the pagination button.s
74
+ *
75
+ * @since 1.5.4
76
+ */
77
+ self.onClickPagination = function() {
78
+ var $a = $( this ),
79
+ $links = $a.closest( '.pagination-links' ),
80
+ currentPage = parseInt( $links.find( '.current-page' ).val() ),
81
+ totalPages = parseInt( $links.find( '.total-pages' ).html() );
82
+
83
+ if ( $a.hasClass( 'first' ) ) {
84
+ self.renderList( 1 );
85
+ } else if ( $a.hasClass( 'prev' ) ) {
86
+ self.renderList( currentPage - 1 );
87
+ } else if ( $a.hasClass( 'next' ) ) {
88
+ self.renderList( currentPage + 1 );
89
+ } else if ( $a.hasClass( 'last' ) ) {
90
+ self.renderList( totalPages );
91
+ }
92
+
93
+ return false;
94
+ };
95
+
96
+ /**
97
+ * @summary Handle the click of the preview button.
98
+ *
99
+ * @since 1.5.4
100
+ */
101
+ self.onClickPreview = function() {
102
+ var data = {
103
+ action: 'boldgrid_backup_exclude_folders_preview',
104
+ security: $container.find( '[name="folder_exclusion_nonce"]' ).val(),
105
+ include: $inputInclude.val(),
106
+ exclude: $inputExclude.val()
107
+ };
108
+
109
+ exclusionList = [];
110
+ $filter.val( '' );
111
+
112
+ // Show the status area and indicate we're loading.
113
+ $status.removeClass( 'hidden' ).html( BoldGridBackupAdmin.spinner_loading );
114
+
115
+ // Hide the preview area.
116
+ $excludeFoldersPreview.addClass( 'hidden' );
117
+
118
+ $.post( ajaxurl, data, function( response ) {
119
+ var success = null;
120
+ if ( response.success !== undefined ) {
121
+ success = response.success;
122
+ }
123
+
124
+ if ( success ) {
125
+ $status.empty();
126
+ exclusionList = response.data;
127
+ self.renderList( 1 );
128
+ } else if ( false === success ) {
129
+ $status.html( response.data );
130
+ } else {
131
+ $status.html( 'Unknown error' );
132
+ }
133
+ } ).error( function() {
134
+ $status.html( 'Unknown error' );
135
+ } );
136
+
137
+ return false;
138
+ };
139
+
140
+ /**
141
+ * @summary Handle the click of one of the samples.
142
+ *
143
+ * @since 1.5.4
144
+ */
145
+ self.onClickSample = function() {
146
+ var $button = $( this ),
147
+ include = $button.attr( 'data-include' ),
148
+ exclude = $button.attr( 'data-exclude' );
149
+
150
+ $inputInclude.val( include ).bgbuDrawAttention();
151
+
152
+ $inputExclude.val( exclude ).bgbuDrawAttention();
153
+
154
+ self.toggleStatus();
155
+
156
+ return false;
157
+ };
158
+
159
+ /**
160
+ * @summary Action to take when backup type has been changed.
161
+ *
162
+ * @since 1.5.4
163
+ */
164
+ self.onChangeType = function() {
165
+ self.toggleConfig();
166
+ };
167
+
168
+ /**
169
+ * @summary Process any key downs.
170
+ *
171
+ * The preview area's pagination has in input box where you can specify a
172
+ * page to jump to. When you enter a number and hit enter, it the browser is
173
+ * actually clicking the preview button (which we don't want it to do). This
174
+ * seems like a stange approach to take, but what we're doing in this
175
+ * function is listening to all key downs on the page. If you're not in the
176
+ * .current-page input, then we do nothing. Otherwise, we prevent default
177
+ * action and do the pagination.
178
+ *
179
+ * @since 1.5.4
180
+ */
181
+ self.onKeyDown = function( e ) {
182
+ var isCurrentPage = $( e.target ).hasClass( 'current-page' );
183
+
184
+ if ( isCurrentPage && 13 === e.keyCode ) {
185
+ self.onSubmitPagination();
186
+ e.preventDefault();
187
+ return false;
188
+ }
189
+
190
+ return true;
191
+ };
192
+
193
+ /**
194
+ * @summary Handle pagination.
195
+ *
196
+ * @since 1.5.4
197
+ */
198
+ self.onSubmitPagination = function() {
199
+ var page = parseInt( $excludeFoldersPreview.find( '.current-page' ).val() ),
200
+ totalPages = parseInt( $container.find( '.total-pages' ).html() );
201
+
202
+ page = 1 > page || page > totalPages ? 1 : page;
203
+
204
+ self.renderList( page );
205
+
206
+ return false;
207
+ };
208
+
209
+ /**
210
+ * @summary Render the list of files that will be backed up.
211
+ *
212
+ * Please note that there may be two lists involved. exclusionList is the
213
+ * main list involved, this is the list we get from the server. If the user
214
+ * has typed into the filter box though, filteredList will be genereated
215
+ * based on the filtered values.
216
+ *
217
+ * @since 1.5.4
218
+ *
219
+ * @todo Possibly move this toward a template system. For now, it works.
220
+ *
221
+ * @param int page The page of results to render.
222
+ */
223
+ self.renderList = function( page ) {
224
+ var startKey,
225
+ perPage = 100,
226
+ lastRecordKey,
227
+ lastAvailableKey = exclusionList.length - 1,
228
+ markup = '',
229
+ x,
230
+ filterVal = $filter.val(),
231
+ filteredNoResults,
232
+ file;
233
+
234
+ page = isNaN( page ) ? 1 : page;
235
+
236
+ // If the user has typed in a filter, then filter our list.
237
+ filteredList = [];
238
+ if ( '' !== filterVal ) {
239
+ exclusionList.forEach( function( file ) {
240
+ if ( -1 !== file.indexOf( filterVal ) ) {
241
+ filteredList.push( file );
242
+ }
243
+ } );
244
+
245
+ lastAvailableKey = filteredList.length - 1;
246
+ }
247
+
248
+ startKey = page * perPage - perPage;
249
+ lastRecordKey = startKey + perPage - 1;
250
+
251
+ /*
252
+ * Action to take if our last record is [99] and our last available
253
+ * record is [50].
254
+ */
255
+ if ( lastRecordKey > lastAvailableKey ) {
256
+ lastRecordKey = lastAvailableKey;
257
+ startKey = lastRecordKey - perPage;
258
+ }
259
+
260
+ // Configure our starting record.
261
+ if ( 0 > startKey ) {
262
+ startKey = 0;
263
+ }
264
+
265
+ // Generate the markup for our list.
266
+ for ( x = startKey; x <= lastRecordKey; x++ ) {
267
+ file = 0 < filteredList.length ? filteredList[x] : exclusionList[x];
268
+
269
+ markup += '<li>' + '<strong>' + ( x + 1 ).toLocaleString( 'en' ) + '</strong>. ' + file + '</li>';
270
+ }
271
+ filteredNoResults = '' !== filterVal && 0 === filteredList.length;
272
+ markup = filteredNoResults ? lang.no_results : markup;
273
+
274
+ $ul.html( markup );
275
+
276
+ self.renderPagination( page, 100 );
277
+ };
278
+
279
+ /**
280
+ * @summary Render the pagination controls.
281
+ *
282
+ * @todo Possibly move this toward a template system. For now, it works.
283
+ *
284
+ * @since 1.5.4
285
+ *
286
+ * @param int page
287
+ * @param int perPage
288
+ */
289
+ self.renderPagination = function( page, perPage ) {
290
+ var markup = '',
291
+ totalCount = '' !== $filter.val() ? filteredList.length : exclusionList.length,
292
+ totalPages = Math.ceil( totalCount / perPage );
293
+
294
+ page = 0 === totalCount ? 0 : page;
295
+
296
+ markup +=
297
+ '<span class="displaying-num">' +
298
+ '<span>' +
299
+ totalCount.toLocaleString( 'en' ) +
300
+ '</span> ' +
301
+ lang.items +
302
+ '</span>' +
303
+ '<span class="pagination-links">';
304
+
305
+ if ( 1 >= page ) {
306
+ markup += '<span class="tablenav-pages-navspan">«</span> ';
307
+ } else {
308
+ markup += '<a class="first" href="#"><span>«</span></a> ';
309
+ }
310
+
311
+ if ( 1 >= page ) {
312
+ markup += '<span class="tablenav-pages-navspan">‹</span> ';
313
+ } else {
314
+ markup += '<a class="prev" href="#"><span>‹</span></a> ';
315
+ }
316
+
317
+ markup +=
318
+ '<span class="paging-input">' +
319
+ '<input class="current-page" type="text" value="' +
320
+ page +
321
+ '" size="1">' +
322
+ '<span class="tablenav-paging-text"> ' +
323
+ lang.of +
324
+ ' <span class="total-pages">' +
325
+ totalPages +
326
+ '</span></span>' +
327
+ '</span> ';
328
+
329
+ if ( page < totalPages ) {
330
+ markup += '<a class="next" href="#"><span>›</span></a> ';
331
+ } else {
332
+ markup += '<span class="tablenav-pages-navspan">›</span> ';
333
+ }
334
+
335
+ if ( page < totalPages ) {
336
+ markup += '<a class="last" href="#"><span>»</span></a> ';
337
+ } else {
338
+ markup += '<span class="tablenav-pages-navspan">»</span> ';
339
+ }
340
+
341
+ markup += '</span>';
342
+
343
+ $excludeFoldersPreview
344
+ .find( '.tablenav-pages' )
345
+ .html( markup )
346
+ .end()
347
+ .removeClass( 'hidden' );
348
+ };
349
+
350
+ /**
351
+ * @summary Toggle display of everything after the "full" or "custom" options.
352
+ *
353
+ * @since 1.5.4
354
+ */
355
+ self.toggleConfig = function() {
356
+ var type = $type.filter( ':checked' ).val(),
357
+ $miscInfo = $( '#folder_misc_info' );
358
+
359
+ if ( 'full' === type ) {
360
+ $trs.hide();
361
+ $miscInfo.hide();
362
+ } else {
363
+ $trs.show();
364
+ $miscInfo.show();
365
+ }
366
+ };
367
+
368
+ /**
369
+ *
370
+ */
371
+ self.toggleStatus = function() {
372
+ var usingDefaults =
373
+ $inputInclude.val() &&
374
+ $inputInclude.val().trim() === lang.default_include &&
375
+ $inputExclude.val().trim() === lang.default_exclude,
376
+ $yesDefault = $container.find( '.yes-default' ),
377
+ $noDefault = $container.find( '.no-default' );
378
+
379
+ if ( usingDefaults ) {
380
+ $yesDefault.show();
381
+ $noDefault.hide();
382
+ } else {
383
+ $yesDefault.hide();
384
+ $noDefault.show();
385
+ }
386
+ };
387
+
388
+ // Onload event listener.
389
+ $( function() {
390
+ $( '#exclude_folders_button' ).on( 'click', self.onClickPreview );
391
+
392
+ $( 'body' )
393
+ .on( 'click', '#exclude_folders_preview .pagination-links a', self.onClickPagination )
394
+ .keydown( self.onKeyDown );
395
+
396
+ $( '.folder_exclude_sample' ).on( 'click', self.onClickSample );
397
+
398
+ $filter.on( 'keyup', self.renderList );
399
+
400
+ $( '#configure_folder_exclude' ).on( 'click', self.onClickConfigure );
401
+
402
+ self.toggleStatus();
403
+ self.toggleConfig();
404
+
405
+ $type.on( 'change', self.onChangeType );
406
+
407
+ $inputInclude.on( 'input', self.toggleStatus ).on( 'focusin', self.bounceHelp );
408
+
409
+ $inputExclude.on( 'input', self.toggleStatus ).on( 'focusin', self.bounceHelp );
410
+ } );
411
+ };
412
+
413
+ new BoldGrid.FolderExclude( jQuery );
admin/js/boldgrid-backup-admin-ftp-settings.js ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BoldGrid Backup FTP Settings
3
+ *
4
+ * @summary JavaScript for handling FTP Settings page.
5
+ *
6
+ * @since 1.5.4
7
+ *
8
+ * @param $ The jQuery object.
9
+ */
10
+
11
+ /* global BoldGridBackupAdminFtpSettings,jQuery */
12
+
13
+ var BoldGrid = BoldGrid || {};
14
+
15
+ BoldGrid.FtpSettings = function( $ ) {
16
+ 'use strict';
17
+
18
+ var self = this,
19
+ lang = BoldGridBackupAdminFtpSettings,
20
+ $action = $( '[name="action"]' ),
21
+ $port = $( '[name="port"]' ),
22
+ $type = $( '[name="type"]' ),
23
+ $form = $port.closest( 'form' ),
24
+ $saveButton = $form.find( '.button-primary' ),
25
+ $deleteButton = $form.find( '.button-secondary' ),
26
+ $spinner = $form.find( '.spinner' );
27
+
28
+ /**
29
+ * @summary Take action when the delete button is clicked.
30
+ *
31
+ * @since 1.6.0
32
+ */
33
+ self.onClickDelete = function() {
34
+ $action.val( 'delete' );
35
+ $form.submit();
36
+ };
37
+
38
+ /**
39
+ * @summary Action to take when form has been submitted.
40
+ *
41
+ * @since 1.5.4
42
+ */
43
+ self.onSubmit = function() {
44
+ $saveButton.attr( 'disabled', true );
45
+
46
+ $deleteButton.attr( 'disabled', true );
47
+
48
+ $spinner.removeClass( 'hidden' );
49
+ };
50
+
51
+ /**
52
+ * @summary Action to take when type has been changed.
53
+ *
54
+ * @since 1.5.4
55
+ */
56
+ self.onTypeChange = function() {
57
+ var suggestedPort = lang.default_port[$type.val()];
58
+
59
+ $port.val( suggestedPort ).bgbuDrawAttention();
60
+ };
61
+
62
+ $( function() {
63
+ $type.on( 'change', self.onTypeChange );
64
+ $form.on( 'submit', self.onSubmit );
65
+ $deleteButton.on( 'click', self.onClickDelete );
66
+ } );
67
+ };
68
+
69
+ BoldGrid.FtpSettings( jQuery );
admin/js/boldgrid-backup-admin-home.js ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BoldGrid Backup admin home page.
3
+ *
4
+ * @summary JavaScript for the BoldGrid Backup admin home page.
5
+ *
6
+ * @since 1.0
7
+ *
8
+ * @param $ The jQuery object.
9
+ */
10
+
11
+ /* global jQuery */
12
+
13
+ var BOLDGRID = BOLDGRID || {};
14
+ BOLDGRID.BACKUP = BOLDGRID.BACKUP || {};
15
+
16
+ BOLDGRID.BACKUP.HOME = function( $ ) {
17
+ 'use strict';
18
+
19
+ // General Variables.
20
+ var self = this,
21
+ $fileInput = $( 'input:file' ),
22
+ $mineCount = $( '.mine' ),
23
+ $mineCountHelp = $( '.subsubsub' ).find( '.dashicons' );
24
+
25
+ // Onload event listener.
26
+ $( function() {
27
+
28
+ // On click action for the Upload button.
29
+ $( '#upload-archive-form' )
30
+ .find( '.button' )
31
+ .on( 'click', self.uploadButtonClicked );
32
+
33
+ $( '.page-title-action.add-new' ).on( 'click', function() {
34
+ $( '#add_new' ).toggle();
35
+ } );
36
+
37
+ $fileInput
38
+ .parent()
39
+ .find( 'input:submit' )
40
+ .attr( 'disabled', true );
41
+
42
+ // On click action for toggling a help section.
43
+ $( '.dashicons-editor-help' ).on( 'click', self.toggleHelp );
44
+
45
+ // Remove restoration notice.
46
+ self.hideRestoreNotice();
47
+
48
+ $fileInput.on( 'change', self.onChangeInput );
49
+
50
+ $mineCount.on( 'click', self.onClickCount ).on( 'mouseover', function() {
51
+ $mineCountHelp.bgbuDrawAttention();
52
+ } );
53
+ } );
54
+
55
+ /**
56
+ * Hide the restore archive notice and enable action buttons.
57
+ *
58
+ * @since 1.2.3
59
+ */
60
+ self.hideRestoreNotice = function() {
61
+
62
+ // Enable the Backup Site Now and all Restore and Delete buttons.
63
+ $( '#backup-site-now, .action-restore, .action-delete' )
64
+ .prop( 'disabled', false )
65
+ .css( 'pointer-events', '' );
66
+
67
+ // Hide the restore notice.
68
+ $( '.restoration-in-progress' ).hide();
69
+ };
70
+
71
+ /**
72
+ * @summary Take action when a backup file is selected for upload.
73
+ *
74
+ * This includes checking the filesize and showing applicable warnings.
75
+ *
76
+ * @since 1.5.2
77
+ */
78
+ self.onChangeInput = function() {
79
+ var $badExtension = $( '#bad_extension' ),
80
+ $fileSizeWarning = $( '[data-id="upload-backup"]:not(span)' ),
81
+ $fileTooLarge = $( '#file_too_large' ),
82
+ $submit = $( 'input:submit' ),
83
+ extension,
84
+ isBadExtension,
85
+ isTooBig,
86
+ maxSize = parseInt( $( '[name="MAX_FILE_SIZE"]' ).val() ),
87
+ name,
88
+ size;
89
+
90
+ if ( ! $fileInput.val() ) {
91
+ $fileSizeWarning.slideUp();
92
+ $fileTooLarge.slideUp();
93
+ $badExtension.slideUp();
94
+ $submit.attr( 'disabled', true );
95
+ return;
96
+ }
97
+
98
+ name = $fileInput[0].files[0].name;
99
+ size = $fileInput[0].files[0].size;
100
+ extension = name.substr( name.lastIndexOf( '.' ) + 1 );
101
+
102
+ isTooBig = 0 > maxSize - size;
103
+ isBadExtension = 'zip' !== extension;
104
+
105
+ if ( isBadExtension ) {
106
+ $badExtension.slideDown();
107
+ } else {
108
+ $badExtension.slideUp();
109
+ }
110
+
111
+ if ( isTooBig ) {
112
+ $fileSizeWarning.slideDown();
113
+ $fileTooLarge.slideDown();
114
+ } else {
115
+ $fileSizeWarning.slideUp();
116
+ $fileTooLarge.slideUp();
117
+ }
118
+
119
+ if ( isTooBig || isBadExtension ) {
120
+ $submit.attr( 'disabled', true );
121
+ } else {
122
+ $submit.attr( 'disabled', false );
123
+ }
124
+ };
125
+
126
+ /**
127
+ * @summary Action to take when a user clicks on a mine count.
128
+ *
129
+ * @since 1.5.4
130
+ */
131
+ self.onClickCount = function() {
132
+ var $anchor = $( this ),
133
+ $p = $anchor.closest( 'p' ),
134
+ $trs = $( '#backup-archive-list-body tr' ),
135
+
136
+ // Type is either on_web_server or on_remote_server
137
+ type = $anchor.attr( 'data-count-type' );
138
+
139
+ // Highlight the count we just clicked on.
140
+ $p.find( '.mine' ).removeClass( 'current' );
141
+ $anchor.addClass( 'current' );
142
+
143
+ if ( 'all' === type ) {
144
+ $trs.show();
145
+ return false;
146
+ }
147
+
148
+ $trs.each( function( index ) {
149
+ var $tr = $( this ),
150
+ $matches = $tr.find( '[data-' + type + '="true"]' );
151
+
152
+ if ( 0 === $matches.length ) {
153
+ $tr.hide();
154
+ } else {
155
+ $tr.show();
156
+ $matches.bgbuDrawAttention();
157
+ }
158
+ } );
159
+
160
+ return false;
161
+ };
162
+
163
+ /**
164
+ * Confirm to delete a selected backup archive file.
165
+ *
166
+ * @since 1.2.2
167
+ */
168
+ self.uploadButtonClicked = function() {
169
+
170
+ // Declare variables.
171
+ var $this = $( this );
172
+
173
+ // Disable the Upload button.
174
+ $this.css( 'pointer-events', 'none' );
175
+
176
+ // Show the spinner.
177
+ $this
178
+ .parent()
179
+ .find( '.spinner' )
180
+ .addClass( 'is-active' )
181
+ .css( 'display', 'inline-block' );
182
+ };
183
+
184
+ /**
185
+ * Toggle a help section.
186
+ *
187
+ * @since 1.2.2
188
+ */
189
+ self.toggleHelp = function() {
190
+ $( this )
191
+ .next( '.help' )
192
+ .toggle();
193
+ };
194
+ };
195
+
196
+ BOLDGRID.BACKUP.HOME( jQuery );
admin/js/boldgrid-backup-admin-rollback.js ADDED
@@ -0,0 +1,440 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BoldGrid Backup admin rollback notice.
3
+ *
4
+ * @summary JavaScript for the BoldGrid Backup admin rollback notice.
5
+ *
6
+ * @since 1.0
7
+ */
8
+
9
+ /* global ajaxurl,boldgrid_backup_admin_rollback,pagenow,jQuery,wp */
10
+
11
+ // Declare namespace.
12
+ var BOLDGRID = BOLDGRID || {};
13
+
14
+ // Define sub-namespace.
15
+ BOLDGRID.BACKUP = BOLDGRID.BACKUP || {};
16
+
17
+ /**
18
+ * BoldGrid Backup admin rollback.
19
+ *
20
+ * @summary JavaScript for the BoldGrid Backup admin rollback notice.
21
+ *
22
+ * @since 1.0
23
+ *
24
+ * @param $ The jQuery object.
25
+ */
26
+ ( function( $ ) {
27
+ 'use strict';
28
+
29
+ // Onload event listener.
30
+ $( function() {
31
+ $( 'body' ).on( 'click', '#cancel-rollback-button', BOLDGRID.BACKUP.RollbackTimer.cancelRollback );
32
+
33
+ BOLDGRID.BACKUP.RollbackTimer.adjustOnAbout();
34
+ } );
35
+
36
+ /**
37
+ * Namespace BOLDGRID.BACKUP.RollbackTimer.
38
+ *
39
+ * @since 1.0
40
+ */
41
+ BOLDGRID.BACKUP.RollbackTimer = {
42
+
43
+ // When we show a global notice in the customizer, it is identified by this code.
44
+ countdownCode: 'boldgrid-backup-countdown',
45
+
46
+ /**
47
+ * Cancel pending rollback.
48
+ *
49
+ * @since 1.0
50
+ */
51
+ cancelRollback: function() {
52
+
53
+ // Declare variables.
54
+ var data,
55
+ cancelNonce,
56
+ wpHttpReferer,
57
+ errorCallback,
58
+ $cancelRollbackSection,
59
+ $cancelRollbackResults,
60
+ $rollbackSpinner,
61
+ $this = $( this ),
62
+ successCallBack;
63
+
64
+ // Disable the Cancel Rollback button.
65
+ $this.attr( 'disabled', 'disabled' ).css( 'pointer-events', 'none' );
66
+
67
+ // Create a context selector for the cancel rollback section.
68
+ $cancelRollbackSection = $( '#cancel-rollback-section' );
69
+
70
+ // Create a context selector for the cancel rollback results.
71
+ $cancelRollbackResults = $( '#cancel-rollback-results' );
72
+
73
+ // Create a context selector for the cancel rollback spinner.
74
+ $rollbackSpinner = $cancelRollbackSection.find( '.spinner' );
75
+
76
+ // Show the spinner.
77
+ $rollbackSpinner.addClass( 'is-active' );
78
+
79
+ $rollbackSpinner.css( 'display', 'inline-block' );
80
+
81
+ // Get the wpnonce and referer values.
82
+ cancelNonce = $cancelRollbackSection.find( '#cancel_rollback_auth' ).val();
83
+
84
+ wpHttpReferer = $cancelRollbackSection.find( '[name="_wp_http_referer"]' ).val();
85
+
86
+ // Create an error callback function.
87
+ errorCallback = function() {
88
+
89
+ // Show error message.
90
+ var markup =
91
+ '<div class="notice notice-error"><p>There was an error processing your request. Please reload the page and try again.</p></div>';
92
+
93
+ $cancelRollbackResults.html( markup );
94
+ };
95
+
96
+ // Generate a data array for the download request.
97
+ data = {
98
+ action: 'boldgrid_cancel_rollback',
99
+ cancel_rollback_auth: cancelNonce,
100
+ _wp_http_referer: wpHttpReferer
101
+ };
102
+
103
+ /**
104
+ * Action to take when we successfully canceled the rollback.
105
+ *
106
+ * @since 1.6.0
107
+ */
108
+ successCallBack = function( response ) {
109
+
110
+ // Remove the restore now section.
111
+ $( '[data-restore-now]' )
112
+ .parent()
113
+ .slideUp();
114
+
115
+ // Insert markup in the results section.
116
+ $cancelRollbackResults.html( response );
117
+
118
+ // Hide the cancel rollback section.
119
+ $cancelRollbackSection.slideUp();
120
+
121
+ if ( 'customize' === pagenow ) {
122
+ wp.customize.notifications.remove( BOLDGRID.BACKUP.RollbackTimer.countdownCode );
123
+ }
124
+ };
125
+
126
+ // Make the call.
127
+ $.ajax( {
128
+ url: ajaxurl,
129
+ data: data,
130
+ type: 'post',
131
+ dataType: 'text',
132
+ success: successCallBack,
133
+ error: errorCallback,
134
+ complete: function() {
135
+
136
+ // Hide the spinner.
137
+ $cancelRollbackSection.find( '.spinner' ).removeClass( 'is-active' );
138
+ }
139
+ } );
140
+
141
+ // Return false so the page does not reload.
142
+ return false;
143
+ },
144
+
145
+ /**
146
+ * Get the time remaining to an end time.
147
+ *
148
+ * @since 1.0
149
+ *
150
+ * @param string endTime A data/time parsed with Date.parse().
151
+ * @return array
152
+ */
153
+ getTimeRemaining: function( endTime ) {
154
+
155
+ // Declare variables.
156
+ var totalSeconds, seconds, minutes;
157
+
158
+ // Parse data into seconds from now.
159
+ totalSeconds = Date.parse( endTime ) - Date.parse( new Date() );
160
+
161
+ // If totalSeconds is less than or equal to zero, then return zero array.
162
+ if ( 0 >= totalSeconds ) {
163
+ return {
164
+ total: 0,
165
+ minutes: '0',
166
+ seconds: '00'
167
+ };
168
+ }
169
+
170
+ // Calculate seconds, minutes, hours, days.
171
+ seconds = Math.floor( ( totalSeconds / 1000 ) % 60 );
172
+ minutes = Math.floor( totalSeconds / 1000 / 60 );
173
+
174
+ // Return the data in an array.
175
+ return {
176
+ total: totalSeconds,
177
+ minutes: minutes,
178
+ seconds: ( '0' + seconds ).slice( -2 )
179
+ };
180
+ },
181
+
182
+ /**
183
+ * Initialize a countdown timer, updating a DOM id.
184
+ *
185
+ * Uses BOLDGRID.BACKUP.RollbackTimer.deadline for the end time/deadline.
186
+ *
187
+ * @since 1.0
188
+ *
189
+ * @see getTimeRemaining().
190
+ * @see updateDeadline().
191
+ *
192
+ * @param string deadline
193
+ */
194
+ initializeClock: function() {
195
+
196
+ // Define variables.
197
+ var $clock,
198
+ interval,
199
+ totalSeconds,
200
+ self = this;
201
+
202
+ // Get the element for the clock display.
203
+ $clock = $( '#rollback-countdown-timer' );
204
+
205
+ // Use an interval of 1 second to update the clock.
206
+ interval = setInterval( function() {
207
+ totalSeconds = self.getTimeRemaining( BOLDGRID.BACKUP.RollbackTimer.deadline );
208
+
209
+ // Update the clock display.
210
+ $clock.html( totalSeconds.minutes + ':' + totalSeconds.seconds );
211
+
212
+ // When the timer reaches zero, stop the countdown and disable the cancel button.
213
+ if ( 0 >= totalSeconds.total ) {
214
+ clearInterval( interval );
215
+
216
+ // Disable the Cancel Rollback button.
217
+ $( '#cancel-rollback-button' )
218
+ .attr( 'disabled', 'disabled' )
219
+ .css( 'pointer-events', 'none' );
220
+ }
221
+ }, 1000 );
222
+ },
223
+
224
+ /**
225
+ * If updating something, then update the timer deadline.
226
+ *
227
+ * @since 1.2
228
+ */
229
+ updateDeadline: function() {
230
+
231
+ // Declare variables.
232
+ var $RollbackDeadline;
233
+
234
+ // Check for the deadline in the source (when completing updates in the admin section).
235
+ $RollbackDeadline = $( 'iframe' )
236
+ .contents()
237
+ .find( '#rollback-deadline' );
238
+
239
+ // Update the rollback timer.
240
+ if ( $RollbackDeadline.length ) {
241
+ BOLDGRID.BACKUP.RollbackTimer.deadline = $RollbackDeadline.text();
242
+ BOLDGRID.BACKUP.RollbackTimer.initializeClock();
243
+ }
244
+ },
245
+
246
+ /**
247
+ * If updating something, then update the timer deadline from the retrieved ISO time.
248
+ *
249
+ * @since 1.2.1
250
+ */
251
+ getUpdatedDeadline: function() {
252
+
253
+ // Declare variables.
254
+ var $bulkActionForm, wpnonce, wpHttpReferer, data;
255
+
256
+ // Create a context selector for bulk-action-form.
257
+ $bulkActionForm = $( '#bulk-action-form' );
258
+
259
+ // Get the bulk-action-form wpnonce.
260
+ wpnonce = $bulkActionForm.find( '#_wpnonce' ).val();
261
+
262
+ // Get the bulk-action-form wpnonce.
263
+ wpHttpReferer = $bulkActionForm.find( '[name="_wp_http_referer"]' ).val();
264
+
265
+ // Use adminajax to get the updated deadline.
266
+ // Generate the data array.
267
+ data = {
268
+ action: 'boldgrid_backup_deadline',
269
+ _wpnonce: wpnonce,
270
+ _wp_http_referer: wpHttpReferer
271
+ };
272
+
273
+ // Make the call.
274
+ return $.ajax( {
275
+ url: ajaxurl,
276
+ data: data,
277
+ type: 'post',
278
+ dataType: 'text',
279
+ success: function( response ) {
280
+
281
+ // Update the rollback timer.
282
+ if ( response.length ) {
283
+ BOLDGRID.BACKUP.RollbackTimer.deadline = response;
284
+ }
285
+
286
+ /*
287
+ * Someone may be waiting to see if we have a deadline, let
288
+ * them know we're done.
289
+ */
290
+ $( 'body' ).trigger( 'boldgrid-backup-have-deadline' );
291
+ }
292
+ } );
293
+ },
294
+
295
+ /**
296
+ * @summary Show the countdown notice.
297
+ *
298
+ * This method makes an ajax request to get the countdown notice. Useful
299
+ * when plugins / themes are updated via ajaxy.
300
+ *
301
+ * @since 1.6.0
302
+ */
303
+ show: function() {
304
+
305
+ /*
306
+ * Show only one countdown.
307
+ *
308
+ * If there is already a countdown showing, abort. The user may be
309
+ * on the themes page updating themes, and if they update two themes,
310
+ * we don't want to show two countdown notices.
311
+ */
312
+ if ( 0 < $( '.boldgrid-backup-countdown:visible' ).length ) {
313
+ return;
314
+ }
315
+
316
+ var data = {
317
+ action: 'boldgrid_backup_get_countdown_notice'
318
+ },
319
+ successCallback;
320
+
321
+ /**
322
+ * Action to take after getting the countdown notice.
323
+ *
324
+ * @since 1.6.0
325
+ */
326
+ successCallback = function( response ) {
327
+ var $notice,
328
+ notification,
329
+ $headerEnd = $( '.wp-header-end' ),
330
+ $wrap = $( '.wrap' ).first();
331
+
332
+ if ( response.success !== undefined && true === response.success ) {
333
+ $( '.boldgrid-backup-protect-now, .boldgrid-backup-protected' ).slideUp();
334
+
335
+ $notice = $( response.data );
336
+
337
+ // Determine where and how to add the notice.
338
+ if ( 'customize' === pagenow ) {
339
+ notification = new wp.customize.Notification(
340
+ BOLDGRID.BACKUP.RollbackTimer.countdownCode,
341
+ { message: $notice.removeClass( 'notice notice-warning' ).html(), type: 'warning' }
342
+ );
343
+ wp.customize.notifications.add( notification );
344
+ } else {
345
+ $notice.addClass( 'hidden' );
346
+
347
+ if ( 1 === $headerEnd.length ) {
348
+ $notice.insertAfter( $headerEnd );
349
+ } else {
350
+ $notice.prependTo( $wrap );
351
+ }
352
+
353
+ $notice.slideDown();
354
+ }
355
+
356
+ /*
357
+ * Allow the countdown to render (especially in the
358
+ * customizer) before initializing the clock.
359
+ */
360
+ setTimeout( function() {
361
+ BOLDGRID.BACKUP.RollbackTimer.initializeClock();
362
+ }, 500 );
363
+ }
364
+ };
365
+
366
+ $.ajax( {
367
+ url: ajaxurl,
368
+ data: data,
369
+ type: 'post',
370
+ dataType: 'json',
371
+ success: successCallback
372
+ } );
373
+ },
374
+
375
+ /**
376
+ * @summary Show the countdown notice on the about page.
377
+ *
378
+ * WordPress hides all admin notices on the wp-admin/about.php page. The
379
+ * countdown notice is important enough to break this mold.
380
+ *
381
+ * @since 1.6.0
382
+ */
383
+ adjustOnAbout: function() {
384
+ if ( 'about' !== pagenow ) {
385
+ return;
386
+ }
387
+
388
+ var $notice = $( '.notice.boldgrid-backup-countdown' );
389
+
390
+ $notice.css( 'display', 'block!important' ).insertBefore( '.wrap' );
391
+ },
392
+
393
+ /**
394
+ * If the rollback countdown timer is needed, then initialize the clock.
395
+ *
396
+ * @since 1.0
397
+ *
398
+ * @see initializeClock().
399
+ */
400
+ init: function() {
401
+
402
+ // Declare vars.
403
+ var $document = $( document ),
404
+ haveDeadline;
405
+
406
+ // Determine whether or not we have a valid deadline.
407
+ haveDeadline =
408
+ 'object' === typeof boldgrid_backup_admin_rollback &&
409
+ boldgrid_backup_admin_rollback.rolloutDeadline !== undefined &&
410
+ '1970' !== boldgrid_backup_admin_rollback.rolloutDeadline.slice( 0, 4 );
411
+
412
+ // If there is a defined rollout deadline, then initialize the timer.
413
+ if ( haveDeadline ) {
414
+
415
+ // Set the end time/deadline.
416
+ BOLDGRID.BACKUP.RollbackTimer.deadline = boldgrid_backup_admin_rollback.rolloutDeadline;
417
+
418
+ // Initialize the clock/timer.
419
+ this.initializeClock();
420
+ }
421
+
422
+ // When the update progress iframe loads, check for a new deadline.
423
+ $( 'iframe' ).on( 'load', this.updateDeadline );
424
+
425
+ // When a plugin is updated via adminajax, then get the new deadline and update the timer.
426
+ $document.on( 'wp-plugin-update-success', this.getUpdatedDeadline );
427
+
428
+ // When a theme is updated via adminajax, then get the new deadline and update the timer.
429
+ $document.on( 'wp-theme-update-success', this.getUpdatedDeadline );
430
+
431
+ $document.on( 'wp-plugin-update-success wp-theme-update-success', this.show );
432
+ }
433
+ };
434
+
435
+ // Initialize the deadline.
436
+ BOLDGRID.BACKUP.RollbackTimer.deadline = '';
437
+
438
+ // Initialize the rollback timer.
439
+ BOLDGRID.BACKUP.RollbackTimer.init();
440
+ } )( jQuery );
admin/js/boldgrid-backup-admin-settings.js ADDED
@@ -0,0 +1,311 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BoldGrid Backup settings.
3
+ *
4
+ * @summary JavaScript for the settings page.
5
+ *
6
+ * @since 1.0
7
+ *
8
+ * @param $ The jQuery object.
9
+ */
10
+
11
+ /* global ajaxurl,bglibLicense,BOLDGRID,jQuery */
12
+
13
+ var BoldGrid = BoldGrid || {};
14
+
15
+ BoldGrid.Settings = function( $ ) {
16
+ 'use strict';
17
+
18
+ // General Variables.
19
+ var self = this,
20
+ $scheduleDow,
21
+ $noBackupDays,
22
+ $useSparingly,
23
+ $backupDir,
24
+ $body = $( 'body' ),
25
+ tb_unload_count,
26
+ $moveBackups;
27
+
28
+ /**
29
+ * Directory to store backups.
30
+ *
31
+ * @since 1.3.6
32
+ */
33
+ $backupDir = $( '#backup-directory-path' );
34
+
35
+ // Define a context selector for schedule-dow.
36
+ $scheduleDow = $( '.schedule-dow' );
37
+
38
+ // Define a context selector for no-backup-days.
39
+ $noBackupDays = $( '#no-backup-days' );
40
+
41
+ /**
42
+ * Message asking user if we should move their backups.
43
+ *
44
+ * @since 1.3.6
45
+ */
46
+ $moveBackups = $( '#move-backups' );
47
+
48
+ /**
49
+ * @summary Number of times tb_unload has been triggered.
50
+ *
51
+ * When a thickbox is closed, tb_unload is called twice. We need to keep
52
+ * track of how many times it's been called so that we know to only run our
53
+ * callback once.
54
+ *
55
+ * @since 1.5.2
56
+ */
57
+ tb_unload_count = 0;
58
+
59
+ /**
60
+ * Message describing resource usage.
61
+ *
62
+ * @since 1.3.1
63
+ */
64
+ $useSparingly = $( '#use-sparingly' );
65
+
66
+ /**
67
+ * @summary Take action when the user clicks Check again.
68
+ *
69
+ * The user is checking their license status again.
70
+ *
71
+ * @since 1.6.0
72
+ */
73
+ self.onClickCheckAgain = function() {
74
+ var $button = $( this ),
75
+ $parent = $button.parent(),
76
+ $licenseString = $parent.find( '#license_string' ),
77
+ $reloadMessage = $( '#license_reload_page' ),
78
+ $spinner = $parent.find( '.spinner' ),
79
+ successFunction,
80
+ errorFunction;
81
+
82
+ $spinner.show();
83
+ $licenseString.empty();
84
+
85
+ errorFunction = function( response ) {
86
+ var error =
87
+ response !== undefined && response.data !== undefined && response.data.string !== undefined ?
88
+ response.data.string :
89
+ bglibLicense.unknownError;
90
+
91
+ $spinner.hide();
92
+ $licenseString.html( error );
93
+ };
94
+
95
+ successFunction = function( response ) {
96
+ if (
97
+ true !== response.success ||
98
+ response.data === undefined ||
99
+ response.data.string === undefined
100
+ ) {
101
+ errorFunction( response );
102
+ return;
103
+ }
104
+
105
+ $spinner.hide();
106
+ $licenseString.html( response.data.string );
107
+
108
+ if ( response.data.isPremium ) {
109
+ $reloadMessage.removeClass( 'hidden' );
110
+ }
111
+ };
112
+
113
+ BOLDGRID.LIBRARY.License.clear( 'boldgrid-backup', successFunction, errorFunction );
114
+
115
+ return false;
116
+ };
117
+
118
+ /**
119
+ * @summary Action to take when a remote storage provider has been clicked.
120
+ *
121
+ * Primary function is to flag the clicked provider with the active class.
122
+ *
123
+ * @since 1.5.2
124
+ */
125
+ self.on_click_provider = function() {
126
+ var $a = $( this ),
127
+ $tr = $a.closest( 'tr' ),
128
+ $table = $a.closest( 'table' );
129
+
130
+ $table.find( 'tr' ).removeClass( 'active' );
131
+
132
+ /*
133
+ * We add the active class so that we can identify the provider that is
134
+ * being updated.
135
+ */
136
+ $tr.addClass( 'active' );
137
+ };
138
+
139
+ /**
140
+ * @summary Action to take when the thickbox is closed.
141
+ *
142
+ * @since 1.5.2
143
+ */
144
+ self.on_tb_unload = function() {
145
+ tb_unload_count++;
146
+
147
+ // Only take action on the odd occurences of tb_unload.
148
+ if ( 0 === tb_unload_count % 2 ) {
149
+ return;
150
+ }
151
+
152
+ self.refresh_storage_configuration();
153
+ };
154
+
155
+ /**
156
+ * @summary Refresh remote storage provider summary.
157
+ *
158
+ * For example, if Amazon S3 was unconfigured, an applicable message will
159
+ * show. After being configured, the "unconfigured" message needs to be
160
+ * updated.
161
+ *
162
+ * @since 1.5.2
163
+ */
164
+ self.refresh_storage_configuration = function() {
165
+ var $tr = $( '#storage_locations tr.active:not(.refreshing)' ),
166
+ $td_configure = $tr.find( 'td.configure' ),
167
+ $nonce = $( '#_wpnonce' ),
168
+ data = {
169
+ action: 'boldgrid_backup_is_setup_' + $tr.attr( 'data-key' ),
170
+ security: $nonce.val()
171
+ },
172
+ $new_tr;
173
+
174
+ $tr.addClass( 'refreshing' );
175
+
176
+ $td_configure.html( '<span class="spinner inline"></span>' );
177
+
178
+ $.post( ajaxurl, data, function( response ) {
179
+ $new_tr = $( response.data );
180
+ $tr.replaceWith( $new_tr );
181
+
182
+ self.toggleNoStorage();
183
+ } );
184
+ };
185
+
186
+ /**
187
+ * @summary Check if any days of the week selected.
188
+ *
189
+ * @since 1.0
190
+ */
191
+ self.scheduleDowChecked = function() {
192
+
193
+ // Define vars.
194
+ var isDowChecked = false;
195
+
196
+ if ( $scheduleDow.find( 'input' ).is( ':checked' ) ) {
197
+ isDowChecked = true;
198
+ }
199
+
200
+ return isDowChecked;
201
+ };
202
+
203
+ /**
204
+ * @summary Toogle the move backups message.
205
+ *
206
+ * @since 1.3.6
207
+ */
208
+ self.toggleMoveBackups = function() {
209
+ if ( $backupDir.val() === $backupDir.prop( 'defaultValue' ) ) {
210
+ $moveBackups.hide();
211
+ } else {
212
+ $moveBackups.show();
213
+ }
214
+ };
215
+
216
+ /**
217
+ * Toggle notice for no backup days selected.
218
+ *
219
+ * @since 1.0
220
+ */
221
+ self.toggleNoBackupDays = function() {
222
+
223
+ // How many days of the week are checked?
224
+ var daysCount = $scheduleDow.find( ':checked' ).length;
225
+
226
+ /*
227
+ * If the user has selected more than 1 day under "Days of the Week", show a message about
228
+ * resource usage.
229
+ *
230
+ * @since 1.3.1
231
+ */
232
+ if ( 1 < daysCount ) {
233
+ $useSparingly.show();
234
+ } else {
235
+ $useSparingly.hide();
236
+ }
237
+
238
+ if ( true === self.scheduleDowChecked() ) {
239
+ $noBackupDays.hide();
240
+ } else {
241
+ $noBackupDays.show();
242
+ }
243
+ };
244
+
245
+ /**
246
+ * @summary Toggle the warning about no backups if no storage selected.
247
+ *
248
+ * @since 1.5.2
249
+ */
250
+ self.toggleNoStorage = function() {
251
+ var count_checked = $( '#storage_locations input[type="checkbox"]:checked' ).length,
252
+ $noStorage = $( '#no_storage' );
253
+
254
+ if ( 0 === count_checked ) {
255
+ $noStorage.show();
256
+ } else {
257
+ $noStorage.hide();
258
+ }
259
+ };
260
+
261
+ /**
262
+ * Handle click of the undismissBoldgridNotice link for the key prompt.
263
+ *
264
+ * @since 1.5.4
265
+ */
266
+ self.undismissBoldgridNotice = function() {
267
+ var data, nonce, wpHttpReferer;
268
+
269
+ // Get the wpnonce and referer values.
270
+ nonce = $( '#set_key_auth' ).val();
271
+ wpHttpReferer = $( '[name="_wp_http_referer"]' ).val();
272
+
273
+ data = {
274
+ action: 'undismissBoldgridNotice',
275
+ notice: 'bg-key-prompt',
276
+ set_key_auth: nonce,
277
+ _wp_http_referer: wpHttpReferer
278
+ };
279
+
280
+ $.post( ajaxurl, data, function() {
281
+ location.reload();
282
+ } );
283
+ };
284
+
285
+ // Onload event listener.
286
+ $( function() {
287
+
288
+ // Check if any days or the week are checked, toggle notice.
289
+ self.toggleNoBackupDays();
290
+
291
+ self.toggleNoStorage();
292
+ $body.on( 'click', '#storage_locations input[type="checkbox"]', self.toggleNoStorage );
293
+
294
+ $backupDir.on( 'input', self.toggleMoveBackups );
295
+
296
+ // On click action for days, check if any days or the week are checked,
297
+ // toggle notice.
298
+ $scheduleDow.on( 'click', self.toggleNoBackupDays );
299
+
300
+ $( window ).on( 'tb_unload', self.on_tb_unload );
301
+
302
+ $body.on( 'click', '#storage_locations .thickbox', self.on_click_provider );
303
+
304
+ $body.on( 'click', '#license_check_again', self.onClickCheckAgain );
305
+
306
+ /** Reverse dismiss action for the Conect Key prompt **/
307
+ $( '.undismissBoldgridNotice' ).on( 'click', self.undismissBoldgridNotice );
308
+ } );
309
+ };
310
+
311
+ BoldGrid.Settings( jQuery );
admin/js/boldgrid-backup-admin-table-include.js ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BoldGrid Backup Table Include.
3
+ *
4
+ * @summary JavaScript for handling Table include settings.
5
+ *
6
+ * @since 1.5.4
7
+ *
8
+ * @param $ The jQuery object.
9
+ */
10
+
11
+ /* global jQuery */
12
+
13
+ var BoldGrid = BoldGrid || {};
14
+
15
+ BoldGrid.TableInclude = function( $ ) {
16
+ 'use strict';
17
+
18
+ var self = this,
19
+ $container = $( '#table_inclusion' ),
20
+ $includeTables = $container.find( '.include-tables [type="checkbox"]' ),
21
+ $type = $container.find( '[name="table_inclusion_type"]' ),
22
+ $configContainer = $container.find( '#table_inclusion_config' ),
23
+
24
+ // Buttons to include / exclude all.
25
+ $buttonAll = $container.find( '#include_all_tables, .include-all' ),
26
+ $buttonNone = $container.find( '#exclude_all_tables' ),
27
+
28
+ // Defaults are the status messages indicating default settings used.
29
+ $yesDefault = $container.find( '.yes-default' ),
30
+ $noDefault = $container.find( '.no-default' );
31
+
32
+ /**
33
+ * @summary Action to take when the type (full / custom) has been changed.
34
+ *
35
+ * @since 1.5.4
36
+ */
37
+ self.onChangeType = function() {
38
+ self.toggleConfig();
39
+ };
40
+
41
+ /**
42
+ * @summary Toogle all database tables so they are all backed up.
43
+ *
44
+ * @since 1.5.4
45
+ */
46
+ self.toggleAll = function() {
47
+ $includeTables.bgbuDrawAttention();
48
+ $includeTables.attr( 'checked', true );
49
+ self.toggleStatus();
50
+
51
+ return false;
52
+ };
53
+
54
+ /**
55
+ * @summary Toggle the area that allows you to choose which tables to backup.
56
+ *
57
+ * @since 1.5.4
58
+ */
59
+ self.toggleConfig = function() {
60
+ var type = $type.filter( ':checked' ).val();
61
+
62
+ if ( 'full' === type ) {
63
+ $configContainer.hide();
64
+ } else {
65
+ $configContainer.show();
66
+ }
67
+ };
68
+
69
+ /**
70
+ * @summary Deselect all tables.
71
+ *
72
+ * @since 1.5.4
73
+ */
74
+ self.toggleNone = function() {
75
+ $includeTables.bgbuDrawAttention();
76
+ $includeTables.attr( 'checked', false );
77
+ self.toggleStatus();
78
+
79
+ return false;
80
+ };
81
+
82
+ /**
83
+ * @summary Toogle the status that tells the user if they're backing up all tables.
84
+ *
85
+ * @since 1.5.4
86
+ */
87
+ self.toggleStatus = function() {
88
+ var allIncluded = $includeTables.length === $includeTables.filter( ':checked' ).length;
89
+
90
+ if ( allIncluded ) {
91
+ $yesDefault.show();
92
+ $noDefault.hide();
93
+ } else {
94
+ $yesDefault.hide();
95
+ $noDefault.show();
96
+ }
97
+ };
98
+
99
+ $( function() {
100
+ $buttonAll.on( 'click', self.toggleAll );
101
+ $buttonNone.on( 'click', self.toggleNone );
102
+
103
+ self.toggleStatus();
104
+ self.toggleConfig();
105
+
106
+ $type.on( 'change', self.onChangeType );
107
+
108
+ $includeTables.on( 'change', self.toggleStatus );
109
+ } );
110
+ };
111
+
112
+ new BoldGrid.TableInclude( jQuery );
admin/js/boldgrid-backup-admin-update-selectors.js ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BoldGrid Backup Admin Update Selectors.
3
+ *
4
+ * JavaScript for handling WordPress' native "update" buttons and links. Primary
5
+ * function is to disable "update" buttons when we're in the middle of making a
6
+ * backup.
7
+ *
8
+ * @since 1.6.0
9
+ *
10
+ * @param $ The jQuery object.
11
+ */
12
+
13
+ /* global boldgrid_backup_admin_update_selectors,jQuery */
14
+
15
+ var BOLDGRID = BOLDGRID || {};
16
+ BOLDGRID.BACKUP = BOLDGRID.BACKUP || {};
17
+
18
+ ( function( $ ) {
19
+ BOLDGRID.BACKUP.UpdateSelectors = {
20
+ lang: boldgrid_backup_admin_update_selectors,
21
+
22
+ $selectors: null,
23
+
24
+ selectors: [
25
+
26
+ // Plugins > Installed Plugins > "Apply" button for "Bulk Actions".
27
+ '#doaction',
28
+ '#doaction2',
29
+
30
+ // Plugins > Installed Plugins > Inline "update now" link on Plugins > Installed Plugins.
31
+ '.update-link',
32
+
33
+ // Dashboard > Updates > "Update Plugins" button.
34
+ '#upgrade-plugins',
35
+ '#upgrade-plugins-2',
36
+
37
+ // Dashboard > Updates > "Update" and "Re-install" WordPress button.
38
+ '#upgrade',
39
+
40
+ // Dashboard > Updates > "Update Themes" button.
41
+ '#upgrade-themes',
42
+ '#upgrade-themes-2',
43
+
44
+ // Dashboard > Customize > Change Themes > Inline "Update now" link.
45
+ // Customizer > Installed themes > "Update now" link.
46
+ '.themes .update-message .button-link',
47
+
48
+ // Dashboard > Customize > Changes Themes > Click a theme > "update now" link.
49
+ // Customizer > Installed themes > click a theme > "update now" link.
50
+ '#update-theme',
51
+
52
+ // BoldGrid Backup - Update Protection > "Backup Site Now" button.
53
+ '#backup-site-now'
54
+ ],
55
+
56
+ /**
57
+ * @summary Enable update selectors.
58
+ *
59
+ * @since 1.6.0
60
+ */
61
+ enable: function() {
62
+ var self = BOLDGRID.BACKUP.UpdateSelectors;
63
+
64
+ self.setSelectors();
65
+
66
+ self.$selectors.each( function() {
67
+ var $el = $( this ),
68
+ $target;
69
+
70
+ $el.attr( 'disabled', false );
71
+
72
+ // See comment in self::disable().
73
+ $target = $el.is( 'a' ) ? $el.parent() : $el;
74
+ $target.attr( 'title', '' ).removeClass( self.lang.waitClass );
75
+ } );
76
+ },
77
+
78
+ /**
79
+ * @summary Disable update selectors.
80
+ *
81
+ * @since 1.6.0
82
+ */
83
+ disable: function() {
84
+
85
+ /*
86
+ * Timeout is required because some "Update" links are added via the
87
+ * wp.template system and do not call a trigger to alert when they're
88
+ * done.
89
+ */
90
+ setTimeout( function() {
91
+ var self = BOLDGRID.BACKUP.UpdateSelectors;
92
+
93
+ self.setSelectors();
94
+
95
+ self.$selectors.each( function() {
96
+ var $el = $( this ),
97
+ $target;
98
+
99
+ $el.attr( 'disabled', true );
100
+
101
+ /*
102
+ * Anchors behave differently. When you set an anchor to disabled,
103
+ * you cannot hover and see a title. For anchors, we'll temporarily
104
+ * adjust the parent instead.
105
+ */
106
+ $target = $el.is( 'a' ) ? $el.parent() : $el;
107
+ $target.attr( 'title', self.lang.backupInProgress ).addClass( self.lang.waitClass );
108
+ } );
109
+ }, 250 );
110
+ },
111
+
112
+ /**
113
+ * @summary Init.
114
+ *
115
+ * @since 1.6.0
116
+ */
117
+ init: function() {
118
+ $( 'body' ).on( 'boldgrid_backup_complete', $.proxy( this.enable, this ) );
119
+ $( 'body' ).on( 'boldgrid_backup_progress_notice_added', $.proxy( this.disable, this ) );
120
+
121
+ this.onInProgress();
122
+ },
123
+
124
+ /**
125
+ * @summary Actions to take when there is a backup in progress.
126
+ *
127
+ * @since 1.6.0
128
+ */
129
+ onInProgress: function() {
130
+ if ( 1 === $( '.boldgrid-backup-in-progress' ).length ) {
131
+ this.disable();
132
+ }
133
+ },
134
+
135
+ /**
136
+ * @summary Set our $selectors, which are used to find "update" buttons.
137
+ *
138
+ * @since 1.6.0
139
+ */
140
+ setSelectors: function() {
141
+ this.$selectors = $( this.selectors.join( ', ' ) );
142
+ }
143
+ };
144
+
145
+ $( function() {
146
+ BOLDGRID.BACKUP.UpdateSelectors.init();
147
+ } );
148
+ } )( jQuery );
admin/js/boldgrid-backup-admin-zip-browser.js ADDED
@@ -0,0 +1,285 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Browser.
3
+ *
4
+ * @summary JS for all admin backup pages.
5
+ *
6
+ * @since 1.5.2
7
+ */
8
+
9
+ /* global ajaxurl,jQuery,boldgrid_backup_zip_browser */
10
+
11
+ var BoldGrid = BoldGrid || {};
12
+
13
+ BoldGrid.ZipBrowser = function( $ ) {
14
+ var self = this,
15
+ $zipBrowser = $( '#zip_browser' ),
16
+ $listing = $zipBrowser.find( '.listing' ),
17
+ spinner = '<span class="spinner inline"></span> ',
18
+ loading = spinner + boldgrid_backup_zip_browser.loading + '...';
19
+
20
+ /**
21
+ * @summary Handle the click of a breadcrumb.
22
+ *
23
+ * @since 1.5.3
24
+ */
25
+ self.onClickBreadcrumb = function() {
26
+ var dir = $( this ).attr( 'data-dir' );
27
+
28
+ self.renderBrowser( dir );
29
+ };
30
+
31
+ /**
32
+ * @summary Handle the click of a file.
33
+ *
34
+ * @since 1.5.3
35
+ */
36
+ self.onClickFile = function() {
37
+ var $a = $( this ),
38
+ $tr = $a.closest( 'tr' ),
39
+ expanded = '1' === $tr.attr( 'data-expanded' ),
40
+ colspan = $tr.find( 'td' ).length,
41
+ $newTr = $(
42
+ '<tr class="file-actions"><td colspan="' + colspan + '">' + loading + '</td></tr>'
43
+ ),
44
+ $dummyTr = $( '<tr></tr>' ),
45
+ data = {
46
+ action: 'boldgrid_backup_browse_archive_file_actions',
47
+ security: $( '#_wpnonce' ).val(),
48
+ filename: $( '#filename' ).val(),
49
+ file: $tr.attr( 'data-dir' )
50
+ };
51
+
52
+ if ( ! expanded ) {
53
+ $newTr.css( 'background-color', $tr.css( 'background-color' ) ).insertAfter( $tr );
54
+ $dummyTr.insertAfter( $newTr );
55
+
56
+ $tr.attr( 'data-expanded', '1' );
57
+
58
+ $.post( ajaxurl, data, function( response ) {
59
+ if ( response.success !== undefined ) {
60
+ $newTr.find( 'td' ).html( response.data );
61
+ } else {
62
+ $newTr.find( 'td' ).html( boldgrid_backup_zip_browser.unknownError );
63
+ }
64
+ } ).error( function() {
65
+ $newTr.find( 'td' ).html( boldgrid_backup_zip_browser.unknownError );
66
+ } );
67
+ } else {
68
+ $tr
69
+ .next( 'tr' )
70
+ .remove()
71
+ .end()
72
+ .next( 'tr' )
73
+ .remove()
74
+ .end()
75
+ .attr( 'data-expanded', '0' );
76
+ }
77
+ };
78
+
79
+ /**
80
+ * @summary Handle the click of a folder.
81
+ *
82
+ * @since 1.5.3
83
+ */
84
+ self.onClickFolder = function() {
85
+ var $a = $( this ),
86
+ $tr = $a.closest( 'tr' ),
87
+ dir = $tr.attr( 'data-dir' );
88
+
89
+ self.renderBrowser( dir );
90
+ };
91
+
92
+ /**
93
+ * @summary Handle the click of the "load archive browser" button.
94
+ *
95
+ * @since 1.5.4
96
+ */
97
+ self.onClickLoadBrowser = function() {
98
+ $( this ).attr( 'disabled', 'disabled' );
99
+
100
+ self.renderBrowser( '.' );
101
+ };
102
+
103
+ /**
104
+ * @summary Handle the click of the "restore this database" button.
105
+ *
106
+ * @since 1.5.4
107
+ */
108
+ self.onClickRestoreDb = function() {
109
+ var $a = $( this ),
110
+ $p = $a.closest( 'p' ),
111
+ $spinner = $a.next(),
112
+ data = {
113
+ action: 'boldgrid_backup_browse_archive_restore_db',
114
+ security: $( '#_wpnonce' ).val(),
115
+ filename: $( '#filename' ).val(),
116
+ file: $a.attr( 'data-file' )
117
+ },
118
+ confirmation,
119
+ status = '<span class="spinner inline"></span> Restoring';
120
+
121
+ confirmation = confirm( boldgrid_backup_zip_browser.confirmDbRestore );
122
+
123
+ if ( ! confirmation ) {
124
+ return false;
125
+ }
126
+
127
+ $p.empty().html( status );
128
+ $a.attr( 'disabled', 'disabled' );
129
+ $spinner.addClass( 'inline middle' );
130
+
131
+ $.post( ajaxurl, data, function( response ) {
132
+ location.reload();
133
+ } ).error( function() {
134
+ location.reload();
135
+ } );
136
+
137
+ return false;
138
+ };
139
+
140
+ /**
141
+ * @summary Handle the postbox-like toggle on thead th's that hide a table.
142
+ *
143
+ * @since 1.5.4
144
+ */
145
+ self.onClickToggle = function() {
146
+ var $toggle = $( this ),
147
+ $tbody = $toggle.closest( 'table' ).find( 'tbody' );
148
+
149
+ $toggle.toggleClass( 'closed' );
150
+
151
+ if ( $toggle.hasClass( 'closed' ) ) {
152
+ $tbody.hide();
153
+ } else {
154
+ $tbody.show();
155
+ }
156
+ };
157
+
158
+ /**
159
+ * @summary Handle the click of the "View details" button for a database.
160
+ *
161
+ * @since 1.5.4
162
+ */
163
+ self.onClickViewDb = function() {
164
+ var $a = $( this ),
165
+ data = {
166
+ action: 'boldgrid_backup_browse_archive_view_db',
167
+ security: $( '#_wpnonce' ).val(),
168
+ filename: $( '#filename' ).val(),
169
+ file: $( '#dump_filename' ).val()
170
+ },
171
+ $details = $( '#db_details' ),
172
+ errorCallback;
173
+
174
+ // Only render the view once.
175
+ if ( 'true' === $details.attr( 'data-rendered' ) ) {
176
+ return;
177
+ }
178
+ $details.attr( 'data-rendered', 'true' );
179
+
180
+ $a.attr( 'disabled', 'disabled' );
181
+
182
+ $details.html( loading );
183
+
184
+ errorCallback = function() {
185
+ $details.html( boldgrid_backup_zip_browser.unknownErrorNotice );
186
+ };
187
+
188
+ $.post( ajaxurl, data, function( response ) {
189
+ var success = response.success !== undefined && true === response.success,
190
+ fail = response.success !== undefined && false === response.success;
191
+
192
+ if ( success || fail ) {
193
+ $details.html( response.data );
194
+ } else {
195
+ errorCallback();
196
+ }
197
+ } ).error( errorCallback );
198
+ };
199
+
200
+ /**
201
+ * @summary Render breadcrumbs.
202
+ *
203
+ * @since 1.5.3
204
+ *
205
+ * @param string dir
206
+ */
207
+ self.renderBreadcrumbs = function( dir ) {
208
+ var split,
209
+ $container = $zipBrowser.find( '.breadcrumbs' ),
210
+ html =
211
+ '<span class="dashicons dashicons-admin-home"></span> <a data-dir-".">' +
212
+ boldgrid_backup_zip_browser.home +
213
+ '</a> ',
214
+ dataDir = '';
215
+
216
+ dir = 'undefined' !== typeof dir ? dir.trim() : '/';
217
+ split = dir.split( '/' );
218
+
219
+ split.forEach( function( element ) {
220
+ if ( '' === element || '.' === element ) {
221
+ return;
222
+ }
223
+
224
+ dataDir += element + '/';
225
+
226
+ html += ' / <a data-dir="' + dataDir + '">' + element + '</a>';
227
+ } );
228
+
229
+ $container.html( html );
230
+ };
231
+
232
+ /**
233
+ * @summary Render the archive browser.
234
+ *
235
+ * @since 1.5.3
236
+ */
237
+ self.renderBrowser = function( dir ) {
238
+ var data,
239
+ colspan = $listing.find( 'thead th' ).length;
240
+
241
+ dir = 'undefined' !== typeof dir ? dir : '.';
242
+
243
+ $zipBrowser.show();
244
+
245
+ data = {
246
+ action: 'boldgrid_backup_browse_archive',
247
+ security: $( '#_wpnonce' ).val(),
248
+ filename: $( '#filename' ).val(),
249
+ dir: dir
250
+ };
251
+
252
+ self.renderBreadcrumbs( dir );
253
+
254
+ $listing.find( 'tbody' ).html( '<tr><td colspan="' + colspan + '">' + loading + '</td></tr>' );
255
+
256
+ $.post( ajaxurl, data, function( response ) {
257
+ if ( response.success !== undefined ) {
258
+ $listing.html( response.data );
259
+ } else {
260
+ $listing.html( boldgrid_backup_zip_browser.unknownBrowseError );
261
+ }
262
+ } ).error( function() {
263
+ $listing.html( boldgrid_backup_zip_browser.unknownBrowseError );
264
+ } );
265
+ };
266
+
267
+ /**
268
+ * @summary Init.
269
+ *
270
+ * @since 1.5.3
271
+ */
272
+ $( function() {
273
+ $( 'body' ).on( 'click', '.listing .folder', self.onClickFolder );
274
+ $( 'body' ).on( 'click', '.listing .file', self.onClickFile );
275
+ $( 'body' ).on( 'click', '.breadcrumbs a', self.onClickBreadcrumb );
276
+ $( 'body' ).on( 'click', '.restore-db', self.onClickRestoreDb );
277
+ $( 'body' ).on( 'click', '.view-db', self.onClickViewDb );
278
+ $( 'body' ).on( 'click', 'th .toggle-indicator', self.onClickToggle );
279
+ $( 'body' ).on( 'click', '.load-browser', self.onClickLoadBrowser );
280
+
281
+ self.renderBrowser( '.' );
282
+ } );
283
+ };
284
+
285
+ BoldGrid.ZipBrowser = new BoldGrid.ZipBrowser( jQuery );
admin/js/boldgrid-backup-admin.js ADDED
@@ -0,0 +1,269 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * This file contains javascript to load on all admin pages of the BoldGrid Backup plugin.
3
+ *
4
+ * @summary JS for all admin backup pages.
5
+ *
6
+ * @since 1.3.1
7
+ */
8
+
9
+ /* global jQuery,pagenow */
10
+
11
+ var BoldGrid = BoldGrid || {};
12
+
13
+ BoldGrid.Backup = function( $ ) {
14
+ var self = this;
15
+
16
+ /**
17
+ * @summary Handle the click of help buttons.
18
+ *
19
+ * @since 1.3.1
20
+ */
21
+ this.bindHelpClick = function() {
22
+ $( 'body' ).on( 'click', '.dashicons-editor-help', function() {
23
+ var id = $( this ).attr( 'data-id' );
24
+
25
+ // If we don't have a data-id, abort.
26
+ if ( id === undefined ) {
27
+ return;
28
+ }
29
+
30
+ // Toggle the help text.
31
+ $( '.help[data-id="' + id + '"]' ).slideToggle();
32
+ } );
33
+ };
34
+
35
+ /**
36
+ * @summary Remove WordPress' important notice about backups.
37
+ *
38
+ * We're already adding our own notice, no need to have 2 notices.
39
+ *
40
+ * @since 1.5.3
41
+ */
42
+ self.hideBackupNotice = function() {
43
+ if ( pagenow === undefined || 'update-core' !== pagenow ) {
44
+ return;
45
+ }
46
+
47
+ $( 'a[href*="WordPress_Backups"]' )
48
+ .closest( '.notice' )
49
+ .remove();
50
+ };
51
+
52
+ /**
53
+ * @summary Handle the clicking of a show / hide toggle.
54
+ *
55
+ * In the example below, the show / hide link has a data-toggle-target attr
56
+ * that helps to identify the element to toggle.
57
+ * # <a href="" data-toggle-target="#more_info">Show</a>
58
+ * # <div id="more_info" class="hidden">
59
+ *
60
+ * @since 1.5.4
61
+ */
62
+ self.onClickToggle = function() {
63
+ var $e = $( this ),
64
+ show = 'Show',
65
+ hide = 'Hide',
66
+ target = $e.attr( 'data-toggle-target' ),
67
+ $target = $( target ),
68
+ isVisible = $target.is( ':visible' );
69
+
70
+ if ( isVisible ) {
71
+ $target.slideUp();
72
+ $e.html( show );
73
+ } else {
74
+ $target.slideDown();
75
+ $e.html( hide );
76
+ }
77
+
78
+ return false;
79
+ };
80
+
81
+ /**
82
+ * @summary Action to take if we have a backup in progress.
83
+ *
84
+ * If we do have a backup in progress, we'll hook into the heartbeat and find
85
+ * out when that backup has been completed.
86
+ *
87
+ * @since 1.6.0
88
+ */
89
+ self.onInProgress = function() {
90
+ var complete = false,
91
+ $inProgressNotice = $( '.boldgrid-backup-in-progress' );
92
+
93
+ // If we're not actually showing an "in progress" notice, abort.
94
+ if ( 1 !== $inProgressNotice.length ) {
95
+ return;
96
+ }
97
+
98
+ // Increase the heartbeat so we can get an update sooner.
99
+ wp.heartbeat.interval( 'fast' );
100
+
101
+ /*
102
+ * When the heartbeat is sent, include that we're looking for an update
103
+ * on the in progress backup.
104
+ */
105
+ $( document ).on( 'heartbeat-send', function( e, data ) {
106
+ if ( ! complete ) {
107
+ data['boldgrid_backup_in_progress'] = true;
108
+ }
109
+ } );
110
+
111
+ // When the heartbeat is received, check to see if the backup has completed.
112
+ $( document ).on( 'heartbeat-tick', function( e, data ) {
113
+ var $notice;
114
+
115
+ if ( undefined === data.boldgrid_backup_in_progress ) {
116
+ return;
117
+ }
118
+
119
+ if ( ! data.boldgrid_backup_in_progress ) {
120
+ $notice = $( data.boldgrid_backup_complete );
121
+ $notice
122
+ .css( 'display', 'none' )
123
+ .insertBefore( $inProgressNotice )
124
+ .slideDown();
125
+
126
+ $inProgressNotice.slideUp();
127
+
128
+ wp.heartbeat.interval( 'standard' );
129
+ complete = true;
130
+
131
+ $( 'body' ).trigger( 'make_notices_dismissible' );
132
+ $( 'body' ).trigger( 'boldgrid_backup_complete' );
133
+ }
134
+ } );
135
+ };
136
+
137
+ /**
138
+ * @summary Make an admin notice dismissible.
139
+ *
140
+ * This is a core WordPress function copied from wp-admin/js/common.js.
141
+ *
142
+ * Unfortunately this function cannot be called upon at will. If we dynamically
143
+ * add a notice and it includess the is-dismissible class, then we'll need
144
+ * to actually make it dismissible.
145
+ *
146
+ * @since 1.6.0
147
+ */
148
+ self.makeNoticesDismissible = function() {
149
+ $( '.notice.is-dismissible' ).each( function() {
150
+ var $el = $( this ),
151
+ $button = $(
152
+ '<button type="button" class="notice-dismiss"><span class="screen-reader-text"></span></button>'
153
+ ),
154
+ btnText = 'undefined' !== typeof commonL10n ? commonL10n.dismiss : wp.customize.l10n.close;
155
+
156
+ // Ensure plain text
157
+ $button.find( '.screen-reader-text' ).text( btnText );
158
+ $button.on( 'click.wp-dismiss-notice', function( event ) {
159
+ event.preventDefault();
160
+ $el.fadeTo( 100, 0, function() {
161
+ $el.slideUp( 100, function() {
162
+ $el.remove();
163
+ } );
164
+ } );
165
+ } );
166
+
167
+ $el.append( $button );
168
+ } );
169
+ };
170
+
171
+ $( function() {
172
+ self.bindHelpClick();
173
+ self.hideBackupNotice();
174
+ self.updatePremiumLink();
175
+
176
+ /*
177
+ * If and when a backup is in progress, we need to begin waiting to hear
178
+ * for that backup to complete.
179
+ */
180
+ self.onInProgress();
181
+ $( 'body' ).on( 'boldgrid_backup_progress_notice_added', self.onInProgress );
182
+
183
+ $( 'body' ).on( 'click', '[data-toggle-target]', self.onClickToggle );
184
+ $( 'body' ).on( 'make_notices_dismissible', self.makeNoticesDismissible );
185
+
186
+ /*
187
+ * Remove temporary "page loading" messages.
188
+ *
189
+ * Some pages may take a few moments to render. For example, when checking
190
+ * ftp credentials, it may take ~3 seconds. We give the user a message,
191
+ * "Checking credentials", and then remove this notices afterwards by
192
+ * removing anything with a bgbu_remove_load class.
193
+ */
194
+ $( '.bgbu-remove-load' ).remove();
195
+ } );
196
+
197
+ /**
198
+ * @summary Open submenu "Get Premium" link in a new tab.
199
+ *
200
+ * WordPress does not have a way to configure dashboard menu items to open
201
+ * in a new tab, so we'll handle it via js.
202
+ *
203
+ * @since 1.6.0
204
+ */
205
+ self.updatePremiumLink = function() {
206
+ $( '#adminmenu' )
207
+ .find( 'a[href="' + BoldGridBackupAdmin.get_premium_url + '"]' )
208
+ .attr( 'target', '_blank' );
209
+ };
210
+ };
211
+
212
+ BoldGrid.Backup( jQuery );
213
+
214
+ /**
215
+ * @summary Draw attention to an element.
216
+ *
217
+ * @since 1.5.4
218
+ */
219
+ jQuery.fn.bgbuDrawAttention = function() {
220
+ var currentColor,
221
+
222
+ // In seconds, minimum time between each animation.
223
+ animateInterval = 1 * 1000,
224
+ d = new Date(),
225
+ lastAnimation = this.attr( 'data-last-animation' );
226
+
227
+ this.attr( 'data-last-animation', d.getTime() );
228
+
229
+ // If we are currently animating this element, return.
230
+ if ( this.parent().hasClass( 'ui-effects-wrapper' ) ) {
231
+ return;
232
+ }
233
+
234
+ // If enough time hasn't passed yet since the last animation, return.
235
+ if ( lastAnimation && d.getTime() - lastAnimation < animateInterval ) {
236
+ return;
237
+ }
238
+
239
+ if ( this.is( 'input' ) ) {
240
+ this.css( 'background', '#ddd' ).animate( { backgroundColor: '#fff' }, 500 );
241
+ } else if ( this.is( '.dashicons-editor-help' ) ) {
242
+ this.effect( 'bounce', { times: 2 }, 'normal' );
243
+ } else if ( this.is( 'span' ) ) {
244
+
245
+ /*
246
+ * Get the original color to animate back to. This is needed because if
247
+ * the user clicks on an element that is in the middle of an animation,
248
+ * the current color will not be the original.
249
+ */
250
+ if ( ! this.attr( 'data-original-color' ) ) {
251
+ this.attr( 'data-original-color', this.css( 'color' ) );
252
+ }
253
+ currentColor = this.attr( 'data-original-color' );
254
+
255
+ this.css( 'color', '#fff' ).animate( { color: currentColor }, 500 );
256
+ }
257
+ };
258
+
259
+ /**
260
+ * @summary Disable all actions found within an element (a, buttons, etc).
261
+ *
262
+ * For example, if we click "restore" on a page, we want to disable all other
263
+ * actions within the wpwrap (IE can't restore and delete at the same time).
264
+ *
265
+ * @since 1.5.4
266
+ */
267
+ jQuery.fn.bgbuDisableActions = function() {
268
+ this.find( 'a, [type="submit"]' ).attr( 'disabled', 'disabled' );
269
+ };
admin/partials/archive-details/browser-entry.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Create the <tr> for each file in the archilve.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.3
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin/partials/archive-details
10
+ */
11
+
12
+ defined( 'WPINC' ) ? : die;
13
+
14
+ $class = $file['folder'] ? 'folder' : 'file';
15
+
16
+ $icon = 'folder' === $class ? 'dashicons dashicons-portfolio' : 'dashicons dashicons-media-default';
17
+
18
+ $size = empty( $file['size'] ) ? '' : Boldgrid_Backup_Admin_Utility::bytes_to_human( $file['size'] );
19
+
20
+ /*
21
+ * Get the last modified time for this file.
22
+ *
23
+ * ZipArchive uses the server's local time as a timestamp, while PclZip uses UTC.
24
+ */
25
+ $mtime = ! empty( $file['mtime'] ) ? $file['mtime'] : null;
26
+ $this->core->time->init( $mtime, $this->core->archive->compressor );
27
+
28
+ return sprintf(
29
+ '<tr data-dir="%1$s">
30
+ <td>
31
+ <span class="%4$s"></span>
32
+ <a class="%2$s">%3$s</a>
33
+ </td>
34
+ <td>
35
+ %5$s
36
+ </td>
37
+ <td>
38
+ %6$s
39
+ </td>
40
+ </tr>',
41
+ $file['filename'],
42
+ $class,
43
+ basename( $file['filename'] ),
44
+ $icon,
45
+ $size,
46
+ $this->core->time->get_span()
47
+ );
48
+
49
+
admin/partials/archive-details/browser.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Display the Archive Browser section on the Archive Details page.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.3
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin/partials/archive-details
10
+ */
11
+
12
+ defined( 'WPINC' ) ? : die;
13
+
14
+ $browser = '
15
+ <div id="zip_browser" class="hidden" data-view-type="file">
16
+
17
+ <div class="breadcrumbs" style="padding:8px 10px; background:#f5f5f5; border: 1px solid #e5e5e5; border-bottom:0px; border-top:0px;">
18
+ </div>
19
+
20
+ <div class="listing">
21
+ <table>
22
+ <tbody>
23
+ </tbody>
24
+ </table>
25
+ </div>
26
+ </div>'
27
+ ;
28
+
29
+ return $browser;
30
+
31
+
admin/partials/archive-details/db.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Display the Database section on the Archive Details page.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.4
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin/partials/archive-details
10
+ */
11
+
12
+ defined( 'WPINC' ) ? : die;
13
+
14
+ $db = array(
15
+ 'browser' => '',
16
+ 'buttons' => '',
17
+ );
18
+
19
+ if ( empty( $dump_file ) ) {
20
+ $db['browser'] = sprintf( '
21
+ <div class="hidden" data-view-type="db">
22
+ <p>%1$s</p>
23
+ </div>',
24
+ __( 'This archive does not contain a database backup.', 'boldgrid-backup' )
25
+ );
26
+ return $db;
27
+ }
28
+
29
+ $contains = __( 'This archive contains the following database backup: <strong>%1$s</strong>', 'boldgrid-backup' );
30
+ $basename = basename( $dump_file );
31
+
32
+ $db = array(
33
+ 'browser' => sprintf( '
34
+ <div class="hidden" data-view-type="db">
35
+ <input type="hidden" id="dump_filename" value="%1$s" />
36
+ <div id="db_details" data-rendered="false"></div>
37
+ </div>',
38
+ $basename
39
+ ),
40
+ 'buttons' => sprintf(
41
+ '<a class="restore-db button button-primary" data-file="%2$s" data-view-type="db" style="display:none;">%1$s</a>%3$s',
42
+ __( 'Restore this database', 'boldgrid-backup' ),
43
+ esc_attr( $basename ),
44
+ $this->core->lang['spinner']
45
+ ),
46
+ );
47
+
48
+ return $db;
49
+
50
+
admin/partials/archive-details/details.php ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Render the details of a particular backup.
4
+ *
5
+ * @since 1.5.4
6
+ *
7
+ * @package Boldgrid_Backup
8
+ * @subpackage Boldgrid_Backup/admin/partials
9
+ *
10
+ * @param bool $archive_found Whether or not the archive was found.
11
+ */
12
+
13
+ defined( 'WPINC' ) ? : die;
14
+
15
+ $details = '';
16
+
17
+ $attribute = '<p><strong>%1$s</strong>: %2$s</p>';
18
+
19
+ $datas = array(
20
+ array(
21
+ 'key' => 'trigger',
22
+ 'title' => __( 'Backup triggered by', 'boldgrid-backup' ),
23
+ ),
24
+ array(
25
+ 'key' => 'compressor',
26
+ 'title' => __( 'Compressor', 'boldgrid-backup' ),
27
+ ),
28
+ array(
29
+ 'key' => 'duration',
30
+ 'title' => __( 'Total duration', 'boldgrid-backup' ),
31
+ 'suffix' => ' ' . __( 'seconds', 'boldgrid-backup' ),
32
+ ),
33
+ array(
34
+ 'key' => 'db_duration',
35
+ 'title' => __( 'Time to backup database', 'boldgrid-backup' ),
36
+ 'suffix' => ' ' . __( 'seconds', 'boldgrid-backup' ),
37
+ ),
38
+ array(
39
+ 'key' => 'mail_success',
40
+ 'title' => __( 'Email sent after backup', 'boldgrid-backup' ),
41
+ 'presentation' => 'bool',
42
+ ),
43
+ array(
44
+ 'key' => 'folder_include',
45
+ 'title' => __( 'Files included', 'boldgrid-backup' ),
46
+ ),
47
+ array(
48
+ 'key' => 'folder_exclude',
49
+ 'title' => __( 'Files excluded', 'boldgrid-backup' ),
50
+ ),
51
+ array(
52
+ 'key' => 'table_exclude',
53
+ 'title' => __( 'Database tables excluded', 'boldgrid-backup' ),
54
+ 'presentation' => 'comma_implode',
55
+ ),
56
+ );
57
+
58
+ foreach ( $datas as $data ) {
59
+ if ( ! isset( $archive[ $data['key'] ] ) ) {
60
+ continue;
61
+ }
62
+
63
+ if ( ! empty( $data['heading'] ) ) {
64
+ $details .= sprintf( '<h2>%1$s:</h2>', $data['heading'] );
65
+ }
66
+
67
+ $value = $archive[ $data['key'] ];
68
+ if ( ! empty( $data['presentation'] ) ) {
69
+ switch ( $data['presentation'] ) {
70
+ case 'bytes_to_human':
71
+ $value = Boldgrid_Backup_Admin_Utility::bytes_to_human( $archive[ $data['key'] ] );
72
+ break;
73
+ case 'bool':
74
+ $value = $archive[ $data['key'] ] ? __( 'yes', 'boldgrid-backup' ) : __( 'no', 'boldgrid-backup' );
75
+ break;
76
+ case 'comma_implode':
77
+ $value = empty( $value ) ? __( 'n/a', 'boldgrid-backup' ) : implode( ', ', $value );
78
+ break;
79
+ }
80
+ }
81
+
82
+ if ( ! empty( $data['suffix'] ) ) {
83
+ $value .= $data['suffix'];
84
+ }
85
+
86
+ if ( ! empty( $data['hidden_input'] ) ) {
87
+ $value .= sprintf( '<input type="hidden" id="%1$s" value="%2$s" />', $data['key'], $archive[ $data['key'] ] );
88
+ }
89
+
90
+ $details .= sprintf( $attribute, $data['title'], $value );
91
+ }
92
+
93
+ return $details;
94
+
95
+
admin/partials/archive-details/not-found.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Display for instances in which backup is not local and not remote.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.6.0
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin/partials/archive-details
10
+ */
11
+
12
+ defined( 'WPINC' ) ? : die;
13
+
14
+ return '<p>' . $this->core->lang['icon_warning'] . __( 'Backup file not found!', 'boldgrid-backup' ) . '</p>';
15
+
16
+
admin/partials/archive-details/only-remote.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Display for instances in which backup is not local, but exists remotely.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.4
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin/partials/archive-details
10
+ */
11
+
12
+ defined( 'WPINC' ) ? : die;
13
+
14
+ return sprintf( '
15
+ <p>
16
+ %1$s <span class="dashicons dashicons-editor-help" data-id="help-web-server"></span>
17
+ </p>
18
+ <p class="help" data-id="help-web-server">
19
+ %4$s
20
+ </p>
21
+ %2$s %3$s
22
+ ',
23
+ __( 'This backup file is not on your <strong>web server</strong>, but it is saved to one or more of your <strong>remote storage providers</strong>. If you would like to restore this backup or review the contents of this backup, you will first need to download it to your web server.', 'boldgrid-backup' ),
24
+ '<a class="button button-primary" id="download_first">Download to web server</a>',
25
+ $this->core->lang['spinner'],
26
+ sprintf(
27
+ __( 'After your backup has been downloaded to the web server, this page will refresh and you will see more options available. To learn more about your web server vs. remote storage providers, <a href="%1$s">click here</a>.', 'boldgrid-backup' ),
28
+ 'admin.php?page=boldgrid-backup-tools&section=section_locations'
29
+ )
30
+ );
31
+
32
+
admin/partials/archive-details/remote-storage.php ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Render remote provider's table on archive details page.
4
+ *
5
+ * This file is included by admin/partials/boldgrid-backup-admin-archive-details.php
6
+ * whis is included by admin/class-boldgrid-backup-admin-archive-details.php
7
+ *
8
+ * @param bool $archive_found Whether or not the archive was found.
9
+ */
10
+
11
+ defined( 'WPINC' ) ? : die;
12
+
13
+ $data['postbox'] = '';
14
+
15
+ $action = 'boldgrid_backup_single_archive_remote_options';
16
+ if ( ! empty( $archive['filepath'] ) ) {
17
+ do_action( $action, $archive['filepath'] );
18
+ } elseif ( ! empty( $archive['filename'] ) ) {
19
+ do_action( $action, $archive['filename'] );
20
+ }
21
+
22
+ if ( empty( $this->remote_storage_li ) ) {
23
+ $data['postbox'] = __( 'No remote storage options available.', 'boldgrid-backup' );
24
+ return $data;
25
+ }
26
+
27
+ $count = 0;
28
+ foreach ( $this->remote_storage_li as $provider ) {
29
+ $count++;
30
+
31
+ // Generate a link to "download to server" from remote provider.
32
+ $download = '';
33
+ if ( ! $archive_found && $provider['uploaded'] ) {
34
+ $download = sprintf( '
35
+ <a class="button download-to-server" data-provider-id="%3$s">%1$s</a>
36
+ %2$s
37
+ ',
38
+ __( 'Download to web server', 'boldgrid-backup' ),
39
+ $this->core->lang['spinner'],
40
+ $provider['id']
41
+ );
42
+ }
43
+
44
+ if ( $provider['uploaded'] ) {
45
+ $upload = '&#10003; ' . __( 'Uploaded', 'boldgrid-backup' );
46
+ } elseif ( $provider['allow_upload'] && $archive_found ) {
47
+ $upload = sprintf(
48
+ '<a class="button button-primary upload" data-provider-id="%2$s">%1$s</a>',
49
+ __( 'Upload', 'boldgrid-backup' ),
50
+ $provider['id']
51
+ );
52
+ } elseif ( isset( $provider['is_setup'] ) and false === $provider['is_setup'] ) {
53
+ $upload = sprintf( __( 'Please go to your <a target="_parent" href="%1$s">%2$s</a> to configure %3$s.', 'boldgrid-backup' ), 'admin.php?page=boldgrid-backup-settings', __( 'settings page', 'boldgrid-backup' ), $provider['title'] );
54
+ } else {
55
+ $upload = '';
56
+ }
57
+
58
+ $data['postbox'] .= sprintf( '
59
+ %5$s
60
+ <div data-remote-provider="%3$s">
61
+ <span style="float:left;" %6$s><strong>%1$s</strong></span>
62
+ <span style="float:right;max-width:50%%;">%2$s</span>
63
+
64
+ <div style="clear:both;"></div>
65
+
66
+ <p>%4$s</p>
67
+ </div>
68
+ ',
69
+ /* 1 */ esc_html( $provider['title'] ),
70
+ /* 2 */ $upload,
71
+ /* 3 */ $provider['id'],
72
+ /* 4 */ $download,
73
+ /* 5 */ 1 !== $count ? '<hr class="separator-small" />' : '',
74
+ /* 6 */ empty( $provider['title_attr'] ) ? '' : sprintf( 'title="%1$s"', esc_attr( $provider['title_attr'] ) )
75
+ );
76
+ }
77
+
78
+ return $data;
79
+
80
+
admin/partials/archives/add-new.php ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file contains the markup necessary to upload a new backup archive.
4
+ *
5
+ * It follows the same structure as the "Upload Plugin" section of plugins.
6
+ *
7
+ * @since 1.5.1
8
+ *
9
+ * @package Boldgrid_Backup
10
+ * @subpackage Boldgrid_Backup/admin/partials/archives
11
+ */
12
+
13
+ defined( 'WPINC' ) ? : die;
14
+
15
+ $max_file_size = Boldgrid_Backup_Admin_Utility::get_upload_limit();
16
+
17
+ $size_limit = esc_html__( 'File size limit', 'boldgrid-backup' ) . ': ' .
18
+ Boldgrid_Backup_Admin_Utility::bytes_to_human(
19
+ Boldgrid_Backup_Admin_Utility::get_upload_limit()
20
+ );
21
+
22
+ $size_info = esc_html__(
23
+ 'To change the limit, you may be able to modify your server\'s php.ini or .htaccess file. Please ask your web hosting provider if you need assistance.',
24
+ 'boldgrid-backup'
25
+ );
26
+
27
+ $upload_info = esc_html__(
28
+ 'You can upload a backup file that was created with BoldGrid Backup.
29
+ If you choose to restore an uploaded file from a different web location (URL), then we will try to ensure that references to URL address are updated.
30
+ There may be times when some items may need to be updated manually.',
31
+ 'boldgrid-backup'
32
+ );
33
+
34
+ $backup_id_notice = sprintf(
35
+ esc_html__(
36
+ 'Your BoldGrid Backup id is %3$s. This backup id is used to determine if a backup archive file is associated with this WordPress installation.
37
+ Manually uploaded archive files must have filenames starting with "%2$s", contain the BoldGrid Backup id "%3$s", and end with "%4$s", to be recognized.%1$s%1$s
38
+ For example: %5$s%1$s%1$s
39
+ Manually uploaded archive files should be uploaded to: %6$s',
40
+ 'boldgrid-backup'
41
+ ),
42
+ '<br />',
43
+ 'boldgrid-backup-',
44
+ $backup_identifier,
45
+ '.zip',
46
+ 'boldgrid-backup-' . $backup_identifier . '-my_backup_file.zip',
47
+ $settings['backup_directory']
48
+ );
49
+
50
+ ?>
51
+
52
+ <div id="add_new" class="upload-plugin">
53
+
54
+ <p class="install-help">
55
+ <?php esc_html_e( 'Upload a Backup Archive', 'boldgrid-backup' ); ?>
56
+ <span class="dashicons dashicons-editor-help" data-id="upload-backup"></span>
57
+ </p>
58
+
59
+ <p class="help wp-upload-form" data-id="upload-backup">
60
+ <?php printf( '%1$s<br />%2$s<br /><br />%3$s', $size_limit, $size_info, $upload_info ); ?>
61
+ </p>
62
+
63
+ <div id="upload-archive-section" class="wp-upload-form">
64
+ <form id="upload-archive-form" method="POST" enctype="multipart/form-data">
65
+ <input type="hidden" name="MAX_FILE_SIZE" value="<?php echo $max_file_size; ?>" />
66
+ <input type="hidden" name="uploading" value="1" />
67
+ <?php wp_nonce_field( 'upload_archive_file' ); ?>
68
+ <input name="file" type="file" />
69
+ <input class="button" type="submit" value="Upload" />
70
+ <span class='spinner'></span>
71
+ </form>
72
+
73
+ <p id="file_too_large" class="hidden">
74
+ <span class="dashicons dashicons-warning yellow"></span> <?php echo __( 'The file you selected is too large.', 'boldgrid-bacup' ); ?>
75
+ </p>
76
+
77
+ <p id="bad_extension" class="hidden">
78
+ <span class="dashicons dashicons-warning yellow"></span> <?php echo __( 'Invalid file format. Please choose a .zip file.', 'boldgrid-bacup' ); ?>
79
+ </p>
80
+ </div>
81
+
82
+ <p class="install-help">
83
+ <?php esc_html_e( 'Have a large site or want to FTP?', 'boldgrid-backup' ); ?>
84
+ <span class="dashicons dashicons-editor-help" data-id="backup-id"></span>
85
+ </p>
86
+
87
+ <div class="help wp-upload-form" data-id="backup-id">
88
+ <?php echo $backup_id_notice; ?>
89
+ </div>
90
+
91
+ </div>
admin/partials/archives/note-pre-backup.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Display a note for the user next to the "Backup Site Now" button.
4
+ *
5
+ * @since 1.3
6
+ *
7
+ * @package Boldgrid_Backup
8
+ * @subpackage Boldgrid_Backup/admin/partials/archives
9
+ */
10
+
11
+ defined( 'WPINC' ) ? : die;
12
+ ?>
13
+
14
+ <p id='note-pre-backup'>
15
+ <?php
16
+ /*
17
+ * Print this text:
18
+ *
19
+ * Note: Backups use resources and <a>must pause your site</a> momentarily. Use sparingly.
20
+ */
21
+ $link = sprintf(
22
+ wp_kses(
23
+ __( '<strong>Note</strong>: Backups use resources and <a href="%s" target="_blank">must pause your site</a> momentarily. Use sparingly. ', 'boldgrid-backup' ),
24
+ array(
25
+ 'a' => array( 'href' => array(), 'target' => array() ),
26
+ 'strong' => array(),
27
+ )
28
+ ),
29
+ esc_url( $this->configs['urls']['resource_usage'] )
30
+ );
31
+ echo $link;
32
+
33
+ /*
34
+ * Print this text:
35
+ *
36
+ * You currently have x backups stored on your server, and your <a>backup settings</a> are
37
+ * only configured to store x. Backing up your site now will delete your oldest backup to
38
+ * make room for your new backup. We recommend you download a backup to your local computer.
39
+ */
40
+ if ( count( $archives ) >= $settings['retention_count'] ) {
41
+ $link = sprintf(
42
+ wp_kses(
43
+ __( 'You currently have %1$s backups stored on your server, and your <a href="%3$s">backup settings</a> are only configured to store %2$s. Backing up your site now will delete your oldest backup to make room for your new backup. We recommend you download a backup to your local computer.', 'boldgrid-backup' ),
44
+ array(
45
+ 'a' => array( 'href' => array() ),
46
+ )
47
+ ),
48
+ count( $archives ),
49
+ $settings['retention_count'],
50
+ get_admin_url( null, 'admin.php?page=boldgrid-backup-settings' )
51
+ );
52
+ echo $link;
53
+ }
54
+ ?>
55
+ </p>
admin/partials/boldgrid-backup-admin-archive-details.php ADDED
@@ -0,0 +1,240 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file contains renders the details page of a backup archive.
4
+ *
5
+ * @since 1.5.1
6
+ *
7
+ * @package Boldgrid_Backup
8
+ * @subpackage Boldgrid_Backup/admin/partials
9
+ *
10
+ * @param bool $archive_found Whether or not the archive was found.
11
+ * @param array $archive An array of details about the archive, similar to
12
+ * the $info created during archiving.
13
+ */
14
+
15
+ defined( 'WPINC' ) ? : die;
16
+
17
+ wp_enqueue_style( 'editor-buttons' );
18
+
19
+ wp_nonce_field( 'boldgrid_backup_remote_storage_upload' );
20
+
21
+ $separator = '<hr class="separator">';
22
+
23
+ $details = include BOLDGRID_BACKUP_PATH . '/admin/partials/archive-details/details.php';
24
+ $remote_storage = include BOLDGRID_BACKUP_PATH . '/admin/partials/archive-details/remote-storage.php';
25
+ $browser = include BOLDGRID_BACKUP_PATH . '/admin/partials/archive-details/browser.php';
26
+ $db = include BOLDGRID_BACKUP_PATH . '/admin/partials/archive-details/db.php';
27
+
28
+ // Special situations where the backup file is not local and/or remote.
29
+ $only_remote = include BOLDGRID_BACKUP_PATH . '/admin/partials/archive-details/only-remote.php';
30
+ $not_found = include BOLDGRID_BACKUP_PATH . '/admin/partials/archive-details/not-found.php';
31
+
32
+ $delete_link = $this->core->archive_actions->get_delete_link( $archive['filename'] );
33
+ $download_button = $this->core->archive_actions->get_download_button( $archive['filename'] );
34
+ $restore_button = $this->core->archive_actions->get_restore_button( $archive['filename'] );
35
+
36
+ if ( ! $archive_found ) {
37
+ $file_size = '';
38
+ $backup_date = '';
39
+ $more_info = '';
40
+ $major_actions = '';
41
+ } else {
42
+ $file_size = sprintf(
43
+ '<div class="misc-pub-section">%1$s: <strong>%2$s</strong></div>',
44
+ __( 'File size', 'boldgrid-backup' ),
45
+ Boldgrid_Backup_Admin_Utility::bytes_to_human( $archive['filesize'] )
46
+ );
47
+
48
+ // dirlist -> lastmodunix -> mtime (last_modified in unix time).
49
+ $this->core->time->init( $archive['lastmodunix'], 'utc' );
50
+ $backup_date = sprintf(
51
+ '<div class="misc-pub-section">%1$s: <strong>%2$s</strong></div>',
52
+ __( 'Backup date', 'boldgrid-backup' ),
53
+ $this->core->time->get_span()
54
+ );
55
+
56
+ $more_info = empty( $details ) ? '' : sprintf( '
57
+ <div class="misc-pub-section">
58
+ More info <a href="" data-toggle-target="#more_info">Show</a>
59
+ <div id="more_info" class="hidden">
60
+ <hr />
61
+ %1$s
62
+ </div>
63
+ </div>',
64
+ $details
65
+ );
66
+
67
+ $major_actions = sprintf( '
68
+ <div id="major-publishing-actions">
69
+ <div id="delete-action">
70
+ %1$s
71
+ </div>
72
+
73
+ <div style="clear:both;"></div>
74
+ </div>',
75
+ $delete_link
76
+ );
77
+ }
78
+
79
+ $main_meta_box = sprintf( '
80
+ <div id="submitdiv" class="postbox">
81
+ <h2 class="hndle ui-sortable-handle"><span>%1$s</span></h2>
82
+ <div class="inside submitbox">
83
+
84
+ <div class="misc-pub-section">%2$s: <strong>%3$s</strong></div>
85
+
86
+ %4$s
87
+
88
+ %5$s
89
+
90
+ %6$s
91
+
92
+ %7$s
93
+
94
+ </div>
95
+ </div>',
96
+ /* 1 */ __( 'Backup Archive', 'boldgrid-backup' ),
97
+ /* 2 */ __( 'File name', 'boldgrid-backup' ),
98
+ /* 3 */ $archive['filename'],
99
+ /* 4 */ $file_size,
100
+ /* 5 */ $backup_date,
101
+ /* 6 */ $more_info,
102
+ /* 7 */ $major_actions
103
+ );
104
+
105
+ $remote_meta_box = sprintf( '
106
+ <div class="postbox remote-storage">
107
+ <h2 class="hndle ui-sortable-handle">
108
+ <span>%1$s</span>
109
+ <span class="dashicons dashicons-editor-help" data-id="remote-storage-help"></span>
110
+ </h2>
111
+
112
+ <div class="inside">
113
+
114
+ <p class="help" data-id="remote-storage-help">%4$s</p>
115
+
116
+ %2$s
117
+ </div>
118
+
119
+ %3$s
120
+ </div>',
121
+ /* 1 */ __( 'Remote Storage', 'boldgrid-backup' ),
122
+ /* 2 */ $remote_storage['postbox'],
123
+ /* 3 */ $this->core->config->is_premium_done ? '' : sprintf( '
124
+ <div class="inside premium wp-clearfix">
125
+ %1$s
126
+ %2$s
127
+ </div>',
128
+ /* 1 */ $this->core->go_pro->get_premium_button(),
129
+ /* 2 */ __( 'Upgrade to <strong>BoldGrid Backup Premium</strong> for more Storage Locations!', 'boldgrid-backup' )
130
+ ),
131
+ /* 4 */ __( 'Secure your backups by keeping copies of them on <a href="admin.php?page=boldgrid-backup-tools&section=section_locations">remote storage</a>.', 'boldgrid-backup' )
132
+ );
133
+
134
+ $editor_tools = sprintf( '
135
+ <div style="padding-top:0px;" id="wp-content-editor-tools" class="wp-editor-tools hide-if-no-js">
136
+ <div id="wp-content-media-buttons" class="wp-media-buttons">
137
+ %1$s
138
+ </div>
139
+ <div class="wp-editor-tabs">
140
+ <button type="button" id="content-tmce" class="wp-switch-editor switch-tmce" data-wp-editor-id="content">%2$s</button>
141
+ <button type="button" id="content-html" class="wp-switch-editor switch-html" data-wp-editor-id="content">%3$s</button>
142
+ </div>
143
+ </div>
144
+ ',
145
+ /* 1 */ $db['buttons'],
146
+ /* 2 */ __( 'Files & Folders', 'boldgrid-backup' ),
147
+ /* 3 */ __( 'Database', 'boldgrid-backup' )
148
+ );
149
+
150
+ $intro = $this->core->config->is_premium_done ? '' : sprintf( '
151
+ <div class="bg-box-bottom premium" style="margin-bottom:15px;">
152
+ <strong>%1$s</strong>
153
+
154
+ <p>
155
+ %2$s
156
+ %3$s
157
+ </p>
158
+ </div>',
159
+ /* 1 */ __( 'One click file restorations', 'boldgrid-backup' ),
160
+ /* 2 */ $this->core->go_pro->get_premium_button(),
161
+ /* 3 */ __( 'Please note that most functionality for the Archive Browser, such as one click file restorations, is contained within the Premium version. For help with restoring a single file without this one click feature, please <a href="https://www.boldgrid.com/support" target="_blank">click here</a>.', 'boldgrid-backup' )
162
+ );
163
+
164
+ $main_content = '
165
+ <div id="postdivrich" class="postarea wp-editor-expand">
166
+
167
+ <div id="wp-content-wrap" class="wp-core-ui wp-editor-wrap tmce-active has-dfw">
168
+
169
+ <h2 style="font-size:initial; padding:0px;">
170
+ ' . __( 'Download & Restore', 'boldgrid-backup' ) . '
171
+ <span class="dashicons dashicons-editor-help" data-id="download_and_restore"></span>
172
+ </h2>
173
+
174
+ <p class="help" data-id="download_and_restore">
175
+ <strong>' . __( 'Download to Local Machine', 'boldgrid-backup' ) . '</strong><br />
176
+ ' . __( 'Backup archives generally should be stored in more locations than just your <strong>web server</strong>. Be sure to keep copies on your <strong>local machine</strong> and / or a <strong>remote storage</strong> provider. Learn more about these different locations <a href="admin.php?page=boldgrid-backup-tools&section=section_locations">here</a>.', 'boldgrid-backup' ) . '<br /><br />
177
+ <strong>' . __( 'Restore', 'boldgrid-backup' ) . '</strong><br />
178
+ ' . __( 'Restore this backup. This will restore all the files and the database in this backup. Use the <strong>Backup Browser</strong> below to look at the backup archive and see what will be restored.', 'boldgrid-backup' ) . '
179
+ </p>
180
+
181
+ <p>
182
+ ' . $download_button . ' ' . $restore_button . '
183
+ </p>
184
+
185
+ <hr class="separator" />
186
+
187
+ <h2 style="font-size:initial; padding:0px;">' . __( 'Backup Browser', 'boldgrid-backup' ) . '</h2>
188
+
189
+ <p>
190
+ ' . __( 'Use the File & Folders and Database tools below to browse the contents of this backup file.', 'boldgrid-backup' ) . '
191
+ </p>
192
+
193
+ ' . $intro . $editor_tools . $browser . $db['browser'] . '
194
+ </div>
195
+ </div>
196
+ ';
197
+
198
+ if ( ! $this->core->archive->is_stored_locally() ) {
199
+
200
+ if ( $this->core->archive->is_stored_remotely() ) {
201
+ $main_content = $only_remote;
202
+ } else {
203
+ $main_content = $not_found;
204
+ $remote_meta_box = '';
205
+ }
206
+ }
207
+
208
+ $page = sprintf( '
209
+ <input type="hidden" id="filename" value="%1$s" />
210
+ <div class="wrap">
211
+ <h1 class="wp-heading-inline">%2$s</h1>
212
+ %3$s
213
+ <div id="poststuff">
214
+ <div id="post-body" class="metabox-holder columns-2">
215
+ <div id="post-body-content" style="position: relative">
216
+ %4$s
217
+ </div>
218
+ <div id="postbox-container-1" class="postbox-container">
219
+ <div id="side-sortables" class="meta-box-sortables ui-sortable" style="">
220
+
221
+ %5$s
222
+
223
+ %6$s
224
+ </div>
225
+ </div>
226
+ </div>
227
+ </div>
228
+ </div>
229
+ ',
230
+ /* 1 */ $archive['filename'],
231
+ /* 2 */ __( 'Backup Archive Details', 'boldgrid-backup' ),
232
+ /* 3 */ include BOLDGRID_BACKUP_PATH . '/admin/partials/boldgrid-backup-admin-nav.php',
233
+ /* 4 */ $main_content,
234
+ /* 5 */ $main_meta_box,
235
+ /* 6 */ $remote_meta_box
236
+ );
237
+
238
+ echo $page;
239
+
240
+
admin/partials/boldgrid-backup-admin-backup-button.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Display a "Backup Site Now" button.
4
+ *
5
+ * @since 1.3
6
+ *
7
+ * @package Boldgrid_Backup
8
+ * @subpackage Boldgrid_Backup/admin/partials/archives
9
+ */
10
+
11
+ defined( 'WPINC' ) ? : die;
12
+
13
+ $core = isset( $this->core ) ? $this->core : $this;
14
+
15
+ // Are we loading the "protect now" form via ajax?
16
+ $update_protection_ajax = ! empty( $_POST['action'] ) && 'boldgrid_backup_get_protect_notice' === $_POST['action'] && ! empty( $_POST['update_protection'] );
17
+
18
+ return sprintf(
19
+ '<div id="backup-site-now-section">
20
+ <form action="#" id="backup-site-now-form" method="POST">
21
+ %1$s
22
+ <p id="you_may_leave" class="hidden">
23
+ %4$s
24
+ </p>
25
+ <p>
26
+ <a id="backup-site-now" class="button button-primary" %3$s >
27
+ %2$s
28
+ </a>
29
+ <span class="spinner"></span>
30
+ </p>
31
+ </form>
32
+ </div>
33
+ <div id="backup-site-now-results"></div>',
34
+ wp_nonce_field( 'boldgrid_backup_now', 'backup_auth', true, false ),
35
+ esc_html( 'Backup Site Now', 'boldgrid-backup' ),
36
+ $update_protection_ajax || $core->auto_rollback->on_update_page ? 'data-updating="true"' : '',
37
+ /* 4 */ __( 'You may leave this page, doing so will not stop your backup.', 'boldgrid-backup' )
38
+ );
39
+
40
+
admin/partials/boldgrid-backup-admin-backup-modal.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Display a "Backup Site Now" button and modal.
4
+ *
5
+ * @since 1.5.4
6
+ *
7
+ * @package Boldgrid_Backup
8
+ * @subpackage Boldgrid_Backup/admin/partials
9
+ */
10
+
11
+ defined( 'WPINC' ) ? : die;
12
+
13
+ return sprintf('
14
+ <div id="backup_now_content" style="display:none;">
15
+ <h2>%1$s</h2>
16
+
17
+ <p>%2$s</p>
18
+
19
+ %3$s
20
+
21
+ %4$s
22
+
23
+ <div style="height:70px;"></div>
24
+
25
+ <div class="plugin-card-bottom">
26
+ %5$s
27
+ </div>
28
+ </div>',
29
+ /* 1 */ __( 'Backup Site Now', 'boldgrid-backup' ),
30
+ /* 2 */ __( 'The <strong>Files and Folders</strong> and <strong>Database</strong> settings below customize which parts of your site to backup.', 'boldgrid-backup' ),
31
+ /* 3 */ include BOLDGRID_BACKUP_PATH . '/admin/partials/settings/folders.php',
32
+ /* 4 */ include BOLDGRID_BACKUP_PATH . '/admin/partials/settings/db.php',
33
+ /* 5 */ include BOLDGRID_BACKUP_PATH . '/admin/partials/boldgrid-backup-admin-backup-button.php'
34
+ );
35
+
36
+
admin/partials/boldgrid-backup-admin-backup.php ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Provide a admin area view for the plugin
4
+ *
5
+ * This file is used to markup the admin-facing aspects of the plugin.
6
+ *
7
+ * @link http://www.boldgrid.com
8
+ * @since 1.0
9
+ *
10
+ * @package Boldgrid_Backup
11
+ * @subpackage Boldgrid_Backup/admin/partials
12
+ */
13
+
14
+ defined( 'WPINC' ) ? : die;
15
+
16
+ $is_restore = ! empty( $_POST['restore_now'] ) && '1' === $_POST['restore_now'];
17
+ $is_success = ! empty( $archive_info ) && empty( $archive_info['error'] );
18
+ $redirect_url = admin_url( 'admin.php?page=boldgrid-backup' );
19
+
20
+ /*
21
+ * Avoid backwards compatibility issues when restoring.
22
+ *
23
+ * For example, let's say you're running "BoldGrid Backup 1.5" and you're
24
+ * trying to restore a "BoldGrid Backup 1.6" archive. The restoration request
25
+ * is being handled by 1.5, who's $core is a bit different than the 1.6 $core.
26
+ * Elements in this file are calling upon $core, but we don't know if $core
27
+ * exists or what characteristics it has.
28
+ *
29
+ * This problem occurs because both 1.5 and 1.6 will include this file after
30
+ * a restoration completes. The version of this file will be loaded from the
31
+ * archive that was just restored.
32
+ *
33
+ * @since 1.6.0
34
+ */
35
+ if ( $is_restore && $is_success ) {
36
+
37
+ /*
38
+ * After restoration, redirect user to the backups page.
39
+ *
40
+ * In Backup 1.6, we introduced the "Archive Details" page. The user may
41
+ * very well be restoring from this page. However, if we restored a backup
42
+ * from an earlier version, that "Archive Details" page may not exists, and
43
+ * the user will get an error.
44
+ *
45
+ * Prior to Backup 1.6, restorations were made by forms submitted via post.
46
+ * In Backup 1.6, restorations are made via ajax.
47
+ *
48
+ * If we're not doing ajax, then the request came from Backup 1.5, and the
49
+ * site is being restored within the new pageload. When the page finally loads,
50
+ * you'll have a newer version of Backup, but you'll have to refresh the page
51
+ * to see it. We'll take care of the refresh for the user.
52
+ */
53
+ if ( ! wp_doing_ajax() ) {
54
+ printf( '<script type="text/javascript">window.location.href = "%1$s";</script>', $redirect_url );
55
+ return;
56
+ }
57
+ }
58
+
59
+ $core = isset( $this->core ) ? $this->core : $this;
60
+
61
+ $core->archive->init( $archive_info['filepath'] );
62
+
63
+ /**
64
+ * If data exists in the $archive_info array, then print results, else show an error message.
65
+ *
66
+ * @param array $archive_info {
67
+ * @type string compressor The code-name for the compressor used to create the archive.
68
+ * @type string $filepath The absolute file path.
69
+ * @type int $filesize The archive file size.
70
+ * @type int $total_size The total size of the uncompressed files.
71
+ * @type string $error A friendly error message.
72
+ * @type int $error_code An integer from a compressor constant.
73
+ * @type string $error_message A human-readable interpretation or the error code.
74
+ * }
75
+ */
76
+
77
+ // Create a link to the settings page.
78
+ $url = admin_url( 'admin.php?page=boldgrid-backup-settings' );
79
+ $settings_page_link = sprintf(
80
+ wp_kses(
81
+ __( 'See <a href="%s">Settings for BoldGrid Backup</a> for details.', 'boldgrid-backup' ),
82
+ array( 'a' => array( 'href' => array() ) )
83
+ ),
84
+ esc_url( $url )
85
+ );
86
+
87
+ if ( ! empty( $archive_info ) ) {
88
+
89
+ if ( ! empty( $archive_info['dryrun'] ) ) {
90
+ $message = array(
91
+ 'class' => 'notice notice-info is-dismissible',
92
+ 'message' => sprintf( '<p>%1$s</p>', esc_html__( 'This was a dry run test', 'boldgrid-backup' ) ),
93
+ );
94
+ }
95
+
96
+ /*
97
+ * Get our success message.
98
+ *
99
+ * Initially, we had one sprintf intelligent enough to determine if we were
100
+ * making a backup or restoring, and make us a nice message. However, having
101
+ * such a fantastic beast in one sprintf made it a bit difficult to manage.
102
+ * We've since split up the sprintf's into two, one for when a backup has
103
+ * completed empty( $_POST['restore_now'] ), and one when a restoration has
104
+ * been completed.
105
+ *
106
+ * @todo Read the above. For the time, we did a quick clean up. However, we
107
+ * may want to further organize this entire file.
108
+ */
109
+ if ( $is_success ) {
110
+ $message = array(
111
+ 'class' => 'notice notice-success is-dismissible boldgrid-backup-complete',
112
+ 'message' => sprintf( '
113
+ <h2 class="header-notice">%1$s - %2$s</h2>
114
+ <p>%3$s <a href="%4$s">%5$s</a></p>
115
+ ',
116
+ /* 1 */ __( 'BoldGrid Backup', 'boldgrid-backup' ),
117
+ /* 2 */ __( 'Backup complete', 'boldgrid-backup' ),
118
+ /* 3 */ esc_html__( 'A backup archive file has been created successfully!', 'boldgrid-backup' ),
119
+ /* 4 */ $core->archive->view_details_url,
120
+ /* 5 */ __( 'View details', 'boldgrid-backup' )
121
+ ),
122
+ );
123
+ } else {
124
+ $message = array(
125
+ 'class' => 'notice notice-error is-dismissible',
126
+ 'message' => esc_html( $archive_info['error'] ),
127
+ 'header' => sprintf(
128
+ '%1$s - %2$s',
129
+ __( 'BoldGrid Backup', 'boldgrid-backup' ),
130
+ empty( $_POST['restore_now'] ) ? __( 'Error creating archive', 'boldgrid-backup' ) : __( 'Error restoring archive', 'boldgrid-backup' )
131
+ ),
132
+ );
133
+ }
134
+ } else {
135
+ $message = array(
136
+ 'class' => 'notice notice-error is-dismissible',
137
+ 'message' => sprintf('
138
+ <p>%1$s</p>
139
+ %2$s
140
+ %3$s
141
+ %4$s %5$s',
142
+ /* 1 */ $is_restore ? esc_html__( 'There was an error restoring the selected backup archive file', 'boldgrid-backup' ) : esc_html__( 'There was an error creating a backup archive file', 'boldgrid-backup' ),
143
+ /* 2 */ empty( $archive_info['filepath'] ) ? '' : '<p>' . sprintf( esc_html__( 'File Path: %s', 'boldgrid-backup' ), $archive_info['filepath'] ) . '</p>',
144
+ /* 3 */ empty( $archive_info['error'] ) ? '' : '<p>' . $archive_info['error'] . '</p>',
145
+ /* 4 */ ! isset( $archive_info['error_message'] ) ? '' : '<p>' . sprintf( __( 'Error Details: %s', 'boldgrid-backup' ), $archive_info['error_message'] ),
146
+ /* 5 */ isset( $archive_info['error_message'] ) && isset( $archive_info['error_code'] ) ? ' (' . $archive_info['error_code'] . ')' : ''
147
+ ),
148
+ );
149
+ }
150
+
151
+ if ( ! isset( $message ) ) {
152
+ $message = array(
153
+ 'class' => 'notice notice-error is-dismissible',
154
+ 'message' => __( 'Unknown error.', 'boldgrid-backup' ),
155
+ 'header' => __( 'BoldGrid Backup', 'boldgrid-backup' ),
156
+ );
157
+ }
158
+
159
+ $message['header'] = isset( $message['header'] ) ? $message['header'] : null;
160
+
161
+ return $message;
admin/partials/boldgrid-backup-admin-home.php ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Provide a admin area view for the plugin
4
+ *
5
+ * This file is used to markup the admin-facing aspects of the plugin.
6
+ *
7
+ * @link http://www.boldgrid.com
8
+ * @since 1.0
9
+ *
10
+ * @package Boldgrid_Backup
11
+ * @subpackage Boldgrid_Backup/admin/partials
12
+ */
13
+
14
+ defined( 'WPINC' ) ? : die;
15
+
16
+ /*
17
+ * Variables passed by scope.
18
+ *
19
+ * @param int $archives_count The archive file count.
20
+ * @param int $archives_size The total size of all archive files.
21
+ * @param array $archives {
22
+ * A numbered array of arrays containing the following indexes.
23
+ * @type string $filepath Archive file path.
24
+ * @type string $filename Archive filename.
25
+ * @type string $filedate Localized file modification date.
26
+ * @type int $filesize The archive file size in bytes.
27
+ * @type int $lastmodunix The archive file modification time in unix seconds.
28
+ * }
29
+ * @param string $backup_identifier The backup identifier for this installation.
30
+ * @param string $table Markup for a table of current backups. If no
31
+ * backups, it's a p tag with appropriate message.
32
+ */
33
+
34
+ $nav = include BOLDGRID_BACKUP_PATH . '/admin/partials/boldgrid-backup-admin-nav.php';
35
+
36
+ // Premium advertisement on the bottom of the archives page.
37
+ $ad = $this->config->is_premium_done ? '' : sprintf( '
38
+ <div class="bg-box-bottom premium wp-clearfix">
39
+ %1$s
40
+ %2$s
41
+ </div>',
42
+ $this->go_pro->get_premium_button(),
43
+ $this->lang['want_to']
44
+ );
45
+
46
+ // Backup now modal.
47
+ $in_modal = true;
48
+ $modal = include BOLDGRID_BACKUP_PATH . '/admin/partials/boldgrid-backup-admin-backup-modal.php';
49
+ $in_modal = false;
50
+
51
+ ?>
52
+ <div class='wrap'>
53
+
54
+ <h1 class="wp-heading-inline"><?php esc_html_e( 'Backup Archives', 'boldgrid-backup' ); ?></h1>
55
+
56
+ <a href="#TB_inline?width=800&amp;height=600&amp;inlineId=backup_now_content" class="thickbox page-title-action page-title-action-primary"><?php echo __( 'Backup Site Now', 'boldgrid-backup' ); ?></a>
57
+
58
+ <a class="page-title-action add-new"><?php echo __( 'Upload Backup', 'boldgrid-backup' ); ?></a>
59
+
60
+ <?php
61
+ echo $nav;
62
+
63
+ include BOLDGRID_BACKUP_PATH . '/admin/partials/archives/add-new.php';
64
+
65
+ echo $table;
66
+
67
+ echo $modal;
68
+
69
+ include BOLDGRID_BACKUP_PATH . '/admin/partials/archives/note-pre-backup.php';
70
+
71
+ echo $ad;
72
+ ?>
73
+
74
+ </div>
admin/partials/boldgrid-backup-admin-mail-restore.php ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Mail template for restoration notifications.
4
+ *
5
+ * @since 1.2.2
6
+ *
7
+ * @package Boldgrid_Backup
8
+ * @subpackage Boldgrid_Backup/admin/partials
9
+ */
10
+
11
+ defined( 'WPINC' ) ? : die;
12
+
13
+ /**
14
+ * This template uses inherited variables.
15
+ *
16
+ * @see Boldgrid_Backup_Admin_Utility::create_site_id()
17
+ *
18
+ * @param bool $dryrun Whether or not is a dry run.
19
+ * @param bool $restore_ok Success of the restoration.
20
+ * @param string $info['filepath'] The file path restored.
21
+ */
22
+ // Create a site identifier.
23
+ $site_id = Boldgrid_Backup_Admin_Utility::create_site_id();
24
+
25
+ // Create subject.
26
+ $subject = sprintf(
27
+ esc_html__( 'Restoration completed for %s', 'boldgrid-backup' ),
28
+ $site_id
29
+ );
30
+
31
+ // Create message.
32
+ $body = esc_html__( 'Hello', 'boldgrid-backup' ) . ",\n\n";
33
+
34
+ if ( $dryrun ) {
35
+ $body .= esc_html__( 'THIS OPERATION WAS A DRY-RUN TEST', 'boldgrid-backup' ) . ".\n\n";
36
+ }
37
+
38
+ if ( $restore_ok ) {
39
+ $body .= esc_html__( 'A backup archive has been restored', 'boldgrid-backup' );
40
+ } else {
41
+ $body .= esc_html__(
42
+ 'An error occurred when attempting to restore a backup archive',
43
+ 'boldgrid-backup'
44
+ );
45
+ }
46
+
47
+ $body .= sprintf(
48
+ __( ' for %s', 'boldgrid-backup' ),
49
+ $site_id
50
+ ) . ".\n\n";
51
+
52
+ $body .= esc_html__( 'Restoration details', 'boldgrid-backup' ) . ":\n";
53
+
54
+ $body .= sprintf(
55
+ esc_html__( 'Archive file path: %s', 'boldgrid-backup' ),
56
+ $info['filepath']
57
+ ) . "\n";
58
+
59
+ $body .= sprintf(
60
+ esc_html__( 'Archive file size: %s', 'boldgrid-backup' ),
61
+ Boldgrid_Backup_Admin_Utility::bytes_to_human( $info['filesize'] )
62
+ ) . "\n";
63
+
64
+ if ( defined( 'DOING_CRON' ) ) {
65
+ $body .= esc_html__(
66
+ 'The restoration request was made via CRON (task scheduler)',
67
+ 'boldgrid-backup'
68
+ ) . ".\n\n";
69
+ }
70
+
71
+ $body .= esc_html__(
72
+ 'You can manage notifications in your WordPress admin panel, under BoldGrid Backup Settings',
73
+ 'boldgrid-backup'
74
+ ) . ".\n\n";
75
+
76
+ $body .= esc_html__( 'Best regards', 'boldgrid-backup' ) . ",\n\n";
77
+
78
+ $body .= esc_html__( 'The BoldGrid Backup plugin', 'boldgrid-backup' ) . "\n\n";
admin/partials/boldgrid-backup-admin-nav.php ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file contains the navbar for all BoldGrid Backup pages.
4
+ *
5
+ * @since 1.5.1
6
+ *
7
+ * @package Boldgrid_Backup
8
+ * @subpackage Boldgrid_Backup/admin/partials
9
+ */
10
+
11
+ defined( 'WPINC' ) ? : die;
12
+
13
+ $active = 'nav-tab-active';
14
+
15
+ $navs = array(
16
+ array(
17
+ 'title' => __( 'Backups', 'boldgrid-backup' ),
18
+ 'href' => 'admin.php?page=boldgrid-backup',
19
+ 'class' => ! empty( $_GET['page'] ) && 'boldgrid-backup' === $_GET['page'] ? $active : '',
20
+ ),
21
+ array(
22
+ 'title' => __( 'Settings', 'boldgrid-backup' ),
23
+ 'href' => 'admin.php?page=boldgrid-backup-settings',
24
+ 'class' => ! empty( $_GET['page'] ) && 'boldgrid-backup-settings' === $_GET['page'] ? $active : '',
25
+ ),
26
+ array(
27
+ 'title' => __( 'Preflight Check', 'boldgrid-backup' ),
28
+ 'href' => 'admin.php?page=boldgrid-backup-test',
29
+ 'class' => ! empty( $_GET['page'] ) && 'boldgrid-backup-test' === $_GET['page'] ? $active : '',
30
+ ),
31
+ array(
32
+ 'title' => __( 'Tools', 'boldgrid-backup' ),
33
+ 'href' => 'admin.php?page=boldgrid-backup-tools',
34
+ 'class' => ! empty( $_GET['page'] ) && 'boldgrid-backup-tools' === $_GET['page'] ? $active : '',
35
+ ),
36
+ );
37
+
38
+ /**
39
+ * Allow the update of our nav menu items.
40
+ *
41
+ * @since 1.5.3
42
+ *
43
+ * @param array $navs
44
+ */
45
+ $navs = apply_filters( 'boldgrid_backup_navs', $navs );
46
+
47
+ $markup = '<h2 class="nav-tab-wrapper">';
48
+ foreach ( $navs as $nav ) {
49
+ $markup .= sprintf(
50
+ '<a class="nav-tab %1$s" href="%2$s">%3$s</a>',
51
+ $nav['class'],
52
+ $nav['href'],
53
+ $nav['title']
54
+ );
55
+ }
56
+ $markup .= '</h2>';
57
+
58
+ return $markup;
59
+
60
+
admin/partials/boldgrid-backup-admin-settings.php ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Provide a admin area view for the plugin
4
+ *
5
+ * This file is used to markup the admin-facing aspects of the plugin.
6
+ *
7
+ * @link http://www.boldgrid.com
8
+ * @since 1.0
9
+ *
10
+ * @package Boldgrid_Backup
11
+ * @subpackage Boldgrid_Backup/admin/partials
12
+ */
13
+
14
+ defined( 'WPINC' ) ? : die;
15
+
16
+ $nav = include BOLDGRID_BACKUP_PATH . '/admin/partials/boldgrid-backup-admin-nav.php';
17
+ $scheduler = include BOLDGRID_BACKUP_PATH . '/admin/partials/settings/scheduler.php';
18
+ $folders_include = include BOLDGRID_BACKUP_PATH . '/admin/partials/settings/folders.php';
19
+ $db = include BOLDGRID_BACKUP_PATH . '/admin/partials/settings/db.php';
20
+ $retention = include BOLDGRID_BACKUP_PATH . '/admin/partials/settings/retention.php';
21
+ $auto_updates = include BOLDGRID_BACKUP_PATH . '/admin/partials/settings/auto-updates.php';
22
+ $notifications = include BOLDGRID_BACKUP_PATH . '/admin/partials/settings/notifications.php';
23
+ $backup_directory = include BOLDGRID_BACKUP_PATH . '/admin/partials/settings/backup-directory.php';
24
+ $connect_key = include BOLDGRID_BACKUP_PATH . '/admin/partials/settings/connect-key.php';
25
+
26
+ $days_of_week = '';
27
+ $time_of_day = '';
28
+ $storage = '';
29
+ if ( $this->core->scheduler->is_available( 'cron' ) || $this->core->scheduler->is_available( 'wp-cron' ) ) {
30
+ $days_of_week = include BOLDGRID_BACKUP_PATH . '/admin/partials/settings/days-of-week.php';
31
+ $time_of_day = include BOLDGRID_BACKUP_PATH . '/admin/partials/settings/time-of-day.php';
32
+ $storage = include BOLDGRID_BACKUP_PATH . '/admin/partials/settings/storage.php';
33
+ }
34
+
35
+ $sections = array(
36
+ 'sections' => array(
37
+ array(
38
+ 'id' => 'section_schedule',
39
+ 'title' => __( 'Backup Schedule', 'boldgrid-backup' ),
40
+ 'content' => $scheduler . $days_of_week . $time_of_day . $storage . $folders_include . $db,
41
+ ),
42
+ array(
43
+ 'id' => 'connect_key',
44
+ 'title' => __( 'BoldGrid Connect Key', 'boldgrid-bacup' ),
45
+ 'content' => $connect_key,
46
+ ),
47
+ array(
48
+ 'id' => 'section_retention',
49
+ 'title' => __( 'Retention', 'boldgrid-backup' ),
50
+ 'content' => $retention,
51
+ ),
52
+ array(
53
+ 'id' => 'section_updates',
54
+ 'title' => __( 'Auto Updates & Rollback', 'boldgrid-backup' ),
55
+ 'content' => $auto_updates,
56
+ ),
57
+ array(
58
+ 'id' => 'section_notifications',
59
+ 'title' => __( 'Notifications', 'boldgrid-backup' ),
60
+ 'content' => $notifications,
61
+ ),
62
+ array(
63
+ 'id' => 'section_directory',
64
+ 'title' => __( 'Backup Directory', 'boldgrid-backup' ),
65
+ 'content' => $backup_directory,
66
+ ),
67
+ ),
68
+ 'post_col_right' => sprintf( '
69
+ <div id="boldgrid-settings-submit-div">
70
+ <p>
71
+ <input id="boldgrid-settings-submit" class="button button-primary" type="submit" name="submit" value="%1$s" />
72
+ </p>
73
+ </div>',
74
+ __( 'Save Changes', 'boldgrid-backup' )
75
+ ),
76
+ );
77
+
78
+ /**
79
+ * Allow other plugins to modify the sections of the settings page.
80
+ *
81
+ * @since 1.5.4
82
+ *
83
+ * @param array $sections
84
+ */
85
+ $sections = apply_filters( 'boldgrid_backup_settings_sections', $sections );
86
+
87
+ /**
88
+ * Render the $sections into displayable markup.
89
+ *
90
+ * @since 1.5.4
91
+ *
92
+ * @param array $sections
93
+ */
94
+ $col_container = apply_filters( 'Boldgrid\Library\Ui\render_col_container', $sections );
95
+ if ( is_array( $col_container ) ) {
96
+ $col_container = $this->core->lang['icon_warning'] . ' ' . __( 'Unable to display settings page. Unknown BoldGrid Library error.', 'boldgrid-backup' );
97
+ }
98
+
99
+ // Check if settings are available, show an error notice if not.
100
+ if ( empty( $settings ) ) {
101
+ add_action( 'admin_footer',
102
+ array(
103
+ $this,
104
+ 'notice_settings_retrieval',
105
+ )
106
+ );
107
+ }
108
+
109
+ wp_nonce_field( 'boldgrid_backup_settings' );
110
+
111
+ ?>
112
+ <div class='wrap'>
113
+ <h1><?php esc_html_e( 'BoldGrid Backup and Restore Settings', 'boldgrid-backup' ); ?></h1>
114
+
115
+ <?php
116
+ echo $nav;
117
+
118
+ /*
119
+ * Print this text:
120
+ *
121
+ * The BoldGrid Backup and Restore system allows you to upgrade your themes and plugins without
122
+ * being afraid it will do something you cannot easily undo. We perform a Preflight Check to see
123
+ * if the needed support is available on your web hosting account.
124
+ */
125
+ $url = admin_url( 'admin.php?page=boldgrid-backup-test' );
126
+ $link = sprintf(
127
+ wp_kses(
128
+ __( 'The BoldGrid Backup and Restore system allows you to upgrade your themes and plugins without being afraid it will do something you cannot easily undo. We perform a <a href="%s">Preflight Check</a> to see if the needed support is available on your web hosting account.', 'boldgrid-backup' ),
129
+ array( 'a' => array( 'href' => array() ) )
130
+ ),
131
+ esc_url( $url )
132
+ );
133
+ echo '<p>' . $link . '</p>';
134
+ ?>
135
+
136
+ <hr />
137
+
138
+ <form id='schedule-form' method='post'>
139
+ <?php
140
+ echo $col_container;
141
+ wp_nonce_field( 'boldgrid-backup-settings', 'settings_auth' );
142
+ printf( '<input type="hidden" name="save_time" value="%1$s" />', time() );
143
+ ?>
144
+ </form>
145
+
146
+ </div>
admin/partials/boldgrid-backup-admin-test.php ADDED
@@ -0,0 +1,377 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Provide a admin area view for the plugin functionality test report
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.0
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin/partials
10
+ */
11
+
12
+ defined( 'WPINC' ) ? : die;
13
+
14
+ // Setup our lang.
15
+ $lang = array(
16
+ 'yes' => __( 'Yes', 'boldgrid-backup' ),
17
+ 'no' => __( 'No', 'boldgrid-backup' ),
18
+ 'untested' => __( 'untested', 'boldgrid-backup' ),
19
+ 'PASS' => __( 'PASS', 'boldgrid-backup' ),
20
+ 'FAIL' => __( 'FAIL', 'boldgrid-backup' ),
21
+ 'not_set' => __( 'not set', 'boldgrid-backup' ),
22
+ 'before_test_compress' => __( 'Before any compressors are tested, please be sure your backup directory is created and has proper permissions set.', 'boldgrid-backup' ),
23
+ 'ensure_dir_perms' => __( 'Please be sure that your backup directory exists. If it does, also ensure it has read, write, and modify permissions.', 'boldgrid-backup' ),
24
+ );
25
+
26
+ $lang['dir_of_dir'] = $lang['ensure_dir_perms'];
27
+ if ( $this->test->is_windows() ) {
28
+ $lang['dir_of_dir'] = __( 'Please review directory permissions. If you are on a Windows server, your user may need to be able to read BOTH the backup directory and its parent directory.', 'boldgrid-backup' );
29
+ }
30
+
31
+ $error_span = '<span class="error">%1$s</span><br />%2$s';
32
+ $warning_span = '<span class="warning">%1$s</span><br />%2$s';
33
+ $success_span = '<span class="success">%1$s</span>';
34
+
35
+ $allowed_tags = array(
36
+ 'span' => array(
37
+ 'class' => array(
38
+ 'error'
39
+ ),
40
+ ),
41
+ 'br' => array(),
42
+ 'pre' => array(),
43
+ );
44
+
45
+ $backup_dir_perms = $this->test->extensive_dir_test( $backup_directory );
46
+
47
+ $php_zip = new Boldgrid_Backup_Admin_Compressor_Php_Zip( $this );
48
+
49
+ $pcl_zip = new Boldgrid_Backup_Admin_Compressor_Pcl_Zip( $this );
50
+
51
+ $valid_backup_dir = $backup_dir_perms['exists'] && $backup_dir_perms['read'] && $backup_dir_perms['write'] && $backup_dir_perms['rename'] && $backup_dir_perms['delete'] && $backup_dir_perms['dirlist'];
52
+
53
+ $timezone = $this->time->get_server_timezone();
54
+ $timezone = false === $timezone ? 'UTC' . $this->time->get_server_offset() : $timezone->getName() . ' / ' . $this->time->get_server_offset();
55
+
56
+ // Run our tests.
57
+ $tests = array(
58
+ array(
59
+ 'id' => 'pass',
60
+ 'k' => __( 'Functionality test status:', 'boldgrid-backup' ),
61
+ 'v' => ( $this->test->run_functionality_tests() ? sprintf( $success_span, $lang['PASS'] ) : sprintf( $error_span, $lang['FAIL'], '' ) ),
62
+ ),
63
+ array(
64
+ 'heading' => __( 'General tests', 'boldgrid-backup' ),
65
+ ),
66
+ array(
67
+ 'k' => __( 'User home directory:', 'boldgrid-backup' ),
68
+ 'v' => $home_dir . ' (' . $home_dir_mode . ')',
69
+ ),
70
+ array(
71
+ 'k' => __( 'User home directory writable?', 'boldgrid-backup' ),
72
+ 'v' => ( $home_dir_writable ? $lang['yes'] : $lang['no'] ),
73
+ ),
74
+ array(
75
+ 'k' => __( 'WordPress directory:', 'boldgrid-backup' ),
76
+ 'v' => ABSPATH,
77
+ ),
78
+ array(
79
+ 'k' => __( 'WordPress directory writable?', 'boldgrid-backup' ),
80
+ 'v' => ( $this->test->get_is_abspath_writable() ? $lang['yes'] : sprintf( $error_span, $lang['no'], '' ) ),
81
+ ),
82
+ array(
83
+ 'k' => __( 'Document root:', 'boldgrid-backup' ),
84
+ 'v' => str_replace( '\\\\', '\\', $_SERVER['DOCUMENT_ROOT'] ),
85
+ ),
86
+ array(
87
+ 'k' => __( 'Current user:', 'boldgrid-backup' ),
88
+ 'v' => get_current_user(),
89
+ ),
90
+ array(
91
+ 'k' => __( 'PHP in safe mode?', 'boldgrid-backup' ),
92
+ 'v' => ( $this->test->is_php_safemode() ? sprintf( $error_span, $lang['no'] ) : $lang['yes'] ),
93
+ ),
94
+ array(
95
+ 'k' => __( 'WordPress version:', 'boldgrid-backup' ),
96
+ 'v' => $wp_version,
97
+ ),
98
+ array(
99
+ 'k' => __( 'Server time zone:', 'boldgrid-backup' ),
100
+ 'v' => $timezone,
101
+ ),
102
+ );
103
+
104
+ $tests[] = array(
105
+ 'heading' => __( 'Backup directory & permissions:', 'boldgrid-backup' ),
106
+ );
107
+
108
+ $tests[] = array(
109
+ 'k' => __( 'Possible backup directory parents:', 'boldgrid-backup' ),
110
+ 'v' => implode( '<br />', $possible_backup_dirs ),
111
+ );
112
+
113
+ $tests[] = array(
114
+ 'k' => __( 'Backup directory:', 'boldgrid-backup' ),
115
+ 'v' => ! empty( $backup_directory ) ? $backup_directory : sprintf( $error_span, $lang['not_set'], '' ),
116
+ );
117
+
118
+ // As set of tests only to run if a backup directory is found.
119
+ if ( ! empty( $backup_directory ) ) {
120
+ $tests[] = array(
121
+ 'k' => __( 'Backup directory without ABSPATH:', 'boldgrid-backup' ),
122
+ 'v' => $this->backup_dir->without_abspath,
123
+ );
124
+
125
+ $tests[] = array(
126
+ 'k' => __( 'Backup directory exists?', 'boldgrid-backup' ),
127
+ 'v' => $backup_dir_perms['exists'] ? $lang['yes'] : sprintf( $error_span, $lang['no'], $lang['ensure_dir_perms'] ),
128
+ );
129
+
130
+ if ( $backup_dir_perms['exists'] ) {
131
+ $tests[] = array(
132
+ 'k' => __( 'Backup directory has read permission?', 'boldgrid-backup' ),
133
+ 'v' => $backup_dir_perms['read'] ? $lang['yes'] : sprintf( $error_span, $lang['no'], $lang['ensure_dir_perms'] ),
134
+ );
135
+
136
+ $tests[] = array(
137
+ 'k' => __( 'Directory listing of backup directory can be fetched?', 'boldgrid-backup' ),
138
+ 'v' => $backup_dir_perms['dirlist'] ? $lang['yes'] : sprintf( $error_span, $lang['no'], $lang['dir_of_dir'] ),
139
+ );
140
+
141
+ $tests[] = array(
142
+ 'k' => __( 'Backup directory has write permission?', 'boldgrid-backup' ),
143
+ 'v' => $backup_dir_perms['write'] ? $lang['yes'] : sprintf( $error_span, $lang['no'], $lang['ensure_dir_perms'] ),
144
+ );
145
+
146
+ $tests[] = array(
147
+ 'k' => __( 'Backup directory has modify permission?', 'boldgrid-backup' ),
148
+ 'v' => $backup_dir_perms['rename'] ? $lang['yes'] : sprintf( $error_span, $lang['no'], $lang['ensure_dir_perms'] ),
149
+ );
150
+
151
+ $tests[] = array(
152
+ 'k' => __( 'Backup directory has delete permission?', 'boldgrid-backup' ),
153
+ 'v' => $backup_dir_perms['delete'] ? $lang['yes'] : sprintf( $error_span, $lang['no'], $lang['ensure_dir_perms'] ),
154
+ );
155
+ }
156
+ }
157
+
158
+ $tests[] = array(
159
+ 'heading' => 'Available compressors',
160
+ );
161
+
162
+ $tests[] = array(
163
+ 'k' => __( 'PHP ZipArchive available?', 'boldgrid-backup' ),
164
+ 'v' => ( $this->config->is_compressor_available( 'php_zip' ) ? 'Yes' : 'No' ),
165
+ );
166
+
167
+ if ( 'php_zip' === $this->compressors->get() ) {
168
+ if ( ! $valid_backup_dir ) {
169
+ $status = sprintf( $warning_span, $lang['untested'], $lang['before_test_compress'] );
170
+ } elseif ( $php_zip->test() ) {
171
+ $status = $lang['yes'];
172
+ } else {
173
+ $status = sprintf( $error_span, $lang['no'], '' );
174
+ }
175
+
176
+ $tests[] = array(
177
+ 'k' => __( 'PHP ZipArchive test passed?', 'boldgrid-backup' ),
178
+ 'v' => $status,
179
+ );
180
+ }
181
+
182
+ $tests[] = array(
183
+ 'k' => __( 'PclZip available?', 'boldgrid-backup' ),
184
+ 'v' => ( $this->config->is_compressor_available( 'pcl_zip' ) ? 'Yes' : 'No' ),
185
+ );
186
+
187
+ if ( 'pcl_zip' === $this->compressors->get() ) {
188
+ if ( ! $valid_backup_dir ) {
189
+ $status = sprintf( $warning_span, $lang['untested'], $lang['before_test_compress'] );
190
+ } elseif ( $pcl_zip->test() ) {
191
+ $status = $lang['yes'];
192
+ } else {
193
+ $status = sprintf( $error_span, $lang['no'], '' );
194
+ }
195
+
196
+ $tests[] = array(
197
+ 'k' => __( 'PclZip test passed?', 'boldgrid-backup' ),
198
+ 'v' => $status,
199
+ );
200
+ }
201
+
202
+ $tests[] = array(
203
+ 'k' => __( 'PHP Bzip2 available?', 'boldgrid-backup' ),
204
+ 'v' => ( $this->config->is_compressor_available( 'php_bz2' ) ? 'Yes' : 'No' ),
205
+ );
206
+
207
+ $tests[] = array(
208
+ 'k' => __( 'PHP Zlib available?', 'boldgrid-backup' ),
209
+ 'v' => ( $this->config->is_compressor_available( 'php_zlib' ) ? 'Yes' : 'No' ),
210
+ );
211
+
212
+ $tests[] = array(
213
+ 'k' => __( 'PHP LZF available?', 'boldgrid-backup' ),
214
+ 'v' => ( $this->config->is_compressor_available( 'php_lzf' ) ? 'Yes' : 'No' ),
215
+ );
216
+
217
+ $tests[] = array(
218
+ 'k' => __( 'System TAR available?', 'boldgrid-backup' ),
219
+ 'v' => ( $this->config->is_compressor_available( 'system_tar' ) ? 'Yes' : 'No' ),
220
+ );
221
+
222
+ $tests[] = array(
223
+ 'k' => __( 'System ZIP available?', 'boldgrid-backup' ),
224
+ 'v' => ( $this->config->is_compressor_available( 'system_zip' ) ? 'Yes' : 'No' ),
225
+ );
226
+
227
+ $tests[] = array(
228
+ 'heading' => __( 'Cron', 'boldgrid-backup' ),
229
+ );
230
+
231
+ $tests[] = array(
232
+ 'k' => __( 'System crontab available?', 'boldgrid-backup' ),
233
+ 'v' => ( $this->test->is_crontab_available() ? $lang['yes'] : sprintf( $warning_span, $lang['no'], '' ) ),
234
+ );
235
+
236
+ $tests[] = array(
237
+ 'k' => __( 'PHP allow_url_fopen enabled?', 'boldgrid-backup' ),
238
+ 'v' => ( ini_get( 'allow_url_fopen' ) ? $lang['yes'] : sprintf( $error_span, $lang['no'], '' ) ),
239
+ );
240
+
241
+ $tests[] = array(
242
+ 'k' => __( 'Cron jobs:', 'boldgrid-backup' ),
243
+ 'v' => '<pre>' . implode( '<br /><br />', $our_crons ) . '</pre>',
244
+ );
245
+
246
+ $tests[] = array(
247
+ 'k' => __( 'WP Cron enabled?', 'boldgrid-backup' ),
248
+ 'v' => ( $this->test->wp_cron_enabled() ? 'Yes' : 'No' ),
249
+ );
250
+
251
+ $tests[] = array(
252
+ 'k' => __( 'WP Cron jobs:', 'boldgrid-backup' ),
253
+ 'v' => '<pre>' . implode( '<br /><br />', $our_wp_crons ) . '</pre>',
254
+ );
255
+
256
+ // Run only these tests if the server is compatible.
257
+ if ( $is_functional ) {
258
+ $tests[] = array(
259
+ 'heading' => __( 'Disk space', 'boldgrid-backup' ),
260
+ );
261
+
262
+ $tests[] = array(
263
+ 'k' => __( 'Directory used to calculate disk space:', __( 'boldgrid-backup' ) ),
264
+ 'v' => $this->home_dir->get_for_disk(),
265
+ );
266
+
267
+ $tests[] = array(
268
+ 'k' => __( 'Disk total space:', 'boldgrid-backup' ),
269
+ 'v' => Boldgrid_Backup_Admin_Utility::bytes_to_human( $disk_space[0] ),
270
+ );
271
+
272
+ $tests[] = array(
273
+ 'k' => __( 'Disk used space:', 'boldgrid-backup' ),
274
+ 'v' => Boldgrid_Backup_Admin_Utility::bytes_to_human( $disk_space[1] ),
275
+ );
276
+
277
+ $tests[] = array(
278
+ 'k' => __( 'Disk free space:', 'boldgrid-backup' ),
279
+ 'v' => Boldgrid_Backup_Admin_Utility::bytes_to_human( $disk_space[2] ),
280
+ );
281
+
282
+ if ( ! empty( $disk_space[3] ) ) {
283
+ $tests[] = array(
284
+ 'k' => __( 'WordPress directory size:', 'boldgrid-backup' ),
285
+ 'v' => Boldgrid_Backup_Admin_Utility::bytes_to_human( $disk_space[3] ),
286
+ );
287
+ }
288
+
289
+ // Calculate possible disk free space after a backup, using the entire WP directory size.
290
+ $disk_free_post = $disk_space[2] - $disk_space[3] - $db_size;
291
+
292
+ if ( $disk_free_post > 0 ) {
293
+ $tests[] = array(
294
+ 'k' => __( 'Estimated free space after backup:', 'boldgrid-backup' ),
295
+ 'v' => Boldgrid_Backup_Admin_Utility::bytes_to_human( $disk_free_post ),
296
+ );
297
+ } else {
298
+ $tests[] = array(
299
+ 'v' => __( 'THERE IS NOT ENOUGH SPACE TO PERFORM A BACKUP!', 'boldgrid-backup' ),
300
+ );
301
+ }
302
+
303
+ $tests[] = array(
304
+ 'heading' => __( 'Database', 'boldgrid-backup' ),
305
+ );
306
+
307
+ $tests[] = array(
308
+ 'k' => __( 'Database size:', 'boldgrid-backup' ),
309
+ 'v' => Boldgrid_Backup_Admin_Utility::bytes_to_human( $db_size ),
310
+ );
311
+
312
+ $tests[] = array(
313
+ 'k' => __( 'WordPress database charset:', 'boldgrid-backup' ),
314
+ 'v' => $db_charset,
315
+ );
316
+
317
+ if ( ! empty( $db_collate ) ) {
318
+ $tests[] = array(
319
+ 'k' => __( 'WordPress database collate:', 'boldgrid-backup' ),
320
+ 'v' => $db_collate,
321
+ );
322
+ }
323
+ }
324
+
325
+ // If server is not compatible, create fail message.
326
+ if ( $is_functional ) {
327
+ $fail_tips = '';
328
+ } else {
329
+ $fail_tips = sprintf('
330
+ <p>
331
+ %1$s<br />
332
+ <a href="%3$s" target="_blank" />%2$s</a>
333
+ </p>',
334
+ esc_html__( 'BoldGrid Backup is not compatible with your hosting account. For further help please see:', 'boldgrid-backup' ),
335
+ esc_html__( 'Making your web hosting account compatible with BoldGrid Backup', 'boldgrid-backup' ),
336
+ esc_url( $this->configs['urls']['compatibility'] )
337
+ );
338
+ }
339
+
340
+ // Create the table that will contain the tests data.
341
+ $table = '<table class="wp-list-table fixed striped">';
342
+ foreach ( $tests as $test ) {
343
+ if ( ! empty( $test['heading'] ) ) {
344
+ $table .= sprintf( '<tr class="heading"><td colspan="2"><h2>%1$s</h2></td></tr>', esc_html( $test['heading'] ) );
345
+ } elseif ( isset( $test['id'] ) && 'pass' === $test['id'] ) {
346
+ $table .= sprintf('<tr><td>%1$s</td><td><strong>%2$s</strong></td></tr>',
347
+ esc_html( $test['k'] ),
348
+ wp_kses( $test['v'], $allowed_tags )
349
+ );
350
+ } elseif ( isset( $test['k'] ) ) {
351
+ $table .= sprintf('<tr><td>%1$s</td><td><em>%2$s</em></td></tr>',
352
+ esc_html( $test['k'] ),
353
+ wp_kses( $test['v'], $allowed_tags )
354
+ );
355
+ } else {
356
+ $table .= sprintf('<tr><td colspan="2">%1$s</td></tr>',
357
+ esc_html( $test['v'] )
358
+ );
359
+ }
360
+ }
361
+ $table .= '</table>';
362
+
363
+ ?>
364
+ <div class="functionality-test-section wrap">
365
+
366
+ <?php
367
+ printf( '<h1>%1$s</h1>', __( 'BoldGrid Backup Preflight Check', 'boldgrid-backup' ) );
368
+
369
+ $nav = include BOLDGRID_BACKUP_PATH . '/admin/partials/boldgrid-backup-admin-nav.php';
370
+ echo $nav;
371
+
372
+ echo $fail_tips;
373
+
374
+ echo $table;
375
+ ?>
376
+
377
+ </div>
admin/partials/boldgrid-backup-admin-tools.php ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Tools.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.4
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin/partials
10
+ */
11
+
12
+ defined( 'WPINC' ) ? : die;
13
+
14
+ $nav = include BOLDGRID_BACKUP_PATH . '/admin/partials/boldgrid-backup-admin-nav.php';
15
+
16
+ $sections = array(
17
+ 'sections' => array(
18
+ array(
19
+ 'id' => 'section_locations',
20
+ 'title' => __( 'Local & Remote', 'boldgrid-backup' ),
21
+ 'content' => include BOLDGRID_BACKUP_PATH . '/admin/partials/tools/local-remote.php',
22
+ ),
23
+ ),
24
+ );
25
+
26
+ /**
27
+ * Allow other plugins to modify the sections of the tools page.
28
+ *
29
+ * @since 1.5.4
30
+ *
31
+ * @param array $sections
32
+ */
33
+ $sections = apply_filters( 'boldgrid_backup_tools_sections', $sections );
34
+
35
+ /**
36
+ * Render the $sections into displayable markup.
37
+ *
38
+ * @since 1.5.4
39
+ *
40
+ * @param array $sections
41
+ */
42
+ $col_container = apply_filters( 'Boldgrid\Library\Ui\render_col_container', $sections );
43
+
44
+ ?>
45
+
46
+ <div class='wrap'>
47
+ <h1><?php esc_html_e( 'BoldGrid Backup and Restore Settings', 'boldgrid-backup' ); ?></h1>
48
+
49
+ <?php
50
+ echo $nav;
51
+ echo $col_container;
52
+ ?>
53
+ </div>
admin/partials/remote/ftp.php ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Backup - FTP Settings page.
4
+ *
5
+ * The file handles the rendering of the settings page.
6
+ */
7
+
8
+ $selected = 'selected="selected"';
9
+ $ftp_selected = 'ftp' === $data['type'] ? $selected : '';
10
+ $sftp_selected = 'sftp' === $data['type'] ? $selected : '';
11
+
12
+ ?>
13
+
14
+ <form method="post">
15
+
16
+ <h1><?php echo __( 'BoldGrid Backup - FTP Settings', 'boldgrid-backup' )?></h1>
17
+
18
+ <hr />
19
+
20
+ <table class="widefat fixed striped">
21
+ <tr>
22
+ <td>
23
+ <?php
24
+ printf( '
25
+ <label for="host">%1$s</label>
26
+ <input type="text" name="host" value="%2$s" minlength="5" title="%3$s" required />
27
+ ',
28
+ __( 'FTP Host', 'boldgrid-backup' ),
29
+ $data['host'],
30
+ __( 'FTP host should be in the format of: example.com', 'boldgrid-backup' )
31
+ );
32
+ ?>
33
+ </td>
34
+ <td></td>
35
+ </tr>
36
+ <tr>
37
+ <td>
38
+ <?php echo __( 'FTP / SFTP', 'boldgrid-backup' ); ?><br />
39
+ <select name="type">
40
+ <option value='ftp' <?php echo esc_attr( $ftp_selected ); ?> >FTP</option>
41
+ <option value='sftp' <?php echo esc_attr( $sftp_selected ); ?> >SFTP</option>
42
+ </select>
43
+ </td>
44
+ <td>
45
+ <?php echo __( 'FTP Port', 'boldgrid-backup' ); ?><br />
46
+ <input type="number" name="port" value="<?php echo $data['port']; ?>" min="1" required />
47
+ </td>
48
+ </tr>
49
+ <tr>
50
+ <td>
51
+ <?php echo __( 'FTP Username', 'boldgrid-backup' ); ?><br />
52
+ <input type="text" name="user" value="<?php echo $data['user']; ?>" required />
53
+ </td>
54
+ <td>
55
+ <?php echo __( 'FTP Password', 'boldgrid-backup' ); ?><br />
56
+ <input type="password" name="pass" value="<?php echo $data['pass']; ?>" required />
57
+ </td>
58
+ </tr>
59
+ <tr>
60
+ <td>
61
+ <?php echo __( 'Retention (Number of backup archives to retain)', 'boldgrid-backup' ) ?><br />
62
+ <input type="number" name="retention_count" value="<?php echo $data['retention_count']; ?>" min="1" required/>
63
+ </td>
64
+ <td></td>
65
+ </tr>
66
+ <tr>
67
+ <td>
68
+ <?php echo __( 'Nickname (If you would like to refer to this account as something other than FTP)', 'boldgrid-backup' ) ?><br />
69
+ <input type="text" name="nickname" value="<?php echo esc_attr( $data['nickname'] ); ?>" />
70
+ </td>
71
+ <td></td>
72
+ </tr>
73
+ </table>
74
+
75
+ <p>
76
+ <?php
77
+ echo $this->core->lang['icon_warning'] . __( 'With automated FTP your credentials must be stored here in your WordPress. They will be encrypted in the database and this protects them significantly, but they could be decrypted in the unlikely event of a compromise. We recommended you use a separate FTP user and password specifically for backups.', 'boldgrid-backup' );
78
+ ?>
79
+ </p>
80
+
81
+ <p>
82
+ <input type="hidden" name="action" value="save" />
83
+ <input class="button button-primary" type="submit" value="<?php echo __( 'Save changes', 'boldgrid-backup' ); ?>" />
84
+ <button class="button button-secondary"><?php echo __( 'Delete settings', 'boldgrid-backup' ); ?></button>
85
+ <span class="spinner inline middle hidden"></span>
86
+ </p>
87
+
88
+ </form>
admin/partials/settings/auto-updates.php ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Show "Auto Updates" on settings page.
4
+ *
5
+ * @since 1.5.4
6
+ */
7
+
8
+ defined( 'WPINC' ) ? : die;
9
+
10
+ ob_start();
11
+ ?>
12
+
13
+ <div class="bg-box">
14
+ <div class="bg-box-top">
15
+ <?php echo __( 'Auto Updates and Rollback', 'boldgrid-inspirations' ); ?>
16
+ </div>
17
+ <div class="bg-box-bottom">
18
+
19
+ <table class="form-table">
20
+ <tr>
21
+ <th>
22
+ <?php esc_html_e( 'Plugin Auto-Updates', 'boldgrid-backup' ); ?>
23
+ <span class="dashicons dashicons-editor-help" data-id="plugin-autoupdate"></span>
24
+
25
+ <p class="help" data-id="plugin-autoupdate">
26
+ <?php
27
+ printf(
28
+ __( 'Automatically perform all plugin updates when available. Enabling this feature adds the <a target="_blank" href="%1$s">auto_update_plugin filter</a>, which enables automatic plugin updates when an update is available.', 'boldgrid-backup' ),
29
+ 'https://codex.wordpress.org/Configuring_Automatic_Background_Updates#Plugin_.26_Theme_Updates_via_Filter'
30
+ );
31
+ ?>
32
+ </p>
33
+ </th>
34
+ <td>
35
+ <input id="plugin-autoupdate-enabled" type="radio" name="plugin_autoupdate" value="1"
36
+ <?php
37
+ if ( isset( $settings['plugin_autoupdate'] ) &&
38
+ 1 === $settings['plugin_autoupdate'] ) {
39
+ ?> checked<?php
40
+ }
41
+ ?> /> <?php esc_html_e( 'Enabled', 'boldgrid-backup' ); ?> &nbsp; <input
42
+ id="plugin-autoupdate-disabled" type="radio" name="plugin_autoupdate" value="0"
43
+ <?php
44
+ if ( ! isset( $settings['plugin_autoupdate'] ) ||
45
+ ! $settings['plugin_autoupdate'] ) {
46
+ ?> checked<?php
47
+ }
48
+ ?> /> <?php esc_html_e( 'Disabled', 'boldgrid-backup' ); ?>
49
+ </td>
50
+ </tr>
51
+
52
+ <tr>
53
+ <th>
54
+ <?php esc_html_e( 'Theme Auto-Updates', 'boldgrid-backup' ); ?>
55
+ <span class="dashicons dashicons-editor-help" data-id="theme-autoupdate"></span>
56
+
57
+ <p class="help" data-id="theme-autoupdate">
58
+ <?php
59
+ printf(
60
+ __( 'Automatically perform all theme updates when available. Enabling this feature adds the <a target="_blank" href="%1$s">auto_update_theme filter</a>, which enables automatic theme updates when an update is available.', 'boldgrid-backup' ),
61
+ 'https://codex.wordpress.org/Configuring_Automatic_Background_Updates#Plugin_.26_Theme_Updates_via_Filter'
62
+ );
63
+ ?>
64
+ <p>
65
+ </th>
66
+ <td>
67
+ <input id="theme-autoupdate-enabled" type="radio" name="theme_autoupdate" value="1"
68
+ <?php
69
+ if ( isset( $settings['theme_autoupdate'] ) &&
70
+ 1 === $settings['theme_autoupdate'] ) {
71
+ ?> checked<?php
72
+ }
73
+ ?> /> <?php esc_html_e( 'Enabled', 'boldgrid-backup' ); ?> &nbsp; <input
74
+ id="theme-autoupdate-disabled" type="radio" name="theme_autoupdate" value="0"
75
+ <?php
76
+ if ( ! isset( $settings['theme_autoupdate'] ) ||
77
+ ! $settings['theme_autoupdate'] ) {
78
+ ?> checked<?php
79
+ }
80
+ ?> /> <?php esc_html_e( 'Disabled', 'boldgrid-backup' ); ?>
81
+ </td>
82
+ </tr>
83
+
84
+ <tr>
85
+ <th>
86
+ <?php echo __( 'Auto Backup<br />Before Updates', 'boldgrid-backup' ); ?>
87
+ <span class='dashicons dashicons-editor-help' data-id='auto-backup'></span>
88
+
89
+ <p class='help' data-id='auto-backup'>
90
+ <?php
91
+ printf(
92
+ __( 'Automatically perform a backup before WordPress updates. When this feature is enabled, a full backup will be made during the <a target="_blank" href="%1$s">pre_auto_update action</a>. ', 'boldgrid-backup' ),
93
+ 'https://developer.wordpress.org/reference/hooks/pre_auto_update/'
94
+ );
95
+ ?>
96
+ <p>
97
+ </th>
98
+ <td>
99
+ <input id='auto-backup-enabled' type='radio' name='auto_backup'
100
+ value='1'
101
+ <?php
102
+ if ( ! isset( $settings['auto_backup'] ) ||
103
+ 1 === $settings['auto_backup'] ) {
104
+ echo ' checked';
105
+ }
106
+ ?> /> <?php esc_html_e( 'Enabled', 'boldgrid-backup' ); ?> &nbsp; <input
107
+ id='auto-backup-disabled' type='radio' name='auto_backup' value='0'
108
+ <?php
109
+ if ( isset( $settings['auto_backup'] ) && 0 === $settings['auto_backup'] ) {
110
+ echo ' checked';
111
+ }
112
+ ?> /> <?php esc_html_e( 'Disabled', 'boldgrid-backup' ); ?>
113
+ </td>
114
+ </tr>
115
+
116
+ <tr>
117
+ <th>
118
+ <?php esc_html_e( 'Auto Rollback', 'boldgrid-backup' ); ?><span class='dashicons dashicons-editor-help' data-id='auto-rollback'></span>
119
+
120
+ <p class='help' data-id='auto-rollback'>
121
+ <?php
122
+ esc_html_e(
123
+ 'If something goes wrong while performing WordPress updates, automatically restore the site using a backup made before updating WordPress. This feature does not apply to auto updates.',
124
+ 'boldgrid-backup'
125
+ );
126
+ ?>
127
+ </p>
128
+ </th>
129
+ <td>
130
+ <input id='auto-rollback-enabled' type='radio' name='auto_rollback'
131
+ value='1'
132
+ <?php
133
+ if ( ! isset( $settings['auto_rollback'] ) ||
134
+ 1 === $settings['auto_rollback'] ) {
135
+ echo ' checked';
136
+ }
137
+ ?> /> <?php esc_html_e( 'Enabled', 'boldgrid-backup' ); ?> &nbsp; <input
138
+ id='auto-rollback-disabled' type='radio' name='auto_rollback'
139
+ value='0'
140
+ <?php
141
+ if ( isset( $settings['auto_rollback'] ) && 0 === $settings['auto_rollback'] ) {
142
+ echo ' checked';
143
+ }
144
+ ?> /> <?php esc_html_e( 'Disabled', 'boldgrid-backup' ); ?>
145
+ </td>
146
+ </tr>
147
+ </table>
148
+ </div>
149
+ </div>
150
+
151
+ <?php
152
+ $output = ob_get_contents();
153
+ ob_end_clean();
154
+ return $output;
155
+ ?>
admin/partials/settings/backup-directory.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Show "Backup Directory" on settings page.
4
+ *
5
+ * @since 1.3.1
6
+ */
7
+
8
+ defined( 'WPINC' ) ? : die;
9
+
10
+ ob_start();
11
+ ?>
12
+
13
+ <div class="bg-box">
14
+ <div class="bg-box-top">
15
+ <?php esc_html_e( 'Backup Directory', 'boldgrid-backup' ); ?>
16
+ <span class='dashicons dashicons-editor-help' data-id='backup-dir'></span>
17
+ </div>
18
+ <div class="bg-box-bottom">
19
+
20
+ <p class="help" data-id="backup-dir">
21
+ <?php
22
+ /*
23
+ * Print this text:
24
+ *
25
+ * For security purposes, please do not set this to a publicly available directory. Once you set
26
+ * this, it is not recommended that you change it again. You can find more help with setting
27
+ * your backup directory <a>here</a>.
28
+ */
29
+ $link = sprintf(
30
+ wp_kses(
31
+ __( 'For security purposes, please do not set this to a publicly available directory. Once you set this, it is not recommended that you change it again. You can find more help with setting your backup directory <a href="%s" target="_blank">here</a>.', 'boldgrid-backup' ),
32
+ array( 'a' => array( 'href' => array(), 'target' => array() ) )
33
+ ),
34
+ esc_url( $this->core->configs['urls']['setting_directory'] )
35
+ );
36
+ echo $link;
37
+ ?>
38
+ </p>
39
+
40
+ <table class='backup-directory form-table'>
41
+ <tr>
42
+ <th><?php esc_html_e( 'Directory to store backup archives', 'boldgrid-backup' ); ?>:</th>
43
+ <td><input id='backup-directory-path' type='text' size='40' name='backup_directory' value='<?php echo $settings['backup_directory']; ?>'></td>
44
+ </tr>
45
+ <tr id="move-backups" class="hidden">
46
+ <th><?php esc_html_e( 'If you change this directory, current backups will not show in the list. Would you like us to move the backups to the new directory?', 'boldgrid-backup' ); ?></th>
47
+ <td><input type='checkbox' name='move-backups' checked /></td>
48
+ </tr>
49
+ </table>
50
+ </div>
51
+ </div>
52
+
53
+ <?php
54
+ $output = ob_get_contents();
55
+ ob_end_clean();
56
+ return $output;
57
+ ?>
admin/partials/settings/compressor.php ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Compressor on settings page.
4
+ *
5
+ * This page is only shown for testers who have appropriate hooks in place.
6
+ *
7
+ * @since 1.5.1
8
+ *
9
+ * @package Boldgrid_Backup
10
+ * @subpackage Boldgrid_Backup/admin/partials/settings
11
+ */
12
+
13
+ defined( 'WPINC' ) ? : die;
14
+
15
+ ob_start();
16
+
17
+ $selected = 'selected="selected"';
18
+
19
+ $available_compressors = array( 'php_zip', 'pcl_zip' );
20
+
21
+ // Settings for compressor.
22
+ $pcl_zip_selected = empty( $settings['compressor'] ) || 'pcl_zip' === $settings['compressor'] ? $selected : '';
23
+ $php_zip_selected = ! empty( $settings['compressor'] ) && 'php_zip' === $settings['compressor'] ? $selected : '';
24
+ $php_zip_option = ! in_array( 'php_zip', $available_compressors ) ? '' : sprintf( '<option value="php_zip" %1$s>ZipArchive</option>', $php_zip_selected );
25
+
26
+ // Settings for extractor.
27
+ $extractor_pcl_zip_selected = ! empty( $settings['extractor'] ) && 'pcl_zip' === $settings['extractor'] ? $selected : '';
28
+ $extractor_php_zip_selected = empty( $settings['extractor'] ) || 'php_zip' === $settings['extractor'] ? $selected : '';
29
+ $extractor_php_zip_option = ! in_array( 'php_zip', $available_compressors ) ? '' : sprintf( '<option value="php_zip" %1$s>ZipArchive</option>', $extractor_php_zip_selected );
30
+
31
+ ?>
32
+ <div class="bg-box">
33
+ <div class="bg-box-top">
34
+ <?php esc_html_e( 'Compressor & Extractor', 'boldgrid-backup' ); ?>
35
+ <span class='dashicons dashicons-editor-help' data-id='compressor'></span>
36
+ </div>
37
+ <div class="bg-box-bottom">
38
+
39
+ <p class="help" data-id="compressor">
40
+ <?php
41
+ echo __( 'These are advanced settings. You do not need to configure this setting.', 'boldgrid-backup' );
42
+ ?>
43
+ </p>
44
+
45
+ <table class="form-table">
46
+ <tr>
47
+ <th><?php echo __( 'Compressor', 'boldgrid-backup' );?>:</th>
48
+ <td>
49
+ <select name="compressor">
50
+ <option value='pcl_zip' <?php echo $pcl_zip_selected; ?> >PclZip</option>
51
+ <?php echo $php_zip_option; ?>
52
+ </select>
53
+ </td>
54
+ </tr>
55
+ <tr>
56
+ <th><?php echo __( 'Extractor', 'boldgrid-backup' ) ?>:</th>
57
+ <td>
58
+ <select name="extractor">
59
+ <option value='pcl_zip' <?php echo $extractor_pcl_zip_selected; ?> >PclZip</option>
60
+ <?php echo $extractor_php_zip_option; ?>
61
+ </select>
62
+ </td>
63
+ </tr>
64
+ </table>
65
+ </div>
66
+ </div>
67
+
68
+ <?php
69
+ $output = ob_get_contents();
70
+ ob_end_clean();
71
+ return $output;
72
+ ?>
admin/partials/settings/connect-key.php ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File: connect-key.php
4
+ *
5
+ * Show Connect Key status.
6
+ *
7
+ * @since 1.5.4
8
+ */
9
+
10
+ defined( 'WPINC' ) ? : die;
11
+
12
+ $is_dismissed = apply_filters( 'Boldgrid\Library\Notice\KeyPrompt\getIsDismissed', false );
13
+ $is_displayed = apply_filters( 'Boldgrid\Library\Notice\KeyPrompt\getIsDisplayed', false );
14
+
15
+ $has_key_entered = ! empty( $this->core->configs['api_key'] );
16
+
17
+ // Check again button to refresh license status.
18
+ $refresh_key = ! $has_key_entered || $this->core->config->get_is_premium() ? '' : '<p>' .
19
+ __( 'If you recently upgraded your BoldGrid Connect Key to Premium, click <strong>Check again</strong> to refresh the status of your license.', 'boldgrid-backup' ) .
20
+ '<br />' .
21
+ sprintf( '<a class="button" id="license_check_again">%1$s</a>', __( 'Check again', 'boldgrid-backup' ) ) .
22
+ ' <strong>' . __( 'License type', 'boldgrid-backup' ) . '</strong>: <span id="license_string">' . $this->core->config->get_license_string() . '</span>' .
23
+ ' <span class="spinner inline" style="display:none;vertical-align:text-bottom;"></span>' .
24
+ '</p>' .
25
+ '<p id="license_reload_page" class="hidden">' .
26
+ $this->core->lang['icon_warning'] .
27
+ __( 'Please reload this page for your new license status to take affect.', 'boldgrid-bakcup' ) .
28
+ '</p>';
29
+
30
+ ob_start();
31
+ if ( $has_key_entered ) {
32
+ printf(
33
+ __( 'You have entered a <strong>%1$s</strong> BoldGrid Connect Key.', 'boldgrid-backup' ),
34
+ $this->core->config->get_is_premium() ? __( 'Premium', 'boldgrid-backup' ) : __( 'Free', 'boldgrid-backup' )
35
+ );
36
+ } elseif ( $is_dismissed ) {
37
+ _e(
38
+ 'You have dismissed the prompt to enter a BoldGrid Connect Key. Click <a class="undismissBoldgridNotice" href="#">here</a> to restore the prompt.',
39
+ 'boldgrid-backup'
40
+ );
41
+
42
+ wp_nonce_field( 'boldgrid_set_key', 'set_key_auth' );
43
+ } else {
44
+ _e( 'Please enter your BoldGrid Connect Key in the form at the top of this page.', 'boldgrid-backup' );
45
+ }
46
+ $output = ob_get_contents();
47
+ ob_end_clean();
48
+
49
+ // Add a "Get Premium" section under the Connect Key.
50
+ $bottom_box_premium = '';
51
+ if ( ! $this->core->config->get_is_premium() && ! $is_displayed ) {
52
+ $bottom_box_premium = '<div class="bg-box-bottom premium">' .
53
+ $this->core->go_pro->get_premium_button() .
54
+ $this->core->lang['want_to'] .
55
+ '</div>';
56
+ }
57
+
58
+ return '
59
+ <div class="bg-box">
60
+ <div class="bg-box-top">
61
+ ' . __( 'BoldGrid Connect Key', 'boldgrid-backup' ) . '
62
+ </div>
63
+ <div class="bg-box-bottom">
64
+ ' . $output . $refresh_key . '
65
+ </div>
66
+ ' . $bottom_box_premium . '
67
+ </div>';
68
+
admin/partials/settings/days-of-week.php ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Days of the week.
4
+ *
5
+ * @since 1.5.4
6
+ */
7
+
8
+ defined( 'WPINC' ) ? : die;
9
+
10
+ ob_start();
11
+ ?>
12
+
13
+ <div class="bg-box schedule-dow">
14
+ <div class="bg-box-top">
15
+ <?php esc_html_e( 'Days of the Week', 'boldgrid-backup' ); ?>
16
+ </div>
17
+ <div class="bg-box-bottom">
18
+ <input id='dow-sunday' type='checkbox' name='dow_sunday' value='1' <?php if ( ! empty( $settings['schedule']['dow_sunday'] ) ) { echo ' checked'; } ?> /><?php esc_html_e( 'Sunday', 'boldgrid-backup' ); ?><br />
19
+ <input id='dow-monday' type='checkbox' name='dow_monday' value='1' <?php if ( ! empty( $settings['schedule']['dow_monday'] ) ) { echo ' checked'; } ?> /><?php esc_html_e( 'Monday', 'boldgrid-backup' ); ?><br />
20
+ <input id='dow-tuesday' type='checkbox' name='dow_tuesday' value='1' <?php if ( ! empty( $settings['schedule']['dow_tuesday'] ) ) { echo ' checked'; } ?> /><?php esc_html_e( 'Tuesday', 'boldgrid-backup' ); ?><br />
21
+ <input id='dow-wednesday' type='checkbox' name='dow_wednesday' value='1' <?php if ( ! empty( $settings['schedule']['dow_wednesday'] ) ) { echo ' checked'; } ?> /><?php esc_html_e( 'Wednesday', 'boldgrid-backup' ); ?><br />
22
+ <input id='dow-thursday' type='checkbox' name='dow_thursday' value='1' <?php if ( ! empty( $settings['schedule']['dow_thursday'] ) ) { echo ' checked'; } ?> /><?php esc_html_e( 'Thursday', 'boldgrid-backup' ); ?><br />
23
+ <input id='dow-friday' type='checkbox' name='dow_friday' value='1' <?php if ( ! empty( $settings['schedule']['dow_friday'] ) ) { echo ' checked'; } ?> /><?php esc_html_e( 'Friday', 'boldgrid-backup' ); ?><br />
24
+ <input id='dow-saturday' type='checkbox' name='dow_saturday' value='1' <?php if ( ! empty( $settings['schedule']['dow_saturday'] ) ) { echo ' checked'; } ?> /><?php esc_html_e( 'Saturday', 'boldgrid-backup' ); ?>
25
+
26
+ <br /><br />
27
+
28
+ <div class='hidden' id='no-backup-days'>
29
+ <p><span class="dashicons dashicons-warning yellow"></span> <?php
30
+ esc_html_e( 'Backup will not occur if no days are selected.', 'boldgrid-backup' );
31
+ ?></p>
32
+ </div>
33
+
34
+ <?php
35
+ $url = $this->core->configs['urls']['resource_usage'];
36
+ $link = sprintf(
37
+ wp_kses(
38
+ __( 'Backups use resources and <a href="%s" target="_blank">must pause your site</a> momentarily. Use sparingly.', 'boldgrid-backup' ),
39
+ array( 'a' => array( 'href' => array(), 'target' => array() ) )
40
+ ),
41
+ esc_url( $url )
42
+ );
43
+ printf( '<div id="use-sparingly" class="hidden"><p><span class="dashicons dashicons-warning yellow"></span> %s</p></div>', $link );
44
+ ?>
45
+ </div>
46
+ </div>
47
+
48
+ <?php
49
+ $output = ob_get_contents();
50
+ ob_end_clean();
51
+ return $output;
52
+ ?>
admin/partials/settings/db.php ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Database settings.
4
+ *
5
+ * @since 1.5.3
6
+ */
7
+
8
+ defined( 'WPINC' ) ? : die;
9
+
10
+ // $core will vary based on how this file is included.
11
+ $core = isset( $this->core ) ? $this->core : $this;
12
+
13
+ $in_modal = isset( $in_modal ) && true === $in_modal;
14
+
15
+ // If we are in a modal, by default we want all tables to be backed up.
16
+ $tables = $core->db_omit->format_prefixed_tables();
17
+
18
+ // Determine if the "Backup all tables" option should be checked.
19
+ $all_included = empty( $settings['exclude_tables'] );
20
+ $type = $core->db_omit->get_settings_type();
21
+ $full_checked = 'full' === $type || $all_included;
22
+
23
+ $checked = 'checked="checked"';
24
+
25
+ $types = sprintf( '
26
+ <input type="radio" name="table_inclusion_type" value="full" %3$s>%1$s<br>
27
+ <input type="radio" name="table_inclusion_type" value="custom" %4$s>%2$s
28
+ ',
29
+ /* 1 */ __( 'Backup all tables (full backup)', 'boldgrid-backup' ),
30
+ /* 2 */ __( 'Custom Backup', 'boldgrid-backup' ),
31
+ /* 3 */ $in_modal || $full_checked ? $checked : '',
32
+ /* 4 */ $in_modal || $full_checked ? '' : $checked
33
+ );
34
+
35
+ $buttons = sprintf( '
36
+ <button id="include_all_tables" class="button button-primary">%1$s</button>
37
+ <button id="exclude_all_tables" class="button button">%2$s</button>
38
+ ',
39
+ /* 1 */ esc_html__( 'Include all', 'boldgrid-backup' ),
40
+ /* 2 */ esc_html__( 'Exclude all', 'boldgrid-backup' )
41
+ );
42
+
43
+ $status = sprintf( '
44
+ <p class="yes-default">
45
+ %1$s
46
+ </p>
47
+ <p class="no-default">
48
+ %2$s %3$s
49
+ </p>
50
+ ',
51
+ /* 1 */ $core->lang['icon_success'] . wp_kses( __( 'All database tables will be backed up.', 'boldgrid-backup' ), $tags ),
52
+ /* 2 */ $core->lang['icon_warning'] . wp_kses( __( 'You are not backing up all database tables. Backups created may not contain all of the needed tables to restore your website in the event of an emergency.', 'boldgrid-backup' ), $tags ),
53
+ /* 3 */ sprintf( '<a href="#" class="include-all">%1$s</a>', __( 'Backup all tables', 'boldgrid-backup' ) )
54
+ );
55
+
56
+ return sprintf( '
57
+ <div class="bg-box" id="table_inclusion">
58
+ <div class="bg-box-top">
59
+ %1$s
60
+ </div>
61
+ <div class="bg-box-bottom wp-clearfix">
62
+ <p>%2$s</p>
63
+
64
+ <div id="table_inclusion_config">
65
+ %3$s
66
+
67
+ <p>%4$s</p>
68
+
69
+ <div class="include-tables">
70
+ %5$s
71
+ </div>
72
+ </div>
73
+ </div>
74
+ </div>
75
+ ',
76
+ /* 1 */ esc_html__( 'Database', 'boldgrid-backup' ),
77
+ /* 2 */ $types,
78
+ /* 3 */ $status,
79
+ /* 4 */ $buttons,
80
+ /* 5 */ $tables
81
+ );
82
+
83
+
admin/partials/settings/folders.php ADDED
@@ -0,0 +1,262 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Folder settings.
4
+ *
5
+ * Which files and folders should be included / excluded?
6
+ *
7
+ * @since 1.5.4
8
+ */
9
+
10
+ defined( 'WPINC' ) ? : die;
11
+
12
+ $nonce = wp_create_nonce( 'folder_exclusion_preview' );
13
+
14
+ $tags = array(
15
+ 'strong' => array(),
16
+ 'a' => array(
17
+ 'href' => array(),
18
+ 'id' => array(),
19
+ ),
20
+ );
21
+
22
+ // $core will vary based on how this file is included.
23
+ $core = isset( $this->core ) ? $this->core : $this;
24
+
25
+ $checked = 'checked="checked"';
26
+
27
+ $in_modal = isset( $in_modal ) && true === $in_modal;
28
+
29
+ $using_defaults = $core->folder_exclusion->is_using_defaults();
30
+
31
+ $markup = '<table class="form-table bulk-action-notice" id="folder_exclusion"><tbody>';
32
+
33
+ // TR for the header and intro
34
+ $tr_header = sprintf( '
35
+ <p>
36
+ <input type="radio" name="folder_exclusion_type" value="full" %3$s>%1$s<br>
37
+ <input type="radio" name="folder_exclusion_type" value="custom" %4$s>%2$s
38
+ </p>',
39
+ /* 1 */ esc_html__( 'Backup all files (full backup)', 'boldgrid-backup' ),
40
+ /* 2 */ esc_html__( 'Custom Backup', 'boldgrid-backup' ),
41
+ /* 3 */ $in_modal || $using_defaults ? $checked : '',
42
+ /* 4 */ ! $in_modal && ! $using_defaults ? $checked : ''
43
+ );
44
+
45
+ // This markup for the legend.
46
+ // TR for the help text.
47
+ $table_legend = sprintf( '
48
+ <table class="folder_exclude_help wp-list-table widefat fixed striped pages">
49
+ <tr>
50
+ <th>*</th>
51
+ <td>
52
+ %1$s<br />
53
+ <span class="example">%2$s</span>
54
+ </td>
55
+ </tr>
56
+ <tr>
57
+ <th>WPCORE</th>
58
+ <td>
59
+ %3$s<br />
60
+ <span class="example">%4$s</span>
61
+ </td>
62
+ </tr>
63
+ <tr>
64
+ <th>/</th>
65
+ <td>
66
+ %5$s<br />
67
+ <span class="example">%6$s</span>
68
+ </td>
69
+ </tr>
70
+ <tr>
71
+ <th>,</th>
72
+ <td>
73
+ %7$s<br />
74
+ <span class="example">%8$s</span>
75
+ </td>
76
+ </tr>
77
+ </table>
78
+ ',
79
+ /* 1 */ wp_kses( __( 'Use an asterisk(*) as a wildcard.', 'boldgrid-inspirations' ), $tags ),
80
+ /* 2 */ wp_kses( __( 'For example, <strong>wp-*</strong> will match <strong>wp-</strong>content, <strong>wp-</strong>admin, and <strong>wp-</strong>includes.', 'boldgrid-inspirations' ), $tags ),
81
+ /* 3 */ wp_kses( __( 'Use WPCORE to specify WordPress core files.', 'boldgrid-bacukp' ), $tags ),
82
+ /* 4 */ wp_kses( __( 'This includes the wp-admin and wp-includes folders, as well as all of the core WordPress files in the root directory of WordPress.', 'boldgrid-backup' ), $tags ),
83
+ /* 5 */ wp_kses( __( 'Begin a filter with a forward slash(/) to specify the file or folder must be in the root WordPress directory.', 'boldgrid-backup' ), $tags ),
84
+ /* 6 */ wp_kses( __( 'For example, <strong>/index.php</strong> will not match index.php files in subdirectories, such as wp-includes/index.php', 'boldgrid-backup' ), $tags ),
85
+ /* 7 */ wp_kses( __( 'Use a comma to add more than one filter.', 'boldgrid-backup' ), $tags ),
86
+ /* 8 */ wp_kses( __( 'For example, <strong>wp-admin,wp-includes</strong> will backup both the wp-admin folder and the wp-includes folder.', 'boldgrid-backup' ), $tags )
87
+ );
88
+
89
+ // Examples
90
+ $table_examples = sprintf( '
91
+ <table class="folder_exclude_help wp-list-table widefat fixed striped pages">
92
+ <tr>
93
+ <th><a href="#" class="folder_exclude_sample" data-include="%1$s" data-exclude="%2$s">%3$s</a></th>
94
+ <td>%4$s</td>
95
+ </tr>
96
+ <tr>
97
+ <th><a href="#" class="folder_exclude_sample" data-include="/wp-content" data-exclude="">%5$s</a></th>
98
+ <td>%6$s</td>
99
+ </tr>
100
+ <tr>
101
+ <th><a href="#" class="folder_exclude_sample" data-include="/wp-content/plugins,/wp-content/themes" data-exclude="">%7$s</a></th>
102
+ <td>%8$s</td>
103
+ </tr>
104
+ <tr>
105
+ <th><a href="#" class="folder_exclude_sample" data-include="*" data-exclude="WPCORE">%9$s</a></th>
106
+ <td>%10$s</td>
107
+ </tr>
108
+ </table>
109
+ ',
110
+ /* 1 */ $core->folder_exclusion->default_include,
111
+ /* 2 */ $core->folder_exclusion->default_exclude,
112
+ /* 3 */ wp_kses( __( 'Default', 'boldgrid-backup' ), $tags ),
113
+ /* 4 */ wp_kses( __( 'These are the <strong>default</strong> settings. Include all core WordPress files and the wp-content folder (which includes your plugins, themes, and uploads).', 'boldgrid-backup' ), $tags ),
114
+ /* 5 */ wp_kses( __( 'Example 1', 'boldgrid-backup' ), $tags ),
115
+ /* 6 */ wp_kses( __( 'Backup only the wp-content folder.', 'boldgrid-backup' ), $tags ),
116
+ /* 7 */ wp_kses( __( 'Example 2', 'boldgrid-backup' ), $tags ),
117
+ /* 8 */ wp_kses( __( 'Backup only the plugins and themes folders.', 'boldgrid-backup' ), $tags ),
118
+ /* 9 */ wp_kses( __( 'Example 3', 'boldgrid-backup' ), $tags ),
119
+ /* 10 */ wp_kses( __( 'Backup everything except WordPress core files.', 'boldgrid-backup' ), $tags )
120
+ );
121
+
122
+ $status = sprintf( '
123
+ <p class="yes-default">
124
+ %1$s
125
+ </p>
126
+ <p class="no-default">
127
+ %2$s %3$s
128
+ </p>
129
+ ',
130
+ /* 1 */ $core->lang['icon_success'] . wp_kses( __( 'You are using the the default <strong>Include</strong> and <strong>Exclude</strong> settings. Backups created will contain all of the needed files to restore your website in the event of an emergency.', 'boldgrid-backup' ), $tags ),
131
+ /* 2 */ $core->lang['icon_warning'] . wp_kses( __( 'You are not using the the default <strong>Include</strong> and <strong>Exclude</strong> settings. Backups created may not contain all of the needed files to restore your website in the event of an emergency.', 'boldgrid-backup' ), $tags ),
132
+ /* 3 */ sprintf(
133
+ '<a href="#" class="folder_exclude_sample" data-include="%1$s" data-exclude="%2$s">%3$s</a>',
134
+ /* 1A */ $core->folder_exclusion->default_include,
135
+ /* 2A */ $core->folder_exclusion->default_exclude,
136
+ /* 3A */ wp_kses( __( 'Use default settings', 'boldgrid-backup' ), $tags )
137
+ )
138
+ );
139
+
140
+ // TR for the help text.
141
+ $tr_help = sprintf( '
142
+ <div id="folder_misc_info">
143
+ <p>
144
+ <span class="dashicons dashicons-editor-help" data-id="folder_exclude_inputs" style="float:left;margin-right:4px;"></span>
145
+ %2$s
146
+ </p>
147
+
148
+ <div class="help" data-id="folder_exclude_inputs" style="padding:20px 0px 20px 20px;margin:15px 0px 0px 0px;">
149
+ %3$s
150
+ %4$s
151
+
152
+ <p>&nbsp;</p>
153
+ <p>&nbsp;</p>
154
+
155
+ %5$s
156
+ %6$s
157
+ </div>
158
+
159
+ <hr class="separator-small" />
160
+
161
+ %7$s
162
+ </div>
163
+ ',
164
+ /* 1 */ $using_defaults ? 'hidden' : '',
165
+ /* 2 */ wp_kses( __( 'Use the <strong>Include</strong> and <strong>Exclude</strong> settings to adjust which files are included in your backup. Click the <strong>Preview</strong> button to see which files will be included in your backup based on your settings.', 'boldgrid-backup' ), $tags ),
166
+ /* 3 */ wp_kses( __( 'The following special characters can be used in your <strong>include</strong> and <strong>exclude</strong> filters:', 'boldgrid-backup' ), $tags ),
167
+ /* 4 */ $table_legend,
168
+ /* 5 */ wp_kses( __( 'For help with creating a filter, click one of the examples. This will fill in the <strong>Include</strong> and <strong>Exclude</strong> settings below.', 'boldgrid-backup' ), $tags ),
169
+ /* 6 */ $table_examples,
170
+ /* 7 */ $status
171
+ );
172
+
173
+ // TR for the include
174
+ $tr_include = sprintf( '
175
+ <tr class="%3$s">
176
+ <th style="padding-top:0px;">
177
+ %1$s
178
+ </th>
179
+ <td style="padding-top:0px;">
180
+ <input type="text" name="folder_exclusion_include" value="%2$s" class="regular-text" />
181
+ </td>
182
+ </tr>
183
+ ',
184
+ /* A1 */ wp_kses( __( 'Include', 'boldgrid-backup' ), $tags ),
185
+ /* A2 */ esc_attr( $settings['folder_exclusion_include'] ),
186
+ /* A1 */ $using_defaults ? 'hidden' : ''
187
+ );
188
+
189
+ // TR for the exclude.
190
+ $tr_exclude = sprintf( '
191
+ <tr class="%3$s">
192
+ <th>
193
+ %1$s
194
+ </th>
195
+ <td>
196
+ <input type="text" name="folder_exclusion_exclude" value="%2$s" class="regular-text" />
197
+ </td>
198
+ </tr>
199
+ ',
200
+ /* 1 */ wp_kses( __( 'Exclude', 'boldgrid-backup' ), $tags ),
201
+ /* 2 */ esc_attr( $settings['folder_exclusion_exclude'] ),
202
+ /* A1 */ $using_defaults ? 'hidden' : ''
203
+ );
204
+
205
+ // TR for the preview
206
+ $tr_preview = sprintf( '
207
+ <tr class="%1$s">
208
+ <th></th>
209
+ <td>
210
+ <p>
211
+ <input type="hidden" name="folder_exclusion_nonce" value="%2$s" />
212
+ <button id="exclude_folders_button" class="button">%3$s</button>
213
+ </p>
214
+
215
+ <p class="status hidden"></p>
216
+
217
+ <div id="exclude_folders_preview" class="hidden">
218
+
219
+ <div class="tablenav">
220
+ <input type="text" id="folder_exclusion_filter" placeholder="%4$s" />
221
+ <div class="tablenav-pages"></div>
222
+ </div>
223
+
224
+ <ul></ul>
225
+ </div>
226
+ </td>
227
+ </tr>
228
+ ',
229
+ /* 1 */ $using_defaults ? 'hidden' : '',
230
+ /* 2 */ $nonce,
231
+ /* 3 */ wp_kses( __( 'Preview', 'boldgrid-backup' ), $tags ),
232
+ /* 4 */ esc_attr( __( 'Filter below results', 'boldgrid-backup' ) )
233
+ );
234
+
235
+ $markup = sprintf( '
236
+ <div class="bg-box" id="folder_exclusion">
237
+ <div class="bg-box-top">
238
+ %1$s
239
+ </div>
240
+ <div class="bg-box-bottom">
241
+ %2$s
242
+ %3$s
243
+ <table class="form-table">
244
+ <tbody>
245
+ %4$s
246
+ %5$s
247
+ %6$s
248
+ </tbody>
249
+ </table>
250
+ </div>
251
+ </div>',
252
+ /* 1 */ esc_html__( 'Files and Folders', 'boldgrid-backup' ),
253
+ /* 2 */ $tr_header,
254
+ /* 3 */ $tr_help,
255
+ /* 4 */ $tr_include,
256
+ /* 5 */ $tr_exclude,
257
+ /* 6 */ $tr_preview
258
+ );
259
+
260
+ return $markup;
261
+
262
+
admin/partials/settings/notifications.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Show "Notifications" on settings page.
4
+ *
5
+ * @since 1.5.1
6
+ */
7
+
8
+ defined( 'WPINC' ) ? : die;
9
+
10
+ ob_start();
11
+ ?>
12
+
13
+ <div class="bg-box">
14
+ <div class="bg-box-top">
15
+ <?php esc_html_e( 'Notifications', 'boldgrid-backup' ); ?>
16
+ </div>
17
+ <div class="bg-box-bottom">
18
+ <table class="form-table">
19
+ <tr>
20
+ <th><?php esc_html_e( 'Notification email address', 'boldgrid-backup' ); ?></th>
21
+ <td>
22
+ <input id='notification-email' type='text' size='40' name='notification_email' value='<?php echo $settings['notification_email']; ?>'></td>
23
+ </tr>
24
+ <tr>
25
+ <th><?php esc_html_e( 'Send an email when a backup completes', 'boldgrid-backup' ); ?></th>
26
+ <td>
27
+ <input id='notification-backup' type='checkbox' name='notify_backup' value='1'
28
+ <?php
29
+ if ( ! isset( $settings['notifications']['backup'] ) ||
30
+ 0 !== $settings['notifications']['backup'] ) {
31
+ echo ' checked';
32
+ }
33
+ ?> />
34
+ </td>
35
+ </tr>
36
+ <tr>
37
+ <th><?php esc_html_e( 'Send an email when a restoration is performed', 'boldgrid-backup' ); ?></th>
38
+ <td>
39
+ <input id='notification-restore' type='checkbox' name='notify_restore' value='1'
40
+ <?php
41
+ if ( ! isset( $settings['notifications']['restore'] ) ||
42
+ 0 !== $settings['notifications']['restore'] ) {
43
+ echo ' checked';
44
+ }
45
+ ?> />
46
+ </td>
47
+ </tr>
48
+ </table>
49
+ </div>
50
+ </div>
51
+
52
+ <?php
53
+ $output = ob_get_contents();
54
+ ob_end_clean();
55
+ return $output;
56
+ ?>
admin/partials/settings/premium-message.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Show free / premium message.
4
+ *
5
+ * @summary Show an intro atop the settings page regarding free / premium version of the plugin.
6
+ *
7
+ * @since 1.3.1
8
+ */
9
+
10
+ defined( 'WPINC' ) ? : die;
11
+
12
+ if( $this->core->config->get_is_premium() ) {
13
+ ?><p><?php
14
+ /*
15
+ * Print this message:
16
+ *
17
+ * You are running the Premium version of the BoldGrid Backup Plugin. Please visit our
18
+ * <a>BoldGrid Backup User Guide</a> for more information.
19
+ */
20
+ printf(
21
+ wp_kses(
22
+ __( 'You are running the Premium version of the BoldGrid Backup Plugin. Please visit our <a href="%s" target="_blank">BoldGrid Backup User Guide</a> for more information.', 'boldgrid-backup' ),
23
+ array( 'a' => array( 'href' => array(), 'target' => array() ) )
24
+ ),
25
+ esc_url( $this->core->configs['urls']['user_guide'] )
26
+ );
27
+ ?></p><?php
28
+ } else {
29
+ /*
30
+ * Print this message:
31
+ *
32
+ * The BoldGrid Backup plugin comes in two versions, the Free and Premium. The Premium
33
+ * version is part of the BoldGrid Premium Suite. To learn about the capabilities of the
34
+ * BoldGrid Backup Plugin, check out our <a>BoldGrid Backup User Guide</a>.
35
+ *
36
+ * Key differences are size of backups supported, scheduling capabilities, and number of
37
+ * archives supported. To upgrade now, go <a>here</a>.
38
+ */
39
+ printf(
40
+ wp_kses(
41
+ __(
42
+ '
43
+ <p>The BoldGrid Backup plugin comes in two versions, the Free and Premium. The Premium version is part of the BoldGrid Premium Suite. To learn about the capabilities of the BoldGrid Backup Plugin, check out our <a href="%1$s" target="_blank">BoldGrid Backup User Guide</a>.</p>
44
+ <p>Key differences are size of backups supported, scheduling capabilities, and number of archives supported. To upgrade now, go <a href="%2$s" target="_blank">here</a>.</p>
45
+ ',
46
+ 'boldgrid-backup'
47
+ ),
48
+ array(
49
+ 'a' => array( 'href' => array(), 'target' => array() ),
50
+ 'p' => array(),
51
+ )
52
+ ),
53
+ esc_url( $this->core->configs['urls']['user_guide'] ),
54
+ esc_url( $this->core->configs['urls']['upgrade'] )
55
+ );
56
+ }
57
+ ?>
admin/partials/settings/retention.php ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Retention settings.
4
+ *
5
+ * @summary Show the retention settings section of the BoldGrid Backup settings page.
6
+ *
7
+ * @since 1.3.1
8
+ */
9
+
10
+ defined( 'WPINC' ) ? : die;
11
+
12
+ ob_start();
13
+
14
+ $is_retention_set = ( isset( $settings['retention_count'] ) );
15
+ ?>
16
+
17
+ <div class="bg-box">
18
+ <div class="bg-box-top">
19
+ <?php esc_html_e( 'Retention', 'boldgrid-backup' ); ?>
20
+ </div>
21
+ <div class="bg-box-bottom">
22
+ <?php esc_html_e( 'Number of backup archives to retain', 'boldgrid-backup' ); ?>
23
+
24
+ <select id='retention-count' name='retention_count'>
25
+ <?php
26
+ // Loop through each <option> and print it.
27
+ for ( $x = 1; $x <= 10; $x ++ ) {
28
+ // Is retention set and $x = that set retention?
29
+ $x_is_retention = ( $is_retention_set && $x === $settings['retention_count'] );
30
+
31
+ // Is retention not set and $x = the default retention?
32
+ $x_is_default = ( ! $is_retention_set && $this->core->config->get_default_retention() === $x );
33
+
34
+ // Should this option be 'selected'?
35
+ $selected = ( ( $x_is_retention || $x_is_default ) ? ' selected' : '' );
36
+
37
+ // Should we flag this option as "Requires Upgrade"?
38
+ if ( ! $this->core->config->get_is_premium() && ( $this->core->config->get_default_retention() + 1 ) === $x ) {
39
+ $requires_upgrade = esc_html__( '- Requires Upgrade', 'boldgrid-backup' );
40
+ } else {
41
+ $requires_upgrade = '';
42
+ }
43
+
44
+ printf( '<option value="%1$d" %2$s>%1$d</option>',
45
+ $x,
46
+ $selected
47
+ );
48
+ }
49
+ ?>
50
+ </select>
51
+ </div>
52
+ </div>
53
+
54
+ <?php
55
+ $output = ob_get_contents();
56
+ ob_end_clean();
57
+ return $output;
58
+ ?>
admin/partials/settings/scheduler.php ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Show "Scheduler" on settings page.
4
+ *
5
+ * @since 1.5.1
6
+ */
7
+
8
+ defined( 'WPINC' ) ? : die;
9
+
10
+ $schedulers_available = $this->core->scheduler->get_available();
11
+
12
+ $schedulers_count = count( $schedulers_available );
13
+
14
+ $scheduler = ! empty( $settings['scheduler'] ) ? $settings['scheduler'] : false;
15
+
16
+ // No need to show the user any options if there is only 1 scheduler available.
17
+ if ( 1 === $schedulers_count ) {
18
+ return sprintf( '<input type="hidden" name="scheduler" value="%1$s" />', key( $schedulers_available ) );
19
+ }
20
+
21
+ $wp_cron_warning = sprintf(
22
+ '<p class="wp-cron-notice hidden"><span class="dashicons dashicons-warning yellow"></span> %1$s</p>',
23
+ __( 'When using WP Cron, we cannot guarantee that backups will be created at the times you specify. Cron is the recommended scheduler.', 'boldgrid-backup' )
24
+ );
25
+
26
+ $scheduler_options = '';
27
+ foreach ( $schedulers_available as $key => $scheduler_data ) {
28
+ $scheduler_options .= sprintf(
29
+ '<option value="%1$s" %3$s>%2$s</option>',
30
+ $key,
31
+ $scheduler_data['title'],
32
+ $key === $scheduler ? 'selected="selected"' : ''
33
+ );
34
+ }
35
+
36
+ $scheduler_select = sprintf( '<select name="scheduler" id="scheduler">%1$s</select>', $scheduler_options );
37
+
38
+ return sprintf( '
39
+ <div class="bg-box">
40
+ <div class="bg-box-top">
41
+ %1$s
42
+ </div>
43
+ <div class="bg-box-bottom">
44
+ %2$s
45
+ %3$s
46
+ </div>
47
+ </div>',
48
+ __( 'Scheduler', 'boldgrid-backup' ),
49
+ $scheduler_select,
50
+ $wp_cron_warning
51
+ );
52
+
53
+
admin/partials/settings/storage-location.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Display a single provider on the settings page.
5
+ *
6
+ * This page returns the html markup needed for the <tr> #storage_locations
7
+ * table.
8
+ *
9
+ * @since 1.5.2
10
+ *
11
+ * @param array $location {
12
+ * A remote storage location / provider.
13
+ *
14
+ * @type string $title Amazon S3
15
+ * @type string $key amazon_s3
16
+ * @type string $configure A url to configure the provider, such as
17
+ * admin.php?page=boldgrid-backup-amazon-s3
18
+ * @type bool $is_setup Whether or not this provider is properly
19
+ * configured / setup.
20
+ * @type bool $enabled Whether or not this provider is enabled.
21
+ * }
22
+ * @return string
23
+ */
24
+
25
+ defined( 'WPINC' ) ? : die;
26
+
27
+ $configure = '';
28
+
29
+ $configure_link = '<a href="%1$s&TB_iframe=true&width=600&height=550" class="thickbox">%2$s</a>';
30
+
31
+ if ( $location['is_setup'] && ! empty( $location['configure'] ) ) {
32
+ $configure = sprintf( '&#10003; %1$s', __( 'Configured', 'boldgrid-backup' ) );
33
+ $configure .= ' (' . sprintf( $configure_link, $location['configure'], __( 'update', 'boldgrid-backup' ) ) . ')';
34
+ } elseif ( ! empty( $location['configure'] ) ) {
35
+ $configure .= sprintf( $configure_link, $location['configure'], __( 'Configure', 'boldgrid-backup' ) );
36
+ }
37
+
38
+ $disabled = $location['is_setup'] ? '' : 'disabled';
39
+
40
+ $checked = isset( $location['enabled'] ) && true === $location['enabled'] ? 'checked' : '';
41
+
42
+ return sprintf( '
43
+ <tr data-key="%4$s">
44
+ <td>
45
+ <input type="checkbox" name="storage_location[%4$s]" value="1" %3$s %5$s> <strong>%1$s</strong>
46
+ </td>
47
+ <td class="configure">
48
+ %2$s
49
+ </td>
50
+ </tr>
51
+ ',
52
+ $location['title'],
53
+ $configure,
54
+ $disabled,
55
+ $location['key'],
56
+ $checked
57
+ );
58
+
59
+
admin/partials/settings/storage.php ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Display "Backup Storage" options.
4
+ *
5
+ * This file is included on the BoldGrid Backup Settings page and helps render
6
+ * the "Backup Storage" section.
7
+ *
8
+ * @since 1.5.2
9
+ */
10
+
11
+ defined( 'WPINC' ) ? : die;
12
+
13
+ ob_start();
14
+
15
+ $storage_locations = array(
16
+ array(
17
+ 'title' => __( 'Web Server', 'boldgrid-backup' ),
18
+ 'key' => 'local',
19
+ 'is_setup' => true,
20
+ 'enabled' => ! empty( $settings['remote']['local']['enabled'] ) && true === $settings['remote']['local']['enabled'],
21
+ ),
22
+ );
23
+
24
+ /**
25
+ * Allow other storage providers to register themselves.
26
+ *
27
+ * @since 1.5.2
28
+ *
29
+ * @param array $storage_locations {
30
+ * An array of details about our storage locations.
31
+ *
32
+ * @type string $title Amazon S3
33
+ * @type string $key amazon_s3
34
+ * @type string $configure admin.php?page=boldgrid-backup-amazon-s3
35
+ * @type bool $is_setup Whether or not this provider is properly configured.
36
+ * @type bool $enabled Whether or not the checkbox should be checked.
37
+ * }
38
+ */
39
+ $storage_locations = apply_filters( 'boldgrid_backup_register_storage_location', $storage_locations );
40
+
41
+ $premium_box = $this->core->config->is_premium_done ? '' : sprintf( '
42
+ <div class="bg-box-bottom premium">
43
+ <input type="checkbox" disabled="true" /> <strong>%1$s</strong>
44
+
45
+ <p>
46
+ %2$s
47
+ %3$s
48
+ </p>
49
+ </div>',
50
+ /* 1 */ __( 'Amazon S3', 'boldgrid-backup' ),
51
+ /* 2 */ $this->core->go_pro->get_premium_button(),
52
+ /* 3 */ __( 'Upgrade to premium for more Storage Locations!', 'boldgrid-backup' )
53
+ );
54
+
55
+ ?>
56
+
57
+ <div class='bg-box'>
58
+ <div class='bg-box-top'>
59
+ <?php echo __( 'Backup Storage', 'boldgrid-backup' ); ?>
60
+ <?php echo '<span class="dashicons dashicons-editor-help" data-id="remote_storage"></span>'; ?>
61
+ </div>
62
+ <div class='bg-box-bottom'>
63
+ <p class="help" data-id="remote_storage">
64
+ <?php echo __( 'The following is a list of storage locations available to store your backup archives on. It is recommended to store your backups on at least 2 different storage locations. You can find more information <a href="admin.php?page=boldgrid-backup-tools&section=section_locations">here</a>.', 'boldgrid-backup' ); ?>
65
+ </p>
66
+
67
+ <table id="storage_locations">
68
+ <?php
69
+ foreach ( $storage_locations as $location ) {
70
+ $tr = include BOLDGRID_BACKUP_PATH . '/admin/partials/settings/storage-location.php';
71
+ echo $tr;
72
+ }
73
+ ?>
74
+ </table>
75
+
76
+ <br />
77
+ <p class="hidden" id="no_storage">
78
+ <span class="dashicons dashicons-warning yellow"></span>
79
+ <?php echo __( 'Backup will not occur if no storage locations are selected.', 'boldgrid-backup' ); ?>
80
+ </p>
81
+ </div>
82
+ <?php echo $premium_box; ?>
83
+ </div>
84
+
85
+ <?php
86
+ $output = ob_get_contents();
87
+ ob_end_clean();
88
+ return $output;
89
+ ?>
admin/partials/settings/time-of-day.php ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Time of day.
4
+ *
5
+ * @since 1.5.4
6
+ */
7
+
8
+ defined( 'WPINC' ) ? : die;
9
+
10
+ $tz_info = $this->core->time->get_timezone_info();
11
+
12
+ ob_start();
13
+ ?>
14
+
15
+ <div class="bg-box">
16
+ <div class="bg-box-top">
17
+ <?php esc_html_e( 'Time of Day', 'boldgrid-backup' ); ?>
18
+ </div>
19
+ <div class="bg-box-bottom">
20
+ <select id='tod-h' name='tod_h'>
21
+ <?php
22
+ for ( $x = 1; $x <= 12; $x ++ ) {
23
+ ?>
24
+ <option value='<?php echo $x;?>'
25
+ <?php
26
+ if ( ! empty( $settings['schedule']['tod_h'] ) && $x === $settings['schedule']['tod_h'] ) {
27
+ echo ' selected';
28
+ }
29
+ ?>><?php echo $x;?></option>
30
+ <?php
31
+ }
32
+ ?>
33
+ </select>
34
+
35
+ <select id='tod-m' name='tod_m'>
36
+ <?php
37
+ for ( $x = 0; $x <= 59; $x ++ ) {
38
+ // Convert $x to a padded string.
39
+ $x = str_pad( $x, 2, '0', STR_PAD_LEFT );
40
+ ?>
41
+ <option value='<?php echo $x;?>'
42
+ <?php
43
+ if ( ! empty( $settings['schedule']['tod_m'] ) && $x == $settings['schedule']['tod_m'] ) {
44
+ echo ' selected';
45
+ }
46
+ ?>><?php echo $x;?></option>
47
+ <?php
48
+ }
49
+ ?>
50
+ </select>
51
+
52
+ <select id='tod-a' name='tod_a'>
53
+ <option value='AM'
54
+ <?php
55
+ if ( ! isset( $settings['schedule']['tod_a'] ) || 'PM' !== $settings['schedule']['tod_a'] ) {
56
+ echo ' selected';
57
+ }
58
+ ?>>AM</option>
59
+ <option value='PM'
60
+ <?php
61
+ if ( isset( $settings['schedule']['tod_a'] ) && 'PM' === $settings['schedule']['tod_a'] ) {
62
+ echo ' selected';
63
+ }
64
+ ?>>PM</option>
65
+ </select>
66
+
67
+ <div style="vertical-align:middle;display:inline-block;">
68
+ <?php echo $tz_info['markup_timezone'] . ' <em>' . $tz_info['markup_change'] . '</em>' ?>
69
+ </div>
70
+
71
+ <p class="wp-cron-notice hidden"><em>WP Cron runs on GMT time, which is currently <?php echo date( 'l g:i a e' )?>.</em></p>
72
+ </div>
73
+ </div>
74
+
75
+ <?php
76
+ $output = ob_get_contents();
77
+ ob_end_clean();
78
+ return $output;
79
+ ?>
admin/partials/tools/local-remote.php ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Show "Auto Updates" on settings page.
4
+ *
5
+ * @since 1.5.4
6
+ */
7
+
8
+ defined( 'WPINC' ) ? : die;
9
+
10
+ // https://github.com/cbschuld/Browser.php
11
+ include_once BOLDGRID_BACKUP_PATH . '/vendor/cbschuld/browser.php/lib/Browser.php';
12
+ $browser = new Browser();
13
+
14
+ ob_start();
15
+
16
+ $local_info = array(
17
+ array(
18
+ 'title' => __( 'Browser', 'boldgrid-backup' ),
19
+ 'value' => $browser->getBrowser() . ' ' . $browser->getVersion(),
20
+ ),
21
+ array(
22
+ 'title' => __( 'Operating System', 'boldgrid-backup' ),
23
+ 'value' => $browser->getPlatform(),
24
+ ),
25
+ );
26
+
27
+ $local_info_markup = '';
28
+ foreach ( $local_info as $info ) {
29
+ if ( empty( $info['value'] ) ) {
30
+ continue;
31
+ }
32
+
33
+ $local_info_markup .= sprintf( '<li><strong>%1$s</strong>: %2$s</li>', $info['title'], $info['value'] );
34
+ }
35
+
36
+ $server_info = array(
37
+ array(
38
+ 'title' => __( 'Server Name', 'boldgrid-backup' ),
39
+ 'key' => 'SERVER_NAME',
40
+ ),
41
+ array(
42
+ 'title' => __( 'Server IP Address', 'boldgrid-backup' ),
43
+ 'key' => 'SERVER_ADDR',
44
+ ),
45
+ array(
46
+ 'title' => __( 'Server Type / OS', 'boldgrid-backup' ),
47
+ 'key' => 'SERVER_SOFTWARE',
48
+ ),
49
+ );
50
+
51
+ $server_info_markup = '';
52
+ foreach ( $server_info as $info ) {
53
+ if ( empty( $_SERVER[ $info['key'] ] ) ) {
54
+ continue;
55
+ }
56
+
57
+ $server_info_markup .= sprintf( '<li><strong>%1$s</strong>: %2$s</li>', $info['title'], $_SERVER[ $info['key'] ] );
58
+ }
59
+
60
+ printf( '
61
+ <h2>%1$s</h2>
62
+ <p>%2$s</p>
63
+ <p>%3$s</p>
64
+ <hr />',
65
+ __( 'Where should I store my backups?', 'boldgrid-backup' ),
66
+ __( 'Throughout the BoldGrid Backup plugin, you will see references to <strong>Local Machine</strong>, <strong>Web Server</strong>, and <strong>Remote Storage</strong>. These are all locations you can save your backup archives to.', 'boldgrid-backup' ),
67
+ __( 'Continue reading below to find out more about each. It is recommended to store backup archives in at least 2 different storage locations.', 'boldgrid-backup' )
68
+ );
69
+
70
+
71
+
72
+ printf( '<h3>%1$s</h3>', __( 'Local Machine', 'boldgrid-backup' ) );
73
+
74
+ printf(
75
+ '<p>%1$s</p>',
76
+ __( 'Your <strong>Local Machine</strong> is the device you are using right now to access the internet. It could be a desktop, laptop, tablet, or even a smart phone.', 'boldgrid-backup' )
77
+ );
78
+
79
+ if ( ! empty( $local_info_markup ) ) {
80
+ printf( '
81
+ <p>%1$s</p>
82
+ %2$s',
83
+ __( 'We are able to see the following information about your <strong>Local Machine</strong>:', 'boldgrid-backup' ),
84
+ $local_info_markup
85
+ );
86
+ }
87
+
88
+ echo '<hr />';
89
+
90
+ printf( '<h3>%1$s</h3>', __( 'Web Server', 'boldgrid-backup' ) );
91
+
92
+ printf(
93
+ '<p>%1$s</p>',
94
+ __( 'The <strong>Web Server</strong> is the server where your WordPress website lives. You usually pay your web hosting provider monthly or yearly for hosting.', 'boldgrid-backup' )
95
+ );
96
+
97
+ if ( ! empty( $server_info_markup ) ) {
98
+ printf( '
99
+ <p>%1$s</p>
100
+ %2$s',
101
+ __( 'We are able to see the following information about your <strong>Web Server</strong>:', 'boldgrid-backup' ),
102
+ $server_info_markup
103
+ );
104
+ }
105
+
106
+ echo '<hr />';
107
+
108
+ printf( '<h3>%1$s</h3>', __( 'Remote Storage', 'boldgrid-backup' ) );
109
+
110
+ printf(
111
+ '<p>%1$s</p>',
112
+ __( '<strong>Remote Storage</strong> providers are servers other than your <em>Local Machine</em> and <em>Web Server</em> where you can store files. For example, <em>FTP</em>, <em>SFTP</em>, and <em>Amazon S3</em> are all considered Remote Storage Providers.', 'boldgrid-backup' )
113
+ );
114
+
115
+ if ( ! $this->core->config->is_premium_done ) {
116
+ printf( '
117
+ <div class="bg-box-bottom premium wp-clearfix">
118
+ %1$s
119
+ %2$s
120
+ </div>',
121
+ $this->core->go_pro->get_premium_button(),
122
+ __( 'Upgrade to <strong>BoldGrid Backup Premium</strong> to gain access to more <em>Remote Storage Providers</em>.', 'boldgrid-backup' )
123
+ );
124
+ }
125
+
126
+
127
+ $output = ob_get_contents();
128
+ ob_end_clean();
129
+ return $output;
130
+
admin/remote/ftp-hooks.php ADDED
@@ -0,0 +1,287 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * FTP Hooks class.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.4
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * FTP Hooks class.
17
+ *
18
+ * The only purpose this class is to be used for is to separate methods that are
19
+ * used for registering a new remove provider. All of these methods are called
20
+ * via hooks.
21
+ *
22
+ * @since 1.5.4
23
+ */
24
+ class Boldgrid_Backup_Admin_Ftp_Hooks {
25
+
26
+ /**
27
+ * The core class object.
28
+ *
29
+ * @since 1.5.4
30
+ * @access private
31
+ * @var Boldgrid_Backup_Admin_Core
32
+ */
33
+ private $core;
34
+
35
+ /**
36
+ * Constructor.
37
+ *
38
+ * @since 1.5.4
39
+ *
40
+ * @param Boldgrid_Backup_Admin_Core $core Core class object.
41
+ */
42
+ public function __construct( $core ) {
43
+ $this->core = $core;
44
+ }
45
+
46
+ /**
47
+ * Add menu items.
48
+ *
49
+ * @since 1.5.4
50
+ */
51
+ public function add_menu_items() {
52
+ $capability = 'administrator';
53
+
54
+ add_submenu_page(
55
+ null,
56
+ __( 'FTP Settings', 'boldgrid-backup' ),
57
+ __( 'FTP Settings', 'boldgrid-backup' ),
58
+ $capability,
59
+ 'boldgrid-backup-ftp',
60
+ array(
61
+ $this->core->ftp->page,
62
+ 'settings',
63
+ )
64
+ );
65
+ }
66
+
67
+ /**
68
+ * Hook into the filter to add all ftp backups to the full list of backups.
69
+ *
70
+ * @since 1.5.4
71
+ */
72
+ public function filter_get_all() {
73
+ $contents = $this->core->ftp->get_contents( true, $this->core->ftp->remote_dir );
74
+ $contents = $this->core->ftp->format_raw_contents( $contents );
75
+
76
+ foreach ( $contents as $item ) {
77
+ $filename = $item['filename'];
78
+
79
+ $backup = array(
80
+ 'filename' => $filename,
81
+ 'last_modified' => $item['time'],
82
+ 'size' => $item['size'],
83
+ 'locations' => array(
84
+ array(
85
+ 'title' => $this->core->ftp->nickname,
86
+ 'on_remote_server' => true,
87
+ 'title_attr' => $this->core->ftp->title_attr,
88
+ ),
89
+ ),
90
+ );
91
+
92
+ $this->core->archives_all->add( $backup );
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Determine if FTP is setup.
98
+ *
99
+ * @since 1.5.4
100
+ */
101
+ public function is_setup_ajax() {
102
+ if ( ! current_user_can( 'update_plugins' ) ) {
103
+ wp_send_json_error( __( 'Permission denied.', 'boldgrid-backup' ) );
104
+ }
105
+
106
+ if ( ! check_ajax_referer( 'boldgrid_backup_settings', 'security', false ) ) {
107
+ wp_send_json_error( __( 'Invalid nonce.', 'boldgrid-backup' ) );
108
+ }
109
+
110
+ $settings = $this->core->settings->get_settings();
111
+
112
+ $location = $this->core->ftp->get_details();
113
+ $tr = include BOLDGRID_BACKUP_PATH . '/admin/partials/settings/storage-location.php';
114
+
115
+ if ( $this->core->ftp->is_setup() ) {
116
+ wp_send_json_success( $tr );
117
+ } else {
118
+ wp_send_json_error( $tr );
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Actions to take after a backup file has been generated.
124
+ *
125
+ * @since 1.5.4
126
+ *
127
+ * @param array $info
128
+ */
129
+ public function post_archive_files( $info ) {
130
+
131
+ /*
132
+ * We only want to add this to the jobs queue if we're in the middle of
133
+ * an automatic backup. If the user simply clicked on "Backup site now",
134
+ * we don't want to automatically send the backup to Amazon, there's a
135
+ * button for that.
136
+ */
137
+ if ( ! $this->core->doing_cron ) {
138
+ return;
139
+ }
140
+
141
+ if ( ! $this->core->remote->is_enabled( $this->core->ftp->key ) || $info['dryrun'] || ! $info['save'] ) {
142
+ return;
143
+ }
144
+
145
+ $args = array(
146
+ 'filepath' => $info['filepath'],
147
+ 'action' => 'boldgrid_backup_' . $this->core->ftp->key . '_upload_post_archive',
148
+ 'action_data' => $info['filepath'],
149
+ 'action_title' => sprintf( __( 'Upload backup file to %1$s', 'boldgrid-backup' ), $this->core->ftp->title ),
150
+ );
151
+
152
+ $this->core->jobs->add( $args );
153
+ }
154
+
155
+ /**
156
+ * Register FTP as a storage location.
157
+ *
158
+ * @since 1.5.4
159
+ */
160
+ public function register_storage_location( $storage_locations ) {
161
+ $storage_locations[] = $this->core->ftp->get_details();
162
+
163
+ return $storage_locations;
164
+ }
165
+
166
+ /**
167
+ * Register FTP on the archive details page.
168
+ *
169
+ * @since 1.5.4
170
+ *
171
+ * @param string $filepath
172
+ */
173
+ public function single_archive_remote_option( $filepath ) {
174
+ $allow_upload = $this->core->ftp->is_setup();
175
+ $uploaded = $this->core->ftp->is_uploaded( $filepath );
176
+
177
+ $this->core->archive_details->remote_storage_li[] = array(
178
+ 'id' => $this->core->ftp->key,
179
+ 'title' => $this->core->ftp->nickname,
180
+ 'title_attr' => $this->core->ftp->title_attr,
181
+ 'uploaded' => $uploaded,
182
+ 'allow_upload' => $allow_upload,
183
+ 'is_setup' => $this->core->ftp->is_setup(),
184
+ );
185
+ }
186
+
187
+ /**
188
+ * Upload a file (triggered by jobs queue).
189
+ *
190
+ * The jobs queue will call this method to upload a file.
191
+ *
192
+ * @since 1.0.0
193
+ *
194
+ * @param string $filepath
195
+ */
196
+ public function upload_post_archiving( $filepath ) {
197
+ $success = $this->core->ftp->upload( $filepath );
198
+
199
+ return $success;
200
+ }
201
+
202
+ /**
203
+ * Handle the ajax request to download an FTP backup locally.
204
+ *
205
+ * @since 1.5.4
206
+ */
207
+ public function wp_ajax_download() {
208
+ $error = __( 'Unable to download backup from FTP', 'bolgrid-bakcup' );
209
+
210
+ // Validation, user role.
211
+ if ( ! current_user_can( 'update_plugins' ) ) {
212
+ $this->core->notice->add_user_notice(
213
+ sprintf( $error . ': ' . __( 'Permission denied.', 'boldgrid-backup' ) ),
214
+ 'notice notice-error'
215
+ );
216
+ wp_send_json_error();
217
+ }
218
+
219
+ // Validation, nonce.
220
+ if ( ! $this->core->archive_details->validate_nonce() ) {
221
+ $this->core->notice->add_user_notice(
222
+ sprintf( $error . ': ' . __( 'Invalid nonce.', 'boldgrid-backup' ) ),
223
+ 'notice notice-error'
224
+ );
225
+ wp_send_json_error();
226
+ }
227
+
228
+ // Validation, $_POST data.
229
+ $filename = ! empty( $_POST['filename'] ) ?
230
+ sanitize_file_name( $_POST['filename'] ) : false;
231
+
232
+ if ( empty( $filename ) ) {
233
+ $this->core->notice->add_user_notice(
234
+ sprintf( $error . ': ' . __( 'Invalid filename.', 'boldgrid-backup' ) ),
235
+ 'notice notice-error'
236
+ );
237
+ wp_send_json_error();
238
+ }
239
+
240
+ $result = $this->core->ftp->download( $filename );
241
+
242
+ if ( $result ) {
243
+ $this->core->notice->add_user_notice(
244
+ sprintf(
245
+ __( '<h2>%2$s</h2><p>Backup file <strong>%1$s</strong> successfully downloaded from FTP.</p>', 'boldgrid-backup' ),
246
+ /* 1 */ $filename,
247
+ /* 2 */ __( 'BoldGrid Backup Premium - FTP Download', 'boldgrid-backup' )
248
+ ),
249
+ 'notice notice-success'
250
+ );
251
+ wp_send_json_success();
252
+ }
253
+ }
254
+
255
+ /**
256
+ * Upload a file (triggered by ajax).
257
+ *
258
+ * @since 1.5.4
259
+ */
260
+ public function wp_ajax_upload() {
261
+ if ( ! current_user_can( 'update_plugins' ) ) {
262
+ wp_send_json_error( __( 'Permission denied.', 'boldgrid-backup' ) );
263
+ }
264
+
265
+ if ( ! $this->core->archive_details->validate_nonce() ) {
266
+ wp_send_json_error( __( 'Invalid nonce.', 'boldgrid-backup' ) );
267
+ }
268
+
269
+ $filename = ! empty( $_POST['filename'] ) ?
270
+ sanitize_file_name( $_POST['filename'] ) : false;
271
+
272
+ $filepath = $this->core->backup_dir->get_path_to( $filename );
273
+
274
+ if ( empty( $filename ) || ! $this->core->wp_filesystem->exists( $filepath ) ) {
275
+ wp_send_json_error( __( 'Invalid archive filepath.', 'boldgrid-backup' ) );
276
+ }
277
+
278
+ $uploaded = $this->core->ftp->upload( $filepath );
279
+
280
+ if ( $uploaded ) {
281
+ wp_send_json_success( 'uploaded!' );
282
+ } else {
283
+ $error = ! empty( $this->core->ftp->errors ) ? implode( '<br /><br />', $this->core->ftp->errors ) : '';
284
+ wp_send_json_error( $error );
285
+ }
286
+ }
287
+ }
admin/remote/ftp-page.php ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * FTP Page class.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.4
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * FTP Page class.
17
+ *
18
+ * @since 1.5.4
19
+ */
20
+ class Boldgrid_Backup_Admin_Ftp_Page {
21
+
22
+ /**
23
+ * The core class object.
24
+ *
25
+ * @since 1.5.4
26
+ * @access private
27
+ * @var Boldgrid_Backup_Admin_Core
28
+ */
29
+ private $core;
30
+
31
+ /**
32
+ * Constructor.
33
+ *
34
+ * @since 1.5.4
35
+ *
36
+ * @param Boldgrid_Backup_Admin_Core $core Core class object.
37
+ */
38
+ public function __construct( $core ) {
39
+ $this->core = $core;
40
+ }
41
+
42
+ /**
43
+ * Enqueue scripts.
44
+ *
45
+ * @since 1.5.4
46
+ */
47
+ public function enqueue_scripts() {
48
+ $handle = 'boldgrid-backup-admin-ftp-settings';
49
+ wp_register_script( $handle,
50
+ plugin_dir_url( dirname( __FILE__ ) ) . 'js/' . $handle . '.js',
51
+ array( 'jquery' ),
52
+ BOLDGRID_BACKUP_VERSION,
53
+ false
54
+ );
55
+ $translation = array(
56
+ 'default_port' => $this->core->ftp->default_port,
57
+ );
58
+ wp_localize_script( $handle, 'BoldGridBackupAdminFtpSettings', $translation );
59
+ wp_enqueue_script( $handle );
60
+
61
+ wp_enqueue_style(
62
+ $handle,
63
+ plugin_dir_url( dirname( __FILE__ ) ) . 'css/' . $handle . '.css',
64
+ array(),
65
+ BOLDGRID_BACKUP_VERSION
66
+ );
67
+ }
68
+
69
+ /**
70
+ * Generate the submenu page for our FTP Settings page.
71
+ *
72
+ * @since 1.5.4
73
+ */
74
+ public function settings() {
75
+ if ( ! current_user_can( 'update_plugins' ) ) {
76
+ return false;
77
+ }
78
+
79
+ $this->enqueue_scripts();
80
+ wp_enqueue_style( 'boldgrid-backup-admin-hide-all' );
81
+
82
+ // Blank data, used when deleting settings.
83
+ $type = $this->core->ftp->default_type;
84
+ $blank_data = array(
85
+ 'type' => $type,
86
+ 'host' => null,
87
+ 'port' => $this->core->ftp->default_port[ $type ],
88
+ 'user' => null,
89
+ 'pass' => null,
90
+ 'retention_count' => $this->core->ftp->retention_count,
91
+ 'nickname' => '',
92
+ );
93
+
94
+ // Post data, used by default or when updating settings.
95
+ $post_data = $this->core->ftp->get_from_post();
96
+
97
+ $action = ! empty( $_POST['action'] ) ? sanitize_key( $_POST['action'] ) : null;
98
+
99
+ switch ( $action ) {
100
+ case 'save':
101
+ echo $this->core->elements['long_checking_creds'];
102
+ ob_flush();
103
+ flush();
104
+
105
+ $this->settings_save();
106
+ $data = $post_data;
107
+ break;
108
+ case 'delete':
109
+ $this->settings_delete();
110
+ $data = $blank_data;
111
+ break;
112
+ default:
113
+ $data = $post_data;
114
+ }
115
+
116
+ include BOLDGRID_BACKUP_PATH . '/admin/partials/remote/ftp.php';
117
+ }
118
+
119
+ /**
120
+ * Process the user's request to update their FTP settings.
121
+ *
122
+ * @since 1.5.4
123
+ */
124
+ public function settings_delete() {
125
+ $ftp = $this->core->ftp;
126
+
127
+ if ( ! current_user_can( 'update_plugins' ) ) {
128
+ return false;
129
+ }
130
+
131
+ $settings = $this->core->settings->get_settings();
132
+ if ( ! isset( $settings['remote'][ $ftp->key ] ) || ! is_array( $settings['remote'][ $ftp->key ] ) ) {
133
+ $settings['remote'][ $ftp->key ] = array();
134
+ }
135
+
136
+ $settings['remote'][ $ftp->key ] = array();
137
+ update_site_option( 'boldgrid_backup_settings', $settings );
138
+
139
+ $ftp->reset();
140
+ $ftp->disconnect();
141
+
142
+ do_action( 'boldgrid_backup_notice', __( 'Settings deleted.', 'boldgrid-backup' ), 'notice updated is-dismissible' );
143
+ }
144
+
145
+ /**
146
+ * Process the user's request to update their FTP settings.
147
+ *
148
+ * @since 1.5.4
149
+ */
150
+ public function settings_save() {
151
+
152
+ // Readability.
153
+ $ftp = $this->core->ftp;
154
+
155
+ if ( ! current_user_can( 'update_plugins' ) ) {
156
+ return false;
157
+ }
158
+
159
+ if ( empty( $_POST ) ) {
160
+ return false;
161
+ }
162
+
163
+ $settings = $this->core->settings->get_settings();
164
+ if ( ! isset( $settings['remote'][ $ftp->key ] ) || ! is_array( $settings['remote'][ $ftp->key ] ) ) {
165
+ $settings['remote'][ $ftp->key ] = array();
166
+ }
167
+
168
+ $data = $ftp->get_from_post();
169
+
170
+ $valid_credentials = $ftp->is_valid_credentials( $data['host'], $data['user'], $data['pass'], $data['port'], $data['type'] );
171
+
172
+ if ( $valid_credentials ) {
173
+ $settings['remote'][ $ftp->key ]['host'] = $data['host'];
174
+ $settings['remote'][ $ftp->key ]['user'] = $data['user'];
175
+ $settings['remote'][ $ftp->key ]['pass'] = $data['pass'];
176
+ $settings['remote'][ $ftp->key ]['port'] = $data['port'];
177
+ $settings['remote'][ $ftp->key ]['type'] = $data['type'];
178
+ }
179
+
180
+ $settings['remote'][ $ftp->key ]['retention_count'] = $data['retention_count'];
181
+ $settings['remote'][ $ftp->key ]['nickname'] = $data['nickname'];
182
+
183
+ if ( ! empty( $ftp->errors ) ) {
184
+ do_action( 'boldgrid_backup_notice', implode( '<br /><br />', $ftp->errors ) );
185
+ } else {
186
+ update_site_option( 'boldgrid_backup_settings', $settings );
187
+ do_action( 'boldgrid_backup_notice', __( 'Settings saved.', 'boldgrid-backup' ), 'notice updated is-dismissible' );
188
+ }
189
+ }
190
+ }
admin/remote/ftp.php ADDED
@@ -0,0 +1,858 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * FTP class.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.4
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * FTP class.
17
+ *
18
+ * @since 1.5.4
19
+ */
20
+ class Boldgrid_Backup_Admin_Ftp {
21
+
22
+ /**
23
+ * An FTP connection.
24
+ *
25
+ * @since 1.5.4
26
+ * @access private
27
+ * @var Resource
28
+ */
29
+ private $connection = null;
30
+
31
+ /**
32
+ * The core class object.
33
+ *
34
+ * @since 1.5.4
35
+ * @access private
36
+ * @var Boldgrid_Backup_Admin_Core
37
+ */
38
+ private $core;
39
+
40
+ /**
41
+ * Default port numbers.
42
+ *
43
+ * @since 1.5.4
44
+ * @access public
45
+ * @var array
46
+ */
47
+ public $default_port = array(
48
+ 'ftp' => 21,
49
+ 'sftp' => 22,
50
+ );
51
+
52
+ /**
53
+ * Default type.
54
+ *
55
+ * @since 1.5.4
56
+ * @access public
57
+ * @var string
58
+ */
59
+ public $default_type = 'sftp';
60
+
61
+ /**
62
+ * Errors.
63
+ *
64
+ * @since 1.5.4
65
+ * @access public
66
+ * @var array
67
+ */
68
+ public $errors = array();
69
+
70
+ /**
71
+ * Hooks class.
72
+ *
73
+ * @since 1.5.4
74
+ * @access public
75
+ * @var Boldgrid_Backup_Admin_Ftp_Hooks
76
+ */
77
+ public $hooks;
78
+
79
+ /**
80
+ * FTP host.
81
+ *
82
+ * @since 1.5.4
83
+ * @access private
84
+ * @var string
85
+ */
86
+ private $host = null;
87
+
88
+ /**
89
+ * Whether or not we have logged in.
90
+ *
91
+ * @since 1.5.4
92
+ * @access public
93
+ * @var bool
94
+ */
95
+ public $logged_in = false;
96
+
97
+ /**
98
+ * Nickname.
99
+ *
100
+ * So the user can refer to their ftp account as something other than ftp.
101
+ *
102
+ * @since 1.6.0
103
+ * @access public
104
+ * @var string
105
+ */
106
+ public $nickname;
107
+
108
+ /**
109
+ * Our key / label for ftp.
110
+ *
111
+ * @since 1.5.4
112
+ * @access public
113
+ * @var string
114
+ */
115
+ public $key = 'ftp';
116
+
117
+ /**
118
+ * FTP password.
119
+ *
120
+ * @since 1.5.4
121
+ * @access private
122
+ * @var string
123
+ */
124
+ private $pass = null;
125
+
126
+ /**
127
+ * FTP remote directory.
128
+ *
129
+ * @since 1.5.4
130
+ * @access public
131
+ * @var string
132
+ */
133
+ public $remote_dir = 'boldgrid_backup';
134
+
135
+ /**
136
+ * Retention count.
137
+ *
138
+ * @since 1.5.4
139
+ * @access public
140
+ * @var int $retention_count
141
+ */
142
+ public $retention_count = 5;
143
+
144
+ /**
145
+ * Default timeout.
146
+ *
147
+ * @since 1.5.4
148
+ * @access public
149
+ * @var int
150
+ */
151
+ public $timeout = 10;
152
+
153
+ /**
154
+ * Our title / label for ftp.
155
+ *
156
+ * @since 1.5.4
157
+ * @access public
158
+ * @var string
159
+ */
160
+ public $title = 'FTP / SFTP';
161
+
162
+ /**
163
+ * Title attribute.
164
+ *
165
+ * If you are using a nickname, hovering over the nickname should show this
166
+ * more clear title.
167
+ *
168
+ * @since 1.6.0
169
+ * @access public
170
+ * @var string
171
+ */
172
+ public $title_attr;
173
+
174
+ /**
175
+ * Our FTP type, ftp or sftp.
176
+ *
177
+ * @since 1.5.4
178
+ * @access public
179
+ * @var string
180
+ */
181
+ public $type = null;
182
+
183
+ /**
184
+ * FTP username.
185
+ *
186
+ * @since 1.5.4
187
+ * @access private
188
+ * @var string
189
+ */
190
+ private $user = null;
191
+
192
+ /**
193
+ * Valid types.
194
+ *
195
+ * @since 1.5.4
196
+ * @access public
197
+ * @var array
198
+ */
199
+ public $valid_types = array( 'ftp', 'sftp' );
200
+
201
+ /**
202
+ * Constructor.
203
+ *
204
+ * @since 1.5.4
205
+ *
206
+ * @param Boldgrid_Backup_Admin_Core $core Core class object.
207
+ */
208
+ public function __construct( $core ) {
209
+ include_once BOLDGRID_BACKUP_PATH . '/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php';
210
+
211
+ $this->core = $core;
212
+ $this->hooks = new Boldgrid_Backup_Admin_Ftp_Hooks( $core );
213
+ $this->page = new Boldgrid_Backup_Admin_Ftp_Page( $core );
214
+ }
215
+
216
+ /**
217
+ * Connect to our ftp server.
218
+ *
219
+ * @since 1.5.4
220
+ */
221
+ public function connect() {
222
+ if ( ! empty( $this->connection ) ) {
223
+ return;
224
+ }
225
+
226
+ $this->init();
227
+
228
+ if ( empty( $this->user ) || empty( $this->pass ) || empty( $this->host ) || empty( $this->type ) || empty( $this->port ) ) {
229
+ return;
230
+ }
231
+
232
+ switch ( $this->type ) {
233
+ case 'ftp':
234
+ $this->connection = ftp_connect( $this->host, $this->port, $this->timeout );
235
+ break;
236
+ case 'sftp':
237
+ $this->connection = new phpseclib\Net\SFTP( $this->host, $this->port );
238
+ break;
239
+ }
240
+ }
241
+
242
+ /**
243
+ * Create backup directory on remote host.
244
+ *
245
+ * @since 1.5.4
246
+ *
247
+ * @return bool False when we were unable to create directory.
248
+ */
249
+ public function create_backup_dir() {
250
+ $this->connect();
251
+ $this->log_in();
252
+ if ( ! $this->logged_in ) {
253
+ return false;
254
+ }
255
+
256
+ $contents = $this->get_contents();
257
+ if ( ! $contents || ! is_array( $contents ) ) {
258
+ $this->errors[] = __( 'Unable to get a directory listing from FTP server.', 'boldgrid-backup' );
259
+ return false;
260
+ } elseif ( in_array( $this->remote_dir, $contents, true ) ) {
261
+ return true;
262
+ }
263
+
264
+ switch ( $this->type ) {
265
+ case 'ftp':
266
+ $created = ftp_mkdir( $this->connection, $this->remote_dir );
267
+ break;
268
+ case 'sftp':
269
+ $created = $this->connection->mkdir( $this->remote_dir );
270
+ break;
271
+ }
272
+
273
+ if ( ! $created ) {
274
+ $this->errors[] = sprintf( __( 'Unable to create the following directory on FTP server: %1$s', 'boldgrid-backup' ), $this->remote_dir );
275
+ }
276
+
277
+ return $created;
278
+ }
279
+
280
+ /**
281
+ * Disconnect from FTP server.
282
+ *
283
+ * @since 1.5.4
284
+ */
285
+ public function disconnect() {
286
+ if ( 'ftp' === $this->type && is_resource( $this->connection ) ) {
287
+ ftp_close( $this->connection );
288
+ $this->connection = null;
289
+ $this->logged_in = false;
290
+ }
291
+ }
292
+
293
+ /**
294
+ * Download a backup via FTP.
295
+ *
296
+ * @since 1.5.4
297
+ *
298
+ * @param string $filename
299
+ * @return bool
300
+ */
301
+ public function download( $filename ) {
302
+ $this->connect();
303
+
304
+ $local_filepath = $this->core->backup_dir->get_path_to( $filename );
305
+ $server_filepath = $this->remote_dir . '/' . $filename;
306
+ $success = false;
307
+
308
+ $this->log_in();
309
+
310
+ switch ( $this->type ) {
311
+ case 'ftp':
312
+ $success = ftp_get( $this->connection, $local_filepath, $server_filepath, FTP_BINARY );
313
+ break;
314
+ case 'sftp':
315
+ $success = $this->connection->get( $server_filepath, $local_filepath );
316
+ break;
317
+ }
318
+
319
+ if ( $success ) {
320
+ $this->core->remote->post_download( $local_filepath );
321
+ }
322
+
323
+ return $success;
324
+ }
325
+
326
+ /**
327
+ * Enforce retention.
328
+ *
329
+ * @since 1.5.4
330
+ */
331
+ public function enforce_retention() {
332
+ if ( empty( $this->retention_count ) ) {
333
+ return;
334
+ }
335
+
336
+ $contents = $this->get_contents( true, $this->remote_dir );
337
+ $backups = $this->format_raw_contents( $contents );
338
+
339
+ $count_to_delete = count( $backups ) - $this->retention_count;
340
+
341
+ if ( empty( $backups ) || $count_to_delete <= 0 ) {
342
+ return false;
343
+ }
344
+
345
+ usort( $backups, function( $a, $b ) {
346
+ return $a['time'] < $b['time'] ? -1 : 1;
347
+ });
348
+
349
+ for ( $x = 0; $x < $count_to_delete; $x++ ) {
350
+ $filename = $backups[ $x ]['filename'];
351
+ $path = $this->remote_dir . '/' . $filename;
352
+
353
+ switch ( $this->type ) {
354
+ case 'ftp':
355
+ ftp_delete( $this->connection, $path );
356
+ break;
357
+ case 'sftp':
358
+ $this->connection->delete( $path, false );
359
+ break;
360
+ }
361
+
362
+ /**
363
+ * Remote file deleted due to remote retention settings.
364
+ *
365
+ * @since 1.5.4
366
+ */
367
+ do_action(
368
+ 'boldgrid_backup_remote_retention_deleted',
369
+ $this->title,
370
+ $filename
371
+ );
372
+ }
373
+ }
374
+
375
+ /**
376
+ * Get our settings from $_POST.
377
+ *
378
+ * @since 1.5.4
379
+ *
380
+ * @return array
381
+ */
382
+ public function get_from_post() {
383
+ $settings = $this->core->settings->get_settings();
384
+
385
+ $values = array(
386
+ array(
387
+ 'key' => 'host',
388
+ 'default' => null,
389
+ 'callback' => 'sanitize_file_name',
390
+ ),
391
+ array(
392
+ 'key' => 'user',
393
+ 'default' => null,
394
+ 'callback' => 'sanitize_text_field',
395
+ ),
396
+ array(
397
+ 'key' => 'pass',
398
+ 'default' => null,
399
+ ),
400
+ array(
401
+ 'key' => 'type',
402
+ 'default' => $this->default_type,
403
+ 'callback' => 'sanitize_key',
404
+ ),
405
+ array(
406
+ 'key' => 'port',
407
+ 'default' => $this->default_port[ $this->default_type ],
408
+ 'callback' => 'intval',
409
+ ),
410
+ array(
411
+ 'key' => 'retention_count',
412
+ 'default' => $this->retention_count,
413
+ 'callback' => 'intval',
414
+ ),
415
+ array(
416
+ 'key' => 'nickname',
417
+ 'default' => '',
418
+ 'callback' => 'stripslashes',
419
+ ),
420
+ );
421
+
422
+ foreach ( $values as $value ) {
423
+ $key = $value['key'];
424
+ $callback = ! empty( $value['callback'] ) ? $value['callback'] : null;
425
+
426
+ if ( ! empty( $_POST[ $key ] ) ) {
427
+ $data[ $key ] = $_POST[ $key ];
428
+ } elseif ( ! empty( $settings['remote'][ $this->key ][ $key ] ) ) {
429
+ $data[ $key ] = $settings['remote'][ $this->key ][ $key ];
430
+ } else {
431
+ $data[ $key ] = $value['default'];
432
+ }
433
+
434
+ // If there is a callback function for sanitizing the value, then run it.
435
+ if ( $callback ) {
436
+ $data[ $key ] = $callback( $data[ $key ] );
437
+ }
438
+ }
439
+
440
+ return $data;
441
+ }
442
+
443
+ /**
444
+ * Format raw contents.
445
+ *
446
+ * This method takes in raw contents and returns an array of backups, with
447
+ * keys defining timestamp and filename.
448
+ *
449
+ * The array of backups returned DO NOT include:
450
+ * # . or .. (typical when getting a directory listing).
451
+ * # Files / backups that do not belong to this site.
452
+ * See $this->core->archive->is_site_archive().
453
+ *
454
+ * @since 1.5.4
455
+ *
456
+ * @param array $conents Raw contents received from this->get_contents.
457
+ * @return array {
458
+ * An array of backups.
459
+ *
460
+ * @type int $time Timestamp file was uploaded to ftp server.
461
+ * @type string $filename
462
+ * }
463
+ */
464
+ public function format_raw_contents( $contents ) {
465
+ $skips = array( '.', '..' );
466
+ $backups = array();
467
+
468
+ if ( ! is_array( $contents ) ) {
469
+ return $backups;
470
+ }
471
+
472
+ foreach ( $contents as $item ) {
473
+
474
+ if ( 'sftp' === $this->type ) {
475
+ $filename = $item['filename'];
476
+ if ( in_array( $filename, $skips, true ) ) {
477
+ continue;
478
+ }
479
+
480
+ $backups[] = array(
481
+ 'time' => $item['mtime'],
482
+ 'filename' => $filename,
483
+ 'size' => $item['size'],
484
+ );
485
+ } else {
486
+ // Before exploding by space, replace multiple spaces with one space.
487
+ $item = preg_replace( '!\s+!', ' ', $item );
488
+
489
+ $exploded_item = explode( ' ', $item );
490
+ $count = count( $exploded_item );
491
+
492
+ $filename = $exploded_item[ $count - 1 ];
493
+ if ( in_array( $filename, $skips, true ) ) {
494
+ continue;
495
+ }
496
+
497
+ // Get the timestamp.
498
+ $month = $exploded_item[ $count - 4 ];
499
+ $day = $exploded_item[ $count - 3 ];
500
+ $time = $exploded_item[ $count - 2 ];
501
+ $time = strtotime( $month . ' ' . $day . ' ' . $time );
502
+
503
+ $backups[] = array(
504
+ 'time' => $time,
505
+ 'filename' => $filename,
506
+ 'size' => $exploded_item[ $count - 5 ],
507
+ );
508
+ }
509
+ }
510
+
511
+ foreach ( $backups as $key => $backup ) {
512
+ if ( ! $this->core->archive->is_site_archive( $backup['filename'] ) ) {
513
+ unset( $backups[ $key ] );
514
+ }
515
+ }
516
+ $backups = array_values( $backups );
517
+
518
+ return $backups;
519
+ }
520
+
521
+ /**
522
+ * Get the remote contents / listing.
523
+ *
524
+ * @since 1.5.4
525
+ *
526
+ * @param bool $raw Whether to get the raw contents (ftp_rawlist) or not
527
+ * (ftp_nlist).
528
+ * @param string $dir The directory to get listing of.
529
+ * @return mixed
530
+ */
531
+ public function get_contents( $raw = false, $dir = '.' ) {
532
+ $this->connect();
533
+ $this->log_in();
534
+ if ( ! $this->logged_in ) {
535
+ $this->errors[] = __( 'Unable to log in to FTP server.', 'boldgrid-backup' );
536
+ return array();
537
+ }
538
+
539
+ switch ( $this->type ) {
540
+ case 'ftp':
541
+ if ( $raw ) {
542
+ return ftp_rawlist( $this->connection, $dir );
543
+ } else {
544
+ return ftp_nlist( $this->connection , $dir );
545
+ }
546
+ break;
547
+ case 'sftp':
548
+ if ( $raw ) {
549
+ return $this->connection->rawlist( $dir );
550
+ } else {
551
+ return $this->connection->nlist( $dir );
552
+ }
553
+ break;
554
+ }
555
+ }
556
+
557
+ /**
558
+ * Get settings.
559
+ *
560
+ * @since 1.5.4
561
+ */
562
+ public function get_details() {
563
+ $is_setup = $this->is_setup();
564
+
565
+ $settings = $this->core->settings->get_settings();
566
+
567
+ return array(
568
+ 'title' => $this->title,
569
+ 'key' => $this->key,
570
+ 'configure' => 'admin.php?page=boldgrid-backup-ftp',
571
+ 'is_setup' => $is_setup,
572
+ 'enabled' => ! empty( $settings['remote'][ $this->key ]['enabled'] ) && $settings['remote'][ $this->key ]['enabled'] && $is_setup,
573
+ );
574
+ }
575
+
576
+ /**
577
+ * Init properties.
578
+ *
579
+ * @since 1.5.4
580
+ */
581
+ public function init() {
582
+ if ( ! empty( $this->user ) || ! empty( $this->pass ) || ! empty( $this->host ) ) {
583
+ return;
584
+ }
585
+
586
+ $settings = $this->core->settings->get_settings();
587
+
588
+ $labels = array( 'user', 'pass', 'host', 'port', 'type', 'retention_count', 'nickname' );
589
+
590
+ $configs = array(
591
+ array(
592
+ 'property' => 'user',
593
+ 'default' => null,
594
+ ),
595
+ array(
596
+ 'property' => 'pass',
597
+ 'default' => null,
598
+ ),
599
+ array(
600
+ 'property' => 'host',
601
+ 'default' => null,
602
+ ),
603
+ array(
604
+ 'property' => 'port',
605
+ 'default' => $this->default_port,
606
+ ),
607
+ array(
608
+ 'property' => 'type',
609
+ 'default' => $this->default_type,
610
+ ),
611
+ array(
612
+ 'property' => 'retention_count',
613
+ 'default' => $this->retention_count,
614
+ ),
615
+ array(
616
+ 'property' => 'nickname',
617
+ 'default' => $this->title,
618
+ ),
619
+ );
620
+
621
+ foreach ( $configs as $config ) {
622
+ $property = $config['property'];
623
+
624
+ if ( ! empty( $settings['remote'][ $this->key ][ $property ] ) ) {
625
+ $this->$property = $settings['remote'][ $this->key ][ $property ];
626
+ } else {
627
+ $this->$property = $config['default'];
628
+ }
629
+ }
630
+
631
+ if ( ! empty( $this->host ) ) {
632
+ $this->title_attr = strtoupper( $this->type ) . ': ' . $this->host;
633
+ }
634
+ }
635
+
636
+ /**
637
+ * Determine whether or not FTP is setup.
638
+ *
639
+ * @since 1.5.4
640
+ *
641
+ * @return bool
642
+ */
643
+ public function is_setup() {
644
+ $this->connect();
645
+ $this->log_in();
646
+
647
+ $logged_in = $this->logged_in;
648
+
649
+ $this->disconnect();
650
+
651
+ return $logged_in;
652
+ }
653
+
654
+ /**
655
+ * Determine if a set of FTP credentials are valid.
656
+ *
657
+ * @since 1.5.4
658
+ *
659
+ * @param string $host
660
+ * @param string $user
661
+ * @param string $pass
662
+ * @param int $port
663
+ * @param string $type
664
+ * @return bool
665
+ */
666
+ public function is_valid_credentials( $host, $user, $pass, $port, $type ) {
667
+ $connection = false;
668
+ $logged_in = false;
669
+ $port = intval( $port );
670
+
671
+ // Avoid a really long timeout.
672
+ if ( 21 === $port && 'sftp' === $type ) {
673
+ $this->errors[] = sprintf( __( 'Unable to connect to %1$s over port %2$u.', 'boldgrid-backup' ), $host, $port );
674
+ return false;
675
+ }
676
+
677
+ switch ( $type ) {
678
+ case 'ftp':
679
+ $connection = @ftp_connect( $host, $port, $this->timeout );
680
+ break;
681
+ case 'sftp':
682
+ $connection = @new phpseclib\Net\SFTP( $host, $port, $this->timeout );
683
+ break;
684
+ }
685
+ if ( ! $connection ) {
686
+ $this->errors[] = sprintf( __( 'Unable to connect to %1$s over port %2$u.', 'boldgrid-backup' ), $host, $port );
687
+ return false;
688
+ }
689
+
690
+ /*
691
+ * Try to login.
692
+ *
693
+ * When:
694
+ * # Connecting over bad ports (like port FTP over port 22)
695
+ * # Using invalid login credentials
696
+ * Notices are thrown instead of catachable errors. This makes it difficult
697
+ * to know if a connection failed because of of a bad port number or because
698
+ * of bad credentials.
699
+ *
700
+ * If we have any trouble connecting, we'll use a custom error handler
701
+ * and throw an Exception.
702
+ */
703
+ $error_caught = false;
704
+ set_error_handler( array( 'Boldgrid_Backup_Admin_Utility', 'handle_error' ) );
705
+ try {
706
+ switch ( $type ) {
707
+ case 'ftp':
708
+ $logged_in = ftp_login( $connection, $user, $pass );
709
+ ftp_close( $connection );
710
+ break;
711
+ case 'sftp':
712
+ $logged_in = $connection->login( $user, $pass );
713
+ break;
714
+ }
715
+ } catch ( Exception $e ) {
716
+ $this->errors[] = $e->getMessage();
717
+ $error_caught = true;
718
+ }
719
+ restore_error_handler();
720
+
721
+ if ( ! $error_caught && ! $logged_in ) {
722
+ $this->errors[] = __( 'Invalid username / password.', 'boldgrid-backup' );
723
+ }
724
+
725
+ return false !== $logged_in;
726
+ }
727
+
728
+ /**
729
+ * Log into the FTP server.
730
+ *
731
+ * @since 1.5.4
732
+ *
733
+ * @return bool
734
+ */
735
+ public function log_in() {
736
+ if ( $this->logged_in ) {
737
+ return;
738
+ }
739
+
740
+ // If we tried to connect but don't have a connection, abort.
741
+ $this->connect();
742
+ if ( empty( $this->connection ) ) {
743
+ return false;
744
+ }
745
+
746
+ switch ( $this->type ) {
747
+ case 'ftp':
748
+ $this->logged_in = @ftp_login( $this->connection, $this->user, $this->pass );
749
+ break;
750
+ case 'sftp':
751
+ $this->logged_in = $this->connection->login( $this->user, $this->pass );
752
+ break;
753
+ }
754
+
755
+ // If we tried to login and it failed, disconnect.
756
+ if ( ! $this->logged_in ) {
757
+ $this->disconnect();
758
+ }
759
+ }
760
+
761
+ /**
762
+ * Reset class properties.
763
+ *
764
+ * If the user wants to delete all FTP settings, after we clear the data from
765
+ * the options, run this method to clear the properties.
766
+ *
767
+ * @since 1.5.4
768
+ */
769
+ public function reset() {
770
+ $this->host = null;
771
+ $this->user = null;
772
+ $this->set_pass( null );
773
+ $this->port = $this->default_port['ftp'];
774
+ $this->retention_count = null;
775
+ $this->type = $this->default_type;
776
+ }
777
+
778
+ /**
779
+ * Set our ftp password.
780
+ *
781
+ * @since 1.5.4
782
+ *
783
+ * @param string $pass
784
+ */
785
+ public function set_pass( $pass ) {
786
+ $this->pass = $pass;
787
+ }
788
+
789
+ /**
790
+ * Determine if a backup archive is uploaded to the remote server.
791
+ *
792
+ * @since 1.5.4
793
+ *
794
+ * @param string $filepath
795
+ */
796
+ public function is_uploaded( $filepath ) {
797
+ $contents = $this->get_contents( false, $this->remote_dir );
798
+
799
+ return ! is_array( $contents ) ? false : in_array( basename( $filepath ), $contents, true );
800
+ }
801
+
802
+ /**
803
+ * Upload a file.
804
+ *
805
+ * @since 1.5.4
806
+ *
807
+ * @param string $filepath
808
+ * @return bool
809
+ */
810
+ public function upload( $filepath ) {
811
+ $remote_file = $this->remote_dir . '/' . basename( $filepath );
812
+
813
+ $this->connect();
814
+ $this->log_in();
815
+ if ( ! $this->logged_in ) {
816
+ $this->errors[] = __( 'Unable to log in to ftp server.', 'boldgrid-backup' );
817
+ return false;
818
+ }
819
+
820
+ $has_remote_dir = $this->create_backup_dir();
821
+ if ( ! $has_remote_dir ) {
822
+ return false;
823
+ }
824
+
825
+ switch ( $this->type ) {
826
+ case 'ftp':
827
+ $uploaded = ftp_put( $this->connection, $remote_file, $filepath, FTP_BINARY );
828
+ break;
829
+ case 'sftp':
830
+ $uploaded = $this->connection->put( $remote_file, $filepath, 1 );
831
+ break;
832
+ }
833
+
834
+ if ( ! $uploaded ) {
835
+ $last_error = error_get_last();
836
+
837
+ $this->disconnect();
838
+ $this->errors[] = __( 'Unable to upload file.', 'boldgrid-backup' );
839
+
840
+ /*
841
+ * The last error message may be important on a failed uploaded,
842
+ * such as ftp_put(): Quota exceeded. Make sure the user sees the
843
+ * last error.
844
+ */
845
+ if ( ! empty( $last_error['message'] ) && ! empty( $last_error['file'] ) && $last_error['file'] === __FILE__ ) {
846
+ $this->errors[] = $last_error['message'];
847
+ }
848
+
849
+ return false;
850
+ }
851
+
852
+ $this->enforce_retention();
853
+
854
+ $this->disconnect();
855
+
856
+ return true;
857
+ }
858
+ }
admin/remote/sftp.php ADDED
@@ -0,0 +1,553 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * SFTP class.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.4
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * SFTP class.
17
+ *
18
+ * @since 1.5.4
19
+ */
20
+ class Boldgrid_Backup_Admin_Sftp {
21
+
22
+ /**
23
+ * An SFTP connection.
24
+ *
25
+ * @since 1.5.4
26
+ * @access private
27
+ * @var Resource
28
+ */
29
+ private $connection = null;
30
+
31
+ /**
32
+ * The core class object.
33
+ *
34
+ * @since 1.5.4
35
+ * @access private
36
+ * @var Boldgrid_Backup_Admin_Core
37
+ */
38
+ private $core;
39
+
40
+ /**
41
+ * Hooks class.
42
+ *
43
+ * @since 1.5.4
44
+ * @access public
45
+ * @var Boldgrid_Backup_Admin_Sftp_Hooks
46
+ */
47
+ public $hooks;
48
+
49
+ /**
50
+ * SFTP host.
51
+ *
52
+ * @since 1.5.4
53
+ * @access private
54
+ * @var string
55
+ */
56
+ private $host = null;
57
+
58
+ /**
59
+ * Whether or not we have logged in.
60
+ *
61
+ * @since 1.5.4
62
+ * @access public
63
+ * @var bool
64
+ */
65
+ public $logged_in = false;
66
+
67
+ /**
68
+ * Our key / label for sftp.
69
+ *
70
+ * @since 1.5.4
71
+ * @access public
72
+ * @var string
73
+ */
74
+ public $key = 'sftp';
75
+
76
+ /**
77
+ * SFTP password.
78
+ *
79
+ * @since 1.5.4
80
+ * @access private
81
+ * @var string
82
+ */
83
+ private $pass = null;
84
+
85
+ /**
86
+ * SFTP port.
87
+ *
88
+ * @since 1.5.4
89
+ * @access public
90
+ * @var int
91
+ */
92
+ public $port = 22;
93
+
94
+ /**
95
+ * SFTP remote directory.
96
+ *
97
+ * @since 1.5.4
98
+ * @access public
99
+ * @var string
100
+ */
101
+ public $remote_dir = 'boldgrid_backup';
102
+
103
+ /**
104
+ * Retention count.
105
+ *
106
+ * @since 1.5.4
107
+ * @access public
108
+ * @var int $retention_count
109
+ */
110
+ public $retention_count = 5;
111
+
112
+ /**
113
+ * Our title / label for sftp.
114
+ *
115
+ * @since 1.5.4
116
+ * @access public
117
+ * @var string
118
+ */
119
+ public $title = 'SFTP';
120
+
121
+ /**
122
+ * SFTP username.
123
+ *
124
+ * @since 1.5.4
125
+ * @access private
126
+ * @var string
127
+ */
128
+ private $user = null;
129
+
130
+ /**
131
+ * Constructor.
132
+ *
133
+ * @since 1.5.4
134
+ *
135
+ * @param Boldgrid_Backup_Admin_Core $core Core class object.
136
+ */
137
+ public function __construct( $core ) {
138
+ include_once BOLDGRID_BACKUP_PATH . '/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php';
139
+
140
+ $this->core = $core;
141
+ // $this->hooks = new Boldgrid_Backup_Admin_Sftp_Hooks( $core );
142
+ }
143
+
144
+ /**
145
+ * Connect to our sftp server.
146
+ *
147
+ * @since 1.5.4
148
+ */
149
+ public function connect() {
150
+ if ( ! empty( $this->connection ) ) {
151
+ return;
152
+ }
153
+
154
+ $this->init();
155
+
156
+ if ( empty( $this->user ) || empty( $this->pass ) || empty( $this->host ) ) {
157
+ return;
158
+ }
159
+
160
+ $this->connection = new phpseclib\Net\SFTP( $this->host, $this->port );
161
+ }
162
+
163
+ /**
164
+ * Create backup directory on remote host.
165
+ *
166
+ * @since 1.5.4
167
+ *
168
+ * @return bool False when we were unable to create directory.
169
+ */
170
+ public function create_backup_dir() {
171
+ $this->connect();
172
+ $this->log_in();
173
+ if ( ! $this->logged_in ) {
174
+ return false;
175
+ }
176
+
177
+ $contents = $this->get_contents();
178
+ if ( in_array( $this->remote_dir, $contents, true ) ) {
179
+ return true;
180
+ }
181
+
182
+ return $this->connection->mkdir( $this->remote_dir );
183
+ }
184
+
185
+ /**
186
+ * Disconnect from SFTP server.
187
+ *
188
+ * @since 1.5.4
189
+ */
190
+ public function disconnect() {
191
+ if ( $this->connection ) {
192
+ // sftp_close( $this->connection );
193
+ // $this->connection = null;
194
+ // $this->logged_in = false;
195
+ }
196
+ }
197
+
198
+ /**
199
+ * Enforce retention.
200
+ *
201
+ * @since 1.5.4
202
+ */
203
+ public function enforce_retention() {
204
+ if ( empty( $this->retention_count ) ) {
205
+ return;
206
+ }
207
+
208
+ $backups = array();
209
+ $contents = $this->get_contents( true, $this->remote_dir );
210
+
211
+ // The contents usually include . and .., so remove 2 from list.
212
+ $count_to_delete = count( $contents ) - $this->retention_count - 2;
213
+
214
+ if ( ! is_array( $contents ) || $count_to_delete <= 0 ) {
215
+ return false;
216
+ }
217
+
218
+ $backups = $this->format_raw_contents( $contents );
219
+
220
+ usort( $backups, function( $a, $b ) {
221
+ return $a['time'] < $b['time'] ? -1 : 1;
222
+ });
223
+
224
+ for ( $x = 0; $x < $count_to_delete; $x++ ) {
225
+ $filename = $backups[ $x ]['filename'];
226
+ $this->connection->delete( $this->remote_dir . '/' . $filename );
227
+
228
+ /**
229
+ * Remote file deleted due to remote retention settings.
230
+ *
231
+ * @since 1.5.4
232
+ */
233
+ do_action(
234
+ 'boldgrid_backup_remote_retention_deleted',
235
+ $this->title,
236
+ $filename
237
+ );
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Format raw contents.
243
+ *
244
+ * This method takes in raw contents and returns an array of backups, with
245
+ * keys defining timestamp and filename.
246
+ *
247
+ * @since 1.5.4
248
+ *
249
+ * @param array $conents Raw contents received from this->get_contents.
250
+ * @return array {
251
+ * An array of backups.
252
+ *
253
+ * @type int $time Timestamp file was uploaded to sftp server.
254
+ * @type string $filename
255
+ * }
256
+ */
257
+ public function format_raw_contents( $contents ) {
258
+ $skips = array( '.', '..' );
259
+ $backups = array();
260
+
261
+ if ( ! is_array( $contents ) ) {
262
+ return array();
263
+ }
264
+
265
+ foreach ( $contents as $item ) {
266
+ $exploded_item = explode( ' ', $item );
267
+
268
+ $count = count( $exploded_item );
269
+
270
+ $filename = $exploded_item[ $count - 1 ];
271
+
272
+ if ( in_array( $filename, $skips, true ) ) {
273
+ continue;
274
+ }
275
+
276
+ // Get the timestamp.
277
+ $month = $exploded_item[ $count - 4 ];
278
+ $day = $exploded_item[ $count - 3 ];
279
+ $time = $exploded_item[ $count - 2 ];
280
+ $time = strtotime( $month . ' ' . $day . ' ' . $time );
281
+
282
+ $backups[] = array(
283
+ 'time' => $time,
284
+ 'filename' => $filename,
285
+ );
286
+ }
287
+
288
+ return $backups;
289
+ }
290
+
291
+ /**
292
+ * Get the remote contents / listing.
293
+ *
294
+ * @since 1.5.4
295
+ *
296
+ * @param bool $raw Whether to get the raw contents (sftp_rawlist) or not
297
+ * (sftp_nlist).
298
+ * @param string $dir The directory to get listing of.
299
+ * @return mixed
300
+ */
301
+ public function get_contents( $raw = false, $dir = '.' ) {
302
+ $this->connect();
303
+ $this->log_in();
304
+ if ( ! $this->logged_in ) {
305
+ return array();
306
+ }
307
+
308
+ if ( $raw ) {
309
+ return $this->connection->rawlist( $dir );
310
+ } else {
311
+ return $this->connection->nlist( $dir );
312
+ }
313
+ }
314
+
315
+ /**
316
+ * Get settings.
317
+ *
318
+ * @since 1.5.4
319
+ */
320
+ public function get_details() {
321
+ $settings = $this->core->settings->get_settings();
322
+
323
+ return array(
324
+ 'title' => $this->title,
325
+ 'key' => $this->key,
326
+ 'configure' => 'admin.php?page=boldgrid-backup-sftp',
327
+ 'is_setup' => $this->is_setup(),
328
+ 'enabled' => ! empty( $settings['remote'][ $this->key ]['enabled'] ) && $settings['remote'][ $this->key ]['enabled'] && $this->is_setup(),
329
+ );
330
+ }
331
+
332
+ /**
333
+ * Init properties.
334
+ *
335
+ * @since 1.5.4
336
+ */
337
+ public function init() {
338
+ if ( ! empty( $this->user ) || ! empty( $this->pass ) || ! empty( $this->host ) ) {
339
+ return;
340
+ }
341
+
342
+ $settings = $this->core->settings->get_settings();
343
+
344
+ $labels = array( 'user', 'pass', 'host', 'port', 'retention_count' );
345
+
346
+ foreach ( $labels as $label ) {
347
+ $this->$label = ! empty( $settings['remote'][ $this->key ][ $label ] ) ? $settings['remote'][ $this->key ][ $label ] : null;
348
+ }
349
+ }
350
+
351
+ /**
352
+ * Determine whether or not SFTP is setup.
353
+ *
354
+ * @since 1.5.4
355
+ *
356
+ * @return bool
357
+ */
358
+ public function is_setup() {
359
+ $this->connect();
360
+ $this->log_in();
361
+
362
+ return $this->logged_in;
363
+ }
364
+
365
+ /**
366
+ * Determine if a set of SFTP credentials are valid.
367
+ *
368
+ * @since 1.5.4
369
+ *
370
+ * @param string $host
371
+ * @param string $user
372
+ * @param string $pass
373
+ * @param int $port
374
+ * @return bool
375
+ */
376
+ public function is_valid_credentials( $host, $user, $pass, $port ) {
377
+ $connection = new phpseclib\Net\SFTP( $host, $port );
378
+ if ( ! $connection ) {
379
+ return false;
380
+ }
381
+
382
+ $logged_in = @$connection->login( $user, $pass );
383
+ if ( ! $logged_in ) {
384
+ // sftp_close( $connection );
385
+ $this->errors[] = __( 'Unable to connect and log in.', 'boldgrid-backup' );
386
+ return false;
387
+ }
388
+
389
+ // sftp_close( $connection );
390
+ return true;
391
+ }
392
+
393
+ /**
394
+ * Log into the SFTP server.
395
+ *
396
+ * @since 1.5.4
397
+ *
398
+ * @return bool
399
+ */
400
+ public function log_in() {
401
+ if ( $this->logged_in ) {
402
+ return;
403
+ }
404
+
405
+ $this->connect();
406
+ if ( empty( $this->connection ) ) {
407
+ return false;
408
+ }
409
+
410
+ $this->logged_in = $this->connection->login( $this->user, $this->pass );
411
+ if ( ! $this->logged_in ) {
412
+ $this->disconnect();
413
+ }
414
+ }
415
+
416
+ /**
417
+ *
418
+ */
419
+ public function is_uploaded( $filepath ) {
420
+ $contents = $this->get_contents( false, $this->remote_dir );
421
+
422
+ return ! is_array( $contents ) ? false : in_array( basename( $filepath ), $contents, true );
423
+ }
424
+
425
+ /**
426
+ * Generate the submenu page for our SFTP Settings page.
427
+ *
428
+ * @since 1.5.4
429
+ */
430
+ public function submenu_page() {
431
+ wp_enqueue_style( 'boldgrid-backup-admin-hide-all' );
432
+
433
+ $this->submenu_page_save();
434
+
435
+ $settings = $this->core->settings->get_settings();
436
+
437
+ $host = ! empty( $settings['remote'][ $this->key ]['host'] ) ? $settings['remote'][ $this->key ]['host'] : null;
438
+ $user = ! empty( $settings['remote'][ $this->key ]['user'] ) ? $settings['remote'][ $this->key ]['user'] : null;
439
+ $pass = ! empty( $settings['remote'][ $this->key ]['pass'] ) ? $settings['remote'][ $this->key ]['pass'] : null;
440
+ $port = ! empty( $settings['remote'][ $this->key ]['port'] ) ? $settings['remote'][ $this->key ]['port'] : $this->port;
441
+ $retention_count = ! empty( $settings['remote'][ $this->key ]['retention_count'] ) ? $settings['remote'][ $this->key ]['retention_count'] : $this->retention_count;
442
+
443
+ include BOLDGRID_BACKUP_PATH . '/admin/partials/remote/sftp.php';
444
+ }
445
+
446
+ /**
447
+ * Process the user's request to update their SFTP settings.
448
+ *
449
+ * @since 1.5.4
450
+ */
451
+ public function submenu_page_save() {
452
+ if ( ! current_user_can( 'update_plugins' ) ) {
453
+ return false;
454
+ }
455
+
456
+ if ( empty( $_POST ) ) {
457
+ return false;
458
+ }
459
+
460
+ $settings = $this->core->settings->get_settings();
461
+ if ( ! isset( $settings['remote'][ $this->key ] ) || ! is_array( $settings['remote'][ $this->key ] ) ) {
462
+ $settings['remote'][ $this->key ] = array();
463
+ }
464
+
465
+ /*
466
+ * If the user has requested to delete all their settings, do that now
467
+ * and return.
468
+ */
469
+ if ( __( 'Delete settings', 'boldgrid-backup' ) === $_POST['submit'] ) {
470
+ $settings['remote'][ $this->key ] = array();
471
+ update_site_option( 'boldgrid_backup_settings', $settings );
472
+
473
+ $this->host = null;
474
+ $this->user = null;
475
+ $this->pass = null;
476
+ $this->retention_count = null;
477
+ $this->disconnect();
478
+
479
+ do_action( 'boldgrid_backup_notice', __( 'Settings saved.', 'boldgrid-backup' ), 'notice updated is-dismissible' );
480
+ return;
481
+ }
482
+
483
+ $errors = array();
484
+
485
+ // Get and validate our credentials.
486
+ $host = ! empty( $_POST['host'] ) ? sanitize_file_name( $_POST['host'] ) : null;
487
+ $user = ! empty( $_POST['user'] ) ? sanitize_text_field( $_POST['user'] ) : null;
488
+ $pass = ! empty( $_POST['pass'] ) ? $_POST['pass'] : null;
489
+ $port = ! empty( $_POST['port'] ) ? (int) $_POST['port'] : $this->port;
490
+
491
+ $valid_credentials = $this->is_valid_credentials( $host, $user, $pass, $port );
492
+
493
+ if ( $valid_credentials ) {
494
+ $settings['remote'][ $this->key ]['host'] = $host;
495
+ $settings['remote'][ $this->key ]['user'] = $user;
496
+ $settings['remote'][ $this->key ]['pass'] = $pass;
497
+ $settings['remote'][ $this->key ]['port'] = $port;
498
+ } elseif ( empty( $this->errors ) ) {
499
+ $this->errors[] = __( 'Unknown error.', 'boldgrid-backup' );
500
+ }
501
+
502
+ $retention_count = ! empty( $_POST['retention_count'] ) &&
503
+ is_numeric( $_POST['retention_count'] ) ?
504
+ (int) $_POST['retention_count'] : $this->retention_count;
505
+
506
+ $settings['remote'][ $this->key ]['retention_count'] = $retention_count;
507
+
508
+ if ( ! empty( $this->errors ) ) {
509
+ do_action( 'boldgrid_backup_notice', implode( '<br /><br />', $this->errors ) );
510
+ } else {
511
+ update_site_option( 'boldgrid_backup_settings', $settings );
512
+ do_action( 'boldgrid_backup_notice', __( 'Settings saved.', 'boldgrid-backup' ), 'notice updated is-dismissible' );
513
+ }
514
+ }
515
+
516
+ /**
517
+ * Upload a file.
518
+ *
519
+ * @since 1.5.4
520
+ *
521
+ * @param string $filepath
522
+ * @return bool
523
+ */
524
+ public function upload( $filepath ) {
525
+ $remote_file = $this->remote_dir . '/' . basename( $filepath );
526
+
527
+ $this->connect();
528
+ $this->log_in();
529
+ if ( ! $this->logged_in ) {
530
+ $this->errors[] = __( 'Unable to log in to sftp server.', 'boldgrid-backup' );
531
+ return false;
532
+ }
533
+
534
+ $has_remote_dir = $this->create_backup_dir();
535
+ if ( ! $has_remote_dir ) {
536
+ $this->errors[] = sprint_f( __( 'Unable to create the following directory on SFTP server: %1$s', 'boldgrid-backup' ), $this->remote_dir );
537
+ return false;
538
+ }
539
+
540
+ $uploaded = $this->connection->put( $remote_file, $filepath, NET_SFTP_LOCAL_FILE );
541
+ if ( ! $uploaded ) {
542
+ $this->disconnect();
543
+ $this->errors[] = __( 'Unable to upload file.', 'boldgrid-backup' );
544
+ return false;
545
+ }
546
+
547
+ $this->enforce_retention();
548
+
549
+ $this->disconnect();
550
+
551
+ return true;
552
+ }
553
+ }
admin/storage/local.php ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Local storage.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.2
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid.com
11
+ * @version $Id$
12
+ * @author BoldGrid.com <wpb@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * Local storage.
17
+ *
18
+ * @since 1.5.2
19
+ */
20
+ class Boldgrid_Backup_Admin_Storage_Local {
21
+
22
+ /**
23
+ * The core class object.
24
+ *
25
+ * @since 1.5.2
26
+ * @access private
27
+ * @var Boldgrid_Backup_Admin_Core
28
+ */
29
+ private $core;
30
+
31
+ /**
32
+ * Constructor.
33
+ *
34
+ * @since 1.5.2
35
+ *
36
+ * @param Boldgrid_Backup_Admin_Core $core
37
+ */
38
+ public function __construct( $core ) {
39
+ $this->core = $core;
40
+ }
41
+
42
+ /**
43
+ * Delete a local backup file.
44
+ *
45
+ * This method is registered to the "boldgrid_backup_delete_local" action.
46
+ * If the user does not wish to keep local copies of backups, after all
47
+ * remote backup providers have been run, this method will run and delete
48
+ * it locally.
49
+ *
50
+ * @since 1.5.2
51
+ *
52
+ * @param string $filepath Full path to backup file.
53
+ */
54
+ public function delete_local( $filepath ) {
55
+ return $this->core->wp_filesystem->delete( $filepath );
56
+ }
57
+
58
+ /**
59
+ * Action to take after a backup file has been created.
60
+ *
61
+ * If the user has not chosen to keep local copies, this method adds the
62
+ * "delete local copy" to the jobs queue.
63
+ *
64
+ * @since 1.5.2
65
+ *
66
+ * @see self::delete_local()
67
+ *
68
+ * @param array $info
69
+ */
70
+ public function post_archive_files( $info ) {
71
+
72
+ /*
73
+ * Do not "delete local copy" in the following scenarios:
74
+ *
75
+ * We only want to add this to the jobs queue if we're in the middle of
76
+ * an automatic backup. If the user simply clicked on "Backup site now",
77
+ * we don't want to automatically delete the backup, there's a button
78
+ * for that.
79
+ *
80
+ * If we're doing a backup immediately before WordPress does an auto
81
+ * update, we want to make sure this backup is not deleted.
82
+ */
83
+ if ( ! $this->core->doing_cron || $this->core->pre_auto_update ) {
84
+ return;
85
+ }
86
+
87
+ if ( $this->core->remote->is_enabled( 'local' ) ) {
88
+ return;
89
+ }
90
+
91
+ $args = array(
92
+ 'filepath' => $info['filepath'],
93
+ 'action' => 'boldgrid_backup_delete_local',
94
+ 'action_data' => $info['filepath'],
95
+ 'action_title' => __( 'Delete backup from Web Server', 'boldgrid-backup' ),
96
+ );
97
+
98
+ $this->core->jobs->add( $args );
99
+ }
100
+ }
boldgrid-backup-cron.php ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Backup cron control script
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.0
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @copyright BoldGrid.com
10
+ * @version $Id$
11
+ * @author BoldGrid.com <wpb@boldgrid.com>
12
+ */
13
+
14
+ // Abort if not being ran from the command line.
15
+ if ( ! isset( $_SERVER['argv'], $_SERVER['argc'] ) || ! $_SERVER['argc'] ) {
16
+ die( 'Error: No parameters were passed. A "siteurl", "mode", and "id" are required.' . PHP_EOL );
17
+ }
18
+
19
+ // Initialize $input and $error.
20
+ $input = null;
21
+ $error = '';
22
+
23
+ /**
24
+ * Parse input variables into an array.
25
+ * Expected parameter: "siteurl"
26
+ */
27
+ parse_str( implode( '&', array_slice( $argv, 1 ) ), $input );
28
+
29
+ $required_arguments = array(
30
+ 'siteurl',
31
+ 'id',
32
+ 'secret',
33
+ 'mode',
34
+ );
35
+
36
+ // Abort if arguments are not passed.
37
+ foreach ( $required_arguments as $required_argument ) {
38
+ if ( empty( $input[ $required_argument ] ) ) {
39
+ $error .= 'Error: "' . $required_argument . '" was not specified.' . PHP_EOL;
40
+ }
41
+ }
42
+
43
+ if ( $error ) {
44
+ die( $error );
45
+ }
46
+
47
+ // Abort if not a valid mode.
48
+ $valid_modes = array(
49
+ 'backup',
50
+ 'restore',
51
+ );
52
+
53
+ if ( ! in_array( $input['mode'], $valid_modes, true ) ) {
54
+ die( 'Error: Invalid mode "' . $input['mode'] . '".' . PHP_EOL );
55
+ }
56
+
57
+ // Make an ajax call to run jobs, and report status.
58
+ $url = $input['siteurl'] . '/wp-admin/admin-ajax.php?action=boldgrid_backup_run_' . $input['mode'] .
59
+ '&id=' . $input['id'] . '&secret=' . $input['secret'] . '&doing_wp_cron=' . time();
60
+
61
+ // Sanitize the url.
62
+ $url = filter_var( $url, FILTER_SANITIZE_URL );
63
+
64
+ $result = file_get_contents( $url );
65
+
66
+ if ( false !== $result ) {
67
+ $message = $result;
68
+ } else {
69
+ $message = 'Error: Could not reach URL address "' . $url . '".';
70
+ }
71
+
72
+ die( $message );
boldgrid-backup.php ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The plugin bootstrap file
4
+ *
5
+ * This file is read by WordPress to generate the plugin information in the plugin
6
+ * admin area. This file also includes all of the dependencies used by the plugin,
7
+ * registers the activation and deactivation functions, and defines a function
8
+ * that starts the plugin.
9
+ *
10
+ * @link http://www.boldgrid.com
11
+ * @since 1.0.1
12
+ * @package Boldgrid_Backup
13
+ *
14
+ * @wordpress-plugin
15
+ * Plugin Name: BoldGrid Backup
16
+ * Plugin URI: https://www.boldgrid.com/boldgrid-backup/
17
+ * Description: BoldGrid Backup provides WordPress backup and restoration with update protection.
18
+ * Version: 1.6.1
19
+ * Author: BoldGrid
20
+ * Author URI: https://www.boldgrid.com/
21
+ * License: GPL-2.0+
22
+ * License URI: http://www.gnu.org/licenses/gpl-2.0.txt
23
+ * Text Domain: boldgrid-backup
24
+ * Domain Path: /languages
25
+ */
26
+
27
+ // If this file is called directly, abort.
28
+ if ( ! defined( 'WPINC' ) ) {
29
+ die();
30
+ }
31
+
32
+ // Define version.
33
+ if ( ! defined( 'BOLDGRID_BACKUP_VERSION' ) ) {
34
+ define( 'BOLDGRID_BACKUP_VERSION', implode( get_file_data( __FILE__, array( 'Version' ), 'plugin' ) ) );
35
+ }
36
+
37
+ // Define boldgrid-backup path.
38
+ if ( ! defined( 'BOLDGRID_BACKUP_PATH' ) ) {
39
+ define( 'BOLDGRID_BACKUP_PATH', dirname( __FILE__ ) );
40
+ }
41
+
42
+ /**
43
+ * The code that runs during plugin activation.
44
+ * This action is documented in includes/class-boldgrid-backup-activator.php
45
+ */
46
+ function activate_boldgrid_backup() {
47
+ require_once BOLDGRID_BACKUP_PATH . '/includes/class-boldgrid-backup-activator.php';
48
+ Boldgrid_Backup_Activator::activate();
49
+ }
50
+
51
+ /**
52
+ * The code that runs during plugin deactivation.
53
+ * This action is documented in includes/class-boldgrid-backup-deactivator.php
54
+ */
55
+ function deactivate_boldgrid_backup() {
56
+ require_once BOLDGRID_BACKUP_PATH . '/includes/class-boldgrid-backup-deactivator.php';
57
+ Boldgrid_Backup_Deactivator::deactivate();
58
+ }
59
+
60
+ /**
61
+ * Begins execution of the plugin.
62
+ *
63
+ * Since everything within the plugin is registered via hooks,
64
+ * then kicking off the plugin from this point in the file does
65
+ * not affect the page life cycle.
66
+ *
67
+ * @since 1.0
68
+ */
69
+ function run_boldgrid_backup() {
70
+ $plugin = new Boldgrid_Backup();
71
+ $plugin->run();
72
+ }
73
+
74
+ /**
75
+ * Load BoldGrid Backup.
76
+ *
77
+ * Before loading, ensure system meets minimium requirements:
78
+ * # vendor folder exists. This is not a system requirement, but we want to make
79
+ * sure the user is NOT running a dev version with a missing vendor folder.
80
+ *
81
+ * @since 1.6.0
82
+ *
83
+ * @return bool
84
+ */
85
+ function load_boldgrid_backup() {
86
+
87
+ // Ensure we have our vendor/autoload.php file.
88
+ $exists_composer = file_exists( BOLDGRID_BACKUP_PATH . '/composer.json' );
89
+ $exists_autoload = file_exists( BOLDGRID_BACKUP_PATH . '/vendor/autoload.php' );
90
+ if ( $exists_composer && ! $exists_autoload ) {
91
+ add_action( 'admin_init', function() {
92
+ deactivate_plugins( 'boldgrid-backup/boldgrid-backup.php', true );
93
+
94
+ add_action( 'admin_notices', function() {
95
+ ?>
96
+ <div class="notice notice-error is-dismissible">
97
+ <p><?php _e( '<strong>BoldGrid Backup</strong> has been deactivated because the vendor folder is missing. Please run <strong>composer install</strong>, or contact your host for further assistance.', 'boldgrid-backup' ); ?></p>
98
+ </div>
99
+ <?php
100
+ });
101
+ });
102
+
103
+ return false;
104
+ }
105
+
106
+ register_activation_hook( __FILE__, 'activate_boldgrid_backup' );
107
+ register_deactivation_hook( __FILE__, 'deactivate_boldgrid_backup' );
108
+
109
+ // Include the autoloader to set plugin options and create instance.
110
+ $loader = require plugin_dir_path( __FILE__ ) . 'vendor/autoload.php';
111
+
112
+ // Load Library.
113
+ $load = new Boldgrid\Library\Util\Load(
114
+ array(
115
+ 'type' => 'plugin',
116
+ 'file' => plugin_basename( __FILE__ ),
117
+ 'loader' => $loader,
118
+ 'keyValidate' => true,
119
+ 'licenseActivate', false,
120
+ )
121
+ );
122
+
123
+ return true;
124
+ }
125
+
126
+ /*
127
+ * Load the plugin.
128
+ *
129
+ * Above is only:
130
+ * # function declarations
131
+ * # constant declarations
132
+ *
133
+ * The initial loading of this plugin is done below.
134
+ *
135
+ * Run the plugin only if on a wp-admin page or when DOING_CRON.
136
+ */
137
+ if ( is_admin() || ( defined( 'DOING_CRON' ) && DOING_CRON ) || defined( 'WP_CLI' ) && WP_CLI ) {
138
+
139
+ // If we could not load boldgrid_backup (missing system requirements), abort.
140
+ if ( ! load_boldgrid_backup() ) {
141
+ return;
142
+ }
143
+
144
+ require_once BOLDGRID_BACKUP_PATH . '/includes/class-boldgrid-backup.php';
145
+ run_boldgrid_backup();
146
+ }
cron/run_jobs.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Backup Run Jobs.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.5.2
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @copyright BoldGrid.com
10
+ * @version $Id$
11
+ * @author BoldGrid.com <wpb@boldgrid.com>
12
+ */
13
+
14
+ // Abort if not being ran from the command line.
15
+ if ( ! isset( $_SERVER['argv'], $_SERVER['argc'] ) || ! $_SERVER['argc'] ) { // WPCS: input var ok; sanitization ok.
16
+ die( 'Error: No parameters were passed. A "siteurl" and "id" are required.' . PHP_EOL ); // WPCS: XSS ok.
17
+ }
18
+
19
+ // Initialize $input and $error.
20
+ $input = null;
21
+ $error = '';
22
+
23
+ /**
24
+ * Parse input variables into an array.
25
+ * Expected parameter: "siteurl"
26
+ */
27
+ parse_str( implode( '&', array_slice( $argv, 1 ) ), $input );
28
+
29
+ $required_arguments = array(
30
+ 'siteurl',
31
+ 'id',
32
+ 'secret',
33
+ );
34
+
35
+ // Abort if arguments are not passed.
36
+ foreach ( $required_arguments as $required_argument ) {
37
+ if ( empty( $input[ $required_argument ] ) ) {
38
+ $error .= 'Error: "' . $required_argument . '" was not specified.' . PHP_EOL;
39
+ }
40
+ }
41
+
42
+ if ( $error ) {
43
+ die( $error ); // WPCS: XSS ok.
44
+ }
45
+
46
+ // Make an ajax call to run jobs, and report status.
47
+ $url = $input['siteurl'] . '/wp-admin/admin-ajax.php?action=boldgrid_backup_run_jobs&id=' .
48
+ $input['id'] . '&secret=' . $input['secret'] . '&doing_wp_cron=' . time();
49
+
50
+ // Sanitize the url.
51
+ $url = filter_var( $url, FILTER_SANITIZE_URL );
52
+
53
+ $result = file_get_contents( $url );
54
+
55
+ if ( false !== $result ) {
56
+ $message = $result;
57
+ } else {
58
+ $message = 'Error: Could not reach URL address "' . $url . '".';
59
+ }
60
+
61
+ die( $message ); // WPCS: XSS ok.
includes/class-boldgrid-backup-activator.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Fired during plugin activation
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.0
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/includes
10
+ */
11
+
12
+ /**
13
+ * Fired during plugin activation.
14
+ *
15
+ * This class defines all code necessary to run during the plugin's activation.
16
+ *
17
+ * @since 1.0
18
+ * @package Boldgrid_Backup
19
+ * @subpackage Boldgrid_Backup/includes
20
+ * @author BoldGrid.com <wpb@boldgrid.com>
21
+ */
22
+ class Boldgrid_Backup_Activator {
23
+
24
+ /**
25
+ * Plugin activation.
26
+ *
27
+ * @since 1.0
28
+ */
29
+ public static function activate() {
30
+ $core = new Boldgrid_Backup_Admin_Core();
31
+ $settings = $core->settings->get_settings();
32
+ $scheduler = ! empty( $settings['scheduler'] ) ? $settings['scheduler'] : null;
33
+
34
+ /*
35
+ * Add all previous crons.
36
+ *
37
+ * The add_all_crons methods called include proper checks to ensure
38
+ * scheduler is available and $settings include a schedule.
39
+ */
40
+ if ( 'cron' === $scheduler ) {
41
+ $core->cron->add_all_crons( $settings );
42
+ } elseif ( 'wp-cron' === $scheduler ) {
43
+ $core->wp_cron->add_all_crons( $settings );
44
+ }
45
+ }
46
+ }
includes/class-boldgrid-backup-deactivator.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Fired during plugin deactivation
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.0
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/includes
10
+ */
11
+
12
+ /**
13
+ * Fired during plugin deactivation.
14
+ *
15
+ * This class defines all code necessary to run during the plugin's deactivation.
16
+ *
17
+ * @since 1.0
18
+ * @package Boldgrid_Backup
19
+ * @subpackage Boldgrid_Backup/includes
20
+ * @author BoldGrid.com <wpb@boldgrid.com>
21
+ */
22
+ class Boldgrid_Backup_Deactivator {
23
+
24
+ /**
25
+ * Plugin deactivation.
26
+ *
27
+ * @since 1.0
28
+ */
29
+ public static function deactivate() {
30
+ $core = new Boldgrid_Backup_Admin_Core();
31
+
32
+ $core->cron->delete_cron_entries( true );
33
+ $core->wp_cron->clear_schedules();
34
+ }
35
+ }
includes/class-boldgrid-backup-i18n.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Define the internationalization functionality
4
+ *
5
+ * Loads and defines the internationalization files for this plugin
6
+ * so that it is ready for translation.
7
+ *
8
+ * @link http://www.boldgrid.com
9
+ * @since 1.0
10
+ *
11
+ * @package Boldgrid_Backup
12
+ * @subpackage Boldgrid_Backup/includes
13
+ */
14
+
15
+ /**
16
+ * Define the internationalization functionality.
17
+ *
18
+ * Loads and defines the internationalization files for this plugin
19
+ * so that it is ready for translation.
20
+ *
21
+ * @since 1.0
22
+ * @package Boldgrid_Backup
23
+ * @subpackage Boldgrid_Backup/includes
24
+ * @author BoldGrid.com <wpb@boldgrid.com>
25
+ */
26
+ class Boldgrid_Backup_i18n {
27
+
28
+ /**
29
+ * Load the plugin text domain for translation.
30
+ *
31
+ * @since 1.0
32
+ */
33
+ public function load_plugin_textdomain() {
34
+ load_plugin_textdomain( 'boldgrid-backup', false,
35
+ dirname( dirname( plugin_basename( __FILE__ ) ) ) . '/languages/' );
36
+ }
37
+ }
includes/class-boldgrid-backup-loader.php ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Register all actions and filters for the plugin
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.0
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/includes
10
+ */
11
+
12
+ /**
13
+ * Register all actions and filters for the plugin.
14
+ *
15
+ * Maintain a list of all hooks that are registered throughout
16
+ * the plugin, and register them with the WordPress API. Call the
17
+ * run function to execute the list of actions and filters.
18
+ *
19
+ * @package Boldgrid_Backup
20
+ * @subpackage Boldgrid_Backup/includes
21
+ * @author BoldGrid.com <wpb@boldgrid.com>
22
+ */
23
+ class Boldgrid_Backup_Loader {
24
+
25
+ /**
26
+ * The array of actions registered with WordPress.
27
+ *
28
+ * @since 1.0
29
+ * @access protected
30
+ * @var array $actions The actions registered with WordPress to fire when the plugin loads.
31
+ */
32
+ protected $actions;
33
+
34
+ /**
35
+ * The array of filters registered with WordPress.
36
+ *
37
+ * @since 1.0
38
+ * @access protected
39
+ * @var array $filters The filters registered with WordPress to fire when the plugin loads.
40
+ */
41
+ protected $filters;
42
+
43
+ /**
44
+ * Initialize the collections used to maintain the actions and filters.
45
+ *
46
+ * @since 1.0
47
+ */
48
+ public function __construct() {
49
+ $this->actions = array();
50
+ $this->filters = array();
51
+ }
52
+
53
+ /**
54
+ * Add a new action to the collection to be registered with WordPress.
55
+ *
56
+ * @since 1.0
57
+ *
58
+ * @param string $hook The name of the WordPress action that is being registered.
59
+ * @param object $component A reference to the instance of the object on which the action is defined.
60
+ * @param string $callback The name of the function definition on the $component.
61
+ * @param int $priority Optional. he priority at which the function should be fired. Default is 10.
62
+ * @param int $accepted_args Optional. The number of arguments that should be passed to the $callback. Default is 1.
63
+ */
64
+ public function add_action( $hook, $component, $callback, $priority = 10, $accepted_args = 1 ) {
65
+ $this->actions = $this->add( $this->actions, $hook, $component, $callback, $priority,
66
+ $accepted_args );
67
+ }
68
+
69
+ /**
70
+ * Add a new filter to the collection to be registered with WordPress.
71
+ *
72
+ * @since 1.0
73
+ *
74
+ * @param string $hook The name of the WordPress filter that is being registered.
75
+ * @param object $component A reference to the instance of the object on which the filter is defined.
76
+ * @param string $callback The name of the function definition on the $component.
77
+ * @param int $priority Optional. he priority at which the function should be fired. Default is 10.
78
+ * @param int $accepted_args Optional. The number of arguments that should be passed to the $callback. Default is 1.
79
+ */
80
+ public function add_filter( $hook, $component, $callback, $priority = 10, $accepted_args = 1 ) {
81
+ $this->filters = $this->add( $this->filters, $hook, $component, $callback, $priority,
82
+ $accepted_args );
83
+ }
84
+
85
+ /**
86
+ * A utility function that is used to register the actions and hooks into a single
87
+ * collection.
88
+ *
89
+ * @since 1.0
90
+ * @access private
91
+ *
92
+ * @param array $hooks The collection of hooks that is being registered (that is, actions or filters).
93
+ * @param string $hook The name of the WordPress filter that is being registered.
94
+ * @param object $component A reference to the instance of the object on which the filter is defined.
95
+ * @param string $callback The name of the function definition on the $component.
96
+ * @param int $priority The priority at which the function should be fired.
97
+ * @param int $accepted_args The number of arguments that should be passed to the $callback.
98
+ * @return array The collection of actions and filters registered with WordPress.
99
+ */
100
+ private function add( $hooks, $hook, $component, $callback, $priority, $accepted_args ) {
101
+ $hooks[] = array(
102
+ 'hook' => $hook,
103
+ 'component' => $component,
104
+ 'callback' => $callback,
105
+ 'priority' => $priority,
106
+ 'accepted_args' => $accepted_args,
107
+ );
108
+
109
+ return $hooks;
110
+ }
111
+
112
+ /**
113
+ * Register the filters and actions with WordPress.
114
+ *
115
+ * @since 1.0
116
+ */
117
+ public function run() {
118
+ foreach ( $this->filters as $hook ) {
119
+ add_filter( $hook['hook'], array(
120
+ $hook['component'],
121
+ $hook['callback'],
122
+ ), $hook['priority'], $hook['accepted_args'] );
123
+ }
124
+
125
+ foreach ( $this->actions as $hook ) {
126
+ add_action( $hook['hook'], array(
127
+ $hook['component'],
128
+ $hook['callback'],
129
+ ), $hook['priority'], $hook['accepted_args'] );
130
+ }
131
+ }
132
+ }
includes/class-boldgrid-backup.php ADDED
@@ -0,0 +1,430 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The file that defines the core plugin class
4
+ *
5
+ * A class definition that includes attributes and functions used across the admin area.
6
+ *
7
+ * @link http://www.boldgrid.com
8
+ * @since 1.0
9
+ *
10
+ * @package Boldgrid_Backup
11
+ * @subpackage Boldgrid_Backup/includes
12
+ */
13
+
14
+ /**
15
+ * The core plugin class.
16
+ *
17
+ * This is used to define internationalization and admin-specific hooks.
18
+ *
19
+ * Also maintains the unique identifier of this plugin as well as the current
20
+ * version of the plugin.
21
+ *
22
+ * @since 1.0
23
+ * @package Boldgrid_Backup
24
+ * @subpackage Boldgrid_Backup/includes
25
+ * @author BoldGrid.com <wpb@boldgrid.com>
26
+ */
27
+ class Boldgrid_Backup {
28
+
29
+ /**
30
+ * The loader that's responsible for maintaining and registering all hooks that power
31
+ * the plugin.
32
+ *
33
+ * @since 1.0
34
+ * @access protected
35
+ * @var Boldgrid_Backup_Loader $loader Maintains and registers all hooks for the plugin.
36
+ */
37
+ protected $loader;
38
+
39
+ /**
40
+ * The unique identifier of this plugin.
41
+ *
42
+ * @since 1.0
43
+ * @access protected
44
+ * @var string $plugin_name The string used to uniquely identify this plugin.
45
+ */
46
+ protected $plugin_name;
47
+
48
+ /**
49
+ * The current version of the plugin.
50
+ *
51
+ * @since 1.0
52
+ * @access protected
53
+ * @var string $version The current version of the plugin.
54
+ */
55
+ protected $version;
56
+
57
+ /**
58
+ * Define the core functionality of the plugin.
59
+ *
60
+ * Set the plugin name and the plugin version that can be used throughout the plugin.
61
+ * Load the dependencies, define the locale, and set the hooks for the admin area of the site.
62
+ *
63
+ * @since 1.0
64
+ */
65
+ public function __construct() {
66
+ $this->plugin_name = 'boldgrid-backup';
67
+ $this->version = ( defined( 'BOLDGRID_BACKUP_VERSION' ) ? BOLDGRID_BACKUP_VERSION : '' );
68
+
69
+ $this->load_dependencies();
70
+ $this->set_locale();
71
+ $this->define_admin_hooks();
72
+ }
73
+
74
+ /**
75
+ * Load the required dependencies for this plugin.
76
+ *
77
+ * Include the following files that make up the plugin:
78
+ *
79
+ * - Boldgrid_Backup_Loader. Orchestrates the hooks of the plugin.
80
+ * - Boldgrid_Backup_i18n. Defines internationalization functionality.
81
+ * - Boldgrid_Backup_Admin. Defines all hooks for the admin area.
82
+ *
83
+ * Create an instance of the loader which will be used to register the hooks
84
+ * with WordPress.
85
+ *
86
+ * @since 1.0
87
+ * @access private
88
+ */
89
+ private function load_dependencies() {
90
+
91
+ require_once BOLDGRID_BACKUP_PATH . '/vendor/autoload.php';
92
+
93
+ /**
94
+ * The class responsible for orchestrating the actions and filters of the
95
+ * core plugin.
96
+ */
97
+ require_once BOLDGRID_BACKUP_PATH . '/includes/class-boldgrid-backup-loader.php';
98
+
99
+ /**
100
+ * The class responsible for defining internationalization functionality
101
+ * of the plugin.
102
+ */
103
+ require_once BOLDGRID_BACKUP_PATH . '/includes/class-boldgrid-backup-i18n.php';
104
+
105
+ /**
106
+ * The class responsible for defining all actions that occur in the admin area.
107
+ */
108
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin.php';
109
+
110
+ /**
111
+ * Include a utility class.
112
+ */
113
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-utility.php';
114
+
115
+ /**
116
+ * The class responsible for the configuration of the plugin.
117
+ */
118
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-config.php';
119
+
120
+ /**
121
+ * The class responsible for the functionality test for the plugin.
122
+ */
123
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-test.php';
124
+
125
+ /**
126
+ * The class responsible for the admin notices for the plugin.
127
+ */
128
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-notice.php';
129
+
130
+ /**
131
+ * The class responsible for the cron functionality in the admin area.
132
+ */
133
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-cron.php';
134
+
135
+ /**
136
+ * The class responsible for the core backup functionality in the admin area.
137
+ */
138
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-core.php';
139
+
140
+ /**
141
+ * The class responsible for the backup settings in the admin area.
142
+ */
143
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-settings.php';
144
+
145
+ /**
146
+ * The class responsible for the PHP profiling functionality using XHProf.
147
+ */
148
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-xhprof.php';
149
+
150
+ /**
151
+ * The class responsible for the plugin file upload functionality in the admin area.
152
+ */
153
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-upload.php';
154
+
155
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-restore-helper.php';
156
+
157
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-restore-git.php';
158
+
159
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-filelist.php';
160
+
161
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-compressor.php';
162
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-compressors.php';
163
+ require_once BOLDGRID_BACKUP_PATH . '/admin/compressor/php_zip.php';
164
+ require_once BOLDGRID_BACKUP_PATH . '/admin/compressor/pcl_zip.php';
165
+
166
+ include BOLDGRID_BACKUP_PATH . '/vendor/ifsnop/mysqldump-php/src/Ifsnop/Mysqldump/Mysqldump.php';
167
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-db-dump.php';
168
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-db-get.php';
169
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-db-import.php';
170
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-db-omit.php';
171
+
172
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-backup-dir.php';
173
+
174
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-archive.php';
175
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-archive-actions.php';
176
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-archives.php';
177
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-archives-all.php';
178
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-archive-browser.php';
179
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-archive-log.php';
180
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-archive-details.php';
181
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-archive-fail.php';
182
+
183
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-wp-cron.php';
184
+
185
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-scheduler.php';
186
+
187
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-home-dir.php';
188
+
189
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-auto-rollback.php';
190
+
191
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-jobs.php';
192
+
193
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-remote.php';
194
+
195
+ require_once BOLDGRID_BACKUP_PATH . '/admin/storage/local.php';
196
+
197
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-email.php';
198
+
199
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-folder-exclusion.php';
200
+
201
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-core-files.php';
202
+
203
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-in-progress.php';
204
+
205
+ require_once BOLDGRID_BACKUP_PATH . '/admin/remote/ftp.php';
206
+ require_once BOLDGRID_BACKUP_PATH . '/admin/remote/ftp-hooks.php';
207
+ require_once BOLDGRID_BACKUP_PATH . '/admin/remote/ftp-page.php';
208
+
209
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-go-pro.php';
210
+
211
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-tools.php';
212
+
213
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-time.php';
214
+
215
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-crypt.php';
216
+
217
+ $this->loader = new Boldgrid_Backup_Loader();
218
+ }
219
+
220
+ /**
221
+ * Define the locale for this plugin for internationalization.
222
+ *
223
+ * Uses the Boldgrid_Backup_i18n class in order to set the domain and to register the hook
224
+ * with WordPress.
225
+ *
226
+ * @since 1.0
227
+ * @access private
228
+ */
229
+ private function set_locale() {
230
+ $plugin_i18n = new Boldgrid_Backup_i18n();
231
+
232
+ $this->loader->add_action( 'plugins_loaded', $plugin_i18n, 'load_plugin_textdomain' );
233
+ }
234
+
235
+ /**
236
+ * Register all of the hooks related to the admin area functionality of the plugin.
237
+ *
238
+ * @since 1.0
239
+ * @access private
240
+ *
241
+ * @return null
242
+ */
243
+ private function define_admin_hooks() {
244
+ // Instantiate a Boldgrid_Backup_Admin class object.
245
+ $plugin_admin = new Boldgrid_Backup_Admin( $this->get_plugin_name(), $this->get_version() );
246
+
247
+ $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_styles' );
248
+
249
+ // Instantiate the admin core.
250
+ $plugin_admin_core = new Boldgrid_Backup_Admin_Core();
251
+
252
+ // Add nav menu items.
253
+ $this->loader->add_action( 'admin_menu', $plugin_admin_core,
254
+ 'add_menu_items'
255
+ );
256
+
257
+ // Add a custom action for admin notices.
258
+ $this->loader->add_action( 'boldgrid_backup_notice', $plugin_admin_core->notice,
259
+ 'boldgrid_backup_notice', 10, 2
260
+ );
261
+
262
+ $this->loader->add_action( 'admin_notices', $plugin_admin_core->notice, 'display_user_notice' );
263
+ $this->loader->add_action( 'wp_ajax_boldgrid_backup_get_countdown_notice', $plugin_admin_core->auto_rollback, 'wp_ajax_get_countdown_notice' );
264
+ $this->loader->add_action( 'wp_ajax_boldgrid_backup_get_protect_notice', $plugin_admin_core->auto_rollback, 'wp_ajax_get_protect_notice' );
265
+ $this->loader->add_action( 'wp_ajax_boldgrid_backup_get_progress_notice', $plugin_admin_core->in_progress, 'wp_ajax_get_progress_notice' );
266
+
267
+ // Add a custom action to handle AJAX callback for creating a backup archive file.
268
+ $this->loader->add_action( 'wp_ajax_boldgrid_backup_now', $plugin_admin_core,
269
+ 'boldgrid_backup_now_callback'
270
+ );
271
+
272
+ // Add a custom action to handle AJAX callback for archive file download buttons.
273
+ $this->loader->add_action( 'wp_ajax_download_archive_file', $plugin_admin_core,
274
+ 'download_archive_file_callback'
275
+ );
276
+
277
+ // Add an action to perform an auto-backup before an auto-update occurs.
278
+ $this->loader->add_action( 'pre_auto_update', $plugin_admin_core,
279
+ 'boldgrid_backup_now_auto'
280
+ );
281
+
282
+ // Add an action to display an admin notice for a pending rollback.
283
+ $this->loader->add_action( 'admin_notices', $plugin_admin_core->auto_rollback,
284
+ 'notice_countdown_show'
285
+ );
286
+
287
+ // Add a custom action to handle AJAX callback for canceling a pending rollback.
288
+ $this->loader->add_action( 'wp_ajax_boldgrid_cancel_rollback', $plugin_admin_core->auto_rollback,
289
+ 'wp_ajax_cancel'
290
+ );
291
+
292
+ $this->loader->add_action( 'admin_notices', $plugin_admin_core->auto_rollback, 'notice_backup_show' );
293
+
294
+ // Add an action to add a cron job to restore after WordPress Updates, unless canceled.
295
+ $this->loader->add_action( 'upgrader_process_complete', $plugin_admin_core->auto_rollback,
296
+ 'notice_deadline_show', 10, 2
297
+ );
298
+
299
+ // Add a custom action to handle AJAX callback for getting the rollback deadline.
300
+ $this->loader->add_action( 'wp_ajax_boldgrid_backup_deadline', $plugin_admin_core->auto_rollback,
301
+ 'wp_ajax_get_deadline'
302
+ );
303
+
304
+ $this->loader->add_action( 'boldgrid_backup_pre_restore', $plugin_admin_core->restore_helper, 'pre_restore' );
305
+ $this->loader->add_action( 'boldgrid_backup_post_restore', $plugin_admin_core->restore_helper, 'post_restore' );
306
+ $this->loader->add_filter( 'boldgrid_backup_post_restore', $plugin_admin_core->archive_log, 'post_restore' );
307
+ $this->loader->add_action( 'boldgrid_backup_post_restore_htaccess', $plugin_admin_core->restore_helper, 'post_restore_htaccess' );
308
+ $this->loader->add_action( 'boldgrid_backup_post_restore_wpconfig', $plugin_admin_core->restore_helper, 'post_restore_wpconfig' );
309
+ $this->loader->add_filter( 'boldgrid_backup_restore_fail', $plugin_admin_core->restore_helper, 'restore_fail' );
310
+
311
+ $this->loader->add_filter( 'boldgrid_backup_cannnot_restore_git_objects', $plugin_admin_core->restore_git, 'chmod_objects' );
312
+
313
+ $this->loader->add_filter( 'boldgrid_backup_file_in_dir', $plugin_admin_core->backup_dir, 'file_in_dir' );
314
+
315
+ $this->loader->add_filter( 'unzip_file_use_ziparchive', $plugin_admin_core->compressors, 'unzip_file_use_ziparchive' );
316
+
317
+ $this->loader->add_filter( 'cron_schedules', $plugin_admin_core->wp_cron, 'cron_schedules' );
318
+ $this->loader->add_action( 'boldgrid_backup_wp_cron_backup', $plugin_admin_core->wp_cron, 'backup' );
319
+ $this->loader->add_action( 'boldgrid_backup_wp_cron_restore', $plugin_admin_core->wp_cron, 'restore' );
320
+
321
+ $this->loader->add_action( 'boldgrid_backup_archive_files_init', $plugin_admin_core->archive_fail, 'archive_files_init' );
322
+
323
+ $this->loader->add_action( 'boldgrid_backup_wp_cron_run_jobs', $plugin_admin_core->jobs, 'run' );
324
+
325
+ $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin_core, 'admin_enqueue_scripts' );
326
+
327
+ $this->loader->add_filter( 'plugins_loaded', $plugin_admin_core, 'init_premium' );
328
+
329
+ $this->loader->add_action( 'boldgrid_backup_delete_local', $plugin_admin_core->local, 'delete_local' );
330
+
331
+ $this->loader->add_action( 'boldgrid_backup_post_archive_files', $plugin_admin_core->local, 'post_archive_files', 100 );
332
+ $this->loader->add_action( 'boldgrid_backup_post_archive_files', $plugin_admin_core->jobs, 'post_archive_files', 200 );
333
+ $this->loader->add_action( 'boldgrid_backup_post_jobs_email', $plugin_admin_core->jobs, 'post_jobs_email' );
334
+
335
+ $this->loader->add_action( 'boldgrid_backup_cron_fail_email', $plugin_admin_core->archive_fail, 'cron_fail_email' );
336
+
337
+ $this->loader->add_action( 'wp_ajax_boldgrid_backup_browse_archive', $plugin_admin_core->archive_browser, 'wp_ajax_browse_archive' );
338
+ $this->loader->add_action( 'wp_ajax_boldgrid_backup_browse_archive_file_actions', $plugin_admin_core->archive_browser, 'wp_ajax_file_actions' );
339
+ $this->loader->add_action( 'wp_ajax_boldgrid_backup_browse_archive_restore_db', $plugin_admin_core->archive_browser, 'wp_ajax_restore_db' );
340
+ $this->loader->add_action( 'wp_ajax_boldgrid_backup_browse_archive_view_db', $plugin_admin_core->archive_browser, 'wp_ajax_view_db' );
341
+
342
+ $this->loader->add_action( 'wp_ajax_boldgrid_backup_restore_archive', $plugin_admin_core, 'wp_ajax_restore' );
343
+
344
+ $this->loader->add_action( 'wp_ajax_boldgrid_backup_exclude_folders_preview', $plugin_admin_core->folder_exclusion, 'wp_ajax_preview' );
345
+
346
+ $this->loader->add_action( 'admin_init', $plugin_admin_core->config, 'admin_init' );
347
+
348
+ $this->loader->add_action( 'admin_init', $plugin_admin_core->auto_rollback, 'enqueue_update_selectors' );
349
+
350
+ $this->loader->add_action( 'admin_init', $plugin_admin_core->cron, 'upgrade_crontab_entries' );
351
+
352
+ /* FTP */
353
+
354
+ // Allow one click upload.
355
+ $this->loader->add_action( 'boldgrid_backup_single_archive_remote_options', $plugin_admin_core->ftp->hooks, 'single_archive_remote_option' );
356
+ // Process upload via ajax.
357
+ $this->loader->add_filter( 'wp_ajax_boldgrid_backup_remote_storage_upload_ftp', $plugin_admin_core->ftp->hooks, 'wp_ajax_upload' );
358
+ // Add to the settings page.
359
+ $this->loader->add_filter( 'boldgrid_backup_register_storage_location', $plugin_admin_core->ftp->hooks, 'register_storage_location' );
360
+ // Add our "configure ftp" page.
361
+ $this->loader->add_action( 'admin_menu', $plugin_admin_core->ftp->hooks, 'add_menu_items' );
362
+ // After updating settings on the settings page, check if we have valid credentials.
363
+ $this->loader->add_action( 'wp_ajax_boldgrid_backup_is_setup_ftp', $plugin_admin_core->ftp->hooks, 'is_setup_ajax' );
364
+ // After a backup file has been created, add remote provider to jobs queue.
365
+ $this->loader->add_action( 'boldgrid_backup_post_archive_files', $plugin_admin_core->ftp->hooks, 'post_archive_files' );
366
+ // This is the filter executed by the jobs queue.
367
+ $this->loader->add_filter( 'boldgrid_backup_ftp_upload_post_archive', $plugin_admin_core->ftp->hooks, 'upload_post_archiving' );
368
+ // Add ftp backups to the "Backups" tab.
369
+ $this->loader->add_action( 'boldgrid_backup_get_all', $plugin_admin_core->ftp->hooks, 'filter_get_all' );
370
+ $this->loader->add_action( 'wp_ajax_boldgrid_backup_remote_storage_download_ftp', $plugin_admin_core->ftp->hooks, 'wp_ajax_download' );
371
+
372
+ $this->loader->add_action( 'admin_notices', $plugin_admin_core->go_pro, 'admin_notice_setup' );
373
+
374
+ $this->loader->add_action( 'boldgrid_backup_pre_dump', $plugin_admin_core->in_progress, 'pre_dump' );
375
+ $this->loader->add_action( 'boldgrid_backup_post_dump', $plugin_admin_core->in_progress, 'post_dump' );
376
+ $this->loader->add_filter( 'heartbeat_received', $plugin_admin_core->in_progress, 'heartbeat_received', 10, 2 );
377
+
378
+ $this->loader->add_action( 'customize_controls_enqueue_scripts', $plugin_admin_core->auto_rollback, 'enqueue_customize_controls' );
379
+
380
+ add_filter( 'pre_update_option_boldgrid_backup_settings', array( 'Boldgrid_Backup_Admin_Crypt', 'pre_update_settings' ), 10, 3 );
381
+ add_filter( 'option_boldgrid_backup_settings', array( 'Boldgrid_Backup_Admin_Crypt', 'option_settings' ), 10, 2 );
382
+
383
+ // Actions run from crontab calls; unauthenticated.
384
+ $this->loader->add_action( 'wp_ajax_nopriv_boldgrid_backup_run_jobs', $plugin_admin_core->jobs, 'run' );
385
+ $this->loader->add_action( 'wp_ajax_nopriv_boldgrid_backup_run_backup', $plugin_admin_core->cron, 'backup' );
386
+ $this->loader->add_action( 'wp_ajax_nopriv_boldgrid_backup_run_restore', $plugin_admin_core->cron, 'restore' );
387
+
388
+ return;
389
+ }
390
+
391
+ /**
392
+ * Run the loader to execute all of the hooks with WordPress.
393
+ *
394
+ * @since 1.0
395
+ */
396
+ public function run() {
397
+ $this->loader->run();
398
+ }
399
+
400
+ /**
401
+ * The name of the plugin used to uniquely identify it within the context of
402
+ * WordPress and to define internationalization functionality.
403
+ *
404
+ * @since 1.0
405
+ * @return string The name of the plugin.
406
+ */
407
+ public function get_plugin_name() {
408
+ return $this->plugin_name;
409
+ }
410
+
411
+ /**
412
+ * The reference to the class that orchestrates the hooks with the plugin.
413
+ *
414
+ * @since 1.0
415
+ * @return Boldgrid_Backup_Loader Orchestrates the hooks of the plugin.
416
+ */
417
+ public function get_loader() {
418
+ return $this->loader;
419
+ }
420
+
421
+ /**
422
+ * Retrieve the version number of the plugin.
423
+ *
424
+ * @since 1.0
425
+ * @return string The version number of the plugin.
426
+ */
427
+ public function get_version() {
428
+ return $this->version;
429
+ }
430
+ }
includes/config/.gitignore ADDED
@@ -0,0 +1 @@
 
1
+ *.local.php
includes/config/config.plugin.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Plugin configuration file
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.0
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/includes
10
+ */
11
+
12
+ // Prevent direct calls.
13
+ if ( ! defined( 'WPINC' ) ) {
14
+ header( 'Status: 403 Forbidden' );
15
+ header( 'HTTP/1.1 403 Forbidden' );
16
+ exit();
17
+ }
18
+
19
+ return array(
20
+ 'ajax_calls' => array(
21
+ 'get_plugin_version' => '/api/open/get-plugin-version',
22
+ 'get_asset' => '/api/open/get-asset',
23
+ ),
24
+ 'asset_server' => 'https://wp-assets.boldgrid.com',
25
+
26
+ 'urls' => array(
27
+ 'compatibility' => 'https://www.boldgrid.com/support/advanced-tutorials/backup-compatibility-guide',
28
+ 'possible_issues' => 'https://www.boldgrid.com/support/advanced-tutorials/backup-userguide#possible-issues',
29
+ 'reduce_size_warning' => 'https://www.boldgrid.com/support/advanced-tutorials/backup-userguide#reduce-size-warning',
30
+ 'resource_usage' => 'https://www.boldgrid.com/support/advanced-tutorials/backup-userguide#resource-usage',
31
+ 'upgrade' => 'https://www.boldgrid.com/support/advanced-tutorials/backup-userguide#upgrade',
32
+ 'user_guide' => 'https://www.boldgrid.com/support/advanced-tutorials/backup-userguide',
33
+ 'restore' => 'https://www.boldgrid.com/support/advanced-tutorials/restoring-boldgrid-backup/',
34
+ 'setting_directory' => 'https://www.boldgrid.com/support/advanced-tutorials/backup-userguide#setting-backup-directory',
35
+ ),
36
+ 'lang' => array(
37
+ 'est_pause' => esc_html__( 'Estimated Pause: %s seconds', 'boldgrid-backup' ),
38
+ ),
39
+ 'plugin_name' => 'boldgrid-backup',
40
+ 'plugin_key_code' => 'backup',
41
+ 'main_file_path' => BOLDGRID_BACKUP_PATH . '/boldgrid-backup.php',
42
+ 'plugin_transient_name' => 'boldgrid_backup_version_data',
43
+ );
includes/config/config.sample.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Plugin sample configuration file
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.0
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/includes
10
+ */
11
+
12
+ // Prevent direct calls.
13
+ if ( ! defined( 'WPINC' ) ) {
14
+ header( 'Status: 403 Forbidden' );
15
+ header( 'HTTP/1.1 403 Forbidden' );
16
+ exit();
17
+ }
18
+
19
+ /**
20
+ * Copy this sample file to config.local.php and update it with any variables that you would like to override
21
+ */
22
+ return array(
23
+ 'asset_server' => 'https://wp-assets-dev.boldgrid.com',
24
+ );
includes/config/index.php ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <?php
2
+ // Silence is golden.
includes/index.php ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <?php
2
+ // Silence is golden.
index.php ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <?php
2
+ // Silence is golden.
languages/boldgrid-backup.pot ADDED
File without changes
readme.txt ADDED
@@ -0,0 +1,276 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === BoldGrid Backup ===
2
+ Contributors: boldgrid, joemoto, imh_brad, rramo012, timph, bgnicolepaschen
3
+ Tags: boldgrid, backup, restore, migrate, migration
4
+ Requires at least: 4.4
5
+ Tested up to: 4.9.6
6
+ Requires PHP: 5.3
7
+ Stable tag: 1.6.1
8
+ License: GPLv2 or later
9
+ License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
+
11
+ BoldGrid Backup provides WordPress backup and restoration with update protection.
12
+
13
+ == Description ==
14
+
15
+ WordPress backup and restoration with update protection.
16
+
17
+ == Installation ==
18
+
19
+ = Minimum Requirements =
20
+
21
+ * PHP 5.3 or higher
22
+ * At least one of the following PHP execution functions enabled: "popen", "proc_open", "exec", "shell_exec", "passthru", or "system".
23
+ * A Cron system with the "crontab" utility, or WP Cron.
24
+
25
+ = Manually =
26
+ 1. Upload the entire boldgrid-backup folder to the /wp-content/plugins/ directory.
27
+ 1. Activate the plugin through the Plugins menu in WordPress.
28
+
29
+ == Changelog ==
30
+
31
+ = 1.6.1 =
32
+
33
+ Release Date: May 24th, 2018
34
+
35
+ * Update: Ran PHPCBF to beautify PHP code.
36
+ * Update: $_POST sanitization
37
+ * Update: Cron system updated to avoid calling core files directly
38
+
39
+ = 1.6.0 =
40
+
41
+ Release Date: April 11th, 2018
42
+
43
+ * New feature: Archive browser, the ability to see what's in a backup.
44
+ * New feature: Database browser, the ability to see at a high level what's in a backup.
45
+ * New feature: 1 click restore database only.
46
+ * New feature: FTP / SFTP support added.
47
+ * New feature: Control which files and database tables are backed up.
48
+ * Compatibility: PclZip support added for creating archives.
49
+ * Compatibility: WP Cron support added for scheduled backups.
50
+ * Compatibility: PHP Script used to backup database, rather than system commands.
51
+ * Improvement: Update admin pages to use WP UI/UX standards.
52
+ * Improvement: Improved UI in regards to time zones.
53
+ * Improvement: Failed items on Preflight Check page are highlighted in red.
54
+ * Improvement: Send email if backup fails via cron.
55
+ * Improvement: More details in Preflight Check to help with troubleshooting.
56
+ * Bug fix: Bug fixed with auto restoration feature.
57
+
58
+ = 1.5 =
59
+
60
+ Release Date: August 8th, 2017
61
+
62
+ * Update: Bump version.
63
+
64
+ = 1.3.12 =
65
+
66
+ Release Date: July 20th, 2017
67
+
68
+ * Update: Updated plugin URI.
69
+
70
+ Release Date: June 27th, 2017
71
+
72
+ = 1.3.11 =
73
+ * New feature: Added auto-update settings for plugins and themes.
74
+ * Bug fix: Skip node_modules paths when creating archives.
75
+
76
+ = 1.3.10 =
77
+
78
+ Release Date: May 16th, 2017
79
+
80
+ * Bug fix: Fixed undefined property when pre-flight test fails.
81
+ * Bug fix: Fixed an undefined index when home dir is not writable.
82
+ * Bug fix: Fixed auto plugin update.
83
+
84
+ = 1.3.9 =
85
+
86
+ Release Date: May 2nd, 2017
87
+
88
+ * Bug fix: Added check and load before using get_plugin_data() for updates.
89
+
90
+ = 1.3.8 =
91
+
92
+ Release Date: April 4th, 2017
93
+
94
+ * Bug fix: After migrating a site via boldgrid-backup, the backup directory was not updated if invalid.
95
+
96
+ = 1.3.7 =
97
+
98
+ Release Date: February 20th, 2017
99
+
100
+ * Bug fix: Fixed issue when installing plugins from the Tools Import page.
101
+ * Bug fix: Fixed check for system tar and zip.
102
+ * Bug fix: Fixed method of locating home directory.
103
+
104
+ = 1.3.6 =
105
+
106
+ Release Date: February 9th, 2017
107
+
108
+ * Update: Show how long the site was paused for.
109
+ * Update: Auto show move backups message.
110
+ * Bug fix: Fixed plugin update checks for some scenarios (WP-CLI, Plesk, etc).
111
+
112
+ = 1.3.5 =
113
+
114
+ Release Date: February 7th, 2017
115
+
116
+ * Bug fix: Fixed plugin update checks for some scenarios (WP-CLI, Plesk, etc).
117
+ * Bug fix: Backing up fails after 5 minutes.
118
+
119
+ = 1.3.4 =
120
+
121
+ Release Date: January 10th, 2017
122
+
123
+ * Update: Update support urls.
124
+ * Update: Close session on gathering disk space api call.
125
+ * Bug fix: Fixed missing link in email.
126
+ * Bug fix: Uncaught TypeError: wp.template is not a function.
127
+ * Testing: Tested on WordPress 4.7.
128
+
129
+ = 1.3.3 =
130
+
131
+ Release Date: December 20th, 2016
132
+
133
+ * Update: Show backup limits to users.
134
+ * Update: Misc notices.
135
+ * Update: Disable backup now button.
136
+ * Update: Prevent backup if account is too large.
137
+
138
+ = 1.3.2 =
139
+
140
+ Release Date: December 6th, 2016
141
+
142
+ * Update: Move backups when changing backup directory.
143
+ * Update: Improve time to calculate disk space.
144
+ * Bug fix: Added double-quote encapsulation to password in mysqldump defaults file.
145
+ * Bug fix: Typo fix.
146
+
147
+
148
+ = 1.3.1 =
149
+
150
+ Release Date: November 15th, 2016
151
+
152
+ * Update: Modify 'last created archive' message with link to archives.
153
+ * Update: Modify backup success message with link to settings.
154
+ * Update: Modify BoldGrid Backup menus.
155
+ * Update: Adjust display of preflight check.
156
+ * Update: Free limitations to days of the week.
157
+ * Update: Free limitations to retention.
158
+ * Update: Standard tooltips.
159
+ * Update: Add intro message to Archive page.
160
+ * Update: Modify backup id section on archives page.
161
+ * Update: Modify Backup Site messages.
162
+ * Update: Cache disk space data.
163
+ * Update: Add free / premium messages next to disk / db sizes.
164
+ * Misc: Added plugin requirements to readme.txt file.
165
+
166
+ = 1.3 =
167
+
168
+ Release Date: October 12th, 2016
169
+
170
+ * Update: Bump version.
171
+
172
+ = 1.2.3 =
173
+
174
+ Release Date: September 20th, 2016
175
+
176
+ * Bug fix: Added handling for restoration if site URL changed. Fixed upload button in Chrome.
177
+ * Bug fix: Load BoldGrid settings from the correct WP option (site/blog).
178
+ * Bug fix: Fixed typo in archive deletion confirmation dialogue.
179
+ * Update: Set version constant from plugin file.
180
+ * Misc: Updated readme.txt for Tested up to 4.6.1.
181
+
182
+ = 1.2.2 =
183
+
184
+ Release Date: September 7th, 2016
185
+
186
+ * New feature: Added the ability to upload a backup archive.
187
+ * Bug fix: Fixed errors during deactivation.
188
+ * Bug fix: Update class was not getting current plugin version.
189
+
190
+ = 1.2.1 =
191
+
192
+ Release Date: August 23rd, 2016
193
+
194
+ * Bug fix: Updates via adminajax now updates the rollback timer.
195
+ * Misc: Updated readme.txt for Tested up to: 4.6.
196
+
197
+ = 1.2 =
198
+
199
+ Release Date: August 9th, 2016
200
+
201
+ * New feature: Added XHProf for optional PHP profiling. Can be enabled in "config.local.php" by setting "xhprof" to true.
202
+ * Bug fix: Fixed auto-update action hook.
203
+ * Bug fix: Changed restore and delete buttons to POST forms, to resolve issue with people reloading the restore URL.
204
+ * Bug fix: Reworked error notices for restoration. Emptying archive list before updating after performing a backup.
205
+ * Bug fix: Disabled backup and restore buttons after starting a restoration.
206
+ * Bug fix: Removed homedir not writable notice; moved info to the functionality test page.
207
+ * Bug fix: Removed add cron action on activation.
208
+ * Redesign: Changed backup duration display seconds to 2 decimal places.
209
+ * Rework: Settings page will now load if functionality test fails.
210
+ * Rework: Cleanup for the rollback admin notice.
211
+ * Rework: Added a warning in the notice for restorations (may get logged-out).
212
+ * Rework: Moved cron methods to a new class.
213
+ * Rework: Reworked for translations.
214
+
215
+ = 1.0.2 =
216
+
217
+ Release Date: July 22nd, 2016
218
+
219
+ * Rework: Removed notice for staged pending rollback.
220
+
221
+ = 1.0.1 =
222
+
223
+ Release Date: July 6th, 2016
224
+
225
+ * New feature: Added setting for notification email address.
226
+ * New feature: Added setting for backup directory.
227
+ * New feature: Cancel auto-rollback if a restoration is performed.
228
+ * New feature: Added Rollback Site Now button in the rollback notice.
229
+ * New feature: Made it possible to change siteurl and retain matched archives (backups made as of this update).
230
+ * New feature: Added capability for auto-updates by BoldGrid API response.
231
+ * Redesign: Formatted the Functionality Test page.
232
+ * Bug fix: Removed PHP SAPI check in cron script.
233
+ * Bug fix: Restoration cron did not always complete.
234
+ * Bug fix: Better aligned rollback countdown timer with cron job.
235
+ * Bug fix: Provided message for empty archive list.
236
+ * Bug fix: Rollback information is now removed after timer reaches 0:00.
237
+ * Bug fix: Test for crontab now works when crontab is empty.
238
+ * Bug fix: Now closing PHP session on backup, download, and restore, so that other PHP requests from the client may load.
239
+ * Testing: Tested on WordPress 4.5.3.
240
+
241
+ = 1.0 =
242
+
243
+ Release Date: June 21st, 2016
244
+
245
+ * Initial public release.
246
+
247
+ == Upgrade Notice ==
248
+
249
+
250
+ == Technical Documentation ==
251
+
252
+
253
+ = Auto Updates & Rollback Settings =
254
+
255
+ #### Plugin Auto-Updates and Theme Auto-Updates
256
+
257
+ Taken from WordPress' [Configuring Automatic Background Updates](https://codex.wordpress.org/Configuring_Automatic_Background_Updates#Plugin_.26_Theme_Updates_via_Filter)
258
+
259
+ > By default, automatic background updates only happen for plugins and themes in special cases, as determined by the WordPress.org API response, which is controlled by the WordPress security team for patching critical vulnerabilities. To enable or disable updates in all cases, you can leverage the auto_update_$type filter, where $type would be replaced with "plugin" or "theme".
260
+
261
+ When these features are enabled, BoldGrid Backup will add *auto_update_plugin* and *auto_update_theme* filters so that *any plugin that has an update available* will update.
262
+
263
+ #### Auto Backup Before Updates
264
+
265
+ Before WordPress does any auto updates, it fires the [pre_auto_update](https://developer.wordpress.org/reference/hooks/pre_auto_update/) hook. If the user has the **Auto Backup Before Updates** option enabled, then a backup will occur before the auto update.
266
+
267
+ #### Auto Rollback
268
+
269
+ *Auto Rollback* is the feature within the BoldGrid Backup plugin that recommends making updates before performing any updates. If you disable this feature, then BoldGrid Backup will not recommend updates.
270
+
271
+ Example notice:
272
+ > BoldGrid Backup - Update Protection
273
+ >
274
+ > On this page you are able to update WordPress, Plugins, and Themes. It is recommended to backup your site before performing updates. If you perform a backup here, before performing updates, then an automatic rollback is possible.
275
+ >
276
+ > Update protection not available until you click Backup Site Now and a backup is created.
uninstall.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Fired when the plugin is uninstalled.
4
+ *
5
+ * @link http://www.boldgrid.com
6
+ * @since 1.0
7
+ *
8
+ * @package Boldgrid_Backup
9
+ */
10
+
11
+ // If uninstall not called from WordPress, then exit.
12
+ if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
13
+ exit();
14
+ }
15
+
16
+ // Delete BoldGrid Backup WordPress options.
17
+ $delete_options = array(
18
+ 'boldgrid_backup_settings',
19
+ 'boldgrid_backup_last_backup',
20
+ 'boldgrid_backup_pending_rollback',
21
+ );
22
+
23
+ foreach ( $delete_options as $option ) {
24
+ delete_site_option( $option );
25
+ }
vendor/autoload.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload.php @generated by Composer
4
+
5
+ require_once __DIR__ . '/composer/autoload_real.php';
6
+
7
+ return ComposerAutoloaderInitf39823383f9fe9e7b3db616106f32896::getLoader();
vendor/boldgrid/library/.eslintrc.js ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // http://eslint.org/docs/user-guide/configuring
2
+
3
+ module.exports = {
4
+ root: true,
5
+ env: {
6
+ browser: true
7
+ },
8
+ // https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style
9
+ extends: 'wordpress',
10
+ plugins: [
11
+ 'html'
12
+ ],
13
+ // Add your custom rules here
14
+ 'rules': {
15
+ 'space-in-parens': ['error', 'always'],
16
+ "wrap-iife": [2, "any"],
17
+ // Allow async-await
18
+ 'generator-star-spacing': 0,
19
+ // Allow debugger during development
20
+ 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
21
+ }
22
+ };
vendor/boldgrid/library/.gitignore ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ composer.phar
2
+ /vendor/
3
+ node_modules/
4
+
5
+ # Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file
6
+ # You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
7
+ # composer.lock
vendor/boldgrid/library/.prettierrc ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ {
2
+ "printWidth": 100,
3
+ "bracketSpacing": true,
4
+ "useTabs": true,
5
+ "singleQuote": true
6
+ }
vendor/boldgrid/library/.travis.yml ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ language: php
2
+
3
+ notifications:
4
+ email:
5
+ on_success: never
6
+ on_failure: change
7
+
8
+ php:
9
+ - 5.6
10
+
11
+ env:
12
+ - WP_VERSION=latest WP_MULTISITE=0
13
+
14
+ matrix:
15
+ include:
16
+ - php: 5.6
17
+ env: WP_VERSION=latest WP_MULTISITE=0
18
+ - php: 5.6
19
+ env: WP_VERSION=latest WP_MULTISITE=1
20
+ - php: 5.6
21
+ env: WP_VERSION=4.7.5 WP_MULTISITE=0
22
+ - php: 5.6
23
+ env: WP_VERSION=4.7.5 WP_MULTISITE=1
24
+ - php: 5.6
25
+ # #
26
+ # Need to fix this error before running on 4.6.1 and below:
27
+ # Fatal error: __clone method called on non-object in
28
+ # /tmp/wordpress-tests-lib/includes/testcase.php on line 287
29
+ # #
30
+ # env: WP_VERSION=4.6.1 WP_MULTISITE=0
31
+ # - php: 5.6
32
+ # env: WP_VERSION=4.6.2 WP_MULTISITE=1
33
+ # - php: 5.6
34
+ # env: WP_VERSION=4.5.3 WP_MULTISITE=0
35
+ # - php: 5.6
36
+ # env: WP_VERSION=4.5.3 WP_MULTISITE=1
37
+ # - php: 5.6
38
+ # env: WP_VERSION=4.4.2 WP_MULTISITE=0
39
+ # - php: 5.6
40
+ # env: WP_VERSION=4.4.2 WP_MULTISITE=1
41
+ # #
42
+ # It doesn't look like WP 4.3 will work.
43
+ # Fatal error: Class 'WP_REST_Server' not found.
44
+ # WP_REST_Server is #since 4.4.0
45
+ # #
46
+ # - php: 5.6
47
+ # env: WP_VERSION=4.3.1 WP_MULTISITE=0
48
+ # - php: 5.6
49
+ # env: WP_VERSION=4.3.1 WP_MULTISITE=1
50
+
51
+ before_script:
52
+ - bash bin/install-wp-tests.sh wordpress_test root '' localhost $WP_VERSION
53
+
54
+ script: phpunit --debug
vendor/boldgrid/library/LICENSE ADDED
@@ -0,0 +1,339 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 2, June 1991
3
+
4
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc., <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.
vendor/boldgrid/library/README.md ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # library
2
+
3
+ The BoldGrid Library for shared code used in official BoldGrid plugins and themes.
4
+
5
+ Using composer, you can get started quickly:
6
+
7
+ ```php
8
+ composer require boldgrid/library
9
+
10
+ ```
11
+
12
+ ## Changelog ##
13
+
14
+ ### 2.3.3 ###
15
+ * Bug fix: JIRA BGINSP-15 Disable Connect Key request button after submission.
16
+
17
+ ### 2.3.2 ###
18
+ * Update: More clear error message on failed ajax license clears.
19
+ * Update: Added prettier-eslint.
20
+
21
+ ### 2.3.1 ###
22
+ * Bug fix: JIRA BGBKUP-180 Fixed empty check for PHP 5.3.
23
+
24
+ ### 2.3.0 ###
25
+ * Feature: JIRA BGBKUP-180 Handle auto updates as configured by the boldgrid_settings option.
26
+
27
+ ### 2.2.2 ###
28
+ * Bug fix: JIRA WPB-3767 Prevent invalid API calls for check-version.
29
+
30
+ ### 2.2.1 ###
31
+ * Bug fix: JIRA WPB-3730 Fixed loading of plugin installer class.
32
+ * Update: JIRA WPB-3725 Use a transient in Checker::findUpdated().
33
+ * Bug fix: JIRA WPB-3724 Do not call getLicense if Connect Key is not available.
34
+ * Update: JIRA WPB-3721 Moved Plugin\Checker back to Library.
35
+ * Bug fix: Duplicate admin notices showing.
36
+
37
+ ### 2.2.0 ###
38
+ * Bug fix: JIRA WPB-3714 Fixed PHP notice in Key::verifyData().
39
+ * Feature: As a user, I can refresh my license key status.
40
+
41
+ ### 2.1.0 ###
42
+ * Feature: JIRA BGTHEME-103 Added ClaimPremiumKey notice.
43
+
44
+ ### 2.0.0 ###
45
+ * Feature: JIRA BGINSP-3 Added filter to display Connect Key prompt admin notice, even if dismissed.
46
+ * Update: JIRA WPB-3684 Moved plugin install to its own package (boldgrid/plugin-install).
47
+ * Feature: JIRA BGBKUP-75 Added dismiss/undismiss for Connect Key prompt/notice.
48
+
49
+ ### 1.1.6 ###
50
+ * Bug fix: JIRA BGBKUP-67 Fixed key prompt is-dismissible, and hid duplicate notice from other plugin.
51
+ * Feature: JIRA WPB-3638 Added post-and-page-builder and boldgrid-easy-seo to the Plugins >> Add New page.
52
+ * Bug fix: JIRA WPB-3636 Fixed invalid version number sent for plugins not installed, but in config.
53
+ * Bug fix: JIRA WPB-3635 API calls now respect release channels.
54
+
55
+ ### 1.1.5 ###
56
+ * Bug fix: JIRA WPB-3518 Fixed fatal error in certain scenarios from double inclusion of WP core files.
57
+
58
+ ### 1.1.4 ###
59
+ * Bug fix: JIRA WPB-3427 Adjusted handling of plugin update transients.
60
+ * Added premium product check to license class.
61
+
62
+ ### 1.1.3 ###
63
+ * Validate plugin before printing card.
64
+
65
+ ### 1.1.2 ###
66
+ * Bug fixes.
67
+
68
+ ### 1.1.1 ###
69
+ * Added form affiliate data.
70
+
71
+ ### 1.1.0 ###
72
+ * Added action for when theme release channel changed.
73
+ * Added Reseller class.
vendor/boldgrid/library/bin/install-wp-tests.sh ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+
3
+ if [ $# -lt 3 ]; then
4
+ echo "usage: $0 <db-name> <db-user> <db-pass> [db-host] [wp-version]"
5
+ exit 1
6
+ fi
7
+
8
+ DB_NAME=$1
9
+ DB_USER=$2
10
+ DB_PASS=$3
11
+ DB_HOST=${4-localhost}
12
+ WP_VERSION=${5-latest}
13
+
14
+ WP_TESTS_DIR=${WP_TESTS_DIR-/tmp/wordpress-tests-lib}
15
+ WP_CORE_DIR=${WP_CORE_DIR-/tmp/wordpress/}
16
+
17
+ set -ex
18
+
19
+ install_wp() {
20
+ mkdir -p $WP_CORE_DIR
21
+
22
+ if [ $WP_VERSION == 'latest' ]; then
23
+ local ARCHIVE_NAME='latest'
24
+ else
25
+ local ARCHIVE_NAME="wordpress-$WP_VERSION"
26
+ fi
27
+
28
+ wget -nv -O /tmp/wordpress.tar.gz https://wordpress.org/${ARCHIVE_NAME}.tar.gz
29
+ tar --strip-components=1 -zxmf /tmp/wordpress.tar.gz -C $WP_CORE_DIR
30
+
31
+ wget -nv -O $WP_CORE_DIR/wp-content/db.php https://raw.github.com/markoheijnen/wp-mysqli/master/db.php
32
+ }
33
+
34
+ install_test_suite() {
35
+ # portable in-place argument for both GNU sed and Mac OSX sed
36
+ if [[ $(uname -s) == 'Darwin' ]]; then
37
+ local ioption='-i .bak'
38
+ else
39
+ local ioption='-i'
40
+ fi
41
+
42
+ # set up testing suite
43
+ mkdir -p $WP_TESTS_DIR
44
+ cd $WP_TESTS_DIR
45
+ svn co --quiet https://develop.svn.wordpress.org/trunk/tests/phpunit/includes/
46
+
47
+ wget -nv -O wp-tests-config.php https://develop.svn.wordpress.org/trunk/wp-tests-config-sample.php
48
+ sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR':" wp-tests-config.php
49
+ sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" wp-tests-config.php
50
+ sed $ioption "s/yourusernamehere/$DB_USER/" wp-tests-config.php
51
+ sed $ioption "s/yourpasswordhere/$DB_PASS/" wp-tests-config.php
52
+ sed $ioption "s|localhost|${DB_HOST}|" wp-tests-config.php
53
+ }
54
+
55
+ install_db() {
56
+ # parse DB_HOST for port or socket references
57
+ local PARTS=(${DB_HOST//\:/ })
58
+ local DB_HOSTNAME=${PARTS[0]};
59
+ local DB_SOCK_OR_PORT=${PARTS[1]};
60
+ local EXTRA=""
61
+
62
+ if ! [ -z $DB_HOSTNAME ] ; then
63
+ if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then
64
+ EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp"
65
+ elif ! [ -z $DB_SOCK_OR_PORT ] ; then
66
+ EXTRA=" --socket=$DB_SOCK_OR_PORT"
67
+ elif ! [ -z $DB_HOSTNAME ] ; then
68
+ EXTRA=" --host=$DB_HOSTNAME --protocol=tcp"
69
+ fi
70
+ fi
71
+
72
+ # create database
73
+ mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA
74
+ }
75
+
76
+ install_wp
77
+ install_test_suite
78
+ install_db
vendor/boldgrid/library/composer.json ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "boldgrid/library",
3
+ "description": "The BoldGrid Library for shared code used in official BoldGrid plugins and themes.",
4
+ "type": "library",
5
+ "license": "GPL-2.0-or-later",
6
+ "authors": [
7
+ {
8
+ "name": "Tim Elsass",
9
+ "email": "dev@tim.ph",
10
+ "homepage": "http://tim.ph",
11
+ "role": "Developer"
12
+ },
13
+ {
14
+ "name": "Rafael Ramos",
15
+ "homepage": "http://rafael-ramos.com",
16
+ "role": "Developer"
17
+ },
18
+ {
19
+ "name": "Joe C",
20
+ "email": "joec@boldgrid.com",
21
+ "role": "Developer"
22
+ },
23
+ {
24
+ "name": "bwmarkle",
25
+ "role": "Developer"
26
+ }
27
+ ],
28
+ "autoload": {
29
+ "psr-4": {
30
+ "Boldgrid\\Library\\Util\\": "src/Util"
31
+ }
32
+ }
33
+ }
vendor/boldgrid/library/package-lock.json ADDED
@@ -0,0 +1,1907 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "requires": true,
3
+ "lockfileVersion": 1,
4
+ "dependencies": {
5
+ "abbrev": {
6
+ "version": "1.1.1",
7
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
8
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
9
+ "dev": true
10
+ },
11
+ "acorn": {
12
+ "version": "5.5.3",
13
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz",
14
+ "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==",
15
+ "dev": true
16
+ },
17
+ "acorn-jsx": {
18
+ "version": "3.0.1",
19
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz",
20
+ "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=",
21
+ "dev": true,
22
+ "requires": {
23
+ "acorn": "3.3.0"
24
+ },
25
+ "dependencies": {
26
+ "acorn": {
27
+ "version": "3.3.0",
28
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
29
+ "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=",
30
+ "dev": true
31
+ }
32
+ }
33
+ },
34
+ "ajv": {
35
+ "version": "5.5.2",
36
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
37
+ "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
38
+ "dev": true,
39
+ "requires": {
40
+ "co": "4.6.0",
41
+ "fast-deep-equal": "1.1.0",
42
+ "fast-json-stable-stringify": "2.0.0",
43
+ "json-schema-traverse": "0.3.1"
44
+ }
45
+ },
46
+ "ajv-keywords": {
47
+ "version": "2.1.1",
48
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz",
49
+ "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=",
50
+ "dev": true
51
+ },
52
+ "ansi-escapes": {
53
+ "version": "3.1.0",
54
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz",
55
+ "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==",
56
+ "dev": true
57
+ },
58
+ "ansi-regex": {
59
+ "version": "2.1.1",
60
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
61
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
62
+ "dev": true
63
+ },
64
+ "ansi-styles": {
65
+ "version": "2.2.1",
66
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
67
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
68
+ "dev": true
69
+ },
70
+ "argparse": {
71
+ "version": "1.0.10",
72
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
73
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
74
+ "dev": true,
75
+ "requires": {
76
+ "sprintf-js": "1.0.3"
77
+ }
78
+ },
79
+ "array-union": {
80
+ "version": "1.0.2",
81
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
82
+ "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
83
+ "dev": true,
84
+ "requires": {
85
+ "array-uniq": "1.0.3"
86
+ }
87
+ },
88
+ "array-uniq": {
89
+ "version": "1.0.3",
90
+ "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
91
+ "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=",
92
+ "dev": true
93
+ },
94
+ "arrify": {
95
+ "version": "1.0.1",
96
+ "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
97
+ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
98
+ "dev": true
99
+ },
100
+ "babel-code-frame": {
101
+ "version": "6.26.0",
102
+ "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
103
+ "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
104
+ "dev": true,
105
+ "requires": {
106
+ "chalk": "1.1.3",
107
+ "esutils": "2.0.2",
108
+ "js-tokens": "3.0.2"
109
+ },
110
+ "dependencies": {
111
+ "chalk": {
112
+ "version": "1.1.3",
113
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
114
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
115
+ "dev": true,
116
+ "requires": {
117
+ "ansi-styles": "2.2.1",
118
+ "escape-string-regexp": "1.0.5",
119
+ "has-ansi": "2.0.0",
120
+ "strip-ansi": "3.0.1",
121
+ "supports-color": "2.0.0"
122
+ }
123
+ },
124
+ "strip-ansi": {
125
+ "version": "3.0.1",
126
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
127
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
128
+ "dev": true,
129
+ "requires": {
130
+ "ansi-regex": "2.1.1"
131
+ }
132
+ }
133
+ }
134
+ },
135
+ "babel-runtime": {
136
+ "version": "6.26.0",
137
+ "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
138
+ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
139
+ "dev": true,
140
+ "requires": {
141
+ "core-js": "2.5.4",
142
+ "regenerator-runtime": "0.11.1"
143
+ }
144
+ },
145
+ "balanced-match": {
146
+ "version": "1.0.0",
147
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
148
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
149
+ "dev": true
150
+ },
151
+ "boolify": {
152
+ "version": "1.0.1",
153
+ "resolved": "https://registry.npmjs.org/boolify/-/boolify-1.0.1.tgz",
154
+ "integrity": "sha1-tcCeF8rNET0Rt7s+04TMASmU2Gs=",
155
+ "dev": true
156
+ },
157
+ "brace-expansion": {
158
+ "version": "1.1.11",
159
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
160
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
161
+ "dev": true,
162
+ "requires": {
163
+ "balanced-match": "1.0.0",
164
+ "concat-map": "0.0.1"
165
+ }
166
+ },
167
+ "buffer-from": {
168
+ "version": "1.0.0",
169
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz",
170
+ "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==",
171
+ "dev": true
172
+ },
173
+ "caller-path": {
174
+ "version": "0.1.0",
175
+ "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz",
176
+ "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=",
177
+ "dev": true,
178
+ "requires": {
179
+ "callsites": "0.2.0"
180
+ }
181
+ },
182
+ "callsites": {
183
+ "version": "0.2.0",
184
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz",
185
+ "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=",
186
+ "dev": true
187
+ },
188
+ "camelcase": {
189
+ "version": "4.1.0",
190
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
191
+ "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
192
+ "dev": true
193
+ },
194
+ "camelcase-keys": {
195
+ "version": "4.2.0",
196
+ "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz",
197
+ "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=",
198
+ "dev": true,
199
+ "requires": {
200
+ "camelcase": "4.1.0",
201
+ "map-obj": "2.0.0",
202
+ "quick-lru": "1.1.0"
203
+ }
204
+ },
205
+ "chalk": {
206
+ "version": "2.3.2",
207
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz",
208
+ "integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==",
209
+ "dev": true,
210
+ "requires": {
211
+ "ansi-styles": "3.2.1",
212
+ "escape-string-regexp": "1.0.5",
213
+ "supports-color": "5.3.0"
214
+ },
215
+ "dependencies": {
216
+ "ansi-styles": {
217
+ "version": "3.2.1",
218
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
219
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
220
+ "dev": true,
221
+ "requires": {
222
+ "color-convert": "1.9.1"
223
+ }
224
+ },
225
+ "supports-color": {
226
+ "version": "5.3.0",
227
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.3.0.tgz",
228
+ "integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==",
229
+ "dev": true,
230
+ "requires": {
231
+ "has-flag": "3.0.0"
232
+ }
233
+ }
234
+ }
235
+ },
236
+ "chardet": {
237
+ "version": "0.4.2",
238
+ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz",
239
+ "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=",
240
+ "dev": true
241
+ },
242
+ "circular-json": {
243
+ "version": "0.3.3",
244
+ "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz",
245
+ "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==",
246
+ "dev": true
247
+ },
248
+ "cli-cursor": {
249
+ "version": "2.1.0",
250
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
251
+ "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
252
+ "dev": true,
253
+ "requires": {
254
+ "restore-cursor": "2.0.0"
255
+ }
256
+ },
257
+ "cli-width": {
258
+ "version": "2.2.0",
259
+ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
260
+ "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
261
+ "dev": true
262
+ },
263
+ "cliui": {
264
+ "version": "3.2.0",
265
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
266
+ "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
267
+ "dev": true,
268
+ "requires": {
269
+ "string-width": "1.0.2",
270
+ "strip-ansi": "3.0.1",
271
+ "wrap-ansi": "2.1.0"
272
+ },
273
+ "dependencies": {
274
+ "is-fullwidth-code-point": {
275
+ "version": "1.0.0",
276
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
277
+ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
278
+ "dev": true,
279
+ "requires": {
280
+ "number-is-nan": "1.0.1"
281
+ }
282
+ },
283
+ "string-width": {
284
+ "version": "1.0.2",
285
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
286
+ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
287
+ "dev": true,
288
+ "requires": {
289
+ "code-point-at": "1.1.0",
290
+ "is-fullwidth-code-point": "1.0.0",
291
+ "strip-ansi": "3.0.1"
292
+ }
293
+ },
294
+ "strip-ansi": {
295
+ "version": "3.0.1",
296
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
297
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
298
+ "dev": true,
299
+ "requires": {
300
+ "ansi-regex": "2.1.1"
301
+ }
302
+ }
303
+ }
304
+ },
305
+ "co": {
306
+ "version": "4.6.0",
307
+ "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
308
+ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
309
+ "dev": true
310
+ },
311
+ "code-point-at": {
312
+ "version": "1.1.0",
313
+ "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
314
+ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
315
+ "dev": true
316
+ },
317
+ "color-convert": {
318
+ "version": "1.9.1",
319
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
320
+ "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
321
+ "dev": true,
322
+ "requires": {
323
+ "color-name": "1.1.3"
324
+ }
325
+ },
326
+ "color-name": {
327
+ "version": "1.1.3",
328
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
329
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
330
+ "dev": true
331
+ },
332
+ "common-tags": {
333
+ "version": "1.7.2",
334
+ "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.7.2.tgz",
335
+ "integrity": "sha512-joj9ZlUOjCrwdbmiLqafeUSgkUM74NqhLsZtSqDmhKudaIY197zTrb8JMl31fMnCUuxwFT23eC/oWvrZzDLRJQ==",
336
+ "dev": true,
337
+ "requires": {
338
+ "babel-runtime": "6.26.0"
339
+ }
340
+ },
341
+ "concat-map": {
342
+ "version": "0.0.1",
343
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
344
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
345
+ "dev": true
346
+ },
347
+ "concat-stream": {
348
+ "version": "1.6.2",
349
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
350
+ "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
351
+ "dev": true,
352
+ "requires": {
353
+ "buffer-from": "1.0.0",
354
+ "inherits": "2.0.3",
355
+ "readable-stream": "2.3.5",
356
+ "typedarray": "0.0.6"
357
+ }
358
+ },
359
+ "core-js": {
360
+ "version": "2.5.4",
361
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.4.tgz",
362
+ "integrity": "sha1-8si/GB8qgLkvNgEhQpzmOi8K6uA=",
363
+ "dev": true
364
+ },
365
+ "core-util-is": {
366
+ "version": "1.0.2",
367
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
368
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
369
+ "dev": true
370
+ },
371
+ "cross-spawn": {
372
+ "version": "5.1.0",
373
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
374
+ "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
375
+ "dev": true,
376
+ "requires": {
377
+ "lru-cache": "4.1.2",
378
+ "shebang-command": "1.2.0",
379
+ "which": "1.3.0"
380
+ }
381
+ },
382
+ "debug": {
383
+ "version": "3.1.0",
384
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
385
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
386
+ "dev": true,
387
+ "requires": {
388
+ "ms": "2.0.0"
389
+ }
390
+ },
391
+ "decamelize": {
392
+ "version": "1.2.0",
393
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
394
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
395
+ "dev": true
396
+ },
397
+ "deep-is": {
398
+ "version": "0.1.3",
399
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
400
+ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
401
+ "dev": true
402
+ },
403
+ "del": {
404
+ "version": "2.2.2",
405
+ "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz",
406
+ "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=",
407
+ "dev": true,
408
+ "requires": {
409
+ "globby": "5.0.0",
410
+ "is-path-cwd": "1.0.0",
411
+ "is-path-in-cwd": "1.0.1",
412
+ "object-assign": "4.1.1",
413
+ "pify": "2.3.0",
414
+ "pinkie-promise": "2.0.1",
415
+ "rimraf": "2.6.2"
416
+ }
417
+ },
418
+ "dlv": {
419
+ "version": "1.1.1",
420
+ "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.1.tgz",
421
+ "integrity": "sha512-b/kUB0D6RgRGG69h5ExsLnUAwfs5Jndfk1pU2ao7/9mVdsxpUBlkFdTkNJThXw1jrLXpUbIIg+h3um5zXi6sFA==",
422
+ "dev": true
423
+ },
424
+ "doctrine": {
425
+ "version": "2.1.0",
426
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
427
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
428
+ "dev": true,
429
+ "requires": {
430
+ "esutils": "2.0.2"
431
+ }
432
+ },
433
+ "dom-serializer": {
434
+ "version": "0.1.0",
435
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz",
436
+ "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=",
437
+ "dev": true,
438
+ "requires": {
439
+ "domelementtype": "1.1.3",
440
+ "entities": "1.1.1"
441
+ },
442
+ "dependencies": {
443
+ "domelementtype": {
444
+ "version": "1.1.3",
445
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz",
446
+ "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=",
447
+ "dev": true
448
+ }
449
+ }
450
+ },
451
+ "domelementtype": {
452
+ "version": "1.3.0",
453
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz",
454
+ "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=",
455
+ "dev": true
456
+ },
457
+ "domhandler": {
458
+ "version": "2.4.1",
459
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.1.tgz",
460
+ "integrity": "sha1-iS5HAAqZvlW783dP/qBWHYh5wlk=",
461
+ "dev": true,
462
+ "requires": {
463
+ "domelementtype": "1.3.0"
464
+ }
465
+ },
466
+ "domutils": {
467
+ "version": "1.7.0",
468
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz",
469
+ "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==",
470
+ "dev": true,
471
+ "requires": {
472
+ "dom-serializer": "0.1.0",
473
+ "domelementtype": "1.3.0"
474
+ }
475
+ },
476
+ "entities": {
477
+ "version": "1.1.1",
478
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz",
479
+ "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=",
480
+ "dev": true
481
+ },
482
+ "escape-string-regexp": {
483
+ "version": "1.0.5",
484
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
485
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
486
+ "dev": true
487
+ },
488
+ "eslint": {
489
+ "version": "4.19.1",
490
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz",
491
+ "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==",
492
+ "dev": true,
493
+ "requires": {
494
+ "ajv": "5.5.2",
495
+ "babel-code-frame": "6.26.0",
496
+ "chalk": "2.3.2",
497
+ "concat-stream": "1.6.2",
498
+ "cross-spawn": "5.1.0",
499
+ "debug": "3.1.0",
500
+ "doctrine": "2.1.0",
501
+ "eslint-scope": "3.7.1",
502
+ "eslint-visitor-keys": "1.0.0",
503
+ "espree": "3.5.4",
504
+ "esquery": "1.0.0",
505
+ "esutils": "2.0.2",
506
+ "file-entry-cache": "2.0.0",
507
+ "functional-red-black-tree": "1.0.1",
508
+ "glob": "7.1.2",
509
+ "globals": "11.4.0",
510
+ "ignore": "3.3.7",
511
+ "imurmurhash": "0.1.4",
512
+ "inquirer": "3.3.0",
513
+ "is-resolvable": "1.1.0",
514
+ "js-yaml": "3.11.0",
515
+ "json-stable-stringify-without-jsonify": "1.0.1",
516
+ "levn": "0.3.0",
517
+ "lodash": "4.17.5",
518
+ "minimatch": "3.0.4",
519
+ "mkdirp": "0.5.1",
520
+ "natural-compare": "1.4.0",
521
+ "optionator": "0.8.2",
522
+ "path-is-inside": "1.0.2",
523
+ "pluralize": "7.0.0",
524
+ "progress": "2.0.0",
525
+ "regexpp": "1.1.0",
526
+ "require-uncached": "1.0.3",
527
+ "semver": "5.5.0",
528
+ "strip-ansi": "4.0.0",
529
+ "strip-json-comments": "2.0.1",
530
+ "table": "4.0.2",
531
+ "text-table": "0.2.0"
532
+ }
533
+ },
534
+ "eslint-config-wordpress": {
535
+ "version": "2.0.0",
536
+ "resolved": "https://registry.npmjs.org/eslint-config-wordpress/-/eslint-config-wordpress-2.0.0.tgz",
537
+ "integrity": "sha1-UgEgbGlk1kgxUjLt9t+9LpJeTNY=",
538
+ "dev": true
539
+ },
540
+ "eslint-plugin-html": {
541
+ "version": "4.0.2",
542
+ "resolved": "https://registry.npmjs.org/eslint-plugin-html/-/eslint-plugin-html-4.0.2.tgz",
543
+ "integrity": "sha512-CrQd0F8GWdNWnu4PFrYZl+LjUCXNVy2h0uhDMtnf/7VKc9HRcnkXSrlg0BSGfptZPSzmwnnwCaREAa9+fnQhYw==",
544
+ "dev": true,
545
+ "requires": {
546
+ "htmlparser2": "3.9.2"
547
+ }
548
+ },
549
+ "eslint-scope": {
550
+ "version": "3.7.1",
551
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz",
552
+ "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=",
553
+ "dev": true,
554
+ "requires": {
555
+ "esrecurse": "4.2.1",
556
+ "estraverse": "4.2.0"
557
+ }
558
+ },
559
+ "eslint-visitor-keys": {
560
+ "version": "1.0.0",
561
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
562
+ "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==",
563
+ "dev": true
564
+ },
565
+ "espree": {
566
+ "version": "3.5.4",
567
+ "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz",
568
+ "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==",
569
+ "dev": true,
570
+ "requires": {
571
+ "acorn": "5.5.3",
572
+ "acorn-jsx": "3.0.1"
573
+ }
574
+ },
575
+ "esprima": {
576
+ "version": "4.0.0",
577
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz",
578
+ "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==",
579
+ "dev": true
580
+ },
581
+ "esquery": {
582
+ "version": "1.0.0",
583
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz",
584
+ "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=",
585
+ "dev": true,
586
+ "requires": {
587
+ "estraverse": "4.2.0"
588
+ }
589
+ },
590
+ "esrecurse": {
591
+ "version": "4.2.1",
592
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
593
+ "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
594
+ "dev": true,
595
+ "requires": {
596
+ "estraverse": "4.2.0"
597
+ }
598
+ },
599
+ "estraverse": {
600
+ "version": "4.2.0",
601
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
602
+ "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=",
603
+ "dev": true
604
+ },
605
+ "esutils": {
606
+ "version": "2.0.2",
607
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
608
+ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
609
+ "dev": true
610
+ },
611
+ "execa": {
612
+ "version": "0.7.0",
613
+ "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz",
614
+ "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=",
615
+ "dev": true,
616
+ "requires": {
617
+ "cross-spawn": "5.1.0",
618
+ "get-stream": "3.0.0",
619
+ "is-stream": "1.1.0",
620
+ "npm-run-path": "2.0.2",
621
+ "p-finally": "1.0.0",
622
+ "signal-exit": "3.0.2",
623
+ "strip-eof": "1.0.0"
624
+ }
625
+ },
626
+ "external-editor": {
627
+ "version": "2.1.0",
628
+ "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.1.0.tgz",
629
+ "integrity": "sha512-E44iT5QVOUJBKij4IIV3uvxuNlbKS38Tw1HiupxEIHPv9qtC2PrDYohbXV5U+1jnfIXttny8gUhj+oZvflFlzA==",
630
+ "dev": true,
631
+ "requires": {
632
+ "chardet": "0.4.2",
633
+ "iconv-lite": "0.4.19",
634
+ "tmp": "0.0.33"
635
+ }
636
+ },
637
+ "fast-deep-equal": {
638
+ "version": "1.1.0",
639
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
640
+ "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=",
641
+ "dev": true
642
+ },
643
+ "fast-json-stable-stringify": {
644
+ "version": "2.0.0",
645
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
646
+ "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
647
+ "dev": true
648
+ },
649
+ "fast-levenshtein": {
650
+ "version": "2.0.6",
651
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
652
+ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
653
+ "dev": true
654
+ },
655
+ "figures": {
656
+ "version": "2.0.0",
657
+ "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
658
+ "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
659
+ "dev": true,
660
+ "requires": {
661
+ "escape-string-regexp": "1.0.5"
662
+ }
663
+ },
664
+ "file-entry-cache": {
665
+ "version": "2.0.0",
666
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz",
667
+ "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=",
668
+ "dev": true,
669
+ "requires": {
670
+ "flat-cache": "1.3.0",
671
+ "object-assign": "4.1.1"
672
+ }
673
+ },
674
+ "find-up": {
675
+ "version": "2.1.0",
676
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
677
+ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
678
+ "dev": true,
679
+ "requires": {
680
+ "locate-path": "2.0.0"
681
+ }
682
+ },
683
+ "flat-cache": {
684
+ "version": "1.3.0",
685
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz",
686
+ "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=",
687
+ "dev": true,
688
+ "requires": {
689
+ "circular-json": "0.3.3",
690
+ "del": "2.2.2",
691
+ "graceful-fs": "4.1.11",
692
+ "write": "0.2.1"
693
+ }
694
+ },
695
+ "fs.realpath": {
696
+ "version": "1.0.0",
697
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
698
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
699
+ "dev": true
700
+ },
701
+ "functional-red-black-tree": {
702
+ "version": "1.0.1",
703
+ "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
704
+ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
705
+ "dev": true
706
+ },
707
+ "get-caller-file": {
708
+ "version": "1.0.2",
709
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz",
710
+ "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=",
711
+ "dev": true
712
+ },
713
+ "get-stdin": {
714
+ "version": "5.0.1",
715
+ "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz",
716
+ "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=",
717
+ "dev": true
718
+ },
719
+ "get-stream": {
720
+ "version": "3.0.0",
721
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
722
+ "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
723
+ "dev": true
724
+ },
725
+ "glob": {
726
+ "version": "7.1.2",
727
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
728
+ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
729
+ "dev": true,
730
+ "requires": {
731
+ "fs.realpath": "1.0.0",
732
+ "inflight": "1.0.6",
733
+ "inherits": "2.0.3",
734
+ "minimatch": "3.0.4",
735
+ "once": "1.4.0",
736
+ "path-is-absolute": "1.0.1"
737
+ }
738
+ },
739
+ "globals": {
740
+ "version": "11.4.0",
741
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.4.0.tgz",
742
+ "integrity": "sha512-Dyzmifil8n/TmSqYDEXbm+C8yitzJQqQIlJQLNRMwa+BOUJpRC19pyVeN12JAjt61xonvXjtff+hJruTRXn5HA==",
743
+ "dev": true
744
+ },
745
+ "globby": {
746
+ "version": "5.0.0",
747
+ "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz",
748
+ "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=",
749
+ "dev": true,
750
+ "requires": {
751
+ "array-union": "1.0.2",
752
+ "arrify": "1.0.1",
753
+ "glob": "7.1.2",
754
+ "object-assign": "4.1.1",
755
+ "pify": "2.3.0",
756
+ "pinkie-promise": "2.0.1"
757
+ }
758
+ },
759
+ "graceful-fs": {
760
+ "version": "4.1.11",
761
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
762
+ "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
763
+ "dev": true
764
+ },
765
+ "has-ansi": {
766
+ "version": "2.0.0",
767
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
768
+ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
769
+ "dev": true,
770
+ "requires": {
771
+ "ansi-regex": "2.1.1"
772
+ }
773
+ },
774
+ "has-flag": {
775
+ "version": "3.0.0",
776
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
777
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
778
+ "dev": true
779
+ },
780
+ "htmlparser2": {
781
+ "version": "3.9.2",
782
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz",
783
+ "integrity": "sha1-G9+HrMoPP55T+k/M6w9LTLsAszg=",
784
+ "dev": true,
785
+ "requires": {
786
+ "domelementtype": "1.3.0",
787
+ "domhandler": "2.4.1",
788
+ "domutils": "1.7.0",
789
+ "entities": "1.1.1",
790
+ "inherits": "2.0.3",
791
+ "readable-stream": "2.3.5"
792
+ }
793
+ },
794
+ "iconv-lite": {
795
+ "version": "0.4.19",
796
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
797
+ "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==",
798
+ "dev": true
799
+ },
800
+ "ignore": {
801
+ "version": "3.3.7",
802
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.7.tgz",
803
+ "integrity": "sha512-YGG3ejvBNHRqu0559EOxxNFihD0AjpvHlC/pdGKd3X3ofe+CoJkYazwNJYTNebqpPKN+VVQbh4ZFn1DivMNuHA==",
804
+ "dev": true
805
+ },
806
+ "imurmurhash": {
807
+ "version": "0.1.4",
808
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
809
+ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
810
+ "dev": true
811
+ },
812
+ "indent-string": {
813
+ "version": "3.2.0",
814
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz",
815
+ "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=",
816
+ "dev": true
817
+ },
818
+ "inflight": {
819
+ "version": "1.0.6",
820
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
821
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
822
+ "dev": true,
823
+ "requires": {
824
+ "once": "1.4.0",
825
+ "wrappy": "1.0.2"
826
+ }
827
+ },
828
+ "inherits": {
829
+ "version": "2.0.3",
830
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
831
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
832
+ "dev": true
833
+ },
834
+ "inquirer": {
835
+ "version": "3.3.0",
836
+ "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz",
837
+ "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==",
838
+ "dev": true,
839
+ "requires": {
840
+ "ansi-escapes": "3.1.0",
841
+ "chalk": "2.3.2",
842
+ "cli-cursor": "2.1.0",
843
+ "cli-width": "2.2.0",
844
+ "external-editor": "2.1.0",
845
+ "figures": "2.0.0",
846
+ "lodash": "4.17.5",
847
+ "mute-stream": "0.0.7",
848
+ "run-async": "2.3.0",
849
+ "rx-lite": "4.0.8",
850
+ "rx-lite-aggregates": "4.0.8",
851
+ "string-width": "2.1.1",
852
+ "strip-ansi": "4.0.0",
853
+ "through": "2.3.8"
854
+ }
855
+ },
856
+ "invert-kv": {
857
+ "version": "1.0.0",
858
+ "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
859
+ "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=",
860
+ "dev": true
861
+ },
862
+ "is-fullwidth-code-point": {
863
+ "version": "2.0.0",
864
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
865
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
866
+ "dev": true
867
+ },
868
+ "is-path-cwd": {
869
+ "version": "1.0.0",
870
+ "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz",
871
+ "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=",
872
+ "dev": true
873
+ },
874
+ "is-path-in-cwd": {
875
+ "version": "1.0.1",
876
+ "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz",
877
+ "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==",
878
+ "dev": true,
879
+ "requires": {
880
+ "is-path-inside": "1.0.1"
881
+ }
882
+ },
883
+ "is-path-inside": {
884
+ "version": "1.0.1",
885
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz",
886
+ "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=",
887
+ "dev": true,
888
+ "requires": {
889
+ "path-is-inside": "1.0.2"
890
+ }
891
+ },
892
+ "is-promise": {
893
+ "version": "2.1.0",
894
+ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
895
+ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
896
+ "dev": true
897
+ },
898
+ "is-resolvable": {
899
+ "version": "1.1.0",
900
+ "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz",
901
+ "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==",
902
+ "dev": true
903
+ },
904
+ "is-stream": {
905
+ "version": "1.1.0",
906
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
907
+ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
908
+ "dev": true
909
+ },
910
+ "isarray": {
911
+ "version": "1.0.0",
912
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
913
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
914
+ "dev": true
915
+ },
916
+ "isexe": {
917
+ "version": "2.0.0",
918
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
919
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
920
+ "dev": true
921
+ },
922
+ "js-tokens": {
923
+ "version": "3.0.2",
924
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
925
+ "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
926
+ "dev": true
927
+ },
928
+ "js-yaml": {
929
+ "version": "3.11.0",
930
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.11.0.tgz",
931
+ "integrity": "sha512-saJstZWv7oNeOyBh3+Dx1qWzhW0+e6/8eDzo7p5rDFqxntSztloLtuKu+Ejhtq82jsilwOIZYsCz+lIjthg1Hw==",
932
+ "dev": true,
933
+ "requires": {
934
+ "argparse": "1.0.10",
935
+ "esprima": "4.0.0"
936
+ }
937
+ },
938
+ "json-schema-traverse": {
939
+ "version": "0.3.1",
940
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
941
+ "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=",
942
+ "dev": true
943
+ },
944
+ "json-stable-stringify-without-jsonify": {
945
+ "version": "1.0.1",
946
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
947
+ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
948
+ "dev": true
949
+ },
950
+ "lcid": {
951
+ "version": "1.0.0",
952
+ "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
953
+ "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
954
+ "dev": true,
955
+ "requires": {
956
+ "invert-kv": "1.0.0"
957
+ }
958
+ },
959
+ "levn": {
960
+ "version": "0.3.0",
961
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
962
+ "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
963
+ "dev": true,
964
+ "requires": {
965
+ "prelude-ls": "1.1.2",
966
+ "type-check": "0.3.2"
967
+ }
968
+ },
969
+ "locate-path": {
970
+ "version": "2.0.0",
971
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
972
+ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
973
+ "dev": true,
974
+ "requires": {
975
+ "p-locate": "2.0.0",
976
+ "path-exists": "3.0.0"
977
+ }
978
+ },
979
+ "lodash": {
980
+ "version": "4.17.5",
981
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz",
982
+ "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==",
983
+ "dev": true
984
+ },
985
+ "lodash.memoize": {
986
+ "version": "4.1.2",
987
+ "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
988
+ "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=",
989
+ "dev": true
990
+ },
991
+ "lodash.merge": {
992
+ "version": "4.6.1",
993
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.1.tgz",
994
+ "integrity": "sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==",
995
+ "dev": true
996
+ },
997
+ "lodash.unescape": {
998
+ "version": "4.0.1",
999
+ "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz",
1000
+ "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=",
1001
+ "dev": true
1002
+ },
1003
+ "loglevel": {
1004
+ "version": "1.6.1",
1005
+ "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.1.tgz",
1006
+ "integrity": "sha1-4PyVEztu8nbNyIh82vJKpvFW+Po=",
1007
+ "dev": true
1008
+ },
1009
+ "loglevel-colored-level-prefix": {
1010
+ "version": "1.0.0",
1011
+ "resolved": "https://registry.npmjs.org/loglevel-colored-level-prefix/-/loglevel-colored-level-prefix-1.0.0.tgz",
1012
+ "integrity": "sha1-akAhj9x64V/HbD0PPmdsRlOIYD4=",
1013
+ "dev": true,
1014
+ "requires": {
1015
+ "chalk": "1.1.3",
1016
+ "loglevel": "1.6.1"
1017
+ },
1018
+ "dependencies": {
1019
+ "chalk": {
1020
+ "version": "1.1.3",
1021
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
1022
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
1023
+ "dev": true,
1024
+ "requires": {
1025
+ "ansi-styles": "2.2.1",
1026
+ "escape-string-regexp": "1.0.5",
1027
+ "has-ansi": "2.0.0",
1028
+ "strip-ansi": "3.0.1",
1029
+ "supports-color": "2.0.0"
1030
+ }
1031
+ },
1032
+ "strip-ansi": {
1033
+ "version": "3.0.1",
1034
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
1035
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
1036
+ "dev": true,
1037
+ "requires": {
1038
+ "ansi-regex": "2.1.1"
1039
+ }
1040
+ }
1041
+ }
1042
+ },
1043
+ "lru-cache": {
1044
+ "version": "4.1.2",
1045
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.2.tgz",
1046
+ "integrity": "sha512-wgeVXhrDwAWnIF/yZARsFnMBtdFXOg1b8RIrhilp+0iDYN4mdQcNZElDZ0e4B64BhaxeQ5zN7PMyvu7we1kPeQ==",
1047
+ "dev": true,
1048
+ "requires": {
1049
+ "pseudomap": "1.0.2",
1050
+ "yallist": "2.1.2"
1051
+ }
1052
+ },
1053
+ "make-plural": {
1054
+ "version": "4.1.1",
1055
+ "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-4.1.1.tgz",
1056
+ "integrity": "sha512-triaMVDDYiB+OU1Mz6ht74+z0Bb/bzNESeMwRboSprI3GRWbOvfxEnpWI0eDixQtMPrC2C0revd4wmuck5GcoQ==",
1057
+ "dev": true,
1058
+ "requires": {
1059
+ "minimist": "1.2.0"
1060
+ },
1061
+ "dependencies": {
1062
+ "minimist": {
1063
+ "version": "1.2.0",
1064
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
1065
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
1066
+ "dev": true,
1067
+ "optional": true
1068
+ }
1069
+ }
1070
+ },
1071
+ "map-obj": {
1072
+ "version": "2.0.0",
1073
+ "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz",
1074
+ "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=",
1075
+ "dev": true
1076
+ },
1077
+ "mem": {
1078
+ "version": "1.1.0",
1079
+ "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz",
1080
+ "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=",
1081
+ "dev": true,
1082
+ "requires": {
1083
+ "mimic-fn": "1.2.0"
1084
+ }
1085
+ },
1086
+ "messageformat": {
1087
+ "version": "1.1.1",
1088
+ "resolved": "https://registry.npmjs.org/messageformat/-/messageformat-1.1.1.tgz",
1089
+ "integrity": "sha512-Q0uXcDtF5pEZsVSyhzDOGgZZK6ykN79VY9CwU3Nv0gsqx62BjdJW0MT+63UkHQ4exe3HE33ZlxR2/YwoJarRTg==",
1090
+ "dev": true,
1091
+ "requires": {
1092
+ "glob": "7.0.6",
1093
+ "make-plural": "4.1.1",
1094
+ "messageformat-parser": "1.1.0",
1095
+ "nopt": "3.0.6",
1096
+ "reserved-words": "0.1.2"
1097
+ },
1098
+ "dependencies": {
1099
+ "glob": {
1100
+ "version": "7.0.6",
1101
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz",
1102
+ "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=",
1103
+ "dev": true,
1104
+ "requires": {
1105
+ "fs.realpath": "1.0.0",
1106
+ "inflight": "1.0.6",
1107
+ "inherits": "2.0.3",
1108
+ "minimatch": "3.0.4",
1109
+ "once": "1.4.0",
1110
+ "path-is-absolute": "1.0.1"
1111
+ }
1112
+ }
1113
+ }
1114
+ },
1115
+ "messageformat-parser": {
1116
+ "version": "1.1.0",
1117
+ "resolved": "https://registry.npmjs.org/messageformat-parser/-/messageformat-parser-1.1.0.tgz",
1118
+ "integrity": "sha512-Hwem6G3MsKDLS1FtBRGIs8T50P1Q00r3srS6QJePCFbad9fq0nYxwf3rnU2BreApRGhmpKMV7oZI06Sy1c9TPA==",
1119
+ "dev": true
1120
+ },
1121
+ "mimic-fn": {
1122
+ "version": "1.2.0",
1123
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
1124
+ "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
1125
+ "dev": true
1126
+ },
1127
+ "minimatch": {
1128
+ "version": "3.0.4",
1129
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
1130
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
1131
+ "dev": true,
1132
+ "requires": {
1133
+ "brace-expansion": "1.1.11"
1134
+ }
1135
+ },
1136
+ "minimist": {
1137
+ "version": "0.0.8",
1138
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
1139
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
1140
+ "dev": true
1141
+ },
1142
+ "mkdirp": {
1143
+ "version": "0.5.1",
1144
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
1145
+ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
1146
+ "dev": true,
1147
+ "requires": {
1148
+ "minimist": "0.0.8"
1149
+ }
1150
+ },
1151
+ "ms": {
1152
+ "version": "2.0.0",
1153
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
1154
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
1155
+ "dev": true
1156
+ },
1157
+ "mute-stream": {
1158
+ "version": "0.0.7",
1159
+ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
1160
+ "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
1161
+ "dev": true
1162
+ },
1163
+ "natural-compare": {
1164
+ "version": "1.4.0",
1165
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
1166
+ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
1167
+ "dev": true
1168
+ },
1169
+ "nopt": {
1170
+ "version": "3.0.6",
1171
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
1172
+ "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
1173
+ "dev": true,
1174
+ "requires": {
1175
+ "abbrev": "1.1.1"
1176
+ }
1177
+ },
1178
+ "npm-run-path": {
1179
+ "version": "2.0.2",
1180
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
1181
+ "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
1182
+ "dev": true,
1183
+ "requires": {
1184
+ "path-key": "2.0.1"
1185
+ }
1186
+ },
1187
+ "number-is-nan": {
1188
+ "version": "1.0.1",
1189
+ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
1190
+ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
1191
+ "dev": true
1192
+ },
1193
+ "object-assign": {
1194
+ "version": "4.1.1",
1195
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
1196
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
1197
+ "dev": true
1198
+ },
1199
+ "once": {
1200
+ "version": "1.4.0",
1201
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
1202
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
1203
+ "dev": true,
1204
+ "requires": {
1205
+ "wrappy": "1.0.2"
1206
+ }
1207
+ },
1208
+ "onetime": {
1209
+ "version": "2.0.1",
1210
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
1211
+ "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
1212
+ "dev": true,
1213
+ "requires": {
1214
+ "mimic-fn": "1.2.0"
1215
+ }
1216
+ },
1217
+ "optionator": {
1218
+ "version": "0.8.2",
1219
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
1220
+ "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
1221
+ "dev": true,
1222
+ "requires": {
1223
+ "deep-is": "0.1.3",
1224
+ "fast-levenshtein": "2.0.6",
1225
+ "levn": "0.3.0",
1226
+ "prelude-ls": "1.1.2",
1227
+ "type-check": "0.3.2",
1228
+ "wordwrap": "1.0.0"
1229
+ }
1230
+ },
1231
+ "os-locale": {
1232
+ "version": "2.1.0",
1233
+ "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz",
1234
+ "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==",
1235
+ "dev": true,
1236
+ "requires": {
1237
+ "execa": "0.7.0",
1238
+ "lcid": "1.0.0",
1239
+ "mem": "1.1.0"
1240
+ }
1241
+ },
1242
+ "os-tmpdir": {
1243
+ "version": "1.0.2",
1244
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
1245
+ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
1246
+ "dev": true
1247
+ },
1248
+ "p-finally": {
1249
+ "version": "1.0.0",
1250
+ "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
1251
+ "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
1252
+ "dev": true
1253
+ },
1254
+ "p-limit": {
1255
+ "version": "1.2.0",
1256
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz",
1257
+ "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==",
1258
+ "dev": true,
1259
+ "requires": {
1260
+ "p-try": "1.0.0"
1261
+ }
1262
+ },
1263
+ "p-locate": {
1264
+ "version": "2.0.0",
1265
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
1266
+ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
1267
+ "dev": true,
1268
+ "requires": {
1269
+ "p-limit": "1.2.0"
1270
+ }
1271
+ },
1272
+ "p-try": {
1273
+ "version": "1.0.0",
1274
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
1275
+ "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
1276
+ "dev": true
1277
+ },
1278
+ "path-exists": {
1279
+ "version": "3.0.0",
1280
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
1281
+ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
1282
+ "dev": true
1283
+ },
1284
+ "path-is-absolute": {
1285
+ "version": "1.0.1",
1286
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
1287
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
1288
+ "dev": true
1289
+ },
1290
+ "path-is-inside": {
1291
+ "version": "1.0.2",
1292
+ "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
1293
+ "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
1294
+ "dev": true
1295
+ },
1296
+ "path-key": {
1297
+ "version": "2.0.1",
1298
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
1299
+ "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
1300
+ "dev": true
1301
+ },
1302
+ "pify": {
1303
+ "version": "2.3.0",
1304
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
1305
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
1306
+ "dev": true
1307
+ },
1308
+ "pinkie": {
1309
+ "version": "2.0.4",
1310
+ "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
1311
+ "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
1312
+ "dev": true
1313
+ },
1314
+ "pinkie-promise": {
1315
+ "version": "2.0.1",
1316
+ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
1317
+ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
1318
+ "dev": true,
1319
+ "requires": {
1320
+ "pinkie": "2.0.4"
1321
+ }
1322
+ },
1323
+ "pluralize": {
1324
+ "version": "7.0.0",
1325
+ "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz",
1326
+ "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==",
1327
+ "dev": true
1328
+ },
1329
+ "prelude-ls": {
1330
+ "version": "1.1.2",
1331
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
1332
+ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
1333
+ "dev": true
1334
+ },
1335
+ "prettier": {
1336
+ "version": "1.11.1",
1337
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.11.1.tgz",
1338
+ "integrity": "sha512-T/KD65Ot0PB97xTrG8afQ46x3oiVhnfGjGESSI9NWYcG92+OUPZKkwHqGWXH2t9jK1crnQjubECW0FuOth+hxw==",
1339
+ "dev": true
1340
+ },
1341
+ "prettier-eslint": {
1342
+ "version": "8.8.1",
1343
+ "resolved": "https://registry.npmjs.org/prettier-eslint/-/prettier-eslint-8.8.1.tgz",
1344
+ "integrity": "sha512-8YMkJZnA+XVfEW6fPet05jpNmSQbD+Htbh/QyOxQcVf2GIUEZsnGP7ZScaM9Mq2Ra2261eCu60E7/TRIy9coXQ==",
1345
+ "dev": true,
1346
+ "requires": {
1347
+ "babel-runtime": "6.26.0",
1348
+ "common-tags": "1.7.2",
1349
+ "dlv": "1.1.1",
1350
+ "eslint": "4.19.1",
1351
+ "indent-string": "3.2.0",
1352
+ "lodash.merge": "4.6.1",
1353
+ "loglevel-colored-level-prefix": "1.0.0",
1354
+ "prettier": "1.11.1",
1355
+ "pretty-format": "22.4.3",
1356
+ "require-relative": "0.8.7",
1357
+ "typescript": "2.8.1",
1358
+ "typescript-eslint-parser": "11.0.0"
1359
+ }
1360
+ },
1361
+ "prettier-eslint-cli": {
1362
+ "version": "4.7.1",
1363
+ "resolved": "https://registry.npmjs.org/prettier-eslint-cli/-/prettier-eslint-cli-4.7.1.tgz",
1364
+ "integrity": "sha512-hQbsGaEVz97oBBcKdsJ46khv0kOGkMyWrXzcFOXW6X8UuetZ/j0yDJkNJgUTVc6PVFbbzBXk+qgd5vos9qzXPQ==",
1365
+ "dev": true,
1366
+ "requires": {
1367
+ "arrify": "1.0.1",
1368
+ "babel-runtime": "6.26.0",
1369
+ "boolify": "1.0.1",
1370
+ "camelcase-keys": "4.2.0",
1371
+ "chalk": "2.3.0",
1372
+ "common-tags": "1.7.2",
1373
+ "eslint": "4.19.1",
1374
+ "find-up": "2.1.0",
1375
+ "get-stdin": "5.0.1",
1376
+ "glob": "7.1.2",
1377
+ "ignore": "3.3.7",
1378
+ "indent-string": "3.2.0",
1379
+ "lodash.memoize": "4.1.2",
1380
+ "loglevel-colored-level-prefix": "1.0.0",
1381
+ "messageformat": "1.1.1",
1382
+ "prettier-eslint": "8.8.1",
1383
+ "rxjs": "5.5.8",
1384
+ "yargs": "10.0.3"
1385
+ },
1386
+ "dependencies": {
1387
+ "ansi-styles": {
1388
+ "version": "3.2.1",
1389
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
1390
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
1391
+ "dev": true,
1392
+ "requires": {
1393
+ "color-convert": "1.9.1"
1394
+ }
1395
+ },
1396
+ "chalk": {
1397
+ "version": "2.3.0",
1398
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz",
1399
+ "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==",
1400
+ "dev": true,
1401
+ "requires": {
1402
+ "ansi-styles": "3.2.1",
1403
+ "escape-string-regexp": "1.0.5",
1404
+ "supports-color": "4.5.0"
1405
+ }
1406
+ },
1407
+ "has-flag": {
1408
+ "version": "2.0.0",
1409
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz",
1410
+ "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=",
1411
+ "dev": true
1412
+ },
1413
+ "supports-color": {
1414
+ "version": "4.5.0",
1415
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz",
1416
+ "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=",
1417
+ "dev": true,
1418
+ "requires": {
1419
+ "has-flag": "2.0.0"
1420
+ }
1421
+ }
1422
+ }
1423
+ },
1424
+ "pretty-format": {
1425
+ "version": "22.4.3",
1426
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-22.4.3.tgz",
1427
+ "integrity": "sha512-S4oT9/sT6MN7/3COoOy+ZJeA92VmOnveLHgrwBE3Z1W5N9S2A1QGNYiE1z75DAENbJrXXUb+OWXhpJcg05QKQQ==",
1428
+ "dev": true,
1429
+ "requires": {
1430
+ "ansi-regex": "3.0.0",
1431
+ "ansi-styles": "3.2.1"
1432
+ },
1433
+ "dependencies": {
1434
+ "ansi-regex": {
1435
+ "version": "3.0.0",
1436
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
1437
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
1438
+ "dev": true
1439
+ },
1440
+ "ansi-styles": {
1441
+ "version": "3.2.1",
1442
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
1443
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
1444
+ "dev": true,
1445
+ "requires": {
1446
+ "color-convert": "1.9.1"
1447
+ }
1448
+ }
1449
+ }
1450
+ },
1451
+ "process-nextick-args": {
1452
+ "version": "2.0.0",
1453
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
1454
+ "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
1455
+ "dev": true
1456
+ },
1457
+ "progress": {
1458
+ "version": "2.0.0",
1459
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz",
1460
+ "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=",
1461
+ "dev": true
1462
+ },
1463
+ "pseudomap": {
1464
+ "version": "1.0.2",
1465
+ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
1466
+ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
1467
+ "dev": true
1468
+ },
1469
+ "quick-lru": {
1470
+ "version": "1.1.0",
1471
+ "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz",
1472
+ "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=",
1473
+ "dev": true
1474
+ },
1475
+ "readable-stream": {
1476
+ "version": "2.3.5",
1477
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz",
1478
+ "integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==",
1479
+ "dev": true,
1480
+ "requires": {
1481
+ "core-util-is": "1.0.2",
1482
+ "inherits": "2.0.3",
1483
+ "isarray": "1.0.0",
1484
+ "process-nextick-args": "2.0.0",
1485
+ "safe-buffer": "5.1.1",
1486
+ "string_decoder": "1.0.3",
1487
+ "util-deprecate": "1.0.2"
1488
+ }
1489
+ },
1490
+ "regenerator-runtime": {
1491
+ "version": "0.11.1",
1492
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
1493
+ "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
1494
+ "dev": true
1495
+ },
1496
+ "regexpp": {
1497
+ "version": "1.1.0",
1498
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz",
1499
+ "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==",
1500
+ "dev": true
1501
+ },
1502
+ "require-directory": {
1503
+ "version": "2.1.1",
1504
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
1505
+ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
1506
+ "dev": true
1507
+ },
1508
+ "require-main-filename": {
1509
+ "version": "1.0.1",
1510
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
1511
+ "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
1512
+ "dev": true
1513
+ },
1514
+ "require-relative": {
1515
+ "version": "0.8.7",
1516
+ "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz",
1517
+ "integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=",
1518
+ "dev": true
1519
+ },
1520
+ "require-uncached": {
1521
+ "version": "1.0.3",
1522
+ "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz",
1523
+ "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=",
1524
+ "dev": true,
1525
+ "requires": {
1526
+ "caller-path": "0.1.0",
1527
+ "resolve-from": "1.0.1"
1528
+ }
1529
+ },
1530
+ "reserved-words": {
1531
+ "version": "0.1.2",
1532
+ "resolved": "https://registry.npmjs.org/reserved-words/-/reserved-words-0.1.2.tgz",
1533
+ "integrity": "sha1-AKCUD5jNUBrqqsMWQR2a3FKzGrE=",
1534
+ "dev": true
1535
+ },
1536
+ "resolve-from": {
1537
+ "version": "1.0.1",
1538
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz",
1539
+ "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=",
1540
+ "dev": true
1541
+ },
1542
+ "restore-cursor": {
1543
+ "version": "2.0.0",
1544
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
1545
+ "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
1546
+ "dev": true,
1547
+ "requires": {
1548
+ "onetime": "2.0.1",
1549
+ "signal-exit": "3.0.2"
1550
+ }
1551
+ },
1552
+ "rimraf": {
1553
+ "version": "2.6.2",
1554
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
1555
+ "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
1556
+ "dev": true,
1557
+ "requires": {
1558
+ "glob": "7.1.2"
1559
+ }
1560
+ },
1561
+ "run-async": {
1562
+ "version": "2.3.0",
1563
+ "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
1564
+ "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
1565
+ "dev": true,
1566
+ "requires": {
1567
+ "is-promise": "2.1.0"
1568
+ }
1569
+ },
1570
+ "rx-lite": {
1571
+ "version": "4.0.8",
1572
+ "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz",
1573
+ "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=",
1574
+ "dev": true
1575
+ },
1576
+ "rx-lite-aggregates": {
1577
+ "version": "4.0.8",
1578
+ "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz",
1579
+ "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=",
1580
+ "dev": true,
1581
+ "requires": {
1582
+ "rx-lite": "4.0.8"
1583
+ }
1584
+ },
1585
+ "rxjs": {
1586
+ "version": "5.5.8",
1587
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.8.tgz",
1588
+ "integrity": "sha512-Bz7qou7VAIoGiglJZbzbXa4vpX5BmTTN2Dj/se6+SwADtw4SihqBIiEa7VmTXJ8pynvq0iFr5Gx9VLyye1rIxQ==",
1589
+ "dev": true,
1590
+ "requires": {
1591
+ "symbol-observable": "1.0.1"
1592
+ }
1593
+ },
1594
+ "safe-buffer": {
1595
+ "version": "5.1.1",
1596
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
1597
+ "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
1598
+ "dev": true
1599
+ },
1600
+ "semver": {
1601
+ "version": "5.5.0",
1602
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
1603
+ "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
1604
+ "dev": true
1605
+ },
1606
+ "set-blocking": {
1607
+ "version": "2.0.0",
1608
+ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
1609
+ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
1610
+ "dev": true
1611
+ },
1612
+ "shebang-command": {
1613
+ "version": "1.2.0",
1614
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
1615
+ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
1616
+ "dev": true,
1617
+ "requires": {
1618
+ "shebang-regex": "1.0.0"
1619
+ }
1620
+ },
1621
+ "shebang-regex": {
1622
+ "version": "1.0.0",
1623
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
1624
+ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
1625
+ "dev": true
1626
+ },
1627
+ "signal-exit": {
1628
+ "version": "3.0.2",
1629
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
1630
+ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
1631
+ "dev": true
1632
+ },
1633
+ "slice-ansi": {
1634
+ "version": "1.0.0",
1635
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz",
1636
+ "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==",
1637
+ "dev": true,
1638
+ "requires": {
1639
+ "is-fullwidth-code-point": "2.0.0"
1640
+ }
1641
+ },
1642
+ "sprintf-js": {
1643
+ "version": "1.0.3",
1644
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
1645
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
1646
+ "dev": true
1647
+ },
1648
+ "string-width": {
1649
+ "version": "2.1.1",
1650
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
1651
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
1652
+ "dev": true,
1653
+ "requires": {
1654
+ "is-fullwidth-code-point": "2.0.0",
1655
+ "strip-ansi": "4.0.0"
1656
+ }
1657
+ },
1658
+ "string_decoder": {
1659
+ "version": "1.0.3",
1660
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
1661
+ "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
1662
+ "dev": true,
1663
+ "requires": {
1664
+ "safe-buffer": "5.1.1"
1665
+ }
1666
+ },
1667
+ "strip-ansi": {
1668
+ "version": "4.0.0",
1669
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
1670
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
1671
+ "dev": true,
1672
+ "requires": {
1673
+ "ansi-regex": "3.0.0"
1674
+ },
1675
+ "dependencies": {
1676
+ "ansi-regex": {
1677
+ "version": "3.0.0",
1678
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
1679
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
1680
+ "dev": true
1681
+ }
1682
+ }
1683
+ },
1684
+ "strip-eof": {
1685
+ "version": "1.0.0",
1686
+ "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
1687
+ "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
1688
+ "dev": true
1689
+ },
1690
+ "strip-json-comments": {
1691
+ "version": "2.0.1",
1692
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
1693
+ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
1694
+ "dev": true
1695
+ },
1696
+ "supports-color": {
1697
+ "version": "2.0.0",
1698
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
1699
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
1700
+ "dev": true
1701
+ },
1702
+ "symbol-observable": {
1703
+ "version": "1.0.1",
1704
+ "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz",
1705
+ "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=",
1706
+ "dev": true
1707
+ },
1708
+ "table": {
1709
+ "version": "4.0.2",
1710
+ "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz",
1711
+ "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==",
1712
+ "dev": true,
1713
+ "requires": {
1714
+ "ajv": "5.5.2",
1715
+ "ajv-keywords": "2.1.1",
1716
+ "chalk": "2.3.2",
1717
+ "lodash": "4.17.5",
1718
+ "slice-ansi": "1.0.0",
1719
+ "string-width": "2.1.1"
1720
+ }
1721
+ },
1722
+ "text-table": {
1723
+ "version": "0.2.0",
1724
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
1725
+ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
1726
+ "dev": true
1727
+ },
1728
+ "through": {
1729
+ "version": "2.3.8",
1730
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
1731
+ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
1732
+ "dev": true
1733
+ },
1734
+ "tmp": {
1735
+ "version": "0.0.33",
1736
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
1737
+ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
1738
+ "dev": true,
1739
+ "requires": {
1740
+ "os-tmpdir": "1.0.2"
1741
+ }
1742
+ },
1743
+ "type-check": {
1744
+ "version": "0.3.2",
1745
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
1746
+ "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
1747
+ "dev": true,
1748
+ "requires": {
1749
+ "prelude-ls": "1.1.2"
1750
+ }
1751
+ },
1752
+ "typedarray": {
1753
+ "version": "0.0.6",
1754
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
1755
+ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
1756
+ "dev": true
1757
+ },
1758
+ "typescript": {
1759
+ "version": "2.8.1",
1760
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.8.1.tgz",
1761
+ "integrity": "sha512-Ao/f6d/4EPLq0YwzsQz8iXflezpTkQzqAyenTiw4kCUGr1uPiFLC3+fZ+gMZz6eeI/qdRUqvC+HxIJzUAzEFdg==",
1762
+ "dev": true
1763
+ },
1764
+ "typescript-eslint-parser": {
1765
+ "version": "11.0.0",
1766
+ "resolved": "https://registry.npmjs.org/typescript-eslint-parser/-/typescript-eslint-parser-11.0.0.tgz",
1767
+ "integrity": "sha512-/fBHTBRBSorWQGKWOOjeMPkzd3o8cOPtFjTRwU5JLNGgVtmMa3KDkiw0R2n+H6ovo9y3OX30/5usm6YTqY44PQ==",
1768
+ "dev": true,
1769
+ "requires": {
1770
+ "lodash.unescape": "4.0.1",
1771
+ "semver": "5.4.1"
1772
+ },
1773
+ "dependencies": {
1774
+ "semver": {
1775
+ "version": "5.4.1",
1776
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
1777
+ "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==",
1778
+ "dev": true
1779
+ }
1780
+ }
1781
+ },
1782
+ "util-deprecate": {
1783
+ "version": "1.0.2",
1784
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
1785
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
1786
+ "dev": true
1787
+ },
1788
+ "which": {
1789
+ "version": "1.3.0",
1790
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz",
1791
+ "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==",
1792
+ "dev": true,
1793
+ "requires": {
1794
+ "isexe": "2.0.0"
1795
+ }
1796
+ },
1797
+ "which-module": {
1798
+ "version": "2.0.0",
1799
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
1800
+ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
1801
+ "dev": true
1802
+ },
1803
+ "wordwrap": {
1804
+ "version": "1.0.0",
1805
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
1806
+ "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
1807
+ "dev": true
1808
+ },
1809
+ "wrap-ansi": {
1810
+ "version": "2.1.0",
1811
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
1812
+ "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
1813
+ "dev": true,
1814
+ "requires": {
1815
+ "string-width": "1.0.2",
1816
+ "strip-ansi": "3.0.1"
1817
+ },
1818
+ "dependencies": {
1819
+ "is-fullwidth-code-point": {
1820
+ "version": "1.0.0",
1821
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
1822
+ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
1823
+ "dev": true,
1824
+ "requires": {
1825
+ "number-is-nan": "1.0.1"
1826
+ }
1827
+ },
1828
+ "string-width": {
1829
+ "version": "1.0.2",
1830
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
1831
+ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
1832
+ "dev": true,
1833
+ "requires": {
1834
+ "code-point-at": "1.1.0",
1835
+ "is-fullwidth-code-point": "1.0.0",
1836
+ "strip-ansi": "3.0.1"
1837
+ }
1838
+ },
1839
+ "strip-ansi": {
1840
+ "version": "3.0.1",
1841
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
1842
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
1843
+ "dev": true,
1844
+ "requires": {
1845
+ "ansi-regex": "2.1.1"
1846
+ }
1847
+ }
1848
+ }
1849
+ },
1850
+ "wrappy": {
1851
+ "version": "1.0.2",
1852
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
1853
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
1854
+ "dev": true
1855
+ },
1856
+ "write": {
1857
+ "version": "0.2.1",
1858
+ "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz",
1859
+ "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=",
1860
+ "dev": true,
1861
+ "requires": {
1862
+ "mkdirp": "0.5.1"
1863
+ }
1864
+ },
1865
+ "y18n": {
1866
+ "version": "3.2.1",
1867
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
1868
+ "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
1869
+ "dev": true
1870
+ },
1871
+ "yallist": {
1872
+ "version": "2.1.2",
1873
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
1874
+ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
1875
+ "dev": true
1876
+ },
1877
+ "yargs": {
1878
+ "version": "10.0.3",
1879
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-10.0.3.tgz",
1880
+ "integrity": "sha512-DqBpQ8NAUX4GyPP/ijDGHsJya4tYqLQrjPr95HNsr1YwL3+daCfvBwg7+gIC6IdJhR2kATh3hb61vjzMWEtjdw==",
1881
+ "dev": true,
1882
+ "requires": {
1883
+ "cliui": "3.2.0",
1884
+ "decamelize": "1.2.0",
1885
+ "find-up": "2.1.0",
1886
+ "get-caller-file": "1.0.2",
1887
+ "os-locale": "2.1.0",
1888
+ "require-directory": "2.1.1",
1889
+ "require-main-filename": "1.0.1",
1890
+ "set-blocking": "2.0.0",
1891
+ "string-width": "2.1.1",
1892
+ "which-module": "2.0.0",
1893
+ "y18n": "3.2.1",
1894
+ "yargs-parser": "8.1.0"
1895
+ }
1896
+ },
1897
+ "yargs-parser": {
1898
+ "version": "8.1.0",
1899
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-8.1.0.tgz",
1900
+ "integrity": "sha512-yP+6QqN8BmrgW2ggLtTbdrOyBNSI7zBa4IykmiV5R1wl1JWNxQvWhMfMdmzIYtKU7oP3OOInY/tl2ov3BDjnJQ==",
1901
+ "dev": true,
1902
+ "requires": {
1903
+ "camelcase": "4.1.0"
1904
+ }
1905
+ }
1906
+ }
1907
+ }
vendor/boldgrid/library/package.json ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "scripts": {
3
+ "js-lint": "prettier-eslint **/*.js --list-different",
4
+ "js-lint-fix": "prettier-eslint **/*.js --write"
5
+ },
6
+ "devDependencies": {
7
+ "eslint": "^4.19.1",
8
+ "eslint-config-wordpress": "^2.0.0",
9
+ "eslint-plugin-html": "^4.0.2",
10
+ "prettier-eslint": "^8.8.1",
11
+ "prettier-eslint-cli": "^4.7.1"
12
+ }
13
+ }
vendor/boldgrid/library/phpunit.xml ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <phpunit
2
+ bootstrap="tests/bootstrap.php"
3
+ backupGlobals="false"
4
+ colors="true"
5
+ convertErrorsToExceptions="true"
6
+ convertNoticesToExceptions="true"
7
+ convertWarningsToExceptions="true"
8
+ >
9
+ <testsuites>
10
+ <testsuite>
11
+ <directory prefix="test-" suffix=".php">./tests/</directory>
12
+ </testsuite>
13
+ </testsuites>
14
+ <filter>
15
+ <whitelist>
16
+ <directory>./</directory>
17
+ </whitelist>
18
+ </filter>
19
+ </phpunit>
vendor/boldgrid/library/src/Library/Api/Availability.php ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Library API Availability.
4
+ *
5
+ * @package Boldgrid\Library
6
+ * @subpackage \Library\Api
7
+ *
8
+ * @version 1.0.0
9
+ * @author BoldGrid <wpb@boldgrid.com>
10
+ */
11
+
12
+ namespace Boldgrid\Library\Library\Api;
13
+
14
+ use \WP_Http;
15
+
16
+ /**
17
+ * BoldGrid Library API Availability class.
18
+ *
19
+ * This class is responsible for checking that a server is
20
+ * able to make calls to the API, and that the API is responsive.
21
+ *
22
+ * @since 1.0.0
23
+ */
24
+ class Availability {
25
+
26
+ /**
27
+ * @var array $url URL to make call to.
28
+ * @var array Request parameters
29
+ */
30
+ private
31
+ $available,
32
+ $url;
33
+
34
+ /**
35
+ * Initialize class and set class properties.
36
+ *
37
+ * @since 1.0.0
38
+ *
39
+ * @param string $url The API URL to test.
40
+ */
41
+ public function __construct( $url ) {
42
+ $this->setUrl( $url );
43
+ $this->setAvailable();
44
+ }
45
+
46
+ /**
47
+ * Sets the API host URL as url class property.
48
+ *
49
+ * @since 1.0.0
50
+ *
51
+ * @param string $url The URL to set.
52
+ *
53
+ * @return string $url The url class property.
54
+ */
55
+ protected function setUrl( $url ) {
56
+ return $this->url = $url;
57
+ }
58
+
59
+ /**
60
+ * Sets the availablity status of API host.
61
+ *
62
+ * @since 1.0.0
63
+ *
64
+ * @return bool $available The available class property.
65
+ */
66
+ protected function setAvailable() {
67
+ return $this->available = $this->checkAvailability();
68
+ }
69
+
70
+ /**
71
+ * Checks that the API can be reached.
72
+ *
73
+ * @since 1.0.0
74
+ *
75
+ * @return bool
76
+ */
77
+ private function checkAvailability() {
78
+
79
+ // Get the boldgrid_available transient.
80
+ $available = get_site_transient( 'boldgrid_available' );
81
+
82
+ // No transient was found.
83
+ $wp_http = new WP_Http();
84
+ $url = $this->getUrl();
85
+
86
+ // Check that calling server is not blocked locally.
87
+ if ( $wp_http->block_request( $url ) === false ) {
88
+ $available = 1;
89
+ }
90
+
91
+ // Update the boldgrid_available transient.
92
+ set_site_transient( 'boldgrid_available', ( int ) $available, 5 * MINUTE_IN_SECONDS );
93
+
94
+ return $available;
95
+ }
96
+
97
+ /**
98
+ * Get the API Host URL that is being tested from the url class property.
99
+ *
100
+ * @since 1.0.0
101
+ *
102
+ * @return string $url The API url to test availability of.
103
+ */
104
+ public function getUrl() {
105
+ return $this->url;
106
+ }
107
+
108
+ /**
109
+ * Get the API availability status from availability class property.
110
+ *
111
+ * @since 1.0.0
112
+ *
113
+ * @return bool $available The available class property.
114
+ */
115
+ public function getAvailable() {
116
+ return $this->available;
117
+ }
118
+ }
vendor/boldgrid/library/src/Library/Api/Call.php ADDED
@@ -0,0 +1,270 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Library API Call.
4
+ *
5
+ * @package Boldgrid\Library
6
+ * @subpackage \Library\Api
7
+ *
8
+ * @version 1.0.0
9
+ * @author BoldGrid <wpb@boldgrid.com>
10
+ */
11
+
12
+ namespace Boldgrid\Library\Library\Api;
13
+
14
+ use Boldgrid\Library\Library\Configs;
15
+
16
+ /**
17
+ * BoldGrid Library API Call Class.
18
+ *
19
+ * This class is responsible for making API calls
20
+ * and returning back the appropriate responses.
21
+ *
22
+ * @since 1.0.0
23
+ */
24
+ class Call {
25
+
26
+ /**
27
+ * @access private
28
+ *
29
+ * @var array $url URL to make call to.
30
+ * @var string $key The API key for request.
31
+ * @var string $siteHash The site hash for request.
32
+ * @var array $args Request parameters
33
+ * @var object $response The API call response object.
34
+ * @var string $error The API call error message.
35
+ */
36
+ private
37
+ $url,
38
+ $key,
39
+ $siteHash,
40
+ $args,
41
+ $response,
42
+ $error;
43
+
44
+ /**
45
+ * Request method.
46
+ *
47
+ * @since 2.2.0
48
+ * @access private
49
+ *
50
+ * @var string
51
+ */
52
+ private $method;
53
+
54
+ /**
55
+ * Initialize class and set class properties.
56
+ *
57
+ * @since 1.0.0
58
+ *
59
+ * @param string $url The URL to make API calls to.
60
+ * @param array $args The arguments to pass to API call.
61
+ */
62
+ public function __construct( $url, array $args = array(), $method = 'post' ) {
63
+ $this->setKey();
64
+ $this->setSiteHash();
65
+ $this->setUrl( $url );
66
+ $this->setArgs( $args );
67
+ $this->setMethod( $method );
68
+ $availability = new Availability( Configs::get( 'api' ) );
69
+ if ( $availability->getAvailable() ) {
70
+ $this->call();
71
+ } else {
72
+ $this->error = 'The API was not able to be reached from this server!';
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Sets the url class property.
78
+ *
79
+ * @since 1.0.0
80
+ *
81
+ * @param string $url The url that the API call will be made to.
82
+ *
83
+ * @return string $url The url class property.
84
+ */
85
+ protected function setUrl( $url ) {
86
+ return $this->url = $url;
87
+ }
88
+
89
+ /**
90
+ * Sets the key class property.
91
+ *
92
+ * @since 1.0.0
93
+ *
94
+ * @return mixed $key The key string or false if not found.
95
+ */
96
+ protected function setKey() {
97
+ return $this->key = get_option( 'boldgrid_api_key' );
98
+ }
99
+
100
+ /**
101
+ * Sets the siteHash class property.
102
+ *
103
+ * @since 1.0.0
104
+ *
105
+ * @return mixed $siteHash The site hash string or false if not found.
106
+ */
107
+ protected function setSiteHash() {
108
+ return $this->siteHash = get_option( 'boldgrid_site_hash' );
109
+ }
110
+
111
+ /**
112
+ * Sets the args class property.
113
+ *
114
+ * These are the arguments being passed for the API call parameters.
115
+ *
116
+ * @since 1.0.0
117
+ *
118
+ * @param array $args API call arguments.
119
+ *
120
+ * @return array $args The args class property.
121
+ */
122
+ protected function setArgs( $args ) {
123
+
124
+ // Check for an API key being stored.
125
+ if ( $this->getKey() ) {
126
+ $args = wp_parse_args( $args, array( 'key' => $this->getKey() ) );
127
+ }
128
+
129
+ // Check for a site hash being stored.
130
+ if ( $this->getSiteHash() ) {
131
+ $args = wp_parse_args( $args, array( 'site_hash' => $this->getSiteHash() ) );
132
+ }
133
+
134
+ return $this->args = array( 'body' => $args );
135
+ }
136
+
137
+ /**
138
+ * Sets the request method.
139
+ *
140
+ * @since 2.2.0
141
+ *
142
+ * @param string $method Request method.
143
+ * @return string
144
+ */
145
+ protected function setMethod( $method ) {
146
+ return $this->method = $method;
147
+ }
148
+
149
+ /**
150
+ * Makes the API call.
151
+ *
152
+ * @since 1.0.0
153
+ *
154
+ * @return bool Was the API call successful?
155
+ */
156
+ private function call() {
157
+ // Make the request.
158
+ if ( 'get' === $this->method ) {
159
+ $response = wp_remote_get( $this->url, $this->args );
160
+ } else {
161
+ $response = wp_remote_post( $this->url, $this->args );
162
+ }
163
+
164
+ // Decode the response and set class property.
165
+ $this->response = json_decode( wp_remote_retrieve_body( $response ) );
166
+
167
+ // Validate the raw response.
168
+ if ( $this->validateResponse( $response ) === false ) {
169
+ return false;
170
+ }
171
+
172
+ // Response should be an object.
173
+ if ( ! is_object( $this->response ) ) {
174
+ $this->error = __( 'An invalid response was returned.', 'boldgrid-inspirations' );
175
+ return false;
176
+ }
177
+
178
+ return true;
179
+ }
180
+
181
+ /**
182
+ * Validates the API response.
183
+ *
184
+ * @since 1.0.0
185
+ *
186
+ * @param object $response API response object.
187
+ *
188
+ * @return bool
189
+ */
190
+ private function validateResponse( $response ) {
191
+
192
+ // Make sure WordPress errors are handled.
193
+ if ( is_wp_error( $response ) ) {
194
+ $this->error = $response->get_error_message();
195
+ return false;
196
+ }
197
+
198
+ // Check for 200 response code from server.
199
+ $responseCode = wp_remote_retrieve_response_code( $response );
200
+ if ( false === strstr( $responseCode, '200' ) ) {
201
+ $responseMessage = wp_remote_retrieve_response_message( $response );
202
+ $this->error = "{$responseCode} {$responseMessage}";
203
+ return false;
204
+ }
205
+
206
+ // Check for nested status code not being successful.
207
+ if ( isset( $this->response->status ) && 200 !== $this->response->status ) {
208
+ $this->error = $this->response->result->data;
209
+ return false;
210
+ }
211
+
212
+ return true;
213
+ }
214
+
215
+ /**
216
+ * Gets the API Key as the key class property.
217
+ *
218
+ * @since 1.0.0
219
+ *
220
+ * @return string $key The key class property.
221
+ */
222
+ public function getKey() {
223
+ return $this->key;
224
+ }
225
+
226
+ /**
227
+ * Gets the Site Hash as the siteHash class property.
228
+ *
229
+ * @since 1.0.0
230
+ *
231
+ * @return string $siteHash The siteHash class property.
232
+ */
233
+ public function getSiteHash() {
234
+ return $this->siteHash;
235
+ }
236
+
237
+ /**
238
+ * Gets the API base URL.
239
+ *
240
+ * @since 1.0.0
241
+ *
242
+ * @return string $url The API base URL as url class property.
243
+ */
244
+ private function getUrl() {
245
+ return $this->url;
246
+ }
247
+
248
+ /**
249
+ * Gets the error messages.
250
+ *
251
+ * @since 1.0.0
252
+ *
253
+ * @return string $error Error message for call as response class property.
254
+ */
255
+ public function getError() {
256
+ return $this->error;
257
+ }
258
+
259
+ /**
260
+ * Gets the API response message.
261
+ *
262
+ * @since 1.0.0
263
+ *
264
+ * @return mixed $response Success response as the response class property.
265
+ */
266
+ public function getResponse() {
267
+ return $this->response;
268
+ }
269
+
270
+ }
vendor/boldgrid/library/src/Library/Configs.php ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Library Configs Class
4
+ *
5
+ * @package Boldgrid\Library
6
+ * @subpackage \Library
7
+ *
8
+ * @version 1.0.0
9
+ * @author BoldGrid <wpb@boldgrid.com>
10
+ */
11
+
12
+ namespace Boldgrid\Library\Library;
13
+
14
+ /**
15
+ * BoldGrid Library Configs Class.
16
+ *
17
+ * This class is responsible for setting and getting configuration
18
+ * options that are set for the library.
19
+ *
20
+ * @since 1.0.0
21
+ */
22
+ class Configs {
23
+
24
+ /**
25
+ * @access private
26
+ *
27
+ * @var array $configs Configuration options.
28
+ */
29
+ private static $configs;
30
+
31
+
32
+ /**
33
+ * Initialize class and set class properties.
34
+ *
35
+ * @since 1.0.0
36
+ *
37
+ * @param array $configs Plugin configuration array.
38
+ */
39
+ public function __construct( $configs = null ) {
40
+ $defaults = include_once dirname( __DIR__ ) . '/library.global.php';
41
+ self::set( $configs, $defaults );
42
+ }
43
+
44
+ /**
45
+ * [set description]
46
+ *
47
+ * @since 1.0.0
48
+ *
49
+ * @param [type] $configs [description]
50
+ * @param [type] $defaults [description]
51
+ *
52
+ * @return object $this self.
53
+ */
54
+ public static function set( $configs, $defaults = null ) {
55
+ $defaults = $defaults ? $defaults : self::get();
56
+
57
+ // Check if local library file is added.
58
+ $localPath = dirname( __DIR__ ) . '/library.local.php';
59
+ if ( file_exists( $localPath ) && is_readable( $localPath ) ) {
60
+ $local = include_once $localPath;
61
+ $defaults = wp_parse_args( $local, $defaults );
62
+ }
63
+
64
+ // Check if constant is added.
65
+ if ( defined( 'BGLIB_CONFIGS' ) ) {
66
+ $localPath = ABSPATH . BGLIB_CONFIGS;
67
+ if ( file_exists( $localPath ) && is_readable( $localPath ) ) {
68
+ $local = include_once $localPath;
69
+ $defaults = wp_parse_args( $local, $defaults );
70
+ }
71
+ }
72
+
73
+ return self::$configs = wp_parse_args( $configs, $defaults );
74
+ }
75
+
76
+ /**
77
+ * Get configs or config by key.
78
+ *
79
+ * @since 1.0.0
80
+ *
81
+ * @param [type] $key [description]
82
+ *
83
+ * @return [type] [description]
84
+ */
85
+ public static function get( $key = null ) {
86
+ $configs = self::$configs;
87
+ if ( $key ) {
88
+ $configs = ! empty( self::$configs[ $key ] ) ? self::$configs[ $key ] : null;
89
+ } else {
90
+ $configs = self::$configs;
91
+ }
92
+
93
+ return $configs;
94
+ }
95
+ }
vendor/boldgrid/library/src/Library/Filter.php ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Library Filter
4
+ *
5
+ * @package Boldgrid\Library
6
+ * @subpackage \Library
7
+ *
8
+ * @version 1.0.0
9
+ * @author BoldGrid <wpb@boldgrid.com>
10
+ */
11
+
12
+ namespace Boldgrid\Library\Library;
13
+
14
+ /**
15
+ * BoldGrid Library Filter Class.
16
+ *
17
+ * This class is responsible for filter/action related methods used within
18
+ * the BoldGrid Library. Special thanks to rarst and scribu for filter ideas.
19
+ *
20
+ * @since 1.0.0
21
+ */
22
+ class Filter {
23
+
24
+ /**
25
+ * Adds hooks.
26
+ *
27
+ * @since 1.0.0
28
+ *
29
+ * @param string $class Class to add hooks for.
30
+ *
31
+ * @return null
32
+ */
33
+ public static function add( $class ) {
34
+ self::doFilter( 'add_filter', $class );
35
+ }
36
+
37
+ /**
38
+ * Remove hooks.
39
+ *
40
+ * @since 1.0.0
41
+ *
42
+ * @param string $class Class to remove hooks from.
43
+ *
44
+ * @return null
45
+ */
46
+ public static function remove( $class ) {
47
+ self::doFilter( 'remove_filter', $class );
48
+ }
49
+
50
+ /**
51
+ * Process hooks.
52
+ *
53
+ * This sets up our automatic filter binding.
54
+ *
55
+ * @since 1.0.0
56
+ *
57
+ * @param string $action Action name.
58
+ * @param string $class Class name.
59
+ *
60
+ * @return null
61
+ */
62
+ private static function doFilter( $action, $class ) {
63
+ $reflection = new \ReflectionClass( $class );
64
+ foreach ( $reflection->getMethods() as $method ) {
65
+ if ( $method->isPublic() && ! $method->isConstructor() ) {
66
+ $comment = $method->getDocComment();
67
+
68
+ // No hooks.
69
+ if ( preg_match( '/@nohook[ \t\*\n]+/', $comment ) ) {
70
+ continue;
71
+ }
72
+
73
+ // Set hook.
74
+ preg_match_all( '/@hook:?\s+([^\s]+)/', $comment, $matches ) ? $matches[1] : $method->name;
75
+ if ( empty( $matches[1] ) ) {
76
+ $hooks = array( $method->name );
77
+ } else {
78
+ $hooks = $matches[1];
79
+ }
80
+
81
+ // Allow setting priority.
82
+ $priority = preg_match( '/@priority:?\s+(\d+)/', $comment, $matches ) ? $matches[1] : 10;
83
+
84
+ // Fire.
85
+ foreach ( $hooks as $hook ) {
86
+ call_user_func( $action, $hook, array( $class, $method->name ), $priority, $method->getNumberOfParameters() );
87
+ }
88
+ }
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Removes an anonymous object filter.
94
+ *
95
+ * @since 1.0.0
96
+ *
97
+ * @global $wp_filter WordPress filter global.
98
+ *
99
+ * @param string $tag Hook name.
100
+ * @param string $class Class name
101
+ * @param string $method Method name
102
+ * @param int $priority Filter priority.
103
+ *
104
+ * @return bool Success of removing filter.
105
+ */
106
+ public static function removeHook( $tag, $class, $name, $priority = 10 ) {
107
+ global $wp_filter;
108
+
109
+ // Check that filter exists.
110
+ if ( isset( $wp_filter[ $tag ] ) ) {
111
+
112
+ /**
113
+ * If filter config is an object, means we're using WordPress 4.7+ and the config is no longer
114
+ * a simple array, and it is an object that implements the ArrayAccess interface.
115
+ *
116
+ * To be backwards compatible, we set $callbacks equal to the correct array as a reference (so $wp_filter is updated).
117
+ *
118
+ * @see https://make.wordpress.org/core/2016/09/08/wp_hook-next-generation-actions-and-filters/
119
+ */
120
+ if ( is_object( $wp_filter[ $tag ] ) && isset( $wp_filter[ $tag ]->callbacks ) ) {
121
+
122
+ // Create $fob object from filter tag, to use below.
123
+ $fob = $wp_filter[ $tag ];
124
+ $callbacks = &$wp_filter[ $tag ]->callbacks;
125
+ } else {
126
+ $callbacks = &$wp_filter[ $tag ];
127
+ }
128
+
129
+ // Exit if there aren't any callbacks for specified priority.
130
+ if ( ! isset( $callbacks[ $priority ] ) || empty( $callbacks[ $priority ] ) ) {
131
+ return false;
132
+ }
133
+
134
+ // Loop through each filter for the specified priority, looking for our class & method.
135
+ foreach( ( array ) $callbacks[ $priority ] as $filter_id => $filter ) {
136
+
137
+ // Filter should always be an array - array( $this, 'method' ), if not goto next.
138
+ if ( ! isset( $filter['function'] ) || ! is_array( $filter['function'] ) ) {
139
+ continue;
140
+ }
141
+
142
+ // If first value in array is not an object, it can't be a class.
143
+ if ( ! is_object( $filter['function'][0] ) ) {
144
+ continue;
145
+ }
146
+
147
+ // Method doesn't match the one we're looking for, goto next.
148
+ if ( $filter['function'][1] !== $name ) {
149
+ continue;
150
+ }
151
+
152
+ // Callback method matched, so check class.
153
+ if ( get_class( $filter['function'][0] ) === $class ) {
154
+
155
+ // WordPress 4.7+ use core remove_filter() since we found the class object.
156
+ if ( isset( $fob ) ) {
157
+
158
+ // Handles removing filter, reseting callback priority keys mid-iteration, etc.
159
+ $fob->remove_filter( $tag, $filter['function'], $priority );
160
+ } else {
161
+
162
+ // Use legacy removal process (pre 4.7).
163
+ unset( $callbacks[ $priority ][ $filter_id ] );
164
+
165
+ // If it was the only filter in that priority, unset that priority.
166
+ if ( empty( $callbacks[ $priority ] ) ) {
167
+ unset( $callbacks[ $priority ] );
168
+ }
169
+
170
+ // If the only filter for that tag, set the tag to an empty array.
171
+ if ( empty( $callbacks ) ) {
172
+ $callbacks = array();
173
+ }
174
+
175
+ // Remove this filter from merged_filters, which specifies if filters have been sorted.
176
+ unset( $GLOBALS['merged_filters'][ $tag ] );
177
+ }
178
+
179
+ return true;
180
+ }
181
+ }
182
+ }
183
+
184
+ return false;
185
+ }
186
+ }
vendor/boldgrid/library/src/Library/Key.php ADDED
@@ -0,0 +1,303 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Library Key Class
4
+ *
5
+ * @package Boldgrid\Library
6
+ * @subpackage \Library
7
+ *
8
+ * @version 1.0.0
9
+ * @author BoldGrid <wpb@boldgrid.com>
10
+ */
11
+
12
+ namespace Boldgrid\Library\Library;
13
+
14
+ /**
15
+ * BoldGrid Library Key Class.
16
+ *
17
+ * This class is responsible for verifying BoldGrid connect key information
18
+ * stored in a user's WordPress.
19
+ *
20
+ * @since 1.0.0
21
+ */
22
+ class Key {
23
+
24
+ /**
25
+ * @access protected
26
+ *
27
+ * @since 1.0.0
28
+ *
29
+ * @var mixed $license License object.
30
+ */
31
+ protected $license;
32
+
33
+ /**
34
+ * @access protected
35
+ *
36
+ * @since 1.0.0
37
+ *
38
+ * @var bool $valid Is key valid?
39
+ */
40
+ protected static $valid;
41
+
42
+ /**
43
+ * @access protected
44
+ *
45
+ * @since 1.1.6
46
+ *
47
+ * @var \Boldgrid\Library\Library\ReleaseChannel
48
+ */
49
+ protected $releaseChannel;
50
+
51
+ /**
52
+ * Initialize class and set class properties.
53
+ *
54
+ * @since 1.0.0
55
+ *
56
+ * @param \Boldgrid\Library\Library\ReleaseChannel $releaseChannel Plugin release channel.
57
+ */
58
+ public function __construct( ReleaseChannel $releaseChannel ) {
59
+ $this->releaseChannel = $releaseChannel;
60
+ $this->setValid();
61
+ $this->setLicense();
62
+ $this->setNotice();
63
+ $this->addNotices();
64
+ }
65
+
66
+ /**
67
+ * Set the license class property.
68
+ *
69
+ * @since 1.0.0
70
+ *
71
+ * @return mixed $license The license object or false.
72
+ */
73
+ public function setLicense() {
74
+ return $this->license = self::$valid ? new License() : false;
75
+ }
76
+
77
+ /**
78
+ * Get the license class property.
79
+ *
80
+ * @since 2.1.0
81
+ *
82
+ * @return Boldgrid\Library\Library\License|false
83
+ */
84
+ public function getLicense() {
85
+ return $this->license;
86
+ }
87
+
88
+ /**
89
+ * Set $valid class property.
90
+ *
91
+ * @since 1.0.0
92
+ *
93
+ * @see \Boldgrid\Library\Library\Key::verifyData()
94
+ *
95
+ * @return bool $valid Is transient/key valid?
96
+ */
97
+ public function setValid() {
98
+ // The API Transient data.
99
+ $data = Configs::get( 'apiData' );
100
+
101
+ // The API Key.
102
+ $key = Configs::get( 'key' );
103
+
104
+ // Flag.
105
+ $valid = false;
106
+
107
+ // If the API data is available in the transient.
108
+ if ( $data && ! empty( $data->license_status ) ) {
109
+ // Let the transient set it's own reported status.
110
+ $valid = $data->license_status;
111
+ }
112
+
113
+ // If we're still not valid, have no transient data, and have a key stored already.
114
+ if ( ! $valid && ! isset( $data->license_status ) && $key ) {
115
+ // Make a call to get data.
116
+ $data = $this->verify();
117
+
118
+ // Errors come back as strings and success comes back as an object we can use.
119
+ if ( $this->verifyData( $data ) ) {
120
+ // Update the data since we know
121
+ $this->save( $data, $key );
122
+
123
+ // Set our flag.
124
+ $valid = true;
125
+ }
126
+ }
127
+
128
+ return self::$valid = $valid;
129
+ }
130
+
131
+ /**
132
+ * Add the appropriate notice to the WordPress dashboard.
133
+ *
134
+ * This method will set the connection issue notice or the prompt key
135
+ * notice to the user's WordPress dashboard.
136
+ *
137
+ * @since 1.0.0
138
+ */
139
+ public function setNotice() {
140
+
141
+ // If we already have transient data saying the API is not available.
142
+ if ( '0' === get_site_transient( 'boldgrid_available' ) ) {
143
+ return new Notice( 'ConnectionIssue' );
144
+ }
145
+
146
+ // If we don't have a key stored, or this is not a valid response when calling.
147
+ if ( ! Configs::get( 'key' ) || ! self::$valid ) {
148
+ return new Notice( 'keyPrompt', $this );
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Call the API check-version endpoint for API data.
154
+ *
155
+ * @since 1.0.0
156
+ *
157
+ * @param array $args API arguments to pass.
158
+ *
159
+ * @return mixed $response Object for valid response, or error message as string.
160
+ */
161
+ public function callCheckVersion( $args ) {
162
+ if ( ! empty( $args['key'] ) ) {
163
+ $call = new Api\Call( Configs::get( 'api' ) . '/api/plugin/checkVersion', $args );
164
+
165
+ // If there's an error set that as the response.
166
+ if ( ! $response = $call->getError() ) {
167
+
168
+ // If the response is successful, then retrieve the response.
169
+ $response = $call->getResponse();
170
+ }
171
+ } else {
172
+ $response = 'No Connect Key available.';
173
+ }
174
+
175
+ // Return the API response.
176
+ return $response;
177
+ }
178
+
179
+ /**
180
+ * Verify API data status.
181
+ *
182
+ * @since 1.1.6
183
+ *
184
+ * @param stdClass $data API response data object, or error string.
185
+ * @return bool
186
+ */
187
+ public function verifyData( $data ) {
188
+ return ! empty( $data->result->data->asset_id );
189
+ }
190
+
191
+ /**
192
+ * Validates the API key and returns details on if it is valid as well as version.
193
+ *
194
+ * @since 1.0.0
195
+ *
196
+ * @see \Boldgrid\Library\Library\ReleaseChannel::getPluginChannel()
197
+ * @see \Boldgrid\Library\Library\ReleaseChannel::getThemeChannel()
198
+ * @see \Boldgrid\Library\Library\Key::callCheckVersion()
199
+ * @see \Boldgrid\Library\Library\Key::verifyData()
200
+ *
201
+ * @return mixed The BoldGrid API Data object or a message string on failure.
202
+ */
203
+ public function verify( $key = null ) {
204
+ $key = $key ? $key : Configs::get( 'key' );
205
+
206
+ // Make an API call for API data.
207
+ $data = $this->callCheckVersion( array(
208
+ 'key' => $key,
209
+ 'channel' => $this->releaseChannel->getPluginChannel(),
210
+ 'theme_channel' => $this->releaseChannel->getThemeChannel(),
211
+ ) );
212
+
213
+ // Let the transient data set it's own validity.
214
+ if ( $this->verifyData( $data ) ) {
215
+ $data->license_status = true;
216
+ }
217
+
218
+ // Return back the transient data object.
219
+ return $data;
220
+ }
221
+
222
+ /**
223
+ * Saves data based on API response.
224
+ *
225
+ * @since 1.0.0
226
+ *
227
+ * @param Object $data API response data.
228
+ * @param string $key The API key being used for call.
229
+ *
230
+ * @return Object $data API response data.
231
+ */
232
+ public function save( $data, $key ) {
233
+ $data->license_status = true;
234
+
235
+ // We can update the available status transient since we know the server was reached.
236
+ // @todo move this into the Call class since it's the earliest point of access.
237
+ set_site_transient( 'boldgrid_available', 1, HOUR_IN_SECONDS );
238
+
239
+ // We update transient with the response of the API check version call.
240
+ set_site_transient( 'boldgrid_api_data', $data, 8 * HOUR_IN_SECONDS );
241
+
242
+ // Update the key option in case key is being overridden by something else so we have the new one next time.
243
+ update_site_option( 'boldgrid_api_key', $key );
244
+
245
+ // Updates the site hash identifier being stored.
246
+ update_site_option( 'boldgrid_site_hash', $data->result->data->site_hash );
247
+
248
+ // This looks for any reseller entries that exist and saves the wp option.
249
+ $this->saveReseller( $data->result->data );
250
+
251
+
252
+ // Returns back the transient data object.
253
+ return $data;
254
+ }
255
+
256
+ /**
257
+ * Saves data based on API response.
258
+ *
259
+ * @since 1.0.0
260
+ *
261
+ * @param Object $data API response data.
262
+ *
263
+ * @return Object $data API response data.
264
+ */
265
+ protected function saveReseller( $data ) {
266
+ $reseller = array();
267
+
268
+ // @todo Check if a known key exists before deleting and skip reading/writing option.
269
+
270
+ // Clear out any previous reseller entries.
271
+ delete_option( 'boldgrid_reseller' );
272
+
273
+ // Loop through the transient data being saved.
274
+ foreach ( $data as $key => $value ) {
275
+
276
+ // Check the data for matching keys.
277
+ if ( 1 === preg_match( '/^reseller_/', $key ) ) {
278
+
279
+ // Set the reseller transient data in the reseller array.
280
+ $reseller[ $key ] = $data->$key;
281
+ }
282
+ }
283
+
284
+ // Check if reseller data was extracted from the transient being saved.
285
+ if ( ! empty( $reseller ) ) {
286
+ update_site_option( 'boldgrid_reseller', $reseller );
287
+ }
288
+
289
+ // Return back the transient data object.
290
+ return $data;
291
+ }
292
+
293
+ /**
294
+ * Add additional admin notices.
295
+ *
296
+ * @since 2.1.0
297
+ *
298
+ * @see \Boldgrid\Library\Library\Notice()
299
+ */
300
+ public function addNotices() {
301
+ $claimPremiumKey = new Notice( 'ClaimPremiumKey', $this );
302
+ }
303
+ }
vendor/boldgrid/library/src/Library/Key/Validate.php ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Library Key Validate
4
+ *
5
+ * @package Boldgrid\Library
6
+ * @subpackage \Key
7
+ *
8
+ * @version 1.0.0
9
+ * @author BoldGrid <wpb@boldgrid.com>
10
+ */
11
+
12
+ namespace Boldgrid\Library\Library\Key;
13
+
14
+ /**
15
+ * Boldgrid Library Key Validate Class.
16
+ *
17
+ * Creates object based on user input of BoldGrid
18
+ * Connect Key. Will check basic formatting of key
19
+ * entered, and provides the key hash to store.
20
+ *
21
+ * @since 1.0.0
22
+ */
23
+ class Validate {
24
+
25
+ /**
26
+ * @access private
27
+ *
28
+ * @since 1.0.0
29
+ *
30
+ * @var string $key User entered API key.
31
+ * @var string $hash API key hash that will be stored.
32
+ * @var bool $valid Is API key correctly formatted?
33
+ */
34
+ private
35
+ $key,
36
+ $hash,
37
+ $valid;
38
+
39
+ /**
40
+ * Initialize class and set class properties.
41
+ *
42
+ * @since 1.0.0
43
+ *
44
+ * @param array $key The API key to validate.
45
+ */
46
+ public function __construct( $key ) {
47
+ $this->setKey( $key );
48
+ if ( $this->isValid() ) {
49
+ $this->setValid( true );
50
+ $this->setHash( $this->getKey() );
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Set the key class property.
56
+ *
57
+ * @since 1.0.0
58
+ *
59
+ * @param string $key The API key to set.
60
+ *
61
+ * @return string $key The key class property.
62
+ */
63
+ public function setKey( $key ) {
64
+ return $this->key = $this->sanitizeKey( $key );
65
+ }
66
+
67
+ /**
68
+ * Set the valid class property.
69
+ *
70
+ * @since 1.0.0
71
+ *
72
+ * @param bool $valid Valid key entry?
73
+ *
74
+ * @return bool $valid The valid class property.
75
+ */
76
+ public function setValid( $valid ) {
77
+ return $this->valid = $valid;
78
+ }
79
+
80
+ /**
81
+ * Sets the hash class property.
82
+ *
83
+ * @since 1.0.0
84
+ *
85
+ * @param string $key API key to get hash of.
86
+ *
87
+ * @return object
88
+ */
89
+ public function setHash( $key = null ) {
90
+ $key = $key ? $this->sanitizeKey( $key ) : $this->getKey();
91
+ return $this->hash = $this->hashKey( $key );
92
+ }
93
+
94
+ /**
95
+ * Sanitizes the key input for loose validation of format.
96
+ *
97
+ * @since 1.0.0
98
+ *
99
+ * @param [type] $key [description]
100
+ *
101
+ * @return string $key Sanitized key.
102
+ */
103
+ public function sanitizeKey( $key ) {
104
+ $key = trim( strtolower( preg_replace( '/-/', '', $key ) ) );
105
+ $key = implode( '-', str_split( $key, 8 ) );
106
+ return sanitize_key( $key );
107
+ }
108
+
109
+ /**
110
+ * [hashKey description]
111
+ *
112
+ * @since 1.0.0
113
+ *
114
+ * @param [type] $key [description]
115
+ *
116
+ * @return [type] [description]
117
+ */
118
+ public function hashKey( $key = null ) {
119
+ $key = $key ? $this->sanitizeKey( $key ) : $this->getKey();
120
+ return md5( $key );
121
+ }
122
+
123
+ /**
124
+ * Checks if key is valid length after format and sanitization is done.
125
+ *
126
+ * @since 1.0.0
127
+ *
128
+ * @param string $key The API key to check.
129
+ *
130
+ * @return bool Is the key valid?
131
+ */
132
+ public function isValid( $key = null ) {
133
+ $key = $key ? $this->sanitizeKey( $key ) : $this->getKey();
134
+ return strlen( $key ) === 35;
135
+ }
136
+
137
+ /**
138
+ * Get the key class property.
139
+ *
140
+ * @since 1.0.0
141
+ *
142
+ * @return string $key The key class property.
143
+ */
144
+ public function getKey() {
145
+ return $this->key;
146
+ }
147
+
148
+ /**
149
+ * Get the hash class property.
150
+ *
151
+ * @since 1.0.0
152
+ *
153
+ * @return string $hash The hash class property.
154
+ */
155
+ public function getHash() {
156
+ return $this->hash;
157
+ }
158
+
159
+ /**
160
+ * Get the valid class property.
161
+ *
162
+ * @since 1.0.0
163
+ *
164
+ * @return bool $valid The valid class property.
165
+ */
166
+ public function getValid() {
167
+ return $this->valid;
168
+ }
169
+ }
vendor/boldgrid/library/src/Library/License.php ADDED
@@ -0,0 +1,373 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Library License Class
4
+ *
5
+ * @package Boldgrid\Library
6
+ * @subpackage \Library\License
7
+ *
8
+ * @version 1.0.0
9
+ * @author BoldGrid <wpb@boldgrid.com>
10
+ */
11
+
12
+ namespace Boldgrid\Library\Library;
13
+
14
+ use Boldgrid\Library\Library;
15
+
16
+ /**
17
+ * BoldGrid Library License Class.
18
+ *
19
+ * This class is responsible for calling the API and retrieving the
20
+ * remote license data required for licensed plugins to function.
21
+ *
22
+ * @since 1.0.0
23
+ */
24
+ class License {
25
+
26
+ /**
27
+ * @access private
28
+ *
29
+ * @since 1.0.0
30
+ *
31
+ * @var array $key Transient data key.
32
+ * @var object $license BoldGrid license details.
33
+ * @var string $licenseString A string representing the type of license, such
34
+ * as "Free" or "Premium".
35
+ * @var object $data BoldGrid license data.
36
+ */
37
+ private
38
+ $key,
39
+ $license,
40
+ $licenseString,
41
+ $data;
42
+
43
+ /**
44
+ * Initialize class and set class properties.
45
+ *
46
+ * @since 1.0.0
47
+ */
48
+ public function __construct() {
49
+ $this->key = $this->setKey();
50
+
51
+ $this->initLicense();
52
+
53
+ Filter::add( $this );
54
+ }
55
+
56
+ /**
57
+ * Handle ajax request to clear license data.
58
+ *
59
+ * @since 2.2.0
60
+ *
61
+ * @hook: wp_ajax_bg_clear_license
62
+ */
63
+ public function ajaxClear() {
64
+ $plugin = ! empty( $_POST['plugin'] ) ? $_POST['plugin'] : null;
65
+ if( empty( $plugin ) ) {
66
+ wp_send_json_error( __( 'Unknown plugin.' ) );
67
+ }
68
+
69
+ if( ! current_user_can( 'manage_options' ) ) {
70
+ wp_send_json_error( __( 'Access denied.', $plugin ) );
71
+ }
72
+
73
+ $success = $this->clearTransient();
74
+ if( ! $success ) {
75
+ wp_send_json_error( array(
76
+ 'string' => sprintf(
77
+ __( 'Failed to clear license data. Unable to delete site transient "%1$s".', $plugin ),
78
+ $this->getKey()
79
+ ),
80
+ ));
81
+ }
82
+
83
+ $this->initLicense();
84
+
85
+ $return = array(
86
+ 'isPremium' => $this->isPremium( $plugin ),
87
+ 'string' => $this->licenseString,
88
+ );
89
+
90
+ wp_send_json_success( $return );
91
+ }
92
+
93
+ /**
94
+ * Register scripts.
95
+ *
96
+ * @since 2.2.0
97
+ *
98
+ * @hook: admin_enqueue_scripts
99
+ */
100
+ public function registerScripts() {
101
+ wp_register_script(
102
+ 'bglib-license',
103
+ Library\Configs::get( 'libraryUrl' ) . 'src/assets/js/license.js',
104
+ 'jQuery'
105
+ );
106
+
107
+ $translations = array(
108
+ 'unknownError' => __( 'Unknown error' ),
109
+ );
110
+
111
+ wp_localize_script( 'bglib-license', 'bglibLicense', $translations );
112
+ }
113
+
114
+ /**
115
+ * Set key class property.
116
+ *
117
+ * This is the key used to access the transient data stored.
118
+ *
119
+ * @since 1.0.0
120
+ *
121
+ * @return string $key The key class property.
122
+ */
123
+ private function setKey() {
124
+ return $this->key = sanitize_key( 'bg_license_data' );
125
+ }
126
+
127
+ /**
128
+ * Set the license class property.
129
+ *
130
+ * @since 1.0.0
131
+ *
132
+ * @return mixed $license The response object or error string of call.
133
+ */
134
+ private function setLicense() {
135
+ if ( ! get_option( 'boldgrid_api_key' ) ) {
136
+ $license = 'Missing Connect Key';
137
+ } else if ( ! $license = $this->getTransient() ) {
138
+ $license = $this->getRemoteLicense();
139
+ }
140
+
141
+ return $this->license = $license;
142
+ }
143
+
144
+ /**
145
+ * Sets the transient for the license data.
146
+ *
147
+ * @since 1.0.0
148
+ *
149
+ * @return bool Was the transient successfully set?
150
+ */
151
+ private function setTransient() {
152
+ return ! $this->getTransient() && set_site_transient( $this->getKey(), $this->getLicense(), $this->getExpiration( $this->getData() ) );
153
+ }
154
+
155
+ /**
156
+ * Check if the current license is valid.
157
+ *
158
+ * Is valid if the refresh-by timestamp is not more than 1 day past due.
159
+ *
160
+ * @since 1.0.0
161
+ *
162
+ * @return mixed Checks if the license data is available.
163
+ */
164
+ private function isValid() {
165
+ $data = $this->getTransient();
166
+ $valid = array( 'key', 'cipher', 'iv', 'data' );
167
+ if ( is_object( $data ) ) {
168
+ $props = array_keys( get_object_vars( $data ) );
169
+ $valid = array_diff( $valid, $props );
170
+ }
171
+
172
+ return empty( $valid );
173
+ }
174
+
175
+ /**
176
+ * Set the data class property.
177
+ *
178
+ * @since 1.0.0
179
+ *
180
+ * @return object $data The license data class property.
181
+ */
182
+ private function setData() {
183
+ if ( $license = $this->getLicense() ) {
184
+ $data = json_decode(
185
+ openssl_decrypt(
186
+ $license->data,
187
+ $license->cipher,
188
+ $license->key,
189
+ 0,
190
+ base64_decode( $license->iv )
191
+ )
192
+ );
193
+ }
194
+
195
+ return $this->data = $data;
196
+ }
197
+
198
+ /**
199
+ * Displays the license notice in the WordPress admin.
200
+ *
201
+ * @since 1.0.0
202
+ */
203
+ protected function licenseNotice() {
204
+ if ( ! $this->isValid() ) {
205
+ new Notice( 'invalidLicense' );
206
+ // Disables the activate message.
207
+ if ( isset( $_GET['activate'] ) ) {
208
+ unset( $_GET['activate'] );
209
+ }
210
+ }
211
+ }
212
+
213
+ /**
214
+ * Runs plugin deactivation when deactivate_plugins is available.
215
+ *
216
+ * @since 1.0.0
217
+ *
218
+ * @hook: admin_init
219
+ */
220
+ public function deactivate() {
221
+ if ( ! $this->isValid() && Configs::get( 'licenseActivate' ) ) {
222
+ delete_site_transient( $this->getKey() );
223
+ deactivate_plugins( Configs::get( 'file' ) );
224
+ }
225
+ }
226
+
227
+ /**
228
+ * Clear the transient containing license data.
229
+ *
230
+ * @since 2.2.0
231
+ *
232
+ * @hook: Boldgrid\Library\License\clearTransient
233
+ *
234
+ * @return bool True on success
235
+ */
236
+ public function clearTransient() {
237
+ return delete_site_transient( $this->getKey() );
238
+ }
239
+
240
+ /**
241
+ * Get the latest license data from the API server.
242
+ *
243
+ * @since 1.0.0
244
+ *
245
+ * @return mixed $response The remote license data object or error string.
246
+ */
247
+ private function getRemoteLicense() {
248
+ $call = new Api\Call( Configs::get( 'api' ) . '/api/plugin/getLicense' );
249
+ if ( ! $response = $call->getError() ) {
250
+ $response = $call->getResponse()->result->data;
251
+ }
252
+
253
+ return $response;
254
+ }
255
+
256
+ /**
257
+ * Get the key class property.
258
+ *
259
+ * @since 1.0.0
260
+ *
261
+ * @return string $key The key class property.
262
+ */
263
+ protected function getKey() {
264
+ return $this->key;
265
+ }
266
+
267
+ /**
268
+ * Get the transient containing license data.
269
+ *
270
+ * @since 1.0.0
271
+ *
272
+ * @return mixed The API license data from the WordPress transient.
273
+ */
274
+ protected function getTransient() {
275
+ return get_site_transient( $this->getKey() );
276
+ }
277
+
278
+ /**
279
+ * Gets the expiration date from data passed.
280
+ *
281
+ * @since 1.0.0
282
+ *
283
+ * @param object $data The data object from API license request.
284
+ *
285
+ * @return string The expiration of the data stored.
286
+ */
287
+ private function getExpiration( $data ) {
288
+ return strtotime( $data->refreshBy ) - strtotime( 'now' );
289
+ }
290
+
291
+ /**
292
+ * Get the license class property.
293
+ *
294
+ * @since 1.0.0
295
+ *
296
+ * @return mixed $license The license class property.
297
+ */
298
+ protected function getLicense() {
299
+ return $this->license;
300
+ }
301
+
302
+ /**
303
+ * Return the license string.
304
+ *
305
+ * @since 2.2.0
306
+ *
307
+ * @return string
308
+ */
309
+ public function getLicenseString() {
310
+ return $this->licenseString;
311
+ }
312
+
313
+ /**
314
+ * Get the data class property.
315
+ *
316
+ * @since 1.0.0
317
+ *
318
+ * @nohook
319
+ *
320
+ * @return $data The data class property.
321
+ */
322
+ public function getData() {
323
+ return $this->data;
324
+ }
325
+
326
+ /**
327
+ * Get license validation.
328
+ *
329
+ * @nohook
330
+ *
331
+ * @return [type] [description]
332
+ */
333
+ public function getValid() {
334
+ return $this->isValid();
335
+ }
336
+
337
+ /**
338
+ * Init the license.
339
+ *
340
+ * This method was originally contained within the constructor, however it
341
+ * was pulled out as it was needed in additional places. For example, in cases
342
+ * we instantiate this class and then clear the license data, we may need to
343
+ * get fresh license data at that time by initializing the license again.
344
+ *
345
+ * @since 2.2.0
346
+ */
347
+ public function initLicense() {
348
+ $this->license = $this->setLicense();
349
+ if ( is_object( $this->getLicense() ) ) {
350
+ $this->data = $this->setData();
351
+ $this->setTransient( $this->getData() );
352
+ $licenseData = array( 'licenseData' => $this->getData() );
353
+ Configs::set( $licenseData, Configs::get() );
354
+ }
355
+ }
356
+
357
+ /**
358
+ * Checks if product is premium or free.
359
+ *
360
+ * @since 1.1.4
361
+ *
362
+ * @nohook
363
+ *
364
+ * @return bool
365
+ */
366
+ public function isPremium( $product ) {
367
+ $isPremium = isset( $this->getData()->$product );
368
+
369
+ $this->licenseString = $isPremium ? __( 'Premium' ) : __( 'Free' );
370
+
371
+ return $isPremium;
372
+ }
373
+ }
vendor/boldgrid/library/src/Library/Notice.php ADDED
@@ -0,0 +1,235 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Library Notice
4
+ *
5
+ * @package Boldgrid\Library
6
+ * @subpackage \Library
7
+ *
8
+ * @version 1.0.0
9
+ * @author BoldGrid <wpb@boldgrid.com>
10
+ */
11
+
12
+ namespace Boldgrid\Library\Library;
13
+
14
+ /**
15
+ * BoldGrid Library Notice Class.
16
+ *
17
+ * This class is responsible for adding any admin notices that are
18
+ * displayed in the WordPress dashboard.
19
+ *
20
+ * @since 1.0.0
21
+ */
22
+ class Notice {
23
+
24
+ /**
25
+ * @access private
26
+ *
27
+ * @var string $name Name of notice to create.
28
+ * @var array $args Optional arguments if required by class.
29
+ * @var mixed $class Instantiated class object or add filters.
30
+ */
31
+ private
32
+ $name,
33
+ $args,
34
+ $class;
35
+
36
+ /**
37
+ * Initialize class and set class properties.
38
+ *
39
+ * @since 1.0.0
40
+ *
41
+ * @param string $name Name of notice to create.
42
+ * @param array $args Optional arguments if required by class being instantiated.
43
+ */
44
+ public function __construct( $name, $args = null ) {
45
+ $this->name = $name;
46
+ $this->args = $args;
47
+ $this->setClass( $name, $args );
48
+
49
+ Filter::add( $this );
50
+ }
51
+
52
+ /**
53
+ * Sets the class class property.
54
+ *
55
+ * @since 1.0.0
56
+ *
57
+ * @param string $name Name of the notice class to initialize.
58
+ * @param array $args Optional arguments to pass to the class initialization.
59
+ *
60
+ * @return string $class The class class property.
61
+ */
62
+ private function setClass( $name, $args ) {
63
+
64
+ // Build the class string dynamically.
65
+ $class = __NAMESPACE__ . '\\Notice\\' . ucfirst( $name );
66
+
67
+ // Create a new instance or add our filters.
68
+ return $this->class = class_exists( $class ) ? new $class( $args ) : Filter::add( $this );
69
+ }
70
+
71
+ /**
72
+ * Show an admin notice.
73
+ *
74
+ * At one point we had this method running on the following hook:
75
+ * Boldgrid\Libary\Notice\show. Because several different notices are
76
+ * instantiating this class (such as the keyPrompt and ClaimPremiumKey notices),
77
+ * the hook was being added more than once, causing duplicate admin notices
78
+ * to show.
79
+ *
80
+ * @since 2.2.0
81
+ *
82
+ * @param string $message
83
+ * @param string $id
84
+ * @param string $class
85
+ */
86
+ public static function show( $message, $id, $class = "notice notice-warning" ) {
87
+ $nonce = wp_nonce_field( 'boldgrid_set_key', 'set_key_auth', true, false );
88
+
89
+ if( self::isDismissed( $id ) ) {
90
+ return;
91
+ }
92
+
93
+ printf( '<div class="%1$s boldgrid-notice is-dismissible" data-notice-id="%2$s">%3$s%4$s</div>',
94
+ $class,
95
+ $id,
96
+ $message,
97
+ $nonce
98
+ );
99
+ }
100
+
101
+ /**
102
+ * Displays the license notice in the WordPress admin.
103
+ *
104
+ * @since 1.0.0
105
+ *
106
+ * @nohook: admin_notices
107
+ */
108
+ public function add( $name = null ) {
109
+ $name = $name ? $name : $this->getName();
110
+ $path = __DIR__;
111
+ $name = ucfirst( $name );
112
+ include "{$path}/Views/{$name}.php";
113
+ }
114
+
115
+ /**
116
+ * Get the name class property.
117
+ *
118
+ * @since 1.0.0
119
+ *
120
+ * @return string $name The name class property.
121
+ */
122
+ protected function getName() {
123
+ return $this->name;
124
+ }
125
+
126
+ /**
127
+ * Get the args class property.
128
+ *
129
+ * @since 1.0.0
130
+ *
131
+ * @return string $name The args class property.
132
+ */
133
+ protected function getArgs() {
134
+ return $this->args;
135
+ }
136
+
137
+ /**
138
+ * Handle dismissal of the key prompt notice.
139
+ *
140
+ * @since 2.1.0
141
+ *
142
+ * @see \Boldgrid\Library\Library\Notice::isDismissed()
143
+ *
144
+ * @uses $_POST['notice'] Notice id.
145
+ *
146
+ * @hook: wp_ajax_dismissBoldgridNotice
147
+ */
148
+ public function dismiss() {
149
+ // Validate nonce.
150
+ if ( isset( $_POST['set_key_auth'] ) && check_ajax_referer( 'boldgrid_set_key', 'set_key_auth', false ) ) {
151
+ $id = sanitize_key( $_POST['notice'] );
152
+
153
+ // Mark the notice as dismissed, if not already done so.
154
+ $dismissal = array(
155
+ 'id' => $id,
156
+ 'timestamp' => time(),
157
+ );
158
+
159
+ if ( ! Notice::isDismissed( $id ) ) {
160
+ add_user_meta( get_current_user_id(), 'boldgrid_dismissed_admin_notices', $dismissal );
161
+ }
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Handle undismissal of the key prompt notice.
167
+ *
168
+ * @since 2.1.0
169
+ *
170
+ * @uses $_POST['notice'] Notice id.
171
+ *
172
+ * @hook: wp_ajax_undismissBoldgridNotice
173
+ */
174
+ public function undismiss() {
175
+ // Validate nonce.
176
+ if ( isset( $_POST['set_key_auth'] ) && ! empty( $_POST['notice'] ) && check_ajax_referer( 'boldgrid_set_key', 'set_key_auth', false ) ) {
177
+ $id = sanitize_key( $_POST['notice'] );
178
+
179
+ // Get all of the notices this user has dismissed.
180
+ $dismissals = get_user_meta( get_current_user_id(), 'boldgrid_dismissed_admin_notices' );
181
+
182
+ // Loop through all of the dismissed notices. If we find the dismissal, then remove it.
183
+ foreach ( $dismissals as $dismissal ) {
184
+ if ( $id === $dismissal['id'] ) {
185
+ delete_user_meta( get_current_user_id(), 'boldgrid_dismissed_admin_notices', $dismissal );
186
+ break;
187
+ }
188
+ }
189
+ }
190
+ }
191
+
192
+ /**
193
+ * Is there a user dismissal record for a particular admin notice id?
194
+ *
195
+ * @since 2.1.0
196
+ *
197
+ * @static
198
+ *
199
+ * @param string $id An admin notice id.
200
+ * @return bool
201
+ */
202
+ public static function isDismissed( $id ) {
203
+ $dismissed = false;
204
+ $id = sanitize_key( $id );
205
+
206
+ // Get all of the notices this user has dismissed.
207
+ $dismissals = get_user_meta( get_current_user_id(), 'boldgrid_dismissed_admin_notices' );
208
+
209
+ // Loop through all of the dismissed notices. If we find our $id, then mark bool and break.
210
+ foreach ( $dismissals as $dismissal ) {
211
+ if ( $id === $dismissal['id'] ) {
212
+ $dismissed = true;
213
+ break;
214
+ }
215
+ }
216
+
217
+ return $dismissed;
218
+ }
219
+
220
+ /**
221
+ * Enqueues the required files.
222
+ *
223
+ * @since 2.1.0
224
+ *
225
+ * @see \Boldgrid\Library\Library\Configs::get()
226
+ *
227
+ * @hook: admin_enqueue_scripts
228
+ */
229
+ public function enqueue() {
230
+ wp_enqueue_script(
231
+ 'bglib-notice-js',
232
+ Configs::get( 'libraryUrl' ) . 'src/assets/js/notice.js'
233
+ );
234
+ }
235
+ }
vendor/boldgrid/library/src/Library/Notice/ClaimPremiumKey.php ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File: ClaimPremiumKey.php
4
+ *
5
+ * The notice for Envato customers that have purchased the BoldGrid Prime theme.
6
+ * They are eligible to receive a free Premium Connect Key.
7
+ *
8
+ * @package Boldgrid\Library
9
+ *
10
+ * @version 2.1.0
11
+ * @author BoldGrid <wpb@boldgrid.com>
12
+ */
13
+
14
+ namespace Boldgrid\Library\Library\Notice;
15
+
16
+ use Boldgrid\Library\Library;
17
+
18
+ /**
19
+ * BoldGrid Library Claim Premium Key Notice.
20
+ *
21
+ * This class is responsible for adding the Claim Premium Key notice logic to a user's WordPress
22
+ * admin pages.
23
+ *
24
+ * @since 2.1.0
25
+ */
26
+ class ClaimPremiumKey {
27
+ /**
28
+ * Bool indicating whether or not this key prompt has been dismissed by the current user.
29
+ *
30
+ * @since 2.1.0
31
+ *
32
+ * @var mixed Null if not set, otherwise bool.
33
+ */
34
+ public static $isDismissed = null;
35
+
36
+ /**
37
+ * Bool indicating whether or not we are currently showing the notice.
38
+ *
39
+ * @since 2.1.0
40
+ *
41
+ * @var bool
42
+ */
43
+ public static $isDisplayed = false;
44
+
45
+ /**
46
+ * @access private
47
+ *
48
+ * @var object $key Key class object.
49
+ * @var string $userNoticeKey When dismissing this prompt, this is the identifier for the
50
+ * user notice.
51
+ */
52
+ private
53
+ $key,
54
+ $userNoticeKey;
55
+
56
+ /**
57
+ * Initialize class and set class properties.
58
+ *
59
+ * This class is automatically instantiated through the following events:
60
+ * #. Boldgrid\Library\Util\Load->__construct
61
+ * #. Boldgrid\Library\Library\Start->__construct
62
+ * #. Boldgrid\Library\Library\Key->__construct
63
+ * #. Boldgrid\Library\Library\Notice->__construct
64
+ * #. Boldgrid\Library\Library\Notice\ClaimPremiumKey->__construct
65
+ *
66
+ * This is important to note because if you ever wanted to instantiate
67
+ * another KeyPrompt class, you should know it's probably already been done
68
+ * so.
69
+ *
70
+ * Some of the class properties (such as isDismissed and isDisplayed) are
71
+ * static and easily retrievable via filters.
72
+ *
73
+ * @since 2.1.0
74
+ *
75
+ * @param \Boldgrid\Library\Library\Key Key object.
76
+ */
77
+ public function __construct( Library\Key $key ) {
78
+ $this->key = $key;
79
+ $this->userNoticeKey = 'bg-claim-premium';
80
+ Library\Filter::add( $this );
81
+ }
82
+
83
+ /**
84
+ * Displays the notice.
85
+ *
86
+ * Enabled if the filter "Boldgrid\Library\Library\Notice\ClaimPremiumKey_enable" is true.
87
+ *
88
+ * Does not display the notice if the user dismissed it, has a Connect Key,
89
+ * or a filter says not to.
90
+ *
91
+ * @since 2.1.0
92
+ *
93
+ * @see \Boldgrid\Library\Library\Notice::isDismissed()
94
+ * @see \Boldgrid\Library\Library\Configs::get()
95
+ *
96
+ * @hook: admin_notices
97
+ */
98
+ public function displayNotice() {
99
+ /**
100
+ * Check if the Envato notice to claim a Premium Connect Key should be enabled.
101
+ *
102
+ * A theme can add this filter and return true, which will enable this notice.
103
+ *
104
+ * @since 2.1.0
105
+ */
106
+ $enabled = apply_filters(
107
+ 'Boldgrid\Library\Library\Notice\ClaimPremiumKey_enable',
108
+ false
109
+ );
110
+
111
+ // If a Connect Key is not saved, then skip this notice; it will be in the key prompt.
112
+ $hasConnectKey = (bool) Library\Configs::get( 'key' );
113
+
114
+ if ( $enabled && $hasConnectKey ) {
115
+ // If user has dismissed the notice, then do not display the notice.
116
+ $display = ! Library\Notice::isDismissed( $this->userNoticeKey );
117
+
118
+ // Do not display if user has an Envato-connected Prime theme.
119
+ $hasEnvatoPrime = $this->key->getLicense()->isPremium( 'envato-prime' );
120
+
121
+ if ( $hasEnvatoPrime ) {
122
+ $display = false;
123
+ }
124
+
125
+ /**
126
+ * Check of there are any overrides for displaying this notice.
127
+ *
128
+ * @since 2.1.0
129
+ */
130
+ $display = apply_filters(
131
+ 'Boldgrid\Library\Library\Notice\ClaimPremiumKey_display',
132
+ $display
133
+ );
134
+
135
+ if ( $display ) {
136
+ include dirname( __DIR__ ) . '/Views/ClaimPremiumKey.php';
137
+
138
+ self::$isDisplayed = true;
139
+ }
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Key input handling.
145
+ *
146
+ * @since 1.0.0
147
+ *
148
+ * @hook: wp_ajax_addKey
149
+ */
150
+ public function addKey() {
151
+ $key = $this->validate();
152
+ $data = $this->key->callCheckVersion( array( 'key' => $key ) );
153
+ $msg = $this->getMessages();
154
+
155
+ if ( is_object( $data ) ) {
156
+ $this->key->save( $data, $key );
157
+ wp_send_json_success( array( 'message' => $msg->success ) );
158
+ } else {
159
+ wp_send_json_error( array( 'message' => $msg->error ) );
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Get static class property isDismissed.
165
+ *
166
+ * @since 2.0.1
167
+ *
168
+ * @see \Boldgrid\Library\Library\Notice::isDismissed()
169
+ *
170
+ * @hook: Boldgrid\Library\Notice\ClaimPremiumKey\getIsDismissed
171
+ */
172
+ public function getIsDismissed() {
173
+ if( is_null( self::$isDismissed ) ) {
174
+ self::$isDismissed = Library\Notice::isDismissed( $this->userNoticeKey );
175
+ }
176
+
177
+ return self::$isDismissed;
178
+ }
179
+
180
+ /**
181
+ * Get static class property isDisplayed.
182
+ *
183
+ * @since 2.0.1
184
+ *
185
+ * @hook: Boldgrid\Library\Notice\KeyPrompt\getIsDisplayed
186
+ */
187
+ public function getIsDisplayed() {
188
+ return self::$isDisplayed;
189
+ }
190
+ }
vendor/boldgrid/library/src/Library/Notice/KeyPrompt.php ADDED
@@ -0,0 +1,244 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Library Key Prompt Notice
4
+ *
5
+ * @package Boldgrid\Library
6
+ * @subpackage \Util
7
+ *
8
+ * @version 1.0.0
9
+ * @author BoldGrid <wpb@boldgrid.com>
10
+ */
11
+
12
+ namespace Boldgrid\Library\Library\Notice;
13
+
14
+ use Boldgrid\Library\Library;
15
+
16
+ /**
17
+ * BoldGrid Library Key Prompt Notice.
18
+ *
19
+ * This class is responsible for adding the Key Prompt notice logic
20
+ * to a user's WordPress dashboard.
21
+ *
22
+ * @since 1.0.0
23
+ */
24
+ class KeyPrompt {
25
+
26
+ /**
27
+ * Bool indicating whether or not this key prompt has been dismissed by the current user.
28
+ *
29
+ * @since 2.0.1
30
+ *
31
+ * @var mixed $isDismissed Null if not set, otherwise bool.
32
+ */
33
+ public static $isDismissed = null;
34
+
35
+ /**
36
+ * Bool indicating whether or not we are currently showing the notice.
37
+ *
38
+ * @since 2.0.1
39
+ *
40
+ * @var bool $isDisplayed
41
+ */
42
+ public static $isDisplayed = false;
43
+
44
+ /**
45
+ * @access private
46
+ *
47
+ * @var object $key Key class object.
48
+ * @var object $messages Messages used for key prompt.
49
+ * @var string $userNoticeKey When dismissing this prompt, this is the
50
+ * identifier for the user notice.
51
+ */
52
+ private
53
+ $key,
54
+ $messages,
55
+ $userNoticeKey;
56
+
57
+ /**
58
+ * Initialize class and set class properties.
59
+ *
60
+ * This class is automatically instantiated through the following events:
61
+ * #. Boldgrid\Library\Util\Load->__construct
62
+ * #. Boldgrid\Library\Library\Start->__construct
63
+ * #. Boldgrid\Library\Library\Key->__construct
64
+ * #. Boldgrid\Library\Library\Notice->__construct
65
+ * #. Boldgrid\Library\Library\Notice\KeyPrompt->__construct
66
+ *
67
+ * This is important to note because if you ever wanted to instantiate
68
+ * another KeyPrompt class, you should know it's probably already been done
69
+ * so.
70
+ *
71
+ * Some of the class properties (such as isDismissed and isDisplayed) are
72
+ * static and easily retrievable via filters.
73
+ *
74
+ * @since 1.0.0
75
+ *
76
+ * @param \Boldgrid\Library\Library\Key Key object.
77
+ */
78
+ public function __construct( Library\Key $key ) {
79
+ $this->key = $key;
80
+ $this->setMessages();
81
+ $this->userNoticeKey = 'bg-key-prompt';
82
+ Library\Filter::add( $this );
83
+ }
84
+
85
+ /**
86
+ * Sets the messages returned by key prompt.
87
+ *
88
+ * @since 1.0.0
89
+ *
90
+ * @return object $messages Messages used by key prompt.
91
+ */
92
+ private function setMessages() {
93
+ $msg = new \stdClass();
94
+ $msg->success = esc_html__( 'Your api key has been saved successfully.', 'boldgrid-inspirations' );
95
+ $msg->error = sprintf( esc_html__( 'Your API key appears to be invalid!%sPlease try to enter your BoldGrid Connect Key again.', 'boldgrid-inspirations' ), '<br />' );
96
+ $msg->nonce = esc_html__( 'Security violation! An invalid nonce was detected.', 'boldgrid-inspirations' );
97
+
98
+ return $this->messages = $msg;
99
+ }
100
+
101
+ /**
102
+ * Adds the required CSS and JS to the WordPress dashboard.
103
+ *
104
+ * @since 1.0.0
105
+ *
106
+ * @hook: admin_enqueue_scripts
107
+ */
108
+ public function enqueue() {
109
+ wp_enqueue_style(
110
+ 'bglib-api-notice-css',
111
+ Library\Configs::get( 'libraryUrl' ) . 'src/assets/css/api-notice.css'
112
+ );
113
+ wp_enqueue_script(
114
+ 'bglib-api-notice-js',
115
+ Library\Configs::get( 'libraryUrl' ) . 'src/assets/js/api-notice.js'
116
+ );
117
+ }
118
+
119
+ /**
120
+ * Displays the notice.
121
+ *
122
+ * @since 1.0.0
123
+ *
124
+ * @see \Boldgrid\Library\Library\Notice::isDismissed()
125
+ *
126
+ * @hook: admin_notices
127
+ */
128
+ public function keyNotice() {
129
+ $display_notice = apply_filters(
130
+ 'Boldgrid\Library\Library\Notice\KeyPrompt_display',
131
+ ( ! Library\Notice::isDismissed( $this->userNoticeKey ) )
132
+ );
133
+
134
+ if ( $display_notice ) {
135
+ $current_user = wp_get_current_user();
136
+ $email = $current_user->user_email;
137
+ $first_name = empty( $current_user->user_firstname ) ? '' : $current_user->user_firstname;
138
+ $last_name = empty( $current_user->user_lastname ) ? '' : $current_user->user_lastname;
139
+ $api = Library\Configs::get( 'api' ) . '/api/open/generateKey';
140
+
141
+ /**
142
+ * Check if the Envato notice to claim a Premium Connect Key should be enabled.
143
+ *
144
+ * A theme can add this filter and return true, which will enable this notice.
145
+ *
146
+ * @since 2.1.0
147
+ */
148
+ $enableClaimMessage = apply_filters(
149
+ 'Boldgrid\Library\Library\Notice\ClaimPremiumKey_enable',
150
+ false
151
+ );
152
+
153
+ include dirname( __DIR__ ) . '/Views/KeyPrompt.php';
154
+
155
+ self::$isDisplayed = true;
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Key input handling.
161
+ *
162
+ * @since 1.0.0
163
+ *
164
+ * @hook: wp_ajax_addKey
165
+ */
166
+ public function addKey() {
167
+ $key = $this->validate();
168
+ $data = $this->key->callCheckVersion( array( 'key' => $key ) );
169
+ $msg = $this->getMessages();
170
+
171
+ if ( is_object( $data ) ) {
172
+ $this->key->save( $data, $key );
173
+ wp_send_json_success( array( 'message' => $msg->success ) );
174
+ } else {
175
+ wp_send_json_error( array( 'message' => $msg->error ) );
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Handles validation of user input on API key entry form.
181
+ *
182
+ * @since 1.0.0
183
+ *
184
+ * @return string The user's validate API key hash.
185
+ */
186
+ protected function validate() {
187
+ $msg = $this->getMessages();
188
+
189
+ // Validate nonce.
190
+ if ( ! isset( $_POST['set_key_auth'] ) || ! check_ajax_referer( 'boldgrid_set_key', 'set_key_auth', false ) ) {
191
+ wp_send_json_error( array( 'message' => $msg->nonce ) );
192
+ }
193
+
194
+ // Validate user input.
195
+ if ( empty( $_POST['api_key'] ) ) {
196
+ wp_send_json_error( array( 'message' => $msg->error ) );
197
+ }
198
+
199
+ // Validate key.
200
+ $valid = new Library\Key\Validate( $_POST['api_key'] );
201
+ if ( ! $valid->getValid() ) {
202
+ wp_send_json_error( array( 'message' => $msg->error ) );
203
+ }
204
+
205
+ return $valid->getHash();
206
+ }
207
+
208
+ /**
209
+ * Get static class property isDismissed.
210
+ *
211
+ * @since 2.0.1
212
+ *
213
+ * @hook: Boldgrid\Library\Notice\KeyPrompt\getIsDismissed
214
+ */
215
+ public function getIsDismissed() {
216
+ if( is_null( self::$isDismissed ) ) {
217
+ self::$isDismissed = Library\Notice::isDismissed( $this->userNoticeKey );
218
+ }
219
+
220
+ return self::$isDismissed;
221
+ }
222
+
223
+ /**
224
+ * Get static class property isDisplayed.
225
+ *
226
+ * @since 2.0.1
227
+ *
228
+ * @hook: Boldgrid\Library\Notice\KeyPrompt\getIsDisplayed
229
+ */
230
+ public function getIsDisplayed() {
231
+ return self::$isDisplayed;
232
+ }
233
+
234
+ /**
235
+ * Gets messages class property.
236
+ *
237
+ * @since 1.0.0
238
+ *
239
+ * @return object $messages The messages class property.
240
+ */
241
+ protected function getMessages() {
242
+ return $this->messages;
243
+ }
244
+ }
vendor/boldgrid/library/src/Library/Plugin/Checker.php ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Library Plugin Checker
4
+ *
5
+ * @package Boldgrid\Plugin
6
+ *
7
+ * @since 1.0.0
8
+ * @author BoldGrid <wpb@boldgrid.com>
9
+ */
10
+
11
+ namespace Boldgrid\Library\Library\Plugin;
12
+
13
+ /**
14
+ * BoldGrid Plugin Checker.
15
+ *
16
+ * This class is responsible for plugin related utility helpers.
17
+ *
18
+ * @since 1.0.0
19
+ */
20
+ class Checker {
21
+ /**
22
+ * Plugin pattern.
23
+ *
24
+ * @since 1.0.0
25
+ *
26
+ * @access private
27
+ *
28
+ * @var string
29
+ */
30
+ private $pluginPattern = '^(boldgrid-|post-and-page-builder)';
31
+
32
+ public function __construct() {
33
+ add_action( 'upgrader_process_complete', function() {
34
+ delete_site_transient( 'boldgrid_plugins_filtered' );
35
+ } );
36
+ }
37
+
38
+ /**
39
+ * Check plugins.
40
+ *
41
+ * @since 1.1.4
42
+ */
43
+ public function run() {
44
+
45
+ add_action( 'boldgrid_plugins_updated',
46
+ array(
47
+ 'Boldgrid\Library\Util\Option',
48
+ 'deletePluginTransients',
49
+ ),
50
+ 10,
51
+ 0
52
+ );
53
+
54
+ if ( is_admin() ) {
55
+ $this->findUpdated();
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Get pluginPattern.
61
+ *
62
+ * @since 2.2.1
63
+ *
64
+ * @return string
65
+ */
66
+ public function getPluginPattern() {
67
+ return $this->pluginPattern;
68
+ }
69
+
70
+ /**
71
+ * Find updated/new BoldGrid plugins.
72
+ *
73
+ * Check for post-update actions for BoldGrid plugins.
74
+ *
75
+ * @since 1.0.0
76
+ *
77
+ * @link https://developer.wordpress.org/reference/functions/get_site_option/
78
+ * @see get_site_option()
79
+ * @see Boldgrid\Library\Util\Plugin::getFiltered()
80
+ *
81
+ * @return bool
82
+ */
83
+ public function findUpdated() {
84
+ $updated = array();
85
+
86
+ $boldgridSettings = get_site_option( 'boldgrid_settings' );
87
+
88
+ $plugins = get_site_transient( 'boldgrid_plugins_filtered' );
89
+
90
+ if ( empty( $plugins ) ) {
91
+ $plugins = \Boldgrid\Library\Library\Util\Plugin::getFiltered( $this->pluginPattern );
92
+ set_site_transient( 'boldgrid_plugins_filtered', $plugins );
93
+ }
94
+
95
+ foreach ( $plugins as $slug => $data ) {
96
+ if ( empty( $boldgridSettings['plugins_checked'][ $slug ][ $data['Version'] ] ) ) {
97
+ $updated[ $slug ] = $data;
98
+
99
+ /**
100
+ * Action triggered when a BoldGrid plugin is found to be updated or new.
101
+ *
102
+ * @since 1.1.4
103
+ *
104
+ * @param string $tag Tag name.
105
+ * @param string $slug Plugin slug (folder/file).
106
+ * @param array $data {
107
+ * Plugin data from a filtered get_plugins() call.
108
+ *
109
+ * @type string $Name Plugin name.
110
+ * @type string $PluginURI Plugin URI.
111
+ * @type string $Version Version number/string.
112
+ * @type string $Description Description.
113
+ * @type string $Author Author name.
114
+ * @type string $AuthorURI Author URI.
115
+ * @type string $TextDomain Text domain.
116
+ * @type string $DomainPath Domain path.
117
+ * @type string $Network Network.
118
+ * @type string $Title Plugin title.
119
+ * @type string $AuthorName Author name.
120
+ * }
121
+ */
122
+ do_action( 'plugin_updated_' . $slug, $slug, $data );
123
+ }
124
+
125
+ $boldgridSettings['plugins_checked'][ $slug ][ $data['Version'] ] = time();
126
+ }
127
+
128
+ if ( $updated ) {
129
+ /**
130
+ * When one or more BoldGrid plugins are updated or new, then delete plugin transients.
131
+ *
132
+ * @since 1.1.4
133
+ */
134
+ do_action( 'boldgrid_plugins_updated' );
135
+ }
136
+
137
+ update_site_option( 'boldgrid_settings', $boldgridSettings );
138
+
139
+ return $updated;
140
+ }
141
+ }
vendor/boldgrid/library/src/Library/Registration.php ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Library Registration.
4
+ *
5
+ * @package Boldgrid\Library
6
+ *
7
+ * @version 2.2.1
8
+ * @author BoldGrid <wpb@boldgrid.com>
9
+ */
10
+
11
+ namespace Boldgrid\Library\Library;
12
+
13
+ /**
14
+ * BoldGrid Library Registration Class.
15
+ *
16
+ * This is a companion class to Boldgrid\Library\Util\Registration class.
17
+ *
18
+ * @since 2.2.1
19
+ */
20
+ class Registration {
21
+
22
+ /**
23
+ * Constructor.
24
+ *
25
+ * @since 2.2.1
26
+ */
27
+ public function __construct() {
28
+ Filter::add( $this );
29
+ }
30
+
31
+ /**
32
+ * Take action before a plugin is upgraded.
33
+ *
34
+ * It's nice that WordPress has an "upgrader_process_complete" action you
35
+ * can hook into to take action after a plugin is upgraded. However, what
36
+ * about an action BEFORE the upgrade? Such an action does not exist.
37
+ *
38
+ * What does exist is the ability to filter our $upgrader options before the
39
+ * upgrade takes place. So, in order to do an action before the upgrade
40
+ * runs, we'll hook into the upgrader_package_options filter.
41
+ *
42
+ * @since 2.2.1
43
+ *
44
+ * @hook: upgrader_package_options
45
+ *
46
+ * @param array $options See filter declaration in
47
+ * wp-admin/includes/class-wp-upgrader.php
48
+ * @return array
49
+ */
50
+ public function preUpgradePlugin( $options ) {
51
+ $plugin = ! empty( $options['hook_extra']['plugin'] ) ? $options['hook_extra']['plugin'] : null;
52
+
53
+ $isBoldgridPlugin = \Boldgrid\Library\Library\Util\Plugin::isBoldgridPlugin( $plugin );
54
+
55
+ if( $isBoldgridPlugin ) {
56
+
57
+ /*
58
+ * Before this plugin is upgraded, remove it from the list of
59
+ * registered libraries.
60
+ *
61
+ * We need to remove it because this plugin may be upgraded
62
+ * (or downgraded) to a version of the plugin where it does not
63
+ * contain the library.
64
+ *
65
+ * Reregistration of this plugin will take place in the constructor
66
+ * the next time this class is instantiated.
67
+ */
68
+ $utilPlugin = new \Boldgrid\Library\Util\Registration\Plugin( $plugin );
69
+ $utilPlugin->deregister();
70
+ }
71
+
72
+ return $options;
73
+ }
74
+ }
vendor/boldgrid/library/src/Library/ReleaseChannel.php ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Library Release Channel Class
4
+ *
5
+ * @package Boldgrid\Library
6
+ * @subpackage \Library
7
+ *
8
+ * @version 1.0.0
9
+ * @author BoldGrid <wpb@boldgrid.com>
10
+ */
11
+
12
+ namespace Boldgrid\Library\Library;
13
+
14
+ use Boldgrid\Library\Util;
15
+
16
+ /**
17
+ * BoldGrid Library Release Channel Class.
18
+ *
19
+ * This class is responsible for retrieving and setting the release channel the
20
+ * user has selected for plugin and theme updates.
21
+ *
22
+ * @since 1.0.0
23
+ */
24
+ class ReleaseChannel {
25
+
26
+ /**
27
+ * @var string $pluginChannel The plugin release channel set for the user.
28
+ * @var string $themeChannel The theme release channel set for the user.
29
+ */
30
+ private
31
+ $pluginChannel,
32
+ $themeChannel;
33
+
34
+ /**
35
+ * Initialize class and set class properties.
36
+ *
37
+ * @since 1.0.0
38
+ */
39
+ public function __construct() {
40
+ $this->setPluginChannel();
41
+ $this->setThemeChannel();
42
+ Filter::add( $this );
43
+ }
44
+
45
+ /**
46
+ * Sets the pluginChannel class property.
47
+ *
48
+ * @since 1.0.0
49
+ *
50
+ * @return object $this Sets $pluginChannel class property.
51
+ */
52
+ private function setPluginChannel() {
53
+ return $this->pluginChannel = Util\Option::get( 'release_channel' ) ? Util\Option::get( 'release_channel' ) : 'stable';
54
+ }
55
+
56
+ /**
57
+ * Sets the themeChannel class property.
58
+ *
59
+ * @since 1.0.0
60
+ *
61
+ * @return object $this Sets $themeChannel class property.
62
+ */
63
+ private function setThemeChannel() {
64
+ return $this->themeChannel = Util\Option::get( 'theme_release_channel' ) ? Util\Option::get( 'theme_release_channel' ) : 'stable';
65
+ }
66
+
67
+ /**
68
+ * Update Plugin Channel
69
+ *
70
+ * This methods fires when boldgrid_settings is updated. We check the values
71
+ * for the new plugin release channel to see if it changed here.
72
+ *
73
+ * @since 1.0.0
74
+ *
75
+ * @hook: update_site_option_boldgrid_settings
76
+ *
77
+ * @param string $option The option name.
78
+ * @param mixed $new New option value being set.
79
+ * @param mixed $old Old option value being set.
80
+ *
81
+ * @return mixed $new The new option being set.
82
+ */
83
+ public function updateChannel( $option, $new, $old ) {
84
+
85
+ // Plugin checks.
86
+ if ( ! empty( $old['release_channel'] ) || ! empty( $new['release_channel'] ) ) {
87
+ if ( $old['release_channel'] !== $new['release_channel'] ) {
88
+ Util\Option::deletePluginTransients();
89
+ wp_update_plugins();
90
+ }
91
+ }
92
+
93
+ // Theme checks.
94
+ if ( ! empty( $old['theme_release_channel'] ) || ! empty( $new['theme_release_channel'] ) ) {
95
+ if ( $old['theme_release_channel'] !== $new['theme_release_channel'] ) {
96
+
97
+ /**
98
+ * Action to take when theme release channel has changed.
99
+ *
100
+ * @since 1.1
101
+ *
102
+ * @param type string $old Old theme release channel.
103
+ * @param type string $new New theme release channel.
104
+ */
105
+ do_action( 'Boldgrid\Library\Library\ReleaseChannel\theme_channel_updated', $old['theme_release_channel'], $new['theme_release_channel'] );
106
+
107
+ delete_site_transient( 'boldgrid_api_data' );
108
+ delete_site_transient( 'update_themes' );
109
+ wp_update_themes();
110
+ }
111
+ }
112
+
113
+ return $new;
114
+ }
115
+
116
+ /**
117
+ * Gets the pluginChannel class property.
118
+ *
119
+ * @since 1.0.0
120
+ *
121
+ * @nohook
122
+ *
123
+ * @return string $pluginChannel Plugin Channel that is set.
124
+ */
125
+ public function getPluginChannel() {
126
+ return $this->pluginChannel;
127
+ }
128
+
129
+ /**
130
+ * Gets the themeChannel class property.
131
+ *
132
+ * @since 1.0.0
133
+ *
134
+ * @nohook
135
+ *
136
+ * @return string $themeChannel Theme Channel that is set.
137
+ */
138
+ public function getThemeChannel() {
139
+ return $this->themeChannel;
140
+ }
141
+ }
vendor/boldgrid/library/src/Library/Reseller.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Library Reseller Class
4
+ *
5
+ * @package Boldgrid\Library
6
+ * @subpackage \Library
7
+ *
8
+ * @version 1.1
9
+ * @author BoldGrid <wpb@boldgrid.com>
10
+ */
11
+
12
+ namespace Boldgrid\Library\Library;
13
+
14
+ /**
15
+ * BoldGrid Library Reseller Class.
16
+ *
17
+ * @since 1.1
18
+ */
19
+ class Reseller {
20
+
21
+ /**
22
+ * BoldGrid Central url.
23
+ *
24
+ * @since 1.1
25
+ *
26
+ * @var string
27
+ */
28
+ public $centralUrl = 'https://www.boldgrid.com/central';
29
+
30
+ /**
31
+ * Reseller data.
32
+ *
33
+ * @since 1.1
34
+ *
35
+ * @var array
36
+ */
37
+ public $data = array();
38
+
39
+ /**
40
+ * Constructor.
41
+ *
42
+ * @since 1.1
43
+ */
44
+ public function __construct() {
45
+ $this->setData();
46
+ Filter::add( $this );
47
+ }
48
+
49
+ /**
50
+ * Set data.
51
+ *
52
+ * @since 1.1
53
+ *
54
+ * @hook: update_option_boldgrid_reseller
55
+ */
56
+ public function setData() {
57
+ $defaults = array(
58
+ 'reseller_coin_url' => $this->centralUrl,
59
+ );
60
+
61
+ $this->data = array_merge( $defaults, get_option( 'boldgrid_reseller', array() ) );
62
+ }
63
+ }
vendor/boldgrid/library/src/Library/Start.php ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Library Start.
4
+ *
5
+ * @package Boldgrid\Library
6
+ * @subpackage \Library
7
+ *
8
+ * @version 1.0.0
9
+ * @author BoldGrid <wpb@boldgrid.com>
10
+ */
11
+
12
+ namespace Boldgrid\Library\Library;
13
+
14
+ /**
15
+ * BoldGrid Library Start Class.
16
+ *
17
+ * This class is responsible for setting up the BoldGrid Library.
18
+ *
19
+ * @since 1.0.0
20
+ */
21
+ class Start {
22
+
23
+ /**
24
+ * @var object $configs Library Configuration Object.
25
+ * @var object $releaseChannel Library ReleaseChannel Object.
26
+ * @var object $pluginInstaller Library Plugin Installer Object.
27
+ * @var object $key Library Key Object.
28
+ */
29
+ private
30
+ $configs,
31
+ $releaseChannel,
32
+ $pluginInstaller,
33
+ $key;
34
+
35
+ /**
36
+ * Initialize class and set class properties.
37
+ *
38
+ * @since 1.0.0
39
+ *
40
+ * @uses \Boldgrid\Library\Library\Configs()
41
+ * @uses \Boldgrid\Library\Library\ReleaseChannel()
42
+ * @uses \Boldgrid\Library\Library\Key()
43
+ * @uses \Boldgrid\Library\Plugin\Installer()
44
+ * @uses \Boldgrid\Library\Library\Start::getReleaseChannel()
45
+ *
46
+ * @param array $configs Plugin configuration array.
47
+ */
48
+ public function __construct( $configs = null ) {
49
+ $configs = $this->filterConfigs( $configs );
50
+
51
+ $this->configs = new Configs( $configs );
52
+ $this->releaseChannel = new ReleaseChannel;
53
+
54
+ if ( Configs::get( 'keyValidate' ) ) {
55
+ $this->key = new Key( $this->getReleaseChannel() );
56
+ }
57
+
58
+ add_action( 'admin_init' , array( $this, 'loadPluginInstaller' ) );
59
+ }
60
+
61
+ /**
62
+ * Get releaseChannel class property.
63
+ *
64
+ * @since 1.0.0
65
+ *
66
+ * @return object $releaseChannel Library\ReleaseChannel object.
67
+ */
68
+ public function getReleaseChannel() {
69
+ return $this->releaseChannel;
70
+ }
71
+
72
+ /**
73
+ * Initialization.
74
+ *
75
+ * @since 1.1.4
76
+ *
77
+ * @uses \Boldgrid\Library\Library\Plugin\Checker::run()
78
+ */
79
+ public function init() {
80
+
81
+ // Registration class runs Filter::add($this) in __construct.
82
+ $registration = new \Boldgrid\Library\Library\Registration();
83
+
84
+ // Update class runs Filter::add($this) in __construct.
85
+ $update = new \Boldgrid\Library\Library\Update();
86
+
87
+ $pluginChecker = new \Boldgrid\Library\Library\Plugin\Checker();
88
+ $pluginChecker->run();
89
+ }
90
+
91
+ /**
92
+ * Load the Plugin\Installer class, if exists.
93
+ *
94
+ * @since 1.1.7
95
+ */
96
+ public function loadPluginInstaller() {
97
+ if ( ! did_action( 'Boldgrid\Library\Library\Start::loadPluginInstaller' ) ) {
98
+ do_action( 'Boldgrid\Library\Library\Start::loadPluginInstaller' );
99
+
100
+ if ( class_exists( '\Boldgrid\Library\Plugin\Installer' ) ) {
101
+ $this->pluginInstaller = new \Boldgrid\Library\Plugin\Installer(
102
+ Configs::get( 'pluginInstaller' ),
103
+ $this->getReleaseChannel()
104
+ );
105
+ }
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Filter the configuration array.
111
+ *
112
+ * @since 2.2.1
113
+ *
114
+ * @param array $configs Configuration array.
115
+ * @return array
116
+ */
117
+ public function filterConfigs( $configs ) {
118
+ if ( ! empty( $configs['libraryDir'] ) ) {
119
+ $configs['libraryUrl'] = str_replace(
120
+ ABSPATH,
121
+ get_site_url() . '/',
122
+ $configs['libraryDir']
123
+ );
124
+ }
125
+
126
+ return $configs;
127
+ }
128
+ }
vendor/boldgrid/library/src/Library/Ui.php ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid UI/UX.
4
+ *
5
+ * @package Boldgrid\Library
6
+ * @subpackage \Library
7
+ *
8
+ * @version 1.1.7
9
+ * @author BoldGrid <wpb@boldgrid.com>
10
+ */
11
+
12
+ namespace Boldgrid\Library\Library;
13
+
14
+ use Boldgrid\Library\Library;
15
+
16
+ /**
17
+ * BoldGrid UI/UX.
18
+ *
19
+ * This class is responsible for managing shared UI/UX.
20
+ *
21
+ * LEFT AND RIGHT COLS
22
+ * <div class="col-container">
23
+ * <div class="col-left">
24
+ * <ul class="bg-left-nav">
25
+ * <li class="active" data-id="section_1">Section 1</li>
26
+ * <li data-id="section_2">Section 2</li>
27
+ * </ul>
28
+ * </div>
29
+ * <div class="col-right">
30
+ * <div class="col-right-section" id="section_1">
31
+ * <div class="bg-box">
32
+ * <div class="bg-box-top">
33
+ * Title
34
+ * </div>
35
+ * <div class="bg-box-bottom">
36
+ * Content
37
+ * </div>
38
+ * <div class="bg-box-bottom premium">
39
+ * <a href="" class="button button-success">Get Premium</a>
40
+ * </div>
41
+ * </div>
42
+ * </div>
43
+ * <div class="col-right-section" id="section_2">Content</div>
44
+ * </div>
45
+ * </div>
46
+ *
47
+ * POSTBOX
48
+ * <div class="postbox">
49
+ * <h2 class="hndle ui-sortable-handle"><span>Remote Storage</span></h2>
50
+ * <div class="inside">
51
+ * Main Content
52
+ * </div>
53
+ * <div class="inside premium wp-clearfix">
54
+ * <a href="" class="button button-success">Get Premium</a>
55
+ * Get Premium Message.
56
+ * </div>
57
+ * </div>
58
+ *
59
+ * @since 1.1.7
60
+ */
61
+ class Ui {
62
+
63
+ /**
64
+ * Constructor.
65
+ *
66
+ * @since 1.1.7
67
+ */
68
+ public function __construct() {
69
+ Filter::add( $this );
70
+ }
71
+
72
+ /**
73
+ * Adds the required CSS and JS to the WordPress dashboard.
74
+ *
75
+ * @since 1.1.7
76
+ *
77
+ * @hook: admin_enqueue_scripts
78
+ */
79
+ public function enqueue() {
80
+ wp_register_style(
81
+ 'bglib-ui-css',
82
+ Library\Configs::get( 'libraryUrl' ) . 'src/assets/css/ui.css'
83
+ );
84
+
85
+ wp_register_script(
86
+ 'bglib-ui-js',
87
+ Library\Configs::get( 'libraryUrl' ) . 'src/assets/js/ui.js',
88
+ 'jquery.sticky-kit'
89
+ );
90
+
91
+ wp_register_script(
92
+ 'bglib-sticky',
93
+ Library\Configs::get( 'libraryUrl' ) . 'src/assets/js/sticky.js',
94
+ 'jquery'
95
+ );
96
+ }
97
+
98
+ /**
99
+ * Create markup for left / right columned container.
100
+ *
101
+ * @since 1.1.7
102
+ *
103
+ * @hook Boldgrid\Library\Ui\render_col_container
104
+ *
105
+ * @param array $sections {
106
+ * An array of data used to create a left and right columned page.
107
+ *
108
+ * @type array $sections {
109
+ * @type string $id Section id.
110
+ * @type string $title Section title.
111
+ * @type string $content Section content.
112
+ * }
113
+ * @type string $post_col_right Markup to display after all col-right
114
+ * sections.
115
+ * }
116
+ * @return string
117
+ */
118
+ public function render_col_container( $sections ) {
119
+
120
+ if( empty( $sections['sections'] ) ) {
121
+ return $sections;
122
+ }
123
+
124
+ $section_count = 0;
125
+
126
+ $show_section = ! empty( $_GET['section'] ) ? $_GET['section'] : null;
127
+
128
+ $content = '';
129
+ $navigation = '<ul class="bg-left-nav">';
130
+ foreach ( $sections['sections'] as $section ) {
131
+ $section_count++;
132
+
133
+ /*
134
+ * Determine which section should be visible first.
135
+ *
136
+ * You can pass in &section=section-id to choose a section other
137
+ * than the first to display by default.
138
+ */
139
+ $is_first_section = is_null( $show_section ) && 1 === $section_count;
140
+ $is_show_section = ! is_null( $show_section ) && $section['id'] === $show_section;
141
+ $nav_class = '';
142
+ $section_style = 'display:none;';
143
+ if( $is_first_section || $is_show_section ) {
144
+ $nav_class = 'active';
145
+ $section_style = '';
146
+ }
147
+
148
+ $navigation .= sprintf( '
149
+ <li class="%3$s" data-section-id="%1$s">%2$s</li>',
150
+ $section['id'],
151
+ $section['title'],
152
+ $nav_class
153
+ );
154
+
155
+ $content .= sprintf( '
156
+ <div class="col-right-section" id="%2$s" style="%3$s">
157
+ %1$s
158
+ </div>',
159
+ $section['content'],
160
+ $section['id'],
161
+ $section_style
162
+ );
163
+ }
164
+ $navigation .= '</ul>';
165
+
166
+ $markup = sprintf( '
167
+ <div id="col-container" class="wp-clearfix">
168
+ <div id="col-left">
169
+ %1$s
170
+ </div>
171
+ <div id="col-right">
172
+ %2$s
173
+ %3$s
174
+ </div>
175
+ </div>',
176
+ $navigation,
177
+ $content,
178
+ ! empty( $sections['post_col_right'] ) ? $sections['post_col_right'] : ''
179
+ );
180
+
181
+ return $markup;
182
+ }
183
+ }
vendor/boldgrid/library/src/Library/Update.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Update.
4
+ *
5
+ * @package Boldgrid\Library
6
+ * @subpackage \Library
7
+ *
8
+ * @version 2.3.0
9
+ * @author BoldGrid <wpb@boldgrid.com>
10
+ */
11
+
12
+ namespace Boldgrid\Library\Library;
13
+
14
+ use Boldgrid\Library\Library;
15
+
16
+ /**
17
+ * BoldGrid Update.
18
+ *
19
+ * The main purpose of this class is to handle auto updates (as configured in
20
+ * the boldgrid_settings option).
21
+ *
22
+ * @since 2.3.0
23
+ *
24
+ * @see https://codex.wordpress.org/Configuring_Automatic_Background_Updates
25
+ */
26
+ class Update {
27
+
28
+ /**
29
+ * Constructor.
30
+ *
31
+ * @since 2.3.0
32
+ */
33
+ public function __construct() {
34
+ Filter::add( $this );
35
+ }
36
+
37
+ /**
38
+ * Auto update plugin.
39
+ *
40
+ * @since 2.3.0
41
+ *
42
+ * @hook: auto_update_plugin
43
+ */
44
+ public function auto_update_plugin() {
45
+ $pluginAutoupdate = \Boldgrid\Library\Util\Option::get( 'plugin_autoupdate' );
46
+
47
+ return ! empty( $pluginAutoupdate );
48
+ }
49
+
50
+ /**
51
+ * Auto update theme.
52
+ *
53
+ * @since 2.3.0
54
+ *
55
+ * @hook: auto_update_theme
56
+ */
57
+ public function auto_update_theme() {
58
+ $themeAutoupdate = \Boldgrid\Library\Util\Option::get( 'theme_autoupdate' );
59
+
60
+ return ! empty( $themeAutoupdate );
61
+ }
62
+ }
vendor/boldgrid/library/src/Library/Util/Plugin.php ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Library Util Plugin.
4
+ *
5
+ * @package Boldgrid\Library
6
+ *
7
+ * @version 2.2.1
8
+ * @author BoldGrid <wpb@boldgrid.com>
9
+ */
10
+
11
+ namespace Boldgrid\Library\Library\Util;
12
+
13
+ /**
14
+ * BoldGrid Library Util Plugin Class.
15
+ *
16
+ * This class is responsible for plugin related utility helpers.
17
+ *
18
+ * @since 2.2.1
19
+ */
20
+ class Plugin {
21
+
22
+ /**
23
+ * Get plugin data filtered by plugin slug pattern.
24
+ *
25
+ * @since 2.2.1
26
+ *
27
+ * @param string $pattern A regex pattern used to filter the plugin data array.
28
+ * @return array
29
+ */
30
+ public static function getFiltered( $pattern = '' ) {
31
+ if ( ! function_exists( 'get_plugins' ) ) {
32
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
33
+ }
34
+
35
+ $plugins = get_plugins();
36
+
37
+ if ( empty( $pattern ) ) {
38
+ return $plugins;
39
+ }
40
+
41
+ $filtered = array();
42
+
43
+ foreach ( $plugins as $slug => $data ) {
44
+ if ( preg_match( '#' . $pattern . '#', $slug ) ) {
45
+ $filtered[ $slug ] = $data;
46
+ }
47
+ }
48
+
49
+ return $filtered;
50
+ }
51
+
52
+ /**
53
+ * Determine if a plugin is a BoldGrid plugin.
54
+ *
55
+ * @since 2.2.1
56
+ *
57
+ * @param string $plugin Such as post-and-page-builder/post-and-page-builder.php
58
+ * @return bool
59
+ */
60
+ public static function isBoldgridPlugin( $plugin ) {
61
+ if( empty( $plugin ) ) {
62
+ return false;
63
+ }
64
+
65
+ $pluginChecker = new \Boldgrid\Library\Library\Plugin\Checker();
66
+
67
+ $plugins = \Boldgrid\Library\Library\Util\Plugin::getFiltered( $pluginChecker->getPluginPattern() );
68
+
69
+ return array_key_exists( $plugin, $plugins );
70
+ }
71
+ }
vendor/boldgrid/library/src/Library/Views/ClaimPremiumKey.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div id="boldgrid_claim_premium_notice"
2
+ class="boldgrid-notice library notice notice-warning is-dismissible"
3
+ data-notice-id="bg-claim-premium">
4
+ <p>
5
+ <?php
6
+ printf(
7
+ esc_html__(
8
+ 'Thank you for your Envato Market purchase.%sPlease visit %sBoldGrid Central%s to link your accounts and claim your Premium Connect Key.',
9
+ 'boldgrid-inspirations'
10
+ ),
11
+ '<br />',
12
+ '<a target="_blank" href="https://www.boldgrid.com/central/code/envato">',
13
+ '</a>'
14
+ );
15
+
16
+ wp_nonce_field( 'boldgrid_set_key', 'set_key_auth' );
17
+ ?>
18
+ </p>
19
+ </div>
vendor/boldgrid/library/src/Library/Views/ConnectionIssue.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div id="container_boldgrid_connection_notice" class="error">
2
+ <h2 class="dashicons-before dashicons-admin-network">
3
+ <?php esc_html_e( 'BoldGrid Connection Issue', 'boldgrid-inspirations' ); ?>
4
+ </h2>
5
+ <p>
6
+ <?php esc_html_e( 'There was an issue reaching the BoldGrid Connect server. Some BoldGrid features may be temporarily unavailable. Please try again in a moment.', 'boldgrid-inspirations' ); ?>
7
+ </p>
8
+ <p>
9
+ <?php printf(
10
+ esc_html__( 'If the issue persists, then please feel free to check our %sBoldGrid Status%s page.', 'boldgrid-inspirations' ),
11
+ '<a target="_blank" href="https://www.boldgrid.com/">',
12
+ '</a>'
13
+ ); ?>
14
+ </p>
15
+ </div>
vendor/boldgrid/library/src/Library/Views/InvalidLicense.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <div class="notice notice-warning is-dismissible">
2
+ <p>
3
+ <?php echo esc_html( 'You must have a valid BoldGrid Premium license in order to use premium plugins.', 'boldgrid-inspirations' ); ?>
4
+ </p>
5
+ </div>
vendor/boldgrid/library/src/Library/Views/KeyPrompt.php ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div id="container_boldgrid_api_key_notice"
2
+ class="boldgrid-notice library error notice is-dismissible"
3
+ data-notice-id="bg-key-prompt">
4
+ <div class="api-notice">
5
+ <h2 class="dashicons-before dashicons-admin-network">
6
+ <?php esc_html_e( 'Enter Your BoldGrid Connect Key', 'boldgrid-inspirations' ); ?>
7
+ </h2>
8
+ <p id="boldgrid_api_key_notice_message">
9
+ <?php printf( esc_html__( 'Please enter your %s32 digit BoldGrid Connect Key%s below and click submit.', 'boldgrid-inspirations' ), '<b>', '</b>' ); ?>
10
+ </p>
11
+ <form id="boldgrid-api-form" autocomplete="off">
12
+ <?php wp_nonce_field( 'boldgrid_set_key', 'set_key_auth' ); ?>
13
+ <div class="tos-box">
14
+ <input id="tos-box" type="checkbox" value="0">
15
+ <?php printf( esc_html__( 'I agree to the %sTerms of Use and Privacy Policy%s.', 'boldgrid-inspirations' ), '<a href="https://www.boldgrid.com/software-privacy-policy/" target="_blank">', '</a>' ); ?>
16
+ </div>
17
+ <br />
18
+ <input type="text" id="boldgrid_api_key" maxlength="37" placeholder="XXXXXXXX - XXXXXXXX - XXXXXXXX - XXXXXXXX" autocomplete="off" />
19
+ <button id="submit_api_key" class="button button-primary">
20
+ <?php esc_html_e( 'Submit', 'boldgrid-inspirations' ); ?>
21
+ </button>
22
+ <span>
23
+ <div id="boldgrid-api-loading" class="boldgrid-wp-spin"></div>
24
+ </span>
25
+ </form>
26
+ <br />
27
+ <?php
28
+ // Display either the Envato message or the default signup message.
29
+ if ( $enableClaimMessage ) {
30
+ printf(
31
+ esc_html__(
32
+ 'Thank you for your Envato Market purchase.%sPlease visit %sBoldGrid Central%s to link your accounts and claim your Premium Connect Key.',
33
+ 'boldgrid-inspirations'
34
+ ),
35
+ '<br />',
36
+ '<a target="_blank" href="https://www.boldgrid.com/central/code/envato">',
37
+ '</a>'
38
+ );
39
+ } else {
40
+ ?>
41
+ <a href="#" class="boldgridApiKeyLink">
42
+ <?php
43
+ esc_html_e( 'Don\'t have a Connect Key yet or lost your Key?', 'boldgrid-inspirations' );
44
+ ?>
45
+ </a>
46
+ <?php
47
+ }
48
+ ?>
49
+ </div>
50
+ <div class="new-api-key hidden">
51
+ <h2 class="dashicons-before dashicons-admin-network">
52
+ <?php esc_html_e( 'Request a BoldGrid Connect Key', 'boldgrid-inspirations' ); ?>
53
+ </h2>
54
+ <a href="#" class="enterKeyLink">
55
+ <?php esc_html_e( 'Have a Connect Key to enter?', 'boldgrid-inspirations' ); ?>
56
+ </a>
57
+ <br />
58
+ <br />
59
+ <div class="key-request-content">
60
+ <p id="requestKeyMessage">
61
+ <?php printf(
62
+ esc_html__(
63
+ 'There are two types of BoldGrid Connect Keys, a free key or an Official Host Premium Connect Key.
64
+ %sA Premium Connect Key is highly recommended and may already come with your hosting account.%s
65
+ If you do not have a Premium Connect Key, then you may request a free key below.
66
+ Please visit %sour site%s for full details.%s
67
+ If you have lost your key, you can have it resent by entering your information below.',
68
+ 'boldgrid-inspirations'
69
+ ),
70
+ '<b>',
71
+ '</b>',
72
+ "<a href='https://www.boldgrid.com/get-it-now/' target='_blank'>",
73
+ '</a>',
74
+ '<br /><br />' );
75
+ ?>
76
+ <br />
77
+ </p>
78
+ <p class="error-alerts"></p>
79
+ <form id="requestKeyForm">
80
+ <label>
81
+ <?php esc_html_e( 'First Name', 'boldgrid-inspirations' ); ?>:
82
+ </label>
83
+ <input type="text" id="firstName" maxlength="50" placeholder="<?php esc_html_e( 'First Name', 'boldgrid-inspirations' ); ?>" value="<?php echo $first_name; ?>" />
84
+ <label>
85
+ <?php esc_html_e( 'Last Name', 'boldgrid-inspirations' ); ?>:
86
+ </label>
87
+ <input type="text" id="lastName" maxlength="50" placeholder="<?php esc_html_e( 'Last Name', 'boldgrid-inspirations' ); ?>" value="<?php echo $last_name; ?>" />
88
+ <label>
89
+ <?php esc_html_e( 'E-mail', 'boldgrid-inspirations' ); ?>:
90
+ </label>
91
+ <input type="text" id="emailAddr" maxlength="50" placeholder="your@name.com" value="<?php echo $email; ?>" />
92
+ <br />
93
+ <input type="hidden" id="siteUrl" value="<?php echo get_admin_url(); ?>" />
94
+ <br />
95
+ <button id="requestKey" class="button button-primary">
96
+ <?php esc_html_e( 'Submit', 'boldgrid-inspirations' ); ?>
97
+ </button>
98
+ <span class="spinner"></span>
99
+ <input type="hidden" id="generate-api-key" value="<?php echo $api ?>" />
100
+ </form>
101
+ </div>
102
+ </div>
103
+ </div>
vendor/boldgrid/library/src/Util/Load.php ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Library Load Utility
4
+ *
5
+ * @package Boldgrid\Library
6
+ * @subpackage \Util
7
+ *
8
+ * @version 1.0.0
9
+ * @author BoldGrid <wpb@boldgrid.com>
10
+ */
11
+
12
+ namespace Boldgrid\Library\Util;
13
+
14
+ /**
15
+ * BoldGrid Library Load Utiliy Class.
16
+ *
17
+ * This class is responsible for determining the highest version of
18
+ * available libraries to load and then loading it.
19
+ *
20
+ * @since 1.0.0
21
+ */
22
+ class Load {
23
+
24
+ /**
25
+ * @access private
26
+ *
27
+ * @since 1.0.0
28
+ *
29
+ * @var array $configs BoldGrid Library configurations.
30
+ * @var array $libraries Available BoldGrid Library versions.
31
+ * @var object $load Highest BoldGrid Library version found to load.
32
+ * @var string $path The path to the BoldGrid Library to load.
33
+ */
34
+ private
35
+ $configs,
36
+ $libraries,
37
+ $load,
38
+ $path;
39
+
40
+ /**
41
+ * Initialize class and set class properties.
42
+ *
43
+ * @since 1.0.0
44
+ *
45
+ * @param array $configs BoldGrid Library configurations.
46
+ */
47
+ public function __construct( array $configs = array() ) {
48
+
49
+ // Set the configuration array.
50
+ $this->configs = $configs;
51
+
52
+ // Build the registration class.
53
+ $class = __NAMESPACE__ . '\\Registration\\' . ucfirst( $this->configs['type'] );
54
+
55
+ // Add hooks for registration.
56
+ $this->registration = new $class( $this->configs['file'] );
57
+
58
+ // Gets the available Library versions that can be loaded.
59
+ $this->libraries = Option::get( 'library' );
60
+
61
+ // Sets the BoldGrid Library version to load.
62
+ $this->setLoad( $this->getLibraries() );
63
+
64
+ // Sets the path to the Library files to load for themes/plugins.
65
+ $this->setPath();
66
+
67
+ // Add loaded Library path to configs.
68
+ $this->configs['libraryDir'] = trailingslashit( $this->getPath() ) . 'vendor/boldgrid/library/';
69
+
70
+ // Add loaded Library URL to configs.
71
+ $this->configs['libraryUrl'] = plugin_dir_url( $this->configs['libraryDir'] ) . 'library/';
72
+
73
+ // Initialize BoldGrid Library.
74
+ $this->load( $this->configs['loader'] );
75
+ }
76
+
77
+ /**
78
+ * Sets the product and version of library to load.
79
+ *
80
+ * This check will loop through the available libraries and load the
81
+ * highest version found. If an active plugin or theme contains a
82
+ * git branch instead of a tagged version, then that library will be
83
+ * loaded instead.
84
+ *
85
+ * @since 1.0.0
86
+ *
87
+ * @param array $libraries Registered library versions from options.
88
+ *
89
+ * @return object $load The product and version to load.
90
+ */
91
+ public function setLoad( $libraries ) {
92
+ $load = false;
93
+ $product = false;
94
+ foreach( $libraries as $name => $version ) {
95
+
96
+ // Check for branch versions normalized (dev/master).
97
+ if ( strpos( $version, 'dev' ) !== false ) {
98
+ $load = $version;
99
+ $product = $name;
100
+ break;
101
+ }
102
+
103
+ // Check for highest loaded version.
104
+ if ( version_compare( $load, $version ) === -1 ) {
105
+ $load = $version;
106
+ $product = $name;
107
+ }
108
+ }
109
+
110
+ return $this->load = ( object ) array( 'product' => $product, 'version' => $load );
111
+ }
112
+
113
+ /**
114
+ * Sets the path class property.
115
+ *
116
+ * This will determine the path to the found product's library to load.
117
+ *
118
+ * @since 1.0.0
119
+ *
120
+ * @return string $path Path to the library to load.
121
+ */
122
+ public function setPath() {
123
+ $found = $this->getLoad();
124
+ $path = false;
125
+
126
+ if ( ! empty( $found->product ) ) {
127
+
128
+ // Loading from must use plugin directory?
129
+ if ( ! is_file( $path = trailingslashit( WPMU_PLUGIN_DIR ) . $found->product ) ) {
130
+
131
+ // Loading from plugin directory?
132
+ if ( ! is_file( $path = trailingslashit( WP_PLUGIN_DIR ) . $found->product ) ) {
133
+
134
+ // Loading from a parent theme directory?
135
+ $path = get_template_directory() . '/inc/boldgrid-theme-framework/includes/theme';
136
+ }
137
+ }
138
+
139
+ // Loading from framework path override directory?
140
+ if ( defined( 'BGTFW_PATH' ) ) {
141
+ $dir = ABSPATH . trim( BGTFW_PATH, '/' ) . '/includes';
142
+ if ( is_dir( $dir . '/vendor/boldgrid/library' ) ) {
143
+ $path = $dir . '/theme';
144
+ }
145
+ }
146
+
147
+ $path = dirname( $path );
148
+ }
149
+
150
+ return $this->path = $path;
151
+ }
152
+
153
+ /**
154
+ * Adds the Library paths to the autoloader.
155
+ *
156
+ * @since 1.0.0
157
+ *
158
+ * @param object $loader Autoloader instance.
159
+ *
160
+ * @return null
161
+ */
162
+ public function load( $loader ) {
163
+ if ( ! empty( $this->configs['libraryDir'] ) ) {
164
+ $library = $this->configs['libraryDir'] . 'src/Library';
165
+
166
+ // Only create a single instance of the BoldGrid Library Start.
167
+ if ( did_action( 'Boldgrid\Library\Library\Start' ) === 0 ) {
168
+ do_action( 'Boldgrid\Library\Library\Start', $library );
169
+
170
+ // Check dir and add PSR-4 dir of the BoldGrid Library to autoload.
171
+ if ( is_dir( $library ) ) {
172
+ $loader->addPsr4( 'Boldgrid\\Library\\Library\\', $library );
173
+ $load = new \Boldgrid\Library\Library\Start( $this->configs );
174
+ $load->init();
175
+ }
176
+ }
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Get the libraries class property.
182
+ *
183
+ * @since 1.0.0
184
+ *
185
+ * @return array $libraries Available libraries.
186
+ */
187
+ public function getLibraries() {
188
+ return $this->libraries;
189
+ }
190
+
191
+ /**
192
+ * Get the load class property.
193
+ *
194
+ * @since 1.0.0
195
+ *
196
+ * @return object $load Highest library version found to load.
197
+ */
198
+ public function getLoad() {
199
+ return $this->load;
200
+ }
201
+
202
+ /**
203
+ * Get the path class property.
204
+ *
205
+ * @since 1.0.0
206
+ *
207
+ * @return string $path path to the library to load.
208
+ */
209
+ public function getPath() {
210
+ return $this->path;
211
+ }
212
+ }
vendor/boldgrid/library/src/Util/Option.php ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Library Option Utility
4
+ *
5
+ * @package Boldgrid\Library
6
+ * @subpackage \Util
7
+ *
8
+ * @version 1.0.0
9
+ * @author BoldGrid <wpb@boldgrid.com>
10
+ */
11
+
12
+ namespace Boldgrid\Library\Util;
13
+
14
+ /**
15
+ * BoldGrid Library Option Class.
16
+ *
17
+ * This class is responsible for working with the WordPress options.
18
+ *
19
+ * @since 1.0.0
20
+ */
21
+ class Option {
22
+
23
+ /**
24
+ * @access public
25
+ *
26
+ * @var string $name Name of the option to use.
27
+ * @var string $key Key to use in option.
28
+ * @var string $option Option data retrieved.
29
+ */
30
+ public static
31
+ $name,
32
+ $key,
33
+ $option;
34
+
35
+
36
+ /**
37
+ * Initialize the option utility.
38
+ *
39
+ * @since 1.0.0
40
+ *
41
+ * @param string $name Name of the option to use.
42
+ * @param string $key Key to use in option.
43
+ *
44
+ * @return null
45
+ */
46
+ public static function init( $name = 'boldgrid_settings', $key = 'library' ) {
47
+ self::$name = $name;
48
+ self::$key = $key;
49
+ self::$option = self::getOption();
50
+ }
51
+
52
+ /**
53
+ * Gets option from WordPress database.
54
+ *
55
+ * @since 1.0.0
56
+ *
57
+ * @return array Returns the option data from WordPress database.
58
+ */
59
+ public static function getOption() {
60
+ return get_site_option( self::$name, array() );
61
+ }
62
+
63
+ /**
64
+ * Set option key, value.
65
+ *
66
+ * @since 1.0.0
67
+ *
68
+ * @param string $key Key to add to option.
69
+ * @param mixed $value Value to assign to key.
70
+ *
71
+ * @return bool Option update successful?
72
+ */
73
+ public static function set( $key, $value ) {
74
+ self::$option[ self::$key ][ $key ] = $value;
75
+ return update_site_option( self::$name, self::$option );
76
+ }
77
+
78
+ /**
79
+ * Deletes by key in stored option.
80
+ *
81
+ * @since 1.0.0
82
+ *
83
+ * @param string $key The key to delete.
84
+ *
85
+ * @return bool Delete option successful?
86
+ */
87
+ public static function delete( $key ) {
88
+ unset( self::$option[ self::$key ][ $key ] );
89
+ return update_site_option( self::$name, self::$option );
90
+ }
91
+
92
+ /**
93
+ * Retrieves an option key from the array.
94
+ *
95
+ * If no key is specified, then the default value will be returned.
96
+ *
97
+ * @since 1.0.0
98
+ *
99
+ * @param mixed $key Key to retrieve from option.
100
+ * @param mixed $default The default value to return if key is not found.
101
+ *
102
+ * @return mixed The key data or the default value if not found.
103
+ */
104
+ public static function get( $key = null, $default = array() ) {
105
+ return $key && ! empty( self::$option[ $key ] ) ? self::$option[ $key ] : $default;
106
+ }
107
+
108
+ /**
109
+ * Deletes plugin-related transients.
110
+ *
111
+ * @since 1.1.4
112
+ */
113
+ public static function deletePluginTransients() {
114
+ $names = array(
115
+ 'boldgrid_plugins',
116
+ 'boldgrid_wporg_plugins',
117
+ 'update_plugins',
118
+ );
119
+
120
+ foreach ( $names as $name ) {
121
+ delete_site_transient( $name );
122
+ }
123
+ }
124
+ }
vendor/boldgrid/library/src/Util/Plugin.php ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Library Plugin Utils
4
+ *
5
+ * @package Boldgrid\Library
6
+ * @subpackage Util
7
+ *
8
+ * @version 1.0.2
9
+ * @author BoldGrid <wpb@boldgrid.com>
10
+ */
11
+
12
+ namespace Boldgrid\Library\Util;
13
+
14
+ /**
15
+ * BoldGrid Library Util Plugin Class.
16
+ *
17
+ * This class is responsible for plugin related utility helpers.
18
+ *
19
+ * @since 1.0.2
20
+ */
21
+ class Plugin {
22
+
23
+ /**
24
+ * Helper to get and verify the plugin file.
25
+ *
26
+ * @since 1.0.2
27
+ *
28
+ * @param string $slug Slug of the plugin to get main file for.
29
+ *
30
+ * @return mixed $file Main plugin file of slug or null if not found.
31
+ */
32
+ public static function getPluginFile( $slug ) {
33
+
34
+ // Load plugin.php if not already included by core.
35
+ if ( ! function_exists( 'get_plugins' ) ) {
36
+ require ABSPATH . '/wp-admin/includes/plugin.php';
37
+ }
38
+
39
+ $plugins = get_plugins();
40
+
41
+ foreach ( $plugins as $file => $info ) {
42
+
43
+ // Get the basename of the plugin.
44
+ $basename = dirname( plugin_basename( $file ) );
45
+ if ( $basename === $slug ) {
46
+ return $file;
47
+ }
48
+ }
49
+
50
+ return null;
51
+ }
52
+ }
vendor/boldgrid/library/src/Util/Registration.php ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Library Registration Utility
4
+ *
5
+ * @package Boldgrid\Library
6
+ * @subpackage \Util\Registration
7
+ *
8
+ * @version 1.0.0
9
+ * @author BoldGrid <wpb@boldgrid.com>
10
+ */
11
+
12
+ namespace Boldgrid\Library\Util;
13
+
14
+ /**
15
+ * BoldGrid Library Registration Class.
16
+ *
17
+ * This class is responsible for handling the registration of
18
+ * a product and it's associated dependency version.
19
+ *
20
+ * @since 1.0.0
21
+ */
22
+ class Registration implements Registration\RegistrationInterface {
23
+
24
+ /**
25
+ * @access protected
26
+ *
27
+ * @var string $product The product identifier.
28
+ * @var string $dependency The dependency to get the version of.
29
+ * @var array $libraries Libraries stored in options.
30
+ */
31
+ protected
32
+ $product,
33
+ $dependency,
34
+ $libraries;
35
+
36
+ /**
37
+ * Initialize class and set class properties.
38
+ *
39
+ * @since 1.0.0
40
+ *
41
+ * @param string $product The path of the product.
42
+ * @param string $dependency The dependency relied on by the product.
43
+ *
44
+ * @return null
45
+ */
46
+ protected function init( $product, $dependency = 'boldgrid/library' ) {
47
+ $this->product = $product;
48
+ $this->dependency = $dependency;
49
+ Option::init();
50
+ $this->libraries = Option::get( 'library' );
51
+ $this->verify();
52
+ }
53
+
54
+ /**
55
+ * Register the product in WordPress options.
56
+ *
57
+ * @since 1.0.0
58
+ *
59
+ * @return null
60
+ */
61
+ public function register() {
62
+
63
+ // Check the dependency version.
64
+ $version = new Version( $this->getDependency() );
65
+ Option::set( $this->getProduct(), $version->getVersion() );
66
+ }
67
+
68
+ /**
69
+ * Deregister the product in WordPress options.
70
+ *
71
+ * @since 1.0.0
72
+ *
73
+ * @return null
74
+ */
75
+ public function deregister() {
76
+ Option::delete( $this->getProduct() );
77
+ }
78
+
79
+ /**
80
+ * Verify the product is found in the library option, or register it
81
+ * if it's not found.
82
+ *
83
+ * @since 1.0.0
84
+ *
85
+ * @return null
86
+ */
87
+ public function verify() {
88
+ if ( ! isset( $this->libraries[ $this->getProduct() ] ) ) {
89
+ $this->register();
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Get the product class property.
95
+ *
96
+ * @since 1.0.0
97
+ *
98
+ * @return string $product The product class property.
99
+ */
100
+ public function getProduct() {
101
+ return $this->product;
102
+ }
103
+
104
+ /**
105
+ * Get the dependency class property.
106
+ *
107
+ * @since 1.0.0
108
+ *
109
+ * @return string $dependency The dependency class property.
110
+ */
111
+ public function getDependency() {
112
+ return $this->dependency;
113
+ }
114
+ }
vendor/boldgrid/library/src/Util/Registration/Plugin.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Library Plugin Registration Utility
4
+ *
5
+ * @package Boldgrid\Library
6
+ * @subpackage \Util\Registration
7
+ *
8
+ * @version 1.0.0
9
+ * @author BoldGrid <wpb@boldgrid.com>
10
+ */
11
+
12
+ namespace Boldgrid\Library\Util\Registration;
13
+
14
+ use Boldgrid\Library\Util;
15
+
16
+ /**
17
+ * BoldGrid Library Plugin Registration Class.
18
+ *
19
+ * This class is responsible for handling the registration of
20
+ * a plugin and it's associated dependency version.
21
+ *
22
+ * @since 1.0.0
23
+ */
24
+ class Plugin extends Util\Registration {
25
+
26
+ /**
27
+ * Initialize class and set class properties.
28
+ *
29
+ * @since 1.0.0
30
+ *
31
+ * @param string $product The path of the product.
32
+ */
33
+ public function __construct( $product ) {
34
+ parent::init( $product );
35
+ register_activation_hook( $this->getProduct(), array( $this, 'register' ) );
36
+ register_deactivation_hook( $this->getProduct(), array( $this, 'deregister' ) );
37
+ }
38
+ }
vendor/boldgrid/library/src/Util/Registration/RegistrationInterface.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Library Registration Utility Interface
4
+ *
5
+ * @package Boldgrid\Library
6
+ * @subpackage \Util\Registration
7
+ *
8
+ * @version 1.0.0
9
+ * @author BoldGrid <wpb@boldgrid.com>
10
+ */
11
+
12
+ namespace Boldgrid\Library\Util\Registration;
13
+
14
+ /**
15
+ * BoldGrid Library Registration Interface
16
+ *
17
+ * This interface dictates how classes that implement it
18
+ * should be registering and deregistering products.
19
+ */
20
+ interface RegistrationInterface {
21
+
22
+ /**
23
+ * Register the product in WordPress options.
24
+ *
25
+ * @since 1.0.0
26
+ *
27
+ * @return null
28
+ */
29
+ public function register();
30
+
31
+ /**
32
+ * Deregister the product in WordPress options.
33
+ *
34
+ * @since 1.0.0
35
+ *
36
+ * @return null
37
+ */
38
+ public function deregister();
39
+
40
+ /**
41
+ * Gets the product class property.
42
+ *
43
+ * @since 1.0.0
44
+ *
45
+ * @return string $product Product class property.
46
+ */
47
+ public function getProduct();
48
+
49
+ /**
50
+ * Get the dependency class property.
51
+ *
52
+ * @since 1.0.0
53
+ *
54
+ * @return string $dependency The dependency class property.
55
+ */
56
+ public function getDependency();
57
+ }
vendor/boldgrid/library/src/Util/Registration/Theme.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Library Theme Registration Utility
4
+ *
5
+ * @package Boldgrid\Library
6
+ * @subpackage \Util\Registration
7
+ *
8
+ * @version 1.0.0
9
+ * @author BoldGrid <wpb@boldgrid.com>
10
+ */
11
+
12
+ namespace Boldgrid\Library\Util\Registration;
13
+
14
+ use Boldgrid\Library\Util;
15
+
16
+ /**
17
+ * BoldGrid Library Theme Registration Class.
18
+ *
19
+ * This class is responsible for handling the registration of
20
+ * a theme and it's associated dependency version.
21
+ *
22
+ * @since 1.0.0
23
+ */
24
+ class Theme extends Util\Registration {
25
+
26
+ /**
27
+ * Initialize class and set class properties.
28
+ *
29
+ * @since 1.0.0
30
+ *
31
+ * @param string $product The path of the product.
32
+ */
33
+ public function __construct( $product ) {
34
+ parent::init( get_template( $product ) );
35
+ add_action( 'after_switch_theme', array( $this, 'register' ) );
36
+ add_action( 'switch_theme', array( $this, 'deregister' ) );
37
+ }
38
+ }
vendor/boldgrid/library/src/Util/Version.php ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Library Version Utility
4
+ *
5
+ * @package Boldgrid\Library
6
+ * @subpackage \Util
7
+ *
8
+ * @version 1.0.0
9
+ * @author BoldGrid <wpb@boldgrid.com>
10
+ */
11
+
12
+ namespace Boldgrid\Library\Util;
13
+
14
+ /**
15
+ * BoldGrid Library Version Utility Class.
16
+ *
17
+ * This class is responsible for determining current version installed of a
18
+ * dependency. This will check the installed composer packages for the version
19
+ * that was used for a particular dependency.
20
+ *
21
+ * @since 1.0.0
22
+ */
23
+ class Version {
24
+
25
+ /**
26
+ * @access private
27
+ *
28
+ * @since 1.0.0
29
+ *
30
+ * @var string $dependency The dependency to get the normalized version of.
31
+ * @var mixed $version Normalized version number if found, or null.
32
+ */
33
+ private
34
+ $dependency,
35
+ $version;
36
+
37
+ /**
38
+ * Initialize class and set class properties.
39
+ *
40
+ * @since 1.0.0
41
+ *
42
+ * @param string $dependency The dependency to check the version data of.
43
+ */
44
+ public function __construct( $dependency ) {
45
+ $this->dependency = $dependency;
46
+ $this->version = $this->setVersion();
47
+ }
48
+
49
+ /**
50
+ * Sets the version of dependency.
51
+ *
52
+ * @since 1.0.0
53
+ *
54
+ * @global $wp_filesystem The WordPress Filesystem API object.
55
+ *
56
+ * @return mixed $version Normalized version number if found or null.
57
+ */
58
+ public function setVersion() {
59
+ global $wp_filesystem;
60
+
61
+ // Ensure that the WP Filesystem API is loaded.
62
+ if ( empty( $wp_filesystem ) ) {
63
+ require_once ABSPATH . '/wp-admin/includes/file.php';
64
+ WP_Filesystem();
65
+ }
66
+
67
+ // Get installed composer package data.
68
+ $vendor = wp_normalize_path( realpath( __DIR__ . '/../../../../' ) );
69
+ $file = $wp_filesystem->get_contents( $vendor . '/composer/installed.json' );
70
+ $installed = json_decode( $file, true );
71
+
72
+ // Check for dep's installed version.
73
+ $version = null;
74
+ if ( $installed ) {
75
+ foreach( $installed as $key => $value ) {
76
+ if ( $value['name'] === $this->getDependency() ) {
77
+ $version = $value['version_normalized'];
78
+ break;
79
+ }
80
+ }
81
+ }
82
+
83
+ return $version;
84
+ }
85
+
86
+ /**
87
+ * Gets the dependency class property.
88
+ *
89
+ * @since 1.0.0
90
+ *
91
+ * @return string Name of the dependency.
92
+ */
93
+ public function getDependency() {
94
+ return $this->dependency;
95
+ }
96
+
97
+ /**
98
+ * Gets the version class property.
99
+ *
100
+ * @since 1.0.0
101
+ *
102
+ * @return string Normalized version of dependency.
103
+ */
104
+ public function getVersion() {
105
+ return $this->version;
106
+ }
107
+ }
vendor/boldgrid/library/src/assets/css/api-notice.css ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #container_boldgrid_api_key_notice:not(.is-dismissible) {
2
+ display: none;
3
+ }
4
+ #container_boldgrid_api_key_notice .dashicons-before:before {
5
+ padding-right: .8em !important;
6
+ font-size: 58px !important;
7
+ color: #ff6600;
8
+ }
9
+ #container_boldgrid_api_key_notice {
10
+ width: 563px;
11
+ padding: 1.4em;
12
+ }
13
+ #requestKeyForm input {
14
+ width: 83%;
15
+ margin: .5em 0;
16
+ }
17
+ #requestKeyForm label {
18
+ display: block;
19
+ }
20
+ #boldgrid_api_key_notice_message,
21
+ .tos-box,
22
+ .key-request-content,
23
+ .enterKeyLink,
24
+ .boldgridApiKeyLink {
25
+ margin-left: 66px;
26
+ }
27
+ #requestKeyMessage {
28
+ margin-right: 66px;
29
+ }
30
+ .error-alerts {
31
+ color: red;
32
+ }
33
+ .error-color::before {
34
+ content: '*';
35
+ padding-right: 5px;
36
+ }
37
+ .error-color {
38
+ color: red;
39
+ }
40
+ input#boldgrid_api_key {
41
+ margin-left: 66px !important;
42
+ width: 59%;
43
+ }
44
+ .boldgrid-wp-spin {
45
+ width: 20px;
46
+ height: 20px;
47
+ float: left;
48
+ border-radius: 100%;
49
+ background-color: #a3a3a3;
50
+ -webkit-animation: rotation 1.5s infinite linear;
51
+ -moz-animation: rotation 1.5s infinite linear;
52
+ -animation: rotation 1.5s infinite linear;
53
+ }
54
+ .boldgrid-wp-spin::after {
55
+ width: 4px;
56
+ height: 4px;
57
+ border-radius: 100%;
58
+ background-color: #fff;
59
+ position: relative;
60
+ left: 10px;
61
+ top: 5px;
62
+ display: block;
63
+ content: '';
64
+ }
65
+ @ -webkit-keyframes rotation {
66
+ from {
67
+ -webkit-transform: rotate(0deg);
68
+ }
69
+ to {
70
+ -webkit-transform: rotate(359deg);
71
+ }
72
+ }
73
+ @ -moz-keyframes rotation {
74
+ from {
75
+ -moz-transform: rotate(0deg);
76
+ }
77
+ to {
78
+ -moz-transform: rotate(359deg);
79
+ }
80
+ }
81
+ @ -keyframes rotation {
82
+ from {
83
+ -transform: rotate(0deg);
84
+ }
85
+ to {
86
+ -transform: rotate(359deg);
87
+ }
88
+ }
89
+ #boldgrid-api-loading {
90
+ margin-left: 423px;
91
+ margin-top: -2em;
92
+ display: none;
93
+ }
94
+ #requestKeyForm .spinner.inline {
95
+ visibility: visible;
96
+ float: none;
97
+ vertical-align: bottom;
98
+ }
vendor/boldgrid/library/src/assets/css/ui.css ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * 1.1 Boxes.
3
+ */
4
+ .bg-box {
5
+ margin-bottom: 20px;
6
+ }
7
+
8
+ .bg-box-top {
9
+ background: #fff;
10
+ border: 1px solid #e1e1e1;
11
+ padding: 10px;
12
+ font-weight: bold;
13
+ }
14
+
15
+ .bg-box-bottom {
16
+ border: 1px solid #e1e1e1;
17
+ border-top: 0px;
18
+ background: #fbfbfb;
19
+ padding: 10px;
20
+ }
21
+
22
+ .bg-box-bottom.premium {
23
+ border-left: 4px solid #46b450;
24
+ }
25
+
26
+ .bg-box-bottom.premium .button-success,
27
+ .postbox .premium .button-success {
28
+ float: right;
29
+ margin: 0px 0px 10px 10px;
30
+ }
31
+
32
+ .postbox .premium {
33
+ border-left: 4px solid #46b450;
34
+ background: #fbfbfb;
35
+ border-top: 1px solid #e1e1e1;
36
+ padding: 10px;
37
+ }
38
+
39
+ /**
40
+ * 1.2 Left navigation.
41
+ *
42
+ * Similar to Inspirations' left nav for categories.
43
+ */
44
+ .bg-left-nav {
45
+ margin: 0px;
46
+ border: 1px solid #e1e1e1;
47
+ }
48
+
49
+ .bg-left-nav li {
50
+ padding: 5px 0px 5px 10px;
51
+ margin: 0px;
52
+ background: #fff;
53
+ cursor: pointer;
54
+ }
55
+
56
+ .bg-left-nav li:hover,
57
+ .bg-left-nav .active {
58
+ background: #0073aa;
59
+ color:#fff;
60
+ }
61
+
62
+ .bg-left-nav li {
63
+ -webkit-transition: background-color .3s linear, color .3s linear;
64
+ -moz-transition: background-color .3s linear, color .3s linear;
65
+ -o-transition: background-color .3s linear, color .3s linear;
66
+ -ms-transition: background-color .3s linear, color .3s linear;
67
+ transition: background-color .3s linear, color .3s linear;
68
+ }
69
+
70
+ /**
71
+ * 1.3 Columns
72
+ *
73
+ * #col-container, #col-left, #col-right taken from Posts > Categories page.
74
+ */
75
+ #col-container {
76
+ margin-top: 20px;
77
+ }
78
+
79
+ #col-container h2:first-child {
80
+ margin-top: 0px;
81
+ }
82
+
83
+ #col-left {
84
+ max-width: 180px;
85
+ }
86
+
87
+ #col-right {
88
+ width: calc(100% - 180px - 20px);
89
+ }
90
+
91
+ /**
92
+ * 1.4 Misc
93
+ */
94
+ .button.button-success {
95
+ border-color: darkgreen;
96
+ box-shadow: 0 1px 0 darkgreen;
97
+ background: green;
98
+ color: #fff;
99
+ }
100
+ .button.button-success:hover,
101
+ .button.button-success:focus {
102
+ border-color: green;
103
+ box-shadow: 0 1px 0 green;
104
+ background: #009900;
105
+ color: #fff;
106
+ }
107
+
108
+ /* Add a primary page title action. */
109
+ .wrap .page-title-action-primary {
110
+ border-color: #0073aa #006799 #006799;
111
+ background: #0085ba;
112
+ color: #fff;
113
+ }
114
+ .wrap .page-title-action-primary:hover {
115
+ border-color: #006799;
116
+ background: #008ec2;
117
+ color: #fff;
118
+ }
119
+
120
+ /**
121
+ * 1.5 Media queries
122
+ */
123
+ @media screen and ( max-width: 782px ) {
124
+ #col-left,
125
+ #col-right {
126
+ max-width: none;
127
+ width: inherit;
128
+ }
129
+
130
+ #col-left {
131
+ margin-bottom: 20px;
132
+ }
133
+ }
vendor/boldgrid/library/src/assets/js/api-notice.js ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ var BOLDGRID = BOLDGRID || {};
2
+ BOLDGRID.LIBRARY = BOLDGRID.LIBRARY || {};
3
+
4
+ BOLDGRID.LIBRARY.Api = function( $ ) {
5
+ var notice,
6
+ self = this;
7
+
8
+ /**
9
+ * Set key if parameter is set.
10
+ */
11
+ $( function() {
12
+ var $activateKey = self.GetURLParameter( 'activateKey' ),
13
+ notice = $( '#container_boldgrid_api_key_notice' );
14
+
15
+ if ( $activateKey ) {
16
+ document.getElementById( 'boldgrid_api_key' ).value = $activateKey;
17
+ }
18
+
19
+ /** Toggle the forms around **/
20
+ $( '.boldgridApiKeyLink', notice ).on( 'click', function() {
21
+ $( '.api-notice', notice ).hide();
22
+ $( '.new-api-key', notice ).fadeIn( 'slow' );
23
+ });
24
+ $( '.enterKeyLink', notice ).on( 'click', function() {
25
+ $( '.new-api-key', notice ).hide();
26
+ $( '.api-notice', notice ).fadeIn( 'slow' );
27
+ });
28
+
29
+ /** Submit action **/
30
+ $( '#requestKeyForm' ).submit( function( event ) {
31
+ event.preventDefault();
32
+
33
+ var posting,
34
+ $form = $( this ),
35
+ $firstName = $form.find( '#firstName' ).val(),
36
+ $lastName = $form.find( '#lastName' ).val(),
37
+ $email = $form.find( '#emailAddr' ).val(),
38
+ $link = $form.find( '#siteUrl' ).val(),
39
+ $alertBox = $( '.error-alerts' ),
40
+ $genericError = 'There was an error communicating with the BoldGrid Connect Key server. Please try again.',
41
+ $submit = $form.find( '#requestKey' ),
42
+ $spinner = $form.find( '.spinner' );
43
+
44
+
45
+
46
+ $('.error-color').removeClass( 'error-color' );
47
+
48
+ // Basic js checks before server-side verification.
49
+ if ( ! $firstName ) {
50
+ $alertBox.text( 'First name is required.' );
51
+ $form.find( '#firstName' ).prev().addClass( 'error-color' );
52
+ return false;
53
+ }
54
+ if ( ! $lastName ) {
55
+ $alertBox.text( 'Last name is required.' );
56
+ $form.find( '#lastName' ).prev().addClass( 'error-color' );
57
+ return false;
58
+ }
59
+ if ( ! ( $email.indexOf( '@' ) > -1 && $email.indexOf( '.' ) > -1 ) ) {
60
+ $alertBox.text( 'Please enter a valid e-mail address.' );
61
+ $form.find( '#emailAddr' ).prev().addClass( 'error-color' );
62
+ return false;
63
+ }
64
+
65
+ $submit.prop( 'disabled', 'disabled' );
66
+ $spinner.addClass( 'inline' );
67
+
68
+ posting = $.post( $( '#generate-api-key' ).val(),
69
+ {
70
+ first: $firstName,
71
+ last: $lastName,
72
+ email: $email,
73
+ link: $link,
74
+ }
75
+ );
76
+
77
+ posting.done( function( response ) {
78
+ $alertBox.text( $genericError );
79
+ if ( 200 === response.status ) {
80
+ $( '.key-request-content' ).text( response.message );
81
+ }
82
+ }).fail( function( post ) {
83
+ var message = post.responseJSON.message
84
+ if ( message.indexOf( 'First name' ) >= 0 ) {
85
+ $form.find( '#firstName' ).prev().addClass( 'error-color' );
86
+ }
87
+ if ( message.indexOf( 'Last name' ) >= 0 ) {
88
+ $form.find( '#lastName' ).prev().addClass( 'error-color' );
89
+ }
90
+ if ( message.indexOf( 'e-mail' ) >= 0 ) {
91
+ $form.find( '#emailAddr' ).prev().addClass( 'error-color' );
92
+ }
93
+ $alertBox.text( message );
94
+
95
+ $submit.prop( 'disabled', false );
96
+ $spinner.removeClass( 'inline' );
97
+ });
98
+ });
99
+
100
+ /**
101
+ * When the submit button is pressed.
102
+ */
103
+ $( '#boldgrid-api-form' ).submit( function( e ){
104
+ e.preventDefault();
105
+ });
106
+
107
+ $( '#boldgrid-api-loading', notice ).hide();
108
+
109
+ $( '#submit_api_key', notice ).on('click', function() {
110
+ $( '#boldgrid_api_key_notice_message' ).empty();
111
+ if ( ! $( '#tos-box:checked').length ) {
112
+ $( '#boldgrid_api_key_notice_message', notice )
113
+ .html( 'You must agree to the Terms of Service before continuing.' )
114
+ .addClass( 'error-color' );
115
+ return false;
116
+ }
117
+ var key = $( '#boldgrid_api_key', notice ).val()
118
+ .replace( /[^a-z0-9]/gi,'' )
119
+ .replace( /(.{8})/g,"$1\-" )
120
+ .slice( 0, -1 );
121
+ if ( ! key || key.length !== 35 ) {
122
+ $( '#boldgrid_api_key_notice_message', notice )
123
+ .html( 'You must enter a valid BoldGrid Connect Key.' )
124
+ .addClass( 'error-color' );
125
+ return false;
126
+ }
127
+ $( '#boldgrid_api_key_notice_message', notice ).removeClass( 'error-color' );
128
+
129
+ self.set( key );
130
+
131
+ // hide the button
132
+ $( this ).hide();
133
+
134
+ // show the loading graphic.
135
+ $( '#boldgrid-api-loading', notice ).show();
136
+ });
137
+ });
138
+
139
+ /**
140
+ * Get parameter from URL
141
+ *
142
+ * @link http://www.jquerybyexample.net/2012/06/get-url-parameters-using-jquery.html
143
+ */
144
+ this.GetURLParameter = function( sParam ) {
145
+ var sPageURL, sURLVariables, sParameterName;
146
+
147
+ sPageURL = window.location.search.substring( 1 );
148
+ sURLVariables = sPageURL.split( '&' );
149
+
150
+ for ( var i = 0; i < sURLVariables.length; i++ ) {
151
+ sParameterName = sURLVariables[i].split( '=' );
152
+
153
+ if ( sParameterName[0] == sParam ) {
154
+ return sParameterName[1];
155
+ }
156
+ }
157
+ };
158
+
159
+ this.trackActivation = function () {
160
+ // Create iframe element.
161
+ var iframe = document.createElement( 'iframe' );
162
+ // Assign iframe ID.
163
+ iframe.setAttribute( 'id', 'tracking' );
164
+ // Assign iframe width.
165
+ iframe.setAttribute( 'width', 0 );
166
+ // Assign iframe height.
167
+ iframe.setAttribute( 'height', 0 );
168
+ // Assign iframe tabindex.
169
+ iframe.setAttribute( 'tabindex', -1 );
170
+ // Place iframe before response message.
171
+ var el = document.getElementById( 'boldgrid_api_key_notice_message' );
172
+ el.parentNode.insertBefore( iframe, el );
173
+ // Assign src URL to iframe.
174
+ iframe.setAttribute( 'src', 'https://www.boldgrid.com/activation/' );
175
+ // Set display:none to iframe;
176
+ iframe.style.display = 'none';
177
+ };
178
+
179
+ /**
180
+ * Set the API key.
181
+ */
182
+ this.set = function( key ) {
183
+ var data, nonce, wpHttpReferer;
184
+ // Get the wpnonce and referer values.
185
+ nonce = $( '#set_key_auth', notice ).val();
186
+ wpHttpReferer = $( '[name="_wp_http_referer"]', notice ).val();
187
+ data = {
188
+ 'action' : 'addKey',
189
+ 'api_key' : key,
190
+ 'set_key_auth' : nonce,
191
+ '_wp_http_referer' : wpHttpReferer,
192
+ };
193
+
194
+ $.post( ajaxurl, data, function( response ) {
195
+ // Declare variables.
196
+ var response, message,
197
+ notice = $( '#container_boldgrid_api_key_notice' );
198
+
199
+ // If the key was saved successfully.
200
+ if ( response.success ) {
201
+ // Change the notice from red to green.
202
+ notice.toggleClass( 'error' ).toggleClass( 'updated' );
203
+
204
+ // Initiate tracking iframe.
205
+ self.trackActivation();
206
+
207
+ $( '#boldgrid_api_key_notice_message', notice )
208
+ .html( response.data.message + ' <a onClick="window.location.reload(true)" style="cursor:pointer;"> Dismiss Notification</a>' );
209
+
210
+ // Remove the loading graphic since success.
211
+ $( '#boldgrid-api-loading', notice ).fadeOut();
212
+
213
+ // Finally hide the input elements as we do not need them anymore.
214
+ $( '#boldgrid_api_key', notice ).fadeOut();
215
+
216
+ // Reload page after 3 seconds.
217
+ setTimeout( function() {
218
+ window.location.reload();
219
+ }, 3000 );
220
+ } else {
221
+ $( '#boldgrid-api-loading', notice ).hide();
222
+ $( '#submit_api_key', notice ).show();
223
+ $( '#boldgrid_api_key_notice_message', notice )
224
+ .html( response.data.message )
225
+ .addClass( 'error-color' );
226
+ }
227
+ });
228
+ };
229
+ };
230
+
231
+ new BOLDGRID.LIBRARY.Api( jQuery );
vendor/boldgrid/library/src/assets/js/license.js ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* global ajaxurl,jQuery */
2
+
3
+ var BOLDGRID = BOLDGRID || {};
4
+ BOLDGRID.LIBRARY = BOLDGRID.LIBRARY || {};
5
+
6
+ ( function( $ ) {
7
+ 'use strict';
8
+
9
+ BOLDGRID.LIBRARY.License = {
10
+
11
+ /**
12
+ * @summary Make a call to clear the license data.
13
+ *
14
+ * @since 2.2.0
15
+ *
16
+ * @param string plugin Plugin name, such as 'boldgrid-backup'.
17
+ * @param function onSuccess
18
+ * @param function onError
19
+ */
20
+ clear: function( plugin, onSuccess, onError ) {
21
+ var data = {
22
+ 'action' : 'bg_clear_license',
23
+ 'plugin' : plugin,
24
+ };
25
+
26
+ $.post( ajaxurl, data, function( response ) {
27
+ onSuccess( response );
28
+ }).error( function() {
29
+ onError();
30
+ });
31
+ },
32
+ };
33
+
34
+ })( jQuery );
35
+
vendor/boldgrid/library/src/assets/js/notice.js ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ var BOLDGRID = BOLDGRID || {};
2
+ BOLDGRID.LIBRARY = BOLDGRID.LIBRARY || {};
3
+
4
+ BOLDGRID.LIBRARY.Notice = function( $ ) {
5
+ var $notices,
6
+ self = this;
7
+
8
+ $( function() {
9
+ $notices = $( '.boldgrid-notice.is-dismissible' );
10
+
11
+ /** Dismissible action **/
12
+ $notices.on( 'click', '.notice-dismiss', self.dismiss );
13
+
14
+ } );
15
+
16
+ /**
17
+ * Handle click of the notice-dismiss icon in the notice.
18
+ *
19
+ * @since 2.1.0
20
+ */
21
+ this.dismiss = function() {
22
+ var $notice = $( this ).closest( '.boldgrid-notice' );
23
+
24
+ $.post( ajaxurl, {
25
+ 'action' : 'dismissBoldgridNotice',
26
+ 'notice' : $notice.data( 'notice-id' ),
27
+ 'set_key_auth' : $( '#set_key_auth', $notice ).val(),
28
+ '_wp_http_referer' : $( '[name="_wp_http_referer"]', $notice ).val(),
29
+ } );
30
+ };
31
+ };
32
+
33
+ new BOLDGRID.LIBRARY.Notice( jQuery );
vendor/boldgrid/library/src/assets/js/sticky.js ADDED
@@ -0,0 +1,288 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Sticky Plugin v1.0.4 for jQuery
2
+ // =============
3
+ // Author: Anthony Garand
4
+ // Improvements by German M. Bravo (Kronuz) and Ruud Kamphuis (ruudk)
5
+ // Improvements by Leonardo C. Daronco (daronco)
6
+ // Created: 02/14/2011
7
+ // Date: 07/20/2015
8
+ // Website: http://stickyjs.com/
9
+ // Description: Makes an element on the page stick on the screen as you scroll
10
+ // It will only set the 'top' and 'position' of your element, you
11
+ // might need to adjust the width in some cases.
12
+
13
+ (function (factory) {
14
+ if (typeof define === 'function' && define.amd) {
15
+ // AMD. Register as an anonymous module.
16
+ define(['jquery'], factory);
17
+ } else if (typeof module === 'object' && module.exports) {
18
+ // Node/CommonJS
19
+ module.exports = factory(require('jquery'));
20
+ } else {
21
+ // Browser globals
22
+ factory(jQuery);
23
+ }
24
+ }(function ($) {
25
+ var slice = Array.prototype.slice; // save ref to original slice()
26
+ var splice = Array.prototype.splice; // save ref to original slice()
27
+
28
+ var defaults = {
29
+ topSpacing: 0,
30
+ bottomSpacing: 0,
31
+ className: 'is-sticky',
32
+ wrapperClassName: 'sticky-wrapper',
33
+ center: false,
34
+ getWidthFrom: '',
35
+ widthFromWrapper: true, // works only when .getWidthFrom is empty
36
+ responsiveWidth: false,
37
+ zIndex: 'inherit'
38
+ },
39
+ $window = $(window),
40
+ $document = $(document),
41
+ sticked = [],
42
+ windowHeight = $window.height(),
43
+ scroller = function() {
44
+ var scrollTop = $window.scrollTop(),
45
+ documentHeight = $document.height(),
46
+ dwh = documentHeight - windowHeight,
47
+ extra = (scrollTop > dwh) ? dwh - scrollTop : 0;
48
+
49
+ for (var i = 0, l = sticked.length; i < l; i++) {
50
+ var s = sticked[i],
51
+ elementTop = s.stickyWrapper.offset().top,
52
+ etse = elementTop - s.topSpacing - extra;
53
+
54
+ //update height in case of dynamic content
55
+ s.stickyWrapper.css('height', s.stickyElement.outerHeight());
56
+
57
+ if (scrollTop <= etse) {
58
+ if (s.currentTop !== null) {
59
+ s.stickyElement
60
+ .css({
61
+ 'width': '',
62
+ 'position': '',
63
+ 'top': '',
64
+ 'z-index': ''
65
+ });
66
+ s.stickyElement.parent().removeClass(s.className);
67
+ s.stickyElement.trigger('sticky-end', [s]);
68
+ s.currentTop = null;
69
+ }
70
+ }
71
+ else {
72
+ var newTop = documentHeight - s.stickyElement.outerHeight()
73
+ - s.topSpacing - s.bottomSpacing - scrollTop - extra;
74
+ if (newTop < 0) {
75
+ newTop = newTop + s.topSpacing;
76
+ } else {
77
+ newTop = s.topSpacing;
78
+ }
79
+ if (s.currentTop !== newTop) {
80
+ var newWidth;
81
+ if (s.getWidthFrom) {
82
+ padding = s.stickyElement.innerWidth() - s.stickyElement.width();
83
+ newWidth = $(s.getWidthFrom).width() - padding || null;
84
+ } else if (s.widthFromWrapper) {
85
+ newWidth = s.stickyWrapper.width();
86
+ }
87
+ if (newWidth == null) {
88
+ newWidth = s.stickyElement.width();
89
+ }
90
+ s.stickyElement
91
+ .css('width', newWidth)
92
+ .css('position', 'fixed')
93
+ .css('top', newTop)
94
+ .css('z-index', s.zIndex);
95
+
96
+ s.stickyElement.parent().addClass(s.className);
97
+
98
+ if (s.currentTop === null) {
99
+ s.stickyElement.trigger('sticky-start', [s]);
100
+ } else {
101
+ // sticky is started but it have to be repositioned
102
+ s.stickyElement.trigger('sticky-update', [s]);
103
+ }
104
+
105
+ if (s.currentTop === s.topSpacing && s.currentTop > newTop || s.currentTop === null && newTop < s.topSpacing) {
106
+ // just reached bottom || just started to stick but bottom is already reached
107
+ s.stickyElement.trigger('sticky-bottom-reached', [s]);
108
+ } else if(s.currentTop !== null && newTop === s.topSpacing && s.currentTop < newTop) {
109
+ // sticky is started && sticked at topSpacing && overflowing from top just finished
110
+ s.stickyElement.trigger('sticky-bottom-unreached', [s]);
111
+ }
112
+
113
+ s.currentTop = newTop;
114
+ }
115
+
116
+ // Check if sticky has reached end of container and stop sticking
117
+ var stickyWrapperContainer = s.stickyWrapper.parent();
118
+ var unstick = (s.stickyElement.offset().top + s.stickyElement.outerHeight() >= stickyWrapperContainer.offset().top + stickyWrapperContainer.outerHeight()) && (s.stickyElement.offset().top <= s.topSpacing);
119
+
120
+ if( unstick ) {
121
+ s.stickyElement
122
+ .css('position', 'absolute')
123
+ .css('top', '')
124
+ .css('bottom', 0)
125
+ .css('z-index', '');
126
+ } else {
127
+ s.stickyElement
128
+ .css('position', 'fixed')
129
+ .css('top', newTop)
130
+ .css('bottom', '')
131
+ .css('z-index', s.zIndex);
132
+ }
133
+ }
134
+ }
135
+ },
136
+ resizer = function() {
137
+ windowHeight = $window.height();
138
+
139
+ for (var i = 0, l = sticked.length; i < l; i++) {
140
+ var s = sticked[i];
141
+ var newWidth = null;
142
+ if (s.getWidthFrom) {
143
+ if (s.responsiveWidth) {
144
+ newWidth = $(s.getWidthFrom).width();
145
+ }
146
+ } else if(s.widthFromWrapper) {
147
+ newWidth = s.stickyWrapper.width();
148
+ }
149
+ if (newWidth != null) {
150
+ s.stickyElement.css('width', newWidth);
151
+ }
152
+ }
153
+ },
154
+ methods = {
155
+ init: function(options) {
156
+ return this.each(function() {
157
+ var o = $.extend({}, defaults, options);
158
+ var stickyElement = $(this);
159
+
160
+ var stickyId = stickyElement.attr('id');
161
+ var wrapperId = stickyId ? stickyId + '-' + defaults.wrapperClassName : defaults.wrapperClassName;
162
+ var wrapper = $('<div></div>')
163
+ .attr('id', wrapperId)
164
+ .addClass(o.wrapperClassName);
165
+
166
+ stickyElement.wrapAll(function() {
167
+ if ($(this).parent("#" + wrapperId).length == 0) {
168
+ return wrapper;
169
+ }
170
+ });
171
+
172
+ var stickyWrapper = stickyElement.parent();
173
+
174
+ if (o.center) {
175
+ stickyWrapper.css({width:stickyElement.outerWidth(),marginLeft:"auto",marginRight:"auto"});
176
+ }
177
+
178
+ if (stickyElement.css("float") === "right") {
179
+ stickyElement.css({"float":"none"}).parent().css({"float":"right"});
180
+ }
181
+
182
+ o.stickyElement = stickyElement;
183
+ o.stickyWrapper = stickyWrapper;
184
+ o.currentTop = null;
185
+
186
+ sticked.push(o);
187
+
188
+ methods.setWrapperHeight(this);
189
+ methods.setupChangeListeners(this);
190
+ });
191
+ },
192
+
193
+ setWrapperHeight: function(stickyElement) {
194
+ var element = $(stickyElement);
195
+ var stickyWrapper = element.parent();
196
+ if (stickyWrapper) {
197
+ stickyWrapper.css('height', element.outerHeight());
198
+ }
199
+ },
200
+
201
+ setupChangeListeners: function(stickyElement) {
202
+ if (window.MutationObserver) {
203
+ var mutationObserver = new window.MutationObserver(function(mutations) {
204
+ if (mutations[0].addedNodes.length || mutations[0].removedNodes.length) {
205
+ methods.setWrapperHeight(stickyElement);
206
+ }
207
+ });
208
+ mutationObserver.observe(stickyElement, {subtree: true, childList: true});
209
+ } else {
210
+ if (window.addEventListener) {
211
+ stickyElement.addEventListener('DOMNodeInserted', function() {
212
+ methods.setWrapperHeight(stickyElement);
213
+ }, false);
214
+ stickyElement.addEventListener('DOMNodeRemoved', function() {
215
+ methods.setWrapperHeight(stickyElement);
216
+ }, false);
217
+ } else if (window.attachEvent) {
218
+ stickyElement.attachEvent('onDOMNodeInserted', function() {
219
+ methods.setWrapperHeight(stickyElement);
220
+ });
221
+ stickyElement.attachEvent('onDOMNodeRemoved', function() {
222
+ methods.setWrapperHeight(stickyElement);
223
+ });
224
+ }
225
+ }
226
+ },
227
+ update: scroller,
228
+ unstick: function(options) {
229
+ return this.each(function() {
230
+ var that = this;
231
+ var unstickyElement = $(that);
232
+
233
+ var removeIdx = -1;
234
+ var i = sticked.length;
235
+ while (i-- > 0) {
236
+ if (sticked[i].stickyElement.get(0) === that) {
237
+ splice.call(sticked,i,1);
238
+ removeIdx = i;
239
+ }
240
+ }
241
+ if(removeIdx !== -1) {
242
+ unstickyElement.unwrap();
243
+ unstickyElement
244
+ .css({
245
+ 'width': '',
246
+ 'position': '',
247
+ 'top': '',
248
+ 'float': '',
249
+ 'z-index': ''
250
+ })
251
+ ;
252
+ }
253
+ });
254
+ }
255
+ };
256
+
257
+ // should be more efficient than using $window.scroll(scroller) and $window.resize(resizer):
258
+ if (window.addEventListener) {
259
+ window.addEventListener('scroll', scroller, false);
260
+ window.addEventListener('resize', resizer, false);
261
+ } else if (window.attachEvent) {
262
+ window.attachEvent('onscroll', scroller);
263
+ window.attachEvent('onresize', resizer);
264
+ }
265
+
266
+ $.fn.sticky = function(method) {
267
+ if (methods[method]) {
268
+ return methods[method].apply(this, slice.call(arguments, 1));
269
+ } else if (typeof method === 'object' || !method ) {
270
+ return methods.init.apply( this, arguments );
271
+ } else {
272
+ $.error('Method ' + method + ' does not exist on jQuery.sticky');
273
+ }
274
+ };
275
+
276
+ $.fn.unstick = function(method) {
277
+ if (methods[method]) {
278
+ return methods[method].apply(this, slice.call(arguments, 1));
279
+ } else if (typeof method === 'object' || !method ) {
280
+ return methods.unstick.apply( this, arguments );
281
+ } else {
282
+ $.error('Method ' + method + ' does not exist on jQuery.sticky');
283
+ }
284
+ };
285
+ $(function() {
286
+ setTimeout(scroller, 0);
287
+ });
288
+ }));
vendor/boldgrid/library/src/assets/js/ui.js ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * BoldGrid Library UI/UX.
3
+ *
4
+ * @summary JavaScript to handle UI/UX.
5
+ *
6
+ * @since 1.1.7
7
+ */
8
+
9
+ /* global jQuery */
10
+
11
+ var BOLDGRID = BOLDGRID || {};
12
+ BOLDGRID.LIBRARY = BOLDGRID.LIBRARY || {};
13
+
14
+ BOLDGRID.LIBRARY.Ui = function( $ ) {
15
+ var self = this,
16
+ $sections,
17
+ $sectionLinks;
18
+
19
+ /**
20
+ * @summary Action to take when a user clicks on a navigation item.
21
+ *
22
+ * @since 1.1.7
23
+ */
24
+ self.onClickSectionLink = function() {
25
+ var $link = $( this ),
26
+ sectionId = '#' + $link.attr( 'data-section-id' );
27
+
28
+ $sectionLinks.removeClass( 'active' );
29
+ $link.addClass( 'active' );
30
+
31
+ $sections.hide();
32
+ $( sectionId ).show();
33
+ };
34
+
35
+ /**
36
+ * @summary Determine stickiness of items.
37
+ *
38
+ * @since 1.1.7
39
+ */
40
+ self.setSticky = function() {
41
+ var width = document.body.clientWidth,
42
+ $leftNav = $( '.bg-left-nav' );
43
+
44
+ if( width >= 782 ) {
45
+ $leftNav.sticky( { topSpacing : 33 } );
46
+ } else {
47
+ $leftNav.unstick();
48
+ }
49
+ };
50
+
51
+ /**
52
+ * @summary Init.
53
+ *
54
+ * @since 1.1.7
55
+ */
56
+ $( function() {
57
+ $sections = $( '.col-right-section' );
58
+ $sectionLinks = $( '[data-section-id]' );
59
+
60
+ $sectionLinks.on( 'click', self.onClickSectionLink );
61
+
62
+ self.setSticky();
63
+
64
+ $( window ).resize( self.setSticky );
65
+ });
66
+ };
67
+
68
+ new BOLDGRID.LIBRARY.Ui( jQuery );
vendor/boldgrid/library/src/library.global.php ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Premium Default Configurations.
4
+ */
5
+
6
+ defined( 'WPFORMS_SHAREASALE_ID' ) || define( 'WPFORMS_SHAREASALE_ID', '1581233' );
7
+
8
+ return array(
9
+ 'api' => 'https://api.boldgrid.com',
10
+ 'option' => 'license',
11
+ 'key' => get_site_option( 'boldgrid_api_key', null ),
12
+ 'apiData' => get_site_transient( 'boldgrid_api_data' ),
13
+ 'themeData' => get_site_transient( 'boldgrid_theme_data' ),
14
+
15
+ // Enable key validation in library.
16
+ 'keyValidate' => true,
17
+
18
+ // Enable license activation/deactivation in library.
19
+ 'licenseActivate' => false,
20
+
21
+ // Library's Plugin Installer for "Plugins > Add New" in WordPress Dashboard.
22
+ 'pluginInstaller' => array(
23
+
24
+ // Enabled the plugin installer feature in library.
25
+ 'enabled' => true,
26
+
27
+ // Default Premium Link.
28
+ 'defaultLink' => 'https://www.boldgrid.com/connect-keys/',
29
+
30
+ // Installable plugins.
31
+ 'plugins' => array(
32
+ 'boldgrid-inspirations' => array(
33
+ 'key' => 'core',
34
+ 'file' => 'boldgrid-inspirations/boldgrid-inspirations.php',
35
+ 'priority' => 20,
36
+ ),
37
+ 'boldgrid-backup' => array(
38
+ 'key' => 'backup',
39
+ 'file' => 'boldgrid-backup/boldgrid-backup.php',
40
+ 'priority' => 40,
41
+ ),
42
+ 'boldgrid-staging' => array(
43
+ 'key' => 'staging',
44
+ 'file' => 'boldgrid-staging/boldgrid-staging.php',
45
+ 'priority' => 60,
46
+ ),
47
+ 'boldgrid-gallery' => array(
48
+ 'key' => 'gallery-wc-canvas',
49
+ 'file' => 'boldgrid-gallery/wc-gallery.php',
50
+ 'priority' => 70,
51
+ ),
52
+ 'boldgrid-ninja-forms' => array(
53
+ 'key' => 'ninja-forms',
54
+ 'file' => 'boldgrid-ninja-forms/ninja-forms.php',
55
+ 'priority' => 80,
56
+ ),
57
+ ),
58
+
59
+ // WordPress.org recommended plugins.
60
+ 'wporgPlugins' => array(
61
+ 'post-and-page-builder' => array(
62
+ 'slug' => 'post-and-page-builder',
63
+ 'link' => '//wordpress.org/plugins/post-and-page-builder/',
64
+ 'priority' => 10,
65
+ ),
66
+ 'boldgrid-easy-seo' => array(
67
+ 'slug' => 'boldgrid-easy-seo',
68
+ 'link' => '//wordpress.org/plugins/boldgrid-easy-seo/',
69
+ 'priority' => 30,
70
+ ),
71
+ 'wpforms-lite' => array(
72
+ 'slug' => 'wpforms-lite',
73
+ 'link' => '//wpforms.com/lite-upgrade/',
74
+ 'priority' => 80,
75
+ ),
76
+ ),
77
+ ),
78
+ 'api_calls' => array(
79
+ 'get_theme_data' => '/api/open/get-theme-data',
80
+ 'get_asset' => '/api/open/get-asset',
81
+ ),
82
+ );
vendor/boldgrid/library/tests/Library/Util/test-plugin.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Source Code
4
+ *
5
+ * @package Boldgrid_Plugintest
6
+ * @copyright BoldGrid.com
7
+ * @version $Id$
8
+ * @author BoldGrid.com <wpb@boldgrid.com>
9
+ */
10
+
11
+ /**
12
+ * BoldGrid Library Util Plugin Test class.
13
+ *
14
+ * @since 2.2.1
15
+ */
16
+ class Test_BoldGrid_Library_Util_Plugin extends WP_UnitTestCase {
17
+
18
+ /**
19
+ * Test getFiltered.
20
+ *
21
+ * @since 2.2.1
22
+ */
23
+ public function testGetFiltered() {
24
+ $plugin = new Boldgrid\Library\Library\Util\Plugin();
25
+
26
+ $allPlugins = get_plugins();
27
+
28
+ // This should find all plugins.
29
+ $plugins = $plugin->getFiltered();
30
+ $this->assertSame( $allPlugins, $plugins );
31
+ $plugins = $plugin->getFiltered( 'php' );
32
+ $this->assertSame( $allPlugins, $plugins );
33
+
34
+ // Hello plugin is included with WordPress.
35
+ $plugins = $plugin->getFiltered( 'hello' );
36
+ $this->assertSame( 1, count( $plugins ) );
37
+
38
+ // No BoldGrid plugins are included with WordPress.
39
+ $plugins = $plugin->getFiltered( 'boldgrid-' );
40
+ $this->assertSame( 0, count( $plugins ) );
41
+ }
42
+ }
vendor/boldgrid/library/tests/Util/test-option.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Source Code
4
+ *
5
+ * @package Boldgrid_Plugintest
6
+ * @copyright BoldGrid.com
7
+ * @version $Id$
8
+ * @author BoldGrid.com <wpb@boldgrid.com>
9
+ */
10
+
11
+ /**
12
+ * BoldGrid Util Option Test class.
13
+ *
14
+ * @since 1.1.4
15
+ */
16
+ class Test_BoldGrid_Util_Option extends WP_UnitTestCase {
17
+
18
+ /**
19
+ * Test getFiltered.
20
+ *
21
+ * @since 1.1.4
22
+ */
23
+ public function testDeletePluginTransients() {
24
+ set_site_transient( 'boldgrid_plugins', 'car' );
25
+ set_site_transient( 'boldgrid_wporg_plugins', 'bus' );
26
+ set_site_transient( 'update_plugins', 'van' );
27
+
28
+ $this->assertSame( get_site_transient('boldgrid_plugins'), 'car' );
29
+ $this->assertSame( get_site_transient('boldgrid_wporg_plugins'), 'bus' );
30
+ $this->assertSame( get_site_transient('update_plugins'), 'van' );
31
+
32
+ Boldgrid\Library\Util\Option::deletePluginTransients();
33
+
34
+ $this->assertSame( get_site_transient('boldgrid_plugins'), false );
35
+ $this->assertSame( get_site_transient('boldgrid_wporg_plugins'), false );
36
+ $this->assertSame( get_site_transient('update_plugins'), false );
37
+ }
38
+ }
vendor/boldgrid/library/tests/bootstrap.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $_tests_dir = getenv( 'WP_TESTS_DIR' );
4
+ if ( ! $_tests_dir ) {
5
+ $_tests_dir = '/tmp/wordpress-tests-lib';
6
+ }
7
+
8
+ require_once $_tests_dir . '/includes/functions.php';
9
+
10
+ require_once dirname( dirname( __FILE__ ) ) . '/src/Library/License.php';
11
+ require_once dirname( dirname( __FILE__ ) ) . '/src/Library/Api/Call.php';
12
+ require_once dirname( dirname( __FILE__ ) ) . '/src/Library/Api/Availability.php';
13
+ require_once dirname( dirname( __FILE__ ) ) . '/src/Library/Configs.php';
14
+ require_once dirname( dirname( __FILE__ ) ) . '/src/Library/Filter.php';
15
+ require_once dirname( dirname( __FILE__ ) ) . '/src/Library/Reseller.php';
16
+ require_once dirname( dirname( __FILE__ ) ) . '/src/Library/Util/Plugin.php';
17
+ require_once dirname( dirname( __FILE__ ) ) . '/src/Util/Option.php';
18
+
19
+ require $_tests_dir . '/includes/bootstrap.php';
vendor/boldgrid/library/tests/test-reseller.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * BoldGrid Source Code
4
+ *
5
+ * @package Boldgrid_Plugintest
6
+ * @copyright BoldGrid.com
7
+ * @version $Id$
8
+ * @author BoldGrid.com <wpb@boldgrid.com>
9
+ */
10
+
11
+ /**
12
+ * BoldGrid Library Reseller Test class.
13
+ *
14
+ * @since 1.1
15
+ */
16
+ class Test_BoldGrid_Libarary_Reseller extends WP_UnitTestCase {
17
+
18
+ /**
19
+ * Test default reseller data.
20
+ */
21
+ public function testDefaults() {
22
+ $coinUrl = 'https://boldgrid.com/reseller-coin-url';
23
+
24
+ delete_option( 'boldgrid_reseller' );
25
+
26
+ $reseller = new Boldgrid\Library\Library\Reseller();
27
+
28
+ // With no reseller data, check that the defaults are being returned.
29
+ $this->assertSame( $reseller->centralUrl, $reseller->data['reseller_coin_url'] );
30
+
31
+ update_option( 'boldgrid_reseller', array(
32
+ 'reseller_coin_url' => $coinUrl,
33
+ ));
34
+
35
+ /*
36
+ * @todo The Reseller class is supposed to hook into update_option
37
+ * boldgrid_reseller, but it is not playing well with phpunit. Don't
38
+ * rely on that filter to run, manually reset the data.
39
+ */
40
+ $reseller->setData();
41
+
42
+ // With reseller data set, ensure defaults are not returned.
43
+ $this->assertSame( $coinUrl, $reseller->data['reseller_coin_url'] );
44
+ }
45
+ }
vendor/boldgrid/library/yarn.lock ADDED
@@ -0,0 +1,1260 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2
+ # yarn lockfile v1
3
+
4
+
5
+ abbrev@1:
6
+ version "1.1.1"
7
+ resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
8
+
9
+ acorn-jsx@^3.0.0:
10
+ version "3.0.1"
11
+ resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b"
12
+ dependencies:
13
+ acorn "^3.0.4"
14
+
15
+ acorn@^3.0.4:
16
+ version "3.3.0"
17
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
18
+
19
+ acorn@^5.5.0:
20
+ version "5.5.3"
21
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.5.3.tgz#f473dd47e0277a08e28e9bec5aeeb04751f0b8c9"
22
+
23
+ ajv-keywords@^2.1.0:
24
+ version "2.1.1"
25
+ resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762"
26
+
27
+ ajv@^5.2.3, ajv@^5.3.0:
28
+ version "5.5.2"
29
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
30
+ dependencies:
31
+ co "^4.6.0"
32
+ fast-deep-equal "^1.0.0"
33
+ fast-json-stable-stringify "^2.0.0"
34
+ json-schema-traverse "^0.3.0"
35
+
36
+ ansi-escapes@^3.0.0:
37
+ version "3.1.0"
38
+ resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.1.0.tgz#f73207bb81207d75fd6c83f125af26eea378ca30"
39
+
40
+ ansi-regex@^2.0.0:
41
+ version "2.1.1"
42
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
43
+
44
+ ansi-regex@^3.0.0:
45
+ version "3.0.0"
46
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
47
+
48
+ ansi-styles@^2.2.1:
49
+ version "2.2.1"
50
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
51
+
52
+ ansi-styles@^3.1.0, ansi-styles@^3.2.0, ansi-styles@^3.2.1:
53
+ version "3.2.1"
54
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
55
+ dependencies:
56
+ color-convert "^1.9.0"
57
+
58
+ argparse@^1.0.7:
59
+ version "1.0.10"
60
+ resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
61
+ dependencies:
62
+ sprintf-js "~1.0.2"
63
+
64
+ array-union@^1.0.1:
65
+ version "1.0.2"
66
+ resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
67
+ dependencies:
68
+ array-uniq "^1.0.1"
69
+
70
+ array-uniq@^1.0.1:
71
+ version "1.0.3"
72
+ resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
73
+
74
+ arrify@^1.0.0, arrify@^1.0.1:
75
+ version "1.0.1"
76
+ resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
77
+
78
+ babel-code-frame@^6.22.0:
79
+ version "6.26.0"
80
+ resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
81
+ dependencies:
82
+ chalk "^1.1.3"
83
+ esutils "^2.0.2"
84
+ js-tokens "^3.0.2"
85
+
86
+ babel-runtime@^6.23.0, babel-runtime@^6.26.0:
87
+ version "6.26.0"
88
+ resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
89
+ dependencies:
90
+ core-js "^2.4.0"
91
+ regenerator-runtime "^0.11.0"
92
+
93
+ balanced-match@^1.0.0:
94
+ version "1.0.0"
95
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
96
+
97
+ boolify@^1.0.0:
98
+ version "1.0.1"
99
+ resolved "https://registry.yarnpkg.com/boolify/-/boolify-1.0.1.tgz#b5c09e17cacd113d11b7bb3ed384cc012994d86b"
100
+
101
+ brace-expansion@^1.1.7:
102
+ version "1.1.11"
103
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
104
+ dependencies:
105
+ balanced-match "^1.0.0"
106
+ concat-map "0.0.1"
107
+
108
+ buffer-from@^1.0.0:
109
+ version "1.0.0"
110
+ resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.0.0.tgz#4cb8832d23612589b0406e9e2956c17f06fdf531"
111
+
112
+ caller-path@^0.1.0:
113
+ version "0.1.0"
114
+ resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f"
115
+ dependencies:
116
+ callsites "^0.2.0"
117
+
118
+ callsites@^0.2.0:
119
+ version "0.2.0"
120
+ resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca"
121
+
122
+ camelcase-keys@^4.1.0:
123
+ version "4.2.0"
124
+ resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-4.2.0.tgz#a2aa5fb1af688758259c32c141426d78923b9b77"
125
+ dependencies:
126
+ camelcase "^4.1.0"
127
+ map-obj "^2.0.0"
128
+ quick-lru "^1.0.0"
129
+
130
+ camelcase@^4.1.0:
131
+ version "4.1.0"
132
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
133
+
134
+ chalk@2.3.0:
135
+ version "2.3.0"
136
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.0.tgz#b5ea48efc9c1793dccc9b4767c93914d3f2d52ba"
137
+ dependencies:
138
+ ansi-styles "^3.1.0"
139
+ escape-string-regexp "^1.0.5"
140
+ supports-color "^4.0.0"
141
+
142
+ chalk@^1.1.3:
143
+ version "1.1.3"
144
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
145
+ dependencies:
146
+ ansi-styles "^2.2.1"
147
+ escape-string-regexp "^1.0.2"
148
+ has-ansi "^2.0.0"
149
+ strip-ansi "^3.0.0"
150
+ supports-color "^2.0.0"
151
+
152
+ chalk@^2.0.0, chalk@^2.1.0:
153
+ version "2.3.2"
154
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.2.tgz#250dc96b07491bfd601e648d66ddf5f60c7a5c65"
155
+ dependencies:
156
+ ansi-styles "^3.2.1"
157
+ escape-string-regexp "^1.0.5"
158
+ supports-color "^5.3.0"
159
+
160
+ chardet@^0.4.0:
161
+ version "0.4.2"
162
+ resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2"
163
+
164
+ circular-json@^0.3.1:
165
+ version "0.3.3"
166
+ resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66"
167
+
168
+ cli-cursor@^2.1.0:
169
+ version "2.1.0"
170
+ resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
171
+ dependencies:
172
+ restore-cursor "^2.0.0"
173
+
174
+ cli-width@^2.0.0:
175
+ version "2.2.0"
176
+ resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639"
177
+
178
+ cliui@^3.2.0:
179
+ version "3.2.0"
180
+ resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d"
181
+ dependencies:
182
+ string-width "^1.0.1"
183
+ strip-ansi "^3.0.1"
184
+ wrap-ansi "^2.0.0"
185
+
186
+ co@^4.6.0:
187
+ version "4.6.0"
188
+ resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
189
+
190
+ code-point-at@^1.0.0:
191
+ version "1.1.0"
192
+ resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
193
+
194
+ color-convert@^1.9.0:
195
+ version "1.9.1"
196
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed"
197
+ dependencies:
198
+ color-name "^1.1.1"
199
+
200
+ color-name@^1.1.1:
201
+ version "1.1.3"
202
+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
203
+
204
+ common-tags@^1.4.0:
205
+ version "1.7.2"
206
+ resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.7.2.tgz#24d9768c63d253a56ecff93845b44b4df1d52771"
207
+ dependencies:
208
+ babel-runtime "^6.26.0"
209
+
210
+ concat-map@0.0.1:
211
+ version "0.0.1"
212
+ resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
213
+
214
+ concat-stream@^1.6.0:
215
+ version "1.6.2"
216
+ resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
217
+ dependencies:
218
+ buffer-from "^1.0.0"
219
+ inherits "^2.0.3"
220
+ readable-stream "^2.2.2"
221
+ typedarray "^0.0.6"
222
+
223
+ core-js@^2.4.0:
224
+ version "2.5.4"
225
+ resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.4.tgz#f2c8bf181f2a80b92f360121429ce63a2f0aeae0"
226
+
227
+ core-util-is@~1.0.0:
228
+ version "1.0.2"
229
+ resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
230
+
231
+ cross-spawn@^5.0.1, cross-spawn@^5.1.0:
232
+ version "5.1.0"
233
+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
234
+ dependencies:
235
+ lru-cache "^4.0.1"
236
+ shebang-command "^1.2.0"
237
+ which "^1.2.9"
238
+
239
+ debug@^3.1.0:
240
+ version "3.1.0"
241
+ resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
242
+ dependencies:
243
+ ms "2.0.0"
244
+
245
+ decamelize@^1.1.1:
246
+ version "1.2.0"
247
+ resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
248
+
249
+ deep-is@~0.1.3:
250
+ version "0.1.3"
251
+ resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
252
+
253
+ del@^2.0.2:
254
+ version "2.2.2"
255
+ resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8"
256
+ dependencies:
257
+ globby "^5.0.0"
258
+ is-path-cwd "^1.0.0"
259
+ is-path-in-cwd "^1.0.0"
260
+ object-assign "^4.0.1"
261
+ pify "^2.0.0"
262
+ pinkie-promise "^2.0.0"
263
+ rimraf "^2.2.8"
264
+
265
+ dlv@^1.1.0:
266
+ version "1.1.1"
267
+ resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.1.tgz#c79d96bfe659a5568001250ed2aaf653992bdd3f"
268
+
269
+ doctrine@^2.1.0:
270
+ version "2.1.0"
271
+ resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d"
272
+ dependencies:
273
+ esutils "^2.0.2"
274
+
275
+ dom-serializer@0:
276
+ version "0.1.0"
277
+ resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
278
+ dependencies:
279
+ domelementtype "~1.1.1"
280
+ entities "~1.1.1"
281
+
282
+ domelementtype@1, domelementtype@^1.3.0:
283
+ version "1.3.0"
284
+ resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2"
285
+
286
+ domelementtype@~1.1.1:
287
+ version "1.1.3"
288
+ resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b"
289
+
290
+ domhandler@^2.3.0:
291
+ version "2.4.1"
292
+ resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.1.tgz#892e47000a99be55bbf3774ffea0561d8879c259"
293
+ dependencies:
294
+ domelementtype "1"
295
+
296
+ domutils@^1.5.1:
297
+ version "1.7.0"
298
+ resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a"
299
+ dependencies:
300
+ dom-serializer "0"
301
+ domelementtype "1"
302
+
303
+ entities@^1.1.1, entities@~1.1.1:
304
+ version "1.1.1"
305
+ resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
306
+
307
+ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
308
+ version "1.0.5"
309
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
310
+
311
+ eslint-config-wordpress@^2.0.0:
312
+ version "2.0.0"
313
+ resolved "https://registry.yarnpkg.com/eslint-config-wordpress/-/eslint-config-wordpress-2.0.0.tgz#5201206c6964d648315232edf6dfbd2e925e4cd6"
314
+
315
+ eslint-plugin-html@^4.0.2:
316
+ version "4.0.2"
317
+ resolved "https://registry.yarnpkg.com/eslint-plugin-html/-/eslint-plugin-html-4.0.2.tgz#0e56149e42c2ffc3f0df6261a8bb96b1a9f2280d"
318
+ dependencies:
319
+ htmlparser2 "^3.8.2"
320
+
321
+ eslint-scope@^3.7.1:
322
+ version "3.7.1"
323
+ resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8"
324
+ dependencies:
325
+ esrecurse "^4.1.0"
326
+ estraverse "^4.1.1"
327
+
328
+ eslint-visitor-keys@^1.0.0:
329
+ version "1.0.0"
330
+ resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d"
331
+
332
+ eslint@^4.0.0, eslint@^4.19.1, eslint@^4.5.0:
333
+ version "4.19.1"
334
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.19.1.tgz#32d1d653e1d90408854bfb296f076ec7e186a300"
335
+ dependencies:
336
+ ajv "^5.3.0"
337
+ babel-code-frame "^6.22.0"
338
+ chalk "^2.1.0"
339
+ concat-stream "^1.6.0"
340
+ cross-spawn "^5.1.0"
341
+ debug "^3.1.0"
342
+ doctrine "^2.1.0"
343
+ eslint-scope "^3.7.1"
344
+ eslint-visitor-keys "^1.0.0"
345
+ espree "^3.5.4"
346
+ esquery "^1.0.0"
347
+ esutils "^2.0.2"
348
+ file-entry-cache "^2.0.0"
349
+ functional-red-black-tree "^1.0.1"
350
+ glob "^7.1.2"
351
+ globals "^11.0.1"
352
+ ignore "^3.3.3"
353
+ imurmurhash "^0.1.4"
354
+ inquirer "^3.0.6"
355
+ is-resolvable "^1.0.0"
356
+ js-yaml "^3.9.1"
357
+ json-stable-stringify-without-jsonify "^1.0.1"
358
+ levn "^0.3.0"
359
+ lodash "^4.17.4"
360
+ minimatch "^3.0.2"
361
+ mkdirp "^0.5.1"
362
+ natural-compare "^1.4.0"
363
+ optionator "^0.8.2"
364
+ path-is-inside "^1.0.2"
365
+ pluralize "^7.0.0"
366
+ progress "^2.0.0"
367
+ regexpp "^1.0.1"
368
+ require-uncached "^1.0.3"
369
+ semver "^5.3.0"
370
+ strip-ansi "^4.0.0"
371
+ strip-json-comments "~2.0.1"
372
+ table "4.0.2"
373
+ text-table "~0.2.0"
374
+
375
+ espree@^3.5.4:
376
+ version "3.5.4"
377
+ resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7"
378
+ dependencies:
379
+ acorn "^5.5.0"
380
+ acorn-jsx "^3.0.0"
381
+
382
+ esprima@^4.0.0:
383
+ version "4.0.0"
384
+ resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804"
385
+
386
+ esquery@^1.0.0:
387
+ version "1.0.0"
388
+ resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.0.tgz#cfba8b57d7fba93f17298a8a006a04cda13d80fa"
389
+ dependencies:
390
+ estraverse "^4.0.0"
391
+
392
+ esrecurse@^4.1.0:
393
+ version "4.2.1"
394
+ resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf"
395
+ dependencies:
396
+ estraverse "^4.1.0"
397
+
398
+ estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1:
399
+ version "4.2.0"
400
+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13"
401
+
402
+ esutils@^2.0.2:
403
+ version "2.0.2"
404
+ resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
405
+
406
+ execa@^0.7.0:
407
+ version "0.7.0"
408
+ resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777"
409
+ dependencies:
410
+ cross-spawn "^5.0.1"
411
+ get-stream "^3.0.0"
412
+ is-stream "^1.1.0"
413
+ npm-run-path "^2.0.0"
414
+ p-finally "^1.0.0"
415
+ signal-exit "^3.0.0"
416
+ strip-eof "^1.0.0"
417
+
418
+ external-editor@^2.0.4:
419
+ version "2.1.0"
420
+ resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.1.0.tgz#3d026a21b7f95b5726387d4200ac160d372c3b48"
421
+ dependencies:
422
+ chardet "^0.4.0"
423
+ iconv-lite "^0.4.17"
424
+ tmp "^0.0.33"
425
+
426
+ fast-deep-equal@^1.0.0:
427
+ version "1.1.0"
428
+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614"
429
+
430
+ fast-json-stable-stringify@^2.0.0:
431
+ version "2.0.0"
432
+ resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
433
+
434
+ fast-levenshtein@~2.0.4:
435
+ version "2.0.6"
436
+ resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
437
+
438
+ figures@^2.0.0:
439
+ version "2.0.0"
440
+ resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962"
441
+ dependencies:
442
+ escape-string-regexp "^1.0.5"
443
+
444
+ file-entry-cache@^2.0.0:
445
+ version "2.0.0"
446
+ resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361"
447
+ dependencies:
448
+ flat-cache "^1.2.1"
449
+ object-assign "^4.0.1"
450
+
451
+ find-up@^2.1.0:
452
+ version "2.1.0"
453
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
454
+ dependencies:
455
+ locate-path "^2.0.0"
456
+
457
+ flat-cache@^1.2.1:
458
+ version "1.3.0"
459
+ resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.0.tgz#d3030b32b38154f4e3b7e9c709f490f7ef97c481"
460
+ dependencies:
461
+ circular-json "^0.3.1"
462
+ del "^2.0.2"
463
+ graceful-fs "^4.1.2"
464
+ write "^0.2.1"
465
+
466
+ fs.realpath@^1.0.0:
467
+ version "1.0.0"
468
+ resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
469
+
470
+ functional-red-black-tree@^1.0.1:
471
+ version "1.0.1"
472
+ resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
473
+
474
+ get-caller-file@^1.0.1:
475
+ version "1.0.2"
476
+ resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5"
477
+
478
+ get-stdin@^5.0.1:
479
+ version "5.0.1"
480
+ resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398"
481
+
482
+ get-stream@^3.0.0:
483
+ version "3.0.0"
484
+ resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
485
+
486
+ glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2:
487
+ version "7.1.2"
488
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
489
+ dependencies:
490
+ fs.realpath "^1.0.0"
491
+ inflight "^1.0.4"
492
+ inherits "2"
493
+ minimatch "^3.0.4"
494
+ once "^1.3.0"
495
+ path-is-absolute "^1.0.0"
496
+
497
+ glob@~7.0.6:
498
+ version "7.0.6"
499
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.6.tgz#211bafaf49e525b8cd93260d14ab136152b3f57a"
500
+ dependencies:
501
+ fs.realpath "^1.0.0"
502
+ inflight "^1.0.4"
503
+ inherits "2"
504
+ minimatch "^3.0.2"
505
+ once "^1.3.0"
506
+ path-is-absolute "^1.0.0"
507
+
508
+ globals@^11.0.1:
509
+ version "11.4.0"
510
+ resolved "https://registry.yarnpkg.com/globals/-/globals-11.4.0.tgz#b85c793349561c16076a3c13549238a27945f1bc"
511
+
512
+ globby@^5.0.0:
513
+ version "5.0.0"
514
+ resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d"
515
+ dependencies:
516
+ array-union "^1.0.1"
517
+ arrify "^1.0.0"
518
+ glob "^7.0.3"
519
+ object-assign "^4.0.1"
520
+ pify "^2.0.0"
521
+ pinkie-promise "^2.0.0"
522
+
523
+ graceful-fs@^4.1.2:
524
+ version "4.1.11"
525
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
526
+
527
+ has-ansi@^2.0.0:
528
+ version "2.0.0"
529
+ resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
530
+ dependencies:
531
+ ansi-regex "^2.0.0"
532
+
533
+ has-flag@^2.0.0:
534
+ version "2.0.0"
535
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51"
536
+
537
+ has-flag@^3.0.0:
538
+ version "3.0.0"
539
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
540
+
541
+ htmlparser2@^3.8.2:
542
+ version "3.9.2"
543
+ resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338"
544
+ dependencies:
545
+ domelementtype "^1.3.0"
546
+ domhandler "^2.3.0"
547
+ domutils "^1.5.1"
548
+ entities "^1.1.1"
549
+ inherits "^2.0.1"
550
+ readable-stream "^2.0.2"
551
+
552
+ iconv-lite@^0.4.17:
553
+ version "0.4.19"
554
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
555
+
556
+ ignore@^3.2.7, ignore@^3.3.3:
557
+ version "3.3.7"
558
+ resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.7.tgz#612289bfb3c220e186a58118618d5be8c1bab021"
559
+
560
+ imurmurhash@^0.1.4:
561
+ version "0.1.4"
562
+ resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
563
+
564
+ indent-string@^3.1.0, indent-string@^3.2.0:
565
+ version "3.2.0"
566
+ resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289"
567
+
568
+ inflight@^1.0.4:
569
+ version "1.0.6"
570
+ resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
571
+ dependencies:
572
+ once "^1.3.0"
573
+ wrappy "1"
574
+
575
+ inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3:
576
+ version "2.0.3"
577
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
578
+
579
+ inquirer@^3.0.6:
580
+ version "3.3.0"
581
+ resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9"
582
+ dependencies:
583
+ ansi-escapes "^3.0.0"
584
+ chalk "^2.0.0"
585
+ cli-cursor "^2.1.0"
586
+ cli-width "^2.0.0"
587
+ external-editor "^2.0.4"
588
+ figures "^2.0.0"
589
+ lodash "^4.3.0"
590
+ mute-stream "0.0.7"
591
+ run-async "^2.2.0"
592
+ rx-lite "^4.0.8"
593
+ rx-lite-aggregates "^4.0.8"
594
+ string-width "^2.1.0"
595
+ strip-ansi "^4.0.0"
596
+ through "^2.3.6"
597
+
598
+ invert-kv@^1.0.0:
599
+ version "1.0.0"
600
+ resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
601
+
602
+ is-fullwidth-code-point@^1.0.0:
603
+ version "1.0.0"
604
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
605
+ dependencies:
606
+ number-is-nan "^1.0.0"
607
+
608
+ is-fullwidth-code-point@^2.0.0:
609
+ version "2.0.0"
610
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
611
+
612
+ is-path-cwd@^1.0.0:
613
+ version "1.0.0"
614
+ resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d"
615
+
616
+ is-path-in-cwd@^1.0.0:
617
+ version "1.0.1"
618
+ resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz#5ac48b345ef675339bd6c7a48a912110b241cf52"
619
+ dependencies:
620
+ is-path-inside "^1.0.0"
621
+
622
+ is-path-inside@^1.0.0:
623
+ version "1.0.1"
624
+ resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036"
625
+ dependencies:
626
+ path-is-inside "^1.0.1"
627
+
628
+ is-promise@^2.1.0:
629
+ version "2.1.0"
630
+ resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
631
+
632
+ is-resolvable@^1.0.0:
633
+ version "1.1.0"
634
+ resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88"
635
+
636
+ is-stream@^1.1.0:
637
+ version "1.1.0"
638
+ resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
639
+
640
+ isarray@~1.0.0:
641
+ version "1.0.0"
642
+ resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
643
+
644
+ isexe@^2.0.0:
645
+ version "2.0.0"
646
+ resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
647
+
648
+ js-tokens@^3.0.2:
649
+ version "3.0.2"
650
+ resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
651
+
652
+ js-yaml@^3.9.1:
653
+ version "3.11.0"
654
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.11.0.tgz#597c1a8bd57152f26d622ce4117851a51f5ebaef"
655
+ dependencies:
656
+ argparse "^1.0.7"
657
+ esprima "^4.0.0"
658
+
659
+ json-schema-traverse@^0.3.0:
660
+ version "0.3.1"
661
+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340"
662
+
663
+ json-stable-stringify-without-jsonify@^1.0.1:
664
+ version "1.0.1"
665
+ resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
666
+
667
+ lcid@^1.0.0:
668
+ version "1.0.0"
669
+ resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835"
670
+ dependencies:
671
+ invert-kv "^1.0.0"
672
+
673
+ levn@^0.3.0, levn@~0.3.0:
674
+ version "0.3.0"
675
+ resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
676
+ dependencies:
677
+ prelude-ls "~1.1.2"
678
+ type-check "~0.3.2"
679
+
680
+ locate-path@^2.0.0:
681
+ version "2.0.0"
682
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
683
+ dependencies:
684
+ p-locate "^2.0.0"
685
+ path-exists "^3.0.0"
686
+
687
+ lodash.memoize@^4.1.2:
688
+ version "4.1.2"
689
+ resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
690
+
691
+ lodash.merge@^4.6.0:
692
+ version "4.6.1"
693
+ resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.1.tgz#adc25d9cb99b9391c59624f379fbba60d7111d54"
694
+
695
+ lodash.unescape@4.0.1:
696
+ version "4.0.1"
697
+ resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c"
698
+
699
+ lodash@^4.17.4, lodash@^4.3.0:
700
+ version "4.17.5"
701
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511"
702
+
703
+ loglevel-colored-level-prefix@^1.0.0:
704
+ version "1.0.0"
705
+ resolved "https://registry.yarnpkg.com/loglevel-colored-level-prefix/-/loglevel-colored-level-prefix-1.0.0.tgz#6a40218fdc7ae15fc76c3d0f3e676c465388603e"
706
+ dependencies:
707
+ chalk "^1.1.3"
708
+ loglevel "^1.4.1"
709
+
710
+ loglevel@^1.4.1:
711
+ version "1.6.1"
712
+ resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.1.tgz#e0fc95133b6ef276cdc8887cdaf24aa6f156f8fa"
713
+
714
+ lru-cache@^4.0.1:
715
+ version "4.1.2"
716
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.2.tgz#45234b2e6e2f2b33da125624c4664929a0224c3f"
717
+ dependencies:
718
+ pseudomap "^1.0.2"
719
+ yallist "^2.1.2"
720
+
721
+ make-plural@^4.1.1:
722
+ version "4.1.1"
723
+ resolved "https://registry.yarnpkg.com/make-plural/-/make-plural-4.1.1.tgz#5658ce9d337487077daed221854c8cef9dd75749"
724
+ optionalDependencies:
725
+ minimist "^1.2.0"
726
+
727
+ map-obj@^2.0.0:
728
+ version "2.0.0"
729
+ resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-2.0.0.tgz#a65cd29087a92598b8791257a523e021222ac1f9"
730
+
731
+ mem@^1.1.0:
732
+ version "1.1.0"
733
+ resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76"
734
+ dependencies:
735
+ mimic-fn "^1.0.0"
736
+
737
+ messageformat-parser@^1.1.0:
738
+ version "1.1.0"
739
+ resolved "https://registry.yarnpkg.com/messageformat-parser/-/messageformat-parser-1.1.0.tgz#13ba2250a76bbde8e0fca0dbb3475f95c594a90a"
740
+
741
+ messageformat@^1.0.2:
742
+ version "1.1.1"
743
+ resolved "https://registry.yarnpkg.com/messageformat/-/messageformat-1.1.1.tgz#ceaa2e6c86929d4807058275a7372b1bd963bdf6"
744
+ dependencies:
745
+ glob "~7.0.6"
746
+ make-plural "^4.1.1"
747
+ messageformat-parser "^1.1.0"
748
+ nopt "~3.0.6"
749
+ reserved-words "^0.1.2"
750
+
751
+ mimic-fn@^1.0.0:
752
+ version "1.2.0"
753
+ resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
754
+
755
+ minimatch@^3.0.2, minimatch@^3.0.4:
756
+ version "3.0.4"
757
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
758
+ dependencies:
759
+ brace-expansion "^1.1.7"
760
+
761
+ minimist@0.0.8:
762
+ version "0.0.8"
763
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
764
+
765
+ minimist@^1.2.0:
766
+ version "1.2.0"
767
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
768
+
769
+ mkdirp@^0.5.1:
770
+ version "0.5.1"
771
+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
772
+ dependencies:
773
+ minimist "0.0.8"
774
+
775
+ ms@2.0.0:
776
+ version "2.0.0"
777
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
778
+
779
+ mute-stream@0.0.7:
780
+ version "0.0.7"
781
+ resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
782
+
783
+ natural-compare@^1.4.0:
784
+ version "1.4.0"
785
+ resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
786
+
787
+ nopt@~3.0.6:
788
+ version "3.0.6"
789
+ resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9"
790
+ dependencies:
791
+ abbrev "1"
792
+
793
+ npm-run-path@^2.0.0:
794
+ version "2.0.2"
795
+ resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
796
+ dependencies:
797
+ path-key "^2.0.0"
798
+
799
+ number-is-nan@^1.0.0:
800
+ version "1.0.1"
801
+ resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
802
+
803
+ object-assign@^4.0.1:
804
+ version "4.1.1"
805
+ resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
806
+
807
+ once@^1.3.0:
808
+ version "1.4.0"
809
+ resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
810
+ dependencies:
811
+ wrappy "1"
812
+
813
+ onetime@^2.0.0:
814
+ version "2.0.1"
815
+ resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4"
816
+ dependencies:
817
+ mimic-fn "^1.0.0"
818
+
819
+ optionator@^0.8.2:
820
+ version "0.8.2"
821
+ resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64"
822
+ dependencies:
823
+ deep-is "~0.1.3"
824
+ fast-levenshtein "~2.0.4"
825
+ levn "~0.3.0"
826
+ prelude-ls "~1.1.2"
827
+ type-check "~0.3.2"
828
+ wordwrap "~1.0.0"
829
+
830
+ os-locale@^2.0.0:
831
+ version "2.1.0"
832
+ resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2"
833
+ dependencies:
834
+ execa "^0.7.0"
835
+ lcid "^1.0.0"
836
+ mem "^1.1.0"
837
+
838
+ os-tmpdir@~1.0.2:
839
+ version "1.0.2"
840
+ resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
841
+
842
+ p-finally@^1.0.0:
843
+ version "1.0.0"
844
+ resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
845
+
846
+ p-limit@^1.1.0:
847
+ version "1.2.0"
848
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.2.0.tgz#0e92b6bedcb59f022c13d0f1949dc82d15909f1c"
849
+ dependencies:
850
+ p-try "^1.0.0"
851
+
852
+ p-locate@^2.0.0:
853
+ version "2.0.0"
854
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
855
+ dependencies:
856
+ p-limit "^1.1.0"
857
+
858
+ p-try@^1.0.0:
859
+ version "1.0.0"
860
+ resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
861
+
862
+ path-exists@^3.0.0:
863
+ version "3.0.0"
864
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
865
+
866
+ path-is-absolute@^1.0.0:
867
+ version "1.0.1"
868
+ resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
869
+
870
+ path-is-inside@^1.0.1, path-is-inside@^1.0.2:
871
+ version "1.0.2"
872
+ resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
873
+
874
+ path-key@^2.0.0:
875
+ version "2.0.1"
876
+ resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
877
+
878
+ pify@^2.0.0:
879
+ version "2.3.0"
880
+ resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
881
+
882
+ pinkie-promise@^2.0.0:
883
+ version "2.0.1"
884
+ resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
885
+ dependencies:
886
+ pinkie "^2.0.0"
887
+
888
+ pinkie@^2.0.0:
889
+ version "2.0.4"
890
+ resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
891
+
892
+ pluralize@^7.0.0:
893
+ version "7.0.0"
894
+ resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777"
895
+
896
+ prelude-ls@~1.1.2:
897
+ version "1.1.2"
898
+ resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
899
+
900
+ prettier-eslint-cli@^4.7.1:
901
+ version "4.7.1"
902
+ resolved "https://registry.yarnpkg.com/prettier-eslint-cli/-/prettier-eslint-cli-4.7.1.tgz#3d103c494baa4e80b99ad53e2b9db7620101859f"
903
+ dependencies:
904
+ arrify "^1.0.1"
905
+ babel-runtime "^6.23.0"
906
+ boolify "^1.0.0"
907
+ camelcase-keys "^4.1.0"
908
+ chalk "2.3.0"
909
+ common-tags "^1.4.0"
910
+ eslint "^4.5.0"
911
+ find-up "^2.1.0"
912
+ get-stdin "^5.0.1"
913
+ glob "^7.1.1"
914
+ ignore "^3.2.7"
915
+ indent-string "^3.1.0"
916
+ lodash.memoize "^4.1.2"
917
+ loglevel-colored-level-prefix "^1.0.0"
918
+ messageformat "^1.0.2"
919
+ prettier-eslint "^8.5.0"
920
+ rxjs "^5.3.0"
921
+ yargs "10.0.3"
922
+
923
+ prettier-eslint@^8.5.0, prettier-eslint@^8.8.1:
924
+ version "8.8.1"
925
+ resolved "https://registry.yarnpkg.com/prettier-eslint/-/prettier-eslint-8.8.1.tgz#38505163274742f2a0b31653c39e40f37ebd07da"
926
+ dependencies:
927
+ babel-runtime "^6.26.0"
928
+ common-tags "^1.4.0"
929
+ dlv "^1.1.0"
930
+ eslint "^4.0.0"
931
+ indent-string "^3.2.0"
932
+ lodash.merge "^4.6.0"
933
+ loglevel-colored-level-prefix "^1.0.0"
934
+ prettier "^1.7.0"
935
+ pretty-format "^22.0.3"
936
+ require-relative "^0.8.7"
937
+ typescript "^2.5.1"
938
+ typescript-eslint-parser "^11.0.0"
939
+
940
+ prettier@^1.7.0:
941
+ version "1.11.1"
942
+ resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.11.1.tgz#61e43fc4cd44e68f2b0dfc2c38cd4bb0fccdcc75"
943
+
944
+ pretty-format@^22.0.3:
945
+ version "22.4.3"
946
+ resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-22.4.3.tgz#f873d780839a9c02e9664c8a082e9ee79eaac16f"
947
+ dependencies:
948
+ ansi-regex "^3.0.0"
949
+ ansi-styles "^3.2.0"
950
+
951
+ process-nextick-args@~2.0.0:
952
+ version "2.0.0"
953
+ resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa"
954
+
955
+ progress@^2.0.0:
956
+ version "2.0.0"
957
+ resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f"
958
+
959
+ pseudomap@^1.0.2:
960
+ version "1.0.2"
961
+ resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
962
+
963
+ quick-lru@^1.0.0:
964
+ version "1.1.0"
965
+ resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8"
966
+
967
+ readable-stream@^2.0.2, readable-stream@^2.2.2:
968
+ version "2.3.5"
969
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.5.tgz#b4f85003a938cbb6ecbce2a124fb1012bd1a838d"
970
+ dependencies:
971
+ core-util-is "~1.0.0"
972
+ inherits "~2.0.3"
973
+ isarray "~1.0.0"
974
+ process-nextick-args "~2.0.0"
975
+ safe-buffer "~5.1.1"
976
+ string_decoder "~1.0.3"
977
+ util-deprecate "~1.0.1"
978
+
979
+ regenerator-runtime@^0.11.0:
980
+ version "0.11.1"
981
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
982
+
983
+ regexpp@^1.0.1:
984
+ version "1.1.0"
985
+ resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-1.1.0.tgz#0e3516dd0b7904f413d2d4193dce4618c3a689ab"
986
+
987
+ require-directory@^2.1.1:
988
+ version "2.1.1"
989
+ resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
990
+
991
+ require-main-filename@^1.0.1:
992
+ version "1.0.1"
993
+ resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
994
+
995
+ require-relative@^0.8.7:
996
+ version "0.8.7"
997
+ resolved "https://registry.yarnpkg.com/require-relative/-/require-relative-0.8.7.tgz#7999539fc9e047a37928fa196f8e1563dabd36de"
998
+
999
+ require-uncached@^1.0.3:
1000
+ version "1.0.3"
1001
+ resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3"
1002
+ dependencies:
1003
+ caller-path "^0.1.0"
1004
+ resolve-from "^1.0.0"
1005
+
1006
+ reserved-words@^0.1.2:
1007
+ version "0.1.2"
1008
+ resolved "https://registry.yarnpkg.com/reserved-words/-/reserved-words-0.1.2.tgz#00a0940f98cd501aeaaac316411d9adc52b31ab1"
1009
+
1010
+ resolve-from@^1.0.0:
1011
+ version "1.0.1"
1012
+ resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226"
1013
+
1014
+ restore-cursor@^2.0.0:
1015
+ version "2.0.0"
1016
+ resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
1017
+ dependencies:
1018
+ onetime "^2.0.0"
1019
+ signal-exit "^3.0.2"
1020
+
1021
+ rimraf@^2.2.8:
1022
+ version "2.6.2"
1023
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36"
1024
+ dependencies:
1025
+ glob "^7.0.5"
1026
+
1027
+ run-async@^2.2.0:
1028
+ version "2.3.0"
1029
+ resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0"
1030
+ dependencies:
1031
+ is-promise "^2.1.0"
1032
+
1033
+ rx-lite-aggregates@^4.0.8:
1034
+ version "4.0.8"
1035
+ resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be"
1036
+ dependencies:
1037
+ rx-lite "*"
1038
+
1039
+ rx-lite@*, rx-lite@^4.0.8:
1040
+ version "4.0.8"
1041
+ resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444"
1042
+
1043
+ rxjs@^5.3.0:
1044
+ version "5.5.8"
1045
+ resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.8.tgz#b2b0809a57614ad6254c03d7446dea0d83ca3791"
1046
+ dependencies:
1047
+ symbol-observable "1.0.1"
1048
+
1049
+ safe-buffer@~5.1.0, safe-buffer@~5.1.1:
1050
+ version "5.1.1"
1051
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
1052
+
1053
+ semver@5.4.1:
1054
+ version "5.4.1"
1055
+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e"
1056
+
1057
+ semver@^5.3.0:
1058
+ version "5.5.0"
1059
+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
1060
+
1061
+ set-blocking@^2.0.0:
1062
+ version "2.0.0"
1063
+ resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
1064
+
1065
+ shebang-command@^1.2.0:
1066
+ version "1.2.0"
1067
+ resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
1068
+ dependencies:
1069
+ shebang-regex "^1.0.0"
1070
+
1071
+ shebang-regex@^1.0.0:
1072
+ version "1.0.0"
1073
+ resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
1074
+
1075
+ signal-exit@^3.0.0, signal-exit@^3.0.2:
1076
+ version "3.0.2"
1077
+ resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
1078
+
1079
+ slice-ansi@1.0.0:
1080
+ version "1.0.0"
1081
+ resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d"
1082
+ dependencies:
1083
+ is-fullwidth-code-point "^2.0.0"
1084
+
1085
+ sprintf-js@~1.0.2:
1086
+ version "1.0.3"
1087
+ resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
1088
+
1089
+ string-width@^1.0.1:
1090
+ version "1.0.2"
1091
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
1092
+ dependencies:
1093
+ code-point-at "^1.0.0"
1094
+ is-fullwidth-code-point "^1.0.0"
1095
+ strip-ansi "^3.0.0"
1096
+
1097
+ string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1:
1098
+ version "2.1.1"
1099
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
1100
+ dependencies:
1101
+ is-fullwidth-code-point "^2.0.0"
1102
+ strip-ansi "^4.0.0"
1103
+
1104
+ string_decoder@~1.0.3:
1105
+ version "1.0.3"
1106
+ resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab"
1107
+ dependencies:
1108
+ safe-buffer "~5.1.0"
1109
+
1110
+ strip-ansi@^3.0.0, strip-ansi@^3.0.1:
1111
+ version "3.0.1"
1112
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
1113
+ dependencies:
1114
+ ansi-regex "^2.0.0"
1115
+
1116
+ strip-ansi@^4.0.0:
1117
+ version "4.0.0"
1118
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
1119
+ dependencies:
1120
+ ansi-regex "^3.0.0"
1121
+
1122
+ strip-eof@^1.0.0:
1123
+ version "1.0.0"
1124
+ resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
1125
+
1126
+ strip-json-comments@~2.0.1:
1127
+ version "2.0.1"
1128
+ resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
1129
+
1130
+ supports-color@^2.0.0:
1131
+ version "2.0.0"
1132
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
1133
+
1134
+ supports-color@^4.0.0:
1135
+ version "4.5.0"
1136
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b"
1137
+ dependencies:
1138
+ has-flag "^2.0.0"
1139
+
1140
+ supports-color@^5.3.0:
1141
+ version "5.3.0"
1142
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.3.0.tgz#5b24ac15db80fa927cf5227a4a33fd3c4c7676c0"
1143
+ dependencies:
1144
+ has-flag "^3.0.0"
1145
+
1146
+ symbol-observable@1.0.1:
1147
+ version "1.0.1"
1148
+ resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4"
1149
+
1150
+ table@4.0.2:
1151
+ version "4.0.2"
1152
+ resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36"
1153
+ dependencies:
1154
+ ajv "^5.2.3"
1155
+ ajv-keywords "^2.1.0"
1156
+ chalk "^2.1.0"
1157
+ lodash "^4.17.4"
1158
+ slice-ansi "1.0.0"
1159
+ string-width "^2.1.1"
1160
+
1161
+ text-table@~0.2.0:
1162
+ version "0.2.0"
1163
+ resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
1164
+
1165
+ through@^2.3.6:
1166
+ version "2.3.8"
1167
+ resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
1168
+
1169
+ tmp@^0.0.33:
1170
+ version "0.0.33"
1171
+ resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
1172
+ dependencies:
1173
+ os-tmpdir "~1.0.2"
1174
+
1175
+ type-check@~0.3.2:
1176
+ version "0.3.2"
1177
+ resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
1178
+ dependencies:
1179
+ prelude-ls "~1.1.2"
1180
+
1181
+ typedarray@^0.0.6:
1182
+ version "0.0.6"
1183
+ resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
1184
+
1185
+ typescript-eslint-parser@^11.0.0:
1186
+ version "11.0.0"
1187
+ resolved "https://registry.yarnpkg.com/typescript-eslint-parser/-/typescript-eslint-parser-11.0.0.tgz#37dba6a0130dd307504aa4b4b21b0d3dc7d4e9f2"
1188
+ dependencies:
1189
+ lodash.unescape "4.0.1"
1190
+ semver "5.4.1"
1191
+
1192
+ typescript@^2.5.1:
1193
+ version "2.8.1"
1194
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.8.1.tgz#6160e4f8f195d5ba81d4876f9c0cc1fbc0820624"
1195
+
1196
+ util-deprecate@~1.0.1:
1197
+ version "1.0.2"
1198
+ resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
1199
+
1200
+ which-module@^2.0.0:
1201
+ version "2.0.0"
1202
+ resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
1203
+
1204
+ which@^1.2.9:
1205
+ version "1.3.0"
1206
+ resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a"
1207
+ dependencies:
1208
+ isexe "^2.0.0"
1209
+
1210
+ wordwrap@~1.0.0:
1211
+ version "1.0.0"
1212
+ resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
1213
+
1214
+ wrap-ansi@^2.0.0:
1215
+ version "2.1.0"
1216
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
1217
+ dependencies:
1218
+ string-width "^1.0.1"
1219
+ strip-ansi "^3.0.1"
1220
+
1221
+ wrappy@1:
1222
+ version "1.0.2"
1223
+ resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
1224
+
1225
+ write@^0.2.1:
1226
+ version "0.2.1"
1227
+ resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757"
1228
+ dependencies:
1229
+ mkdirp "^0.5.1"
1230
+
1231
+ y18n@^3.2.1:
1232
+ version "3.2.1"
1233
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"
1234
+
1235
+ yallist@^2.1.2:
1236
+ version "2.1.2"
1237
+ resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
1238
+
1239
+ yargs-parser@^8.0.0:
1240
+ version "8.1.0"
1241
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-8.1.0.tgz#f1376a33b6629a5d063782944da732631e966950"
1242
+ dependencies:
1243
+ camelcase "^4.1.0"
1244
+
1245
+ yargs@10.0.3:
1246
+ version "10.0.3"
1247
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-10.0.3.tgz#6542debd9080ad517ec5048fb454efe9e4d4aaae"
1248
+ dependencies:
1249
+ cliui "^3.2.0"
1250
+ decamelize "^1.1.1"
1251
+ find-up "^2.1.0"
1252
+ get-caller-file "^1.0.1"
1253
+ os-locale "^2.0.0"
1254
+ require-directory "^2.1.1"
1255
+ require-main-filename "^1.0.1"
1256
+ set-blocking "^2.0.0"
1257
+ string-width "^2.0.0"
1258
+ which-module "^2.0.0"
1259
+ y18n "^3.2.1"
1260
+ yargs-parser "^8.0.0"
vendor/cbschuld/browser.php ADDED
@@ -0,0 +1 @@
 
1
+ Subproject commit 6bde9efb0b14a4917affe741500c29149419e491
vendor/composer/ClassLoader.php ADDED
@@ -0,0 +1,445 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Composer.
5
+ *
6
+ * (c) Nils Adermann <naderman@naderman.de>
7
+ * Jordi Boggiano <j.boggiano@seld.be>
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ namespace Composer\Autoload;
14
+
15
+ /**
16
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
17
+ *
18
+ * $loader = new \Composer\Autoload\ClassLoader();
19
+ *
20
+ * // register classes with namespaces
21
+ * $loader->add('Symfony\Component', __DIR__.'/component');
22
+ * $loader->add('Symfony', __DIR__.'/framework');
23
+ *
24
+ * // activate the autoloader
25
+ * $loader->register();
26
+ *
27
+ * // to enable searching the include path (eg. for PEAR packages)
28
+ * $loader->setUseIncludePath(true);
29
+ *
30
+ * In this example, if you try to use a class in the Symfony\Component
31
+ * namespace or one of its children (Symfony\Component\Console for instance),
32
+ * the autoloader will first look for the class under the component/
33
+ * directory, and it will then fallback to the framework/ directory if not
34
+ * found before giving up.
35
+ *
36
+ * This class is loosely based on the Symfony UniversalClassLoader.
37
+ *
38
+ * @author Fabien Potencier <fabien@symfony.com>
39
+ * @author Jordi Boggiano <j.boggiano@seld.be>
40
+ * @see http://www.php-fig.org/psr/psr-0/
41
+ * @see http://www.php-fig.org/psr/psr-4/
42
+ */
43
+ class ClassLoader
44
+ {
45
+ // PSR-4
46
+ private $prefixLengthsPsr4 = array();
47
+ private $prefixDirsPsr4 = array();
48
+ private $fallbackDirsPsr4 = array();
49
+
50
+ // PSR-0
51
+ private $prefixesPsr0 = array();
52
+ private $fallbackDirsPsr0 = array();
53
+
54
+ private $useIncludePath = false;
55
+ private $classMap = array();
56
+ private $classMapAuthoritative = false;
57
+ private $missingClasses = array();
58
+ private $apcuPrefix;
59
+
60
+ public function getPrefixes()
61
+ {
62
+ if (!empty($this->prefixesPsr0)) {
63
+ return call_user_func_array('array_merge', $this->prefixesPsr0);
64
+ }
65
+
66
+ return array();
67
+ }
68
+
69
+ public function getPrefixesPsr4()
70
+ {
71
+ return $this->prefixDirsPsr4;
72
+ }
73
+
74
+ public function getFallbackDirs()
75
+ {
76
+ return $this->fallbackDirsPsr0;
77
+ }
78
+
79
+ public function getFallbackDirsPsr4()
80
+ {
81
+ return $this->fallbackDirsPsr4;
82
+ }
83
+
84
+ public function getClassMap()
85
+ {
86
+ return $this->classMap;
87
+ }
88
+
89
+ /**
90
+ * @param array $classMap Class to filename map
91
+ */
92
+ public function addClassMap(array $classMap)
93
+ {
94
+ if ($this->classMap) {
95
+ $this->classMap = array_merge($this->classMap, $classMap);
96
+ } else {
97
+ $this->classMap = $classMap;
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Registers a set of PSR-0 directories for a given prefix, either
103
+ * appending or prepending to the ones previously set for this prefix.
104
+ *
105
+ * @param string $prefix The prefix
106
+ * @param array|string $paths The PSR-0 root directories
107
+ * @param bool $prepend Whether to prepend the directories
108
+ */
109
+ public function add($prefix, $paths, $prepend = false)
110
+ {
111
+ if (!$prefix) {
112
+ if ($prepend) {
113
+ $this->fallbackDirsPsr0 = array_merge(
114
+ (array) $paths,
115
+ $this->fallbackDirsPsr0
116
+ );
117
+ } else {
118
+ $this->fallbackDirsPsr0 = array_merge(
119
+ $this->fallbackDirsPsr0,
120
+ (array) $paths
121
+ );
122
+ }
123
+
124
+ return;
125
+ }
126
+
127
+ $first = $prefix[0];
128
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
129
+ $this->prefixesPsr0[$first][$prefix] = (array) $paths;
130
+
131
+ return;
132
+ }
133
+ if ($prepend) {
134
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
135
+ (array) $paths,
136
+ $this->prefixesPsr0[$first][$prefix]
137
+ );
138
+ } else {
139
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
140
+ $this->prefixesPsr0[$first][$prefix],
141
+ (array) $paths
142
+ );
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Registers a set of PSR-4 directories for a given namespace, either
148
+ * appending or prepending to the ones previously set for this namespace.
149
+ *
150
+ * @param string $prefix The prefix/namespace, with trailing '\\'
151
+ * @param array|string $paths The PSR-4 base directories
152
+ * @param bool $prepend Whether to prepend the directories
153
+ *
154
+ * @throws \InvalidArgumentException
155
+ */
156
+ public function addPsr4($prefix, $paths, $prepend = false)
157
+ {
158
+ if (!$prefix) {
159
+ // Register directories for the root namespace.
160
+ if ($prepend) {
161
+ $this->fallbackDirsPsr4 = array_merge(
162
+ (array) $paths,
163
+ $this->fallbackDirsPsr4
164
+ );
165
+ } else {
166
+ $this->fallbackDirsPsr4 = array_merge(
167
+ $this->fallbackDirsPsr4,
168
+ (array) $paths
169
+ );
170
+ }
171
+ } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
172
+ // Register directories for a new namespace.
173
+ $length = strlen($prefix);
174
+ if ('\\' !== $prefix[$length - 1]) {
175
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
176
+ }
177
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
178
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
179
+ } elseif ($prepend) {
180
+ // Prepend directories for an already registered namespace.
181
+ $this->prefixDirsPsr4[$prefix] = array_merge(
182
+ (array) $paths,
183
+ $this->prefixDirsPsr4[$prefix]
184
+ );
185
+ } else {
186
+ // Append directories for an already registered namespace.
187
+ $this->prefixDirsPsr4[$prefix] = array_merge(
188
+ $this->prefixDirsPsr4[$prefix],
189
+ (array) $paths
190
+ );
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Registers a set of PSR-0 directories for a given prefix,
196
+ * replacing any others previously set for this prefix.
197
+ *
198
+ * @param string $prefix The prefix
199
+ * @param array|string $paths The PSR-0 base directories
200
+ */
201
+ public function set($prefix, $paths)
202
+ {
203
+ if (!$prefix) {
204
+ $this->fallbackDirsPsr0 = (array) $paths;
205
+ } else {
206
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Registers a set of PSR-4 directories for a given namespace,
212
+ * replacing any others previously set for this namespace.
213
+ *
214
+ * @param string $prefix The prefix/namespace, with trailing '\\'
215
+ * @param array|string $paths The PSR-4 base directories
216
+ *
217
+ * @throws \InvalidArgumentException
218
+ */
219
+ public function setPsr4($prefix, $paths)
220
+ {
221
+ if (!$prefix) {
222
+ $this->fallbackDirsPsr4 = (array) $paths;
223
+ } else {
224
+ $length = strlen($prefix);
225
+ if ('\\' !== $prefix[$length - 1]) {
226
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
227
+ }
228
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
229
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Turns on searching the include path for class files.
235
+ *
236
+ * @param bool $useIncludePath
237
+ */
238
+ public function setUseIncludePath($useIncludePath)
239
+ {
240
+ $this->useIncludePath = $useIncludePath;
241
+ }
242
+
243
+ /**
244
+ * Can be used to check if the autoloader uses the include path to check
245
+ * for classes.
246
+ *
247
+ * @return bool
248
+ */
249
+ public function getUseIncludePath()
250
+ {
251
+ return $this->useIncludePath;
252
+ }
253
+
254
+ /**
255
+ * Turns off searching the prefix and fallback directories for classes
256
+ * that have not been registered with the class map.
257
+ *
258
+ * @param bool $classMapAuthoritative
259
+ */
260
+ public function setClassMapAuthoritative($classMapAuthoritative)
261
+ {
262
+ $this->classMapAuthoritative = $classMapAuthoritative;
263
+ }
264
+
265
+ /**
266
+ * Should class lookup fail if not found in the current class map?
267
+ *
268
+ * @return bool
269
+ */
270
+ public function isClassMapAuthoritative()
271
+ {
272
+ return $this->classMapAuthoritative;
273
+ }
274
+
275
+ /**
276
+ * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
277
+ *
278
+ * @param string|null $apcuPrefix
279
+ */
280
+ public function setApcuPrefix($apcuPrefix)
281
+ {
282
+ $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
283
+ }
284
+
285
+ /**
286
+ * The APCu prefix in use, or null if APCu caching is not enabled.
287
+ *
288
+ * @return string|null
289
+ */
290
+ public function getApcuPrefix()
291
+ {
292
+ return $this->apcuPrefix;
293
+ }
294
+
295
+ /**
296
+ * Registers this instance as an autoloader.
297
+ *
298
+ * @param bool $prepend Whether to prepend the autoloader or not
299
+ */
300
+ public function register($prepend = false)
301
+ {
302
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
303
+ }
304
+
305
+ /**
306
+ * Unregisters this instance as an autoloader.
307
+ */
308
+ public function unregister()
309
+ {
310
+ spl_autoload_unregister(array($this, 'loadClass'));
311
+ }
312
+
313
+ /**
314
+ * Loads the given class or interface.
315
+ *
316
+ * @param string $class The name of the class
317
+ * @return bool|null True if loaded, null otherwise
318
+ */
319
+ public function loadClass($class)
320
+ {
321
+ if ($file = $this->findFile($class)) {
322
+ includeFile($file);
323
+
324
+ return true;
325
+ }
326
+ }
327
+
328
+ /**
329
+ * Finds the path to the file where the class is defined.
330
+ *
331
+ * @param string $class The name of the class
332
+ *
333
+ * @return string|false The path if found, false otherwise
334
+ */
335
+ public function findFile($class)
336
+ {
337
+ // class map lookup
338
+ if (isset($this->classMap[$class])) {
339
+ return $this->classMap[$class];
340
+ }
341
+ if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
342
+ return false;
343
+ }
344
+ if (null !== $this->apcuPrefix) {
345
+ $file = apcu_fetch($this->apcuPrefix.$class, $hit);
346
+ if ($hit) {
347
+ return $file;
348
+ }
349
+ }
350
+
351
+ $file = $this->findFileWithExtension($class, '.php');
352
+
353
+ // Search for Hack files if we are running on HHVM
354
+ if (false === $file && defined('HHVM_VERSION')) {
355
+ $file = $this->findFileWithExtension($class, '.hh');
356
+ }
357
+
358
+ if (null !== $this->apcuPrefix) {
359
+ apcu_add($this->apcuPrefix.$class, $file);
360
+ }
361
+
362
+ if (false === $file) {
363
+ // Remember that this class does not exist.
364
+ $this->missingClasses[$class] = true;
365
+ }
366
+
367
+ return $file;
368
+ }
369
+
370
+ private function findFileWithExtension($class, $ext)
371
+ {
372
+ // PSR-4 lookup
373
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
374
+
375
+ $first = $class[0];
376
+ if (isset($this->prefixLengthsPsr4[$first])) {
377
+ $subPath = $class;
378
+ while (false !== $lastPos = strrpos($subPath, '\\')) {
379
+ $subPath = substr($subPath, 0, $lastPos);
380
+ $search = $subPath.'\\';
381
+ if (isset($this->prefixDirsPsr4[$search])) {
382
+ $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
383
+ foreach ($this->prefixDirsPsr4[$search] as $dir) {
384
+ if (file_exists($file = $dir . $pathEnd)) {
385
+ return $file;
386
+ }
387
+ }
388
+ }
389
+ }
390
+ }
391
+
392
+ // PSR-4 fallback dirs
393
+ foreach ($this->fallbackDirsPsr4 as $dir) {
394
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
395
+ return $file;
396
+ }
397
+ }
398
+
399
+ // PSR-0 lookup
400
+ if (false !== $pos = strrpos($class, '\\')) {
401
+ // namespaced class name
402
+ $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
403
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
404
+ } else {
405
+ // PEAR-like class name
406
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
407
+ }
408
+
409
+ if (isset($this->prefixesPsr0[$first])) {
410
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
411
+ if (0 === strpos($class, $prefix)) {
412
+ foreach ($dirs as $dir) {
413
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
414
+ return $file;
415
+ }
416
+ }
417
+ }
418
+ }
419
+ }
420
+
421
+ // PSR-0 fallback dirs
422
+ foreach ($this->fallbackDirsPsr0 as $dir) {
423
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
424
+ return $file;
425
+ }
426
+ }
427
+
428
+ // PSR-0 include paths.
429
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
430
+ return $file;
431
+ }
432
+
433
+ return false;
434
+ }
435
+ }
436
+
437
+ /**
438
+ * Scope isolated include.
439
+ *
440
+ * Prevents access to $this/self from included files.
441
+ */
442
+ function includeFile($file)
443
+ {
444
+ include $file;
445
+ }
vendor/composer/LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ Copyright (c) Nils Adermann, Jordi Boggiano
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is furnished
9
+ to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ THE SOFTWARE.
21
+
vendor/composer/autoload_classmap.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_classmap.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ );
vendor/composer/autoload_files.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_files.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ 'f15d016d70663d5e96ccd2b863511eb8' => $vendorDir . '/cbschuld/browser.php/lib/Browser.php',
10
+ 'decc78cc4436b1292c6c0d151b19445c' => $vendorDir . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
11
+ );
vendor/composer/autoload_namespaces.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_namespaces.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ );
vendor/composer/autoload_psr4.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_psr4.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ 'phpseclib\\' => array($vendorDir . '/phpseclib/phpseclib/phpseclib'),
10
+ 'Ifsnop\\' => array($vendorDir . '/ifsnop/mysqldump-php/src/Ifsnop'),
11
+ 'Boldgrid\\Library\\Util\\' => array($vendorDir . '/boldgrid/library/src/Util'),
12
+ );
vendor/composer/autoload_real.php ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_real.php @generated by Composer
4
+
5
+ class ComposerAutoloaderInitf39823383f9fe9e7b3db616106f32896
6
+ {
7
+ private static $loader;
8
+
9
+ public static function loadClassLoader($class)
10
+ {
11
+ if ('Composer\Autoload\ClassLoader' === $class) {
12
+ require __DIR__ . '/ClassLoader.php';
13
+ }
14
+ }
15
+
16
+ public static function getLoader()
17
+ {
18
+ if (null !== self::$loader) {
19
+ return self::$loader;
20
+ }
21
+
22
+ spl_autoload_register(array('ComposerAutoloaderInitf39823383f9fe9e7b3db616106f32896', 'loadClassLoader'), true, true);
23
+ self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
+ spl_autoload_unregister(array('ComposerAutoloaderInitf39823383f9fe9e7b3db616106f32896', 'loadClassLoader'));
25
+
26
+ $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
27
+ if ($useStaticLoader) {
28
+ require_once __DIR__ . '/autoload_static.php';
29
+
30
+ call_user_func(\Composer\Autoload\ComposerStaticInitf39823383f9fe9e7b3db616106f32896::getInitializer($loader));
31
+ } else {
32
+ $map = require __DIR__ . '/autoload_namespaces.php';
33
+ foreach ($map as $namespace => $path) {
34
+ $loader->set($namespace, $path);
35
+ }
36
+
37
+ $map = require __DIR__ . '/autoload_psr4.php';
38
+ foreach ($map as $namespace => $path) {
39
+ $loader->setPsr4($namespace, $path);
40
+ }
41
+
42
+ $classMap = require __DIR__ . '/autoload_classmap.php';
43
+ if ($classMap) {
44
+ $loader->addClassMap($classMap);
45
+ }
46
+ }
47
+
48
+ $loader->register(true);
49
+
50
+ if ($useStaticLoader) {
51
+ $includeFiles = Composer\Autoload\ComposerStaticInitf39823383f9fe9e7b3db616106f32896::$files;
52
+ } else {
53
+ $includeFiles = require __DIR__ . '/autoload_files.php';
54
+ }
55
+ foreach ($includeFiles as $fileIdentifier => $file) {
56
+ composerRequiref39823383f9fe9e7b3db616106f32896($fileIdentifier, $file);
57
+ }
58
+
59
+ return $loader;
60
+ }
61
+ }
62
+
63
+ function composerRequiref39823383f9fe9e7b3db616106f32896($fileIdentifier, $file)
64
+ {
65
+ if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
66
+ require $file;
67
+
68
+ $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
69
+ }
70
+ }
vendor/composer/autoload_static.php ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_static.php @generated by Composer
4
+
5
+ namespace Composer\Autoload;
6
+
7
+ class ComposerStaticInitf39823383f9fe9e7b3db616106f32896
8
+ {
9
+ public static $files = array (
10
+ 'f15d016d70663d5e96ccd2b863511eb8' => __DIR__ . '/..' . '/cbschuld/browser.php/lib/Browser.php',
11
+ 'decc78cc4436b1292c6c0d151b19445c' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
12
+ );
13
+
14
+ public static $prefixLengthsPsr4 = array (
15
+ 'p' =>
16
+ array (
17
+ 'phpseclib\\' => 10,
18
+ ),
19
+ 'I' =>
20
+ array (
21
+ 'Ifsnop\\' => 7,
22
+ ),
23
+ 'B' =>
24
+ array (
25
+ 'Boldgrid\\Library\\Util\\' => 22,
26
+ ),
27
+ );
28
+
29
+ public static $prefixDirsPsr4 = array (
30
+ 'phpseclib\\' =>
31
+ array (
32
+ 0 => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib',
33
+ ),
34
+ 'Ifsnop\\' =>
35
+ array (
36
+ 0 => __DIR__ . '/..' . '/ifsnop/mysqldump-php/src/Ifsnop',
37
+ ),
38
+ 'Boldgrid\\Library\\Util\\' =>
39
+ array (
40
+ 0 => __DIR__ . '/..' . '/boldgrid/library/src/Util',
41
+ ),
42
+ );
43
+
44
+ public static function getInitializer(ClassLoader $loader)
45
+ {
46
+ return \Closure::bind(function () use ($loader) {
47
+ $loader->prefixLengthsPsr4 = ComposerStaticInitf39823383f9fe9e7b3db616106f32896::$prefixLengthsPsr4;
48
+ $loader->prefixDirsPsr4 = ComposerStaticInitf39823383f9fe9e7b3db616106f32896::$prefixDirsPsr4;
49
+
50
+ }, null, ClassLoader::class);
51
+ }
52
+ }
vendor/composer/installed.json ADDED
@@ -0,0 +1,248 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "name": "boldgrid/library",
4
+ "version": "2.3.3",
5
+ "version_normalized": "2.3.3.0",
6
+ "source": {
7
+ "type": "git",
8
+ "url": "https://github.com/BoldGrid/library.git",
9
+ "reference": "fa13062b0812ecb9c913f8ac47b619211b2699de"
10
+ },
11
+ "dist": {
12
+ "type": "zip",
13
+ "url": "https://api.github.com/repos/BoldGrid/library/zipball/fa13062b0812ecb9c913f8ac47b619211b2699de",
14
+ "reference": "fa13062b0812ecb9c913f8ac47b619211b2699de",
15
+ "shasum": ""
16
+ },
17
+ "time": "2018-05-08T13:27:34+00:00",
18
+ "type": "library",
19
+ "installation-source": "dist",
20
+ "autoload": {
21
+ "psr-4": {
22
+ "Boldgrid\\Library\\Util\\": "src/Util"
23
+ }
24
+ },
25
+ "notification-url": "https://packagist.org/downloads/",
26
+ "license": [
27
+ "GPL-2.0-or-later"
28
+ ],
29
+ "authors": [
30
+ {
31
+ "name": "Tim Elsass",
32
+ "email": "dev@tim.ph",
33
+ "homepage": "http://tim.ph",
34
+ "role": "Developer"
35
+ },
36
+ {
37
+ "name": "Rafael Ramos",
38
+ "homepage": "http://rafael-ramos.com",
39
+ "role": "Developer"
40
+ },
41
+ {
42
+ "name": "bwmarkle",
43
+ "role": "Developer"
44
+ },
45
+ {
46
+ "name": "Joe C",
47
+ "email": "joec@boldgrid.com",
48
+ "role": "Developer"
49
+ }
50
+ ],
51
+ "description": "The BoldGrid Library for shared code used in official BoldGrid plugins and themes."
52
+ },
53
+ {
54
+ "name": "cbschuld/browser.php",
55
+ "version": "dev-master",
56
+ "version_normalized": "9999999-dev",
57
+ "source": {
58
+ "type": "git",
59
+ "url": "https://github.com/cbschuld/Browser.php.git",
60
+ "reference": "6bde9efb0b14a4917affe741500c29149419e491"
61
+ },
62
+ "dist": {
63
+ "type": "zip",
64
+ "url": "https://api.github.com/repos/cbschuld/Browser.php/zipball/6bde9efb0b14a4917affe741500c29149419e491",
65
+ "reference": "6bde9efb0b14a4917affe741500c29149419e491",
66
+ "shasum": ""
67
+ },
68
+ "time": "2017-07-31T23:43:17+00:00",
69
+ "type": "library",
70
+ "extra": {
71
+ "branch-alias": {
72
+ "dev-master": "1.95-dev"
73
+ }
74
+ },
75
+ "installation-source": "source",
76
+ "autoload": {
77
+ "files": [
78
+ "lib/Browser.php"
79
+ ]
80
+ },
81
+ "notification-url": "https://packagist.org/downloads/",
82
+ "license": [
83
+ "MIT"
84
+ ],
85
+ "authors": [
86
+ {
87
+ "name": "Chris Schuld",
88
+ "email": "chris@chrisschuld.com",
89
+ "homepage": "http://chrisschuld.com"
90
+ }
91
+ ],
92
+ "description": "A PHP Class to detect a user's Browser",
93
+ "homepage": "http://chrisschuld.com/projects/browser-php-detecting-a-users-browser-from-php.html",
94
+ "keywords": [
95
+ "browser",
96
+ "detection",
97
+ "user agent"
98
+ ]
99
+ },
100
+ {
101
+ "name": "ifsnop/mysqldump-php",
102
+ "version": "v2.4",
103
+ "version_normalized": "2.4.0.0",
104
+ "source": {
105
+ "type": "git",
106
+ "url": "https://github.com/ifsnop/mysqldump-php.git",
107
+ "reference": "2a633b3da5db1e5bdc39c1b71c697ef197c39eeb"
108
+ },
109
+ "dist": {
110
+ "type": "zip",
111
+ "url": "https://api.github.com/repos/ifsnop/mysqldump-php/zipball/2a633b3da5db1e5bdc39c1b71c697ef197c39eeb",
112
+ "reference": "2a633b3da5db1e5bdc39c1b71c697ef197c39eeb",
113
+ "shasum": ""
114
+ },
115
+ "require": {
116
+ "php": ">=5.3.0"
117
+ },
118
+ "require-dev": {
119
+ "phpunit/phpunit": "3.7.*",
120
+ "squizlabs/php_codesniffer": "1.*"
121
+ },
122
+ "time": "2018-03-07T22:27:23+00:00",
123
+ "type": "library",
124
+ "installation-source": "dist",
125
+ "autoload": {
126
+ "psr-4": {
127
+ "Ifsnop\\": "src/Ifsnop/"
128
+ }
129
+ },
130
+ "notification-url": "https://packagist.org/downloads/",
131
+ "license": [
132
+ "MIT"
133
+ ],
134
+ "authors": [
135
+ {
136
+ "name": "Diego Torres",
137
+ "homepage": "https://github.com/ifsnop",
138
+ "role": "Developer"
139
+ }
140
+ ],
141
+ "description": "This is a php version of linux's mysqldump in terminal \"$ mysqldump -u username -p...\"",
142
+ "homepage": "https://github.com/ifsnop/mysqldump-php",
143
+ "keywords": [
144
+ "backup",
145
+ "database",
146
+ "dump",
147
+ "export",
148
+ "mysql",
149
+ "mysqldump",
150
+ "pdo",
151
+ "sqlite"
152
+ ]
153
+ },
154
+ {
155
+ "name": "phpseclib/phpseclib",
156
+ "version": "2.0.11",
157
+ "version_normalized": "2.0.11.0",
158
+ "source": {
159
+ "type": "git",
160
+ "url": "https://github.com/phpseclib/phpseclib.git",
161
+ "reference": "7053f06f91b3de78e143d430e55a8f7889efc08b"
162
+ },
163
+ "dist": {
164
+ "type": "zip",
165
+ "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/7053f06f91b3de78e143d430e55a8f7889efc08b",
166
+ "reference": "7053f06f91b3de78e143d430e55a8f7889efc08b",
167
+ "shasum": ""
168
+ },
169
+ "require": {
170
+ "php": ">=5.3.3"
171
+ },
172
+ "require-dev": {
173
+ "phing/phing": "~2.7",
174
+ "phpunit/phpunit": "^4.8.35|^5.7|^6.0",
175
+ "sami/sami": "~2.0",
176
+ "squizlabs/php_codesniffer": "~2.0"
177
+ },
178
+ "suggest": {
179
+ "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.",
180
+ "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.",
181
+ "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.",
182
+ "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations."
183
+ },
184
+ "time": "2018-04-15T16:55:05+00:00",
185
+ "type": "library",
186
+ "installation-source": "dist",
187
+ "autoload": {
188
+ "files": [
189
+ "phpseclib/bootstrap.php"
190
+ ],
191
+ "psr-4": {
192
+ "phpseclib\\": "phpseclib/"
193
+ }
194
+ },
195
+ "notification-url": "https://packagist.org/downloads/",
196
+ "license": [
197
+ "MIT"
198
+ ],
199
+ "authors": [
200
+ {
201
+ "name": "Jim Wigginton",
202
+ "email": "terrafrost@php.net",
203
+ "role": "Lead Developer"
204
+ },
205
+ {
206
+ "name": "Patrick Monnerat",
207
+ "email": "pm@datasphere.ch",
208
+ "role": "Developer"
209
+ },
210
+ {
211
+ "name": "Andreas Fischer",
212
+ "email": "bantu@phpbb.com",
213
+ "role": "Developer"
214
+ },
215
+ {
216
+ "name": "Hans-Jürgen Petrich",
217
+ "email": "petrich@tronic-media.com",
218
+ "role": "Developer"
219
+ },
220
+ {
221
+ "name": "Graham Campbell",
222
+ "email": "graham@alt-three.com",
223
+ "role": "Developer"
224
+ }
225
+ ],
226
+ "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.",
227
+ "homepage": "http://phpseclib.sourceforge.net",
228
+ "keywords": [
229
+ "BigInteger",
230
+ "aes",
231
+ "asn.1",
232
+ "asn1",
233
+ "blowfish",
234
+ "crypto",
235
+ "cryptography",
236
+ "encryption",
237
+ "rsa",
238
+ "security",
239
+ "sftp",
240
+ "signature",
241
+ "signing",
242
+ "ssh",
243
+ "twofish",
244
+ "x.509",
245
+ "x509"
246
+ ]
247
+ }
248
+ ]
vendor/ifsnop/mysqldump-php/.gitignore ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ # editors
2
+ /.project
3
+ /.settings
4
+ # Vim swap files
5
+ .*.sw*
6
+
7
+ /composer.lock
8
+ /composer.phar
9
+ /vendor/
vendor/ifsnop/mysqldump-php/.scrutinizer.yml ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ before_commands:
2
+ - 'composer install --dev --prefer-source'
3
+
4
+ checks:
5
+ php: true
6
+
7
+ filter:
8
+ excluded_paths: [vendor/*, tests/*]
9
+
10
+ tools:
11
+ php_mess_detector:
12
+ enabled: true
13
+ config:
14
+ controversial_rules:
15
+ camel_case_method_name: false
16
+
17
+ sensiolabs_security_checker: true
18
+
19
+ # Analyzes the size and structure of a PHP project.
20
+ php_pdepend: true
21
+ php_loc:
22
+ enabled: true
23
+
24
+ # We recommend to use PHP Code Similarity Analyzer instead as it is
25
+ # robust against code modifications and provides better targets for
26
+ # refactoring.
27
+ php_cpd: false
28
+ php_sim: true
29
+
30
+ # PHP Analyzer is our own analysis tool for PHP code. It’s latest
31
+ # version is exclusively available through the hosted version on
32
+ # scrutinizer-ci.com.
33
+ php_analyzer: true
34
+ php_code_sniffer:
35
+ enabled: true
36
+ config:
37
+ standard: "PSR1"
38
+ sniffs:
39
+ generic:
40
+ naming_conventions:
41
+ camel_caps_function_name_sniff: false
42
+ files:
43
+ one_class_per_file_sniff: false
vendor/ifsnop/mysqldump-php/.travis.yml ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ dist: trusty
2
+ sudo: required
3
+ group: deprecated-2017Q4
4
+
5
+ language: php
6
+
7
+ php:
8
+ - 7.2
9
+ - 7.1
10
+ - 7.0
11
+ - 5.6
12
+ - 5.5
13
+ - 5.4
14
+ # - 5.3
15
+ - hhvm
16
+ - nightly
17
+
18
+ matrix:
19
+ allow_failures:
20
+ - php: nightly
21
+ - php: hhvm
22
+
23
+ services:
24
+ - mysql
25
+
26
+ before_script:
27
+ - curl -s http://getcomposer.org/installer | php
28
+ - php composer.phar install
29
+ - sudo service mysql stop || echo "mysql not stopped"
30
+ - echo mysql-apt-config mysql-apt-config/select-server select mysql-5.7 | sudo debconf-set-selections
31
+ - wget http://dev.mysql.com/get/mysql-apt-config_0.7.3-1_all.deb
32
+ - sudo dpkg --install mysql-apt-config_0.7.3-1_all.deb
33
+ - sudo apt-get update -q
34
+ - sudo apt-get install -q -y --force-yes -o Dpkg::Options::=--force-confnew mysql-server
35
+ - sudo mysql_upgrade
36
+ - sudo service mysql stop || echo "mysql not stopped"
37
+ - sudo mysqld_safe --skip-grant-tables &
38
+ - sleep 4
39
+ - sudo mysql -e "use mysql; update user set authentication_string=PASSWORD('') where User='root'; update user set plugin='mysql_native_password';FLUSH PRIVILEGES;"
40
+ - sudo mysqladmin shutdown
41
+ - sleep 1
42
+ - sudo service mysql start
43
+ - mysql -V
44
+ - tests/create_users.sh
45
+
46
+ script:
47
+ - php -l src/Ifsnop/Mysqldump/Mysqldump.php
48
+ - php src/Ifsnop/Mysqldump/Mysqldump.php
49
+ - cd tests && ./test.sh
vendor/ifsnop/mysqldump-php/LICENSE ADDED
@@ -0,0 +1,674 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
3
+
4
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
5
+ Everyone is permitted to copy and distribute verbatim copies
6
+ of this license document, but changing it is not allowed.
7
+
8
+ Preamble
9
+
10
+ The GNU General Public License is a free, copyleft license for
11
+ software and other kinds of works.
12
+
13
+ The licenses for most software and other practical works are designed
14
+ to take away your freedom to share and change the works. By contrast,
15
+ the GNU General Public License is intended to guarantee your freedom to
16
+ share and change all versions of a program--to make sure it remains free
17
+ software for all its users. We, the Free Software Foundation, use the
18
+ GNU General Public License for most of our software; it applies also to
19
+ any other work released this way by its authors. You can apply it to
20
+ your programs, too.
21
+
22
+ When we speak of free software, we are referring to freedom, not
23
+ price. Our General Public Licenses are designed to make sure that you
24
+ have the freedom to distribute copies of free software (and charge for
25
+ them if you wish), that you receive source code or can get it if you
26
+ want it, that you can change the software or use pieces of it in new
27
+ free programs, and that you know you can do these things.
28
+
29
+ To protect your rights, we need to prevent others from denying you
30
+ these rights or asking you to surrender the rights. Therefore, you have
31
+ certain responsibilities if you distribute copies of the software, or if
32
+ you modify it: responsibilities to respect the freedom of others.
33
+
34
+ For example, if you distribute copies of such a program, whether
35
+ gratis or for a fee, you must pass on to the recipients the same
36
+ freedoms that you received. You must make sure that they, too, receive
37
+ or can get the source code. And you must show them these terms so they
38
+ know their rights.
39
+
40
+ Developers that use the GNU GPL protect your rights with two steps:
41
+ (1) assert copyright on the software, and (2) offer you this License
42
+ giving you legal permission to copy, distribute and/or modify it.
43
+
44
+ For the developers' and authors' protection, the GPL clearly explains
45
+ that there is no warranty for this free software. For both users' and
46
+ authors' sake, the GPL requires that modified versions be marked as
47
+ changed, so that their problems will not be attributed erroneously to
48
+ authors of previous versions.
49
+
50
+ Some devices are designed to deny users access to install or run
51
+ modified versions of the software inside them, although the manufacturer
52
+ can do so. This is fundamentally incompatible with the aim of
53
+ protecting users' freedom to change the software. The systematic
54
+ pattern of such abuse occurs in the area of products for individuals to
55
+ use, which is precisely where it is most unacceptable. Therefore, we
56
+ have designed this version of the GPL to prohibit the practice for those
57
+ products. If such problems arise substantially in other domains, we
58
+ stand ready to extend this provision to those domains in future versions
59
+ of the GPL, as needed to protect the freedom of users.
60
+
61
+ Finally, every program is threatened constantly by software patents.
62
+ States should not allow patents to restrict development and use of
63
+ software on general-purpose computers, but in those that do, we wish to
64
+ avoid the special danger that patents applied to a free program could
65
+ make it effectively proprietary. To prevent this, the GPL assures that
66
+ patents cannot be used to render the program non-free.
67
+
68
+ The precise terms and conditions for copying, distribution and
69
+ modification follow.
70
+
71
+ TERMS AND CONDITIONS
72
+
73
+ 0. Definitions.
74
+
75
+ "This License" refers to version 3 of the GNU General Public License.
76
+
77
+ "Copyright" also means copyright-like laws that apply to other kinds of
78
+ works, such as semiconductor masks.
79
+
80
+ "The Program" refers to any copyrightable work licensed under this
81
+ License. Each licensee is addressed as "you". "Licensees" and
82
+ "recipients" may be individuals or organizations.
83
+
84
+ To "modify" a work means to copy from or adapt all or part of the work
85
+ in a fashion requiring copyright permission, other than the making of an
86
+ exact copy. The resulting work is called a "modified version" of the
87
+ earlier work or a work "based on" the earlier work.
88
+
89
+ A "covered work" means either the unmodified Program or a work based
90
+ on the Program.
91
+
92
+ To "propagate" a work means to do anything with it that, without
93
+ permission, would make you directly or secondarily liable for
94
+ infringement under applicable copyright law, except executing it on a
95
+ computer or modifying a private copy. Propagation includes copying,
96
+ distribution (with or without modification), making available to the
97
+ public, and in some countries other activities as well.
98
+
99
+ To "convey" a work means any kind of propagation that enables other
100
+ parties to make or receive copies. Mere interaction with a user through
101
+ a computer network, with no transfer of a copy, is not conveying.
102
+
103
+ An interactive user interface displays "Appropriate Legal Notices"
104
+ to the extent that it includes a convenient and prominently visible
105
+ feature that (1) displays an appropriate copyright notice, and (2)
106
+ tells the user that there is no warranty for the work (except to the
107
+ extent that warranties are provided), that licensees may convey the
108
+ work under this License, and how to view a copy of this License. If
109
+ the interface presents a list of user commands or options, such as a
110
+ menu, a prominent item in the list meets this criterion.
111
+
112
+ 1. Source Code.
113
+
114
+ The "source code" for a work means the preferred form of the work
115
+ for making modifications to it. "Object code" means any non-source
116
+ form of a work.
117
+
118
+ A "Standard Interface" means an interface that either is an official
119
+ standard defined by a recognized standards body, or, in the case of
120
+ interfaces specified for a particular programming language, one that
121
+ is widely used among developers working in that language.
122
+
123
+ The "System Libraries" of an executable work include anything, other
124
+ than the work as a whole, that (a) is included in the normal form of
125
+ packaging a Major Component, but which is not part of that Major
126
+ Component, and (b) serves only to enable use of the work with that
127
+ Major Component, or to implement a Standard Interface for which an
128
+ implementation is available to the public in source code form. A
129
+ "Major Component", in this context, means a major essential component
130
+ (kernel, window system, and so on) of the specific operating system
131
+ (if any) on which the executable work runs, or a compiler used to
132
+ produce the work, or an object code interpreter used to run it.
133
+
134
+ The "Corresponding Source" for a work in object code form means all
135
+ the source code needed to generate, install, and (for an executable
136
+ work) run the object code and to modify the work, including scripts to
137
+ control those activities. However, it does not include the work's
138
+ System Libraries, or general-purpose tools or generally available free
139
+ programs which are used unmodified in performing those activities but
140
+ which are not part of the work. For example, Corresponding Source
141
+ includes interface definition files associated with source files for
142
+ the work, and the source code for shared libraries and dynamically
143
+ linked subprograms that the work is specifically designed to require,
144
+ such as by intimate data communication or control flow between those
145
+ subprograms and other parts of the work.
146
+
147
+ The Corresponding Source need not include anything that users
148
+ can regenerate automatically from other parts of the Corresponding
149
+ Source.
150
+
151
+ The Corresponding Source for a work in source code form is that
152
+ same work.
153
+
154
+ 2. Basic Permissions.
155
+
156
+ All rights granted under this License are granted for the term of
157
+ copyright on the Program, and are irrevocable provided the stated
158
+ conditions are met. This License explicitly affirms your unlimited
159
+ permission to run the unmodified Program. The output from running a
160
+ covered work is covered by this License only if the output, given its
161
+ content, constitutes a covered work. This License acknowledges your
162
+ rights of fair use or other equivalent, as provided by copyright law.
163
+
164
+ You may make, run and propagate covered works that you do not
165
+ convey, without conditions so long as your license otherwise remains
166
+ in force. You may convey covered works to others for the sole purpose
167
+ of having them make modifications exclusively for you, or provide you
168
+ with facilities for running those works, provided that you comply with
169
+ the terms of this License in conveying all material for which you do
170
+ not control copyright. Those thus making or running the covered works
171
+ for you must do so exclusively on your behalf, under your direction
172
+ and control, on terms that prohibit them from making any copies of
173
+ your copyrighted material outside their relationship with you.
174
+
175
+ Conveying under any other circumstances is permitted solely under
176
+ the conditions stated below. Sublicensing is not allowed; section 10
177
+ makes it unnecessary.
178
+
179
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180
+
181
+ No covered work shall be deemed part of an effective technological
182
+ measure under any applicable law fulfilling obligations under article
183
+ 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184
+ similar laws prohibiting or restricting circumvention of such
185
+ measures.
186
+
187
+ When you convey a covered work, you waive any legal power to forbid
188
+ circumvention of technological measures to the extent such circumvention
189
+ is effected by exercising rights under this License with respect to
190
+ the covered work, and you disclaim any intention to limit operation or
191
+ modification of the work as a means of enforcing, against the work's
192
+ users, your or third parties' legal rights to forbid circumvention of
193
+ technological measures.
194
+
195
+ 4. Conveying Verbatim Copies.
196
+
197
+ You may convey verbatim copies of the Program's source code as you
198
+ receive it, in any medium, provided that you conspicuously and
199
+ appropriately publish on each copy an appropriate copyright notice;
200
+ keep intact all notices stating that this License and any
201
+ non-permissive terms added in accord with section 7 apply to the code;
202
+ keep intact all notices of the absence of any warranty; and give all
203
+ recipients a copy of this License along with the Program.
204
+
205
+ You may charge any price or no price for each copy that you convey,
206
+ and you may offer support or warranty protection for a fee.
207
+
208
+ 5. Conveying Modified Source Versions.
209
+
210
+ You may convey a work based on the Program, or the modifications to
211
+ produce it from the Program, in the form of source code under the
212
+ terms of section 4, provided that you also meet all of these conditions:
213
+
214
+ a) The work must carry prominent notices stating that you modified
215
+ it, and giving a relevant date.
216
+
217
+ b) The work must carry prominent notices stating that it is
218
+ released under this License and any conditions added under section
219
+ 7. This requirement modifies the requirement in section 4 to
220
+ "keep intact all notices".
221
+
222
+ c) You must license the entire work, as a whole, under this
223
+ License to anyone who comes into possession of a copy. This
224
+ License will therefore apply, along with any applicable section 7
225
+ additional terms, to the whole of the work, and all its parts,
226
+ regardless of how they are packaged. This License gives no
227
+ permission to license the work in any other way, but it does not
228
+ invalidate such permission if you have separately received it.
229
+
230
+ d) If the work has interactive user interfaces, each must display
231
+ Appropriate Legal Notices; however, if the Program has interactive
232
+ interfaces that do not display Appropriate Legal Notices, your
233
+ work need not make them do so.
234
+
235
+ A compilation of a covered work with other separate and independent
236
+ works, which are not by their nature extensions of the covered work,
237
+ and which are not combined with it such as to form a larger program,
238
+ in or on a volume of a storage or distribution medium, is called an
239
+ "aggregate" if the compilation and its resulting copyright are not
240
+ used to limit the access or legal rights of the compilation's users
241
+ beyond what the individual works permit. Inclusion of a covered work
242
+ in an aggregate does not cause this License to apply to the other
243
+ parts of the aggregate.
244
+
245
+ 6. Conveying Non-Source Forms.
246
+
247
+ You may convey a covered work in object code form under the terms
248
+ of sections 4 and 5, provided that you also convey the
249
+ machine-readable Corresponding Source under the terms of this License,
250
+ in one of these ways:
251
+
252
+ a) Convey the object code in, or embodied in, a physical product
253
+ (including a physical distribution medium), accompanied by the
254
+ Corresponding Source fixed on a durable physical medium
255
+ customarily used for software interchange.
256
+
257
+ b) Convey the object code in, or embodied in, a physical product
258
+ (including a physical distribution medium), accompanied by a
259
+ written offer, valid for at least three years and valid for as
260
+ long as you offer spare parts or customer support for that product
261
+ model, to give anyone who possesses the object code either (1) a
262
+ copy of the Corresponding Source for all the software in the
263
+ product that is covered by this License, on a durable physical
264
+ medium customarily used for software interchange, for a price no
265
+ more than your reasonable cost of physically performing this
266
+ conveying of source, or (2) access to copy the
267
+ Corresponding Source from a network server at no charge.
268
+
269
+ c) Convey individual copies of the object code with a copy of the
270
+ written offer to provide the Corresponding Source. This
271
+ alternative is allowed only occasionally and noncommercially, and
272
+ only if you received the object code with such an offer, in accord
273
+ with subsection 6b.
274
+
275
+ d) Convey the object code by offering access from a designated
276
+ place (gratis or for a charge), and offer equivalent access to the
277
+ Corresponding Source in the same way through the same place at no
278
+ further charge. You need not require recipients to copy the
279
+ Corresponding Source along with the object code. If the place to
280
+ copy the object code is a network server, the Corresponding Source
281
+ may be on a different server (operated by you or a third party)
282
+ that supports equivalent copying facilities, provided you maintain
283
+ clear directions next to the object code saying where to find the
284
+ Corresponding Source. Regardless of what server hosts the
285
+ Corresponding Source, you remain obligated to ensure that it is
286
+ available for as long as needed to satisfy these requirements.
287
+
288
+ e) Convey the object code using peer-to-peer transmission, provided
289
+ you inform other peers where the object code and Corresponding
290
+ Source of the work are being offered to the general public at no
291
+ charge under subsection 6d.
292
+
293
+ A separable portion of the object code, whose source code is excluded
294
+ from the Corresponding Source as a System Library, need not be
295
+ included in conveying the object code work.
296
+
297
+ A "User Product" is either (1) a "consumer product", which means any
298
+ tangible personal property which is normally used for personal, family,
299
+ or household purposes, or (2) anything designed or sold for incorporation
300
+ into a dwelling. In determining whether a product is a consumer product,
301
+ doubtful cases shall be resolved in favor of coverage. For a particular
302
+ product received by a particular user, "normally used" refers to a
303
+ typical or common use of that class of product, regardless of the status
304
+ of the particular user or of the way in which the particular user
305
+ actually uses, or expects or is expected to use, the product. A product
306
+ is a consumer product regardless of whether the product has substantial
307
+ commercial, industrial or non-consumer uses, unless such uses represent
308
+ the only significant mode of use of the product.
309
+
310
+ "Installation Information" for a User Product means any methods,
311
+ procedures, authorization keys, or other information required to install
312
+ and execute modified versions of a covered work in that User Product from
313
+ a modified version of its Corresponding Source. The information must
314
+ suffice to ensure that the continued functioning of the modified object
315
+ code is in no case prevented or interfered with solely because
316
+ modification has been made.
317
+
318
+ If you convey an object code work under this section in, or with, or
319
+ specifically for use in, a User Product, and the conveying occurs as
320
+ part of a transaction in which the right of possession and use of the
321
+ User Product is transferred to the recipient in perpetuity or for a
322
+ fixed term (regardless of how the transaction is characterized), the
323
+ Corresponding Source conveyed under this section must be accompanied
324
+ by the Installation Information. But this requirement does not apply
325
+ if neither you nor any third party retains the ability to install
326
+ modified object code on the User Product (for example, the work has
327
+ been installed in ROM).
328
+
329
+ The requirement to provide Installation Information does not include a
330
+ requirement to continue to provide support service, warranty, or updates
331
+ for a work that has been modified or installed by the recipient, or for
332
+ the User Product in which it has been modified or installed. Access to a
333
+ network may be denied when the modification itself materially and
334
+ adversely affects the operation of the network or violates the rules and
335
+ protocols for communication across the network.
336
+
337
+ Corresponding Source conveyed, and Installation Information provided,
338
+ in accord with this section must be in a format that is publicly
339
+ documented (and with an implementation available to the public in
340
+ source code form), and must require no special password or key for
341
+ unpacking, reading or copying.
342
+
343
+ 7. Additional Terms.
344
+
345
+ "Additional permissions" are terms that supplement the terms of this
346
+ License by making exceptions from one or more of its conditions.
347
+ Additional permissions that are applicable to the entire Program shall
348
+ be treated as though they were included in this License, to the extent
349
+ that they are valid under applicable law. If additional permissions
350
+ apply only to part of the Program, that part may be used separately
351
+ under those permissions, but the entire Program remains governed by
352
+ this License without regard to the additional permissions.
353
+
354
+ When you convey a copy of a covered work, you may at your option
355
+ remove any additional permissions from that copy, or from any part of
356
+ it. (Additional permissions may be written to require their own
357
+ removal in certain cases when you modify the work.) You may place
358
+ additional permissions on material, added by you to a covered work,
359
+ for which you have or can give appropriate copyright permission.
360
+
361
+ Notwithstanding any other provision of this License, for material you
362
+ add to a covered work, you may (if authorized by the copyright holders of
363
+ that material) supplement the terms of this License with terms:
364
+
365
+ a) Disclaiming warranty or limiting liability differently from the
366
+ terms of sections 15 and 16 of this License; or
367
+
368
+ b) Requiring preservation of specified reasonable legal notices or
369
+ author attributions in that material or in the Appropriate Legal
370
+ Notices displayed by works containing it; or
371
+
372
+ c) Prohibiting misrepresentation of the origin of that material, or
373
+ requiring that modified versions of such material be marked in
374
+ reasonable ways as different from the original version; or
375
+
376
+ d) Limiting the use for publicity purposes of names of licensors or
377
+ authors of the material; or
378
+
379
+ e) Declining to grant rights under trademark law for use of some
380
+ trade names, trademarks, or service marks; or
381
+
382
+ f) Requiring indemnification of licensors and authors of that
383
+ material by anyone who conveys the material (or modified versions of
384
+ it) with contractual assumptions of liability to the recipient, for
385
+ any liability that these contractual assumptions directly impose on
386
+ those licensors and authors.
387
+
388
+ All other non-permissive additional terms are considered "further
389
+ restrictions" within the meaning of section 10. If the Program as you
390
+ received it, or any part of it, contains a notice stating that it is
391
+ governed by this License along with a term that is a further
392
+ restriction, you may remove that term. If a license document contains
393
+ a further restriction but permits relicensing or conveying under this
394
+ License, you may add to a covered work material governed by the terms
395
+ of that license document, provided that the further restriction does
396
+ not survive such relicensing or conveying.
397
+
398
+ If you add terms to a covered work in accord with this section, you
399
+ must place, in the relevant source files, a statement of the
400
+ additional terms that apply to those files, or a notice indicating
401
+ where to find the applicable terms.
402
+
403
+ Additional terms, permissive or non-permissive, may be stated in the
404
+ form of a separately written license, or stated as exceptions;
405
+ the above requirements apply either way.
406
+
407
+ 8. Termination.
408
+
409
+ You may not propagate or modify a covered work except as expressly
410
+ provided under this License. Any attempt otherwise to propagate or
411
+ modify it is void, and will automatically terminate your rights under
412
+ this License (including any patent licenses granted under the third
413
+ paragraph of section 11).
414
+
415
+ However, if you cease all violation of this License, then your
416
+ license from a particular copyright holder is reinstated (a)
417
+ provisionally, unless and until the copyright holder explicitly and
418
+ finally terminates your license, and (b) permanently, if the copyright
419
+ holder fails to notify you of the violation by some reasonable means
420
+ prior to 60 days after the cessation.
421
+
422
+ Moreover, your license from a particular copyright holder is
423
+ reinstated permanently if the copyright holder notifies you of the
424
+ violation by some reasonable means, this is the first time you have
425
+ received notice of violation of this License (for any work) from that
426
+ copyright holder, and you cure the violation prior to 30 days after
427
+ your receipt of the notice.
428
+
429
+ Termination of your rights under this section does not terminate the
430
+ licenses of parties who have received copies or rights from you under
431
+ this License. If your rights have been terminated and not permanently
432
+ reinstated, you do not qualify to receive new licenses for the same
433
+ material under section 10.
434
+
435
+ 9. Acceptance Not Required for Having Copies.
436
+
437
+ You are not required to accept this License in order to receive or
438
+ run a copy of the Program. Ancillary propagation of a covered work
439
+ occurring solely as a consequence of using peer-to-peer transmission
440
+ to receive a copy likewise does not require acceptance. However,
441
+ nothing other than this License grants you permission to propagate or
442
+ modify any covered work. These actions infringe copyright if you do
443
+ not accept this License. Therefore, by modifying or propagating a
444
+ covered work, you indicate your acceptance of this License to do so.
445
+
446
+ 10. Automatic Licensing of Downstream Recipients.
447
+
448
+ Each time you convey a covered work, the recipient automatically
449
+ receives a license from the original licensors, to run, modify and
450
+ propagate that work, subject to this License. You are not responsible
451
+ for enforcing compliance by third parties with this License.
452
+
453
+ An "entity transaction" is a transaction transferring control of an
454
+ organization, or substantially all assets of one, or subdividing an
455
+ organization, or merging organizations. If propagation of a covered
456
+ work results from an entity transaction, each party to that
457
+ transaction who receives a copy of the work also receives whatever
458
+ licenses to the work the party's predecessor in interest had or could
459
+ give under the previous paragraph, plus a right to possession of the
460
+ Corresponding Source of the work from the predecessor in interest, if
461
+ the predecessor has it or can get it with reasonable efforts.
462
+
463
+ You may not impose any further restrictions on the exercise of the
464
+ rights granted or affirmed under this License. For example, you may
465
+ not impose a license fee, royalty, or other charge for exercise of
466
+ rights granted under this License, and you may not initiate litigation
467
+ (including a cross-claim or counterclaim in a lawsuit) alleging that
468
+ any patent claim is infringed by making, using, selling, offering for
469
+ sale, or importing the Program or any portion of it.
470
+
471
+ 11. Patents.
472
+
473
+ A "contributor" is a copyright holder who authorizes use under this
474
+ License of the Program or a work on which the Program is based. The
475
+ work thus licensed is called the contributor's "contributor version".
476
+
477
+ A contributor's "essential patent claims" are all patent claims
478
+ owned or controlled by the contributor, whether already acquired or
479
+ hereafter acquired, that would be infringed by some manner, permitted
480
+ by this License, of making, using, or selling its contributor version,
481
+ but do not include claims that would be infringed only as a
482
+ consequence of further modification of the contributor version. For
483
+ purposes of this definition, "control" includes the right to grant
484
+ patent sublicenses in a manner consistent with the requirements of
485
+ this License.
486
+
487
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
488
+ patent license under the contributor's essential patent claims, to
489
+ make, use, sell, offer for sale, import and otherwise run, modify and
490
+ propagate the contents of its contributor version.
491
+
492
+ In the following three paragraphs, a "patent license" is any express
493
+ agreement or commitment, however denominated, not to enforce a patent
494
+ (such as an express permission to practice a patent or covenant not to
495
+ sue for patent infringement). To "grant" such a patent license to a
496
+ party means to make such an agreement or commitment not to enforce a
497
+ patent against the party.
498
+
499
+ If you convey a covered work, knowingly relying on a patent license,
500
+ and the Corresponding Source of the work is not available for anyone
501
+ to copy, free of charge and under the terms of this License, through a
502
+ publicly available network server or other readily accessible means,
503
+ then you must either (1) cause the Corresponding Source to be so
504
+ available, or (2) arrange to deprive yourself of the benefit of the
505
+ patent license for this particular work, or (3) arrange, in a manner
506
+ consistent with the requirements of this License, to extend the patent
507
+ license to downstream recipients. "Knowingly relying" means you have
508
+ actual knowledge that, but for the patent license, your conveying the
509
+ covered work in a country, or your recipient's use of the covered work
510
+ in a country, would infringe one or more identifiable patents in that
511
+ country that you have reason to believe are valid.
512
+
513
+ If, pursuant to or in connection with a single transaction or
514
+ arrangement, you convey, or propagate by procuring conveyance of, a
515
+ covered work, and grant a patent license to some of the parties
516
+ receiving the covered work authorizing them to use, propagate, modify
517
+ or convey a specific copy of the covered work, then the patent license
518
+ you grant is automatically extended to all recipients of the covered
519
+ work and works based on it.
520
+
521
+ A patent license is "discriminatory" if it does not include within
522
+ the scope of its coverage, prohibits the exercise of, or is
523
+ conditioned on the non-exercise of one or more of the rights that are
524
+ specifically granted under this License. You may not convey a covered
525
+ work if you are a party to an arrangement with a third party that is
526
+ in the business of distributing software, under which you make payment
527
+ to the third party based on the extent of your activity of conveying
528
+ the work, and under which the third party grants, to any of the
529
+ parties who would receive the covered work from you, a discriminatory
530
+ patent license (a) in connection with copies of the covered work
531
+ conveyed by you (or copies made from those copies), or (b) primarily
532
+ for and in connection with specific products or compilations that
533
+ contain the covered work, unless you entered into that arrangement,
534
+ or that patent license was granted, prior to 28 March 2007.
535
+
536
+ Nothing in this License shall be construed as excluding or limiting
537
+ any implied license or other defenses to infringement that may
538
+ otherwise be available to you under applicable patent law.
539
+
540
+ 12. No Surrender of Others' Freedom.
541
+
542
+ If conditions are imposed on you (whether by court order, agreement or
543
+ otherwise) that contradict the conditions of this License, they do not
544
+ excuse you from the conditions of this License. If you cannot convey a
545
+ covered work so as to satisfy simultaneously your obligations under this
546
+ License and any other pertinent obligations, then as a consequence you may
547
+ not convey it at all. For example, if you agree to terms that obligate you
548
+ to collect a royalty for further conveying from those to whom you convey
549
+ the Program, the only way you could satisfy both those terms and this
550
+ License would be to refrain entirely from conveying the Program.
551
+
552
+ 13. Use with the GNU Affero General Public License.
553
+
554
+ Notwithstanding any other provision of this License, you have
555
+ permission to link or combine any covered work with a work licensed
556
+ under version 3 of the GNU Affero General Public License into a single
557
+ combined work, and to convey the resulting work. The terms of this
558
+ License will continue to apply to the part which is the covered work,
559
+ but the special requirements of the GNU Affero General Public License,
560
+ section 13, concerning interaction through a network will apply to the
561
+ combination as such.
562
+
563
+ 14. Revised Versions of this License.
564
+
565
+ The Free Software Foundation may publish revised and/or new versions of
566
+ the GNU General Public License from time to time. Such new versions will
567
+ be similar in spirit to the present version, but may differ in detail to
568
+ address new problems or concerns.
569
+
570
+ Each version is given a distinguishing version number. If the
571
+ Program specifies that a certain numbered version of the GNU General
572
+ Public License "or any later version" applies to it, you have the
573
+ option of following the terms and conditions either of that numbered
574
+ version or of any later version published by the Free Software
575
+ Foundation. If the Program does not specify a version number of the
576
+ GNU General Public License, you may choose any version ever published
577
+ by the Free Software Foundation.
578
+
579
+ If the Program specifies that a proxy can decide which future
580
+ versions of the GNU General Public License can be used, that proxy's
581
+ public statement of acceptance of a version permanently authorizes you
582
+ to choose that version for the Program.
583
+
584
+ Later license versions may give you additional or different
585
+ permissions. However, no additional obligations are imposed on any
586
+ author or copyright holder as a result of your choosing to follow a
587
+ later version.
588
+
589
+ 15. Disclaimer of Warranty.
590
+
591
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592
+ APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593
+ HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594
+ OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596
+ PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597
+ IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598
+ ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599
+
600
+ 16. Limitation of Liability.
601
+
602
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604
+ THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605
+ GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606
+ USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607
+ DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608
+ PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609
+ EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610
+ SUCH DAMAGES.
611
+
612
+ 17. Interpretation of Sections 15 and 16.
613
+
614
+ If the disclaimer of warranty and limitation of liability provided
615
+ above cannot be given local legal effect according to their terms,
616
+ reviewing courts shall apply local law that most closely approximates
617
+ an absolute waiver of all civil liability in connection with the
618
+ Program, unless a warranty or assumption of liability accompanies a
619
+ copy of the Program in return for a fee.
620
+
621
+ END OF TERMS AND CONDITIONS
622
+
623
+ How to Apply These Terms to Your New Programs
624
+
625
+ If you develop a new program, and you want it to be of the greatest
626
+ possible use to the public, the best way to achieve this is to make it
627
+ free software which everyone can redistribute and change under these terms.
628
+
629
+ To do so, attach the following notices to the p