Version Description
- Updated links
Download this release
Release Info
Developer | nick843 |
Plugin | WP Clone by WP Academy |
Version | 2.2.10 |
Comparing to | |
See all releases |
Code changes from version 2.2.9 to 2.2.10
- analyst/assets/css/customize.css +280 -280
- analyst/assets/index.php +2 -2
- analyst/assets/js/customize.js +29 -29
- analyst/autoload.php +40 -40
- analyst/index.php +2 -2
- analyst/main.php +36 -36
- analyst/sdk_resolver.php +79 -79
- analyst/src/Account/Account.php +584 -584
- analyst/src/Account/AccountData.php +176 -176
- analyst/src/Account/AccountDataFactory.php +125 -125
- analyst/src/Analyst.php +167 -167
- analyst/src/ApiRequestor.php +257 -257
- analyst/src/ApiResponse.php +44 -44
- analyst/src/Cache/DatabaseCache.php +127 -127
- analyst/src/Collector.php +217 -217
- analyst/src/Contracts/AnalystContract.php +12 -12
- analyst/src/Contracts/CacheContract.php +47 -47
- analyst/src/Contracts/HttpClientContract.php +25 -25
- analyst/src/Contracts/RequestContract.php +22 -22
- analyst/src/Contracts/RequestorContract.php +44 -44
- analyst/src/Contracts/TrackerContract.php +69 -69
- analyst/src/Core/AbstractFactory.php +27 -27
- analyst/src/Http/CurlHttpClient.php +102 -102
- analyst/src/Http/DummyHttpClient.php +33 -33
- analyst/src/Http/Requests/AbstractLoggerRequest.php +64 -64
- analyst/src/Http/Requests/ActivateRequest.php +42 -42
- analyst/src/Http/Requests/DeactivateRequest.php +64 -64
- analyst/src/Http/Requests/InstallRequest.php +38 -38
- analyst/src/Http/Requests/OptInRequest.php +42 -42
- analyst/src/Http/Requests/OptOutRequest.php +40 -40
- analyst/src/Http/Requests/UninstallRequest.php +36 -36
- analyst/src/Http/WordPressHttpClient.php +61 -61
- analyst/src/Mutator.php +103 -103
- analyst/src/Notices/Notice.php +121 -121
- analyst/src/Notices/NoticeFactory.php +130 -130
- analyst/src/helpers.php +84 -84
- analyst/templates/forms/deactivate.php +156 -156
- analyst/templates/forms/install.php +113 -113
- analyst/templates/notice.php +10 -10
- analyst/templates/optin.php +60 -60
- analyst/templates/optout.php +109 -109
- analyst/version.php +15 -15
- lib/class.wpc-wpdb.php +145 -145
- lib/css/style.css +299 -299
- lib/functions.php +1516 -1516
- lib/icit_srdb_replacer.php +154 -154
- lib/js/backupmanager.js +353 -353
- lib/js/clipboard.min.js +6 -6
- lib/view.php +285 -285
- readme.txt +186 -182
- wpclone.php +577 -577
analyst/assets/css/customize.css
CHANGED
@@ -1,280 +1,280 @@
|
|
1 |
-
.analyst-action-opt {
|
2 |
-
cursor: pointer;
|
3 |
-
}
|
4 |
-
|
5 |
-
.analyst-modal {
|
6 |
-
color: #000000;
|
7 |
-
display: none;
|
8 |
-
position: fixed;
|
9 |
-
z-index: 1000;
|
10 |
-
padding-top: 100px;
|
11 |
-
left: 0;
|
12 |
-
top: 0;
|
13 |
-
width: 100%;
|
14 |
-
height: 100%;
|
15 |
-
overflow: auto;
|
16 |
-
background-color: rgb(0,0,0);
|
17 |
-
background-color: rgba(0,0,0,0.4);
|
18 |
-
}
|
19 |
-
|
20 |
-
.analyst-modal-content {
|
21 |
-
font-family: Helvetica, serif;
|
22 |
-
position: relative;
|
23 |
-
background-color: #fefefe;
|
24 |
-
margin: auto;
|
25 |
-
padding: 35px 35px 20px;
|
26 |
-
border: 1px solid #F2F2F2;
|
27 |
-
width: 40%;
|
28 |
-
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19);
|
29 |
-
-webkit-animation-name: analyst-animatetop;
|
30 |
-
-webkit-animation-duration: 0.4s;
|
31 |
-
animation-name: analyst-animatetop;
|
32 |
-
animation-duration: 0.4s
|
33 |
-
}
|
34 |
-
|
35 |
-
.analyst-btn-success {
|
36 |
-
cursor: pointer;
|
37 |
-
color: #ffffff;
|
38 |
-
background-color: #00AF5E;
|
39 |
-
border: none;
|
40 |
-
width: 100%;
|
41 |
-
font-size: 18px;
|
42 |
-
padding: 8px;
|
43 |
-
font-weight: bold;
|
44 |
-
}
|
45 |
-
|
46 |
-
.analyst-btn-grey {
|
47 |
-
cursor: pointer;
|
48 |
-
color: #2D2D2D;
|
49 |
-
background-color: #D8D8D8;
|
50 |
-
border: none;
|
51 |
-
width: 100%;
|
52 |
-
font-size: 18px;
|
53 |
-
padding: 8px;
|
54 |
-
font-weight: bold;
|
55 |
-
}
|
56 |
-
|
57 |
-
.analyst-btn-secondary-ghost {
|
58 |
-
cursor: pointer;
|
59 |
-
background: transparent;
|
60 |
-
border: none;
|
61 |
-
color: #898686;
|
62 |
-
font-size: 18px;
|
63 |
-
}
|
64 |
-
|
65 |
-
.analyst-modal-def-top-padding {
|
66 |
-
padding-top: 20px;
|
67 |
-
}
|
68 |
-
|
69 |
-
.analyst-modal-header {
|
70 |
-
font-size: 20px;
|
71 |
-
font-weight: bold;
|
72 |
-
}
|
73 |
-
|
74 |
-
/*INSTALL STYLES*/
|
75 |
-
.analyst-install-footer {
|
76 |
-
padding-top: 10px;
|
77 |
-
text-align: center;
|
78 |
-
}
|
79 |
-
|
80 |
-
.analyst-install-image-block {
|
81 |
-
width: 140px;
|
82 |
-
}
|
83 |
-
|
84 |
-
.analyst-install-image-block img {
|
85 |
-
width: inherit;
|
86 |
-
}
|
87 |
-
|
88 |
-
.analyst-install-description-block {
|
89 |
-
padding-left: 40px;
|
90 |
-
padding-top: 5px
|
91 |
-
}
|
92 |
-
|
93 |
-
.analyst-install-description-text {
|
94 |
-
font-size: 16px;
|
95 |
-
color: #000000;
|
96 |
-
}
|
97 |
-
|
98 |
-
.analyst-install-permissions-list {
|
99 |
-
list-style: disc inside;
|
100 |
-
}
|
101 |
-
|
102 |
-
.analyst-install-permissions-list li {
|
103 |
-
padding-left: 15px;
|
104 |
-
margin-bottom: 2px;
|
105 |
-
}
|
106 |
-
|
107 |
-
.analyst-install-footer span {
|
108 |
-
color: #8a8787;
|
109 |
-
padding-right: 10px;
|
110 |
-
padding-left: 10px;
|
111 |
-
}
|
112 |
-
|
113 |
-
.analyst-install-footer span:not(:last-child) {
|
114 |
-
border-right: 1px solid #8a8787;
|
115 |
-
}
|
116 |
-
|
117 |
-
/*INSTALL STYLES*/
|
118 |
-
|
119 |
-
.reason-answer {
|
120 |
-
padding: 7px;
|
121 |
-
margin-left: 23px;
|
122 |
-
border: 1px solid #F2F2F2;
|
123 |
-
}
|
124 |
-
|
125 |
-
.analyst-link {
|
126 |
-
color: #00AF5E;
|
127 |
-
text-decoration: none;
|
128 |
-
}
|
129 |
-
|
130 |
-
.analyst-action-text {
|
131 |
-
cursor: pointer;
|
132 |
-
}
|
133 |
-
|
134 |
-
.analyst-action-text:hover {
|
135 |
-
color: #9d9a9a;
|
136 |
-
}
|
137 |
-
|
138 |
-
.analyst-disable-modal-mask {
|
139 |
-
width: 100%;
|
140 |
-
height: 100%;
|
141 |
-
opacity: 0.5;
|
142 |
-
position: absolute;
|
143 |
-
background: white;
|
144 |
-
top: 0;
|
145 |
-
left: 0;
|
146 |
-
}
|
147 |
-
|
148 |
-
.analyst-smile-image {
|
149 |
-
vertical-align: middle;
|
150 |
-
padding-bottom: 3px;
|
151 |
-
width: 24px;
|
152 |
-
}
|
153 |
-
|
154 |
-
#analyst-deactivation-reasons li {
|
155 |
-
padding-bottom: 3px;
|
156 |
-
font-size: 16px;
|
157 |
-
color: #000000;
|
158 |
-
}
|
159 |
-
|
160 |
-
@-webkit-keyframes analyst-animatetop {
|
161 |
-
from {top:-300px; opacity:0}
|
162 |
-
to {top:0; opacity:1}
|
163 |
-
}
|
164 |
-
|
165 |
-
@keyframes analyst-animatetop {
|
166 |
-
from {top:-300px; opacity:0}
|
167 |
-
to {top:0; opacity:1}
|
168 |
-
}
|
169 |
-
|
170 |
-
.analyst-modal-close {
|
171 |
-
color: #48036F;
|
172 |
-
font-size: 28px;
|
173 |
-
font-weight: bold;
|
174 |
-
top: 12px;
|
175 |
-
position: absolute;
|
176 |
-
right: 15px;
|
177 |
-
}
|
178 |
-
|
179 |
-
.analyst-modal-close:hover,
|
180 |
-
.analyst-modal-close:focus {
|
181 |
-
color: #000;
|
182 |
-
text-decoration: none;
|
183 |
-
cursor: pointer;
|
184 |
-
}
|
185 |
-
|
186 |
-
.analyst-modal-body {padding: 2px 16px;}
|
187 |
-
|
188 |
-
.analyst-modal-footer {
|
189 |
-
padding: 6px 16px;
|
190 |
-
background-color: #FFE773;
|
191 |
-
color: white;
|
192 |
-
}
|
193 |
-
|
194 |
-
#analyst-deactivate-modal .question-answer input, textarea {
|
195 |
-
margin-top: 5px;
|
196 |
-
width: 100%;
|
197 |
-
}
|
198 |
-
|
199 |
-
.analyst-btn-primary {
|
200 |
-
cursor: pointer;
|
201 |
-
border: none;
|
202 |
-
display:inline-block;
|
203 |
-
padding:0.7em 1.4em;
|
204 |
-
margin:0 0.3em 0.3em 0;
|
205 |
-
border-radius:0.15em;
|
206 |
-
box-sizing: border-box;
|
207 |
-
text-decoration:none;
|
208 |
-
font-family:'Roboto',sans-serif;
|
209 |
-
text-transform:uppercase;
|
210 |
-
font-weight:400;
|
211 |
-
color:#FFFFFF;
|
212 |
-
background-color:#9F3ED5;
|
213 |
-
box-shadow:inset 0 -0.6em 0 -0.35em rgba(0,0,0,0.17);
|
214 |
-
text-align:center;
|
215 |
-
position:relative;
|
216 |
-
}
|
217 |
-
|
218 |
-
.analyst-btn-primary:disabled {
|
219 |
-
background-color: #AD66D5;
|
220 |
-
cursor: not-allowed;
|
221 |
-
}
|
222 |
-
|
223 |
-
.analyst-btn-primary:active{
|
224 |
-
top:0.1em;
|
225 |
-
}
|
226 |
-
@media all and (max-width:30em){
|
227 |
-
.analyst-btn-primary {
|
228 |
-
display:block;
|
229 |
-
margin:0.4em auto;
|
230 |
-
}
|
231 |
-
}
|
232 |
-
|
233 |
-
.analyst-btn-secondary {
|
234 |
-
cursor: pointer;
|
235 |
-
border: none;
|
236 |
-
display:inline-block;
|
237 |
-
padding:0.7em 1.4em;
|
238 |
-
margin:0 0.3em 0.3em 0;
|
239 |
-
border-radius:0.15em;
|
240 |
-
box-sizing: border-box;
|
241 |
-
text-decoration:none;
|
242 |
-
font-family:'Roboto',sans-serif;
|
243 |
-
text-transform:uppercase;
|
244 |
-
font-weight:400;
|
245 |
-
color:#FFFFFF;
|
246 |
-
background-color:#6C8CD5;
|
247 |
-
box-shadow:inset 0 -0.6em 0 -0.35em rgba(0,0,0,0.17);
|
248 |
-
text-align:center;
|
249 |
-
position:relative;
|
250 |
-
}
|
251 |
-
|
252 |
-
.analyst-btn-secondary:disabled {
|
253 |
-
background-color: #6C8CD5;
|
254 |
-
cursor: not-allowed;
|
255 |
-
}
|
256 |
-
|
257 |
-
.analyst-btn-secondary:active{
|
258 |
-
top:0.1em;
|
259 |
-
}
|
260 |
-
@media all and (max-width:30em){
|
261 |
-
.analyst-btn-secondary {
|
262 |
-
display:block;
|
263 |
-
margin:0.4em auto;
|
264 |
-
}
|
265 |
-
}
|
266 |
-
|
267 |
-
.analyst-notice {
|
268 |
-
padding-right: 38px;
|
269 |
-
position: relative;
|
270 |
-
margin-bottom: 30px !important;
|
271 |
-
}
|
272 |
-
|
273 |
-
.analyst-notice .analyst-plugin-name {
|
274 |
-
background-color: #00000024;
|
275 |
-
padding-left: 7px;
|
276 |
-
padding-right: 7px;
|
277 |
-
position: absolute;
|
278 |
-
top: 100%;
|
279 |
-
border-radius: 0 0 5px 5px;
|
280 |
-
}
|
1 |
+
.analyst-action-opt {
|
2 |
+
cursor: pointer;
|
3 |
+
}
|
4 |
+
|
5 |
+
.analyst-modal {
|
6 |
+
color: #000000;
|
7 |
+
display: none;
|
8 |
+
position: fixed;
|
9 |
+
z-index: 1000;
|
10 |
+
padding-top: 100px;
|
11 |
+
left: 0;
|
12 |
+
top: 0;
|
13 |
+
width: 100%;
|
14 |
+
height: 100%;
|
15 |
+
overflow: auto;
|
16 |
+
background-color: rgb(0,0,0);
|
17 |
+
background-color: rgba(0,0,0,0.4);
|
18 |
+
}
|
19 |
+
|
20 |
+
.analyst-modal-content {
|
21 |
+
font-family: Helvetica, serif;
|
22 |
+
position: relative;
|
23 |
+
background-color: #fefefe;
|
24 |
+
margin: auto;
|
25 |
+
padding: 35px 35px 20px;
|
26 |
+
border: 1px solid #F2F2F2;
|
27 |
+
width: 40%;
|
28 |
+
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19);
|
29 |
+
-webkit-animation-name: analyst-animatetop;
|
30 |
+
-webkit-animation-duration: 0.4s;
|
31 |
+
animation-name: analyst-animatetop;
|
32 |
+
animation-duration: 0.4s
|
33 |
+
}
|
34 |
+
|
35 |
+
.analyst-btn-success {
|
36 |
+
cursor: pointer;
|
37 |
+
color: #ffffff;
|
38 |
+
background-color: #00AF5E;
|
39 |
+
border: none;
|
40 |
+
width: 100%;
|
41 |
+
font-size: 18px;
|
42 |
+
padding: 8px;
|
43 |
+
font-weight: bold;
|
44 |
+
}
|
45 |
+
|
46 |
+
.analyst-btn-grey {
|
47 |
+
cursor: pointer;
|
48 |
+
color: #2D2D2D;
|
49 |
+
background-color: #D8D8D8;
|
50 |
+
border: none;
|
51 |
+
width: 100%;
|
52 |
+
font-size: 18px;
|
53 |
+
padding: 8px;
|
54 |
+
font-weight: bold;
|
55 |
+
}
|
56 |
+
|
57 |
+
.analyst-btn-secondary-ghost {
|
58 |
+
cursor: pointer;
|
59 |
+
background: transparent;
|
60 |
+
border: none;
|
61 |
+
color: #898686;
|
62 |
+
font-size: 18px;
|
63 |
+
}
|
64 |
+
|
65 |
+
.analyst-modal-def-top-padding {
|
66 |
+
padding-top: 20px;
|
67 |
+
}
|
68 |
+
|
69 |
+
.analyst-modal-header {
|
70 |
+
font-size: 20px;
|
71 |
+
font-weight: bold;
|
72 |
+
}
|
73 |
+
|
74 |
+
/*INSTALL STYLES*/
|
75 |
+
.analyst-install-footer {
|
76 |
+
padding-top: 10px;
|
77 |
+
text-align: center;
|
78 |
+
}
|
79 |
+
|
80 |
+
.analyst-install-image-block {
|
81 |
+
width: 140px;
|
82 |
+
}
|
83 |
+
|
84 |
+
.analyst-install-image-block img {
|
85 |
+
width: inherit;
|
86 |
+
}
|
87 |
+
|
88 |
+
.analyst-install-description-block {
|
89 |
+
padding-left: 40px;
|
90 |
+
padding-top: 5px
|
91 |
+
}
|
92 |
+
|
93 |
+
.analyst-install-description-text {
|
94 |
+
font-size: 16px;
|
95 |
+
color: #000000;
|
96 |
+
}
|
97 |
+
|
98 |
+
.analyst-install-permissions-list {
|
99 |
+
list-style: disc inside;
|
100 |
+
}
|
101 |
+
|
102 |
+
.analyst-install-permissions-list li {
|
103 |
+
padding-left: 15px;
|
104 |
+
margin-bottom: 2px;
|
105 |
+
}
|
106 |
+
|
107 |
+
.analyst-install-footer span {
|
108 |
+
color: #8a8787;
|
109 |
+
padding-right: 10px;
|
110 |
+
padding-left: 10px;
|
111 |
+
}
|
112 |
+
|
113 |
+
.analyst-install-footer span:not(:last-child) {
|
114 |
+
border-right: 1px solid #8a8787;
|
115 |
+
}
|
116 |
+
|
117 |
+
/*INSTALL STYLES*/
|
118 |
+
|
119 |
+
.reason-answer {
|
120 |
+
padding: 7px;
|
121 |
+
margin-left: 23px;
|
122 |
+
border: 1px solid #F2F2F2;
|
123 |
+
}
|
124 |
+
|
125 |
+
.analyst-link {
|
126 |
+
color: #00AF5E;
|
127 |
+
text-decoration: none;
|
128 |
+
}
|
129 |
+
|
130 |
+
.analyst-action-text {
|
131 |
+
cursor: pointer;
|
132 |
+
}
|
133 |
+
|
134 |
+
.analyst-action-text:hover {
|
135 |
+
color: #9d9a9a;
|
136 |
+
}
|
137 |
+
|
138 |
+
.analyst-disable-modal-mask {
|
139 |
+
width: 100%;
|
140 |
+
height: 100%;
|
141 |
+
opacity: 0.5;
|
142 |
+
position: absolute;
|
143 |
+
background: white;
|
144 |
+
top: 0;
|
145 |
+
left: 0;
|
146 |
+
}
|
147 |
+
|
148 |
+
.analyst-smile-image {
|
149 |
+
vertical-align: middle;
|
150 |
+
padding-bottom: 3px;
|
151 |
+
width: 24px;
|
152 |
+
}
|
153 |
+
|
154 |
+
#analyst-deactivation-reasons li {
|
155 |
+
padding-bottom: 3px;
|
156 |
+
font-size: 16px;
|
157 |
+
color: #000000;
|
158 |
+
}
|
159 |
+
|
160 |
+
@-webkit-keyframes analyst-animatetop {
|
161 |
+
from {top:-300px; opacity:0}
|
162 |
+
to {top:0; opacity:1}
|
163 |
+
}
|
164 |
+
|
165 |
+
@keyframes analyst-animatetop {
|
166 |
+
from {top:-300px; opacity:0}
|
167 |
+
to {top:0; opacity:1}
|
168 |
+
}
|
169 |
+
|
170 |
+
.analyst-modal-close {
|
171 |
+
color: #48036F;
|
172 |
+
font-size: 28px;
|
173 |
+
font-weight: bold;
|
174 |
+
top: 12px;
|
175 |
+
position: absolute;
|
176 |
+
right: 15px;
|
177 |
+
}
|
178 |
+
|
179 |
+
.analyst-modal-close:hover,
|
180 |
+
.analyst-modal-close:focus {
|
181 |
+
color: #000;
|
182 |
+
text-decoration: none;
|
183 |
+
cursor: pointer;
|
184 |
+
}
|
185 |
+
|
186 |
+
.analyst-modal-body {padding: 2px 16px;}
|
187 |
+
|
188 |
+
.analyst-modal-footer {
|
189 |
+
padding: 6px 16px;
|
190 |
+
background-color: #FFE773;
|
191 |
+
color: white;
|
192 |
+
}
|
193 |
+
|
194 |
+
#analyst-deactivate-modal .question-answer input, textarea {
|
195 |
+
margin-top: 5px;
|
196 |
+
width: 100%;
|
197 |
+
}
|
198 |
+
|
199 |
+
.analyst-btn-primary {
|
200 |
+
cursor: pointer;
|
201 |
+
border: none;
|
202 |
+
display:inline-block;
|
203 |
+
padding:0.7em 1.4em;
|
204 |
+
margin:0 0.3em 0.3em 0;
|
205 |
+
border-radius:0.15em;
|
206 |
+
box-sizing: border-box;
|
207 |
+
text-decoration:none;
|
208 |
+
font-family:'Roboto',sans-serif;
|
209 |
+
text-transform:uppercase;
|
210 |
+
font-weight:400;
|
211 |
+
color:#FFFFFF;
|
212 |
+
background-color:#9F3ED5;
|
213 |
+
box-shadow:inset 0 -0.6em 0 -0.35em rgba(0,0,0,0.17);
|
214 |
+
text-align:center;
|
215 |
+
position:relative;
|
216 |
+
}
|
217 |
+
|
218 |
+
.analyst-btn-primary:disabled {
|
219 |
+
background-color: #AD66D5;
|
220 |
+
cursor: not-allowed;
|
221 |
+
}
|
222 |
+
|
223 |
+
.analyst-btn-primary:active{
|
224 |
+
top:0.1em;
|
225 |
+
}
|
226 |
+
@media all and (max-width:30em){
|
227 |
+
.analyst-btn-primary {
|
228 |
+
display:block;
|
229 |
+
margin:0.4em auto;
|
230 |
+
}
|
231 |
+
}
|
232 |
+
|
233 |
+
.analyst-btn-secondary {
|
234 |
+
cursor: pointer;
|
235 |
+
border: none;
|
236 |
+
display:inline-block;
|
237 |
+
padding:0.7em 1.4em;
|
238 |
+
margin:0 0.3em 0.3em 0;
|
239 |
+
border-radius:0.15em;
|
240 |
+
box-sizing: border-box;
|
241 |
+
text-decoration:none;
|
242 |
+
font-family:'Roboto',sans-serif;
|
243 |
+
text-transform:uppercase;
|
244 |
+
font-weight:400;
|
245 |
+
color:#FFFFFF;
|
246 |
+
background-color:#6C8CD5;
|
247 |
+
box-shadow:inset 0 -0.6em 0 -0.35em rgba(0,0,0,0.17);
|
248 |
+
text-align:center;
|
249 |
+
position:relative;
|
250 |
+
}
|
251 |
+
|
252 |
+
.analyst-btn-secondary:disabled {
|
253 |
+
background-color: #6C8CD5;
|
254 |
+
cursor: not-allowed;
|
255 |
+
}
|
256 |
+
|
257 |
+
.analyst-btn-secondary:active{
|
258 |
+
top:0.1em;
|
259 |
+
}
|
260 |
+
@media all and (max-width:30em){
|
261 |
+
.analyst-btn-secondary {
|
262 |
+
display:block;
|
263 |
+
margin:0.4em auto;
|
264 |
+
}
|
265 |
+
}
|
266 |
+
|
267 |
+
.analyst-notice {
|
268 |
+
padding-right: 38px;
|
269 |
+
position: relative;
|
270 |
+
margin-bottom: 30px !important;
|
271 |
+
}
|
272 |
+
|
273 |
+
.analyst-notice .analyst-plugin-name {
|
274 |
+
background-color: #00000024;
|
275 |
+
padding-left: 7px;
|
276 |
+
padding-right: 7px;
|
277 |
+
position: absolute;
|
278 |
+
top: 100%;
|
279 |
+
border-radius: 0 0 5px 5px;
|
280 |
+
}
|
analyst/assets/index.php
CHANGED
@@ -1,2 +1,2 @@
|
|
1 |
-
<?php
|
2 |
-
// Silence is golden.
|
1 |
+
<?php
|
2 |
+
// Silence is golden.
|
analyst/assets/js/customize.js
CHANGED
@@ -1,29 +1,29 @@
|
|
1 |
-
(function ($) {
|
2 |
-
$(document).on('click', '.analyst-notice-dismiss', function () {
|
3 |
-
var id = $(this).attr('analyst-notice-id');
|
4 |
-
var self = this;
|
5 |
-
|
6 |
-
$.post(ajaxurl, {action: 'analyst_notification_dismiss', id: id})
|
7 |
-
.done(function () {
|
8 |
-
$(self).parent().fadeOut()
|
9 |
-
})
|
10 |
-
})
|
11 |
-
|
12 |
-
var url = new URL(window.location.href)
|
13 |
-
|
14 |
-
if (url.searchParams.has('verify')) {
|
15 |
-
var pluginId = url.searchParams.get('plugin_id')
|
16 |
-
|
17 |
-
$.ajax({
|
18 |
-
url: ajaxurl,
|
19 |
-
method: 'POST',
|
20 |
-
data: {
|
21 |
-
action: 'analyst_install_verified_' + pluginId,
|
22 |
-
},
|
23 |
-
success: function () {
|
24 |
-
// Refresh page without query params
|
25 |
-
window.location.href = window.location.origin + window.location.pathname
|
26 |
-
}
|
27 |
-
})
|
28 |
-
}
|
29 |
-
})(jQuery)
|
1 |
+
(function ($) {
|
2 |
+
$(document).on('click', '.analyst-notice-dismiss', function () {
|
3 |
+
var id = $(this).attr('analyst-notice-id');
|
4 |
+
var self = this;
|
5 |
+
|
6 |
+
$.post(ajaxurl, {action: 'analyst_notification_dismiss', id: id})
|
7 |
+
.done(function () {
|
8 |
+
$(self).parent().fadeOut()
|
9 |
+
})
|
10 |
+
})
|
11 |
+
|
12 |
+
var url = new URL(window.location.href)
|
13 |
+
|
14 |
+
if (url.searchParams.has('verify')) {
|
15 |
+
var pluginId = url.searchParams.get('plugin_id')
|
16 |
+
|
17 |
+
$.ajax({
|
18 |
+
url: ajaxurl,
|
19 |
+
method: 'POST',
|
20 |
+
data: {
|
21 |
+
action: 'analyst_install_verified_' + pluginId,
|
22 |
+
},
|
23 |
+
success: function () {
|
24 |
+
// Refresh page without query params
|
25 |
+
window.location.href = window.location.origin + window.location.pathname
|
26 |
+
}
|
27 |
+
})
|
28 |
+
}
|
29 |
+
})(jQuery)
|
analyst/autoload.php
CHANGED
@@ -1,40 +1,40 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
require_once __DIR__ . '/src/helpers.php';
|
4 |
-
|
5 |
-
require_once __DIR__ . '/src/Contracts/HttpClientContract.php';
|
6 |
-
require_once __DIR__ . '/src/Contracts/RequestContract.php';
|
7 |
-
require_once __DIR__ . '/src/Contracts/RequestorContract.php';
|
8 |
-
require_once __DIR__ . '/src/Contracts/TrackerContract.php';
|
9 |
-
require_once __DIR__ . '/src/Contracts/CacheContract.php';
|
10 |
-
|
11 |
-
require_once __DIR__ . '/src/Core/AbstractFactory.php';
|
12 |
-
|
13 |
-
require_once __DIR__ . '/src/Cache/DatabaseCache.php';
|
14 |
-
|
15 |
-
require_once __DIR__ . '/src/Account/Account.php';
|
16 |
-
require_once __DIR__ . '/src/Account/AccountData.php';
|
17 |
-
require_once __DIR__ . '/src/Account/AccountDataFactory.php';
|
18 |
-
require_once __DIR__ . '/src/Contracts/AnalystContract.php';
|
19 |
-
|
20 |
-
require_once __DIR__ . '/src/Http/Requests/AbstractLoggerRequest.php';
|
21 |
-
require_once __DIR__ . '/src/Http/Requests/ActivateRequest.php';
|
22 |
-
require_once __DIR__ . '/src/Http/Requests/DeactivateRequest.php';
|
23 |
-
require_once __DIR__ . '/src/Http/Requests/InstallRequest.php';
|
24 |
-
require_once __DIR__ . '/src/Http/Requests/OptInRequest.php';
|
25 |
-
require_once __DIR__ . '/src/Http/Requests/OptOutRequest.php';
|
26 |
-
require_once __DIR__ . '/src/Http/Requests/UninstallRequest.php';
|
27 |
-
|
28 |
-
require_once __DIR__ . '/src/Http/CurlHttpClient.php';
|
29 |
-
require_once __DIR__ . '/src/Http/DummyHttpClient.php';
|
30 |
-
require_once __DIR__ . '/src/Http/WordPressHttpClient.php';
|
31 |
-
|
32 |
-
require_once __DIR__ . '/src/Notices/Notice.php';
|
33 |
-
require_once __DIR__ . '/src/Notices/NoticeFactory.php';
|
34 |
-
|
35 |
-
require_once __DIR__ . '/src/Analyst.php';
|
36 |
-
require_once __DIR__ . '/src/ApiRequestor.php';
|
37 |
-
require_once __DIR__ . '/src/ApiResponse.php';
|
38 |
-
require_once __DIR__ . '/src/Collector.php';
|
39 |
-
require_once __DIR__ . '/src/Mutator.php';
|
40 |
-
|
1 |
+
<?php
|
2 |
+
|
3 |
+
require_once __DIR__ . '/src/helpers.php';
|
4 |
+
|
5 |
+
require_once __DIR__ . '/src/Contracts/HttpClientContract.php';
|
6 |
+
require_once __DIR__ . '/src/Contracts/RequestContract.php';
|
7 |
+
require_once __DIR__ . '/src/Contracts/RequestorContract.php';
|
8 |
+
require_once __DIR__ . '/src/Contracts/TrackerContract.php';
|
9 |
+
require_once __DIR__ . '/src/Contracts/CacheContract.php';
|
10 |
+
|
11 |
+
require_once __DIR__ . '/src/Core/AbstractFactory.php';
|
12 |
+
|
13 |
+
require_once __DIR__ . '/src/Cache/DatabaseCache.php';
|
14 |
+
|
15 |
+
require_once __DIR__ . '/src/Account/Account.php';
|
16 |
+
require_once __DIR__ . '/src/Account/AccountData.php';
|
17 |
+
require_once __DIR__ . '/src/Account/AccountDataFactory.php';
|
18 |
+
require_once __DIR__ . '/src/Contracts/AnalystContract.php';
|
19 |
+
|
20 |
+
require_once __DIR__ . '/src/Http/Requests/AbstractLoggerRequest.php';
|
21 |
+
require_once __DIR__ . '/src/Http/Requests/ActivateRequest.php';
|
22 |
+
require_once __DIR__ . '/src/Http/Requests/DeactivateRequest.php';
|
23 |
+
require_once __DIR__ . '/src/Http/Requests/InstallRequest.php';
|
24 |
+
require_once __DIR__ . '/src/Http/Requests/OptInRequest.php';
|
25 |
+
require_once __DIR__ . '/src/Http/Requests/OptOutRequest.php';
|
26 |
+
require_once __DIR__ . '/src/Http/Requests/UninstallRequest.php';
|
27 |
+
|
28 |
+
require_once __DIR__ . '/src/Http/CurlHttpClient.php';
|
29 |
+
require_once __DIR__ . '/src/Http/DummyHttpClient.php';
|
30 |
+
require_once __DIR__ . '/src/Http/WordPressHttpClient.php';
|
31 |
+
|
32 |
+
require_once __DIR__ . '/src/Notices/Notice.php';
|
33 |
+
require_once __DIR__ . '/src/Notices/NoticeFactory.php';
|
34 |
+
|
35 |
+
require_once __DIR__ . '/src/Analyst.php';
|
36 |
+
require_once __DIR__ . '/src/ApiRequestor.php';
|
37 |
+
require_once __DIR__ . '/src/ApiResponse.php';
|
38 |
+
require_once __DIR__ . '/src/Collector.php';
|
39 |
+
require_once __DIR__ . '/src/Mutator.php';
|
40 |
+
|
analyst/index.php
CHANGED
@@ -1,2 +1,2 @@
|
|
1 |
-
<?php
|
2 |
-
// Silence
|
1 |
+
<?php
|
2 |
+
// Silence
|
analyst/main.php
CHANGED
@@ -1,36 +1,36 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
require_once 'sdk_resolver.php';
|
4 |
-
|
5 |
-
|
6 |
-
if (!function_exists('analyst_init')) {
|
7 |
-
/**
|
8 |
-
* Initialize analyst
|
9 |
-
*
|
10 |
-
* @param array $options
|
11 |
-
*/
|
12 |
-
function analyst_init ($options) {
|
13 |
-
// Try resolve latest supported SDK
|
14 |
-
// In case resolving is failed exit the execution
|
15 |
-
try {
|
16 |
-
analyst_resolve_sdk($options['base-dir']);
|
17 |
-
} catch (Exception $exception) {
|
18 |
-
error_log('[ANALYST] Cannot resolve any supported SDK');
|
19 |
-
return;
|
20 |
-
}
|
21 |
-
|
22 |
-
try {
|
23 |
-
global /** @var Analyst\Analyst $analyst */
|
24 |
-
$analyst;
|
25 |
-
|
26 |
-
// Set global instance of analyst
|
27 |
-
if (!$analyst) {
|
28 |
-
$analyst = Analyst\Analyst::getInstance();
|
29 |
-
}
|
30 |
-
|
31 |
-
$analyst->registerAccount(new Account\Account($options['client-id'], $options['client-secret'], $options['base-dir']));
|
32 |
-
} catch (Exception $e) {
|
33 |
-
error_log('Analyst SDK receive an error: [' . $e->getMessage() . '] Please contact our support at support@analyst.com');
|
34 |
-
}
|
35 |
-
}
|
36 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
require_once 'sdk_resolver.php';
|
4 |
+
|
5 |
+
|
6 |
+
if (!function_exists('analyst_init')) {
|
7 |
+
/**
|
8 |
+
* Initialize analyst
|
9 |
+
*
|
10 |
+
* @param array $options
|
11 |
+
*/
|
12 |
+
function analyst_init ($options) {
|
13 |
+
// Try resolve latest supported SDK
|
14 |
+
// In case resolving is failed exit the execution
|
15 |
+
try {
|
16 |
+
analyst_resolve_sdk($options['base-dir']);
|
17 |
+
} catch (Exception $exception) {
|
18 |
+
error_log('[ANALYST] Cannot resolve any supported SDK');
|
19 |
+
return;
|
20 |
+
}
|
21 |
+
|
22 |
+
try {
|
23 |
+
global /** @var Analyst\Analyst $analyst */
|
24 |
+
$analyst;
|
25 |
+
|
26 |
+
// Set global instance of analyst
|
27 |
+
if (!$analyst) {
|
28 |
+
$analyst = Analyst\Analyst::getInstance();
|
29 |
+
}
|
30 |
+
|
31 |
+
$analyst->registerAccount(new Account\Account($options['client-id'], $options['client-secret'], $options['base-dir']));
|
32 |
+
} catch (Exception $e) {
|
33 |
+
error_log('Analyst SDK receive an error: [' . $e->getMessage() . '] Please contact our support at support@analyst.com');
|
34 |
+
}
|
35 |
+
}
|
36 |
+
}
|
analyst/sdk_resolver.php
CHANGED
@@ -1,79 +1,79 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
if (!function_exists('analyst_resolve_sdk')) {
|
4 |
-
|
5 |
-
/**
|
6 |
-
* Resolve supported sdk versions and load latest supported one
|
7 |
-
* also bootstrap sdk with autoloader
|
8 |
-
*
|
9 |
-
* @since 1.1.3
|
10 |
-
*
|
11 |
-
* @param null $thisPluginPath
|
12 |
-
* @return void
|
13 |
-
* @throws Exception
|
14 |
-
*/
|
15 |
-
function analyst_resolve_sdk($thisPluginPath = null) {
|
16 |
-
static $loaded = false;
|
17 |
-
|
18 |
-
// Exit if we already resolved SDK
|
19 |
-
if ($loaded) return;
|
20 |
-
|
21 |
-
$plugins = get_option('active_plugins');
|
22 |
-
|
23 |
-
if ($thisPluginPath) {
|
24 |
-
array_push($plugins, plugin_basename($thisPluginPath));
|
25 |
-
}
|
26 |
-
|
27 |
-
$pluginsFolder = WP_PLUGIN_DIR;
|
28 |
-
|
29 |
-
$possibleSDKs = array_map(function ($path) use ($pluginsFolder) {
|
30 |
-
$sdkFolder = sprintf('%s/%s/analyst/', $pluginsFolder, dirname($path));
|
31 |
-
|
32 |
-
$sdkFolder = str_replace('\\', '/', $sdkFolder);
|
33 |
-
|
34 |
-
$versionPath = $sdkFolder . 'version.php';
|
35 |
-
|
36 |
-
if (file_exists($versionPath)) {
|
37 |
-
return require $versionPath;
|
38 |
-
}
|
39 |
-
|
40 |
-
return false;
|
41 |
-
}, $plugins);
|
42 |
-
|
43 |
-
global $wp_version;
|
44 |
-
|
45 |
-
// Filter out plugins which has no SDK
|
46 |
-
$SDKs = array_filter($possibleSDKs, function ($s) {return is_array($s);});
|
47 |
-
|
48 |
-
// Filter SDKs which is supported by PHP and WP
|
49 |
-
$supported = array_values(array_filter($SDKs, function ($sdk) use($wp_version) {
|
50 |
-
$phpSupported = version_compare(PHP_VERSION, $sdk['php']) >= 0;
|
51 |
-
$wpSupported = version_compare($wp_version, $sdk['wp']) >= 0;
|
52 |
-
|
53 |
-
return $phpSupported && $wpSupported;
|
54 |
-
}));
|
55 |
-
|
56 |
-
// Sort SDK by version in descending order
|
57 |
-
uasort($supported, function ($x, $y) {
|
58 |
-
return version_compare($y['sdk'], $x['sdk']);
|
59 |
-
});
|
60 |
-
|
61 |
-
// Reset sorted values keys
|
62 |
-
$supported = array_values($supported);
|
63 |
-
|
64 |
-
if (!isset($supported[0])) {
|
65 |
-
throw new Exception('There is no SDK which is support current PHP version and WP version');
|
66 |
-
}
|
67 |
-
|
68 |
-
// Autoload files for supported SDK
|
69 |
-
$autoloaderPath = str_replace(
|
70 |
-
'\\',
|
71 |
-
'/',
|
72 |
-
sprintf('%s/autoload.php', $supported[0]['path'])
|
73 |
-
);
|
74 |
-
|
75 |
-
require_once $autoloaderPath;
|
76 |
-
|
77 |
-
$loaded = true;
|
78 |
-
}
|
79 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
if (!function_exists('analyst_resolve_sdk')) {
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Resolve supported sdk versions and load latest supported one
|
7 |
+
* also bootstrap sdk with autoloader
|
8 |
+
*
|
9 |
+
* @since 1.1.3
|
10 |
+
*
|
11 |
+
* @param null $thisPluginPath
|
12 |
+
* @return void
|
13 |
+
* @throws Exception
|
14 |
+
*/
|
15 |
+
function analyst_resolve_sdk($thisPluginPath = null) {
|
16 |
+
static $loaded = false;
|
17 |
+
|
18 |
+
// Exit if we already resolved SDK
|
19 |
+
if ($loaded) return;
|
20 |
+
|
21 |
+
$plugins = get_option('active_plugins');
|
22 |
+
|
23 |
+
if ($thisPluginPath) {
|
24 |
+
array_push($plugins, plugin_basename($thisPluginPath));
|
25 |
+
}
|
26 |
+
|
27 |
+
$pluginsFolder = WP_PLUGIN_DIR;
|
28 |
+
|
29 |
+
$possibleSDKs = array_map(function ($path) use ($pluginsFolder) {
|
30 |
+
$sdkFolder = sprintf('%s/%s/analyst/', $pluginsFolder, dirname($path));
|
31 |
+
|
32 |
+
$sdkFolder = str_replace('\\', '/', $sdkFolder);
|
33 |
+
|
34 |
+
$versionPath = $sdkFolder . 'version.php';
|
35 |
+
|
36 |
+
if (file_exists($versionPath)) {
|
37 |
+
return require $versionPath;
|
38 |
+
}
|
39 |
+
|
40 |
+
return false;
|
41 |
+
}, $plugins);
|
42 |
+
|
43 |
+
global $wp_version;
|
44 |
+
|
45 |
+
// Filter out plugins which has no SDK
|
46 |
+
$SDKs = array_filter($possibleSDKs, function ($s) {return is_array($s);});
|
47 |
+
|
48 |
+
// Filter SDKs which is supported by PHP and WP
|
49 |
+
$supported = array_values(array_filter($SDKs, function ($sdk) use($wp_version) {
|
50 |
+
$phpSupported = version_compare(PHP_VERSION, $sdk['php']) >= 0;
|
51 |
+
$wpSupported = version_compare($wp_version, $sdk['wp']) >= 0;
|
52 |
+
|
53 |
+
return $phpSupported && $wpSupported;
|
54 |
+
}));
|
55 |
+
|
56 |
+
// Sort SDK by version in descending order
|
57 |
+
uasort($supported, function ($x, $y) {
|
58 |
+
return version_compare($y['sdk'], $x['sdk']);
|
59 |
+
});
|
60 |
+
|
61 |
+
// Reset sorted values keys
|
62 |
+
$supported = array_values($supported);
|
63 |
+
|
64 |
+
if (!isset($supported[0])) {
|
65 |
+
throw new Exception('There is no SDK which is support current PHP version and WP version');
|
66 |
+
}
|
67 |
+
|
68 |
+
// Autoload files for supported SDK
|
69 |
+
$autoloaderPath = str_replace(
|
70 |
+
'\\',
|
71 |
+
'/',
|
72 |
+
sprintf('%s/autoload.php', $supported[0]['path'])
|
73 |
+
);
|
74 |
+
|
75 |
+
require_once $autoloaderPath;
|
76 |
+
|
77 |
+
$loaded = true;
|
78 |
+
}
|
79 |
+
}
|
analyst/src/Account/Account.php
CHANGED
@@ -1,584 +1,584 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace Account;
|
4 |
-
|
5 |
-
use Analyst\Analyst;
|
6 |
-
use Analyst\ApiRequestor;
|
7 |
-
use Analyst\Cache\DatabaseCache;
|
8 |
-
use Analyst\Collector;
|
9 |
-
use Analyst\Http\Requests\ActivateRequest;
|
10 |
-
use Analyst\Http\Requests\DeactivateRequest;
|
11 |
-
use Analyst\Http\Requests\InstallRequest;
|
12 |
-
use Analyst\Http\Requests\OptInRequest;
|
13 |
-
use Analyst\Http\Requests\OptOutRequest;
|
14 |
-
use Analyst\Http\Requests\UninstallRequest;
|
15 |
-
use Analyst\Notices\Notice;
|
16 |
-
use Analyst\Notices\NoticeFactory;
|
17 |
-
use Analyst\Contracts\TrackerContract;
|
18 |
-
use Analyst\Contracts\RequestorContract;
|
19 |
-
|
20 |
-
/**
|
21 |
-
* Class Account
|
22 |
-
*
|
23 |
-
* This is plugin's account object
|
24 |
-
*/
|
25 |
-
class Account implements TrackerContract
|
26 |
-
{
|
27 |
-
/**
|
28 |
-
* Account id
|
29 |
-
*
|
30 |
-
* @var string
|
31 |
-
*/
|
32 |
-
protected $id;
|
33 |
-
|
34 |
-
/**
|
35 |
-
* Basename of plugin
|
36 |
-
*
|
37 |
-
* @var string
|
38 |
-
*/
|
39 |
-
protected $path;
|
40 |
-
|
41 |
-
/**
|
42 |
-
* Whether plugin is active or not
|
43 |
-
*
|
44 |
-
* @var bool
|
45 |
-
*/
|
46 |
-
protected $isInstalled = false;
|
47 |
-
|
48 |
-
/**
|
49 |
-
* Is user sign in for data tracking
|
50 |
-
*
|
51 |
-
* @var bool
|
52 |
-
*/
|
53 |
-
protected $isOptedIn = false;
|
54 |
-
|
55 |
-
/**
|
56 |
-
* Is user accepted permissions grant
|
57 |
-
* for collection site data
|
58 |
-
*
|
59 |
-
* @var bool
|
60 |
-
*/
|
61 |
-
protected $isSigned = false;
|
62 |
-
|
63 |
-
/**
|
64 |
-
* Is user ever resolved install modal window?
|
65 |
-
*
|
66 |
-
* @var bool
|
67 |
-
*/
|
68 |
-
protected $isInstallResolved = false;
|
69 |
-
|
70 |
-
/**
|
71 |
-
* Public secret code
|
72 |
-
*
|
73 |
-
* @var string
|
74 |
-
*/
|
75 |
-
protected $clientSecret;
|
76 |
-
|
77 |
-
/**
|
78 |
-
* @var AccountData
|
79 |
-
*/
|
80 |
-
protected $data;
|
81 |
-
|
82 |
-
/**
|
83 |
-
* Base plugin path
|
84 |
-
*
|
85 |
-
* @var string
|
86 |
-
*/
|
87 |
-
protected $basePluginPath;
|
88 |
-
|
89 |
-
/**
|
90 |
-
* @var RequestorContract
|
91 |
-
*/
|
92 |
-
protected $requestor;
|
93 |
-
|
94 |
-
/**
|
95 |
-
* @var Collector
|
96 |
-
*/
|
97 |
-
protected $collector;
|
98 |
-
|
99 |
-
/**
|
100 |
-
* Account constructor.
|
101 |
-
* @param $id
|
102 |
-
* @param $secret
|
103 |
-
* @param $baseDir
|
104 |
-
*/
|
105 |
-
public function __construct($id, $secret, $baseDir)
|
106 |
-
{
|
107 |
-
$this->id = $id;
|
108 |
-
$this->clientSecret = $secret;
|
109 |
-
|
110 |
-
$this->path = $baseDir;
|
111 |
-
|
112 |
-
$this->basePluginPath = plugin_basename($baseDir);
|
113 |
-
}
|
114 |
-
|
115 |
-
/**
|
116 |
-
* @return string
|
117 |
-
*/
|
118 |
-
public function getPath()
|
119 |
-
{
|
120 |
-
return $this->path;
|
121 |
-
}
|
122 |
-
|
123 |
-
/**
|
124 |
-
* @param string $path
|
125 |
-
*/
|
126 |
-
public function setPath($path)
|
127 |
-
{
|
128 |
-
$this->data->setPath($path);
|
129 |
-
|
130 |
-
$this->path = $path;
|
131 |
-
}
|
132 |
-
|
133 |
-
/**
|
134 |
-
* @return bool
|
135 |
-
*/
|
136 |
-
public function isOptedIn()
|
137 |
-
{
|
138 |
-
return $this->isOptedIn;
|
139 |
-
}
|
140 |
-
|
141 |
-
/**
|
142 |
-
* @param bool $isOptedIn
|
143 |
-
*/
|
144 |
-
public function setIsOptedIn($isOptedIn)
|
145 |
-
{
|
146 |
-
$this->data->setIsOptedIn($isOptedIn);
|
147 |
-
|
148 |
-
$this->isOptedIn = $isOptedIn;
|
149 |
-
}
|
150 |
-
|
151 |
-
/**
|
152 |
-
* Whether plugin is active
|
153 |
-
*
|
154 |
-
* @return bool
|
155 |
-
*/
|
156 |
-
public function isActive()
|
157 |
-
{
|
158 |
-
return is_plugin_active($this->path);
|
159 |
-
}
|
160 |
-
|
161 |
-
/**
|
162 |
-
* @param string $id
|
163 |
-
*/
|
164 |
-
public function setId($id)
|
165 |
-
{
|
166 |
-
$this->id = $id;
|
167 |
-
}
|
168 |
-
|
169 |
-
/**
|
170 |
-
* @return string
|
171 |
-
*/
|
172 |
-
public function getId()
|
173 |
-
{
|
174 |
-
return $this->id;
|
175 |
-
}
|
176 |
-
|
177 |
-
/**
|
178 |
-
* @return bool
|
179 |
-
*/
|
180 |
-
public function isInstalled()
|
181 |
-
{
|
182 |
-
return $this->isInstalled;
|
183 |
-
}
|
184 |
-
|
185 |
-
/**
|
186 |
-
* @param bool $isInstalled
|
187 |
-
*/
|
188 |
-
public function setIsInstalled($isInstalled)
|
189 |
-
{
|
190 |
-
$this->data->setIsInstalled($isInstalled);
|
191 |
-
|
192 |
-
$this->isInstalled = $isInstalled;
|
193 |
-
}
|
194 |
-
|
195 |
-
/**
|
196 |
-
* Should register activation and deactivation
|
197 |
-
* event hooks
|
198 |
-
*
|
199 |
-
* @return void
|
200 |
-
*/
|
201 |
-
public function registerHooks()
|
202 |
-
{
|
203 |
-
register_activation_hook($this->basePluginPath, [&$this, 'onActivePluginListener']);
|
204 |
-
register_uninstall_hook($this->basePluginPath, ['Account\Account', 'onUninstallPluginListener']);
|
205 |
-
|
206 |
-
$this->addFilter('plugin_action_links', [&$this, 'onRenderActionLinksHook']);
|
207 |
-
|
208 |
-
$this->addAjax('analyst_opt_in', [&$this, 'onOptInListener']);
|
209 |
-
$this->addAjax('analyst_opt_out', [&$this, 'onOptOutListener']);
|
210 |
-
$this->addAjax('analyst_plugin_deactivate', [&$this, 'onDeactivatePluginListener']);
|
211 |
-
$this->addAjax('analyst_install', [&$this, 'onInstallListener']);
|
212 |
-
$this->addAjax('analyst_skip_install', [&$this, 'onSkipInstallListener']);
|
213 |
-
$this->addAjax('analyst_install_verified', [&$this, 'onInstallVerifiedListener']);
|
214 |
-
}
|
215 |
-
|
216 |
-
/**
|
217 |
-
* Will fire when admin activates plugin
|
218 |
-
*
|
219 |
-
* @return void
|
220 |
-
*/
|
221 |
-
public function onActivePluginListener()
|
222 |
-
{
|
223 |
-
if (!$this->isInstallResolved()) {
|
224 |
-
DatabaseCache::getInstance()->put('plugin_to_install', $this->id);
|
225 |
-
}
|
226 |
-
|
227 |
-
if (!$this->isAllowingLogging()) return;
|
228 |
-
|
229 |
-
ActivateRequest::make($this->collector, $this->id, $this->path)
|
230 |
-
->execute($this->requestor);
|
231 |
-
|
232 |
-
$this->setIsInstalled(true);
|
233 |
-
|
234 |
-
AccountDataFactory::syncData();
|
235 |
-
}
|
236 |
-
|
237 |
-
/**
|
238 |
-
* Will fire when admin deactivates plugin
|
239 |
-
*
|
240 |
-
* @return void
|
241 |
-
*/
|
242 |
-
public function onDeactivatePluginListener()
|
243 |
-
{
|
244 |
-
if (!$this->isAllowingLogging()) return;
|
245 |
-
|
246 |
-
$question = isset($_POST['question']) ? stripslashes($_POST['question']) : null;
|
247 |
-
$reason = isset($_POST['reason']) ? stripslashes($_POST['reason']) : null;
|
248 |
-
|
249 |
-
DeactivateRequest::make($this->collector, $this->id, $this->path, $question, $reason)
|
250 |
-
->execute($this->requestor);
|
251 |
-
|
252 |
-
$this->setIsInstalled(false);
|
253 |
-
|
254 |
-
AccountDataFactory::syncData();
|
255 |
-
|
256 |
-
wp_send_json_success();
|
257 |
-
}
|
258 |
-
|
259 |
-
/**
|
260 |
-
* Will fire when user opted in
|
261 |
-
*
|
262 |
-
* @return void
|
263 |
-
*/
|
264 |
-
public function onOptInListener()
|
265 |
-
{
|
266 |
-
OptInRequest::make($this->collector, $this->id, $this->path)->execute($this->requestor);
|
267 |
-
|
268 |
-
$this->setIsOptedIn(true);
|
269 |
-
|
270 |
-
AccountDataFactory::syncData();
|
271 |
-
|
272 |
-
wp_die();
|
273 |
-
}
|
274 |
-
|
275 |
-
/**
|
276 |
-
* Will fire when user opted out
|
277 |
-
*
|
278 |
-
* @return void
|
279 |
-
*/
|
280 |
-
public function onOptOutListener()
|
281 |
-
{
|
282 |
-
OptOutRequest::make($this->collector, $this->id, $this->path)->execute($this->requestor);
|
283 |
-
|
284 |
-
$this->setIsOptedIn(false);
|
285 |
-
|
286 |
-
AccountDataFactory::syncData();
|
287 |
-
|
288 |
-
wp_send_json_success();
|
289 |
-
}
|
290 |
-
|
291 |
-
/**
|
292 |
-
* Will fire when user accept opt-in
|
293 |
-
* at first time
|
294 |
-
*
|
295 |
-
* @return void
|
296 |
-
*/
|
297 |
-
public function onInstallListener()
|
298 |
-
{
|
299 |
-
$cache = DatabaseCache::getInstance();
|
300 |
-
|
301 |
-
// Set flag to true which indicates that install is resolved
|
302 |
-
// also remove install plugin id from cache
|
303 |
-
$this->setIsInstallResolved(true);
|
304 |
-
$cache->delete('plugin_to_install');
|
305 |
-
|
306 |
-
InstallRequest::make($this->collector, $this->id, $this->path)->execute($this->requestor);
|
307 |
-
|
308 |
-
$this->setIsSigned(true);
|
309 |
-
|
310 |
-
$this->setIsOptedIn(true);
|
311 |
-
|
312 |
-
$factory = NoticeFactory::instance();
|
313 |
-
|
314 |
-
$message = sprintf('Please confirm your email by clicking on the link we sent to %s. This makes sure you’re not a bot.', $this->collector->getGeneralEmailAddress());
|
315 |
-
|
316 |
-
$notificationId = uniqid();
|
317 |
-
|
318 |
-
$notice = Notice::make(
|
319 |
-
$notificationId,
|
320 |
-
$this->getId(),
|
321 |
-
$message,
|
322 |
-
$this->collector->getPluginName($this->path)
|
323 |
-
);
|
324 |
-
|
325 |
-
$factory->addNotice($notice);
|
326 |
-
|
327 |
-
AccountDataFactory::syncData();
|
328 |
-
|
329 |
-
// Set email confirmation notification id to cache
|
330 |
-
// se we can extract and remove it when user confirmed email
|
331 |
-
$cache->put(
|
332 |
-
sprintf('account_email_confirmation_%s', $this->getId()),
|
333 |
-
$notificationId
|
334 |
-
);
|
335 |
-
|
336 |
-
wp_send_json_success();
|
337 |
-
}
|
338 |
-
|
339 |
-
/**
|
340 |
-
* Will fire when user skipped installation
|
341 |
-
*
|
342 |
-
* @return void
|
343 |
-
*/
|
344 |
-
public function onSkipInstallListener()
|
345 |
-
{
|
346 |
-
// Set flag to true which indicates that install is resolved
|
347 |
-
// also remove install plugin id from cache
|
348 |
-
$this->setIsInstallResolved(true);
|
349 |
-
DatabaseCache::getInstance()->delete('plugin_to_install');
|
350 |
-
}
|
351 |
-
|
352 |
-
/**
|
353 |
-
* Will fire when user delete plugin through admin panel.
|
354 |
-
* This action will happen if admin at least once
|
355 |
-
* activated the plugin.
|
356 |
-
*
|
357 |
-
* @return void
|
358 |
-
* @throws \Exception
|
359 |
-
*/
|
360 |
-
public static function onUninstallPluginListener()
|
361 |
-
{
|
362 |
-
$factory = AccountDataFactory::instance();
|
363 |
-
|
364 |
-
$pluginFile = substr(current_filter(), strlen( 'uninstall_' ));
|
365 |
-
|
366 |
-
$account = $factory->getAccountDataByBasePath($pluginFile);
|
367 |
-
|
368 |
-
// If account somehow is not found, exit the execution
|
369 |
-
if (!$account) return;
|
370 |
-
|
371 |
-
$analyst = Analyst::getInstance();
|
372 |
-
|
373 |
-
$collector = new Collector($analyst);
|
374 |
-
|
375 |
-
$requestor = new ApiRequestor($account->getId(), $account->getSecret(), $analyst->getApiBase());
|
376 |
-
|
377 |
-
// Just send request to log uninstall event not caring about response
|
378 |
-
UninstallRequest::make($collector, $account->getId(), $account->getPath())->execute($requestor);
|
379 |
-
|
380 |
-
$factory->sync();
|
381 |
-
}
|
382 |
-
|
383 |
-
/**
|
384 |
-
* Fires when used verified his account
|
385 |
-
*/
|
386 |
-
public function onInstallVerifiedListener()
|
387 |
-
{
|
388 |
-
$factory = NoticeFactory::instance();
|
389 |
-
|
390 |
-
$notice = Notice::make(
|
391 |
-
uniqid(),
|
392 |
-
$this->getId(),
|
393 |
-
'Thank you for confirming your email.',
|
394 |
-
$this->collector->getPluginName($this->path)
|
395 |
-
);
|
396 |
-
|
397 |
-
$factory->addNotice($notice);
|
398 |
-
|
399 |
-
// Remove confirmation notification
|
400 |
-
$confirmationNotificationId = DatabaseCache::getInstance()->pop(sprintf('account_email_confirmation_%s', $this->getId()));
|
401 |
-
$factory->remove($confirmationNotificationId);
|
402 |
-
|
403 |
-
AccountDataFactory::syncData();
|
404 |
-
|
405 |
-
wp_send_json_success();
|
406 |
-
}
|
407 |
-
|
408 |
-
/**
|
409 |
-
* Will fire when wp renders plugin
|
410 |
-
* action buttons
|
411 |
-
*
|
412 |
-
* @param $defaultLinks
|
413 |
-
* @return array
|
414 |
-
*/
|
415 |
-
public function onRenderActionLinksHook($defaultLinks)
|
416 |
-
{
|
417 |
-
$customLinks = [];
|
418 |
-
|
419 |
-
$customLinks[] = $this->isOptedIn()
|
420 |
-
? '<a class="analyst-action-opt analyst-opt-out" analyst-plugin-id="' . $this->getId() . '" analyst-plugin-signed="' . (int) $this->isSigned() . '">Opt Out</a>'
|
421 |
-
: '<a class="analyst-action-opt analyst-opt-in" analyst-plugin-id="' . $this->getId() . '" analyst-plugin-signed="' . (int) $this->isSigned() . '">Opt In</a>';
|
422 |
-
|
423 |
-
// Append anchor to find specific deactivation link
|
424 |
-
if (isset($defaultLinks['deactivate'])) {
|
425 |
-
$defaultLinks['deactivate'] .= '<span analyst-plugin-id="' . $this->getId() . '" analyst-plugin-opted-in="' . (int) $this->isOptedIn() . '"></span>';
|
426 |
-
}
|
427 |
-
|
428 |
-
return array_merge($customLinks, $defaultLinks);
|
429 |
-
}
|
430 |
-
|
431 |
-
/**
|
432 |
-
* @return AccountData
|
433 |
-
*/
|
434 |
-
public function getData()
|
435 |
-
{
|
436 |
-
return $this->data;
|
437 |
-
}
|
438 |
-
|
439 |
-
/**
|
440 |
-
* @param AccountData $data
|
441 |
-
*/
|
442 |
-
public function setData(AccountData $data)
|
443 |
-
{
|
444 |
-
$this->data = $data;
|
445 |
-
|
446 |
-
$this->setIsOptedIn($data->isOptedIn());
|
447 |
-
$this->setIsInstalled($data->isInstalled());
|
448 |
-
$this->setIsSigned($data->isSigned());
|
449 |
-
$this->setIsInstallResolved($data->isInstallResolved());
|
450 |
-
}
|
451 |
-
|
452 |
-
/**
|
453 |
-
* Resolves valid action name
|
454 |
-
* based on client id
|
455 |
-
*
|
456 |
-
* @param $action
|
457 |
-
* @return string
|
458 |
-
*/
|
459 |
-
private function resolveActionName($action)
|
460 |
-
{
|
461 |
-
return sprintf('%s_%s', $action, $this->id);
|
462 |
-
}
|
463 |
-
|
464 |
-
/**
|
465 |
-
* Register action for current plugin
|
466 |
-
*
|
467 |
-
* @param $action
|
468 |
-
* @param $callback
|
469 |
-
*/
|
470 |
-
private function addFilter($action, $callback)
|
471 |
-
{
|
472 |
-
$validAction = sprintf('%s_%s', $action, $this->basePluginPath);
|
473 |
-
|
474 |
-
add_filter($validAction, $callback, 10);
|
475 |
-
}
|
476 |
-
|
477 |
-
/**
|
478 |
-
* Add ajax action for current plugin
|
479 |
-
*
|
480 |
-
* @param $action
|
481 |
-
* @param $callback
|
482 |
-
* @param bool $raw Format action ??
|
483 |
-
*/
|
484 |
-
private function addAjax($action, $callback, $raw = false)
|
485 |
-
{
|
486 |
-
$validAction = $raw ? $action : sprintf('%s%s', 'wp_ajax_', $this->resolveActionName($action));
|
487 |
-
|
488 |
-
add_action($validAction, $callback);
|
489 |
-
}
|
490 |
-
|
491 |
-
/**
|
492 |
-
* @return bool
|
493 |
-
*/
|
494 |
-
public function isSigned()
|
495 |
-
{
|
496 |
-
return $this->isSigned;
|
497 |
-
}
|
498 |
-
|
499 |
-
/**
|
500 |
-
* @param bool $isSigned
|
501 |
-
*/
|
502 |
-
public function setIsSigned($isSigned)
|
503 |
-
{
|
504 |
-
$this->data->setIsSigned($isSigned);
|
505 |
-
|
506 |
-
$this->isSigned = $isSigned;
|
507 |
-
}
|
508 |
-
|
509 |
-
/**
|
510 |
-
* @return RequestorContract
|
511 |
-
*/
|
512 |
-
public function getRequestor()
|
513 |
-
{
|
514 |
-
return $this->requestor;
|
515 |
-
}
|
516 |
-
|
517 |
-
/**
|
518 |
-
* @param RequestorContract $requestor
|
519 |
-
*/
|
520 |
-
public function setRequestor(RequestorContract $requestor)
|
521 |
-
{
|
522 |
-
$this->requestor = $requestor;
|
523 |
-
}
|
524 |
-
|
525 |
-
/**
|
526 |
-
* @return string
|
527 |
-
*/
|
528 |
-
public function getClientSecret()
|
529 |
-
{
|
530 |
-
return $this->clientSecret;
|
531 |
-
}
|
532 |
-
|
533 |
-
/**
|
534 |
-
* @return Collector
|
535 |
-
*/
|
536 |
-
public function getCollector()
|
537 |
-
{
|
538 |
-
return $this->collector;
|
539 |
-
}
|
540 |
-
|
541 |
-
/**
|
542 |
-
* @param Collector $collector
|
543 |
-
*/
|
544 |
-
public function setCollector(Collector $collector)
|
545 |
-
{
|
546 |
-
$this->collector = $collector;
|
547 |
-
}
|
548 |
-
|
549 |
-
/**
|
550 |
-
* Do we allowing logging
|
551 |
-
*
|
552 |
-
* @return bool
|
553 |
-
*/
|
554 |
-
public function isAllowingLogging()
|
555 |
-
{
|
556 |
-
return $this->isOptedIn;
|
557 |
-
}
|
558 |
-
|
559 |
-
/**
|
560 |
-
* @return string
|
561 |
-
*/
|
562 |
-
public function getBasePluginPath()
|
563 |
-
{
|
564 |
-
return $this->basePluginPath;
|
565 |
-
}
|
566 |
-
|
567 |
-
/**
|
568 |
-
* @return bool
|
569 |
-
*/
|
570 |
-
public function isInstallResolved()
|
571 |
-
{
|
572 |
-
return $this->isInstallResolved;
|
573 |
-
}
|
574 |
-
|
575 |
-
/**
|
576 |
-
* @param bool $isInstallResolved
|
577 |
-
*/
|
578 |
-
public function setIsInstallResolved($isInstallResolved)
|
579 |
-
{
|
580 |
-
$this->data->setIsInstallResolved($isInstallResolved);
|
581 |
-
|
582 |
-
$this->isInstallResolved = $isInstallResolved;
|
583 |
-
}
|
584 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Account;
|
4 |
+
|
5 |
+
use Analyst\Analyst;
|
6 |
+
use Analyst\ApiRequestor;
|
7 |
+
use Analyst\Cache\DatabaseCache;
|
8 |
+
use Analyst\Collector;
|
9 |
+
use Analyst\Http\Requests\ActivateRequest;
|
10 |
+
use Analyst\Http\Requests\DeactivateRequest;
|
11 |
+
use Analyst\Http\Requests\InstallRequest;
|
12 |
+
use Analyst\Http\Requests\OptInRequest;
|
13 |
+
use Analyst\Http\Requests\OptOutRequest;
|
14 |
+
use Analyst\Http\Requests\UninstallRequest;
|
15 |
+
use Analyst\Notices\Notice;
|
16 |
+
use Analyst\Notices\NoticeFactory;
|
17 |
+
use Analyst\Contracts\TrackerContract;
|
18 |
+
use Analyst\Contracts\RequestorContract;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Class Account
|
22 |
+
*
|
23 |
+
* This is plugin's account object
|
24 |
+
*/
|
25 |
+
class Account implements TrackerContract
|
26 |
+
{
|
27 |
+
/**
|
28 |
+
* Account id
|
29 |
+
*
|
30 |
+
* @var string
|
31 |
+
*/
|
32 |
+
protected $id;
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Basename of plugin
|
36 |
+
*
|
37 |
+
* @var string
|
38 |
+
*/
|
39 |
+
protected $path;
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Whether plugin is active or not
|
43 |
+
*
|
44 |
+
* @var bool
|
45 |
+
*/
|
46 |
+
protected $isInstalled = false;
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Is user sign in for data tracking
|
50 |
+
*
|
51 |
+
* @var bool
|
52 |
+
*/
|
53 |
+
protected $isOptedIn = false;
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Is user accepted permissions grant
|
57 |
+
* for collection site data
|
58 |
+
*
|
59 |
+
* @var bool
|
60 |
+
*/
|
61 |
+
protected $isSigned = false;
|
62 |
+
|
63 |
+
/**
|
64 |
+
* Is user ever resolved install modal window?
|
65 |
+
*
|
66 |
+
* @var bool
|
67 |
+
*/
|
68 |
+
protected $isInstallResolved = false;
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Public secret code
|
72 |
+
*
|
73 |
+
* @var string
|
74 |
+
*/
|
75 |
+
protected $clientSecret;
|
76 |
+
|
77 |
+
/**
|
78 |
+
* @var AccountData
|
79 |
+
*/
|
80 |
+
protected $data;
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Base plugin path
|
84 |
+
*
|
85 |
+
* @var string
|
86 |
+
*/
|
87 |
+
protected $basePluginPath;
|
88 |
+
|
89 |
+
/**
|
90 |
+
* @var RequestorContract
|
91 |
+
*/
|
92 |
+
protected $requestor;
|
93 |
+
|
94 |
+
/**
|
95 |
+
* @var Collector
|
96 |
+
*/
|
97 |
+
protected $collector;
|
98 |
+
|
99 |
+
/**
|
100 |
+
* Account constructor.
|
101 |
+
* @param $id
|
102 |
+
* @param $secret
|
103 |
+
* @param $baseDir
|
104 |
+
*/
|
105 |
+
public function __construct($id, $secret, $baseDir)
|
106 |
+
{
|
107 |
+
$this->id = $id;
|
108 |
+
$this->clientSecret = $secret;
|
109 |
+
|
110 |
+
$this->path = $baseDir;
|
111 |
+
|
112 |
+
$this->basePluginPath = plugin_basename($baseDir);
|
113 |
+
}
|
114 |
+
|
115 |
+
/**
|
116 |
+
* @return string
|
117 |
+
*/
|
118 |
+
public function getPath()
|
119 |
+
{
|
120 |
+
return $this->path;
|
121 |
+
}
|
122 |
+
|
123 |
+
/**
|
124 |
+
* @param string $path
|
125 |
+
*/
|
126 |
+
public function setPath($path)
|
127 |
+
{
|
128 |
+
$this->data->setPath($path);
|
129 |
+
|
130 |
+
$this->path = $path;
|
131 |
+
}
|
132 |
+
|
133 |
+
/**
|
134 |
+
* @return bool
|
135 |
+
*/
|
136 |
+
public function isOptedIn()
|
137 |
+
{
|
138 |
+
return $this->isOptedIn;
|
139 |
+
}
|
140 |
+
|
141 |
+
/**
|
142 |
+
* @param bool $isOptedIn
|
143 |
+
*/
|
144 |
+
public function setIsOptedIn($isOptedIn)
|
145 |
+
{
|
146 |
+
$this->data->setIsOptedIn($isOptedIn);
|
147 |
+
|
148 |
+
$this->isOptedIn = $isOptedIn;
|
149 |
+
}
|
150 |
+
|
151 |
+
/**
|
152 |
+
* Whether plugin is active
|
153 |
+
*
|
154 |
+
* @return bool
|
155 |
+
*/
|
156 |
+
public function isActive()
|
157 |
+
{
|
158 |
+
return is_plugin_active($this->path);
|
159 |
+
}
|
160 |
+
|
161 |
+
/**
|
162 |
+
* @param string $id
|
163 |
+
*/
|
164 |
+
public function setId($id)
|
165 |
+
{
|
166 |
+
$this->id = $id;
|
167 |
+
}
|
168 |
+
|
169 |
+
/**
|
170 |
+
* @return string
|
171 |
+
*/
|
172 |
+
public function getId()
|
173 |
+
{
|
174 |
+
return $this->id;
|
175 |
+
}
|
176 |
+
|
177 |
+
/**
|
178 |
+
* @return bool
|
179 |
+
*/
|
180 |
+
public function isInstalled()
|
181 |
+
{
|
182 |
+
return $this->isInstalled;
|
183 |
+
}
|
184 |
+
|
185 |
+
/**
|
186 |
+
* @param bool $isInstalled
|
187 |
+
*/
|
188 |
+
public function setIsInstalled($isInstalled)
|
189 |
+
{
|
190 |
+
$this->data->setIsInstalled($isInstalled);
|
191 |
+
|
192 |
+
$this->isInstalled = $isInstalled;
|
193 |
+
}
|
194 |
+
|
195 |
+
/**
|
196 |
+
* Should register activation and deactivation
|
197 |
+
* event hooks
|
198 |
+
*
|
199 |
+
* @return void
|
200 |
+
*/
|
201 |
+
public function registerHooks()
|
202 |
+
{
|
203 |
+
register_activation_hook($this->basePluginPath, [&$this, 'onActivePluginListener']);
|
204 |
+
register_uninstall_hook($this->basePluginPath, ['Account\Account', 'onUninstallPluginListener']);
|
205 |
+
|
206 |
+
$this->addFilter('plugin_action_links', [&$this, 'onRenderActionLinksHook']);
|
207 |
+
|
208 |
+
$this->addAjax('analyst_opt_in', [&$this, 'onOptInListener']);
|
209 |
+
$this->addAjax('analyst_opt_out', [&$this, 'onOptOutListener']);
|
210 |
+
$this->addAjax('analyst_plugin_deactivate', [&$this, 'onDeactivatePluginListener']);
|
211 |
+
$this->addAjax('analyst_install', [&$this, 'onInstallListener']);
|
212 |
+
$this->addAjax('analyst_skip_install', [&$this, 'onSkipInstallListener']);
|
213 |
+
$this->addAjax('analyst_install_verified', [&$this, 'onInstallVerifiedListener']);
|
214 |
+
}
|
215 |
+
|
216 |
+
/**
|
217 |
+
* Will fire when admin activates plugin
|
218 |
+
*
|
219 |
+
* @return void
|
220 |
+
*/
|
221 |
+
public function onActivePluginListener()
|
222 |
+
{
|
223 |
+
if (!$this->isInstallResolved()) {
|
224 |
+
DatabaseCache::getInstance()->put('plugin_to_install', $this->id);
|
225 |
+
}
|
226 |
+
|
227 |
+
if (!$this->isAllowingLogging()) return;
|
228 |
+
|
229 |
+
ActivateRequest::make($this->collector, $this->id, $this->path)
|
230 |
+
->execute($this->requestor);
|
231 |
+
|
232 |
+
$this->setIsInstalled(true);
|
233 |
+
|
234 |
+
AccountDataFactory::syncData();
|
235 |
+
}
|
236 |
+
|
237 |
+
/**
|
238 |
+
* Will fire when admin deactivates plugin
|
239 |
+
*
|
240 |
+
* @return void
|
241 |
+
*/
|
242 |
+
public function onDeactivatePluginListener()
|
243 |
+
{
|
244 |
+
if (!$this->isAllowingLogging()) return;
|
245 |
+
|
246 |
+
$question = isset($_POST['question']) ? stripslashes($_POST['question']) : null;
|
247 |
+
$reason = isset($_POST['reason']) ? stripslashes($_POST['reason']) : null;
|
248 |
+
|
249 |
+
DeactivateRequest::make($this->collector, $this->id, $this->path, $question, $reason)
|
250 |
+
->execute($this->requestor);
|
251 |
+
|
252 |
+
$this->setIsInstalled(false);
|
253 |
+
|
254 |
+
AccountDataFactory::syncData();
|
255 |
+
|
256 |
+
wp_send_json_success();
|
257 |
+
}
|
258 |
+
|
259 |
+
/**
|
260 |
+
* Will fire when user opted in
|
261 |
+
*
|
262 |
+
* @return void
|
263 |
+
*/
|
264 |
+
public function onOptInListener()
|
265 |
+
{
|
266 |
+
OptInRequest::make($this->collector, $this->id, $this->path)->execute($this->requestor);
|
267 |
+
|
268 |
+
$this->setIsOptedIn(true);
|
269 |
+
|
270 |
+
AccountDataFactory::syncData();
|
271 |
+
|
272 |
+
wp_die();
|
273 |
+
}
|
274 |
+
|
275 |
+
/**
|
276 |
+
* Will fire when user opted out
|
277 |
+
*
|
278 |
+
* @return void
|
279 |
+
*/
|
280 |
+
public function onOptOutListener()
|
281 |
+
{
|
282 |
+
OptOutRequest::make($this->collector, $this->id, $this->path)->execute($this->requestor);
|
283 |
+
|
284 |
+
$this->setIsOptedIn(false);
|
285 |
+
|
286 |
+
AccountDataFactory::syncData();
|
287 |
+
|
288 |
+
wp_send_json_success();
|
289 |
+
}
|
290 |
+
|
291 |
+
/**
|
292 |
+
* Will fire when user accept opt-in
|
293 |
+
* at first time
|
294 |
+
*
|
295 |
+
* @return void
|
296 |
+
*/
|
297 |
+
public function onInstallListener()
|
298 |
+
{
|
299 |
+
$cache = DatabaseCache::getInstance();
|
300 |
+
|
301 |
+
// Set flag to true which indicates that install is resolved
|
302 |
+
// also remove install plugin id from cache
|
303 |
+
$this->setIsInstallResolved(true);
|
304 |
+
$cache->delete('plugin_to_install');
|
305 |
+
|
306 |
+
InstallRequest::make($this->collector, $this->id, $this->path)->execute($this->requestor);
|
307 |
+
|
308 |
+
$this->setIsSigned(true);
|
309 |
+
|
310 |
+
$this->setIsOptedIn(true);
|
311 |
+
|
312 |
+
$factory = NoticeFactory::instance();
|
313 |
+
|
314 |
+
$message = sprintf('Please confirm your email by clicking on the link we sent to %s. This makes sure you’re not a bot.', $this->collector->getGeneralEmailAddress());
|
315 |
+
|
316 |
+
$notificationId = uniqid();
|
317 |
+
|
318 |
+
$notice = Notice::make(
|
319 |
+
$notificationId,
|
320 |
+
$this->getId(),
|
321 |
+
$message,
|
322 |
+
$this->collector->getPluginName($this->path)
|
323 |
+
);
|
324 |
+
|
325 |
+
$factory->addNotice($notice);
|
326 |
+
|
327 |
+
AccountDataFactory::syncData();
|
328 |
+
|
329 |
+
// Set email confirmation notification id to cache
|
330 |
+
// se we can extract and remove it when user confirmed email
|
331 |
+
$cache->put(
|
332 |
+
sprintf('account_email_confirmation_%s', $this->getId()),
|
333 |
+
$notificationId
|
334 |
+
);
|
335 |
+
|
336 |
+
wp_send_json_success();
|
337 |
+
}
|
338 |
+
|
339 |
+
/**
|
340 |
+
* Will fire when user skipped installation
|
341 |
+
*
|
342 |
+
* @return void
|
343 |
+
*/
|
344 |
+
public function onSkipInstallListener()
|
345 |
+
{
|
346 |
+
// Set flag to true which indicates that install is resolved
|
347 |
+
// also remove install plugin id from cache
|
348 |
+
$this->setIsInstallResolved(true);
|
349 |
+
DatabaseCache::getInstance()->delete('plugin_to_install');
|
350 |
+
}
|
351 |
+
|
352 |
+
/**
|
353 |
+
* Will fire when user delete plugin through admin panel.
|
354 |
+
* This action will happen if admin at least once
|
355 |
+
* activated the plugin.
|
356 |
+
*
|
357 |
+
* @return void
|
358 |
+
* @throws \Exception
|
359 |
+
*/
|
360 |
+
public static function onUninstallPluginListener()
|
361 |
+
{
|
362 |
+
$factory = AccountDataFactory::instance();
|
363 |
+
|
364 |
+
$pluginFile = substr(current_filter(), strlen( 'uninstall_' ));
|
365 |
+
|
366 |
+
$account = $factory->getAccountDataByBasePath($pluginFile);
|
367 |
+
|
368 |
+
// If account somehow is not found, exit the execution
|
369 |
+
if (!$account) return;
|
370 |
+
|
371 |
+
$analyst = Analyst::getInstance();
|
372 |
+
|
373 |
+
$collector = new Collector($analyst);
|
374 |
+
|
375 |
+
$requestor = new ApiRequestor($account->getId(), $account->getSecret(), $analyst->getApiBase());
|
376 |
+
|
377 |
+
// Just send request to log uninstall event not caring about response
|
378 |
+
UninstallRequest::make($collector, $account->getId(), $account->getPath())->execute($requestor);
|
379 |
+
|
380 |
+
$factory->sync();
|
381 |
+
}
|
382 |
+
|
383 |
+
/**
|
384 |
+
* Fires when used verified his account
|
385 |
+
*/
|
386 |
+
public function onInstallVerifiedListener()
|
387 |
+
{
|
388 |
+
$factory = NoticeFactory::instance();
|
389 |
+
|
390 |
+
$notice = Notice::make(
|
391 |
+
uniqid(),
|
392 |
+
$this->getId(),
|
393 |
+
'Thank you for confirming your email.',
|
394 |
+
$this->collector->getPluginName($this->path)
|
395 |
+
);
|
396 |
+
|
397 |
+
$factory->addNotice($notice);
|
398 |
+
|
399 |
+
// Remove confirmation notification
|
400 |
+
$confirmationNotificationId = DatabaseCache::getInstance()->pop(sprintf('account_email_confirmation_%s', $this->getId()));
|
401 |
+
$factory->remove($confirmationNotificationId);
|
402 |
+
|
403 |
+
AccountDataFactory::syncData();
|
404 |
+
|
405 |
+
wp_send_json_success();
|
406 |
+
}
|
407 |
+
|
408 |
+
/**
|
409 |
+
* Will fire when wp renders plugin
|
410 |
+
* action buttons
|
411 |
+
*
|
412 |
+
* @param $defaultLinks
|
413 |
+
* @return array
|
414 |
+
*/
|
415 |
+
public function onRenderActionLinksHook($defaultLinks)
|
416 |
+
{
|
417 |
+
$customLinks = [];
|
418 |
+
|
419 |
+
$customLinks[] = $this->isOptedIn()
|
420 |
+
? '<a class="analyst-action-opt analyst-opt-out" analyst-plugin-id="' . $this->getId() . '" analyst-plugin-signed="' . (int) $this->isSigned() . '">Opt Out</a>'
|
421 |
+
: '<a class="analyst-action-opt analyst-opt-in" analyst-plugin-id="' . $this->getId() . '" analyst-plugin-signed="' . (int) $this->isSigned() . '">Opt In</a>';
|
422 |
+
|
423 |
+
// Append anchor to find specific deactivation link
|
424 |
+
if (isset($defaultLinks['deactivate'])) {
|
425 |
+
$defaultLinks['deactivate'] .= '<span analyst-plugin-id="' . $this->getId() . '" analyst-plugin-opted-in="' . (int) $this->isOptedIn() . '"></span>';
|
426 |
+
}
|
427 |
+
|
428 |
+
return array_merge($customLinks, $defaultLinks);
|
429 |
+
}
|
430 |
+
|
431 |
+
/**
|
432 |
+
* @return AccountData
|
433 |
+
*/
|
434 |
+
public function getData()
|
435 |
+
{
|
436 |
+
return $this->data;
|
437 |
+
}
|
438 |
+
|
439 |
+
/**
|
440 |
+
* @param AccountData $data
|
441 |
+
*/
|
442 |
+
public function setData(AccountData $data)
|
443 |
+
{
|
444 |
+
$this->data = $data;
|
445 |
+
|
446 |
+
$this->setIsOptedIn($data->isOptedIn());
|
447 |
+
$this->setIsInstalled($data->isInstalled());
|
448 |
+
$this->setIsSigned($data->isSigned());
|
449 |
+
$this->setIsInstallResolved($data->isInstallResolved());
|
450 |
+
}
|
451 |
+
|
452 |
+
/**
|
453 |
+
* Resolves valid action name
|
454 |
+
* based on client id
|
455 |
+
*
|
456 |
+
* @param $action
|
457 |
+
* @return string
|
458 |
+
*/
|
459 |
+
private function resolveActionName($action)
|
460 |
+
{
|
461 |
+
return sprintf('%s_%s', $action, $this->id);
|
462 |
+
}
|
463 |
+
|
464 |
+
/**
|
465 |
+
* Register action for current plugin
|
466 |
+
*
|
467 |
+
* @param $action
|
468 |
+
* @param $callback
|
469 |
+
*/
|
470 |
+
private function addFilter($action, $callback)
|
471 |
+
{
|
472 |
+
$validAction = sprintf('%s_%s', $action, $this->basePluginPath);
|
473 |
+
|
474 |
+
add_filter($validAction, $callback, 10);
|
475 |
+
}
|
476 |
+
|
477 |
+
/**
|
478 |
+
* Add ajax action for current plugin
|
479 |
+
*
|
480 |
+
* @param $action
|
481 |
+
* @param $callback
|
482 |
+
* @param bool $raw Format action ??
|
483 |
+
*/
|
484 |
+
private function addAjax($action, $callback, $raw = false)
|
485 |
+
{
|
486 |
+
$validAction = $raw ? $action : sprintf('%s%s', 'wp_ajax_', $this->resolveActionName($action));
|
487 |
+
|
488 |
+
add_action($validAction, $callback);
|
489 |
+
}
|
490 |
+
|
491 |
+
/**
|
492 |
+
* @return bool
|
493 |
+
*/
|
494 |
+
public function isSigned()
|
495 |
+
{
|
496 |
+
return $this->isSigned;
|
497 |
+
}
|
498 |
+
|
499 |
+
/**
|
500 |
+
* @param bool $isSigned
|
501 |
+
*/
|
502 |
+
public function setIsSigned($isSigned)
|
503 |
+
{
|
504 |
+
$this->data->setIsSigned($isSigned);
|
505 |
+
|
506 |
+
$this->isSigned = $isSigned;
|
507 |
+
}
|
508 |
+
|
509 |
+
/**
|
510 |
+
* @return RequestorContract
|
511 |
+
*/
|
512 |
+
public function getRequestor()
|
513 |
+
{
|
514 |
+
return $this->requestor;
|
515 |
+
}
|
516 |
+
|
517 |
+
/**
|
518 |
+
* @param RequestorContract $requestor
|
519 |
+
*/
|
520 |
+
public function setRequestor(RequestorContract $requestor)
|
521 |
+
{
|
522 |
+
$this->requestor = $requestor;
|
523 |
+
}
|
524 |
+
|
525 |
+
/**
|
526 |
+
* @return string
|
527 |
+
*/
|
528 |
+
public function getClientSecret()
|
529 |
+
{
|
530 |
+
return $this->clientSecret;
|
531 |
+
}
|
532 |
+
|
533 |
+
/**
|
534 |
+
* @return Collector
|
535 |
+
*/
|
536 |
+
public function getCollector()
|
537 |
+
{
|
538 |
+
return $this->collector;
|
539 |
+
}
|
540 |
+
|
541 |
+
/**
|
542 |
+
* @param Collector $collector
|
543 |
+
*/
|
544 |
+
public function setCollector(Collector $collector)
|
545 |
+
{
|
546 |
+
$this->collector = $collector;
|
547 |
+
}
|
548 |
+
|
549 |
+
/**
|
550 |
+
* Do we allowing logging
|
551 |
+
*
|
552 |
+
* @return bool
|
553 |
+
*/
|
554 |
+
public function isAllowingLogging()
|
555 |
+
{
|
556 |
+
return $this->isOptedIn;
|
557 |
+
}
|
558 |
+
|
559 |
+
/**
|
560 |
+
* @return string
|
561 |
+
*/
|
562 |
+
public function getBasePluginPath()
|
563 |
+
{
|
564 |
+
return $this->basePluginPath;
|
565 |
+
}
|
566 |
+
|
567 |
+
/**
|
568 |
+
* @return bool
|
569 |
+
*/
|
570 |
+
public function isInstallResolved()
|
571 |
+
{
|
572 |
+
return $this->isInstallResolved;
|
573 |
+
}
|
574 |
+
|
575 |
+
/**
|
576 |
+
* @param bool $isInstallResolved
|
577 |
+
*/
|
578 |
+
public function setIsInstallResolved($isInstallResolved)
|
579 |
+
{
|
580 |
+
$this->data->setIsInstallResolved($isInstallResolved);
|
581 |
+
|
582 |
+
$this->isInstallResolved = $isInstallResolved;
|
583 |
+
}
|
584 |
+
}
|
analyst/src/Account/AccountData.php
CHANGED
@@ -1,176 +1,176 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace Account;
|
4 |
-
|
5 |
-
/**
|
6 |
-
* Class AccountData is the data holder
|
7 |
-
* for Analyst\Account\Account class
|
8 |
-
* which is unserialized from database
|
9 |
-
*/
|
10 |
-
class AccountData
|
11 |
-
{
|
12 |
-
/**
|
13 |
-
* Account id
|
14 |
-
*
|
15 |
-
* @var string
|
16 |
-
*/
|
17 |
-
protected $id;
|
18 |
-
|
19 |
-
/**
|
20 |
-
* Account secret key
|
21 |
-
*
|
22 |
-
* @var string
|
23 |
-
*/
|
24 |
-
protected $secret;
|
25 |
-
|
26 |
-
/**
|
27 |
-
* Basename of plugin
|
28 |
-
*
|
29 |
-
* @var string
|
30 |
-
*/
|
31 |
-
protected $path;
|
32 |
-
|
33 |
-
/**
|
34 |
-
* Whether admin accepted opt in
|
35 |
-
* terms and permissions
|
36 |
-
*
|
37 |
-
* @var bool
|
38 |
-
*/
|
39 |
-
protected $isInstalled = false;
|
40 |
-
|
41 |
-
/**
|
42 |
-
* Is user sign in for data tracking
|
43 |
-
*
|
44 |
-
* @var bool
|
45 |
-
*/
|
46 |
-
protected $isOptedIn = false;
|
47 |
-
|
48 |
-
/**
|
49 |
-
* Is user accepted permissions grant
|
50 |
-
* for collection site data
|
51 |
-
*
|
52 |
-
* @var bool
|
53 |
-
*/
|
54 |
-
protected $isSigned = false;
|
55 |
-
|
56 |
-
/**
|
57 |
-
* Is user ever resolved install modal window?
|
58 |
-
*
|
59 |
-
* @var bool
|
60 |
-
*/
|
61 |
-
protected $isInstallResolved;
|
62 |
-
|
63 |
-
/**
|
64 |
-
* @return string
|
65 |
-
*/
|
66 |
-
public function getId()
|
67 |
-
{
|
68 |
-
return $this->id;
|
69 |
-
}
|
70 |
-
|
71 |
-
/**
|
72 |
-
* @param string $id
|
73 |
-
*/
|
74 |
-
public function setId($id)
|
75 |
-
{
|
76 |
-
$this->id = $id;
|
77 |
-
}
|
78 |
-
|
79 |
-
/**
|
80 |
-
* @param string $path
|
81 |
-
* @return AccountData
|
82 |
-
*/
|
83 |
-
public function setPath($path)
|
84 |
-
{
|
85 |
-
$this->path = $path;
|
86 |
-
return $this;
|
87 |
-
}
|
88 |
-
|
89 |
-
/**
|
90 |
-
* @return bool
|
91 |
-
*/
|
92 |
-
public function isInstalled()
|
93 |
-
{
|
94 |
-
return $this->isInstalled;
|
95 |
-
}
|
96 |
-
|
97 |
-
/**
|
98 |
-
* @param bool $isInstalled
|
99 |
-
*/
|
100 |
-
public function setIsInstalled($isInstalled)
|
101 |
-
{
|
102 |
-
$this->isInstalled = $isInstalled;
|
103 |
-
}
|
104 |
-
|
105 |
-
/**
|
106 |
-
* @return bool
|
107 |
-
*/
|
108 |
-
public function isOptedIn()
|
109 |
-
{
|
110 |
-
return $this->isOptedIn;
|
111 |
-
}
|
112 |
-
|
113 |
-
/**
|
114 |
-
* @param bool $isOptedIn
|
115 |
-
*/
|
116 |
-
public function setIsOptedIn($isOptedIn)
|
117 |
-
{
|
118 |
-
$this->isOptedIn = $isOptedIn;
|
119 |
-
}
|
120 |
-
|
121 |
-
/**
|
122 |
-
* @return bool
|
123 |
-
*/
|
124 |
-
public function isSigned()
|
125 |
-
{
|
126 |
-
return $this->isSigned;
|
127 |
-
}
|
128 |
-
|
129 |
-
/**
|
130 |
-
* @param bool $isSigned
|
131 |
-
*/
|
132 |
-
public function setIsSigned($isSigned)
|
133 |
-
{
|
134 |
-
$this->isSigned = $isSigned;
|
135 |
-
}
|
136 |
-
|
137 |
-
/**
|
138 |
-
* @return string
|
139 |
-
*/
|
140 |
-
public function getPath()
|
141 |
-
{
|
142 |
-
return $this->path;
|
143 |
-
}
|
144 |
-
|
145 |
-
/**
|
146 |
-
* @return string
|
147 |
-
*/
|
148 |
-
public function getSecret()
|
149 |
-
{
|
150 |
-
return $this->secret;
|
151 |
-
}
|
152 |
-
|
153 |
-
/**
|
154 |
-
* @param string $secret
|
155 |
-
*/
|
156 |
-
public function setSecret($secret)
|
157 |
-
{
|
158 |
-
$this->secret = $secret;
|
159 |
-
}
|
160 |
-
|
161 |
-
/**
|
162 |
-
* @return bool
|
163 |
-
*/
|
164 |
-
public function isInstallResolved()
|
165 |
-
{
|
166 |
-
return $this->isInstallResolved;
|
167 |
-
}
|
168 |
-
|
169 |
-
/**
|
170 |
-
* @param bool $isInstallResolved
|
171 |
-
*/
|
172 |
-
public function setIsInstallResolved($isInstallResolved)
|
173 |
-
{
|
174 |
-
$this->isInstallResolved = $isInstallResolved;
|
175 |
-
}
|
176 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Account;
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Class AccountData is the data holder
|
7 |
+
* for Analyst\Account\Account class
|
8 |
+
* which is unserialized from database
|
9 |
+
*/
|
10 |
+
class AccountData
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Account id
|
14 |
+
*
|
15 |
+
* @var string
|
16 |
+
*/
|
17 |
+
protected $id;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Account secret key
|
21 |
+
*
|
22 |
+
* @var string
|
23 |
+
*/
|
24 |
+
protected $secret;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Basename of plugin
|
28 |
+
*
|
29 |
+
* @var string
|
30 |
+
*/
|
31 |
+
protected $path;
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Whether admin accepted opt in
|
35 |
+
* terms and permissions
|
36 |
+
*
|
37 |
+
* @var bool
|
38 |
+
*/
|
39 |
+
protected $isInstalled = false;
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Is user sign in for data tracking
|
43 |
+
*
|
44 |
+
* @var bool
|
45 |
+
*/
|
46 |
+
protected $isOptedIn = false;
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Is user accepted permissions grant
|
50 |
+
* for collection site data
|
51 |
+
*
|
52 |
+
* @var bool
|
53 |
+
*/
|
54 |
+
protected $isSigned = false;
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Is user ever resolved install modal window?
|
58 |
+
*
|
59 |
+
* @var bool
|
60 |
+
*/
|
61 |
+
protected $isInstallResolved;
|
62 |
+
|
63 |
+
/**
|
64 |
+
* @return string
|
65 |
+
*/
|
66 |
+
public function getId()
|
67 |
+
{
|
68 |
+
return $this->id;
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* @param string $id
|
73 |
+
*/
|
74 |
+
public function setId($id)
|
75 |
+
{
|
76 |
+
$this->id = $id;
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* @param string $path
|
81 |
+
* @return AccountData
|
82 |
+
*/
|
83 |
+
public function setPath($path)
|
84 |
+
{
|
85 |
+
$this->path = $path;
|
86 |
+
return $this;
|
87 |
+
}
|
88 |
+
|
89 |
+
/**
|
90 |
+
* @return bool
|
91 |
+
*/
|
92 |
+
public function isInstalled()
|
93 |
+
{
|
94 |
+
return $this->isInstalled;
|
95 |
+
}
|
96 |
+
|
97 |
+
/**
|
98 |
+
* @param bool $isInstalled
|
99 |
+
*/
|
100 |
+
public function setIsInstalled($isInstalled)
|
101 |
+
{
|
102 |
+
$this->isInstalled = $isInstalled;
|
103 |
+
}
|
104 |
+
|
105 |
+
/**
|
106 |
+
* @return bool
|
107 |
+
*/
|
108 |
+
public function isOptedIn()
|
109 |
+
{
|
110 |
+
return $this->isOptedIn;
|
111 |
+
}
|
112 |
+
|
113 |
+
/**
|
114 |
+
* @param bool $isOptedIn
|
115 |
+
*/
|
116 |
+
public function setIsOptedIn($isOptedIn)
|
117 |
+
{
|
118 |
+
$this->isOptedIn = $isOptedIn;
|
119 |
+
}
|
120 |
+
|
121 |
+
/**
|
122 |
+
* @return bool
|
123 |
+
*/
|
124 |
+
public function isSigned()
|
125 |
+
{
|
126 |
+
return $this->isSigned;
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* @param bool $isSigned
|
131 |
+
*/
|
132 |
+
public function setIsSigned($isSigned)
|
133 |
+
{
|
134 |
+
$this->isSigned = $isSigned;
|
135 |
+
}
|
136 |
+
|
137 |
+
/**
|
138 |
+
* @return string
|
139 |
+
*/
|
140 |
+
public function getPath()
|
141 |
+
{
|
142 |
+
return $this->path;
|
143 |
+
}
|
144 |
+
|
145 |
+
/**
|
146 |
+
* @return string
|
147 |
+
*/
|
148 |
+
public function getSecret()
|
149 |
+
{
|
150 |
+
return $this->secret;
|
151 |
+
}
|
152 |
+
|
153 |
+
/**
|
154 |
+
* @param string $secret
|
155 |
+
*/
|
156 |
+
public function setSecret($secret)
|
157 |
+
{
|
158 |
+
$this->secret = $secret;
|
159 |
+
}
|
160 |
+
|
161 |
+
/**
|
162 |
+
* @return bool
|
163 |
+
*/
|
164 |
+
public function isInstallResolved()
|
165 |
+
{
|
166 |
+
return $this->isInstallResolved;
|
167 |
+
}
|
168 |
+
|
169 |
+
/**
|
170 |
+
* @param bool $isInstallResolved
|
171 |
+
*/
|
172 |
+
public function setIsInstallResolved($isInstallResolved)
|
173 |
+
{
|
174 |
+
$this->isInstallResolved = $isInstallResolved;
|
175 |
+
}
|
176 |
+
}
|
analyst/src/Account/AccountDataFactory.php
CHANGED
@@ -1,125 +1,125 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace Account;
|
4 |
-
|
5 |
-
|
6 |
-
use Analyst\Core\AbstractFactory;
|
7 |
-
|
8 |
-
/**
|
9 |
-
* Class AccountDataFactory
|
10 |
-
*
|
11 |
-
* Holds information about this
|
12 |
-
* wordpress project plugins accounts
|
13 |
-
*
|
14 |
-
*/
|
15 |
-
class AccountDataFactory extends AbstractFactory
|
16 |
-
{
|
17 |
-
private static $instance;
|
18 |
-
|
19 |
-
CONST OPTIONS_KEY = 'analyst_accounts_data';
|
20 |
-
|
21 |
-
/**
|
22 |
-
* @var AccountData[]
|
23 |
-
*/
|
24 |
-
protected $accounts = [];
|
25 |
-
|
26 |
-
/**
|
27 |
-
* Read factory from options or make fresh instance
|
28 |
-
*
|
29 |
-
* @return static
|
30 |
-
*/
|
31 |
-
public static function instance()
|
32 |
-
{
|
33 |
-
if (!static::$instance) {
|
34 |
-
$raw = get_option(self::OPTIONS_KEY);
|
35 |
-
|
36 |
-
// In case object is already unserialized
|
37 |
-
// and instance of AccountDataFactory we
|
38 |
-
// return it, in other case deal with
|
39 |
-
// serialized string data
|
40 |
-
if ($raw instanceof self) {
|
41 |
-
static::$instance = $raw;
|
42 |
-
} else {
|
43 |
-
static::$instance = is_string($raw) ? static::unserialize($raw) : new self();
|
44 |
-
}
|
45 |
-
}
|
46 |
-
|
47 |
-
return static::$instance;
|
48 |
-
}
|
49 |
-
|
50 |
-
/**
|
51 |
-
* Sync this object data with cache
|
52 |
-
*/
|
53 |
-
public function sync()
|
54 |
-
{
|
55 |
-
update_option(self::OPTIONS_KEY, serialize($this));
|
56 |
-
}
|
57 |
-
|
58 |
-
/**
|
59 |
-
* Sync this instance data with cache
|
60 |
-
*/
|
61 |
-
public static function syncData()
|
62 |
-
{
|
63 |
-
static::instance()->sync();
|
64 |
-
}
|
65 |
-
|
66 |
-
/**
|
67 |
-
* Find plugin account data or create fresh one
|
68 |
-
*
|
69 |
-
* @param Account $account
|
70 |
-
* @return AccountData|null
|
71 |
-
*/
|
72 |
-
public function resolvePluginAccountData(Account $account)
|
73 |
-
{
|
74 |
-
$accountData = $this->findAccountDataById($account->getId());
|
75 |
-
|
76 |
-
if (!$accountData) {
|
77 |
-
$accountData = new AccountData();
|
78 |
-
|
79 |
-
// Set proper default values
|
80 |
-
$accountData->setPath($account->getPath());
|
81 |
-
$accountData->setId($account->getId());
|
82 |
-
$accountData->setSecret($account->getClientSecret());
|
83 |
-
|
84 |
-
array_push($this->accounts, $accountData);
|
85 |
-
}
|
86 |
-
|
87 |
-
return $accountData;
|
88 |
-
}
|
89 |
-
|
90 |
-
/**
|
91 |
-
* Should return account data by base path
|
92 |
-
*
|
93 |
-
* @param $basePath
|
94 |
-
* @return AccountData
|
95 |
-
*/
|
96 |
-
public function getAccountDataByBasePath($basePath)
|
97 |
-
{
|
98 |
-
foreach ($this->accounts as $iterable) {
|
99 |
-
$iterableBasePath = plugin_basename($iterable->getPath());
|
100 |
-
|
101 |
-
if ($iterableBasePath === $basePath) {
|
102 |
-
return $iterable;
|
103 |
-
}
|
104 |
-
}
|
105 |
-
|
106 |
-
return null;
|
107 |
-
}
|
108 |
-
|
109 |
-
/**
|
110 |
-
* Return account by id
|
111 |
-
*
|
112 |
-
* @param $id
|
113 |
-
* @return AccountData|null
|
114 |
-
*/
|
115 |
-
private function findAccountDataById($id)
|
116 |
-
{
|
117 |
-
foreach ($this->accounts as &$iterable) {
|
118 |
-
if ($iterable->getId() === $id) {
|
119 |
-
return $iterable;
|
120 |
-
}
|
121 |
-
}
|
122 |
-
|
123 |
-
return null;
|
124 |
-
}
|
125 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Account;
|
4 |
+
|
5 |
+
|
6 |
+
use Analyst\Core\AbstractFactory;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Class AccountDataFactory
|
10 |
+
*
|
11 |
+
* Holds information about this
|
12 |
+
* wordpress project plugins accounts
|
13 |
+
*
|
14 |
+
*/
|
15 |
+
class AccountDataFactory extends AbstractFactory
|
16 |
+
{
|
17 |
+
private static $instance;
|
18 |
+
|
19 |
+
CONST OPTIONS_KEY = 'analyst_accounts_data';
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @var AccountData[]
|
23 |
+
*/
|
24 |
+
protected $accounts = [];
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Read factory from options or make fresh instance
|
28 |
+
*
|
29 |
+
* @return static
|
30 |
+
*/
|
31 |
+
public static function instance()
|
32 |
+
{
|
33 |
+
if (!static::$instance) {
|
34 |
+
$raw = get_option(self::OPTIONS_KEY);
|
35 |
+
|
36 |
+
// In case object is already unserialized
|
37 |
+
// and instance of AccountDataFactory we
|
38 |
+
// return it, in other case deal with
|
39 |
+
// serialized string data
|
40 |
+
if ($raw instanceof self) {
|
41 |
+
static::$instance = $raw;
|
42 |
+
} else {
|
43 |
+
static::$instance = is_string($raw) ? static::unserialize($raw) : new self();
|
44 |
+
}
|
45 |
+
}
|
46 |
+
|
47 |
+
return static::$instance;
|
48 |
+
}
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Sync this object data with cache
|
52 |
+
*/
|
53 |
+
public function sync()
|
54 |
+
{
|
55 |
+
update_option(self::OPTIONS_KEY, serialize($this));
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Sync this instance data with cache
|
60 |
+
*/
|
61 |
+
public static function syncData()
|
62 |
+
{
|
63 |
+
static::instance()->sync();
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Find plugin account data or create fresh one
|
68 |
+
*
|
69 |
+
* @param Account $account
|
70 |
+
* @return AccountData|null
|
71 |
+
*/
|
72 |
+
public function resolvePluginAccountData(Account $account)
|
73 |
+
{
|
74 |
+
$accountData = $this->findAccountDataById($account->getId());
|
75 |
+
|
76 |
+
if (!$accountData) {
|
77 |
+
$accountData = new AccountData();
|
78 |
+
|
79 |
+
// Set proper default values
|
80 |
+
$accountData->setPath($account->getPath());
|
81 |
+
$accountData->setId($account->getId());
|
82 |
+
$accountData->setSecret($account->getClientSecret());
|
83 |
+
|
84 |
+
array_push($this->accounts, $accountData);
|
85 |
+
}
|
86 |
+
|
87 |
+
return $accountData;
|
88 |
+
}
|
89 |
+
|
90 |
+
/**
|
91 |
+
* Should return account data by base path
|
92 |
+
*
|
93 |
+
* @param $basePath
|
94 |
+
* @return AccountData
|
95 |
+
*/
|
96 |
+
public function getAccountDataByBasePath($basePath)
|
97 |
+
{
|
98 |
+
foreach ($this->accounts as $iterable) {
|
99 |
+
$iterableBasePath = plugin_basename($iterable->getPath());
|
100 |
+
|
101 |
+
if ($iterableBasePath === $basePath) {
|
102 |
+
return $iterable;
|
103 |
+
}
|
104 |
+
}
|
105 |
+
|
106 |
+
return null;
|
107 |
+
}
|
108 |
+
|
109 |
+
/**
|
110 |
+
* Return account by id
|
111 |
+
*
|
112 |
+
* @param $id
|
113 |
+
* @return AccountData|null
|
114 |
+
*/
|
115 |
+
private function findAccountDataById($id)
|
116 |
+
{
|
117 |
+
foreach ($this->accounts as &$iterable) {
|
118 |
+
if ($iterable->getId() === $id) {
|
119 |
+
return $iterable;
|
120 |
+
}
|
121 |
+
}
|
122 |
+
|
123 |
+
return null;
|
124 |
+
}
|
125 |
+
}
|
analyst/src/Analyst.php
CHANGED
@@ -1,167 +1,167 @@
|
|
1 |
-
<?php
|
2 |
-
namespace Analyst;
|
3 |
-
|
4 |
-
use Account\Account;
|
5 |
-
use Account\AccountDataFactory;
|
6 |
-
use Analyst\Contracts\AnalystContract;
|
7 |
-
use Analyst\Contracts\RequestorContract;
|
8 |
-
|
9 |
-
class Analyst implements AnalystContract
|
10 |
-
{
|
11 |
-
/**
|
12 |
-
* All plugin's accounts
|
13 |
-
*
|
14 |
-
* @var array
|
15 |
-
*/
|
16 |
-
protected $accounts = array();
|
17 |
-
|
18 |
-
/**
|
19 |
-
* @var Mutator
|
20 |
-
*/
|
21 |
-
protected $mutator;
|
22 |
-
|
23 |
-
/**
|
24 |
-
* @var AccountDataFactory
|
25 |
-
*/
|
26 |
-
protected $accountDataFactory;
|
27 |
-
|
28 |
-
/**
|
29 |
-
* Base url to api
|
30 |
-
*
|
31 |
-
* @var string
|
32 |
-
*/
|
33 |
-
protected $apiBase = 'https://feedback.sellcodes.com/api/v1';
|
34 |
-
|
35 |
-
/**
|
36 |
-
* @var Collector
|
37 |
-
*/
|
38 |
-
protected $collector;
|
39 |
-
|
40 |
-
/**
|
41 |
-
* Singleton instance
|
42 |
-
*
|
43 |
-
* @var static
|
44 |
-
*/
|
45 |
-
protected static $instance;
|
46 |
-
|
47 |
-
/**
|
48 |
-
* Get instance of analyst
|
49 |
-
*
|
50 |
-
* @return Analyst
|
51 |
-
* @throws \Exception
|
52 |
-
*/
|
53 |
-
public static function getInstance()
|
54 |
-
{
|
55 |
-
if (!static::$instance) {
|
56 |
-
static::$instance = new Analyst();
|
57 |
-
}
|
58 |
-
|
59 |
-
return static::$instance;
|
60 |
-
}
|
61 |
-
|
62 |
-
protected function __construct()
|
63 |
-
{
|
64 |
-
$this->mutator = new Mutator();
|
65 |
-
|
66 |
-
$this->accountDataFactory = AccountDataFactory::instance();
|
67 |
-
|
68 |
-
$this->mutator->initialize();
|
69 |
-
|
70 |
-
$this->collector = new Collector($this);
|
71 |
-
|
72 |
-
$this->initialize();
|
73 |
-
}
|
74 |
-
|
75 |
-
/**
|
76 |
-
* Initialize rest of application
|
77 |
-
*/
|
78 |
-
public function initialize()
|
79 |
-
{
|
80 |
-
add_action('init', function () {
|
81 |
-
$this->collector->loadCurrentUser();
|
82 |
-
});
|
83 |
-
}
|
84 |
-
|
85 |
-
/**
|
86 |
-
* Register new account
|
87 |
-
*
|
88 |
-
* @param Account $account
|
89 |
-
* @return Analyst
|
90 |
-
* @throws \Exception
|
91 |
-
*/
|
92 |
-
public function registerAccount($account)
|
93 |
-
{
|
94 |
-
// Stop propagation when account is already registered
|
95 |
-
if ($this->isAccountRegistered($account)) {
|
96 |
-
return $this;
|
97 |
-
}
|
98 |
-
|
99 |
-
// Resolve account data from factory
|
100 |
-
$accountData = $this->accountDataFactory->resolvePluginAccountData($account);
|
101 |
-
|
102 |
-
$account->setData($accountData);
|
103 |
-
|
104 |
-
$account->setRequestor(
|
105 |
-
$this->resolveRequestorForAccount($account)
|
106 |
-
);
|
107 |
-
|
108 |
-
$account->setCollector($this->collector);
|
109 |
-
|
110 |
-
$account->registerHooks();
|
111 |
-
|
112 |
-
$this->accounts[$account->getId()] = $account;
|
113 |
-
|
114 |
-
return $this;
|
115 |
-
}
|
116 |
-
|
117 |
-
/**
|
118 |
-
* Must return version of analyst
|
119 |
-
*
|
120 |
-
* @return string
|
121 |
-
*/
|
122 |
-
public static function version()
|
123 |
-
{
|
124 |
-
$version = require __DIR__ . '/../version.php';
|
125 |
-
|
126 |
-
return $version['sdk'];
|
127 |
-
}
|
128 |
-
|
129 |
-
/**
|
130 |
-
* Is this account registered
|
131 |
-
*
|
132 |
-
* @param Account $account
|
133 |
-
* @return bool
|
134 |
-
*/
|
135 |
-
protected function isAccountRegistered($account)
|
136 |
-
{
|
137 |
-
return isset($this->accounts[$account->getId()]);
|
138 |
-
}
|
139 |
-
|
140 |
-
/**
|
141 |
-
* Resolves requestor for account
|
142 |
-
*
|
143 |
-
* @param Account $account
|
144 |
-
* @return RequestorContract
|
145 |
-
* @throws \Exception
|
146 |
-
*/
|
147 |
-
protected function resolveRequestorForAccount(Account $account)
|
148 |
-
{
|
149 |
-
$requestor = new ApiRequestor($account->getId(), $account->getClientSecret(), $this->apiBase);
|
150 |
-
|
151 |
-
// Set SDK version
|
152 |
-
$requestor->setDefaultHeader(
|
153 |
-
'x-analyst-client-user-agent',
|
154 |
-
sprintf('Analyst/%s', $this->version())
|
155 |
-
);
|
156 |
-
|
157 |
-
return $requestor;
|
158 |
-
}
|
159 |
-
|
160 |
-
/**
|
161 |
-
* @return string
|
162 |
-
*/
|
163 |
-
public function getApiBase()
|
164 |
-
{
|
165 |
-
return $this->apiBase;
|
166 |
-
}
|
167 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace Analyst;
|
3 |
+
|
4 |
+
use Account\Account;
|
5 |
+
use Account\AccountDataFactory;
|
6 |
+
use Analyst\Contracts\AnalystContract;
|
7 |
+
use Analyst\Contracts\RequestorContract;
|
8 |
+
|
9 |
+
class Analyst implements AnalystContract
|
10 |
+
{
|
11 |
+
/**
|
12 |
+
* All plugin's accounts
|
13 |
+
*
|
14 |
+
* @var array
|
15 |
+
*/
|
16 |
+
protected $accounts = array();
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @var Mutator
|
20 |
+
*/
|
21 |
+
protected $mutator;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @var AccountDataFactory
|
25 |
+
*/
|
26 |
+
protected $accountDataFactory;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Base url to api
|
30 |
+
*
|
31 |
+
* @var string
|
32 |
+
*/
|
33 |
+
protected $apiBase = 'https://feedback.sellcodes.com/api/v1';
|
34 |
+
|
35 |
+
/**
|
36 |
+
* @var Collector
|
37 |
+
*/
|
38 |
+
protected $collector;
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Singleton instance
|
42 |
+
*
|
43 |
+
* @var static
|
44 |
+
*/
|
45 |
+
protected static $instance;
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Get instance of analyst
|
49 |
+
*
|
50 |
+
* @return Analyst
|
51 |
+
* @throws \Exception
|
52 |
+
*/
|
53 |
+
public static function getInstance()
|
54 |
+
{
|
55 |
+
if (!static::$instance) {
|
56 |
+
static::$instance = new Analyst();
|
57 |
+
}
|
58 |
+
|
59 |
+
return static::$instance;
|
60 |
+
}
|
61 |
+
|
62 |
+
protected function __construct()
|
63 |
+
{
|
64 |
+
$this->mutator = new Mutator();
|
65 |
+
|
66 |
+
$this->accountDataFactory = AccountDataFactory::instance();
|
67 |
+
|
68 |
+
$this->mutator->initialize();
|
69 |
+
|
70 |
+
$this->collector = new Collector($this);
|
71 |
+
|
72 |
+
$this->initialize();
|
73 |
+
}
|
74 |
+
|
75 |
+
/**
|
76 |
+
* Initialize rest of application
|
77 |
+
*/
|
78 |
+
public function initialize()
|
79 |
+
{
|
80 |
+
add_action('init', function () {
|
81 |
+
$this->collector->loadCurrentUser();
|
82 |
+
});
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Register new account
|
87 |
+
*
|
88 |
+
* @param Account $account
|
89 |
+
* @return Analyst
|
90 |
+
* @throws \Exception
|
91 |
+
*/
|
92 |
+
public function registerAccount($account)
|
93 |
+
{
|
94 |
+
// Stop propagation when account is already registered
|
95 |
+
if ($this->isAccountRegistered($account)) {
|
96 |
+
return $this;
|
97 |
+
}
|
98 |
+
|
99 |
+
// Resolve account data from factory
|
100 |
+
$accountData = $this->accountDataFactory->resolvePluginAccountData($account);
|
101 |
+
|
102 |
+
$account->setData($accountData);
|
103 |
+
|
104 |
+
$account->setRequestor(
|
105 |
+
$this->resolveRequestorForAccount($account)
|
106 |
+
);
|
107 |
+
|
108 |
+
$account->setCollector($this->collector);
|
109 |
+
|
110 |
+
$account->registerHooks();
|
111 |
+
|
112 |
+
$this->accounts[$account->getId()] = $account;
|
113 |
+
|
114 |
+
return $this;
|
115 |
+
}
|
116 |
+
|
117 |
+
/**
|
118 |
+
* Must return version of analyst
|
119 |
+
*
|
120 |
+
* @return string
|
121 |
+
*/
|
122 |
+
public static function version()
|
123 |
+
{
|
124 |
+
$version = require __DIR__ . '/../version.php';
|
125 |
+
|
126 |
+
return $version['sdk'];
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* Is this account registered
|
131 |
+
*
|
132 |
+
* @param Account $account
|
133 |
+
* @return bool
|
134 |
+
*/
|
135 |
+
protected function isAccountRegistered($account)
|
136 |
+
{
|
137 |
+
return isset($this->accounts[$account->getId()]);
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Resolves requestor for account
|
142 |
+
*
|
143 |
+
* @param Account $account
|
144 |
+
* @return RequestorContract
|
145 |
+
* @throws \Exception
|
146 |
+
*/
|
147 |
+
protected function resolveRequestorForAccount(Account $account)
|
148 |
+
{
|
149 |
+
$requestor = new ApiRequestor($account->getId(), $account->getClientSecret(), $this->apiBase);
|
150 |
+
|
151 |
+
// Set SDK version
|
152 |
+
$requestor->setDefaultHeader(
|
153 |
+
'x-analyst-client-user-agent',
|
154 |
+
sprintf('Analyst/%s', $this->version())
|
155 |
+
);
|
156 |
+
|
157 |
+
return $requestor;
|
158 |
+
}
|
159 |
+
|
160 |
+
/**
|
161 |
+
* @return string
|
162 |
+
*/
|
163 |
+
public function getApiBase()
|
164 |
+
{
|
165 |
+
return $this->apiBase;
|
166 |
+
}
|
167 |
+
}
|
analyst/src/ApiRequestor.php
CHANGED
@@ -1,257 +1,257 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace Analyst;
|
4 |
-
|
5 |
-
use Exception;
|
6 |
-
use Analyst\Contracts\HttpClientContract;
|
7 |
-
use Analyst\Contracts\RequestorContract;
|
8 |
-
|
9 |
-
class ApiRequestor implements RequestorContract
|
10 |
-
{
|
11 |
-
/**
|
12 |
-
* Supported http client
|
13 |
-
*
|
14 |
-
* @var HttpClientContract
|
15 |
-
*/
|
16 |
-
protected $httpClient;
|
17 |
-
|
18 |
-
/**
|
19 |
-
* @var string
|
20 |
-
*/
|
21 |
-
protected $clientId;
|
22 |
-
|
23 |
-
/**
|
24 |
-
* @var string
|
25 |
-
*/
|
26 |
-
protected $clientSecret;
|
27 |
-
|
28 |
-
/**
|
29 |
-
* @var string
|
30 |
-
*/
|
31 |
-
protected $apiBase;
|
32 |
-
|
33 |
-
/**
|
34 |
-
* Default headers to be sent
|
35 |
-
*
|
36 |
-
* @var array
|
37 |
-
*/
|
38 |
-
protected $defaultHeaders = [
|
39 |
-
'accept' => 'application/json',
|
40 |
-
'content-type' => 'application/json'
|
41 |
-
];
|
42 |
-
|
43 |
-
/**
|
44 |
-
* Prioritized http clients
|
45 |
-
*
|
46 |
-
* @var array
|
47 |
-
*/
|
48 |
-
protected $availableClients = [
|
49 |
-
'Analyst\Http\WordPressHttpClient',
|
50 |
-
'Analyst\Http\CurlHttpClient',
|
51 |
-
'Analyst\Http\DummyHttpClient',
|
52 |
-
];
|
53 |
-
|
54 |
-
/**
|
55 |
-
* ApiRequestor constructor.
|
56 |
-
* @param $id
|
57 |
-
* @param $secret
|
58 |
-
* @param $apiBase
|
59 |
-
* @throws \Exception
|
60 |
-
*/
|
61 |
-
public function __construct($id, $secret, $apiBase)
|
62 |
-
{
|
63 |
-
$this->clientId = $id;
|
64 |
-
$this->clientSecret = $secret;
|
65 |
-
|
66 |
-
$this->setApiBase($apiBase);
|
67 |
-
|
68 |
-
$this->httpClient = $this->resolveHttpClient();
|
69 |
-
}
|
70 |
-
|
71 |
-
/**
|
72 |
-
* Set api base url
|
73 |
-
*
|
74 |
-
* @param $url
|
75 |
-
*/
|
76 |
-
public function setApiBase($url)
|
77 |
-
{
|
78 |
-
$this->apiBase = $url;
|
79 |
-
}
|
80 |
-
|
81 |
-
/**
|
82 |
-
* Get request
|
83 |
-
*
|
84 |
-
* @param $url
|
85 |
-
* @param array $headers
|
86 |
-
* @return mixed
|
87 |
-
*/
|
88 |
-
public function get($url, $headers = [])
|
89 |
-
{
|
90 |
-
return $this->request('GET', $url, null, $headers);
|
91 |
-
}
|
92 |
-
|
93 |
-
/**
|
94 |
-
* Post request
|
95 |
-
*
|
96 |
-
* @param $url
|
97 |
-
* @param $body
|
98 |
-
* @param array $headers
|
99 |
-
* @return mixed
|
100 |
-
*/
|
101 |
-
public function post($url, $body = [], $headers = [])
|
102 |
-
{
|
103 |
-
return $this->request('POST', $url, $body, $headers);
|
104 |
-
}
|
105 |
-
|
106 |
-
/**
|
107 |
-
* Put request
|
108 |
-
*
|
109 |
-
* @param $url
|
110 |
-
* @param $body
|
111 |
-
* @param array $headers
|
112 |
-
* @return mixed
|
113 |
-
*/
|
114 |
-
public function put($url, $body = [], $headers = [])
|
115 |
-
{
|
116 |
-
return $this->request('PUT', $url, $body, $headers);
|
117 |
-
}
|
118 |
-
|
119 |
-
/**
|
120 |
-
* Delete request
|
121 |
-
*
|
122 |
-
* @param $url
|
123 |
-
* @param array $headers
|
124 |
-
* @return mixed
|
125 |
-
*/
|
126 |
-
public function delete($url, $headers = [])
|
127 |
-
{
|
128 |
-
return $this->request('DELETE', $url, null, $headers);
|
129 |
-
}
|
130 |
-
|
131 |
-
/**
|
132 |
-
* Make request to api
|
133 |
-
*
|
134 |
-
* @param $method
|
135 |
-
* @param $url
|
136 |
-
* @param array $body
|
137 |
-
* @param array $headers
|
138 |
-
* @return mixed
|
139 |
-
*/
|
140 |
-
protected function request($method, $url, $body = [], $headers = [])
|
141 |
-
{
|
142 |
-
$fullUrl = $this->resolveFullUrl($url);
|
143 |
-
|
144 |
-
$date = date('r', time());
|
145 |
-
|
146 |
-
$headers['date'] = $date;
|
147 |
-
$headers['signature'] = $this->resolveSignature($this->clientSecret, $method, $fullUrl, $body, $date);
|
148 |
-
|
149 |
-
// Lowercase header names
|
150 |
-
$headers = $this->prepareHeaders(
|
151 |
-
array_merge($headers, $this->defaultHeaders)
|
152 |
-
);
|
153 |
-
|
154 |
-
$response = $this->httpClient->request($method, $fullUrl, $body, $headers);
|
155 |
-
|
156 |
-
// TODO: Check response code and take actions
|
157 |
-
|
158 |
-
return $response;
|
159 |
-
}
|
160 |
-
|
161 |
-
/**
|
162 |
-
* Set one default header
|
163 |
-
*
|
164 |
-
* @param $header
|
165 |
-
* @param $value
|
166 |
-
*/
|
167 |
-
public function setDefaultHeader($header, $value)
|
168 |
-
{
|
169 |
-
$this->defaultHeaders[
|
170 |
-
$this->resolveValidHeaderName($header)
|
171 |
-
] = $value;
|
172 |
-
}
|
173 |
-
|
174 |
-
/**
|
175 |
-
* Resolves supported http client
|
176 |
-
*
|
177 |
-
* @return HttpClientContract
|
178 |
-
* @throws Exception
|
179 |
-
*/
|
180 |
-
protected function resolveHttpClient()
|
181 |
-
{
|
182 |
-
$clients = array_filter($this->availableClients, $this->guessClientSupportEnvironment());
|
183 |
-
|
184 |
-
if (!isset($clients[0])) {
|
185 |
-
throw new Exception('There is no http client which this application can support');
|
186 |
-
}
|
187 |
-
|
188 |
-
// Instantiate first supported http client
|
189 |
-
return new $clients[0];
|
190 |
-
}
|
191 |
-
|
192 |
-
/**
|
193 |
-
* This will filter out clients which is not supported
|
194 |
-
* by the current environment
|
195 |
-
*
|
196 |
-
* @return \Closure
|
197 |
-
*/
|
198 |
-
protected function guessClientSupportEnvironment()
|
199 |
-
{
|
200 |
-
return function ($client) {
|
201 |
-
return forward_static_call([$client, 'hasSupport']);
|
202 |
-
};
|
203 |
-
}
|
204 |
-
|
205 |
-
/**
|
206 |
-
* Resolves valid header name
|
207 |
-
*
|
208 |
-
* @param $headerName
|
209 |
-
* @return string
|
210 |
-
*/
|
211 |
-
private function resolveValidHeaderName($headerName)
|
212 |
-
{
|
213 |
-
return strtolower($headerName);
|
214 |
-
}
|
215 |
-
|
216 |
-
/**
|
217 |
-
* Lowercase header names
|
218 |
-
*
|
219 |
-
* @param $headers
|
220 |
-
* @return array
|
221 |
-
*/
|
222 |
-
private function prepareHeaders($headers)
|
223 |
-
{
|
224 |
-
return array_change_key_case($headers, CASE_LOWER);
|
225 |
-
}
|
226 |
-
|
227 |
-
/**
|
228 |
-
* Sign request
|
229 |
-
*
|
230 |
-
* @param $key
|
231 |
-
* @param $method
|
232 |
-
* @param $url
|
233 |
-
* @param $body
|
234 |
-
* @param $date
|
235 |
-
*
|
236 |
-
* @return false|string
|
237 |
-
*/
|
238 |
-
private function resolveSignature($key, $method, $url, $body, $date)
|
239 |
-
{
|
240 |
-
$string = implode('\n', [$method, $url, md5(json_encode($body)), $date]);
|
241 |
-
|
242 |
-
$contentSecret = hash_hmac('sha256', $string, $key);
|
243 |
-
|
244 |
-
return sprintf('%s:%s', $this->clientId, $contentSecret);
|
245 |
-
}
|
246 |
-
|
247 |
-
/**
|
248 |
-
* Compose full url
|
249 |
-
*
|
250 |
-
* @param $url
|
251 |
-
* @return string
|
252 |
-
*/
|
253 |
-
private function resolveFullUrl($url)
|
254 |
-
{
|
255 |
-
return sprintf('%s/%s', $this->apiBase, trim($url, '/'));
|
256 |
-
}
|
257 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst;
|
4 |
+
|
5 |
+
use Exception;
|
6 |
+
use Analyst\Contracts\HttpClientContract;
|
7 |
+
use Analyst\Contracts\RequestorContract;
|
8 |
+
|
9 |
+
class ApiRequestor implements RequestorContract
|
10 |
+
{
|
11 |
+
/**
|
12 |
+
* Supported http client
|
13 |
+
*
|
14 |
+
* @var HttpClientContract
|
15 |
+
*/
|
16 |
+
protected $httpClient;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @var string
|
20 |
+
*/
|
21 |
+
protected $clientId;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @var string
|
25 |
+
*/
|
26 |
+
protected $clientSecret;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @var string
|
30 |
+
*/
|
31 |
+
protected $apiBase;
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Default headers to be sent
|
35 |
+
*
|
36 |
+
* @var array
|
37 |
+
*/
|
38 |
+
protected $defaultHeaders = [
|
39 |
+
'accept' => 'application/json',
|
40 |
+
'content-type' => 'application/json'
|
41 |
+
];
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Prioritized http clients
|
45 |
+
*
|
46 |
+
* @var array
|
47 |
+
*/
|
48 |
+
protected $availableClients = [
|
49 |
+
'Analyst\Http\WordPressHttpClient',
|
50 |
+
'Analyst\Http\CurlHttpClient',
|
51 |
+
'Analyst\Http\DummyHttpClient',
|
52 |
+
];
|
53 |
+
|
54 |
+
/**
|
55 |
+
* ApiRequestor constructor.
|
56 |
+
* @param $id
|
57 |
+
* @param $secret
|
58 |
+
* @param $apiBase
|
59 |
+
* @throws \Exception
|
60 |
+
*/
|
61 |
+
public function __construct($id, $secret, $apiBase)
|
62 |
+
{
|
63 |
+
$this->clientId = $id;
|
64 |
+
$this->clientSecret = $secret;
|
65 |
+
|
66 |
+
$this->setApiBase($apiBase);
|
67 |
+
|
68 |
+
$this->httpClient = $this->resolveHttpClient();
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Set api base url
|
73 |
+
*
|
74 |
+
* @param $url
|
75 |
+
*/
|
76 |
+
public function setApiBase($url)
|
77 |
+
{
|
78 |
+
$this->apiBase = $url;
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Get request
|
83 |
+
*
|
84 |
+
* @param $url
|
85 |
+
* @param array $headers
|
86 |
+
* @return mixed
|
87 |
+
*/
|
88 |
+
public function get($url, $headers = [])
|
89 |
+
{
|
90 |
+
return $this->request('GET', $url, null, $headers);
|
91 |
+
}
|
92 |
+
|
93 |
+
/**
|
94 |
+
* Post request
|
95 |
+
*
|
96 |
+
* @param $url
|
97 |
+
* @param $body
|
98 |
+
* @param array $headers
|
99 |
+
* @return mixed
|
100 |
+
*/
|
101 |
+
public function post($url, $body = [], $headers = [])
|
102 |
+
{
|
103 |
+
return $this->request('POST', $url, $body, $headers);
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* Put request
|
108 |
+
*
|
109 |
+
* @param $url
|
110 |
+
* @param $body
|
111 |
+
* @param array $headers
|
112 |
+
* @return mixed
|
113 |
+
*/
|
114 |
+
public function put($url, $body = [], $headers = [])
|
115 |
+
{
|
116 |
+
return $this->request('PUT', $url, $body, $headers);
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* Delete request
|
121 |
+
*
|
122 |
+
* @param $url
|
123 |
+
* @param array $headers
|
124 |
+
* @return mixed
|
125 |
+
*/
|
126 |
+
public function delete($url, $headers = [])
|
127 |
+
{
|
128 |
+
return $this->request('DELETE', $url, null, $headers);
|
129 |
+
}
|
130 |
+
|
131 |
+
/**
|
132 |
+
* Make request to api
|
133 |
+
*
|
134 |
+
* @param $method
|
135 |
+
* @param $url
|
136 |
+
* @param array $body
|
137 |
+
* @param array $headers
|
138 |
+
* @return mixed
|
139 |
+
*/
|
140 |
+
protected function request($method, $url, $body = [], $headers = [])
|
141 |
+
{
|
142 |
+
$fullUrl = $this->resolveFullUrl($url);
|
143 |
+
|
144 |
+
$date = date('r', time());
|
145 |
+
|
146 |
+
$headers['date'] = $date;
|
147 |
+
$headers['signature'] = $this->resolveSignature($this->clientSecret, $method, $fullUrl, $body, $date);
|
148 |
+
|
149 |
+
// Lowercase header names
|
150 |
+
$headers = $this->prepareHeaders(
|
151 |
+
array_merge($headers, $this->defaultHeaders)
|
152 |
+
);
|
153 |
+
|
154 |
+
$response = $this->httpClient->request($method, $fullUrl, $body, $headers);
|
155 |
+
|
156 |
+
// TODO: Check response code and take actions
|
157 |
+
|
158 |
+
return $response;
|
159 |
+
}
|
160 |
+
|
161 |
+
/**
|
162 |
+
* Set one default header
|
163 |
+
*
|
164 |
+
* @param $header
|
165 |
+
* @param $value
|
166 |
+
*/
|
167 |
+
public function setDefaultHeader($header, $value)
|
168 |
+
{
|
169 |
+
$this->defaultHeaders[
|
170 |
+
$this->resolveValidHeaderName($header)
|
171 |
+
] = $value;
|
172 |
+
}
|
173 |
+
|
174 |
+
/**
|
175 |
+
* Resolves supported http client
|
176 |
+
*
|
177 |
+
* @return HttpClientContract
|
178 |
+
* @throws Exception
|
179 |
+
*/
|
180 |
+
protected function resolveHttpClient()
|
181 |
+
{
|
182 |
+
$clients = array_filter($this->availableClients, $this->guessClientSupportEnvironment());
|
183 |
+
|
184 |
+
if (!isset($clients[0])) {
|
185 |
+
throw new Exception('There is no http client which this application can support');
|
186 |
+
}
|
187 |
+
|
188 |
+
// Instantiate first supported http client
|
189 |
+
return new $clients[0];
|
190 |
+
}
|
191 |
+
|
192 |
+
/**
|
193 |
+
* This will filter out clients which is not supported
|
194 |
+
* by the current environment
|
195 |
+
*
|
196 |
+
* @return \Closure
|
197 |
+
*/
|
198 |
+
protected function guessClientSupportEnvironment()
|
199 |
+
{
|
200 |
+
return function ($client) {
|
201 |
+
return forward_static_call([$client, 'hasSupport']);
|
202 |
+
};
|
203 |
+
}
|
204 |
+
|
205 |
+
/**
|
206 |
+
* Resolves valid header name
|
207 |
+
*
|
208 |
+
* @param $headerName
|
209 |
+
* @return string
|
210 |
+
*/
|
211 |
+
private function resolveValidHeaderName($headerName)
|
212 |
+
{
|
213 |
+
return strtolower($headerName);
|
214 |
+
}
|
215 |
+
|
216 |
+
/**
|
217 |
+
* Lowercase header names
|
218 |
+
*
|
219 |
+
* @param $headers
|
220 |
+
* @return array
|
221 |
+
*/
|
222 |
+
private function prepareHeaders($headers)
|
223 |
+
{
|
224 |
+
return array_change_key_case($headers, CASE_LOWER);
|
225 |
+
}
|
226 |
+
|
227 |
+
/**
|
228 |
+
* Sign request
|
229 |
+
*
|
230 |
+
* @param $key
|
231 |
+
* @param $method
|
232 |
+
* @param $url
|
233 |
+
* @param $body
|
234 |
+
* @param $date
|
235 |
+
*
|
236 |
+
* @return false|string
|
237 |
+
*/
|
238 |
+
private function resolveSignature($key, $method, $url, $body, $date)
|
239 |
+
{
|
240 |
+
$string = implode('\n', [$method, $url, md5(json_encode($body)), $date]);
|
241 |
+
|
242 |
+
$contentSecret = hash_hmac('sha256', $string, $key);
|
243 |
+
|
244 |
+
return sprintf('%s:%s', $this->clientId, $contentSecret);
|
245 |
+
}
|
246 |
+
|
247 |
+
/**
|
248 |
+
* Compose full url
|
249 |
+
*
|
250 |
+
* @param $url
|
251 |
+
* @return string
|
252 |
+
*/
|
253 |
+
private function resolveFullUrl($url)
|
254 |
+
{
|
255 |
+
return sprintf('%s/%s', $this->apiBase, trim($url, '/'));
|
256 |
+
}
|
257 |
+
}
|
analyst/src/ApiResponse.php
CHANGED
@@ -1,44 +1,44 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace Analyst;
|
4 |
-
|
5 |
-
class ApiResponse
|
6 |
-
{
|
7 |
-
/**
|
8 |
-
* Response headers
|
9 |
-
*
|
10 |
-
* @var array
|
11 |
-
*/
|
12 |
-
public $headers;
|
13 |
-
|
14 |
-
/**
|
15 |
-
* Response body
|
16 |
-
*
|
17 |
-
* @var mixed
|
18 |
-
*/
|
19 |
-
public $body;
|
20 |
-
|
21 |
-
/**
|
22 |
-
* Status code
|
23 |
-
*
|
24 |
-
* @var string
|
25 |
-
*/
|
26 |
-
public $code;
|
27 |
-
|
28 |
-
public function __construct($body, $code, $headers)
|
29 |
-
{
|
30 |
-
$this->body = $body;
|
31 |
-
$this->code = $code;
|
32 |
-
$this->headers = $headers;
|
33 |
-
}
|
34 |
-
|
35 |
-
/**
|
36 |
-
* Whether status code is successful
|
37 |
-
*
|
38 |
-
* @return bool
|
39 |
-
*/
|
40 |
-
public function isSuccess()
|
41 |
-
{
|
42 |
-
return $this->code >= 200 && $this->code < 300;
|
43 |
-
}
|
44 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst;
|
4 |
+
|
5 |
+
class ApiResponse
|
6 |
+
{
|
7 |
+
/**
|
8 |
+
* Response headers
|
9 |
+
*
|
10 |
+
* @var array
|
11 |
+
*/
|
12 |
+
public $headers;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Response body
|
16 |
+
*
|
17 |
+
* @var mixed
|
18 |
+
*/
|
19 |
+
public $body;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Status code
|
23 |
+
*
|
24 |
+
* @var string
|
25 |
+
*/
|
26 |
+
public $code;
|
27 |
+
|
28 |
+
public function __construct($body, $code, $headers)
|
29 |
+
{
|
30 |
+
$this->body = $body;
|
31 |
+
$this->code = $code;
|
32 |
+
$this->headers = $headers;
|
33 |
+
}
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Whether status code is successful
|
37 |
+
*
|
38 |
+
* @return bool
|
39 |
+
*/
|
40 |
+
public function isSuccess()
|
41 |
+
{
|
42 |
+
return $this->code >= 200 && $this->code < 300;
|
43 |
+
}
|
44 |
+
}
|
analyst/src/Cache/DatabaseCache.php
CHANGED
@@ -1,127 +1,127 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace Analyst\Cache;
|
4 |
-
|
5 |
-
use Analyst\Contracts\CacheContract;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* Class DatabaseCache
|
9 |
-
*
|
10 |
-
* @since 1.1.5
|
11 |
-
*/
|
12 |
-
class DatabaseCache implements CacheContract
|
13 |
-
{
|
14 |
-
const OPTION_KEY = 'analyst_cache';
|
15 |
-
|
16 |
-
protected static $instance;
|
17 |
-
|
18 |
-
/**
|
19 |
-
* Get instance of db cache
|
20 |
-
*
|
21 |
-
* @return DatabaseCache
|
22 |
-
*/
|
23 |
-
public static function getInstance()
|
24 |
-
{
|
25 |
-
if (!self::$instance) {
|
26 |
-
self::$instance = new DatabaseCache();
|
27 |
-
}
|
28 |
-
|
29 |
-
return self::$instance;
|
30 |
-
}
|
31 |
-
|
32 |
-
/**
|
33 |
-
* Key value pair
|
34 |
-
*
|
35 |
-
* @var array[]
|
36 |
-
*/
|
37 |
-
protected $values = [];
|
38 |
-
|
39 |
-
/**
|
40 |
-
* DatabaseCache constructor.
|
41 |
-
*/
|
42 |
-
public function __construct()
|
43 |
-
{
|
44 |
-
$raw = get_option(self::OPTION_KEY, serialize([]));
|
45 |
-
|
46 |
-
// Raw data may be an array already
|
47 |
-
$this->values = is_array($raw) ? $raw : @unserialize($raw);
|
48 |
-
|
49 |
-
// In case serialization is failed
|
50 |
-
// make sure values is an array
|
51 |
-
if (!is_array($this->values)) {
|
52 |
-
$this->values = [];
|
53 |
-
}
|
54 |
-
}
|
55 |
-
|
56 |
-
/**
|
57 |
-
* Save value with given key
|
58 |
-
*
|
59 |
-
* @param string $key
|
60 |
-
* @param string $value
|
61 |
-
*
|
62 |
-
* @return static
|
63 |
-
*/
|
64 |
-
public function put($key, $value)
|
65 |
-
{
|
66 |
-
$this->values[$key] = $value;
|
67 |
-
|
68 |
-
$this->sync();
|
69 |
-
|
70 |
-
return $this;
|
71 |
-
}
|
72 |
-
|
73 |
-
/**
|
74 |
-
* Get value by given key
|
75 |
-
*
|
76 |
-
* @param $key
|
77 |
-
*
|
78 |
-
* @param null $default
|
79 |
-
* @return string
|
80 |
-
*/
|
81 |
-
public function get($key, $default = null)
|
82 |
-
{
|
83 |
-
$value = isset($this->values[$key]) ? $this->values[$key] : $default;
|
84 |
-
|
85 |
-
return $value;
|
86 |
-
}
|
87 |
-
|
88 |
-
/**
|
89 |
-
* @param $key
|
90 |
-
*
|
91 |
-
* @return static
|
92 |
-
*/
|
93 |
-
public function delete($key)
|
94 |
-
{
|
95 |
-
if (isset($this->values[$key])) {
|
96 |
-
unset($this->values[$key]);
|
97 |
-
|
98 |
-
$this->sync();
|
99 |
-
}
|
100 |
-
|
101 |
-
return $this;
|
102 |
-
}
|
103 |
-
|
104 |
-
/**
|
105 |
-
* Update cache in DB
|
106 |
-
*/
|
107 |
-
protected function sync()
|
108 |
-
{
|
109 |
-
update_option(self::OPTION_KEY, serialize($this->values));
|
110 |
-
}
|
111 |
-
|
112 |
-
/**
|
113 |
-
* Should get value and remove it from cache
|
114 |
-
*
|
115 |
-
* @param $key
|
116 |
-
* @param null $default
|
117 |
-
* @return mixed
|
118 |
-
*/
|
119 |
-
public function pop($key, $default = null)
|
120 |
-
{
|
121 |
-
$value = $this->get($key);
|
122 |
-
|
123 |
-
$this->delete($key);
|
124 |
-
|
125 |
-
return $value;
|
126 |
-
}
|
127 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Cache;
|
4 |
+
|
5 |
+
use Analyst\Contracts\CacheContract;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Class DatabaseCache
|
9 |
+
*
|
10 |
+
* @since 1.1.5
|
11 |
+
*/
|
12 |
+
class DatabaseCache implements CacheContract
|
13 |
+
{
|
14 |
+
const OPTION_KEY = 'analyst_cache';
|
15 |
+
|
16 |
+
protected static $instance;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Get instance of db cache
|
20 |
+
*
|
21 |
+
* @return DatabaseCache
|
22 |
+
*/
|
23 |
+
public static function getInstance()
|
24 |
+
{
|
25 |
+
if (!self::$instance) {
|
26 |
+
self::$instance = new DatabaseCache();
|
27 |
+
}
|
28 |
+
|
29 |
+
return self::$instance;
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Key value pair
|
34 |
+
*
|
35 |
+
* @var array[]
|
36 |
+
*/
|
37 |
+
protected $values = [];
|
38 |
+
|
39 |
+
/**
|
40 |
+
* DatabaseCache constructor.
|
41 |
+
*/
|
42 |
+
public function __construct()
|
43 |
+
{
|
44 |
+
$raw = get_option(self::OPTION_KEY, serialize([]));
|
45 |
+
|
46 |
+
// Raw data may be an array already
|
47 |
+
$this->values = is_array($raw) ? $raw : @unserialize($raw);
|
48 |
+
|
49 |
+
// In case serialization is failed
|
50 |
+
// make sure values is an array
|
51 |
+
if (!is_array($this->values)) {
|
52 |
+
$this->values = [];
|
53 |
+
}
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Save value with given key
|
58 |
+
*
|
59 |
+
* @param string $key
|
60 |
+
* @param string $value
|
61 |
+
*
|
62 |
+
* @return static
|
63 |
+
*/
|
64 |
+
public function put($key, $value)
|
65 |
+
{
|
66 |
+
$this->values[$key] = $value;
|
67 |
+
|
68 |
+
$this->sync();
|
69 |
+
|
70 |
+
return $this;
|
71 |
+
}
|
72 |
+
|
73 |
+
/**
|
74 |
+
* Get value by given key
|
75 |
+
*
|
76 |
+
* @param $key
|
77 |
+
*
|
78 |
+
* @param null $default
|
79 |
+
* @return string
|
80 |
+
*/
|
81 |
+
public function get($key, $default = null)
|
82 |
+
{
|
83 |
+
$value = isset($this->values[$key]) ? $this->values[$key] : $default;
|
84 |
+
|
85 |
+
return $value;
|
86 |
+
}
|
87 |
+
|
88 |
+
/**
|
89 |
+
* @param $key
|
90 |
+
*
|
91 |
+
* @return static
|
92 |
+
*/
|
93 |
+
public function delete($key)
|
94 |
+
{
|
95 |
+
if (isset($this->values[$key])) {
|
96 |
+
unset($this->values[$key]);
|
97 |
+
|
98 |
+
$this->sync();
|
99 |
+
}
|
100 |
+
|
101 |
+
return $this;
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Update cache in DB
|
106 |
+
*/
|
107 |
+
protected function sync()
|
108 |
+
{
|
109 |
+
update_option(self::OPTION_KEY, serialize($this->values));
|
110 |
+
}
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Should get value and remove it from cache
|
114 |
+
*
|
115 |
+
* @param $key
|
116 |
+
* @param null $default
|
117 |
+
* @return mixed
|
118 |
+
*/
|
119 |
+
public function pop($key, $default = null)
|
120 |
+
{
|
121 |
+
$value = $this->get($key);
|
122 |
+
|
123 |
+
$this->delete($key);
|
124 |
+
|
125 |
+
return $value;
|
126 |
+
}
|
127 |
+
}
|
analyst/src/Collector.php
CHANGED
@@ -1,217 +1,217 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace Analyst;
|
4 |
-
|
5 |
-
use Analyst\Contracts\AnalystContract;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* Class Collector is a set of getters
|
9 |
-
* to retrieve some data from wp site
|
10 |
-
*/
|
11 |
-
class Collector
|
12 |
-
{
|
13 |
-
/**
|
14 |
-
* @var AnalystContract
|
15 |
-
*/
|
16 |
-
protected $sdk;
|
17 |
-
|
18 |
-
/**
|
19 |
-
* @var \WP_User
|
20 |
-
*/
|
21 |
-
protected $user;
|
22 |
-
|
23 |
-
public function __construct(AnalystContract $sdk)
|
24 |
-
{
|
25 |
-
$this->sdk = $sdk;
|
26 |
-
}
|
27 |
-
|
28 |
-
/**
|
29 |
-
* Load current user into memory
|
30 |
-
*/
|
31 |
-
public function loadCurrentUser()
|
32 |
-
{
|
33 |
-
$this->user = wp_get_current_user();
|
34 |
-
}
|
35 |
-
|
36 |
-
/**
|
37 |
-
* Get site url
|
38 |
-
*
|
39 |
-
* @return string
|
40 |
-
*/
|
41 |
-
public function getSiteUrl()
|
42 |
-
{
|
43 |
-
return get_option('siteurl');
|
44 |
-
}
|
45 |
-
|
46 |
-
/**
|
47 |
-
* Get current user email
|
48 |
-
*
|
49 |
-
* @return string
|
50 |
-
*/
|
51 |
-
public function getCurrentUserEmail()
|
52 |
-
{
|
53 |
-
return $this->user->user_email;
|
54 |
-
}
|
55 |
-
|
56 |
-
/**
|
57 |
-
* Get's email from general settings
|
58 |
-
*
|
59 |
-
* @return string
|
60 |
-
*/
|
61 |
-
public function getGeneralEmailAddress()
|
62 |
-
{
|
63 |
-
return get_option('admin_email');
|
64 |
-
}
|
65 |
-
|
66 |
-
/**
|
67 |
-
* Is this user administrator
|
68 |
-
*
|
69 |
-
* @return bool
|
70 |
-
*/
|
71 |
-
public function isUserAdministrator()
|
72 |
-
{
|
73 |
-
return in_array('administrator', $this->user->roles);
|
74 |
-
}
|
75 |
-
|
76 |
-
/**
|
77 |
-
* User name
|
78 |
-
*
|
79 |
-
* @return string
|
80 |
-
*/
|
81 |
-
public function getCurrentUserName()
|
82 |
-
{
|
83 |
-
return $this->user ? $this->user->user_nicename : 'unknown';
|
84 |
-
}
|
85 |
-
|
86 |
-
/**
|
87 |
-
* WP version
|
88 |
-
*
|
89 |
-
* @return string
|
90 |
-
*/
|
91 |
-
public function getWordPressVersion()
|
92 |
-
{
|
93 |
-
global $wp_version;
|
94 |
-
|
95 |
-
return $wp_version;
|
96 |
-
}
|
97 |
-
|
98 |
-
/**
|
99 |
-
* PHP version
|
100 |
-
*
|
101 |
-
* @return string
|
102 |
-
*/
|
103 |
-
public function getPHPVersion()
|
104 |
-
{
|
105 |
-
return phpversion();
|
106 |
-
}
|
107 |
-
|
108 |
-
/**
|
109 |
-
* Resolves plugin information
|
110 |
-
*
|
111 |
-
* @param string $path Absolute path to plugin
|
112 |
-
* @return array
|
113 |
-
*/
|
114 |
-
public function resolvePluginData($path)
|
115 |
-
{
|
116 |
-
if( !function_exists('get_plugin_data') ){
|
117 |
-
require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
|
118 |
-
}
|
119 |
-
|
120 |
-
return get_plugin_data($path);
|
121 |
-
}
|
122 |
-
|
123 |
-
/**
|
124 |
-
* Get plugin name by path
|
125 |
-
*
|
126 |
-
* @param $path
|
127 |
-
* @return string
|
128 |
-
*/
|
129 |
-
public function getPluginName($path)
|
130 |
-
{
|
131 |
-
$data = $this->resolvePluginData($path);
|
132 |
-
|
133 |
-
return $data['Name'];
|
134 |
-
}
|
135 |
-
|
136 |
-
/**
|
137 |
-
* Get plugin version
|
138 |
-
*
|
139 |
-
* @param $path
|
140 |
-
* @return string
|
141 |
-
*/
|
142 |
-
public function getPluginVersion($path)
|
143 |
-
{
|
144 |
-
$data = $this->resolvePluginData($path);
|
145 |
-
|
146 |
-
return $data['Version'] ? $data['Version'] : null;
|
147 |
-
}
|
148 |
-
|
149 |
-
/**
|
150 |
-
* Get server ip
|
151 |
-
*
|
152 |
-
* @return string
|
153 |
-
*/
|
154 |
-
public function getServerIp()
|
155 |
-
{
|
156 |
-
return $_SERVER['SERVER_ADDR'];
|
157 |
-
}
|
158 |
-
|
159 |
-
/**
|
160 |
-
* @return string
|
161 |
-
*/
|
162 |
-
public function getSDKVersion()
|
163 |
-
{
|
164 |
-
return $this->sdk->version();
|
165 |
-
}
|
166 |
-
|
167 |
-
/**
|
168 |
-
* @return string
|
169 |
-
*/
|
170 |
-
public function getMysqlVersion()
|
171 |
-
{
|
172 |
-
$conn = mysqli_connect(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
|
173 |
-
|
174 |
-
$version = mysqli_get_server_info($conn);
|
175 |
-
|
176 |
-
return $version ? $version : 'unknown';
|
177 |
-
}
|
178 |
-
|
179 |
-
/**
|
180 |
-
* @return string
|
181 |
-
*/
|
182 |
-
public function getSiteLanguage()
|
183 |
-
{
|
184 |
-
return get_locale();
|
185 |
-
}
|
186 |
-
|
187 |
-
|
188 |
-
/**
|
189 |
-
* Current WP theme
|
190 |
-
*
|
191 |
-
* @return false|string
|
192 |
-
*/
|
193 |
-
public function getCurrentThemeName()
|
194 |
-
{
|
195 |
-
return wp_get_theme()->get('Name');
|
196 |
-
}
|
197 |
-
|
198 |
-
/**
|
199 |
-
* Get active plugins list
|
200 |
-
*
|
201 |
-
* @return array
|
202 |
-
*/
|
203 |
-
public function getActivePluginsList()
|
204 |
-
{
|
205 |
-
if (!function_exists('get_plugins')) {
|
206 |
-
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
207 |
-
}
|
208 |
-
|
209 |
-
$allPlugins = get_plugins();
|
210 |
-
|
211 |
-
$activePluginsNames = array_map(function ($path) use ($allPlugins) {
|
212 |
-
return $allPlugins[$path]['Name'];
|
213 |
-
}, get_option('active_plugins'));
|
214 |
-
|
215 |
-
return $activePluginsNames;
|
216 |
-
}
|
217 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst;
|
4 |
+
|
5 |
+
use Analyst\Contracts\AnalystContract;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Class Collector is a set of getters
|
9 |
+
* to retrieve some data from wp site
|
10 |
+
*/
|
11 |
+
class Collector
|
12 |
+
{
|
13 |
+
/**
|
14 |
+
* @var AnalystContract
|
15 |
+
*/
|
16 |
+
protected $sdk;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @var \WP_User
|
20 |
+
*/
|
21 |
+
protected $user;
|
22 |
+
|
23 |
+
public function __construct(AnalystContract $sdk)
|
24 |
+
{
|
25 |
+
$this->sdk = $sdk;
|
26 |
+
}
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Load current user into memory
|
30 |
+
*/
|
31 |
+
public function loadCurrentUser()
|
32 |
+
{
|
33 |
+
$this->user = wp_get_current_user();
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Get site url
|
38 |
+
*
|
39 |
+
* @return string
|
40 |
+
*/
|
41 |
+
public function getSiteUrl()
|
42 |
+
{
|
43 |
+
return get_option('siteurl');
|
44 |
+
}
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Get current user email
|
48 |
+
*
|
49 |
+
* @return string
|
50 |
+
*/
|
51 |
+
public function getCurrentUserEmail()
|
52 |
+
{
|
53 |
+
return $this->user->user_email;
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Get's email from general settings
|
58 |
+
*
|
59 |
+
* @return string
|
60 |
+
*/
|
61 |
+
public function getGeneralEmailAddress()
|
62 |
+
{
|
63 |
+
return get_option('admin_email');
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Is this user administrator
|
68 |
+
*
|
69 |
+
* @return bool
|
70 |
+
*/
|
71 |
+
public function isUserAdministrator()
|
72 |
+
{
|
73 |
+
return in_array('administrator', $this->user->roles);
|
74 |
+
}
|
75 |
+
|
76 |
+
/**
|
77 |
+
* User name
|
78 |
+
*
|
79 |
+
* @return string
|
80 |
+
*/
|
81 |
+
public function getCurrentUserName()
|
82 |
+
{
|
83 |
+
return $this->user ? $this->user->user_nicename : 'unknown';
|
84 |
+
}
|
85 |
+
|
86 |
+
/**
|
87 |
+
* WP version
|
88 |
+
*
|
89 |
+
* @return string
|
90 |
+
*/
|
91 |
+
public function getWordPressVersion()
|
92 |
+
{
|
93 |
+
global $wp_version;
|
94 |
+
|
95 |
+
return $wp_version;
|
96 |
+
}
|
97 |
+
|
98 |
+
/**
|
99 |
+
* PHP version
|
100 |
+
*
|
101 |
+
* @return string
|
102 |
+
*/
|
103 |
+
public function getPHPVersion()
|
104 |
+
{
|
105 |
+
return phpversion();
|
106 |
+
}
|
107 |
+
|
108 |
+
/**
|
109 |
+
* Resolves plugin information
|
110 |
+
*
|
111 |
+
* @param string $path Absolute path to plugin
|
112 |
+
* @return array
|
113 |
+
*/
|
114 |
+
public function resolvePluginData($path)
|
115 |
+
{
|
116 |
+
if( !function_exists('get_plugin_data') ){
|
117 |
+
require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
|
118 |
+
}
|
119 |
+
|
120 |
+
return get_plugin_data($path);
|
121 |
+
}
|
122 |
+
|
123 |
+
/**
|
124 |
+
* Get plugin name by path
|
125 |
+
*
|
126 |
+
* @param $path
|
127 |
+
* @return string
|
128 |
+
*/
|
129 |
+
public function getPluginName($path)
|
130 |
+
{
|
131 |
+
$data = $this->resolvePluginData($path);
|
132 |
+
|
133 |
+
return $data['Name'];
|
134 |
+
}
|
135 |
+
|
136 |
+
/**
|
137 |
+
* Get plugin version
|
138 |
+
*
|
139 |
+
* @param $path
|
140 |
+
* @return string
|
141 |
+
*/
|
142 |
+
public function getPluginVersion($path)
|
143 |
+
{
|
144 |
+
$data = $this->resolvePluginData($path);
|
145 |
+
|
146 |
+
return $data['Version'] ? $data['Version'] : null;
|
147 |
+
}
|
148 |
+
|
149 |
+
/**
|
150 |
+
* Get server ip
|
151 |
+
*
|
152 |
+
* @return string
|
153 |
+
*/
|
154 |
+
public function getServerIp()
|
155 |
+
{
|
156 |
+
return $_SERVER['SERVER_ADDR'];
|
157 |
+
}
|
158 |
+
|
159 |
+
/**
|
160 |
+
* @return string
|
161 |
+
*/
|
162 |
+
public function getSDKVersion()
|
163 |
+
{
|
164 |
+
return $this->sdk->version();
|
165 |
+
}
|
166 |
+
|
167 |
+
/**
|
168 |
+
* @return string
|
169 |
+
*/
|
170 |
+
public function getMysqlVersion()
|
171 |
+
{
|
172 |
+
$conn = mysqli_connect(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
|
173 |
+
|
174 |
+
$version = mysqli_get_server_info($conn);
|
175 |
+
|
176 |
+
return $version ? $version : 'unknown';
|
177 |
+
}
|
178 |
+
|
179 |
+
/**
|
180 |
+
* @return string
|
181 |
+
*/
|
182 |
+
public function getSiteLanguage()
|
183 |
+
{
|
184 |
+
return get_locale();
|
185 |
+
}
|
186 |
+
|
187 |
+
|
188 |
+
/**
|
189 |
+
* Current WP theme
|
190 |
+
*
|
191 |
+
* @return false|string
|
192 |
+
*/
|
193 |
+
public function getCurrentThemeName()
|
194 |
+
{
|
195 |
+
return wp_get_theme()->get('Name');
|
196 |
+
}
|
197 |
+
|
198 |
+
/**
|
199 |
+
* Get active plugins list
|
200 |
+
*
|
201 |
+
* @return array
|
202 |
+
*/
|
203 |
+
public function getActivePluginsList()
|
204 |
+
{
|
205 |
+
if (!function_exists('get_plugins')) {
|
206 |
+
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
207 |
+
}
|
208 |
+
|
209 |
+
$allPlugins = get_plugins();
|
210 |
+
|
211 |
+
$activePluginsNames = array_map(function ($path) use ($allPlugins) {
|
212 |
+
return $allPlugins[$path]['Name'];
|
213 |
+
}, get_option('active_plugins'));
|
214 |
+
|
215 |
+
return $activePluginsNames;
|
216 |
+
}
|
217 |
+
}
|
analyst/src/Contracts/AnalystContract.php
CHANGED
@@ -1,12 +1,12 @@
|
|
1 |
-
<?php
|
2 |
-
namespace Analyst\Contracts;
|
3 |
-
|
4 |
-
interface AnalystContract
|
5 |
-
{
|
6 |
-
/**
|
7 |
-
* Must return version of analyst
|
8 |
-
*
|
9 |
-
* @return string
|
10 |
-
*/
|
11 |
-
public static function version();
|
12 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace Analyst\Contracts;
|
3 |
+
|
4 |
+
interface AnalystContract
|
5 |
+
{
|
6 |
+
/**
|
7 |
+
* Must return version of analyst
|
8 |
+
*
|
9 |
+
* @return string
|
10 |
+
*/
|
11 |
+
public static function version();
|
12 |
+
}
|
analyst/src/Contracts/CacheContract.php
CHANGED
@@ -1,47 +1,47 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace Analyst\Contracts;
|
4 |
-
|
5 |
-
/**
|
6 |
-
* Interface CacheContract
|
7 |
-
*
|
8 |
-
* @since 1.1.5
|
9 |
-
*/
|
10 |
-
interface CacheContract
|
11 |
-
{
|
12 |
-
/**
|
13 |
-
* Save value with given key
|
14 |
-
*
|
15 |
-
* @param string $key
|
16 |
-
* @param string $value
|
17 |
-
*
|
18 |
-
* @return static
|
19 |
-
*/
|
20 |
-
public function put($key, $value);
|
21 |
-
|
22 |
-
/**
|
23 |
-
* Get value by given key
|
24 |
-
*
|
25 |
-
* @param $key
|
26 |
-
*
|
27 |
-
* @param null $default
|
28 |
-
* @return string
|
29 |
-
*/
|
30 |
-
public function get($key, $default = null);
|
31 |
-
|
32 |
-
/**
|
33 |
-
* @param $key
|
34 |
-
*
|
35 |
-
* @return static
|
36 |
-
*/
|
37 |
-
public function delete($key);
|
38 |
-
|
39 |
-
/**
|
40 |
-
* Should get value and remove it from cache
|
41 |
-
*
|
42 |
-
* @param $key
|
43 |
-
* @param null $default
|
44 |
-
* @return mixed
|
45 |
-
*/
|
46 |
-
public function pop($key, $default = null);
|
47 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Contracts;
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Interface CacheContract
|
7 |
+
*
|
8 |
+
* @since 1.1.5
|
9 |
+
*/
|
10 |
+
interface CacheContract
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Save value with given key
|
14 |
+
*
|
15 |
+
* @param string $key
|
16 |
+
* @param string $value
|
17 |
+
*
|
18 |
+
* @return static
|
19 |
+
*/
|
20 |
+
public function put($key, $value);
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Get value by given key
|
24 |
+
*
|
25 |
+
* @param $key
|
26 |
+
*
|
27 |
+
* @param null $default
|
28 |
+
* @return string
|
29 |
+
*/
|
30 |
+
public function get($key, $default = null);
|
31 |
+
|
32 |
+
/**
|
33 |
+
* @param $key
|
34 |
+
*
|
35 |
+
* @return static
|
36 |
+
*/
|
37 |
+
public function delete($key);
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Should get value and remove it from cache
|
41 |
+
*
|
42 |
+
* @param $key
|
43 |
+
* @param null $default
|
44 |
+
* @return mixed
|
45 |
+
*/
|
46 |
+
public function pop($key, $default = null);
|
47 |
+
}
|
analyst/src/Contracts/HttpClientContract.php
CHANGED
@@ -1,25 +1,25 @@
|
|
1 |
-
<?php
|
2 |
-
namespace Analyst\Contracts;
|
3 |
-
|
4 |
-
use Analyst\ApiResponse;
|
5 |
-
|
6 |
-
interface HttpClientContract
|
7 |
-
{
|
8 |
-
/**
|
9 |
-
* Make an http request
|
10 |
-
*
|
11 |
-
* @param $method
|
12 |
-
* @param $url
|
13 |
-
* @param $body
|
14 |
-
* @param $headers
|
15 |
-
* @return ApiResponse
|
16 |
-
*/
|
17 |
-
public function request($method, $url, $body, $headers);
|
18 |
-
|
19 |
-
/**
|
20 |
-
* Must return `true` if client is supported
|
21 |
-
*
|
22 |
-
* @return bool
|
23 |
-
*/
|
24 |
-
public static function hasSupport();
|
25 |
-
}
|
1 |
+
<?php
|
2 |
+
namespace Analyst\Contracts;
|
3 |
+
|
4 |
+
use Analyst\ApiResponse;
|
5 |
+
|
6 |
+
interface HttpClientContract
|
7 |
+
{
|
8 |
+
/**
|
9 |
+
* Make an http request
|
10 |
+
*
|
11 |
+
* @param $method
|
12 |
+
* @param $url
|
13 |
+
* @param $body
|
14 |
+
* @param $headers
|
15 |
+
* @return ApiResponse
|
16 |
+
*/
|
17 |
+
public function request($method, $url, $body, $headers);
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Must return `true` if client is supported
|
21 |
+
*
|
22 |
+
* @return bool
|
23 |
+
*/
|
24 |
+
public static function hasSupport();
|
25 |
+
}
|
analyst/src/Contracts/RequestContract.php
CHANGED
@@ -1,22 +1,22 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace Analyst\Contracts;
|
4 |
-
|
5 |
-
use Analyst\ApiResponse;
|
6 |
-
|
7 |
-
interface RequestContract
|
8 |
-
{
|
9 |
-
/**
|
10 |
-
* Cast request data to array
|
11 |
-
*
|
12 |
-
* @return array
|
13 |
-
*/
|
14 |
-
public function toArray();
|
15 |
-
|
16 |
-
/**
|
17 |
-
* Execute the request
|
18 |
-
* @param RequestorContract $requestor
|
19 |
-
* @return ApiResponse
|
20 |
-
*/
|
21 |
-
public function execute(RequestorContract $requestor);
|
22 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Contracts;
|
4 |
+
|
5 |
+
use Analyst\ApiResponse;
|
6 |
+
|
7 |
+
interface RequestContract
|
8 |
+
{
|
9 |
+
/**
|
10 |
+
* Cast request data to array
|
11 |
+
*
|
12 |
+
* @return array
|
13 |
+
*/
|
14 |
+
public function toArray();
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Execute the request
|
18 |
+
* @param RequestorContract $requestor
|
19 |
+
* @return ApiResponse
|
20 |
+
*/
|
21 |
+
public function execute(RequestorContract $requestor);
|
22 |
+
}
|
analyst/src/Contracts/RequestorContract.php
CHANGED
@@ -1,44 +1,44 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace Analyst\Contracts;
|
4 |
-
|
5 |
-
interface RequestorContract
|
6 |
-
{
|
7 |
-
/**
|
8 |
-
* Get request
|
9 |
-
*
|
10 |
-
* @param $url
|
11 |
-
* @param array $headers
|
12 |
-
* @return mixed
|
13 |
-
*/
|
14 |
-
public function get($url, $headers = []);
|
15 |
-
|
16 |
-
/**
|
17 |
-
* Post request
|
18 |
-
*
|
19 |
-
* @param $url
|
20 |
-
* @param $body
|
21 |
-
* @param array $headers
|
22 |
-
* @return mixed
|
23 |
-
*/
|
24 |
-
public function post($url, $body = [], $headers = []);
|
25 |
-
|
26 |
-
/**
|
27 |
-
* Put request
|
28 |
-
*
|
29 |
-
* @param $url
|
30 |
-
* @param $body
|
31 |
-
* @param array $headers
|
32 |
-
* @return mixed
|
33 |
-
*/
|
34 |
-
public function put($url, $body = [], $headers = []);
|
35 |
-
|
36 |
-
/**
|
37 |
-
* Delete request
|
38 |
-
*
|
39 |
-
* @param $url
|
40 |
-
* @param array $headers
|
41 |
-
* @return mixed
|
42 |
-
*/
|
43 |
-
public function delete($url, $headers = []);
|
44 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Contracts;
|
4 |
+
|
5 |
+
interface RequestorContract
|
6 |
+
{
|
7 |
+
/**
|
8 |
+
* Get request
|
9 |
+
*
|
10 |
+
* @param $url
|
11 |
+
* @param array $headers
|
12 |
+
* @return mixed
|
13 |
+
*/
|
14 |
+
public function get($url, $headers = []);
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Post request
|
18 |
+
*
|
19 |
+
* @param $url
|
20 |
+
* @param $body
|
21 |
+
* @param array $headers
|
22 |
+
* @return mixed
|
23 |
+
*/
|
24 |
+
public function post($url, $body = [], $headers = []);
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Put request
|
28 |
+
*
|
29 |
+
* @param $url
|
30 |
+
* @param $body
|
31 |
+
* @param array $headers
|
32 |
+
* @return mixed
|
33 |
+
*/
|
34 |
+
public function put($url, $body = [], $headers = []);
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Delete request
|
38 |
+
*
|
39 |
+
* @param $url
|
40 |
+
* @param array $headers
|
41 |
+
* @return mixed
|
42 |
+
*/
|
43 |
+
public function delete($url, $headers = []);
|
44 |
+
}
|
analyst/src/Contracts/TrackerContract.php
CHANGED
@@ -1,69 +1,69 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace Analyst\Contracts;
|
4 |
-
|
5 |
-
interface TrackerContract
|
6 |
-
{
|
7 |
-
/**
|
8 |
-
* Should register activation and deactivation
|
9 |
-
* event hooks
|
10 |
-
*
|
11 |
-
* @return void
|
12 |
-
*/
|
13 |
-
public function registerHooks();
|
14 |
-
|
15 |
-
/**
|
16 |
-
* Will fire when admin activates plugin
|
17 |
-
*
|
18 |
-
* @return void
|
19 |
-
*/
|
20 |
-
public function onActivePluginListener();
|
21 |
-
|
22 |
-
/**
|
23 |
-
* Will fire when admin deactivates plugin
|
24 |
-
*
|
25 |
-
* @return void
|
26 |
-
*/
|
27 |
-
public function onDeactivatePluginListener();
|
28 |
-
|
29 |
-
/**
|
30 |
-
* Will fire when user opted in
|
31 |
-
*
|
32 |
-
* @return void
|
33 |
-
*/
|
34 |
-
public function onOptInListener();
|
35 |
-
|
36 |
-
/**
|
37 |
-
* Will fire when user opted out
|
38 |
-
*
|
39 |
-
* @return void
|
40 |
-
*/
|
41 |
-
public function onOptOutListener();
|
42 |
-
|
43 |
-
/**
|
44 |
-
* Will fire when user accept opt/in at first time
|
45 |
-
*
|
46 |
-
* @return void
|
47 |
-
*/
|
48 |
-
public function onInstallListener();
|
49 |
-
|
50 |
-
/**
|
51 |
-
* Will fire when user skipped installation
|
52 |
-
*
|
53 |
-
* @return void
|
54 |
-
*/
|
55 |
-
public function onSkipInstallListener();
|
56 |
-
|
57 |
-
/**
|
58 |
-
* Will fire when user delete plugin through admin panel.
|
59 |
-
* This action will happen if admin at least once
|
60 |
-
* activated the plugin.
|
61 |
-
*
|
62 |
-
* The register_uninstall_hook function accepts only static
|
63 |
-
* function or global function to be executed, so this is
|
64 |
-
* why this method is static
|
65 |
-
*
|
66 |
-
* @return void
|
67 |
-
*/
|
68 |
-
public static function onUninstallPluginListener();
|
69 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Contracts;
|
4 |
+
|
5 |
+
interface TrackerContract
|
6 |
+
{
|
7 |
+
/**
|
8 |
+
* Should register activation and deactivation
|
9 |
+
* event hooks
|
10 |
+
*
|
11 |
+
* @return void
|
12 |
+
*/
|
13 |
+
public function registerHooks();
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Will fire when admin activates plugin
|
17 |
+
*
|
18 |
+
* @return void
|
19 |
+
*/
|
20 |
+
public function onActivePluginListener();
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Will fire when admin deactivates plugin
|
24 |
+
*
|
25 |
+
* @return void
|
26 |
+
*/
|
27 |
+
public function onDeactivatePluginListener();
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Will fire when user opted in
|
31 |
+
*
|
32 |
+
* @return void
|
33 |
+
*/
|
34 |
+
public function onOptInListener();
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Will fire when user opted out
|
38 |
+
*
|
39 |
+
* @return void
|
40 |
+
*/
|
41 |
+
public function onOptOutListener();
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Will fire when user accept opt/in at first time
|
45 |
+
*
|
46 |
+
* @return void
|
47 |
+
*/
|
48 |
+
public function onInstallListener();
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Will fire when user skipped installation
|
52 |
+
*
|
53 |
+
* @return void
|
54 |
+
*/
|
55 |
+
public function onSkipInstallListener();
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Will fire when user delete plugin through admin panel.
|
59 |
+
* This action will happen if admin at least once
|
60 |
+
* activated the plugin.
|
61 |
+
*
|
62 |
+
* The register_uninstall_hook function accepts only static
|
63 |
+
* function or global function to be executed, so this is
|
64 |
+
* why this method is static
|
65 |
+
*
|
66 |
+
* @return void
|
67 |
+
*/
|
68 |
+
public static function onUninstallPluginListener();
|
69 |
+
}
|
analyst/src/Core/AbstractFactory.php
CHANGED
@@ -1,27 +1,27 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace Analyst\Core;
|
4 |
-
|
5 |
-
abstract class AbstractFactory
|
6 |
-
{
|
7 |
-
/**
|
8 |
-
* Unserialize to static::class instance
|
9 |
-
*
|
10 |
-
* @param $raw
|
11 |
-
* @return static
|
12 |
-
*/
|
13 |
-
protected static function unserialize($raw)
|
14 |
-
{
|
15 |
-
$instance = @unserialize($raw);
|
16 |
-
|
17 |
-
$isProperObject = is_object($instance) && $instance instanceof static;
|
18 |
-
|
19 |
-
// In case for some reason unserialized object is not
|
20 |
-
// static::class we make sure it is static::class
|
21 |
-
if (!$isProperObject) {
|
22 |
-
$instance = new static();
|
23 |
-
}
|
24 |
-
|
25 |
-
return $instance;
|
26 |
-
}
|
27 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Core;
|
4 |
+
|
5 |
+
abstract class AbstractFactory
|
6 |
+
{
|
7 |
+
/**
|
8 |
+
* Unserialize to static::class instance
|
9 |
+
*
|
10 |
+
* @param $raw
|
11 |
+
* @return static
|
12 |
+
*/
|
13 |
+
protected static function unserialize($raw)
|
14 |
+
{
|
15 |
+
$instance = @unserialize($raw);
|
16 |
+
|
17 |
+
$isProperObject = is_object($instance) && $instance instanceof static;
|
18 |
+
|
19 |
+
// In case for some reason unserialized object is not
|
20 |
+
// static::class we make sure it is static::class
|
21 |
+
if (!$isProperObject) {
|
22 |
+
$instance = new static();
|
23 |
+
}
|
24 |
+
|
25 |
+
return $instance;
|
26 |
+
}
|
27 |
+
}
|
analyst/src/Http/CurlHttpClient.php
CHANGED
@@ -1,102 +1,102 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace Analyst\Http;
|
4 |
-
|
5 |
-
use Analyst\ApiResponse;
|
6 |
-
use Analyst\Contracts\HttpClientContract;
|
7 |
-
|
8 |
-
class CurlHttpClient implements HttpClientContract
|
9 |
-
{
|
10 |
-
/**
|
11 |
-
* Make an http request
|
12 |
-
*
|
13 |
-
* @param $method
|
14 |
-
* @param $url
|
15 |
-
* @param array $body
|
16 |
-
* @param $headers
|
17 |
-
* @return mixed
|
18 |
-
*/
|
19 |
-
public function request($method, $url, $body, $headers)
|
20 |
-
{
|
21 |
-
$method = strtoupper($method);
|
22 |
-
|
23 |
-
$options = [
|
24 |
-
CURLOPT_RETURNTRANSFER => true,
|
25 |
-
CURLOPT_URL => $url,
|
26 |
-
CURLOPT_HTTPHEADER => $this->prepareRequestHeaders($headers),
|
27 |
-
CURLOPT_CUSTOMREQUEST => $method,
|
28 |
-
CURLOPT_FAILONERROR => true,
|
29 |
-
CURLOPT_HEADER => true,
|
30 |
-
CURLOPT_TIMEOUT => 30,
|
31 |
-
];
|
32 |
-
|
33 |
-
if ($method === 'POST') {
|
34 |
-
$options[CURLOPT_POST] = 1;
|
35 |
-
$options[CURLOPT_POSTFIELDS] = json_encode($body);
|
36 |
-
}
|
37 |
-
|
38 |
-
$curl = curl_init();
|
39 |
-
|
40 |
-
curl_setopt_array($curl, $options);
|
41 |
-
|
42 |
-
$response = curl_exec($curl);
|
43 |
-
|
44 |
-
list($rawHeaders, $rawBody) = explode("\r\n\r\n", $response, 2);
|
45 |
-
|
46 |
-
$info = curl_getinfo($curl);
|
47 |
-
|
48 |
-
curl_close($curl);
|
49 |
-
|
50 |
-
$responseHeaders = $this->resolveResponseHeaders($rawHeaders);
|
51 |
-
$responseBody = json_decode($rawBody, true);
|
52 |
-
|
53 |
-
return new ApiResponse($responseBody, $info['http_code'], $responseHeaders);
|
54 |
-
}
|
55 |
-
|
56 |
-
/**
|
57 |
-
* Must return `true` if client is supported
|
58 |
-
*
|
59 |
-
* @return bool
|
60 |
-
*/
|
61 |
-
public static function hasSupport()
|
62 |
-
{
|
63 |
-
return function_exists('curl_version');
|
64 |
-
}
|
65 |
-
|
66 |
-
/**
|
67 |
-
* Modify request headers from key value pair
|
68 |
-
* to vector array
|
69 |
-
*
|
70 |
-
* @param array $headers
|
71 |
-
* @return array
|
72 |
-
*/
|
73 |
-
protected function prepareRequestHeaders ($headers)
|
74 |
-
{
|
75 |
-
return array_map(function ($key, $value) {
|
76 |
-
return sprintf('%s:%s', $key, $value);
|
77 |
-
}, array_keys($headers), $headers);
|
78 |
-
}
|
79 |
-
|
80 |
-
/**
|
81 |
-
* Resolve raw response headers as
|
82 |
-
* associative array
|
83 |
-
*
|
84 |
-
* @param $rawHeaders
|
85 |
-
* @return array
|
86 |
-
*/
|
87 |
-
private function resolveResponseHeaders($rawHeaders)
|
88 |
-
{
|
89 |
-
$headers = [];
|
90 |
-
|
91 |
-
foreach (explode("\r\n", $rawHeaders) as $i => $line) {
|
92 |
-
$parts = explode(': ', $line);
|
93 |
-
|
94 |
-
if (count($parts) === 1) {
|
95 |
-
continue;
|
96 |
-
}
|
97 |
-
|
98 |
-
$headers[$parts[0]] = $parts[1];
|
99 |
-
}
|
100 |
-
return $headers;
|
101 |
-
}
|
102 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Http;
|
4 |
+
|
5 |
+
use Analyst\ApiResponse;
|
6 |
+
use Analyst\Contracts\HttpClientContract;
|
7 |
+
|
8 |
+
class CurlHttpClient implements HttpClientContract
|
9 |
+
{
|
10 |
+
/**
|
11 |
+
* Make an http request
|
12 |
+
*
|
13 |
+
* @param $method
|
14 |
+
* @param $url
|
15 |
+
* @param array $body
|
16 |
+
* @param $headers
|
17 |
+
* @return mixed
|
18 |
+
*/
|
19 |
+
public function request($method, $url, $body, $headers)
|
20 |
+
{
|
21 |
+
$method = strtoupper($method);
|
22 |
+
|
23 |
+
$options = [
|
24 |
+
CURLOPT_RETURNTRANSFER => true,
|
25 |
+
CURLOPT_URL => $url,
|
26 |
+
CURLOPT_HTTPHEADER => $this->prepareRequestHeaders($headers),
|
27 |
+
CURLOPT_CUSTOMREQUEST => $method,
|
28 |
+
CURLOPT_FAILONERROR => true,
|
29 |
+
CURLOPT_HEADER => true,
|
30 |
+
CURLOPT_TIMEOUT => 30,
|
31 |
+
];
|
32 |
+
|
33 |
+
if ($method === 'POST') {
|
34 |
+
$options[CURLOPT_POST] = 1;
|
35 |
+
$options[CURLOPT_POSTFIELDS] = json_encode($body);
|
36 |
+
}
|
37 |
+
|
38 |
+
$curl = curl_init();
|
39 |
+
|
40 |
+
curl_setopt_array($curl, $options);
|
41 |
+
|
42 |
+
$response = curl_exec($curl);
|
43 |
+
|
44 |
+
list($rawHeaders, $rawBody) = explode("\r\n\r\n", $response, 2);
|
45 |
+
|
46 |
+
$info = curl_getinfo($curl);
|
47 |
+
|
48 |
+
curl_close($curl);
|
49 |
+
|
50 |
+
$responseHeaders = $this->resolveResponseHeaders($rawHeaders);
|
51 |
+
$responseBody = json_decode($rawBody, true);
|
52 |
+
|
53 |
+
return new ApiResponse($responseBody, $info['http_code'], $responseHeaders);
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Must return `true` if client is supported
|
58 |
+
*
|
59 |
+
* @return bool
|
60 |
+
*/
|
61 |
+
public static function hasSupport()
|
62 |
+
{
|
63 |
+
return function_exists('curl_version');
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Modify request headers from key value pair
|
68 |
+
* to vector array
|
69 |
+
*
|
70 |
+
* @param array $headers
|
71 |
+
* @return array
|
72 |
+
*/
|
73 |
+
protected function prepareRequestHeaders ($headers)
|
74 |
+
{
|
75 |
+
return array_map(function ($key, $value) {
|
76 |
+
return sprintf('%s:%s', $key, $value);
|
77 |
+
}, array_keys($headers), $headers);
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* Resolve raw response headers as
|
82 |
+
* associative array
|
83 |
+
*
|
84 |
+
* @param $rawHeaders
|
85 |
+
* @return array
|
86 |
+
*/
|
87 |
+
private function resolveResponseHeaders($rawHeaders)
|
88 |
+
{
|
89 |
+
$headers = [];
|
90 |
+
|
91 |
+
foreach (explode("\r\n", $rawHeaders) as $i => $line) {
|
92 |
+
$parts = explode(': ', $line);
|
93 |
+
|
94 |
+
if (count($parts) === 1) {
|
95 |
+
continue;
|
96 |
+
}
|
97 |
+
|
98 |
+
$headers[$parts[0]] = $parts[1];
|
99 |
+
}
|
100 |
+
return $headers;
|
101 |
+
}
|
102 |
+
}
|
analyst/src/Http/DummyHttpClient.php
CHANGED
@@ -1,33 +1,33 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace Analyst\Http;
|
4 |
-
|
5 |
-
use Analyst\ApiResponse;
|
6 |
-
use Analyst\Contracts\HttpClientContract;
|
7 |
-
|
8 |
-
class DummyHttpClient implements HttpClientContract
|
9 |
-
{
|
10 |
-
/**
|
11 |
-
* Make an http request
|
12 |
-
*
|
13 |
-
* @param $method
|
14 |
-
* @param $url
|
15 |
-
* @param $body
|
16 |
-
* @param $headers
|
17 |
-
* @return ApiResponse
|
18 |
-
*/
|
19 |
-
public function request($method, $url, $body, $headers)
|
20 |
-
{
|
21 |
-
return new ApiResponse('Dummy response', 200, []);
|
22 |
-
}
|
23 |
-
|
24 |
-
/**
|
25 |
-
* Must return `true` if client is supported
|
26 |
-
*
|
27 |
-
* @return bool
|
28 |
-
*/
|
29 |
-
public static function hasSupport()
|
30 |
-
{
|
31 |
-
return true;
|
32 |
-
}
|
33 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Http;
|
4 |
+
|
5 |
+
use Analyst\ApiResponse;
|
6 |
+
use Analyst\Contracts\HttpClientContract;
|
7 |
+
|
8 |
+
class DummyHttpClient implements HttpClientContract
|
9 |
+
{
|
10 |
+
/**
|
11 |
+
* Make an http request
|
12 |
+
*
|
13 |
+
* @param $method
|
14 |
+
* @param $url
|
15 |
+
* @param $body
|
16 |
+
* @param $headers
|
17 |
+
* @return ApiResponse
|
18 |
+
*/
|
19 |
+
public function request($method, $url, $body, $headers)
|
20 |
+
{
|
21 |
+
return new ApiResponse('Dummy response', 200, []);
|
22 |
+
}
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Must return `true` if client is supported
|
26 |
+
*
|
27 |
+
* @return bool
|
28 |
+
*/
|
29 |
+
public static function hasSupport()
|
30 |
+
{
|
31 |
+
return true;
|
32 |
+
}
|
33 |
+
}
|
analyst/src/Http/Requests/AbstractLoggerRequest.php
CHANGED
@@ -1,64 +1,64 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace Analyst\Http\Requests;
|
4 |
-
|
5 |
-
use Analyst\ApiResponse;
|
6 |
-
use Analyst\Collector;
|
7 |
-
use Analyst\Contracts\RequestContract;
|
8 |
-
use Analyst\Contracts\RequestorContract;
|
9 |
-
|
10 |
-
abstract class AbstractLoggerRequest implements RequestContract
|
11 |
-
{
|
12 |
-
/**
|
13 |
-
* @var Collector
|
14 |
-
*/
|
15 |
-
protected $collector;
|
16 |
-
|
17 |
-
/**
|
18 |
-
* @var integer
|
19 |
-
*/
|
20 |
-
protected $id;
|
21 |
-
|
22 |
-
/**
|
23 |
-
* @var string
|
24 |
-
*/
|
25 |
-
protected $path;
|
26 |
-
|
27 |
-
public function __construct(Collector $collector, $pluginId, $path)
|
28 |
-
{
|
29 |
-
$this->collector = $collector;
|
30 |
-
$this->id = $pluginId;
|
31 |
-
$this->path = $path;
|
32 |
-
}
|
33 |
-
|
34 |
-
/**
|
35 |
-
* Cast request data to array
|
36 |
-
*
|
37 |
-
* @return array
|
38 |
-
*/
|
39 |
-
public function toArray()
|
40 |
-
{
|
41 |
-
return [
|
42 |
-
'plugin_id' => $this->id,
|
43 |
-
'php_version' => $this->collector->getPHPVersion(),
|
44 |
-
'wp_version' => $this->collector->getWordPressVersion(),
|
45 |
-
'plugin_version' => $this->collector->getPluginVersion($this->path),
|
46 |
-
'url' => $this->collector->getSiteUrl(),
|
47 |
-
'sdk_version' => $this->collector->getSDKVersion(),
|
48 |
-
'ip' => $this->collector->getServerIp(),
|
49 |
-
'mysql_version' => $this->collector->getMysqlVersion(),
|
50 |
-
'locale' => $this->collector->getSiteLanguage(),
|
51 |
-
'current_theme' => $this->collector->getCurrentThemeName(),
|
52 |
-
'active_plugins_list' => implode(', ', $this->collector->getActivePluginsList()),
|
53 |
-
'email' => $this->collector->getGeneralEmailAddress(),
|
54 |
-
'name' => $this->collector->getCurrentUserName()
|
55 |
-
];
|
56 |
-
}
|
57 |
-
|
58 |
-
/**
|
59 |
-
* Execute the request
|
60 |
-
* @param RequestorContract $requestor
|
61 |
-
* @return ApiResponse
|
62 |
-
*/
|
63 |
-
public abstract function execute(RequestorContract $requestor);
|
64 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Http\Requests;
|
4 |
+
|
5 |
+
use Analyst\ApiResponse;
|
6 |
+
use Analyst\Collector;
|
7 |
+
use Analyst\Contracts\RequestContract;
|
8 |
+
use Analyst\Contracts\RequestorContract;
|
9 |
+
|
10 |
+
abstract class AbstractLoggerRequest implements RequestContract
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* @var Collector
|
14 |
+
*/
|
15 |
+
protected $collector;
|
16 |
+
|
17 |
+
/**
|
18 |
+
* @var integer
|
19 |
+
*/
|
20 |
+
protected $id;
|
21 |
+
|
22 |
+
/**
|
23 |
+
* @var string
|
24 |
+
*/
|
25 |
+
protected $path;
|
26 |
+
|
27 |
+
public function __construct(Collector $collector, $pluginId, $path)
|
28 |
+
{
|
29 |
+
$this->collector = $collector;
|
30 |
+
$this->id = $pluginId;
|
31 |
+
$this->path = $path;
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Cast request data to array
|
36 |
+
*
|
37 |
+
* @return array
|
38 |
+
*/
|
39 |
+
public function toArray()
|
40 |
+
{
|
41 |
+
return [
|
42 |
+
'plugin_id' => $this->id,
|
43 |
+
'php_version' => $this->collector->getPHPVersion(),
|
44 |
+
'wp_version' => $this->collector->getWordPressVersion(),
|
45 |
+
'plugin_version' => $this->collector->getPluginVersion($this->path),
|
46 |
+
'url' => $this->collector->getSiteUrl(),
|
47 |
+
'sdk_version' => $this->collector->getSDKVersion(),
|
48 |
+
'ip' => $this->collector->getServerIp(),
|
49 |
+
'mysql_version' => $this->collector->getMysqlVersion(),
|
50 |
+
'locale' => $this->collector->getSiteLanguage(),
|
51 |
+
'current_theme' => $this->collector->getCurrentThemeName(),
|
52 |
+
'active_plugins_list' => implode(', ', $this->collector->getActivePluginsList()),
|
53 |
+
'email' => $this->collector->getGeneralEmailAddress(),
|
54 |
+
'name' => $this->collector->getCurrentUserName()
|
55 |
+
];
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Execute the request
|
60 |
+
* @param RequestorContract $requestor
|
61 |
+
* @return ApiResponse
|
62 |
+
*/
|
63 |
+
public abstract function execute(RequestorContract $requestor);
|
64 |
+
}
|
analyst/src/Http/Requests/ActivateRequest.php
CHANGED
@@ -1,42 +1,42 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace Analyst\Http\Requests;
|
4 |
-
|
5 |
-
use Analyst\ApiResponse;
|
6 |
-
use Analyst\Collector;
|
7 |
-
use Analyst\Contracts\RequestContract;
|
8 |
-
use Analyst\Contracts\RequestorContract;
|
9 |
-
|
10 |
-
/**
|
11 |
-
* Class ActivateRequest
|
12 |
-
*
|
13 |
-
* Is is very similar to install request
|
14 |
-
* but with different path
|
15 |
-
*
|
16 |
-
* @since 0.9.12
|
17 |
-
*/
|
18 |
-
class ActivateRequest extends AbstractLoggerRequest
|
19 |
-
{
|
20 |
-
/**
|
21 |
-
* Execute the request
|
22 |
-
* @param RequestorContract $requestor
|
23 |
-
* @return ApiResponse
|
24 |
-
*/
|
25 |
-
public function execute(RequestorContract $requestor)
|
26 |
-
{
|
27 |
-
return $requestor->post('logger/activate', $this->toArray());
|
28 |
-
}
|
29 |
-
|
30 |
-
/**
|
31 |
-
* Make request instance
|
32 |
-
*
|
33 |
-
* @param Collector $collector
|
34 |
-
* @param $pluginId
|
35 |
-
* @param $path
|
36 |
-
* @return static
|
37 |
-
*/
|
38 |
-
public static function make(Collector $collector, $pluginId, $path)
|
39 |
-
{
|
40 |
-
return new static($collector, $pluginId, $path);
|
41 |
-
}
|
42 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Http\Requests;
|
4 |
+
|
5 |
+
use Analyst\ApiResponse;
|
6 |
+
use Analyst\Collector;
|
7 |
+
use Analyst\Contracts\RequestContract;
|
8 |
+
use Analyst\Contracts\RequestorContract;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Class ActivateRequest
|
12 |
+
*
|
13 |
+
* Is is very similar to install request
|
14 |
+
* but with different path
|
15 |
+
*
|
16 |
+
* @since 0.9.12
|
17 |
+
*/
|
18 |
+
class ActivateRequest extends AbstractLoggerRequest
|
19 |
+
{
|
20 |
+
/**
|
21 |
+
* Execute the request
|
22 |
+
* @param RequestorContract $requestor
|
23 |
+
* @return ApiResponse
|
24 |
+
*/
|
25 |
+
public function execute(RequestorContract $requestor)
|
26 |
+
{
|
27 |
+
return $requestor->post('logger/activate', $this->toArray());
|
28 |
+
}
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Make request instance
|
32 |
+
*
|
33 |
+
* @param Collector $collector
|
34 |
+
* @param $pluginId
|
35 |
+
* @param $path
|
36 |
+
* @return static
|
37 |
+
*/
|
38 |
+
public static function make(Collector $collector, $pluginId, $path)
|
39 |
+
{
|
40 |
+
return new static($collector, $pluginId, $path);
|
41 |
+
}
|
42 |
+
}
|
analyst/src/Http/Requests/DeactivateRequest.php
CHANGED
@@ -1,64 +1,64 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace Analyst\Http\Requests;
|
4 |
-
|
5 |
-
use Analyst\ApiResponse;
|
6 |
-
use Analyst\Collector;
|
7 |
-
use Analyst\Contracts\RequestorContract;
|
8 |
-
|
9 |
-
/**
|
10 |
-
* Class DeactivateRequest
|
11 |
-
*
|
12 |
-
* @since 0.9.10
|
13 |
-
*/
|
14 |
-
class DeactivateRequest extends AbstractLoggerRequest
|
15 |
-
{
|
16 |
-
/**
|
17 |
-
* @var string
|
18 |
-
*/
|
19 |
-
protected $question;
|
20 |
-
|
21 |
-
/**
|
22 |
-
* @var string
|
23 |
-
*/
|
24 |
-
protected $answer;
|
25 |
-
|
26 |
-
/**
|
27 |
-
* @param Collector $collector
|
28 |
-
* @param $pluginId
|
29 |
-
* @param $path
|
30 |
-
* @param $question
|
31 |
-
* @param $answer
|
32 |
-
* @return static
|
33 |
-
*/
|
34 |
-
public static function make(Collector $collector, $pluginId, $path, $question, $answer)
|
35 |
-
{
|
36 |
-
return new static($collector, $pluginId, $path, $question, $answer);
|
37 |
-
}
|
38 |
-
|
39 |
-
public function __construct(Collector $collector, $pluginId, $path, $question, $answer)
|
40 |
-
{
|
41 |
-
parent::__construct($collector, $pluginId, $path);
|
42 |
-
|
43 |
-
$this->question = $question;
|
44 |
-
$this->answer = $answer;
|
45 |
-
}
|
46 |
-
|
47 |
-
public function toArray()
|
48 |
-
{
|
49 |
-
return array_merge(parent::toArray(), [
|
50 |
-
'question' => $this->question,
|
51 |
-
'answer' => $this->answer,
|
52 |
-
]);
|
53 |
-
}
|
54 |
-
|
55 |
-
/**
|
56 |
-
* Execute the request
|
57 |
-
* @param RequestorContract $requestor
|
58 |
-
* @return ApiResponse
|
59 |
-
*/
|
60 |
-
public function execute(RequestorContract $requestor)
|
61 |
-
{
|
62 |
-
return $requestor->post('logger/deactivate', $this->toArray());
|
63 |
-
}
|
64 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Http\Requests;
|
4 |
+
|
5 |
+
use Analyst\ApiResponse;
|
6 |
+
use Analyst\Collector;
|
7 |
+
use Analyst\Contracts\RequestorContract;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Class DeactivateRequest
|
11 |
+
*
|
12 |
+
* @since 0.9.10
|
13 |
+
*/
|
14 |
+
class DeactivateRequest extends AbstractLoggerRequest
|
15 |
+
{
|
16 |
+
/**
|
17 |
+
* @var string
|
18 |
+
*/
|
19 |
+
protected $question;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @var string
|
23 |
+
*/
|
24 |
+
protected $answer;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* @param Collector $collector
|
28 |
+
* @param $pluginId
|
29 |
+
* @param $path
|
30 |
+
* @param $question
|
31 |
+
* @param $answer
|
32 |
+
* @return static
|
33 |
+
*/
|
34 |
+
public static function make(Collector $collector, $pluginId, $path, $question, $answer)
|
35 |
+
{
|
36 |
+
return new static($collector, $pluginId, $path, $question, $answer);
|
37 |
+
}
|
38 |
+
|
39 |
+
public function __construct(Collector $collector, $pluginId, $path, $question, $answer)
|
40 |
+
{
|
41 |
+
parent::__construct($collector, $pluginId, $path);
|
42 |
+
|
43 |
+
$this->question = $question;
|
44 |
+
$this->answer = $answer;
|
45 |
+
}
|
46 |
+
|
47 |
+
public function toArray()
|
48 |
+
{
|
49 |
+
return array_merge(parent::toArray(), [
|
50 |
+
'question' => $this->question,
|
51 |
+
'answer' => $this->answer,
|
52 |
+
]);
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Execute the request
|
57 |
+
* @param RequestorContract $requestor
|
58 |
+
* @return ApiResponse
|
59 |
+
*/
|
60 |
+
public function execute(RequestorContract $requestor)
|
61 |
+
{
|
62 |
+
return $requestor->post('logger/deactivate', $this->toArray());
|
63 |
+
}
|
64 |
+
}
|
analyst/src/Http/Requests/InstallRequest.php
CHANGED
@@ -1,38 +1,38 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace Analyst\Http\Requests;
|
4 |
-
|
5 |
-
use Analyst\ApiResponse;
|
6 |
-
use Analyst\Collector;
|
7 |
-
use Analyst\Contracts\RequestorContract;
|
8 |
-
|
9 |
-
/**
|
10 |
-
* Class InstallRequest
|
11 |
-
*
|
12 |
-
* @since 0.9.4
|
13 |
-
*/
|
14 |
-
class InstallRequest extends AbstractLoggerRequest
|
15 |
-
{
|
16 |
-
/**
|
17 |
-
* Execute the request
|
18 |
-
* @param RequestorContract $requestor
|
19 |
-
* @return ApiResponse
|
20 |
-
*/
|
21 |
-
public function execute(RequestorContract $requestor)
|
22 |
-
{
|
23 |
-
return $requestor->post('logger/install', $this->toArray());
|
24 |
-
}
|
25 |
-
|
26 |
-
/**
|
27 |
-
* Make request instance
|
28 |
-
*
|
29 |
-
* @param Collector $collector
|
30 |
-
* @param $pluginId
|
31 |
-
* @param $path
|
32 |
-
* @return static
|
33 |
-
*/
|
34 |
-
public static function make(Collector $collector, $pluginId, $path)
|
35 |
-
{
|
36 |
-
return new static($collector, $pluginId, $path);
|
37 |
-
}
|
38 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Http\Requests;
|
4 |
+
|
5 |
+
use Analyst\ApiResponse;
|
6 |
+
use Analyst\Collector;
|
7 |
+
use Analyst\Contracts\RequestorContract;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Class InstallRequest
|
11 |
+
*
|
12 |
+
* @since 0.9.4
|
13 |
+
*/
|
14 |
+
class InstallRequest extends AbstractLoggerRequest
|
15 |
+
{
|
16 |
+
/**
|
17 |
+
* Execute the request
|
18 |
+
* @param RequestorContract $requestor
|
19 |
+
* @return ApiResponse
|
20 |
+
*/
|
21 |
+
public function execute(RequestorContract $requestor)
|
22 |
+
{
|
23 |
+
return $requestor->post('logger/install', $this->toArray());
|
24 |
+
}
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Make request instance
|
28 |
+
*
|
29 |
+
* @param Collector $collector
|
30 |
+
* @param $pluginId
|
31 |
+
* @param $path
|
32 |
+
* @return static
|
33 |
+
*/
|
34 |
+
public static function make(Collector $collector, $pluginId, $path)
|
35 |
+
{
|
36 |
+
return new static($collector, $pluginId, $path);
|
37 |
+
}
|
38 |
+
}
|
analyst/src/Http/Requests/OptInRequest.php
CHANGED
@@ -1,42 +1,42 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace Analyst\Http\Requests;
|
4 |
-
|
5 |
-
use Analyst\ApiResponse;
|
6 |
-
use Analyst\Collector;
|
7 |
-
use Analyst\Contracts\RequestContract;
|
8 |
-
use Analyst\Contracts\RequestorContract;
|
9 |
-
|
10 |
-
/**
|
11 |
-
* Class OptInRequest
|
12 |
-
*
|
13 |
-
* Is is very similar to install request
|
14 |
-
* but with different path
|
15 |
-
*
|
16 |
-
* @since 0.9.5
|
17 |
-
*/
|
18 |
-
class OptInRequest extends AbstractLoggerRequest
|
19 |
-
{
|
20 |
-
/**
|
21 |
-
* Execute the request
|
22 |
-
* @param RequestorContract $requestor
|
23 |
-
* @return ApiResponse
|
24 |
-
*/
|
25 |
-
public function execute(RequestorContract $requestor)
|
26 |
-
{
|
27 |
-
return $requestor->post('logger/opt-in', $this->toArray());
|
28 |
-
}
|
29 |
-
|
30 |
-
/**
|
31 |
-
* Make request instance
|
32 |
-
*
|
33 |
-
* @param Collector $collector
|
34 |
-
* @param $pluginId
|
35 |
-
* @param $path
|
36 |
-
* @return static
|
37 |
-
*/
|
38 |
-
public static function make(Collector $collector, $pluginId, $path)
|
39 |
-
{
|
40 |
-
return new static($collector, $pluginId, $path);
|
41 |
-
}
|
42 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Http\Requests;
|
4 |
+
|
5 |
+
use Analyst\ApiResponse;
|
6 |
+
use Analyst\Collector;
|
7 |
+
use Analyst\Contracts\RequestContract;
|
8 |
+
use Analyst\Contracts\RequestorContract;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Class OptInRequest
|
12 |
+
*
|
13 |
+
* Is is very similar to install request
|
14 |
+
* but with different path
|
15 |
+
*
|
16 |
+
* @since 0.9.5
|
17 |
+
*/
|
18 |
+
class OptInRequest extends AbstractLoggerRequest
|
19 |
+
{
|
20 |
+
/**
|
21 |
+
* Execute the request
|
22 |
+
* @param RequestorContract $requestor
|
23 |
+
* @return ApiResponse
|
24 |
+
*/
|
25 |
+
public function execute(RequestorContract $requestor)
|
26 |
+
{
|
27 |
+
return $requestor->post('logger/opt-in', $this->toArray());
|
28 |
+
}
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Make request instance
|
32 |
+
*
|
33 |
+
* @param Collector $collector
|
34 |
+
* @param $pluginId
|
35 |
+
* @param $path
|
36 |
+
* @return static
|
37 |
+
*/
|
38 |
+
public static function make(Collector $collector, $pluginId, $path)
|
39 |
+
{
|
40 |
+
return new static($collector, $pluginId, $path);
|
41 |
+
}
|
42 |
+
}
|
analyst/src/Http/Requests/OptOutRequest.php
CHANGED
@@ -1,40 +1,40 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace Analyst\Http\Requests;
|
4 |
-
|
5 |
-
use Analyst\ApiResponse;
|
6 |
-
use Analyst\Collector;
|
7 |
-
use Analyst\Contracts\RequestContract;
|
8 |
-
use Analyst\Contracts\RequestorContract;
|
9 |
-
|
10 |
-
/**
|
11 |
-
* Class OptOutRequest
|
12 |
-
*
|
13 |
-
* Is is very similar to install request
|
14 |
-
* but with different path
|
15 |
-
*
|
16 |
-
* @since 0.9.9
|
17 |
-
*/
|
18 |
-
class OptOutRequest extends AbstractLoggerRequest
|
19 |
-
{
|
20 |
-
/**
|
21 |
-
* @param Collector $collector
|
22 |
-
* @param $pluginId
|
23 |
-
* @param $path
|
24 |
-
* @return static
|
25 |
-
*/
|
26 |
-
public static function make(Collector $collector, $pluginId, $path)
|
27 |
-
{
|
28 |
-
return new static($collector, $pluginId, $path);
|
29 |
-
}
|
30 |
-
|
31 |
-
/**
|
32 |
-
* Execute the request
|
33 |
-
* @param RequestorContract $requestor
|
34 |
-
* @return ApiResponse
|
35 |
-
*/
|
36 |
-
public function execute(RequestorContract $requestor)
|
37 |
-
{
|
38 |
-
return $requestor->post('logger/opt-out', $this->toArray());
|
39 |
-
}
|
40 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Http\Requests;
|
4 |
+
|
5 |
+
use Analyst\ApiResponse;
|
6 |
+
use Analyst\Collector;
|
7 |
+
use Analyst\Contracts\RequestContract;
|
8 |
+
use Analyst\Contracts\RequestorContract;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Class OptOutRequest
|
12 |
+
*
|
13 |
+
* Is is very similar to install request
|
14 |
+
* but with different path
|
15 |
+
*
|
16 |
+
* @since 0.9.9
|
17 |
+
*/
|
18 |
+
class OptOutRequest extends AbstractLoggerRequest
|
19 |
+
{
|
20 |
+
/**
|
21 |
+
* @param Collector $collector
|
22 |
+
* @param $pluginId
|
23 |
+
* @param $path
|
24 |
+
* @return static
|
25 |
+
*/
|
26 |
+
public static function make(Collector $collector, $pluginId, $path)
|
27 |
+
{
|
28 |
+
return new static($collector, $pluginId, $path);
|
29 |
+
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Execute the request
|
33 |
+
* @param RequestorContract $requestor
|
34 |
+
* @return ApiResponse
|
35 |
+
*/
|
36 |
+
public function execute(RequestorContract $requestor)
|
37 |
+
{
|
38 |
+
return $requestor->post('logger/opt-out', $this->toArray());
|
39 |
+
}
|
40 |
+
}
|
analyst/src/Http/Requests/UninstallRequest.php
CHANGED
@@ -1,36 +1,36 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace Analyst\Http\Requests;
|
4 |
-
|
5 |
-
use Analyst\ApiResponse;
|
6 |
-
use Analyst\Collector;
|
7 |
-
use Analyst\Contracts\RequestorContract;
|
8 |
-
|
9 |
-
/**
|
10 |
-
* Class DeactivateRequest
|
11 |
-
*
|
12 |
-
* @since 0.9.13
|
13 |
-
*/
|
14 |
-
class UninstallRequest extends AbstractLoggerRequest
|
15 |
-
{
|
16 |
-
/**
|
17 |
-
* @param Collector $collector
|
18 |
-
* @param $pluginId
|
19 |
-
* @param $path
|
20 |
-
* @return static
|
21 |
-
*/
|
22 |
-
public static function make(Collector $collector, $pluginId, $path)
|
23 |
-
{
|
24 |
-
return new static($collector, $pluginId, $path);
|
25 |
-
}
|
26 |
-
|
27 |
-
/**
|
28 |
-
* Execute the request
|
29 |
-
* @param RequestorContract $requestor
|
30 |
-
* @return ApiResponse
|
31 |
-
*/
|
32 |
-
public function execute(RequestorContract $requestor)
|
33 |
-
{
|
34 |
-
return $requestor->post('logger/uninstall', $this->toArray());
|
35 |
-
}
|
36 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Http\Requests;
|
4 |
+
|
5 |
+
use Analyst\ApiResponse;
|
6 |
+
use Analyst\Collector;
|
7 |
+
use Analyst\Contracts\RequestorContract;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Class DeactivateRequest
|
11 |
+
*
|
12 |
+
* @since 0.9.13
|
13 |
+
*/
|
14 |
+
class UninstallRequest extends AbstractLoggerRequest
|
15 |
+
{
|
16 |
+
/**
|
17 |
+
* @param Collector $collector
|
18 |
+
* @param $pluginId
|
19 |
+
* @param $path
|
20 |
+
* @return static
|
21 |
+
*/
|
22 |
+
public static function make(Collector $collector, $pluginId, $path)
|
23 |
+
{
|
24 |
+
return new static($collector, $pluginId, $path);
|
25 |
+
}
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Execute the request
|
29 |
+
* @param RequestorContract $requestor
|
30 |
+
* @return ApiResponse
|
31 |
+
*/
|
32 |
+
public function execute(RequestorContract $requestor)
|
33 |
+
{
|
34 |
+
return $requestor->post('logger/uninstall', $this->toArray());
|
35 |
+
}
|
36 |
+
}
|
analyst/src/Http/WordPressHttpClient.php
CHANGED
@@ -1,61 +1,61 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace Analyst\Http;
|
4 |
-
|
5 |
-
use WP_Error;
|
6 |
-
use Analyst\ApiResponse;
|
7 |
-
use Analyst\Contracts\HttpClientContract;
|
8 |
-
use Requests_Utility_CaseInsensitiveDictionary;
|
9 |
-
|
10 |
-
class WordPressHttpClient implements HttpClientContract
|
11 |
-
{
|
12 |
-
/**
|
13 |
-
* Make an http request
|
14 |
-
*
|
15 |
-
* @param $method
|
16 |
-
* @param $url
|
17 |
-
* @param $body
|
18 |
-
* @param $headers
|
19 |
-
* @return ApiResponse
|
20 |
-
*/
|
21 |
-
public function request($method, $url, $body, $headers)
|
22 |
-
{
|
23 |
-
$options = [
|
24 |
-
'body' => json_encode($body),
|
25 |
-
'headers' => $headers,
|
26 |
-
'method' => $method,
|
27 |
-
'timeout' => 30,
|
28 |
-
];
|
29 |
-
|
30 |
-
$response = wp_remote_request($url, $options);
|
31 |
-
|
32 |
-
$body = [];
|
33 |
-
$responseHeaders = [];
|
34 |
-
|
35 |
-
if ($response instanceof WP_Error) {
|
36 |
-
$code = $response->get_error_code();
|
37 |
-
} else {
|
38 |
-
/** @var Requests_Utility_CaseInsensitiveDictionary $headers */
|
39 |
-
$responseHeaders = $response['headers']->getAll();
|
40 |
-
$body = json_decode($response['body'], true);
|
41 |
-
$code = $response['response']['code'];
|
42 |
-
}
|
43 |
-
|
44 |
-
|
45 |
-
return new ApiResponse(
|
46 |
-
$body,
|
47 |
-
$code,
|
48 |
-
$responseHeaders
|
49 |
-
);
|
50 |
-
}
|
51 |
-
|
52 |
-
/**
|
53 |
-
* Must return `true` if client is supported
|
54 |
-
*
|
55 |
-
* @return bool
|
56 |
-
*/
|
57 |
-
public static function hasSupport()
|
58 |
-
{
|
59 |
-
return function_exists('wp_remote_request');
|
60 |
-
}
|
61 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Http;
|
4 |
+
|
5 |
+
use WP_Error;
|
6 |
+
use Analyst\ApiResponse;
|
7 |
+
use Analyst\Contracts\HttpClientContract;
|
8 |
+
use Requests_Utility_CaseInsensitiveDictionary;
|
9 |
+
|
10 |
+
class WordPressHttpClient implements HttpClientContract
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Make an http request
|
14 |
+
*
|
15 |
+
* @param $method
|
16 |
+
* @param $url
|
17 |
+
* @param $body
|
18 |
+
* @param $headers
|
19 |
+
* @return ApiResponse
|
20 |
+
*/
|
21 |
+
public function request($method, $url, $body, $headers)
|
22 |
+
{
|
23 |
+
$options = [
|
24 |
+
'body' => json_encode($body),
|
25 |
+
'headers' => $headers,
|
26 |
+
'method' => $method,
|
27 |
+
'timeout' => 30,
|
28 |
+
];
|
29 |
+
|
30 |
+
$response = wp_remote_request($url, $options);
|
31 |
+
|
32 |
+
$body = [];
|
33 |
+
$responseHeaders = [];
|
34 |
+
|
35 |
+
if ($response instanceof WP_Error) {
|
36 |
+
$code = $response->get_error_code();
|
37 |
+
} else {
|
38 |
+
/** @var Requests_Utility_CaseInsensitiveDictionary $headers */
|
39 |
+
$responseHeaders = $response['headers']->getAll();
|
40 |
+
$body = json_decode($response['body'], true);
|
41 |
+
$code = $response['response']['code'];
|
42 |
+
}
|
43 |
+
|
44 |
+
|
45 |
+
return new ApiResponse(
|
46 |
+
$body,
|
47 |
+
$code,
|
48 |
+
$responseHeaders
|
49 |
+
);
|
50 |
+
}
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Must return `true` if client is supported
|
54 |
+
*
|
55 |
+
* @return bool
|
56 |
+
*/
|
57 |
+
public static function hasSupport()
|
58 |
+
{
|
59 |
+
return function_exists('wp_remote_request');
|
60 |
+
}
|
61 |
+
}
|
analyst/src/Mutator.php
CHANGED
@@ -1,103 +1,103 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace Analyst;
|
4 |
-
|
5 |
-
use Analyst\Cache\DatabaseCache;
|
6 |
-
use Analyst\Contracts\CacheContract;
|
7 |
-
use Analyst\Notices\NoticeFactory;
|
8 |
-
|
9 |
-
/**
|
10 |
-
* Class Mutator mutates (modifies) UX with additional
|
11 |
-
* functional
|
12 |
-
*/
|
13 |
-
class Mutator
|
14 |
-
{
|
15 |
-
protected $notices = [];
|
16 |
-
|
17 |
-
/**
|
18 |
-
* @var NoticeFactory
|
19 |
-
*/
|
20 |
-
protected $factory;
|
21 |
-
|
22 |
-
/**
|
23 |
-
* @var CacheContract
|
24 |
-
*/
|
25 |
-
protected $cache;
|
26 |
-
|
27 |
-
public function __construct()
|
28 |
-
{
|
29 |
-
$this->factory = NoticeFactory::instance();
|
30 |
-
|
31 |
-
$this->notices = $this->factory->getNotices();
|
32 |
-
|
33 |
-
$this->cache = DatabaseCache::getInstance();
|
34 |
-
}
|
35 |
-
|
36 |
-
/**
|
37 |
-
* Register filters all necessary stuff.
|
38 |
-
* Can be invoked only once.
|
39 |
-
*
|
40 |
-
* @return void
|
41 |
-
*/
|
42 |
-
public function initialize()
|
43 |
-
{
|
44 |
-
$this->registerLinks();
|
45 |
-
$this->registerAssets();
|
46 |
-
$this->registerHooks();
|
47 |
-
}
|
48 |
-
|
49 |
-
/**
|
50 |
-
* Register all necessary filters and templates
|
51 |
-
*
|
52 |
-
* @return void
|
53 |
-
*/
|
54 |
-
protected function registerLinks()
|
55 |
-
{
|
56 |
-
add_action('admin_footer', function () {
|
57 |
-
analyst_require_template('optout.php', [
|
58 |
-
'shieldImage' => analyst_assets_url('img/shield_question.png')
|
59 |
-
]);
|
60 |
-
|
61 |
-
analyst_require_template('optin.php');
|
62 |
-
|
63 |
-
analyst_require_template('forms/deactivate.php', [
|
64 |
-
'pencilImage' => analyst_assets_url('img/pencil.png'),
|
65 |
-
'smileImage' => analyst_assets_url('img/smile.png'),
|
66 |
-
]);
|
67 |
-
|
68 |
-
analyst_require_template('forms/install.php', [
|
69 |
-
'pluginToInstall' => $this->cache->get('plugin_to_install'),
|
70 |
-
'shieldImage' => analyst_assets_url('img/shield_success.png'),
|
71 |
-
]);
|
72 |
-
});
|
73 |
-
|
74 |
-
add_action('admin_notices',function () {
|
75 |
-
foreach ($this->notices as $notice) {
|
76 |
-
analyst_require_template('notice.php', ['notice' => $notice]);
|
77 |
-
}
|
78 |
-
});
|
79 |
-
}
|
80 |
-
|
81 |
-
/**
|
82 |
-
* Register all assets
|
83 |
-
*/
|
84 |
-
public function registerAssets()
|
85 |
-
{
|
86 |
-
add_action('admin_enqueue_scripts', function () {
|
87 |
-
wp_enqueue_style('analyst_custom', analyst_assets_url('/css/customize.css'));
|
88 |
-
wp_enqueue_script('analyst_custom', analyst_assets_url('/js/customize.js'));
|
89 |
-
});
|
90 |
-
}
|
91 |
-
|
92 |
-
/**
|
93 |
-
* Register action hooks
|
94 |
-
*/
|
95 |
-
public function registerHooks()
|
96 |
-
{
|
97 |
-
add_action('wp_ajax_analyst_notification_dismiss', function () {
|
98 |
-
$this->factory->remove($_POST['id']);
|
99 |
-
|
100 |
-
$this->factory->sync();
|
101 |
-
});
|
102 |
-
}
|
103 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst;
|
4 |
+
|
5 |
+
use Analyst\Cache\DatabaseCache;
|
6 |
+
use Analyst\Contracts\CacheContract;
|
7 |
+
use Analyst\Notices\NoticeFactory;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Class Mutator mutates (modifies) UX with additional
|
11 |
+
* functional
|
12 |
+
*/
|
13 |
+
class Mutator
|
14 |
+
{
|
15 |
+
protected $notices = [];
|
16 |
+
|
17 |
+
/**
|
18 |
+
* @var NoticeFactory
|
19 |
+
*/
|
20 |
+
protected $factory;
|
21 |
+
|
22 |
+
/**
|
23 |
+
* @var CacheContract
|
24 |
+
*/
|
25 |
+
protected $cache;
|
26 |
+
|
27 |
+
public function __construct()
|
28 |
+
{
|
29 |
+
$this->factory = NoticeFactory::instance();
|
30 |
+
|
31 |
+
$this->notices = $this->factory->getNotices();
|
32 |
+
|
33 |
+
$this->cache = DatabaseCache::getInstance();
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Register filters all necessary stuff.
|
38 |
+
* Can be invoked only once.
|
39 |
+
*
|
40 |
+
* @return void
|
41 |
+
*/
|
42 |
+
public function initialize()
|
43 |
+
{
|
44 |
+
$this->registerLinks();
|
45 |
+
$this->registerAssets();
|
46 |
+
$this->registerHooks();
|
47 |
+
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Register all necessary filters and templates
|
51 |
+
*
|
52 |
+
* @return void
|
53 |
+
*/
|
54 |
+
protected function registerLinks()
|
55 |
+
{
|
56 |
+
add_action('admin_footer', function () {
|
57 |
+
analyst_require_template('optout.php', [
|
58 |
+
'shieldImage' => analyst_assets_url('img/shield_question.png')
|
59 |
+
]);
|
60 |
+
|
61 |
+
analyst_require_template('optin.php');
|
62 |
+
|
63 |
+
analyst_require_template('forms/deactivate.php', [
|
64 |
+
'pencilImage' => analyst_assets_url('img/pencil.png'),
|
65 |
+
'smileImage' => analyst_assets_url('img/smile.png'),
|
66 |
+
]);
|
67 |
+
|
68 |
+
analyst_require_template('forms/install.php', [
|
69 |
+
'pluginToInstall' => $this->cache->get('plugin_to_install'),
|
70 |
+
'shieldImage' => analyst_assets_url('img/shield_success.png'),
|
71 |
+
]);
|
72 |
+
});
|
73 |
+
|
74 |
+
add_action('admin_notices',function () {
|
75 |
+
foreach ($this->notices as $notice) {
|
76 |
+
analyst_require_template('notice.php', ['notice' => $notice]);
|
77 |
+
}
|
78 |
+
});
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Register all assets
|
83 |
+
*/
|
84 |
+
public function registerAssets()
|
85 |
+
{
|
86 |
+
add_action('admin_enqueue_scripts', function () {
|
87 |
+
wp_enqueue_style('analyst_custom', analyst_assets_url('/css/customize.css'));
|
88 |
+
wp_enqueue_script('analyst_custom', analyst_assets_url('/js/customize.js'));
|
89 |
+
});
|
90 |
+
}
|
91 |
+
|
92 |
+
/**
|
93 |
+
* Register action hooks
|
94 |
+
*/
|
95 |
+
public function registerHooks()
|
96 |
+
{
|
97 |
+
add_action('wp_ajax_analyst_notification_dismiss', function () {
|
98 |
+
$this->factory->remove($_POST['id']);
|
99 |
+
|
100 |
+
$this->factory->sync();
|
101 |
+
});
|
102 |
+
}
|
103 |
+
}
|
analyst/src/Notices/Notice.php
CHANGED
@@ -1,121 +1,121 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace Analyst\Notices;
|
4 |
-
|
5 |
-
class Notice
|
6 |
-
{
|
7 |
-
/**
|
8 |
-
* Id of notice
|
9 |
-
*
|
10 |
-
* @var string
|
11 |
-
*/
|
12 |
-
protected $id;
|
13 |
-
|
14 |
-
/**
|
15 |
-
* Body of notice
|
16 |
-
*
|
17 |
-
* @var string
|
18 |
-
*/
|
19 |
-
protected $body;
|
20 |
-
|
21 |
-
/**
|
22 |
-
* Account id
|
23 |
-
*
|
24 |
-
* @var string
|
25 |
-
*/
|
26 |
-
protected $accountId;
|
27 |
-
|
28 |
-
/**
|
29 |
-
* The plugin name
|
30 |
-
*
|
31 |
-
* @var string
|
32 |
-
*/
|
33 |
-
protected $pluginName;
|
34 |
-
|
35 |
-
/**
|
36 |
-
* New notice
|
37 |
-
*
|
38 |
-
* @param $id
|
39 |
-
* @param $accountId
|
40 |
-
* @param $body
|
41 |
-
* @param null $pluginName
|
42 |
-
*
|
43 |
-
* @return Notice
|
44 |
-
*/
|
45 |
-
public static function make($id, $accountId, $body, $pluginName = null)
|
46 |
-
{
|
47 |
-
return new Notice($id, $accountId, $body, $pluginName);
|
48 |
-
}
|
49 |
-
|
50 |
-
public function __construct($id, $accountId, $body, $pluginName)
|
51 |
-
{
|
52 |
-
$this->setId($id);
|
53 |
-
$this->setBody($body);
|
54 |
-
$this->setAccountId($accountId);
|
55 |
-
$this->setPluginName($pluginName);
|
56 |
-
}
|
57 |
-
|
58 |
-
/**
|
59 |
-
* @return string
|
60 |
-
*/
|
61 |
-
public function getId()
|
62 |
-
{
|
63 |
-
return $this->id;
|
64 |
-
}
|
65 |
-
|
66 |
-
/**
|
67 |
-
* @param string $id
|
68 |
-
*/
|
69 |
-
public function setId($id)
|
70 |
-
{
|
71 |
-
$this->id = $id;
|
72 |
-
}
|
73 |
-
|
74 |
-
/**
|
75 |
-
* @return string
|
76 |
-
*/
|
77 |
-
public function getBody()
|
78 |
-
{
|
79 |
-
return $this->body;
|
80 |
-
}
|
81 |
-
|
82 |
-
/**
|
83 |
-
* @param string $body
|
84 |
-
*/
|
85 |
-
public function setBody($body)
|
86 |
-
{
|
87 |
-
$this->body = $body;
|
88 |
-
}
|
89 |
-
|
90 |
-
/**
|
91 |
-
* @return string
|
92 |
-
*/
|
93 |
-
public function getAccountId()
|
94 |
-
{
|
95 |
-
return $this->accountId;
|
96 |
-
}
|
97 |
-
|
98 |
-
/**
|
99 |
-
* @param string $accountId
|
100 |
-
*/
|
101 |
-
public function setAccountId($accountId)
|
102 |
-
{
|
103 |
-
$this->accountId = $accountId;
|
104 |
-
}
|
105 |
-
|
106 |
-
/**
|
107 |
-
* @return string|null
|
108 |
-
*/
|
109 |
-
public function getPluginName()
|
110 |
-
{
|
111 |
-
return $this->pluginName;
|
112 |
-
}
|
113 |
-
|
114 |
-
/**
|
115 |
-
* @param string $pluginName
|
116 |
-
*/
|
117 |
-
public function setPluginName($pluginName)
|
118 |
-
{
|
119 |
-
$this->pluginName = $pluginName;
|
120 |
-
}
|
121 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Notices;
|
4 |
+
|
5 |
+
class Notice
|
6 |
+
{
|
7 |
+
/**
|
8 |
+
* Id of notice
|
9 |
+
*
|
10 |
+
* @var string
|
11 |
+
*/
|
12 |
+
protected $id;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Body of notice
|
16 |
+
*
|
17 |
+
* @var string
|
18 |
+
*/
|
19 |
+
protected $body;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Account id
|
23 |
+
*
|
24 |
+
* @var string
|
25 |
+
*/
|
26 |
+
protected $accountId;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* The plugin name
|
30 |
+
*
|
31 |
+
* @var string
|
32 |
+
*/
|
33 |
+
protected $pluginName;
|
34 |
+
|
35 |
+
/**
|
36 |
+
* New notice
|
37 |
+
*
|
38 |
+
* @param $id
|
39 |
+
* @param $accountId
|
40 |
+
* @param $body
|
41 |
+
* @param null $pluginName
|
42 |
+
*
|
43 |
+
* @return Notice
|
44 |
+
*/
|
45 |
+
public static function make($id, $accountId, $body, $pluginName = null)
|
46 |
+
{
|
47 |
+
return new Notice($id, $accountId, $body, $pluginName);
|
48 |
+
}
|
49 |
+
|
50 |
+
public function __construct($id, $accountId, $body, $pluginName)
|
51 |
+
{
|
52 |
+
$this->setId($id);
|
53 |
+
$this->setBody($body);
|
54 |
+
$this->setAccountId($accountId);
|
55 |
+
$this->setPluginName($pluginName);
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* @return string
|
60 |
+
*/
|
61 |
+
public function getId()
|
62 |
+
{
|
63 |
+
return $this->id;
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* @param string $id
|
68 |
+
*/
|
69 |
+
public function setId($id)
|
70 |
+
{
|
71 |
+
$this->id = $id;
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* @return string
|
76 |
+
*/
|
77 |
+
public function getBody()
|
78 |
+
{
|
79 |
+
return $this->body;
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
* @param string $body
|
84 |
+
*/
|
85 |
+
public function setBody($body)
|
86 |
+
{
|
87 |
+
$this->body = $body;
|
88 |
+
}
|
89 |
+
|
90 |
+
/**
|
91 |
+
* @return string
|
92 |
+
*/
|
93 |
+
public function getAccountId()
|
94 |
+
{
|
95 |
+
return $this->accountId;
|
96 |
+
}
|
97 |
+
|
98 |
+
/**
|
99 |
+
* @param string $accountId
|
100 |
+
*/
|
101 |
+
public function setAccountId($accountId)
|
102 |
+
{
|
103 |
+
$this->accountId = $accountId;
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* @return string|null
|
108 |
+
*/
|
109 |
+
public function getPluginName()
|
110 |
+
{
|
111 |
+
return $this->pluginName;
|
112 |
+
}
|
113 |
+
|
114 |
+
/**
|
115 |
+
* @param string $pluginName
|
116 |
+
*/
|
117 |
+
public function setPluginName($pluginName)
|
118 |
+
{
|
119 |
+
$this->pluginName = $pluginName;
|
120 |
+
}
|
121 |
+
}
|
analyst/src/Notices/NoticeFactory.php
CHANGED
@@ -1,130 +1,130 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace Analyst\Notices;
|
4 |
-
|
5 |
-
use Analyst\Core\AbstractFactory;
|
6 |
-
|
7 |
-
class NoticeFactory extends AbstractFactory
|
8 |
-
{
|
9 |
-
private static $instance;
|
10 |
-
|
11 |
-
CONST OPTIONS_KEY = 'analyst_notices';
|
12 |
-
|
13 |
-
/**
|
14 |
-
* Application notifications
|
15 |
-
*
|
16 |
-
* @var array
|
17 |
-
*/
|
18 |
-
protected $notices = [];
|
19 |
-
|
20 |
-
/**
|
21 |
-
* Read factory from options or make fresh instance
|
22 |
-
*
|
23 |
-
* @return NoticeFactory
|
24 |
-
*/
|
25 |
-
public static function instance()
|
26 |
-
{
|
27 |
-
if (!static::$instance) {
|
28 |
-
$raw = get_option(self::OPTIONS_KEY);
|
29 |
-
|
30 |
-
// In case object is already unserialized
|
31 |
-
// and instance of AccountDataFactory we
|
32 |
-
// return it, in other case deal with
|
33 |
-
// serialized string data
|
34 |
-
if ($raw instanceof self) {
|
35 |
-
static::$instance = $raw;
|
36 |
-
} else {
|
37 |
-
static::$instance = is_string($raw) ? static::unserialize($raw) : new self();
|
38 |
-
}
|
39 |
-
}
|
40 |
-
|
41 |
-
return static::$instance;
|
42 |
-
}
|
43 |
-
|
44 |
-
/**
|
45 |
-
* Sync this object data with cache
|
46 |
-
*/
|
47 |
-
public function sync()
|
48 |
-
{
|
49 |
-
update_option(self::OPTIONS_KEY, serialize($this));
|
50 |
-
}
|
51 |
-
|
52 |
-
/**
|
53 |
-
* Sync this instance data with cache
|
54 |
-
*/
|
55 |
-
public static function syncData()
|
56 |
-
{
|
57 |
-
static::instance()->sync();
|
58 |
-
}
|
59 |
-
|
60 |
-
/**
|
61 |
-
* @return array
|
62 |
-
*/
|
63 |
-
public function getNotices()
|
64 |
-
{
|
65 |
-
return $this->notices;
|
66 |
-
}
|
67 |
-
|
68 |
-
/**
|
69 |
-
* Filter out notices for certain account
|
70 |
-
*
|
71 |
-
* @param $accountId
|
72 |
-
* @return array
|
73 |
-
*/
|
74 |
-
public function getNoticesForAccount($accountId)
|
75 |
-
{
|
76 |
-
return array_filter($this->notices, function (Notice $notice) use ($accountId) {
|
77 |
-
return $notice->getAccountId() === $accountId;
|
78 |
-
});
|
79 |
-
}
|
80 |
-
|
81 |
-
/**
|
82 |
-
* Add new notice
|
83 |
-
*
|
84 |
-
* @param $notice
|
85 |
-
*
|
86 |
-
* @return $this
|
87 |
-
*/
|
88 |
-
public function addNotice($notice)
|
89 |
-
{
|
90 |
-
array_push($this->notices, $notice);
|
91 |
-
|
92 |
-
$this->sync();
|
93 |
-
|
94 |
-
return $this;
|
95 |
-
}
|
96 |
-
|
97 |
-
/**
|
98 |
-
* Find notice by id
|
99 |
-
*
|
100 |
-
* @param $id
|
101 |
-
* @return Notice|null
|
102 |
-
*/
|
103 |
-
public function find($id)
|
104 |
-
{
|
105 |
-
$notices = array_filter($this->notices, function (Notice $notice) use ($id) {
|
106 |
-
return $notice->getId() === $id;
|
107 |
-
});
|
108 |
-
|
109 |
-
return array_pop($notices);
|
110 |
-
}
|
111 |
-
|
112 |
-
/**
|
113 |
-
* Remove notice by it's id
|
114 |
-
*
|
115 |
-
* @param $id
|
116 |
-
*/
|
117 |
-
public function remove($id)
|
118 |
-
{
|
119 |
-
// Get key of notice to remove
|
120 |
-
$key = array_search(
|
121 |
-
$this->find($id),
|
122 |
-
$this->notices
|
123 |
-
);
|
124 |
-
|
125 |
-
// Unset notice with key
|
126 |
-
unset($this->notices[$key]);
|
127 |
-
|
128 |
-
$this->sync();
|
129 |
-
}
|
130 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Notices;
|
4 |
+
|
5 |
+
use Analyst\Core\AbstractFactory;
|
6 |
+
|
7 |
+
class NoticeFactory extends AbstractFactory
|
8 |
+
{
|
9 |
+
private static $instance;
|
10 |
+
|
11 |
+
CONST OPTIONS_KEY = 'analyst_notices';
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Application notifications
|
15 |
+
*
|
16 |
+
* @var array
|
17 |
+
*/
|
18 |
+
protected $notices = [];
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Read factory from options or make fresh instance
|
22 |
+
*
|
23 |
+
* @return NoticeFactory
|
24 |
+
*/
|
25 |
+
public static function instance()
|
26 |
+
{
|
27 |
+
if (!static::$instance) {
|
28 |
+
$raw = get_option(self::OPTIONS_KEY);
|
29 |
+
|
30 |
+
// In case object is already unserialized
|
31 |
+
// and instance of AccountDataFactory we
|
32 |
+
// return it, in other case deal with
|
33 |
+
// serialized string data
|
34 |
+
if ($raw instanceof self) {
|
35 |
+
static::$instance = $raw;
|
36 |
+
} else {
|
37 |
+
static::$instance = is_string($raw) ? static::unserialize($raw) : new self();
|
38 |
+
}
|
39 |
+
}
|
40 |
+
|
41 |
+
return static::$instance;
|
42 |
+
}
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Sync this object data with cache
|
46 |
+
*/
|
47 |
+
public function sync()
|
48 |
+
{
|
49 |
+
update_option(self::OPTIONS_KEY, serialize($this));
|
50 |
+
}
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Sync this instance data with cache
|
54 |
+
*/
|
55 |
+
public static function syncData()
|
56 |
+
{
|
57 |
+
static::instance()->sync();
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* @return array
|
62 |
+
*/
|
63 |
+
public function getNotices()
|
64 |
+
{
|
65 |
+
return $this->notices;
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* Filter out notices for certain account
|
70 |
+
*
|
71 |
+
* @param $accountId
|
72 |
+
* @return array
|
73 |
+
*/
|
74 |
+
public function getNoticesForAccount($accountId)
|
75 |
+
{
|
76 |
+
return array_filter($this->notices, function (Notice $notice) use ($accountId) {
|
77 |
+
return $notice->getAccountId() === $accountId;
|
78 |
+
});
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Add new notice
|
83 |
+
*
|
84 |
+
* @param $notice
|
85 |
+
*
|
86 |
+
* @return $this
|
87 |
+
*/
|
88 |
+
public function addNotice($notice)
|
89 |
+
{
|
90 |
+
array_push($this->notices, $notice);
|
91 |
+
|
92 |
+
$this->sync();
|
93 |
+
|
94 |
+
return $this;
|
95 |
+
}
|
96 |
+
|
97 |
+
/**
|
98 |
+
* Find notice by id
|
99 |
+
*
|
100 |
+
* @param $id
|
101 |
+
* @return Notice|null
|
102 |
+
*/
|
103 |
+
public function find($id)
|
104 |
+
{
|
105 |
+
$notices = array_filter($this->notices, function (Notice $notice) use ($id) {
|
106 |
+
return $notice->getId() === $id;
|
107 |
+
});
|
108 |
+
|
109 |
+
return array_pop($notices);
|
110 |
+
}
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Remove notice by it's id
|
114 |
+
*
|
115 |
+
* @param $id
|
116 |
+
*/
|
117 |
+
public function remove($id)
|
118 |
+
{
|
119 |
+
// Get key of notice to remove
|
120 |
+
$key = array_search(
|
121 |
+
$this->find($id),
|
122 |
+
$this->notices
|
123 |
+
);
|
124 |
+
|
125 |
+
// Unset notice with key
|
126 |
+
unset($this->notices[$key]);
|
127 |
+
|
128 |
+
$this->sync();
|
129 |
+
}
|
130 |
+
}
|
analyst/src/helpers.php
CHANGED
@@ -1,84 +1,84 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
if (! function_exists('analyst_assets_path')) {
|
4 |
-
/**
|
5 |
-
* Generates path to file in assets folder
|
6 |
-
*
|
7 |
-
* @param $file
|
8 |
-
* @return string
|
9 |
-
*/
|
10 |
-
function analyst_assets_path($file)
|
11 |
-
{
|
12 |
-
$path = sprintf('%s/assets/%s', realpath(__DIR__ . '/..'), trim($file, '/'));
|
13 |
-
|
14 |
-
return wp_normalize_path($path);
|
15 |
-
}
|
16 |
-
}
|
17 |
-
|
18 |
-
|
19 |
-
if (! function_exists('analyst_assets_url')) {
|
20 |
-
/**
|
21 |
-
* Generates url to file in assets folder
|
22 |
-
*
|
23 |
-
* @param $file
|
24 |
-
* @return string
|
25 |
-
*/
|
26 |
-
function analyst_assets_url($file)
|
27 |
-
{
|
28 |
-
$absolutePath = analyst_assets_path($file);
|
29 |
-
|
30 |
-
// We can always rely on WP_PLUGIN_DIR, because that's where
|
31 |
-
// wordpress install it's plugin's. So we remove last segment
|
32 |
-
// of that path to get the content dir AKA directly where
|
33 |
-
// plugins are installed and make the magic...
|
34 |
-
$contentDir = is_link(WP_PLUGIN_DIR) ?
|
35 |
-
dirname(wp_normalize_path(readlink(WP_PLUGIN_DIR))) :
|
36 |
-
dirname(wp_normalize_path(WP_PLUGIN_DIR));
|
37 |
-
|
38 |
-
$relativePath = str_replace( $contentDir, '', $absolutePath);
|
39 |
-
|
40 |
-
return content_url(wp_normalize_path($relativePath));
|
41 |
-
}
|
42 |
-
}
|
43 |
-
|
44 |
-
if (! function_exists('analyst_templates_path')) {
|
45 |
-
/**
|
46 |
-
* Generates path to file in templates folder
|
47 |
-
*
|
48 |
-
* @param $file
|
49 |
-
* @return string
|
50 |
-
*/
|
51 |
-
function analyst_templates_path($file)
|
52 |
-
{
|
53 |
-
$path = sprintf('%s/templates/%s', realpath(__DIR__ . '/..'), trim($file, '/'));
|
54 |
-
|
55 |
-
return wp_normalize_path($path);
|
56 |
-
}
|
57 |
-
}
|
58 |
-
|
59 |
-
if (! function_exists('analyst_require_template')) {
|
60 |
-
/**
|
61 |
-
* Require certain template with data
|
62 |
-
*
|
63 |
-
* @param $file
|
64 |
-
* @param array $data
|
65 |
-
*/
|
66 |
-
function analyst_require_template($file, $data = [])
|
67 |
-
{
|
68 |
-
// Extract data to current scope table
|
69 |
-
extract($data);
|
70 |
-
|
71 |
-
require analyst_templates_path($file);
|
72 |
-
}
|
73 |
-
}
|
74 |
-
|
75 |
-
if (! function_exists('dd')) {
|
76 |
-
/**
|
77 |
-
* Dump some data
|
78 |
-
*/
|
79 |
-
function dd ()
|
80 |
-
{
|
81 |
-
var_dump(func_get_args());
|
82 |
-
die();
|
83 |
-
}
|
84 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
if (! function_exists('analyst_assets_path')) {
|
4 |
+
/**
|
5 |
+
* Generates path to file in assets folder
|
6 |
+
*
|
7 |
+
* @param $file
|
8 |
+
* @return string
|
9 |
+
*/
|
10 |
+
function analyst_assets_path($file)
|
11 |
+
{
|
12 |
+
$path = sprintf('%s/assets/%s', realpath(__DIR__ . '/..'), trim($file, '/'));
|
13 |
+
|
14 |
+
return wp_normalize_path($path);
|
15 |
+
}
|
16 |
+
}
|
17 |
+
|
18 |
+
|
19 |
+
if (! function_exists('analyst_assets_url')) {
|
20 |
+
/**
|
21 |
+
* Generates url to file in assets folder
|
22 |
+
*
|
23 |
+
* @param $file
|
24 |
+
* @return string
|
25 |
+
*/
|
26 |
+
function analyst_assets_url($file)
|
27 |
+
{
|
28 |
+
$absolutePath = analyst_assets_path($file);
|
29 |
+
|
30 |
+
// We can always rely on WP_PLUGIN_DIR, because that's where
|
31 |
+
// wordpress install it's plugin's. So we remove last segment
|
32 |
+
// of that path to get the content dir AKA directly where
|
33 |
+
// plugins are installed and make the magic...
|
34 |
+
$contentDir = is_link(WP_PLUGIN_DIR) ?
|
35 |
+
dirname(wp_normalize_path(readlink(WP_PLUGIN_DIR))) :
|
36 |
+
dirname(wp_normalize_path(WP_PLUGIN_DIR));
|
37 |
+
|
38 |
+
$relativePath = str_replace( $contentDir, '', $absolutePath);
|
39 |
+
|
40 |
+
return content_url(wp_normalize_path($relativePath));
|
41 |
+
}
|
42 |
+
}
|
43 |
+
|
44 |
+
if (! function_exists('analyst_templates_path')) {
|
45 |
+
/**
|
46 |
+
* Generates path to file in templates folder
|
47 |
+
*
|
48 |
+
* @param $file
|
49 |
+
* @return string
|
50 |
+
*/
|
51 |
+
function analyst_templates_path($file)
|
52 |
+
{
|
53 |
+
$path = sprintf('%s/templates/%s', realpath(__DIR__ . '/..'), trim($file, '/'));
|
54 |
+
|
55 |
+
return wp_normalize_path($path);
|
56 |
+
}
|
57 |
+
}
|
58 |
+
|
59 |
+
if (! function_exists('analyst_require_template')) {
|
60 |
+
/**
|
61 |
+
* Require certain template with data
|
62 |
+
*
|
63 |
+
* @param $file
|
64 |
+
* @param array $data
|
65 |
+
*/
|
66 |
+
function analyst_require_template($file, $data = [])
|
67 |
+
{
|
68 |
+
// Extract data to current scope table
|
69 |
+
extract($data);
|
70 |
+
|
71 |
+
require analyst_templates_path($file);
|
72 |
+
}
|
73 |
+
}
|
74 |
+
|
75 |
+
if (! function_exists('dd')) {
|
76 |
+
/**
|
77 |
+
* Dump some data
|
78 |
+
*/
|
79 |
+
function dd ()
|
80 |
+
{
|
81 |
+
var_dump(func_get_args());
|
82 |
+
die();
|
83 |
+
}
|
84 |
+
}
|
analyst/templates/forms/deactivate.php
CHANGED
@@ -1,156 +1,156 @@
|
|
1 |
-
<div id="analyst-deactivate-modal" class="analyst-modal" style="display: none">
|
2 |
-
<div class="analyst-modal-content" style="width: 500px">
|
3 |
-
<div class="analyst-disable-modal-mask" id="analyst-disable-deactivate-modal-mask" style="display: none"></div>
|
4 |
-
<div style="display: flex">
|
5 |
-
<div class="analyst-install-image-block" style="width: 80px">
|
6 |
-
<img src="<?=$pencilImage?>"/>
|
7 |
-
</div>
|
8 |
-
<div class="analyst-install-description-block" style="padding-left: 20px">
|
9 |
-
<strong class="analyst-modal-header">Why do you deactivate?</strong>
|
10 |
-
<div class="analyst-install-description-text" style="padding-top: 2px">
|
11 |
-
Please let us know, so we can improve it! Thank you <img class="analyst-smile-image" src="<?=$smileImage?>" alt="">
|
12 |
-
</div>
|
13 |
-
</div>
|
14 |
-
</div>
|
15 |
-
<div>
|
16 |
-
<ul id="analyst-deactivation-reasons">
|
17 |
-
<li>
|
18 |
-
<label>
|
19 |
-
<span>
|
20 |
-
<input type="radio" name="deactivation-reason">
|
21 |
-
</span>
|
22 |
-
<span class="question" data-question="I couldn't understand how to make it work">I couldn't understand how to make it work</span>
|
23 |
-
</label>
|
24 |
-
</li>
|
25 |
-
<li data-input-type="textarea" data-input-placeholder="What should have worked, but didn’t?">
|
26 |
-
<label>
|
27 |
-
<span>
|
28 |
-
<input type="radio" name="deactivation-reason">
|
29 |
-
</span>
|
30 |
-
<span class="question" data-question="The plugin didn't work as expected">The plugin didn't work as expected</span>
|
31 |
-
</label>
|
32 |
-
<div class="question-answer"></div>
|
33 |
-
</li>
|
34 |
-
<li data-input-type="input" data-input-placeholder="What is the plugin name?">
|
35 |
-
<label>
|
36 |
-
<span>
|
37 |
-
<input type="radio" name="deactivation-reason">
|
38 |
-
</span>
|
39 |
-
<span class="question" data-question="I found a better plugin">I found a better plugin</span>
|
40 |
-
</label>
|
41 |
-
<div class="question-answer"></div>
|
42 |
-
</li>
|
43 |
-
<li>
|
44 |
-
<label>
|
45 |
-
<span>
|
46 |
-
<input type="radio" name="deactivation-reason">
|
47 |
-
</span>
|
48 |
-
<span class="question" data-question="It's a temporary deactivation">It's a temporary deactivation</span>
|
49 |
-
</label>
|
50 |
-
<div class="question-answer"></div>
|
51 |
-
</li>
|
52 |
-
<li data-input-type="textarea" data-input-placeholder="Please provide the reason of deactivation">
|
53 |
-
<label>
|
54 |
-
<span>
|
55 |
-
<input type="radio" name="deactivation-reason">
|
56 |
-
</span>
|
57 |
-
<span class="question" data-question="Other">Other</span>
|
58 |
-
</label>
|
59 |
-
<div class="question-answer"></div>
|
60 |
-
</li>
|
61 |
-
</ul>
|
62 |
-
<p id="analyst-deactivation-error" style="color: #dc3232; font-size: 16px; display: none">Please let us know the reason for de-activation. Thank you!</p>
|
63 |
-
</div>
|
64 |
-
<div>
|
65 |
-
<button class="analyst-btn-grey" id="analyst-disabled-plugin-action">Deactivate</button>
|
66 |
-
</div>
|
67 |
-
<div class="" style="text-align: center; font-size: 18px; padding-top: 10px">
|
68 |
-
<button class="analyst-btn-secondary-ghost analyst-deactivate-modal-close" style="color: #cccccc">Cancel</button>
|
69 |
-
</div>
|
70 |
-
</div>
|
71 |
-
</div>
|
72 |
-
|
73 |
-
<script type="text/javascript">
|
74 |
-
(function ($) {
|
75 |
-
$('.deactivate').click(function (e) {
|
76 |
-
var anchor = $(this).find('[analyst-plugin-id]')
|
77 |
-
var pluginId = anchor.attr('analyst-plugin-id')
|
78 |
-
var isOptedIn = anchor.attr('analyst-plugin-opted-in') === '1'
|
79 |
-
|
80 |
-
// Do not ask for reason if not opted in
|
81 |
-
if (!isOptedIn) {
|
82 |
-
return
|
83 |
-
}
|
84 |
-
|
85 |
-
e.preventDefault()
|
86 |
-
|
87 |
-
$('#analyst-deactivate-modal')
|
88 |
-
.attr({
|
89 |
-
'analyst-plugin-id': pluginId,
|
90 |
-
'analyst-redirect-url': $(this).find('a').attr('href')
|
91 |
-
})
|
92 |
-
.show()
|
93 |
-
})
|
94 |
-
|
95 |
-
$('.analyst-deactivate-modal-close').click(function () {
|
96 |
-
$('#analyst-deactivate-modal').hide()
|
97 |
-
})
|
98 |
-
|
99 |
-
$('#analyst-deactivation-reasons input[name="deactivation-reason"]').change(function () {
|
100 |
-
$('.question-answer').empty()
|
101 |
-
|
102 |
-
var root = $('#analyst-deactivation-reasons input[name="deactivation-reason"]:checked').parents('li')
|
103 |
-
|
104 |
-
$('#analyst-deactivation-error').hide()
|
105 |
-
|
106 |
-
if (!root.attr('data-input-type')) return
|
107 |
-
|
108 |
-
var reasonInput = $('<' + root.attr('data-input-type') + '/>').attr({placeholder: root.attr('data-input-placeholder'), class: 'reason-answer'})
|
109 |
-
|
110 |
-
root.find('.question-answer').append(reasonInput)
|
111 |
-
})
|
112 |
-
|
113 |
-
$('#analyst-disabled-plugin-action').click(function () {
|
114 |
-
var pluginId = $('#analyst-deactivate-modal').attr('analyst-plugin-id')
|
115 |
-
var pluginDeactivationUrl = $('#analyst-deactivate-modal').attr('analyst-redirect-url')
|
116 |
-
|
117 |
-
var root = $('#analyst-deactivation-reasons input[name="deactivation-reason"]:checked').parents('li');
|
118 |
-
|
119 |
-
var reason = root.find('.question-answer .reason-answer').val();
|
120 |
-
|
121 |
-
var question = root.find('.question').attr('data-question').trim()
|
122 |
-
|
123 |
-
var $errorBlock = $('#analyst-deactivation-error')
|
124 |
-
|
125 |
-
if (!question) {
|
126 |
-
return $errorBlock.show()
|
127 |
-
}
|
128 |
-
|
129 |
-
$errorBlock.hide()
|
130 |
-
|
131 |
-
var data = {
|
132 |
-
action: 'analyst_plugin_deactivate_' + pluginId,
|
133 |
-
question: question
|
134 |
-
}
|
135 |
-
|
136 |
-
if (reason) {
|
137 |
-
data['reason'] = reason.trim();
|
138 |
-
}
|
139 |
-
|
140 |
-
$(this).attr('disabled', true).text('Deactivating...');
|
141 |
-
|
142 |
-
$('#analyst-disable-deactivate-modal-mask').show();
|
143 |
-
|
144 |
-
$.ajax({
|
145 |
-
url: ajaxurl,
|
146 |
-
method: 'POST',
|
147 |
-
data: data
|
148 |
-
}).done(function () {
|
149 |
-
window.location.href = pluginDeactivationUrl
|
150 |
-
|
151 |
-
$('#analyst-disable-deactivate-modal-mask').hide();
|
152 |
-
})
|
153 |
-
})
|
154 |
-
|
155 |
-
})(jQuery)
|
156 |
-
</script>
|
1 |
+
<div id="analyst-deactivate-modal" class="analyst-modal" style="display: none">
|
2 |
+
<div class="analyst-modal-content" style="width: 500px">
|
3 |
+
<div class="analyst-disable-modal-mask" id="analyst-disable-deactivate-modal-mask" style="display: none"></div>
|
4 |
+
<div style="display: flex">
|
5 |
+
<div class="analyst-install-image-block" style="width: 80px">
|
6 |
+
<img src="<?=$pencilImage?>"/>
|
7 |
+
</div>
|
8 |
+
<div class="analyst-install-description-block" style="padding-left: 20px">
|
9 |
+
<strong class="analyst-modal-header">Why do you deactivate?</strong>
|
10 |
+
<div class="analyst-install-description-text" style="padding-top: 2px">
|
11 |
+
Please let us know, so we can improve it! Thank you <img class="analyst-smile-image" src="<?=$smileImage?>" alt="">
|
12 |
+
</div>
|
13 |
+
</div>
|
14 |
+
</div>
|
15 |
+
<div>
|
16 |
+
<ul id="analyst-deactivation-reasons">
|
17 |
+
<li>
|
18 |
+
<label>
|
19 |
+
<span>
|
20 |
+
<input type="radio" name="deactivation-reason">
|
21 |
+
</span>
|
22 |
+
<span class="question" data-question="I couldn't understand how to make it work">I couldn't understand how to make it work</span>
|
23 |
+
</label>
|
24 |
+
</li>
|
25 |
+
<li data-input-type="textarea" data-input-placeholder="What should have worked, but didn’t?">
|
26 |
+
<label>
|
27 |
+
<span>
|
28 |
+
<input type="radio" name="deactivation-reason">
|
29 |
+
</span>
|
30 |
+
<span class="question" data-question="The plugin didn't work as expected">The plugin didn't work as expected</span>
|
31 |
+
</label>
|
32 |
+
<div class="question-answer"></div>
|
33 |
+
</li>
|
34 |
+
<li data-input-type="input" data-input-placeholder="What is the plugin name?">
|
35 |
+
<label>
|
36 |
+
<span>
|
37 |
+
<input type="radio" name="deactivation-reason">
|
38 |
+
</span>
|
39 |
+
<span class="question" data-question="I found a better plugin">I found a better plugin</span>
|
40 |
+
</label>
|
41 |
+
<div class="question-answer"></div>
|
42 |
+
</li>
|
43 |
+
<li>
|
44 |
+
<label>
|
45 |
+
<span>
|
46 |
+
<input type="radio" name="deactivation-reason">
|
47 |
+
</span>
|
48 |
+
<span class="question" data-question="It's a temporary deactivation">It's a temporary deactivation</span>
|
49 |
+
</label>
|
50 |
+
<div class="question-answer"></div>
|
51 |
+
</li>
|
52 |
+
<li data-input-type="textarea" data-input-placeholder="Please provide the reason of deactivation">
|
53 |
+
<label>
|
54 |
+
<span>
|
55 |
+
<input type="radio" name="deactivation-reason">
|
56 |
+
</span>
|
57 |
+
<span class="question" data-question="Other">Other</span>
|
58 |
+
</label>
|
59 |
+
<div class="question-answer"></div>
|
60 |
+
</li>
|
61 |
+
</ul>
|
62 |
+
<p id="analyst-deactivation-error" style="color: #dc3232; font-size: 16px; display: none">Please let us know the reason for de-activation. Thank you!</p>
|
63 |
+
</div>
|
64 |
+
<div>
|
65 |
+
<button class="analyst-btn-grey" id="analyst-disabled-plugin-action">Deactivate</button>
|
66 |
+
</div>
|
67 |
+
<div class="" style="text-align: center; font-size: 18px; padding-top: 10px">
|
68 |
+
<button class="analyst-btn-secondary-ghost analyst-deactivate-modal-close" style="color: #cccccc">Cancel</button>
|
69 |
+
</div>
|
70 |
+
</div>
|
71 |
+
</div>
|
72 |
+
|
73 |
+
<script type="text/javascript">
|
74 |
+
(function ($) {
|
75 |
+
$('.deactivate').click(function (e) {
|
76 |
+
var anchor = $(this).find('[analyst-plugin-id]')
|
77 |
+
var pluginId = anchor.attr('analyst-plugin-id')
|
78 |
+
var isOptedIn = anchor.attr('analyst-plugin-opted-in') === '1'
|
79 |
+
|
80 |
+
// Do not ask for reason if not opted in
|
81 |
+
if (!isOptedIn) {
|
82 |
+
return
|
83 |
+
}
|
84 |
+
|
85 |
+
e.preventDefault()
|
86 |
+
|
87 |
+
$('#analyst-deactivate-modal')
|
88 |
+
.attr({
|
89 |
+
'analyst-plugin-id': pluginId,
|
90 |
+
'analyst-redirect-url': $(this).find('a').attr('href')
|
91 |
+
})
|
92 |
+
.show()
|
93 |
+
})
|
94 |
+
|
95 |
+
$('.analyst-deactivate-modal-close').click(function () {
|
96 |
+
$('#analyst-deactivate-modal').hide()
|
97 |
+
})
|
98 |
+
|
99 |
+
$('#analyst-deactivation-reasons input[name="deactivation-reason"]').change(function () {
|
100 |
+
$('.question-answer').empty()
|
101 |
+
|
102 |
+
var root = $('#analyst-deactivation-reasons input[name="deactivation-reason"]:checked').parents('li')
|
103 |
+
|
104 |
+
$('#analyst-deactivation-error').hide()
|
105 |
+
|
106 |
+
if (!root.attr('data-input-type')) return
|
107 |
+
|
108 |
+
var reasonInput = $('<' + root.attr('data-input-type') + '/>').attr({placeholder: root.attr('data-input-placeholder'), class: 'reason-answer'})
|
109 |
+
|
110 |
+
root.find('.question-answer').append(reasonInput)
|
111 |
+
})
|
112 |
+
|
113 |
+
$('#analyst-disabled-plugin-action').click(function () {
|
114 |
+
var pluginId = $('#analyst-deactivate-modal').attr('analyst-plugin-id')
|
115 |
+
var pluginDeactivationUrl = $('#analyst-deactivate-modal').attr('analyst-redirect-url')
|
116 |
+
|
117 |
+
var root = $('#analyst-deactivation-reasons input[name="deactivation-reason"]:checked').parents('li');
|
118 |
+
|
119 |
+
var reason = root.find('.question-answer .reason-answer').val();
|
120 |
+
|
121 |
+
var question = root.find('.question').attr('data-question').trim()
|
122 |
+
|
123 |
+
var $errorBlock = $('#analyst-deactivation-error')
|
124 |
+
|
125 |
+
if (!question) {
|
126 |
+
return $errorBlock.show()
|
127 |
+
}
|
128 |
+
|
129 |
+
$errorBlock.hide()
|
130 |
+
|
131 |
+
var data = {
|
132 |
+
action: 'analyst_plugin_deactivate_' + pluginId,
|
133 |
+
question: question
|
134 |
+
}
|
135 |
+
|
136 |
+
if (reason) {
|
137 |
+
data['reason'] = reason.trim();
|
138 |
+
}
|
139 |
+
|
140 |
+
$(this).attr('disabled', true).text('Deactivating...');
|
141 |
+
|
142 |
+
$('#analyst-disable-deactivate-modal-mask').show();
|
143 |
+
|
144 |
+
$.ajax({
|
145 |
+
url: ajaxurl,
|
146 |
+
method: 'POST',
|
147 |
+
data: data
|
148 |
+
}).done(function () {
|
149 |
+
window.location.href = pluginDeactivationUrl
|
150 |
+
|
151 |
+
$('#analyst-disable-deactivate-modal-mask').hide();
|
152 |
+
})
|
153 |
+
})
|
154 |
+
|
155 |
+
})(jQuery)
|
156 |
+
</script>
|
analyst/templates/forms/install.php
CHANGED
@@ -1,113 +1,113 @@
|
|
1 |
-
<div id="analyst-install-modal" class="analyst-modal" style="display: none" analyst-plugin-id="<?=$pluginToInstall?>">
|
2 |
-
<div class="analyst-modal-content" style="width: 450px">
|
3 |
-
<div class="analyst-disable-modal-mask" id="analyst-disable-install-modal-mask" style="display: none"></div>
|
4 |
-
<div style="display: flex">
|
5 |
-
<div class="analyst-install-image-block">
|
6 |
-
<img src="<?=$shieldImage?>"/>
|
7 |
-
</div>
|
8 |
-
<div class="analyst-install-description-block">
|
9 |
-
<strong class="analyst-modal-header">Stay on the safe side</strong>
|
10 |
-
<p class="analyst-install-description-text">Receive our plugin’s alerts in
|
11 |
-
case of <strong>critical security</strong> & feature
|
12 |
-
updates and allow non-sensitive
|
13 |
-
diagnostic tracking.</p>
|
14 |
-
</div>
|
15 |
-
</div>
|
16 |
-
<div class="analyst-modal-def-top-padding">
|
17 |
-
<button class="analyst-btn-success" id="analyst-install-action">Allow & Continue ></button>
|
18 |
-
</div>
|
19 |
-
<div class="analyst-modal-def-top-padding" id="analyst-permissions-block" style="display: none">
|
20 |
-
<span>You’re granting these permissions:</span>
|
21 |
-
<ul class="analyst-install-permissions-list">
|
22 |
-
<li><strong>Your profile information</strong> (name and email) </li>
|
23 |
-
<li><strong>Your site information</strong> (URL, WP version, PHP info, plugins & themes)</li>
|
24 |
-
<li><strong>Plugin notices</strong> (updates, announcements, marketing, no spam)</li>
|
25 |
-
<li><strong>Plugin events</strong> (activation, deactivation and uninstall)</li>
|
26 |
-
</ul>
|
27 |
-
</div>
|
28 |
-
<div class="analyst-install-footer analyst-modal-def-top-padding">
|
29 |
-
<span class="analyst-action-text" id="analyst-permissions-toggle">Learn more</span>
|
30 |
-
<span id="analyst-powered-by" style="display: none;">Powered by <a href="https://sellcodes.com/blog/wordpress-feedback-system-for-plugin-creators/?utm_source=optin_screen" target="_blank" class="analyst-link">Sellcodes.com</a></span>
|
31 |
-
<span class="analyst-action-text analyst-install-modal-close" id="analyst-install-skip">Skip</span>
|
32 |
-
</div>
|
33 |
-
<div id="analyst-install-error" class="analyst-modal-def-top-padding" style="display: none; text-align: center">
|
34 |
-
<span style="color: #dc3232; font-size: 16px">Service unavailable. Please try again later</span>
|
35 |
-
</div>
|
36 |
-
</div>
|
37 |
-
</div>
|
38 |
-
|
39 |
-
<script type="text/javascript">
|
40 |
-
(function ($) {
|
41 |
-
|
42 |
-
var installPlugin = function (pluginId) {
|
43 |
-
var $error = $('#analyst-install-error')
|
44 |
-
|
45 |
-
$error.hide()
|
46 |
-
|
47 |
-
$.ajax({
|
48 |
-
url: ajaxurl,
|
49 |
-
method: 'POST',
|
50 |
-
data: {
|
51 |
-
action: 'analyst_install_' + pluginId
|
52 |
-
},
|
53 |
-
success: function (data) {
|
54 |
-
if (data && !data.success) {
|
55 |
-
//error
|
56 |
-
$('#analyst-install-modal').hide()
|
57 |
-
|
58 |
-
return
|
59 |
-
}
|
60 |
-
|
61 |
-
window.location.reload()
|
62 |
-
},
|
63 |
-
error: function () {
|
64 |
-
$('#analyst-install-modal').hide()
|
65 |
-
}
|
66 |
-
}).done(function () {
|
67 |
-
$('#analyst-disable-install-modal-mask').hide()
|
68 |
-
|
69 |
-
$('#analyst-install-action')
|
70 |
-
.attr('disabled', false)
|
71 |
-
.text('Allow & Continue >')
|
72 |
-
})
|
73 |
-
}
|
74 |
-
|
75 |
-
if ($('#analyst-install-modal').attr('analyst-plugin-id')) {
|
76 |
-
$('#analyst-install-modal').show()
|
77 |
-
}
|
78 |
-
|
79 |
-
|
80 |
-
$('.analyst-install-modal-close').click(function () {
|
81 |
-
$('#analyst-install-modal').hide()
|
82 |
-
})
|
83 |
-
|
84 |
-
$('#analyst-install-action').click(function () {
|
85 |
-
var pluginId = $('#analyst-install-modal').attr('analyst-plugin-id')
|
86 |
-
|
87 |
-
$('#analyst-install-action')
|
88 |
-
.attr('disabled', true)
|
89 |
-
.text('Please wait...')
|
90 |
-
|
91 |
-
$('#analyst-disable-install-modal-mask').show()
|
92 |
-
|
93 |
-
installPlugin(pluginId)
|
94 |
-
})
|
95 |
-
|
96 |
-
$('#analyst-permissions-toggle').click(function () {
|
97 |
-
var isVisible = $('#analyst-permissions-block').toggle().is(':visible')
|
98 |
-
|
99 |
-
isVisible ? $(this).text('Close section') : $(this).text('Learn more')
|
100 |
-
|
101 |
-
var poweredBy = $('#analyst-powered-by')
|
102 |
-
isVisible ? poweredBy.show() : poweredBy.hide()
|
103 |
-
})
|
104 |
-
|
105 |
-
$('#analyst-install-skip').click(function () {
|
106 |
-
var pluginId = $('#analyst-install-modal').attr('analyst-plugin-id')
|
107 |
-
|
108 |
-
$.post(ajaxurl, {action: 'analyst_skip_install_' + pluginId}).done(function () {
|
109 |
-
$('#analyst-install-modal').hide()
|
110 |
-
})
|
111 |
-
})
|
112 |
-
})(jQuery)
|
113 |
-
</script>
|
1 |
+
<div id="analyst-install-modal" class="analyst-modal" style="display: none" analyst-plugin-id="<?=$pluginToInstall?>">
|
2 |
+
<div class="analyst-modal-content" style="width: 450px">
|
3 |
+
<div class="analyst-disable-modal-mask" id="analyst-disable-install-modal-mask" style="display: none"></div>
|
4 |
+
<div style="display: flex">
|
5 |
+
<div class="analyst-install-image-block">
|
6 |
+
<img src="<?=$shieldImage?>"/>
|
7 |
+
</div>
|
8 |
+
<div class="analyst-install-description-block">
|
9 |
+
<strong class="analyst-modal-header">Stay on the safe side</strong>
|
10 |
+
<p class="analyst-install-description-text">Receive our plugin’s alerts in
|
11 |
+
case of <strong>critical security</strong> & feature
|
12 |
+
updates and allow non-sensitive
|
13 |
+
diagnostic tracking.</p>
|
14 |
+
</div>
|
15 |
+
</div>
|
16 |
+
<div class="analyst-modal-def-top-padding">
|
17 |
+
<button class="analyst-btn-success" id="analyst-install-action">Allow & Continue ></button>
|
18 |
+
</div>
|
19 |
+
<div class="analyst-modal-def-top-padding" id="analyst-permissions-block" style="display: none">
|
20 |
+
<span>You’re granting these permissions:</span>
|
21 |
+
<ul class="analyst-install-permissions-list">
|
22 |
+
<li><strong>Your profile information</strong> (name and email) </li>
|
23 |
+
<li><strong>Your site information</strong> (URL, WP version, PHP info, plugins & themes)</li>
|
24 |
+
<li><strong>Plugin notices</strong> (updates, announcements, marketing, no spam)</li>
|
25 |
+
<li><strong>Plugin events</strong> (activation, deactivation and uninstall)</li>
|
26 |
+
</ul>
|
27 |
+
</div>
|
28 |
+
<div class="analyst-install-footer analyst-modal-def-top-padding">
|
29 |
+
<span class="analyst-action-text" id="analyst-permissions-toggle">Learn more</span>
|
30 |
+
<span id="analyst-powered-by" style="display: none;">Powered by <a href="https://sellcodes.com/blog/wordpress-feedback-system-for-plugin-creators/?utm_source=optin_screen" target="_blank" class="analyst-link">Sellcodes.com</a></span>
|
31 |
+
<span class="analyst-action-text analyst-install-modal-close" id="analyst-install-skip">Skip</span>
|
32 |
+
</div>
|
33 |
+
<div id="analyst-install-error" class="analyst-modal-def-top-padding" style="display: none; text-align: center">
|
34 |
+
<span style="color: #dc3232; font-size: 16px">Service unavailable. Please try again later</span>
|
35 |
+
</div>
|
36 |
+
</div>
|
37 |
+
</div>
|
38 |
+
|
39 |
+
<script type="text/javascript">
|
40 |
+
(function ($) {
|
41 |
+
|
42 |
+
var installPlugin = function (pluginId) {
|
43 |
+
var $error = $('#analyst-install-error')
|
44 |
+
|
45 |
+
$error.hide()
|
46 |
+
|
47 |
+
$.ajax({
|
48 |
+
url: ajaxurl,
|
49 |
+
method: 'POST',
|
50 |
+
data: {
|
51 |
+
action: 'analyst_install_' + pluginId
|
52 |
+
},
|
53 |
+
success: function (data) {
|
54 |
+
if (data && !data.success) {
|
55 |
+
//error
|
56 |
+
$('#analyst-install-modal').hide()
|
57 |
+
|
58 |
+
return
|
59 |
+
}
|
60 |
+
|
61 |
+
window.location.reload()
|
62 |
+
},
|
63 |
+
error: function () {
|
64 |
+
$('#analyst-install-modal').hide()
|
65 |
+
}
|
66 |
+
}).done(function () {
|
67 |
+
$('#analyst-disable-install-modal-mask').hide()
|
68 |
+
|
69 |
+
$('#analyst-install-action')
|
70 |
+
.attr('disabled', false)
|
71 |
+
.text('Allow & Continue >')
|
72 |
+
})
|
73 |
+
}
|
74 |
+
|
75 |
+
if ($('#analyst-install-modal').attr('analyst-plugin-id')) {
|
76 |
+
$('#analyst-install-modal').show()
|
77 |
+
}
|
78 |
+
|
79 |
+
|
80 |
+
$('.analyst-install-modal-close').click(function () {
|
81 |
+
$('#analyst-install-modal').hide()
|
82 |
+
})
|
83 |
+
|
84 |
+
$('#analyst-install-action').click(function () {
|
85 |
+
var pluginId = $('#analyst-install-modal').attr('analyst-plugin-id')
|
86 |
+
|
87 |
+
$('#analyst-install-action')
|
88 |
+
.attr('disabled', true)
|
89 |
+
.text('Please wait...')
|
90 |
+
|
91 |
+
$('#analyst-disable-install-modal-mask').show()
|
92 |
+
|
93 |
+
installPlugin(pluginId)
|
94 |
+
})
|
95 |
+
|
96 |
+
$('#analyst-permissions-toggle').click(function () {
|
97 |
+
var isVisible = $('#analyst-permissions-block').toggle().is(':visible')
|
98 |
+
|
99 |
+
isVisible ? $(this).text('Close section') : $(this).text('Learn more')
|
100 |
+
|
101 |
+
var poweredBy = $('#analyst-powered-by')
|
102 |
+
isVisible ? poweredBy.show() : poweredBy.hide()
|
103 |
+
})
|
104 |
+
|
105 |
+
$('#analyst-install-skip').click(function () {
|
106 |
+
var pluginId = $('#analyst-install-modal').attr('analyst-plugin-id')
|
107 |
+
|
108 |
+
$.post(ajaxurl, {action: 'analyst_skip_install_' + pluginId}).done(function () {
|
109 |
+
$('#analyst-install-modal').hide()
|
110 |
+
})
|
111 |
+
})
|
112 |
+
})(jQuery)
|
113 |
+
</script>
|
analyst/templates/notice.php
CHANGED
@@ -1,10 +1,10 @@
|
|
1 |
-
<div class="notice notice-success analyst-notice">
|
2 |
-
<p>
|
3 |
-
<strong class="analyst-plugin-name"><?=$notice->getPluginName()?></strong>
|
4 |
-
<?=$notice->getBody()?>
|
5 |
-
</p>
|
6 |
-
|
7 |
-
<button type="button" class="analyst-notice-dismiss notice-dismiss" analyst-notice-id="<?=$notice->getId()?>">
|
8 |
-
<span class="screen-reader-text">Dismiss this notice.</span>
|
9 |
-
</button>
|
10 |
-
</div>
|
1 |
+
<div class="notice notice-success analyst-notice">
|
2 |
+
<p>
|
3 |
+
<strong class="analyst-plugin-name"><?=$notice->getPluginName()?></strong>
|
4 |
+
<?=$notice->getBody()?>
|
5 |
+
</p>
|
6 |
+
|
7 |
+
<button type="button" class="analyst-notice-dismiss notice-dismiss" analyst-notice-id="<?=$notice->getId()?>">
|
8 |
+
<span class="screen-reader-text">Dismiss this notice.</span>
|
9 |
+
</button>
|
10 |
+
</div>
|
analyst/templates/optin.php
CHANGED
@@ -1,60 +1,60 @@
|
|
1 |
-
<script type="text/javascript">
|
2 |
-
|
3 |
-
(function ($) {
|
4 |
-
var isOptingIn = false
|
5 |
-
|
6 |
-
$('#analyst-opt-in-modal').appendTo($('body'))
|
7 |
-
|
8 |
-
var makeOptIn = function (pluginId) {
|
9 |
-
if (isOptingIn) return
|
10 |
-
|
11 |
-
isOptingIn = true
|
12 |
-
|
13 |
-
$.ajax({
|
14 |
-
url: ajaxurl,
|
15 |
-
method: 'POST',
|
16 |
-
data: {
|
17 |
-
action: 'analyst_opt_in_' + pluginId,
|
18 |
-
},
|
19 |
-
success: function () {
|
20 |
-
$('#analyst-opt-in-modal').hide()
|
21 |
-
|
22 |
-
isOptingIn = false
|
23 |
-
|
24 |
-
var optOutAction = $('<a />').attr({
|
25 |
-
class: 'analyst-action-opt analyst-opt-out',
|
26 |
-
'analyst-plugin-id': pluginId,
|
27 |
-
'analyst-plugin-signed': '1'
|
28 |
-
})
|
29 |
-
.text('Opt Out')
|
30 |
-
$('.analyst-opt-in[analyst-plugin-id="'+ pluginId +'"').replaceWith(optOutAction)
|
31 |
-
|
32 |
-
$('[analyst-plugin-id="' + pluginId + '"').attr('analyst-plugin-opted-in', 1)
|
33 |
-
}
|
34 |
-
})
|
35 |
-
}
|
36 |
-
|
37 |
-
$(document).on('click', '.analyst-opt-in:not([loading])', function() {
|
38 |
-
var pluginId = $(this).attr('analyst-plugin-id')
|
39 |
-
var isSigned = $(this).attr('analyst-plugin-signed') === '1'
|
40 |
-
|
41 |
-
if (!isSigned) {
|
42 |
-
$('#analyst-install-modal')
|
43 |
-
.attr('analyst-plugin-id', pluginId)
|
44 |
-
.show()
|
45 |
-
|
46 |
-
return;
|
47 |
-
}
|
48 |
-
|
49 |
-
$('#analyst-install-modal').attr({'analyst-plugin-id': pluginId})
|
50 |
-
|
51 |
-
$(this).attr('loading', true).text('Opting In...')
|
52 |
-
|
53 |
-
makeOptIn(pluginId);
|
54 |
-
})
|
55 |
-
|
56 |
-
$('.opt-in-modal-close').click(function () {
|
57 |
-
$('#analyst-opt-in-modal').hide()
|
58 |
-
})
|
59 |
-
})(jQuery)
|
60 |
-
</script>
|
1 |
+
<script type="text/javascript">
|
2 |
+
|
3 |
+
(function ($) {
|
4 |
+
var isOptingIn = false
|
5 |
+
|
6 |
+
$('#analyst-opt-in-modal').appendTo($('body'))
|
7 |
+
|
8 |
+
var makeOptIn = function (pluginId) {
|
9 |
+
if (isOptingIn) return
|
10 |
+
|
11 |
+
isOptingIn = true
|
12 |
+
|
13 |
+
$.ajax({
|
14 |
+
url: ajaxurl,
|
15 |
+
method: 'POST',
|
16 |
+
data: {
|
17 |
+
action: 'analyst_opt_in_' + pluginId,
|
18 |
+
},
|
19 |
+
success: function () {
|
20 |
+
$('#analyst-opt-in-modal').hide()
|
21 |
+
|
22 |
+
isOptingIn = false
|
23 |
+
|
24 |
+
var optOutAction = $('<a />').attr({
|
25 |
+
class: 'analyst-action-opt analyst-opt-out',
|
26 |
+
'analyst-plugin-id': pluginId,
|
27 |
+
'analyst-plugin-signed': '1'
|
28 |
+
})
|
29 |
+
.text('Opt Out')
|
30 |
+
$('.analyst-opt-in[analyst-plugin-id="'+ pluginId +'"').replaceWith(optOutAction)
|
31 |
+
|
32 |
+
$('[analyst-plugin-id="' + pluginId + '"').attr('analyst-plugin-opted-in', 1)
|
33 |
+
}
|
34 |
+
})
|
35 |
+
}
|
36 |
+
|
37 |
+
$(document).on('click', '.analyst-opt-in:not([loading])', function() {
|
38 |
+
var pluginId = $(this).attr('analyst-plugin-id')
|
39 |
+
var isSigned = $(this).attr('analyst-plugin-signed') === '1'
|
40 |
+
|
41 |
+
if (!isSigned) {
|
42 |
+
$('#analyst-install-modal')
|
43 |
+
.attr('analyst-plugin-id', pluginId)
|
44 |
+
.show()
|
45 |
+
|
46 |
+
return;
|
47 |
+
}
|
48 |
+
|
49 |
+
$('#analyst-install-modal').attr({'analyst-plugin-id': pluginId})
|
50 |
+
|
51 |
+
$(this).attr('loading', true).text('Opting In...')
|
52 |
+
|
53 |
+
makeOptIn(pluginId);
|
54 |
+
})
|
55 |
+
|
56 |
+
$('.opt-in-modal-close').click(function () {
|
57 |
+
$('#analyst-opt-in-modal').hide()
|
58 |
+
})
|
59 |
+
})(jQuery)
|
60 |
+
</script>
|
analyst/templates/optout.php
CHANGED
@@ -1,109 +1,109 @@
|
|
1 |
-
<div id="analyst-opt-out-modal" class="analyst-modal" style="display: none">
|
2 |
-
<div class="analyst-modal-content" style="width: 600px">
|
3 |
-
<div class="analyst-disable-modal-mask" id="analyst-disable-opt-out-modal-mask" style="display: none"></div>
|
4 |
-
<div style="display: flex">
|
5 |
-
<div class="analyst-install-image-block" style="width: 120px">
|
6 |
-
<img src="<?=$shieldImage?>"/>
|
7 |
-
</div>
|
8 |
-
<div class="analyst-install-description-block">
|
9 |
-
<strong class="analyst-modal-header">By opting out, we cannot alert you anymore in case of important security updates.</strong>
|
10 |
-
<p class="analyst-install-description-text">
|
11 |
-
In addition, we won’t get pointers how to further improve the plugin based on your integration with our plugin.
|
12 |
-
</p>
|
13 |
-
</div>
|
14 |
-
</div>
|
15 |
-
<div class="analyst-modal-def-top-padding">
|
16 |
-
<button class="analyst-btn-success opt-out-modal-close">Ok, don't opt out</button>
|
17 |
-
</div>
|
18 |
-
<div class="analyst-modal-def-top-padding" style="text-align: center;">
|
19 |
-
<button class="analyst-btn-secondary-ghost" id="opt-out-action">Opt out</button>
|
20 |
-
</div>
|
21 |
-
<div id="analyst-opt-out-error" class="analyst-modal-def-top-padding" style="display: none;">
|
22 |
-
<span style="color: #dc3232; font-size: 16px">Service unavailable. Please try again later</span>
|
23 |
-
</div>
|
24 |
-
</div>
|
25 |
-
</div>
|
26 |
-
</div>
|
27 |
-
|
28 |
-
<script type="text/javascript">
|
29 |
-
|
30 |
-
(function ($) {
|
31 |
-
var isOptingOut = false
|
32 |
-
|
33 |
-
$('#analyst-opt-out-modal').appendTo($('body'))
|
34 |
-
|
35 |
-
$(document).on('click', '.analyst-opt-out', function() {
|
36 |
-
var pluginId = $(this).attr('analyst-plugin-id')
|
37 |
-
|
38 |
-
$('#analyst-opt-out-modal')
|
39 |
-
.attr({'analyst-plugin-id': pluginId})
|
40 |
-
.show()
|
41 |
-
})
|
42 |
-
|
43 |
-
$('.opt-out-modal-close').click(function () {
|
44 |
-
$('#analyst-opt-out-modal').hide()
|
45 |
-
})
|
46 |
-
|
47 |
-
$('#opt-out-action').click(function () {
|
48 |
-
if (isOptingOut) return
|
49 |
-
|
50 |
-
var $mask = $('#analyst-disable-opt-out-modal-mask')
|
51 |
-
var $error = $('#analyst-opt-out-error')
|
52 |
-
|
53 |
-
var pluginId = $('#analyst-opt-out-modal').attr('analyst-plugin-id')
|
54 |
-
|
55 |
-
$mask.show()
|
56 |
-
$error.hide()
|
57 |
-
|
58 |
-
var self = this
|
59 |
-
|
60 |
-
isOptingOut = true
|
61 |
-
|
62 |
-
$(self).text('Opting out...')
|
63 |
-
|
64 |
-
$.ajax({
|
65 |
-
url: ajaxurl,
|
66 |
-
method: 'POST',
|
67 |
-
data: {
|
68 |
-
action: 'analyst_opt_out_' + pluginId,
|
69 |
-
},
|
70 |
-
success: function (data) {
|
71 |
-
$(self).text('Opt out')
|
72 |
-
|
73 |
-
if (data && !data.success) {
|
74 |
-
$('#analyst-opt-out-modal').hide()
|
75 |
-
|
76 |
-
return
|
77 |
-
}
|
78 |
-
|
79 |
-
$error.hide()
|
80 |
-
|
81 |
-
$('#analyst-opt-out-modal').hide()
|
82 |
-
|
83 |
-
isOptingOut = false
|
84 |
-
|
85 |
-
var optInAction = $('<a />').attr({
|
86 |
-
class: 'analyst-action-opt analyst-opt-in',
|
87 |
-
'analyst-plugin-id': pluginId,
|
88 |
-
'analyst-plugin-signed': '1'
|
89 |
-
})
|
90 |
-
.text('Opt In')
|
91 |
-
$('.analyst-opt-out[analyst-plugin-id="'+ pluginId +'"').replaceWith(optInAction)
|
92 |
-
|
93 |
-
$('[analyst-plugin-id="' + pluginId + '"').attr('analyst-plugin-opted-in', 0)
|
94 |
-
|
95 |
-
$mask.hide()
|
96 |
-
},
|
97 |
-
error: function () {
|
98 |
-
$('#analyst-opt-out-error').show()
|
99 |
-
|
100 |
-
$(self).text('Opt out')
|
101 |
-
}
|
102 |
-
}).done(function () {
|
103 |
-
$mask.hide()
|
104 |
-
|
105 |
-
isOptingOut = false
|
106 |
-
})
|
107 |
-
})
|
108 |
-
})(jQuery)
|
109 |
-
</script>
|
1 |
+
<div id="analyst-opt-out-modal" class="analyst-modal" style="display: none">
|
2 |
+
<div class="analyst-modal-content" style="width: 600px">
|
3 |
+
<div class="analyst-disable-modal-mask" id="analyst-disable-opt-out-modal-mask" style="display: none"></div>
|
4 |
+
<div style="display: flex">
|
5 |
+
<div class="analyst-install-image-block" style="width: 120px">
|
6 |
+
<img src="<?=$shieldImage?>"/>
|
7 |
+
</div>
|
8 |
+
<div class="analyst-install-description-block">
|
9 |
+
<strong class="analyst-modal-header">By opting out, we cannot alert you anymore in case of important security updates.</strong>
|
10 |
+
<p class="analyst-install-description-text">
|
11 |
+
In addition, we won’t get pointers how to further improve the plugin based on your integration with our plugin.
|
12 |
+
</p>
|
13 |
+
</div>
|
14 |
+
</div>
|
15 |
+
<div class="analyst-modal-def-top-padding">
|
16 |
+
<button class="analyst-btn-success opt-out-modal-close">Ok, don't opt out</button>
|
17 |
+
</div>
|
18 |
+
<div class="analyst-modal-def-top-padding" style="text-align: center;">
|
19 |
+
<button class="analyst-btn-secondary-ghost" id="opt-out-action">Opt out</button>
|
20 |
+
</div>
|
21 |
+
<div id="analyst-opt-out-error" class="analyst-modal-def-top-padding" style="display: none;">
|
22 |
+
<span style="color: #dc3232; font-size: 16px">Service unavailable. Please try again later</span>
|
23 |
+
</div>
|
24 |
+
</div>
|
25 |
+
</div>
|
26 |
+
</div>
|
27 |
+
|
28 |
+
<script type="text/javascript">
|
29 |
+
|
30 |
+
(function ($) {
|
31 |
+
var isOptingOut = false
|
32 |
+
|
33 |
+
$('#analyst-opt-out-modal').appendTo($('body'))
|
34 |
+
|
35 |
+
$(document).on('click', '.analyst-opt-out', function() {
|
36 |
+
var pluginId = $(this).attr('analyst-plugin-id')
|
37 |
+
|
38 |
+
$('#analyst-opt-out-modal')
|
39 |
+
.attr({'analyst-plugin-id': pluginId})
|
40 |
+
.show()
|
41 |
+
})
|
42 |
+
|
43 |
+
$('.opt-out-modal-close').click(function () {
|
44 |
+
$('#analyst-opt-out-modal').hide()
|
45 |
+
})
|
46 |
+
|
47 |
+
$('#opt-out-action').click(function () {
|
48 |
+
if (isOptingOut) return
|
49 |
+
|
50 |
+
var $mask = $('#analyst-disable-opt-out-modal-mask')
|
51 |
+
var $error = $('#analyst-opt-out-error')
|
52 |
+
|
53 |
+
var pluginId = $('#analyst-opt-out-modal').attr('analyst-plugin-id')
|
54 |
+
|
55 |
+
$mask.show()
|
56 |
+
$error.hide()
|
57 |
+
|
58 |
+
var self = this
|
59 |
+
|
60 |
+
isOptingOut = true
|
61 |
+
|
62 |
+
$(self).text('Opting out...')
|
63 |
+
|
64 |
+
$.ajax({
|
65 |
+
url: ajaxurl,
|
66 |
+
method: 'POST',
|
67 |
+
data: {
|
68 |
+
action: 'analyst_opt_out_' + pluginId,
|
69 |
+
},
|
70 |
+
success: function (data) {
|
71 |
+
$(self).text('Opt out')
|
72 |
+
|
73 |
+
if (data && !data.success) {
|
74 |
+
$('#analyst-opt-out-modal').hide()
|
75 |
+
|
76 |
+
return
|
77 |
+
}
|
78 |
+
|
79 |
+
$error.hide()
|
80 |
+
|
81 |
+
$('#analyst-opt-out-modal').hide()
|
82 |
+
|
83 |
+
isOptingOut = false
|
84 |
+
|
85 |
+
var optInAction = $('<a />').attr({
|
86 |
+
class: 'analyst-action-opt analyst-opt-in',
|
87 |
+
'analyst-plugin-id': pluginId,
|
88 |
+
'analyst-plugin-signed': '1'
|
89 |
+
})
|
90 |
+
.text('Opt In')
|
91 |
+
$('.analyst-opt-out[analyst-plugin-id="'+ pluginId +'"').replaceWith(optInAction)
|
92 |
+
|
93 |
+
$('[analyst-plugin-id="' + pluginId + '"').attr('analyst-plugin-opted-in', 0)
|
94 |
+
|
95 |
+
$mask.hide()
|
96 |
+
},
|
97 |
+
error: function () {
|
98 |
+
$('#analyst-opt-out-error').show()
|
99 |
+
|
100 |
+
$(self).text('Opt out')
|
101 |
+
}
|
102 |
+
}).done(function () {
|
103 |
+
$mask.hide()
|
104 |
+
|
105 |
+
isOptingOut = false
|
106 |
+
})
|
107 |
+
})
|
108 |
+
})(jQuery)
|
109 |
+
</script>
|
analyst/version.php
CHANGED
@@ -1,15 +1,15 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
return array(
|
4 |
-
// The sdk version
|
5 |
-
'sdk' => '1.3.30',
|
6 |
-
|
7 |
-
// Minimum supported WordPress version
|
8 |
-
'wp' => '4.7',
|
9 |
-
|
10 |
-
// Supported PHP version
|
11 |
-
'php' => '5.4',
|
12 |
-
|
13 |
-
// Path to current SDK$
|
14 |
-
'path' => __DIR__,
|
15 |
-
);
|
1 |
+
<?php
|
2 |
+
|
3 |
+
return array(
|
4 |
+
// The sdk version
|
5 |
+
'sdk' => '1.3.30',
|
6 |
+
|
7 |
+
// Minimum supported WordPress version
|
8 |
+
'wp' => '4.7',
|
9 |
+
|
10 |
+
// Supported PHP version
|
11 |
+
'php' => '5.4',
|
12 |
+
|
13 |
+
// Path to current SDK$
|
14 |
+
'path' => __DIR__,
|
15 |
+
);
|
lib/class.wpc-wpdb.php
CHANGED
@@ -1,145 +1,145 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* @author Andy Camm
|
4 |
-
*/
|
5 |
-
|
6 |
-
// Subclass wpdb to ensure compatibility with WordPress and to use
|
7 |
-
// the appropriate MySQL module (uses MySQLi on PHP >= 5.5)
|
8 |
-
// and provide access to the additional raw MySQL/MySQLi module calls
|
9 |
-
// that we are using
|
10 |
-
class wpc_wpdb extends wpdb
|
11 |
-
{
|
12 |
-
|
13 |
-
// This is copied from the base class as its use_mysqli member is private
|
14 |
-
protected $use_mysqli = false;
|
15 |
-
|
16 |
-
public function __construct( $dbuser, $dbpassword, $dbname, $dbhost ) {
|
17 |
-
parent::__construct($dbuser, $dbpassword, $dbname, $dbhost);
|
18 |
-
|
19 |
-
// This is copied from the base class as its use_mysqli member is private
|
20 |
-
// Use ext/mysqli if it exists and:
|
21 |
-
// - WP_USE_EXT_MYSQL is defined as false, or
|
22 |
-
// - We are a development version of WordPress, or
|
23 |
-
// - We are running PHP 5.5 or greater, or
|
24 |
-
// - ext/mysql is not loaded.
|
25 |
-
//
|
26 |
-
if ( function_exists( 'mysqli_connect' ) ) {
|
27 |
-
if ( defined( 'WP_USE_EXT_MYSQL' ) ) {
|
28 |
-
$this->use_mysqli = ! WP_USE_EXT_MYSQL;
|
29 |
-
} elseif ( version_compare( phpversion(), '5.5', '>=' ) || ! function_exists( 'mysql_connect' ) ) {
|
30 |
-
$this->use_mysqli = true;
|
31 |
-
} elseif ( false !== strpos( $GLOBALS['wp_version'], '-' ) ) {
|
32 |
-
$this->use_mysqli = true;
|
33 |
-
}
|
34 |
-
}
|
35 |
-
}
|
36 |
-
|
37 |
-
public function get_dbh()
|
38 |
-
{
|
39 |
-
return $this->dbh;
|
40 |
-
}
|
41 |
-
|
42 |
-
public function query($querystring)
|
43 |
-
{
|
44 |
-
if ($this->use_mysqli)
|
45 |
-
{
|
46 |
-
return $this->get_dbh()->query($querystring);
|
47 |
-
}
|
48 |
-
else
|
49 |
-
{
|
50 |
-
return mysql_query($querystring, $this->get_dbh());
|
51 |
-
}
|
52 |
-
}
|
53 |
-
|
54 |
-
public function ping()
|
55 |
-
{
|
56 |
-
if ($this->use_mysqli)
|
57 |
-
{
|
58 |
-
return $this->get_dbh()->ping();
|
59 |
-
}
|
60 |
-
else
|
61 |
-
{
|
62 |
-
return mysql_ping($this->get_dbh());
|
63 |
-
}
|
64 |
-
}
|
65 |
-
|
66 |
-
|
67 |
-
public function close()
|
68 |
-
{
|
69 |
-
if ($this->use_mysqli)
|
70 |
-
{
|
71 |
-
$this->get_dbh()->close();
|
72 |
-
}
|
73 |
-
else{
|
74 |
-
return mysql_close($this->get_dbh());
|
75 |
-
}
|
76 |
-
}
|
77 |
-
|
78 |
-
public function error()
|
79 |
-
{
|
80 |
-
if ($this->use_mysqli)
|
81 |
-
{
|
82 |
-
return $this->get_dbh()->error;
|
83 |
-
}
|
84 |
-
else
|
85 |
-
{
|
86 |
-
return mysql_error($this->get_dbh());
|
87 |
-
}
|
88 |
-
}
|
89 |
-
|
90 |
-
public function errno()
|
91 |
-
{
|
92 |
-
if ($this->use_mysqli)
|
93 |
-
{
|
94 |
-
return $this->get_dbh()->errno;
|
95 |
-
}
|
96 |
-
else
|
97 |
-
{
|
98 |
-
return mysql_errno($this->get_dbh());
|
99 |
-
}
|
100 |
-
}
|
101 |
-
|
102 |
-
|
103 |
-
public function real_escape_string($str)
|
104 |
-
{
|
105 |
-
return $this->_escape($str);
|
106 |
-
}
|
107 |
-
|
108 |
-
public function num_fields( $result)
|
109 |
-
{
|
110 |
-
if ($this->use_mysqli)
|
111 |
-
{
|
112 |
-
return $result->field_count;
|
113 |
-
}
|
114 |
-
else
|
115 |
-
{
|
116 |
-
return mysql_num_fields($result);
|
117 |
-
}
|
118 |
-
}
|
119 |
-
|
120 |
-
public function fetch_array($result)
|
121 |
-
{
|
122 |
-
if ($this->use_mysqli)
|
123 |
-
{
|
124 |
-
return $result->fetch_array();
|
125 |
-
|
126 |
-
}
|
127 |
-
else {
|
128 |
-
return mysql_fetch_array($result);
|
129 |
-
}
|
130 |
-
}
|
131 |
-
|
132 |
-
// Fetch a row from a query result
|
133 |
-
public function fetch_row( $result)
|
134 |
-
{
|
135 |
-
if ($this->use_mysqli)
|
136 |
-
{
|
137 |
-
return $result->fetch_row();
|
138 |
-
}
|
139 |
-
else
|
140 |
-
{
|
141 |
-
return mysql_fetch_row($result);
|
142 |
-
}
|
143 |
-
}
|
144 |
-
|
145 |
-
}
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @author Andy Camm
|
4 |
+
*/
|
5 |
+
|
6 |
+
// Subclass wpdb to ensure compatibility with WordPress and to use
|
7 |
+
// the appropriate MySQL module (uses MySQLi on PHP >= 5.5)
|
8 |
+
// and provide access to the additional raw MySQL/MySQLi module calls
|
9 |
+
// that we are using
|
10 |
+
class wpc_wpdb extends wpdb
|
11 |
+
{
|
12 |
+
|
13 |
+
// This is copied from the base class as its use_mysqli member is private
|
14 |
+
protected $use_mysqli = false;
|
15 |
+
|
16 |
+
public function __construct( $dbuser, $dbpassword, $dbname, $dbhost ) {
|
17 |
+
parent::__construct($dbuser, $dbpassword, $dbname, $dbhost);
|
18 |
+
|
19 |
+
// This is copied from the base class as its use_mysqli member is private
|
20 |
+
// Use ext/mysqli if it exists and:
|
21 |
+
// - WP_USE_EXT_MYSQL is defined as false, or
|
22 |
+
// - We are a development version of WordPress, or
|
23 |
+
// - We are running PHP 5.5 or greater, or
|
24 |
+
// - ext/mysql is not loaded.
|
25 |
+
//
|
26 |
+
if ( function_exists( 'mysqli_connect' ) ) {
|
27 |
+
if ( defined( 'WP_USE_EXT_MYSQL' ) ) {
|
28 |
+
$this->use_mysqli = ! WP_USE_EXT_MYSQL;
|
29 |
+
} elseif ( version_compare( phpversion(), '5.5', '>=' ) || ! function_exists( 'mysql_connect' ) ) {
|
30 |
+
$this->use_mysqli = true;
|
31 |
+
} elseif ( false !== strpos( $GLOBALS['wp_version'], '-' ) ) {
|
32 |
+
$this->use_mysqli = true;
|
33 |
+
}
|
34 |
+
}
|
35 |
+
}
|
36 |
+
|
37 |
+
public function get_dbh()
|
38 |
+
{
|
39 |
+
return $this->dbh;
|
40 |
+
}
|
41 |
+
|
42 |
+
public function query($querystring)
|
43 |
+
{
|
44 |
+
if ($this->use_mysqli)
|
45 |
+
{
|
46 |
+
return $this->get_dbh()->query($querystring);
|
47 |
+
}
|
48 |
+
else
|
49 |
+
{
|
50 |
+
return mysql_query($querystring, $this->get_dbh());
|
51 |
+
}
|
52 |
+
}
|
53 |
+
|
54 |
+
public function ping()
|
55 |
+
{
|
56 |
+
if ($this->use_mysqli)
|
57 |
+
{
|
58 |
+
return $this->get_dbh()->ping();
|
59 |
+
}
|
60 |
+
else
|
61 |
+
{
|
62 |
+
return mysql_ping($this->get_dbh());
|
63 |
+
}
|
64 |
+
}
|
65 |
+
|
66 |
+
|
67 |
+
public function close()
|
68 |
+
{
|
69 |
+
if ($this->use_mysqli)
|
70 |
+
{
|
71 |
+
$this->get_dbh()->close();
|
72 |
+
}
|
73 |
+
else{
|
74 |
+
return mysql_close($this->get_dbh());
|
75 |
+
}
|
76 |
+
}
|
77 |
+
|
78 |
+
public function error()
|
79 |
+
{
|
80 |
+
if ($this->use_mysqli)
|
81 |
+
{
|
82 |
+
return $this->get_dbh()->error;
|
83 |
+
}
|
84 |
+
else
|
85 |
+
{
|
86 |
+
return mysql_error($this->get_dbh());
|
87 |
+
}
|
88 |
+
}
|
89 |
+
|
90 |
+
public function errno()
|
91 |
+
{
|
92 |
+
if ($this->use_mysqli)
|
93 |
+
{
|
94 |
+
return $this->get_dbh()->errno;
|
95 |
+
}
|
96 |
+
else
|
97 |
+
{
|
98 |
+
return mysql_errno($this->get_dbh());
|
99 |
+
}
|
100 |
+
}
|
101 |
+
|
102 |
+
|
103 |
+
public function real_escape_string($str)
|
104 |
+
{
|
105 |
+
return $this->_escape($str);
|
106 |
+
}
|
107 |
+
|
108 |
+
public function num_fields( $result)
|
109 |
+
{
|
110 |
+
if ($this->use_mysqli)
|
111 |
+
{
|
112 |
+
return $result->field_count;
|
113 |
+
}
|
114 |
+
else
|
115 |
+
{
|
116 |
+
return mysql_num_fields($result);
|
117 |
+
}
|
118 |
+
}
|
119 |
+
|
120 |
+
public function fetch_array($result)
|
121 |
+
{
|
122 |
+
if ($this->use_mysqli)
|
123 |
+
{
|
124 |
+
return $result->fetch_array();
|
125 |
+
|
126 |
+
}
|
127 |
+
else {
|
128 |
+
return mysql_fetch_array($result);
|
129 |
+
}
|
130 |
+
}
|
131 |
+
|
132 |
+
// Fetch a row from a query result
|
133 |
+
public function fetch_row( $result)
|
134 |
+
{
|
135 |
+
if ($this->use_mysqli)
|
136 |
+
{
|
137 |
+
return $result->fetch_row();
|
138 |
+
}
|
139 |
+
else
|
140 |
+
{
|
141 |
+
return mysql_fetch_row($result);
|
142 |
+
}
|
143 |
+
}
|
144 |
+
|
145 |
+
}
|
lib/css/style.css
CHANGED
@@ -1,299 +1,299 @@
|
|
1 |
-
@import url('https://fonts.googleapis.com/css?family=Montserrat&display=swap');
|
2 |
-
#wrapper {
|
3 |
-
|
4 |
-
}
|
5 |
-
|
6 |
-
#MainView {
|
7 |
-
margin: 13px;
|
8 |
-
padding: 10px;
|
9 |
-
max-width: 60%;
|
10 |
-
float: left;
|
11 |
-
border-radius: 5px;
|
12 |
-
-moz-box-shadow: 5px 5px 15px #aaa;
|
13 |
-
-webkit-box-shadow: 5px 5px 15px #aaa;
|
14 |
-
box-shadow: 5px 5px 15px #aaa;
|
15 |
-
}
|
16 |
-
|
17 |
-
#sidebar {
|
18 |
-
float: left;
|
19 |
-
}
|
20 |
-
|
21 |
-
#MainView h2 {
|
22 |
-
padding-bottom: 10px;
|
23 |
-
}
|
24 |
-
|
25 |
-
#sidebar ul {
|
26 |
-
max-width: 300px;
|
27 |
-
padding: 10px 10px 10px 30px;
|
28 |
-
margin-left: 5px;
|
29 |
-
border: 1px solid #bbb;
|
30 |
-
border-radius: 5px;
|
31 |
-
list-style: disc;
|
32 |
-
-moz-box-shadow: 5px 5px 15px #aaa;
|
33 |
-
-webkit-box-shadow: 5px 5px 15px #aaa;
|
34 |
-
box-shadow: 5px 5px 15px #aaa;
|
35 |
-
}
|
36 |
-
|
37 |
-
#sidebar ul h2 {
|
38 |
-
border-bottom: 5px solid #ccc;
|
39 |
-
padding-bottom: 10px;
|
40 |
-
text-shadow: 5px 5px 10px #888;
|
41 |
-
}
|
42 |
-
|
43 |
-
#sidebar ul h3 {
|
44 |
-
border-bottom: 2px solid #ccc;
|
45 |
-
padding: 10px 0 5px 0;
|
46 |
-
}
|
47 |
-
|
48 |
-
#backupForm #create-backup-option {
|
49 |
-
margin-bottom: 5px;
|
50 |
-
}
|
51 |
-
|
52 |
-
#RestoreOptions, #file_directory {
|
53 |
-
display: none;
|
54 |
-
}
|
55 |
-
|
56 |
-
.width-60 {
|
57 |
-
max-width: 60%;
|
58 |
-
}
|
59 |
-
div.info {
|
60 |
-
padding: 15px;
|
61 |
-
margin: 30px 0;
|
62 |
-
margin-right: 25px;
|
63 |
-
background: #e1e1ea;
|
64 |
-
border-radius: 5px;
|
65 |
-
-moz-box-shadow: 5px 5px 15px #aaa;
|
66 |
-
-webkit-box-shadow: 5px 5px 15px #aaa;
|
67 |
-
box-shadow: 5px 5px 15px #aaa;
|
68 |
-
}
|
69 |
-
|
70 |
-
div.try {
|
71 |
-
display: block;
|
72 |
-
}
|
73 |
-
|
74 |
-
table.restore-backup-options {
|
75 |
-
padding: 0 5px;
|
76 |
-
border-collapse: collapse;
|
77 |
-
}
|
78 |
-
|
79 |
-
table.restore-backup-options td, table.restore-backup-options th {
|
80 |
-
border: 1px solid #ccc;
|
81 |
-
padding: 3px;
|
82 |
-
}
|
83 |
-
|
84 |
-
.error {
|
85 |
-
border: solid 1px #c00;
|
86 |
-
padding: 5px;
|
87 |
-
background-color: #FFEBE8;
|
88 |
-
text-align: center;
|
89 |
-
border-radius: 5px;
|
90 |
-
-moz-box-shadow: 5px 5px 15px #aaa;
|
91 |
-
-webkit-box-shadow: 5px 5px 15px #aaa;
|
92 |
-
box-shadow: 5px 5px 15px #aaa;
|
93 |
-
}
|
94 |
-
|
95 |
-
.deleted {
|
96 |
-
background: #00cc99;
|
97 |
-
text-align: center;
|
98 |
-
}
|
99 |
-
|
100 |
-
.delete-error {
|
101 |
-
text-align: center;
|
102 |
-
background: #fff0b3;
|
103 |
-
}
|
104 |
-
|
105 |
-
a#close-thickbox {
|
106 |
-
position: absolute;
|
107 |
-
top: 0;
|
108 |
-
right: 0;
|
109 |
-
font-weight: bold;
|
110 |
-
}
|
111 |
-
|
112 |
-
div#search-n-replace {
|
113 |
-
display: none;
|
114 |
-
position: relative;
|
115 |
-
}
|
116 |
-
|
117 |
-
div#search-n-replace-info{
|
118 |
-
min-height: 20px;
|
119 |
-
}
|
120 |
-
|
121 |
-
a.copy-button {
|
122 |
-
}
|
123 |
-
|
124 |
-
.btn-primary.active, .btn-warning.active, .btn-danger.active, .btn-success.active, .btn-info.active, .btn-inverse.active {
|
125 |
-
color: rgba(255, 255, 255, 0.75);
|
126 |
-
}
|
127 |
-
|
128 |
-
.btn {
|
129 |
-
border-color: rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.25);
|
130 |
-
margin: 20px 0;
|
131 |
-
padding: 15px 30px;
|
132 |
-
font-size: 1.5em;
|
133 |
-
font-weight: bold;
|
134 |
-
}
|
135 |
-
|
136 |
-
input[type="submit"].btn-primary {
|
137 |
-
background-color: #006DCC;
|
138 |
-
background-image: -moz-linear-gradient(top, #0088CC, #0044CC);
|
139 |
-
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088CC), to(#0044CC));
|
140 |
-
background-image: -webkit-linear-gradient(top, #0088CC, #0044CC);
|
141 |
-
background-image: -o-linear-gradient(top, #0088CC, #0044CC);
|
142 |
-
background-image: linear-gradient(to bottom, #0088CC, #0044CC);
|
143 |
-
background-repeat: repeat-x;
|
144 |
-
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
145 |
-
color: #FFFFFF;
|
146 |
-
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
147 |
-
}
|
148 |
-
|
149 |
-
input[type="submit"].btn-primary:hover, input[type="submit"].btn-primary:active, .btn-primary.active, .btn-primary.disabled, .btn-primary[disabled] {
|
150 |
-
background-color: #0044CC;
|
151 |
-
color: #EEEEEE;
|
152 |
-
}
|
153 |
-
.btn-primary:active, .btn-primary.active {
|
154 |
-
}
|
155 |
-
|
156 |
-
input[type="submit"].btn-warning {
|
157 |
-
background-color: #FAA732;
|
158 |
-
background-image: linear-gradient(to bottom, #FBB450, #F89406);
|
159 |
-
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#FBB450), to(#F89406));
|
160 |
-
background-image: -webkit-linear-gradient(top, #FBB450, #F89406);
|
161 |
-
background-image: -o-linear-gradient(top, #FBB450, #F89406);
|
162 |
-
background-image: linear-gradient(to bottom, #FBB450, #F89406);
|
163 |
-
background-repeat: repeat-x;
|
164 |
-
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
165 |
-
color: #FFFFFF;
|
166 |
-
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
167 |
-
}
|
168 |
-
input[type="submit"].btn-warning:hover, input[type="submit"].btn-warning:active, .btn-warning.active, .btn-warning.disabled, .btn-warning[disabled] {
|
169 |
-
background-color: #F89406;
|
170 |
-
color: #EEEEEE;
|
171 |
-
}
|
172 |
-
|
173 |
-
textarea {
|
174 |
-
width: 100%;
|
175 |
-
padding: 0px
|
176 |
-
}
|
177 |
-
|
178 |
-
input.Url {
|
179 |
-
width: 80%;
|
180 |
-
padding: 0px
|
181 |
-
}
|
182 |
-
|
183 |
-
.btn-warning:active, .btn-warning.active {
|
184 |
-
}
|
185 |
-
|
186 |
-
.wpclone_notice {
|
187 |
-
max-width: 500px;
|
188 |
-
padding: 5px;
|
189 |
-
margin: 25px 0 0 0;
|
190 |
-
border-radius: 5px;
|
191 |
-
}
|
192 |
-
|
193 |
-
.plugin-large-notice{
|
194 |
-
overflow: hidden;;
|
195 |
-
}
|
196 |
-
|
197 |
-
/** Banner CSS **/
|
198 |
-
.banner-1{
|
199 |
-
min-height: 744px;
|
200 |
-
width: auto;
|
201 |
-
background-size: cover;
|
202 |
-
padding-top: 48px;
|
203 |
-
padding-left: 61px;
|
204 |
-
padding-right: 85px;
|
205 |
-
font-family: 'Montserrat', sans-serif;
|
206 |
-
margin-right: 30px;
|
207 |
-
margin-top: 20px;
|
208 |
-
}
|
209 |
-
|
210 |
-
.banner-1 .heading{
|
211 |
-
color: #0f9087;
|
212 |
-
font-size: 26px;
|
213 |
-
}
|
214 |
-
|
215 |
-
.banner-1 .nutshell-list{
|
216 |
-
color: #3A3A3A;
|
217 |
-
font-size: 18px;
|
218 |
-
line-height: 22px;
|
219 |
-
}
|
220 |
-
|
221 |
-
|
222 |
-
.banner-1 .banner-footer{
|
223 |
-
margin-top: 62px;
|
224 |
-
font-size: 18px;
|
225 |
-
line-height: 29px;
|
226 |
-
}
|
227 |
-
|
228 |
-
.banner-1 .button1{
|
229 |
-
/*-webkit-font-smoothing: antialiased;
|
230 |
-
background-color: #0f9087;
|
231 |
-
border: none;
|
232 |
-
color: #fff;
|
233 |
-
display: inline-block;
|
234 |
-
text-decoration: none;
|
235 |
-
user-select: none;
|
236 |
-
letter-spacing: 1px;
|
237 |
-
padding: 15px 35px;
|
238 |
-
text-transform: uppercase;
|
239 |
-
transition: all 0.1s ease-out;
|
240 |
-
border-radius: 15px;*/
|
241 |
-
|
242 |
-
}
|
243 |
-
.banner-1 .button2{
|
244 |
-
-webkit-font-smoothing: antialiased;
|
245 |
-
background-color: #0f9087;
|
246 |
-
border: none;
|
247 |
-
color: #fff;
|
248 |
-
display: inline-block;
|
249 |
-
text-decoration: none;
|
250 |
-
user-select: none;
|
251 |
-
letter-spacing: 1px;
|
252 |
-
padding: 15px 35px;
|
253 |
-
text-transform: uppercase;
|
254 |
-
transition: all 0.1s ease-out;
|
255 |
-
border-radius: 15px;
|
256 |
-
}
|
257 |
-
|
258 |
-
.banner-1 .close-icon {
|
259 |
-
float: right;
|
260 |
-
margin-top: -30px;
|
261 |
-
margin-right: -65px;
|
262 |
-
cursor: pointer;
|
263 |
-
}
|
264 |
-
|
265 |
-
.plugin-large-notice .banner-1-collapsed{
|
266 |
-
min-height: 63px;
|
267 |
-
width: auto;
|
268 |
-
/*padding-top: 48px;
|
269 |
-
padding-left: 61px;
|
270 |
-
padding-right: 85px;*/
|
271 |
-
font-family: 'Montserrat', sans-serif;
|
272 |
-
margin-right: 30px;
|
273 |
-
margin-top: 20px;
|
274 |
-
}
|
275 |
-
|
276 |
-
.plugin-large-notice .banner-1-collapsed p.left-text {
|
277 |
-
font-size: 20px;
|
278 |
-
line-height: 25px;
|
279 |
-
color: #ffffff;
|
280 |
-
font-family: 'Montserrat', sans-serif;
|
281 |
-
padding-left: 15px;
|
282 |
-
float: left;
|
283 |
-
}
|
284 |
-
|
285 |
-
.plugin-large-notice .banner-1-collapsed p.left-text a {
|
286 |
-
font-size: 15px;
|
287 |
-
color: white;
|
288 |
-
text-decoration: underline;
|
289 |
-
}
|
290 |
-
|
291 |
-
.plugin-large-notice .banner-1-collapsed p.remove-for-good {
|
292 |
-
float: right;
|
293 |
-
font-size: 16px;
|
294 |
-
color: #ffffff;
|
295 |
-
margin-right: 30px;
|
296 |
-
line-height: 35px;
|
297 |
-
cursor: pointer;
|
298 |
-
}
|
299 |
-
|
1 |
+
@import url('https://fonts.googleapis.com/css?family=Montserrat&display=swap');
|
2 |
+
#wrapper {
|
3 |
+
|
4 |
+
}
|
5 |
+
|
6 |
+
#MainView {
|
7 |
+
margin: 13px;
|
8 |
+
padding: 10px;
|
9 |
+
max-width: 60%;
|
10 |
+
float: left;
|
11 |
+
border-radius: 5px;
|
12 |
+
-moz-box-shadow: 5px 5px 15px #aaa;
|
13 |
+
-webkit-box-shadow: 5px 5px 15px #aaa;
|
14 |
+
box-shadow: 5px 5px 15px #aaa;
|
15 |
+
}
|
16 |
+
|
17 |
+
#sidebar {
|
18 |
+
float: left;
|
19 |
+
}
|
20 |
+
|
21 |
+
#MainView h2 {
|
22 |
+
padding-bottom: 10px;
|
23 |
+
}
|
24 |
+
|
25 |
+
#sidebar ul {
|
26 |
+
max-width: 300px;
|
27 |
+
padding: 10px 10px 10px 30px;
|
28 |
+
margin-left: 5px;
|
29 |
+
border: 1px solid #bbb;
|
30 |
+
border-radius: 5px;
|
31 |
+
list-style: disc;
|
32 |
+
-moz-box-shadow: 5px 5px 15px #aaa;
|
33 |
+
-webkit-box-shadow: 5px 5px 15px #aaa;
|
34 |
+
box-shadow: 5px 5px 15px #aaa;
|
35 |
+
}
|
36 |
+
|
37 |
+
#sidebar ul h2 {
|
38 |
+
border-bottom: 5px solid #ccc;
|
39 |
+
padding-bottom: 10px;
|
40 |
+
text-shadow: 5px 5px 10px #888;
|
41 |
+
}
|
42 |
+
|
43 |
+
#sidebar ul h3 {
|
44 |
+
border-bottom: 2px solid #ccc;
|
45 |
+
padding: 10px 0 5px 0;
|
46 |
+
}
|
47 |
+
|
48 |
+
#backupForm #create-backup-option {
|
49 |
+
margin-bottom: 5px;
|
50 |
+
}
|
51 |
+
|
52 |
+
#RestoreOptions, #file_directory {
|
53 |
+
display: none;
|
54 |
+
}
|
55 |
+
|
56 |
+
.width-60 {
|
57 |
+
max-width: 60%;
|
58 |
+
}
|
59 |
+
div.info {
|
60 |
+
padding: 15px;
|
61 |
+
margin: 30px 0;
|
62 |
+
margin-right: 25px;
|
63 |
+
background: #e1e1ea;
|
64 |
+
border-radius: 5px;
|
65 |
+
-moz-box-shadow: 5px 5px 15px #aaa;
|
66 |
+
-webkit-box-shadow: 5px 5px 15px #aaa;
|
67 |
+
box-shadow: 5px 5px 15px #aaa;
|
68 |
+
}
|
69 |
+
|
70 |
+
div.try {
|
71 |
+
display: block;
|
72 |
+
}
|
73 |
+
|
74 |
+
table.restore-backup-options {
|
75 |
+
padding: 0 5px;
|
76 |
+
border-collapse: collapse;
|
77 |
+
}
|
78 |
+
|
79 |
+
table.restore-backup-options td, table.restore-backup-options th {
|
80 |
+
border: 1px solid #ccc;
|
81 |
+
padding: 3px;
|
82 |
+
}
|
83 |
+
|
84 |
+
.error {
|
85 |
+
border: solid 1px #c00;
|
86 |
+
padding: 5px;
|
87 |
+
background-color: #FFEBE8;
|
88 |
+
text-align: center;
|
89 |
+
border-radius: 5px;
|
90 |
+
-moz-box-shadow: 5px 5px 15px #aaa;
|
91 |
+
-webkit-box-shadow: 5px 5px 15px #aaa;
|
92 |
+
box-shadow: 5px 5px 15px #aaa;
|
93 |
+
}
|
94 |
+
|
95 |
+
.deleted {
|
96 |
+
background: #00cc99;
|
97 |
+
text-align: center;
|
98 |
+
}
|
99 |
+
|
100 |
+
.delete-error {
|
101 |
+
text-align: center;
|
102 |
+
background: #fff0b3;
|
103 |
+
}
|
104 |
+
|
105 |
+
a#close-thickbox {
|
106 |
+
position: absolute;
|
107 |
+
top: 0;
|
108 |
+
right: 0;
|
109 |
+
font-weight: bold;
|
110 |
+
}
|
111 |
+
|
112 |
+
div#search-n-replace {
|
113 |
+
display: none;
|
114 |
+
position: relative;
|
115 |
+
}
|
116 |
+
|
117 |
+
div#search-n-replace-info{
|
118 |
+
min-height: 20px;
|
119 |
+
}
|
120 |
+
|
121 |
+
a.copy-button {
|
122 |
+
}
|
123 |
+
|
124 |
+
.btn-primary.active, .btn-warning.active, .btn-danger.active, .btn-success.active, .btn-info.active, .btn-inverse.active {
|
125 |
+
color: rgba(255, 255, 255, 0.75);
|
126 |
+
}
|
127 |
+
|
128 |
+
.btn {
|
129 |
+
border-color: rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.25);
|
130 |
+
margin: 20px 0;
|
131 |
+
padding: 15px 30px;
|
132 |
+
font-size: 1.5em;
|
133 |
+
font-weight: bold;
|
134 |
+
}
|
135 |
+
|
136 |
+
input[type="submit"].btn-primary {
|
137 |
+
background-color: #006DCC;
|
138 |
+
background-image: -moz-linear-gradient(top, #0088CC, #0044CC);
|
139 |
+
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088CC), to(#0044CC));
|
140 |
+
background-image: -webkit-linear-gradient(top, #0088CC, #0044CC);
|
141 |
+
background-image: -o-linear-gradient(top, #0088CC, #0044CC);
|
142 |
+
background-image: linear-gradient(to bottom, #0088CC, #0044CC);
|
143 |
+
background-repeat: repeat-x;
|
144 |
+
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
145 |
+
color: #FFFFFF;
|
146 |
+
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
147 |
+
}
|
148 |
+
|
149 |
+
input[type="submit"].btn-primary:hover, input[type="submit"].btn-primary:active, .btn-primary.active, .btn-primary.disabled, .btn-primary[disabled] {
|
150 |
+
background-color: #0044CC;
|
151 |
+
color: #EEEEEE;
|
152 |
+
}
|
153 |
+
.btn-primary:active, .btn-primary.active {
|
154 |
+
}
|
155 |
+
|
156 |
+
input[type="submit"].btn-warning {
|
157 |
+
background-color: #FAA732;
|
158 |
+
background-image: linear-gradient(to bottom, #FBB450, #F89406);
|
159 |
+
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#FBB450), to(#F89406));
|
160 |
+
background-image: -webkit-linear-gradient(top, #FBB450, #F89406);
|
161 |
+
background-image: -o-linear-gradient(top, #FBB450, #F89406);
|
162 |
+
background-image: linear-gradient(to bottom, #FBB450, #F89406);
|
163 |
+
background-repeat: repeat-x;
|
164 |
+
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
165 |
+
color: #FFFFFF;
|
166 |
+
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
167 |
+
}
|
168 |
+
input[type="submit"].btn-warning:hover, input[type="submit"].btn-warning:active, .btn-warning.active, .btn-warning.disabled, .btn-warning[disabled] {
|
169 |
+
background-color: #F89406;
|
170 |
+
color: #EEEEEE;
|
171 |
+
}
|
172 |
+
|
173 |
+
textarea {
|
174 |
+
width: 100%;
|
175 |
+
padding: 0px
|
176 |
+
}
|
177 |
+
|
178 |
+
input.Url {
|
179 |
+
width: 80%;
|
180 |
+
padding: 0px
|
181 |
+
}
|
182 |
+
|
183 |
+
.btn-warning:active, .btn-warning.active {
|
184 |
+
}
|
185 |
+
|
186 |
+
.wpclone_notice {
|
187 |
+
max-width: 500px;
|
188 |
+
padding: 5px;
|
189 |
+
margin: 25px 0 0 0;
|
190 |
+
border-radius: 5px;
|
191 |
+
}
|
192 |
+
|
193 |
+
.plugin-large-notice{
|
194 |
+
overflow: hidden;;
|
195 |
+
}
|
196 |
+
|
197 |
+
/** Banner CSS **/
|
198 |
+
.banner-1{
|
199 |
+
min-height: 744px;
|
200 |
+
width: auto;
|
201 |
+
background-size: cover;
|
202 |
+
padding-top: 48px;
|
203 |
+
padding-left: 61px;
|
204 |
+
padding-right: 85px;
|
205 |
+
font-family: 'Montserrat', sans-serif;
|
206 |
+
margin-right: 30px;
|
207 |
+
margin-top: 20px;
|
208 |
+
}
|
209 |
+
|
210 |
+
.banner-1 .heading{
|
211 |
+
color: #0f9087;
|
212 |
+
font-size: 26px;
|
213 |
+
}
|
214 |
+
|
215 |
+
.banner-1 .nutshell-list{
|
216 |
+
color: #3A3A3A;
|
217 |
+
font-size: 18px;
|
218 |
+
line-height: 22px;
|
219 |
+
}
|
220 |
+
|
221 |
+
|
222 |
+
.banner-1 .banner-footer{
|
223 |
+
margin-top: 62px;
|
224 |
+
font-size: 18px;
|
225 |
+
line-height: 29px;
|
226 |
+
}
|
227 |
+
|
228 |
+
.banner-1 .button1{
|
229 |
+
/*-webkit-font-smoothing: antialiased;
|
230 |
+
background-color: #0f9087;
|
231 |
+
border: none;
|
232 |
+
color: #fff;
|
233 |
+
display: inline-block;
|
234 |
+
text-decoration: none;
|
235 |
+
user-select: none;
|
236 |
+
letter-spacing: 1px;
|
237 |
+
padding: 15px 35px;
|
238 |
+
text-transform: uppercase;
|
239 |
+
transition: all 0.1s ease-out;
|
240 |
+
border-radius: 15px;*/
|
241 |
+
|
242 |
+
}
|
243 |
+
.banner-1 .button2{
|
244 |
+
-webkit-font-smoothing: antialiased;
|
245 |
+
background-color: #0f9087;
|
246 |
+
border: none;
|
247 |
+
color: #fff;
|
248 |
+
display: inline-block;
|
249 |
+
text-decoration: none;
|
250 |
+
user-select: none;
|
251 |
+
letter-spacing: 1px;
|
252 |
+
padding: 15px 35px;
|
253 |
+
text-transform: uppercase;
|
254 |
+
transition: all 0.1s ease-out;
|
255 |
+
border-radius: 15px;
|
256 |
+
}
|
257 |
+
|
258 |
+
.banner-1 .close-icon {
|
259 |
+
float: right;
|
260 |
+
margin-top: -30px;
|
261 |
+
margin-right: -65px;
|
262 |
+
cursor: pointer;
|
263 |
+
}
|
264 |
+
|
265 |
+
.plugin-large-notice .banner-1-collapsed{
|
266 |
+
min-height: 63px;
|
267 |
+
width: auto;
|
268 |
+
/*padding-top: 48px;
|
269 |
+
padding-left: 61px;
|
270 |
+
padding-right: 85px;*/
|
271 |
+
font-family: 'Montserrat', sans-serif;
|
272 |
+
margin-right: 30px;
|
273 |
+
margin-top: 20px;
|
274 |
+
}
|
275 |
+
|
276 |
+
.plugin-large-notice .banner-1-collapsed p.left-text {
|
277 |
+
font-size: 20px;
|
278 |
+
line-height: 25px;
|
279 |
+
color: #ffffff;
|
280 |
+
font-family: 'Montserrat', sans-serif;
|
281 |
+
padding-left: 15px;
|
282 |
+
float: left;
|
283 |
+
}
|
284 |
+
|
285 |
+
.plugin-large-notice .banner-1-collapsed p.left-text a {
|
286 |
+
font-size: 15px;
|
287 |
+
color: white;
|
288 |
+
text-decoration: underline;
|
289 |
+
}
|
290 |
+
|
291 |
+
.plugin-large-notice .banner-1-collapsed p.remove-for-good {
|
292 |
+
float: right;
|
293 |
+
font-size: 16px;
|
294 |
+
color: #ffffff;
|
295 |
+
margin-right: 30px;
|
296 |
+
line-height: 35px;
|
297 |
+
cursor: pointer;
|
298 |
+
}
|
299 |
+
|
lib/functions.php
CHANGED
@@ -1,1517 +1,1517 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* wp-clone-by-wp-academy Wordpress plugin to backup and migrate Wordpress website.
|
4 |
-
*
|
5 |
-
* @URI https://wordpress.org/plugins/wp-clone-by-wp-academy
|
6 |
-
*
|
7 |
-
* @developed by Shaharia Azam <mail@shaharia.com>
|
8 |
-
*/
|
9 |
-
function wpCloneSafePathMode($path) {
|
10 |
-
return str_replace("\\", "/", $path);
|
11 |
-
}
|
12 |
-
|
13 |
-
function wpCloneDirectory($path) {
|
14 |
-
return rtrim(str_replace("//", "/", wpCloneSafePathMode($path)), '/') . '/';
|
15 |
-
}
|
16 |
-
|
17 |
-
function convertPathIntoUrl($path) {
|
18 |
-
return str_replace(rtrim(WPCLONE_ROOT, "/\\"), site_url(), $path);
|
19 |
-
}
|
20 |
-
|
21 |
-
function convertUrlIntoPath($url) {
|
22 |
-
return str_replace(site_url(), rtrim(WPCLONE_ROOT, "/\\"), $url);
|
23 |
-
}
|
24 |
-
|
25 |
-
function wpa_db_backup_wpdb($destination)
|
26 |
-
{
|
27 |
-
global $wpdb;
|
28 |
-
|
29 |
-
$return = '';
|
30 |
-
|
31 |
-
// Get all of the tables
|
32 |
-
if( isset( $_POST['ignore_prefix'] ) && 'true' === $_POST['ignore_prefix'] ) {
|
33 |
-
wpa_wpc_log( 'ignore prefix enabled, backing up all the tables' );
|
34 |
-
$tables = $wpdb->get_col('SHOW TABLES');
|
35 |
-
|
36 |
-
} else {
|
37 |
-
wpa_wpc_log( sprintf( 'backing up tables with "%s" prefix', $wpdb->prefix ) );
|
38 |
-
$tables = $wpdb->get_col('SHOW TABLES LIKE "' . $wpdb->prefix . '%"');
|
39 |
-
|
40 |
-
}
|
41 |
-
|
42 |
-
wpa_wpc_log( sprintf( 'number of tables to backup - %d', count( $tables ) ) );
|
43 |
-
|
44 |
-
// Cycle through each provided table
|
45 |
-
foreach ($tables as $table) {
|
46 |
-
|
47 |
-
// First part of the output � remove the table
|
48 |
-
$result = $wpdb->get_results("SELECT * FROM {$table}", ARRAY_N);
|
49 |
-
$numberOfItems = count($result);
|
50 |
-
if ($numberOfItems == 0) {
|
51 |
-
// Empty table - don't attempt to use $result[0] as it doesn't exist
|
52 |
-
$numberOfFields = 0;
|
53 |
-
}
|
54 |
-
else {
|
55 |
-
$numberOfFields = count($result[0]);
|
56 |
-
}
|
57 |
-
|
58 |
-
// Second part of the output � create table
|
59 |
-
$row2 = $wpdb->get_row("SHOW CREATE TABLE {$table}", ARRAY_N);
|
60 |
-
$return.= 'DROP TABLE IF EXISTS '.$table.';';
|
61 |
-
$return .= "\n\n" . $row2[1] . ";\n\n";
|
62 |
-
|
63 |
-
// Third part of the output � insert values into new table
|
64 |
-
for ($currentRowNumber = 0; $currentRowNumber < $numberOfItems; $currentRowNumber++) {
|
65 |
-
|
66 |
-
$row = $result[$currentRowNumber];
|
67 |
-
$query = "INSERT INTO {$table} VALUES(";
|
68 |
-
|
69 |
-
for ($j = 0; $j < $numberOfFields; $j++) {
|
70 |
-
// Change to 'isset()' instead of 'empty()' as 'empty()' returns true for the
|
71 |
-
// string "0" - but we may need to explicitly set value to 0 for fields where this
|
72 |
-
// is not the default. This makes the output of this method identical to the
|
73 |
-
// wpa_db_backup_direct() method
|
74 |
-
$query .= (!isset($row[$j])) ? '"", ' : '"' . esc_sql($row[$j]) . '", ';
|
75 |
-
}
|
76 |
-
|
77 |
-
$return .= substr($query, 0, -2) . ");\n";
|
78 |
-
|
79 |
-
}
|
80 |
-
|
81 |
-
$return .= "\n";
|
82 |
-
}
|
83 |
-
|
84 |
-
// Generate the filename for the sql file
|
85 |
-
$File_open = fopen($destination . '/database.sql', 'w+');
|
86 |
-
|
87 |
-
// Save the sql file
|
88 |
-
fwrite($File_open, $return);
|
89 |
-
|
90 |
-
//file close
|
91 |
-
fclose($File_open);
|
92 |
-
|
93 |
-
$wpdb->flush();
|
94 |
-
}
|
95 |
-
|
96 |
-
/**
|
97 |
-
* @link http://davidwalsh.name/backup-mysql-database-php
|
98 |
-
*/
|
99 |
-
function wpa_db_backup_direct($destination)
|
100 |
-
{
|
101 |
-
|
102 |
-
global $wpdb;
|
103 |
-
$prefix = $wpdb->prefix;
|
104 |
-
$wpcdb = wpa_wpc_mysql_connect();
|
105 |
-
if ( false === $wpcdb->get_dbh() ) {
|
106 |
-
wpa_backup_error('db', $wpcdb->error() );
|
107 |
-
}
|
108 |
-
|
109 |
-
$tables = array();
|
110 |
-
|
111 |
-
if( isset( $_POST['ignore_prefix'] ) && 'true' === $_POST['ignore_prefix'] ) {
|
112 |
-
wpa_wpc_log( 'ignore prefix enabled, backing up all the tables' );
|
113 |
-
$result = $wpcbd->query('SHOW TABLES');
|
114 |
-
|
115 |
-
} else {
|
116 |
-
wpa_wpc_log( sprintf( 'backing up tables with "%s" prefix', $prefix ) );
|
117 |
-
$result = $wpcdb->query('SHOW TABLES LIKE "' . $prefix . '%"');
|
118 |
-
}
|
119 |
-
|
120 |
-
if ( false === $result ) {
|
121 |
-
wpa_backup_error('db', $wpcdb->error() );
|
122 |
-
}
|
123 |
-
|
124 |
-
while( $row = $wpcdb->fetch_row( $result ) ) {
|
125 |
-
$tables[] = $row[0];
|
126 |
-
}
|
127 |
-
|
128 |
-
wpa_wpc_log( sprintf( 'number of tables to backup - %d', count( $tables ) ) );
|
129 |
-
$return = '';
|
130 |
-
|
131 |
-
foreach($tables as $table)
|
132 |
-
{
|
133 |
-
$result = $wpcdb->query( 'SELECT * FROM ' . $table );
|
134 |
-
if ( false === $result ) {
|
135 |
-
wpa_backup_error('db', $wpcdb->error() );
|
136 |
-
}
|
137 |
-
$num_fields = $wpcdb->num_fields($result);
|
138 |
-
|
139 |
-
$return.= 'DROP TABLE IF EXISTS '.$table.';';
|
140 |
-
$row2 = $wpcdb->fetch_row( $wpcdb->query( 'SHOW CREATE TABLE ' . $table ) );
|
141 |
-
$return.= "\n\n".$row2[1].";\n\n";
|
142 |
-
|
143 |
-
for ($i = 0; $i < $num_fields; $i++)
|
144 |
-
{
|
145 |
-
while($row = $wpcdb->fetch_row($result))
|
146 |
-
{
|
147 |
-
$return.= 'INSERT INTO '.$table.' VALUES(';
|
148 |
-
for($j=0; $j<$num_fields; $j++)
|
149 |
-
{
|
150 |
-
$row[$j] = $wpdb->escape( $row[$j] );
|
151 |
-
if (isset($row[$j])) { $return.= '"'.$row[$j].'"' ; } else { $return.= '""'; }
|
152 |
-
if ($j<($num_fields-1)) { $return.= ', '; } // Add extra space to match wpdb backup method
|
153 |
-
}
|
154 |
-
$return.= ");\n";
|
155 |
-
}
|
156 |
-
}
|
157 |
-
$return.="\n";
|
158 |
-
|
159 |
-
}
|
160 |
-
//save file
|
161 |
-
$handle = fopen($destination . '/database.sql','w+');
|
162 |
-
fwrite($handle,$return);
|
163 |
-
fclose($handle);
|
164 |
-
}
|
165 |
-
|
166 |
-
function wpa_insert_data($name, $size)
|
167 |
-
{
|
168 |
-
$backups = get_option( 'wpclone_backups' );
|
169 |
-
$time = current_time( 'timestamp', get_option('gmt_offset') );
|
170 |
-
global $current_user;
|
171 |
-
$backup = array(
|
172 |
-
$time => array(
|
173 |
-
'name' => $name . '.zip',
|
174 |
-
'log' => $name . '.log',
|
175 |
-
'creator' => $current_user->user_login,
|
176 |
-
'size' => $size
|
177 |
-
)
|
178 |
-
);
|
179 |
-
|
180 |
-
if( false === $backups ) {
|
181 |
-
add_option( 'wpclone_backups', $backup );
|
182 |
-
return;
|
183 |
-
}
|
184 |
-
|
185 |
-
$backups = $backups + $backup;
|
186 |
-
update_option( 'wpclone_backups', $backups );
|
187 |
-
|
188 |
-
return;
|
189 |
-
|
190 |
-
}
|
191 |
-
|
192 |
-
function CreateWPFullBackupZip($backupName, $zipmode, $use_wpdb = false )
|
193 |
-
{
|
194 |
-
$folderToBeZipped = WPCLONE_DIR_BACKUP . 'wpclone_backup';
|
195 |
-
$htaccess = "<Files>\r\n\tOrder allow,deny\r\n\tDeny from all\r\n\tSatisfy all\r\n</Files>";
|
196 |
-
$zipFileName = WPCLONE_DIR_BACKUP . $backupName . '.zip';
|
197 |
-
$exclude = wpa_excluded_dirs();
|
198 |
-
$dbonly = isset( $_POST['dbonly'] ) && 'true' == $_POST['dbonly'] ? true : false;
|
199 |
-
$skip = 25 * 1024 * 1024;
|
200 |
-
|
201 |
-
if( isset( $_POST['skipfiles'] ) && '' !== $_POST['skipfiles'] ) {
|
202 |
-
|
203 |
-
if( 0 === $_POST['skipfiles'] ) {
|
204 |
-
$skip = false;
|
205 |
-
|
206 |
-
} else {
|
207 |
-
$skip = $_POST['skipfiles'] * 1024 * 1024;
|
208 |
-
|
209 |
-
}
|
210 |
-
|
211 |
-
}
|
212 |
-
|
213 |
-
if( false === mkdir( $folderToBeZipped ) ) {
|
214 |
-
wpa_backup_error ( 'file', sprintf( __( 'Unable to create the temporary backup directory,please make sure that PHP has permission to write into the <code>%s</code> directory.' ), WPCLONE_DIR_BACKUP ) );
|
215 |
-
}
|
216 |
-
|
217 |
-
file_put_contents( $folderToBeZipped . '/.htaccess', $htaccess );
|
218 |
-
|
219 |
-
if( $dbonly ) {
|
220 |
-
wpa_wpc_log ( 'database only backup, no files will be copied' );
|
221 |
-
}
|
222 |
-
|
223 |
-
if( false === $dbonly ) {
|
224 |
-
if( $skip ) {
|
225 |
-
wpa_wpc_log( sprintf( 'files larger than %s will be excluded from the backup', bytesToSize( $skip ) ) );
|
226 |
-
}
|
227 |
-
wpa_wpc_log( 'generating file list' );
|
228 |
-
file_put_contents( $folderToBeZipped . '/file.list', serialize( wpa_wpc_get_filelist( WPCLONE_WP_CONTENT, $exclude, $skip ) ) );
|
229 |
-
wpa_wpc_log( 'finished generating file list' );
|
230 |
-
}
|
231 |
-
|
232 |
-
wpa_save_prefix($folderToBeZipped);
|
233 |
-
/* error handler is called from within the db backup functions */
|
234 |
-
if ( $use_wpdb ) {
|
235 |
-
wpa_wpc_log ( 'database backup started [wpdb]' );
|
236 |
-
wpa_db_backup_wpdb( $folderToBeZipped );
|
237 |
-
} else {
|
238 |
-
wpa_wpc_log ( 'database backup started' );
|
239 |
-
wpa_db_backup_direct( $folderToBeZipped );
|
240 |
-
}
|
241 |
-
wpa_wpc_log ( 'database backup finished' );
|
242 |
-
|
243 |
-
/* error handler is called from within the wpa_zip function */
|
244 |
-
|
245 |
-
wpa_zip($zipFileName, $folderToBeZipped, $zipmode);
|
246 |
-
|
247 |
-
wpa_delete_dir( $folderToBeZipped );
|
248 |
-
|
249 |
-
if( ! file_exists( $zipFileName ) ) {
|
250 |
-
wpa_backup_error( 'backup', 'possibly out of free disk space' );
|
251 |
-
}
|
252 |
-
$zipSize = filesize($zipFileName);
|
253 |
-
return array($backupName, $zipSize);
|
254 |
-
}
|
255 |
-
|
256 |
-
function DeleteWPBackupZip($nm)
|
257 |
-
{
|
258 |
-
$backups = get_option( 'wpclone_backups' );
|
259 |
-
|
260 |
-
if( empty( $backups ) || ! isset( $backups[$nm] ) ) {
|
261 |
-
return array(
|
262 |
-
'status' => 'failed',
|
263 |
-
'msg' => 'Something is not quite right here, refresh the backup list and try again later.' );
|
264 |
-
}
|
265 |
-
|
266 |
-
if( isset( $backups[$nm]['log'] ) && file_exists( WPCLONE_DIR_BACKUP . $backups[$nm]['log'] ) ) {
|
267 |
-
@unlink( WPCLONE_DIR_BACKUP . $backups[$nm]['log'] );
|
268 |
-
|
269 |
-
}
|
270 |
-
|
271 |
-
if ( file_exists( WPCLONE_DIR_BACKUP . $backups[$nm]['name'] ) ) {
|
272 |
-
|
273 |
-
if( ! unlink( WPCLONE_DIR_BACKUP . $backups[$nm]['name'] ) ) {
|
274 |
-
return array(
|
275 |
-
'status' => 'failed',
|
276 |
-
'msg' => 'Unable to delete file' );
|
277 |
-
}
|
278 |
-
|
279 |
-
unset( $backups[$nm] );
|
280 |
-
update_option( 'wpclone_backups', $backups );
|
281 |
-
return array(
|
282 |
-
'status' => 'deleted',
|
283 |
-
'msg' => 'File deleted' );
|
284 |
-
|
285 |
-
} else {
|
286 |
-
|
287 |
-
return array(
|
288 |
-
'status' => 'failed',
|
289 |
-
'msg' => 'File not found. Refresh the backup list to remove missing backups.' );
|
290 |
-
|
291 |
-
}
|
292 |
-
|
293 |
-
}
|
294 |
-
|
295 |
-
function bytesToSize($bytes, $precision = 2)
|
296 |
-
{
|
297 |
-
$kilobyte = 1024;
|
298 |
-
$megabyte = $kilobyte * 1024;
|
299 |
-
$gigabyte = $megabyte * 1024;
|
300 |
-
$terabyte = $gigabyte * 1024;
|
301 |
-
if (($bytes >= 0) && ($bytes < $kilobyte)) {
|
302 |
-
return $bytes . ' B';
|
303 |
-
} elseif (($bytes >= $kilobyte) && ($bytes < $megabyte)) {
|
304 |
-
return round($bytes / $kilobyte, $precision) . ' KB';
|
305 |
-
} elseif (($bytes >= $megabyte) && ($bytes < $gigabyte)) {
|
306 |
-
return round($bytes / $megabyte, $precision) . ' MB';
|
307 |
-
} elseif (($bytes >= $gigabyte) && ($bytes < $terabyte)) {
|
308 |
-
return round($bytes / $gigabyte, $precision) . ' GB';
|
309 |
-
} elseif ($bytes >= $terabyte) {
|
310 |
-
return round($bytes / $terabyte, $precision) . ' TB';
|
311 |
-
} else {
|
312 |
-
return $bytes . ' B';
|
313 |
-
}
|
314 |
-
}
|
315 |
-
|
316 |
-
function wpa_wpc_get_url( $db ) {
|
317 |
-
|
318 |
-
$pos = strpos( $db, 'siteurl' ) + 8;
|
319 |
-
$urlStartPos = strpos( $db, '"', $pos ) + 1;
|
320 |
-
$urlEndPos = strpos( $db, '"', $urlStartPos );
|
321 |
-
$backupSiteUrl = substr( $db, $urlStartPos, $urlEndPos - $urlStartPos );
|
322 |
-
return $backupSiteUrl;
|
323 |
-
|
324 |
-
}
|
325 |
-
|
326 |
-
|
327 |
-
function wpa_wpc_mysql_connect() {
|
328 |
-
// Use subclass of wpdb to ensure compatibility with WordPress database and use the appropriate MySQL module
|
329 |
-
// and provide the extra functions we need
|
330 |
-
$db = new wpc_wpdb( DB_USER, DB_PASSWORD, DB_NAME, DB_HOST );
|
331 |
-
return $db;
|
332 |
-
}
|
333 |
-
|
334 |
-
/**
|
335 |
-
* @param type $search URL of the previous site.
|
336 |
-
* @param type $replace URL of the current site.
|
337 |
-
* @return type total time it took for the operation.
|
338 |
-
*/
|
339 |
-
function wpa_safe_replace_wrapper ( $search, $replace, $prefix ) {
|
340 |
-
if ( !function_exists( 'icit_srdb_replacer' ) && !function_exists( 'recursive_unserialize_replace' ) ) {
|
341 |
-
require_once 'icit_srdb_replacer.php';
|
342 |
-
}
|
343 |
-
|
344 |
-
wpa_wpc_log( 'search and replace started' );
|
345 |
-
|
346 |
-
$wpcdb = wpa_wpc_mysql_connect();
|
347 |
-
|
348 |
-
if ( false === $wpcdb->get_dbh() ) {
|
349 |
-
|
350 |
-
wpa_wpc_log( 'mysql connection failure @ safe replace wrapper - error : "' . $wpdbc->error() . '" retrying..' );
|
351 |
-
|
352 |
-
$wpcdb->close();
|
353 |
-
sleep(1);
|
354 |
-
// Try to create a new connection
|
355 |
-
$wpcdb = wpa_wpc_mysql_connect();
|
356 |
-
}
|
357 |
-
|
358 |
-
$all_tables = array();
|
359 |
-
|
360 |
-
if( isset( $_POST['ignore_prefix'] ) && 'true' === $_POST['ignore_prefix'] ) {
|
361 |
-
wpa_wpc_log( 'ignore table prefix enabled, search and replace will scan all the tables in the database' );
|
362 |
-
$all_tables_mysql = @$wpcdb->query( 'SHOW TABLES' );
|
363 |
-
|
364 |
-
} else {
|
365 |
-
$all_tables_mysql = @$wpcdb->query( 'SHOW TABLES LIKE "' . $prefix . '%"' );
|
366 |
-
}
|
367 |
-
|
368 |
-
while ( $table = $wpcdb->fetch_array( $all_tables_mysql ) ) {
|
369 |
-
$all_tables[] = $table[ 0 ];
|
370 |
-
}
|
371 |
-
|
372 |
-
wpa_wpc_log( sprintf( 'there are %d tables to scan', count( $all_tables ) ) );
|
373 |
-
|
374 |
-
$report = icit_srdb_replacer( $wpcdb, $search, $replace, $all_tables );
|
375 |
-
$wpcdb->close( );
|
376 |
-
wpa_wpc_log( 'search and replace finished' );
|
377 |
-
return $report;
|
378 |
-
}
|
379 |
-
|
380 |
-
function wpa_wpc_temp_dir() {
|
381 |
-
|
382 |
-
global $wp_filesystem;
|
383 |
-
$temp_dir = trailingslashit( WPCLONE_WP_CONTENT ) . 'wpclone-temp';
|
384 |
-
$err = $wp_filesystem->mkdir( $temp_dir );
|
385 |
-
|
386 |
-
if ( is_wp_error( $err ) ) {
|
387 |
-
wpa_backup_error('dirrest', $err->get_error_message(), true );
|
388 |
-
}
|
389 |
-
|
390 |
-
$content = "<Files>\r\n\tOrder allow,deny\r\n\tDeny from all\r\n\tSatisfy all\r\n</Files>";
|
391 |
-
$file = trailingslashit( $temp_dir ) . '.htaccess';
|
392 |
-
$wp_filesystem->put_contents( $file, $content, 0644 );
|
393 |
-
|
394 |
-
return $temp_dir;
|
395 |
-
|
396 |
-
}
|
397 |
-
|
398 |
-
function processRestoringBackup($url, $zipmode) {
|
399 |
-
if( true === is_multisite() )
|
400 |
-
die( 'wpclone does not work on multisite installs.' );
|
401 |
-
|
402 |
-
wpa_cleanup( true );
|
403 |
-
if (!is_string($url) || '' == $url) {
|
404 |
-
wpa_backup_error( 'restore', sprintf( __( 'The provided URL "<code>%s</code>" is either not valid or empty' ), $url ), true );
|
405 |
-
}
|
406 |
-
|
407 |
-
global $wp_filesystem;
|
408 |
-
$GLOBALS['wpclone']['logfile'] = 'wpclone_restore_' . current_time( 'dS_M_Y_h-iA', false ) . '_' . wp_generate_password( 10, false ) . '.log';
|
409 |
-
|
410 |
-
wpa_wpc_log_start( 'restore' );
|
411 |
-
|
412 |
-
if( $zipmode ) {
|
413 |
-
define( 'PCLZIP_TEMPORARY_DIR', WPCLONE_DIR_BACKUP );
|
414 |
-
|
415 |
-
}
|
416 |
-
|
417 |
-
$temp_dir = wpa_wpc_temp_dir();
|
418 |
-
$site_url = site_url();
|
419 |
-
$permalink_url = admin_url( 'options-permalink.php' );
|
420 |
-
$zipfile = wpa_fetch_file($url);
|
421 |
-
$report = wpa_wpc_process_db( $zipfile, $zipmode );
|
422 |
-
$unzipped_folder = wpCloneSafePathMode( trailingslashit( $temp_dir ) . 'wpclone_backup' );
|
423 |
-
|
424 |
-
|
425 |
-
wpa_unzip( $zipfile, $temp_dir, $zipmode );
|
426 |
-
wpa_wpc_log( 'copying files..' );
|
427 |
-
wpa_copy( $unzipped_folder . '/wp-content', WPCLONE_WP_CONTENT );
|
428 |
-
|
429 |
-
wpa_wpc_log( 'deleting temp directory..' );
|
430 |
-
$wp_filesystem->delete( $temp_dir, true );
|
431 |
-
/* remove the zip file only if it was downloaded from an external location. */
|
432 |
-
$wptmp = explode( '.', $zipfile );
|
433 |
-
if ( in_array( 'tmp', $wptmp ) ) {
|
434 |
-
wpa_wpc_log( 'deleting downloaded zip file..' );
|
435 |
-
$wp_filesystem->delete( $zipfile );
|
436 |
-
}
|
437 |
-
|
438 |
-
wpa_wpc_log( 'restore finished' );
|
439 |
-
|
440 |
-
echo '<div class=""><h1>Restore Successful!</h1>';
|
441 |
-
printf( 'Visit your restored site [ <a href="%s" target=blank>here</a> ]<br><br>', $site_url );
|
442 |
-
printf( '<strong>You may need to re-save your permalink structure <a href="%s" target=blank>Here</a></strong>', $permalink_url );
|
443 |
-
printf( '</br><a href="%s">log</a>', convertPathIntoUrl( WPCLONE_DIR_BACKUP . $GLOBALS['wpclone']['logfile'] ) );
|
444 |
-
?>
|
445 |
-
<?php
|
446 |
-
unset( $GLOBALS['wpclone'] );
|
447 |
-
echo wpa_wpc_search_n_replace_report( $report );
|
448 |
-
|
449 |
-
?>
|
450 |
-
|
451 |
-
<style rel="stylesheet">
|
452 |
-
.banner-2-after-restore {
|
453 |
-
min-height: 280px;
|
454 |
-
width: auto;
|
455 |
-
background-size: cover;
|
456 |
-
padding-top: 24px;
|
457 |
-
padding-left: 25px;
|
458 |
-
padding-right: 25px;
|
459 |
-
font-family: 'Montserrat', sans-serif;
|
460 |
-
margin-right: 30px;
|
461 |
-
margin-top: 20px;
|
462 |
-
padding-bottom: 20px;
|
463 |
-
}
|
464 |
-
.banner-2-after-restore p{
|
465 |
-
font-size: 18px;
|
466 |
-
}
|
467 |
-
|
468 |
-
.banner-2-after-restore p a{
|
469 |
-
color: #3eb9b4;
|
470 |
-
text-decoration: none;
|
471 |
-
}
|
472 |
-
</style>
|
473 |
-
<div class="plugin-large-notice-restore-success">
|
474 |
-
<div class="banner-2-after-restore" style="background-image: url('<?php echo plugins_url( 'lib/img/banner_success_rating_bg.jpg', WPCLONE_ROOT_FILE_PATH )?>')">
|
475 |
-
<div style="float: right;" id="banner-2-restore-close-icon"><img src='<?php echo plugins_url( 'lib/img/banner_close_icon.png', WPCLONE_ROOT_FILE_PATH )?>'> </div>
|
476 |
-
<p>
|
477 |
-
Could you please do us a <strong>BIG favor</strong> and consider contributing to our crowdfunding effort <a target="_blank" href="https://sellcodes.com/q1OGuSox">here</a>? <br> <br>
|
478 |
-
|
479 |
-
<span style="text-decoration: underline">Background:</span> This is a great plugin, but it has been neglected for a long time, <br>
|
480 |
-
and we would like to change that, i.e. completely re-work the entire plugin, <br> make it 100% bug-free, and
|
481 |
-
add many more features (while still keeping it super-simple to use!) <br>. We're short on cash,
|
482 |
-
and need your help for that. Thank you!
|
483 |
-
<br><br>
|
484 |
-
|
485 |
-
<a href="https://sellcodes.com/q1OGuSox" target="_blank">=> Visit the crowdfunding page</a>
|
486 |
-
<br><br>
|
487 |
-
If something doesn’t work as it should, please <a href="https://wordpress.org/support/plugin/wp-clone-by-wp-academy/" target="_blank">ask us in the forum</a> . We’ll try to respond quickly!
|
488 |
-
</p>
|
489 |
-
</div>
|
490 |
-
</div>
|
491 |
-
|
492 |
-
<?php
|
493 |
-
}
|
494 |
-
|
495 |
-
function wpa_wpc_search_n_replace_report( $report ) {
|
496 |
-
|
497 |
-
if( is_string( $report ) ) {
|
498 |
-
return sprintf( '<div class="info"><p>%s</p></div>', $report );
|
499 |
-
}
|
500 |
-
|
501 |
-
$time = array_sum( explode( ' ', $report[ 'end' ] ) ) - array_sum( explode( ' ', $report[ 'start' ] ) );
|
502 |
-
$return = sprintf( '<div class="info"><p>Search and replace scanned <strong>%d</strong> tables with a total of <strong>%d</strong> rows. ' , $report['tables'], $report['rows'] );
|
503 |
-
$return .= sprintf( '<strong>%d</strong> cells were changed and <strong>%d</strong> db updates were performed in <strong>%f</strong> seconds.</p></div>', $report['change'], $report['updates'], $time );
|
504 |
-
|
505 |
-
if ( ! empty( $report['errors'] ) && is_array( $report['errors'] ) ) {
|
506 |
-
$return .= '<div>';
|
507 |
-
$return .= '<h4>search and replace returned the following errors.</h4>';
|
508 |
-
foreach( $report['errors'] as $error ) {
|
509 |
-
$return .= '<p class="error">' . $error . '</p>';
|
510 |
-
}
|
511 |
-
$return .= '</div>';
|
512 |
-
}
|
513 |
-
|
514 |
-
return $return;
|
515 |
-
|
516 |
-
}
|
517 |
-
|
518 |
-
function wpa_save_prefix($path) {
|
519 |
-
global $wpdb;
|
520 |
-
$prefix = $wpdb->prefix;
|
521 |
-
$file = $path . '/prefix.txt';
|
522 |
-
if ( is_dir($path) && is_writable($path) ) {
|
523 |
-
file_put_contents($file, $prefix);
|
524 |
-
}
|
525 |
-
}
|
526 |
-
/**
|
527 |
-
* Checks to see whether the destination site's table prefix matches that of the origin site.old prefix is returned in case of a mismatch.
|
528 |
-
*
|
529 |
-
* @param type $file path to the prefix.txt file.
|
530 |
-
* @return type bool string
|
531 |
-
*/
|
532 |
-
function wpa_check_prefix($file) {
|
533 |
-
global $wpdb;
|
534 |
-
$prefix = $wpdb->prefix;
|
535 |
-
if (file_exists($file) && is_readable($file)) {
|
536 |
-
$old_prefix = file_get_contents($file);
|
537 |
-
if ( $prefix !== $old_prefix ) {
|
538 |
-
return $old_prefix;
|
539 |
-
}
|
540 |
-
else {
|
541 |
-
return false;
|
542 |
-
}
|
543 |
-
}
|
544 |
-
return false;
|
545 |
-
}
|
546 |
-
|
547 |
-
/**
|
548 |
-
* @since 2.0.6
|
549 |
-
*
|
550 |
-
* @param type $zipfile path to the zip file that needs to be extracted.
|
551 |
-
* @param type $path the place to where the file needs to be extracted.
|
552 |
-
* @return as false in the event of failure.
|
553 |
-
*/
|
554 |
-
function wpa_unzip($zipfile, $path, $zipmode = false){
|
555 |
-
|
556 |
-
if ( $zipmode || ( ! in_array('ZipArchive', get_declared_classes() ) || ! class_exists( 'ZipArchive' ) ) ) {
|
557 |
-
|
558 |
-
wpa_wpc_log( 'extracting archive using pclzip' );
|
559 |
-
|
560 |
-
if ( ini_get('mbstring.func_overload') && function_exists('mb_internal_encoding') ) {
|
561 |
-
$previous_encoding = mb_internal_encoding();
|
562 |
-
mb_internal_encoding('ISO-8859-1');
|
563 |
-
}
|
564 |
-
|
565 |
-
require_once ( ABSPATH . 'wp-admin/includes/class-pclzip.php' );
|
566 |
-
$z = new PclZip($zipfile);
|
567 |
-
|
568 |
-
$files = $z->extract( PCLZIP_OPT_PATH, $path );
|
569 |
-
|
570 |
-
if ( isset( $previous_encoding ) ) {
|
571 |
-
mb_internal_encoding( $previous_encoding );
|
572 |
-
|
573 |
-
}
|
574 |
-
|
575 |
-
if ( $files === 0 ) {
|
576 |
-
wpa_backup_error( 'pclunzip', $z->errorInfo(true), true );
|
577 |
-
}
|
578 |
-
|
579 |
-
} else {
|
580 |
-
wpa_wpc_log( 'extracting archive using ziparchive' );
|
581 |
-
wpa_wpc_unzip( $zipfile, $path );
|
582 |
-
|
583 |
-
}
|
584 |
-
|
585 |
-
}
|
586 |
-
/**
|
587 |
-
* @since 2.0.6
|
588 |
-
*
|
589 |
-
* @param type $name name of the zip file.
|
590 |
-
* @param type $file_list an array of files that needs to be archived.
|
591 |
-
*/
|
592 |
-
function wpa_zip($zip_name, $folder, $zipmode = false){
|
593 |
-
if ( $zipmode || (!in_array('ZipArchive', get_declared_classes()) || !class_exists('ZipArchive')) ) {
|
594 |
-
wpa_wpc_log( 'archiving files using pclzip' );
|
595 |
-
$zipmode = true;
|
596 |
-
define('PCLZIP_TEMPORARY_DIR', WPCLONE_DIR_BACKUP);
|
597 |
-
require_once ( ABSPATH . 'wp-admin/includes/class-pclzip.php');
|
598 |
-
$z = new PclZip($zip_name);
|
599 |
-
$v_list = $z->create($folder, PCLZIP_OPT_REMOVE_PATH, WPCLONE_DIR_BACKUP );
|
600 |
-
if ($v_list == 0) {
|
601 |
-
wpa_backup_error( 'pclzip', $z->errorInfo(true) );
|
602 |
-
}
|
603 |
-
$file_list = wpa_wpc_zip( $z, $zipmode );
|
604 |
-
|
605 |
-
if( $file_list ) {
|
606 |
-
$z->add( $file_list, PCLZIP_OPT_REMOVE_PATH, WPCLONE_ROOT, PCLZIP_OPT_ADD_PATH, 'wpclone_backup' );
|
607 |
-
}
|
608 |
-
|
609 |
-
$z->delete( PCLZIP_OPT_BY_NAME, 'wpclone_backup/file.list' );
|
610 |
-
|
611 |
-
} else {
|
612 |
-
wpa_wpc_log( 'archiving files using ziparchive' );
|
613 |
-
$z = new ZipArchive();
|
614 |
-
if ( true !== $z->open( $zip_name, ZIPARCHIVE::CREATE ) ) {
|
615 |
-
wpa_backup_error( 'zip', $z->getStatusString() );
|
616 |
-
}
|
617 |
-
wpa_ziparc($z, $folder, WPCLONE_DIR_BACKUP);
|
618 |
-
|
619 |
-
wpa_wpc_zip( $z, $zipmode );
|
620 |
-
$z->deleteName( 'wpclone_backup/file.list' );
|
621 |
-
$z->close();
|
622 |
-
|
623 |
-
}
|
624 |
-
|
625 |
-
|
626 |
-
}
|
627 |
-
|
628 |
-
function wpa_ziparc($zip, $dir, $base) {
|
629 |
-
$new_folder = str_replace($base, '', $dir);
|
630 |
-
$zip->addEmptyDir($new_folder);
|
631 |
-
foreach( glob( $dir . '/*' ) as $file ){
|
632 |
-
if( is_dir($file) ) {
|
633 |
-
wpa_ziparc($zip, $file, $base);
|
634 |
-
} else {
|
635 |
-
$new_file = str_replace( $base, '', $file );
|
636 |
-
$zip->addFile($file, $new_file);
|
637 |
-
}
|
638 |
-
}
|
639 |
-
}
|
640 |
-
/**
|
641 |
-
* just a simple function to increase PHP limits.
|
642 |
-
* @since 2.0.6
|
643 |
-
*/
|
644 |
-
function wpa_bump_limits(){
|
645 |
-
$GLOBALS['wpclone'] = array();
|
646 |
-
$GLOBALS['wpclone']['time'] = isset( $_POST['maxexec'] ) && '' != $_POST['maxexec'] ? $_POST['maxexec'] : 600; /* 10 minutes */
|
647 |
-
$GLOBALS['wpclone']['mem'] = isset ( $_POST['maxmem'] ) && '' != $_POST['maxmem'] ? $_POST['maxmem'] . 'M' : '1024M';
|
648 |
-
|
649 |
-
@ini_set('memory_limit', $GLOBALS['wpclone']['mem']);
|
650 |
-
@ini_set('max_execution_time', $GLOBALS['wpclone']['time']);
|
651 |
-
@ini_set('mysql.connect_timeout', $GLOBALS['wpclone']['time']);
|
652 |
-
@ini_set('default_socket_timeout', $GLOBALS['wpclone']['time']);
|
653 |
-
}
|
654 |
-
|
655 |
-
/**
|
656 |
-
* @since 2.0.6
|
657 |
-
*/
|
658 |
-
function wpa_wpfs_init(){
|
659 |
-
if (!empty($_REQUEST['del'])) {
|
660 |
-
wpa_remove_backup();
|
661 |
-
return true;
|
662 |
-
}
|
663 |
-
if (empty($_POST)) return false;
|
664 |
-
check_admin_referer('wpclone-submit');
|
665 |
-
|
666 |
-
wpa_bump_limits();
|
667 |
-
|
668 |
-
if (isset($_POST['createBackup'])) {
|
669 |
-
wpa_create_backup();
|
670 |
-
return true;
|
671 |
-
}
|
672 |
-
|
673 |
-
$form_post = wp_nonce_url('admin.php?page=wp-clone', 'wpclone-submit');
|
674 |
-
$extra_fields = array( 'restore_from_url', 'maxmem', 'maxexec', 'zipmode', 'ignore_prefix', 'wipedb', 'mysql_check', 'restoreBackup', 'createBackup' );
|
675 |
-
$type = '';
|
676 |
-
if ( false === ($creds = request_filesystem_credentials($form_post, $type, false, false, $extra_fields)) ){
|
677 |
-
return true;
|
678 |
-
}
|
679 |
-
if (!WP_Filesystem($creds)) {
|
680 |
-
request_filesystem_credentials($form_post, $type, true, false, $extra_fields);
|
681 |
-
return true;
|
682 |
-
}
|
683 |
-
|
684 |
-
$zipmode = isset($_POST['zipmode']) ? true : false;
|
685 |
-
$url = isset($_POST['restoreBackup']) ? $_POST['restoreBackup'] : $_POST['restore_from_url'];
|
686 |
-
processRestoringBackup($url, $zipmode);
|
687 |
-
return true;
|
688 |
-
}
|
689 |
-
/**
|
690 |
-
* @since 2.0.6
|
691 |
-
*/
|
692 |
-
function wpa_copy($source, $target) {
|
693 |
-
global $wp_filesystem;
|
694 |
-
if (is_readable($source)) {
|
695 |
-
if (is_dir($source)) {
|
696 |
-
if (!file_exists($target)) {
|
697 |
-
$wp_filesystem->mkdir($target);
|
698 |
-
}
|
699 |
-
$d = dir($source);
|
700 |
-
while (FALSE !== ($entry = $d->read())) {
|
701 |
-
if ($entry == '.' || $entry == '..') {
|
702 |
-
continue;
|
703 |
-
}
|
704 |
-
$Entry = "{$source}/{$entry}";
|
705 |
-
if (is_dir($Entry)) {
|
706 |
-
wpa_copy($Entry, $target . '/' . $entry);
|
707 |
-
} else {
|
708 |
-
$wp_filesystem->copy($Entry, $target . '/' . $entry, true, FS_CHMOD_FILE);
|
709 |
-
}
|
710 |
-
}
|
711 |
-
$d->close();
|
712 |
-
}
|
713 |
-
else {
|
714 |
-
$wp_filesystem->copy($source, $target, true);
|
715 |
-
}
|
716 |
-
}
|
717 |
-
}
|
718 |
-
/**
|
719 |
-
* @since 2.0.6
|
720 |
-
*/
|
721 |
-
function wpa_replace_prefix( $current, $new ){
|
722 |
-
|
723 |
-
$wpconfig = wpa_wpconfig_path();
|
724 |
-
global $wp_filesystem;
|
725 |
-
|
726 |
-
if ( ! $wp_filesystem->is_writable($wpconfig) ) {
|
727 |
-
if( false === $wp_filesystem->chmod( $wpconfig ) )
|
728 |
-
wpa_backup_error('wpconfig', sprintf( __( "<code>%s</code> is not writable and wpclone was unable to change the file permissions." ), $wpconfig ), true );
|
729 |
-
|
730 |
-
}
|
731 |
-
|
732 |
-
$content = file( $wpconfig );
|
733 |
-
|
734 |
-
foreach( $content as $key => $value ) {
|
735 |
-
|
736 |
-
if( false !== strpos( $value, '$table_prefix' ) ) {
|
737 |
-
$content[$key] = str_replace( $current, $new, $value );
|
738 |
-
}
|
739 |
-
|
740 |
-
}
|
741 |
-
|
742 |
-
$content = implode( $content );
|
743 |
-
$wp_filesystem->put_contents( $wpconfig, $content, 0600 );
|
744 |
-
|
745 |
-
}
|
746 |
-
/**
|
747 |
-
* @since 2.0.6
|
748 |
-
*/
|
749 |
-
function wpa_create_backup (){
|
750 |
-
|
751 |
-
if( true === is_multisite() )
|
752 |
-
die( 'wpclone does not work on multisite installs.' );
|
753 |
-
if ( !file_exists(WPCLONE_DIR_BACKUP) ) {
|
754 |
-
wpa_create_directory();
|
755 |
-
}
|
756 |
-
wpa_cleanup();
|
757 |
-
$use_wpdb = isset( $_POST['use_wpdb'] ) && 'true' == $_POST['use_wpdb'] ? true : false;
|
758 |
-
$backupName = wpa_backup_name();
|
759 |
-
$GLOBALS['wpclone']['logfile'] = $backupName . '.log';
|
760 |
-
|
761 |
-
wpa_wpc_log_start( 'backup' );
|
762 |
-
|
763 |
-
$zipmode = isset($_POST['zipmode']) ? true : false;
|
764 |
-
list($zipFileName, $zipSize) = CreateWPFullBackupZip($backupName, $zipmode, $use_wpdb);
|
765 |
-
|
766 |
-
wpa_insert_data($zipFileName, $zipSize);
|
767 |
-
$backZipPath = convertPathIntoUrl(WPCLONE_DIR_BACKUP . $zipFileName . '.zip');
|
768 |
-
$zipSize = bytesToSize($zipSize);
|
769 |
-
wpa_wpc_log( 'backup finished');
|
770 |
-
|
771 |
-
echo <<<EOF
|
772 |
-
|
773 |
-
<h1>Backup Successful!</h1>
|
774 |
-
|
775 |
-
<br />
|
776 |
-
|
777 |
-
Here is your backup file : <br />
|
778 |
-
|
779 |
-
<a href='{$backZipPath}'><span>{$backZipPath}</span></a> ( {$zipSize} ) |
|
780 |
-
<input type='hidden' name='backupUrl' class='backupUrl' value="{$backZipPath}" />
|
781 |
-
<a class='copy-button' href='#' data-clipboard-text='{$backZipPath}'>Copy URL</a> <br /><br />
|
782 |
-
|
783 |
-
(Copy that link and paste it into the "Restore URL" of your new WordPress installation to clone this site)
|
784 |
-
EOF;
|
785 |
-
printf( '</br><a href="%s">log</a>', convertPathIntoUrl( WPCLONE_DIR_BACKUP . $GLOBALS['wpclone']['logfile'] ) );
|
786 |
-
unset( $GLOBALS['wpclone'] );
|
787 |
-
}
|
788 |
-
/**
|
789 |
-
* @since 2.0.6
|
790 |
-
*/
|
791 |
-
function wpa_remove_backup(){
|
792 |
-
check_admin_referer('wpclone-submit');
|
793 |
-
$deleteRow = DeleteWPBackupZip($_REQUEST['del']);
|
794 |
-
echo $deleteRow['msg'];
|
795 |
-
|
796 |
-
}
|
797 |
-
/**
|
798 |
-
* @since 2.1.2
|
799 |
-
* copypasta from wp-load.php
|
800 |
-
* @return the path to wp-config.php
|
801 |
-
*/
|
802 |
-
function wpa_wpconfig_path () {
|
803 |
-
|
804 |
-
if ( file_exists( ABSPATH . 'wp-config.php') ) {
|
805 |
-
|
806 |
-
/** The config file resides in ABSPATH */
|
807 |
-
return ABSPATH . 'wp-config.php';
|
808 |
-
|
809 |
-
}
|
810 |
-
elseif ( file_exists( dirname(ABSPATH) . '/wp-config.php' ) && ! file_exists( dirname(ABSPATH) . '/wp-settings.php' ) ) {
|
811 |
-
|
812 |
-
/** The config file resides one level above ABSPATH but is not part of another install */
|
813 |
-
return dirname(ABSPATH) . '/wp-config.php';
|
814 |
-
|
815 |
-
}
|
816 |
-
else {
|
817 |
-
|
818 |
-
return false;
|
819 |
-
|
820 |
-
}
|
821 |
-
|
822 |
-
}
|
823 |
-
|
824 |
-
function wcbwa_download_url_alternate( $url, $timeout = 300, $signature_verification = false ){
|
825 |
-
//WARNING: The file is not automatically deleted, The script must unlink() the file.
|
826 |
-
if ( ! $url ) {
|
827 |
-
return new WP_Error( 'http_no_url', __( 'Invalid URL Provided.' ) );
|
828 |
-
}
|
829 |
-
|
830 |
-
$url_filename = basename( parse_url( $url, PHP_URL_PATH ) );
|
831 |
-
|
832 |
-
$tmpfname = wp_tempnam( $url_filename );
|
833 |
-
if ( ! $tmpfname ) {
|
834 |
-
return new WP_Error( 'http_no_file', __( 'Could not create Temporary file.' ) );
|
835 |
-
}
|
836 |
-
|
837 |
-
$response = wp_remote_get(
|
838 |
-
$url,
|
839 |
-
array(
|
840 |
-
'timeout' => $timeout,
|
841 |
-
'stream' => true,
|
842 |
-
'filename' => $tmpfname,
|
843 |
-
)
|
844 |
-
);
|
845 |
-
|
846 |
-
if ( is_wp_error( $response ) ) {
|
847 |
-
unlink( $tmpfname );
|
848 |
-
return $response;
|
849 |
-
}
|
850 |
-
|
851 |
-
$response_code = wp_remote_retrieve_response_code( $response );
|
852 |
-
|
853 |
-
if ( 200 != $response_code ) {
|
854 |
-
$data = array(
|
855 |
-
'code' => $response_code,
|
856 |
-
);
|
857 |
-
|
858 |
-
// Retrieve a sample of the response body for debugging purposes.
|
859 |
-
$tmpf = fopen( $tmpfname, 'rb' );
|
860 |
-
if ( $tmpf ) {
|
861 |
-
/**
|
862 |
-
* Filters the maximum error response body size in `download_url()`.
|
863 |
-
*
|
864 |
-
* @since 5.1.0
|
865 |
-
*
|
866 |
-
* @see download_url()
|
867 |
-
*
|
868 |
-
* @param int $size The maximum error response body size. Default 1 KB.
|
869 |
-
*/
|
870 |
-
$response_size = apply_filters( 'download_url_error_max_body_size', KB_IN_BYTES );
|
871 |
-
$data['body'] = fread( $tmpf, $response_size );
|
872 |
-
fclose( $tmpf );
|
873 |
-
}
|
874 |
-
|
875 |
-
unlink( $tmpfname );
|
876 |
-
return new WP_Error( 'http_404', trim( wp_remote_retrieve_response_message( $response ) ), $data );
|
877 |
-
}
|
878 |
-
|
879 |
-
$content_md5 = wp_remote_retrieve_header( $response, 'content-md5' );
|
880 |
-
if ( $content_md5 ) {
|
881 |
-
$md5_check = verify_file_md5( $tmpfname, $content_md5 );
|
882 |
-
if ( is_wp_error( $md5_check ) ) {
|
883 |
-
unlink( $tmpfname );
|
884 |
-
return $md5_check;
|
885 |
-
}
|
886 |
-
}
|
887 |
-
|
888 |
-
// If the caller expects signature verification to occur, check to see if this URL supports it.
|
889 |
-
if ( $signature_verification ) {
|
890 |
-
/**
|
891 |
-
* Filters the list of hosts which should have Signature Verification attempteds on.
|
892 |
-
*
|
893 |
-
* @since 5.2.0
|
894 |
-
*
|
895 |
-
* @param array List of hostnames.
|
896 |
-
*/
|
897 |
-
$signed_hostnames = apply_filters( 'wp_signature_hosts', array( 'wordpress.org', 'downloads.wordpress.org', 's.w.org' ) );
|
898 |
-
$signature_verification = in_array( parse_url( $url, PHP_URL_HOST ), $signed_hostnames, true );
|
899 |
-
}
|
900 |
-
|
901 |
-
// Perform signature valiation if supported.
|
902 |
-
if ( $signature_verification ) {
|
903 |
-
$signature = wp_remote_retrieve_header( $response, 'x-content-signature' );
|
904 |
-
if ( ! $signature ) {
|
905 |
-
// Retrieve signatures from a file if the header wasn't included.
|
906 |
-
// WordPress.org stores signatures at $package_url.sig
|
907 |
-
|
908 |
-
$signature_url = false;
|
909 |
-
$url_path = parse_url( $url, PHP_URL_PATH );
|
910 |
-
if ( substr( $url_path, -4 ) == '.zip' || substr( $url_path, -7 ) == '.tar.gz' ) {
|
911 |
-
$signature_url = str_replace( $url_path, $url_path . '.sig', $url );
|
912 |
-
}
|
913 |
-
|
914 |
-
/**
|
915 |
-
* Filter the URL where the signature for a file is located.
|
916 |
-
*
|
917 |
-
* @since 5.2.0
|
918 |
-
*
|
919 |
-
* @param false|string $signature_url The URL where signatures can be found for a file, or false if none are known.
|
920 |
-
* @param string $url The URL being verified.
|
921 |
-
*/
|
922 |
-
$signature_url = apply_filters( 'wp_signature_url', $signature_url, $url );
|
923 |
-
|
924 |
-
if ( $signature_url ) {
|
925 |
-
$signature_request = wp_safe_remote_get(
|
926 |
-
$signature_url,
|
927 |
-
array(
|
928 |
-
'limit_response_size' => 10 * 1024, // 10KB should be large enough for quite a few signatures.
|
929 |
-
)
|
930 |
-
);
|
931 |
-
|
932 |
-
if ( ! is_wp_error( $signature_request ) && 200 === wp_remote_retrieve_response_code( $signature_request ) ) {
|
933 |
-
$signature = explode( "\n", wp_remote_retrieve_body( $signature_request ) );
|
934 |
-
}
|
935 |
-
}
|
936 |
-
}
|
937 |
-
|
938 |
-
// Perform the checks.
|
939 |
-
$signature_verification = verify_file_signature( $tmpfname, $signature, basename( parse_url( $url, PHP_URL_PATH ) ) );
|
940 |
-
}
|
941 |
-
|
942 |
-
if ( is_wp_error( $signature_verification ) ) {
|
943 |
-
if (
|
944 |
-
/**
|
945 |
-
* Filters whether Signature Verification failures should be allowed to soft fail.
|
946 |
-
*
|
947 |
-
* WARNING: This may be removed from a future release.
|
948 |
-
*
|
949 |
-
* @since 5.2.0
|
950 |
-
*
|
951 |
-
* @param bool $signature_softfail If a softfail is allowed.
|
952 |
-
* @param string $url The url being accessed.
|
953 |
-
*/
|
954 |
-
apply_filters( 'wp_signature_softfail', true, $url )
|
955 |
-
) {
|
956 |
-
$signature_verification->add_data( $tmpfname, 'softfail-filename' );
|
957 |
-
} else {
|
958 |
-
// Hard-fail.
|
959 |
-
unlink( $tmpfname );
|
960 |
-
}
|
961 |
-
|
962 |
-
return $signature_verification;
|
963 |
-
}
|
964 |
-
|
965 |
-
return $tmpfname;
|
966 |
-
}
|
967 |
-
|
968 |
-
function wpa_fetch_file($path){
|
969 |
-
$z = pathinfo($path);
|
970 |
-
global $wp_filesystem;
|
971 |
-
if ( $wp_filesystem->is_file(WPCLONE_DIR_BACKUP . $z['basename']) ) {
|
972 |
-
wpa_wpc_log( 'file exists in the backup folder, filesize - ' . bytesToSize( filesize( WPCLONE_DIR_BACKUP . $z['basename'] ) ) );
|
973 |
-
return WPCLONE_DIR_BACKUP . $z['basename'];
|
974 |
-
}
|
975 |
-
else {
|
976 |
-
wpa_wpc_log( 'file download started' );
|
977 |
-
$url = wcbwa_download_url_alternate($path, ini_get("max_execution_time"));
|
978 |
-
//$url = download_url($path);
|
979 |
-
if ( is_wp_error($url) ) {
|
980 |
-
wpa_backup_error( 'url', $url->get_error_message(), true );
|
981 |
-
}
|
982 |
-
wpa_wpc_log( 'download finished, filesize - ' . bytesToSize( filesize( $url ) ) );
|
983 |
-
return $url;
|
984 |
-
}
|
985 |
-
}
|
986 |
-
|
987 |
-
function wpa_backup_name() {
|
988 |
-
$backup_name = 'wpclone_backup_' . current_time( 'dS_M_Y_h-iA', false ) . '_' . get_option( 'blogname' );
|
989 |
-
$backup_name = substr( str_replace( ' ', '', $backup_name ), 0, 40 );
|
990 |
-
$rand_str = substr( str_shuffle( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" ), 0, 10 );
|
991 |
-
$backup_name = sanitize_file_name( $backup_name ) . '_' . $rand_str;
|
992 |
-
return $backup_name;
|
993 |
-
}
|
994 |
-
|
995 |
-
function wpa_backup_error($error, $data, $restore = false) {
|
996 |
-
|
997 |
-
$temp_dir = $restore ? trailingslashit( WPCLONE_WP_CONTENT ) . 'wpclone-temp' : trailingslashit( WPCLONE_DIR_BACKUP ) . 'wpclone_backup';
|
998 |
-
$disp_dir = str_replace( WPCLONE_ROOT, '**SITE-ROOT**/', wpCloneSafePathMode( $temp_dir ) );
|
999 |
-
|
1000 |
-
if( !file_exists( $temp_dir ) ) {
|
1001 |
-
unset($temp_dir);
|
1002 |
-
}
|
1003 |
-
|
1004 |
-
switch ( $error ) :
|
1005 |
-
/* during backup */
|
1006 |
-
case 'file' :
|
1007 |
-
$error = __( 'while copying files into the temp directory' );
|
1008 |
-
break;
|
1009 |
-
case 'db' :
|
1010 |
-
$error = __( 'during the database backup' );
|
1011 |
-
break;
|
1012 |
-
case 'zip' :
|
1013 |
-
$error = __( 'while creating the zip file using PHP\'s ZipArchive library' );
|
1014 |
-
break;
|
1015 |
-
case 'pclzip' :
|
1016 |
-
$error = __( 'while creating the zip file using the PclZip library' );
|
1017 |
-
break;
|
1018 |
-
/* during restore */
|
1019 |
-
case 'dirrest' :
|
1020 |
-
$error = __( 'while creating the temp directory' );
|
1021 |
-
break;
|
1022 |
-
case 'filerest' :
|
1023 |
-
$error = __( 'while copying files from the temp directory into the wp-content directory' );
|
1024 |
-
break;
|
1025 |
-
case 'dbrest' :
|
1026 |
-
$error = __( 'while cloning the database' );
|
1027 |
-
break;
|
1028 |
-
case 'unzip' :
|
1029 |
-
$error = __( 'while extracting the zip file using php ziparchive' );
|
1030 |
-
break;
|
1031 |
-
case 'pclunzip' :
|
1032 |
-
$error = __( 'while extracting the zip file using the PclZip library' );
|
1033 |
-
break;
|
1034 |
-
case 'url' :
|
1035 |
-
$error = __( 'while downloading the zip file' );
|
1036 |
-
break;
|
1037 |
-
case 'wpconfig' :
|
1038 |
-
$error = __( 'while trying to modify the table prefix in the wp-config.php file' );
|
1039 |
-
break;
|
1040 |
-
/* and a catch all for the things that aren't covered above */
|
1041 |
-
default :
|
1042 |
-
$error = sprintf( __( 'during the %s process' ), $error );
|
1043 |
-
endswitch;
|
1044 |
-
|
1045 |
-
echo '<div class="wpclone_notice updated">';
|
1046 |
-
printf( __( 'The plugin encountered an error %s,the following error message was returned:</br>' ), $error );
|
1047 |
-
echo '<div class="error">' . __( 'Error Message : ' ) . $data . '</div></br>';
|
1048 |
-
if( isset( $temp_dir ) ) {
|
1049 |
-
printf( __( 'Temporary files created in <code>%s</code> will be deleted.' ), $disp_dir );
|
1050 |
-
echo '</div>';
|
1051 |
-
if( $restore ) {
|
1052 |
-
global $wp_filesystem;
|
1053 |
-
$wp_filesystem->delete($temp_dir, true);
|
1054 |
-
} else {
|
1055 |
-
wpa_delete_dir( $temp_dir );
|
1056 |
-
}
|
1057 |
-
} else {
|
1058 |
-
echo '</div>';
|
1059 |
-
}
|
1060 |
-
die;
|
1061 |
-
}
|
1062 |
-
|
1063 |
-
function wpa_cleanup( $restore = false ) {
|
1064 |
-
$backup_dir = $restore ? trailingslashit( WPCLONE_WP_CONTENT ) . 'wpclone-temp' : trailingslashit( WPCLONE_DIR_BACKUP ) . 'wpclone_backup';
|
1065 |
-
if ( file_exists( $backup_dir ) && is_dir( $backup_dir ) ) {
|
1066 |
-
if( $restore ) {
|
1067 |
-
global $wp_filesystem;
|
1068 |
-
$wp_filesystem->delete($backup_dir, true);
|
1069 |
-
} else {
|
1070 |
-
wpa_delete_dir( $backup_dir );
|
1071 |
-
}
|
1072 |
-
}
|
1073 |
-
}
|
1074 |
-
/**
|
1075 |
-
* recursively copies a directory from one place to another. excludes 'uploads/wp-clone' by default.
|
1076 |
-
* @since 2.1.6
|
1077 |
-
* @param string $from
|
1078 |
-
* @param string $to
|
1079 |
-
* @param array $exclude an array of directory paths to exclude.
|
1080 |
-
*/
|
1081 |
-
function wpa_copy_dir( $from, $to, $exclude ) {
|
1082 |
-
if( false === stripos( wpCloneSafePathMode( $from ), rtrim( wpCloneSafePathMode( WPCLONE_DIR_BACKUP ), "/\\" ) ) ) {
|
1083 |
-
if( !file_exists( $to ) )
|
1084 |
-
@mkdir ( $to );
|
1085 |
-
$files = array_diff( scandir( $from ), array( '.', '..' ) );
|
1086 |
-
foreach( $files as $file ) {
|
1087 |
-
if( in_array( $from . '/' . $file, $exclude ) ) {
|
1088 |
-
continue;
|
1089 |
-
} else {
|
1090 |
-
if( is_dir( $from . '/' . $file ) ) {
|
1091 |
-
wpa_copy_dir( $from . '/' . $file, $to . '/' . $file, $exclude );
|
1092 |
-
} else {
|
1093 |
-
@copy( $from . '/' . $file, $to . '/' . $file );
|
1094 |
-
}
|
1095 |
-
}
|
1096 |
-
}
|
1097 |
-
unset( $files );
|
1098 |
-
}
|
1099 |
-
}
|
1100 |
-
/**
|
1101 |
-
* recursively deletes all the files in the given directory.
|
1102 |
-
* @since 2.1.6
|
1103 |
-
* @param string $dir path to the directory that needs to be deleted.
|
1104 |
-
*/
|
1105 |
-
function wpa_delete_dir( $dir ) {
|
1106 |
-
if( !empty( $dir ) ) {
|
1107 |
-
$dir = trailingslashit( $dir );
|
1108 |
-
$files = array_diff( scandir( $dir ), array( '.', '..' ) );
|
1109 |
-
foreach ( $files as $file ) {
|
1110 |
-
if( is_dir( $dir . $file ) ) {
|
1111 |
-
wpa_delete_dir( $dir . $file );
|
1112 |
-
} else {
|
1113 |
-
@unlink( $dir . $file );
|
1114 |
-
}
|
1115 |
-
}
|
1116 |
-
@rmdir($dir);
|
1117 |
-
}
|
1118 |
-
}
|
1119 |
-
/**
|
1120 |
-
* @since 2.1.6
|
1121 |
-
*/
|
1122 |
-
function wpa_excluded_dirs() {
|
1123 |
-
$exclude = array();
|
1124 |
-
if( isset( $_POST['exclude'] ) && '' != $_POST['exclude'] ) {
|
1125 |
-
foreach( explode( "\n", $_POST['exclude'] ) as $ex ) {
|
1126 |
-
$ex = trim( $ex );
|
1127 |
-
if( '' !== $ex ) {
|
1128 |
-
$ex = trim( $ex, "/\\" );
|
1129 |
-
wpa_wpc_log( sprintf( 'files inside "**SITE_ROOT**/wp-content/%s/" will not be included in the backup', $ex ) );
|
1130 |
-
$exclude[] = wpCloneSafePathMode( trailingslashit( WPCLONE_WP_CONTENT ) . $ex ) ;
|
1131 |
-
}
|
1132 |
-
}
|
1133 |
-
}
|
1134 |
-
return $exclude;
|
1135 |
-
}
|
1136 |
-
|
1137 |
-
function wpa_wpc_dir_size( $path ) {
|
1138 |
-
|
1139 |
-
$i = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $path ) );
|
1140 |
-
$size = 0;
|
1141 |
-
$files = 0;
|
1142 |
-
|
1143 |
-
foreach( $i as $file => $info ) {
|
1144 |
-
|
1145 |
-
if( false === strpos( wpCloneSafePathMode( $file ), WPCLONE_DIR_BACKUP ) ) {
|
1146 |
-
$size += $info->getSize();
|
1147 |
-
$files++;
|
1148 |
-
|
1149 |
-
}
|
1150 |
-
|
1151 |
-
}
|
1152 |
-
|
1153 |
-
$ret = array(
|
1154 |
-
'dbsize' => wpa_wpc_db_size(),
|
1155 |
-
'size' => bytesToSize( $size ),
|
1156 |
-
'files' => $files,
|
1157 |
-
'time' => time()
|
1158 |
-
);
|
1159 |
-
|
1160 |
-
update_option( 'wpclone_directory_scan', $ret );
|
1161 |
-
unset( $ret['time'] );
|
1162 |
-
return $ret;
|
1163 |
-
|
1164 |
-
}
|
1165 |
-
|
1166 |
-
function wpa_wpc_db_size() {
|
1167 |
-
|
1168 |
-
global $wpdb;
|
1169 |
-
$sql = 'SELECT sum(data_length + index_length) FROM information_schema.TABLES WHERE table_schema = "' . DB_NAME . '"';
|
1170 |
-
$size = $wpdb->get_var( $sql );
|
1171 |
-
return bytesToSize( $size );
|
1172 |
-
|
1173 |
-
}
|
1174 |
-
|
1175 |
-
function wpa_wpc_scan_dir() {
|
1176 |
-
|
1177 |
-
$backups = get_option( 'wpclone_backups' );
|
1178 |
-
$backup_list = array();
|
1179 |
-
$files = array();
|
1180 |
-
$old_backups = array();
|
1181 |
-
|
1182 |
-
foreach( glob( WPCLONE_DIR_BACKUP . '*.zip' ) as $file ){
|
1183 |
-
|
1184 |
-
$files[] = str_replace( WPCLONE_DIR_BACKUP, '', $file );
|
1185 |
-
|
1186 |
-
}
|
1187 |
-
|
1188 |
-
if( false === $backups ) {
|
1189 |
-
$backups = array();
|
1190 |
-
}
|
1191 |
-
|
1192 |
-
foreach( $backups as $key => $backup ) {
|
1193 |
-
|
1194 |
-
if( ! file_exists( WPCLONE_DIR_BACKUP . $backup['name'] ) ) {
|
1195 |
-
unset( $backups[$key] );
|
1196 |
-
continue;
|
1197 |
-
}
|
1198 |
-
$backup_list[] = $backup['name'];
|
1199 |
-
|
1200 |
-
}
|
1201 |
-
|
1202 |
-
|
1203 |
-
$list = wpa_wpc_filter_list( $files, $backup_list );
|
1204 |
-
|
1205 |
-
if( ! empty( $list ) ) {
|
1206 |
-
|
1207 |
-
foreach( $list as $backup ) {
|
1208 |
-
|
1209 |
-
$time = strtotime( substr( str_replace( array( 'wpclone_backup_', '_', '-' ), array( '', ' ', ':' ), $backup ), 0, 21 ) ) + rand(1, 60);
|
1210 |
-
$old_backups[$time] = array(
|
1211 |
-
'name' => $backup,
|
1212 |
-
'creator' => 'dirscan',
|
1213 |
-
'size' => @filesize( WPCLONE_DIR_BACKUP . $backup )
|
1214 |
-
);
|
1215 |
-
|
1216 |
-
}
|
1217 |
-
|
1218 |
-
}
|
1219 |
-
|
1220 |
-
$backups = $backups + $old_backups;
|
1221 |
-
ksort( $backups );
|
1222 |
-
update_option( 'wpclone_backups', $backups );
|
1223 |
-
|
1224 |
-
}
|
1225 |
-
|
1226 |
-
/*
|
1227 |
-
* @link http://stackoverflow.com/questions/2479963/how-does-array-diff-work/6700430#6700430
|
1228 |
-
*/
|
1229 |
-
function wpa_wpc_filter_list( $a, $b ) {
|
1230 |
-
|
1231 |
-
$return = array();
|
1232 |
-
foreach( $a as $v ) {
|
1233 |
-
$return[$v] = '';
|
1234 |
-
}
|
1235 |
-
foreach( $b as $v ) {
|
1236 |
-
unset( $return[$v] );
|
1237 |
-
}
|
1238 |
-
return array_keys( $return );
|
1239 |
-
|
1240 |
-
}
|
1241 |
-
|
1242 |
-
function wpa_wpc_log( $msg ) {
|
1243 |
-
|
1244 |
-
if( ! isset( $GLOBALS['wpclone']['logfile'] ) ) {
|
1245 |
-
return;
|
1246 |
-
}
|
1247 |
-
$file = WPCLONE_DIR_BACKUP . $GLOBALS['wpclone']['logfile'];
|
1248 |
-
$time = date( 'l, d-M-Y H:i:s', time() + ( get_option( 'gmt_offset' ) * HOUR_IN_SECONDS ) );
|
1249 |
-
$msg = $time . ' - ' . $msg . "\r\n";
|
1250 |
-
file_put_contents( $file, $msg, FILE_APPEND );
|
1251 |
-
|
1252 |
-
}
|
1253 |
-
|
1254 |
-
function wpa_wpc_log_start( $action ) {
|
1255 |
-
|
1256 |
-
global $wp_version;
|
1257 |
-
global $wpdb;
|
1258 |
-
|
1259 |
-
wpa_wpc_log( sprintf( '%s started', $action ) );
|
1260 |
-
wpa_wpc_log( 'wp version : ' . $wp_version );
|
1261 |
-
wpa_wpc_log( 'php version : ' . phpversion() );
|
1262 |
-
wpa_wpc_log( 'mysql version : ' . $wpdb->db_version() );
|
1263 |
-
wpa_wpc_log( 'memory limit : ' . ini_get( 'memory_limit' ) );
|
1264 |
-
wpa_wpc_log( 'execution time : ' . ini_get( 'max_execution_time' ) );
|
1265 |
-
wpa_wpc_log( 'mysql timeout : ' . ini_get( 'mysql.connect_timeout' ) );
|
1266 |
-
wpa_wpc_log( 'socket timeout : ' . ini_get( 'default_socket_timeout' ) );
|
1267 |
-
|
1268 |
-
}
|
1269 |
-
|
1270 |
-
function wpa_wpc_strpos_array( $array, $haystack ) {
|
1271 |
-
|
1272 |
-
foreach( $array as $needle ) {
|
1273 |
-
if( false !== strpos( $haystack, $needle ) ) {
|
1274 |
-
return true;
|
1275 |
-
|
1276 |
-
}
|
1277 |
-
|
1278 |
-
}
|
1279 |
-
|
1280 |
-
}
|
1281 |
-
|
1282 |
-
function wpa_wpc_get_filelist( $path, $exclude, $skip = false ) {
|
1283 |
-
|
1284 |
-
$i = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $path, FilesystemIterator::CURRENT_AS_SELF | FilesystemIterator::UNIX_PATHS | FilesystemIterator::SKIP_DOTS ) );
|
1285 |
-
$skipped = 0;
|
1286 |
-
$size = 0;
|
1287 |
-
$files = 0;
|
1288 |
-
$list = array();
|
1289 |
-
|
1290 |
-
foreach( $i as $file => $info ) {
|
1291 |
-
|
1292 |
-
$file = wpCloneSafePathMode( $file );
|
1293 |
-
|
1294 |
-
if( false !== strpos( $file, WPCLONE_DIR_BACKUP ) ) {
|
1295 |
-
continue;
|
1296 |
-
}
|
1297 |
-
|
1298 |
-
if( false !== strpos( $file, WPCLONE_DIR_PLUGIN ) ) {
|
1299 |
-
continue;
|
1300 |
-
}
|
1301 |
-
|
1302 |
-
if( ! $info->isReadable() ) {
|
1303 |
-
$skipped++;
|
1304 |
-
wpa_wpc_log( sprintf( 'file skipped, file is not readable - "%s"',
|
1305 |
-
str_replace( WPCLONE_ROOT, '**SITE-ROOT**/', $file ) ) );
|
1306 |
-
continue;
|
1307 |
-
|
1308 |
-
}
|
1309 |
-
|
1310 |
-
if( ! empty( $exclude ) && wpa_wpc_strpos_array( $exclude, $file ) ) {
|
1311 |
-
$skipped++;
|
1312 |
-
wpa_wpc_log( sprintf( 'file is inside an excluded directory, and it will not be included in the backup - "%s"',
|
1313 |
-
str_replace( WPCLONE_ROOT, '**SITE-ROOT**/', $file ) ) );
|
1314 |
-
continue;
|
1315 |
-
|
1316 |
-
}
|
1317 |
-
|
1318 |
-
if( $skip && $info->getSize() > $skip ) {
|
1319 |
-
$skipped++;
|
1320 |
-
wpa_wpc_log( sprintf( 'file skipped, file is larger than %s - "%s" %s',
|
1321 |
-
bytesToSize( $skip ), str_replace( WPCLONE_ROOT, '**SITE-ROOT**/', $file ), bytesToSize( $info->getSize() ) ) );
|
1322 |
-
continue;
|
1323 |
-
|
1324 |
-
}
|
1325 |
-
|
1326 |
-
if( $info->isFile() ) {
|
1327 |
-
$list[] = $file;
|
1328 |
-
$files++;
|
1329 |
-
$size += $info->getSize();
|
1330 |
-
|
1331 |
-
}
|
1332 |
-
|
1333 |
-
}
|
1334 |
-
|
1335 |
-
if( $skipped > 0 ) {
|
1336 |
-
wpa_wpc_log( sprintf( '%d files were excluded from the backup', $skipped ) );
|
1337 |
-
}
|
1338 |
-
|
1339 |
-
wpa_wpc_log( sprintf( 'number of files to include in the archive is %d, and their uncompressed size is %s',
|
1340 |
-
$files, bytesToSize( $size ) ) );
|
1341 |
-
|
1342 |
-
return $list;
|
1343 |
-
|
1344 |
-
}
|
1345 |
-
|
1346 |
-
|
1347 |
-
function wpa_wpc_zip( $zh, $zipmode ) {
|
1348 |
-
|
1349 |
-
$file = WPCLONE_DIR_BACKUP . 'wpclone_backup/file.list';
|
1350 |
-
|
1351 |
-
if( is_readable( $file ) ) {
|
1352 |
-
$filelist = unserialize( file_get_contents( $file ) );
|
1353 |
-
|
1354 |
-
} else {
|
1355 |
-
return false;
|
1356 |
-
|
1357 |
-
}
|
1358 |
-
|
1359 |
-
if( $zipmode ) {
|
1360 |
-
return $filelist;
|
1361 |
-
|
1362 |
-
}
|
1363 |
-
|
1364 |
-
foreach( $filelist as $file ) {
|
1365 |
-
$zh->addFile( $file, str_replace( WPCLONE_ROOT, 'wpclone_backup/', $file ) );
|
1366 |
-
|
1367 |
-
}
|
1368 |
-
|
1369 |
-
$zh->deleteName( 'wpclone_backup/file.list' );
|
1370 |
-
|
1371 |
-
}
|
1372 |
-
|
1373 |
-
function wpa_wpc_unzip( $zipfile, $temp_dir ) {
|
1374 |
-
|
1375 |
-
$z = new ZipArchive();
|
1376 |
-
|
1377 |
-
if( true === $z->open( $zipfile ) ) {
|
1378 |
-
$z->extractTo( $temp_dir );
|
1379 |
-
|
1380 |
-
} else {
|
1381 |
-
wpa_wpc_log( sprintf( 'failed to open the zip file : %s', $z->getStatusString() ) );
|
1382 |
-
wpa_backup_error( 'unzip', $z->getStatusString(), true );
|
1383 |
-
|
1384 |
-
}
|
1385 |
-
|
1386 |
-
}
|
1387 |
-
|
1388 |
-
function wpa_wpc_get_db( $zipfile, $zipmode ) {
|
1389 |
-
$ret = array();
|
1390 |
-
if( $zipmode || ( ! in_array( 'ZipArchive', get_declared_classes() ) || ! class_exists( 'ZipArchive' ) ) ) {
|
1391 |
-
|
1392 |
-
wpa_wpc_log( 'extracting database using pclzip' );
|
1393 |
-
|
1394 |
-
if ( ini_get('mbstring.func_overload') && function_exists('mb_internal_encoding') ) {
|
1395 |
-
$previous_encoding = mb_internal_encoding();
|
1396 |
-
mb_internal_encoding('ISO-8859-1');
|
1397 |
-
}
|
1398 |
-
|
1399 |
-
require_once ( ABSPATH . 'wp-admin/includes/class-pclzip.php' );
|
1400 |
-
$z = new PclZip($zipfile);
|
1401 |
-
$database = $z->extract( PCLZIP_OPT_BY_NAME, 'wpclone_backup/database.sql', PCLZIP_OPT_EXTRACT_AS_STRING );
|
1402 |
-
$prefix = $z->extract( PCLZIP_OPT_BY_NAME, 'wpclone_backup/prefix.txt', PCLZIP_OPT_EXTRACT_AS_STRING );
|
1403 |
-
|
1404 |
-
if ( isset( $previous_encoding ) ) {
|
1405 |
-
mb_internal_encoding($previous_encoding);
|
1406 |
-
|
1407 |
-
}
|
1408 |
-
|
1409 |
-
if( 'ok' === $database[0]['status'] && 'ok' === $prefix[0]['status'] ) {
|
1410 |
-
$ret['database'] = $database[0]['content'];
|
1411 |
-
$ret['prefix'] = $prefix[0]['content'];
|
1412 |
-
|
1413 |
-
} else {
|
1414 |
-
wpa_backup_error( 'pclunzip', $z->errorInfo(true), true );
|
1415 |
-
|
1416 |
-
}
|
1417 |
-
|
1418 |
-
return $ret;
|
1419 |
-
|
1420 |
-
} else {
|
1421 |
-
|
1422 |
-
$z = new ZipArchive();
|
1423 |
-
|
1424 |
-
if( true === $z->open( $zipfile ) ) {
|
1425 |
-
|
1426 |
-
wpa_wpc_log( 'extracting database using ziparchive' );
|
1427 |
-
$ret['database'] = $z->getFromName( 'wpclone_backup/database.sql' );
|
1428 |
-
$ret['prefix'] = $z->getFromName( 'wpclone_backup/prefix.txt' );
|
1429 |
-
if( false === $ret['database'] || false === $ret['prefix'] ) {
|
1430 |
-
wpa_backup_error( 'unzip', $z->getStatusString(), true );
|
1431 |
-
|
1432 |
-
}
|
1433 |
-
|
1434 |
-
$z->close();
|
1435 |
-
return $ret;
|
1436 |
-
|
1437 |
-
} else {
|
1438 |
-
wpa_backup_error( 'unzip', $z->getStatusString(), true );
|
1439 |
-
|
1440 |
-
}
|
1441 |
-
|
1442 |
-
}
|
1443 |
-
|
1444 |
-
}
|
1445 |
-
|
1446 |
-
function wpa_wpc_process_db( $zipfile, $zipmode = false ) {
|
1447 |
-
|
1448 |
-
$files = wpa_wpc_get_db( $zipfile, $zipmode );
|
1449 |
-
|
1450 |
-
$prefix = wpa_wpc_get_prefix( $files['prefix'] );
|
1451 |
-
$old_url = untrailingslashit( wpa_wpc_get_url( $files['database'] ) );
|
1452 |
-
$cur_url = untrailingslashit( site_url() );
|
1453 |
-
$found = false;
|
1454 |
-
$db = explode( ";\n", $files['database'] );
|
1455 |
-
$wpcdb = wpa_wpc_mysql_connect();
|
1456 |
-
|
1457 |
-
wpa_wpc_log( 'database import started' );
|
1458 |
-
foreach( $db as $query ) {
|
1459 |
-
|
1460 |
-
if( ! $found && false !== strpos( $query, '"siteurl",' ) ) {
|
1461 |
-
$query = str_replace( $old_url, $cur_url, $query, $count );
|
1462 |
-
wpa_wpc_log( sprintf( 'updating mysql query with current site\'s url - new query : "%s"', ltrim( $query ) ) );
|
1463 |
-
if( $count > 0 ) {
|
1464 |
-
$found = true;
|
1465 |
-
|
1466 |
-
}
|
1467 |
-
|
1468 |
-
}
|
1469 |
-
|
1470 |
-
if( isset( $_POST['mysql_check'] ) && 'true' === $_POST['mysql_check'] ) {
|
1471 |
-
if( ! $wpcdb->ping() ) {
|
1472 |
-
$wpcdb->close();
|
1473 |
-
$wpcdb = wpa_wpc_mysql_connect();
|
1474 |
-
}
|
1475 |
-
}
|
1476 |
-
|
1477 |
-
$status = $wpcdb->query( $query );
|
1478 |
-
|
1479 |
-
if( false === $status ) {
|
1480 |
-
wpa_wpc_log( sprintf( 'mysql query failed. error : %d %s - query : "%s"', $wpcdb->errno(), $wpcdb->error(), ltrim( $query ) ) );
|
1481 |
-
}
|
1482 |
-
|
1483 |
-
}
|
1484 |
-
wpa_wpc_log( 'database import finished' );
|
1485 |
-
|
1486 |
-
if( $cur_url === $old_url ) {
|
1487 |
-
wpa_wpc_log( 'URLs are similar, skipping search and replace' );
|
1488 |
-
$report = 'Search and replace did not run because the URLs are similar';
|
1489 |
-
|
1490 |
-
} else {
|
1491 |
-
$report = wpa_safe_replace_wrapper( $old_url, $cur_url, $prefix );
|
1492 |
-
|
1493 |
-
}
|
1494 |
-
|
1495 |
-
return $report;
|
1496 |
-
|
1497 |
-
|
1498 |
-
}
|
1499 |
-
|
1500 |
-
function wpa_wpc_get_prefix( $prev_prefix ) {
|
1501 |
-
|
1502 |
-
global $wpdb;
|
1503 |
-
$cur_prefix = $wpdb->prefix;
|
1504 |
-
|
1505 |
-
if ( $cur_prefix !== $prev_prefix ) {
|
1506 |
-
wpa_wpc_log( sprintf( 'changing prefix from "%s" to "%s"', $cur_prefix, $prev_prefix ) );
|
1507 |
-
wpa_replace_prefix( $cur_prefix, $prev_prefix );
|
1508 |
-
$cur_prefix = $prev_prefix;
|
1509 |
-
|
1510 |
-
}
|
1511 |
-
|
1512 |
-
return $cur_prefix;
|
1513 |
-
|
1514 |
-
}
|
1515 |
-
|
1516 |
-
|
1517 |
/* end of file */
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* wp-clone-by-wp-academy Wordpress plugin to backup and migrate Wordpress website.
|
4 |
+
*
|
5 |
+
* @URI https://wordpress.org/plugins/wp-clone-by-wp-academy
|
6 |
+
*
|
7 |
+
* @developed by Shaharia Azam <mail@shaharia.com>
|
8 |
+
*/
|
9 |
+
function wpCloneSafePathMode($path) {
|
10 |
+
return str_replace("\\", "/", $path);
|
11 |
+
}
|
12 |
+
|
13 |
+
function wpCloneDirectory($path) {
|
14 |
+
return rtrim(str_replace("//", "/", wpCloneSafePathMode($path)), '/') . '/';
|
15 |
+
}
|
16 |
+
|
17 |
+
function convertPathIntoUrl($path) {
|
18 |
+
return str_replace(rtrim(WPCLONE_ROOT, "/\\"), site_url(), $path);
|
19 |
+
}
|
20 |
+
|
21 |
+
function convertUrlIntoPath($url) {
|
22 |
+
return str_replace(site_url(), rtrim(WPCLONE_ROOT, "/\\"), $url);
|
23 |
+
}
|
24 |
+
|
25 |
+
function wpa_db_backup_wpdb($destination)
|
26 |
+
{
|
27 |
+
global $wpdb;
|
28 |
+
|
29 |
+
$return = '';
|
30 |
+
|
31 |
+
// Get all of the tables
|
32 |
+
if( isset( $_POST['ignore_prefix'] ) && 'true' === $_POST['ignore_prefix'] ) {
|
33 |
+
wpa_wpc_log( 'ignore prefix enabled, backing up all the tables' );
|
34 |
+
$tables = $wpdb->get_col('SHOW TABLES');
|
35 |
+
|
36 |
+
} else {
|
37 |
+
wpa_wpc_log( sprintf( 'backing up tables with "%s" prefix', $wpdb->prefix ) );
|
38 |
+
$tables = $wpdb->get_col('SHOW TABLES LIKE "' . $wpdb->prefix . '%"');
|
39 |
+
|
40 |
+
}
|
41 |
+
|
42 |
+
wpa_wpc_log( sprintf( 'number of tables to backup - %d', count( $tables ) ) );
|
43 |
+
|
44 |
+
// Cycle through each provided table
|
45 |
+
foreach ($tables as $table) {
|
46 |
+
|
47 |
+
// First part of the output � remove the table
|
48 |
+
$result = $wpdb->get_results("SELECT * FROM {$table}", ARRAY_N);
|
49 |
+
$numberOfItems = count($result);
|
50 |
+
if ($numberOfItems == 0) {
|
51 |
+
// Empty table - don't attempt to use $result[0] as it doesn't exist
|
52 |
+
$numberOfFields = 0;
|
53 |
+
}
|
54 |
+
else {
|
55 |
+
$numberOfFields = count($result[0]);
|
56 |
+
}
|
57 |
+
|
58 |
+
// Second part of the output � create table
|
59 |
+
$row2 = $wpdb->get_row("SHOW CREATE TABLE {$table}", ARRAY_N);
|
60 |
+
$return.= 'DROP TABLE IF EXISTS '.$table.';';
|
61 |
+
$return .= "\n\n" . $row2[1] . ";\n\n";
|
62 |
+
|
63 |
+
// Third part of the output � insert values into new table
|
64 |
+
for ($currentRowNumber = 0; $currentRowNumber < $numberOfItems; $currentRowNumber++) {
|
65 |
+
|
66 |
+
$row = $result[$currentRowNumber];
|
67 |
+
$query = "INSERT INTO {$table} VALUES(";
|
68 |
+
|
69 |
+
for ($j = 0; $j < $numberOfFields; $j++) {
|
70 |
+
// Change to 'isset()' instead of 'empty()' as 'empty()' returns true for the
|
71 |
+
// string "0" - but we may need to explicitly set value to 0 for fields where this
|
72 |
+
// is not the default. This makes the output of this method identical to the
|
73 |
+
// wpa_db_backup_direct() method
|
74 |
+
$query .= (!isset($row[$j])) ? '"", ' : '"' . esc_sql($row[$j]) . '", ';
|
75 |
+
}
|
76 |
+
|
77 |
+
$return .= substr($query, 0, -2) . ");\n";
|
78 |
+
|
79 |
+
}
|
80 |
+
|
81 |
+
$return .= "\n";
|
82 |
+
}
|
83 |
+
|
84 |
+
// Generate the filename for the sql file
|
85 |
+
$File_open = fopen($destination . '/database.sql', 'w+');
|
86 |
+
|
87 |
+
// Save the sql file
|
88 |
+
fwrite($File_open, $return);
|
89 |
+
|
90 |
+
//file close
|
91 |
+
fclose($File_open);
|
92 |
+
|
93 |
+
$wpdb->flush();
|
94 |
+
}
|
95 |
+
|
96 |
+
/**
|
97 |
+
* @link http://davidwalsh.name/backup-mysql-database-php
|
98 |
+
*/
|
99 |
+
function wpa_db_backup_direct($destination)
|
100 |
+
{
|
101 |
+
|
102 |
+
global $wpdb;
|
103 |
+
$prefix = $wpdb->prefix;
|
104 |
+
$wpcdb = wpa_wpc_mysql_connect();
|
105 |
+
if ( false === $wpcdb->get_dbh() ) {
|
106 |
+
wpa_backup_error('db', $wpcdb->error() );
|
107 |
+
}
|
108 |
+
|
109 |
+
$tables = array();
|
110 |
+
|
111 |
+
if( isset( $_POST['ignore_prefix'] ) && 'true' === $_POST['ignore_prefix'] ) {
|
112 |
+
wpa_wpc_log( 'ignore prefix enabled, backing up all the tables' );
|
113 |
+
$result = $wpcbd->query('SHOW TABLES');
|
114 |
+
|
115 |
+
} else {
|
116 |
+
wpa_wpc_log( sprintf( 'backing up tables with "%s" prefix', $prefix ) );
|
117 |
+
$result = $wpcdb->query('SHOW TABLES LIKE "' . $prefix . '%"');
|
118 |
+
}
|
119 |
+
|
120 |
+
if ( false === $result ) {
|
121 |
+
wpa_backup_error('db', $wpcdb->error() );
|
122 |
+
}
|
123 |
+
|
124 |
+
while( $row = $wpcdb->fetch_row( $result ) ) {
|
125 |
+
$tables[] = $row[0];
|
126 |
+
}
|
127 |
+
|
128 |
+
wpa_wpc_log( sprintf( 'number of tables to backup - %d', count( $tables ) ) );
|
129 |
+
$return = '';
|
130 |
+
|
131 |
+
foreach($tables as $table)
|
132 |
+
{
|
133 |
+
$result = $wpcdb->query( 'SELECT * FROM ' . $table );
|
134 |
+
if ( false === $result ) {
|
135 |
+
wpa_backup_error('db', $wpcdb->error() );
|
136 |
+
}
|
137 |
+
$num_fields = $wpcdb->num_fields($result);
|
138 |
+
|
139 |
+
$return.= 'DROP TABLE IF EXISTS '.$table.';';
|
140 |
+
$row2 = $wpcdb->fetch_row( $wpcdb->query( 'SHOW CREATE TABLE ' . $table ) );
|
141 |
+
$return.= "\n\n".$row2[1].";\n\n";
|
142 |
+
|
143 |
+
for ($i = 0; $i < $num_fields; $i++)
|
144 |
+
{
|
145 |
+
while($row = $wpcdb->fetch_row($result))
|
146 |
+
{
|
147 |
+
$return.= 'INSERT INTO '.$table.' VALUES(';
|
148 |
+
for($j=0; $j<$num_fields; $j++)
|
149 |
+
{
|
150 |
+
$row[$j] = $wpdb->escape( $row[$j] );
|
151 |
+
if (isset($row[$j])) { $return.= '"'.$row[$j].'"' ; } else { $return.= '""'; }
|
152 |
+
if ($j<($num_fields-1)) { $return.= ', '; } // Add extra space to match wpdb backup method
|
153 |
+
}
|
154 |
+
$return.= ");\n";
|
155 |
+
}
|
156 |
+
}
|
157 |
+
$return.="\n";
|
158 |
+
|
159 |
+
}
|
160 |
+
//save file
|
161 |
+
$handle = fopen($destination . '/database.sql','w+');
|
162 |
+
fwrite($handle,$return);
|
163 |
+
fclose($handle);
|
164 |
+
}
|
165 |
+
|
166 |
+
function wpa_insert_data($name, $size)
|
167 |
+
{
|
168 |
+
$backups = get_option( 'wpclone_backups' );
|
169 |
+
$time = current_time( 'timestamp', get_option('gmt_offset') );
|
170 |
+
global $current_user;
|
171 |
+
$backup = array(
|
172 |
+
$time => array(
|
173 |
+
'name' => $name . '.zip',
|
174 |
+
'log' => $name . '.log',
|
175 |
+
'creator' => $current_user->user_login,
|
176 |
+
'size' => $size
|
177 |
+
)
|
178 |
+
);
|
179 |
+
|
180 |
+
if( false === $backups ) {
|
181 |
+
add_option( 'wpclone_backups', $backup );
|
182 |
+
return;
|
183 |
+
}
|
184 |
+
|
185 |
+
$backups = $backups + $backup;
|
186 |
+
update_option( 'wpclone_backups', $backups );
|
187 |
+
|
188 |
+
return;
|
189 |
+
|
190 |
+
}
|
191 |
+
|
192 |
+
function CreateWPFullBackupZip($backupName, $zipmode, $use_wpdb = false )
|
193 |
+
{
|
194 |
+
$folderToBeZipped = WPCLONE_DIR_BACKUP . 'wpclone_backup';
|
195 |
+
$htaccess = "<Files>\r\n\tOrder allow,deny\r\n\tDeny from all\r\n\tSatisfy all\r\n</Files>";
|
196 |
+
$zipFileName = WPCLONE_DIR_BACKUP . $backupName . '.zip';
|
197 |
+
$exclude = wpa_excluded_dirs();
|
198 |
+
$dbonly = isset( $_POST['dbonly'] ) && 'true' == $_POST['dbonly'] ? true : false;
|
199 |
+
$skip = 25 * 1024 * 1024;
|
200 |
+
|
201 |
+
if( isset( $_POST['skipfiles'] ) && '' !== $_POST['skipfiles'] ) {
|
202 |
+
|
203 |
+
if( 0 === $_POST['skipfiles'] ) {
|
204 |
+
$skip = false;
|
205 |
+
|
206 |
+
} else {
|
207 |
+
$skip = $_POST['skipfiles'] * 1024 * 1024;
|
208 |
+
|
209 |
+
}
|
210 |
+
|
211 |
+
}
|
212 |
+
|
213 |
+
if( false === mkdir( $folderToBeZipped ) ) {
|
214 |
+
wpa_backup_error ( 'file', sprintf( __( 'Unable to create the temporary backup directory,please make sure that PHP has permission to write into the <code>%s</code> directory.' ), WPCLONE_DIR_BACKUP ) );
|
215 |
+
}
|
216 |
+
|
217 |
+
file_put_contents( $folderToBeZipped . '/.htaccess', $htaccess );
|
218 |
+
|
219 |
+
if( $dbonly ) {
|
220 |
+
wpa_wpc_log ( 'database only backup, no files will be copied' );
|
221 |
+
}
|
222 |
+
|
223 |
+
if( false === $dbonly ) {
|
224 |
+
if( $skip ) {
|
225 |
+
wpa_wpc_log( sprintf( 'files larger than %s will be excluded from the backup', bytesToSize( $skip ) ) );
|
226 |
+
}
|
227 |
+
wpa_wpc_log( 'generating file list' );
|
228 |
+
file_put_contents( $folderToBeZipped . '/file.list', serialize( wpa_wpc_get_filelist( WPCLONE_WP_CONTENT, $exclude, $skip ) ) );
|
229 |
+
wpa_wpc_log( 'finished generating file list' );
|
230 |
+
}
|
231 |
+
|
232 |
+
wpa_save_prefix($folderToBeZipped);
|
233 |
+
/* error handler is called from within the db backup functions */
|
234 |
+
if ( $use_wpdb ) {
|
235 |
+
wpa_wpc_log ( 'database backup started [wpdb]' );
|
236 |
+
wpa_db_backup_wpdb( $folderToBeZipped );
|
237 |
+
} else {
|
238 |
+
wpa_wpc_log ( 'database backup started' );
|
239 |
+
wpa_db_backup_direct( $folderToBeZipped );
|
240 |
+
}
|
241 |
+
wpa_wpc_log ( 'database backup finished' );
|
242 |
+
|
243 |
+
/* error handler is called from within the wpa_zip function */
|
244 |
+
|
245 |
+
wpa_zip($zipFileName, $folderToBeZipped, $zipmode);
|
246 |
+
|
247 |
+
wpa_delete_dir( $folderToBeZipped );
|
248 |
+
|
249 |
+
if( ! file_exists( $zipFileName ) ) {
|
250 |
+
wpa_backup_error( 'backup', 'possibly out of free disk space' );
|
251 |
+
}
|
252 |
+
$zipSize = filesize($zipFileName);
|
253 |
+
return array($backupName, $zipSize);
|
254 |
+
}
|
255 |
+
|
256 |
+
function DeleteWPBackupZip($nm)
|
257 |
+
{
|
258 |
+
$backups = get_option( 'wpclone_backups' );
|
259 |
+
|
260 |
+
if( empty( $backups ) || ! isset( $backups[$nm] ) ) {
|
261 |
+
return array(
|
262 |
+
'status' => 'failed',
|
263 |
+
'msg' => 'Something is not quite right here, refresh the backup list and try again later.' );
|
264 |
+
}
|
265 |
+
|
266 |
+
if( isset( $backups[$nm]['log'] ) && file_exists( WPCLONE_DIR_BACKUP . $backups[$nm]['log'] ) ) {
|
267 |
+
@unlink( WPCLONE_DIR_BACKUP . $backups[$nm]['log'] );
|
268 |
+
|
269 |
+
}
|
270 |
+
|
271 |
+
if ( file_exists( WPCLONE_DIR_BACKUP . $backups[$nm]['name'] ) ) {
|
272 |
+
|
273 |
+
if( ! unlink( WPCLONE_DIR_BACKUP . $backups[$nm]['name'] ) ) {
|
274 |
+
return array(
|
275 |
+
'status' => 'failed',
|
276 |
+
'msg' => 'Unable to delete file' );
|
277 |
+
}
|
278 |
+
|
279 |
+
unset( $backups[$nm] );
|
280 |
+
update_option( 'wpclone_backups', $backups );
|
281 |
+
return array(
|
282 |
+
'status' => 'deleted',
|
283 |
+
'msg' => 'File deleted' );
|
284 |
+
|
285 |
+
} else {
|
286 |
+
|
287 |
+
return array(
|
288 |
+
'status' => 'failed',
|
289 |
+
'msg' => 'File not found. Refresh the backup list to remove missing backups.' );
|
290 |
+
|
291 |
+
}
|
292 |
+
|
293 |
+
}
|
294 |
+
|
295 |
+
function bytesToSize($bytes, $precision = 2)
|
296 |
+
{
|
297 |
+
$kilobyte = 1024;
|
298 |
+
$megabyte = $kilobyte * 1024;
|
299 |
+
$gigabyte = $megabyte * 1024;
|
300 |
+
$terabyte = $gigabyte * 1024;
|
301 |
+
if (($bytes >= 0) && ($bytes < $kilobyte)) {
|
302 |
+
return $bytes . ' B';
|
303 |
+
} elseif (($bytes >= $kilobyte) && ($bytes < $megabyte)) {
|
304 |
+
return round($bytes / $kilobyte, $precision) . ' KB';
|
305 |
+
} elseif (($bytes >= $megabyte) && ($bytes < $gigabyte)) {
|
306 |
+
return round($bytes / $megabyte, $precision) . ' MB';
|
307 |
+
} elseif (($bytes >= $gigabyte) && ($bytes < $terabyte)) {
|
308 |
+
return round($bytes / $gigabyte, $precision) . ' GB';
|
309 |
+
} elseif ($bytes >= $terabyte) {
|
310 |
+
return round($bytes / $terabyte, $precision) . ' TB';
|
311 |
+
} else {
|
312 |
+
return $bytes . ' B';
|
313 |
+
}
|
314 |
+
}
|
315 |
+
|
316 |
+
function wpa_wpc_get_url( $db ) {
|
317 |
+
|
318 |
+
$pos = strpos( $db, 'siteurl' ) + 8;
|
319 |
+
$urlStartPos = strpos( $db, '"', $pos ) + 1;
|
320 |
+
$urlEndPos = strpos( $db, '"', $urlStartPos );
|
321 |
+
$backupSiteUrl = substr( $db, $urlStartPos, $urlEndPos - $urlStartPos );
|
322 |
+
return $backupSiteUrl;
|
323 |
+
|
324 |
+
}
|
325 |
+
|
326 |
+
|
327 |
+
function wpa_wpc_mysql_connect() {
|
328 |
+
// Use subclass of wpdb to ensure compatibility with WordPress database and use the appropriate MySQL module
|
329 |
+
// and provide the extra functions we need
|
330 |
+
$db = new wpc_wpdb( DB_USER, DB_PASSWORD, DB_NAME, DB_HOST );
|
331 |
+
return $db;
|
332 |
+
}
|
333 |
+
|
334 |
+
/**
|
335 |
+
* @param type $search URL of the previous site.
|
336 |
+
* @param type $replace URL of the current site.
|
337 |
+
* @return type total time it took for the operation.
|
338 |
+
*/
|
339 |
+
function wpa_safe_replace_wrapper ( $search, $replace, $prefix ) {
|
340 |
+
if ( !function_exists( 'icit_srdb_replacer' ) && !function_exists( 'recursive_unserialize_replace' ) ) {
|
341 |
+
require_once 'icit_srdb_replacer.php';
|
342 |
+
}
|
343 |
+
|
344 |
+
wpa_wpc_log( 'search and replace started' );
|
345 |
+
|
346 |
+
$wpcdb = wpa_wpc_mysql_connect();
|
347 |
+
|
348 |
+
if ( false === $wpcdb->get_dbh() ) {
|
349 |
+
|
350 |
+
wpa_wpc_log( 'mysql connection failure @ safe replace wrapper - error : "' . $wpdbc->error() . '" retrying..' );
|
351 |
+
|
352 |
+
$wpcdb->close();
|
353 |
+
sleep(1);
|
354 |
+
// Try to create a new connection
|
355 |
+
$wpcdb = wpa_wpc_mysql_connect();
|
356 |
+
}
|
357 |
+
|
358 |
+
$all_tables = array();
|
359 |
+
|
360 |
+
if( isset( $_POST['ignore_prefix'] ) && 'true' === $_POST['ignore_prefix'] ) {
|
361 |
+
wpa_wpc_log( 'ignore table prefix enabled, search and replace will scan all the tables in the database' );
|
362 |
+
$all_tables_mysql = @$wpcdb->query( 'SHOW TABLES' );
|
363 |
+
|
364 |
+
} else {
|
365 |
+
$all_tables_mysql = @$wpcdb->query( 'SHOW TABLES LIKE "' . $prefix . '%"' );
|
366 |
+
}
|
367 |
+
|
368 |
+
while ( $table = $wpcdb->fetch_array( $all_tables_mysql ) ) {
|
369 |
+
$all_tables[] = $table[ 0 ];
|
370 |
+
}
|
371 |
+
|
372 |
+
wpa_wpc_log( sprintf( 'there are %d tables to scan', count( $all_tables ) ) );
|
373 |
+
|
374 |
+
$report = icit_srdb_replacer( $wpcdb, $search, $replace, $all_tables );
|
375 |
+
$wpcdb->close( );
|
376 |
+
wpa_wpc_log( 'search and replace finished' );
|
377 |
+
return $report;
|
378 |
+
}
|
379 |
+
|
380 |
+
function wpa_wpc_temp_dir() {
|
381 |
+
|
382 |
+
global $wp_filesystem;
|
383 |
+
$temp_dir = trailingslashit( WPCLONE_WP_CONTENT ) . 'wpclone-temp';
|
384 |
+
$err = $wp_filesystem->mkdir( $temp_dir );
|
385 |
+
|
386 |
+
if ( is_wp_error( $err ) ) {
|
387 |
+
wpa_backup_error('dirrest', $err->get_error_message(), true );
|
388 |
+
}
|
389 |
+
|
390 |
+
$content = "<Files>\r\n\tOrder allow,deny\r\n\tDeny from all\r\n\tSatisfy all\r\n</Files>";
|
391 |
+
$file = trailingslashit( $temp_dir ) . '.htaccess';
|
392 |
+
$wp_filesystem->put_contents( $file, $content, 0644 );
|
393 |
+
|
394 |
+
return $temp_dir;
|
395 |
+
|
396 |
+
}
|
397 |
+
|
398 |
+
function processRestoringBackup($url, $zipmode) {
|
399 |
+
if( true === is_multisite() )
|
400 |
+
die( 'wpclone does not work on multisite installs.' );
|
401 |
+
|
402 |
+
wpa_cleanup( true );
|
403 |
+
if (!is_string($url) || '' == $url) {
|
404 |
+
wpa_backup_error( 'restore', sprintf( __( 'The provided URL "<code>%s</code>" is either not valid or empty' ), $url ), true );
|
405 |
+
}
|
406 |
+
|
407 |
+
global $wp_filesystem;
|
408 |
+
$GLOBALS['wpclone']['logfile'] = 'wpclone_restore_' . current_time( 'dS_M_Y_h-iA', false ) . '_' . wp_generate_password( 10, false ) . '.log';
|
409 |
+
|
410 |
+
wpa_wpc_log_start( 'restore' );
|
411 |
+
|
412 |
+
if( $zipmode ) {
|
413 |
+
define( 'PCLZIP_TEMPORARY_DIR', WPCLONE_DIR_BACKUP );
|
414 |
+
|
415 |
+
}
|
416 |
+
|
417 |
+
$temp_dir = wpa_wpc_temp_dir();
|
418 |
+
$site_url = site_url();
|
419 |
+
$permalink_url = admin_url( 'options-permalink.php' );
|
420 |
+
$zipfile = wpa_fetch_file($url);
|
421 |
+
$report = wpa_wpc_process_db( $zipfile, $zipmode );
|
422 |
+
$unzipped_folder = wpCloneSafePathMode( trailingslashit( $temp_dir ) . 'wpclone_backup' );
|
423 |
+
|
424 |
+
|
425 |
+
wpa_unzip( $zipfile, $temp_dir, $zipmode );
|
426 |
+
wpa_wpc_log( 'copying files..' );
|
427 |
+
wpa_copy( $unzipped_folder . '/wp-content', WPCLONE_WP_CONTENT );
|
428 |
+
|
429 |
+
wpa_wpc_log( 'deleting temp directory..' );
|
430 |
+
$wp_filesystem->delete( $temp_dir, true );
|
431 |
+
/* remove the zip file only if it was downloaded from an external location. */
|
432 |
+
$wptmp = explode( '.', $zipfile );
|
433 |
+
if ( in_array( 'tmp', $wptmp ) ) {
|
434 |
+
wpa_wpc_log( 'deleting downloaded zip file..' );
|
435 |
+
$wp_filesystem->delete( $zipfile );
|
436 |
+
}
|
437 |
+
|
438 |
+
wpa_wpc_log( 'restore finished' );
|
439 |
+
|
440 |
+
echo '<div class=""><h1>Restore Successful!</h1>';
|
441 |
+
printf( 'Visit your restored site [ <a href="%s" target=blank>here</a> ]<br><br>', $site_url );
|
442 |
+
printf( '<strong>You may need to re-save your permalink structure <a href="%s" target=blank>Here</a></strong>', $permalink_url );
|
443 |
+
printf( '</br><a href="%s">log</a>', convertPathIntoUrl( WPCLONE_DIR_BACKUP . $GLOBALS['wpclone']['logfile'] ) );
|
444 |
+
?>
|
445 |
+
<?php
|
446 |
+
unset( $GLOBALS['wpclone'] );
|
447 |
+
echo wpa_wpc_search_n_replace_report( $report );
|
448 |
+
|
449 |
+
?>
|
450 |
+
|
451 |
+
<style rel="stylesheet">
|
452 |
+
.banner-2-after-restore {
|
453 |
+
min-height: 280px;
|
454 |
+
width: auto;
|
455 |
+
background-size: cover;
|
456 |
+
padding-top: 24px;
|
457 |
+
padding-left: 25px;
|
458 |
+
padding-right: 25px;
|
459 |
+
font-family: 'Montserrat', sans-serif;
|
460 |
+
margin-right: 30px;
|
461 |
+
margin-top: 20px;
|
462 |
+
padding-bottom: 20px;
|
463 |
+
}
|
464 |
+
.banner-2-after-restore p{
|
465 |
+
font-size: 18px;
|
466 |
+
}
|
467 |
+
|
468 |
+
.banner-2-after-restore p a{
|
469 |
+
color: #3eb9b4;
|
470 |
+
text-decoration: none;
|
471 |
+
}
|
472 |
+
</style>
|
473 |
+
<div class="plugin-large-notice-restore-success">
|
474 |
+
<div class="banner-2-after-restore" style="background-image: url('<?php echo plugins_url( 'lib/img/banner_success_rating_bg.jpg', WPCLONE_ROOT_FILE_PATH )?>')">
|
475 |
+
<div style="float: right;" id="banner-2-restore-close-icon"><img src='<?php echo plugins_url( 'lib/img/banner_close_icon.png', WPCLONE_ROOT_FILE_PATH )?>'> </div>
|
476 |
+
<p>
|
477 |
+
Could you please do us a <strong>BIG favor</strong> and consider contributing to our crowdfunding effort <a target="_blank" href="https://sellcodes.com/q1OGuSox">here</a>? <br> <br>
|
478 |
+
|
479 |
+
<span style="text-decoration: underline">Background:</span> This is a great plugin, but it has been neglected for a long time, <br>
|
480 |
+
and we would like to change that, i.e. completely re-work the entire plugin, <br> make it 100% bug-free, and
|
481 |
+
add many more features (while still keeping it super-simple to use!) <br>. We're short on cash,
|
482 |
+
and need your help for that. Thank you!
|
483 |
+
<br><br>
|
484 |
+
|
485 |
+
<a href="https://sellcodes.com/q1OGuSox" target="_blank">=> Visit the crowdfunding page</a>
|
486 |
+
<br><br>
|
487 |
+
If something doesn’t work as it should, please <a href="https://wordpress.org/support/plugin/wp-clone-by-wp-academy/" target="_blank">ask us in the forum</a> . We’ll try to respond quickly!
|
488 |
+
</p>
|
489 |
+
</div>
|
490 |
+
</div>
|
491 |
+
|
492 |
+
<?php
|
493 |
+
}
|
494 |
+
|
495 |
+
function wpa_wpc_search_n_replace_report( $report ) {
|
496 |
+
|
497 |
+
if( is_string( $report ) ) {
|
498 |
+
return sprintf( '<div class="info"><p>%s</p></div>', $report );
|
499 |
+
}
|
500 |
+
|
501 |
+
$time = array_sum( explode( ' ', $report[ 'end' ] ) ) - array_sum( explode( ' ', $report[ 'start' ] ) );
|
502 |
+
$return = sprintf( '<div class="info"><p>Search and replace scanned <strong>%d</strong> tables with a total of <strong>%d</strong> rows. ' , $report['tables'], $report['rows'] );
|
503 |
+
$return .= sprintf( '<strong>%d</strong> cells were changed and <strong>%d</strong> db updates were performed in <strong>%f</strong> seconds.</p></div>', $report['change'], $report['updates'], $time );
|
504 |
+
|
505 |
+
if ( ! empty( $report['errors'] ) && is_array( $report['errors'] ) ) {
|
506 |
+
$return .= '<div>';
|
507 |
+
$return .= '<h4>search and replace returned the following errors.</h4>';
|
508 |
+
foreach( $report['errors'] as $error ) {
|
509 |
+
$return .= '<p class="error">' . $error . '</p>';
|
510 |
+
}
|
511 |
+
$return .= '</div>';
|
512 |
+
}
|
513 |
+
|
514 |
+
return $return;
|
515 |
+
|
516 |
+
}
|
517 |
+
|
518 |
+
function wpa_save_prefix($path) {
|
519 |
+
global $wpdb;
|
520 |
+
$prefix = $wpdb->prefix;
|
521 |
+
$file = $path . '/prefix.txt';
|
522 |
+
if ( is_dir($path) && is_writable($path) ) {
|
523 |
+
file_put_contents($file, $prefix);
|
524 |
+
}
|
525 |
+
}
|
526 |
+
/**
|
527 |
+
* Checks to see whether the destination site's table prefix matches that of the origin site.old prefix is returned in case of a mismatch.
|
528 |
+
*
|
529 |
+
* @param type $file path to the prefix.txt file.
|
530 |
+
* @return type bool string
|
531 |
+
*/
|
532 |
+
function wpa_check_prefix($file) {
|
533 |
+
global $wpdb;
|
534 |
+
$prefix = $wpdb->prefix;
|
535 |
+
if (file_exists($file) && is_readable($file)) {
|
536 |
+
$old_prefix = file_get_contents($file);
|
537 |
+
if ( $prefix !== $old_prefix ) {
|
538 |
+
return $old_prefix;
|
539 |
+
}
|
540 |
+
else {
|
541 |
+
return false;
|
542 |
+
}
|
543 |
+
}
|
544 |
+
return false;
|
545 |
+
}
|
546 |
+
|
547 |
+
/**
|
548 |
+
* @since 2.0.6
|
549 |
+
*
|
550 |
+
* @param type $zipfile path to the zip file that needs to be extracted.
|
551 |
+
* @param type $path the place to where the file needs to be extracted.
|
552 |
+
* @return as false in the event of failure.
|
553 |
+
*/
|
554 |
+
function wpa_unzip($zipfile, $path, $zipmode = false){
|
555 |
+
|
556 |
+
if ( $zipmode || ( ! in_array('ZipArchive', get_declared_classes() ) || ! class_exists( 'ZipArchive' ) ) ) {
|
557 |
+
|
558 |
+
wpa_wpc_log( 'extracting archive using pclzip' );
|
559 |
+
|
560 |
+
if ( ini_get('mbstring.func_overload') && function_exists('mb_internal_encoding') ) {
|
561 |
+
$previous_encoding = mb_internal_encoding();
|
562 |
+
mb_internal_encoding('ISO-8859-1');
|
563 |
+
}
|
564 |
+
|
565 |
+
require_once ( ABSPATH . 'wp-admin/includes/class-pclzip.php' );
|
566 |
+
$z = new PclZip($zipfile);
|
567 |
+
|
568 |
+
$files = $z->extract( PCLZIP_OPT_PATH, $path );
|
569 |
+
|
570 |
+
if ( isset( $previous_encoding ) ) {
|
571 |
+
mb_internal_encoding( $previous_encoding );
|
572 |
+
|
573 |
+
}
|
574 |
+
|
575 |
+
if ( $files === 0 ) {
|
576 |
+
wpa_backup_error( 'pclunzip', $z->errorInfo(true), true );
|
577 |
+
}
|
578 |
+
|
579 |
+
} else {
|
580 |
+
wpa_wpc_log( 'extracting archive using ziparchive' );
|
581 |
+
wpa_wpc_unzip( $zipfile, $path );
|
582 |
+
|
583 |
+
}
|
584 |
+
|
585 |
+
}
|
586 |
+
/**
|
587 |
+
* @since 2.0.6
|
588 |
+
*
|
589 |
+
* @param type $name name of the zip file.
|
590 |
+
* @param type $file_list an array of files that needs to be archived.
|
591 |
+
*/
|
592 |
+
function wpa_zip($zip_name, $folder, $zipmode = false){
|
593 |
+
if ( $zipmode || (!in_array('ZipArchive', get_declared_classes()) || !class_exists('ZipArchive')) ) {
|
594 |
+
wpa_wpc_log( 'archiving files using pclzip' );
|
595 |
+
$zipmode = true;
|
596 |
+
define('PCLZIP_TEMPORARY_DIR', WPCLONE_DIR_BACKUP);
|
597 |
+
require_once ( ABSPATH . 'wp-admin/includes/class-pclzip.php');
|
598 |
+
$z = new PclZip($zip_name);
|
599 |
+
$v_list = $z->create($folder, PCLZIP_OPT_REMOVE_PATH, WPCLONE_DIR_BACKUP );
|
600 |
+
if ($v_list == 0) {
|
601 |
+
wpa_backup_error( 'pclzip', $z->errorInfo(true) );
|
602 |
+
}
|
603 |
+
$file_list = wpa_wpc_zip( $z, $zipmode );
|
604 |
+
|
605 |
+
if( $file_list ) {
|
606 |
+
$z->add( $file_list, PCLZIP_OPT_REMOVE_PATH, WPCLONE_ROOT, PCLZIP_OPT_ADD_PATH, 'wpclone_backup' );
|
607 |
+
}
|
608 |
+
|
609 |
+
$z->delete( PCLZIP_OPT_BY_NAME, 'wpclone_backup/file.list' );
|
610 |
+
|
611 |
+
} else {
|
612 |
+
wpa_wpc_log( 'archiving files using ziparchive' );
|
613 |
+
$z = new ZipArchive();
|
614 |
+
if ( true !== $z->open( $zip_name, ZIPARCHIVE::CREATE ) ) {
|
615 |
+
wpa_backup_error( 'zip', $z->getStatusString() );
|
616 |
+
}
|
617 |
+
wpa_ziparc($z, $folder, WPCLONE_DIR_BACKUP);
|
618 |
+
|
619 |
+
wpa_wpc_zip( $z, $zipmode );
|
620 |
+
$z->deleteName( 'wpclone_backup/file.list' );
|
621 |
+
$z->close();
|
622 |
+
|
623 |
+
}
|
624 |
+
|
625 |
+
|
626 |
+
}
|
627 |
+
|
628 |
+
function wpa_ziparc($zip, $dir, $base) {
|
629 |
+
$new_folder = str_replace($base, '', $dir);
|
630 |
+
$zip->addEmptyDir($new_folder);
|
631 |
+
foreach( glob( $dir . '/*' ) as $file ){
|
632 |
+
if( is_dir($file) ) {
|
633 |
+
wpa_ziparc($zip, $file, $base);
|
634 |
+
} else {
|
635 |
+
$new_file = str_replace( $base, '', $file );
|
636 |
+
$zip->addFile($file, $new_file);
|
637 |
+
}
|
638 |
+
}
|
639 |
+
}
|
640 |
+
/**
|
641 |
+
* just a simple function to increase PHP limits.
|
642 |
+
* @since 2.0.6
|
643 |
+
*/
|
644 |
+
function wpa_bump_limits(){
|
645 |
+
$GLOBALS['wpclone'] = array();
|
646 |
+
$GLOBALS['wpclone']['time'] = isset( $_POST['maxexec'] ) && '' != $_POST['maxexec'] ? $_POST['maxexec'] : 600; /* 10 minutes */
|
647 |
+
$GLOBALS['wpclone']['mem'] = isset ( $_POST['maxmem'] ) && '' != $_POST['maxmem'] ? $_POST['maxmem'] . 'M' : '1024M';
|
648 |
+
|
649 |
+
@ini_set('memory_limit', $GLOBALS['wpclone']['mem']);
|
650 |
+
@ini_set('max_execution_time', $GLOBALS['wpclone']['time']);
|
651 |
+
@ini_set('mysql.connect_timeout', $GLOBALS['wpclone']['time']);
|
652 |
+
@ini_set('default_socket_timeout', $GLOBALS['wpclone']['time']);
|
653 |
+
}
|
654 |
+
|
655 |
+
/**
|
656 |
+
* @since 2.0.6
|
657 |
+
*/
|
658 |
+
function wpa_wpfs_init(){
|
659 |
+
if (!empty($_REQUEST['del'])) {
|
660 |
+
wpa_remove_backup();
|
661 |
+
return true;
|
662 |
+
}
|
663 |
+
if (empty($_POST)) return false;
|
664 |
+
check_admin_referer('wpclone-submit');
|
665 |
+
|
666 |
+
wpa_bump_limits();
|
667 |
+
|
668 |
+
if (isset($_POST['createBackup'])) {
|
669 |
+
wpa_create_backup();
|
670 |
+
return true;
|
671 |
+
}
|
672 |
+
|
673 |
+
$form_post = wp_nonce_url('admin.php?page=wp-clone', 'wpclone-submit');
|
674 |
+
$extra_fields = array( 'restore_from_url', 'maxmem', 'maxexec', 'zipmode', 'ignore_prefix', 'wipedb', 'mysql_check', 'restoreBackup', 'createBackup' );
|
675 |
+
$type = '';
|
676 |
+
if ( false === ($creds = request_filesystem_credentials($form_post, $type, false, false, $extra_fields)) ){
|
677 |
+
return true;
|
678 |
+
}
|
679 |
+
if (!WP_Filesystem($creds)) {
|
680 |
+
request_filesystem_credentials($form_post, $type, true, false, $extra_fields);
|
681 |
+
return true;
|
682 |
+
}
|
683 |
+
|
684 |
+
$zipmode = isset($_POST['zipmode']) ? true : false;
|
685 |
+
$url = isset($_POST['restoreBackup']) ? $_POST['restoreBackup'] : $_POST['restore_from_url'];
|
686 |
+
processRestoringBackup($url, $zipmode);
|
687 |
+
return true;
|
688 |
+
}
|
689 |
+
/**
|
690 |
+
* @since 2.0.6
|
691 |
+
*/
|
692 |
+
function wpa_copy($source, $target) {
|
693 |
+
global $wp_filesystem;
|
694 |
+
if (is_readable($source)) {
|
695 |
+
if (is_dir($source)) {
|
696 |
+
if (!file_exists($target)) {
|
697 |
+
$wp_filesystem->mkdir($target);
|
698 |
+
}
|
699 |
+
$d = dir($source);
|
700 |
+
while (FALSE !== ($entry = $d->read())) {
|
701 |
+
if ($entry == '.' || $entry == '..') {
|
702 |
+
continue;
|
703 |
+
}
|
704 |
+
$Entry = "{$source}/{$entry}";
|
705 |
+
if (is_dir($Entry)) {
|
706 |
+
wpa_copy($Entry, $target . '/' . $entry);
|
707 |
+
} else {
|
708 |
+
$wp_filesystem->copy($Entry, $target . '/' . $entry, true, FS_CHMOD_FILE);
|
709 |
+
}
|
710 |
+
}
|
711 |
+
$d->close();
|
712 |
+
}
|
713 |
+
else {
|
714 |
+
$wp_filesystem->copy($source, $target, true);
|
715 |
+
}
|
716 |
+
}
|
717 |
+
}
|
718 |
+
/**
|
719 |
+
* @since 2.0.6
|
720 |
+
*/
|
721 |
+
function wpa_replace_prefix( $current, $new ){
|
722 |
+
|
723 |
+
$wpconfig = wpa_wpconfig_path();
|
724 |
+
global $wp_filesystem;
|
725 |
+
|
726 |
+
if ( ! $wp_filesystem->is_writable($wpconfig) ) {
|
727 |
+
if( false === $wp_filesystem->chmod( $wpconfig ) )
|
728 |
+
wpa_backup_error('wpconfig', sprintf( __( "<code>%s</code> is not writable and wpclone was unable to change the file permissions." ), $wpconfig ), true );
|
729 |
+
|
730 |
+
}
|
731 |
+
|
732 |
+
$content = file( $wpconfig );
|
733 |
+
|
734 |
+
foreach( $content as $key => $value ) {
|
735 |
+
|
736 |
+
if( false !== strpos( $value, '$table_prefix' ) ) {
|
737 |
+
$content[$key] = str_replace( $current, $new, $value );
|
738 |
+
}
|
739 |
+
|
740 |
+
}
|
741 |
+
|
742 |
+
$content = implode( $content );
|
743 |
+
$wp_filesystem->put_contents( $wpconfig, $content, 0600 );
|
744 |
+
|
745 |
+
}
|
746 |
+
/**
|
747 |
+
* @since 2.0.6
|
748 |
+
*/
|
749 |
+
function wpa_create_backup (){
|
750 |
+
|
751 |
+
if( true === is_multisite() )
|
752 |
+
die( 'wpclone does not work on multisite installs.' );
|
753 |
+
if ( !file_exists(WPCLONE_DIR_BACKUP) ) {
|
754 |
+
wpa_create_directory();
|
755 |
+
}
|
756 |
+
wpa_cleanup();
|
757 |
+
$use_wpdb = isset( $_POST['use_wpdb'] ) && 'true' == $_POST['use_wpdb'] ? true : false;
|
758 |
+
$backupName = wpa_backup_name();
|
759 |
+
$GLOBALS['wpclone']['logfile'] = $backupName . '.log';
|
760 |
+
|
761 |
+
wpa_wpc_log_start( 'backup' );
|
762 |
+
|
763 |
+
$zipmode = isset($_POST['zipmode']) ? true : false;
|
764 |
+
list($zipFileName, $zipSize) = CreateWPFullBackupZip($backupName, $zipmode, $use_wpdb);
|
765 |
+
|
766 |
+
wpa_insert_data($zipFileName, $zipSize);
|
767 |
+
$backZipPath = convertPathIntoUrl(WPCLONE_DIR_BACKUP . $zipFileName . '.zip');
|
768 |
+
$zipSize = bytesToSize($zipSize);
|
769 |
+
wpa_wpc_log( 'backup finished');
|
770 |
+
|
771 |
+
echo <<<EOF
|
772 |
+
|
773 |
+
<h1>Backup Successful!</h1>
|
774 |
+
|
775 |
+
<br />
|
776 |
+
|
777 |
+
Here is your backup file : <br />
|
778 |
+
|
779 |
+
<a href='{$backZipPath}'><span>{$backZipPath}</span></a> ( {$zipSize} ) |
|
780 |
+
<input type='hidden' name='backupUrl' class='backupUrl' value="{$backZipPath}" />
|
781 |
+
<a class='copy-button' href='#' data-clipboard-text='{$backZipPath}'>Copy URL</a> <br /><br />
|
782 |
+
|
783 |
+
(Copy that link and paste it into the "Restore URL" of your new WordPress installation to clone this site)
|
784 |
+
EOF;
|
785 |
+
printf( '</br><a href="%s">log</a>', convertPathIntoUrl( WPCLONE_DIR_BACKUP . $GLOBALS['wpclone']['logfile'] ) );
|
786 |
+
unset( $GLOBALS['wpclone'] );
|
787 |
+
}
|
788 |
+
/**
|
789 |
+
* @since 2.0.6
|
790 |
+
*/
|
791 |
+
function wpa_remove_backup(){
|
792 |
+
check_admin_referer('wpclone-submit');
|
793 |
+
$deleteRow = DeleteWPBackupZip($_REQUEST['del']);
|
794 |
+
echo $deleteRow['msg'];
|
795 |
+
|
796 |
+
}
|
797 |
+
/**
|
798 |
+
* @since 2.1.2
|
799 |
+
* copypasta from wp-load.php
|
800 |
+
* @return the path to wp-config.php
|
801 |
+
*/
|
802 |
+
function wpa_wpconfig_path () {
|
803 |
+
|
804 |
+
if ( file_exists( ABSPATH . 'wp-config.php') ) {
|
805 |
+
|
806 |
+
/** The config file resides in ABSPATH */
|
807 |
+
return ABSPATH . 'wp-config.php';
|
808 |
+
|
809 |
+
}
|
810 |
+
elseif ( file_exists( dirname(ABSPATH) . '/wp-config.php' ) && ! file_exists( dirname(ABSPATH) . '/wp-settings.php' ) ) {
|
811 |
+
|
812 |
+
/** The config file resides one level above ABSPATH but is not part of another install */
|
813 |
+
return dirname(ABSPATH) . '/wp-config.php';
|
814 |
+
|
815 |
+
}
|
816 |
+
else {
|
817 |
+
|
818 |
+
return false;
|
819 |
+
|
820 |
+
}
|
821 |
+
|
822 |
+
}
|
823 |
+
|
824 |
+
function wcbwa_download_url_alternate( $url, $timeout = 300, $signature_verification = false ){
|
825 |
+
//WARNING: The file is not automatically deleted, The script must unlink() the file.
|
826 |
+
if ( ! $url ) {
|
827 |
+
return new WP_Error( 'http_no_url', __( 'Invalid URL Provided.' ) );
|
828 |
+
}
|
829 |
+
|
830 |
+
$url_filename = basename( parse_url( $url, PHP_URL_PATH ) );
|
831 |
+
|
832 |
+
$tmpfname = wp_tempnam( $url_filename );
|
833 |
+
if ( ! $tmpfname ) {
|
834 |
+
return new WP_Error( 'http_no_file', __( 'Could not create Temporary file.' ) );
|
835 |
+
}
|
836 |
+
|
837 |
+
$response = wp_remote_get(
|
838 |
+
$url,
|
839 |
+
array(
|
840 |
+
'timeout' => $timeout,
|
841 |
+
'stream' => true,
|
842 |
+
'filename' => $tmpfname,
|
843 |
+
)
|
844 |
+
);
|
845 |
+
|
846 |
+
if ( is_wp_error( $response ) ) {
|
847 |
+
unlink( $tmpfname );
|
848 |
+
return $response;
|
849 |
+
}
|
850 |
+
|
851 |
+
$response_code = wp_remote_retrieve_response_code( $response );
|
852 |
+
|
853 |
+
if ( 200 != $response_code ) {
|
854 |
+
$data = array(
|
855 |
+
'code' => $response_code,
|
856 |
+
);
|
857 |
+
|
858 |
+
// Retrieve a sample of the response body for debugging purposes.
|
859 |
+
$tmpf = fopen( $tmpfname, 'rb' );
|
860 |
+
if ( $tmpf ) {
|
861 |
+
/**
|
862 |
+
* Filters the maximum error response body size in `download_url()`.
|
863 |
+
*
|
864 |
+
* @since 5.1.0
|
865 |
+
*
|
866 |
+
* @see download_url()
|
867 |
+
*
|
868 |
+
* @param int $size The maximum error response body size. Default 1 KB.
|
869 |
+
*/
|
870 |
+
$response_size = apply_filters( 'download_url_error_max_body_size', KB_IN_BYTES );
|
871 |
+
$data['body'] = fread( $tmpf, $response_size );
|
872 |
+
fclose( $tmpf );
|
873 |
+
}
|
874 |
+
|
875 |
+
unlink( $tmpfname );
|
876 |
+
return new WP_Error( 'http_404', trim( wp_remote_retrieve_response_message( $response ) ), $data );
|
877 |
+
}
|
878 |
+
|
879 |
+
$content_md5 = wp_remote_retrieve_header( $response, 'content-md5' );
|
880 |
+
if ( $content_md5 ) {
|
881 |
+
$md5_check = verify_file_md5( $tmpfname, $content_md5 );
|
882 |
+
if ( is_wp_error( $md5_check ) ) {
|
883 |
+
unlink( $tmpfname );
|
884 |
+
return $md5_check;
|
885 |
+
}
|
886 |
+
}
|
887 |
+
|
888 |
+
// If the caller expects signature verification to occur, check to see if this URL supports it.
|
889 |
+
if ( $signature_verification ) {
|
890 |
+
/**
|
891 |
+
* Filters the list of hosts which should have Signature Verification attempteds on.
|
892 |
+
*
|
893 |
+
* @since 5.2.0
|
894 |
+
*
|
895 |
+
* @param array List of hostnames.
|
896 |
+
*/
|
897 |
+
$signed_hostnames = apply_filters( 'wp_signature_hosts', array( 'wordpress.org', 'downloads.wordpress.org', 's.w.org' ) );
|
898 |
+
$signature_verification = in_array( parse_url( $url, PHP_URL_HOST ), $signed_hostnames, true );
|
899 |
+
}
|
900 |
+
|
901 |
+
// Perform signature valiation if supported.
|
902 |
+
if ( $signature_verification ) {
|
903 |
+
$signature = wp_remote_retrieve_header( $response, 'x-content-signature' );
|
904 |
+
if ( ! $signature ) {
|
905 |
+
// Retrieve signatures from a file if the header wasn't included.
|
906 |
+
// WordPress.org stores signatures at $package_url.sig
|
907 |
+
|
908 |
+
$signature_url = false;
|
909 |
+
$url_path = parse_url( $url, PHP_URL_PATH );
|
910 |
+
if ( substr( $url_path, -4 ) == '.zip' || substr( $url_path, -7 ) == '.tar.gz' ) {
|
911 |
+
$signature_url = str_replace( $url_path, $url_path . '.sig', $url );
|
912 |
+
}
|
913 |
+
|
914 |
+
/**
|
915 |
+
* Filter the URL where the signature for a file is located.
|
916 |
+
*
|
917 |
+
* @since 5.2.0
|
918 |
+
*
|
919 |
+
* @param false|string $signature_url The URL where signatures can be found for a file, or false if none are known.
|
920 |
+
* @param string $url The URL being verified.
|
921 |
+
*/
|
922 |
+
$signature_url = apply_filters( 'wp_signature_url', $signature_url, $url );
|
923 |
+
|
924 |
+
if ( $signature_url ) {
|
925 |
+
$signature_request = wp_safe_remote_get(
|
926 |
+
$signature_url,
|
927 |
+
array(
|
928 |
+
'limit_response_size' => 10 * 1024, // 10KB should be large enough for quite a few signatures.
|
929 |
+
)
|
930 |
+
);
|
931 |
+
|
932 |
+
if ( ! is_wp_error( $signature_request ) && 200 === wp_remote_retrieve_response_code( $signature_request ) ) {
|
933 |
+
$signature = explode( "\n", wp_remote_retrieve_body( $signature_request ) );
|
934 |
+
}
|
935 |
+
}
|
936 |
+
}
|
937 |
+
|
938 |
+
// Perform the checks.
|
939 |
+
$signature_verification = verify_file_signature( $tmpfname, $signature, basename( parse_url( $url, PHP_URL_PATH ) ) );
|
940 |
+
}
|
941 |
+
|
942 |
+
if ( is_wp_error( $signature_verification ) ) {
|
943 |
+
if (
|
944 |
+
/**
|
945 |
+
* Filters whether Signature Verification failures should be allowed to soft fail.
|
946 |
+
*
|
947 |
+
* WARNING: This may be removed from a future release.
|
948 |
+
*
|
949 |
+
* @since 5.2.0
|
950 |
+
*
|
951 |
+
* @param bool $signature_softfail If a softfail is allowed.
|
952 |
+
* @param string $url The url being accessed.
|
953 |
+
*/
|
954 |
+
apply_filters( 'wp_signature_softfail', true, $url )
|
955 |
+
) {
|
956 |
+
$signature_verification->add_data( $tmpfname, 'softfail-filename' );
|
957 |
+
} else {
|
958 |
+
// Hard-fail.
|
959 |
+
unlink( $tmpfname );
|
960 |
+
}
|
961 |
+
|
962 |
+
return $signature_verification;
|
963 |
+
}
|
964 |
+
|
965 |
+
return $tmpfname;
|
966 |
+
}
|
967 |
+
|
968 |
+
function wpa_fetch_file($path){
|
969 |
+
$z = pathinfo($path);
|
970 |
+
global $wp_filesystem;
|
971 |
+
if ( $wp_filesystem->is_file(WPCLONE_DIR_BACKUP . $z['basename']) ) {
|
972 |
+
wpa_wpc_log( 'file exists in the backup folder, filesize - ' . bytesToSize( filesize( WPCLONE_DIR_BACKUP . $z['basename'] ) ) );
|
973 |
+
return WPCLONE_DIR_BACKUP . $z['basename'];
|
974 |
+
}
|
975 |
+
else {
|
976 |
+
wpa_wpc_log( 'file download started' );
|
977 |
+
$url = wcbwa_download_url_alternate($path, ini_get("max_execution_time"));
|
978 |
+
//$url = download_url($path);
|
979 |
+
if ( is_wp_error($url) ) {
|
980 |
+
wpa_backup_error( 'url', $url->get_error_message(), true );
|
981 |
+
}
|
982 |
+
wpa_wpc_log( 'download finished, filesize - ' . bytesToSize( filesize( $url ) ) );
|
983 |
+
return $url;
|
984 |
+
}
|
985 |
+
}
|
986 |
+
|
987 |
+
function wpa_backup_name() {
|
988 |
+
$backup_name = 'wpclone_backup_' . current_time( 'dS_M_Y_h-iA', false ) . '_' . get_option( 'blogname' );
|
989 |
+
$backup_name = substr( str_replace( ' ', '', $backup_name ), 0, 40 );
|
990 |
+
$rand_str = substr( str_shuffle( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" ), 0, 10 );
|
991 |
+
$backup_name = sanitize_file_name( $backup_name ) . '_' . $rand_str;
|
992 |
+
return $backup_name;
|
993 |
+
}
|
994 |
+
|
995 |
+
function wpa_backup_error($error, $data, $restore = false) {
|
996 |
+
|
997 |
+
$temp_dir = $restore ? trailingslashit( WPCLONE_WP_CONTENT ) . 'wpclone-temp' : trailingslashit( WPCLONE_DIR_BACKUP ) . 'wpclone_backup';
|
998 |
+
$disp_dir = str_replace( WPCLONE_ROOT, '**SITE-ROOT**/', wpCloneSafePathMode( $temp_dir ) );
|
999 |
+
|
1000 |
+
if( !file_exists( $temp_dir ) ) {
|
1001 |
+
unset($temp_dir);
|
1002 |
+
}
|
1003 |
+
|
1004 |
+
switch ( $error ) :
|
1005 |
+
/* during backup */
|
1006 |
+
case 'file' :
|
1007 |
+
$error = __( 'while copying files into the temp directory' );
|
1008 |
+
break;
|
1009 |
+
case 'db' :
|
1010 |
+
$error = __( 'during the database backup' );
|
1011 |
+
break;
|
1012 |
+
case 'zip' :
|
1013 |
+
$error = __( 'while creating the zip file using PHP\'s ZipArchive library' );
|
1014 |
+
break;
|
1015 |
+
case 'pclzip' :
|
1016 |
+
$error = __( 'while creating the zip file using the PclZip library' );
|
1017 |
+
break;
|
1018 |
+
/* during restore */
|
1019 |
+
case 'dirrest' :
|
1020 |
+
$error = __( 'while creating the temp directory' );
|
1021 |
+
break;
|
1022 |
+
case 'filerest' :
|
1023 |
+
$error = __( 'while copying files from the temp directory into the wp-content directory' );
|
1024 |
+
break;
|
1025 |
+
case 'dbrest' :
|
1026 |
+
$error = __( 'while cloning the database' );
|
1027 |
+
break;
|
1028 |
+
case 'unzip' :
|
1029 |
+
$error = __( 'while extracting the zip file using php ziparchive' );
|
1030 |
+
break;
|
1031 |
+
case 'pclunzip' :
|
1032 |
+
$error = __( 'while extracting the zip file using the PclZip library' );
|
1033 |
+
break;
|
1034 |
+
case 'url' :
|
1035 |
+
$error = __( 'while downloading the zip file' );
|
1036 |
+
break;
|
1037 |
+
case 'wpconfig' :
|
1038 |
+
$error = __( 'while trying to modify the table prefix in the wp-config.php file' );
|
1039 |
+
break;
|
1040 |
+
/* and a catch all for the things that aren't covered above */
|
1041 |
+
default :
|
1042 |
+
$error = sprintf( __( 'during the %s process' ), $error );
|
1043 |
+
endswitch;
|
1044 |
+
|
1045 |
+
echo '<div class="wpclone_notice updated">';
|
1046 |
+
printf( __( 'The plugin encountered an error %s,the following error message was returned:</br>' ), $error );
|
1047 |
+
echo '<div class="error">' . __( 'Error Message : ' ) . $data . '</div></br>';
|
1048 |
+
if( isset( $temp_dir ) ) {
|
1049 |
+
printf( __( 'Temporary files created in <code>%s</code> will be deleted.' ), $disp_dir );
|
1050 |
+
echo '</div>';
|
1051 |
+
if( $restore ) {
|
1052 |
+
global $wp_filesystem;
|
1053 |
+
$wp_filesystem->delete($temp_dir, true);
|
1054 |
+
} else {
|
1055 |
+
wpa_delete_dir( $temp_dir );
|
1056 |
+
}
|
1057 |
+
} else {
|
1058 |
+
echo '</div>';
|
1059 |
+
}
|
1060 |
+
die;
|
1061 |
+
}
|
1062 |
+
|
1063 |
+
function wpa_cleanup( $restore = false ) {
|
1064 |
+
$backup_dir = $restore ? trailingslashit( WPCLONE_WP_CONTENT ) . 'wpclone-temp' : trailingslashit( WPCLONE_DIR_BACKUP ) . 'wpclone_backup';
|
1065 |
+
if ( file_exists( $backup_dir ) && is_dir( $backup_dir ) ) {
|
1066 |
+
if( $restore ) {
|
1067 |
+
global $wp_filesystem;
|
1068 |
+
$wp_filesystem->delete($backup_dir, true);
|
1069 |
+
} else {
|
1070 |
+
wpa_delete_dir( $backup_dir );
|
1071 |
+
}
|
1072 |
+
}
|
1073 |
+
}
|
1074 |
+
/**
|
1075 |
+
* recursively copies a directory from one place to another. excludes 'uploads/wp-clone' by default.
|
1076 |
+
* @since 2.1.6
|
1077 |
+
* @param string $from
|
1078 |
+
* @param string $to
|
1079 |
+
* @param array $exclude an array of directory paths to exclude.
|
1080 |
+
*/
|
1081 |
+
function wpa_copy_dir( $from, $to, $exclude ) {
|
1082 |
+
if( false === stripos( wpCloneSafePathMode( $from ), rtrim( wpCloneSafePathMode( WPCLONE_DIR_BACKUP ), "/\\" ) ) ) {
|
1083 |
+
if( !file_exists( $to ) )
|
1084 |
+
@mkdir ( $to );
|
1085 |
+
$files = array_diff( scandir( $from ), array( '.', '..' ) );
|
1086 |
+
foreach( $files as $file ) {
|
1087 |
+
if( in_array( $from . '/' . $file, $exclude ) ) {
|
1088 |
+
continue;
|
1089 |
+
} else {
|
1090 |
+
if( is_dir( $from . '/' . $file ) ) {
|
1091 |
+
wpa_copy_dir( $from . '/' . $file, $to . '/' . $file, $exclude );
|
1092 |
+
} else {
|
1093 |
+
@copy( $from . '/' . $file, $to . '/' . $file );
|
1094 |
+
}
|
1095 |
+
}
|
1096 |
+
}
|
1097 |
+
unset( $files );
|
1098 |
+
}
|
1099 |
+
}
|
1100 |
+
/**
|
1101 |
+
* recursively deletes all the files in the given directory.
|
1102 |
+
* @since 2.1.6
|
1103 |
+
* @param string $dir path to the directory that needs to be deleted.
|
1104 |
+
*/
|
1105 |
+
function wpa_delete_dir( $dir ) {
|
1106 |
+
if( !empty( $dir ) ) {
|
1107 |
+
$dir = trailingslashit( $dir );
|
1108 |
+
$files = array_diff( scandir( $dir ), array( '.', '..' ) );
|
1109 |
+
foreach ( $files as $file ) {
|
1110 |
+
if( is_dir( $dir . $file ) ) {
|
1111 |
+
wpa_delete_dir( $dir . $file );
|
1112 |
+
} else {
|
1113 |
+
@unlink( $dir . $file );
|
1114 |
+
}
|
1115 |
+
}
|
1116 |
+
@rmdir($dir);
|
1117 |
+
}
|
1118 |
+
}
|
1119 |
+
/**
|
1120 |
+
* @since 2.1.6
|
1121 |
+
*/
|
1122 |
+
function wpa_excluded_dirs() {
|
1123 |
+
$exclude = array();
|
1124 |
+
if( isset( $_POST['exclude'] ) && '' != $_POST['exclude'] ) {
|
1125 |
+
foreach( explode( "\n", $_POST['exclude'] ) as $ex ) {
|
1126 |
+
$ex = trim( $ex );
|
1127 |
+
if( '' !== $ex ) {
|
1128 |
+
$ex = trim( $ex, "/\\" );
|
1129 |
+
wpa_wpc_log( sprintf( 'files inside "**SITE_ROOT**/wp-content/%s/" will not be included in the backup', $ex ) );
|
1130 |
+
$exclude[] = wpCloneSafePathMode( trailingslashit( WPCLONE_WP_CONTENT ) . $ex ) ;
|
1131 |
+
}
|
1132 |
+
}
|
1133 |
+
}
|
1134 |
+
return $exclude;
|
1135 |
+
}
|
1136 |
+
|
1137 |
+
function wpa_wpc_dir_size( $path ) {
|
1138 |
+
|
1139 |
+
$i = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $path ) );
|
1140 |
+
$size = 0;
|
1141 |
+
$files = 0;
|
1142 |
+
|
1143 |
+
foreach( $i as $file => $info ) {
|
1144 |
+
|
1145 |
+
if( false === strpos( wpCloneSafePathMode( $file ), WPCLONE_DIR_BACKUP ) ) {
|
1146 |
+
$size += $info->getSize();
|
1147 |
+
$files++;
|
1148 |
+
|
1149 |
+
}
|
1150 |
+
|
1151 |
+
}
|
1152 |
+
|
1153 |
+
$ret = array(
|
1154 |
+
'dbsize' => wpa_wpc_db_size(),
|
1155 |
+
'size' => bytesToSize( $size ),
|
1156 |
+
'files' => $files,
|
1157 |
+
'time' => time()
|
1158 |
+
);
|
1159 |
+
|
1160 |
+
update_option( 'wpclone_directory_scan', $ret );
|
1161 |
+
unset( $ret['time'] );
|
1162 |
+
return $ret;
|
1163 |
+
|
1164 |
+
}
|
1165 |
+
|
1166 |
+
function wpa_wpc_db_size() {
|
1167 |
+
|
1168 |
+
global $wpdb;
|
1169 |
+
$sql = 'SELECT sum(data_length + index_length) FROM information_schema.TABLES WHERE table_schema = "' . DB_NAME . '"';
|
1170 |
+
$size = $wpdb->get_var( $sql );
|
1171 |
+
return bytesToSize( $size );
|
1172 |
+
|
1173 |
+
}
|
1174 |
+
|
1175 |
+
function wpa_wpc_scan_dir() {
|
1176 |
+
|
1177 |
+
$backups = get_option( 'wpclone_backups' );
|
1178 |
+
$backup_list = array();
|
1179 |
+
$files = array();
|
1180 |
+
$old_backups = array();
|
1181 |
+
|
1182 |
+
foreach( glob( WPCLONE_DIR_BACKUP . '*.zip' ) as $file ){
|
1183 |
+
|
1184 |
+
$files[] = str_replace( WPCLONE_DIR_BACKUP, '', $file );
|
1185 |
+
|
1186 |
+
}
|
1187 |
+
|
1188 |
+
if( false === $backups ) {
|
1189 |
+
$backups = array();
|
1190 |
+
}
|
1191 |
+
|
1192 |
+
foreach( $backups as $key => $backup ) {
|
1193 |
+
|
1194 |
+
if( ! file_exists( WPCLONE_DIR_BACKUP . $backup['name'] ) ) {
|
1195 |
+
unset( $backups[$key] );
|
1196 |
+
continue;
|
1197 |
+
}
|
1198 |
+
$backup_list[] = $backup['name'];
|
1199 |
+
|
1200 |
+
}
|
1201 |
+
|
1202 |
+
|
1203 |
+
$list = wpa_wpc_filter_list( $files, $backup_list );
|
1204 |
+
|
1205 |
+
if( ! empty( $list ) ) {
|
1206 |
+
|
1207 |
+
foreach( $list as $backup ) {
|
1208 |
+
|
1209 |
+
$time = strtotime( substr( str_replace( array( 'wpclone_backup_', '_', '-' ), array( '', ' ', ':' ), $backup ), 0, 21 ) ) + rand(1, 60);
|
1210 |
+
$old_backups[$time] = array(
|
1211 |
+
'name' => $backup,
|
1212 |
+
'creator' => 'dirscan',
|
1213 |
+
'size' => @filesize( WPCLONE_DIR_BACKUP . $backup )
|
1214 |
+
);
|
1215 |
+
|
1216 |
+
}
|
1217 |
+
|
1218 |
+
}
|
1219 |
+
|
1220 |
+
$backups = $backups + $old_backups;
|
1221 |
+
ksort( $backups );
|
1222 |
+
update_option( 'wpclone_backups', $backups );
|
1223 |
+
|
1224 |
+
}
|
1225 |
+
|
1226 |
+
/*
|
1227 |
+
* @link http://stackoverflow.com/questions/2479963/how-does-array-diff-work/6700430#6700430
|
1228 |
+
*/
|
1229 |
+
function wpa_wpc_filter_list( $a, $b ) {
|
1230 |
+
|
1231 |
+
$return = array();
|
1232 |
+
foreach( $a as $v ) {
|
1233 |
+
$return[$v] = '';
|
1234 |
+
}
|
1235 |
+
foreach( $b as $v ) {
|
1236 |
+
unset( $return[$v] );
|
1237 |
+
}
|
1238 |
+
return array_keys( $return );
|
1239 |
+
|
1240 |
+
}
|
1241 |
+
|
1242 |
+
function wpa_wpc_log( $msg ) {
|
1243 |
+
|
1244 |
+
if( ! isset( $GLOBALS['wpclone']['logfile'] ) ) {
|
1245 |
+
return;
|
1246 |
+
}
|
1247 |
+
$file = WPCLONE_DIR_BACKUP . $GLOBALS['wpclone']['logfile'];
|
1248 |
+
$time = date( 'l, d-M-Y H:i:s', time() + ( get_option( 'gmt_offset' ) * HOUR_IN_SECONDS ) );
|
1249 |
+
$msg = $time . ' - ' . $msg . "\r\n";
|
1250 |
+
file_put_contents( $file, $msg, FILE_APPEND );
|
1251 |
+
|
1252 |
+
}
|
1253 |
+
|
1254 |
+
function wpa_wpc_log_start( $action ) {
|
1255 |
+
|
1256 |
+
global $wp_version;
|
1257 |
+
global $wpdb;
|
1258 |
+
|
1259 |
+
wpa_wpc_log( sprintf( '%s started', $action ) );
|
1260 |
+
wpa_wpc_log( 'wp version : ' . $wp_version );
|
1261 |
+
wpa_wpc_log( 'php version : ' . phpversion() );
|
1262 |
+
wpa_wpc_log( 'mysql version : ' . $wpdb->db_version() );
|
1263 |
+
wpa_wpc_log( 'memory limit : ' . ini_get( 'memory_limit' ) );
|
1264 |
+
wpa_wpc_log( 'execution time : ' . ini_get( 'max_execution_time' ) );
|
1265 |
+
wpa_wpc_log( 'mysql timeout : ' . ini_get( 'mysql.connect_timeout' ) );
|
1266 |
+
wpa_wpc_log( 'socket timeout : ' . ini_get( 'default_socket_timeout' ) );
|
1267 |
+
|
1268 |
+
}
|
1269 |
+
|
1270 |
+
function wpa_wpc_strpos_array( $array, $haystack ) {
|
1271 |
+
|
1272 |
+
foreach( $array as $needle ) {
|
1273 |
+
if( false !== strpos( $haystack, $needle ) ) {
|
1274 |
+
return true;
|
1275 |
+
|
1276 |
+
}
|
1277 |
+
|
1278 |
+
}
|
1279 |
+
|
1280 |
+
}
|
1281 |
+
|
1282 |
+
function wpa_wpc_get_filelist( $path, $exclude, $skip = false ) {
|
1283 |
+
|
1284 |
+
$i = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $path, FilesystemIterator::CURRENT_AS_SELF | FilesystemIterator::UNIX_PATHS | FilesystemIterator::SKIP_DOTS ) );
|
1285 |
+
$skipped = 0;
|
1286 |
+
$size = 0;
|
1287 |
+
$files = 0;
|
1288 |
+
$list = array();
|
1289 |
+
|
1290 |
+
foreach( $i as $file => $info ) {
|
1291 |
+
|
1292 |
+
$file = wpCloneSafePathMode( $file );
|
1293 |
+
|
1294 |
+
if( false !== strpos( $file, WPCLONE_DIR_BACKUP ) ) {
|
1295 |
+
continue;
|
1296 |
+
}
|
1297 |
+
|
1298 |
+
if( false !== strpos( $file, WPCLONE_DIR_PLUGIN ) ) {
|
1299 |
+
continue;
|
1300 |
+
}
|
1301 |
+
|
1302 |
+
if( ! $info->isReadable() ) {
|
1303 |
+
$skipped++;
|
1304 |
+
wpa_wpc_log( sprintf( 'file skipped, file is not readable - "%s"',
|
1305 |
+
str_replace( WPCLONE_ROOT, '**SITE-ROOT**/', $file ) ) );
|
1306 |
+
continue;
|
1307 |
+
|
1308 |
+
}
|
1309 |
+
|
1310 |
+
if( ! empty( $exclude ) && wpa_wpc_strpos_array( $exclude, $file ) ) {
|
1311 |
+
$skipped++;
|
1312 |
+
wpa_wpc_log( sprintf( 'file is inside an excluded directory, and it will not be included in the backup - "%s"',
|
1313 |
+
str_replace( WPCLONE_ROOT, '**SITE-ROOT**/', $file ) ) );
|
1314 |
+
continue;
|
1315 |
+
|
1316 |
+
}
|
1317 |
+
|
1318 |
+
if( $skip && $info->getSize() > $skip ) {
|
1319 |
+
$skipped++;
|
1320 |
+
wpa_wpc_log( sprintf( 'file skipped, file is larger than %s - "%s" %s',
|
1321 |
+
bytesToSize( $skip ), str_replace( WPCLONE_ROOT, '**SITE-ROOT**/', $file ), bytesToSize( $info->getSize() ) ) );
|
1322 |
+
continue;
|
1323 |
+
|
1324 |
+
}
|
1325 |
+
|
1326 |
+
if( $info->isFile() ) {
|
1327 |
+
$list[] = $file;
|
1328 |
+
$files++;
|
1329 |
+
$size += $info->getSize();
|
1330 |
+
|
1331 |
+
}
|
1332 |
+
|
1333 |
+
}
|
1334 |
+
|
1335 |
+
if( $skipped > 0 ) {
|
1336 |
+
wpa_wpc_log( sprintf( '%d files were excluded from the backup', $skipped ) );
|
1337 |
+
}
|
1338 |
+
|
1339 |
+
wpa_wpc_log( sprintf( 'number of files to include in the archive is %d, and their uncompressed size is %s',
|
1340 |
+
$files, bytesToSize( $size ) ) );
|
1341 |
+
|
1342 |
+
return $list;
|
1343 |
+
|
1344 |
+
}
|
1345 |
+
|
1346 |
+
|
1347 |
+
function wpa_wpc_zip( $zh, $zipmode ) {
|
1348 |
+
|
1349 |
+
$file = WPCLONE_DIR_BACKUP . 'wpclone_backup/file.list';
|
1350 |
+
|
1351 |
+
if( is_readable( $file ) ) {
|
1352 |
+
$filelist = unserialize( file_get_contents( $file ) );
|
1353 |
+
|
1354 |
+
} else {
|
1355 |
+
return false;
|
1356 |
+
|
1357 |
+
}
|
1358 |
+
|
1359 |
+
if( $zipmode ) {
|
1360 |
+
return $filelist;
|
1361 |
+
|
1362 |
+
}
|
1363 |
+
|
1364 |
+
foreach( $filelist as $file ) {
|
1365 |
+
$zh->addFile( $file, str_replace( WPCLONE_ROOT, 'wpclone_backup/', $file ) );
|
1366 |
+
|
1367 |
+
}
|
1368 |
+
|
1369 |
+
$zh->deleteName( 'wpclone_backup/file.list' );
|
1370 |
+
|
1371 |
+
}
|
1372 |
+
|
1373 |
+
function wpa_wpc_unzip( $zipfile, $temp_dir ) {
|
1374 |
+
|
1375 |
+
$z = new ZipArchive();
|
1376 |
+
|
1377 |
+
if( true === $z->open( $zipfile ) ) {
|
1378 |
+
$z->extractTo( $temp_dir );
|
1379 |
+
|
1380 |
+
} else {
|
1381 |
+
wpa_wpc_log( sprintf( 'failed to open the zip file : %s', $z->getStatusString() ) );
|
1382 |
+
wpa_backup_error( 'unzip', $z->getStatusString(), true );
|
1383 |
+
|
1384 |
+
}
|
1385 |
+
|
1386 |
+
}
|
1387 |
+
|
1388 |
+
function wpa_wpc_get_db( $zipfile, $zipmode ) {
|
1389 |
+
$ret = array();
|
1390 |
+
if( $zipmode || ( ! in_array( 'ZipArchive', get_declared_classes() ) || ! class_exists( 'ZipArchive' ) ) ) {
|
1391 |
+
|
1392 |
+
wpa_wpc_log( 'extracting database using pclzip' );
|
1393 |
+
|
1394 |
+
if ( ini_get('mbstring.func_overload') && function_exists('mb_internal_encoding') ) {
|
1395 |
+
$previous_encoding = mb_internal_encoding();
|
1396 |
+
mb_internal_encoding('ISO-8859-1');
|
1397 |
+
}
|
1398 |
+
|
1399 |
+
require_once ( ABSPATH . 'wp-admin/includes/class-pclzip.php' );
|
1400 |
+
$z = new PclZip($zipfile);
|
1401 |
+
$database = $z->extract( PCLZIP_OPT_BY_NAME, 'wpclone_backup/database.sql', PCLZIP_OPT_EXTRACT_AS_STRING );
|
1402 |
+
$prefix = $z->extract( PCLZIP_OPT_BY_NAME, 'wpclone_backup/prefix.txt', PCLZIP_OPT_EXTRACT_AS_STRING );
|
1403 |
+
|
1404 |
+
if ( isset( $previous_encoding ) ) {
|
1405 |
+
mb_internal_encoding($previous_encoding);
|
1406 |
+
|
1407 |
+
}
|
1408 |
+
|
1409 |
+
if( 'ok' === $database[0]['status'] && 'ok' === $prefix[0]['status'] ) {
|
1410 |
+
$ret['database'] = $database[0]['content'];
|
1411 |
+
$ret['prefix'] = $prefix[0]['content'];
|
1412 |
+
|
1413 |
+
} else {
|
1414 |
+
wpa_backup_error( 'pclunzip', $z->errorInfo(true), true );
|
1415 |
+
|
1416 |
+
}
|
1417 |
+
|
1418 |
+
return $ret;
|
1419 |
+
|
1420 |
+
} else {
|
1421 |
+
|
1422 |
+
$z = new ZipArchive();
|
1423 |
+
|
1424 |
+
if( true === $z->open( $zipfile ) ) {
|
1425 |
+
|
1426 |
+
wpa_wpc_log( 'extracting database using ziparchive' );
|
1427 |
+
$ret['database'] = $z->getFromName( 'wpclone_backup/database.sql' );
|
1428 |
+
$ret['prefix'] = $z->getFromName( 'wpclone_backup/prefix.txt' );
|
1429 |
+
if( false === $ret['database'] || false === $ret['prefix'] ) {
|
1430 |
+
wpa_backup_error( 'unzip', $z->getStatusString(), true );
|
1431 |
+
|
1432 |
+
}
|
1433 |
+
|
1434 |
+
$z->close();
|
1435 |
+
return $ret;
|
1436 |
+
|
1437 |
+
} else {
|
1438 |
+
wpa_backup_error( 'unzip', $z->getStatusString(), true );
|
1439 |
+
|
1440 |
+
}
|
1441 |
+
|
1442 |
+
}
|
1443 |
+
|
1444 |
+
}
|
1445 |
+
|
1446 |
+
function wpa_wpc_process_db( $zipfile, $zipmode = false ) {
|
1447 |
+
|
1448 |
+
$files = wpa_wpc_get_db( $zipfile, $zipmode );
|
1449 |
+
|
1450 |
+
$prefix = wpa_wpc_get_prefix( $files['prefix'] );
|
1451 |
+
$old_url = untrailingslashit( wpa_wpc_get_url( $files['database'] ) );
|
1452 |
+
$cur_url = untrailingslashit( site_url() );
|
1453 |
+
$found = false;
|
1454 |
+
$db = explode( ";\n", $files['database'] );
|
1455 |
+
$wpcdb = wpa_wpc_mysql_connect();
|
1456 |
+
|
1457 |
+
wpa_wpc_log( 'database import started' );
|
1458 |
+
foreach( $db as $query ) {
|
1459 |
+
|
1460 |
+
if( ! $found && false !== strpos( $query, '"siteurl",' ) ) {
|
1461 |
+
$query = str_replace( $old_url, $cur_url, $query, $count );
|
1462 |
+
wpa_wpc_log( sprintf( 'updating mysql query with current site\'s url - new query : "%s"', ltrim( $query ) ) );
|
1463 |
+
if( $count > 0 ) {
|
1464 |
+
$found = true;
|
1465 |
+
|
1466 |
+
}
|
1467 |
+
|
1468 |
+
}
|
1469 |
+
|
1470 |
+
if( isset( $_POST['mysql_check'] ) && 'true' === $_POST['mysql_check'] ) {
|
1471 |
+
if( ! $wpcdb->ping() ) {
|
1472 |
+
$wpcdb->close();
|
1473 |
+
$wpcdb = wpa_wpc_mysql_connect();
|
1474 |
+
}
|
1475 |
+
}
|
1476 |
+
|
1477 |
+
$status = $wpcdb->query( $query );
|
1478 |
+
|
1479 |
+
if( false === $status ) {
|
1480 |
+
wpa_wpc_log( sprintf( 'mysql query failed. error : %d %s - query : "%s"', $wpcdb->errno(), $wpcdb->error(), ltrim( $query ) ) );
|
1481 |
+
}
|
1482 |
+
|
1483 |
+
}
|
1484 |
+
wpa_wpc_log( 'database import finished' );
|
1485 |
+
|
1486 |
+
if( $cur_url === $old_url ) {
|
1487 |
+
wpa_wpc_log( 'URLs are similar, skipping search and replace' );
|
1488 |
+
$report = 'Search and replace did not run because the URLs are similar';
|
1489 |
+
|
1490 |
+
} else {
|
1491 |
+
$report = wpa_safe_replace_wrapper( $old_url, $cur_url, $prefix );
|
1492 |
+
|
1493 |
+
}
|
1494 |
+
|
1495 |
+
return $report;
|
1496 |
+
|
1497 |
+
|
1498 |
+
}
|
1499 |
+
|
1500 |
+
function wpa_wpc_get_prefix( $prev_prefix ) {
|
1501 |
+
|
1502 |
+
global $wpdb;
|
1503 |
+
$cur_prefix = $wpdb->prefix;
|
1504 |
+
|
1505 |
+
if ( $cur_prefix !== $prev_prefix ) {
|
1506 |
+
wpa_wpc_log( sprintf( 'changing prefix from "%s" to "%s"', $cur_prefix, $prev_prefix ) );
|
1507 |
+
wpa_replace_prefix( $cur_prefix, $prev_prefix );
|
1508 |
+
$cur_prefix = $prev_prefix;
|
1509 |
+
|
1510 |
+
}
|
1511 |
+
|
1512 |
+
return $cur_prefix;
|
1513 |
+
|
1514 |
+
}
|
1515 |
+
|
1516 |
+
|
1517 |
/* end of file */
|
lib/icit_srdb_replacer.php
CHANGED
@@ -1,154 +1,154 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* Take a serialised array and unserialise it replacing elements as needed and
|
4 |
-
* unserialising any subordinate arrays and performing the replace on those too.
|
5 |
-
*
|
6 |
-
* @param string $from String we're looking to replace.
|
7 |
-
* @param string $to What we want it to be replaced with
|
8 |
-
* @param array $data Used to pass any subordinate arrays back to in.
|
9 |
-
* @param bool $serialised Does the array passed via $data need serialising.
|
10 |
-
*
|
11 |
-
* @return array The original array with all elements replaced as needed.
|
12 |
-
*/
|
13 |
-
function recursive_unserialize_replace( $from = '', $to = '', $data = '', $serialised = false ) {
|
14 |
-
|
15 |
-
// some unseriliased data cannot be re-serialised eg. SimpleXMLElements
|
16 |
-
try {
|
17 |
-
|
18 |
-
if ( is_string( $data ) && ( $unserialized = @unserialize( $data ) ) !== false ) {
|
19 |
-
$data = recursive_unserialize_replace( $from, $to, $unserialized, true );
|
20 |
-
}
|
21 |
-
|
22 |
-
elseif ( is_array( $data ) ) {
|
23 |
-
$_tmp = array( );
|
24 |
-
foreach ( $data as $key => $value ) {
|
25 |
-
$_tmp[ $key ] = recursive_unserialize_replace( $from, $to, $value, false );
|
26 |
-
}
|
27 |
-
|
28 |
-
$data = $_tmp;
|
29 |
-
unset( $_tmp );
|
30 |
-
}
|
31 |
-
|
32 |
-
else {
|
33 |
-
if ( is_string( $data ) )
|
34 |
-
$data = str_replace( $from, $to, $data );
|
35 |
-
}
|
36 |
-
|
37 |
-
if ( $serialised )
|
38 |
-
return serialize( $data );
|
39 |
-
|
40 |
-
} catch( Exception $error ) {
|
41 |
-
|
42 |
-
}
|
43 |
-
|
44 |
-
return $data;
|
45 |
-
}
|
46 |
-
|
47 |
-
/**
|
48 |
-
* The main loop triggered in step 5. Up here to keep it out of the way of the
|
49 |
-
* HTML. This walks every table in the db that was selected in step 3 and then
|
50 |
-
* walks every row and column replacing all occurences of a string with another.
|
51 |
-
* We split large tables into 50,000 row blocks when dealing with them to save
|
52 |
-
* on memmory consumption.
|
53 |
-
*
|
54 |
-
* @param wpc_wpdb $wpcdb The db wrapper object
|
55 |
-
* @param string $search What we want to replace
|
56 |
-
* @param string $replace What we want to replace it with.
|
57 |
-
* @param array $tables The tables we want to look at.
|
58 |
-
*
|
59 |
-
* @return array Collection of information gathered during the run.
|
60 |
-
*/
|
61 |
-
function icit_srdb_replacer( $wpcdb, $search = '', $replace = '', $tables = array( ) ) {
|
62 |
-
global $guid, $exclude_cols;
|
63 |
-
|
64 |
-
$report = array( 'tables' => 0,
|
65 |
-
'rows' => 0,
|
66 |
-
'change' => 0,
|
67 |
-
'updates' => 0,
|
68 |
-
'start' => microtime( ),
|
69 |
-
'end' => microtime( ),
|
70 |
-
'errors' => array( ),
|
71 |
-
);
|
72 |
-
|
73 |
-
if ( is_array( $tables ) && ! empty( $tables ) ) {
|
74 |
-
foreach( $tables as $table ) {
|
75 |
-
$report[ 'tables' ]++;
|
76 |
-
|
77 |
-
$columns = array( );
|
78 |
-
|
79 |
-
// Get a list of columns in this table
|
80 |
-
$fields = $wpcdb->query( 'DESCRIBE ' . $table );
|
81 |
-
while( $column = $wpcdb->fetch_array( $fields ) )
|
82 |
-
$columns[ $column[ 'Field' ] ] = $column[ 'Key' ] == 'PRI' ? true : false;
|
83 |
-
|
84 |
-
// Count the number of rows we have in the table if large we'll split into blocks, This is a mod from Simon Wheatley
|
85 |
-
$row_count = $wpcdb->query( 'SELECT COUNT(*) FROM ' . $table );
|
86 |
-
$rows_result = $wpcdb->fetch_array( $row_count );
|
87 |
-
$row_count = $rows_result[ 0 ];
|
88 |
-
if ( $row_count == 0 )
|
89 |
-
continue;
|
90 |
-
|
91 |
-
$page_size = 50000;
|
92 |
-
$pages = ceil( $row_count / $page_size );
|
93 |
-
|
94 |
-
for( $page = 0; $page < $pages; $page++ ) {
|
95 |
-
|
96 |
-
$current_row = 0;
|
97 |
-
$start = $page * $page_size;
|
98 |
-
$end = $start + $page_size;
|
99 |
-
// Grab the content of the table
|
100 |
-
$data = $wpcdb->query( sprintf( 'SELECT * FROM %s LIMIT %d, %d', $table, $start, $end ) );
|
101 |
-
|
102 |
-
if ( ! $data )
|
103 |
-
$report[ 'errors' ][] = $wpcdb->error();
|
104 |
-
|
105 |
-
while ( $row = $wpcdb->fetch_array( $data ) ) {
|
106 |
-
|
107 |
-
$report[ 'rows' ]++; // Increment the row counter
|
108 |
-
$current_row++;
|
109 |
-
|
110 |
-
$update_sql = array( );
|
111 |
-
$where_sql = array( );
|
112 |
-
$upd = false;
|
113 |
-
|
114 |
-
foreach( $columns as $column => $primary_key ) {
|
115 |
-
if ( $guid == 1 && in_array( $column, $exclude_cols ) )
|
116 |
-
continue;
|
117 |
-
|
118 |
-
$edited_data = $data_to_fix = $row[ $column ];
|
119 |
-
|
120 |
-
// Run a search replace on the data that'll respect the serialisation.
|
121 |
-
$edited_data = recursive_unserialize_replace( $search, $replace, $data_to_fix );
|
122 |
-
|
123 |
-
// Something was changed
|
124 |
-
if ( $edited_data != $data_to_fix ) {
|
125 |
-
$report[ 'change' ]++;
|
126 |
-
$update_sql[] = $column . ' = "' . $wpcdb->real_escape_string( $edited_data ) . '"';
|
127 |
-
$upd = true;
|
128 |
-
}
|
129 |
-
|
130 |
-
if ( $primary_key )
|
131 |
-
$where_sql[] = $column . ' = "' . $wpcdb->real_escape_string( $data_to_fix ) . '"';
|
132 |
-
}
|
133 |
-
|
134 |
-
if ( $upd && ! empty( $where_sql ) ) {
|
135 |
-
$sql = 'UPDATE ' . $table . ' SET ' . implode( ', ', $update_sql ) . ' WHERE ' . implode( ' AND ', array_filter( $where_sql ) );
|
136 |
-
$result = $wpcdb->query( $sql );
|
137 |
-
if ( ! $result )
|
138 |
-
$report[ 'errors' ][] = $wpcdb->error( );
|
139 |
-
else
|
140 |
-
$report[ 'updates' ]++;
|
141 |
-
|
142 |
-
} elseif ( $upd ) {
|
143 |
-
$report[ 'errors' ][] = sprintf( '"%s" has no primary key, manual change needed on row %s.', $table, $current_row );
|
144 |
-
}
|
145 |
-
|
146 |
-
}
|
147 |
-
}
|
148 |
-
}
|
149 |
-
|
150 |
-
}
|
151 |
-
$report[ 'end' ] = microtime( );
|
152 |
-
|
153 |
-
return $report;
|
154 |
-
}
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Take a serialised array and unserialise it replacing elements as needed and
|
4 |
+
* unserialising any subordinate arrays and performing the replace on those too.
|
5 |
+
*
|
6 |
+
* @param string $from String we're looking to replace.
|
7 |
+
* @param string $to What we want it to be replaced with
|
8 |
+
* @param array $data Used to pass any subordinate arrays back to in.
|
9 |
+
* @param bool $serialised Does the array passed via $data need serialising.
|
10 |
+
*
|
11 |
+
* @return array The original array with all elements replaced as needed.
|
12 |
+
*/
|
13 |
+
function recursive_unserialize_replace( $from = '', $to = '', $data = '', $serialised = false ) {
|
14 |
+
|
15 |
+
// some unseriliased data cannot be re-serialised eg. SimpleXMLElements
|
16 |
+
try {
|
17 |
+
|
18 |
+
if ( is_string( $data ) && ( $unserialized = @unserialize( $data ) ) !== false ) {
|
19 |
+
$data = recursive_unserialize_replace( $from, $to, $unserialized, true );
|
20 |
+
}
|
21 |
+
|
22 |
+
elseif ( is_array( $data ) ) {
|
23 |
+
$_tmp = array( );
|
24 |
+
foreach ( $data as $key => $value ) {
|
25 |
+
$_tmp[ $key ] = recursive_unserialize_replace( $from, $to, $value, false );
|
26 |
+
}
|
27 |
+
|
28 |
+
$data = $_tmp;
|
29 |
+
unset( $_tmp );
|
30 |
+
}
|
31 |
+
|
32 |
+
else {
|
33 |
+
if ( is_string( $data ) )
|
34 |
+
$data = str_replace( $from, $to, $data );
|
35 |
+
}
|
36 |
+
|
37 |
+
if ( $serialised )
|
38 |
+
return serialize( $data );
|
39 |
+
|
40 |
+
} catch( Exception $error ) {
|
41 |
+
|
42 |
+
}
|
43 |
+
|
44 |
+
return $data;
|
45 |
+
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* The main loop triggered in step 5. Up here to keep it out of the way of the
|
49 |
+
* HTML. This walks every table in the db that was selected in step 3 and then
|
50 |
+
* walks every row and column replacing all occurences of a string with another.
|
51 |
+
* We split large tables into 50,000 row blocks when dealing with them to save
|
52 |
+
* on memmory consumption.
|
53 |
+
*
|
54 |
+
* @param wpc_wpdb $wpcdb The db wrapper object
|
55 |
+
* @param string $search What we want to replace
|
56 |
+
* @param string $replace What we want to replace it with.
|
57 |
+
* @param array $tables The tables we want to look at.
|
58 |
+
*
|
59 |
+
* @return array Collection of information gathered during the run.
|
60 |
+
*/
|
61 |
+
function icit_srdb_replacer( $wpcdb, $search = '', $replace = '', $tables = array( ) ) {
|
62 |
+
global $guid, $exclude_cols;
|
63 |
+
|
64 |
+
$report = array( 'tables' => 0,
|
65 |
+
'rows' => 0,
|
66 |
+
'change' => 0,
|
67 |
+
'updates' => 0,
|
68 |
+
'start' => microtime( ),
|
69 |
+
'end' => microtime( ),
|
70 |
+
'errors' => array( ),
|
71 |
+
);
|
72 |
+
|
73 |
+
if ( is_array( $tables ) && ! empty( $tables ) ) {
|
74 |
+
foreach( $tables as $table ) {
|
75 |
+
$report[ 'tables' ]++;
|
76 |
+
|
77 |
+
$columns = array( );
|
78 |
+
|
79 |
+
// Get a list of columns in this table
|
80 |
+
$fields = $wpcdb->query( 'DESCRIBE ' . $table );
|
81 |
+
while( $column = $wpcdb->fetch_array( $fields ) )
|
82 |
+
$columns[ $column[ 'Field' ] ] = $column[ 'Key' ] == 'PRI' ? true : false;
|
83 |
+
|
84 |
+
// Count the number of rows we have in the table if large we'll split into blocks, This is a mod from Simon Wheatley
|
85 |
+
$row_count = $wpcdb->query( 'SELECT COUNT(*) FROM ' . $table );
|
86 |
+
$rows_result = $wpcdb->fetch_array( $row_count );
|
87 |
+
$row_count = $rows_result[ 0 ];
|
88 |
+
if ( $row_count == 0 )
|
89 |
+
continue;
|
90 |
+
|
91 |
+
$page_size = 50000;
|
92 |
+
$pages = ceil( $row_count / $page_size );
|
93 |
+
|
94 |
+
for( $page = 0; $page < $pages; $page++ ) {
|
95 |
+
|
96 |
+
$current_row = 0;
|
97 |
+
$start = $page * $page_size;
|
98 |
+
$end = $start + $page_size;
|
99 |
+
// Grab the content of the table
|
100 |
+
$data = $wpcdb->query( sprintf( 'SELECT * FROM %s LIMIT %d, %d', $table, $start, $end ) );
|
101 |
+
|
102 |
+
if ( ! $data )
|
103 |
+
$report[ 'errors' ][] = $wpcdb->error();
|
104 |
+
|
105 |
+
while ( $row = $wpcdb->fetch_array( $data ) ) {
|
106 |
+
|
107 |
+
$report[ 'rows' ]++; // Increment the row counter
|
108 |
+
$current_row++;
|
109 |
+
|
110 |
+
$update_sql = array( );
|
111 |
+
$where_sql = array( );
|
112 |
+
$upd = false;
|
113 |
+
|
114 |
+
foreach( $columns as $column => $primary_key ) {
|
115 |
+
if ( $guid == 1 && in_array( $column, $exclude_cols ) )
|
116 |
+
continue;
|
117 |
+
|
118 |
+
$edited_data = $data_to_fix = $row[ $column ];
|
119 |
+
|
120 |
+
// Run a search replace on the data that'll respect the serialisation.
|
121 |
+
$edited_data = recursive_unserialize_replace( $search, $replace, $data_to_fix );
|
122 |
+
|
123 |
+
// Something was changed
|
124 |
+
if ( $edited_data != $data_to_fix ) {
|
125 |
+
$report[ 'change' ]++;
|
126 |
+
$update_sql[] = $column . ' = "' . $wpcdb->real_escape_string( $edited_data ) . '"';
|
127 |
+
$upd = true;
|
128 |
+
}
|
129 |
+
|
130 |
+
if ( $primary_key )
|
131 |
+
$where_sql[] = $column . ' = "' . $wpcdb->real_escape_string( $data_to_fix ) . '"';
|
132 |
+
}
|
133 |
+
|
134 |
+
if ( $upd && ! empty( $where_sql ) ) {
|
135 |
+
$sql = 'UPDATE ' . $table . ' SET ' . implode( ', ', $update_sql ) . ' WHERE ' . implode( ' AND ', array_filter( $where_sql ) );
|
136 |
+
$result = $wpcdb->query( $sql );
|
137 |
+
if ( ! $result )
|
138 |
+
$report[ 'errors' ][] = $wpcdb->error( );
|
139 |
+
else
|
140 |
+
$report[ 'updates' ]++;
|
141 |
+
|
142 |
+
} elseif ( $upd ) {
|
143 |
+
$report[ 'errors' ][] = sprintf( '"%s" has no primary key, manual change needed on row %s.', $table, $current_row );
|
144 |
+
}
|
145 |
+
|
146 |
+
}
|
147 |
+
}
|
148 |
+
}
|
149 |
+
|
150 |
+
}
|
151 |
+
$report[ 'end' ] = microtime( );
|
152 |
+
|
153 |
+
return $report;
|
154 |
+
}
|
lib/js/backupmanager.js
CHANGED
@@ -1,354 +1,354 @@
|
|
1 |
-
jQuery(function($) {
|
2 |
-
|
3 |
-
initialize();
|
4 |
-
bindActions();
|
5 |
-
getSize();
|
6 |
-
scanBackupDir();
|
7 |
-
uninstall();
|
8 |
-
deleteFile();
|
9 |
-
|
10 |
-
function initialize() {
|
11 |
-
$("input[name$='backupChoice']").removeAttr('checked');
|
12 |
-
checkCreateBackupOption();
|
13 |
-
}
|
14 |
-
|
15 |
-
function bindActions() {
|
16 |
-
|
17 |
-
$("a#close-thickbox").click(function(e) {
|
18 |
-
e.preventDefault();
|
19 |
-
tb_remove();
|
20 |
-
$("div#search-n-replace-info").html('');
|
21 |
-
$("input[name='searchfor'], input[name='replacewith']").val('');
|
22 |
-
$("input[name='ignoreprefix']").attr('checked', false);
|
23 |
-
});
|
24 |
-
|
25 |
-
$("input[name='search-n-replace-submit']").click(function(e) {
|
26 |
-
e.preventDefault();
|
27 |
-
if( $(this).data("working") ) return;
|
28 |
-
$("#search-n-replace-info").html('').css( 'background', 'url(' + wpclone.spinner + ') no-repeat center' )
|
29 |
-
$(this).data("working", true);
|
30 |
-
search_and_replace();
|
31 |
-
});
|
32 |
-
|
33 |
-
$("input[id='fullBackup']").click(function() {
|
34 |
-
|
35 |
-
$("#RestoreOptions").hide("fast");
|
36 |
-
$("#file_directory").hide("fast");
|
37 |
-
$("input[name$='createBackup']").attr('checked', true);
|
38 |
-
$("input[name$='backupUrl']").removeAttr('checked');
|
39 |
-
$("#backupChoices").show("fast");
|
40 |
-
$("input#submit").val("Create Backup");
|
41 |
-
|
42 |
-
});
|
43 |
-
|
44 |
-
$("input[id='customBackup']").click(function() {
|
45 |
-
|
46 |
-
$("#RestoreOptions").hide("fast");
|
47 |
-
$("#file_directory").show("fast");
|
48 |
-
$("input[name$='createBackup']").attr('checked', true);
|
49 |
-
$("input[name$='backupUrl']").removeAttr('checked');
|
50 |
-
$("#backupChoices").show("fast");
|
51 |
-
$("input#submit").val("Create Backup");
|
52 |
-
|
53 |
-
});
|
54 |
-
|
55 |
-
$("input[name$='createBackup']").click(function() {
|
56 |
-
|
57 |
-
$("#RestoreOptions").hide("fast");
|
58 |
-
$("input[name$='backupUrl']").attr('checked', false);
|
59 |
-
$("input[class$='restoreBackup']").each(function(){ $(this).attr('checked', false) });
|
60 |
-
checkCreateBackupOption();
|
61 |
-
|
62 |
-
});
|
63 |
-
|
64 |
-
$("input[class$='restoreBackup']").click(function() {
|
65 |
-
|
66 |
-
$("#RestoreOptions").show("fast");
|
67 |
-
$("input[name$='backupUrl']").attr('checked', false);
|
68 |
-
$(this).attr('checked', true);
|
69 |
-
unCheckCreateBackupOption();
|
70 |
-
$("input#submit").val("Restore Backup").removeClass("btn-primary").addClass("btn-warning");
|
71 |
-
|
72 |
-
});
|
73 |
-
|
74 |
-
$("input[name$='backupUrl']").click(function() {
|
75 |
-
prepareBackUrlOption();
|
76 |
-
$("input#submit").removeClass("btn-primary").addClass("btn-warning");
|
77 |
-
});
|
78 |
-
|
79 |
-
$("input[name$='restore_from_url']").focus(function() {
|
80 |
-
prepareBackUrlOption();
|
81 |
-
$("input#submit").removeClass("btn-primary").addClass("btn-warning");
|
82 |
-
});
|
83 |
-
|
84 |
-
$("input#submit").click(function() {
|
85 |
-
|
86 |
-
if ($('#backupUrl').is(':checked')) {
|
87 |
-
|
88 |
-
if ($("input[name$='restore_from_url']").val() == '') {
|
89 |
-
alert('Please enter the url you want to restore from.');
|
90 |
-
} else if (!$('#approve').is(':checked')) {
|
91 |
-
alert('Please confirm that you agree to our terms by checking the "I AGREE" checkbox.');
|
92 |
-
} else {
|
93 |
-
return getConfirmation('restore');
|
94 |
-
}
|
95 |
-
|
96 |
-
return false;
|
97 |
-
|
98 |
-
} else if ($('input[class$="restoreBackup"]').is(':checked')) {
|
99 |
-
|
100 |
-
if ($('#approve').is(':checked')) {
|
101 |
-
return getConfirmation('restore');
|
102 |
-
}
|
103 |
-
|
104 |
-
alert('Please confirm that you agree to our terms by checking "I AGREE (Required for "Restore" function):" checkbox.');
|
105 |
-
return false;
|
106 |
-
|
107 |
-
} else {
|
108 |
-
return getConfirmation('create backup');
|
109 |
-
}
|
110 |
-
|
111 |
-
function getConfirmation(toDo) {
|
112 |
-
return confirm('This may take a few minutes. Proceed to ' + toDo + ' now?');
|
113 |
-
}
|
114 |
-
});
|
115 |
-
|
116 |
-
function unCheckCreateBackupOption() {
|
117 |
-
$("input[name$='createBackup']").attr('checked', false);
|
118 |
-
$("#backupChoices").hide("fast");
|
119 |
-
}
|
120 |
-
|
121 |
-
function prepareBackUrlOption() {
|
122 |
-
$("#RestoreOptions").show("fast");
|
123 |
-
$("input[name$='backupUrl']").attr('checked', true);
|
124 |
-
$("input[class$='restoreBackup']").attr('checked', false);
|
125 |
-
unCheckCreateBackupOption();
|
126 |
-
$("input#submit").val('Restore from URL');
|
127 |
-
}
|
128 |
-
|
129 |
-
}
|
130 |
-
|
131 |
-
function checkCreateBackupOption() {
|
132 |
-
$("input[name$='createBackup']").attr('checked', true);
|
133 |
-
$("#backupChoices").show("fast");
|
134 |
-
$("input#submit").val("Create Backup").removeClass("btn-warning").addClass("btn-primary");
|
135 |
-
$("input[id='fullBackup']").attr('checked',
|
136 |
-
$("input[name$='createBackup']").is(':checked') && !$("input[id$='customBackup']").is(':checked'));
|
137 |
-
}
|
138 |
-
|
139 |
-
function getSize() {
|
140 |
-
|
141 |
-
$.ajax({
|
142 |
-
url: ajaxurl,
|
143 |
-
type: 'get',
|
144 |
-
data: {
|
145 |
-
'action': 'wpclone-ajax-size',
|
146 |
-
'nonce': wpclone.nonce
|
147 |
-
},
|
148 |
-
success: function(data){
|
149 |
-
data = $.parseJSON(data);
|
150 |
-
var cache = '';
|
151 |
-
if( 'undefined' !== typeof data.time ) {
|
152 |
-
cache = '</br>(calculated ' + data.time + ' minute[s] ago.)';
|
153 |
-
}
|
154 |
-
$("span#filesize").html( "Number of files in wp-content directory - <code>" + data.files + "</code>, and their total size - <code>" + data.size + "</code> (files larger than 25MB will be excluded from the backup, you can change it from advanced settings) </br>Database size is <code>" + data.dbsize + "</code>." + cache );
|
155 |
-
},
|
156 |
-
error: function(e){
|
157 |
-
$("span#filesize").html( "Unable to calculate size." );
|
158 |
-
}
|
159 |
-
});
|
160 |
-
|
161 |
-
}
|
162 |
-
|
163 |
-
function scanBackupDir() {
|
164 |
-
|
165 |
-
$("a#dirscan").click( function(e){
|
166 |
-
|
167 |
-
e.preventDefault();
|
168 |
-
$(this).html("<img src='" + wpclone.spinner + "'>");
|
169 |
-
|
170 |
-
$.ajax({
|
171 |
-
url: ajaxurl,
|
172 |
-
type: 'get',
|
173 |
-
data: {
|
174 |
-
'action': 'wpclone-ajax-dir',
|
175 |
-
'nonce': wpclone.nonce
|
176 |
-
},
|
177 |
-
success: function(data){
|
178 |
-
window.location.reload(true);
|
179 |
-
},
|
180 |
-
error: function(e){
|
181 |
-
}
|
182 |
-
});
|
183 |
-
|
184 |
-
|
185 |
-
});
|
186 |
-
|
187 |
-
}
|
188 |
-
|
189 |
-
function uninstall() {
|
190 |
-
|
191 |
-
$("a#uninstall").click( function(e){
|
192 |
-
|
193 |
-
e.preventDefault();
|
194 |
-
if( ! confirm('This will delete all your backups files, are you sure?') ) return;
|
195 |
-
$(this).html("<img src='" + wpclone.spinner + "'>");
|
196 |
-
$.ajax({
|
197 |
-
url: ajaxurl,
|
198 |
-
type: 'get',
|
199 |
-
data: {
|
200 |
-
'action': 'wpclone-ajax-uninstall',
|
201 |
-
'nonce': wpclone.nonce
|
202 |
-
},
|
203 |
-
success: function(data){
|
204 |
-
window.location.reload(true);
|
205 |
-
},
|
206 |
-
error: function(e){
|
207 |
-
}
|
208 |
-
});
|
209 |
-
|
210 |
-
|
211 |
-
});
|
212 |
-
|
213 |
-
}
|
214 |
-
|
215 |
-
function deleteFile() {
|
216 |
-
|
217 |
-
$("table.restore-backup-options a.delete").click( function(e){
|
218 |
-
e.preventDefault();
|
219 |
-
var row = $(this).closest("tr");
|
220 |
-
var cell = $(this).closest("td");
|
221 |
-
$(cell).html("<img src='" + wpclone.spinner + "'>");
|
222 |
-
|
223 |
-
$.ajax({
|
224 |
-
url: ajaxurl,
|
225 |
-
type: 'get',
|
226 |
-
data: {
|
227 |
-
'action': 'wpclone-ajax-delete',
|
228 |
-
'fileid': $(this).data("fileid"),
|
229 |
-
'nonce': wpclone.nonce
|
230 |
-
},
|
231 |
-
success: function(data){
|
232 |
-
data = $.parseJSON( data );
|
233 |
-
|
234 |
-
if( 'deleted' == data.status ) {
|
235 |
-
$(row).html("<td colspan='5'><strong>" + data.msg + "</strong></td>");
|
236 |
-
$(row).addClass('deleted').hide(700);
|
237 |
-
} else {
|
238 |
-
$(row).html("<td colspan='5'>" + data.msg + "</td>").addClass('delete-error');
|
239 |
-
}
|
240 |
-
},
|
241 |
-
error: function(e){
|
242 |
-
}
|
243 |
-
});
|
244 |
-
|
245 |
-
});
|
246 |
-
|
247 |
-
}
|
248 |
-
|
249 |
-
function search_and_replace() {
|
250 |
-
|
251 |
-
var prefix = '';
|
252 |
-
|
253 |
-
if( $("input[name='ignoreprefix']").prop("checked") ) {
|
254 |
-
prefix = 'true';
|
255 |
-
}
|
256 |
-
|
257 |
-
$.ajax({
|
258 |
-
url: ajaxurl,
|
259 |
-
type: 'post',
|
260 |
-
data: {
|
261 |
-
action : 'wpclone-search-n-replace',
|
262 |
-
search : $("input[name='searchfor']").val(),
|
263 |
-
replace: $("input[name='replacewith']").val(),
|
264 |
-
ignore_prefix : prefix,
|
265 |
-
nonce : wpclone.nonce
|
266 |
-
},
|
267 |
-
success: function(data) {
|
268 |
-
$("div#search-n-replace-info").css('background', '').append(data);
|
269 |
-
},
|
270 |
-
error: function(e){
|
271 |
-
console.log(e);
|
272 |
-
},
|
273 |
-
complete: function(){
|
274 |
-
$("input[name='search-n-replace-submit']").removeData("working");
|
275 |
-
}
|
276 |
-
|
277 |
-
});
|
278 |
-
|
279 |
-
}
|
280 |
-
|
281 |
-
$("#banner-2-restore-close-icon").on("click", function (e) {
|
282 |
-
$(".plugin-large-notice-restore-success").hide();
|
283 |
-
})
|
284 |
-
|
285 |
-
//Banner notice
|
286 |
-
$(".banner-1 .close-icon").on("click", function (e) {
|
287 |
-
$(".banner-1-collapsed").show(100);
|
288 |
-
$(".banner-1").hide(100);
|
289 |
-
|
290 |
-
$.ajax({
|
291 |
-
url: ajaxurl,
|
292 |
-
type: 'get',
|
293 |
-
data: {
|
294 |
-
'action': 'wpclone-ajax-banner1-close'
|
295 |
-
},
|
296 |
-
success: function(data){
|
297 |
-
console.log(data);
|
298 |
-
},
|
299 |
-
error: function(e){
|
300 |
-
}
|
301 |
-
});
|
302 |
-
})
|
303 |
-
|
304 |
-
$(".banner-1-collapsed .remove-for-good").on("click", function (e) {
|
305 |
-
$(".banner-1-collapsed").hide();
|
306 |
-
|
307 |
-
$.ajax({
|
308 |
-
url: ajaxurl,
|
309 |
-
type: 'get',
|
310 |
-
data: {
|
311 |
-
'action': 'wpclone-ajax-banner1-removed'
|
312 |
-
},
|
313 |
-
success: function(data){
|
314 |
-
console.log(data);
|
315 |
-
},
|
316 |
-
error: function(e){
|
317 |
-
}
|
318 |
-
});
|
319 |
-
})
|
320 |
-
$(".banner-1 .close-icon").on("click", function (e) {
|
321 |
-
$(".banner-1-collapsed").show(100);
|
322 |
-
$(".banner-1").hide(100);
|
323 |
-
|
324 |
-
$.ajax({
|
325 |
-
url: ajaxurl,
|
326 |
-
type: 'get',
|
327 |
-
data: {
|
328 |
-
'action': 'wpclone-ajax-banner1-close'
|
329 |
-
},
|
330 |
-
success: function(data){
|
331 |
-
console.log(data);
|
332 |
-
},
|
333 |
-
error: function(e){
|
334 |
-
}
|
335 |
-
});
|
336 |
-
})
|
337 |
-
|
338 |
-
$(".banner-1-collapsed .remove-for-good").on("click", function (e) {
|
339 |
-
$(".banner-1-collapsed").hide();
|
340 |
-
|
341 |
-
$.ajax({
|
342 |
-
url: ajaxurl,
|
343 |
-
type: 'get',
|
344 |
-
data: {
|
345 |
-
'action': 'wpclone-ajax-banner1-removed'
|
346 |
-
},
|
347 |
-
success: function(data){
|
348 |
-
console.log(data);
|
349 |
-
},
|
350 |
-
error: function(e){
|
351 |
-
}
|
352 |
-
});
|
353 |
-
})
|
354 |
});
|
1 |
+
jQuery(function($) {
|
2 |
+
|
3 |
+
initialize();
|
4 |
+
bindActions();
|
5 |
+
getSize();
|
6 |
+
scanBackupDir();
|
7 |
+
uninstall();
|
8 |
+
deleteFile();
|
9 |
+
|
10 |
+
function initialize() {
|
11 |
+
$("input[name$='backupChoice']").removeAttr('checked');
|
12 |
+
checkCreateBackupOption();
|
13 |
+
}
|
14 |
+
|
15 |
+
function bindActions() {
|
16 |
+
|
17 |
+
$("a#close-thickbox").click(function(e) {
|
18 |
+
e.preventDefault();
|
19 |
+
tb_remove();
|
20 |
+
$("div#search-n-replace-info").html('');
|
21 |
+
$("input[name='searchfor'], input[name='replacewith']").val('');
|
22 |
+
$("input[name='ignoreprefix']").attr('checked', false);
|
23 |
+
});
|
24 |
+
|
25 |
+
$("input[name='search-n-replace-submit']").click(function(e) {
|
26 |
+
e.preventDefault();
|
27 |
+
if( $(this).data("working") ) return;
|
28 |
+
$("#search-n-replace-info").html('').css( 'background', 'url(' + wpclone.spinner + ') no-repeat center' )
|
29 |
+
$(this).data("working", true);
|
30 |
+
search_and_replace();
|
31 |
+
});
|
32 |
+
|
33 |
+
$("input[id='fullBackup']").click(function() {
|
34 |
+
|
35 |
+
$("#RestoreOptions").hide("fast");
|
36 |
+
$("#file_directory").hide("fast");
|
37 |
+
$("input[name$='createBackup']").attr('checked', true);
|
38 |
+
$("input[name$='backupUrl']").removeAttr('checked');
|
39 |
+
$("#backupChoices").show("fast");
|
40 |
+
$("input#submit").val("Create Backup");
|
41 |
+
|
42 |
+
});
|
43 |
+
|
44 |
+
$("input[id='customBackup']").click(function() {
|
45 |
+
|
46 |
+
$("#RestoreOptions").hide("fast");
|
47 |
+
$("#file_directory").show("fast");
|
48 |
+
$("input[name$='createBackup']").attr('checked', true);
|
49 |
+
$("input[name$='backupUrl']").removeAttr('checked');
|
50 |
+
$("#backupChoices").show("fast");
|
51 |
+
$("input#submit").val("Create Backup");
|
52 |
+
|
53 |
+
});
|
54 |
+
|
55 |
+
$("input[name$='createBackup']").click(function() {
|
56 |
+
|
57 |
+
$("#RestoreOptions").hide("fast");
|
58 |
+
$("input[name$='backupUrl']").attr('checked', false);
|
59 |
+
$("input[class$='restoreBackup']").each(function(){ $(this).attr('checked', false) });
|
60 |
+
checkCreateBackupOption();
|
61 |
+
|
62 |
+
});
|
63 |
+
|
64 |
+
$("input[class$='restoreBackup']").click(function() {
|
65 |
+
|
66 |
+
$("#RestoreOptions").show("fast");
|
67 |
+
$("input[name$='backupUrl']").attr('checked', false);
|
68 |
+
$(this).attr('checked', true);
|
69 |
+
unCheckCreateBackupOption();
|
70 |
+
$("input#submit").val("Restore Backup").removeClass("btn-primary").addClass("btn-warning");
|
71 |
+
|
72 |
+
});
|
73 |
+
|
74 |
+
$("input[name$='backupUrl']").click(function() {
|
75 |
+
prepareBackUrlOption();
|
76 |
+
$("input#submit").removeClass("btn-primary").addClass("btn-warning");
|
77 |
+
});
|
78 |
+
|
79 |
+
$("input[name$='restore_from_url']").focus(function() {
|
80 |
+
prepareBackUrlOption();
|
81 |
+
$("input#submit").removeClass("btn-primary").addClass("btn-warning");
|
82 |
+
});
|
83 |
+
|
84 |
+
$("input#submit").click(function() {
|
85 |
+
|
86 |
+
if ($('#backupUrl').is(':checked')) {
|
87 |
+
|
88 |
+
if ($("input[name$='restore_from_url']").val() == '') {
|
89 |
+
alert('Please enter the url you want to restore from.');
|
90 |
+
} else if (!$('#approve').is(':checked')) {
|
91 |
+
alert('Please confirm that you agree to our terms by checking the "I AGREE" checkbox.');
|
92 |
+
} else {
|
93 |
+
return getConfirmation('restore');
|
94 |
+
}
|
95 |
+
|
96 |
+
return false;
|
97 |
+
|
98 |
+
} else if ($('input[class$="restoreBackup"]').is(':checked')) {
|
99 |
+
|
100 |
+
if ($('#approve').is(':checked')) {
|
101 |
+
return getConfirmation('restore');
|
102 |
+
}
|
103 |
+
|
104 |
+
alert('Please confirm that you agree to our terms by checking "I AGREE (Required for "Restore" function):" checkbox.');
|
105 |
+
return false;
|
106 |
+
|
107 |
+
} else {
|
108 |
+
return getConfirmation('create backup');
|
109 |
+
}
|
110 |
+
|
111 |
+
function getConfirmation(toDo) {
|
112 |
+
return confirm('This may take a few minutes. Proceed to ' + toDo + ' now?');
|
113 |
+
}
|
114 |
+
});
|
115 |
+
|
116 |
+
function unCheckCreateBackupOption() {
|
117 |
+
$("input[name$='createBackup']").attr('checked', false);
|
118 |
+
$("#backupChoices").hide("fast");
|
119 |
+
}
|
120 |
+
|
121 |
+
function prepareBackUrlOption() {
|
122 |
+
$("#RestoreOptions").show("fast");
|
123 |
+
$("input[name$='backupUrl']").attr('checked', true);
|
124 |
+
$("input[class$='restoreBackup']").attr('checked', false);
|
125 |
+
unCheckCreateBackupOption();
|
126 |
+
$("input#submit").val('Restore from URL');
|
127 |
+
}
|
128 |
+
|
129 |
+
}
|
130 |
+
|
131 |
+
function checkCreateBackupOption() {
|
132 |
+
$("input[name$='createBackup']").attr('checked', true);
|
133 |
+
$("#backupChoices").show("fast");
|
134 |
+
$("input#submit").val("Create Backup").removeClass("btn-warning").addClass("btn-primary");
|
135 |
+
$("input[id='fullBackup']").attr('checked',
|
136 |
+
$("input[name$='createBackup']").is(':checked') && !$("input[id$='customBackup']").is(':checked'));
|
137 |
+
}
|
138 |
+
|
139 |
+
function getSize() {
|
140 |
+
|
141 |
+
$.ajax({
|
142 |
+
url: ajaxurl,
|
143 |
+
type: 'get',
|
144 |
+
data: {
|
145 |
+
'action': 'wpclone-ajax-size',
|
146 |
+
'nonce': wpclone.nonce
|
147 |
+
},
|
148 |
+
success: function(data){
|
149 |
+
data = $.parseJSON(data);
|
150 |
+
var cache = '';
|
151 |
+
if( 'undefined' !== typeof data.time ) {
|
152 |
+
cache = '</br>(calculated ' + data.time + ' minute[s] ago.)';
|
153 |
+
}
|
154 |
+
$("span#filesize").html( "Number of files in wp-content directory - <code>" + data.files + "</code>, and their total size - <code>" + data.size + "</code> (files larger than 25MB will be excluded from the backup, you can change it from advanced settings) </br>Database size is <code>" + data.dbsize + "</code>." + cache );
|
155 |
+
},
|
156 |
+
error: function(e){
|
157 |
+
$("span#filesize").html( "Unable to calculate size." );
|
158 |
+
}
|
159 |
+
});
|
160 |
+
|
161 |
+
}
|
162 |
+
|
163 |
+
function scanBackupDir() {
|
164 |
+
|
165 |
+
$("a#dirscan").click( function(e){
|
166 |
+
|
167 |
+
e.preventDefault();
|
168 |
+
$(this).html("<img src='" + wpclone.spinner + "'>");
|
169 |
+
|
170 |
+
$.ajax({
|
171 |
+
url: ajaxurl,
|
172 |
+
type: 'get',
|
173 |
+
data: {
|
174 |
+
'action': 'wpclone-ajax-dir',
|
175 |
+
'nonce': wpclone.nonce
|
176 |
+
},
|
177 |
+
success: function(data){
|
178 |
+
window.location.reload(true);
|
179 |
+
},
|
180 |
+
error: function(e){
|
181 |
+
}
|
182 |
+
});
|
183 |
+
|
184 |
+
|
185 |
+
});
|
186 |
+
|
187 |
+
}
|
188 |
+
|
189 |
+
function uninstall() {
|
190 |
+
|
191 |
+
$("a#uninstall").click( function(e){
|
192 |
+
|
193 |
+
e.preventDefault();
|
194 |
+
if( ! confirm('This will delete all your backups files, are you sure?') ) return;
|
195 |
+
$(this).html("<img src='" + wpclone.spinner + "'>");
|
196 |
+
$.ajax({
|
197 |
+
url: ajaxurl,
|
198 |
+
type: 'get',
|
199 |
+
data: {
|
200 |
+
'action': 'wpclone-ajax-uninstall',
|
201 |
+
'nonce': wpclone.nonce
|
202 |
+
},
|
203 |
+
success: function(data){
|
204 |
+
window.location.reload(true);
|
205 |
+
},
|
206 |
+
error: function(e){
|
207 |
+
}
|
208 |
+
});
|
209 |
+
|
210 |
+
|
211 |
+
});
|
212 |
+
|
213 |
+
}
|
214 |
+
|
215 |
+
function deleteFile() {
|
216 |
+
|
217 |
+
$("table.restore-backup-options a.delete").click( function(e){
|
218 |
+
e.preventDefault();
|
219 |
+
var row = $(this).closest("tr");
|
220 |
+
var cell = $(this).closest("td");
|
221 |
+
$(cell).html("<img src='" + wpclone.spinner + "'>");
|
222 |
+
|
223 |
+
$.ajax({
|
224 |
+
url: ajaxurl,
|
225 |
+
type: 'get',
|
226 |
+
data: {
|
227 |
+
'action': 'wpclone-ajax-delete',
|
228 |
+
'fileid': $(this).data("fileid"),
|
229 |
+
'nonce': wpclone.nonce
|
230 |
+
},
|
231 |
+
success: function(data){
|
232 |
+
data = $.parseJSON( data );
|
233 |
+
|
234 |
+
if( 'deleted' == data.status ) {
|
235 |
+
$(row).html("<td colspan='5'><strong>" + data.msg + "</strong></td>");
|
236 |
+
$(row).addClass('deleted').hide(700);
|
237 |
+
} else {
|
238 |
+
$(row).html("<td colspan='5'>" + data.msg + "</td>").addClass('delete-error');
|
239 |
+
}
|
240 |
+
},
|
241 |
+
error: function(e){
|
242 |
+
}
|
243 |
+
});
|
244 |
+
|
245 |
+
});
|
246 |
+
|
247 |
+
}
|
248 |
+
|
249 |
+
function search_and_replace() {
|
250 |
+
|
251 |
+
var prefix = '';
|
252 |
+
|
253 |
+
if( $("input[name='ignoreprefix']").prop("checked") ) {
|
254 |
+
prefix = 'true';
|
255 |
+
}
|
256 |
+
|
257 |
+
$.ajax({
|
258 |
+
url: ajaxurl,
|
259 |
+
type: 'post',
|
260 |
+
data: {
|
261 |
+
action : 'wpclone-search-n-replace',
|
262 |
+
search : $("input[name='searchfor']").val(),
|
263 |
+
replace: $("input[name='replacewith']").val(),
|
264 |
+
ignore_prefix : prefix,
|
265 |
+
nonce : wpclone.nonce
|
266 |
+
},
|
267 |
+
success: function(data) {
|
268 |
+
$("div#search-n-replace-info").css('background', '').append(data);
|
269 |
+
},
|
270 |
+
error: function(e){
|
271 |
+
console.log(e);
|
272 |
+
},
|
273 |
+
complete: function(){
|
274 |
+
$("input[name='search-n-replace-submit']").removeData("working");
|
275 |
+
}
|
276 |
+
|
277 |
+
});
|
278 |
+
|
279 |
+
}
|
280 |
+
|
281 |
+
$("#banner-2-restore-close-icon").on("click", function (e) {
|
282 |
+
$(".plugin-large-notice-restore-success").hide();
|
283 |
+
})
|
284 |
+
|
285 |
+
//Banner notice
|
286 |
+
$(".banner-1 .close-icon").on("click", function (e) {
|
287 |
+
$(".banner-1-collapsed").show(100);
|
288 |
+
$(".banner-1").hide(100);
|
289 |
+
|
290 |
+
$.ajax({
|
291 |
+
url: ajaxurl,
|
292 |
+
type: 'get',
|
293 |
+
data: {
|
294 |
+
'action': 'wpclone-ajax-banner1-close'
|
295 |
+
},
|
296 |
+
success: function(data){
|
297 |
+
console.log(data);
|
298 |
+
},
|
299 |
+
error: function(e){
|
300 |
+
}
|
301 |
+
});
|
302 |
+
})
|
303 |
+
|
304 |
+
$(".banner-1-collapsed .remove-for-good").on("click", function (e) {
|
305 |
+
$(".banner-1-collapsed").hide();
|
306 |
+
|
307 |
+
$.ajax({
|
308 |
+
url: ajaxurl,
|
309 |
+
type: 'get',
|
310 |
+
data: {
|
311 |
+
'action': 'wpclone-ajax-banner1-removed'
|
312 |
+
},
|
313 |
+
success: function(data){
|
314 |
+
console.log(data);
|
315 |
+
},
|
316 |
+
error: function(e){
|
317 |
+
}
|
318 |
+
});
|
319 |
+
})
|
320 |
+
$(".banner-1 .close-icon").on("click", function (e) {
|
321 |
+
$(".banner-1-collapsed").show(100);
|
322 |
+
$(".banner-1").hide(100);
|
323 |
+
|
324 |
+
$.ajax({
|
325 |
+
url: ajaxurl,
|
326 |
+
type: 'get',
|
327 |
+
data: {
|
328 |
+
'action': 'wpclone-ajax-banner1-close'
|
329 |
+
},
|
330 |
+
success: function(data){
|
331 |
+
console.log(data);
|
332 |
+
},
|
333 |
+
error: function(e){
|
334 |
+
}
|
335 |
+
});
|
336 |
+
})
|
337 |
+
|
338 |
+
$(".banner-1-collapsed .remove-for-good").on("click", function (e) {
|
339 |
+
$(".banner-1-collapsed").hide();
|
340 |
+
|
341 |
+
$.ajax({
|
342 |
+
url: ajaxurl,
|
343 |
+
type: 'get',
|
344 |
+
data: {
|
345 |
+
'action': 'wpclone-ajax-banner1-removed'
|
346 |
+
},
|
347 |
+
success: function(data){
|
348 |
+
console.log(data);
|
349 |
+
},
|
350 |
+
error: function(e){
|
351 |
+
}
|
352 |
+
});
|
353 |
+
})
|
354 |
});
|
lib/js/clipboard.min.js
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
-
/*!
|
2 |
-
* clipboard.js v2.0.4
|
3 |
-
* https://zenorocha.github.io/clipboard.js
|
4 |
-
*
|
5 |
-
* Licensed MIT © Zeno Rocha
|
6 |
-
*/
|
7 |
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return function(n){var o={};function r(t){if(o[t])return o[t].exports;var e=o[t]={i:t,l:!1,exports:{}};return n[t].call(e.exports,e,e.exports,r),e.l=!0,e.exports}return r.m=n,r.c=o,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=0)}([function(t,e,n){"use strict";var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},i=function(){function o(t,e){for(var n=0;n<e.length;n++){var o=e[n];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(t,o.key,o)}}return function(t,e,n){return e&&o(t.prototype,e),n&&o(t,n),t}}(),a=o(n(1)),c=o(n(3)),u=o(n(4));function o(t){return t&&t.__esModule?t:{default:t}}var l=function(t){function o(t,e){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,o);var n=function(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}(this,(o.__proto__||Object.getPrototypeOf(o)).call(this));return n.resolveOptions(e),n.listenClick(t),n}return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(o,c.default),i(o,[{key:"resolveOptions",value:function(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{};this.action="function"==typeof t.action?t.action:this.defaultAction,this.target="function"==typeof t.target?t.target:this.defaultTarget,this.text="function"==typeof t.text?t.text:this.defaultText,this.container="object"===r(t.container)?t.container:document.body}},{key:"listenClick",value:function(t){var e=this;this.listener=(0,u.default)(t,"click",function(t){return e.onClick(t)})}},{key:"onClick",value:function(t){var e=t.delegateTarget||t.currentTarget;this.clipboardAction&&(this.clipboardAction=null),this.clipboardAction=new a.default({action:this.action(e),target:this.target(e),text:this.text(e),container:this.container,trigger:e,emitter:this})}},{key:"defaultAction",value:function(t){return s("action",t)}},{key:"defaultTarget",value:function(t){var e=s("target",t);if(e)return document.querySelector(e)}},{key:"defaultText",value:function(t){return s("text",t)}},{key:"destroy",value:function(){this.listener.destroy(),this.clipboardAction&&(this.clipboardAction.destroy(),this.clipboardAction=null)}}],[{key:"isSupported",value:function(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:["copy","cut"],e="string"==typeof t?[t]:t,n=!!document.queryCommandSupported;return e.forEach(function(t){n=n&&!!document.queryCommandSupported(t)}),n}}]),o}();function s(t,e){var n="data-clipboard-"+t;if(e.hasAttribute(n))return e.getAttribute(n)}t.exports=l},function(t,e,n){"use strict";var o,r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},i=function(){function o(t,e){for(var n=0;n<e.length;n++){var o=e[n];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(t,o.key,o)}}return function(t,e,n){return e&&o(t.prototype,e),n&&o(t,n),t}}(),a=n(2),c=(o=a)&&o.__esModule?o:{default:o};var u=function(){function e(t){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,e),this.resolveOptions(t),this.initSelection()}return i(e,[{key:"resolveOptions",value:function(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{};this.action=t.action,this.container=t.container,this.emitter=t.emitter,this.target=t.target,this.text=t.text,this.trigger=t.trigger,this.selectedText=""}},{key:"initSelection",value:function(){this.text?this.selectFake():this.target&&this.selectTarget()}},{key:"selectFake",value:function(){var t=this,e="rtl"==document.documentElement.getAttribute("dir");this.removeFake(),this.fakeHandlerCallback=function(){return t.removeFake()},this.fakeHandler=this.container.addEventListener("click",this.fakeHandlerCallback)||!0,this.fakeElem=document.createElement("textarea"),this.fakeElem.style.fontSize="12pt",this.fakeElem.style.border="0",this.fakeElem.style.padding="0",this.fakeElem.style.margin="0",this.fakeElem.style.position="absolute",this.fakeElem.style[e?"right":"left"]="-9999px";var n=window.pageYOffset||document.documentElement.scrollTop;this.fakeElem.style.top=n+"px",this.fakeElem.setAttribute("readonly",""),this.fakeElem.value=this.text,this.container.appendChild(this.fakeElem),this.selectedText=(0,c.default)(this.fakeElem),this.copyText()}},{key:"removeFake",value:function(){this.fakeHandler&&(this.container.removeEventListener("click",this.fakeHandlerCallback),this.fakeHandler=null,this.fakeHandlerCallback=null),this.fakeElem&&(this.container.removeChild(this.fakeElem),this.fakeElem=null)}},{key:"selectTarget",value:function(){this.selectedText=(0,c.default)(this.target),this.copyText()}},{key:"copyText",value:function(){var e=void 0;try{e=document.execCommand(this.action)}catch(t){e=!1}this.handleResult(e)}},{key:"handleResult",value:function(t){this.emitter.emit(t?"success":"error",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})}},{key:"clearSelection",value:function(){this.trigger&&this.trigger.focus(),window.getSelection().removeAllRanges()}},{key:"destroy",value:function(){this.removeFake()}},{key:"action",set:function(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:"copy";if(this._action=t,"copy"!==this._action&&"cut"!==this._action)throw new Error('Invalid "action" value, use either "copy" or "cut"')},get:function(){return this._action}},{key:"target",set:function(t){if(void 0!==t){if(!t||"object"!==(void 0===t?"undefined":r(t))||1!==t.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===this.action&&t.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===this.action&&(t.hasAttribute("readonly")||t.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');this._target=t}},get:function(){return this._target}}]),e}();t.exports=u},function(t,e){t.exports=function(t){var e;if("SELECT"===t.nodeName)t.focus(),e=t.value;else if("INPUT"===t.nodeName||"TEXTAREA"===t.nodeName){var n=t.hasAttribute("readonly");n||t.setAttribute("readonly",""),t.select(),t.setSelectionRange(0,t.value.length),n||t.removeAttribute("readonly"),e=t.value}else{t.hasAttribute("contenteditable")&&t.focus();var o=window.getSelection(),r=document.createRange();r.selectNodeContents(t),o.removeAllRanges(),o.addRange(r),e=o.toString()}return e}},function(t,e){function n(){}n.prototype={on:function(t,e,n){var o=this.e||(this.e={});return(o[t]||(o[t]=[])).push({fn:e,ctx:n}),this},once:function(t,e,n){var o=this;function r(){o.off(t,r),e.apply(n,arguments)}return r._=e,this.on(t,r,n)},emit:function(t){for(var e=[].slice.call(arguments,1),n=((this.e||(this.e={}))[t]||[]).slice(),o=0,r=n.length;o<r;o++)n[o].fn.apply(n[o].ctx,e);return this},off:function(t,e){var n=this.e||(this.e={}),o=n[t],r=[];if(o&&e)for(var i=0,a=o.length;i<a;i++)o[i].fn!==e&&o[i].fn._!==e&&r.push(o[i]);return r.length?n[t]=r:delete n[t],this}},t.exports=n},function(t,e,n){var d=n(5),h=n(6);t.exports=function(t,e,n){if(!t&&!e&&!n)throw new Error("Missing required arguments");if(!d.string(e))throw new TypeError("Second argument must be a String");if(!d.fn(n))throw new TypeError("Third argument must be a Function");if(d.node(t))return s=e,f=n,(l=t).addEventListener(s,f),{destroy:function(){l.removeEventListener(s,f)}};if(d.nodeList(t))return a=t,c=e,u=n,Array.prototype.forEach.call(a,function(t){t.addEventListener(c,u)}),{destroy:function(){Array.prototype.forEach.call(a,function(t){t.removeEventListener(c,u)})}};if(d.string(t))return o=t,r=e,i=n,h(document.body,o,r,i);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList");var o,r,i,a,c,u,l,s,f}},function(t,n){n.node=function(t){return void 0!==t&&t instanceof HTMLElement&&1===t.nodeType},n.nodeList=function(t){var e=Object.prototype.toString.call(t);return void 0!==t&&("[object NodeList]"===e||"[object HTMLCollection]"===e)&&"length"in t&&(0===t.length||n.node(t[0]))},n.string=function(t){return"string"==typeof t||t instanceof String},n.fn=function(t){return"[object Function]"===Object.prototype.toString.call(t)}},function(t,e,n){var a=n(7);function i(t,e,n,o,r){var i=function(e,n,t,o){return function(t){t.delegateTarget=a(t.target,n),t.delegateTarget&&o.call(e,t)}}.apply(this,arguments);return t.addEventListener(n,i,r),{destroy:function(){t.removeEventListener(n,i,r)}}}t.exports=function(t,e,n,o,r){return"function"==typeof t.addEventListener?i.apply(null,arguments):"function"==typeof n?i.bind(null,document).apply(null,arguments):("string"==typeof t&&(t=document.querySelectorAll(t)),Array.prototype.map.call(t,function(t){return i(t,e,n,o,r)}))}},function(t,e){if("undefined"!=typeof Element&&!Element.prototype.matches){var n=Element.prototype;n.matches=n.matchesSelector||n.mozMatchesSelector||n.msMatchesSelector||n.oMatchesSelector||n.webkitMatchesSelector}t.exports=function(t,e){for(;t&&9!==t.nodeType;){if("function"==typeof t.matches&&t.matches(e))return t;t=t.parentNode}}}])});
|
1 |
+
/*!
|
2 |
+
* clipboard.js v2.0.4
|
3 |
+
* https://zenorocha.github.io/clipboard.js
|
4 |
+
*
|
5 |
+
* Licensed MIT © Zeno Rocha
|
6 |
+
*/
|
7 |
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return function(n){var o={};function r(t){if(o[t])return o[t].exports;var e=o[t]={i:t,l:!1,exports:{}};return n[t].call(e.exports,e,e.exports,r),e.l=!0,e.exports}return r.m=n,r.c=o,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=0)}([function(t,e,n){"use strict";var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},i=function(){function o(t,e){for(var n=0;n<e.length;n++){var o=e[n];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(t,o.key,o)}}return function(t,e,n){return e&&o(t.prototype,e),n&&o(t,n),t}}(),a=o(n(1)),c=o(n(3)),u=o(n(4));function o(t){return t&&t.__esModule?t:{default:t}}var l=function(t){function o(t,e){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,o);var n=function(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}(this,(o.__proto__||Object.getPrototypeOf(o)).call(this));return n.resolveOptions(e),n.listenClick(t),n}return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(o,c.default),i(o,[{key:"resolveOptions",value:function(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{};this.action="function"==typeof t.action?t.action:this.defaultAction,this.target="function"==typeof t.target?t.target:this.defaultTarget,this.text="function"==typeof t.text?t.text:this.defaultText,this.container="object"===r(t.container)?t.container:document.body}},{key:"listenClick",value:function(t){var e=this;this.listener=(0,u.default)(t,"click",function(t){return e.onClick(t)})}},{key:"onClick",value:function(t){var e=t.delegateTarget||t.currentTarget;this.clipboardAction&&(this.clipboardAction=null),this.clipboardAction=new a.default({action:this.action(e),target:this.target(e),text:this.text(e),container:this.container,trigger:e,emitter:this})}},{key:"defaultAction",value:function(t){return s("action",t)}},{key:"defaultTarget",value:function(t){var e=s("target",t);if(e)return document.querySelector(e)}},{key:"defaultText",value:function(t){return s("text",t)}},{key:"destroy",value:function(){this.listener.destroy(),this.clipboardAction&&(this.clipboardAction.destroy(),this.clipboardAction=null)}}],[{key:"isSupported",value:function(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:["copy","cut"],e="string"==typeof t?[t]:t,n=!!document.queryCommandSupported;return e.forEach(function(t){n=n&&!!document.queryCommandSupported(t)}),n}}]),o}();function s(t,e){var n="data-clipboard-"+t;if(e.hasAttribute(n))return e.getAttribute(n)}t.exports=l},function(t,e,n){"use strict";var o,r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},i=function(){function o(t,e){for(var n=0;n<e.length;n++){var o=e[n];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(t,o.key,o)}}return function(t,e,n){return e&&o(t.prototype,e),n&&o(t,n),t}}(),a=n(2),c=(o=a)&&o.__esModule?o:{default:o};var u=function(){function e(t){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,e),this.resolveOptions(t),this.initSelection()}return i(e,[{key:"resolveOptions",value:function(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{};this.action=t.action,this.container=t.container,this.emitter=t.emitter,this.target=t.target,this.text=t.text,this.trigger=t.trigger,this.selectedText=""}},{key:"initSelection",value:function(){this.text?this.selectFake():this.target&&this.selectTarget()}},{key:"selectFake",value:function(){var t=this,e="rtl"==document.documentElement.getAttribute("dir");this.removeFake(),this.fakeHandlerCallback=function(){return t.removeFake()},this.fakeHandler=this.container.addEventListener("click",this.fakeHandlerCallback)||!0,this.fakeElem=document.createElement("textarea"),this.fakeElem.style.fontSize="12pt",this.fakeElem.style.border="0",this.fakeElem.style.padding="0",this.fakeElem.style.margin="0",this.fakeElem.style.position="absolute",this.fakeElem.style[e?"right":"left"]="-9999px";var n=window.pageYOffset||document.documentElement.scrollTop;this.fakeElem.style.top=n+"px",this.fakeElem.setAttribute("readonly",""),this.fakeElem.value=this.text,this.container.appendChild(this.fakeElem),this.selectedText=(0,c.default)(this.fakeElem),this.copyText()}},{key:"removeFake",value:function(){this.fakeHandler&&(this.container.removeEventListener("click",this.fakeHandlerCallback),this.fakeHandler=null,this.fakeHandlerCallback=null),this.fakeElem&&(this.container.removeChild(this.fakeElem),this.fakeElem=null)}},{key:"selectTarget",value:function(){this.selectedText=(0,c.default)(this.target),this.copyText()}},{key:"copyText",value:function(){var e=void 0;try{e=document.execCommand(this.action)}catch(t){e=!1}this.handleResult(e)}},{key:"handleResult",value:function(t){this.emitter.emit(t?"success":"error",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})}},{key:"clearSelection",value:function(){this.trigger&&this.trigger.focus(),window.getSelection().removeAllRanges()}},{key:"destroy",value:function(){this.removeFake()}},{key:"action",set:function(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:"copy";if(this._action=t,"copy"!==this._action&&"cut"!==this._action)throw new Error('Invalid "action" value, use either "copy" or "cut"')},get:function(){return this._action}},{key:"target",set:function(t){if(void 0!==t){if(!t||"object"!==(void 0===t?"undefined":r(t))||1!==t.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===this.action&&t.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===this.action&&(t.hasAttribute("readonly")||t.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');this._target=t}},get:function(){return this._target}}]),e}();t.exports=u},function(t,e){t.exports=function(t){var e;if("SELECT"===t.nodeName)t.focus(),e=t.value;else if("INPUT"===t.nodeName||"TEXTAREA"===t.nodeName){var n=t.hasAttribute("readonly");n||t.setAttribute("readonly",""),t.select(),t.setSelectionRange(0,t.value.length),n||t.removeAttribute("readonly"),e=t.value}else{t.hasAttribute("contenteditable")&&t.focus();var o=window.getSelection(),r=document.createRange();r.selectNodeContents(t),o.removeAllRanges(),o.addRange(r),e=o.toString()}return e}},function(t,e){function n(){}n.prototype={on:function(t,e,n){var o=this.e||(this.e={});return(o[t]||(o[t]=[])).push({fn:e,ctx:n}),this},once:function(t,e,n){var o=this;function r(){o.off(t,r),e.apply(n,arguments)}return r._=e,this.on(t,r,n)},emit:function(t){for(var e=[].slice.call(arguments,1),n=((this.e||(this.e={}))[t]||[]).slice(),o=0,r=n.length;o<r;o++)n[o].fn.apply(n[o].ctx,e);return this},off:function(t,e){var n=this.e||(this.e={}),o=n[t],r=[];if(o&&e)for(var i=0,a=o.length;i<a;i++)o[i].fn!==e&&o[i].fn._!==e&&r.push(o[i]);return r.length?n[t]=r:delete n[t],this}},t.exports=n},function(t,e,n){var d=n(5),h=n(6);t.exports=function(t,e,n){if(!t&&!e&&!n)throw new Error("Missing required arguments");if(!d.string(e))throw new TypeError("Second argument must be a String");if(!d.fn(n))throw new TypeError("Third argument must be a Function");if(d.node(t))return s=e,f=n,(l=t).addEventListener(s,f),{destroy:function(){l.removeEventListener(s,f)}};if(d.nodeList(t))return a=t,c=e,u=n,Array.prototype.forEach.call(a,function(t){t.addEventListener(c,u)}),{destroy:function(){Array.prototype.forEach.call(a,function(t){t.removeEventListener(c,u)})}};if(d.string(t))return o=t,r=e,i=n,h(document.body,o,r,i);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList");var o,r,i,a,c,u,l,s,f}},function(t,n){n.node=function(t){return void 0!==t&&t instanceof HTMLElement&&1===t.nodeType},n.nodeList=function(t){var e=Object.prototype.toString.call(t);return void 0!==t&&("[object NodeList]"===e||"[object HTMLCollection]"===e)&&"length"in t&&(0===t.length||n.node(t[0]))},n.string=function(t){return"string"==typeof t||t instanceof String},n.fn=function(t){return"[object Function]"===Object.prototype.toString.call(t)}},function(t,e,n){var a=n(7);function i(t,e,n,o,r){var i=function(e,n,t,o){return function(t){t.delegateTarget=a(t.target,n),t.delegateTarget&&o.call(e,t)}}.apply(this,arguments);return t.addEventListener(n,i,r),{destroy:function(){t.removeEventListener(n,i,r)}}}t.exports=function(t,e,n,o,r){return"function"==typeof t.addEventListener?i.apply(null,arguments):"function"==typeof n?i.bind(null,document).apply(null,arguments):("string"==typeof t&&(t=document.querySelectorAll(t)),Array.prototype.map.call(t,function(t){return i(t,e,n,o,r)}))}},function(t,e){if("undefined"!=typeof Element&&!Element.prototype.matches){var n=Element.prototype;n.matches=n.matchesSelector||n.mozMatchesSelector||n.msMatchesSelector||n.oMatchesSelector||n.webkitMatchesSelector}t.exports=function(t,e){for(;t&&9!==t.nodeType;){if("function"==typeof t.matches&&t.matches(e))return t;t=t.parentNode}}}])});
|
lib/view.php
CHANGED
@@ -1,286 +1,286 @@
|
|
1 |
-
<script type="text/javascript">
|
2 |
-
jQuery ( function($) {
|
3 |
-
var clipboard = new ClipboardJS('.copy-button');
|
4 |
-
clipboard.on('success', function(e) {
|
5 |
-
if(e.text.length > 0){
|
6 |
-
alert("URL has been copied successfully!");
|
7 |
-
}
|
8 |
-
});
|
9 |
-
});
|
10 |
-
|
11 |
-
</script>
|
12 |
-
|
13 |
-
<?php
|
14 |
-
if (wpa_wpfs_init()) return;
|
15 |
-
|
16 |
-
if( false === get_option( 'wpclone_backups' ) ) wpa_wpc_import_db();
|
17 |
-
$backups = get_option( 'wpclone_backups' );
|
18 |
-
?>
|
19 |
-
<div id="search-n-replace">
|
20 |
-
<a href="#" id="close-thickbox" class="button">X</a>
|
21 |
-
<form name="searchnreplace" action="#" method="post">
|
22 |
-
<table class="searchnreplace">
|
23 |
-
<tr><th><label for="searchfor">Search for</label></th><td colspan="5"><input type="text" name="searchfor" /></td></tr>
|
24 |
-
<tr><th><label for="replacewith">Replace with</label></th><td colspan="5"><input type="text" name="replacewith" /></td></tr>
|
25 |
-
<tr><th><label for="ignoreprefix">Ignore table prefix</label></th><td colspan="2"><input type="checkbox" name="ignoreprefix" value="true" /></td></tr>
|
26 |
-
</table>
|
27 |
-
<input type="submit" class="button" name="search-n-replace-submit" value="Run">
|
28 |
-
</form>
|
29 |
-
<div id="search-n-replace-info"></div>
|
30 |
-
</div>
|
31 |
-
|
32 |
-
<div id="wrapper">
|
33 |
-
<!--<div class="plugin-large-notice">
|
34 |
-
<div class="banner-1-collapsed" style="display:none; background-image: url('<?php /*echo plugins_url( 'lib/img/banner_bg_fold.jpg', __FILE__ )*/?>')">
|
35 |
-
<p class="left-text"><strong>BIG NEWS:</strong> We want WP Clone to arise from the dead. <a href="#">Read more</a></p>
|
36 |
-
<p class="remove-for-good">Remove for good (please first read it!)</p>
|
37 |
-
</div>
|
38 |
-
<div class="banner-1" style="background-image: url('<?php /*echo plugins_url( 'lib/img/banner_bg.jpg', __FILE__ )*/?>')">
|
39 |
-
<div class="close-icon"><img src='<?php /*echo plugins_url( 'lib/img/banner_close_icon.png', __FILE__ )*/?>'> </div>
|
40 |
-
<div class="heading">BIG NEWS: <strong>We want WP Clone to arise from the dead.</strong> Please help us!</div>
|
41 |
-
<div style="margin-top: 27px; font-size: 20px; color: #3a3a3a">The key points in a nutshell:</div>
|
42 |
-
<div class="nutshell-list">
|
43 |
-
<ul>
|
44 |
-
<li>1. New contributors have been added to the plugin, and with it comes new motivation to make it a kick-ass product!</li>
|
45 |
-
<li>2. Some fixes have been applied, the plugin now works in 90% of cases (and a further 9% if you follow the process as
|
46 |
-
outlined on the plugin page)</li>
|
47 |
-
<li>
|
48 |
-
3. We want to revive the plugin, make it work in 100% of cases, and add many more features. As we’re short on cash,
|
49 |
-
we’re crowdfunding it, and need your help:
|
50 |
-
<ul style="margin-left: 30px;margin-top: 15px;">
|
51 |
-
<li>
|
52 |
-
a. <span style="text-decoration: underline;">Contribution of 5 or 10 USD:</span> You get the warm fuzzy feeling from giving a sincere “Thank you” for a plugin which <br>
|
53 |
-
probably saved your butt a few times in the past, and helping to further develop it!
|
54 |
-
</li>
|
55 |
-
<li>
|
56 |
-
b. <span style="text-decoration: underline;">Contributions of 15 USD+:</span> As in a), plus you will be rewarded with a <strong>free plugin license</strong> <br>
|
57 |
-
(for the premium product which we will create)
|
58 |
-
</li>
|
59 |
-
<li>
|
60 |
-
c. <span style="text-decoration: underline;">Contributions of 50 USD+:</span> As in a), plus an <strong>unlimited websites premium license.</strong> <br>
|
61 |
-
This a fantastic, one-time deal. The plugin will provide many more features <br>
|
62 |
-
- such as backup scheduling, backup to external servers etc.<br>
|
63 |
-
while still being super-easy to use!
|
64 |
-
</li>
|
65 |
-
</ul>
|
66 |
-
</li>
|
67 |
-
</ul>
|
68 |
-
</div>
|
69 |
-
<div class="banner-footer">
|
70 |
-
The crowdfunding target is USD 3,000. If we don’t reach it you’ll be refunded*. <br>
|
71 |
-
Thank you for your support - we really depend on it!
|
72 |
-
</div>
|
73 |
-
<div style="margin-top: 33px;">
|
74 |
-
<a href="#" class="button1">Contribute</a>
|
75 |
-
<a href="#" class="button1"> Contribute & ger free license(s)</a>
|
76 |
-
</div>
|
77 |
-
<p style="margin-top: 33px;">
|
78 |
-
Also check out the updated plugin description.
|
79 |
-
</p>
|
80 |
-
<p style="margin-top: 33px; color: #0f9087">
|
81 |
-
*With the exception of the 5 or 10 USD amounts. We want you to have that warm fuzzy feeling forever ;)
|
82 |
-
</p>
|
83 |
-
</div>
|
84 |
-
</div>-->
|
85 |
-
<div id="MainView">
|
86 |
-
|
87 |
-
<h2>Welcome to WP Clone
|
88 |
-
|
89 |
-
<p>You can use this tool to create a backup of this site and (optionally) restore it to another server, or another WordPress installation on the same server.</p>
|
90 |
-
|
91 |
-
<p><strong>Here is how it works:</strong> the "Backup" function will give you a URL that you can then copy and paste
|
92 |
-
into the "Restore" dialog of a new WordPress site, which will clone the original site to the new site. You must
|
93 |
-
install the plugin on the new site and then run the WP Clone > Restore function.</p>
|
94 |
-
<p><b>Attention:</b> The restore process will fail on approximately 10% of installations. PLEASE read the <a href="https://wordpress.org/plugins/wp-clone-by-wp-academy/" target="_blank">plugin page</a> for more information. Only restore on a clean slate site.</p>
|
95 |
-
|
96 |
-
<p><strong>Choose your selection below:</strong> either create a backup of this site, or choose which backup you
|
97 |
-
would like to restore.</p>
|
98 |
-
|
99 |
-
<p> </p>
|
100 |
-
|
101 |
-
<form id="backupForm" name="backupForm" action="#" method="post">
|
102 |
-
<?php
|
103 |
-
if ( isset($_GET['mode']) && 'advanced' == $_GET['mode'] ) { ?>
|
104 |
-
<div class="info width-60">
|
105 |
-
<table>
|
106 |
-
<tr align="left"><th colspan=""><label for="zipmode">Alternate zip method</label></th><td colspan="2"><input type="checkbox" name="zipmode" value="alt" /></td></tr>
|
107 |
-
<tr align="left"><th><label for="use_wpdb">Use wpdb to backup the database</label></th><td colspan="2"><input type="checkbox" name="use_wpdb" value="true" /></td></tr>
|
108 |
-
<tr align="left"><th><label for="ignore_prefix">Ignore table prefix</label></th><td colspan="2"><input type="checkbox" name="ignore_prefix" value="true" /></td></tr>
|
109 |
-
<tr>
|
110 |
-
<td colspan="4">
|
111 |
-
<p>If enabled during a backup, all the tables in the database will be included in the backup.</br>
|
112 |
-
If enabled during a restore, search and replace will alter all the tables in the database.</br>
|
113 |
-
By default, only the tables that share the wordpress table prefix are included/altered during a backup/restore.</p>
|
114 |
-
</td>
|
115 |
-
</tr>
|
116 |
-
<tr align="left"><th><label for="mysql_check">Refresh MySQL connection during Restore</label></th><td colspan="2"><input type="checkbox" name="mysql_check" value="true" /></td></tr>
|
117 |
-
<tr>
|
118 |
-
<td colspan="4">
|
119 |
-
<p>This will check the MySQL connection inside the main loop before each database query during restore. Enable this if the restored site is incomplete.</p>
|
120 |
-
</td>
|
121 |
-
</tr>
|
122 |
-
<tr><td colspan="4"><h3>Overriding the Maximum memory and Execution time</h3></td></tr>
|
123 |
-
<tr><td colspan="4"><p>You can use these two fields to override the maximum memory and execution time on most hosts.</br>
|
124 |
-
For example, if you want to increase the RAM to 2GB, enter <code>2048</code> into the Maximum memory limit field.</br>
|
125 |
-
And if you want to increase the execution time to 15 minutes, enter <code>900</code> into the Script execution time field.</br>
|
126 |
-
Default values will be used if you leave them blank. The default value for RAM is 1024MB and the default value for execution time is 600 seconds (ten minutes).</p></td></tr>
|
127 |
-
<tr align="left"><th><label for="maxmem">Maximum memory limit</label></th><td colspan="2"><input type="text" name="maxmem" /></td></tr>
|
128 |
-
<tr align="left"><th><label for="maxexec">Script execution time</label></th><td><input type="text" name="maxexec" /></td></tr>
|
129 |
-
<tr><td colspan="4"><h3>Exclude directories from backup, and backup database only</h3></td></tr>
|
130 |
-
<tr><td colspan="4"><p>Depending on your web host, WP Clone may not work for large sites.
|
131 |
-
You may, however, exclude all of your 'wp-content' directory from the backup (use "Backup database only" option below), or exclude specific directories.
|
132 |
-
You would then copy these files over to the new site via FTP before restoring the backup with WP Clone.</p>
|
133 |
-
<p>You could also skip files that are larger than the value entered into the below field. For example, enter <code>100</code> if you want to skip files larger than 100MB.
|
134 |
-
The default value of 25MB will be used If you leave it blank. Enter <code>0</code> if you want to disable it.</p></td></tr>
|
135 |
-
<tr align="left"><th><label for="dbonly">Backup database only</label></th><td colspan="2"><input type="checkbox" name="dbonly" value="true" /></td></tr>
|
136 |
-
<tr align="left"><th><label for="skipfiles">Skip files larger than</label></th><td><input type="text" name="skipfiles" /> <strong>MB</strong></td></tr>
|
137 |
-
<tr align="left"><th><label for="exclude">Excluded directories</label></th><td><textarea cols="70" rows="5" name="exclude" ></textarea></td></tr>
|
138 |
-
<tr><th></th><td colspan="5"><p>Enter one per line, i.e. <code>uploads/backups</code>,use the forward slash <code>/</code> as the directory separator. Directories start at 'wp-content' level.</br>
|
139 |
-
</br>For example, BackWPup saves its backups into <code>/wp-content/uploads/backwpup-abc123-backups/</code> (the middle part, 'abc123' in this case, is random characters).
|
140 |
-
If you wanted to exclude that directory, you have to enter <code>uploads/backwpup-abc123-backups</code> into the above field.</p></td></tr>
|
141 |
-
</table>
|
142 |
-
</div>
|
143 |
-
<?php
|
144 |
-
}
|
145 |
-
?>
|
146 |
-
<strong>Create Backup</strong>
|
147 |
-
<input id="createBackup" name="createBackup" type="radio" value="fullBackup" checked="checked"/><br/><br/>
|
148 |
-
|
149 |
-
<?php if( false !== $backups && ! empty( $backups ) ) : ?>
|
150 |
-
|
151 |
-
<div class="try">
|
152 |
-
|
153 |
-
<table class="restore-backup-options">
|
154 |
-
|
155 |
-
<?php
|
156 |
-
|
157 |
-
foreach ($backups AS $key => $backup) :
|
158 |
-
|
159 |
-
$filename = convertPathIntoUrl(WPCLONE_DIR_BACKUP . $backup['name']);
|
160 |
-
$url = wp_nonce_url( get_bloginfo('wpurl') . '/wp-admin/admin.php?page=wp-clone&del=' . $key, 'wpclone-submit');
|
161 |
-
|
162 |
-
?>
|
163 |
-
<tr>
|
164 |
-
<th>Restore backup</th>
|
165 |
-
|
166 |
-
<td><input class="restoreBackup" name="restoreBackup" type="radio" value="<?php echo $filename ?>" /></td>
|
167 |
-
|
168 |
-
<td>
|
169 |
-
<a href="<?php echo $filename ?>" class="zclip"> (<?php echo bytesToSize($backup['size']);?>) <?php echo $backup['name'] ?></a>
|
170 |
-
<input type="hidden" name="backup_name" value="<?php echo $filename ?>" />
|
171 |
-
</td>
|
172 |
-
<?php
|
173 |
-
if( isset( $backup['log'] ) ){
|
174 |
-
printf( '<td><a href="%s">log</a></td>', convertPathIntoUrl(WPCLONE_DIR_BACKUP . $backup['log'] ) );
|
175 |
-
} else {
|
176 |
-
echo '<td>—</td>';
|
177 |
-
}
|
178 |
-
?>
|
179 |
-
<td><a class="copy-button" href="#" data-clipboard-text="<?php echo $filename ?>" >Copy URL</a></td>
|
180 |
-
<td><a href="<?php echo $url; ?>" class="delete" data-fileid="<?php echo $key; ?>">Delete</a></td>
|
181 |
-
|
182 |
-
</tr>
|
183 |
-
|
184 |
-
<?php endforeach ?>
|
185 |
-
|
186 |
-
</table>
|
187 |
-
</div>
|
188 |
-
|
189 |
-
<?php endif ?>
|
190 |
-
|
191 |
-
<strong>Restore from URL:</strong><input id="backupUrl" name="backupUrl" type="radio" value="backupUrl"/>
|
192 |
-
|
193 |
-
<input type="text" name="restore_from_url" class="Url" value="" size="80px"/><br/><br/>
|
194 |
-
|
195 |
-
<div class="RestoreOptions" id="RestoreOptions">
|
196 |
-
|
197 |
-
<input type="checkbox" name="approve" id="approve" /> I AGREE (Required for "Restore" function):<br/>
|
198 |
-
|
199 |
-
1. You have nothing of value in your current site <strong>[<?php echo site_url() ?>]</strong><br/>
|
200 |
-
|
201 |
-
2. Your current site at <strong>[<?php echo site_url() ?>]</strong> may become unusable in case of failure,
|
202 |
-
and you will need to re-install WordPress<br/>
|
203 |
-
|
204 |
-
3. Your WordPress database <strong>[<?php echo DB_NAME; ?>]</strong> will be overwritten from the database in the backup file. <br/>
|
205 |
-
|
206 |
-
</div>
|
207 |
-
|
208 |
-
<input id="submit" name="submit" class="btn-primary btn" type="submit" value="Create Backup"/>
|
209 |
-
|
210 |
-
|
211 |
-
<?php wp_nonce_field('wpclone-submit')?>
|
212 |
-
</form>
|
213 |
-
<?php
|
214 |
-
if(!isset($_GET['mode'])){
|
215 |
-
$link = admin_url( 'admin.php?page=wp-clone&mode=advanced' );
|
216 |
-
echo "<p style='padding:5px;'><a href='{$link}' style='margin-top:10px'>Advanced Settings</a></p>";
|
217 |
-
}
|
218 |
-
|
219 |
-
|
220 |
-
echo "<p><a href='#' id='dirscan' class='button' style='margin-top:10px'>Scan and repopulate the backup list</a>"
|
221 |
-
. "</br>Use the above button to refresh your backup list. It will list <em>all</em> the zip files found in the backup directory, it will also remove references to files that no longer exist.</p>";
|
222 |
-
|
223 |
-
wpa_wpc_sysinfo();
|
224 |
-
|
225 |
-
echo "<p><a href='#' id='uninstall' class='button' style='margin-top:10px'>Delete backups and remove database entries</a>"
|
226 |
-
. "</br>WP Clone does not remove backups when you deactivate the plugin. Use the above button if you want to remove all the backups.</p>";
|
227 |
-
|
228 |
-
echo '<p><a href="#TB_inline?height=200&width=600&inlineId=search-n-replace&modal=true" class="thickbox">Search and Replace</a></p>';
|
229 |
-
|
230 |
-
|
231 |
-
?>
|
232 |
-
</div>
|
233 |
-
<div id="sidebar">
|
234 |
-
|
235 |
-
<ul>
|
236 |
-
<h2 style="color: #0f9087">We need your help!</h2>
|
237 |
-
<p>PLEASE contribute to our crowdfunding effort to create the best backup & migration plugin on the market. <a href="https://sellcodes.com/q1OGuSox" target="_blank"> Go to crowdfunding page.</a></p>
|
238 |
-
|
239 |
-
</ul>
|
240 |
-
|
241 |
-
<ul>
|
242 |
-
<h2>Use WP Academy’s Transfer Service</h2>
|
243 |
-
<p>Save time and avoid headaches with WP Academy’s <a target="_blank" href="https://sellcodes.com/
|
244 |
-
|
245 |
-
</ul>
|
246 |
-
|
247 |
-
<ul>
|
248 |
-
<h2>Help & Support</h2>
|
249 |
-
<p>If you face any issues, we’re very happy to answer your questions in the <a href="http://wordpress.org/support/plugin/wp-clone-by-wp-academy" target="_blank" title="Support Forum">Support Forum</a>. <br><br>
|
250 |
-
We still have to catch up on the old support threads, however we’ll treat new questions with a high priority! :)</p>
|
251 |
-
</ul>
|
252 |
-
|
253 |
-
</div>
|
254 |
-
</div> <!--wrapper-->
|
255 |
-
<p style="clear: both;" ></p>
|
256 |
-
<?php
|
257 |
-
|
258 |
-
function wpa_wpc_sysinfo(){
|
259 |
-
global $wpdb;
|
260 |
-
echo '<div class="info width-60">';
|
261 |
-
echo '<h3>System Info:</h3><p>';
|
262 |
-
echo 'Memory limit: ' . ini_get('memory_limit');
|
263 |
-
if( false === ini_set( 'memory_limit', '257M' ) ) {
|
264 |
-
echo ' <span style="color:#660000">memory limit cannot be increased</span></br>';
|
265 |
-
} else {
|
266 |
-
echo '</br>';
|
267 |
-
}
|
268 |
-
echo 'Maximum execution time: ' . ini_get('max_execution_time') . ' seconds</br>';
|
269 |
-
echo 'PHP version : ' . phpversion() . '</br>';
|
270 |
-
echo 'MySQL version : ' . $wpdb->db_version() . '</br>';
|
271 |
-
if (ini_get('safe_mode')) { echo '<span style="color:#f11">PHP is running in safemode!</span></br>'; }
|
272 |
-
printf( 'Root directory : <code>%s</code></br>', WPCLONE_ROOT );
|
273 |
-
if ( ! file_exists( WPCLONE_DIR_BACKUP ) ) {
|
274 |
-
echo 'Backup path :<span style="color:#660000">Backup directory not found. '
|
275 |
-
. 'Unless there is a permissions or ownership issue, refreshing the backup list should create the directory.</span></br>';
|
276 |
-
} else {
|
277 |
-
echo 'Backup directory : <code>' . WPCLONE_DIR_BACKUP . '</code></br>';
|
278 |
-
}
|
279 |
-
echo 'Files : <span id="filesize"><img src="' . esc_url( admin_url( 'images/spinner.gif' ) ) . '"></span></br>';
|
280 |
-
if ( file_exists( WPCLONE_DIR_BACKUP ) && !is_writable(WPCLONE_DIR_BACKUP)) { echo '<span style="color:#f11">Backup directory is not writable, please change its permissions.</span></br>'; }
|
281 |
-
if (!is_writable(WPCLONE_WP_CONTENT)) { echo '<span style="color:#f11">wp-content is not writable, please change its permissions before you perform a restore.</span></br>'; }
|
282 |
-
if (!is_writable(wpa_wpconfig_path())) { echo '<span style="color:#f11">wp-config.php is not writable, please change its permissions before you perform a restore.</span></br>'; }
|
283 |
-
echo '</p></div>';
|
284 |
-
}
|
285 |
-
|
286 |
/** it all ends here folks. */
|
1 |
+
<script type="text/javascript">
|
2 |
+
jQuery ( function($) {
|
3 |
+
var clipboard = new ClipboardJS('.copy-button');
|
4 |
+
clipboard.on('success', function(e) {
|
5 |
+
if(e.text.length > 0){
|
6 |
+
alert("URL has been copied successfully!");
|
7 |
+
}
|
8 |
+
});
|
9 |
+
});
|
10 |
+
|
11 |
+
</script>
|
12 |
+
|
13 |
+
<?php
|
14 |
+
if (wpa_wpfs_init()) return;
|
15 |
+
|
16 |
+
if( false === get_option( 'wpclone_backups' ) ) wpa_wpc_import_db();
|
17 |
+
$backups = get_option( 'wpclone_backups' );
|
18 |
+
?>
|
19 |
+
<div id="search-n-replace">
|
20 |
+
<a href="#" id="close-thickbox" class="button">X</a>
|
21 |
+
<form name="searchnreplace" action="#" method="post">
|
22 |
+
<table class="searchnreplace">
|
23 |
+
<tr><th><label for="searchfor">Search for</label></th><td colspan="5"><input type="text" name="searchfor" /></td></tr>
|
24 |
+
<tr><th><label for="replacewith">Replace with</label></th><td colspan="5"><input type="text" name="replacewith" /></td></tr>
|
25 |
+
<tr><th><label for="ignoreprefix">Ignore table prefix</label></th><td colspan="2"><input type="checkbox" name="ignoreprefix" value="true" /></td></tr>
|
26 |
+
</table>
|
27 |
+
<input type="submit" class="button" name="search-n-replace-submit" value="Run">
|
28 |
+
</form>
|
29 |
+
<div id="search-n-replace-info"></div>
|
30 |
+
</div>
|
31 |
+
|
32 |
+
<div id="wrapper">
|
33 |
+
<!--<div class="plugin-large-notice">
|
34 |
+
<div class="banner-1-collapsed" style="display:none; background-image: url('<?php /*echo plugins_url( 'lib/img/banner_bg_fold.jpg', __FILE__ )*/?>')">
|
35 |
+
<p class="left-text"><strong>BIG NEWS:</strong> We want WP Clone to arise from the dead. <a href="#">Read more</a></p>
|
36 |
+
<p class="remove-for-good">Remove for good (please first read it!)</p>
|
37 |
+
</div>
|
38 |
+
<div class="banner-1" style="background-image: url('<?php /*echo plugins_url( 'lib/img/banner_bg.jpg', __FILE__ )*/?>')">
|
39 |
+
<div class="close-icon"><img src='<?php /*echo plugins_url( 'lib/img/banner_close_icon.png', __FILE__ )*/?>'> </div>
|
40 |
+
<div class="heading">BIG NEWS: <strong>We want WP Clone to arise from the dead.</strong> Please help us!</div>
|
41 |
+
<div style="margin-top: 27px; font-size: 20px; color: #3a3a3a">The key points in a nutshell:</div>
|
42 |
+
<div class="nutshell-list">
|
43 |
+
<ul>
|
44 |
+
<li>1. New contributors have been added to the plugin, and with it comes new motivation to make it a kick-ass product!</li>
|
45 |
+
<li>2. Some fixes have been applied, the plugin now works in 90% of cases (and a further 9% if you follow the process as
|
46 |
+
outlined on the plugin page)</li>
|
47 |
+
<li>
|
48 |
+
3. We want to revive the plugin, make it work in 100% of cases, and add many more features. As we’re short on cash,
|
49 |
+
we’re crowdfunding it, and need your help:
|
50 |
+
<ul style="margin-left: 30px;margin-top: 15px;">
|
51 |
+
<li>
|
52 |
+
a. <span style="text-decoration: underline;">Contribution of 5 or 10 USD:</span> You get the warm fuzzy feeling from giving a sincere “Thank you” for a plugin which <br>
|
53 |
+
probably saved your butt a few times in the past, and helping to further develop it!
|
54 |
+
</li>
|
55 |
+
<li>
|
56 |
+
b. <span style="text-decoration: underline;">Contributions of 15 USD+:</span> As in a), plus you will be rewarded with a <strong>free plugin license</strong> <br>
|
57 |
+
(for the premium product which we will create)
|
58 |
+
</li>
|
59 |
+
<li>
|
60 |
+
c. <span style="text-decoration: underline;">Contributions of 50 USD+:</span> As in a), plus an <strong>unlimited websites premium license.</strong> <br>
|
61 |
+
This a fantastic, one-time deal. The plugin will provide many more features <br>
|
62 |
+
- such as backup scheduling, backup to external servers etc.<br>
|
63 |
+
while still being super-easy to use!
|
64 |
+
</li>
|
65 |
+
</ul>
|
66 |
+
</li>
|
67 |
+
</ul>
|
68 |
+
</div>
|
69 |
+
<div class="banner-footer">
|
70 |
+
The crowdfunding target is USD 3,000. If we don’t reach it you’ll be refunded*. <br>
|
71 |
+
Thank you for your support - we really depend on it!
|
72 |
+
</div>
|
73 |
+
<div style="margin-top: 33px;">
|
74 |
+
<a href="#" class="button1">Contribute</a>
|
75 |
+
<a href="#" class="button1"> Contribute & ger free license(s)</a>
|
76 |
+
</div>
|
77 |
+
<p style="margin-top: 33px;">
|
78 |
+
Also check out the updated plugin description.
|
79 |
+
</p>
|
80 |
+
<p style="margin-top: 33px; color: #0f9087">
|
81 |
+
*With the exception of the 5 or 10 USD amounts. We want you to have that warm fuzzy feeling forever ;)
|
82 |
+
</p>
|
83 |
+
</div>
|
84 |
+
</div>-->
|
85 |
+
<div id="MainView">
|
86 |
+
|
87 |
+
<h2>Welcome to WP Clone, by <a href="http://wpacademy.com">WP Academy</a></h2>
|
88 |
+
|
89 |
+
<p>You can use this tool to create a backup of this site and (optionally) restore it to another server, or another WordPress installation on the same server.</p>
|
90 |
+
|
91 |
+
<p><strong>Here is how it works:</strong> the "Backup" function will give you a URL that you can then copy and paste
|
92 |
+
into the "Restore" dialog of a new WordPress site, which will clone the original site to the new site. You must
|
93 |
+
install the plugin on the new site and then run the WP Clone > Restore function.</p>
|
94 |
+
<p><b>Attention:</b> The restore process will fail on approximately 10% of installations. PLEASE read the <a href="https://wordpress.org/plugins/wp-clone-by-wp-academy/" target="_blank">plugin page</a> for more information. Only restore on a clean slate site.</p>
|
95 |
+
|
96 |
+
<p><strong>Choose your selection below:</strong> either create a backup of this site, or choose which backup you
|
97 |
+
would like to restore.</p>
|
98 |
+
|
99 |
+
<p> </p>
|
100 |
+
|
101 |
+
<form id="backupForm" name="backupForm" action="#" method="post">
|
102 |
+
<?php
|
103 |
+
if ( isset($_GET['mode']) && 'advanced' == $_GET['mode'] ) { ?>
|
104 |
+
<div class="info width-60">
|
105 |
+
<table>
|
106 |
+
<tr align="left"><th colspan=""><label for="zipmode">Alternate zip method</label></th><td colspan="2"><input type="checkbox" name="zipmode" value="alt" /></td></tr>
|
107 |
+
<tr align="left"><th><label for="use_wpdb">Use wpdb to backup the database</label></th><td colspan="2"><input type="checkbox" name="use_wpdb" value="true" /></td></tr>
|
108 |
+
<tr align="left"><th><label for="ignore_prefix">Ignore table prefix</label></th><td colspan="2"><input type="checkbox" name="ignore_prefix" value="true" /></td></tr>
|
109 |
+
<tr>
|
110 |
+
<td colspan="4">
|
111 |
+
<p>If enabled during a backup, all the tables in the database will be included in the backup.</br>
|
112 |
+
If enabled during a restore, search and replace will alter all the tables in the database.</br>
|
113 |
+
By default, only the tables that share the wordpress table prefix are included/altered during a backup/restore.</p>
|
114 |
+
</td>
|
115 |
+
</tr>
|
116 |
+
<tr align="left"><th><label for="mysql_check">Refresh MySQL connection during Restore</label></th><td colspan="2"><input type="checkbox" name="mysql_check" value="true" /></td></tr>
|
117 |
+
<tr>
|
118 |
+
<td colspan="4">
|
119 |
+
<p>This will check the MySQL connection inside the main loop before each database query during restore. Enable this if the restored site is incomplete.</p>
|
120 |
+
</td>
|
121 |
+
</tr>
|
122 |
+
<tr><td colspan="4"><h3>Overriding the Maximum memory and Execution time</h3></td></tr>
|
123 |
+
<tr><td colspan="4"><p>You can use these two fields to override the maximum memory and execution time on most hosts.</br>
|
124 |
+
For example, if you want to increase the RAM to 2GB, enter <code>2048</code> into the Maximum memory limit field.</br>
|
125 |
+
And if you want to increase the execution time to 15 minutes, enter <code>900</code> into the Script execution time field.</br>
|
126 |
+
Default values will be used if you leave them blank. The default value for RAM is 1024MB and the default value for execution time is 600 seconds (ten minutes).</p></td></tr>
|
127 |
+
<tr align="left"><th><label for="maxmem">Maximum memory limit</label></th><td colspan="2"><input type="text" name="maxmem" /></td></tr>
|
128 |
+
<tr align="left"><th><label for="maxexec">Script execution time</label></th><td><input type="text" name="maxexec" /></td></tr>
|
129 |
+
<tr><td colspan="4"><h3>Exclude directories from backup, and backup database only</h3></td></tr>
|
130 |
+
<tr><td colspan="4"><p>Depending on your web host, WP Clone may not work for large sites.
|
131 |
+
You may, however, exclude all of your 'wp-content' directory from the backup (use "Backup database only" option below), or exclude specific directories.
|
132 |
+
You would then copy these files over to the new site via FTP before restoring the backup with WP Clone.</p>
|
133 |
+
<p>You could also skip files that are larger than the value entered into the below field. For example, enter <code>100</code> if you want to skip files larger than 100MB.
|
134 |
+
The default value of 25MB will be used If you leave it blank. Enter <code>0</code> if you want to disable it.</p></td></tr>
|
135 |
+
<tr align="left"><th><label for="dbonly">Backup database only</label></th><td colspan="2"><input type="checkbox" name="dbonly" value="true" /></td></tr>
|
136 |
+
<tr align="left"><th><label for="skipfiles">Skip files larger than</label></th><td><input type="text" name="skipfiles" /> <strong>MB</strong></td></tr>
|
137 |
+
<tr align="left"><th><label for="exclude">Excluded directories</label></th><td><textarea cols="70" rows="5" name="exclude" ></textarea></td></tr>
|
138 |
+
<tr><th></th><td colspan="5"><p>Enter one per line, i.e. <code>uploads/backups</code>,use the forward slash <code>/</code> as the directory separator. Directories start at 'wp-content' level.</br>
|
139 |
+
</br>For example, BackWPup saves its backups into <code>/wp-content/uploads/backwpup-abc123-backups/</code> (the middle part, 'abc123' in this case, is random characters).
|
140 |
+
If you wanted to exclude that directory, you have to enter <code>uploads/backwpup-abc123-backups</code> into the above field.</p></td></tr>
|
141 |
+
</table>
|
142 |
+
</div>
|
143 |
+
<?php
|
144 |
+
}
|
145 |
+
?>
|
146 |
+
<strong>Create Backup</strong>
|
147 |
+
<input id="createBackup" name="createBackup" type="radio" value="fullBackup" checked="checked"/><br/><br/>
|
148 |
+
|
149 |
+
<?php if( false !== $backups && ! empty( $backups ) ) : ?>
|
150 |
+
|
151 |
+
<div class="try">
|
152 |
+
|
153 |
+
<table class="restore-backup-options">
|
154 |
+
|
155 |
+
<?php
|
156 |
+
|
157 |
+
foreach ($backups AS $key => $backup) :
|
158 |
+
|
159 |
+
$filename = convertPathIntoUrl(WPCLONE_DIR_BACKUP . $backup['name']);
|
160 |
+
$url = wp_nonce_url( get_bloginfo('wpurl') . '/wp-admin/admin.php?page=wp-clone&del=' . $key, 'wpclone-submit');
|
161 |
+
|
162 |
+
?>
|
163 |
+
<tr>
|
164 |
+
<th>Restore backup</th>
|
165 |
+
|
166 |
+
<td><input class="restoreBackup" name="restoreBackup" type="radio" value="<?php echo $filename ?>" /></td>
|
167 |
+
|
168 |
+
<td>
|
169 |
+
<a href="<?php echo $filename ?>" class="zclip"> (<?php echo bytesToSize($backup['size']);?>) <?php echo $backup['name'] ?></a>
|
170 |
+
<input type="hidden" name="backup_name" value="<?php echo $filename ?>" />
|
171 |
+
</td>
|
172 |
+
<?php
|
173 |
+
if( isset( $backup['log'] ) ){
|
174 |
+
printf( '<td><a href="%s">log</a></td>', convertPathIntoUrl(WPCLONE_DIR_BACKUP . $backup['log'] ) );
|
175 |
+
} else {
|
176 |
+
echo '<td>—</td>';
|
177 |
+
}
|
178 |
+
?>
|
179 |
+
<td><a class="copy-button" href="#" data-clipboard-text="<?php echo $filename ?>" >Copy URL</a></td>
|
180 |
+
<td><a href="<?php echo $url; ?>" class="delete" data-fileid="<?php echo $key; ?>">Delete</a></td>
|
181 |
+
|
182 |
+
</tr>
|
183 |
+
|
184 |
+
<?php endforeach ?>
|
185 |
+
|
186 |
+
</table>
|
187 |
+
</div>
|
188 |
+
|
189 |
+
<?php endif ?>
|
190 |
+
|
191 |
+
<strong>Restore from URL:</strong><input id="backupUrl" name="backupUrl" type="radio" value="backupUrl"/>
|
192 |
+
|
193 |
+
<input type="text" name="restore_from_url" class="Url" value="" size="80px"/><br/><br/>
|
194 |
+
|
195 |
+
<div class="RestoreOptions" id="RestoreOptions">
|
196 |
+
|
197 |
+
<input type="checkbox" name="approve" id="approve" /> I AGREE (Required for "Restore" function):<br/>
|
198 |
+
|
199 |
+
1. You have nothing of value in your current site <strong>[<?php echo site_url() ?>]</strong><br/>
|
200 |
+
|
201 |
+
2. Your current site at <strong>[<?php echo site_url() ?>]</strong> may become unusable in case of failure,
|
202 |
+
and you will need to re-install WordPress<br/>
|
203 |
+
|
204 |
+
3. Your WordPress database <strong>[<?php echo DB_NAME; ?>]</strong> will be overwritten from the database in the backup file. <br/>
|
205 |
+
|
206 |
+
</div>
|
207 |
+
|
208 |
+
<input id="submit" name="submit" class="btn-primary btn" type="submit" value="Create Backup"/>
|
209 |
+
|
210 |
+
|
211 |
+
<?php wp_nonce_field('wpclone-submit')?>
|
212 |
+
</form>
|
213 |
+
<?php
|
214 |
+
if(!isset($_GET['mode'])){
|
215 |
+
$link = admin_url( 'admin.php?page=wp-clone&mode=advanced' );
|
216 |
+
echo "<p style='padding:5px;'><a href='{$link}' style='margin-top:10px'>Advanced Settings</a></p>";
|
217 |
+
}
|
218 |
+
|
219 |
+
|
220 |
+
echo "<p><a href='#' id='dirscan' class='button' style='margin-top:10px'>Scan and repopulate the backup list</a>"
|
221 |
+
. "</br>Use the above button to refresh your backup list. It will list <em>all</em> the zip files found in the backup directory, it will also remove references to files that no longer exist.</p>";
|
222 |
+
|
223 |
+
wpa_wpc_sysinfo();
|
224 |
+
|
225 |
+
echo "<p><a href='#' id='uninstall' class='button' style='margin-top:10px'>Delete backups and remove database entries</a>"
|
226 |
+
. "</br>WP Clone does not remove backups when you deactivate the plugin. Use the above button if you want to remove all the backups.</p>";
|
227 |
+
|
228 |
+
echo '<p><a href="#TB_inline?height=200&width=600&inlineId=search-n-replace&modal=true" class="thickbox">Search and Replace</a></p>';
|
229 |
+
|
230 |
+
|
231 |
+
?>
|
232 |
+
</div>
|
233 |
+
<div id="sidebar">
|
234 |
+
|
235 |
+
<ul>
|
236 |
+
<h2 style="color: #0f9087">We need your help!</h2>
|
237 |
+
<p>PLEASE contribute to our crowdfunding effort to create the best backup & migration plugin on the market. <a href="https://sellcodes.com/q1OGuSox" target="_blank"> Go to crowdfunding page.</a></p>
|
238 |
+
|
239 |
+
</ul>
|
240 |
+
|
241 |
+
<ul>
|
242 |
+
<h2>Use WP Academy’s Transfer Service</h2>
|
243 |
+
<p>Save time and avoid headaches with WP Academy’s <a target="_blank" href="https://sellcodes.com/fJxO4jci">Premium Transfer Service.</a></p>
|
244 |
+
|
245 |
+
</ul>
|
246 |
+
|
247 |
+
<ul>
|
248 |
+
<h2>Help & Support</h2>
|
249 |
+
<p>If you face any issues, we’re very happy to answer your questions in the <a href="http://wordpress.org/support/plugin/wp-clone-by-wp-academy" target="_blank" title="Support Forum">Support Forum</a>. <br><br>
|
250 |
+
We still have to catch up on the old support threads, however we’ll treat new questions with a high priority! :)</p>
|
251 |
+
</ul>
|
252 |
+
|
253 |
+
</div>
|
254 |
+
</div> <!--wrapper-->
|
255 |
+
<p style="clear: both;" ></p>
|
256 |
+
<?php
|
257 |
+
|
258 |
+
function wpa_wpc_sysinfo(){
|
259 |
+
global $wpdb;
|
260 |
+
echo '<div class="info width-60">';
|
261 |
+
echo '<h3>System Info:</h3><p>';
|
262 |
+
echo 'Memory limit: ' . ini_get('memory_limit');
|
263 |
+
if( false === ini_set( 'memory_limit', '257M' ) ) {
|
264 |
+
echo ' <span style="color:#660000">memory limit cannot be increased</span></br>';
|
265 |
+
} else {
|
266 |
+
echo '</br>';
|
267 |
+
}
|
268 |
+
echo 'Maximum execution time: ' . ini_get('max_execution_time') . ' seconds</br>';
|
269 |
+
echo 'PHP version : ' . phpversion() . '</br>';
|
270 |
+
echo 'MySQL version : ' . $wpdb->db_version() . '</br>';
|
271 |
+
if (ini_get('safe_mode')) { echo '<span style="color:#f11">PHP is running in safemode!</span></br>'; }
|
272 |
+
printf( 'Root directory : <code>%s</code></br>', WPCLONE_ROOT );
|
273 |
+
if ( ! file_exists( WPCLONE_DIR_BACKUP ) ) {
|
274 |
+
echo 'Backup path :<span style="color:#660000">Backup directory not found. '
|
275 |
+
. 'Unless there is a permissions or ownership issue, refreshing the backup list should create the directory.</span></br>';
|
276 |
+
} else {
|
277 |
+
echo 'Backup directory : <code>' . WPCLONE_DIR_BACKUP . '</code></br>';
|
278 |
+
}
|
279 |
+
echo 'Files : <span id="filesize"><img src="' . esc_url( admin_url( 'images/spinner.gif' ) ) . '"></span></br>';
|
280 |
+
if ( file_exists( WPCLONE_DIR_BACKUP ) && !is_writable(WPCLONE_DIR_BACKUP)) { echo '<span style="color:#f11">Backup directory is not writable, please change its permissions.</span></br>'; }
|
281 |
+
if (!is_writable(WPCLONE_WP_CONTENT)) { echo '<span style="color:#f11">wp-content is not writable, please change its permissions before you perform a restore.</span></br>'; }
|
282 |
+
if (!is_writable(wpa_wpconfig_path())) { echo '<span style="color:#f11">wp-config.php is not writable, please change its permissions before you perform a restore.</span></br>'; }
|
283 |
+
echo '</p></div>';
|
284 |
+
}
|
285 |
+
|
286 |
/** it all ends here folks. */
|
readme.txt
CHANGED
@@ -1,183 +1,187 @@
|
|
1 |
-
=== Clone ===
|
2 |
-
Contributors: wpacademy, migrate, nick843
|
3 |
-
Donate link: https://sellcodes.com/q1OGuSox
|
4 |
-
Tags: migrate, clone, backup, migration, backups, copy, restore, recover, restoration, duplicate
|
5 |
-
Author URI: http://wpacademy.com
|
6 |
-
Requires PHP: 5.5
|
7 |
-
Requires at least: 3.3
|
8 |
-
Tested up to: 5.3
|
9 |
-
Stable tag: 2.2.
|
10 |
-
License: GPLv3
|
11 |
-
License URI: https://www.gnu.org/licenses/gpl-3.0.en.html
|
12 |
-
|
13 |
-
|
14 |
-
100% FREE clone and migration
|
15 |
-
|
16 |
-
== Description ==
|
17 |
-
|
18 |
-
= WP Clone is back! =
|
19 |
-
|
20 |
-
Or, to be precise: we **want** it to come back!
|
21 |
-
|
22 |
-
After 3 years of neglection we believe it's time for a revival. New contributors got added to the plugin, and with it comes new motivation to make it the best backup solution on the market!
|
23 |
-
|
24 |
-
By that, we mean:
|
25 |
-
|
26 |
-
- Finally make it work in all cases
|
27 |
-
- Add more features, but **keep the simplicity**
|
28 |
-
- Get user trust back by delivering a top service!
|
29 |
-
|
30 |
-
But to achieve that…
|
31 |
-
|
32 |
-
= We need your help! =
|
33 |
-
|
34 |
-
Good development takes time & money, and we need your support.
|
35 |
-
|
36 |
-
We decided to [crowdfund it](https://sellcodes.com/q1OGuSox), meaning:
|
37 |
-
|
38 |
-
- **Contributions of 5-10 USD**: Feel great by giving a sincere "Thank you" and helping us to develop the plugin further. If the plugin made your life easier in the past, it's a great opportunity to show some appreciation :)
|
39 |
-
- **Contributions of 15 USD**: As in 1.), plus you'll get a free premium license (for the premium plugin which we will create*). A contribution of 30 USD gets you 2 licenses.
|
40 |
-
- **Contributions of 50 USD** (or more): As in 1), plus you'll get an unlimited websites premium license. This a fantastic, one-time deal. The plugin will provide many more features - such as backup scheduling, backup to external servers etc. - while still being super-easy to use!
|
41 |
-
|
42 |
-
All licenses are lifetime licenses and valid on both commercial and non-commercial websites.
|
43 |
-
|
44 |
-
*We will create the premium plugin only if we reach our (quite modest) crowdfunding goal of 3,000 USD. If we don't hit it, you'll get your contributions refunded (except the 5 and 10 USD amounts - those are basically for the past :).
|
45 |
-
|
46 |
-
Please help us out, and see the status quo of our crowdfunding effort, on the [WP clone crowdfunding page](https://sellcodes.com/q1OGuSox)!
|
47 |
-
|
48 |
-
= Where do we stand today? =
|
49 |
-
|
50 |
-
But first, let’s back up ;) - where does the (free) plugin stand now?
|
51 |
-
|
52 |
-
WP Clone is still the easiest, fastest, cheapest and most secure way to backup, migrate or clone a WordPress site to another domain or hosting server.
|
53 |
-
|
54 |
-
You can also use it to backup, migrate or clone your site to/from local server hosting, to create backup of your site for development or testing purposes, and to install pre-configured backups of WordPress.
|
55 |
-
|
56 |
-
WP Clone is a superior to other backup & migrate plugins for the following reasons:
|
57 |
-
|
58 |
-
* It does not require FTP access to backup files you migrate or clone, neither the source or destination site; just install a new WordPress on the destination site, install our backup plugin, and follow the prompts to migrate or clone your site.
|
59 |
-
* It does not backup or restore the WordPress system files (it just creates user content and database backups); reducing upload time for migration and improving security of your site
|
60 |
-
* It fetches the site backup via your host's direct http connection, which saves you from having to upload large backup files, making it easier to migrate.
|
61 |
-
|
62 |
-
= What are today's limitations? =
|
63 |
-
|
64 |
-
Today:
|
65 |
-
|
66 |
-
- 90% of cases: Backups & migrations work flawlessly (we fixed some key bugs in the most recent version)
|
67 |
-
- 9% of cases: Backups or migrations fail due to your hoster's configurations (most likely limits in up- and downloads) which is typically the case when you backup or migrate very large sites. However, there's a workaround: simply do a “Database Only” backup (use “Advanced Settings”), transfer the wp-content directory over with FTP, and then restore new site. Then backup and migration also works.
|
68 |
-
- 1% of cases: Your site/hosting is abnormal (pardon our French) and backup or migration doesn't work. However: that's what we'll now be working on, so that eventually backups and migrations will work in all cases.
|
69 |
-
|
70 |
-
The 1% case means:
|
71 |
-
|
72 |
-
- Basic rule: DO NOT use it as your only backup solution! Only use it for migrations (so that if something fails, you still have the files on your old site as backup).
|
73 |
-
- If you want to use it as backup, test it by restoring the backup file on a new site. If that works fine you should be safe.
|
74 |
-
- In any case, we cannot take any responsibility if backup or migration fails.
|
75 |
-
|
76 |
-
Note:
|
77 |
-
* There is never an issue in damaging the source installation (i.e. on the site where you create the backup). So backup sites at your pleasure. If your backup succeeds then chances are good that the migration (i.e. restore on another site) will also succeed. But don't take any chances.
|
78 |
-
* If backup or migration (restore) fails, just try it again. Often it works on second attempt.
|
79 |
-
|
80 |
-
= Other tips how to backup and migrate =
|
81 |
-
|
82 |
-
* NEVER overwrite an installation for which you do not have an alternate backup source (e.g. a cPanel backup). Normally you would restore the backup onto a fresh WP installation on another host or on a subdomain. If the restore fails your destination site might become unusable, so be prepared to enter cPanel and then destroy / recreate the new installation if necessary.
|
83 |
-
* DO NOT use our backup plugin on WP Engine or any hosting system with proprietary operating system. Instead, use their built-in backup tools.
|
84 |
-
* Large sites (>2GB) might take as long as an hour to backup and migrate. Sites of 250 MB or less should take no more than a minute or two to backup, depending on your server.
|
85 |
-
* We recommend you deactivate and delete page caching, security and maybe redirection plugins before you migrate, and re-install them on the new site, if necessary. In general, delete all unnecessary plugins and data from your site before you backup. You can also use the "Exclude directories" option if you have large media files, which you can then copy back to the new site with FTP.
|
86 |
-
* How to copy from local server to your hosted website: Create a backup of the local site in the usual way, then save the backup file (right-click > Save) to your local disk. Upload this file to the root directory of your destination website and then use this url in the “Restore” dialog of the new site: http://yourdomain.com/<name of the backup file.zip>.
|
87 |
-
|
88 |
-
= Help Video =
|
89 |
-
|
90 |
-
|
91 |
-
(Old video, but may still be helpful; we’ll replace it with an updated one soon)
|
92 |
-
|
93 |
-
[youtube http://www.youtube.com/watch?v=xN5Ffhyn4Ao]
|
94 |
-
|
95 |
-
= Credits =
|
96 |
-
WP Backup uses functions from the "Safe Search and Replace on Database with Serialized Data" script first written by David Coveney of Interconnect IT Ltd (UK) http://www.davidcoveney.com or http://www.interconnectit.com and released under the WTFPL http://sam.zoy.org/wtfpl/. Partial script with full changelog is placed inside 'lib/files' directory.
|
97 |
-
|
98 |
-
Thank you for reading this far. Please don’t forget to [contribute to create a kick-ass plugin](https://sellcodes.com/q1OGuSox). Thank you!
|
99 |
-
== Installation ==
|
100 |
-
|
101 |
-
1. Navigate to Plugins > Add New
|
102 |
-
2. Search for "WP Clone by WP Academy"
|
103 |
-
3. Install and activate the backup and migration plugin
|
104 |
-
4. Follow remaining instructions in the help video
|
105 |
-
|
106 |
-
== Frequently Asked Questions ==
|
107 |
-
Backup and migration FAQ are under construction
|
108 |
-
|
109 |
-
== Changelog ==
|
110 |
-
|
111 |
-
= 2.2.
|
112 |
-
* Updated
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
* Updated migration texts
|
117 |
-
|
118 |
-
= 2.2.
|
119 |
-
* Updated
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
*
|
124 |
-
|
125 |
-
= 2.2.
|
126 |
-
*
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
*
|
132 |
-
|
133 |
-
= 2.2.
|
134 |
-
*
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
* Fixed:
|
140 |
-
|
141 |
-
|
142 |
-
*
|
143 |
-
* Added:
|
144 |
-
*
|
145 |
-
*
|
146 |
-
|
147 |
-
|
148 |
-
*
|
149 |
-
|
150 |
-
|
151 |
-
*
|
152 |
-
* Added:
|
153 |
-
* Added:
|
154 |
-
*
|
155 |
-
*
|
156 |
-
*
|
157 |
-
* Changed:
|
158 |
-
|
159 |
-
|
160 |
-
*
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
*
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
*
|
170 |
-
|
171 |
-
|
172 |
-
*
|
173 |
-
|
174 |
-
|
175 |
-
*
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
==
|
182 |
-
|
|
|
|
|
|
|
|
|
183 |
* Please upgrade!
|
1 |
+
=== Clone ===
|
2 |
+
Contributors: wpacademy, migrate, nick843
|
3 |
+
Donate link: https://sellcodes.com/q1OGuSox
|
4 |
+
Tags: migrate, clone, backup, migration, backups, copy, restore, recover, restoration, duplicate
|
5 |
+
Author URI: http://wpacademy.com
|
6 |
+
Requires PHP: 5.5
|
7 |
+
Requires at least: 3.3
|
8 |
+
Tested up to: 5.3
|
9 |
+
Stable tag: 2.2.10
|
10 |
+
License: GPLv3
|
11 |
+
License URI: https://www.gnu.org/licenses/gpl-3.0.en.html
|
12 |
+
|
13 |
+
|
14 |
+
100% FREE clone and migration
|
15 |
+
|
16 |
+
== Description ==
|
17 |
+
|
18 |
+
= WP Clone is back! =
|
19 |
+
|
20 |
+
Or, to be precise: we **want** it to come back!
|
21 |
+
|
22 |
+
After 3 years of neglection we believe it's time for a revival. New contributors got added to the plugin, and with it comes new motivation to make it the best backup solution on the market!
|
23 |
+
|
24 |
+
By that, we mean:
|
25 |
+
|
26 |
+
- Finally make it work in all cases
|
27 |
+
- Add more features, but **keep the simplicity**
|
28 |
+
- Get user trust back by delivering a top service!
|
29 |
+
|
30 |
+
But to achieve that…
|
31 |
+
|
32 |
+
= We need your help! =
|
33 |
+
|
34 |
+
Good development takes time & money, and we need your support.
|
35 |
+
|
36 |
+
We decided to [crowdfund it](https://sellcodes.com/q1OGuSox), meaning:
|
37 |
+
|
38 |
+
- **Contributions of 5-10 USD**: Feel great by giving a sincere "Thank you" and helping us to develop the plugin further. If the plugin made your life easier in the past, it's a great opportunity to show some appreciation :)
|
39 |
+
- **Contributions of 15 USD**: As in 1.), plus you'll get a free premium license (for the premium plugin which we will create*). A contribution of 30 USD gets you 2 licenses.
|
40 |
+
- **Contributions of 50 USD** (or more): As in 1), plus you'll get an unlimited websites premium license. This a fantastic, one-time deal. The plugin will provide many more features - such as backup scheduling, backup to external servers etc. - while still being super-easy to use!
|
41 |
+
|
42 |
+
All licenses are lifetime licenses and valid on both commercial and non-commercial websites.
|
43 |
+
|
44 |
+
*We will create the premium plugin only if we reach our (quite modest) crowdfunding goal of 3,000 USD. If we don't hit it, you'll get your contributions refunded (except the 5 and 10 USD amounts - those are basically for the past :).
|
45 |
+
|
46 |
+
Please help us out, and see the status quo of our crowdfunding effort, on the [WP clone crowdfunding page](https://sellcodes.com/q1OGuSox)!
|
47 |
+
|
48 |
+
= Where do we stand today? =
|
49 |
+
|
50 |
+
But first, let’s back up ;) - where does the (free) plugin stand now?
|
51 |
+
|
52 |
+
WP Clone is still the easiest, fastest, cheapest and most secure way to backup, migrate or clone a WordPress site to another domain or hosting server.
|
53 |
+
|
54 |
+
You can also use it to backup, migrate or clone your site to/from local server hosting, to create backup of your site for development or testing purposes, and to install pre-configured backups of WordPress.
|
55 |
+
|
56 |
+
WP Clone is a superior to other backup & migrate plugins for the following reasons:
|
57 |
+
|
58 |
+
* It does not require FTP access to backup files you migrate or clone, neither the source or destination site; just install a new WordPress on the destination site, install our backup plugin, and follow the prompts to migrate or clone your site.
|
59 |
+
* It does not backup or restore the WordPress system files (it just creates user content and database backups); reducing upload time for migration and improving security of your site
|
60 |
+
* It fetches the site backup via your host's direct http connection, which saves you from having to upload large backup files, making it easier to migrate.
|
61 |
+
|
62 |
+
= What are today's limitations? =
|
63 |
+
|
64 |
+
Today:
|
65 |
+
|
66 |
+
- 90% of cases: Backups & migrations work flawlessly (we fixed some key bugs in the most recent version)
|
67 |
+
- 9% of cases: Backups or migrations fail due to your hoster's configurations (most likely limits in up- and downloads) which is typically the case when you backup or migrate very large sites. However, there's a workaround: simply do a “Database Only” backup (use “Advanced Settings”), transfer the wp-content directory over with FTP, and then restore new site. Then backup and migration also works.
|
68 |
+
- 1% of cases: Your site/hosting is abnormal (pardon our French) and backup or migration doesn't work. However: that's what we'll now be working on, so that eventually backups and migrations will work in all cases.
|
69 |
+
|
70 |
+
The 1% case means:
|
71 |
+
|
72 |
+
- Basic rule: DO NOT use it as your only backup solution! Only use it for migrations (so that if something fails, you still have the files on your old site as backup).
|
73 |
+
- If you want to use it as backup, test it by restoring the backup file on a new site. If that works fine you should be safe.
|
74 |
+
- In any case, we cannot take any responsibility if backup or migration fails.
|
75 |
+
|
76 |
+
Note:
|
77 |
+
* There is never an issue in damaging the source installation (i.e. on the site where you create the backup). So backup sites at your pleasure. If your backup succeeds then chances are good that the migration (i.e. restore on another site) will also succeed. But don't take any chances.
|
78 |
+
* If backup or migration (restore) fails, just try it again. Often it works on second attempt.
|
79 |
+
|
80 |
+
= Other tips how to backup and migrate =
|
81 |
+
|
82 |
+
* NEVER overwrite an installation for which you do not have an alternate backup source (e.g. a cPanel backup). Normally you would restore the backup onto a fresh WP installation on another host or on a subdomain. If the restore fails your destination site might become unusable, so be prepared to enter cPanel and then destroy / recreate the new installation if necessary.
|
83 |
+
* DO NOT use our backup plugin on WP Engine or any hosting system with proprietary operating system. Instead, use their built-in backup tools.
|
84 |
+
* Large sites (>2GB) might take as long as an hour to backup and migrate. Sites of 250 MB or less should take no more than a minute or two to backup, depending on your server.
|
85 |
+
* We recommend you deactivate and delete page caching, security and maybe redirection plugins before you migrate, and re-install them on the new site, if necessary. In general, delete all unnecessary plugins and data from your site before you backup. You can also use the "Exclude directories" option if you have large media files, which you can then copy back to the new site with FTP.
|
86 |
+
* How to copy from local server to your hosted website: Create a backup of the local site in the usual way, then save the backup file (right-click > Save) to your local disk. Upload this file to the root directory of your destination website and then use this url in the “Restore” dialog of the new site: http://yourdomain.com/<name of the backup file.zip>.
|
87 |
+
|
88 |
+
= Help Video =
|
89 |
+
|
90 |
+
|
91 |
+
(Old video, but may still be helpful; we’ll replace it with an updated one soon)
|
92 |
+
|
93 |
+
[youtube http://www.youtube.com/watch?v=xN5Ffhyn4Ao]
|
94 |
+
|
95 |
+
= Credits =
|
96 |
+
WP Backup uses functions from the "Safe Search and Replace on Database with Serialized Data" script first written by David Coveney of Interconnect IT Ltd (UK) http://www.davidcoveney.com or http://www.interconnectit.com and released under the WTFPL http://sam.zoy.org/wtfpl/. Partial script with full changelog is placed inside 'lib/files' directory.
|
97 |
+
|
98 |
+
Thank you for reading this far. Please don’t forget to [contribute to create a kick-ass plugin](https://sellcodes.com/q1OGuSox). Thank you!
|
99 |
+
== Installation ==
|
100 |
+
|
101 |
+
1. Navigate to Plugins > Add New
|
102 |
+
2. Search for "WP Clone by WP Academy"
|
103 |
+
3. Install and activate the backup and migration plugin
|
104 |
+
4. Follow remaining instructions in the help video
|
105 |
+
|
106 |
+
== Frequently Asked Questions ==
|
107 |
+
Backup and migration FAQ are under construction
|
108 |
+
|
109 |
+
== Changelog ==
|
110 |
+
|
111 |
+
= 2.2.10 =
|
112 |
+
* Updated links
|
113 |
+
|
114 |
+
= 2.2.9 =
|
115 |
+
* Updated feedback version
|
116 |
+
* Updated migration texts
|
117 |
+
|
118 |
+
= 2.2.8 =
|
119 |
+
* Updated migration texts
|
120 |
+
|
121 |
+
= 2.2.7 =
|
122 |
+
* Updated backup plugin pic
|
123 |
+
* Updated backup texts
|
124 |
+
|
125 |
+
= 2.2.6 =
|
126 |
+
* Integrated feedback system
|
127 |
+
|
128 |
+
= 2.2.5 =
|
129 |
+
* Major bug that % got hashed fixed
|
130 |
+
* Other basic bug fixes (e.g. backups and migrations didn’t work on new domains)
|
131 |
+
* Texts in plugin updated, broken links removed
|
132 |
+
|
133 |
+
= 2.2.4 =
|
134 |
+
* Updated: `Tested up to` tag for backup and migration
|
135 |
+
|
136 |
+
= 2.2.3 =
|
137 |
+
* Added: PHP7 support of backup and migration
|
138 |
+
* Added: a multisite check during restore (not required at time of backup)
|
139 |
+
* Fixed: failed backups due to unreadable files
|
140 |
+
|
141 |
+
= 2.2.1 =
|
142 |
+
* Fixed: Backup names will use the time zone selected in general settings
|
143 |
+
* Added: basic backup and migration logs
|
144 |
+
* Added: An option to exclude files in backup and migration based on size (files larger than 25MB will be excluded by default for backups and migration)
|
145 |
+
* Added: An option to ignore the wordpress table prefix during backup and migration
|
146 |
+
* Added: An option to check the mysql connection during migration (restore)
|
147 |
+
* Changed: Files are no longer copied to a temporary location during backup
|
148 |
+
* Changed: siteurl option is updated during the database import
|
149 |
+
|
150 |
+
= 2.2 =
|
151 |
+
* Fixed: Missing backups that some users encountered after upgrading to 2.1.9
|
152 |
+
* Added: An option to refresh the backup list
|
153 |
+
* Added: An option to remove the database entry and delete all the backup files
|
154 |
+
* Added: A section that shows the uncompressed database size and the uncompressed size and the number of files that will be archived during a full backup
|
155 |
+
* Added: Notes in the advanced settings section regarding the Maximum memory limit and the Script backup execution time fields.
|
156 |
+
* Added: The report returned from the search and replace process into the restore successful page
|
157 |
+
* Changed: Moved the backup list location from the custom table to the wp_options table. (previous backups will be imported and the custom table will be removed on existing installations)
|
158 |
+
* Changed: Only the tables with the wordpress table prefix will be altered during a restore
|
159 |
+
* Changed: Only the tables with the wordpress table prefix will be saved during a backup
|
160 |
+
* Changed: Backup deletion is now handled using AJAX
|
161 |
+
|
162 |
+
= 2.1.6 =
|
163 |
+
* Added: An option to exclude specific directories during backup
|
164 |
+
* Added: An option to only backup the database
|
165 |
+
* Changed: File operations during backup are now handled directly instead of using the WP filesystem abstraction class
|
166 |
+
|
167 |
+
= 2.1.4 =
|
168 |
+
* Fixed: When javascript is disabled,submit button shows "Create Backup" but the plugin attempts to do a restore
|
169 |
+
* Changed: The temporary directory location during the backup restore process from '/wp-content/' to '/wp-content/wpclone-temp/'
|
170 |
+
|
171 |
+
= 2.1.3 =
|
172 |
+
* Added: An option to backup the database using WordPress' WPDB class
|
173 |
+
* Removed: The need to keep the original backup names intact
|
174 |
+
* Changed: The backup name structure
|
175 |
+
* Changed: Backup file downloads are now handled using WP core functions
|
176 |
+
|
177 |
+
= 2.0.2 =
|
178 |
+
* Initial release
|
179 |
+
|
180 |
+
|
181 |
+
== Screenshots ==
|
182 |
+
1. Configuration Page
|
183 |
+
|
184 |
+
== Upgrade Notice ==
|
185 |
+
|
186 |
+
= 2.2.10 =
|
187 |
* Please upgrade!
|
wpclone.php
CHANGED
@@ -1,578 +1,578 @@
|
|
1 |
-
<?php
|
2 |
-
/*
|
3 |
-
Plugin name: WP Clone by WP Academy
|
4 |
-
Plugin URI: http://wpacademy.com/software/
|
5 |
-
Description: Move or copy a WordPress site to another server or to another domain name, move to/from local server hosting, and backup sites.
|
6 |
-
Author: WP Academy
|
7 |
-
Version: 2.2.
|
8 |
-
Author URI: http://wpacademy.com/
|
9 |
-
*/
|
10 |
-
require_once 'analyst/main.php';
|
11 |
-
|
12 |
-
analyst_init(array(
|
13 |
-
'client-id' => '9zdex5mar85kmgya',
|
14 |
-
'client-secret' => 'd5702a59d32c01c211316717493096485d5156e8',
|
15 |
-
'base-dir' => __FILE__
|
16 |
-
));
|
17 |
-
|
18 |
-
|
19 |
-
/**
|
20 |
-
*
|
21 |
-
* @URI https://wordpress.org/plugins/wp-clone-by-wp-academy
|
22 |
-
*
|
23 |
-
* @developed by Shaharia Azam <mail@shaharia.com>
|
24 |
-
*/
|
25 |
-
|
26 |
-
include_once 'lib/functions.php';
|
27 |
-
include_once 'lib/class.wpc-wpdb.php';
|
28 |
-
|
29 |
-
$upload_dir = wp_upload_dir();
|
30 |
-
|
31 |
-
define('WPBACKUP_FILE_PERMISSION', 0755);
|
32 |
-
define('WPCLONE_ROOT', rtrim(str_replace("\\", "/", ABSPATH), "/\\") . '/');
|
33 |
-
define('WPCLONE_BACKUP_FOLDER', 'wp-clone');
|
34 |
-
define('WPCLONE_DIR_UPLOADS', str_replace('\\', '/', $upload_dir['basedir']));
|
35 |
-
define('WPCLONE_DIR_PLUGIN', str_replace('\\', '/', plugin_dir_path(__FILE__)));
|
36 |
-
define('WPCLONE_URL_PLUGIN', plugin_dir_url(__FILE__));
|
37 |
-
define('WPCLONE_DIR_BACKUP', WPCLONE_DIR_UPLOADS . '/' . WPCLONE_BACKUP_FOLDER . '/');
|
38 |
-
define('WPCLONE_INSTALLER_PATH', WPCLONE_DIR_PLUGIN);
|
39 |
-
define('WPCLONE_WP_CONTENT' , str_replace('\\', '/', WP_CONTENT_DIR));
|
40 |
-
define('WPCLONE_ROOT_FILE_PATH' , __FILE__);
|
41 |
-
|
42 |
-
|
43 |
-
/* Init options & tables during activation & deregister init option */
|
44 |
-
|
45 |
-
register_activation_hook((__FILE__), 'wpa_wpclone_activate');
|
46 |
-
register_deactivation_hook(__FILE__ , 'wpa_wpclone_deactivate');
|
47 |
-
register_uninstall_hook(__FILE__ , 'wpa_wpclone_uninstall');
|
48 |
-
add_action('admin_menu', 'wpclone_plugin_menu');
|
49 |
-
add_action( 'wp_ajax_wpclone-ajax-size', 'wpa_wpc_ajax_size' );
|
50 |
-
add_action( 'wp_ajax_wpclone-ajax-dir', 'wpa_wpc_ajax_dir' );
|
51 |
-
add_action( 'wp_ajax_wpclone-ajax-delete', 'wpa_wpc_ajax_delete' );
|
52 |
-
add_action( 'wp_ajax_wpclone-ajax-uninstall', 'wpa_wpc_ajax_uninstall' );
|
53 |
-
add_action( 'wp_ajax_wpclone-search-n-replace', 'wpa_wpc_ajax_search_n_replace' );
|
54 |
-
add_action( 'wp_ajax_wpclone-ajax-banner1-close', 'wpclone_ajax_banner1_close' );
|
55 |
-
add_action( 'wp_ajax_wpclone-ajax-banner1-removed', 'wpclone_ajax_banner1_removed' );
|
56 |
-
add_action( 'wp_ajax_wpclone-ajax-banner1-getstatus', 'wpclone_ajax_banner1_getstatus' );
|
57 |
-
add_action('admin_init', 'wpa_wpc_plugin_redirect');
|
58 |
-
add_action('admin_head', 'wpclone_admin_head_scripts');
|
59 |
-
add_action('admin_footer', 'wpclone_admin_footer_scripts');
|
60 |
-
add_action( 'admin_notices', 'wpclone_admin_notice__success' );
|
61 |
-
|
62 |
-
function wpclone_plugin_menu() {
|
63 |
-
add_menu_page (
|
64 |
-
'WP Clone Plugin Options',
|
65 |
-
'WP Clone',
|
66 |
-
'manage_options',
|
67 |
-
'wp-clone',
|
68 |
-
'wpclone_plugin_options'
|
69 |
-
);
|
70 |
-
}
|
71 |
-
|
72 |
-
function wpa_wpc_ajax_size() {
|
73 |
-
|
74 |
-
check_ajax_referer( 'wpclone-ajax-submit', 'nonce' );
|
75 |
-
|
76 |
-
$cached = get_option( 'wpclone_directory_scan' );
|
77 |
-
$interval = 600; /* 10 minutes */
|
78 |
-
|
79 |
-
if( false !== $cached && time() - $cached['time'] < $interval ) {
|
80 |
-
$size = $cached;
|
81 |
-
$size['time'] = date( 'i', time() - $size['time'] );
|
82 |
-
} else {
|
83 |
-
$size = wpa_wpc_dir_size( WP_CONTENT_DIR );
|
84 |
-
}
|
85 |
-
|
86 |
-
echo json_encode( $size );
|
87 |
-
wp_die();
|
88 |
-
|
89 |
-
}
|
90 |
-
|
91 |
-
function wpa_wpc_ajax_dir() {
|
92 |
-
|
93 |
-
check_ajax_referer( 'wpclone-ajax-submit', 'nonce' );
|
94 |
-
if( ! file_exists( WPCLONE_DIR_BACKUP ) ) wpa_create_directory();
|
95 |
-
wpa_wpc_scan_dir();
|
96 |
-
wp_die();
|
97 |
-
|
98 |
-
}
|
99 |
-
|
100 |
-
function wpa_wpc_ajax_delete() {
|
101 |
-
|
102 |
-
check_ajax_referer( 'wpclone-ajax-submit', 'nonce' );
|
103 |
-
|
104 |
-
if( isset( $_REQUEST['fileid'] ) && ! empty( $_REQUEST['fileid'] ) ) {
|
105 |
-
|
106 |
-
echo json_encode( DeleteWPBackupZip( $_REQUEST['fileid'] ) );
|
107 |
-
|
108 |
-
|
109 |
-
}
|
110 |
-
|
111 |
-
wp_die();
|
112 |
-
|
113 |
-
}
|
114 |
-
|
115 |
-
function wpa_wpc_ajax_uninstall() {
|
116 |
-
|
117 |
-
check_ajax_referer( 'wpclone-ajax-submit', 'nonce' );
|
118 |
-
if( file_exists( WPCLONE_DIR_BACKUP ) ) {
|
119 |
-
wpa_delete_dir( WPCLONE_DIR_BACKUP );
|
120 |
-
|
121 |
-
}
|
122 |
-
|
123 |
-
if( file_exists( WPCLONE_WP_CONTENT . 'wpclone-temp' ) ) {
|
124 |
-
wpa_delete_dir( WPCLONE_WP_CONTENT . 'wpclone-temp' );
|
125 |
-
|
126 |
-
}
|
127 |
-
|
128 |
-
delete_option( 'wpclone_backups' );
|
129 |
-
wpa_wpc_remove_table();
|
130 |
-
wp_die();
|
131 |
-
|
132 |
-
}
|
133 |
-
|
134 |
-
function wpa_wpc_ajax_search_n_replace() {
|
135 |
-
check_ajax_referer( 'wpclone-ajax-submit', 'nonce' );
|
136 |
-
global $wpdb;
|
137 |
-
$search = isset( $_POST['search'] ) ? $_POST['search'] : '';
|
138 |
-
$replace = isset( $_POST['replace'] ) ? $_POST['replace'] : '';
|
139 |
-
|
140 |
-
if( empty( $search ) || empty( $replace ) ) {
|
141 |
-
echo '<p class="error">Search and Replace values cannot be empty.</p>';
|
142 |
-
wp_die();
|
143 |
-
}
|
144 |
-
|
145 |
-
wpa_bump_limits();
|
146 |
-
$report = wpa_safe_replace_wrapper( $search, $replace, $wpdb->prefix );
|
147 |
-
echo wpa_wpc_search_n_replace_report( $report );
|
148 |
-
|
149 |
-
wp_die();
|
150 |
-
}
|
151 |
-
|
152 |
-
function wpclone_plugin_options() {
|
153 |
-
include_once 'lib/view.php';
|
154 |
-
}
|
155 |
-
|
156 |
-
function wpa_enqueue_scripts(){
|
157 |
-
wp_register_script('clipboard', plugin_dir_url(__FILE__) . '/lib/js/clipboard.min.js', array('jquery'));
|
158 |
-
wp_register_script('wpclone', plugin_dir_url(__FILE__) . '/lib/js/backupmanager.js', array('jquery'));
|
159 |
-
wp_register_style('wpclone', plugin_dir_url(__FILE__) . '/lib/css/style.css');
|
160 |
-
wp_localize_script('wpclone', 'wpclone', array( 'nonce' => wp_create_nonce( 'wpclone-ajax-submit' ), 'spinner' => esc_url( admin_url( 'images/spinner.gif' ) ) ) );
|
161 |
-
wp_enqueue_script('clipboard');
|
162 |
-
wp_enqueue_script('wpclone');
|
163 |
-
wp_enqueue_style('wpclone');
|
164 |
-
wp_deregister_script('heartbeat');
|
165 |
-
add_thickbox();
|
166 |
-
}
|
167 |
-
if( isset($_GET['page']) && 'wp-clone' == $_GET['page'] ) add_action('admin_enqueue_scripts', 'wpa_enqueue_scripts');
|
168 |
-
|
169 |
-
function wpa_wpclone_activate() {
|
170 |
-
|
171 |
-
//Control after activating redirect to settings page
|
172 |
-
add_option('wpa_wpc_plugin_do_activation_redirect', true);
|
173 |
-
|
174 |
-
wpa_create_directory();
|
175 |
-
}
|
176 |
-
|
177 |
-
function wpa_wpclone_deactivate() {
|
178 |
-
|
179 |
-
//Control after activating redirect to settings page
|
180 |
-
delete_option("wpa_activation_redirect_required");
|
181 |
-
|
182 |
-
if( file_exists( WPCLONE_DIR_BACKUP ) ) {
|
183 |
-
$data = "<Files>\r\n\tOrder allow,deny\r\n\tDeny from all\r\n\tSatisfy all\r\n</Files>";
|
184 |
-
$file = WPCLONE_DIR_BACKUP . '.htaccess';
|
185 |
-
file_put_contents($file, $data);
|
186 |
-
}
|
187 |
-
|
188 |
-
}
|
189 |
-
|
190 |
-
function wpa_wpclone_uninstall() {
|
191 |
-
//Control after activating redirect to settings page
|
192 |
-
delete_option("wpa_activation_redirect_required");
|
193 |
-
delete_option("wpclone_ajax_banner1_close");
|
194 |
-
delete_option("wpclone_ajax_banner1_removed");
|
195 |
-
}
|
196 |
-
|
197 |
-
function wpa_wpc_remove_table() {
|
198 |
-
global $wpdb;
|
199 |
-
$wp_backup = $wpdb->prefix . 'wpclone';
|
200 |
-
$wpdb->query ("DROP TABLE IF EXISTS $wp_backup");
|
201 |
-
}
|
202 |
-
|
203 |
-
function wpa_create_directory() {
|
204 |
-
$indexFile = (WPCLONE_DIR_BACKUP.'index.html');
|
205 |
-
$htacc = WPCLONE_DIR_BACKUP . '.htaccess';
|
206 |
-
$htacc_data = "Options All -Indexes";
|
207 |
-
if (!file_exists($indexFile)) {
|
208 |
-
if(!file_exists(WPCLONE_DIR_BACKUP)) {
|
209 |
-
if(!mkdir(WPCLONE_DIR_BACKUP, WPBACKUP_FILE_PERMISSION)) {
|
210 |
-
die("Unable to create directory '" . rtrim(WPCLONE_DIR_BACKUP, "/\\"). "'. Please set 0755 permission to wp-content.");
|
211 |
-
}
|
212 |
-
}
|
213 |
-
$handle = fopen($indexFile, "w");
|
214 |
-
fclose($handle);
|
215 |
-
}
|
216 |
-
if( file_exists( $htacc ) ) {
|
217 |
-
@unlink ( $htacc );
|
218 |
-
}
|
219 |
-
file_put_contents($htacc, $htacc_data);
|
220 |
-
}
|
221 |
-
|
222 |
-
function wpa_wpc_import_db(){
|
223 |
-
|
224 |
-
global $wpdb;
|
225 |
-
$table_name = $wpdb->prefix . 'wpclone';
|
226 |
-
|
227 |
-
if( $wpdb->get_var( "SHOW TABLES LIKE '$table_name'") === $table_name ) {
|
228 |
-
|
229 |
-
$old_backups = array();
|
230 |
-
$result = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}wpclone ORDER BY id DESC", ARRAY_A);
|
231 |
-
|
232 |
-
foreach( $result as $row ) {
|
233 |
-
|
234 |
-
$time = strtotime( $row['data_time'] );
|
235 |
-
$old_backups[$time] = array(
|
236 |
-
'name' => $row['backup_name'],
|
237 |
-
'creator' => $row['creator'],
|
238 |
-
'size' => $row['backup_size']
|
239 |
-
|
240 |
-
);
|
241 |
-
|
242 |
-
}
|
243 |
-
|
244 |
-
if( false !== get_option( 'wpclone_backups' ) ) {
|
245 |
-
$old_backups = get_option( 'wpclone_backups' ) + $old_backups;
|
246 |
-
}
|
247 |
-
|
248 |
-
update_option( 'wpclone_backups', $old_backups );
|
249 |
-
|
250 |
-
wpa_wpc_remove_table();
|
251 |
-
|
252 |
-
}
|
253 |
-
|
254 |
-
|
255 |
-
}
|
256 |
-
|
257 |
-
function wpa_wpc_msnotice() {
|
258 |
-
echo '<div class="error">';
|
259 |
-
echo '<h4>WP Clone Notice.</h4>';
|
260 |
-
echo '<p>WP Clone is not compatible with multisite installations.</p></div>';
|
261 |
-
}
|
262 |
-
|
263 |
-
if ( is_multisite() )
|
264 |
-
add_action( 'admin_notices', 'wpa_wpc_msnotice');
|
265 |
-
|
266 |
-
function wpa_wpc_phpnotice() {
|
267 |
-
echo '<div class="error">';
|
268 |
-
echo '<h4>WP Clone Notice.</h4>';
|
269 |
-
printf( '<p>WP Clone is not compatible with PHP %s, please upgrade to PHP 5.3 or newer.</p></div>', phpversion() );
|
270 |
-
}
|
271 |
-
|
272 |
-
if( version_compare( phpversion(), '5.3', '<' ) ){
|
273 |
-
add_action( 'admin_notices', 'wpa_wpc_phpnotice');
|
274 |
-
}
|
275 |
-
|
276 |
-
function wpa_wpc_plugin_redirect()
|
277 |
-
|
278 |
-
{
|
279 |
-
|
280 |
-
//Control after activating redirect to settings page
|
281 |
-
if (get_option('wpa_wpc_plugin_do_activation_redirect', false)) {
|
282 |
-
|
283 |
-
delete_option('wpa_wpc_plugin_do_activation_redirect');
|
284 |
-
|
285 |
-
wp_redirect(admin_url('admin.php?page=wp-clone'));
|
286 |
-
}
|
287 |
-
}
|
288 |
-
|
289 |
-
|
290 |
-
//Banner functionality
|
291 |
-
function wpclone_ajax_banner1_close(){
|
292 |
-
update_option("wpclone_ajax_banner1_close", true);
|
293 |
-
update_option("wpclone_ajax_banner1_removed", false);
|
294 |
-
wp_send_json_success(["success" => true]);
|
295 |
-
exit();
|
296 |
-
}
|
297 |
-
|
298 |
-
function wpclone_ajax_banner1_removed(){
|
299 |
-
update_option("wpclone_ajax_banner1_close", true);
|
300 |
-
update_option("wpclone_ajax_banner1_removed", true);
|
301 |
-
wp_send_json_success(["success" => true]);
|
302 |
-
exit();
|
303 |
-
}
|
304 |
-
|
305 |
-
function wpclone_ajax_banner1_getstatus(){
|
306 |
-
wp_send_json_success([
|
307 |
-
'wpclone_ajax_banner1_close' => get_option("wpclone_ajax_banner1_close", "0"),
|
308 |
-
'wpclone_ajax_banner1_removed' => get_option("wpclone_ajax_banner1_removed", "0")
|
309 |
-
]);
|
310 |
-
exit();
|
311 |
-
}
|
312 |
-
|
313 |
-
function wpclone_admin_head_scripts(){
|
314 |
-
echo '<style rel="stylesheet">
|
315 |
-
/** Banner CSS **/
|
316 |
-
.banner-1{
|
317 |
-
min-height: 744px;
|
318 |
-
width: auto;
|
319 |
-
background-size: cover;
|
320 |
-
padding-top: 48px;
|
321 |
-
padding-left: 61px;
|
322 |
-
padding-right: 85px;
|
323 |
-
font-family: \'Montserrat\', sans-serif;
|
324 |
-
margin-right: 30px;
|
325 |
-
margin-top: 20px;
|
326 |
-
}
|
327 |
-
|
328 |
-
.banner-1 .heading{
|
329 |
-
color: #0f9087;
|
330 |
-
font-size: 26px;
|
331 |
-
}
|
332 |
-
|
333 |
-
.banner-1 .nutshell-list{
|
334 |
-
color: #3A3A3A;
|
335 |
-
font-size: 18px;
|
336 |
-
line-height: 22px;
|
337 |
-
}
|
338 |
-
|
339 |
-
.banner-1 .nutshell-list li{
|
340 |
-
list-style-position: inside;
|
341 |
-
text-indent: -1em;
|
342 |
-
padding-left: 20px;
|
343 |
-
}
|
344 |
-
|
345 |
-
|
346 |
-
.banner-1 .banner-footer {
|
347 |
-
margin-top: 25px;
|
348 |
-
font-size: 18px;
|
349 |
-
line-height: 29px;
|
350 |
-
}
|
351 |
-
|
352 |
-
.button1 span.sc-button {
|
353 |
-
-webkit-font-smoothing: antialiased;
|
354 |
-
background-color: #0f9087;
|
355 |
-
border: none;
|
356 |
-
color: #fff;
|
357 |
-
display: inline-block;
|
358 |
-
text-decoration: none;
|
359 |
-
user-select: none;
|
360 |
-
letter-spacing: 1px;
|
361 |
-
padding-left: 25px;
|
362 |
-
padding-right: 25px;
|
363 |
-
padding-top: 12px;
|
364 |
-
padding-bottom: 12px;
|
365 |
-
transition: all 0.1s ease-out;
|
366 |
-
border-radius: 15px;
|
367 |
-
}
|
368 |
-
|
369 |
-
.banner-1 .button1{
|
370 |
-
|
371 |
-
|
372 |
-
}
|
373 |
-
.banner-1 .button2{
|
374 |
-
-webkit-font-smoothing: antialiased;
|
375 |
-
background-color: #0f9087;
|
376 |
-
border: none;
|
377 |
-
color: #fff;
|
378 |
-
display: inline-block;
|
379 |
-
text-decoration: none;
|
380 |
-
user-select: none;
|
381 |
-
letter-spacing: 1px;
|
382 |
-
padding: 12px 35px;
|
383 |
-
text-transform: uppercase;
|
384 |
-
transition: all 0.1s ease-out;
|
385 |
-
border-radius: 10px;
|
386 |
-
}
|
387 |
-
|
388 |
-
.banner-1 .close-icon {
|
389 |
-
float: right;
|
390 |
-
margin-top: -30px;
|
391 |
-
margin-right: -65px;
|
392 |
-
cursor: pointer;
|
393 |
-
}
|
394 |
-
|
395 |
-
.plugin-large-notice .banner-1-collapsed{
|
396 |
-
min-height: 63px;
|
397 |
-
width: auto;
|
398 |
-
/*padding-top: 48px;
|
399 |
-
padding-left: 61px;
|
400 |
-
padding-right: 85px;*/
|
401 |
-
font-family: \'Montserrat\', sans-serif;
|
402 |
-
margin-right: 30px;
|
403 |
-
margin-top: 20px;
|
404 |
-
}
|
405 |
-
|
406 |
-
.plugin-large-notice .banner-1-collapsed p.left-text {
|
407 |
-
font-size: 20px;
|
408 |
-
line-height: 25px;
|
409 |
-
color: #0f9087;
|
410 |
-
font-family: \'Montserrat\', sans-serif;
|
411 |
-
padding-left: 15px;
|
412 |
-
float: left;
|
413 |
-
}
|
414 |
-
|
415 |
-
.plugin-large-notice .banner-1-collapsed p.left-text a {
|
416 |
-
font-size: 15px;
|
417 |
-
color: #0f9087;
|
418 |
-
text-decoration: underline;
|
419 |
-
}
|
420 |
-
|
421 |
-
.plugin-large-notice .banner-1-collapsed p.remove-for-good {
|
422 |
-
float: right;
|
423 |
-
font-size: 16px;
|
424 |
-
color: #0f9087;
|
425 |
-
margin-right: 30px;
|
426 |
-
line-height: 35px;
|
427 |
-
cursor: pointer;
|
428 |
-
}
|
429 |
-
.nutshell-list a {
|
430 |
-
color: #0f9087;
|
431 |
-
text-decoration: underline;
|
432 |
-
}
|
433 |
-
</style>';
|
434 |
-
}
|
435 |
-
|
436 |
-
function wpclone_admin_footer_scripts(){
|
437 |
-
echo '<script>
|
438 |
-
jQuery(function($) {
|
439 |
-
//Banner notice
|
440 |
-
$("document").ready(function (e) {
|
441 |
-
$.ajax({
|
442 |
-
url: ajaxurl,
|
443 |
-
type: \'get\',
|
444 |
-
data: {
|
445 |
-
\'action\': \'wpclone-ajax-banner1-getstatus\'
|
446 |
-
},
|
447 |
-
success: function(data){
|
448 |
-
var urlParams = new URLSearchParams(window.location.search);
|
449 |
-
var currentPage = urlParams.get("page");
|
450 |
-
|
451 |
-
if(data.data.wpclone_ajax_banner1_close === "1" && data.data.wpclone_ajax_banner1_removed === "1"){
|
452 |
-
$(".banner-1-collapsed").hide().remove();
|
453 |
-
$(".banner-1").hide().remove();
|
454 |
-
}else if(data.data.wpclone_ajax_banner1_close === "1" && data.data.wpclone_ajax_banner1_removed != "1"){
|
455 |
-
if(currentPage === "wp-clone"){
|
456 |
-
$(".banner-1-collapsed").show();
|
457 |
-
$(".banner-1").hide();
|
458 |
-
}else{
|
459 |
-
$(".banner-1-collapsed").show();
|
460 |
-
$(".banner-1").hide();
|
461 |
-
}
|
462 |
-
}else if(data.data.wpclone_ajax_banner1_close === "0" && data.data.wpclone_ajax_banner1_removed === "0"){
|
463 |
-
if(currentPage === "wp-clone"){
|
464 |
-
$(".banner-1-collapsed").hide();
|
465 |
-
$(".banner-1").show();
|
466 |
-
}else{
|
467 |
-
$(".banner-1-collapsed").show();
|
468 |
-
$(".banner-1").hide();
|
469 |
-
}
|
470 |
-
}
|
471 |
-
},
|
472 |
-
error: function(e){
|
473 |
-
}
|
474 |
-
});
|
475 |
-
});
|
476 |
-
$("a#show-large-banner-1").on("click", function(){
|
477 |
-
$(".banner-1-collapsed").hide();
|
478 |
-
$(".banner-1").show(100);
|
479 |
-
});
|
480 |
-
$("#please-first-read-it").on("click", function(){
|
481 |
-
$(".banner-1-collapsed").hide();
|
482 |
-
$(".banner-1").show(100);
|
483 |
-
});
|
484 |
-
$(".banner-1 .close-icon").on("click", function (e) {
|
485 |
-
$(".banner-1-collapsed").show(100);
|
486 |
-
$(".banner-1").hide(100);
|
487 |
-
|
488 |
-
$.ajax({
|
489 |
-
url: ajaxurl,
|
490 |
-
type: \'get\',
|
491 |
-
data: {
|
492 |
-
\'action\': \'wpclone-ajax-banner1-close\'
|
493 |
-
},
|
494 |
-
success: function(data){
|
495 |
-
console.log(data);
|
496 |
-
},
|
497 |
-
error: function(e){
|
498 |
-
}
|
499 |
-
});
|
500 |
-
})
|
501 |
-
|
502 |
-
$(".banner-1-collapsed #remove-for-good-text").on("click", function (e) {
|
503 |
-
$(".banner-1-collapsed").hide();
|
504 |
-
|
505 |
-
$.ajax({
|
506 |
-
url: ajaxurl,
|
507 |
-
type: \'get\',
|
508 |
-
data: {
|
509 |
-
\'action\': \'wpclone-ajax-banner1-removed\'
|
510 |
-
},
|
511 |
-
success: function(data){
|
512 |
-
console.log(data);
|
513 |
-
},
|
514 |
-
error: function(e){
|
515 |
-
}
|
516 |
-
});
|
517 |
-
})
|
518 |
-
});
|
519 |
-
</script>';
|
520 |
-
}
|
521 |
-
|
522 |
-
function wpclone_admin_notice__success() {
|
523 |
-
?>
|
524 |
-
<script type="text/javascript" src="https://sellcodes.com/quick_purchase/q1OGuSox/embed.js" async="async"></script>
|
525 |
-
<div style="clear: both; margin-top: 2px;"></div>
|
526 |
-
<div class="plugin-large-notice">
|
527 |
-
<div class="banner-1-collapsed" style="display:none; background-image: url('<?php echo plugins_url( 'lib/img/banner_bg_fold_2.jpg', __FILE__ )?>')">
|
528 |
-
<p class="left-text"><strong>BIG NEWS:</strong> We want WP Clone to arise from the dead. <a href="#" id="show-large-banner-1">Read more</a></p>
|
529 |
-
<p class="remove-for-good"><span id="remove-for-good-text" style="text-decoration: underline">Remove for good</span> <span style="font-size: 14px; cursor: pointer;">(please first <span id="please-first-read-it" style="text-decoration: underline">read it</span>!)</span></p>
|
530 |
-
</div>
|
531 |
-
<div class="banner-1" style="display:none;background-image: url('<?php echo plugins_url( 'lib/img/banner_bg.jpg', __FILE__ )?>')">
|
532 |
-
<div class="close-icon"><img src='<?php echo plugins_url( 'lib/img/banner_close_icon.png', __FILE__ )?>'> </div>
|
533 |
-
<div class="heading">BIG NEWS: <strong>We want WP Clone to arise from the dead.</strong> Please help us!</div>
|
534 |
-
<div style="margin-top: 27px; font-size: 20px; color: #3a3a3a">The key points in a nutshell:</div>
|
535 |
-
<div class="nutshell-list">
|
536 |
-
<ul>
|
537 |
-
<li>1. New contributors have been added to the plugin, and with it comes new motivation to make it a kick-ass product!</li>
|
538 |
-
<li>2. Some fixes have been applied, the plugin now works in 90% of cases (and a further 9% if you follow the process as
|
539 |
-
outlined on the <a href="https://wordpress.org/plugins/wp-clone-by-wp-academy/" target="_blank">plugin page</a>)</li>
|
540 |
-
<li>
|
541 |
-
3. We want to revive the plugin, make it work in 100% of cases, and add many more features. As we’re short on cash,
|
542 |
-
we’re crowdfunding it, and need your help:
|
543 |
-
<ul style="margin-left: 30px;margin-top: 15px;">
|
544 |
-
<li>
|
545 |
-
a. <span class="sellcodes-quick-purchase" style="float: none;"><span style="text-decoration: underline; font-family: 'Montserrat', sans-serif;" class="sc-button" data-product-id="q1OGuSox" data-option-id="FgUPGiaV">Contribution of 5 or 10 USD:</span></span> You get the warm fuzzy feeling from giving a sincere “Thank you” for a plugin which <br>
|
546 |
-
probably made your life easier in the past, and helping to further develop it. Plus: a free backlink to your site!
|
547 |
-
</li>
|
548 |
-
<li>
|
549 |
-
b. <span class="sellcodes-quick-purchase" style="float: none;"><span style="text-decoration: underline; font-family: 'Montserrat', sans-serif;" class="sc-button" data-product-id="q1OGuSox" data-option-id="HtNSwPAK">Contribution of 15 USD:</span></span> As in a), plus you will be rewarded with a <strong>free plugin license</strong> <br>
|
550 |
-
(for the premium product which we will create). A contribution of 30 USD gets you 2 licenses.
|
551 |
-
</li>
|
552 |
-
<li>
|
553 |
-
c. <span class="sellcodes-quick-purchase" style="float: none;"><span style="text-decoration: underline; font-family: 'Montserrat', sans-serif;" class="sc-button" data-product-id="q1OGuSox" data-option-id="3DV66HIl">Contribution of 50 USD:</span></span> As in a), plus an <strong>unlimited websites premium license.</strong> <br>
|
554 |
-
This a fantastic, one-time deal. The plugin will provide many more features <br>
|
555 |
-
- such as backup scheduling, backup to external servers etc. - while still <br>being super-easy to use! It will be the best on the market – <strong style="text-decoration: underline">guaranteed</strong>.
|
556 |
-
</li>
|
557 |
-
</ul>
|
558 |
-
</li>
|
559 |
-
</ul>
|
560 |
-
</div>
|
561 |
-
<div class="banner-footer">
|
562 |
-
<span>All licenses are <strong>lifetime licenses</strong> and valid on both commercial and non-commercial <br>websites. The crowdfunding target is USD 3,000. If we don’t reach it you’ll be refunded*.</span> <br> <br>
|
563 |
-
Thank you for your support - we <span style="text-decoration: underline;">really</span> depend on it!
|
564 |
-
</div>
|
565 |
-
<div style="margin-top: 33px;">
|
566 |
-
<a href="#" class="button1"><span class="sellcodes-quick-purchase" style="float: none;"><span style="letter-spacing: 1.2px; color: #ffffff; text-decoration: none; font-family: 'Montserrat', sans-serif;" class="sc-button" data-product-id="q1OGuSox" data-option-id="FgUPGiaV">Contribute</span></span></a>
|
567 |
-
<a href="#" class="button1"><span class="sellcodes-quick-purchase" style="float: none;"><span style="letter-spacing: 1.2px; color: #ffffff; text-decoration: none; font-family: 'Montserrat', sans-serif;" class="sc-button" data-product-id="q1OGuSox" data-option-id="3DV66HIl">Contribute & get free license(s)</span></span></a>
|
568 |
-
</div>
|
569 |
-
<p style="margin-top: 33px;">
|
570 |
-
Also check out the <a href="https://wordpress.org/plugins/wp-clone-by-wp-academy/" target="_blank" style="color: #0f9087">updated plugin description.</a> To follow our funding progress please go <a href="https://sellcodes.com/q1OGuSox" target="_blank" style="color: #0f9087">here</a>.
|
571 |
-
</p>
|
572 |
-
<p style="margin-top: 33px; color: #0f9087">
|
573 |
-
*With the exception of the 5 or 10 USD amounts. We want you to have that warm fuzzy feeling forever ;)
|
574 |
-
</p>
|
575 |
-
</div>
|
576 |
-
</div>
|
577 |
-
<?php
|
578 |
}
|
1 |
+
<?php
|
2 |
+
/*
|
3 |
+
Plugin name: WP Clone by WP Academy
|
4 |
+
Plugin URI: http://wpacademy.com/software/
|
5 |
+
Description: Move or copy a WordPress site to another server or to another domain name, move to/from local server hosting, and backup sites.
|
6 |
+
Author: WP Academy
|
7 |
+
Version: 2.2.10
|
8 |
+
Author URI: http://wpacademy.com/
|
9 |
+
*/
|
10 |
+
require_once 'analyst/main.php';
|
11 |
+
|
12 |
+
analyst_init(array(
|
13 |
+
'client-id' => '9zdex5mar85kmgya',
|
14 |
+
'client-secret' => 'd5702a59d32c01c211316717493096485d5156e8',
|
15 |
+
'base-dir' => __FILE__
|
16 |
+
));
|
17 |
+
|
18 |
+
|
19 |
+
/**
|
20 |
+
*
|
21 |
+
* @URI https://wordpress.org/plugins/wp-clone-by-wp-academy
|
22 |
+
*
|
23 |
+
* @developed by Shaharia Azam <mail@shaharia.com>
|
24 |
+
*/
|
25 |
+
|
26 |
+
include_once 'lib/functions.php';
|
27 |
+
include_once 'lib/class.wpc-wpdb.php';
|
28 |
+
|
29 |
+
$upload_dir = wp_upload_dir();
|
30 |
+
|
31 |
+
define('WPBACKUP_FILE_PERMISSION', 0755);
|
32 |
+
define('WPCLONE_ROOT', rtrim(str_replace("\\", "/", ABSPATH), "/\\") . '/');
|
33 |
+
define('WPCLONE_BACKUP_FOLDER', 'wp-clone');
|
34 |
+
define('WPCLONE_DIR_UPLOADS', str_replace('\\', '/', $upload_dir['basedir']));
|
35 |
+
define('WPCLONE_DIR_PLUGIN', str_replace('\\', '/', plugin_dir_path(__FILE__)));
|
36 |
+
define('WPCLONE_URL_PLUGIN', plugin_dir_url(__FILE__));
|
37 |
+
define('WPCLONE_DIR_BACKUP', WPCLONE_DIR_UPLOADS . '/' . WPCLONE_BACKUP_FOLDER . '/');
|
38 |
+
define('WPCLONE_INSTALLER_PATH', WPCLONE_DIR_PLUGIN);
|
39 |
+
define('WPCLONE_WP_CONTENT' , str_replace('\\', '/', WP_CONTENT_DIR));
|
40 |
+
define('WPCLONE_ROOT_FILE_PATH' , __FILE__);
|
41 |
+
|
42 |
+
|
43 |
+
/* Init options & tables during activation & deregister init option */
|
44 |
+
|
45 |
+
register_activation_hook((__FILE__), 'wpa_wpclone_activate');
|
46 |
+
register_deactivation_hook(__FILE__ , 'wpa_wpclone_deactivate');
|
47 |
+
register_uninstall_hook(__FILE__ , 'wpa_wpclone_uninstall');
|
48 |
+
add_action('admin_menu', 'wpclone_plugin_menu');
|
49 |
+
add_action( 'wp_ajax_wpclone-ajax-size', 'wpa_wpc_ajax_size' );
|
50 |
+
add_action( 'wp_ajax_wpclone-ajax-dir', 'wpa_wpc_ajax_dir' );
|
51 |
+
add_action( 'wp_ajax_wpclone-ajax-delete', 'wpa_wpc_ajax_delete' );
|
52 |
+
add_action( 'wp_ajax_wpclone-ajax-uninstall', 'wpa_wpc_ajax_uninstall' );
|
53 |
+
add_action( 'wp_ajax_wpclone-search-n-replace', 'wpa_wpc_ajax_search_n_replace' );
|
54 |
+
add_action( 'wp_ajax_wpclone-ajax-banner1-close', 'wpclone_ajax_banner1_close' );
|
55 |
+
add_action( 'wp_ajax_wpclone-ajax-banner1-removed', 'wpclone_ajax_banner1_removed' );
|
56 |
+
add_action( 'wp_ajax_wpclone-ajax-banner1-getstatus', 'wpclone_ajax_banner1_getstatus' );
|
57 |
+
add_action('admin_init', 'wpa_wpc_plugin_redirect');
|
58 |
+
add_action('admin_head', 'wpclone_admin_head_scripts');
|
59 |
+
add_action('admin_footer', 'wpclone_admin_footer_scripts');
|
60 |
+
add_action( 'admin_notices', 'wpclone_admin_notice__success' );
|
61 |
+
|
62 |
+
function wpclone_plugin_menu() {
|
63 |
+
add_menu_page (
|
64 |
+
'WP Clone Plugin Options',
|
65 |
+
'WP Clone',
|
66 |
+
'manage_options',
|
67 |
+
'wp-clone',
|
68 |
+
'wpclone_plugin_options'
|
69 |
+
);
|
70 |
+
}
|
71 |
+
|
72 |
+
function wpa_wpc_ajax_size() {
|
73 |
+
|
74 |
+
check_ajax_referer( 'wpclone-ajax-submit', 'nonce' );
|
75 |
+
|
76 |
+
$cached = get_option( 'wpclone_directory_scan' );
|
77 |
+
$interval = 600; /* 10 minutes */
|
78 |
+
|
79 |
+
if( false !== $cached && time() - $cached['time'] < $interval ) {
|
80 |
+
$size = $cached;
|
81 |
+
$size['time'] = date( 'i', time() - $size['time'] );
|
82 |
+
} else {
|
83 |
+
$size = wpa_wpc_dir_size( WP_CONTENT_DIR );
|
84 |
+
}
|
85 |
+
|
86 |
+
echo json_encode( $size );
|
87 |
+
wp_die();
|
88 |
+
|
89 |
+
}
|
90 |
+
|
91 |
+
function wpa_wpc_ajax_dir() {
|
92 |
+
|
93 |
+
check_ajax_referer( 'wpclone-ajax-submit', 'nonce' );
|
94 |
+
if( ! file_exists( WPCLONE_DIR_BACKUP ) ) wpa_create_directory();
|
95 |
+
wpa_wpc_scan_dir();
|
96 |
+
wp_die();
|
97 |
+
|
98 |
+
}
|
99 |
+
|
100 |
+
function wpa_wpc_ajax_delete() {
|
101 |
+
|
102 |
+
check_ajax_referer( 'wpclone-ajax-submit', 'nonce' );
|
103 |
+
|
104 |
+
if( isset( $_REQUEST['fileid'] ) && ! empty( $_REQUEST['fileid'] ) ) {
|
105 |
+
|
106 |
+
echo json_encode( DeleteWPBackupZip( $_REQUEST['fileid'] ) );
|
107 |
+
|
108 |
+
|
109 |
+
}
|
110 |
+
|
111 |
+
wp_die();
|
112 |
+
|
113 |
+
}
|
114 |
+
|
115 |
+
function wpa_wpc_ajax_uninstall() {
|
116 |
+
|
117 |
+
check_ajax_referer( 'wpclone-ajax-submit', 'nonce' );
|
118 |
+
if( file_exists( WPCLONE_DIR_BACKUP ) ) {
|
119 |
+
wpa_delete_dir( WPCLONE_DIR_BACKUP );
|
120 |
+
|
121 |
+
}
|
122 |
+
|
123 |
+
if( file_exists( WPCLONE_WP_CONTENT . 'wpclone-temp' ) ) {
|
124 |
+
wpa_delete_dir( WPCLONE_WP_CONTENT . 'wpclone-temp' );
|
125 |
+
|
126 |
+
}
|
127 |
+
|
128 |
+
delete_option( 'wpclone_backups' );
|
129 |
+
wpa_wpc_remove_table();
|
130 |
+
wp_die();
|
131 |
+
|
132 |
+
}
|
133 |
+
|
134 |
+
function wpa_wpc_ajax_search_n_replace() {
|
135 |
+
check_ajax_referer( 'wpclone-ajax-submit', 'nonce' );
|
136 |
+
global $wpdb;
|
137 |
+
$search = isset( $_POST['search'] ) ? $_POST['search'] : '';
|
138 |
+
$replace = isset( $_POST['replace'] ) ? $_POST['replace'] : '';
|
139 |
+
|
140 |
+
if( empty( $search ) || empty( $replace ) ) {
|
141 |
+
echo '<p class="error">Search and Replace values cannot be empty.</p>';
|
142 |
+
wp_die();
|
143 |
+
}
|
144 |
+
|
145 |
+
wpa_bump_limits();
|
146 |
+
$report = wpa_safe_replace_wrapper( $search, $replace, $wpdb->prefix );
|
147 |
+
echo wpa_wpc_search_n_replace_report( $report );
|
148 |
+
|
149 |
+
wp_die();
|
150 |
+
}
|
151 |
+
|
152 |
+
function wpclone_plugin_options() {
|
153 |
+
include_once 'lib/view.php';
|
154 |
+
}
|
155 |
+
|
156 |
+
function wpa_enqueue_scripts(){
|
157 |
+
wp_register_script('clipboard', plugin_dir_url(__FILE__) . '/lib/js/clipboard.min.js', array('jquery'));
|
158 |
+
wp_register_script('wpclone', plugin_dir_url(__FILE__) . '/lib/js/backupmanager.js', array('jquery'));
|
159 |
+
wp_register_style('wpclone', plugin_dir_url(__FILE__) . '/lib/css/style.css');
|
160 |
+
wp_localize_script('wpclone', 'wpclone', array( 'nonce' => wp_create_nonce( 'wpclone-ajax-submit' ), 'spinner' => esc_url( admin_url( 'images/spinner.gif' ) ) ) );
|
161 |
+
wp_enqueue_script('clipboard');
|
162 |
+
wp_enqueue_script('wpclone');
|
163 |
+
wp_enqueue_style('wpclone');
|
164 |
+
wp_deregister_script('heartbeat');
|
165 |
+
add_thickbox();
|
166 |
+
}
|
167 |
+
if( isset($_GET['page']) && 'wp-clone' == $_GET['page'] ) add_action('admin_enqueue_scripts', 'wpa_enqueue_scripts');
|
168 |
+
|
169 |
+
function wpa_wpclone_activate() {
|
170 |
+
|
171 |
+
//Control after activating redirect to settings page
|
172 |
+
add_option('wpa_wpc_plugin_do_activation_redirect', true);
|
173 |
+
|
174 |
+
wpa_create_directory();
|
175 |
+
}
|
176 |
+
|
177 |
+
function wpa_wpclone_deactivate() {
|
178 |
+
|
179 |
+
//Control after activating redirect to settings page
|
180 |
+
delete_option("wpa_activation_redirect_required");
|
181 |
+
|
182 |
+
if( file_exists( WPCLONE_DIR_BACKUP ) ) {
|
183 |
+
$data = "<Files>\r\n\tOrder allow,deny\r\n\tDeny from all\r\n\tSatisfy all\r\n</Files>";
|
184 |
+
$file = WPCLONE_DIR_BACKUP . '.htaccess';
|
185 |
+
file_put_contents($file, $data);
|
186 |
+
}
|
187 |
+
|
188 |
+
}
|
189 |
+
|
190 |
+
function wpa_wpclone_uninstall() {
|
191 |
+
//Control after activating redirect to settings page
|
192 |
+
delete_option("wpa_activation_redirect_required");
|
193 |
+
delete_option("wpclone_ajax_banner1_close");
|
194 |
+
delete_option("wpclone_ajax_banner1_removed");
|
195 |
+
}
|
196 |
+
|
197 |
+
function wpa_wpc_remove_table() {
|
198 |
+
global $wpdb;
|
199 |
+
$wp_backup = $wpdb->prefix . 'wpclone';
|
200 |
+
$wpdb->query ("DROP TABLE IF EXISTS $wp_backup");
|
201 |
+
}
|
202 |
+
|
203 |
+
function wpa_create_directory() {
|
204 |
+
$indexFile = (WPCLONE_DIR_BACKUP.'index.html');
|
205 |
+
$htacc = WPCLONE_DIR_BACKUP . '.htaccess';
|
206 |
+
$htacc_data = "Options All -Indexes";
|
207 |
+
if (!file_exists($indexFile)) {
|
208 |
+
if(!file_exists(WPCLONE_DIR_BACKUP)) {
|
209 |
+
if(!mkdir(WPCLONE_DIR_BACKUP, WPBACKUP_FILE_PERMISSION)) {
|
210 |
+
die("Unable to create directory '" . rtrim(WPCLONE_DIR_BACKUP, "/\\"). "'. Please set 0755 permission to wp-content.");
|
211 |
+
}
|
212 |
+
}
|
213 |
+
$handle = fopen($indexFile, "w");
|
214 |
+
fclose($handle);
|
215 |
+
}
|
216 |
+
if( file_exists( $htacc ) ) {
|
217 |
+
@unlink ( $htacc );
|
218 |
+
}
|
219 |
+
file_put_contents($htacc, $htacc_data);
|
220 |
+
}
|
221 |
+
|
222 |
+
function wpa_wpc_import_db(){
|
223 |
+
|
224 |
+
global $wpdb;
|
225 |
+
$table_name = $wpdb->prefix . 'wpclone';
|
226 |
+
|
227 |
+
if( $wpdb->get_var( "SHOW TABLES LIKE '$table_name'") === $table_name ) {
|
228 |
+
|
229 |
+
$old_backups = array();
|
230 |
+
$result = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}wpclone ORDER BY id DESC", ARRAY_A);
|
231 |
+
|
232 |
+
foreach( $result as $row ) {
|
233 |
+
|
234 |
+
$time = strtotime( $row['data_time'] );
|
235 |
+
$old_backups[$time] = array(
|
236 |
+
'name' => $row['backup_name'],
|
237 |
+
'creator' => $row['creator'],
|
238 |
+
'size' => $row['backup_size']
|
239 |
+
|
240 |
+
);
|
241 |
+
|
242 |
+
}
|
243 |
+
|
244 |
+
if( false !== get_option( 'wpclone_backups' ) ) {
|
245 |
+
$old_backups = get_option( 'wpclone_backups' ) + $old_backups;
|
246 |
+
}
|
247 |
+
|
248 |
+
update_option( 'wpclone_backups', $old_backups );
|
249 |
+
|
250 |
+
wpa_wpc_remove_table();
|
251 |
+
|
252 |
+
}
|
253 |
+
|
254 |
+
|
255 |
+
}
|
256 |
+
|
257 |
+
function wpa_wpc_msnotice() {
|
258 |
+
echo '<div class="error">';
|
259 |
+
echo '<h4>WP Clone Notice.</h4>';
|
260 |
+
echo '<p>WP Clone is not compatible with multisite installations.</p></div>';
|
261 |
+
}
|
262 |
+
|
263 |
+
if ( is_multisite() )
|
264 |
+
add_action( 'admin_notices', 'wpa_wpc_msnotice');
|
265 |
+
|
266 |
+
function wpa_wpc_phpnotice() {
|
267 |
+
echo '<div class="error">';
|
268 |
+
echo '<h4>WP Clone Notice.</h4>';
|
269 |
+
printf( '<p>WP Clone is not compatible with PHP %s, please upgrade to PHP 5.3 or newer.</p></div>', phpversion() );
|
270 |
+
}
|
271 |
+
|
272 |
+
if( version_compare( phpversion(), '5.3', '<' ) ){
|
273 |
+
add_action( 'admin_notices', 'wpa_wpc_phpnotice');
|
274 |
+
}
|
275 |
+
|
276 |
+
function wpa_wpc_plugin_redirect()
|
277 |
+
|
278 |
+
{
|
279 |
+
|
280 |
+
//Control after activating redirect to settings page
|
281 |
+
if (get_option('wpa_wpc_plugin_do_activation_redirect', false)) {
|
282 |
+
|
283 |
+
delete_option('wpa_wpc_plugin_do_activation_redirect');
|
284 |
+
|
285 |
+
wp_redirect(admin_url('admin.php?page=wp-clone'));
|
286 |
+
}
|
287 |
+
}
|
288 |
+
|
289 |
+
|
290 |
+
//Banner functionality
|
291 |
+
function wpclone_ajax_banner1_close(){
|
292 |
+
update_option("wpclone_ajax_banner1_close", true);
|
293 |
+
update_option("wpclone_ajax_banner1_removed", false);
|
294 |
+
wp_send_json_success(["success" => true]);
|
295 |
+
exit();
|
296 |
+
}
|
297 |
+
|
298 |
+
function wpclone_ajax_banner1_removed(){
|
299 |
+
update_option("wpclone_ajax_banner1_close", true);
|
300 |
+
update_option("wpclone_ajax_banner1_removed", true);
|
301 |
+
wp_send_json_success(["success" => true]);
|
302 |
+
exit();
|
303 |
+
}
|
304 |
+
|
305 |
+
function wpclone_ajax_banner1_getstatus(){
|
306 |
+
wp_send_json_success([
|
307 |
+
'wpclone_ajax_banner1_close' => get_option("wpclone_ajax_banner1_close", "0"),
|
308 |
+
'wpclone_ajax_banner1_removed' => get_option("wpclone_ajax_banner1_removed", "0")
|
309 |
+
]);
|
310 |
+
exit();
|
311 |
+
}
|
312 |
+
|
313 |
+
function wpclone_admin_head_scripts(){
|
314 |
+
echo '<style rel="stylesheet">
|
315 |
+
/** Banner CSS **/
|
316 |
+
.banner-1{
|
317 |
+
min-height: 744px;
|
318 |
+
width: auto;
|
319 |
+
background-size: cover;
|
320 |
+
padding-top: 48px;
|
321 |
+
padding-left: 61px;
|
322 |
+
padding-right: 85px;
|
323 |
+
font-family: \'Montserrat\', sans-serif;
|
324 |
+
margin-right: 30px;
|
325 |
+
margin-top: 20px;
|
326 |
+
}
|
327 |
+
|
328 |
+
.banner-1 .heading{
|
329 |
+
color: #0f9087;
|
330 |
+
font-size: 26px;
|
331 |
+
}
|
332 |
+
|
333 |
+
.banner-1 .nutshell-list{
|
334 |
+
color: #3A3A3A;
|
335 |
+
font-size: 18px;
|
336 |
+
line-height: 22px;
|
337 |
+
}
|
338 |
+
|
339 |
+
.banner-1 .nutshell-list li{
|
340 |
+
list-style-position: inside;
|
341 |
+
text-indent: -1em;
|
342 |
+
padding-left: 20px;
|
343 |
+
}
|
344 |
+
|
345 |
+
|
346 |
+
.banner-1 .banner-footer {
|
347 |
+
margin-top: 25px;
|
348 |
+
font-size: 18px;
|
349 |
+
line-height: 29px;
|
350 |
+
}
|
351 |
+
|
352 |
+
.button1 span.sc-button {
|
353 |
+
-webkit-font-smoothing: antialiased;
|
354 |
+
background-color: #0f9087;
|
355 |
+
border: none;
|
356 |
+
color: #fff;
|
357 |
+
display: inline-block;
|
358 |
+
text-decoration: none;
|
359 |
+
user-select: none;
|
360 |
+
letter-spacing: 1px;
|
361 |
+
padding-left: 25px;
|
362 |
+
padding-right: 25px;
|
363 |
+
padding-top: 12px;
|
364 |
+
padding-bottom: 12px;
|
365 |
+
transition: all 0.1s ease-out;
|
366 |
+
border-radius: 15px;
|
367 |
+
}
|
368 |
+
|
369 |
+
.banner-1 .button1{
|
370 |
+
|
371 |
+
|
372 |
+
}
|
373 |
+
.banner-1 .button2{
|
374 |
+
-webkit-font-smoothing: antialiased;
|
375 |
+
background-color: #0f9087;
|
376 |
+
border: none;
|
377 |
+
color: #fff;
|
378 |
+
display: inline-block;
|
379 |
+
text-decoration: none;
|
380 |
+
user-select: none;
|
381 |
+
letter-spacing: 1px;
|
382 |
+
padding: 12px 35px;
|
383 |
+
text-transform: uppercase;
|
384 |
+
transition: all 0.1s ease-out;
|
385 |
+
border-radius: 10px;
|
386 |
+
}
|
387 |
+
|
388 |
+
.banner-1 .close-icon {
|
389 |
+
float: right;
|
390 |
+
margin-top: -30px;
|
391 |
+
margin-right: -65px;
|
392 |
+
cursor: pointer;
|
393 |
+
}
|
394 |
+
|
395 |
+
.plugin-large-notice .banner-1-collapsed{
|
396 |
+
min-height: 63px;
|
397 |
+
width: auto;
|
398 |
+
/*padding-top: 48px;
|
399 |
+
padding-left: 61px;
|
400 |
+
padding-right: 85px;*/
|
401 |
+
font-family: \'Montserrat\', sans-serif;
|
402 |
+
margin-right: 30px;
|
403 |
+
margin-top: 20px;
|
404 |
+
}
|
405 |
+
|
406 |
+
.plugin-large-notice .banner-1-collapsed p.left-text {
|
407 |
+
font-size: 20px;
|
408 |
+
line-height: 25px;
|
409 |
+
color: #0f9087;
|
410 |
+
font-family: \'Montserrat\', sans-serif;
|
411 |
+
padding-left: 15px;
|
412 |
+
float: left;
|
413 |
+
}
|
414 |
+
|
415 |
+
.plugin-large-notice .banner-1-collapsed p.left-text a {
|
416 |
+
font-size: 15px;
|
417 |
+
color: #0f9087;
|
418 |
+
text-decoration: underline;
|
419 |
+
}
|
420 |
+
|
421 |
+
.plugin-large-notice .banner-1-collapsed p.remove-for-good {
|
422 |
+
float: right;
|
423 |
+
font-size: 16px;
|
424 |
+
color: #0f9087;
|
425 |
+
margin-right: 30px;
|
426 |
+
line-height: 35px;
|
427 |
+
cursor: pointer;
|
428 |
+
}
|
429 |
+
.nutshell-list a {
|
430 |
+
color: #0f9087;
|
431 |
+
text-decoration: underline;
|
432 |
+
}
|
433 |
+
</style>';
|
434 |
+
}
|
435 |
+
|
436 |
+
function wpclone_admin_footer_scripts(){
|
437 |
+
echo '<script>
|
438 |
+
jQuery(function($) {
|
439 |
+
//Banner notice
|
440 |
+
$("document").ready(function (e) {
|
441 |
+
$.ajax({
|
442 |
+
url: ajaxurl,
|
443 |
+
type: \'get\',
|
444 |
+
data: {
|
445 |
+
\'action\': \'wpclone-ajax-banner1-getstatus\'
|
446 |
+
},
|
447 |
+
success: function(data){
|
448 |
+
var urlParams = new URLSearchParams(window.location.search);
|
449 |
+
var currentPage = urlParams.get("page");
|
450 |
+
|
451 |
+
if(data.data.wpclone_ajax_banner1_close === "1" && data.data.wpclone_ajax_banner1_removed === "1"){
|
452 |
+
$(".banner-1-collapsed").hide().remove();
|
453 |
+
$(".banner-1").hide().remove();
|
454 |
+
}else if(data.data.wpclone_ajax_banner1_close === "1" && data.data.wpclone_ajax_banner1_removed != "1"){
|
455 |
+
if(currentPage === "wp-clone"){
|
456 |
+
$(".banner-1-collapsed").show();
|
457 |
+
$(".banner-1").hide();
|
458 |
+
}else{
|
459 |
+
$(".banner-1-collapsed").show();
|
460 |
+
$(".banner-1").hide();
|
461 |
+
}
|
462 |
+
}else if(data.data.wpclone_ajax_banner1_close === "0" && data.data.wpclone_ajax_banner1_removed === "0"){
|
463 |
+
if(currentPage === "wp-clone"){
|
464 |
+
$(".banner-1-collapsed").hide();
|
465 |
+
$(".banner-1").show();
|
466 |
+
}else{
|
467 |
+
$(".banner-1-collapsed").show();
|
468 |
+
$(".banner-1").hide();
|
469 |
+
}
|
470 |
+
}
|
471 |
+
},
|
472 |
+
error: function(e){
|
473 |
+
}
|
474 |
+
});
|
475 |
+
});
|
476 |
+
$("a#show-large-banner-1").on("click", function(){
|
477 |
+
$(".banner-1-collapsed").hide();
|
478 |
+
$(".banner-1").show(100);
|
479 |
+
});
|
480 |
+
$("#please-first-read-it").on("click", function(){
|
481 |
+
$(".banner-1-collapsed").hide();
|
482 |
+
$(".banner-1").show(100);
|
483 |
+
});
|
484 |
+
$(".banner-1 .close-icon").on("click", function (e) {
|
485 |
+
$(".banner-1-collapsed").show(100);
|
486 |
+
$(".banner-1").hide(100);
|
487 |
+
|
488 |
+
$.ajax({
|
489 |
+
url: ajaxurl,
|
490 |
+
type: \'get\',
|
491 |
+
data: {
|
492 |
+
\'action\': \'wpclone-ajax-banner1-close\'
|
493 |
+
},
|
494 |
+
success: function(data){
|
495 |
+
console.log(data);
|
496 |
+
},
|
497 |
+
error: function(e){
|
498 |
+
}
|
499 |
+
});
|
500 |
+
})
|
501 |
+
|
502 |
+
$(".banner-1-collapsed #remove-for-good-text").on("click", function (e) {
|
503 |
+
$(".banner-1-collapsed").hide();
|
504 |
+
|
505 |
+
$.ajax({
|
506 |
+
url: ajaxurl,
|
507 |
+
type: \'get\',
|
508 |
+
data: {
|
509 |
+
\'action\': \'wpclone-ajax-banner1-removed\'
|
510 |
+
},
|
511 |
+
success: function(data){
|
512 |
+
console.log(data);
|
513 |
+
},
|
514 |
+
error: function(e){
|
515 |
+
}
|
516 |
+
});
|
517 |
+
})
|
518 |
+
});
|
519 |
+
</script>';
|
520 |
+
}
|
521 |
+
|
522 |
+
function wpclone_admin_notice__success() {
|
523 |
+
?>
|
524 |
+
<script type="text/javascript" src="https://sellcodes.com/quick_purchase/q1OGuSox/embed.js" async="async"></script>
|
525 |
+
<div style="clear: both; margin-top: 2px;"></div>
|
526 |
+
<div class="plugin-large-notice">
|
527 |
+
<div class="banner-1-collapsed" style="display:none; background-image: url('<?php echo plugins_url( 'lib/img/banner_bg_fold_2.jpg', __FILE__ )?>')">
|
528 |
+
<p class="left-text"><strong>BIG NEWS:</strong> We want WP Clone to arise from the dead. <a href="#" id="show-large-banner-1">Read more</a></p>
|
529 |
+
<p class="remove-for-good"><span id="remove-for-good-text" style="text-decoration: underline">Remove for good</span> <span style="font-size: 14px; cursor: pointer;">(please first <span id="please-first-read-it" style="text-decoration: underline">read it</span>!)</span></p>
|
530 |
+
</div>
|
531 |
+
<div class="banner-1" style="display:none;background-image: url('<?php echo plugins_url( 'lib/img/banner_bg.jpg', __FILE__ )?>')">
|
532 |
+
<div class="close-icon"><img src='<?php echo plugins_url( 'lib/img/banner_close_icon.png', __FILE__ )?>'> </div>
|
533 |
+
<div class="heading">BIG NEWS: <strong>We want WP Clone to arise from the dead.</strong> Please help us!</div>
|
534 |
+
<div style="margin-top: 27px; font-size: 20px; color: #3a3a3a">The key points in a nutshell:</div>
|
535 |
+
<div class="nutshell-list">
|
536 |
+
<ul>
|
537 |
+
<li>1. New contributors have been added to the plugin, and with it comes new motivation to make it a kick-ass product!</li>
|
538 |
+
<li>2. Some fixes have been applied, the plugin now works in 90% of cases (and a further 9% if you follow the process as
|
539 |
+
outlined on the <a href="https://wordpress.org/plugins/wp-clone-by-wp-academy/" target="_blank">plugin page</a>)</li>
|
540 |
+
<li>
|
541 |
+
3. We want to revive the plugin, make it work in 100% of cases, and add many more features. As we’re short on cash,
|
542 |
+
we’re crowdfunding it, and need your help:
|
543 |
+
<ul style="margin-left: 30px;margin-top: 15px;">
|
544 |
+
<li>
|
545 |
+
a. <span class="sellcodes-quick-purchase" style="float: none;"><span style="text-decoration: underline; font-family: 'Montserrat', sans-serif;" class="sc-button" data-product-id="q1OGuSox" data-option-id="FgUPGiaV">Contribution of 5 or 10 USD:</span></span> You get the warm fuzzy feeling from giving a sincere “Thank you” for a plugin which <br>
|
546 |
+
probably made your life easier in the past, and helping to further develop it. Plus: a free backlink to your site!
|
547 |
+
</li>
|
548 |
+
<li>
|
549 |
+
b. <span class="sellcodes-quick-purchase" style="float: none;"><span style="text-decoration: underline; font-family: 'Montserrat', sans-serif;" class="sc-button" data-product-id="q1OGuSox" data-option-id="HtNSwPAK">Contribution of 15 USD:</span></span> As in a), plus you will be rewarded with a <strong>free plugin license</strong> <br>
|
550 |
+
(for the premium product which we will create). A contribution of 30 USD gets you 2 licenses.
|
551 |
+
</li>
|
552 |
+
<li>
|
553 |
+
c. <span class="sellcodes-quick-purchase" style="float: none;"><span style="text-decoration: underline; font-family: 'Montserrat', sans-serif;" class="sc-button" data-product-id="q1OGuSox" data-option-id="3DV66HIl">Contribution of 50 USD:</span></span> As in a), plus an <strong>unlimited websites premium license.</strong> <br>
|
554 |
+
This a fantastic, one-time deal. The plugin will provide many more features <br>
|
555 |
+
- such as backup scheduling, backup to external servers etc. - while still <br>being super-easy to use! It will be the best on the market – <strong style="text-decoration: underline">guaranteed</strong>.
|
556 |
+
</li>
|
557 |
+
</ul>
|
558 |
+
</li>
|
559 |
+
</ul>
|
560 |
+
</div>
|
561 |
+
<div class="banner-footer">
|
562 |
+
<span>All licenses are <strong>lifetime licenses</strong> and valid on both commercial and non-commercial <br>websites. The crowdfunding target is USD 3,000. If we don’t reach it you’ll be refunded*.</span> <br> <br>
|
563 |
+
Thank you for your support - we <span style="text-decoration: underline;">really</span> depend on it!
|
564 |
+
</div>
|
565 |
+
<div style="margin-top: 33px;">
|
566 |
+
<a href="#" class="button1"><span class="sellcodes-quick-purchase" style="float: none;"><span style="letter-spacing: 1.2px; color: #ffffff; text-decoration: none; font-family: 'Montserrat', sans-serif;" class="sc-button" data-product-id="q1OGuSox" data-option-id="FgUPGiaV">Contribute</span></span></a>
|
567 |
+
<a href="#" class="button1"><span class="sellcodes-quick-purchase" style="float: none;"><span style="letter-spacing: 1.2px; color: #ffffff; text-decoration: none; font-family: 'Montserrat', sans-serif;" class="sc-button" data-product-id="q1OGuSox" data-option-id="3DV66HIl">Contribute & get free license(s)</span></span></a>
|
568 |
+
</div>
|
569 |
+
<p style="margin-top: 33px;">
|
570 |
+
Also check out the <a href="https://wordpress.org/plugins/wp-clone-by-wp-academy/" target="_blank" style="color: #0f9087">updated plugin description.</a> To follow our funding progress please go <a href="https://sellcodes.com/q1OGuSox" target="_blank" style="color: #0f9087">here</a>.
|
571 |
+
</p>
|
572 |
+
<p style="margin-top: 33px; color: #0f9087">
|
573 |
+
*With the exception of the 5 or 10 USD amounts. We want you to have that warm fuzzy feeling forever ;)
|
574 |
+
</p>
|
575 |
+
</div>
|
576 |
+
</div>
|
577 |
+
<?php
|
578 |
}
|