Version Description
Download this release
Release Info
Developer | cory@lamle.org |
Plugin | Duplicator – WordPress Migration Plugin |
Version | 1.5.0 |
Comparing to | |
See all releases |
Code changes from version 1.4.7.2 to 1.5.0
- assets/css/images/index.php +2 -1
- assets/css/index.php +2 -1
- assets/css/style.css +35 -5
- assets/img/index.php +2 -1
- assets/js/duplicator-tooltip.js +122 -0
- assets/js/index.php +2 -1
- assets/js/javascript.php +223 -223
- assets/js/jquery.qtip/index.php +2 -1
- assets/js/popper/popper.js +1961 -0
- assets/js/popper/popper.min.js +5 -0
- assets/js/tippy/dup-pro-tippy.css +91 -0
- assets/js/tippy/index.php +3 -0
- assets/js/tippy/tippy-bundle.umd.js +2509 -0
- assets/js/tippy/tippy-bundle.umd.min.js +1 -0
- assets/js/tippy/tippy.css +1 -0
- assets/webfonts/index.php +2 -1
- classes/class.archive.config.php +44 -48
- classes/class.constants.php +1 -0
- classes/class.db.php +316 -261
- classes/class.io.php +18 -121
- classes/class.logging.php +254 -238
- classes/class.password.php +235 -229
- classes/class.plugin.upgrade.php +8 -8
- classes/class.server.php +326 -323
- classes/class.settings.php +254 -268
- classes/host/class.custom.host.manager.php +13 -12
- classes/host/class.flywheel.host.php +2 -3
- classes/host/class.godaddy.host.php +7 -4
- classes/host/class.liquidweb.host.php +10 -3
- classes/host/class.pantheon.host.php +9 -4
- classes/host/class.wordpresscom.host.php +9 -4
- classes/host/class.wpengine.host.php +8 -5
- classes/host/interface.host.php +3 -2
- classes/index.php +2 -1
- classes/package/class.pack.archive.file.list.php +202 -0
- classes/package/class.pack.archive.filters.php +96 -96
- classes/package/class.pack.archive.php +1014 -805
- classes/package/class.pack.archive.zip.php +61 -48
- classes/package/class.pack.database.php +787 -779
- classes/package/class.pack.installer.php +1043 -551
- classes/package/class.pack.php +1858 -1805
- classes/package/duparchive/class.pack.archive.duparchive.php +268 -313
- classes/package/duparchive/class.pack.archive.duparchive.state.create.php +66 -80
- classes/package/duparchive/class.pack.archive.duparchive.state.expand.php +88 -133
- classes/package/duparchive/index.php +2 -1
- classes/package/index.php +2 -1
- classes/ui/class.ui.dialog.php +219 -235
- classes/ui/class.ui.messages.php +111 -117
- classes/ui/class.ui.notice.php +136 -82
- classes/ui/class.ui.screen.base.php +23 -20
- classes/ui/class.ui.viewstate.php +85 -83
- classes/ui/index.php +2 -1
- classes/utilities/class.u.json.php +166 -167
- classes/utilities/class.u.migration.php +0 -137
- classes/utilities/class.u.multisite.php +264 -39
- classes/utilities/class.u.patch.php +35 -4
- classes/utilities/class.u.php +133 -144
- classes/utilities/class.u.scancheck.php +169 -181
- classes/utilities/class.u.shell.php +49 -45
- classes/utilities/class.u.string.php +96 -99
- classes/utilities/class.u.validator.php +220 -231
- classes/utilities/class.u.zip.php +160 -154
- ctrls/class.web.services.php +19 -22
- ctrls/ctrl.base.php +152 -153
- ctrls/ctrl.package.php +430 -479
- ctrls/ctrl.tools.php +183 -132
- ctrls/ctrl.ui.php +51 -52
- ctrls/index.php +2 -1
- deactivation.php +434 -440
- define.php +70 -78
assets/css/images/index.php
CHANGED
@@ -1,2 +1,3 @@
|
|
1 |
<?php
|
2 |
-
|
|
1 |
<?php
|
2 |
+
|
3 |
+
//silent
|
assets/css/index.php
CHANGED
@@ -1,2 +1,3 @@
|
|
1 |
<?php
|
2 |
-
|
|
1 |
<?php
|
2 |
+
|
3 |
+
//silent
|
assets/css/style.css
CHANGED
@@ -22,14 +22,16 @@ i.grey-icon {color:#777}
|
|
22 |
.no-decoration {text-decoration:none;}
|
23 |
p.description {padding-top:3px}
|
24 |
.dup-guide-txt-color {color:#b0b0b0;}
|
|
|
|
|
25 |
|
26 |
/*TABS*/
|
27 |
ul.category-tabs li {cursor:pointer;user-select: none;}
|
28 |
|
29 |
/*BOXES:Expandable sections */
|
30 |
div.dup-box {padding:0px; display:block; background-color:#fff; border:1px solid #e5e5e5; box-shadow:0 1px 1px rgba(0,0,0,.04);}
|
31 |
-
div.dup-box-title {font-size:20px; padding:12px 0 3px 12px; font-weight:bold; cursor:pointer; height:30px; margin:0; color:#000; }
|
32 |
-
div.dup-box-title:hover {color
|
33 |
div.dup-box-arrow {text-decoration:none!important; float:right; width:27px; height:30px; font-size:16px; cursor:pointer; padding:1px 0 0 0; white-space:nowrap}
|
34 |
div.dup-box-panel {padding:10px 15px 10px 15px; border-top:1px solid #EEEEEE; margin:-1px 0 0 0;}
|
35 |
div.dup-redirect {font-size:16px; font-weight:bold; padding:10px}
|
@@ -66,6 +68,34 @@ div.dup-global-error-reserved-files p.pass-lnks {line-height:24px; margin:-7px 0
|
|
66 |
div.dup-global-error-reserved-files div.pass-msg {padding:5px 0 0 10px; font-size:11px; color:#999; font-style:italic}
|
67 |
div.dup-wpnotice-box {display:none;}
|
68 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
69 |
/*================================================
|
70 |
PARSLEY:Overrides*/
|
71 |
input.parsley-error, textarea.parsley-error {
|
@@ -210,6 +240,8 @@ div.dup-screen-hlp-info {line-height:26px; padding:10px 0 10px 0}
|
|
210 |
}
|
211 |
|
212 |
/** Settings **/
|
|
|
|
|
213 |
#installer-name-mode-option {
|
214 |
line-height:25px;
|
215 |
}
|
@@ -221,9 +253,7 @@ div.dup-screen-hlp-info {line-height:26px; padding:10px 0 10px 0}
|
|
221 |
line-height:18px;
|
222 |
}
|
223 |
|
224 |
-
|
225 |
-
margin:1em 0;
|
226 |
-
}
|
227 |
|
228 |
.storage_pos_fixed_label {
|
229 |
display:inline-block;
|
22 |
.no-decoration {text-decoration:none;}
|
23 |
p.description {padding-top:3px}
|
24 |
.dup-guide-txt-color {color:#b0b0b0;}
|
25 |
+
i.shield-on {color:#337114}
|
26 |
+
i.shield-off {color:maroon}
|
27 |
|
28 |
/*TABS*/
|
29 |
ul.category-tabs li {cursor:pointer;user-select: none;}
|
30 |
|
31 |
/*BOXES:Expandable sections */
|
32 |
div.dup-box {padding:0px; display:block; background-color:#fff; border:1px solid #e5e5e5; box-shadow:0 1px 1px rgba(0,0,0,.04);}
|
33 |
+
div.dup-box-title {font-size:20px; padding:12px 0 3px 12px; font-weight:bold; cursor:pointer; height:30px; margin:0; color:#000; user-select:none; }
|
34 |
+
div.dup-box-title:hover {background-color: #f9f9f9;}
|
35 |
div.dup-box-arrow {text-decoration:none!important; float:right; width:27px; height:30px; font-size:16px; cursor:pointer; padding:1px 0 0 0; white-space:nowrap}
|
36 |
div.dup-box-panel {padding:10px 15px 10px 15px; border-top:1px solid #EEEEEE; margin:-1px 0 0 0;}
|
37 |
div.dup-redirect {font-size:16px; font-weight:bold; padding:10px}
|
68 |
div.dup-global-error-reserved-files div.pass-msg {padding:5px 0 0 10px; font-size:11px; color:#999; font-style:italic}
|
69 |
div.dup-wpnotice-box {display:none;}
|
70 |
|
71 |
+
.dup-migration-pass-wrapper p {
|
72 |
+
font-size: 14px;
|
73 |
+
}
|
74 |
+
|
75 |
+
.dup-migration-pass-title {
|
76 |
+
color: green;
|
77 |
+
font-size: 20px;
|
78 |
+
margin: 10px 0;
|
79 |
+
font-weight: bold;
|
80 |
+
}
|
81 |
+
|
82 |
+
.dup-stored-minstallation-files {
|
83 |
+
margin-left: 20px;
|
84 |
+
line-height: 1;
|
85 |
+
font-style: italic;
|
86 |
+
margin-top: 0;
|
87 |
+
font-size: 12px;
|
88 |
+
}
|
89 |
+
|
90 |
+
.dup-migration-pass-wrapper .sub-note {
|
91 |
+
font-size: 12px;
|
92 |
+
}
|
93 |
+
|
94 |
+
label.dup-store-lbl:hover {
|
95 |
+
color:#000;
|
96 |
+
font-weight:500;
|
97 |
+
}
|
98 |
+
|
99 |
/*================================================
|
100 |
PARSLEY:Overrides*/
|
101 |
input.parsley-error, textarea.parsley-error {
|
240 |
}
|
241 |
|
242 |
/** Settings **/
|
243 |
+
.dup-settings-pages p.description {max-width:700px; font-size:13px; color:#666; padding-left:15px; text-align:justify; }
|
244 |
+
#dup-lite-inst-mode-details p { margin:1em 0; max-width:700px; font-size:13px; color:#666; padding-left:15px; text-align:justify;}
|
245 |
#installer-name-mode-option {
|
246 |
line-height:25px;
|
247 |
}
|
253 |
line-height:18px;
|
254 |
}
|
255 |
|
256 |
+
|
|
|
|
|
257 |
|
258 |
.storage_pos_fixed_label {
|
259 |
display:inline-block;
|
assets/img/index.php
CHANGED
@@ -1,2 +1,3 @@
|
|
1 |
<?php
|
2 |
-
|
|
1 |
<?php
|
2 |
+
|
3 |
+
//silent
|
assets/js/duplicator-tooltip.js
ADDED
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*! dup tooltip */
|
2 |
+
(function ($) {
|
3 |
+
DuplicatorTooltip = {
|
4 |
+
initialized: false,
|
5 |
+
messages: {
|
6 |
+
'copy': 'Copy to clipboard',
|
7 |
+
'copied': 'copied to clipboard',
|
8 |
+
'copyUnable': 'Unable to copy'
|
9 |
+
},
|
10 |
+
load: function () {
|
11 |
+
if (this.initialized) {
|
12 |
+
return;
|
13 |
+
}
|
14 |
+
|
15 |
+
this.loadSelector('[data-tooltip]');
|
16 |
+
this.loadCopySelector('[data-dup-copy-value]');
|
17 |
+
|
18 |
+
this.initialized = true;
|
19 |
+
},
|
20 |
+
loadSelector: function (selector) {
|
21 |
+
$(selector).each(function () {
|
22 |
+
if (this._tippy) {
|
23 |
+
// already init
|
24 |
+
return;
|
25 |
+
}
|
26 |
+
|
27 |
+
tippy(this, {
|
28 |
+
content: function (ref) {
|
29 |
+
var header = ref.dataset.tooltipTitle;
|
30 |
+
var body = ref.dataset.tooltip;
|
31 |
+
var res = header !== undefined ? '<h3>' + header + '</h3>' : '';
|
32 |
+
res += '<div class="dup-tippy-content">' + body + '</div>';
|
33 |
+
return res;
|
34 |
+
},
|
35 |
+
allowHTML: true,
|
36 |
+
interactive: true,
|
37 |
+
placement: this.dataset.tooltipPlacement ? this.dataset.tooltipPlacement : 'bottom-start',
|
38 |
+
theme: 'duplicator',
|
39 |
+
zIndex: 900000,
|
40 |
+
appendTo: document.body
|
41 |
+
});
|
42 |
+
$(this).data('dup-tooltip-loaded', true);
|
43 |
+
});
|
44 |
+
},
|
45 |
+
loadCopySelector: function (selector) {
|
46 |
+
$(selector).each(function () {
|
47 |
+
if (this._tippy) {
|
48 |
+
// already init
|
49 |
+
return;
|
50 |
+
}
|
51 |
+
|
52 |
+
var element = $(this);
|
53 |
+
if (element.hasClass('disabled')) {
|
54 |
+
return;
|
55 |
+
}
|
56 |
+
|
57 |
+
var tippyElement = tippy(this, {
|
58 |
+
allowHTML: true,
|
59 |
+
placement: this.dataset.tooltipPlacement ? this.dataset.tooltipPlacement : 'bottom-start',
|
60 |
+
theme: 'duplicator',
|
61 |
+
zIndex: 900000,
|
62 |
+
hideOnClick: false,
|
63 |
+
trigger: 'manual'
|
64 |
+
});
|
65 |
+
|
66 |
+
var copyTitle = element.is('[data-dup-copy-title]') ? element.data('dup-copy-title') : DuplicatorTooltip.messages.copy;
|
67 |
+
tippyElement.setContent('<div class="dup-tippy-content">' + copyTitle + '</div>');
|
68 |
+
|
69 |
+
//Have to set manually otherwise might hide on click.
|
70 |
+
element.mouseover(function () {
|
71 |
+
tippyElement.show();
|
72 |
+
}).mouseout(function () {
|
73 |
+
tippyElement.hide();
|
74 |
+
});
|
75 |
+
|
76 |
+
element.click(function () {
|
77 |
+
var valueToCopy = element.data('dup-copy-value');
|
78 |
+
var copiedTitle = element.is('[data-dup-copied-title]') ? element.data('dup-copied-title') : valueToCopy + ' ' + DuplicatorTooltip.messages.copied;
|
79 |
+
var message = DuplicatorTooltip.messages.copyUnable;
|
80 |
+
var tmpArea = jQuery("<textarea></textarea>").css({
|
81 |
+
position: 'absolute',
|
82 |
+
top: '-10000px'
|
83 |
+
}).text(valueToCopy).appendTo("body");
|
84 |
+
tmpArea.select();
|
85 |
+
|
86 |
+
try {
|
87 |
+
message = document.execCommand('copy') ? copiedTitle : 'Unable to copy';
|
88 |
+
} catch (err) {
|
89 |
+
console.log(err);
|
90 |
+
}
|
91 |
+
|
92 |
+
tippyElement.setContent('<div class="dup-tippy-content">' + message + '</div>');
|
93 |
+
tippyElement.setProps({ theme: 'duplicator-filled' });
|
94 |
+
|
95 |
+
setTimeout(function () {
|
96 |
+
tippyElement.setContent('<div class="dup-tippy-content">' + copyTitle + '</div>');
|
97 |
+
tippyElement.setProps({ theme: 'duplicator' });
|
98 |
+
}, 2000);
|
99 |
+
});
|
100 |
+
});
|
101 |
+
},
|
102 |
+
updateElementContent: function (selector, content) {
|
103 |
+
if ($(selector).get(0)) {
|
104 |
+
$(selector).get(0)._tippy.setContent('<div class="dup-tippy-content">' + content + '</div>');
|
105 |
+
}
|
106 |
+
},
|
107 |
+
unload: function () {
|
108 |
+
var tooltips = document.querySelectorAll('[data-tooltip], [data-dup-copy-value]');
|
109 |
+
tooltips.forEach(function (element) {
|
110 |
+
if (element._tippy) {
|
111 |
+
element._tippy.destroy();
|
112 |
+
element._tippy = null;
|
113 |
+
}
|
114 |
+
});
|
115 |
+
this.initialized = false;
|
116 |
+
},
|
117 |
+
reload: function () {
|
118 |
+
this.unload();
|
119 |
+
this.load();
|
120 |
+
}
|
121 |
+
}
|
122 |
+
})(jQuery);
|
assets/js/index.php
CHANGED
@@ -1,2 +1,3 @@
|
|
1 |
<?php
|
2 |
-
|
|
1 |
<?php
|
2 |
+
|
3 |
+
//silent
|
assets/js/javascript.php
CHANGED
@@ -3,15 +3,15 @@ defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
|
3 |
?>
|
4 |
<script>
|
5 |
/* DESCRIPTION: Methods and Objects in this file are global and common in
|
6 |
-
* nature use this file to place all shared methods and varibles */
|
7 |
|
8 |
//UNIQUE NAMESPACE
|
9 |
-
Duplicator
|
10 |
-
Duplicator.UI
|
11 |
-
Duplicator.Pack
|
12 |
Duplicator.Settings = new Object();
|
13 |
-
Duplicator.Tools
|
14 |
-
Duplicator.Debug
|
15 |
|
16 |
//GLOBAL CONSTANTS
|
17 |
Duplicator.DEBUG_AJAX_RESPONSE = false;
|
@@ -19,193 +19,193 @@ Duplicator.AJAX_TIMER = null;
|
|
19 |
|
20 |
Duplicator.parseJSON = function(mixData) {
|
21 |
try {
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
return false;
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
return false;
|
61 |
}
|
62 |
|
63 |
|
64 |
/* ============================================================================
|
65 |
* BASE NAMESPACE: All methods at the top of the Duplicator Namespace
|
66 |
-
* ============================================================================
|
67 |
|
68 |
-
/*
|
69 |
Duplicator.StartAjaxTimer = function()
|
70 |
{
|
71 |
-
|
72 |
};
|
73 |
|
74 |
-
/*
|
75 |
Duplicator.EndAjaxTimer = function()
|
76 |
{
|
77 |
-
|
78 |
-
|
79 |
};
|
80 |
|
81 |
-
/*
|
82 |
-
*
|
83 |
Duplicator.ReloadWindow = function(data)
|
84 |
{
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
};
|
91 |
|
92 |
//Basic Util Methods here:
|
93 |
Duplicator.OpenLogWindow = function(target)
|
94 |
{
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
};
|
102 |
|
103 |
|
104 |
/* ============================================================================
|
105 |
* UI NAMESPACE: All methods at the top of the Duplicator Namespace
|
106 |
-
* ============================================================================
|
107 |
|
108 |
-
/*
|
109 |
Duplicator.UI.SaveViewState = function (key, value)
|
110 |
{
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
}
|
135 |
|
136 |
-
/*
|
137 |
Duplicator.UI.SaveMulViewStates = function (states)
|
138 |
{
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
}
|
160 |
|
161 |
/* Animates the progress bar */
|
162 |
Duplicator.UI.AnimateProgressBar = function(id)
|
163 |
{
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
}
|
176 |
|
177 |
Duplicator.UI.IsSaveViewState = true;
|
178 |
/* Toggle MetaBoxes */
|
179 |
Duplicator.UI.ToggleMetaBox = function()
|
180 |
{
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
}
|
194 |
|
195 |
Duplicator.UI.readonly = function(item)
|
196 |
{
|
197 |
-
|
198 |
}
|
199 |
|
200 |
Duplicator.UI.disable = function(item)
|
201 |
{
|
202 |
-
|
203 |
}
|
204 |
|
205 |
Duplicator.UI.enable = function(item)
|
206 |
{
|
207 |
-
|
208 |
-
|
209 |
}
|
210 |
|
211 |
//Init
|
@@ -290,108 +290,108 @@ jQuery(document).ready(function($)
|
|
290 |
});
|
291 |
};
|
292 |
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
|
334 |
|
335 |
Duplicator.UI.loadQtip();
|
336 |
Duplicator.UI.loadSimpeQtip();
|
337 |
Duplicator.UI.Copytext();
|
338 |
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
|
384 |
});
|
385 |
|
386 |
jQuery(document).ready(function($) {
|
387 |
$('.duplicator-message .notice-dismiss, .duplicator-message .duplicator-notice-dismiss, .duplicator-message .duplicator-notice-rate-now').on('click', function (event) {
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
$.post(ajaxurl, {
|
392 |
action: 'duplicator_set_admin_notice_viewed',
|
393 |
notice_id: $(this).closest('.duplicator-message-dismissed').data('notice_id'),
|
394 |
-
|
395 |
});
|
396 |
var $wrapperElm = $(this).closest('.duplicator-message-dismissed');
|
397 |
$wrapperElm.fadeTo(100, 0, function () {
|
3 |
?>
|
4 |
<script>
|
5 |
/* DESCRIPTION: Methods and Objects in this file are global and common in
|
6 |
+
* nature use this file to place all shared methods and varibles */
|
7 |
|
8 |
//UNIQUE NAMESPACE
|
9 |
+
Duplicator = new Object();
|
10 |
+
Duplicator.UI = new Object();
|
11 |
+
Duplicator.Pack = new Object();
|
12 |
Duplicator.Settings = new Object();
|
13 |
+
Duplicator.Tools = new Object();
|
14 |
+
Duplicator.Debug = new Object();
|
15 |
|
16 |
//GLOBAL CONSTANTS
|
17 |
Duplicator.DEBUG_AJAX_RESPONSE = false;
|
19 |
|
20 |
Duplicator.parseJSON = function(mixData) {
|
21 |
try {
|
22 |
+
var parsed = JSON.parse(mixData);
|
23 |
+
return parsed;
|
24 |
+
} catch (e) {
|
25 |
+
console.log("JSON parse failed - 1");
|
26 |
+
console.log(mixData);
|
27 |
+
}
|
28 |
+
|
29 |
+
if (mixData.indexOf('[') > -1 && mixData.indexOf('{') > -1) {
|
30 |
+
if (mixData.indexOf('{') < mixData.indexOf('[')) {
|
31 |
+
var startBracket = '{';
|
32 |
+
var endBracket = '}';
|
33 |
+
} else {
|
34 |
+
var startBracket = '[';
|
35 |
+
var endBracket = ']';
|
36 |
+
}
|
37 |
+
} else if (mixData.indexOf('[') > -1 && mixData.indexOf('{') === -1) {
|
38 |
+
var startBracket = '[';
|
39 |
+
var endBracket = ']';
|
40 |
+
} else {
|
41 |
+
var startBracket = '{';
|
42 |
+
var endBracket = '}';
|
43 |
+
}
|
44 |
+
|
45 |
+
var jsonStartPos = mixData.indexOf(startBracket);
|
46 |
+
var jsonLastPos = mixData.lastIndexOf(endBracket);
|
47 |
+
if (jsonStartPos > -1 && jsonLastPos > -1) {
|
48 |
+
var expectedJsonStr = mixData.slice(jsonStartPos, jsonLastPos + 1);
|
49 |
+
try {
|
50 |
+
var parsed = JSON.parse(expectedJsonStr);
|
51 |
+
return parsed;
|
52 |
+
} catch (e) {
|
53 |
+
console.log("JSON parse failed - 2");
|
54 |
+
console.log(mixData);
|
55 |
+
throw e;
|
56 |
return false;
|
57 |
+
}
|
58 |
+
}
|
59 |
+
throw "could not parse the JSON";
|
60 |
return false;
|
61 |
}
|
62 |
|
63 |
|
64 |
/* ============================================================================
|
65 |
* BASE NAMESPACE: All methods at the top of the Duplicator Namespace
|
66 |
+
* ============================================================================ */
|
67 |
|
68 |
+
/* Starts a timer for Ajax calls */
|
69 |
Duplicator.StartAjaxTimer = function()
|
70 |
{
|
71 |
+
Duplicator.AJAX_TIMER = new Date();
|
72 |
};
|
73 |
|
74 |
+
/* Ends a timer for Ajax calls */
|
75 |
Duplicator.EndAjaxTimer = function()
|
76 |
{
|
77 |
+
var endTime = new Date();
|
78 |
+
Duplicator.AJAX_TIMER = (endTime.getTime() - Duplicator.AJAX_TIMER) /1000;
|
79 |
};
|
80 |
|
81 |
+
/* Reloads the current window
|
82 |
+
* @param data An xhr object */
|
83 |
Duplicator.ReloadWindow = function(data)
|
84 |
{
|
85 |
+
if (Duplicator.DEBUG_AJAX_RESPONSE) {
|
86 |
+
Duplicator.Pack.ShowError('debug on', data);
|
87 |
+
} else {
|
88 |
+
window.location.reload(true);
|
89 |
+
}
|
90 |
};
|
91 |
|
92 |
//Basic Util Methods here:
|
93 |
Duplicator.OpenLogWindow = function(target)
|
94 |
{
|
95 |
+
var target = "log-win" || null;
|
96 |
+
if (target != null) {
|
97 |
+
window.open('?page=duplicator-tools&tab=diagnostics§ion=log', 'log-win');
|
98 |
+
} else {
|
99 |
+
window.open('<?php echo esc_js(DUP_Settings::getSsdirUrl()); ?>' + '/' + log)
|
100 |
+
}
|
101 |
};
|
102 |
|
103 |
|
104 |
/* ============================================================================
|
105 |
* UI NAMESPACE: All methods at the top of the Duplicator Namespace
|
106 |
+
* ============================================================================ */
|
107 |
|
108 |
+
/* Saves the state of a UI element */
|
109 |
Duplicator.UI.SaveViewState = function (key, value)
|
110 |
{
|
111 |
+
if (key != undefined && value != undefined ) {
|
112 |
+
jQuery.ajax({
|
113 |
+
type: "POST",
|
114 |
+
url: ajaxurl,
|
115 |
+
dataType: "text",
|
116 |
+
data: {
|
117 |
+
action : 'DUP_CTRL_UI_SaveViewState',
|
118 |
+
key: key,
|
119 |
+
value: value,
|
120 |
+
nonce: '<?php echo wp_create_nonce('DUP_CTRL_UI_SaveViewState'); ?>'
|
121 |
+
},
|
122 |
+
success: function(respData) {
|
123 |
+
try {
|
124 |
+
var data = Duplicator.parseJSON(respData);
|
125 |
+
} catch(err) {
|
126 |
+
console.error(err);
|
127 |
+
console.error('JSON parse failed for response data: ' + respData);
|
128 |
+
return false;
|
129 |
+
}
|
130 |
+
},
|
131 |
+
error: function(data) {}
|
132 |
+
});
|
133 |
+
}
|
134 |
}
|
135 |
|
136 |
+
/* Saves multiple states of a UI element */
|
137 |
Duplicator.UI.SaveMulViewStates = function (states)
|
138 |
{
|
139 |
+
jQuery.ajax({
|
140 |
+
type: "POST",
|
141 |
+
url: ajaxurl,
|
142 |
+
dataType: "text",
|
143 |
+
data: {
|
144 |
+
action : 'DUP_CTRL_UI_SaveViewState',
|
145 |
+
states: states,
|
146 |
+
nonce: '<?php echo wp_create_nonce('DUP_CTRL_UI_SaveViewState'); ?>'
|
147 |
+
},
|
148 |
+
success: function(respData) {
|
149 |
+
try {
|
150 |
+
var data = Duplicator.parseJSON(respData);
|
151 |
+
} catch(err) {
|
152 |
+
console.error(err);
|
153 |
+
console.error('JSON parse failed for response data: ' + respData);
|
154 |
+
return false;
|
155 |
+
}
|
156 |
+
},
|
157 |
+
error: function(data) {}
|
158 |
+
});
|
159 |
}
|
160 |
|
161 |
/* Animates the progress bar */
|
162 |
Duplicator.UI.AnimateProgressBar = function(id)
|
163 |
{
|
164 |
+
//Create Progress Bar
|
165 |
+
var $mainbar = jQuery("#" + id);
|
166 |
+
$mainbar.progressbar({ value: 100 });
|
167 |
+
$mainbar.height(25);
|
168 |
+
runAnimation($mainbar);
|
169 |
+
|
170 |
+
function runAnimation($pb) {
|
171 |
+
$pb.css({ "padding-left": "0%", "padding-right": "90%" });
|
172 |
+
$pb.progressbar("option", "value", 100);
|
173 |
+
$pb.animate({ paddingLeft: "90%", paddingRight: "0%" }, 3000, "linear", function () { runAnimation($pb); });
|
174 |
+
}
|
175 |
}
|
176 |
|
177 |
Duplicator.UI.IsSaveViewState = true;
|
178 |
/* Toggle MetaBoxes */
|
179 |
Duplicator.UI.ToggleMetaBox = function()
|
180 |
{
|
181 |
+
var $title = jQuery(this);
|
182 |
+
var $panel = $title.parent().find('.dup-box-panel');
|
183 |
+
var $arrow = $title.parent().find('.dup-box-arrow i');
|
184 |
+
var key = $panel.attr('id');
|
185 |
+
var value = $panel.is(":visible") ? 0 : 1;
|
186 |
+
$panel.toggle();
|
187 |
+
if (Duplicator.UI.IsSaveViewState)
|
188 |
+
Duplicator.UI.SaveViewState(key, value);
|
189 |
+
(value)
|
190 |
+
? $arrow.removeClass().addClass('fa fa-caret-up')
|
191 |
+
: $arrow.removeClass().addClass('fa fa-caret-down');
|
192 |
+
|
193 |
}
|
194 |
|
195 |
Duplicator.UI.readonly = function(item)
|
196 |
{
|
197 |
+
jQuery(item).attr('readonly', 'true').css({color:'#999'});
|
198 |
}
|
199 |
|
200 |
Duplicator.UI.disable = function(item)
|
201 |
{
|
202 |
+
jQuery(item).attr('disabled', 'true').css({color:'#999'});
|
203 |
}
|
204 |
|
205 |
Duplicator.UI.enable = function(item)
|
206 |
{
|
207 |
+
jQuery(item).removeAttr('disabled').css({color:'#000'});
|
208 |
+
jQuery(item).removeAttr('readonly').css({color:'#000'});
|
209 |
}
|
210 |
|
211 |
//Init
|
290 |
});
|
291 |
};
|
292 |
|
293 |
+
//INIT: Tabs
|
294 |
+
$("div[data-dup-tabs='true']").each(function () {
|
295 |
+
|
296 |
+
//Load Tab Setup
|
297 |
+
var $root = $(this);
|
298 |
+
var $lblRoot = $root.find('ul:first-child')
|
299 |
+
var $lblKids = $lblRoot.children('li');
|
300 |
+
var $pnls = $root.children('div');
|
301 |
+
|
302 |
+
//Apply Styles
|
303 |
+
$root.addClass('categorydiv');
|
304 |
+
$lblRoot.addClass('category-tabs');
|
305 |
+
$pnls.addClass('tabs-panel').css('display', 'none');
|
306 |
+
$lblKids.eq(0).addClass('tabs').css('font-weight', 'bold');
|
307 |
+
$pnls.eq(0).show();
|
308 |
+
|
309 |
+
//Attach Events
|
310 |
+
$lblKids.click(function(evt)
|
311 |
+
{
|
312 |
+
var $lbls = $(evt.target).parent().children('li');
|
313 |
+
var $pnls = $(evt.target).parent().parent().children('div');
|
314 |
+
var index = ($(evt.target).index());
|
315 |
+
|
316 |
+
$lbls.removeClass('tabs').css('font-weight', 'normal');
|
317 |
+
$lbls.eq(index).addClass('tabs').css('font-weight', 'bold');
|
318 |
+
$pnls.hide();
|
319 |
+
$pnls.eq(index).show();
|
320 |
+
});
|
321 |
+
});
|
322 |
+
|
323 |
+
//Init: Toggle MetaBoxes
|
324 |
+
$('div.dup-box div.dup-box-title').each(function() {
|
325 |
+
var $title = $(this);
|
326 |
+
var $panel = $title.parent().find('.dup-box-panel');
|
327 |
+
var $arrow = $title.find('.dup-box-arrow');
|
328 |
+
$title.click(Duplicator.UI.ToggleMetaBox);
|
329 |
+
($panel.is(":visible"))
|
330 |
+
? $arrow.html('<i class="fa fa-caret-up"></i>')
|
331 |
+
: $arrow.html('<i class="fa fa-caret-down"></i>');
|
332 |
+
});
|
333 |
|
334 |
|
335 |
Duplicator.UI.loadQtip();
|
336 |
Duplicator.UI.loadSimpeQtip();
|
337 |
Duplicator.UI.Copytext();
|
338 |
|
339 |
+
//HANDLEBARS HELPERS
|
340 |
+
if (typeof(Handlebars) != "undefined"){
|
341 |
+
|
342 |
+
function _handleBarscheckCondition(v1, operator, v2) {
|
343 |
+
switch(operator) {
|
344 |
+
case '==':
|
345 |
+
return (v1 == v2);
|
346 |
+
case '===':
|
347 |
+
return (v1 === v2);
|
348 |
+
case '!==':
|
349 |
+
return (v1 !== v2);
|
350 |
+
case '<':
|
351 |
+
return (v1 < v2);
|
352 |
+
case '<=':
|
353 |
+
return (v1 <= v2);
|
354 |
+
case '>':
|
355 |
+
return (v1 > v2);
|
356 |
+
case '>=':
|
357 |
+
return (v1 >= v2);
|
358 |
+
case '&&':
|
359 |
+
return (v1 && v2);
|
360 |
+
case '||':
|
361 |
+
return (v1 || v2);
|
362 |
+
case 'obj||':
|
363 |
+
v1 = typeof(v1) == 'object' ? v1.length : v1;
|
364 |
+
v2 = typeof(v2) == 'object' ? v2.length : v2;
|
365 |
+
return (v1 !=0 || v2 != 0);
|
366 |
+
default:
|
367 |
+
return false;
|
368 |
+
}
|
369 |
+
}
|
370 |
+
|
371 |
+
Handlebars.registerHelper('ifCond', function (v1, operator, v2, options) {
|
372 |
+
return _handleBarscheckCondition(v1, operator, v2)
|
373 |
+
? options.fn(this)
|
374 |
+
: options.inverse(this);
|
375 |
+
});
|
376 |
+
|
377 |
+
Handlebars.registerHelper('if_eq', function(a, b, opts) { return (a == b) ? opts.fn(this) : opts.inverse(this);});
|
378 |
+
Handlebars.registerHelper('if_neq', function(a, b, opts) { return (a != b) ? opts.fn(this) : opts.inverse(this);});
|
379 |
+
}
|
380 |
+
|
381 |
+
//Prevent notice boxes from flashing as its re-positioned in DOM
|
382 |
+
$('div.dup-wpnotice-box').show(300);
|
383 |
|
384 |
});
|
385 |
|
386 |
jQuery(document).ready(function($) {
|
387 |
$('.duplicator-message .notice-dismiss, .duplicator-message .duplicator-notice-dismiss, .duplicator-message .duplicator-notice-rate-now').on('click', function (event) {
|
388 |
+
if ('button button-primary duplicator-notice-rate-now' !== $(event.target).attr('class')) {
|
389 |
+
event.preventDefault();
|
390 |
+
}
|
391 |
$.post(ajaxurl, {
|
392 |
action: 'duplicator_set_admin_notice_viewed',
|
393 |
notice_id: $(this).closest('.duplicator-message-dismissed').data('notice_id'),
|
394 |
+
nonce: '<?php echo wp_create_nonce('duplicator_set_admin_notice_viewed'); ?>'
|
395 |
});
|
396 |
var $wrapperElm = $(this).closest('.duplicator-message-dismissed');
|
397 |
$wrapperElm.fadeTo(100, 0, function () {
|
assets/js/jquery.qtip/index.php
CHANGED
@@ -1,2 +1,3 @@
|
|
1 |
<?php
|
2 |
-
|
|
1 |
<?php
|
2 |
+
|
3 |
+
//silent
|
assets/js/popper/popper.js
ADDED
@@ -0,0 +1,1961 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*!
|
2 |
+
* @popperjs/core v2.6.0 - MIT License
|
3 |
+
*/
|
4 |
+
|
5 |
+
(function (global, factory) {
|
6 |
+
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
7 |
+
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
8 |
+
(global = global || self, factory(global.Popper = {}));
|
9 |
+
}(this, (function (exports) {
|
10 |
+
'use strict';
|
11 |
+
|
12 |
+
function getBoundingClientRect(element)
|
13 |
+
{
|
14 |
+
var rect = element.getBoundingClientRect();
|
15 |
+
return {
|
16 |
+
width: rect.width,
|
17 |
+
height: rect.height,
|
18 |
+
top: rect.top,
|
19 |
+
right: rect.right,
|
20 |
+
bottom: rect.bottom,
|
21 |
+
left: rect.left,
|
22 |
+
x: rect.left,
|
23 |
+
y: rect.top
|
24 |
+
};
|
25 |
+
}
|
26 |
+
|
27 |
+
/*:: import type { Window } from '../types'; */
|
28 |
+
|
29 |
+
/*:: declare function getWindow(node: Node | Window): Window; */
|
30 |
+
function getWindow(node)
|
31 |
+
{
|
32 |
+
if (node.toString() !== '[object Window]') {
|
33 |
+
var ownerDocument = node.ownerDocument;
|
34 |
+
return ownerDocument ? ownerDocument.defaultView || window : window;
|
35 |
+
}
|
36 |
+
|
37 |
+
return node;
|
38 |
+
}
|
39 |
+
|
40 |
+
function getWindowScroll(node)
|
41 |
+
{
|
42 |
+
var win = getWindow(node);
|
43 |
+
var scrollLeft = win.pageXOffset;
|
44 |
+
var scrollTop = win.pageYOffset;
|
45 |
+
return {
|
46 |
+
scrollLeft: scrollLeft,
|
47 |
+
scrollTop: scrollTop
|
48 |
+
};
|
49 |
+
}
|
50 |
+
|
51 |
+
/*:: declare function isElement(node: mixed): boolean %checks(node instanceof
|
52 |
+
Element); */
|
53 |
+
|
54 |
+
function isElement(node)
|
55 |
+
{
|
56 |
+
var OwnElement = getWindow(node).Element;
|
57 |
+
return node instanceof OwnElement || node instanceof Element;
|
58 |
+
}
|
59 |
+
/*:: declare function isHTMLElement(node: mixed): boolean %checks(node instanceof
|
60 |
+
HTMLElement); */
|
61 |
+
|
62 |
+
|
63 |
+
function isHTMLElement(node)
|
64 |
+
{
|
65 |
+
var OwnElement = getWindow(node).HTMLElement;
|
66 |
+
return node instanceof OwnElement || node instanceof HTMLElement;
|
67 |
+
}
|
68 |
+
/*:: declare function isShadowRoot(node: mixed): boolean %checks(node instanceof
|
69 |
+
ShadowRoot); */
|
70 |
+
|
71 |
+
|
72 |
+
function isShadowRoot(node)
|
73 |
+
{
|
74 |
+
var OwnElement = getWindow(node).ShadowRoot;
|
75 |
+
return node instanceof OwnElement || node instanceof ShadowRoot;
|
76 |
+
}
|
77 |
+
|
78 |
+
function getHTMLElementScroll(element)
|
79 |
+
{
|
80 |
+
return {
|
81 |
+
scrollLeft: element.scrollLeft,
|
82 |
+
scrollTop: element.scrollTop
|
83 |
+
};
|
84 |
+
}
|
85 |
+
|
86 |
+
function getNodeScroll(node)
|
87 |
+
{
|
88 |
+
if (node === getWindow(node) || !isHTMLElement(node)) {
|
89 |
+
return getWindowScroll(node);
|
90 |
+
} else {
|
91 |
+
return getHTMLElementScroll(node);
|
92 |
+
}
|
93 |
+
}
|
94 |
+
|
95 |
+
function getNodeName(element)
|
96 |
+
{
|
97 |
+
return element ? (element.nodeName || '').toLowerCase() : null;
|
98 |
+
}
|
99 |
+
|
100 |
+
function getDocumentElement(element)
|
101 |
+
{
|
102 |
+
// $FlowFixMe[incompatible-return]: assume body is always available
|
103 |
+
return ((isElement(element) ? element.ownerDocument : // $FlowFixMe[prop-missing]
|
104 |
+
element.document) || window.document).documentElement;
|
105 |
+
}
|
106 |
+
|
107 |
+
function getWindowScrollBarX(element)
|
108 |
+
{
|
109 |
+
// If <html> has a CSS width greater than the viewport, then this will be
|
110 |
+
// incorrect for RTL.
|
111 |
+
// Popper 1 is broken in this case and never had a bug report so let's assume
|
112 |
+
// it's not an issue. I don't think anyone ever specifies width on <html>
|
113 |
+
// anyway.
|
114 |
+
// Browsers where the left scrollbar doesn't cause an issue report `0` for
|
115 |
+
// this (e.g. Edge 2019, IE11, Safari)
|
116 |
+
return getBoundingClientRect(getDocumentElement(element)).left + getWindowScroll(element).scrollLeft;
|
117 |
+
}
|
118 |
+
|
119 |
+
function getComputedStyle(element)
|
120 |
+
{
|
121 |
+
return getWindow(element).getComputedStyle(element);
|
122 |
+
}
|
123 |
+
|
124 |
+
function isScrollParent(element)
|
125 |
+
{
|
126 |
+
// Firefox wants us to check `-x` and `-y` variations as well
|
127 |
+
var _getComputedStyle = getComputedStyle(element),
|
128 |
+
overflow = _getComputedStyle.overflow,
|
129 |
+
overflowX = _getComputedStyle.overflowX,
|
130 |
+
overflowY = _getComputedStyle.overflowY;
|
131 |
+
|
132 |
+
return /auto|scroll|overlay|hidden/.test(overflow + overflowY + overflowX);
|
133 |
+
}
|
134 |
+
|
135 |
+
// Composite means it takes into account transforms as well as layout.
|
136 |
+
|
137 |
+
function getCompositeRect(elementOrVirtualElement, offsetParent, isFixed)
|
138 |
+
{
|
139 |
+
if (isFixed === void 0) {
|
140 |
+
isFixed = false;
|
141 |
+
}
|
142 |
+
|
143 |
+
var documentElement = getDocumentElement(offsetParent);
|
144 |
+
var rect = getBoundingClientRect(elementOrVirtualElement);
|
145 |
+
var isOffsetParentAnElement = isHTMLElement(offsetParent);
|
146 |
+
var scroll = {
|
147 |
+
scrollLeft: 0,
|
148 |
+
scrollTop: 0
|
149 |
+
};
|
150 |
+
var offsets = {
|
151 |
+
x: 0,
|
152 |
+
y: 0
|
153 |
+
};
|
154 |
+
|
155 |
+
if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) {
|
156 |
+
if (getNodeName(offsetParent) !== 'body' || // https://github.com/popperjs/popper-core/issues/1078
|
157 |
+
isScrollParent(documentElement)) {
|
158 |
+
scroll = getNodeScroll(offsetParent);
|
159 |
+
}
|
160 |
+
|
161 |
+
if (isHTMLElement(offsetParent)) {
|
162 |
+
offsets = getBoundingClientRect(offsetParent);
|
163 |
+
offsets.x += offsetParent.clientLeft;
|
164 |
+
offsets.y += offsetParent.clientTop;
|
165 |
+
} else if (documentElement) {
|
166 |
+
offsets.x = getWindowScrollBarX(documentElement);
|
167 |
+
}
|
168 |
+
}
|
169 |
+
|
170 |
+
return {
|
171 |
+
x: rect.left + scroll.scrollLeft - offsets.x,
|
172 |
+
y: rect.top + scroll.scrollTop - offsets.y,
|
173 |
+
width: rect.width,
|
174 |
+
height: rect.height
|
175 |
+
};
|
176 |
+
}
|
177 |
+
|
178 |
+
// Returns the layout rect of an element relative to its offsetParent. Layout
|
179 |
+
// means it doesn't take into account transforms.
|
180 |
+
function getLayoutRect(element)
|
181 |
+
{
|
182 |
+
return {
|
183 |
+
x: element.offsetLeft,
|
184 |
+
y: element.offsetTop,
|
185 |
+
width: element.offsetWidth,
|
186 |
+
height: element.offsetHeight
|
187 |
+
};
|
188 |
+
}
|
189 |
+
|
190 |
+
function getParentNode(element)
|
191 |
+
{
|
192 |
+
if (getNodeName(element) === 'html') {
|
193 |
+
return element;
|
194 |
+
}
|
195 |
+
|
196 |
+
return ( // this is a quicker (but less type safe) way to save quite some bytes from the bundle
|
197 |
+
// $FlowFixMe[incompatible-return]
|
198 |
+
// $FlowFixMe[prop-missing]
|
199 |
+
element.assignedSlot || // step into the shadow DOM of the parent of a slotted node
|
200 |
+
element.parentNode || // DOM Element detected
|
201 |
+
// $FlowFixMe[incompatible-return]: need a better way to handle this...
|
202 |
+
element.host || // ShadowRoot detected
|
203 |
+
// $FlowFixMe[incompatible-call]: HTMLElement is a Node
|
204 |
+
getDocumentElement(element) // fallback
|
205 |
+
|
206 |
+
);
|
207 |
+
}
|
208 |
+
|
209 |
+
function getScrollParent(node)
|
210 |
+
{
|
211 |
+
if (['html', 'body', '#document'].indexOf(getNodeName(node)) >= 0) {
|
212 |
+
// $FlowFixMe[incompatible-return]: assume body is always available
|
213 |
+
return node.ownerDocument.body;
|
214 |
+
}
|
215 |
+
|
216 |
+
if (isHTMLElement(node) && isScrollParent(node)) {
|
217 |
+
return node;
|
218 |
+
}
|
219 |
+
|
220 |
+
return getScrollParent(getParentNode(node));
|
221 |
+
}
|
222 |
+
|
223 |
+
/*
|
224 |
+
given a DOM element, return the list of all scroll parents, up the list of ancesors
|
225 |
+
until we get to the top window object. This list is what we attach scroll listeners
|
226 |
+
to, because if any of these parent elements scroll, we'll need to re-calculate the
|
227 |
+
reference element's position.
|
228 |
+
*/
|
229 |
+
|
230 |
+
function listScrollParents(element, list)
|
231 |
+
{
|
232 |
+
if (list === void 0) {
|
233 |
+
list = [];
|
234 |
+
}
|
235 |
+
|
236 |
+
var scrollParent = getScrollParent(element);
|
237 |
+
var isBody = getNodeName(scrollParent) === 'body';
|
238 |
+
var win = getWindow(scrollParent);
|
239 |
+
var target = isBody ? [win].concat(win.visualViewport || [], isScrollParent(scrollParent) ? scrollParent : []) : scrollParent;
|
240 |
+
var updatedList = list.concat(target);
|
241 |
+
return isBody ? updatedList : // $FlowFixMe[incompatible-call]: isBody tells us target will be an HTMLElement here
|
242 |
+
updatedList.concat(listScrollParents(getParentNode(target)));
|
243 |
+
}
|
244 |
+
|
245 |
+
function isTableElement(element)
|
246 |
+
{
|
247 |
+
return ['table', 'td', 'th'].indexOf(getNodeName(element)) >= 0;
|
248 |
+
}
|
249 |
+
|
250 |
+
function getTrueOffsetParent(element)
|
251 |
+
{
|
252 |
+
if (!isHTMLElement(element) || // https://github.com/popperjs/popper-core/issues/837
|
253 |
+
getComputedStyle(element).position === 'fixed') {
|
254 |
+
return null;
|
255 |
+
}
|
256 |
+
|
257 |
+
var offsetParent = element.offsetParent;
|
258 |
+
|
259 |
+
if (offsetParent) {
|
260 |
+
var html = getDocumentElement(offsetParent);
|
261 |
+
|
262 |
+
if (getNodeName(offsetParent) === 'body' && getComputedStyle(offsetParent).position === 'static' && getComputedStyle(html).position !== 'static') {
|
263 |
+
return html;
|
264 |
+
}
|
265 |
+
}
|
266 |
+
|
267 |
+
return offsetParent;
|
268 |
+
} // `.offsetParent` reports `null` for fixed elements, while absolute elements
|
269 |
+
// return the containing block
|
270 |
+
|
271 |
+
|
272 |
+
function getContainingBlock(element)
|
273 |
+
{
|
274 |
+
var currentNode = getParentNode(element);
|
275 |
+
|
276 |
+
while (isHTMLElement(currentNode) && ['html', 'body'].indexOf(getNodeName(currentNode)) < 0) {
|
277 |
+
var css = getComputedStyle(currentNode); // This is non-exhaustive but covers the most common CSS properties that
|
278 |
+
// create a containing block.
|
279 |
+
|
280 |
+
if (css.transform !== 'none' || css.perspective !== 'none' || css.willChange && css.willChange !== 'auto') {
|
281 |
+
return currentNode;
|
282 |
+
} else {
|
283 |
+
currentNode = currentNode.parentNode;
|
284 |
+
}
|
285 |
+
}
|
286 |
+
|
287 |
+
return null;
|
288 |
+
} // Gets the closest ancestor positioned element. Handles some edge cases,
|
289 |
+
// such as table ancestors and cross browser bugs.
|
290 |
+
|
291 |
+
|
292 |
+
function getOffsetParent(element)
|
293 |
+
{
|
294 |
+
var window = getWindow(element);
|
295 |
+
var offsetParent = getTrueOffsetParent(element);
|
296 |
+
|
297 |
+
while (offsetParent && isTableElement(offsetParent) && getComputedStyle(offsetParent).position === 'static') {
|
298 |
+
offsetParent = getTrueOffsetParent(offsetParent);
|
299 |
+
}
|
300 |
+
|
301 |
+
if (offsetParent && getNodeName(offsetParent) === 'body' && getComputedStyle(offsetParent).position === 'static') {
|
302 |
+
return window;
|
303 |
+
}
|
304 |
+
|
305 |
+
return offsetParent || getContainingBlock(element) || window;
|
306 |
+
}
|
307 |
+
|
308 |
+
var top = 'top';
|
309 |
+
var bottom = 'bottom';
|
310 |
+
var right = 'right';
|
311 |
+
var left = 'left';
|
312 |
+
var auto = 'auto';
|
313 |
+
var basePlacements = [top, bottom, right, left];
|
314 |
+
var start = 'start';
|
315 |
+
var end = 'end';
|
316 |
+
var clippingParents = 'clippingParents';
|
317 |
+
var viewport = 'viewport';
|
318 |
+
var popper = 'popper';
|
319 |
+
var reference = 'reference';
|
320 |
+
var variationPlacements = /*#__PURE__*/ basePlacements.reduce(function (acc, placement) {
|
321 |
+
return acc.concat([placement + "-" + start, placement + "-" + end]);
|
322 |
+
}, []);
|
323 |
+
var placements = /*#__PURE__*/ [].concat(basePlacements, [auto]).reduce(function (acc, placement) {
|
324 |
+
return acc.concat([placement, placement + "-" + start, placement + "-" + end]);
|
325 |
+
}, []); // modifiers that need to read the DOM
|
326 |
+
|
327 |
+
var beforeRead = 'beforeRead';
|
328 |
+
var read = 'read';
|
329 |
+
var afterRead = 'afterRead'; // pure-logic modifiers
|
330 |
+
|
331 |
+
var beforeMain = 'beforeMain';
|
332 |
+
var main = 'main';
|
333 |
+
var afterMain = 'afterMain'; // modifier with the purpose to write to the DOM (or write into a framework state)
|
334 |
+
|
335 |
+
var beforeWrite = 'beforeWrite';
|
336 |
+
var write = 'write';
|
337 |
+
var afterWrite = 'afterWrite';
|
338 |
+
var modifierPhases = [beforeRead, read, afterRead, beforeMain, main, afterMain, beforeWrite, write, afterWrite];
|
339 |
+
|
340 |
+
function order(modifiers)
|
341 |
+
{
|
342 |
+
var map = new Map();
|
343 |
+
var visited = new Set();
|
344 |
+
var result = [];
|
345 |
+
modifiers.forEach(function (modifier) {
|
346 |
+
map.set(modifier.name, modifier);
|
347 |
+
}); // On visiting object, check for its dependencies and visit them recursively
|
348 |
+
|
349 |
+
function sort(modifier)
|
350 |
+
{
|
351 |
+
visited.add(modifier.name);
|
352 |
+
var requires = [].concat(modifier.requires || [], modifier.requiresIfExists || []);
|
353 |
+
requires.forEach(function (dep) {
|
354 |
+
if (!visited.has(dep)) {
|
355 |
+
var depModifier = map.get(dep);
|
356 |
+
|
357 |
+
if (depModifier) {
|
358 |
+
sort(depModifier);
|
359 |
+
}
|
360 |
+
}
|
361 |
+
});
|
362 |
+
result.push(modifier);
|
363 |
+
}
|
364 |
+
|
365 |
+
modifiers.forEach(function (modifier) {
|
366 |
+
if (!visited.has(modifier.name)) {
|
367 |
+
// check for visited object
|
368 |
+
sort(modifier);
|
369 |
+
}
|
370 |
+
});
|
371 |
+
return result;
|
372 |
+
}
|
373 |
+
|
374 |
+
function orderModifiers(modifiers)
|
375 |
+
{
|
376 |
+
// order based on dependencies
|
377 |
+
var orderedModifiers = order(modifiers); // order based on phase
|
378 |
+
|
379 |
+
return modifierPhases.reduce(function (acc, phase) {
|
380 |
+
return acc.concat(orderedModifiers.filter(function (modifier) {
|
381 |
+
return modifier.phase === phase;
|
382 |
+
}));
|
383 |
+
}, []);
|
384 |
+
}
|
385 |
+
|
386 |
+
function debounce(fn)
|
387 |
+
{
|
388 |
+
var pending;
|
389 |
+
return function () {
|
390 |
+
if (!pending) {
|
391 |
+
pending = new Promise(function (resolve) {
|
392 |
+
Promise.resolve().then(function () {
|
393 |
+
pending = undefined;
|
394 |
+
resolve(fn());
|
395 |
+
});
|
396 |
+
});
|
397 |
+
}
|
398 |
+
|
399 |
+
return pending;
|
400 |
+
};
|
401 |
+
}
|
402 |
+
|
403 |
+
function format(str)
|
404 |
+
{
|
405 |
+
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
406 |
+
args[_key - 1] = arguments[_key];
|
407 |
+
}
|
408 |
+
|
409 |
+
return [].concat(args).reduce(function (p, c) {
|
410 |
+
return p.replace(/%s/, c);
|
411 |
+
}, str);
|
412 |
+
}
|
413 |
+
|
414 |
+
var INVALID_MODIFIER_ERROR = 'Popper: modifier "%s" provided an invalid %s property, expected %s but got %s';
|
415 |
+
var MISSING_DEPENDENCY_ERROR = 'Popper: modifier "%s" requires "%s", but "%s" modifier is not available';
|
416 |
+
var VALID_PROPERTIES = ['name', 'enabled', 'phase', 'fn', 'effect', 'requires', 'options'];
|
417 |
+
|
418 |
+
function validateModifiers(modifiers)
|
419 |
+
{
|
420 |
+
modifiers.forEach(function (modifier) {
|
421 |
+
Object.keys(modifier).forEach(function (key) {
|
422 |
+
switch (key) {
|
423 |
+
case 'name':
|
424 |
+
if (typeof modifier.name !== 'string') {
|
425 |
+
console.error(format(INVALID_MODIFIER_ERROR, String(modifier.name), '"name"', '"string"', "\"" + String(modifier.name) + "\""));
|
426 |
+
}
|
427 |
+
|
428 |
+
break;
|
429 |
+
|
430 |
+
case 'enabled':
|
431 |
+
if (typeof modifier.enabled !== 'boolean') {
|
432 |
+
console.error(format(INVALID_MODIFIER_ERROR, modifier.name, '"enabled"', '"boolean"', "\"" + String(modifier.enabled) + "\""));
|
433 |
+
}
|
434 |
+
|
435 |
+
case 'phase':
|
436 |
+
if (modifierPhases.indexOf(modifier.phase) < 0) {
|
437 |
+
console.error(format(INVALID_MODIFIER_ERROR, modifier.name, '"phase"', "either " + modifierPhases.join(', '), "\"" + String(modifier.phase) + "\""));
|
438 |
+
}
|
439 |
+
|
440 |
+
break;
|
441 |
+
|
442 |
+
case 'fn':
|
443 |
+
if (typeof modifier.fn !== 'function') {
|
444 |
+
console.error(format(INVALID_MODIFIER_ERROR, modifier.name, '"fn"', '"function"', "\"" + String(modifier.fn) + "\""));
|
445 |
+
}
|
446 |
+
|
447 |
+
break;
|
448 |
+
|
449 |
+
case 'effect':
|
450 |
+
if (typeof modifier.effect !== 'function') {
|
451 |
+
console.error(format(INVALID_MODIFIER_ERROR, modifier.name, '"effect"', '"function"', "\"" + String(modifier.fn) + "\""));
|
452 |
+
}
|
453 |
+
|
454 |
+
break;
|
455 |
+
|
456 |
+
case 'requires':
|
457 |
+
if (!Array.isArray(modifier.requires)) {
|
458 |
+
console.error(format(INVALID_MODIFIER_ERROR, modifier.name, '"requires"', '"array"', "\"" + String(modifier.requires) + "\""));
|
459 |
+
}
|
460 |
+
|
461 |
+
break;
|
462 |
+
|
463 |
+
case 'requiresIfExists':
|
464 |
+
if (!Array.isArray(modifier.requiresIfExists)) {
|
465 |
+
console.error(format(INVALID_MODIFIER_ERROR, modifier.name, '"requiresIfExists"', '"array"', "\"" + String(modifier.requiresIfExists) + "\""));
|
466 |
+
}
|
467 |
+
|
468 |
+
break;
|
469 |
+
|
470 |
+
case 'options':
|
471 |
+
case 'data':
|
472 |
+
break;
|
473 |
+
|
474 |
+
default:
|
475 |
+
console.error("PopperJS: an invalid property has been provided to the \"" + modifier.name + "\" modifier, valid properties are " + VALID_PROPERTIES.map(function (s) {
|
476 |
+
return "\"" + s + "\"";
|
477 |
+
}).join(', ') + "; but \"" + key + "\" was provided.");
|
478 |
+
}
|
479 |
+
|
480 |
+
modifier.requires && modifier.requires.forEach(function (requirement) {
|
481 |
+
if (modifiers.find(function (mod) {
|
482 |
+
return mod.name === requirement;
|
483 |
+
}) == null) {
|
484 |
+
console.error(format(MISSING_DEPENDENCY_ERROR, String(modifier.name), requirement, requirement));
|
485 |
+
}
|
486 |
+
});
|
487 |
+
});
|
488 |
+
});
|
489 |
+
}
|
490 |
+
|
491 |
+
function uniqueBy(arr, fn)
|
492 |
+
{
|
493 |
+
var identifiers = new Set();
|
494 |
+
return arr.filter(function (item) {
|
495 |
+
var identifier = fn(item);
|
496 |
+
|
497 |
+
if (!identifiers.has(identifier)) {
|
498 |
+
identifiers.add(identifier);
|
499 |
+
return true;
|
500 |
+
}
|
501 |
+
});
|
502 |
+
}
|
503 |
+
|
504 |
+
function getBasePlacement(placement)
|
505 |
+
{
|
506 |
+
return placement.split('-')[0];
|
507 |
+
}
|
508 |
+
|
509 |
+
function mergeByName(modifiers)
|
510 |
+
{
|
511 |
+
var merged = modifiers.reduce(function (merged, current) {
|
512 |
+
var existing = merged[current.name];
|
513 |
+
merged[current.name] = existing ? Object.assign(Object.assign(Object.assign({}, existing), current), {}, {
|
514 |
+
options: Object.assign(Object.assign({}, existing.options), current.options),
|
515 |
+
data: Object.assign(Object.assign({}, existing.data), current.data)
|
516 |
+
}) : current;
|
517 |
+
return merged;
|
518 |
+
}, {}); // IE11 does not support Object.values
|
519 |
+
|
520 |
+
return Object.keys(merged).map(function (key) {
|
521 |
+
return merged[key];
|
522 |
+
});
|
523 |
+
}
|
524 |
+
|
525 |
+
function getViewportRect(element)
|
526 |
+
{
|
527 |
+
var win = getWindow(element);
|
528 |
+
var html = getDocumentElement(element);
|
529 |
+
var visualViewport = win.visualViewport;
|
530 |
+
var width = html.clientWidth;
|
531 |
+
var height = html.clientHeight;
|
532 |
+
var x = 0;
|
533 |
+
var y = 0; // NB: This isn't supported on iOS <= 12. If the keyboard is open, the popper
|
534 |
+
// can be obscured underneath it.
|
535 |
+
// Also, `html.clientHeight` adds the bottom bar height in Safari iOS, even
|
536 |
+
// if it isn't open, so if this isn't available, the popper will be detected
|
537 |
+
// to overflow the bottom of the screen too early.
|
538 |
+
|
539 |
+
if (visualViewport) {
|
540 |
+
width = visualViewport.width;
|
541 |
+
height = visualViewport.height; // Uses Layout Viewport (like Chrome; Safari does not currently)
|
542 |
+
// In Chrome, it returns a value very close to 0 (+/-) but contains rounding
|
543 |
+
// errors due to floating point numbers, so we need to check precision.
|
544 |
+
// Safari returns a number <= 0, usually < -1 when pinch-zoomed
|
545 |
+
// Feature detection fails in mobile emulation mode in Chrome.
|
546 |
+
// Math.abs(win.innerWidth / visualViewport.scale - visualViewport.width) <
|
547 |
+
// 0.001
|
548 |
+
// Fallback here: "Not Safari" userAgent
|
549 |
+
|
550 |
+
if (!/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {
|
551 |
+
x = visualViewport.offsetLeft;
|
552 |
+
y = visualViewport.offsetTop;
|
553 |
+
}
|
554 |
+
}
|
555 |
+
|
556 |
+
return {
|
557 |
+
width: width,
|
558 |
+
height: height,
|
559 |
+
x: x + getWindowScrollBarX(element),
|
560 |
+
y: y
|
561 |
+
};
|
562 |
+
}
|
563 |
+
|
564 |
+
// of the `<html>` and `<body>` rect bounds if horizontally scrollable
|
565 |
+
|
566 |
+
function getDocumentRect(element)
|
567 |
+
{
|
568 |
+
var html = getDocumentElement(element);
|
569 |
+
var winScroll = getWindowScroll(element);
|
570 |
+
var body = element.ownerDocument.body;
|
571 |
+
var width = Math.max(html.scrollWidth, html.clientWidth, body ? body.scrollWidth : 0, body ? body.clientWidth : 0);
|
572 |
+
var height = Math.max(html.scrollHeight, html.clientHeight, body ? body.scrollHeight : 0, body ? body.clientHeight : 0);
|
573 |
+
var x = -winScroll.scrollLeft + getWindowScrollBarX(element);
|
574 |
+
var y = -winScroll.scrollTop;
|
575 |
+
|
576 |
+
if (getComputedStyle(body || html).direction === 'rtl') {
|
577 |
+
x += Math.max(html.clientWidth, body ? body.clientWidth : 0) - width;
|
578 |
+
}
|
579 |
+
|
580 |
+
return {
|
581 |
+
width: width,
|
582 |
+
height: height,
|
583 |
+
x: x,
|
584 |
+
y: y
|
585 |
+
};
|
586 |
+
}
|
587 |
+
|
588 |
+
function contains(parent, child)
|
589 |
+
{
|
590 |
+
var rootNode = child.getRootNode && child.getRootNode(); // First, attempt with faster native method
|
591 |
+
// then fallback to custom implementation with Shadow DOM support
|
592 |
+
if (parent.contains(child)) {
|
593 |
+
return true;
|
594 |
+
} else if (rootNode && isShadowRoot(rootNode)) {
|
595 |
+
var next = child;
|
596 |
+
|
597 |
+
do {
|
598 |
+
if (next && parent.isSameNode(next)) {
|
599 |
+
return true;
|
600 |
+
} // $FlowFixMe[prop-missing]: need a better way to handle this...
|
601 |
+
|
602 |
+
|
603 |
+
next = next.parentNode || next.host;
|
604 |
+
} while (next);
|
605 |
+
} // Give up, the result is false
|
606 |
+
|
607 |
+
|
608 |
+
return false;
|
609 |
+
}
|
610 |
+
|
611 |
+
function rectToClientRect(rect)
|
612 |
+
{
|
613 |
+
return Object.assign(Object.assign({}, rect), {}, {
|
614 |
+
left: rect.x,
|
615 |
+
top: rect.y,
|
616 |
+
right: rect.x + rect.width,
|
617 |
+
bottom: rect.y + rect.height
|
618 |
+
});
|
619 |
+
}
|
620 |
+
|
621 |
+
function getInnerBoundingClientRect(element)
|
622 |
+
{
|
623 |
+
var rect = getBoundingClientRect(element);
|
624 |
+
rect.top = rect.top + element.clientTop;
|
625 |
+
rect.left = rect.left + element.clientLeft;
|
626 |
+
rect.bottom = rect.top + element.clientHeight;
|
627 |
+
rect.right = rect.left + element.clientWidth;
|
628 |
+
rect.width = element.clientWidth;
|
629 |
+
rect.height = element.clientHeight;
|
630 |
+
rect.x = rect.left;
|
631 |
+
rect.y = rect.top;
|
632 |
+
return rect;
|
633 |
+
}
|
634 |
+
|
635 |
+
function getClientRectFromMixedType(element, clippingParent)
|
636 |
+
{
|
637 |
+
return clippingParent === viewport ? rectToClientRect(getViewportRect(element)) : isHTMLElement(clippingParent) ? getInnerBoundingClientRect(clippingParent) : rectToClientRect(getDocumentRect(getDocumentElement(element)));
|
638 |
+
} // A "clipping parent" is an overflowable container with the characteristic of
|
639 |
+
// clipping (or hiding) overflowing elements with a position different from
|
640 |
+
// `initial`
|
641 |
+
|
642 |
+
|
643 |
+
function getClippingParents(element)
|
644 |
+
{
|
645 |
+
var clippingParents = listScrollParents(getParentNode(element));
|
646 |
+
var canEscapeClipping = ['absolute', 'fixed'].indexOf(getComputedStyle(element).position) >= 0;
|
647 |
+
var clipperElement = canEscapeClipping && isHTMLElement(element) ? getOffsetParent(element) : element;
|
648 |
+
|
649 |
+
if (!isElement(clipperElement)) {
|
650 |
+
return [];
|
651 |
+
} // $FlowFixMe[incompatible-return]: https://github.com/facebook/flow/issues/1414
|
652 |
+
|
653 |
+
|
654 |
+
return clippingParents.filter(function (clippingParent) {
|
655 |
+
return isElement(clippingParent) && contains(clippingParent, clipperElement) && getNodeName(clippingParent) !== 'body';
|
656 |
+
});
|
657 |
+
} // Gets the maximum area that the element is visible in due to any number of
|
658 |
+
// clipping parents
|
659 |
+
|
660 |
+
|
661 |
+
function getClippingRect(element, boundary, rootBoundary)
|
662 |
+
{
|
663 |
+
var mainClippingParents = boundary === 'clippingParents' ? getClippingParents(element) : [].concat(boundary);
|
664 |
+
var clippingParents = [].concat(mainClippingParents, [rootBoundary]);
|
665 |
+
var firstClippingParent = clippingParents[0];
|
666 |
+
var clippingRect = clippingParents.reduce(function (accRect, clippingParent) {
|
667 |
+
var rect = getClientRectFromMixedType(element, clippingParent);
|
668 |
+
accRect.top = Math.max(rect.top, accRect.top);
|
669 |
+
accRect.right = Math.min(rect.right, accRect.right);
|
670 |
+
accRect.bottom = Math.min(rect.bottom, accRect.bottom);
|
671 |
+
accRect.left = Math.max(rect.left, accRect.left);
|
672 |
+
return accRect;
|
673 |
+
}, getClientRectFromMixedType(element, firstClippingParent));
|
674 |
+
clippingRect.width = clippingRect.right - clippingRect.left;
|
675 |
+
clippingRect.height = clippingRect.bottom - clippingRect.top;
|
676 |
+
clippingRect.x = clippingRect.left;
|
677 |
+
clippingRect.y = clippingRect.top;
|
678 |
+
return clippingRect;
|
679 |
+
}
|
680 |
+
|
681 |
+
function getVariation(placement)
|
682 |
+
{
|
683 |
+
return placement.split('-')[1];
|
684 |
+
}
|
685 |
+
|
686 |
+
function getMainAxisFromPlacement(placement)
|
687 |
+
{
|
688 |
+
return ['top', 'bottom'].indexOf(placement) >= 0 ? 'x' : 'y';
|
689 |
+
}
|
690 |
+
|
691 |
+
function computeOffsets(_ref)
|
692 |
+
{
|
693 |
+
var reference = _ref.reference,
|
694 |
+
element = _ref.element,
|
695 |
+
placement = _ref.placement;
|
696 |
+
var basePlacement = placement ? getBasePlacement(placement) : null;
|
697 |
+
var variation = placement ? getVariation(placement) : null;
|
698 |
+
var commonX = reference.x + reference.width / 2 - element.width / 2;
|
699 |
+
var commonY = reference.y + reference.height / 2 - element.height / 2;
|
700 |
+
var offsets;
|
701 |
+
|
702 |
+
switch (basePlacement) {
|
703 |
+
case top:
|
704 |
+
offsets = {
|
705 |
+
x: commonX,
|
706 |
+
y: reference.y - element.height
|
707 |
+
};
|
708 |
+
break;
|
709 |
+
|
710 |
+
case bottom:
|
711 |
+
offsets = {
|
712 |
+
x: commonX,
|
713 |
+
y: reference.y + reference.height
|
714 |
+
};
|
715 |
+
break;
|
716 |
+
|
717 |
+
case right:
|
718 |
+
offsets = {
|
719 |
+
x: reference.x + reference.width,
|
720 |
+
y: commonY
|
721 |
+
};
|
722 |
+
break;
|
723 |
+
|
724 |
+
case left:
|
725 |
+
offsets = {
|
726 |
+
x: reference.x - element.width,
|
727 |
+
y: commonY
|
728 |
+
};
|
729 |
+
break;
|
730 |
+
|
731 |
+
default:
|
732 |
+
offsets = {
|
733 |
+
x: reference.x,
|
734 |
+
y: reference.y
|
735 |
+
};
|
736 |
+
}
|
737 |
+
|
738 |
+
var mainAxis = basePlacement ? getMainAxisFromPlacement(basePlacement) : null;
|
739 |
+
|
740 |
+
if (mainAxis != null) {
|
741 |
+
var len = mainAxis === 'y' ? 'height' : 'width';
|
742 |
+
|
743 |
+
switch (variation) {
|
744 |
+
case start:
|
745 |
+
offsets[mainAxis] = offsets[mainAxis] - (reference[len] / 2 - element[len] / 2);
|
746 |
+
break;
|
747 |
+
|
748 |
+
case end:
|
749 |
+
offsets[mainAxis] = offsets[mainAxis] + (reference[len] / 2 - element[len] / 2);
|
750 |
+
break;
|
751 |
+
}
|
752 |
+
}
|
753 |
+
|
754 |
+
return offsets;
|
755 |
+
}
|
756 |
+
|
757 |
+
function getFreshSideObject()
|
758 |
+
{
|
759 |
+
return {
|
760 |
+
top: 0,
|
761 |
+
right: 0,
|
762 |
+
bottom: 0,
|
763 |
+
left: 0
|
764 |
+
};
|
765 |
+
}
|
766 |
+
|
767 |
+
function mergePaddingObject(paddingObject)
|
768 |
+
{
|
769 |
+
return Object.assign(Object.assign({}, getFreshSideObject()), paddingObject);
|
770 |
+
}
|
771 |
+
|
772 |
+
function expandToHashMap(value, keys)
|
773 |
+
{
|
774 |
+
return keys.reduce(function (hashMap, key) {
|
775 |
+
hashMap[key] = value;
|
776 |
+
return hashMap;
|
777 |
+
}, {});
|
778 |
+
}
|
779 |
+
|
780 |
+
function detectOverflow(state, options)
|
781 |
+
{
|
782 |
+
if (options === void 0) {
|
783 |
+
options = {};
|
784 |
+
}
|
785 |
+
|
786 |
+
var _options = options,
|
787 |
+
_options$placement = _options.placement,
|
788 |
+
placement = _options$placement === void 0 ? state.placement : _options$placement,
|
789 |
+
_options$boundary = _options.boundary,
|
790 |
+
boundary = _options$boundary === void 0 ? clippingParents : _options$boundary,
|
791 |
+
_options$rootBoundary = _options.rootBoundary,
|
792 |
+
rootBoundary = _options$rootBoundary === void 0 ? viewport : _options$rootBoundary,
|
793 |
+
_options$elementConte = _options.elementContext,
|
794 |
+
elementContext = _options$elementConte === void 0 ? popper : _options$elementConte,
|
795 |
+
_options$altBoundary = _options.altBoundary,
|
796 |
+
altBoundary = _options$altBoundary === void 0 ? false : _options$altBoundary,
|
797 |
+
_options$padding = _options.padding,
|
798 |
+
padding = _options$padding === void 0 ? 0 : _options$padding;
|
799 |
+
var paddingObject = mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements));
|
800 |
+
var altContext = elementContext === popper ? reference : popper;
|
801 |
+
var referenceElement = state.elements.reference;
|
802 |
+
var popperRect = state.rects.popper;
|
803 |
+
var element = state.elements[altBoundary ? altContext : elementContext];
|
804 |
+
var clippingClientRect = getClippingRect(isElement(element) ? element : element.contextElement || getDocumentElement(state.elements.popper), boundary, rootBoundary);
|
805 |
+
var referenceClientRect = getBoundingClientRect(referenceElement);
|
806 |
+
var popperOffsets = computeOffsets({
|
807 |
+
reference: referenceClientRect,
|
808 |
+
element: popperRect,
|
809 |
+
strategy: 'absolute',
|
810 |
+
placement: placement
|
811 |
+
});
|
812 |
+
var popperClientRect = rectToClientRect(Object.assign(Object.assign({}, popperRect), popperOffsets));
|
813 |
+
var elementClientRect = elementContext === popper ? popperClientRect : referenceClientRect; // positive = overflowing the clipping rect
|
814 |
+
// 0 or negative = within the clipping rect
|
815 |
+
|
816 |
+
var overflowOffsets = {
|
817 |
+
top: clippingClientRect.top - elementClientRect.top + paddingObject.top,
|
818 |
+
bottom: elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom,
|
819 |
+
left: clippingClientRect.left - elementClientRect.left + paddingObject.left,
|
820 |
+
right: elementClientRect.right - clippingClientRect.right + paddingObject.right
|
821 |
+
};
|
822 |
+
var offsetData = state.modifiersData.offset; // Offsets can be applied only to the popper element
|
823 |
+
|
824 |
+
if (elementContext === popper && offsetData) {
|
825 |
+
var offset = offsetData[placement];
|
826 |
+
Object.keys(overflowOffsets).forEach(function (key) {
|
827 |
+
var multiply = [right, bottom].indexOf(key) >= 0 ? 1 : -1;
|
828 |
+
var axis = [top, bottom].indexOf(key) >= 0 ? 'y' : 'x';
|
829 |
+
overflowOffsets[key] += offset[axis] * multiply;
|
830 |
+
});
|
831 |
+
}
|
832 |
+
|
833 |
+
return overflowOffsets;
|
834 |
+
}
|
835 |
+
|
836 |
+
var INVALID_ELEMENT_ERROR = 'Popper: Invalid reference or popper argument provided. They must be either a DOM element or virtual element.';
|
837 |
+
var INFINITE_LOOP_ERROR = 'Popper: An infinite loop in the modifiers cycle has been detected! The cycle has been interrupted to prevent a browser crash.';
|
838 |
+
var DEFAULT_OPTIONS = {
|
839 |
+
placement: 'bottom',
|
840 |
+
modifiers: [],
|
841 |
+
strategy: 'absolute'
|
842 |
+
};
|
843 |
+
|
844 |
+
function areValidElements()
|
845 |
+
{
|
846 |
+
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
847 |
+
args[_key] = arguments[_key];
|
848 |
+
}
|
849 |
+
|
850 |
+
return !args.some(function (element) {
|
851 |
+
return !(element && typeof element.getBoundingClientRect === 'function');
|
852 |
+
});
|
853 |
+
}
|
854 |
+
|
855 |
+
function popperGenerator(generatorOptions)
|
856 |
+
{
|
857 |
+
if (generatorOptions === void 0) {
|
858 |
+
generatorOptions = {};
|
859 |
+
}
|
860 |
+
|
861 |
+
var _generatorOptions = generatorOptions,
|
862 |
+
_generatorOptions$def = _generatorOptions.defaultModifiers,
|
863 |
+
defaultModifiers = _generatorOptions$def === void 0 ? [] : _generatorOptions$def,
|
864 |
+
_generatorOptions$def2 = _generatorOptions.defaultOptions,
|
865 |
+
defaultOptions = _generatorOptions$def2 === void 0 ? DEFAULT_OPTIONS : _generatorOptions$def2;
|
866 |
+
return function createPopper(reference, popper, options)
|
867 |
+
{
|
868 |
+
if (options === void 0) {
|
869 |
+
options = defaultOptions;
|
870 |
+
}
|
871 |
+
|
872 |
+
var state = {
|
873 |
+
placement: 'bottom',
|
874 |
+
orderedModifiers: [],
|
875 |
+
options: Object.assign(Object.assign({}, DEFAULT_OPTIONS), defaultOptions),
|
876 |
+
modifiersData: {},
|
877 |
+
elements: {
|
878 |
+
reference: reference,
|
879 |
+
popper: popper
|
880 |
+
},
|
881 |
+
attributes: {},
|
882 |
+
styles: {}
|
883 |
+
};
|
884 |
+
var effectCleanupFns = [];
|
885 |
+
var isDestroyed = false;
|
886 |
+
var instance = {
|
887 |
+
state: state,
|
888 |
+
setOptions: function setOptions(options)
|
889 |
+
{
|
890 |
+
cleanupModifierEffects();
|
891 |
+
state.options = Object.assign(Object.assign(Object.assign({}, defaultOptions), state.options), options);
|
892 |
+
state.scrollParents = {
|
893 |
+
reference: isElement(reference) ? listScrollParents(reference) : reference.contextElement ? listScrollParents(reference.contextElement) : [],
|
894 |
+
popper: listScrollParents(popper)
|
895 |
+
}; // Orders the modifiers based on their dependencies and `phase`
|
896 |
+
// properties
|
897 |
+
|
898 |
+
var orderedModifiers = orderModifiers(mergeByName([].concat(defaultModifiers, state.options.modifiers))); // Strip out disabled modifiers
|
899 |
+
|
900 |
+
state.orderedModifiers = orderedModifiers.filter(function (m) {
|
901 |
+
return m.enabled;
|
902 |
+
}); // Validate the provided modifiers so that the consumer will get warned
|
903 |
+
// if one of the modifiers is invalid for any reason
|
904 |
+
|
905 |
+
{
|
906 |
+
var modifiers = uniqueBy([].concat(orderedModifiers, state.options.modifiers), function (_ref) {
|
907 |
+
var name = _ref.name;
|
908 |
+
return name;
|
909 |
+
});
|
910 |
+
validateModifiers(modifiers);
|
911 |
+
|
912 |
+
if (getBasePlacement(state.options.placement) === auto) {
|
913 |
+
var flipModifier = state.orderedModifiers.find(function (_ref2) {
|
914 |
+
var name = _ref2.name;
|
915 |
+
return name === 'flip';
|
916 |
+
});
|
917 |
+
|
918 |
+
if (!flipModifier) {
|
919 |
+
console.error(['Popper: "auto" placements require the "flip" modifier be', 'present and enabled to work.'].join(' '));
|
920 |
+
}
|
921 |
+
}
|
922 |
+
|
923 |
+
var _getComputedStyle = getComputedStyle(popper),
|
924 |
+
marginTop = _getComputedStyle.marginTop,
|
925 |
+
marginRight = _getComputedStyle.marginRight,
|
926 |
+
marginBottom = _getComputedStyle.marginBottom,
|
927 |
+
marginLeft = _getComputedStyle.marginLeft; // We no longer take into account `margins` on the popper, and it can
|
928 |
+
// cause bugs with positioning, so we'll warn the consumer
|
929 |
+
|
930 |
+
|
931 |
+
if ([marginTop, marginRight, marginBottom, marginLeft].some(function (margin) {
|
932 |
+
return parseFloat(margin);
|
933 |
+
})) {
|
934 |
+
console.warn(['Popper: CSS "margin" styles cannot be used to apply padding', 'between the popper and its reference element or boundary.', 'To replicate margin, use the `offset` modifier, as well as', 'the `padding` option in the `preventOverflow` and `flip`', 'modifiers.'].join(' '));
|
935 |
+
}
|
936 |
+
}
|
937 |
+
|
938 |
+
runModifierEffects();
|
939 |
+
return instance.update();
|
940 |
+
},
|
941 |
+
// Sync update – it will always be executed, even if not necessary. This
|
942 |
+
// is useful for low frequency updates where sync behavior simplifies the
|
943 |
+
// logic.
|
944 |
+
// For high frequency updates (e.g. `resize` and `scroll` events), always
|
945 |
+
// prefer the async Popper#update method
|
946 |
+
forceUpdate: function forceUpdate()
|
947 |
+
{
|
948 |
+
if (isDestroyed) {
|
949 |
+
return;
|
950 |
+
}
|
951 |
+
|
952 |
+
var _state$elements = state.elements,
|
953 |
+
reference = _state$elements.reference,
|
954 |
+
popper = _state$elements.popper; // Don't proceed if `reference` or `popper` are not valid elements
|
955 |
+
// anymore
|
956 |
+
|
957 |
+
if (!areValidElements(reference, popper)) {
|
958 |
+
{
|
959 |
+
console.error(INVALID_ELEMENT_ERROR);
|
960 |
+
}
|
961 |
+
|
962 |
+
return;
|
963 |
+
} // Store the reference and popper rects to be read by modifiers
|
964 |
+
|
965 |
+
|
966 |
+
state.rects = {
|
967 |
+
reference: getCompositeRect(reference, getOffsetParent(popper), state.options.strategy === 'fixed'),
|
968 |
+
popper: getLayoutRect(popper)
|
969 |
+
}; // Modifiers have the ability to reset the current update cycle. The
|
970 |
+
// most common use case for this is the `flip` modifier changing the
|
971 |
+
// placement, which then needs to re-run all the modifiers, because the
|
972 |
+
// logic was previously ran for the previous placement and is therefore
|
973 |
+
// stale/incorrect
|
974 |
+
|
975 |
+
state.reset = false;
|
976 |
+
state.placement = state.options.placement; // On each update cycle, the `modifiersData` property for each modifier
|
977 |
+
// is filled with the initial data specified by the modifier. This means
|
978 |
+
// it doesn't persist and is fresh on each update.
|
979 |
+
// To ensure persistent data, use `${name}#persistent`
|
980 |
+
|
981 |
+
state.orderedModifiers.forEach(function (modifier) {
|
982 |
+
return state.modifiersData[modifier.name] = Object.assign({}, modifier.data);
|
983 |
+
});
|
984 |
+
var __debug_loops__ = 0;
|
985 |
+
|
986 |
+
for (var index = 0; index < state.orderedModifiers.length; index++) {
|
987 |
+
{
|
988 |
+
__debug_loops__ += 1;
|
989 |
+
|
990 |
+
if (__debug_loops__ > 100) {
|
991 |
+
console.error(INFINITE_LOOP_ERROR);
|
992 |
+
break;
|
993 |
+
}
|
994 |
+
}
|
995 |
+
|
996 |
+
if (state.reset === true) {
|
997 |
+
state.reset = false;
|
998 |
+
index = -1;
|
999 |
+
continue;
|
1000 |
+
}
|
1001 |
+
|
1002 |
+
var _state$orderedModifie = state.orderedModifiers[index],
|
1003 |
+
fn = _state$orderedModifie.fn,
|
1004 |
+
_state$orderedModifie2 = _state$orderedModifie.options,
|
1005 |
+
_options = _state$orderedModifie2 === void 0 ? {} : _state$orderedModifie2,
|
1006 |
+
name = _state$orderedModifie.name;
|
1007 |
+
|
1008 |
+
if (typeof fn === 'function') {
|
1009 |
+
state = fn({
|
1010 |
+
state: state,
|
1011 |
+
options: _options,
|
1012 |
+
name: name,
|
1013 |
+
instance: instance
|
1014 |
+
}) || state;
|
1015 |
+
}
|
1016 |
+
}
|
1017 |
+
},
|
1018 |
+
// Async and optimistically optimized update – it will not be executed if
|
1019 |
+
// not necessary (debounced to run at most once-per-tick)
|
1020 |
+
update: debounce(function () {
|
1021 |
+
return new Promise(function (resolve) {
|
1022 |
+
instance.forceUpdate();
|
1023 |
+
resolve(state);
|
1024 |
+
});
|
1025 |
+
}),
|
1026 |
+
destroy: function destroy()
|
1027 |
+
{
|
1028 |
+
cleanupModifierEffects();
|
1029 |
+
isDestroyed = true;
|
1030 |
+
}
|
1031 |
+
};
|
1032 |
+
|
1033 |
+
if (!areValidElements(reference, popper)) {
|
1034 |
+
{
|
1035 |
+
console.error(INVALID_ELEMENT_ERROR);
|
1036 |
+
}
|
1037 |
+
|
1038 |
+
return instance;
|
1039 |
+
}
|
1040 |
+
|
1041 |
+
instance.setOptions(options).then(function (state) {
|
1042 |
+
if (!isDestroyed && options.onFirstUpdate) {
|
1043 |
+
options.onFirstUpdate(state);
|
1044 |
+
}
|
1045 |
+
}); // Modifiers have the ability to execute arbitrary code before the first
|
1046 |
+
// update cycle runs. They will be executed in the same order as the update
|
1047 |
+
// cycle. This is useful when a modifier adds some persistent data that
|
1048 |
+
// other modifiers need to use, but the modifier is run after the dependent
|
1049 |
+
// one.
|
1050 |
+
|
1051 |
+
function runModifierEffects()
|
1052 |
+
{
|
1053 |
+
state.orderedModifiers.forEach(function (_ref3) {
|
1054 |
+
var name = _ref3.name,
|
1055 |
+
_ref3$options = _ref3.options,
|
1056 |
+
options = _ref3$options === void 0 ? {} : _ref3$options,
|
1057 |
+
effect = _ref3.effect;
|
1058 |
+
|
1059 |
+
if (typeof effect === 'function') {
|
1060 |
+
var cleanupFn = effect({
|
1061 |
+
state: state,
|
1062 |
+
name: name,
|
1063 |
+
instance: instance,
|
1064 |
+
options: options
|
1065 |
+
});
|
1066 |
+
|
1067 |
+
var noopFn = function noopFn()
|
1068 |
+
{};
|
1069 |
+
|
1070 |
+
effectCleanupFns.push(cleanupFn || noopFn);
|
1071 |
+
}
|
1072 |
+
});
|
1073 |
+
}
|
1074 |
+
|
1075 |
+
function cleanupModifierEffects()
|
1076 |
+
{
|
1077 |
+
effectCleanupFns.forEach(function (fn) {
|
1078 |
+
return fn();
|
1079 |
+
});
|
1080 |
+
effectCleanupFns = [];
|
1081 |
+
}
|
1082 |
+
|
1083 |
+
return instance;
|
1084 |
+
};
|
1085 |
+
}
|
1086 |
+
|
1087 |
+
var passive = {
|
1088 |
+
passive: true
|
1089 |
+
};
|
1090 |
+
|
1091 |
+
function effect(_ref)
|
1092 |
+
{
|
1093 |
+
var state = _ref.state,
|
1094 |
+
instance = _ref.instance,
|
1095 |
+
options = _ref.options;
|
1096 |
+
var _options$scroll = options.scroll,
|
1097 |
+
scroll = _options$scroll === void 0 ? true : _options$scroll,
|
1098 |
+
_options$resize = options.resize,
|
1099 |
+
resize = _options$resize === void 0 ? true : _options$resize;
|
1100 |
+
var window = getWindow(state.elements.popper);
|
1101 |
+
var scrollParents = [].concat(state.scrollParents.reference, state.scrollParents.popper);
|
1102 |
+
|
1103 |
+
if (scroll) {
|
1104 |
+
scrollParents.forEach(function (scrollParent) {
|
1105 |
+
scrollParent.addEventListener('scroll', instance.update, passive);
|
1106 |
+
});
|
1107 |
+
}
|
1108 |
+
|
1109 |
+
if (resize) {
|
1110 |
+
window.addEventListener('resize', instance.update, passive);
|
1111 |
+
}
|
1112 |
+
|
1113 |
+
return function () {
|
1114 |
+
if (scroll) {
|
1115 |
+
scrollParents.forEach(function (scrollParent) {
|
1116 |
+
scrollParent.removeEventListener('scroll', instance.update, passive);
|
1117 |
+
});
|
1118 |
+
}
|
1119 |
+
|
1120 |
+
if (resize) {
|
1121 |
+
window.removeEventListener('resize', instance.update, passive);
|
1122 |
+
}
|
1123 |
+
};
|
1124 |
+
} // eslint-disable-next-line import/no-unused-modules
|
1125 |
+
|
1126 |
+
|
1127 |
+
var eventListeners = {
|
1128 |
+
name: 'eventListeners',
|
1129 |
+
enabled: true,
|
1130 |
+
phase: 'write',
|
1131 |
+
fn: function fn()
|
1132 |
+
{},
|
1133 |
+
effect: effect,
|
1134 |
+
data: {}
|
1135 |
+
};
|
1136 |
+
|
1137 |
+
function popperOffsets(_ref)
|
1138 |
+
{
|
1139 |
+
var state = _ref.state,
|
1140 |
+
name = _ref.name;
|
1141 |
+
// Offsets are the actual position the popper needs to have to be
|
1142 |
+
// properly positioned near its reference element
|
1143 |
+
// This is the most basic placement, and will be adjusted by
|
1144 |
+
// the modifiers in the next step
|
1145 |
+
state.modifiersData[name] = computeOffsets({
|
1146 |
+
reference: state.rects.reference,
|
1147 |
+
element: state.rects.popper,
|
1148 |
+
strategy: 'absolute',
|
1149 |
+
placement: state.placement
|
1150 |
+
});
|
1151 |
+
} // eslint-disable-next-line import/no-unused-modules
|
1152 |
+
|
1153 |
+
|
1154 |
+
var popperOffsets$1 = {
|
1155 |
+
name: 'popperOffsets',
|
1156 |
+
enabled: true,
|
1157 |
+
phase: 'read',
|
1158 |
+
fn: popperOffsets,
|
1159 |
+
data: {}
|
1160 |
+
};
|
1161 |
+
|
1162 |
+
var unsetSides = {
|
1163 |
+
top: 'auto',
|
1164 |
+
right: 'auto',
|
1165 |
+
bottom: 'auto',
|
1166 |
+
left: 'auto'
|
1167 |
+
}; // Round the offsets to the nearest suitable subpixel based on the DPR.
|
1168 |
+
// Zooming can change the DPR, but it seems to report a value that will
|
1169 |
+
// cleanly divide the values into the appropriate subpixels.
|
1170 |
+
|
1171 |
+
function roundOffsetsByDPR(_ref)
|
1172 |
+
{
|
1173 |
+
var x = _ref.x,
|
1174 |
+
y = _ref.y;
|
1175 |
+
var win = window;
|
1176 |
+
var dpr = win.devicePixelRatio || 1;
|
1177 |
+
return {
|
1178 |
+
x: Math.round(x * dpr) / dpr || 0,
|
1179 |
+
y: Math.round(y * dpr) / dpr || 0
|
1180 |
+
};
|
1181 |
+
}
|
1182 |
+
|
1183 |
+
function mapToStyles(_ref2)
|
1184 |
+
{
|
1185 |
+
var _Object$assign2;
|
1186 |
+
|
1187 |
+
var popper = _ref2.popper,
|
1188 |
+
popperRect = _ref2.popperRect,
|
1189 |
+
placement = _ref2.placement,
|
1190 |
+
offsets = _ref2.offsets,
|
1191 |
+
position = _ref2.position,
|
1192 |
+
gpuAcceleration = _ref2.gpuAcceleration,
|
1193 |
+
adaptive = _ref2.adaptive,
|
1194 |
+
roundOffsets = _ref2.roundOffsets;
|
1195 |
+
|
1196 |
+
var _ref3 = roundOffsets ? roundOffsetsByDPR(offsets) : offsets,
|
1197 |
+
_ref3$x = _ref3.x,
|
1198 |
+
x = _ref3$x === void 0 ? 0 : _ref3$x,
|
1199 |
+
_ref3$y = _ref3.y,
|
1200 |
+
y = _ref3$y === void 0 ? 0 : _ref3$y;
|
1201 |
+
|
1202 |
+
var hasX = offsets.hasOwnProperty('x');
|
1203 |
+
var hasY = offsets.hasOwnProperty('y');
|
1204 |
+
var sideX = left;
|
1205 |
+
var sideY = top;
|
1206 |
+
var win = window;
|
1207 |
+
|
1208 |
+
if (adaptive) {
|
1209 |
+
var offsetParent = getOffsetParent(popper);
|
1210 |
+
|
1211 |
+
if (offsetParent === getWindow(popper)) {
|
1212 |
+
offsetParent = getDocumentElement(popper);
|
1213 |
+
} // $FlowFixMe[incompatible-cast]: force type refinement, we compare offsetParent with window above, but Flow doesn't detect it
|
1214 |
+
|
1215 |
+
/*:: offsetParent = (offsetParent: Element); */
|
1216 |
+
|
1217 |
+
|
1218 |
+
if (placement === top) {
|
1219 |
+
sideY = bottom;
|
1220 |
+
y -= offsetParent.clientHeight - popperRect.height;
|
1221 |
+
y *= gpuAcceleration ? 1 : -1;
|
1222 |
+
}
|
1223 |
+
|
1224 |
+
if (placement === left) {
|
1225 |
+
sideX = right;
|
1226 |
+
x -= offsetParent.clientWidth - popperRect.width;
|
1227 |
+
x *= gpuAcceleration ? 1 : -1;
|
1228 |
+
}
|
1229 |
+
}
|
1230 |
+
|
1231 |
+
var commonStyles = Object.assign({
|
1232 |
+
position: position
|
1233 |
+
}, adaptive && unsetSides);
|
1234 |
+
|
1235 |
+
if (gpuAcceleration) {
|
1236 |
+
var _Object$assign;
|
1237 |
+
|
1238 |
+
return Object.assign(Object.assign({}, commonStyles), {}, (_Object$assign = {}, _Object$assign[sideY] = hasY ? '0' : '', _Object$assign[sideX] = hasX ? '0' : '', _Object$assign.transform = (win.devicePixelRatio || 1) < 2 ? "translate(" + x + "px, " + y + "px)" : "translate3d(" + x + "px, " + y + "px, 0)", _Object$assign));
|
1239 |
+
}
|
1240 |
+
|
1241 |
+
return Object.assign(Object.assign({}, commonStyles), {}, (_Object$assign2 = {}, _Object$assign2[sideY] = hasY ? y + "px" : '', _Object$assign2[sideX] = hasX ? x + "px" : '', _Object$assign2.transform = '', _Object$assign2));
|
1242 |
+
}
|
1243 |
+
|
1244 |
+
function computeStyles(_ref4)
|
1245 |
+
{
|
1246 |
+
var state = _ref4.state,
|
1247 |
+
options = _ref4.options;
|
1248 |
+
var _options$gpuAccelerat = options.gpuAcceleration,
|
1249 |
+
gpuAcceleration = _options$gpuAccelerat === void 0 ? true : _options$gpuAccelerat,
|
1250 |
+
_options$adaptive = options.adaptive,
|
1251 |
+
adaptive = _options$adaptive === void 0 ? true : _options$adaptive,
|
1252 |
+
_options$roundOffsets = options.roundOffsets,
|
1253 |
+
roundOffsets = _options$roundOffsets === void 0 ? true : _options$roundOffsets;
|
1254 |
+
|
1255 |
+
{
|
1256 |
+
var transitionProperty = getComputedStyle(state.elements.popper).transitionProperty || '';
|
1257 |
+
|
1258 |
+
if (adaptive && ['transform', 'top', 'right', 'bottom', 'left'].some(function (property) {
|
1259 |
+
return transitionProperty.indexOf(property) >= 0;
|
1260 |
+
})) {
|
1261 |
+
console.warn(['Popper: Detected CSS transitions on at least one of the following', 'CSS properties: "transform", "top", "right", "bottom", "left".', '\n\n', 'Disable the "computeStyles" modifier\'s `adaptive` option to allow', 'for smooth transitions, or remove these properties from the CSS', 'transition declaration on the popper element if only transitioning', 'opacity or background-color for example.', '\n\n', 'We recommend using the popper element as a wrapper around an inner', 'element that can have any CSS property transitioned for animations.'].join(' '));
|
1262 |
+
}
|
1263 |
+
}
|
1264 |
+
|
1265 |
+
var commonStyles = {
|
1266 |
+
placement: getBasePlacement(state.placement),
|
1267 |
+
popper: state.elements.popper,
|
1268 |
+
popperRect: state.rects.popper,
|
1269 |
+
gpuAcceleration: gpuAcceleration
|
1270 |
+
};
|
1271 |
+
|
1272 |
+
if (state.modifiersData.popperOffsets != null) {
|
1273 |
+
state.styles.popper = Object.assign(Object.assign({}, state.styles.popper), mapToStyles(Object.assign(Object.assign({}, commonStyles), {}, {
|
1274 |
+
offsets: state.modifiersData.popperOffsets,
|
1275 |
+
position: state.options.strategy,
|
1276 |
+
adaptive: adaptive,
|
1277 |
+
roundOffsets: roundOffsets
|
1278 |
+
})));
|
1279 |
+
}
|
1280 |
+
|
1281 |
+
if (state.modifiersData.arrow != null) {
|
1282 |
+
state.styles.arrow = Object.assign(Object.assign({}, state.styles.arrow), mapToStyles(Object.assign(Object.assign({}, commonStyles), {}, {
|
1283 |
+
offsets: state.modifiersData.arrow,
|
1284 |
+
position: 'absolute',
|
1285 |
+
adaptive: false,
|
1286 |
+
roundOffsets: roundOffsets
|
1287 |
+
})));
|
1288 |
+
}
|
1289 |
+
|
1290 |
+
state.attributes.popper = Object.assign(Object.assign({}, state.attributes.popper), {}, {
|
1291 |
+
'data-popper-placement': state.placement
|
1292 |
+
});
|
1293 |
+
} // eslint-disable-next-line import/no-unused-modules
|
1294 |
+
|
1295 |
+
|
1296 |
+
var computeStyles$1 = {
|
1297 |
+
name: 'computeStyles',
|
1298 |
+
enabled: true,
|
1299 |
+
phase: 'beforeWrite',
|
1300 |
+
fn: computeStyles,
|
1301 |
+
data: {}
|
1302 |
+
};
|
1303 |
+
|
1304 |
+
// and applies them to the HTMLElements such as popper and arrow
|
1305 |
+
|
1306 |
+
function applyStyles(_ref)
|
1307 |
+
{
|
1308 |
+
var state = _ref.state;
|
1309 |
+
Object.keys(state.elements).forEach(function (name) {
|
1310 |
+
var style = state.styles[name] || {};
|
1311 |
+
var attributes = state.attributes[name] || {};
|
1312 |
+
var element = state.elements[name]; // arrow is optional + virtual elements
|
1313 |
+
|
1314 |
+
if (!isHTMLElement(element) || !getNodeName(element)) {
|
1315 |
+
return;
|
1316 |
+
} // Flow doesn't support to extend this property, but it's the most
|
1317 |
+
// effective way to apply styles to an HTMLElement
|
1318 |
+
// $FlowFixMe[cannot-write]
|
1319 |
+
|
1320 |
+
|
1321 |
+
Object.assign(element.style, style);
|
1322 |
+
Object.keys(attributes).forEach(function (name) {
|
1323 |
+
var value = attributes[name];
|
1324 |
+
|
1325 |
+
if (value === false) {
|
1326 |
+
element.removeAttribute(name);
|
1327 |
+
} else {
|
1328 |
+
element.setAttribute(name, value === true ? '' : value);
|
1329 |
+
}
|
1330 |
+
});
|
1331 |
+
});
|
1332 |
+
}
|
1333 |
+
|
1334 |
+
function effect$1(_ref2)
|
1335 |
+
{
|
1336 |
+
var state = _ref2.state;
|
1337 |
+
var initialStyles = {
|
1338 |
+
popper: {
|
1339 |
+
position: state.options.strategy,
|
1340 |
+
left: '0',
|
1341 |
+
top: '0',
|
1342 |
+
margin: '0'
|
1343 |
+
},
|
1344 |
+
arrow: {
|
1345 |
+
position: 'absolute'
|
1346 |
+
},
|
1347 |
+
reference: {}
|
1348 |
+
};
|
1349 |
+
Object.assign(state.elements.popper.style, initialStyles.popper);
|
1350 |
+
|
1351 |
+
if (state.elements.arrow) {
|
1352 |
+
Object.assign(state.elements.arrow.style, initialStyles.arrow);
|
1353 |
+
}
|
1354 |
+
|
1355 |
+
return function () {
|
1356 |
+
Object.keys(state.elements).forEach(function (name) {
|
1357 |
+
var element = state.elements[name];
|
1358 |
+
var attributes = state.attributes[name] || {};
|
1359 |
+
var styleProperties = Object.keys(state.styles.hasOwnProperty(name) ? state.styles[name] : initialStyles[name]); // Set all values to an empty string to unset them
|
1360 |
+
|
1361 |
+
var style = styleProperties.reduce(function (style, property) {
|
1362 |
+
style[property] = '';
|
1363 |
+
return style;
|
1364 |
+
}, {}); // arrow is optional + virtual elements
|
1365 |
+
|
1366 |
+
if (!isHTMLElement(element) || !getNodeName(element)) {
|
1367 |
+
return;
|
1368 |
+
}
|
1369 |
+
|
1370 |
+
Object.assign(element.style, style);
|
1371 |
+
Object.keys(attributes).forEach(function (attribute) {
|
1372 |
+
element.removeAttribute(attribute);
|
1373 |
+
});
|
1374 |
+
});
|
1375 |
+
};
|
1376 |
+
} // eslint-disable-next-line import/no-unused-modules
|
1377 |
+
|
1378 |
+
|
1379 |
+
var applyStyles$1 = {
|
1380 |
+
name: 'applyStyles',
|
1381 |
+
enabled: true,
|
1382 |
+
phase: 'write',
|
1383 |
+
fn: applyStyles,
|
1384 |
+
effect: effect$1,
|
1385 |
+
requires: ['computeStyles']
|
1386 |
+
};
|
1387 |
+
|
1388 |
+
function distanceAndSkiddingToXY(placement, rects, offset)
|
1389 |
+
{
|
1390 |
+
var basePlacement = getBasePlacement(placement);
|
1391 |
+
var invertDistance = [left, top].indexOf(basePlacement) >= 0 ? -1 : 1;
|
1392 |
+
|
1393 |
+
var _ref = typeof offset === 'function' ? offset(Object.assign(Object.assign({}, rects), {}, {
|
1394 |
+
placement: placement
|
1395 |
+
})) : offset,
|
1396 |
+
skidding = _ref[0],
|
1397 |
+
distance = _ref[1];
|
1398 |
+
|
1399 |
+
skidding = skidding || 0;
|
1400 |
+
distance = (distance || 0) * invertDistance;
|
1401 |
+
return [left, right].indexOf(basePlacement) >= 0 ? {
|
1402 |
+
x: distance,
|
1403 |
+
y: skidding
|
1404 |
+
} : {
|
1405 |
+
x: skidding,
|
1406 |
+
y: distance
|
1407 |
+
};
|
1408 |
+
}
|
1409 |
+
|
1410 |
+
function offset(_ref2)
|
1411 |
+
{
|
1412 |
+
var state = _ref2.state,
|
1413 |
+
options = _ref2.options,
|
1414 |
+
name = _ref2.name;
|
1415 |
+
var _options$offset = options.offset,
|
1416 |
+
offset = _options$offset === void 0 ? [0, 0] : _options$offset;
|
1417 |
+
var data = placements.reduce(function (acc, placement) {
|
1418 |
+
acc[placement] = distanceAndSkiddingToXY(placement, state.rects, offset);
|
1419 |
+
return acc;
|
1420 |
+
}, {});
|
1421 |
+
var _data$state$placement = data[state.placement],
|
1422 |
+
x = _data$state$placement.x,
|
1423 |
+
y = _data$state$placement.y;
|
1424 |
+
|
1425 |
+
if (state.modifiersData.popperOffsets != null) {
|
1426 |
+
state.modifiersData.popperOffsets.x += x;
|
1427 |
+
state.modifiersData.popperOffsets.y += y;
|
1428 |
+
}
|
1429 |
+
|
1430 |
+
state.modifiersData[name] = data;
|
1431 |
+
} // eslint-disable-next-line import/no-unused-modules
|
1432 |
+
|
1433 |
+
|
1434 |
+
var offset$1 = {
|
1435 |
+
name: 'offset',
|
1436 |
+
enabled: true,
|
1437 |
+
phase: 'main',
|
1438 |
+
requires: ['popperOffsets'],
|
1439 |
+
fn: offset
|
1440 |
+
};
|
1441 |
+
|
1442 |
+
var hash = {
|
1443 |
+
left: 'right',
|
1444 |
+
right: 'left',
|
1445 |
+
bottom: 'top',
|
1446 |
+
top: 'bottom'
|
1447 |
+
};
|
1448 |
+
|
1449 |
+
function getOppositePlacement(placement)
|
1450 |
+
{
|
1451 |
+
return placement.replace(/left|right|bottom|top/g, function (matched) {
|
1452 |
+
return hash[matched];
|
1453 |
+
});
|
1454 |
+
}
|
1455 |
+
|
1456 |
+
var hash$1 = {
|
1457 |
+
start: 'end',
|
1458 |
+
end: 'start'
|
1459 |
+
};
|
1460 |
+
|
1461 |
+
function getOppositeVariationPlacement(placement)
|
1462 |
+
{
|
1463 |
+
return placement.replace(/start|end/g, function (matched) {
|
1464 |
+
return hash$1[matched];
|
1465 |
+
});
|
1466 |
+
}
|
1467 |
+
|
1468 |
+
/*:: type OverflowsMap = { [ComputedPlacement]: number }; */
|
1469 |
+
|
1470 |
+
/*;; type OverflowsMap = { [key in ComputedPlacement]: number }; */
|
1471 |
+
function computeAutoPlacement(state, options)
|
1472 |
+
{
|
1473 |
+
if (options === void 0) {
|
1474 |
+
options = {};
|
1475 |
+
}
|
1476 |
+
|
1477 |
+
var _options = options,
|
1478 |
+
placement = _options.placement,
|
1479 |
+
boundary = _options.boundary,
|
1480 |
+
rootBoundary = _options.rootBoundary,
|
1481 |
+
padding = _options.padding,
|
1482 |
+
flipVariations = _options.flipVariations,
|
1483 |
+
_options$allowedAutoP = _options.allowedAutoPlacements,
|
1484 |
+
allowedAutoPlacements = _options$allowedAutoP === void 0 ? placements : _options$allowedAutoP;
|
1485 |
+
var variation = getVariation(placement);
|
1486 |
+
var placements$1 = variation ? flipVariations ? variationPlacements : variationPlacements.filter(function (placement) {
|
1487 |
+
return getVariation(placement) === variation;
|
1488 |
+
}) : basePlacements;
|
1489 |
+
var allowedPlacements = placements$1.filter(function (placement) {
|
1490 |
+
return allowedAutoPlacements.indexOf(placement) >= 0;
|
1491 |
+
});
|
1492 |
+
|
1493 |
+
if (allowedPlacements.length === 0) {
|
1494 |
+
allowedPlacements = placements$1;
|
1495 |
+
|
1496 |
+
{
|
1497 |
+
console.error(['Popper: The `allowedAutoPlacements` option did not allow any', 'placements. Ensure the `placement` option matches the variation', 'of the allowed placements.', 'For example, "auto" cannot be used to allow "bottom-start".', 'Use "auto-start" instead.'].join(' '));
|
1498 |
+
}
|
1499 |
+
} // $FlowFixMe[incompatible-type]: Flow seems to have problems with two array unions...
|
1500 |
+
|
1501 |
+
|
1502 |
+
var overflows = allowedPlacements.reduce(function (acc, placement) {
|
1503 |
+
acc[placement] = detectOverflow(state, {
|
1504 |
+
placement: placement,
|
1505 |
+
boundary: boundary,
|
1506 |
+
rootBoundary: rootBoundary,
|
1507 |
+
padding: padding
|
1508 |
+
})[getBasePlacement(placement)];
|
1509 |
+
return acc;
|
1510 |
+
}, {});
|
1511 |
+
return Object.keys(overflows).sort(function (a, b) {
|
1512 |
+
return overflows[a] - overflows[b];
|
1513 |
+
});
|
1514 |
+
}
|
1515 |
+
|
1516 |
+
function getExpandedFallbackPlacements(placement)
|
1517 |
+
{
|
1518 |
+
if (getBasePlacement(placement) === auto) {
|
1519 |
+
return [];
|
1520 |
+
}
|
1521 |
+
|
1522 |
+
var oppositePlacement = getOppositePlacement(placement);
|
1523 |
+
return [getOppositeVariationPlacement(placement), oppositePlacement, getOppositeVariationPlacement(oppositePlacement)];
|
1524 |
+
}
|
1525 |
+
|
1526 |
+
function flip(_ref)
|
1527 |
+
{
|
1528 |
+
var state = _ref.state,
|
1529 |
+
options = _ref.options,
|
1530 |
+
name = _ref.name;
|
1531 |
+
|
1532 |
+
if (state.modifiersData[name]._skip) {
|
1533 |
+
return;
|
1534 |
+
}
|
1535 |
+
|
1536 |
+
var _options$mainAxis = options.mainAxis,
|
1537 |
+
checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis,
|
1538 |
+
_options$altAxis = options.altAxis,
|
1539 |
+
checkAltAxis = _options$altAxis === void 0 ? true : _options$altAxis,
|
1540 |
+
specifiedFallbackPlacements = options.fallbackPlacements,
|
1541 |
+
padding = options.padding,
|
1542 |
+
boundary = options.boundary,
|
1543 |
+
rootBoundary = options.rootBoundary,
|
1544 |
+
altBoundary = options.altBoundary,
|
1545 |
+
_options$flipVariatio = options.flipVariations,
|
1546 |
+
flipVariations = _options$flipVariatio === void 0 ? true : _options$flipVariatio,
|
1547 |
+
allowedAutoPlacements = options.allowedAutoPlacements;
|
1548 |
+
var preferredPlacement = state.options.placement;
|
1549 |
+
var basePlacement = getBasePlacement(preferredPlacement);
|
1550 |
+
var isBasePlacement = basePlacement === preferredPlacement;
|
1551 |
+
var fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipVariations ? [getOppositePlacement(preferredPlacement)] : getExpandedFallbackPlacements(preferredPlacement));
|
1552 |
+
var placements = [preferredPlacement].concat(fallbackPlacements).reduce(function (acc, placement) {
|
1553 |
+
return acc.concat(getBasePlacement(placement) === auto ? computeAutoPlacement(state, {
|
1554 |
+
placement: placement,
|
1555 |
+
boundary: boundary,
|
1556 |
+
rootBoundary: rootBoundary,
|
1557 |
+
padding: padding,
|
1558 |
+
flipVariations: flipVariations,
|
1559 |
+
allowedAutoPlacements: allowedAutoPlacements
|
1560 |
+
}) : placement);
|
1561 |
+
}, []);
|
1562 |
+
var referenceRect = state.rects.reference;
|
1563 |
+
var popperRect = state.rects.popper;
|
1564 |
+
var checksMap = new Map();
|
1565 |
+
var makeFallbackChecks = true;
|
1566 |
+
var firstFittingPlacement = placements[0];
|
1567 |
+
|
1568 |
+
for (var i = 0; i < placements.length; i++) {
|
1569 |
+
var placement = placements[i];
|
1570 |
+
|
1571 |
+
var _basePlacement = getBasePlacement(placement);
|
1572 |
+
|
1573 |
+
var isStartVariation = getVariation(placement) === start;
|
1574 |
+
var isVertical = [top, bottom].indexOf(_basePlacement) >= 0;
|
1575 |
+
var len = isVertical ? 'width' : 'height';
|
1576 |
+
var overflow = detectOverflow(state, {
|
1577 |
+
placement: placement,
|
1578 |
+
boundary: boundary,
|
1579 |
+
rootBoundary: rootBoundary,
|
1580 |
+
altBoundary: altBoundary,
|
1581 |
+
padding: padding
|
1582 |
+
});
|
1583 |
+
var mainVariationSide = isVertical ? isStartVariation ? right : left : isStartVariation ? bottom : top;
|
1584 |
+
|
1585 |
+
if (referenceRect[len] > popperRect[len]) {
|
1586 |
+
mainVariationSide = getOppositePlacement(mainVariationSide);
|
1587 |
+
}
|
1588 |
+
|
1589 |
+
var altVariationSide = getOppositePlacement(mainVariationSide);
|
1590 |
+
var checks = [];
|
1591 |
+
|
1592 |
+
if (checkMainAxis) {
|
1593 |
+
checks.push(overflow[_basePlacement] <= 0);
|
1594 |
+
}
|
1595 |
+
|
1596 |
+
if (checkAltAxis) {
|
1597 |
+
checks.push(overflow[mainVariationSide] <= 0, overflow[altVariationSide] <= 0);
|
1598 |
+
}
|
1599 |
+
|
1600 |
+
if (checks.every(function (check) {
|
1601 |
+
return check;
|
1602 |
+
})) {
|
1603 |
+
firstFittingPlacement = placement;
|
1604 |
+
makeFallbackChecks = false;
|
1605 |
+
break;
|
1606 |
+
}
|
1607 |
+
|
1608 |
+
checksMap.set(placement, checks);
|
1609 |
+
}
|
1610 |
+
|
1611 |
+
if (makeFallbackChecks) {
|
1612 |
+
// `2` may be desired in some cases – research later
|
1613 |
+
var numberOfChecks = flipVariations ? 3 : 1;
|
1614 |
+
|
1615 |
+
var _loop = function _loop(_i)
|
1616 |
+
{
|
1617 |
+
var fittingPlacement = placements.find(function (placement) {
|
1618 |
+
var checks = checksMap.get(placement);
|
1619 |
+
|
1620 |
+
if (checks) {
|
1621 |
+
return checks.slice(0, _i).every(function (check) {
|
1622 |
+
return check;
|
1623 |
+
});
|
1624 |
+
}
|
1625 |
+
});
|
1626 |
+
|
1627 |
+
if (fittingPlacement) {
|
1628 |
+
firstFittingPlacement = fittingPlacement;
|
1629 |
+
return "break";
|
1630 |
+
}
|
1631 |
+
};
|
1632 |
+
|
1633 |
+
for (var _i = numberOfChecks; _i > 0; _i--) {
|
1634 |
+
var _ret = _loop(_i);
|
1635 |
+
|
1636 |
+
if (_ret === "break") {
|
1637 |
+
break;
|
1638 |
+
}
|
1639 |
+
}
|
1640 |
+
}
|
1641 |
+
|
1642 |
+
if (state.placement !== firstFittingPlacement) {
|
1643 |
+
state.modifiersData[name]._skip = true;
|
1644 |
+
state.placement = firstFittingPlacement;
|
1645 |
+
state.reset = true;
|
1646 |
+
}
|
1647 |
+
} // eslint-disable-next-line import/no-unused-modules
|
1648 |
+
|
1649 |
+
|
1650 |
+
var flip$1 = {
|
1651 |
+
name: 'flip',
|
1652 |
+
enabled: true,
|
1653 |
+
phase: 'main',
|
1654 |
+
fn: flip,
|
1655 |
+
requiresIfExists: ['offset'],
|
1656 |
+
data: {
|
1657 |
+
_skip: false
|
1658 |
+
}
|
1659 |
+
};
|
1660 |
+
|
1661 |
+
function getAltAxis(axis)
|
1662 |
+
{
|
1663 |
+
return axis === 'x' ? 'y' : 'x';
|
1664 |
+
}
|
1665 |
+
|
1666 |
+
function within(min, value, max)
|
1667 |
+
{
|
1668 |
+
return Math.max(min, Math.min(value, max));
|
1669 |
+
}
|
1670 |
+
|
1671 |
+
function preventOverflow(_ref)
|
1672 |
+
{
|
1673 |
+
var state = _ref.state,
|
1674 |
+
options = _ref.options,
|
1675 |
+
name = _ref.name;
|
1676 |
+
var _options$mainAxis = options.mainAxis,
|
1677 |
+
checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis,
|
1678 |
+
_options$altAxis = options.altAxis,
|
1679 |
+
checkAltAxis = _options$altAxis === void 0 ? false : _options$altAxis,
|
1680 |
+
boundary = options.boundary,
|
1681 |
+
rootBoundary = options.rootBoundary,
|
1682 |
+
altBoundary = options.altBoundary,
|
1683 |
+
padding = options.padding,
|
1684 |
+
_options$tether = options.tether,
|
1685 |
+
tether = _options$tether === void 0 ? true : _options$tether,
|
1686 |
+
_options$tetherOffset = options.tetherOffset,
|
1687 |
+
tetherOffset = _options$tetherOffset === void 0 ? 0 : _options$tetherOffset;
|
1688 |
+
var overflow = detectOverflow(state, {
|
1689 |
+
boundary: boundary,
|
1690 |
+
rootBoundary: rootBoundary,
|
1691 |
+
padding: padding,
|
1692 |
+
altBoundary: altBoundary
|
1693 |
+
});
|
1694 |
+
var basePlacement = getBasePlacement(state.placement);
|
1695 |
+
var variation = getVariation(state.placement);
|
1696 |
+
var isBasePlacement = !variation;
|
1697 |
+
var mainAxis = getMainAxisFromPlacement(basePlacement);
|
1698 |
+
var altAxis = getAltAxis(mainAxis);
|
1699 |
+
var popperOffsets = state.modifiersData.popperOffsets;
|
1700 |
+
var referenceRect = state.rects.reference;
|
1701 |
+
var popperRect = state.rects.popper;
|
1702 |
+
var tetherOffsetValue = typeof tetherOffset === 'function' ? tetherOffset(Object.assign(Object.assign({}, state.rects), {}, {
|
1703 |
+
placement: state.placement
|
1704 |
+
})) : tetherOffset;
|
1705 |
+
var data = {
|
1706 |
+
x: 0,
|
1707 |
+
y: 0
|
1708 |
+
};
|
1709 |
+
|
1710 |
+
if (!popperOffsets) {
|
1711 |
+
return;
|
1712 |
+
}
|
1713 |
+
|
1714 |
+
if (checkMainAxis) {
|
1715 |
+
var mainSide = mainAxis === 'y' ? top : left;
|
1716 |
+
var altSide = mainAxis === 'y' ? bottom : right;
|
1717 |
+
var len = mainAxis === 'y' ? 'height' : 'width';
|
1718 |
+
var offset = popperOffsets[mainAxis];
|
1719 |
+
var min = popperOffsets[mainAxis] + overflow[mainSide];
|
1720 |
+
var max = popperOffsets[mainAxis] - overflow[altSide];
|
1721 |
+
var additive = tether ? -popperRect[len] / 2 : 0;
|
1722 |
+
var minLen = variation === start ? referenceRect[len] : popperRect[len];
|
1723 |
+
var maxLen = variation === start ? -popperRect[len] : -referenceRect[len]; // We need to include the arrow in the calculation so the arrow doesn't go
|
1724 |
+
// outside the reference bounds
|
1725 |
+
|
1726 |
+
var arrowElement = state.elements.arrow;
|
1727 |
+
var arrowRect = tether && arrowElement ? getLayoutRect(arrowElement) : {
|
1728 |
+
width: 0,
|
1729 |
+
height: 0
|
1730 |
+
};
|
1731 |
+
var arrowPaddingObject = state.modifiersData['arrow#persistent'] ? state.modifiersData['arrow#persistent'].padding : getFreshSideObject();
|
1732 |
+
var arrowPaddingMin = arrowPaddingObject[mainSide];
|
1733 |
+
var arrowPaddingMax = arrowPaddingObject[altSide]; // If the reference length is smaller than the arrow length, we don't want
|
1734 |
+
// to include its full size in the calculation. If the reference is small
|
1735 |
+
// and near the edge of a boundary, the popper can overflow even if the
|
1736 |
+
// reference is not overflowing as well (e.g. virtual elements with no
|
1737 |
+
// width or height)
|
1738 |
+
|
1739 |
+
var arrowLen = within(0, referenceRect[len], arrowRect[len]);
|
1740 |
+
var minOffset = isBasePlacement ? referenceRect[len] / 2 - additive - arrowLen - arrowPaddingMin - tetherOffsetValue : minLen - arrowLen - arrowPaddingMin - tetherOffsetValue;
|
1741 |
+
var maxOffset = isBasePlacement ? -referenceRect[len] / 2 + additive + arrowLen + arrowPaddingMax + tetherOffsetValue : maxLen + arrowLen + arrowPaddingMax + tetherOffsetValue;
|
1742 |
+
var arrowOffsetParent = state.elements.arrow && getOffsetParent(state.elements.arrow);
|
1743 |
+
var clientOffset = arrowOffsetParent ? mainAxis === 'y' ? arrowOffsetParent.clientTop || 0 : arrowOffsetParent.clientLeft || 0 : 0;
|
1744 |
+
var offsetModifierValue = state.modifiersData.offset ? state.modifiersData.offset[state.placement][mainAxis] : 0;
|
1745 |
+
var tetherMin = popperOffsets[mainAxis] + minOffset - offsetModifierValue - clientOffset;
|
1746 |
+
var tetherMax = popperOffsets[mainAxis] + maxOffset - offsetModifierValue;
|
1747 |
+
var preventedOffset = within(tether ? Math.min(min, tetherMin) : min, offset, tether ? Math.max(max, tetherMax) : max);
|
1748 |
+
popperOffsets[mainAxis] = preventedOffset;
|
1749 |
+
data[mainAxis] = preventedOffset - offset;
|
1750 |
+
}
|
1751 |
+
|
1752 |
+
if (checkAltAxis) {
|
1753 |
+
var _mainSide = mainAxis === 'x' ? top : left;
|
1754 |
+
|
1755 |
+
var _altSide = mainAxis === 'x' ? bottom : right;
|
1756 |
+
|
1757 |
+
var _offset = popperOffsets[altAxis];
|
1758 |
+
|
1759 |
+
var _min = _offset + overflow[_mainSide];
|
1760 |
+
|
1761 |
+
var _max = _offset - overflow[_altSide];
|
1762 |
+
|
1763 |
+
var _preventedOffset = within(_min, _offset, _max);
|
1764 |
+
|
1765 |
+
popperOffsets[altAxis] = _preventedOffset;
|
1766 |
+
data[altAxis] = _preventedOffset - _offset;
|
1767 |
+
}
|
1768 |
+
|
1769 |
+
state.modifiersData[name] = data;
|
1770 |
+
} // eslint-disable-next-line import/no-unused-modules
|
1771 |
+
|
1772 |
+
|
1773 |
+
var preventOverflow$1 = {
|
1774 |
+
name: 'preventOverflow',
|
1775 |
+
enabled: true,
|
1776 |
+
phase: 'main',
|
1777 |
+
fn: preventOverflow,
|
1778 |
+
requiresIfExists: ['offset']
|
1779 |
+
};
|
1780 |
+
|
1781 |
+
function arrow(_ref)
|
1782 |
+
{
|
1783 |
+
var _state$modifiersData$;
|
1784 |
+
|
1785 |
+
var state = _ref.state,
|
1786 |
+
name = _ref.name;
|
1787 |
+
var arrowElement = state.elements.arrow;
|
1788 |
+
var popperOffsets = state.modifiersData.popperOffsets;
|
1789 |
+
var basePlacement = getBasePlacement(state.placement);
|
1790 |
+
var axis = getMainAxisFromPlacement(basePlacement);
|
1791 |
+
var isVertical = [left, right].indexOf(basePlacement) >= 0;
|
1792 |
+
var len = isVertical ? 'height' : 'width';
|
1793 |
+
|
1794 |
+
if (!arrowElement || !popperOffsets) {
|
1795 |
+
return;
|
1796 |
+
}
|
1797 |
+
|
1798 |
+
var paddingObject = state.modifiersData[name + "#persistent"].padding;
|
1799 |
+
var arrowRect = getLayoutRect(arrowElement);
|
1800 |
+
var minProp = axis === 'y' ? top : left;
|
1801 |
+
var maxProp = axis === 'y' ? bottom : right;
|
1802 |
+
var endDiff = state.rects.reference[len] + state.rects.reference[axis] - popperOffsets[axis] - state.rects.popper[len];
|
1803 |
+
var startDiff = popperOffsets[axis] - state.rects.reference[axis];
|
1804 |
+
var arrowOffsetParent = getOffsetParent(arrowElement);
|
1805 |
+
var clientSize = arrowOffsetParent ? axis === 'y' ? arrowOffsetParent.clientHeight || 0 : arrowOffsetParent.clientWidth || 0 : 0;
|
1806 |
+
var centerToReference = endDiff / 2 - startDiff / 2; // Make sure the arrow doesn't overflow the popper if the center point is
|
1807 |
+
// outside of the popper bounds
|
1808 |
+
|
1809 |
+
var min = paddingObject[minProp];
|
1810 |
+
var max = clientSize - arrowRect[len] - paddingObject[maxProp];
|
1811 |
+
var center = clientSize / 2 - arrowRect[len] / 2 + centerToReference;
|
1812 |
+
var offset = within(min, center, max); // Prevents breaking syntax highlighting...
|
1813 |
+
|
1814 |
+
var axisProp = axis;
|
1815 |
+
state.modifiersData[name] = (_state$modifiersData$ = {}, _state$modifiersData$[axisProp] = offset, _state$modifiersData$.centerOffset = offset - center, _state$modifiersData$);
|
1816 |
+
}
|
1817 |
+
|
1818 |
+
function effect$2(_ref2)
|
1819 |
+
{
|
1820 |
+
var state = _ref2.state,
|
1821 |
+
options = _ref2.options,
|
1822 |
+
name = _ref2.name;
|
1823 |
+
var _options$element = options.element,
|
1824 |
+
arrowElement = _options$element === void 0 ? '[data-popper-arrow]' : _options$element,
|
1825 |
+
_options$padding = options.padding,
|
1826 |
+
padding = _options$padding === void 0 ? 0 : _options$padding;
|
1827 |
+
|
1828 |
+
if (arrowElement == null) {
|
1829 |
+
return;
|
1830 |
+
} // CSS selector
|
1831 |
+
|
1832 |
+
|
1833 |
+
if (typeof arrowElement === 'string') {
|
1834 |
+
arrowElement = state.elements.popper.querySelector(arrowElement);
|
1835 |
+
|
1836 |
+
if (!arrowElement) {
|
1837 |
+
return;
|
1838 |
+
}
|
1839 |
+
}
|
1840 |
+
|
1841 |
+
{
|
1842 |
+
if (!isHTMLElement(arrowElement)) {
|
1843 |
+
console.error(['Popper: "arrow" element must be an HTMLElement (not an SVGElement).', 'To use an SVG arrow, wrap it in an HTMLElement that will be used as', 'the arrow.'].join(' '));
|
1844 |
+
}
|
1845 |
+
}
|
1846 |
+
|
1847 |
+
if (!contains(state.elements.popper, arrowElement)) {
|
1848 |
+
{
|
1849 |
+
console.error(['Popper: "arrow" modifier\'s `element` must be a child of the popper', 'element.'].join(' '));
|
1850 |
+
}
|
1851 |
+
|
1852 |
+
return;
|
1853 |
+
}
|
1854 |
+
|
1855 |
+
state.elements.arrow = arrowElement;
|
1856 |
+
state.modifiersData[name + "#persistent"] = {
|
1857 |
+
padding: mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements))
|
1858 |
+
};
|
1859 |
+
} // eslint-disable-next-line import/no-unused-modules
|
1860 |
+
|
1861 |
+
|
1862 |
+
var arrow$1 = {
|
1863 |
+
name: 'arrow',
|
1864 |
+
enabled: true,
|
1865 |
+
phase: 'main',
|
1866 |
+
fn: arrow,
|
1867 |
+
effect: effect$2,
|
1868 |
+
requires: ['popperOffsets'],
|
1869 |
+
requiresIfExists: ['preventOverflow']
|
1870 |
+
};
|
1871 |
+
|
1872 |
+
function getSideOffsets(overflow, rect, preventedOffsets)
|
1873 |
+
{
|
1874 |
+
if (preventedOffsets === void 0) {
|
1875 |
+
preventedOffsets = {
|
1876 |
+
x: 0,
|
1877 |
+
y: 0
|
1878 |
+
};
|
1879 |
+
}
|
1880 |
+
|
1881 |
+
return {
|
1882 |
+
top: overflow.top - rect.height - preventedOffsets.y,
|
1883 |
+
right: overflow.right - rect.width + preventedOffsets.x,
|
1884 |
+
bottom: overflow.bottom - rect.height + preventedOffsets.y,
|
1885 |
+
left: overflow.left - rect.width - preventedOffsets.x
|
1886 |
+
};
|
1887 |
+
}
|
1888 |
+
|
1889 |
+
function isAnySideFullyClipped(overflow)
|
1890 |
+
{
|
1891 |
+
return [top, right, bottom, left].some(function (side) {
|
1892 |
+
return overflow[side] >= 0;
|
1893 |
+
});
|
1894 |
+
}
|
1895 |
+
|
1896 |
+
function hide(_ref)
|
1897 |
+
{
|
1898 |
+
var state = _ref.state,
|
1899 |
+
name = _ref.name;
|
1900 |
+
var referenceRect = state.rects.reference;
|
1901 |
+
var popperRect = state.rects.popper;
|
1902 |
+
var preventedOffsets = state.modifiersData.preventOverflow;
|
1903 |
+
var referenceOverflow = detectOverflow(state, {
|
1904 |
+
elementContext: 'reference'
|
1905 |
+
});
|
1906 |
+
var popperAltOverflow = detectOverflow(state, {
|
1907 |
+
altBoundary: true
|
1908 |
+
});
|
1909 |
+
var referenceClippingOffsets = getSideOffsets(referenceOverflow, referenceRect);
|
1910 |
+
var popperEscapeOffsets = getSideOffsets(popperAltOverflow, popperRect, preventedOffsets);
|
1911 |
+
var isReferenceHidden = isAnySideFullyClipped(referenceClippingOffsets);
|
1912 |
+
var hasPopperEscaped = isAnySideFullyClipped(popperEscapeOffsets);
|
1913 |
+
state.modifiersData[name] = {
|
1914 |
+
referenceClippingOffsets: referenceClippingOffsets,
|
1915 |
+
popperEscapeOffsets: popperEscapeOffsets,
|
1916 |
+
isReferenceHidden: isReferenceHidden,
|
1917 |
+
hasPopperEscaped: hasPopperEscaped
|
1918 |
+
};
|
1919 |
+
state.attributes.popper = Object.assign(Object.assign({}, state.attributes.popper), {}, {
|
1920 |
+
'data-popper-reference-hidden': isReferenceHidden,
|
1921 |
+
'data-popper-escaped': hasPopperEscaped
|
1922 |
+
});
|
1923 |
+
} // eslint-disable-next-line import/no-unused-modules
|
1924 |
+
|
1925 |
+
|
1926 |
+
var hide$1 = {
|
1927 |
+
name: 'hide',
|
1928 |
+
enabled: true,
|
1929 |
+
phase: 'main',
|
1930 |
+
requiresIfExists: ['preventOverflow'],
|
1931 |
+
fn: hide
|
1932 |
+
};
|
1933 |
+
|
1934 |
+
var defaultModifiers = [eventListeners, popperOffsets$1, computeStyles$1, applyStyles$1];
|
1935 |
+
var createPopper = /*#__PURE__*/ popperGenerator({
|
1936 |
+
defaultModifiers: defaultModifiers
|
1937 |
+
}); // eslint-disable-next-line import/no-unused-modules
|
1938 |
+
|
1939 |
+
var defaultModifiers$1 = [eventListeners, popperOffsets$1, computeStyles$1, applyStyles$1, offset$1, flip$1, preventOverflow$1, arrow$1, hide$1];
|
1940 |
+
var createPopper$1 = /*#__PURE__*/ popperGenerator({
|
1941 |
+
defaultModifiers: defaultModifiers$1
|
1942 |
+
}); // eslint-disable-next-line import/no-unused-modules
|
1943 |
+
|
1944 |
+
exports.applyStyles = applyStyles$1;
|
1945 |
+
exports.arrow = arrow$1;
|
1946 |
+
exports.computeStyles = computeStyles$1;
|
1947 |
+
exports.createPopper = createPopper$1;
|
1948 |
+
exports.createPopperLite = createPopper;
|
1949 |
+
exports.defaultModifiers = defaultModifiers$1;
|
1950 |
+
exports.detectOverflow = detectOverflow;
|
1951 |
+
exports.eventListeners = eventListeners;
|
1952 |
+
exports.flip = flip$1;
|
1953 |
+
exports.hide = hide$1;
|
1954 |
+
exports.offset = offset$1;
|
1955 |
+
exports.popperGenerator = popperGenerator;
|
1956 |
+
exports.popperOffsets = popperOffsets$1;
|
1957 |
+
exports.preventOverflow = preventOverflow$1;
|
1958 |
+
|
1959 |
+
Object.defineProperty(exports, '__esModule', { value: true });
|
1960 |
+
|
1961 |
+
})));
|
assets/js/popper/popper.min.js
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*!
|
2 |
+
* @popperjs/core v2.6.0 - MIT License
|
3 |
+
*/
|
4 |
+
|
5 |
+
"use strict";!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e=e||self).Popper={})}(this,(function(e){function t(e){return{width:(e=e.getBoundingClientRect()).width,height:e.height,top:e.top,right:e.right,bottom:e.bottom,left:e.left,x:e.left,y:e.top}}function n(e){return"[object Window]"!==e.toString()?(e=e.ownerDocument)&&e.defaultView||window:e}function r(e){return{scrollLeft:(e=n(e)).pageXOffset,scrollTop:e.pageYOffset}}function o(e){return e instanceof n(e).Element||e instanceof Element}function i(e){return e instanceof n(e).HTMLElement||e instanceof HTMLElement}function a(e){return e?(e.nodeName||"").toLowerCase():null}function s(e){return((o(e)?e.ownerDocument:e.document)||window.document).documentElement}function f(e){return t(s(e)).left+r(e).scrollLeft}function c(e){return n(e).getComputedStyle(e)}function p(e){return e=c(e),/auto|scroll|overlay|hidden/.test(e.overflow+e.overflowY+e.overflowX)}function l(e,o,c){void 0===c&&(c=!1);var l=s(o);e=t(e);var u=i(o),d={scrollLeft:0,scrollTop:0},m={x:0,y:0};return(u||!u&&!c)&&(("body"!==a(o)||p(l))&&(d=o!==n(o)&&i(o)?{scrollLeft:o.scrollLeft,scrollTop:o.scrollTop}:r(o)),i(o)?((m=t(o)).x+=o.clientLeft,m.y+=o.clientTop):l&&(m.x=f(l))),{x:e.left+d.scrollLeft-m.x,y:e.top+d.scrollTop-m.y,width:e.width,height:e.height}}function u(e){return{x:e.offsetLeft,y:e.offsetTop,width:e.offsetWidth,height:e.offsetHeight}}function d(e){return"html"===a(e)?e:e.assignedSlot||e.parentNode||e.host||s(e)}function m(e,t){void 0===t&&(t=[]);var r=function e(t){return 0<=["html","body","#document"].indexOf(a(t))?t.ownerDocument.body:i(t)&&p(t)?t:e(d(t))}(e);e="body"===a(r);var o=n(r);return r=e?[o].concat(o.visualViewport||[],p(r)?r:[]):r,t=t.concat(r),e?t:t.concat(m(d(r)))}function h(e){if(!i(e)||"fixed"===c(e).position)return null;if(e=e.offsetParent){var t=s(e);if("body"===a(e)&&"static"===c(e).position&&"static"!==c(t).position)return t}return e}function g(e){for(var t=n(e),r=h(e);r&&0<=["table","td","th"].indexOf(a(r))&&"static"===c(r).position;)r=h(r);if(r&&"body"===a(r)&&"static"===c(r).position)return t;if(!r)e:{for(e=d(e);i(e)&&0>["html","body"].indexOf(a(e));){if("none"!==(r=c(e)).transform||"none"!==r.perspective||r.willChange&&"auto"!==r.willChange){r=e;break e}e=e.parentNode}r=null}return r||t}function v(e){var t=new Map,n=new Set,r=[];return e.forEach((function(e){t.set(e.name,e)})),e.forEach((function(e){n.has(e.name)||function e(o){n.add(o.name),[].concat(o.requires||[],o.requiresIfExists||[]).forEach((function(r){n.has(r)||(r=t.get(r))&&e(r)})),r.push(o)}(e)})),r}function b(e){var t;return function(){return t||(t=new Promise((function(n){Promise.resolve().then((function(){t=void 0,n(e())}))}))),t}}function y(e){return e.split("-")[0]}function O(e,t){var r,o=t.getRootNode&&t.getRootNode();if(e.contains(t))return!0;if((r=o)&&(r=o instanceof(r=n(o).ShadowRoot)||o instanceof ShadowRoot),r)do{if(t&&e.isSameNode(t))return!0;t=t.parentNode||t.host}while(t);return!1}function w(e){return Object.assign(Object.assign({},e),{},{left:e.x,top:e.y,right:e.x+e.width,bottom:e.y+e.height})}function x(e,o){if("viewport"===o){o=n(e);var a=s(e);o=o.visualViewport;var p=a.clientWidth;a=a.clientHeight;var l=0,u=0;o&&(p=o.width,a=o.height,/^((?!chrome|android).)*safari/i.test(navigator.userAgent)||(l=o.offsetLeft,u=o.offsetTop)),e=w(e={width:p,height:a,x:l+f(e),y:u})}else i(o)?((e=t(o)).top+=o.clientTop,e.left+=o.clientLeft,e.bottom=e.top+o.clientHeight,e.right=e.left+o.clientWidth,e.width=o.clientWidth,e.height=o.clientHeight,e.x=e.left,e.y=e.top):(u=s(e),e=s(u),l=r(u),o=u.ownerDocument.body,p=Math.max(e.scrollWidth,e.clientWidth,o?o.scrollWidth:0,o?o.clientWidth:0),a=Math.max(e.scrollHeight,e.clientHeight,o?o.scrollHeight:0,o?o.clientHeight:0),u=-l.scrollLeft+f(u),l=-l.scrollTop,"rtl"===c(o||e).direction&&(u+=Math.max(e.clientWidth,o?o.clientWidth:0)-p),e=w({width:p,height:a,x:u,y:l}));return e}function j(e,t,n){return t="clippingParents"===t?function(e){var t=m(d(e)),n=0<=["absolute","fixed"].indexOf(c(e).position)&&i(e)?g(e):e;return o(n)?t.filter((function(e){return o(e)&&O(e,n)&&"body"!==a(e)})):[]}(e):[].concat(t),(n=(n=[].concat(t,[n])).reduce((function(t,n){return n=x(e,n),t.top=Math.max(n.top,t.top),t.right=Math.min(n.right,t.right),t.bottom=Math.min(n.bottom,t.bottom),t.left=Math.max(n.left,t.left),t}),x(e,n[0]))).width=n.right-n.left,n.height=n.bottom-n.top,n.x=n.left,n.y=n.top,n}function M(e){return 0<=["top","bottom"].indexOf(e)?"x":"y"}function E(e){var t=e.reference,n=e.element,r=(e=e.placement)?y(e):null;e=e?e.split("-")[1]:null;var o=t.x+t.width/2-n.width/2,i=t.y+t.height/2-n.height/2;switch(r){case"top":o={x:o,y:t.y-n.height};break;case"bottom":o={x:o,y:t.y+t.height};break;case"right":o={x:t.x+t.width,y:i};break;case"left":o={x:t.x-n.width,y:i};break;default:o={x:t.x,y:t.y}}if(null!=(r=r?M(r):null))switch(i="y"===r?"height":"width",e){case"start":o[r]-=t[i]/2-n[i]/2;break;case"end":o[r]+=t[i]/2-n[i]/2}return o}function D(e){return Object.assign(Object.assign({},{top:0,right:0,bottom:0,left:0}),e)}function P(e,t){return t.reduce((function(t,n){return t[n]=e,t}),{})}function L(e,n){void 0===n&&(n={});var r=n;n=void 0===(n=r.placement)?e.placement:n;var i=r.boundary,a=void 0===i?"clippingParents":i,f=void 0===(i=r.rootBoundary)?"viewport":i;i=void 0===(i=r.elementContext)?"popper":i;var c=r.altBoundary,p=void 0!==c&&c;r=D("number"!=typeof(r=void 0===(r=r.padding)?0:r)?r:P(r,T));var l=e.elements.reference;c=e.rects.popper,a=j(o(p=e.elements[p?"popper"===i?"reference":"popper":i])?p:p.contextElement||s(e.elements.popper),a,f),p=E({reference:f=t(l),element:c,strategy:"absolute",placement:n}),c=w(Object.assign(Object.assign({},c),p)),f="popper"===i?c:f;var u={top:a.top-f.top+r.top,bottom:f.bottom-a.bottom+r.bottom,left:a.left-f.left+r.left,right:f.right-a.right+r.right};if(e=e.modifiersData.offset,"popper"===i&&e){var d=e[n];Object.keys(u).forEach((function(e){var t=0<=["right","bottom"].indexOf(e)?1:-1,n=0<=["top","bottom"].indexOf(e)?"y":"x";u[e]+=d[n]*t}))}return u}function k(){for(var e=arguments.length,t=Array(e),n=0;n<e;n++)t[n]=arguments[n];return!t.some((function(e){return!(e&&"function"==typeof e.getBoundingClientRect)}))}function B(e){void 0===e&&(e={});var t=e.defaultModifiers,n=void 0===t?[]:t,r=void 0===(e=e.defaultOptions)?V:e;return function(e,t,i){function a(){f.forEach((function(e){return e()})),f=[]}void 0===i&&(i=r);var s={placement:"bottom",orderedModifiers:[],options:Object.assign(Object.assign({},V),r),modifiersData:{},elements:{reference:e,popper:t},attributes:{},styles:{}},f=[],c=!1,p={state:s,setOptions:function(i){return a(),s.options=Object.assign(Object.assign(Object.assign({},r),s.options),i),s.scrollParents={reference:o(e)?m(e):e.contextElement?m(e.contextElement):[],popper:m(t)},i=function(e){var t=v(e);return N.reduce((function(e,n){return e.concat(t.filter((function(e){return e.phase===n})))}),[])}(function(e){var t=e.reduce((function(e,t){var n=e[t.name];return e[t.name]=n?Object.assign(Object.assign(Object.assign({},n),t),{},{options:Object.assign(Object.assign({},n.options),t.options),data:Object.assign(Object.assign({},n.data),t.data)}):t,e}),{});return Object.keys(t).map((function(e){return t[e]}))}([].concat(n,s.options.modifiers))),s.orderedModifiers=i.filter((function(e){return e.enabled})),s.orderedModifiers.forEach((function(e){var t=e.name,n=e.options;n=void 0===n?{}:n,"function"==typeof(e=e.effect)&&(t=e({state:s,name:t,instance:p,options:n}),f.push(t||function(){}))})),p.update()},forceUpdate:function(){if(!c){var e=s.elements,t=e.reference;if(k(t,e=e.popper))for(s.rects={reference:l(t,g(e),"fixed"===s.options.strategy),popper:u(e)},s.reset=!1,s.placement=s.options.placement,s.orderedModifiers.forEach((function(e){return s.modifiersData[e.name]=Object.assign({},e.data)})),t=0;t<s.orderedModifiers.length;t++)if(!0===s.reset)s.reset=!1,t=-1;else{var n=s.orderedModifiers[t];e=n.fn;var r=n.options;r=void 0===r?{}:r,n=n.name,"function"==typeof e&&(s=e({state:s,options:r,name:n,instance:p})||s)}}},update:b((function(){return new Promise((function(e){p.forceUpdate(),e(s)}))})),destroy:function(){a(),c=!0}};return k(e,t)?(p.setOptions(i).then((function(e){!c&&i.onFirstUpdate&&i.onFirstUpdate(e)})),p):p}}function W(e){var t,r=e.popper,o=e.popperRect,i=e.placement,a=e.offsets,f=e.position,c=e.gpuAcceleration,p=e.adaptive;e.roundOffsets?(e=window.devicePixelRatio||1,e={x:Math.round(a.x*e)/e||0,y:Math.round(a.y*e)/e||0}):e=a;var l=e;e=void 0===(e=l.x)?0:e,l=void 0===(l=l.y)?0:l;var u=a.hasOwnProperty("x");a=a.hasOwnProperty("y");var d,m="left",h="top",v=window;if(p){var b=g(r);b===n(r)&&(b=s(r)),"top"===i&&(h="bottom",l-=b.clientHeight-o.height,l*=c?1:-1),"left"===i&&(m="right",e-=b.clientWidth-o.width,e*=c?1:-1)}return r=Object.assign({position:f},p&&z),c?Object.assign(Object.assign({},r),{},((d={})[h]=a?"0":"",d[m]=u?"0":"",d.transform=2>(v.devicePixelRatio||1)?"translate("+e+"px, "+l+"px)":"translate3d("+e+"px, "+l+"px, 0)",d)):Object.assign(Object.assign({},r),{},((t={})[h]=a?l+"px":"",t[m]=u?e+"px":"",t.transform="",t))}function A(e){return e.replace(/left|right|bottom|top/g,(function(e){return G[e]}))}function H(e){return e.replace(/start|end/g,(function(e){return J[e]}))}function R(e,t,n){return void 0===n&&(n={x:0,y:0}),{top:e.top-t.height-n.y,right:e.right-t.width+n.x,bottom:e.bottom-t.height+n.y,left:e.left-t.width-n.x}}function S(e){return["top","right","bottom","left"].some((function(t){return 0<=e[t]}))}var T=["top","bottom","right","left"],q=T.reduce((function(e,t){return e.concat([t+"-start",t+"-end"])}),[]),C=[].concat(T,["auto"]).reduce((function(e,t){return e.concat([t,t+"-start",t+"-end"])}),[]),N="beforeRead read afterRead beforeMain main afterMain beforeWrite write afterWrite".split(" "),V={placement:"bottom",modifiers:[],strategy:"absolute"},I={passive:!0},_={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:function(e){var t=e.state,r=e.instance,o=(e=e.options).scroll,i=void 0===o||o,a=void 0===(e=e.resize)||e,s=n(t.elements.popper),f=[].concat(t.scrollParents.reference,t.scrollParents.popper);return i&&f.forEach((function(e){e.addEventListener("scroll",r.update,I)})),a&&s.addEventListener("resize",r.update,I),function(){i&&f.forEach((function(e){e.removeEventListener("scroll",r.update,I)})),a&&s.removeEventListener("resize",r.update,I)}},data:{}},U={name:"popperOffsets",enabled:!0,phase:"read",fn:function(e){var t=e.state;t.modifiersData[e.name]=E({reference:t.rects.reference,element:t.rects.popper,strategy:"absolute",placement:t.placement})},data:{}},z={top:"auto",right:"auto",bottom:"auto",left:"auto"},F={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:function(e){var t=e.state,n=e.options;e=void 0===(e=n.gpuAcceleration)||e;var r=n.adaptive;r=void 0===r||r,n=void 0===(n=n.roundOffsets)||n,e={placement:y(t.placement),popper:t.elements.popper,popperRect:t.rects.popper,gpuAcceleration:e},null!=t.modifiersData.popperOffsets&&(t.styles.popper=Object.assign(Object.assign({},t.styles.popper),W(Object.assign(Object.assign({},e),{},{offsets:t.modifiersData.popperOffsets,position:t.options.strategy,adaptive:r,roundOffsets:n})))),null!=t.modifiersData.arrow&&(t.styles.arrow=Object.assign(Object.assign({},t.styles.arrow),W(Object.assign(Object.assign({},e),{},{offsets:t.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:n})))),t.attributes.popper=Object.assign(Object.assign({},t.attributes.popper),{},{"data-popper-placement":t.placement})},data:{}},X={name:"applyStyles",enabled:!0,phase:"write",fn:function(e){var t=e.state;Object.keys(t.elements).forEach((function(e){var n=t.styles[e]||{},r=t.attributes[e]||{},o=t.elements[e];i(o)&&a(o)&&(Object.assign(o.style,n),Object.keys(r).forEach((function(e){var t=r[e];!1===t?o.removeAttribute(e):o.setAttribute(e,!0===t?"":t)})))}))},effect:function(e){var t=e.state,n={popper:{position:t.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(t.elements.popper.style,n.popper),t.elements.arrow&&Object.assign(t.elements.arrow.style,n.arrow),function(){Object.keys(t.elements).forEach((function(e){var r=t.elements[e],o=t.attributes[e]||{};e=Object.keys(t.styles.hasOwnProperty(e)?t.styles[e]:n[e]).reduce((function(e,t){return e[t]="",e}),{}),i(r)&&a(r)&&(Object.assign(r.style,e),Object.keys(o).forEach((function(e){r.removeAttribute(e)})))}))}},requires:["computeStyles"]},Y={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:function(e){var t=e.state,n=e.name,r=void 0===(e=e.options.offset)?[0,0]:e,o=(e=C.reduce((function(e,n){var o=t.rects,i=y(n),a=0<=["left","top"].indexOf(i)?-1:1,s="function"==typeof r?r(Object.assign(Object.assign({},o),{},{placement:n})):r;return o=(o=s[0])||0,s=((s=s[1])||0)*a,i=0<=["left","right"].indexOf(i)?{x:s,y:o}:{x:o,y:s},e[n]=i,e}),{}))[t.placement],i=o.x;o=o.y,null!=t.modifiersData.popperOffsets&&(t.modifiersData.popperOffsets.x+=i,t.modifiersData.popperOffsets.y+=o),t.modifiersData[n]=e}},G={left:"right",right:"left",bottom:"top",top:"bottom"},J={start:"end",end:"start"},K={name:"flip",enabled:!0,phase:"main",fn:function(e){var t=e.state,n=e.options;if(e=e.name,!t.modifiersData[e]._skip){var r=n.mainAxis;r=void 0===r||r;var o=n.altAxis;o=void 0===o||o;var i=n.fallbackPlacements,a=n.padding,s=n.boundary,f=n.rootBoundary,c=n.altBoundary,p=n.flipVariations,l=void 0===p||p,u=n.allowedAutoPlacements;p=y(n=t.options.placement),i=i||(p!==n&&l?function(e){if("auto"===y(e))return[];var t=A(e);return[H(e),t,H(t)]}(n):[A(n)]);var d=[n].concat(i).reduce((function(e,n){return e.concat("auto"===y(n)?function(e,t){void 0===t&&(t={});var n=t.boundary,r=t.rootBoundary,o=t.padding,i=t.flipVariations,a=t.allowedAutoPlacements,s=void 0===a?C:a,f=t.placement.split("-")[1];0===(i=(t=f?i?q:q.filter((function(e){return e.split("-")[1]===f})):T).filter((function(e){return 0<=s.indexOf(e)}))).length&&(i=t);var c=i.reduce((function(t,i){return t[i]=L(e,{placement:i,boundary:n,rootBoundary:r,padding:o})[y(i)],t}),{});return Object.keys(c).sort((function(e,t){return c[e]-c[t]}))}(t,{placement:n,boundary:s,rootBoundary:f,padding:a,flipVariations:l,allowedAutoPlacements:u}):n)}),[]);n=t.rects.reference,i=t.rects.popper;var m=new Map;p=!0;for(var h=d[0],g=0;g<d.length;g++){var v=d[g],b=y(v),O="start"===v.split("-")[1],w=0<=["top","bottom"].indexOf(b),x=w?"width":"height",j=L(t,{placement:v,boundary:s,rootBoundary:f,altBoundary:c,padding:a});if(O=w?O?"right":"left":O?"bottom":"top",n[x]>i[x]&&(O=A(O)),x=A(O),w=[],r&&w.push(0>=j[b]),o&&w.push(0>=j[O],0>=j[x]),w.every((function(e){return e}))){h=v,p=!1;break}m.set(v,w)}if(p)for(r=function(e){var t=d.find((function(t){if(t=m.get(t))return t.slice(0,e).every((function(e){return e}))}));if(t)return h=t,"break"},o=l?3:1;0<o&&"break"!==r(o);o--);t.placement!==h&&(t.modifiersData[e]._skip=!0,t.placement=h,t.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}},Q={name:"preventOverflow",enabled:!0,phase:"main",fn:function(e){var t=e.state,n=e.options;e=e.name;var r=n.mainAxis,o=void 0===r||r;r=void 0!==(r=n.altAxis)&&r;var i=n.tether;i=void 0===i||i;var a=n.tetherOffset,s=void 0===a?0:a;n=L(t,{boundary:n.boundary,rootBoundary:n.rootBoundary,padding:n.padding,altBoundary:n.altBoundary}),a=y(t.placement);var f=t.placement.split("-")[1],c=!f,p=M(a);a="x"===p?"y":"x";var l=t.modifiersData.popperOffsets,d=t.rects.reference,m=t.rects.popper,h="function"==typeof s?s(Object.assign(Object.assign({},t.rects),{},{placement:t.placement})):s;if(s={x:0,y:0},l){if(o){var v="y"===p?"top":"left",b="y"===p?"bottom":"right",O="y"===p?"height":"width";o=l[p];var w=l[p]+n[v],x=l[p]-n[b],j=i?-m[O]/2:0,E="start"===f?d[O]:m[O];f="start"===f?-m[O]:-d[O],m=t.elements.arrow,m=i&&m?u(m):{width:0,height:0};var D=t.modifiersData["arrow#persistent"]?t.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0};v=D[v],b=D[b],m=Math.max(0,Math.min(d[O],m[O])),E=c?d[O]/2-j-m-v-h:E-m-v-h,c=c?-d[O]/2+j+m+b+h:f+m+b+h,h=t.elements.arrow&&g(t.elements.arrow),d=t.modifiersData.offset?t.modifiersData.offset[t.placement][p]:0,h=l[p]+E-d-(h?"y"===p?h.clientTop||0:h.clientLeft||0:0),c=l[p]+c-d,i=Math.max(i?Math.min(w,h):w,Math.min(o,i?Math.max(x,c):x)),l[p]=i,s[p]=i-o}r&&(r=l[a],i=Math.max(r+n["x"===p?"top":"left"],Math.min(r,r-n["x"===p?"bottom":"right"])),l[a]=i,s[a]=i-r),t.modifiersData[e]=s}},requiresIfExists:["offset"]},Z={name:"arrow",enabled:!0,phase:"main",fn:function(e){var t,n=e.state;e=e.name;var r=n.elements.arrow,o=n.modifiersData.popperOffsets,i=y(n.placement),a=M(i);if(i=0<=["left","right"].indexOf(i)?"height":"width",r&&o){var s=n.modifiersData[e+"#persistent"].padding,f=u(r),c="y"===a?"top":"left",p="y"===a?"bottom":"right",l=n.rects.reference[i]+n.rects.reference[a]-o[a]-n.rects.popper[i];o=o[a]-n.rects.reference[a],l=(r=(r=g(r))?"y"===a?r.clientHeight||0:r.clientWidth||0:0)/2-f[i]/2+(l/2-o/2),i=Math.max(s[c],Math.min(l,r-f[i]-s[p])),n.modifiersData[e]=((t={})[a]=i,t.centerOffset=i-l,t)}},effect:function(e){var t=e.state,n=e.options;e=e.name;var r=n.element;if(r=void 0===r?"[data-popper-arrow]":r,n=void 0===(n=n.padding)?0:n,null!=r){if("string"==typeof r&&!(r=t.elements.popper.querySelector(r)))return;O(t.elements.popper,r)&&(t.elements.arrow=r,t.modifiersData[e+"#persistent"]={padding:D("number"!=typeof n?n:P(n,T))})}},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]},$={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:function(e){var t=e.state;e=e.name;var n=t.rects.reference,r=t.rects.popper,o=t.modifiersData.preventOverflow,i=L(t,{elementContext:"reference"}),a=L(t,{altBoundary:!0});n=R(i,n),r=R(a,r,o),o=S(n),a=S(r),t.modifiersData[e]={referenceClippingOffsets:n,popperEscapeOffsets:r,isReferenceHidden:o,hasPopperEscaped:a},t.attributes.popper=Object.assign(Object.assign({},t.attributes.popper),{},{"data-popper-reference-hidden":o,"data-popper-escaped":a})}},ee=B({defaultModifiers:[_,U,F,X]}),te=[_,U,F,X,Y,K,Q,Z,$],ne=B({defaultModifiers:te});e.applyStyles=X,e.arrow=Z,e.computeStyles=F,e.createPopper=ne,e.createPopperLite=ee,e.defaultModifiers=te,e.detectOverflow=L,e.eventListeners=_,e.flip=K,e.hide=$,e.offset=Y,e.popperGenerator=B,e.popperOffsets=U,e.preventOverflow=Q,Object.defineProperty(e,"__esModule",{value:!0})}));
|
assets/js/tippy/dup-pro-tippy.css
ADDED
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*! ================================================
|
2 |
+
* DUPLICATOR TIPPY STYLE
|
3 |
+
* Copyright:Snap Creek LLC 2015-2021
|
4 |
+
* ================================================ */
|
5 |
+
|
6 |
+
.tippy-box[data-theme~='duplicator'],
|
7 |
+
.tippy-box[data-theme~='duplicator-filled'] {
|
8 |
+
background-color: white;
|
9 |
+
color: black;
|
10 |
+
font-size: 12px;
|
11 |
+
border-width: 1px;
|
12 |
+
border-style: solid;
|
13 |
+
border-radius: 0;
|
14 |
+
padding: 0;
|
15 |
+
max-width: 250px;
|
16 |
+
}
|
17 |
+
|
18 |
+
.tippy-box[data-theme~='duplicator'] .tippy-content,
|
19 |
+
.tippy-box[data-theme~='duplicator-filled'] .tippy-content {
|
20 |
+
padding: 0;
|
21 |
+
}
|
22 |
+
|
23 |
+
.tippy-box[data-theme~='duplicator'] h3,
|
24 |
+
.tippy-box[data-theme~='duplicator-filled'] h3 {
|
25 |
+
display: block;
|
26 |
+
box-sizing: border-box;
|
27 |
+
margin: 0;
|
28 |
+
width: 100%;
|
29 |
+
padding: 5px;
|
30 |
+
font-weight: bold;
|
31 |
+
font-size: 12px;
|
32 |
+
}
|
33 |
+
|
34 |
+
.tippy-box[data-theme~='duplicator'] .dup-tippy-content,
|
35 |
+
.tippy-box[data-theme~='duplicator-filled'] .dup-tippy-content {
|
36 |
+
padding: 5px;
|
37 |
+
}
|
38 |
+
|
39 |
+
.tippy-box[data-theme~='duplicator'] .dup-tippy-content *:first-child,
|
40 |
+
.tippy-box[data-theme~='duplicator-filled'] .dup-tippy-content *:first-child {
|
41 |
+
margin-top: 0;
|
42 |
+
}
|
43 |
+
|
44 |
+
.tippy-box[data-theme~='duplicator'] .dup-tippy-content *:last-child,
|
45 |
+
.tippy-box[data-theme~='duplicator-filled'] .dup-tippy-content *:last-child {
|
46 |
+
margin-bottom: 0;
|
47 |
+
}
|
48 |
+
|
49 |
+
.tippy-box[data-placement^='top']>.tippy-arrow::before {
|
50 |
+
bottom: -9px;
|
51 |
+
}
|
52 |
+
|
53 |
+
.tippy-box[data-placement^='bottom']>.tippy-arrow::before {
|
54 |
+
top: -9px;
|
55 |
+
}
|
56 |
+
|
57 |
+
.tippy-box[data-theme~='duplicator'],
|
58 |
+
.tippy-box[data-theme~='duplicator-filled'] {
|
59 |
+
border-color: #13659C;
|
60 |
+
}
|
61 |
+
|
62 |
+
.tippy-box[data-theme~='duplicator'] h3,
|
63 |
+
.tippy-box[data-theme~='duplicato-filled'] h3 {
|
64 |
+
background-color: #13659C;
|
65 |
+
color: white;
|
66 |
+
}
|
67 |
+
|
68 |
+
.tippy-box[data-theme~='duplicator-filled'] .tippy-content {
|
69 |
+
background-color: #13659C;
|
70 |
+
color: white;
|
71 |
+
}
|
72 |
+
|
73 |
+
.tippy-box[data-theme~='duplicator'][data-placement^='top']>.tippy-arrow::before,
|
74 |
+
.tippy-box[data-theme~='duplicator-filled'][data-placement^='top']>.tippy-arrow::before {
|
75 |
+
border-top-color: #13659C;
|
76 |
+
}
|
77 |
+
|
78 |
+
.tippy-box[data-theme~='duplicator'][data-placement^='bottom']>.tippy-arrow::before,
|
79 |
+
.tippy-box[data-theme~='duplicator-filled'][data-placement^='bottom']>.tippy-arrow::before {
|
80 |
+
border-bottom-color: #13659C;
|
81 |
+
}
|
82 |
+
|
83 |
+
.tippy-box[data-theme~='duplicator'][data-placement^='left']>.tippy-arrow::before,
|
84 |
+
.tippy-box[data-theme~='duplicator-filled'][data-placement^='left']>.tippy-arrow::before {
|
85 |
+
border-left-color: #13659C;
|
86 |
+
}
|
87 |
+
|
88 |
+
.tippy-box[data-theme~='duplicator'][data-placement^='right']>.tippy-arrow::before,
|
89 |
+
.tippy-box[data-theme~='duplicator-filled'][data-placement^='right']>.tippy-arrow::before {
|
90 |
+
border-right-color: #13659C;
|
91 |
+
}
|
assets/js/tippy/index.php
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
//silent
|
assets/js/tippy/tippy-bundle.umd.js
ADDED
@@ -0,0 +1,2509 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*!
|
2 |
+
* tippy.js v6.2.7
|
3 |
+
* (c) 2017-2020 atomiks
|
4 |
+
* MIT License
|
5 |
+
*/
|
6 |
+
(function (global, factory) {
|
7 |
+
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('@popperjs/core')) :
|
8 |
+
typeof define === 'function' && define.amd ? define(['@popperjs/core'], factory) :
|
9 |
+
(global = global || self, global.tippy = factory(global.Popper));
|
10 |
+
}(this, (function (core) {
|
11 |
+
'use strict';
|
12 |
+
|
13 |
+
var css = ".tippy-box[data-animation=fade][data-state=hidden]{opacity:0}[data-tippy-root]{max-width:calc(100vw - 10px)}.tippy-box{position:relative;background-color:#333;color:#fff;border-radius:4px;font-size:14px;line-height:1.4;outline:0;transition-property:transform,visibility,opacity}.tippy-box[data-placement^=top]>.tippy-arrow{bottom:0}.tippy-box[data-placement^=top]>.tippy-arrow:before{bottom:-7px;left:0;border-width:8px 8px 0;border-top-color:initial;transform-origin:center top}.tippy-box[data-placement^=bottom]>.tippy-arrow{top:0}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{top:-7px;left:0;border-width:0 8px 8px;border-bottom-color:initial;transform-origin:center bottom}.tippy-box[data-placement^=left]>.tippy-arrow{right:0}.tippy-box[data-placement^=left]>.tippy-arrow:before{border-width:8px 0 8px 8px;border-left-color:initial;right:-7px;transform-origin:center left}.tippy-box[data-placement^=right]>.tippy-arrow{left:0}.tippy-box[data-placement^=right]>.tippy-arrow:before{left:-7px;border-width:8px 8px 8px 0;border-right-color:initial;transform-origin:center right}.tippy-box[data-inertia][data-state=visible]{transition-timing-function:cubic-bezier(.54,1.5,.38,1.11)}.tippy-arrow{width:16px;height:16px;color:#333}.tippy-arrow:before{content:\"\";position:absolute;border-color:transparent;border-style:solid}.tippy-content{position:relative;padding:5px 9px;z-index:1}";
|
14 |
+
|
15 |
+
function injectCSS(css)
|
16 |
+
{
|
17 |
+
var style = document.createElement('style');
|
18 |
+
style.textContent = css;
|
19 |
+
style.setAttribute('data-tippy-stylesheet', '');
|
20 |
+
var head = document.head;
|
21 |
+
var firstStyleOrLinkTag = document.querySelector('head>style,head>link');
|
22 |
+
|
23 |
+
if (firstStyleOrLinkTag) {
|
24 |
+
head.insertBefore(style, firstStyleOrLinkTag);
|
25 |
+
} else {
|
26 |
+
head.appendChild(style);
|
27 |
+
}
|
28 |
+
}
|
29 |
+
|
30 |
+
var isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
|
31 |
+
var ua = isBrowser ? navigator.userAgent : '';
|
32 |
+
var isIE = /MSIE |Trident\//.test(ua);
|
33 |
+
|
34 |
+
var ROUND_ARROW = '<svg width="16" height="6" xmlns="http://www.w3.org/2000/svg"><path d="M0 6s1.796-.013 4.67-3.615C5.851.9 6.93.006 8 0c1.07-.006 2.148.887 3.343 2.385C14.233 6.005 16 6 16 6H0z"></svg>';
|
35 |
+
var BOX_CLASS = "tippy-box";
|
36 |
+
var CONTENT_CLASS = "tippy-content";
|
37 |
+
var BACKDROP_CLASS = "tippy-backdrop";
|
38 |
+
var ARROW_CLASS = "tippy-arrow";
|
39 |
+
var SVG_ARROW_CLASS = "tippy-svg-arrow";
|
40 |
+
var TOUCH_OPTIONS = {
|
41 |
+
passive: true,
|
42 |
+
capture: true
|
43 |
+
};
|
44 |
+
|
45 |
+
function hasOwnProperty(obj, key)
|
46 |
+
{
|
47 |
+
return {}.hasOwnProperty.call(obj, key);
|
48 |
+
}
|
49 |
+
function getValueAtIndexOrReturn(value, index, defaultValue)
|
50 |
+
{
|
51 |
+
if (Array.isArray(value)) {
|
52 |
+
var v = value[index];
|
53 |
+
return v == null ? Array.isArray(defaultValue) ? defaultValue[index] : defaultValue : v;
|
54 |
+
}
|
55 |
+
|
56 |
+
return value;
|
57 |
+
}
|
58 |
+
function isType(value, type)
|
59 |
+
{
|
60 |
+
var str = {}.toString.call(value);
|
61 |
+
return str.indexOf('[object') === 0 && str.indexOf(type + "]") > -1;
|
62 |
+
}
|
63 |
+
function invokeWithArgsOrReturn(value, args)
|
64 |
+
{
|
65 |
+
return typeof value === 'function' ? value.apply(void 0, args) : value;
|
66 |
+
}
|
67 |
+
function debounce(fn, ms)
|
68 |
+
{
|
69 |
+
// Avoid wrapping in `setTimeout` if ms is 0 anyway
|
70 |
+
if (ms === 0) {
|
71 |
+
return fn;
|
72 |
+
}
|
73 |
+
|
74 |
+
var timeout;
|
75 |
+
return function (arg) {
|
76 |
+
clearTimeout(timeout);
|
77 |
+
timeout = setTimeout(function () {
|
78 |
+
fn(arg);
|
79 |
+
}, ms);
|
80 |
+
};
|
81 |
+
}
|
82 |
+
function removeProperties(obj, keys)
|
83 |
+
{
|
84 |
+
var clone = Object.assign({}, obj);
|
85 |
+
keys.forEach(function (key) {
|
86 |
+
delete clone[key];
|
87 |
+
});
|
88 |
+
return clone;
|
89 |
+
}
|
90 |
+
function splitBySpaces(value)
|
91 |
+
{
|
92 |
+
return value.split(/\s+/).filter(Boolean);
|
93 |
+
}
|
94 |
+
function normalizeToArray(value)
|
95 |
+
{
|
96 |
+
return [].concat(value);
|
97 |
+
}
|
98 |
+
function pushIfUnique(arr, value)
|
99 |
+
{
|
100 |
+
if (arr.indexOf(value) === -1) {
|
101 |
+
arr.push(value);
|
102 |
+
}
|
103 |
+
}
|
104 |
+
function unique(arr)
|
105 |
+
{
|
106 |
+
return arr.filter(function (item, index) {
|
107 |
+
return arr.indexOf(item) === index;
|
108 |
+
});
|
109 |
+
}
|
110 |
+
function getBasePlacement(placement)
|
111 |
+
{
|
112 |
+
return placement.split('-')[0];
|
113 |
+
}
|
114 |
+
function arrayFrom(value)
|
115 |
+
{
|
116 |
+
return [].slice.call(value);
|
117 |
+
}
|
118 |
+
function removeUndefinedProps(obj)
|
119 |
+
{
|
120 |
+
return Object.keys(obj).reduce(function (acc, key) {
|
121 |
+
if (obj[key] !== undefined) {
|
122 |
+
acc[key] = obj[key];
|
123 |
+
}
|
124 |
+
|
125 |
+
return acc;
|
126 |
+
}, {});
|
127 |
+
}
|
128 |
+
|
129 |
+
function div()
|
130 |
+
{
|
131 |
+
return document.createElement('div');
|
132 |
+
}
|
133 |
+
function isElement(value)
|
134 |
+
{
|
135 |
+
return ['Element', 'Fragment'].some(function (type) {
|
136 |
+
return isType(value, type);
|
137 |
+
});
|
138 |
+
}
|
139 |
+
function isNodeList(value)
|
140 |
+
{
|
141 |
+
return isType(value, 'NodeList');
|
142 |
+
}
|
143 |
+
function isMouseEvent(value)
|
144 |
+
{
|
145 |
+
return isType(value, 'MouseEvent');
|
146 |
+
}
|
147 |
+
function isReferenceElement(value)
|
148 |
+
{
|
149 |
+
return !!(value && value._tippy && value._tippy.reference === value);
|
150 |
+
}
|
151 |
+
function getArrayOfElements(value)
|
152 |
+
{
|
153 |
+
if (isElement(value)) {
|
154 |
+
return [value];
|
155 |
+
}
|
156 |
+
|
157 |
+
if (isNodeList(value)) {
|
158 |
+
return arrayFrom(value);
|
159 |
+
}
|
160 |
+
|
161 |
+
if (Array.isArray(value)) {
|
162 |
+
return value;
|
163 |
+
}
|
164 |
+
|
165 |
+
return arrayFrom(document.querySelectorAll(value));
|
166 |
+
}
|
167 |
+
function setTransitionDuration(els, value)
|
168 |
+
{
|
169 |
+
els.forEach(function (el) {
|
170 |
+
if (el) {
|
171 |
+
el.style.transitionDuration = value + "ms";
|
172 |
+
}
|
173 |
+
});
|
174 |
+
}
|
175 |
+
function setVisibilityState(els, state)
|
176 |
+
{
|
177 |
+
els.forEach(function (el) {
|
178 |
+
if (el) {
|
179 |
+
el.setAttribute('data-state', state);
|
180 |
+
}
|
181 |
+
});
|
182 |
+
}
|
183 |
+
function getOwnerDocument(elementOrElements)
|
184 |
+
{
|
185 |
+
var _normalizeToArray = normalizeToArray(elementOrElements),
|
186 |
+
element = _normalizeToArray[0];
|
187 |
+
|
188 |
+
return element ? element.ownerDocument || document : document;
|
189 |
+
}
|
190 |
+
function isCursorOutsideInteractiveBorder(popperTreeData, event)
|
191 |
+
{
|
192 |
+
var clientX = event.clientX,
|
193 |
+
clientY = event.clientY;
|
194 |
+
return popperTreeData.every(function (_ref) {
|
195 |
+
var popperRect = _ref.popperRect,
|
196 |
+
popperState = _ref.popperState,
|
197 |
+
props = _ref.props;
|
198 |
+
var interactiveBorder = props.interactiveBorder;
|
199 |
+
var basePlacement = getBasePlacement(popperState.placement);
|
200 |
+
var offsetData = popperState.modifiersData.offset;
|
201 |
+
|
202 |
+
if (!offsetData) {
|
203 |
+
return true;
|
204 |
+
}
|
205 |
+
|
206 |
+
var topDistance = basePlacement === 'bottom' ? offsetData.top.y : 0;
|
207 |
+
var bottomDistance = basePlacement === 'top' ? offsetData.bottom.y : 0;
|
208 |
+
var leftDistance = basePlacement === 'right' ? offsetData.left.x : 0;
|
209 |
+
var rightDistance = basePlacement === 'left' ? offsetData.right.x : 0;
|
210 |
+
var exceedsTop = popperRect.top - clientY + topDistance > interactiveBorder;
|
211 |
+
var exceedsBottom = clientY - popperRect.bottom - bottomDistance > interactiveBorder;
|
212 |
+
var exceedsLeft = popperRect.left - clientX + leftDistance > interactiveBorder;
|
213 |
+
var exceedsRight = clientX - popperRect.right - rightDistance > interactiveBorder;
|
214 |
+
return exceedsTop || exceedsBottom || exceedsLeft || exceedsRight;
|
215 |
+
});
|
216 |
+
}
|
217 |
+
function updateTransitionEndListener(box, action, listener)
|
218 |
+
{
|
219 |
+
var method = action + "EventListener"; // some browsers apparently support `transition` (unprefixed) but only fire
|
220 |
+
// `webkitTransitionEnd`...
|
221 |
+
|
222 |
+
['transitionend', 'webkitTransitionEnd'].forEach(function (event) {
|
223 |
+
box[method](event, listener);
|
224 |
+
});
|
225 |
+
}
|
226 |
+
|
227 |
+
var currentInput = {
|
228 |
+
isTouch: false
|
229 |
+
};
|
230 |
+
var lastMouseMoveTime = 0;
|
231 |
+
/**
|
232 |
+
* When a `touchstart` event is fired, it's assumed the user is using touch
|
233 |
+
* input. We'll bind a `mousemove` event listener to listen for mouse input in
|
234 |
+
* the future. This way, the `isTouch` property is fully dynamic and will handle
|
235 |
+
* hybrid devices that use a mix of touch + mouse input.
|
236 |
+
*/
|
237 |
+
|
238 |
+
function onDocumentTouchStart()
|
239 |
+
{
|
240 |
+
if (currentInput.isTouch) {
|
241 |
+
return;
|
242 |
+
}
|
243 |
+
|
244 |
+
currentInput.isTouch = true;
|
245 |
+
|
246 |
+
if (window.performance) {
|
247 |
+
document.addEventListener('mousemove', onDocumentMouseMove);
|
248 |
+
}
|
249 |
+
}
|
250 |
+
/**
|
251 |
+
* When two `mousemove` event are fired consecutively within 20ms, it's assumed
|
252 |
+
* the user is using mouse input again. `mousemove` can fire on touch devices as
|
253 |
+
* well, but very rarely that quickly.
|
254 |
+
*/
|
255 |
+
|
256 |
+
function onDocumentMouseMove()
|
257 |
+
{
|
258 |
+
var now = performance.now();
|
259 |
+
|
260 |
+
if (now - lastMouseMoveTime < 20) {
|
261 |
+
currentInput.isTouch = false;
|
262 |
+
document.removeEventListener('mousemove', onDocumentMouseMove);
|
263 |
+
}
|
264 |
+
|
265 |
+
lastMouseMoveTime = now;
|
266 |
+
}
|
267 |
+
/**
|
268 |
+
* When an element is in focus and has a tippy, leaving the tab/window and
|
269 |
+
* returning causes it to show again. For mouse users this is unexpected, but
|
270 |
+
* for keyboard use it makes sense.
|
271 |
+
* TODO: find a better technique to solve this problem
|
272 |
+
*/
|
273 |
+
|
274 |
+
function onWindowBlur()
|
275 |
+
{
|
276 |
+
var activeElement = document.activeElement;
|
277 |
+
|
278 |
+
if (isReferenceElement(activeElement)) {
|
279 |
+
var instance = activeElement._tippy;
|
280 |
+
|
281 |
+
if (activeElement.blur && !instance.state.isVisible) {
|
282 |
+
activeElement.blur();
|
283 |
+
}
|
284 |
+
}
|
285 |
+
}
|
286 |
+
function bindGlobalEventListeners()
|
287 |
+
{
|
288 |
+
document.addEventListener('touchstart', onDocumentTouchStart, TOUCH_OPTIONS);
|
289 |
+
window.addEventListener('blur', onWindowBlur);
|
290 |
+
}
|
291 |
+
|
292 |
+
function createMemoryLeakWarning(method)
|
293 |
+
{
|
294 |
+
var txt = method === 'destroy' ? 'n already-' : ' ';
|
295 |
+
return [method + "() was called on a" + txt + "destroyed instance. This is a no-op but", 'indicates a potential memory leak.'].join(' ');
|
296 |
+
}
|
297 |
+
function clean(value)
|
298 |
+
{
|
299 |
+
var spacesAndTabs = /[ \t]{2,}/g;
|
300 |
+
var lineStartWithSpaces = /^[ \t]*/gm;
|
301 |
+
return value.replace(spacesAndTabs, ' ').replace(lineStartWithSpaces, '').trim();
|
302 |
+
}
|
303 |
+
|
304 |
+
function getDevMessage(message)
|
305 |
+
{
|
306 |
+
return clean("\n %ctippy.js\n\n %c" + clean(message) + "\n\n %c\uD83D\uDC77\u200D This is a development-only message. It will be removed in production.\n ");
|
307 |
+
}
|
308 |
+
|
309 |
+
function getFormattedMessage(message)
|
310 |
+
{
|
311 |
+
return [getDevMessage(message), // title
|
312 |
+
'color: #00C584; font-size: 1.3em; font-weight: bold;', // message
|
313 |
+
'line-height: 1.5', // footer
|
314 |
+
'color: #a6a095;'];
|
315 |
+
} // Assume warnings and errors never have the same message
|
316 |
+
|
317 |
+
var visitedMessages;
|
318 |
+
|
319 |
+
{
|
320 |
+
resetVisitedMessages();
|
321 |
+
}
|
322 |
+
|
323 |
+
function resetVisitedMessages()
|
324 |
+
{
|
325 |
+
visitedMessages = new Set();
|
326 |
+
}
|
327 |
+
function warnWhen(condition, message)
|
328 |
+
{
|
329 |
+
if (condition && !visitedMessages.has(message)) {
|
330 |
+
var _console;
|
331 |
+
|
332 |
+
visitedMessages.add(message);
|
333 |
+
|
334 |
+
(_console = console).warn.apply(_console, getFormattedMessage(message));
|
335 |
+
}
|
336 |
+
}
|
337 |
+
function errorWhen(condition, message)
|
338 |
+
{
|
339 |
+
if (condition && !visitedMessages.has(message)) {
|
340 |
+
var _console2;
|
341 |
+
|
342 |
+
visitedMessages.add(message);
|
343 |
+
|
344 |
+
(_console2 = console).error.apply(_console2, getFormattedMessage(message));
|
345 |
+
}
|
346 |
+
}
|
347 |
+
function validateTargets(targets)
|
348 |
+
{
|
349 |
+
var didPassFalsyValue = !targets;
|
350 |
+
var didPassPlainObject = Object.prototype.toString.call(targets) === '[object Object]' && !targets.addEventListener;
|
351 |
+
errorWhen(didPassFalsyValue, ['tippy() was passed', '`' + String(targets) + '`', 'as its targets (first) argument. Valid types are: String, Element,', 'Element[], or NodeList.'].join(' '));
|
352 |
+
errorWhen(didPassPlainObject, ['tippy() was passed a plain object which is not supported as an argument', 'for virtual positioning. Use props.getReferenceClientRect instead.'].join(' '));
|
353 |
+
}
|
354 |
+
|
355 |
+
var pluginProps = {
|
356 |
+
animateFill: false,
|
357 |
+
followCursor: false,
|
358 |
+
inlinePositioning: false,
|
359 |
+
sticky: false
|
360 |
+
};
|
361 |
+
var renderProps = {
|
362 |
+
allowHTML: false,
|
363 |
+
animation: 'fade',
|
364 |
+
arrow: true,
|
365 |
+
content: '',
|
366 |
+
inertia: false,
|
367 |
+
maxWidth: 350,
|
368 |
+
role: 'tooltip',
|
369 |
+
theme: '',
|
370 |
+
zIndex: 9999
|
371 |
+
};
|
372 |
+
var defaultProps = Object.assign({
|
373 |
+
appendTo: function appendTo()
|
374 |
+
{
|
375 |
+
return document.body;
|
376 |
+
},
|
377 |
+
aria: {
|
378 |
+
content: 'auto',
|
379 |
+
expanded: 'auto'
|
380 |
+
},
|
381 |
+
delay: 0,
|
382 |
+
duration: [300, 250],
|
383 |
+
getReferenceClientRect: null,
|
384 |
+
hideOnClick: true,
|
385 |
+
ignoreAttributes: false,
|
386 |
+
interactive: false,
|
387 |
+
interactiveBorder: 2,
|
388 |
+
interactiveDebounce: 0,
|
389 |
+
moveTransition: '',
|
390 |
+
offset: [0, 10],
|
391 |
+
onAfterUpdate: function onAfterUpdate()
|
392 |
+
{},
|
393 |
+
onBeforeUpdate: function onBeforeUpdate()
|
394 |
+
{},
|
395 |
+
onCreate: function onCreate()
|
396 |
+
{},
|
397 |
+
onDestroy: function onDestroy()
|
398 |
+
{},
|
399 |
+
onHidden: function onHidden()
|
400 |
+
{},
|
401 |
+
onHide: function onHide()
|
402 |
+
{},
|
403 |
+
onMount: function onMount()
|
404 |
+
{},
|
405 |
+
onShow: function onShow()
|
406 |
+
{},
|
407 |
+
onShown: function onShown()
|
408 |
+
{},
|
409 |
+
onTrigger: function onTrigger()
|
410 |
+
{},
|
411 |
+
onUntrigger: function onUntrigger()
|
412 |
+
{},
|
413 |
+
onClickOutside: function onClickOutside()
|
414 |
+
{},
|
415 |
+
placement: 'top',
|
416 |
+
plugins: [],
|
417 |
+
popperOptions: {},
|
418 |
+
render: null,
|
419 |
+
showOnCreate: false,
|
420 |
+
touch: true,
|
421 |
+
trigger: 'mouseenter focus',
|
422 |
+
triggerTarget: null
|
423 |
+
}, pluginProps, {}, renderProps);
|
424 |
+
var defaultKeys = Object.keys(defaultProps);
|
425 |
+
var setDefaultProps = function setDefaultProps(partialProps)
|
426 |
+
{
|
427 |
+
/* istanbul ignore else */
|
428 |
+
{
|
429 |
+
validateProps(partialProps, []);
|
430 |
+
}
|
431 |
+
|
432 |
+
var keys = Object.keys(partialProps);
|
433 |
+
keys.forEach(function (key) {
|
434 |
+
defaultProps[key] = partialProps[key];
|
435 |
+
});
|
436 |
+
};
|
437 |
+
function getExtendedPassedProps(passedProps)
|
438 |
+
{
|
439 |
+
var plugins = passedProps.plugins || [];
|
440 |
+
var pluginProps = plugins.reduce(function (acc, plugin) {
|
441 |
+
var name = plugin.name,
|
442 |
+
defaultValue = plugin.defaultValue;
|
443 |
+
|
444 |
+
if (name) {
|
445 |
+
acc[name] = passedProps[name] !== undefined ? passedProps[name] : defaultValue;
|
446 |
+
}
|
447 |
+
|
448 |
+
return acc;
|
449 |
+
}, {});
|
450 |
+
return Object.assign({}, passedProps, {}, pluginProps);
|
451 |
+
}
|
452 |
+
function getDataAttributeProps(reference, plugins)
|
453 |
+
{
|
454 |
+
var propKeys = plugins ? Object.keys(getExtendedPassedProps(Object.assign({}, defaultProps, {
|
455 |
+
plugins: plugins
|
456 |
+
}))) : defaultKeys;
|
457 |
+
var props = propKeys.reduce(function (acc, key) {
|
458 |
+
var valueAsString = (reference.getAttribute("data-tippy-" + key) || '').trim();
|
459 |
+
|
460 |
+
if (!valueAsString) {
|
461 |
+
return acc;
|
462 |
+
}
|
463 |
+
|
464 |
+
if (key === 'content') {
|
465 |
+
acc[key] = valueAsString;
|
466 |
+
} else {
|
467 |
+
try {
|
468 |
+
acc[key] = JSON.parse(valueAsString);
|
469 |
+
} catch (e) {
|
470 |
+
acc[key] = valueAsString;
|
471 |
+
}
|
472 |
+
}
|
473 |
+
|
474 |
+
return acc;
|
475 |
+
}, {});
|
476 |
+
return props;
|
477 |
+
}
|
478 |
+
function evaluateProps(reference, props)
|
479 |
+
{
|
480 |
+
var out = Object.assign({}, props, {
|
481 |
+
content: invokeWithArgsOrReturn(props.content, [reference])
|
482 |
+
}, props.ignoreAttributes ? {} : getDataAttributeProps(reference, props.plugins));
|
483 |
+
out.aria = Object.assign({}, defaultProps.aria, {}, out.aria);
|
484 |
+
out.aria = {
|
485 |
+
expanded: out.aria.expanded === 'auto' ? props.interactive : out.aria.expanded,
|
486 |
+
content: out.aria.content === 'auto' ? props.interactive ? null : 'describedby' : out.aria.content
|
487 |
+
};
|
488 |
+
return out;
|
489 |
+
}
|
490 |
+
function validateProps(partialProps, plugins)
|
491 |
+
{
|
492 |
+
if (partialProps === void 0) {
|
493 |
+
partialProps = {};
|
494 |
+
}
|
495 |
+
|
496 |
+
if (plugins === void 0) {
|
497 |
+
plugins = [];
|
498 |
+
}
|
499 |
+
|
500 |
+
var keys = Object.keys(partialProps);
|
501 |
+
keys.forEach(function (prop) {
|
502 |
+
var nonPluginProps = removeProperties(defaultProps, Object.keys(pluginProps));
|
503 |
+
var didPassUnknownProp = !hasOwnProperty(nonPluginProps, prop); // Check if the prop exists in `plugins`
|
504 |
+
|
505 |
+
if (didPassUnknownProp) {
|
506 |
+
didPassUnknownProp = plugins.filter(function (plugin) {
|
507 |
+
return plugin.name === prop;
|
508 |
+
}).length === 0;
|
509 |
+
}
|
510 |
+
|
511 |
+
warnWhen(didPassUnknownProp, ["`" + prop + "`", "is not a valid prop. You may have spelled it incorrectly, or if it's", 'a plugin, forgot to pass it in an array as props.plugins.', '\n\n', 'All props: https://atomiks.github.io/tippyjs/v6/all-props/\n', 'Plugins: https://atomiks.github.io/tippyjs/v6/plugins/'].join(' '));
|
512 |
+
});
|
513 |
+
}
|
514 |
+
|
515 |
+
var innerHTML = function innerHTML()
|
516 |
+
{
|
517 |
+
return 'innerHTML';
|
518 |
+
};
|
519 |
+
|
520 |
+
function dangerouslySetInnerHTML(element, html)
|
521 |
+
{
|
522 |
+
element[innerHTML()] = html;
|
523 |
+
}
|
524 |
+
|
525 |
+
function createArrowElement(value)
|
526 |
+
{
|
527 |
+
var arrow = div();
|
528 |
+
|
529 |
+
if (value === true) {
|
530 |
+
arrow.className = ARROW_CLASS;
|
531 |
+
} else {
|
532 |
+
arrow.className = SVG_ARROW_CLASS;
|
533 |
+
|
534 |
+
if (isElement(value)) {
|
535 |
+
arrow.appendChild(value);
|
536 |
+
} else {
|
537 |
+
dangerouslySetInnerHTML(arrow, value);
|
538 |
+
}
|
539 |
+
}
|
540 |
+
|
541 |
+
return arrow;
|
542 |
+
}
|
543 |
+
|
544 |
+
function setContent(content, props)
|
545 |
+
{
|
546 |
+
if (isElement(props.content)) {
|
547 |
+
dangerouslySetInnerHTML(content, '');
|
548 |
+
content.appendChild(props.content);
|
549 |
+
} else if (typeof props.content !== 'function') {
|
550 |
+
if (props.allowHTML) {
|
551 |
+
dangerouslySetInnerHTML(content, props.content);
|
552 |
+
} else {
|
553 |
+
content.textContent = props.content;
|
554 |
+
}
|
555 |
+
}
|
556 |
+
}
|
557 |
+
function getChildren(popper)
|
558 |
+
{
|
559 |
+
var box = popper.firstElementChild;
|
560 |
+
var boxChildren = arrayFrom(box.children);
|
561 |
+
return {
|
562 |
+
box: box,
|
563 |
+
content: boxChildren.find(function (node) {
|
564 |
+
return node.classList.contains(CONTENT_CLASS);
|
565 |
+
}),
|
566 |
+
arrow: boxChildren.find(function (node) {
|
567 |
+
return node.classList.contains(ARROW_CLASS) || node.classList.contains(SVG_ARROW_CLASS);
|
568 |
+
}),
|
569 |
+
backdrop: boxChildren.find(function (node) {
|
570 |
+
return node.classList.contains(BACKDROP_CLASS);
|
571 |
+
})
|
572 |
+
};
|
573 |
+
}
|
574 |
+
function render(instance)
|
575 |
+
{
|
576 |
+
var popper = div();
|
577 |
+
var box = div();
|
578 |
+
box.className = BOX_CLASS;
|
579 |
+
box.setAttribute('data-state', 'hidden');
|
580 |
+
box.setAttribute('tabindex', '-1');
|
581 |
+
var content = div();
|
582 |
+
content.className = CONTENT_CLASS;
|
583 |
+
content.setAttribute('data-state', 'hidden');
|
584 |
+
setContent(content, instance.props);
|
585 |
+
popper.appendChild(box);
|
586 |
+
box.appendChild(content);
|
587 |
+
onUpdate(instance.props, instance.props);
|
588 |
+
|
589 |
+
function onUpdate(prevProps, nextProps)
|
590 |
+
{
|
591 |
+
var _getChildren = getChildren(popper),
|
592 |
+
box = _getChildren.box,
|
593 |
+
content = _getChildren.content,
|
594 |
+
arrow = _getChildren.arrow;
|
595 |
+
|
596 |
+
if (nextProps.theme) {
|
597 |
+
box.setAttribute('data-theme', nextProps.theme);
|
598 |
+
} else {
|
599 |
+
box.removeAttribute('data-theme');
|
600 |
+
}
|
601 |
+
|
602 |
+
if (typeof nextProps.animation === 'string') {
|
603 |
+
box.setAttribute('data-animation', nextProps.animation);
|
604 |
+
} else {
|
605 |
+
box.removeAttribute('data-animation');
|
606 |
+
}
|
607 |
+
|
608 |
+
if (nextProps.inertia) {
|
609 |
+
box.setAttribute('data-inertia', '');
|
610 |
+
} else {
|
611 |
+
box.removeAttribute('data-inertia');
|
612 |
+
}
|
613 |
+
|
614 |
+
box.style.maxWidth = typeof nextProps.maxWidth === 'number' ? nextProps.maxWidth + "px" : nextProps.maxWidth;
|
615 |
+
|
616 |
+
if (nextProps.role) {
|
617 |
+
box.setAttribute('role', nextProps.role);
|
618 |
+
} else {
|
619 |
+
box.removeAttribute('role');
|
620 |
+
}
|
621 |
+
|
622 |
+
if (prevProps.content !== nextProps.content || prevProps.allowHTML !== nextProps.allowHTML) {
|
623 |
+
setContent(content, instance.props);
|
624 |
+
}
|
625 |
+
|
626 |
+
if (nextProps.arrow) {
|
627 |
+
if (!arrow) {
|
628 |
+
box.appendChild(createArrowElement(nextProps.arrow));
|
629 |
+
} else if (prevProps.arrow !== nextProps.arrow) {
|
630 |
+
box.removeChild(arrow);
|
631 |
+
box.appendChild(createArrowElement(nextProps.arrow));
|
632 |
+
}
|
633 |
+
} else if (arrow) {
|
634 |
+
box.removeChild(arrow);
|
635 |
+
}
|
636 |
+
}
|
637 |
+
|
638 |
+
return {
|
639 |
+
popper: popper,
|
640 |
+
onUpdate: onUpdate
|
641 |
+
};
|
642 |
+
} // Runtime check to identify if the render function is the default one; this
|
643 |
+
// way we can apply default CSS transitions logic and it can be tree-shaken away
|
644 |
+
|
645 |
+
render.$$tippy = true;
|
646 |
+
|
647 |
+
var idCounter = 1;
|
648 |
+
var mouseMoveListeners = []; // Used by `hideAll()`
|
649 |
+
|
650 |
+
var mountedInstances = [];
|
651 |
+
function createTippy(reference, passedProps)
|
652 |
+
{
|
653 |
+
var props = evaluateProps(reference, Object.assign({}, defaultProps, {}, getExtendedPassedProps(removeUndefinedProps(passedProps)))); // ===========================================================================
|
654 |
+
// 🔒 Private members
|
655 |
+
// ===========================================================================
|
656 |
+
|
657 |
+
var showTimeout;
|
658 |
+
var hideTimeout;
|
659 |
+
var scheduleHideAnimationFrame;
|
660 |
+
var isVisibleFromClick = false;
|
661 |
+
var didHideDueToDocumentMouseDown = false;
|
662 |
+
var didTouchMove = false;
|
663 |
+
var ignoreOnFirstUpdate = false;
|
664 |
+
var lastTriggerEvent;
|
665 |
+
var currentTransitionEndListener;
|
666 |
+
var onFirstUpdate;
|
667 |
+
var listeners = [];
|
668 |
+
var debouncedOnMouseMove = debounce(onMouseMove, props.interactiveDebounce);
|
669 |
+
var currentTarget; // ===========================================================================
|
670 |
+
// 🔑 Public members
|
671 |
+
// ===========================================================================
|
672 |
+
|
673 |
+
var id = idCounter++;
|
674 |
+
var popperInstance = null;
|
675 |
+
var plugins = unique(props.plugins);
|
676 |
+
var state = {
|
677 |
+
// Is the instance currently enabled?
|
678 |
+
isEnabled: true,
|
679 |
+
// Is the tippy currently showing and not transitioning out?
|
680 |
+
isVisible: false,
|
681 |
+
// Has the instance been destroyed?
|
682 |
+
isDestroyed: false,
|
683 |
+
// Is the tippy currently mounted to the DOM?
|
684 |
+
isMounted: false,
|
685 |
+
// Has the tippy finished transitioning in?
|
686 |
+
isShown: false
|
687 |
+
};
|
688 |
+
var instance = {
|
689 |
+
// properties
|
690 |
+
id: id,
|
691 |
+
reference: reference,
|
692 |
+
popper: div(),
|
693 |
+
popperInstance: popperInstance,
|
694 |
+
props: props,
|
695 |
+
state: state,
|
696 |
+
plugins: plugins,
|
697 |
+
// methods
|
698 |
+
clearDelayTimeouts: clearDelayTimeouts,
|
699 |
+
setProps: setProps,
|
700 |
+
setContent: setContent,
|
701 |
+
show: show,
|
702 |
+
hide: hide,
|
703 |
+
hideWithInteractivity: hideWithInteractivity,
|
704 |
+
enable: enable,
|
705 |
+
disable: disable,
|
706 |
+
unmount: unmount,
|
707 |
+
destroy: destroy
|
708 |
+
}; // TODO: Investigate why this early return causes a TDZ error in the tests —
|
709 |
+
// it doesn't seem to happen in the browser
|
710 |
+
|
711 |
+
/* istanbul ignore if */
|
712 |
+
|
713 |
+
if (!props.render) {
|
714 |
+
{
|
715 |
+
errorWhen(true, 'render() function has not been supplied.');
|
716 |
+
}
|
717 |
+
|
718 |
+
return instance;
|
719 |
+
} // ===========================================================================
|
720 |
+
// Initial mutations
|
721 |
+
// ===========================================================================
|
722 |
+
|
723 |
+
|
724 |
+
var _props$render = props.render(instance),
|
725 |
+
popper = _props$render.popper,
|
726 |
+
onUpdate = _props$render.onUpdate;
|
727 |
+
|
728 |
+
popper.setAttribute('data-tippy-root', '');
|
729 |
+
popper.id = "tippy-" + instance.id;
|
730 |
+
instance.popper = popper;
|
731 |
+
reference._tippy = instance;
|
732 |
+
popper._tippy = instance;
|
733 |
+
var pluginsHooks = plugins.map(function (plugin) {
|
734 |
+
return plugin.fn(instance);
|
735 |
+
});
|
736 |
+
var hasAriaExpanded = reference.hasAttribute('aria-expanded');
|
737 |
+
addListeners();
|
738 |
+
handleAriaExpandedAttribute();
|
739 |
+
handleStyles();
|
740 |
+
invokeHook('onCreate', [instance]);
|
741 |
+
|
742 |
+
if (props.showOnCreate) {
|
743 |
+
scheduleShow();
|
744 |
+
} // Prevent a tippy with a delay from hiding if the cursor left then returned
|
745 |
+
// before it started hiding
|
746 |
+
|
747 |
+
|
748 |
+
popper.addEventListener('mouseenter', function () {
|
749 |
+
if (instance.props.interactive && instance.state.isVisible) {
|
750 |
+
instance.clearDelayTimeouts();
|
751 |
+
}
|
752 |
+
});
|
753 |
+
popper.addEventListener('mouseleave', function (event) {
|
754 |
+
if (instance.props.interactive && instance.props.trigger.indexOf('mouseenter') >= 0) {
|
755 |
+
getDocument().addEventListener('mousemove', debouncedOnMouseMove);
|
756 |
+
debouncedOnMouseMove(event);
|
757 |
+
}
|
758 |
+
});
|
759 |
+
return instance; // ===========================================================================
|
760 |
+
// 🔒 Private methods
|
761 |
+
// ===========================================================================
|
762 |
+
|
763 |
+
function getNormalizedTouchSettings()
|
764 |
+
{
|
765 |
+
var touch = instance.props.touch;
|
766 |
+
return Array.isArray(touch) ? touch : [touch, 0];
|
767 |
+
}
|
768 |
+
|
769 |
+
function getIsCustomTouchBehavior()
|
770 |
+
{
|
771 |
+
return getNormalizedTouchSettings()[0] === 'hold';
|
772 |
+
}
|
773 |
+
|
774 |
+
function getIsDefaultRenderFn()
|
775 |
+
{
|
776 |
+
var _instance$props$rende;
|
777 |
+
|
778 |
+
// @ts-ignore
|
779 |
+
return !!((_instance$props$rende = instance.props.render) == null ? void 0 : _instance$props$rende.$$tippy);
|
780 |
+
}
|
781 |
+
|
782 |
+
function getCurrentTarget()
|
783 |
+
{
|
784 |
+
return currentTarget || reference;
|
785 |
+
}
|
786 |
+
|
787 |
+
function getDocument()
|
788 |
+
{
|
789 |
+
var parent = getCurrentTarget().parentNode;
|
790 |
+
return parent ? getOwnerDocument(parent) : document;
|
791 |
+
}
|
792 |
+
|
793 |
+
function getDefaultTemplateChildren()
|
794 |
+
{
|
795 |
+
return getChildren(popper);
|
796 |
+
}
|
797 |
+
|
798 |
+
function getDelay(isShow)
|
799 |
+
{
|
800 |
+
// For touch or keyboard input, force `0` delay for UX reasons
|
801 |
+
// Also if the instance is mounted but not visible (transitioning out),
|
802 |
+
// ignore delay
|
803 |
+
if (instance.state.isMounted && !instance.state.isVisible || currentInput.isTouch || lastTriggerEvent && lastTriggerEvent.type === 'focus') {
|
804 |
+
return 0;
|
805 |
+
}
|
806 |
+
|
807 |
+
return getValueAtIndexOrReturn(instance.props.delay, isShow ? 0 : 1, defaultProps.delay);
|
808 |
+
}
|
809 |
+
|
810 |
+
function handleStyles()
|
811 |
+
{
|
812 |
+
popper.style.pointerEvents = instance.props.interactive && instance.state.isVisible ? '' : 'none';
|
813 |
+
popper.style.zIndex = "" + instance.props.zIndex;
|
814 |
+
}
|
815 |
+
|
816 |
+
function invokeHook(hook, args, shouldInvokePropsHook)
|
817 |
+
{
|
818 |
+
if (shouldInvokePropsHook === void 0) {
|
819 |
+
shouldInvokePropsHook = true;
|
820 |
+
}
|
821 |
+
|
822 |
+
pluginsHooks.forEach(function (pluginHooks) {
|
823 |
+
if (pluginHooks[hook]) {
|
824 |
+
pluginHooks[hook].apply(void 0, args);
|
825 |
+
}
|
826 |
+
});
|
827 |
+
|
828 |
+
if (shouldInvokePropsHook) {
|
829 |
+
var _instance$props;
|
830 |
+
|
831 |
+
(_instance$props = instance.props)[hook].apply(_instance$props, args);
|
832 |
+
}
|
833 |
+
}
|
834 |
+
|
835 |
+
function handleAriaContentAttribute()
|
836 |
+
{
|
837 |
+
var aria = instance.props.aria;
|
838 |
+
|
839 |
+
if (!aria.content) {
|
840 |
+
return;
|
841 |
+
}
|
842 |
+
|
843 |
+
var attr = "aria-" + aria.content;
|
844 |
+
var id = popper.id;
|
845 |
+
var nodes = normalizeToArray(instance.props.triggerTarget || reference);
|
846 |
+
nodes.forEach(function (node) {
|
847 |
+
var currentValue = node.getAttribute(attr);
|
848 |
+
|
849 |
+
if (instance.state.isVisible) {
|
850 |
+
node.setAttribute(attr, currentValue ? currentValue + " " + id : id);
|
851 |
+
} else {
|
852 |
+
var nextValue = currentValue && currentValue.replace(id, '').trim();
|
853 |
+
|
854 |
+
if (nextValue) {
|
855 |
+
node.setAttribute(attr, nextValue);
|
856 |
+
} else {
|
857 |
+
node.removeAttribute(attr);
|
858 |
+
}
|
859 |
+
}
|
860 |
+
});
|
861 |
+
}
|
862 |
+
|
863 |
+
function handleAriaExpandedAttribute()
|
864 |
+
{
|
865 |
+
if (hasAriaExpanded || !instance.props.aria.expanded) {
|
866 |
+
return;
|
867 |
+
}
|
868 |
+
|
869 |
+
var nodes = normalizeToArray(instance.props.triggerTarget || reference);
|
870 |
+
nodes.forEach(function (node) {
|
871 |
+
if (instance.props.interactive) {
|
872 |
+
node.setAttribute('aria-expanded', instance.state.isVisible && node === getCurrentTarget() ? 'true' : 'false');
|
873 |
+
} else {
|
874 |
+
node.removeAttribute('aria-expanded');
|
875 |
+
}
|
876 |
+
});
|
877 |
+
}
|
878 |
+
|
879 |
+
function cleanupInteractiveMouseListeners()
|
880 |
+
{
|
881 |
+
getDocument().removeEventListener('mousemove', debouncedOnMouseMove);
|
882 |
+
mouseMoveListeners = mouseMoveListeners.filter(function (listener) {
|
883 |
+
return listener !== debouncedOnMouseMove;
|
884 |
+
});
|
885 |
+
}
|
886 |
+
|
887 |
+
function onDocumentPress(event)
|
888 |
+
{
|
889 |
+
// Moved finger to scroll instead of an intentional tap outside
|
890 |
+
if (currentInput.isTouch) {
|
891 |
+
if (didTouchMove || event.type === 'mousedown') {
|
892 |
+
return;
|
893 |
+
}
|
894 |
+
} // Clicked on interactive popper
|
895 |
+
|
896 |
+
|
897 |
+
if (instance.props.interactive && popper.contains(event.target)) {
|
898 |
+
return;
|
899 |
+
} // Clicked on the event listeners target
|
900 |
+
|
901 |
+
|
902 |
+
if (getCurrentTarget().contains(event.target)) {
|
903 |
+
if (currentInput.isTouch) {
|
904 |
+
return;
|
905 |
+
}
|
906 |
+
|
907 |
+
if (instance.state.isVisible && instance.props.trigger.indexOf('click') >= 0) {
|
908 |
+
return;
|
909 |
+
}
|
910 |
+
} else {
|
911 |
+
invokeHook('onClickOutside', [instance, event]);
|
912 |
+
}
|
913 |
+
|
914 |
+
if (instance.props.hideOnClick === true) {
|
915 |
+
instance.clearDelayTimeouts();
|
916 |
+
instance.hide(); // `mousedown` event is fired right before `focus` if pressing the
|
917 |
+
// currentTarget. This lets a tippy with `focus` trigger know that it
|
918 |
+
// should not show
|
919 |
+
|
920 |
+
didHideDueToDocumentMouseDown = true;
|
921 |
+
setTimeout(function () {
|
922 |
+
didHideDueToDocumentMouseDown = false;
|
923 |
+
}); // The listener gets added in `scheduleShow()`, but this may be hiding it
|
924 |
+
// before it shows, and hide()'s early bail-out behavior can prevent it
|
925 |
+
// from being cleaned up
|
926 |
+
|
927 |
+
if (!instance.state.isMounted) {
|
928 |
+
removeDocumentPress();
|
929 |
+
}
|
930 |
+
}
|
931 |
+
}
|
932 |
+
|
933 |
+
function onTouchMove()
|
934 |
+
{
|
935 |
+
didTouchMove = true;
|
936 |
+
}
|
937 |
+
|
938 |
+
function onTouchStart()
|
939 |
+
{
|
940 |
+
didTouchMove = false;
|
941 |
+
}
|
942 |
+
|
943 |
+
function addDocumentPress()
|
944 |
+
{
|
945 |
+
var doc = getDocument();
|
946 |
+
doc.addEventListener('mousedown', onDocumentPress, true);
|
947 |
+
doc.addEventListener('touchend', onDocumentPress, TOUCH_OPTIONS);
|
948 |
+
doc.addEventListener('touchstart', onTouchStart, TOUCH_OPTIONS);
|
949 |
+
doc.addEventListener('touchmove', onTouchMove, TOUCH_OPTIONS);
|
950 |
+
}
|
951 |
+
|
952 |
+
function removeDocumentPress()
|
953 |
+
{
|
954 |
+
var doc = getDocument();
|
955 |
+
doc.removeEventListener('mousedown', onDocumentPress, true);
|
956 |
+
doc.removeEventListener('touchend', onDocumentPress, TOUCH_OPTIONS);
|
957 |
+
doc.removeEventListener('touchstart', onTouchStart, TOUCH_OPTIONS);
|
958 |
+
doc.removeEventListener('touchmove', onTouchMove, TOUCH_OPTIONS);
|
959 |
+
}
|
960 |
+
|
961 |
+
function onTransitionedOut(duration, callback)
|
962 |
+
{
|
963 |
+
onTransitionEnd(duration, function () {
|
964 |
+
if (!instance.state.isVisible && popper.parentNode && popper.parentNode.contains(popper)) {
|
965 |
+
callback();
|
966 |
+
}
|
967 |
+
});
|
968 |
+
}
|
969 |
+
|
970 |
+
function onTransitionedIn(duration, callback)
|
971 |
+
{
|
972 |
+
onTransitionEnd(duration, callback);
|
973 |
+
}
|
974 |
+
|
975 |
+
function onTransitionEnd(duration, callback)
|
976 |
+
{
|
977 |
+
var box = getDefaultTemplateChildren().box;
|
978 |
+
|
979 |
+
function listener(event)
|
980 |
+
{
|
981 |
+
if (event.target === box) {
|
982 |
+
updateTransitionEndListener(box, 'remove', listener);
|
983 |
+
callback();
|
984 |
+
}
|
985 |
+
} // Make callback synchronous if duration is 0
|
986 |
+
// `transitionend` won't fire otherwise
|
987 |
+
|
988 |
+
|
989 |
+
if (duration === 0) {
|
990 |
+
return callback();
|
991 |
+
}
|
992 |
+
|
993 |
+
updateTransitionEndListener(box, 'remove', currentTransitionEndListener);
|
994 |
+
updateTransitionEndListener(box, 'add', listener);
|
995 |
+
currentTransitionEndListener = listener;
|
996 |
+
}
|
997 |
+
|
998 |
+
function on(eventType, handler, options)
|
999 |
+
{
|
1000 |
+
if (options === void 0) {
|
1001 |
+
options = false;
|
1002 |
+
}
|
1003 |
+
|
1004 |
+
var nodes = normalizeToArray(instance.props.triggerTarget || reference);
|
1005 |
+
nodes.forEach(function (node) {
|
1006 |
+
node.addEventListener(eventType, handler, options);
|
1007 |
+
listeners.push({
|
1008 |
+
node: node,
|
1009 |
+
eventType: eventType,
|
1010 |
+
handler: handler,
|
1011 |
+
options: options
|
1012 |
+
});
|
1013 |
+
});
|
1014 |
+
}
|
1015 |
+
|
1016 |
+
function addListeners()
|
1017 |
+
{
|
1018 |
+
if (getIsCustomTouchBehavior()) {
|
1019 |
+
on('touchstart', onTrigger, {
|
1020 |
+
passive: true
|
1021 |
+
});
|
1022 |
+
on('touchend', onMouseLeave, {
|
1023 |
+
passive: true
|
1024 |
+
});
|
1025 |
+
}
|
1026 |
+
|
1027 |
+
splitBySpaces(instance.props.trigger).forEach(function (eventType) {
|
1028 |
+
if (eventType === 'manual') {
|
1029 |
+
return;
|
1030 |
+
}
|
1031 |
+
|
1032 |
+
on(eventType, onTrigger);
|
1033 |
+
|
1034 |
+
switch (eventType) {
|
1035 |
+
case 'mouseenter':
|
1036 |
+
on('mouseleave', onMouseLeave);
|
1037 |
+
break;
|
1038 |
+
|
1039 |
+
case 'focus':
|
1040 |
+
on(isIE ? 'focusout' : 'blur', onBlurOrFocusOut);
|
1041 |
+
break;
|
1042 |
+
|
1043 |
+
case 'focusin':
|
1044 |
+
on('focusout', onBlurOrFocusOut);
|
1045 |
+
break;
|
1046 |
+
}
|
1047 |
+
});
|
1048 |
+
}
|
1049 |
+
|
1050 |
+
function removeListeners()
|
1051 |
+
{
|
1052 |
+
listeners.forEach(function (_ref) {
|
1053 |
+
var node = _ref.node,
|
1054 |
+
eventType = _ref.eventType,
|
1055 |
+
handler = _ref.handler,
|
1056 |
+
options = _ref.options;
|
1057 |
+
node.removeEventListener(eventType, handler, options);
|
1058 |
+
});
|
1059 |
+
listeners = [];
|
1060 |
+
}
|
1061 |
+
|
1062 |
+
function onTrigger(event)
|
1063 |
+
{
|
1064 |
+
var _lastTriggerEvent;
|
1065 |
+
|
1066 |
+
var shouldScheduleClickHide = false;
|
1067 |
+
|
1068 |
+
if (!instance.state.isEnabled || isEventListenerStopped(event) || didHideDueToDocumentMouseDown) {
|
1069 |
+
return;
|
1070 |
+
}
|
1071 |
+
|
1072 |
+
var wasFocused = ((_lastTriggerEvent = lastTriggerEvent) == null ? void 0 : _lastTriggerEvent.type) === 'focus';
|
1073 |
+
lastTriggerEvent = event;
|
1074 |
+
currentTarget = event.currentTarget;
|
1075 |
+
handleAriaExpandedAttribute();
|
1076 |
+
|
1077 |
+
if (!instance.state.isVisible && isMouseEvent(event)) {
|
1078 |
+
// If scrolling, `mouseenter` events can be fired if the cursor lands
|
1079 |
+
// over a new target, but `mousemove` events don't get fired. This
|
1080 |
+
// causes interactive tooltips to get stuck open until the cursor is
|
1081 |
+
// moved
|
1082 |
+
mouseMoveListeners.forEach(function (listener) {
|
1083 |
+
return listener(event);
|
1084 |
+
});
|
1085 |
+
} // Toggle show/hide when clicking click-triggered tooltips
|
1086 |
+
|
1087 |
+
|
1088 |
+
if (event.type === 'click' && (instance.props.trigger.indexOf('mouseenter') < 0 || isVisibleFromClick) && instance.props.hideOnClick !== false && instance.state.isVisible) {
|
1089 |
+
shouldScheduleClickHide = true;
|
1090 |
+
} else {
|
1091 |
+
scheduleShow(event);
|
1092 |
+
}
|
1093 |
+
|
1094 |
+
if (event.type === 'click') {
|
1095 |
+
isVisibleFromClick = !shouldScheduleClickHide;
|
1096 |
+
}
|
1097 |
+
|
1098 |
+
if (shouldScheduleClickHide && !wasFocused) {
|
1099 |
+
scheduleHide(event);
|
1100 |
+
}
|
1101 |
+
}
|
1102 |
+
|
1103 |
+
function onMouseMove(event)
|
1104 |
+
{
|
1105 |
+
var target = event.target;
|
1106 |
+
var isCursorOverReferenceOrPopper = getCurrentTarget().contains(target) || popper.contains(target);
|
1107 |
+
|
1108 |
+
if (event.type === 'mousemove' && isCursorOverReferenceOrPopper) {
|
1109 |
+
return;
|
1110 |
+
}
|
1111 |
+
|
1112 |
+
var popperTreeData = getNestedPopperTree().concat(popper).map(function (popper) {
|
1113 |
+
var _instance$popperInsta;
|
1114 |
+
|
1115 |
+
var instance = popper._tippy;
|
1116 |
+
var state = (_instance$popperInsta = instance.popperInstance) == null ? void 0 : _instance$popperInsta.state;
|
1117 |
+
|
1118 |
+
if (state) {
|
1119 |
+
return {
|
1120 |
+
popperRect: popper.getBoundingClientRect(),
|
1121 |
+
popperState: state,
|
1122 |
+
props: props
|
1123 |
+
};
|
1124 |
+
}
|
1125 |
+
|
1126 |
+
return null;
|
1127 |
+
}).filter(Boolean);
|
1128 |
+
|
1129 |
+
if (isCursorOutsideInteractiveBorder(popperTreeData, event)) {
|
1130 |
+
cleanupInteractiveMouseListeners();
|
1131 |
+
scheduleHide(event);
|
1132 |
+
}
|
1133 |
+
}
|
1134 |
+
|
1135 |
+
function onMouseLeave(event)
|
1136 |
+
{
|
1137 |
+
var shouldBail = isEventListenerStopped(event) || instance.props.trigger.indexOf('click') >= 0 && isVisibleFromClick;
|
1138 |
+
|
1139 |
+
if (shouldBail) {
|
1140 |
+
return;
|
1141 |
+
}
|
1142 |
+
|
1143 |
+
if (instance.props.interactive) {
|
1144 |
+
instance.hideWithInteractivity(event);
|
1145 |
+
return;
|
1146 |
+
}
|
1147 |
+
|
1148 |
+
scheduleHide(event);
|
1149 |
+
}
|
1150 |
+
|
1151 |
+
function onBlurOrFocusOut(event)
|
1152 |
+
{
|
1153 |
+
if (instance.props.trigger.indexOf('focusin') < 0 && event.target !== getCurrentTarget()) {
|
1154 |
+
return;
|
1155 |
+
} // If focus was moved to within the popper
|
1156 |
+
|
1157 |
+
|
1158 |
+
if (instance.props.interactive && event.relatedTarget && popper.contains(event.relatedTarget)) {
|
1159 |
+
return;
|
1160 |
+
}
|
1161 |
+
|
1162 |
+
scheduleHide(event);
|
1163 |
+
}
|
1164 |
+
|
1165 |
+
function isEventListenerStopped(event)
|
1166 |
+
{
|
1167 |
+
return currentInput.isTouch ? getIsCustomTouchBehavior() !== event.type.indexOf('touch') >= 0 : false;
|
1168 |
+
}
|
1169 |
+
|
1170 |
+
function createPopperInstance()
|
1171 |
+
{
|
1172 |
+
destroyPopperInstance();
|
1173 |
+
var _instance$props2 = instance.props,
|
1174 |
+
popperOptions = _instance$props2.popperOptions,
|
1175 |
+
placement = _instance$props2.placement,
|
1176 |
+
offset = _instance$props2.offset,
|
1177 |
+
getReferenceClientRect = _instance$props2.getReferenceClientRect,
|
1178 |
+
moveTransition = _instance$props2.moveTransition;
|
1179 |
+
var arrow = getIsDefaultRenderFn() ? getChildren(popper).arrow : null;
|
1180 |
+
var computedReference = getReferenceClientRect ? {
|
1181 |
+
getBoundingClientRect: getReferenceClientRect,
|
1182 |
+
contextElement: getReferenceClientRect.contextElement || getCurrentTarget()
|
1183 |
+
} : reference;
|
1184 |
+
var tippyModifier = {
|
1185 |
+
name: '$$tippy',
|
1186 |
+
enabled: true,
|
1187 |
+
phase: 'beforeWrite',
|
1188 |
+
requires: ['computeStyles'],
|
1189 |
+
fn: function fn(_ref2)
|
1190 |
+
{
|
1191 |
+
var state = _ref2.state;
|
1192 |
+
|
1193 |
+
if (getIsDefaultRenderFn()) {
|
1194 |
+
var _getDefaultTemplateCh = getDefaultTemplateChildren(),
|
1195 |
+
box = _getDefaultTemplateCh.box;
|
1196 |
+
|
1197 |
+
['placement', 'reference-hidden', 'escaped'].forEach(function (attr) {
|
1198 |
+
if (attr === 'placement') {
|
1199 |
+
box.setAttribute('data-placement', state.placement);
|
1200 |
+
} else {
|
1201 |
+
if (state.attributes.popper["data-popper-" + attr]) {
|
1202 |
+
box.setAttribute("data-" + attr, '');
|
1203 |
+
} else {
|
1204 |
+
box.removeAttribute("data-" + attr);
|
1205 |
+
}
|
1206 |
+
}
|
1207 |
+
});
|
1208 |
+
state.attributes.popper = {};
|
1209 |
+
}
|
1210 |
+
}
|
1211 |
+
};
|
1212 |
+
var modifiers = [{
|
1213 |
+
name: 'offset',
|
1214 |
+
options: {
|
1215 |
+
offset: offset
|
1216 |
+
}
|
1217 |
+
}, {
|
1218 |
+
name: 'preventOverflow',
|
1219 |
+
options: {
|
1220 |
+
padding: {
|
1221 |
+
top: 2,
|
1222 |
+
bottom: 2,
|
1223 |
+
left: 5,
|
1224 |
+
right: 5
|
1225 |
+
}
|
1226 |
+
}
|
1227 |
+
}, {
|
1228 |
+
name: 'flip',
|
1229 |
+
options: {
|
1230 |
+
padding: 5
|
1231 |
+
}
|
1232 |
+
}, {
|
1233 |
+
name: 'computeStyles',
|
1234 |
+
options: {
|
1235 |
+
adaptive: !moveTransition
|
1236 |
+
}
|
1237 |
+
}, tippyModifier];
|
1238 |
+
|
1239 |
+
if (getIsDefaultRenderFn() && arrow) {
|
1240 |
+
modifiers.push({
|
1241 |
+
name: 'arrow',
|
1242 |
+
options: {
|
1243 |
+
element: arrow,
|
1244 |
+
padding: 3
|
1245 |
+
}
|
1246 |
+
});
|
1247 |
+
}
|
1248 |
+
|
1249 |
+
modifiers.push.apply(modifiers, (popperOptions == null ? void 0 : popperOptions.modifiers) || []);
|
1250 |
+
instance.popperInstance = core.createPopper(computedReference, popper, Object.assign({}, popperOptions, {
|
1251 |
+
placement: placement,
|
1252 |
+
onFirstUpdate: onFirstUpdate,
|
1253 |
+
modifiers: modifiers
|
1254 |
+
}));
|
1255 |
+
}
|
1256 |
+
|
1257 |
+
function destroyPopperInstance()
|
1258 |
+
{
|
1259 |
+
if (instance.popperInstance) {
|
1260 |
+
instance.popperInstance.destroy();
|
1261 |
+
instance.popperInstance = null;
|
1262 |
+
}
|
1263 |
+
}
|
1264 |
+
|
1265 |
+
function mount()
|
1266 |
+
{
|
1267 |
+
var appendTo = instance.props.appendTo;
|
1268 |
+
var parentNode; // By default, we'll append the popper to the triggerTargets's parentNode so
|
1269 |
+
// it's directly after the reference element so the elements inside the
|
1270 |
+
// tippy can be tabbed to
|
1271 |
+
// If there are clipping issues, the user can specify a different appendTo
|
1272 |
+
// and ensure focus management is handled correctly manually
|
1273 |
+
|
1274 |
+
var node = getCurrentTarget();
|
1275 |
+
|
1276 |
+
if (instance.props.interactive && appendTo === defaultProps.appendTo || appendTo === 'parent') {
|
1277 |
+
parentNode = node.parentNode;
|
1278 |
+
} else {
|
1279 |
+
parentNode = invokeWithArgsOrReturn(appendTo, [node]);
|
1280 |
+
} // The popper element needs to exist on the DOM before its position can be
|
1281 |
+
// updated as Popper needs to read its dimensions
|
1282 |
+
|
1283 |
+
|
1284 |
+
if (!parentNode.contains(popper)) {
|
1285 |
+
parentNode.appendChild(popper);
|
1286 |
+
}
|
1287 |
+
|
1288 |
+
createPopperInstance();
|
1289 |
+
/* istanbul ignore else */
|
1290 |
+
|
1291 |
+
{
|
1292 |
+
// Accessibility check
|
1293 |
+
warnWhen(instance.props.interactive && appendTo === defaultProps.appendTo && node.nextElementSibling !== popper, ['Interactive tippy element may not be accessible via keyboard', 'navigation because it is not directly after the reference element', 'in the DOM source order.', '\n\n', 'Using a wrapper <div> or <span> tag around the reference element', 'solves this by creating a new parentNode context.', '\n\n', 'Specifying `appendTo: document.body` silences this warning, but it', 'assumes you are using a focus management solution to handle', 'keyboard navigation.', '\n\n', 'See: https://atomiks.github.io/tippyjs/v6/accessibility/#interactivity'].join(' '));
|
1294 |
+
}
|
1295 |
+
}
|
1296 |
+
|
1297 |
+
function getNestedPopperTree()
|
1298 |
+
{
|
1299 |
+
return arrayFrom(popper.querySelectorAll('[data-tippy-root]'));
|
1300 |
+
}
|
1301 |
+
|
1302 |
+
function scheduleShow(event)
|
1303 |
+
{
|
1304 |
+
instance.clearDelayTimeouts();
|
1305 |
+
|
1306 |
+
if (event) {
|
1307 |
+
invokeHook('onTrigger', [instance, event]);
|
1308 |
+
}
|
1309 |
+
|
1310 |
+
addDocumentPress();
|
1311 |
+
var delay = getDelay(true);
|
1312 |
+
|
1313 |
+
var _getNormalizedTouchSe = getNormalizedTouchSettings(),
|
1314 |
+
touchValue = _getNormalizedTouchSe[0],
|
1315 |
+
touchDelay = _getNormalizedTouchSe[1];
|
1316 |
+
|
1317 |
+
if (currentInput.isTouch && touchValue === 'hold' && touchDelay) {
|
1318 |
+
delay = touchDelay;
|
1319 |
+
}
|
1320 |
+
|
1321 |
+
if (delay) {
|
1322 |
+
showTimeout = setTimeout(function () {
|
1323 |
+
instance.show();
|
1324 |
+
}, delay);
|
1325 |
+
} else {
|
1326 |
+
instance.show();
|
1327 |
+
}
|
1328 |
+
}
|
1329 |
+
|
1330 |
+
function scheduleHide(event)
|
1331 |
+
{
|
1332 |
+
instance.clearDelayTimeouts();
|
1333 |
+
invokeHook('onUntrigger', [instance, event]);
|
1334 |
+
|
1335 |
+
if (!instance.state.isVisible) {
|
1336 |
+
removeDocumentPress();
|
1337 |
+
return;
|
1338 |
+
} // For interactive tippies, scheduleHide is added to a document.body handler
|
1339 |
+
// from onMouseLeave so must intercept scheduled hides from mousemove/leave
|
1340 |
+
// events when trigger contains mouseenter and click, and the tip is
|
1341 |
+
// currently shown as a result of a click.
|
1342 |
+
|
1343 |
+
|
1344 |
+
if (instance.props.trigger.indexOf('mouseenter') >= 0 && instance.props.trigger.indexOf('click') >= 0 && ['mouseleave', 'mousemove'].indexOf(event.type) >= 0 && isVisibleFromClick) {
|
1345 |
+
return;
|
1346 |
+
}
|
1347 |
+
|
1348 |
+
var delay = getDelay(false);
|
1349 |
+
|
1350 |
+
if (delay) {
|
1351 |
+
hideTimeout = setTimeout(function () {
|
1352 |
+
if (instance.state.isVisible) {
|
1353 |
+
instance.hide();
|
1354 |
+
}
|
1355 |
+
}, delay);
|
1356 |
+
} else {
|
1357 |
+
// Fixes a `transitionend` problem when it fires 1 frame too
|
1358 |
+
// late sometimes, we don't want hide() to be called.
|
1359 |
+
scheduleHideAnimationFrame = requestAnimationFrame(function () {
|
1360 |
+
instance.hide();
|
1361 |
+
});
|
1362 |
+
}
|
1363 |
+
} // ===========================================================================
|
1364 |
+
// 🔑 Public methods
|
1365 |
+
// ===========================================================================
|
1366 |
+
|
1367 |
+
|
1368 |
+
function enable()
|
1369 |
+
{
|
1370 |
+
instance.state.isEnabled = true;
|
1371 |
+
}
|
1372 |
+
|
1373 |
+
function disable()
|
1374 |
+
{
|
1375 |
+
// Disabling the instance should also hide it
|
1376 |
+
// https://github.com/atomiks/tippy.js-react/issues/106
|
1377 |
+
instance.hide();
|
1378 |
+
instance.state.isEnabled = false;
|
1379 |
+
}
|
1380 |
+
|
1381 |
+
function clearDelayTimeouts()
|
1382 |
+
{
|
1383 |
+
clearTimeout(showTimeout);
|
1384 |
+
clearTimeout(hideTimeout);
|
1385 |
+
cancelAnimationFrame(scheduleHideAnimationFrame);
|
1386 |
+
}
|
1387 |
+
|
1388 |
+
function setProps(partialProps)
|
1389 |
+
{
|
1390 |
+
/* istanbul ignore else */
|
1391 |
+
{
|
1392 |
+
warnWhen(instance.state.isDestroyed, createMemoryLeakWarning('setProps'));
|
1393 |
+
}
|
1394 |
+
|
1395 |
+
if (instance.state.isDestroyed) {
|
1396 |
+
return;
|
1397 |
+
}
|
1398 |
+
|
1399 |
+
invokeHook('onBeforeUpdate', [instance, partialProps]);
|
1400 |
+
removeListeners();
|
1401 |
+
var prevProps = instance.props;
|
1402 |
+
var nextProps = evaluateProps(reference, Object.assign({}, instance.props, {}, partialProps, {
|
1403 |
+
ignoreAttributes: true
|
1404 |
+
}));
|
1405 |
+
instance.props = nextProps;
|
1406 |
+
addListeners();
|
1407 |
+
|
1408 |
+
if (prevProps.interactiveDebounce !== nextProps.interactiveDebounce) {
|
1409 |
+
cleanupInteractiveMouseListeners();
|
1410 |
+
debouncedOnMouseMove = debounce(onMouseMove, nextProps.interactiveDebounce);
|
1411 |
+
} // Ensure stale aria-expanded attributes are removed
|
1412 |
+
|
1413 |
+
|
1414 |
+
if (prevProps.triggerTarget && !nextProps.triggerTarget) {
|
1415 |
+
normalizeToArray(prevProps.triggerTarget).forEach(function (node) {
|
1416 |
+
node.removeAttribute('aria-expanded');
|
1417 |
+
});
|
1418 |
+
} else if (nextProps.triggerTarget) {
|
1419 |
+
reference.removeAttribute('aria-expanded');
|
1420 |
+
}
|
1421 |
+
|
1422 |
+
handleAriaExpandedAttribute();
|
1423 |
+
handleStyles();
|
1424 |
+
|
1425 |
+
if (onUpdate) {
|
1426 |
+
onUpdate(prevProps, nextProps);
|
1427 |
+
}
|
1428 |
+
|
1429 |
+
if (instance.popperInstance) {
|
1430 |
+
createPopperInstance(); // Fixes an issue with nested tippies if they are all getting re-rendered,
|
1431 |
+
// and the nested ones get re-rendered first.
|
1432 |
+
// https://github.com/atomiks/tippyjs-react/issues/177
|
1433 |
+
// TODO: find a cleaner / more efficient solution(!)
|
1434 |
+
|
1435 |
+
getNestedPopperTree().forEach(function (nestedPopper) {
|
1436 |
+
// React (and other UI libs likely) requires a rAF wrapper as it flushes
|
1437 |
+
// its work in one
|
1438 |
+
requestAnimationFrame(nestedPopper._tippy.popperInstance.forceUpdate);
|
1439 |
+
});
|
1440 |
+
}
|
1441 |
+
|
1442 |
+
invokeHook('onAfterUpdate', [instance, partialProps]);
|
1443 |
+
}
|
1444 |
+
|
1445 |
+
function setContent(content)
|
1446 |
+
{
|
1447 |
+
instance.setProps({
|
1448 |
+
content: content
|
1449 |
+
});
|
1450 |
+
}
|
1451 |
+
|
1452 |
+
function show()
|
1453 |
+
{
|
1454 |
+
/* istanbul ignore else */
|
1455 |
+
{
|
1456 |
+
warnWhen(instance.state.isDestroyed, createMemoryLeakWarning('show'));
|
1457 |
+
} // Early bail-out
|
1458 |
+
|
1459 |
+
|
1460 |
+
var isAlreadyVisible = instance.state.isVisible;
|
1461 |
+
var isDestroyed = instance.state.isDestroyed;
|
1462 |
+
var isDisabled = !instance.state.isEnabled;
|
1463 |
+
var isTouchAndTouchDisabled = currentInput.isTouch && !instance.props.touch;
|
1464 |
+
var duration = getValueAtIndexOrReturn(instance.props.duration, 0, defaultProps.duration);
|
1465 |
+
|
1466 |
+
if (isAlreadyVisible || isDestroyed || isDisabled || isTouchAndTouchDisabled) {
|
1467 |
+
return;
|
1468 |
+
} // Normalize `disabled` behavior across browsers.
|
1469 |
+
// Firefox allows events on disabled elements, but Chrome doesn't.
|
1470 |
+
// Using a wrapper element (i.e. <span>) is recommended.
|
1471 |
+
|
1472 |
+
|
1473 |
+
if (getCurrentTarget().hasAttribute('disabled')) {
|
1474 |
+
return;
|
1475 |
+
}
|
1476 |
+
|
1477 |
+
invokeHook('onShow', [instance], false);
|
1478 |
+
|
1479 |
+
if (instance.props.onShow(instance) === false) {
|
1480 |
+
return;
|
1481 |
+
}
|
1482 |
+
|
1483 |
+
instance.state.isVisible = true;
|
1484 |
+
|
1485 |
+
if (getIsDefaultRenderFn()) {
|
1486 |
+
popper.style.visibility = 'visible';
|
1487 |
+
}
|
1488 |
+
|
1489 |
+
handleStyles();
|
1490 |
+
addDocumentPress();
|
1491 |
+
|
1492 |
+
if (!instance.state.isMounted) {
|
1493 |
+
popper.style.transition = 'none';
|
1494 |
+
} // If flipping to the opposite side after hiding at least once, the
|
1495 |
+
// animation will use the wrong placement without resetting the duration
|
1496 |
+
|
1497 |
+
|
1498 |
+
if (getIsDefaultRenderFn()) {
|
1499 |
+
var _getDefaultTemplateCh2 = getDefaultTemplateChildren(),
|
1500 |
+
box = _getDefaultTemplateCh2.box,
|
1501 |
+
content = _getDefaultTemplateCh2.content;
|
1502 |
+
|
1503 |
+
setTransitionDuration([box, content], 0);
|
1504 |
+
}
|
1505 |
+
|
1506 |
+
onFirstUpdate = function onFirstUpdate()
|
1507 |
+
{
|
1508 |
+
if (!instance.state.isVisible || ignoreOnFirstUpdate) {
|
1509 |
+
return;
|
1510 |
+
}
|
1511 |
+
|
1512 |
+
ignoreOnFirstUpdate = true; // reflow
|
1513 |
+
|
1514 |
+
void popper.offsetHeight;
|
1515 |
+
popper.style.transition = instance.props.moveTransition;
|
1516 |
+
|
1517 |
+
if (getIsDefaultRenderFn() && instance.props.animation) {
|
1518 |
+
var _getDefaultTemplateCh3 = getDefaultTemplateChildren(),
|
1519 |
+
_box = _getDefaultTemplateCh3.box,
|
1520 |
+
_content = _getDefaultTemplateCh3.content;
|
1521 |
+
|
1522 |
+
setTransitionDuration([_box, _content], duration);
|
1523 |
+
setVisibilityState([_box, _content], 'visible');
|
1524 |
+
}
|
1525 |
+
|
1526 |
+
handleAriaContentAttribute();
|
1527 |
+
handleAriaExpandedAttribute();
|
1528 |
+
pushIfUnique(mountedInstances, instance);
|
1529 |
+
instance.state.isMounted = true;
|
1530 |
+
invokeHook('onMount', [instance]);
|
1531 |
+
|
1532 |
+
if (instance.props.animation && getIsDefaultRenderFn()) {
|
1533 |
+
onTransitionedIn(duration, function () {
|
1534 |
+
instance.state.isShown = true;
|
1535 |
+
invokeHook('onShown', [instance]);
|
1536 |
+
});
|
1537 |
+
}
|
1538 |
+
};
|
1539 |
+
|
1540 |
+
mount();
|
1541 |
+
}
|
1542 |
+
|
1543 |
+
function hide()
|
1544 |
+
{
|
1545 |
+
/* istanbul ignore else */
|
1546 |
+
{
|
1547 |
+
warnWhen(instance.state.isDestroyed, createMemoryLeakWarning('hide'));
|
1548 |
+
} // Early bail-out
|
1549 |
+
|
1550 |
+
|
1551 |
+
var isAlreadyHidden = !instance.state.isVisible;
|
1552 |
+
var isDestroyed = instance.state.isDestroyed;
|
1553 |
+
var isDisabled = !instance.state.isEnabled;
|
1554 |
+
var duration = getValueAtIndexOrReturn(instance.props.duration, 1, defaultProps.duration);
|
1555 |
+
|
1556 |
+
if (isAlreadyHidden || isDestroyed || isDisabled) {
|
1557 |
+
return;
|
1558 |
+
}
|
1559 |
+
|
1560 |
+
invokeHook('onHide', [instance], false);
|
1561 |
+
|
1562 |
+
if (instance.props.onHide(instance) === false) {
|
1563 |
+
return;
|
1564 |
+
}
|
1565 |
+
|
1566 |
+
instance.state.isVisible = false;
|
1567 |
+
instance.state.isShown = false;
|
1568 |
+
ignoreOnFirstUpdate = false;
|
1569 |
+
isVisibleFromClick = false;
|
1570 |
+
|
1571 |
+
if (getIsDefaultRenderFn()) {
|
1572 |
+
popper.style.visibility = 'hidden';
|
1573 |
+
}
|
1574 |
+
|
1575 |
+
cleanupInteractiveMouseListeners();
|
1576 |
+
removeDocumentPress();
|
1577 |
+
handleStyles();
|
1578 |
+
|
1579 |
+
if (getIsDefaultRenderFn()) {
|
1580 |
+
var _getDefaultTemplateCh4 = getDefaultTemplateChildren(),
|
1581 |
+
box = _getDefaultTemplateCh4.box,
|
1582 |
+
content = _getDefaultTemplateCh4.content;
|
1583 |
+
|
1584 |
+
if (instance.props.animation) {
|
1585 |
+
setTransitionDuration([box, content], duration);
|
1586 |
+
setVisibilityState([box, content], 'hidden');
|
1587 |
+
}
|
1588 |
+
}
|
1589 |
+
|
1590 |
+
handleAriaContentAttribute();
|
1591 |
+
handleAriaExpandedAttribute();
|
1592 |
+
|
1593 |
+
if (instance.props.animation) {
|
1594 |
+
if (getIsDefaultRenderFn()) {
|
1595 |
+
onTransitionedOut(duration, instance.unmount);
|
1596 |
+
}
|
1597 |
+
} else {
|
1598 |
+
instance.unmount();
|
1599 |
+
}
|
1600 |
+
}
|
1601 |
+
|
1602 |
+
function hideWithInteractivity(event)
|
1603 |
+
{
|
1604 |
+
/* istanbul ignore else */
|
1605 |
+
{
|
1606 |
+
warnWhen(instance.state.isDestroyed, createMemoryLeakWarning('hideWithInteractivity'));
|
1607 |
+
}
|
1608 |
+
|
1609 |
+
getDocument().addEventListener('mousemove', debouncedOnMouseMove);
|
1610 |
+
pushIfUnique(mouseMoveListeners, debouncedOnMouseMove);
|
1611 |
+
debouncedOnMouseMove(event);
|
1612 |
+
}
|
1613 |
+
|
1614 |
+
function unmount()
|
1615 |
+
{
|
1616 |
+
/* istanbul ignore else */
|
1617 |
+
{
|
1618 |
+
warnWhen(instance.state.isDestroyed, createMemoryLeakWarning('unmount'));
|
1619 |
+
}
|
1620 |
+
|
1621 |
+
if (instance.state.isVisible) {
|
1622 |
+
instance.hide();
|
1623 |
+
}
|
1624 |
+
|
1625 |
+
if (!instance.state.isMounted) {
|
1626 |
+
return;
|
1627 |
+
}
|
1628 |
+
|
1629 |
+
destroyPopperInstance(); // If a popper is not interactive, it will be appended outside the popper
|
1630 |
+
// tree by default. This seems mainly for interactive tippies, but we should
|
1631 |
+
// find a workaround if possible
|
1632 |
+
|
1633 |
+
getNestedPopperTree().forEach(function (nestedPopper) {
|
1634 |
+
nestedPopper._tippy.unmount();
|
1635 |
+
});
|
1636 |
+
|
1637 |
+
if (popper.parentNode) {
|
1638 |
+
popper.parentNode.removeChild(popper);
|
1639 |
+
}
|
1640 |
+
|
1641 |
+
mountedInstances = mountedInstances.filter(function (i) {
|
1642 |
+
return i !== instance;
|
1643 |
+
});
|
1644 |
+
instance.state.isMounted = false;
|
1645 |
+
invokeHook('onHidden', [instance]);
|
1646 |
+
}
|
1647 |
+
|
1648 |
+
function destroy()
|
1649 |
+
{
|
1650 |
+
/* istanbul ignore else */
|
1651 |
+
{
|
1652 |
+
warnWhen(instance.state.isDestroyed, createMemoryLeakWarning('destroy'));
|
1653 |
+
}
|
1654 |
+
|
1655 |
+
if (instance.state.isDestroyed) {
|
1656 |
+
return;
|
1657 |
+
}
|
1658 |
+
|
1659 |
+
instance.clearDelayTimeouts();
|
1660 |
+
instance.unmount();
|
1661 |
+
removeListeners();
|
1662 |
+
delete reference._tippy;
|
1663 |
+
instance.state.isDestroyed = true;
|
1664 |
+
invokeHook('onDestroy', [instance]);
|
1665 |
+
}
|
1666 |
+
}
|
1667 |
+
|
1668 |
+
function tippy(targets, optionalProps)
|
1669 |
+
{
|
1670 |
+
if (optionalProps === void 0) {
|
1671 |
+
optionalProps = {};
|
1672 |
+
}
|
1673 |
+
|
1674 |
+
var plugins = defaultProps.plugins.concat(optionalProps.plugins || []);
|
1675 |
+
/* istanbul ignore else */
|
1676 |
+
|
1677 |
+
{
|
1678 |
+
validateTargets(targets);
|
1679 |
+
validateProps(optionalProps, plugins);
|
1680 |
+
}
|
1681 |
+
|
1682 |
+
bindGlobalEventListeners();
|
1683 |
+
var passedProps = Object.assign({}, optionalProps, {
|
1684 |
+
plugins: plugins
|
1685 |
+
});
|
1686 |
+
var elements = getArrayOfElements(targets);
|
1687 |
+
/* istanbul ignore else */
|
1688 |
+
|
1689 |
+
{
|
1690 |
+
var isSingleContentElement = isElement(passedProps.content);
|
1691 |
+
var isMoreThanOneReferenceElement = elements.length > 1;
|
1692 |
+
warnWhen(isSingleContentElement && isMoreThanOneReferenceElement, ['tippy() was passed an Element as the `content` prop, but more than', 'one tippy instance was created by this invocation. This means the', 'content element will only be appended to the last tippy instance.', '\n\n', 'Instead, pass the .innerHTML of the element, or use a function that', 'returns a cloned version of the element instead.', '\n\n', '1) content: element.innerHTML\n', '2) content: () => element.cloneNode(true)'].join(' '));
|
1693 |
+
}
|
1694 |
+
|
1695 |
+
var instances = elements.reduce(function (acc, reference) {
|
1696 |
+
var instance = reference && createTippy(reference, passedProps);
|
1697 |
+
|
1698 |
+
if (instance) {
|
1699 |
+
acc.push(instance);
|
1700 |
+
}
|
1701 |
+
|
1702 |
+
return acc;
|
1703 |
+
}, []);
|
1704 |
+
return isElement(targets) ? instances[0] : instances;
|
1705 |
+
}
|
1706 |
+
|
1707 |
+
tippy.defaultProps = defaultProps;
|
1708 |
+
tippy.setDefaultProps = setDefaultProps;
|
1709 |
+
tippy.currentInput = currentInput;
|
1710 |
+
var hideAll = function hideAll(_temp)
|
1711 |
+
{
|
1712 |
+
var _ref = _temp === void 0 ? {} : _temp,
|
1713 |
+
excludedReferenceOrInstance = _ref.exclude,
|
1714 |
+
duration = _ref.duration;
|
1715 |
+
|
1716 |
+
mountedInstances.forEach(function (instance) {
|
1717 |
+
var isExcluded = false;
|
1718 |
+
|
1719 |
+
if (excludedReferenceOrInstance) {
|
1720 |
+
isExcluded = isReferenceElement(excludedReferenceOrInstance) ? instance.reference === excludedReferenceOrInstance : instance.popper === excludedReferenceOrInstance.popper;
|
1721 |
+
}
|
1722 |
+
|
1723 |
+
if (!isExcluded) {
|
1724 |
+
var originalDuration = instance.props.duration;
|
1725 |
+
instance.setProps({
|
1726 |
+
duration: duration
|
1727 |
+
});
|
1728 |
+
instance.hide();
|
1729 |
+
|
1730 |
+
if (!instance.state.isDestroyed) {
|
1731 |
+
instance.setProps({
|
1732 |
+
duration: originalDuration
|
1733 |
+
});
|
1734 |
+
}
|
1735 |
+
}
|
1736 |
+
});
|
1737 |
+
};
|
1738 |
+
|
1739 |
+
var createSingleton = function createSingleton(tippyInstances, optionalProps)
|
1740 |
+
{
|
1741 |
+
if (optionalProps === void 0) {
|
1742 |
+
optionalProps = {};
|
1743 |
+
}
|
1744 |
+
|
1745 |
+
/* istanbul ignore else */
|
1746 |
+
{
|
1747 |
+
errorWhen(!Array.isArray(tippyInstances), ['The first argument passed to createSingleton() must be an array of', 'tippy instances. The passed value was', String(tippyInstances)].join(' '));
|
1748 |
+
}
|
1749 |
+
|
1750 |
+
var individualInstances = tippyInstances;
|
1751 |
+
var references = [];
|
1752 |
+
var currentTarget;
|
1753 |
+
var overrides = optionalProps.overrides;
|
1754 |
+
var interceptSetPropsCleanups = [];
|
1755 |
+
|
1756 |
+
function setReferences()
|
1757 |
+
{
|
1758 |
+
references = individualInstances.map(function (instance) {
|
1759 |
+
return instance.reference;
|
1760 |
+
});
|
1761 |
+
}
|
1762 |
+
|
1763 |
+
function enableInstances(isEnabled)
|
1764 |
+
{
|
1765 |
+
individualInstances.forEach(function (instance) {
|
1766 |
+
if (isEnabled) {
|
1767 |
+
instance.enable();
|
1768 |
+
} else {
|
1769 |
+
instance.disable();
|
1770 |
+
}
|
1771 |
+
});
|
1772 |
+
}
|
1773 |
+
|
1774 |
+
function interceptSetProps(singleton)
|
1775 |
+
{
|
1776 |
+
return individualInstances.map(function (instance) {
|
1777 |
+
var originalSetProps = instance.setProps;
|
1778 |
+
|
1779 |
+
instance.setProps = function (props) {
|
1780 |
+
originalSetProps(props);
|
1781 |
+
|
1782 |
+
if (instance.reference === currentTarget) {
|
1783 |
+
singleton.setProps(props);
|
1784 |
+
}
|
1785 |
+
};
|
1786 |
+
|
1787 |
+
return function () {
|
1788 |
+
instance.setProps = originalSetProps;
|
1789 |
+
};
|
1790 |
+
});
|
1791 |
+
}
|
1792 |
+
|
1793 |
+
enableInstances(false);
|
1794 |
+
setReferences();
|
1795 |
+
var plugin = {
|
1796 |
+
fn: function fn()
|
1797 |
+
{
|
1798 |
+
return {
|
1799 |
+
onDestroy: function onDestroy()
|
1800 |
+
{
|
1801 |
+
enableInstances(true);
|
1802 |
+
},
|
1803 |
+
onTrigger: function onTrigger(instance, event)
|
1804 |
+
{
|
1805 |
+
var target = event.currentTarget;
|
1806 |
+
var index = references.indexOf(target); // bail-out
|
1807 |
+
|
1808 |
+
if (target === currentTarget) {
|
1809 |
+
return;
|
1810 |
+
}
|
1811 |
+
|
1812 |
+
currentTarget = target;
|
1813 |
+
var overrideProps = (overrides || []).concat('content').reduce(function (acc, prop) {
|
1814 |
+
acc[prop] = individualInstances[index].props[prop];
|
1815 |
+
return acc;
|
1816 |
+
}, {});
|
1817 |
+
instance.setProps(Object.assign({}, overrideProps, {
|
1818 |
+
getReferenceClientRect: typeof overrideProps.getReferenceClientRect === 'function' ? overrideProps.getReferenceClientRect : function () {
|
1819 |
+
return target.getBoundingClientRect();
|
1820 |
+
}
|
1821 |
+
}));
|
1822 |
+
}
|
1823 |
+
};
|
1824 |
+
}
|
1825 |
+
};
|
1826 |
+
var singleton = tippy(div(), Object.assign({}, removeProperties(optionalProps, ['overrides']), {
|
1827 |
+
plugins: [plugin].concat(optionalProps.plugins || []),
|
1828 |
+
triggerTarget: references
|
1829 |
+
}));
|
1830 |
+
var originalSetProps = singleton.setProps;
|
1831 |
+
|
1832 |
+
singleton.setProps = function (props) {
|
1833 |
+
overrides = props.overrides || overrides;
|
1834 |
+
originalSetProps(props);
|
1835 |
+
};
|
1836 |
+
|
1837 |
+
singleton.setInstances = function (nextInstances) {
|
1838 |
+
enableInstances(true);
|
1839 |
+
interceptSetPropsCleanups.forEach(function (fn) {
|
1840 |
+
return fn();
|
1841 |
+
});
|
1842 |
+
individualInstances = nextInstances;
|
1843 |
+
enableInstances(false);
|
1844 |
+
setReferences();
|
1845 |
+
interceptSetProps(singleton);
|
1846 |
+
singleton.setProps({
|
1847 |
+
triggerTarget: references
|
1848 |
+
});
|
1849 |
+
};
|
1850 |
+
|
1851 |
+
interceptSetPropsCleanups = interceptSetProps(singleton);
|
1852 |
+
return singleton;
|
1853 |
+
};
|
1854 |
+
|
1855 |
+
var BUBBLING_EVENTS_MAP = {
|
1856 |
+
mouseover: 'mouseenter',
|
1857 |
+
focusin: 'focus',
|
1858 |
+
click: 'click'
|
1859 |
+
};
|
1860 |
+
/**
|
1861 |
+
* Creates a delegate instance that controls the creation of tippy instances
|
1862 |
+
* for child elements (`target` CSS selector).
|
1863 |
+
*/
|
1864 |
+
|
1865 |
+
function delegate(targets, props)
|
1866 |
+
{
|
1867 |
+
/* istanbul ignore else */
|
1868 |
+
{
|
1869 |
+
errorWhen(!(props && props.target), ['You must specity a `target` prop indicating a CSS selector string matching', 'the target elements that should receive a tippy.'].join(' '));
|
1870 |
+
}
|
1871 |
+
|
1872 |
+
var listeners = [];
|
1873 |
+
var childTippyInstances = [];
|
1874 |
+
var disabled = false;
|
1875 |
+
var target = props.target;
|
1876 |
+
var nativeProps = removeProperties(props, ['target']);
|
1877 |
+
var parentProps = Object.assign({}, nativeProps, {
|
1878 |
+
trigger: 'manual',
|
1879 |
+
touch: false
|
1880 |
+
});
|
1881 |
+
var childProps = Object.assign({}, nativeProps, {
|
1882 |
+
showOnCreate: true
|
1883 |
+
});
|
1884 |
+
var returnValue = tippy(targets, parentProps);
|
1885 |
+
var normalizedReturnValue = normalizeToArray(returnValue);
|
1886 |
+
|
1887 |
+
function onTrigger(event)
|
1888 |
+
{
|
1889 |
+
if (!event.target || disabled) {
|
1890 |
+
return;
|
1891 |
+
}
|
1892 |
+
|
1893 |
+
var targetNode = event.target.closest(target);
|
1894 |
+
|
1895 |
+
if (!targetNode) {
|
1896 |
+
return;
|
1897 |
+
} // Get relevant trigger with fallbacks:
|
1898 |
+
// 1. Check `data-tippy-trigger` attribute on target node
|
1899 |
+
// 2. Fallback to `trigger` passed to `delegate()`
|
1900 |
+
// 3. Fallback to `defaultProps.trigger`
|
1901 |
+
|
1902 |
+
|
1903 |
+
var trigger = targetNode.getAttribute('data-tippy-trigger') || props.trigger || defaultProps.trigger; // @ts-ignore
|
1904 |
+
|
1905 |
+
if (targetNode._tippy) {
|
1906 |
+
return;
|
1907 |
+
}
|
1908 |
+
|
1909 |
+
if (event.type === 'touchstart' && typeof childProps.touch === 'boolean') {
|
1910 |
+
return;
|
1911 |
+
}
|
1912 |
+
|
1913 |
+
if (event.type !== 'touchstart' && trigger.indexOf(BUBBLING_EVENTS_MAP[event.type]) < 0) {
|
1914 |
+
return;
|
1915 |
+
}
|
1916 |
+
|
1917 |
+
var instance = tippy(targetNode, childProps);
|
1918 |
+
|
1919 |
+
if (instance) {
|
1920 |
+
childTippyInstances = childTippyInstances.concat(instance);
|
1921 |
+
}
|
1922 |
+
}
|
1923 |
+
|
1924 |
+
function on(node, eventType, handler, options)
|
1925 |
+
{
|
1926 |
+
if (options === void 0) {
|
1927 |
+
options = false;
|
1928 |
+
}
|
1929 |
+
|
1930 |
+
node.addEventListener(eventType, handler, options);
|
1931 |
+
listeners.push({
|
1932 |
+
node: node,
|
1933 |
+
eventType: eventType,
|
1934 |
+
handler: handler,
|
1935 |
+
options: options
|
1936 |
+
});
|
1937 |
+
}
|
1938 |
+
|
1939 |
+
function addEventListeners(instance)
|
1940 |
+
{
|
1941 |
+
var reference = instance.reference;
|
1942 |
+
on(reference, 'touchstart', onTrigger);
|
1943 |
+
on(reference, 'mouseover', onTrigger);
|
1944 |
+
on(reference, 'focusin', onTrigger);
|
1945 |
+
on(reference, 'click', onTrigger);
|
1946 |
+
}
|
1947 |
+
|
1948 |
+
function removeEventListeners()
|
1949 |
+
{
|
1950 |
+
listeners.forEach(function (_ref) {
|
1951 |
+
var node = _ref.node,
|
1952 |
+
eventType = _ref.eventType,
|
1953 |
+
handler = _ref.handler,
|
1954 |
+
options = _ref.options;
|
1955 |
+
node.removeEventListener(eventType, handler, options);
|
1956 |
+
});
|
1957 |
+
listeners = [];
|
1958 |
+
}
|
1959 |
+
|
1960 |
+
function applyMutations(instance)
|
1961 |
+
{
|
1962 |
+
var originalDestroy = instance.destroy;
|
1963 |
+
var originalEnable = instance.enable;
|
1964 |
+
var originalDisable = instance.disable;
|
1965 |
+
|
1966 |
+
instance.destroy = function (shouldDestroyChildInstances) {
|
1967 |
+
if (shouldDestroyChildInstances === void 0) {
|
1968 |
+
shouldDestroyChildInstances = true;
|
1969 |
+
}
|
1970 |
+
|
1971 |
+
if (shouldDestroyChildInstances) {
|
1972 |
+
childTippyInstances.forEach(function (instance) {
|
1973 |
+
instance.destroy();
|
1974 |
+
});
|
1975 |
+
}
|
1976 |
+
|
1977 |
+
childTippyInstances = [];
|
1978 |
+
removeEventListeners();
|
1979 |
+
originalDestroy();
|
1980 |
+
};
|
1981 |
+
|
1982 |
+
instance.enable = function () {
|
1983 |
+
originalEnable();
|
1984 |
+
childTippyInstances.forEach(function (instance) {
|
1985 |
+
return instance.enable();
|
1986 |
+
});
|
1987 |
+
disabled = false;
|
1988 |
+
};
|
1989 |
+
|
1990 |
+
instance.disable = function () {
|
1991 |
+
originalDisable();
|
1992 |
+
childTippyInstances.forEach(function (instance) {
|
1993 |
+
return instance.disable();
|
1994 |
+
});
|
1995 |
+
disabled = true;
|
1996 |
+
};
|
1997 |
+
|
1998 |
+
addEventListeners(instance);
|
1999 |
+
}
|
2000 |
+
|
2001 |
+
normalizedReturnValue.forEach(applyMutations);
|
2002 |
+
return returnValue;
|
2003 |
+
}
|
2004 |
+
|
2005 |
+
var animateFill = {
|
2006 |
+
name: 'animateFill',
|
2007 |
+
defaultValue: false,
|
2008 |
+
fn: function fn(instance)
|
2009 |
+
{
|
2010 |
+
var _instance$props$rende;
|
2011 |
+
|
2012 |
+
// @ts-ignore
|
2013 |
+
if (!((_instance$props$rende = instance.props.render) == null ? void 0 : _instance$props$rende.$$tippy)) {
|
2014 |
+
{
|
2015 |
+
errorWhen(instance.props.animateFill, 'The `animateFill` plugin requires the default render function.');
|
2016 |
+
}
|
2017 |
+
|
2018 |
+
return {};
|
2019 |
+
}
|
2020 |
+
|
2021 |
+
var _getChildren = getChildren(instance.popper),
|
2022 |
+
box = _getChildren.box,
|
2023 |
+
content = _getChildren.content;
|
2024 |
+
|
2025 |
+
var backdrop = instance.props.animateFill ? createBackdropElement() : null;
|
2026 |
+
return {
|
2027 |
+
onCreate: function onCreate()
|
2028 |
+
{
|
2029 |
+
if (backdrop) {
|
2030 |
+
box.insertBefore(backdrop, box.firstElementChild);
|
2031 |
+
box.setAttribute('data-animatefill', '');
|
2032 |
+
box.style.overflow = 'hidden';
|
2033 |
+
instance.setProps({
|
2034 |
+
arrow: false,
|
2035 |
+
animation: 'shift-away'
|
2036 |
+
});
|
2037 |
+
}
|
2038 |
+
},
|
2039 |
+
onMount: function onMount()
|
2040 |
+
{
|
2041 |
+
if (backdrop) {
|
2042 |
+
var transitionDuration = box.style.transitionDuration;
|
2043 |
+
var duration = Number(transitionDuration.replace('ms', '')); // The content should fade in after the backdrop has mostly filled the
|
2044 |
+
// tooltip element. `clip-path` is the other alternative but is not
|
2045 |
+
// well-supported and is buggy on some devices.
|
2046 |
+
|
2047 |
+
content.style.transitionDelay = Math.round(duration / 10) + "ms";
|
2048 |
+
backdrop.style.transitionDuration = transitionDuration;
|
2049 |
+
setVisibilityState([backdrop], 'visible');
|
2050 |
+
}
|
2051 |
+
},
|
2052 |
+
onShow: function onShow()
|
2053 |
+
{
|
2054 |
+
if (backdrop) {
|
2055 |
+
backdrop.style.transitionDuration = '0ms';
|
2056 |
+
}
|
2057 |
+
},
|
2058 |
+
onHide: function onHide()
|
2059 |
+
{
|
2060 |
+
if (backdrop) {
|
2061 |
+
setVisibilityState([backdrop], 'hidden');
|
2062 |
+
}
|
2063 |
+
}
|
2064 |
+
};
|
2065 |
+
}
|
2066 |
+
};
|
2067 |
+
|
2068 |
+
function createBackdropElement()
|
2069 |
+
{
|
2070 |
+
var backdrop = div();
|
2071 |
+
backdrop.className = BACKDROP_CLASS;
|
2072 |
+
setVisibilityState([backdrop], 'hidden');
|
2073 |
+
return backdrop;
|
2074 |
+
}
|
2075 |
+
|
2076 |
+
var mouseCoords = {
|
2077 |
+
clientX: 0,
|
2078 |
+
clientY: 0
|
2079 |
+
};
|
2080 |
+
var activeInstances = [];
|
2081 |
+
|
2082 |
+
function storeMouseCoords(_ref)
|
2083 |
+
{
|
2084 |
+
var clientX = _ref.clientX,
|
2085 |
+
clientY = _ref.clientY;
|
2086 |
+
mouseCoords = {
|
2087 |
+
clientX: clientX,
|
2088 |
+
clientY: clientY
|
2089 |
+
};
|
2090 |
+
}
|
2091 |
+
|
2092 |
+
function addMouseCoordsListener(doc)
|
2093 |
+
{
|
2094 |
+
doc.addEventListener('mousemove', storeMouseCoords);
|
2095 |
+
}
|
2096 |
+
|
2097 |
+
function removeMouseCoordsListener(doc)
|
2098 |
+
{
|
2099 |
+
doc.removeEventListener('mousemove', storeMouseCoords);
|
2100 |
+
}
|
2101 |
+
|
2102 |
+
var followCursor = {
|
2103 |
+
name: 'followCursor',
|
2104 |
+
defaultValue: false,
|
2105 |
+
fn: function fn(instance)
|
2106 |
+
{
|
2107 |
+
var reference = instance.reference;
|
2108 |
+
var doc = getOwnerDocument(instance.props.triggerTarget || reference);
|
2109 |
+
var isInternalUpdate = false;
|
2110 |
+
var wasFocusEvent = false;
|
2111 |
+
var isUnmounted = true;
|
2112 |
+
var prevProps = instance.props;
|
2113 |
+
|
2114 |
+
function getIsInitialBehavior()
|
2115 |
+
{
|
2116 |
+
return instance.props.followCursor === 'initial' && instance.state.isVisible;
|
2117 |
+
}
|
2118 |
+
|
2119 |
+
function addListener()
|
2120 |
+
{
|
2121 |
+
doc.addEventListener('mousemove', onMouseMove);
|
2122 |
+
}
|
2123 |
+
|
2124 |
+
function removeListener()
|
2125 |
+
{
|
2126 |
+
doc.removeEventListener('mousemove', onMouseMove);
|
2127 |
+
}
|
2128 |
+
|
2129 |
+
function unsetGetReferenceClientRect()
|
2130 |
+
{
|
2131 |
+
isInternalUpdate = true;
|
2132 |
+
instance.setProps({
|
2133 |
+
getReferenceClientRect: null
|
2134 |
+
});
|
2135 |
+
isInternalUpdate = false;
|
2136 |
+
}
|
2137 |
+
|
2138 |
+
function onMouseMove(event)
|
2139 |
+
{
|
2140 |
+
// If the instance is interactive, avoid updating the position unless it's
|
2141 |
+
// over the reference element
|
2142 |
+
var isCursorOverReference = event.target ? reference.contains(event.target) : true;
|
2143 |
+
var followCursor = instance.props.followCursor;
|
2144 |
+
var clientX = event.clientX,
|
2145 |
+
clientY = event.clientY;
|
2146 |
+
var rect = reference.getBoundingClientRect();
|
2147 |
+
var relativeX = clientX - rect.left;
|
2148 |
+
var relativeY = clientY - rect.top;
|
2149 |
+
|
2150 |
+
if (isCursorOverReference || !instance.props.interactive) {
|
2151 |
+
instance.setProps({
|
2152 |
+
getReferenceClientRect: function getReferenceClientRect()
|
2153 |
+
{
|
2154 |
+
var rect = reference.getBoundingClientRect();
|
2155 |
+
var x = clientX;
|
2156 |
+
var y = clientY;
|
2157 |
+
|
2158 |
+
if (followCursor === 'initial') {
|
2159 |
+
x = rect.left + relativeX;
|
2160 |
+
y = rect.top + relativeY;
|
2161 |
+
}
|
2162 |
+
|
2163 |
+
var top = followCursor === 'horizontal' ? rect.top : y;
|
2164 |
+
var right = followCursor === 'vertical' ? rect.right : x;
|
2165 |
+
var bottom = followCursor === 'horizontal' ? rect.bottom : y;
|
2166 |
+
var left = followCursor === 'vertical' ? rect.left : x;
|
2167 |
+
return {
|
2168 |
+
width: right - left,
|
2169 |
+
height: bottom - top,
|
2170 |
+
top: top,
|
2171 |
+
right: right,
|
2172 |
+
bottom: bottom,
|
2173 |
+
left: left
|
2174 |
+
};
|
2175 |
+
}
|
2176 |
+
});
|
2177 |
+
}
|
2178 |
+
}
|
2179 |
+
|
2180 |
+
function create()
|
2181 |
+
{
|
2182 |
+
if (instance.props.followCursor) {
|
2183 |
+
activeInstances.push({
|
2184 |
+
instance: instance,
|
2185 |
+
doc: doc
|
2186 |
+
});
|
2187 |
+
addMouseCoordsListener(doc);
|
2188 |
+
}
|
2189 |
+
}
|
2190 |
+
|
2191 |
+
function destroy()
|
2192 |
+
{
|
2193 |
+
activeInstances = activeInstances.filter(function (data) {
|
2194 |
+
return data.instance !== instance;
|
2195 |
+
});
|
2196 |
+
|
2197 |
+
if (activeInstances.filter(function (data) {
|
2198 |
+
return data.doc === doc;
|
2199 |
+
}).length === 0) {
|
2200 |
+
removeMouseCoordsListener(doc);
|
2201 |
+
}
|
2202 |
+
}
|
2203 |
+
|
2204 |
+
return {
|
2205 |
+
onCreate: create,
|
2206 |
+
onDestroy: destroy,
|
2207 |
+
onBeforeUpdate: function onBeforeUpdate()
|
2208 |
+
{
|
2209 |
+
prevProps = instance.props;
|
2210 |
+
},
|
2211 |
+
onAfterUpdate: function onAfterUpdate(_, _ref2)
|
2212 |
+
{
|
2213 |
+
var followCursor = _ref2.followCursor;
|
2214 |
+
|
2215 |
+
if (isInternalUpdate) {
|
2216 |
+
return;
|
2217 |
+
}
|
2218 |
+
|
2219 |
+
if (followCursor !== undefined && prevProps.followCursor !== followCursor) {
|
2220 |
+
destroy();
|
2221 |
+
|
2222 |
+
if (followCursor) {
|
2223 |
+
create();
|
2224 |
+
|
2225 |
+
if (instance.state.isMounted && !wasFocusEvent && !getIsInitialBehavior()) {
|
2226 |
+
addListener();
|
2227 |
+
}
|
2228 |
+
} else {
|
2229 |
+
removeListener();
|
2230 |
+
unsetGetReferenceClientRect();
|
2231 |
+
}
|
2232 |
+
}
|
2233 |
+
},
|
2234 |
+
onMount: function onMount()
|
2235 |
+
{
|
2236 |
+
if (instance.props.followCursor && !wasFocusEvent) {
|
2237 |
+
if (isUnmounted) {
|
2238 |
+
onMouseMove(mouseCoords);
|
2239 |
+
isUnmounted = false;
|
2240 |
+
}
|
2241 |
+
|
2242 |
+
if (!getIsInitialBehavior()) {
|
2243 |
+
addListener();
|
2244 |
+
}
|
2245 |
+
}
|
2246 |
+
},
|
2247 |
+
onTrigger: function onTrigger(_, event)
|
2248 |
+
{
|
2249 |
+
if (isMouseEvent(event)) {
|
2250 |
+
mouseCoords = {
|
2251 |
+
clientX: event.clientX,
|
2252 |
+
clientY: event.clientY
|
2253 |
+
};
|
2254 |
+
}
|
2255 |
+
|
2256 |
+
wasFocusEvent = event.type === 'focus';
|
2257 |
+
},
|
2258 |
+
onHidden: function onHidden()
|
2259 |
+
{
|
2260 |
+
if (instance.props.followCursor) {
|
2261 |
+
unsetGetReferenceClientRect();
|
2262 |
+
removeListener();
|
2263 |
+
isUnmounted = true;
|
2264 |
+
}
|
2265 |
+
}
|
2266 |
+
};
|
2267 |
+
}
|
2268 |
+
};
|
2269 |
+
|
2270 |
+
function getProps(props, modifier)
|
2271 |
+
{
|
2272 |
+
var _props$popperOptions;
|
2273 |
+
|
2274 |
+
return {
|
2275 |
+
popperOptions: Object.assign({}, props.popperOptions, {
|
2276 |
+
modifiers: [].concat((((_props$popperOptions = props.popperOptions) == null ? void 0 : _props$popperOptions.modifiers) || []).filter(function (_ref) {
|
2277 |
+
var name = _ref.name;
|
2278 |
+
return name !== modifier.name;
|
2279 |
+
}), [modifier])
|
2280 |
+
})
|
2281 |
+
};
|
2282 |
+
}
|
2283 |
+
|
2284 |
+
var inlinePositioning = {
|
2285 |
+
name: 'inlinePositioning',
|
2286 |
+
defaultValue: false,
|
2287 |
+
fn: function fn(instance)
|
2288 |
+
{
|
2289 |
+
var reference = instance.reference;
|
2290 |
+
|
2291 |
+
function isEnabled()
|
2292 |
+
{
|
2293 |
+
return !!instance.props.inlinePositioning;
|
2294 |
+
}
|
2295 |
+
|
2296 |
+
var placement;
|
2297 |
+
var cursorRectIndex = -1;
|
2298 |
+
var isInternalUpdate = false;
|
2299 |
+
var modifier = {
|
2300 |
+
name: 'tippyInlinePositioning',
|
2301 |
+
enabled: true,
|
2302 |
+
phase: 'afterWrite',
|
2303 |
+
fn: function fn(_ref2)
|
2304 |
+
{
|
2305 |
+
var state = _ref2.state;
|
2306 |
+
|
2307 |
+
if (isEnabled()) {
|
2308 |
+
if (placement !== state.placement) {
|
2309 |
+
instance.setProps({
|
2310 |
+
getReferenceClientRect: function getReferenceClientRect()
|
2311 |
+
{
|
2312 |
+
return _getReferenceClientRect(state.placement);
|
2313 |
+
}
|
2314 |
+
});
|
2315 |
+
}
|
2316 |
+
|
2317 |
+
placement = state.placement;
|
2318 |
+
}
|
2319 |
+
}
|
2320 |
+
};
|
2321 |
+
|
2322 |
+
function _getReferenceClientRect(placement)
|
2323 |
+
{
|
2324 |
+
return getInlineBoundingClientRect(getBasePlacement(placement), reference.getBoundingClientRect(), arrayFrom(reference.getClientRects()), cursorRectIndex);
|
2325 |
+
}
|
2326 |
+
|
2327 |
+
function setInternalProps(partialProps)
|
2328 |
+
{
|
2329 |
+
isInternalUpdate = true;
|
2330 |
+
instance.setProps(partialProps);
|
2331 |
+
isInternalUpdate = false;
|
2332 |
+
}
|
2333 |
+
|
2334 |
+
function addModifier()
|
2335 |
+
{
|
2336 |
+
if (!isInternalUpdate) {
|
2337 |
+
setInternalProps(getProps(instance.props, modifier));
|
2338 |
+
}
|
2339 |
+
}
|
2340 |
+
|
2341 |
+
return {
|
2342 |
+
onCreate: addModifier,
|
2343 |
+
onAfterUpdate: addModifier,
|
2344 |
+
onTrigger: function onTrigger(_, event)
|
2345 |
+
{
|
2346 |
+
if (isMouseEvent(event)) {
|
2347 |
+
var rects = arrayFrom(instance.reference.getClientRects());
|
2348 |
+
var cursorRect = rects.find(function (rect) {
|
2349 |
+
return rect.left - 2 <= event.clientX && rect.right + 2 >= event.clientX && rect.top - 2 <= event.clientY && rect.bottom + 2 >= event.clientY;
|
2350 |
+
});
|
2351 |
+
cursorRectIndex = rects.indexOf(cursorRect);
|
2352 |
+
}
|
2353 |
+
},
|
2354 |
+
onUntrigger: function onUntrigger()
|
2355 |
+
{
|
2356 |
+
cursorRectIndex = -1;
|
2357 |
+
}
|
2358 |
+
};
|
2359 |
+
}
|
2360 |
+
};
|
2361 |
+
function getInlineBoundingClientRect(currentBasePlacement, boundingRect, clientRects, cursorRectIndex)
|
2362 |
+
{
|
2363 |
+
// Not an inline element, or placement is not yet known
|
2364 |
+
if (clientRects.length < 2 || currentBasePlacement === null) {
|
2365 |
+
return boundingRect;
|
2366 |
+
} // There are two rects and they are disjoined
|
2367 |
+
|
2368 |
+
|
2369 |
+
if (clientRects.length === 2 && cursorRectIndex >= 0 && clientRects[0].left > clientRects[1].right) {
|
2370 |
+
return clientRects[cursorRectIndex] || boundingRect;
|
2371 |
+
}
|
2372 |
+
|
2373 |
+
switch (currentBasePlacement) {
|
2374 |
+
case 'top':
|
2375 |
+
case 'bottom':
|
2376 |
+
{
|
2377 |
+
var firstRect = clientRects[0];
|
2378 |
+
var lastRect = clientRects[clientRects.length - 1];
|
2379 |
+
var isTop = currentBasePlacement === 'top';
|
2380 |
+
var top = firstRect.top;
|
2381 |
+
var bottom = lastRect.bottom;
|
2382 |
+
var left = isTop ? firstRect.left : lastRect.left;
|
2383 |
+
var right = isTop ? firstRect.right : lastRect.right;
|
2384 |
+
var width = right - left;
|
2385 |
+
var height = bottom - top;
|
2386 |
+
return {
|
2387 |
+
top: top,
|
2388 |
+
bottom: bottom,
|
2389 |
+
left: left,
|
2390 |
+
right: right,
|
2391 |
+
width: width,
|
2392 |
+
height: height
|
2393 |
+
};
|
2394 |
+
}
|
2395 |
+
|
2396 |
+
case 'left':
|
2397 |
+
case 'right':
|
2398 |
+
{
|
2399 |
+
var minLeft = Math.min.apply(Math, clientRects.map(function (rects) {
|
2400 |
+
return rects.left;
|
2401 |
+
}));
|
2402 |
+
var maxRight = Math.max.apply(Math, clientRects.map(function (rects) {
|
2403 |
+
return rects.right;
|
2404 |
+
}));
|
2405 |
+
var measureRects = clientRects.filter(function (rect) {
|
2406 |
+
return currentBasePlacement === 'left' ? rect.left === minLeft : rect.right === maxRight;
|
2407 |
+
});
|
2408 |
+
var _top = measureRects[0].top;
|
2409 |
+
var _bottom = measureRects[measureRects.length - 1].bottom;
|
2410 |
+
var _left = minLeft;
|
2411 |
+
var _right = maxRight;
|
2412 |
+
|
2413 |
+
var _width = _right - _left;
|
2414 |
+
|
2415 |
+
var _height = _bottom - _top;
|
2416 |
+
|
2417 |
+
return {
|
2418 |
+
top: _top,
|
2419 |
+
bottom: _bottom,
|
2420 |
+
left: _left,
|
2421 |
+
right: _right,
|
2422 |
+
width: _width,
|
2423 |
+
height: _height
|
2424 |
+
};
|
2425 |
+
}
|
2426 |
+
|
2427 |
+
default:
|
2428 |
+
{
|
2429 |
+
return boundingRect;
|
2430 |
+
}
|
2431 |
+
}
|
2432 |
+
}
|
2433 |
+
|
2434 |
+
var sticky = {
|
2435 |
+
name: 'sticky',
|
2436 |
+
defaultValue: false,
|
2437 |
+
fn: function fn(instance)
|
2438 |
+
{
|
2439 |
+
var reference = instance.reference,
|
2440 |
+
popper = instance.popper;
|
2441 |
+
|
2442 |
+
function getReference()
|
2443 |
+
{
|
2444 |
+
return instance.popperInstance ? instance.popperInstance.state.elements.reference : reference;
|
2445 |
+
}
|
2446 |
+
|
2447 |
+
function shouldCheck(value)
|
2448 |
+
{
|
2449 |
+
return instance.props.sticky === true || instance.props.sticky === value;
|
2450 |
+
}
|
2451 |
+
|
2452 |
+
var prevRefRect = null;
|
2453 |
+
var prevPopRect = null;
|
2454 |
+
|
2455 |
+
function updatePosition()
|
2456 |
+
{
|
2457 |
+
var currentRefRect = shouldCheck('reference') ? getReference().getBoundingClientRect() : null;
|
2458 |
+
var currentPopRect = shouldCheck('popper') ? popper.getBoundingClientRect() : null;
|
2459 |
+
|
2460 |
+
if (currentRefRect && areRectsDifferent(prevRefRect, currentRefRect) || currentPopRect && areRectsDifferent(prevPopRect, currentPopRect)) {
|
2461 |
+
if (instance.popperInstance) {
|
2462 |
+
instance.popperInstance.update();
|
2463 |
+
}
|
2464 |
+
}
|
2465 |
+
|
2466 |
+
prevRefRect = currentRefRect;
|
2467 |
+
prevPopRect = currentPopRect;
|
2468 |
+
|
2469 |
+
if (instance.state.isMounted) {
|
2470 |
+
requestAnimationFrame(updatePosition);
|
2471 |
+
}
|
2472 |
+
}
|
2473 |
+
|
2474 |
+
return {
|
2475 |
+
onMount: function onMount()
|
2476 |
+
{
|
2477 |
+
if (instance.props.sticky) {
|
2478 |
+
updatePosition();
|
2479 |
+
}
|
2480 |
+
}
|
2481 |
+
};
|
2482 |
+
}
|
2483 |
+
};
|
2484 |
+
|
2485 |
+
function areRectsDifferent(rectA, rectB)
|
2486 |
+
{
|
2487 |
+
if (rectA && rectB) {
|
2488 |
+
return rectA.top !== rectB.top || rectA.right !== rectB.right || rectA.bottom !== rectB.bottom || rectA.left !== rectB.left;
|
2489 |
+
}
|
2490 |
+
|
2491 |
+
return true;
|
2492 |
+
}
|
2493 |
+
|
2494 |
+
if (isBrowser) {
|
2495 |
+
injectCSS(css);
|
2496 |
+
}
|
2497 |
+
|
2498 |
+
tippy.setDefaultProps({
|
2499 |
+
plugins: [animateFill, followCursor, inlinePositioning, sticky],
|
2500 |
+
render: render
|
2501 |
+
});
|
2502 |
+
tippy.createSingleton = createSingleton;
|
2503 |
+
tippy.delegate = delegate;
|
2504 |
+
tippy.hideAll = hideAll;
|
2505 |
+
tippy.roundArrow = ROUND_ARROW;
|
2506 |
+
|
2507 |
+
return tippy;
|
2508 |
+
|
2509 |
+
})));
|
assets/js/tippy/tippy-bundle.umd.min.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("@popperjs/core")):"function"==typeof define&&define.amd?define(["@popperjs/core"],e):(t=t||self).tippy=e(t.Popper)}(this,(function(t){"use strict";var e="undefined"!=typeof window&&"undefined"!=typeof document,n=e?navigator.userAgent:"",r=/MSIE |Trident\//.test(n),i={passive:!0,capture:!0};function o(t,e,n){if(Array.isArray(t)){var r=t[e];return null==r?Array.isArray(n)?n[e]:n:r}return t}function a(t,e){var n={}.toString.call(t);return 0===n.indexOf("[object")&&n.indexOf(e+"]")>-1}function s(t,e){return"function"==typeof t?t.apply(void 0,e):t}function u(t,e){return 0===e?t:function(r){clearTimeout(n),n=setTimeout((function(){t(r)}),e)};var n}function c(t,e){var n=Object.assign({},t);return e.forEach((function(t){delete n[t]})),n}function p(t){return[].concat(t)}function f(t,e){-1===t.indexOf(e)&&t.push(e)}function l(t){return t.split("-")[0]}function d(t){return[].slice.call(t)}function v(){return document.createElement("div")}function m(t){return["Element","Fragment"].some((function(e){return a(t,e)}))}function g(t){return a(t,"MouseEvent")}function h(t){return!(!t||!t._tippy||t._tippy.reference!==t)}function b(t){return m(t)?[t]:function(t){return a(t,"NodeList")}(t)?d(t):Array.isArray(t)?t:d(document.querySelectorAll(t))}function y(t,e){t.forEach((function(t){t&&(t.style.transitionDuration=e+"ms")}))}function x(t,e){t.forEach((function(t){t&&t.setAttribute("data-state",e)}))}function w(t){var e=p(t)[0];return e&&e.ownerDocument||document}function E(t,e,n){var r=e+"EventListener";["transitionend","webkitTransitionEnd"].forEach((function(e){t[r](e,n)}))}var T={isTouch:!1},C=0;function A(){T.isTouch||(T.isTouch=!0,window.performance&&document.addEventListener("mousemove",O))}function O(){var t=performance.now();t-C<20&&(T.isTouch=!1,document.removeEventListener("mousemove",O)),C=t}function L(){var t=document.activeElement;if(h(t)){var e=t._tippy;t.blur&&!e.state.isVisible&&t.blur()}}var D=Object.assign({appendTo:function(){return document.body},aria:{content:"auto",expanded:"auto"},delay:0,duration:[300,250],getReferenceClientRect:null,hideOnClick:!0,ignoreAttributes:!1,interactive:!1,interactiveBorder:2,interactiveDebounce:0,moveTransition:"",offset:[0,10],onAfterUpdate:function(){},onBeforeUpdate:function(){},onCreate:function(){},onDestroy:function(){},onHidden:function(){},onHide:function(){},onMount:function(){},onShow:function(){},onShown:function(){},onTrigger:function(){},onUntrigger:function(){},onClickOutside:function(){},placement:"top",plugins:[],popperOptions:{},render:null,showOnCreate:!1,touch:!0,trigger:"mouseenter focus",triggerTarget:null},{animateFill:!1,followCursor:!1,inlinePositioning:!1,sticky:!1},{},{allowHTML:!1,animation:"fade",arrow:!0,content:"",inertia:!1,maxWidth:350,role:"tooltip",theme:"",zIndex:9999}),k=Object.keys(D);function R(t){var e=(t.plugins||[]).reduce((function(e,n){var r=n.name,i=n.defaultValue;return r&&(e[r]=void 0!==t[r]?t[r]:i),e}),{});return Object.assign({},t,{},e)}function M(t,e){var n=Object.assign({},e,{content:s(e.content,[t])},e.ignoreAttributes?{}:function(t,e){return(e?Object.keys(R(Object.assign({},D,{plugins:e}))):k).reduce((function(e,n){var r=(t.getAttribute("data-tippy-"+n)||"").trim();if(!r)return e;if("content"===n)e[n]=r;else try{e[n]=JSON.parse(r)}catch(t){e[n]=r}return e}),{})}(t,e.plugins));return n.aria=Object.assign({},D.aria,{},n.aria),n.aria={expanded:"auto"===n.aria.expanded?e.interactive:n.aria.expanded,content:"auto"===n.aria.content?e.interactive?null:"describedby":n.aria.content},n}function P(t,e){t.innerHTML=e}function V(t){var e=v();return!0===t?e.className="tippy-arrow":(e.className="tippy-svg-arrow",m(t)?e.appendChild(t):P(e,t)),e}function j(t,e){m(e.content)?(P(t,""),t.appendChild(e.content)):"function"!=typeof e.content&&(e.allowHTML?P(t,e.content):t.textContent=e.content)}function I(t){var e=t.firstElementChild,n=d(e.children);return{box:e,content:n.find((function(t){return t.classList.contains("tippy-content")})),arrow:n.find((function(t){return t.classList.contains("tippy-arrow")||t.classList.contains("tippy-svg-arrow")})),backdrop:n.find((function(t){return t.classList.contains("tippy-backdrop")}))}}function S(t){var e=v(),n=v();n.className="tippy-box",n.setAttribute("data-state","hidden"),n.setAttribute("tabindex","-1");var r=v();function i(n,r){var i=I(e),o=i.box,a=i.content,s=i.arrow;r.theme?o.setAttribute("data-theme",r.theme):o.removeAttribute("data-theme"),"string"==typeof r.animation?o.setAttribute("data-animation",r.animation):o.removeAttribute("data-animation"),r.inertia?o.setAttribute("data-inertia",""):o.removeAttribute("data-inertia"),o.style.maxWidth="number"==typeof r.maxWidth?r.maxWidth+"px":r.maxWidth,r.role?o.setAttribute("role",r.role):o.removeAttribute("role"),n.content===r.content&&n.allowHTML===r.allowHTML||j(a,t.props),r.arrow?s?n.arrow!==r.arrow&&(o.removeChild(s),o.appendChild(V(r.arrow))):o.appendChild(V(r.arrow)):s&&o.removeChild(s)}return r.className="tippy-content",r.setAttribute("data-state","hidden"),j(r,t.props),e.appendChild(n),n.appendChild(r),i(t.props,t.props),{popper:e,onUpdate:i}}S.$$tippy=!0;var B=1,H=[],N=[];function U(e,n){var a,c,m,h,b,C,A,O,L,k=M(e,Object.assign({},D,{},R((a=n,Object.keys(a).reduce((function(t,e){return void 0!==a[e]&&(t[e]=a[e]),t}),{}))))),P=!1,V=!1,j=!1,S=!1,U=[],_=u(bt,k.interactiveDebounce),z=B++,F=(L=k.plugins).filter((function(t,e){return L.indexOf(t)===e})),W={id:z,reference:e,popper:v(),popperInstance:null,props:k,state:{isEnabled:!0,isVisible:!1,isDestroyed:!1,isMounted:!1,isShown:!1},plugins:F,clearDelayTimeouts:function(){clearTimeout(c),clearTimeout(m),cancelAnimationFrame(h)},setProps:function(t){if(W.state.isDestroyed)return;it("onBeforeUpdate",[W,t]),gt();var n=W.props,r=M(e,Object.assign({},W.props,{},t,{ignoreAttributes:!0}));W.props=r,mt(),n.interactiveDebounce!==r.interactiveDebounce&&(st(),_=u(bt,r.interactiveDebounce));n.triggerTarget&&!r.triggerTarget?p(n.triggerTarget).forEach((function(t){t.removeAttribute("aria-expanded")})):r.triggerTarget&&e.removeAttribute("aria-expanded");at(),rt(),q&&q(n,r);W.popperInstance&&(Et(),Ct().forEach((function(t){requestAnimationFrame(t._tippy.popperInstance.forceUpdate)})));it("onAfterUpdate",[W,t])},setContent:function(t){W.setProps({content:t})},show:function(){var t=W.state.isVisible,e=W.state.isDestroyed,n=!W.state.isEnabled,r=T.isTouch&&!W.props.touch,i=o(W.props.duration,0,D.duration);if(t||e||n||r)return;if(Z().hasAttribute("disabled"))return;if(it("onShow",[W],!1),!1===W.props.onShow(W))return;W.state.isVisible=!0,Q()&&(Y.style.visibility="visible");rt(),ft(),W.state.isMounted||(Y.style.transition="none");if(Q()){var a=et(),u=a.box,c=a.content;y([u,c],0)}A=function(){if(W.state.isVisible&&!S){if(S=!0,Y.offsetHeight,Y.style.transition=W.props.moveTransition,Q()&&W.props.animation){var t=et(),e=t.box,n=t.content;y([e,n],i),x([e,n],"visible")}ot(),at(),f(N,W),W.state.isMounted=!0,it("onMount",[W]),W.props.animation&&Q()&&function(t,e){dt(t,e)}(i,(function(){W.state.isShown=!0,it("onShown",[W])}))}},function(){var t,e=W.props.appendTo,n=Z();t=W.props.interactive&&e===D.appendTo||"parent"===e?n.parentNode:s(e,[n]);t.contains(Y)||t.appendChild(Y);Et()}()},hide:function(){var t=!W.state.isVisible,e=W.state.isDestroyed,n=!W.state.isEnabled,r=o(W.props.duration,1,D.duration);if(t||e||n)return;if(it("onHide",[W],!1),!1===W.props.onHide(W))return;W.state.isVisible=!1,W.state.isShown=!1,S=!1,P=!1,Q()&&(Y.style.visibility="hidden");if(st(),lt(),rt(),Q()){var i=et(),a=i.box,s=i.content;W.props.animation&&(y([a,s],r),x([a,s],"hidden"))}ot(),at(),W.props.animation?Q()&&function(t,e){dt(t,(function(){!W.state.isVisible&&Y.parentNode&&Y.parentNode.contains(Y)&&e()}))}(r,W.unmount):W.unmount()},hideWithInteractivity:function(t){tt().addEventListener("mousemove",_),f(H,_),_(t)},enable:function(){W.state.isEnabled=!0},disable:function(){W.hide(),W.state.isEnabled=!1},unmount:function(){W.state.isVisible&&W.hide();if(!W.state.isMounted)return;Tt(),Ct().forEach((function(t){t._tippy.unmount()})),Y.parentNode&&Y.parentNode.removeChild(Y);N=N.filter((function(t){return t!==W})),W.state.isMounted=!1,it("onHidden",[W])},destroy:function(){if(W.state.isDestroyed)return;W.clearDelayTimeouts(),W.unmount(),gt(),delete e._tippy,W.state.isDestroyed=!0,it("onDestroy",[W])}};if(!k.render)return W;var X=k.render(W),Y=X.popper,q=X.onUpdate;Y.setAttribute("data-tippy-root",""),Y.id="tippy-"+W.id,W.popper=Y,e._tippy=W,Y._tippy=W;var $=F.map((function(t){return t.fn(W)})),J=e.hasAttribute("aria-expanded");return mt(),at(),rt(),it("onCreate",[W]),k.showOnCreate&&At(),Y.addEventListener("mouseenter",(function(){W.props.interactive&&W.state.isVisible&&W.clearDelayTimeouts()})),Y.addEventListener("mouseleave",(function(t){W.props.interactive&&W.props.trigger.indexOf("mouseenter")>=0&&(tt().addEventListener("mousemove",_),_(t))})),W;function G(){var t=W.props.touch;return Array.isArray(t)?t:[t,0]}function K(){return"hold"===G()[0]}function Q(){var t;return!!(null==(t=W.props.render)?void 0:t.$$tippy)}function Z(){return O||e}function tt(){var t=Z().parentNode;return t?w(t):document}function et(){return I(Y)}function nt(t){return W.state.isMounted&&!W.state.isVisible||T.isTouch||b&&"focus"===b.type?0:o(W.props.delay,t?0:1,D.delay)}function rt(){Y.style.pointerEvents=W.props.interactive&&W.state.isVisible?"":"none",Y.style.zIndex=""+W.props.zIndex}function it(t,e,n){var r;(void 0===n&&(n=!0),$.forEach((function(n){n[t]&&n[t].apply(void 0,e)})),n)&&(r=W.props)[t].apply(r,e)}function ot(){var t=W.props.aria;if(t.content){var n="aria-"+t.content,r=Y.id;p(W.props.triggerTarget||e).forEach((function(t){var e=t.getAttribute(n);if(W.state.isVisible)t.setAttribute(n,e?e+" "+r:r);else{var i=e&&e.replace(r,"").trim();i?t.setAttribute(n,i):t.removeAttribute(n)}}))}}function at(){!J&&W.props.aria.expanded&&p(W.props.triggerTarget||e).forEach((function(t){W.props.interactive?t.setAttribute("aria-expanded",W.state.isVisible&&t===Z()?"true":"false"):t.removeAttribute("aria-expanded")}))}function st(){tt().removeEventListener("mousemove",_),H=H.filter((function(t){return t!==_}))}function ut(t){if(!(T.isTouch&&(j||"mousedown"===t.type)||W.props.interactive&&Y.contains(t.target))){if(Z().contains(t.target)){if(T.isTouch)return;if(W.state.isVisible&&W.props.trigger.indexOf("click")>=0)return}else it("onClickOutside",[W,t]);!0===W.props.hideOnClick&&(W.clearDelayTimeouts(),W.hide(),V=!0,setTimeout((function(){V=!1})),W.state.isMounted||lt())}}function ct(){j=!0}function pt(){j=!1}function ft(){var t=tt();t.addEventListener("mousedown",ut,!0),t.addEventListener("touchend",ut,i),t.addEventListener("touchstart",pt,i),t.addEventListener("touchmove",ct,i)}function lt(){var t=tt();t.removeEventListener("mousedown",ut,!0),t.removeEventListener("touchend",ut,i),t.removeEventListener("touchstart",pt,i),t.removeEventListener("touchmove",ct,i)}function dt(t,e){var n=et().box;function r(t){t.target===n&&(E(n,"remove",r),e())}if(0===t)return e();E(n,"remove",C),E(n,"add",r),C=r}function vt(t,n,r){void 0===r&&(r=!1),p(W.props.triggerTarget||e).forEach((function(e){e.addEventListener(t,n,r),U.push({node:e,eventType:t,handler:n,options:r})}))}function mt(){var t;K()&&(vt("touchstart",ht,{passive:!0}),vt("touchend",yt,{passive:!0})),(t=W.props.trigger,t.split(/\s+/).filter(Boolean)).forEach((function(t){if("manual"!==t)switch(vt(t,ht),t){case"mouseenter":vt("mouseleave",yt);break;case"focus":vt(r?"focusout":"blur",xt);break;case"focusin":vt("focusout",xt)}}))}function gt(){U.forEach((function(t){var e=t.node,n=t.eventType,r=t.handler,i=t.options;e.removeEventListener(n,r,i)})),U=[]}function ht(t){var e,n=!1;if(W.state.isEnabled&&!wt(t)&&!V){var r="focus"===(null==(e=b)?void 0:e.type);b=t,O=t.currentTarget,at(),!W.state.isVisible&&g(t)&&H.forEach((function(e){return e(t)})),"click"===t.type&&(W.props.trigger.indexOf("mouseenter")<0||P)&&!1!==W.props.hideOnClick&&W.state.isVisible?n=!0:At(t),"click"===t.type&&(P=!n),n&&!r&&Ot(t)}}function bt(t){var e=t.target,n=Z().contains(e)||Y.contains(e);"mousemove"===t.type&&n||function(t,e){var n=e.clientX,r=e.clientY;return t.every((function(t){var e=t.popperRect,i=t.popperState,o=t.props.interactiveBorder,a=l(i.placement),s=i.modifiersData.offset;if(!s)return!0;var u="bottom"===a?s.top.y:0,c="top"===a?s.bottom.y:0,p="right"===a?s.left.x:0,f="left"===a?s.right.x:0,d=e.top-r+u>o,v=r-e.bottom-c>o,m=e.left-n+p>o,g=n-e.right-f>o;return d||v||m||g}))}(Ct().concat(Y).map((function(t){var e,n=null==(e=t._tippy.popperInstance)?void 0:e.state;return n?{popperRect:t.getBoundingClientRect(),popperState:n,props:k}:null})).filter(Boolean),t)&&(st(),Ot(t))}function yt(t){wt(t)||W.props.trigger.indexOf("click")>=0&&P||(W.props.interactive?W.hideWithInteractivity(t):Ot(t))}function xt(t){W.props.trigger.indexOf("focusin")<0&&t.target!==Z()||W.props.interactive&&t.relatedTarget&&Y.contains(t.relatedTarget)||Ot(t)}function wt(t){return!!T.isTouch&&K()!==t.type.indexOf("touch")>=0}function Et(){Tt();var n=W.props,r=n.popperOptions,i=n.placement,o=n.offset,a=n.getReferenceClientRect,s=n.moveTransition,u=Q()?I(Y).arrow:null,c=a?{getBoundingClientRect:a,contextElement:a.contextElement||Z()}:e,p=[{name:"offset",options:{offset:o}},{name:"preventOverflow",options:{padding:{top:2,bottom:2,left:5,right:5}}},{name:"flip",options:{padding:5}},{name:"computeStyles",options:{adaptive:!s}},{name:"$$tippy",enabled:!0,phase:"beforeWrite",requires:["computeStyles"],fn:function(t){var e=t.state;if(Q()){var n=et().box;["placement","reference-hidden","escaped"].forEach((function(t){"placement"===t?n.setAttribute("data-placement",e.placement):e.attributes.popper["data-popper-"+t]?n.setAttribute("data-"+t,""):n.removeAttribute("data-"+t)})),e.attributes.popper={}}}}];Q()&&u&&p.push({name:"arrow",options:{element:u,padding:3}}),p.push.apply(p,(null==r?void 0:r.modifiers)||[]),W.popperInstance=t.createPopper(c,Y,Object.assign({},r,{placement:i,onFirstUpdate:A,modifiers:p}))}function Tt(){W.popperInstance&&(W.popperInstance.destroy(),W.popperInstance=null)}function Ct(){return d(Y.querySelectorAll("[data-tippy-root]"))}function At(t){W.clearDelayTimeouts(),t&&it("onTrigger",[W,t]),ft();var e=nt(!0),n=G(),r=n[0],i=n[1];T.isTouch&&"hold"===r&&i&&(e=i),e?c=setTimeout((function(){W.show()}),e):W.show()}function Ot(t){if(W.clearDelayTimeouts(),it("onUntrigger",[W,t]),W.state.isVisible){if(!(W.props.trigger.indexOf("mouseenter")>=0&&W.props.trigger.indexOf("click")>=0&&["mouseleave","mousemove"].indexOf(t.type)>=0&&P)){var e=nt(!1);e?m=setTimeout((function(){W.state.isVisible&&W.hide()}),e):h=requestAnimationFrame((function(){W.hide()}))}}else lt()}}function _(t,e){void 0===e&&(e={});var n=D.plugins.concat(e.plugins||[]);document.addEventListener("touchstart",A,i),window.addEventListener("blur",L);var r=Object.assign({},e,{plugins:n}),o=b(t).reduce((function(t,e){var n=e&&U(e,r);return n&&t.push(n),t}),[]);return m(t)?o[0]:o}_.defaultProps=D,_.setDefaultProps=function(t){Object.keys(t).forEach((function(e){D[e]=t[e]}))},_.currentInput=T;var z={mouseover:"mouseenter",focusin:"focus",click:"click"};var F={name:"animateFill",defaultValue:!1,fn:function(t){var e;if(!(null==(e=t.props.render)?void 0:e.$$tippy))return{};var n=I(t.popper),r=n.box,i=n.content,o=t.props.animateFill?function(){var t=v();return t.className="tippy-backdrop",x([t],"hidden"),t}():null;return{onCreate:function(){o&&(r.insertBefore(o,r.firstElementChild),r.setAttribute("data-animatefill",""),r.style.overflow="hidden",t.setProps({arrow:!1,animation:"shift-away"}))},onMount:function(){if(o){var t=r.style.transitionDuration,e=Number(t.replace("ms",""));i.style.transitionDelay=Math.round(e/10)+"ms",o.style.transitionDuration=t,x([o],"visible")}},onShow:function(){o&&(o.style.transitionDuration="0ms")},onHide:function(){o&&x([o],"hidden")}}}};var W={clientX:0,clientY:0},X=[];function Y(t){var e=t.clientX,n=t.clientY;W={clientX:e,clientY:n}}var q={name:"followCursor",defaultValue:!1,fn:function(t){var e=t.reference,n=w(t.props.triggerTarget||e),r=!1,i=!1,o=!0,a=t.props;function s(){return"initial"===t.props.followCursor&&t.state.isVisible}function u(){n.addEventListener("mousemove",f)}function c(){n.removeEventListener("mousemove",f)}function p(){r=!0,t.setProps({getReferenceClientRect:null}),r=!1}function f(n){var r=!n.target||e.contains(n.target),i=t.props.followCursor,o=n.clientX,a=n.clientY,s=e.getBoundingClientRect(),u=o-s.left,c=a-s.top;!r&&t.props.interactive||t.setProps({getReferenceClientRect:function(){var t=e.getBoundingClientRect(),n=o,r=a;"initial"===i&&(n=t.left+u,r=t.top+c);var s="horizontal"===i?t.top:r,p="vertical"===i?t.right:n,f="horizontal"===i?t.bottom:r,l="vertical"===i?t.left:n;return{width:p-l,height:f-s,top:s,right:p,bottom:f,left:l}}})}function l(){t.props.followCursor&&(X.push({instance:t,doc:n}),function(t){t.addEventListener("mousemove",Y)}(n))}function d(){0===(X=X.filter((function(e){return e.instance!==t}))).filter((function(t){return t.doc===n})).length&&function(t){t.removeEventListener("mousemove",Y)}(n)}return{onCreate:l,onDestroy:d,onBeforeUpdate:function(){a=t.props},onAfterUpdate:function(e,n){var o=n.followCursor;r||void 0!==o&&a.followCursor!==o&&(d(),o?(l(),!t.state.isMounted||i||s()||u()):(c(),p()))},onMount:function(){t.props.followCursor&&!i&&(o&&(f(W),o=!1),s()||u())},onTrigger:function(t,e){g(e)&&(W={clientX:e.clientX,clientY:e.clientY}),i="focus"===e.type},onHidden:function(){t.props.followCursor&&(p(),c(),o=!0)}}}};var $={name:"inlinePositioning",defaultValue:!1,fn:function(t){var e,n=t.reference;var r=-1,i=!1,o={name:"tippyInlinePositioning",enabled:!0,phase:"afterWrite",fn:function(i){var o=i.state;t.props.inlinePositioning&&(e!==o.placement&&t.setProps({getReferenceClientRect:function(){return function(t){return function(t,e,n,r){if(n.length<2||null===t)return e;if(2===n.length&&r>=0&&n[0].left>n[1].right)return n[r]||e;switch(t){case"top":case"bottom":var i=n[0],o=n[n.length-1],a="top"===t,s=i.top,u=o.bottom,c=a?i.left:o.left,p=a?i.right:o.right;return{top:s,bottom:u,left:c,right:p,width:p-c,height:u-s};case"left":case"right":var f=Math.min.apply(Math,n.map((function(t){return t.left}))),l=Math.max.apply(Math,n.map((function(t){return t.right}))),d=n.filter((function(e){return"left"===t?e.left===f:e.right===l})),v=d[0].top,m=d[d.length-1].bottom;return{top:v,bottom:m,left:f,right:l,width:l-f,height:m-v};default:return e}}(l(t),n.getBoundingClientRect(),d(n.getClientRects()),r)}(o.placement)}}),e=o.placement)}};function a(){var e;i||(e=function(t,e){var n;return{popperOptions:Object.assign({},t.popperOptions,{modifiers:[].concat(((null==(n=t.popperOptions)?void 0:n.modifiers)||[]).filter((function(t){return t.name!==e.name})),[e])})}}(t.props,o),i=!0,t.setProps(e),i=!1)}return{onCreate:a,onAfterUpdate:a,onTrigger:function(e,n){if(g(n)){var i=d(t.reference.getClientRects()),o=i.find((function(t){return t.left-2<=n.clientX&&t.right+2>=n.clientX&&t.top-2<=n.clientY&&t.bottom+2>=n.clientY}));r=i.indexOf(o)}},onUntrigger:function(){r=-1}}}};var J={name:"sticky",defaultValue:!1,fn:function(t){var e=t.reference,n=t.popper;function r(e){return!0===t.props.sticky||t.props.sticky===e}var i=null,o=null;function a(){var s=r("reference")?(t.popperInstance?t.popperInstance.state.elements.reference:e).getBoundingClientRect():null,u=r("popper")?n.getBoundingClientRect():null;(s&&G(i,s)||u&&G(o,u))&&t.popperInstance&&t.popperInstance.update(),i=s,o=u,t.state.isMounted&&requestAnimationFrame(a)}return{onMount:function(){t.props.sticky&&a()}}}};function G(t,e){return!t||!e||(t.top!==e.top||t.right!==e.right||t.bottom!==e.bottom||t.left!==e.left)}return e&&function(t){var e=document.createElement("style");e.textContent=t,e.setAttribute("data-tippy-stylesheet","");var n=document.head,r=document.querySelector("head>style,head>link");r?n.insertBefore(e,r):n.appendChild(e)}('.tippy-box[data-animation=fade][data-state=hidden]{opacity:0}[data-tippy-root]{max-width:calc(100vw - 10px)}.tippy-box{position:relative;background-color:#333;color:#fff;border-radius:4px;font-size:14px;line-height:1.4;outline:0;transition-property:transform,visibility,opacity}.tippy-box[data-placement^=top]>.tippy-arrow{bottom:0}.tippy-box[data-placement^=top]>.tippy-arrow:before{bottom:-7px;left:0;border-width:8px 8px 0;border-top-color:initial;transform-origin:center top}.tippy-box[data-placement^=bottom]>.tippy-arrow{top:0}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{top:-7px;left:0;border-width:0 8px 8px;border-bottom-color:initial;transform-origin:center bottom}.tippy-box[data-placement^=left]>.tippy-arrow{right:0}.tippy-box[data-placement^=left]>.tippy-arrow:before{border-width:8px 0 8px 8px;border-left-color:initial;right:-7px;transform-origin:center left}.tippy-box[data-placement^=right]>.tippy-arrow{left:0}.tippy-box[data-placement^=right]>.tippy-arrow:before{left:-7px;border-width:8px 8px 8px 0;border-right-color:initial;transform-origin:center right}.tippy-box[data-inertia][data-state=visible]{transition-timing-function:cubic-bezier(.54,1.5,.38,1.11)}.tippy-arrow{width:16px;height:16px;color:#333}.tippy-arrow:before{content:"";position:absolute;border-color:transparent;border-style:solid}.tippy-content{position:relative;padding:5px 9px;z-index:1}'),_.setDefaultProps({plugins:[F,q,$,J],render:S}),_.createSingleton=function(t,e){void 0===e&&(e={});var n,r=t,i=[],o=e.overrides,a=[];function s(){i=r.map((function(t){return t.reference}))}function u(t){r.forEach((function(e){t?e.enable():e.disable()}))}function p(t){return r.map((function(e){var r=e.setProps;return e.setProps=function(i){r(i),e.reference===n&&t.setProps(i)},function(){e.setProps=r}}))}u(!1),s();var f={fn:function(){return{onDestroy:function(){u(!0)},onTrigger:function(t,e){var a=e.currentTarget,s=i.indexOf(a);if(a!==n){n=a;var u=(o||[]).concat("content").reduce((function(t,e){return t[e]=r[s].props[e],t}),{});t.setProps(Object.assign({},u,{getReferenceClientRect:"function"==typeof u.getReferenceClientRect?u.getReferenceClientRect:function(){return a.getBoundingClientRect()}}))}}}}},l=_(v(),Object.assign({},c(e,["overrides"]),{plugins:[f].concat(e.plugins||[]),triggerTarget:i})),d=l.setProps;return l.setProps=function(t){o=t.overrides||o,d(t)},l.setInstances=function(t){u(!0),a.forEach((function(t){return t()})),r=t,u(!1),s(),p(l),l.setProps({triggerTarget:i})},a=p(l),l},_.delegate=function(t,e){var n=[],r=[],i=!1,o=e.target,a=c(e,["target"]),s=Object.assign({},a,{trigger:"manual",touch:!1}),u=Object.assign({},a,{showOnCreate:!0}),f=_(t,s);function l(t){if(t.target&&!i){var n=t.target.closest(o);if(n){var a=n.getAttribute("data-tippy-trigger")||e.trigger||D.trigger;if(!n._tippy&&!("touchstart"===t.type&&"boolean"==typeof u.touch||"touchstart"!==t.type&&a.indexOf(z[t.type])<0)){var s=_(n,u);s&&(r=r.concat(s))}}}}function d(t,e,r,i){void 0===i&&(i=!1),t.addEventListener(e,r,i),n.push({node:t,eventType:e,handler:r,options:i})}return p(f).forEach((function(t){var e=t.destroy,o=t.enable,a=t.disable;t.destroy=function(t){void 0===t&&(t=!0),t&&r.forEach((function(t){t.destroy()})),r=[],n.forEach((function(t){var e=t.node,n=t.eventType,r=t.handler,i=t.options;e.removeEventListener(n,r,i)})),n=[],e()},t.enable=function(){o(),r.forEach((function(t){return t.enable()})),i=!1},t.disable=function(){a(),r.forEach((function(t){return t.disable()})),i=!0},function(t){var e=t.reference;d(e,"touchstart",l),d(e,"mouseover",l),d(e,"focusin",l),d(e,"click",l)}(t)})),f},_.hideAll=function(t){var e=void 0===t?{}:t,n=e.exclude,r=e.duration;N.forEach((function(t){var e=!1;if(n&&(e=h(n)?t.reference===n:t.popper===n.popper),!e){var i=t.props.duration;t.setProps({duration:r}),t.hide(),t.state.isDestroyed||t.setProps({duration:i})}}))},_.roundArrow='<svg width="16" height="6" xmlns="http://www.w3.org/2000/svg"><path d="M0 6s1.796-.013 4.67-3.615C5.851.9 6.93.006 8 0c1.07-.006 2.148.887 3.343 2.385C14.233 6.005 16 6 16 6H0z"></svg>',_}));
|
assets/js/tippy/tippy.css
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
.tippy-box[data-animation=fade][data-state=hidden]{opacity:0}[data-tippy-root]{max-width:calc(100vw - 10px)}.tippy-box{position:relative;background-color:#333;color:#fff;border-radius:4px;font-size:14px;line-height:1.4;outline:0;transition-property:transform,visibility,opacity}.tippy-box[data-placement^=top]>.tippy-arrow{bottom:0}.tippy-box[data-placement^=top]>.tippy-arrow:before{bottom:-7px;left:0;border-width:8px 8px 0;border-top-color:initial;transform-origin:center top}.tippy-box[data-placement^=bottom]>.tippy-arrow{top:0}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{top:-7px;left:0;border-width:0 8px 8px;border-bottom-color:initial;transform-origin:center bottom}.tippy-box[data-placement^=left]>.tippy-arrow{right:0}.tippy-box[data-placement^=left]>.tippy-arrow:before{border-width:8px 0 8px 8px;border-left-color:initial;right:-7px;transform-origin:center left}.tippy-box[data-placement^=right]>.tippy-arrow{left:0}.tippy-box[data-placement^=right]>.tippy-arrow:before{left:-7px;border-width:8px 8px 8px 0;border-right-color:initial;transform-origin:center right}.tippy-box[data-inertia][data-state=visible]{transition-timing-function:cubic-bezier(.54,1.5,.38,1.11)}.tippy-arrow{width:16px;height:16px;color:#333}.tippy-arrow:before{content:"";position:absolute;border-color:transparent;border-style:solid}.tippy-content{position:relative;padding:5px 9px;z-index:1}
|
assets/webfonts/index.php
CHANGED
@@ -1,2 +1,3 @@
|
|
1 |
<?php
|
2 |
-
|
|
1 |
<?php
|
2 |
+
|
3 |
+
//silent
|
classes/class.archive.config.php
CHANGED
@@ -1,48 +1,44 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
public $
|
14 |
-
public $
|
15 |
-
public $
|
16 |
-
public $
|
17 |
-
public $
|
18 |
-
public $
|
19 |
-
public $
|
20 |
-
|
21 |
-
public $
|
22 |
-
public $
|
23 |
-
public $
|
24 |
-
public $
|
25 |
-
public $
|
26 |
-
|
27 |
-
public $
|
28 |
-
public $
|
29 |
-
|
30 |
-
|
31 |
-
public $
|
32 |
-
public $
|
33 |
-
public $
|
34 |
-
public $
|
35 |
-
public $
|
36 |
-
public $
|
37 |
-
public $
|
38 |
-
public $
|
39 |
-
public $
|
40 |
-
|
41 |
-
|
42 |
-
public $
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
public $is_outer_root_wp_config_file;
|
47 |
-
public $is_outer_root_wp_content_dir;
|
48 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class DUP_Archive_Config
|
4 |
+
{
|
5 |
+
public $dup_type = 'lite';
|
6 |
+
public $created = '';
|
7 |
+
public $version_dup = '';
|
8 |
+
public $version_wp = '';
|
9 |
+
public $version_db = '';
|
10 |
+
public $version_php = '';
|
11 |
+
public $version_os = '';
|
12 |
+
public $blogname = '';
|
13 |
+
public $exportOnlyDB = false;
|
14 |
+
public $secure_on = false;
|
15 |
+
public $secure_pass = '';
|
16 |
+
public $dbhost = null;
|
17 |
+
public $dbname = null;
|
18 |
+
public $dbuser = null;
|
19 |
+
public $dbpass = null;
|
20 |
+
public $cpnl_host = null;
|
21 |
+
public $cpnl_user = null;
|
22 |
+
public $cpnl_pass = null;
|
23 |
+
public $cpnl_enable = null;
|
24 |
+
public $wp_tableprefix = '';
|
25 |
+
public $mu_mode = 0;
|
26 |
+
public $mu_generation = 0;
|
27 |
+
public $mu_siteadmins = array();
|
28 |
+
public $subsites = array();
|
29 |
+
public $main_site_id = 1;
|
30 |
+
public $mu_is_filtered = false;
|
31 |
+
public $license_limit = 0;
|
32 |
+
public $dbInfo = array();
|
33 |
+
public $packInfo = array();
|
34 |
+
public $fileInfo = array();
|
35 |
+
public $wpInfo = array();
|
36 |
+
public $opts_delete = array();
|
37 |
+
public $brand = array();
|
38 |
+
public $overwriteInstallerParams = array();
|
39 |
+
public $installer_base_name = '';
|
40 |
+
public $installer_backup_name = '';
|
41 |
+
public $package_name = '';
|
42 |
+
public $package_hash = '';
|
43 |
+
public $package_notes = '';
|
44 |
+
}
|
|
|
|
|
|
|
|
classes/class.constants.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
defined("ABSPATH") || exit;
|
3 |
class DUP_Constants
|
4 |
{
|
1 |
<?php
|
2 |
+
|
3 |
defined("ABSPATH") || exit;
|
4 |
class DUP_Constants
|
5 |
{
|
classes/class.db.php
CHANGED
@@ -1,261 +1,316 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
*
|
6 |
-
*
|
7 |
-
*
|
8 |
-
*
|
9 |
-
*
|
10 |
-
* @
|
11 |
-
* @
|
12 |
-
*
|
13 |
-
|
14 |
-
|
15 |
-
if
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
* Returns
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
$package_mysqldump
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
'mysqldump
|
137 |
-
|
138 |
-
'C:/
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
'mysqldump',
|
150 |
-
'/
|
151 |
-
'/usr/
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
)
|
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 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
$
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
4 |
+
/**
|
5 |
+
* Lightweight abstraction layer for common simple database routines
|
6 |
+
*
|
7 |
+
* Standard: PSR-2
|
8 |
+
* @link http://www.php-fig.org/psr/psr-2
|
9 |
+
*
|
10 |
+
* @package Duplicator
|
11 |
+
* @subpackage classes/utilities
|
12 |
+
* @copyright (c) 2017, Snapcreek LLC
|
13 |
+
*
|
14 |
+
*/
|
15 |
+
// Exit if accessed directly
|
16 |
+
if (!defined('DUPLICATOR_VERSION')) {
|
17 |
+
exit;
|
18 |
+
}
|
19 |
+
|
20 |
+
class DUP_DB extends wpdb
|
21 |
+
{
|
22 |
+
const MAX_TABLE_COUNT_IN_PACKET = 100;
|
23 |
+
public static $remove_placeholder_escape_exists = null;
|
24 |
+
public static function init()
|
25 |
+
{
|
26 |
+
global $wpdb;
|
27 |
+
self::$remove_placeholder_escape_exists = method_exists($wpdb, 'remove_placeholder_escape');
|
28 |
+
}
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Get the requested MySQL system variable
|
32 |
+
*
|
33 |
+
* @param string $name The database variable name to lookup
|
34 |
+
*
|
35 |
+
* @return string the server variable to query for
|
36 |
+
*/
|
37 |
+
public static function getVariable($name)
|
38 |
+
{
|
39 |
+
global $wpdb;
|
40 |
+
if (strlen($name)) {
|
41 |
+
$row = $wpdb->get_row("SHOW VARIABLES LIKE '{$name}'", ARRAY_N);
|
42 |
+
return isset($row[1]) ? $row[1] : null;
|
43 |
+
} else {
|
44 |
+
return null;
|
45 |
+
}
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Gets the MySQL database version number
|
50 |
+
*
|
51 |
+
* @param bool $full True: Gets the full version
|
52 |
+
* False: Gets only the numeric portion i.e. 5.5.6 or 10.1.2 (for MariaDB)
|
53 |
+
*
|
54 |
+
* @return false|string 0 on failure, version number on success
|
55 |
+
*/
|
56 |
+
public static function getVersion($full = false)
|
57 |
+
{
|
58 |
+
global $wpdb;
|
59 |
+
if ($full) {
|
60 |
+
$version = self::getVariable('version');
|
61 |
+
} else {
|
62 |
+
$version = preg_replace('/[^0-9.].*/', '', self::getVariable('version'));
|
63 |
+
}
|
64 |
+
|
65 |
+
//Fall-back for servers that have restricted SQL for SHOW statement
|
66 |
+
if (empty($version)) {
|
67 |
+
$version = $wpdb->db_version();
|
68 |
+
}
|
69 |
+
|
70 |
+
return empty($version) ? 0 : $version;
|
71 |
+
}
|
72 |
+
|
73 |
+
/**
|
74 |
+
* Try to return the mysqldump path on Windows servers
|
75 |
+
*
|
76 |
+
* @return boolean|string
|
77 |
+
*/
|
78 |
+
public static function getWindowsMySqlDumpRealPath()
|
79 |
+
{
|
80 |
+
if (function_exists('php_ini_loaded_file')) {
|
81 |
+
$get_php_ini_path = php_ini_loaded_file();
|
82 |
+
if (file_exists($get_php_ini_path)) {
|
83 |
+
$search = array(
|
84 |
+
dirname(dirname($get_php_ini_path)) . '/mysql/bin/mysqldump.exe',
|
85 |
+
dirname(dirname(dirname($get_php_ini_path))) . '/mysql/bin/mysqldump.exe',
|
86 |
+
dirname(dirname($get_php_ini_path)) . '/mysql/bin/mysqldump',
|
87 |
+
dirname(dirname(dirname($get_php_ini_path))) . '/mysql/bin/mysqldump',
|
88 |
+
);
|
89 |
+
foreach ($search as $mysqldump) {
|
90 |
+
if (file_exists($mysqldump)) {
|
91 |
+
return str_replace("\\", "/", $mysqldump);
|
92 |
+
}
|
93 |
+
}
|
94 |
+
}
|
95 |
+
}
|
96 |
+
|
97 |
+
unset($search);
|
98 |
+
unset($get_php_ini_path);
|
99 |
+
return false;
|
100 |
+
}
|
101 |
+
|
102 |
+
/**
|
103 |
+
* Returns the correct database build mode PHP, MYSQLDUMP, PHPCHUNKING
|
104 |
+
*
|
105 |
+
* @return string Returns a string with one of theses three values PHP, MYSQLDUMP, PHPCHUNKING
|
106 |
+
*/
|
107 |
+
public static function getBuildMode()
|
108 |
+
{
|
109 |
+
$package_mysqldump = DUP_Settings::Get('package_mysqldump');
|
110 |
+
$mysqlDumpPath = DUP_DB::getMySqlDumpPath();
|
111 |
+
return ($mysqlDumpPath && $package_mysqldump) ? 'MYSQLDUMP' : 'PHP';
|
112 |
+
}
|
113 |
+
|
114 |
+
/**
|
115 |
+
* Returns the mysqldump path if the server is enabled to execute it otherwise false
|
116 |
+
*
|
117 |
+
* @return boolean|string
|
118 |
+
*/
|
119 |
+
public static function getMySqlDumpPath()
|
120 |
+
{
|
121 |
+
//Is shell_exec possible
|
122 |
+
if (!DUP_Util::hasShellExec()) {
|
123 |
+
return false;
|
124 |
+
}
|
125 |
+
|
126 |
+
$custom_mysqldump_path = DUP_Settings::Get('package_mysqldump_path');
|
127 |
+
$custom_mysqldump_path = (strlen($custom_mysqldump_path)) ? $custom_mysqldump_path : '';
|
128 |
+
//Common Windows Paths
|
129 |
+
if (DUP_Util::isWindows()) {
|
130 |
+
$paths = array(
|
131 |
+
$custom_mysqldump_path,
|
132 |
+
'mysqldump.exe',
|
133 |
+
self::getWindowsMySqlDumpRealPath(),
|
134 |
+
'C:/xampp/mysql/bin/mysqldump.exe',
|
135 |
+
'C:/Program Files/xampp/mysql/bin/mysqldump',
|
136 |
+
'C:/Program Files/MySQL/MySQL Server 6.0/bin/mysqldump',
|
137 |
+
'C:/Program Files/MySQL/MySQL Server 5.5/bin/mysqldump',
|
138 |
+
'C:/Program Files/MySQL/MySQL Server 5.4/bin/mysqldump'
|
139 |
+
);
|
140 |
+
//Common Linux Paths
|
141 |
+
} else {
|
142 |
+
$paths = array(
|
143 |
+
$custom_mysqldump_path,
|
144 |
+
'mysqldump',
|
145 |
+
'/usr/local/bin/mysqldump',
|
146 |
+
'/usr/local/mysql/bin/mysqldump',
|
147 |
+
'/usr/mysql/bin/mysqldump',
|
148 |
+
'/usr/bin/mysqldump',
|
149 |
+
'/opt/local/lib/mysql6/bin/mysqldump',
|
150 |
+
'/opt/local/lib/mysql5/bin/mysqldump',
|
151 |
+
'/usr/bin/mysqldump',
|
152 |
+
);
|
153 |
+
}
|
154 |
+
|
155 |
+
$exec_available = function_exists('exec');
|
156 |
+
foreach ($paths as $path) {
|
157 |
+
if (@file_exists($path)) {
|
158 |
+
if (DUP_Util::isExecutable($path)) {
|
159 |
+
return $path;
|
160 |
+
}
|
161 |
+
} elseif ($exec_available) {
|
162 |
+
$out = array();
|
163 |
+
$rc = -1;
|
164 |
+
$cmd = $path . ' --help';
|
165 |
+
@exec($cmd, $out, $rc);
|
166 |
+
if ($rc === 0) {
|
167 |
+
return $path;
|
168 |
+
}
|
169 |
+
}
|
170 |
+
}
|
171 |
+
|
172 |
+
return false;
|
173 |
+
}
|
174 |
+
|
175 |
+
/**
|
176 |
+
* Get Sql query to create table which is given.
|
177 |
+
*
|
178 |
+
* @param string $table Table name
|
179 |
+
* @return string mysql query create table
|
180 |
+
*/
|
181 |
+
private static function getCreateTableQuery($table)
|
182 |
+
{
|
183 |
+
$row = $GLOBALS['wpdb']->get_row('SHOW CREATE TABLE `' . esc_sql($table) . '`', ARRAY_N);
|
184 |
+
return $row[1];
|
185 |
+
}
|
186 |
+
|
187 |
+
/**
|
188 |
+
* Returns all collation types that are assigned to the tables in
|
189 |
+
* the current database. Each element in the array is unique
|
190 |
+
*
|
191 |
+
* @param array $tables A list of tables to include from the search
|
192 |
+
*
|
193 |
+
* @return array Returns an array with all the character set being used
|
194 |
+
*/
|
195 |
+
public static function getTableCharSetList($tables)
|
196 |
+
{
|
197 |
+
$charSets = array();
|
198 |
+
try {
|
199 |
+
foreach ($tables as $table) {
|
200 |
+
$createTableQuery = self::getCreateTableQuery($table);
|
201 |
+
if (preg_match('/ CHARSET=([^\s;]+)/i', $createTableQuery, $charsetMatch)) {
|
202 |
+
if (!in_array($charsetMatch[1], $charSets)) {
|
203 |
+
$charSets[] = $charsetMatch[1];
|
204 |
+
}
|
205 |
+
}
|
206 |
+
}
|
207 |
+
return $charSets;
|
208 |
+
} catch (Exception $ex) {
|
209 |
+
return $charSets;
|
210 |
+
}
|
211 |
+
}
|
212 |
+
|
213 |
+
/**
|
214 |
+
* Returns all collation types that are assigned to the tables and columns table in
|
215 |
+
* the current database. Each element in the array is unique
|
216 |
+
*
|
217 |
+
* @param array &$tablesToInclude A list of tables to include in the search.
|
218 |
+
* Parameter does not change in the function, is passed by reference only to avoid copying.
|
219 |
+
*
|
220 |
+
* @return array Returns an array with all the collation types being used
|
221 |
+
*/
|
222 |
+
public static function getTableCollationList($tablesToInclude)
|
223 |
+
{
|
224 |
+
global $wpdb;
|
225 |
+
static $collations = null;
|
226 |
+
if (is_null($collations)) {
|
227 |
+
$collations = array();
|
228 |
+
//use half the number of tables since we are using them twice
|
229 |
+
foreach (array_chunk($tablesToInclude, self::MAX_TABLE_COUNT_IN_PACKET) as $tablesChunk) {
|
230 |
+
$sqlTables = implode(",", array_map(array(__CLASS__, 'escValueToQueryString'), $tablesChunk));
|
231 |
+
//UNION is by default DISTINCT
|
232 |
+
$query = "SELECT `COLLATION_NAME` FROM `information_schema`.`columns` WHERE `COLLATION_NAME` IS NOT NULL AND `table_schema` = '{$wpdb->dbname}' "
|
233 |
+
. "AND `table_name` in (" . $sqlTables . ")"
|
234 |
+
. "UNION SELECT `TABLE_COLLATION` FROM `information_schema`.`tables` WHERE `TABLE_COLLATION` IS NOT NULL AND `table_schema` = '{$wpdb->dbname}' "
|
235 |
+
. "AND `table_name` in (" . $sqlTables . ")";
|
236 |
+
if (!$wpdb->query($query)) {
|
237 |
+
DUP_Log::Info("GET TABLE COLLATION ERROR: " . $wpdb->last_error);
|
238 |
+
continue;
|
239 |
+
}
|
240 |
+
|
241 |
+
$collations = array_unique(array_merge($collations, $wpdb->get_col()));
|
242 |
+
}
|
243 |
+
sort($collations);
|
244 |
+
}
|
245 |
+
return $collations;
|
246 |
+
}
|
247 |
+
|
248 |
+
/**
|
249 |
+
* Returns list of MySQL engines used by $tablesToInclude in the current DB
|
250 |
+
*
|
251 |
+
* @param string[] $tablesToInclude tables to check the engines for
|
252 |
+
* @return null
|
253 |
+
* @throws Exception
|
254 |
+
*/
|
255 |
+
public static function getTableEngineList($tablesToInclude)
|
256 |
+
{
|
257 |
+
global $wpdb;
|
258 |
+
static $engines = null;
|
259 |
+
if (is_null($engines)) {
|
260 |
+
$engines = array();
|
261 |
+
foreach (array_chunk($tablesToInclude, self::MAX_TABLE_COUNT_IN_PACKET) as $tablesChunk) {
|
262 |
+
$query = "SELECT DISTINCT `ENGINE` FROM `information_schema`.`tables` WHERE `ENGINE` IS NOT NULL AND `table_schema` = '{$wpdb->dbname}' "
|
263 |
+
. "AND `table_name` in (" . implode(",", array_map(array(__CLASS__, 'escValueToQueryString'), $tablesChunk)) . ")";
|
264 |
+
if (!$wpdb->query($query)) {
|
265 |
+
DUP_Log::info("GET TABLE ENGINES ERROR: " . $wpdb->last_error);
|
266 |
+
}
|
267 |
+
|
268 |
+
$engines = array_unique(array_merge($engines, $wpdb->get_col($query)));
|
269 |
+
}
|
270 |
+
}
|
271 |
+
|
272 |
+
return $engines;
|
273 |
+
}
|
274 |
+
|
275 |
+
/**
|
276 |
+
* Returns an escaped SQL string
|
277 |
+
*
|
278 |
+
* @param string $sql The SQL to escape
|
279 |
+
* @param bool $removePlaceholderEscape Patch for how the default WP function works.
|
280 |
+
*
|
281 |
+
* @return boolean|string
|
282 |
+
* @also see: https://make.wordpress.org/core/2017/10/31/changed-behaviour-of-esc_sql-in-wordpress-4-8-3/
|
283 |
+
*/
|
284 |
+
public static function escSQL($sql, $removePlaceholderEscape = false)
|
285 |
+
{
|
286 |
+
global $wpdb;
|
287 |
+
$removePlaceholderEscape = $removePlaceholderEscape && self::$remove_placeholder_escape_exists;
|
288 |
+
if ($removePlaceholderEscape) {
|
289 |
+
return $wpdb->remove_placeholder_escape(@esc_sql($sql));
|
290 |
+
} else {
|
291 |
+
return @esc_sql($sql);
|
292 |
+
}
|
293 |
+
}
|
294 |
+
|
295 |
+
/**
|
296 |
+
* this function escape sql string without add and remove remove_placeholder_escape
|
297 |
+
* doesn't work on array
|
298 |
+
*
|
299 |
+
* @global type $wpdb
|
300 |
+
* @param mixed $sql
|
301 |
+
* @return string
|
302 |
+
*/
|
303 |
+
public static function escValueToQueryString($value)
|
304 |
+
{
|
305 |
+
global $wpdb;
|
306 |
+
if (is_null($value)) {
|
307 |
+
return 'NULL';
|
308 |
+
}
|
309 |
+
|
310 |
+
if ($wpdb->use_mysqli) {
|
311 |
+
return '"' . mysqli_real_escape_string($wpdb->dbh, $value) . '"';
|
312 |
+
} else {
|
313 |
+
return '"' . mysql_real_escape_string($value, $wpdb->dbh) . '"';
|
314 |
+
}
|
315 |
+
}
|
316 |
+
}
|
classes/class.io.php
CHANGED
@@ -1,121 +1,18 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
|
4 |
-
*
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
*/
|
20 |
-
public static function deleteFile($file)
|
21 |
-
{
|
22 |
-
if (file_exists($file)) {
|
23 |
-
if (@unlink($file) === false) {
|
24 |
-
DUP_Log::Info("Duplicator could not delete file: '{$file}'");
|
25 |
-
return false;
|
26 |
-
}
|
27 |
-
}
|
28 |
-
return true;
|
29 |
-
}
|
30 |
-
|
31 |
-
/**
|
32 |
-
* Removes a directory recursively except for the root of a WP Site
|
33 |
-
*
|
34 |
-
* @param string $directory The full filepath to the directory to remove
|
35 |
-
*
|
36 |
-
* @return TRUE on success FALSE on failure
|
37 |
-
*/
|
38 |
-
public static function deleteTree($directory)
|
39 |
-
{
|
40 |
-
$success = true;
|
41 |
-
|
42 |
-
if(!file_exists("{$directory}/wp-config.php")) {
|
43 |
-
$filenames = array_diff(scandir($directory), array('.', '..'));
|
44 |
-
|
45 |
-
foreach ($filenames as $filename) {
|
46 |
-
if (is_dir("$directory/$filename")) {
|
47 |
-
$success = self::deleteTree("$directory/$filename");
|
48 |
-
} else {
|
49 |
-
$success = @unlink("$directory/$filename");
|
50 |
-
}
|
51 |
-
|
52 |
-
if ($success === false) {
|
53 |
-
break;
|
54 |
-
}
|
55 |
-
}
|
56 |
-
} else {
|
57 |
-
return false;
|
58 |
-
}
|
59 |
-
|
60 |
-
return $success && @rmdir($directory);
|
61 |
-
}
|
62 |
-
|
63 |
-
/**
|
64 |
-
* Safely copies a file to a directory
|
65 |
-
*
|
66 |
-
* @param string $source_file The full filepath to the file to copy
|
67 |
-
* @param string $dest_dir The full path to the destination directory were the file will be copied
|
68 |
-
* @param string $delete_first Delete file before copying the new one
|
69 |
-
*
|
70 |
-
* @return TRUE on success or if file does not exist. FALSE on failure
|
71 |
-
*/
|
72 |
-
public static function copyFile($source_file, $dest_dir, $delete_first = false)
|
73 |
-
{
|
74 |
-
//Create directory
|
75 |
-
if (file_exists($dest_dir) == false)
|
76 |
-
{
|
77 |
-
if (wp_mkdir_p($dest_dir) === false) {
|
78 |
-
return false;
|
79 |
-
}
|
80 |
-
}
|
81 |
-
|
82 |
-
//Remove file with same name before copy
|
83 |
-
$filename = basename($source_file);
|
84 |
-
$dest_filepath = $dest_dir . "/$filename";
|
85 |
-
if($delete_first)
|
86 |
-
{
|
87 |
-
self::deleteFile($dest_filepath);
|
88 |
-
}
|
89 |
-
|
90 |
-
return copy($source_file, $dest_filepath);
|
91 |
-
}
|
92 |
-
|
93 |
-
/**
|
94 |
-
* Prepends data to an existing file
|
95 |
-
*
|
96 |
-
* @param string $file The full file path to the file
|
97 |
-
* @param string $content The content to prepend to the file
|
98 |
-
*
|
99 |
-
* @return TRUE on success or if file does not exist. FALSE on failure
|
100 |
-
*/
|
101 |
-
public static function fwritePrepend($file, $prepend)
|
102 |
-
{
|
103 |
-
if (!file_exists($file) || !is_writable($file)) {
|
104 |
-
return false;
|
105 |
-
}
|
106 |
-
|
107 |
-
$handle = fopen($file, "r+");
|
108 |
-
$len = strlen($prepend);
|
109 |
-
$final_len = filesize($file) + $len;
|
110 |
-
$cache_old = fread($handle, $len);
|
111 |
-
rewind($handle);
|
112 |
-
$i = 1;
|
113 |
-
while (ftell($handle) < $final_len) {
|
114 |
-
fwrite($handle, $prepend);
|
115 |
-
$prepend = $cache_old;
|
116 |
-
$cache_old = fread($handle, $len);
|
117 |
-
fseek($handle, $i * $len);
|
118 |
-
$i++;
|
119 |
-
}
|
120 |
-
}
|
121 |
-
}
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @copyright 2022 Snap Creek LLC
|
4 |
+
* Class for all IO operations
|
5 |
+
*/
|
6 |
+
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
7 |
+
if (! defined('DUPLICATOR_VERSION')) {
|
8 |
+
exit;
|
9 |
+
}
|
10 |
+
|
11 |
+
/**
|
12 |
+
* This class is deprecated. Please use:
|
13 |
+
* Duplicator\Libs\Snap\SnapIO
|
14 |
+
*/
|
15 |
+
class DUP_IO
|
16 |
+
{
|
17 |
+
/* !!DO NOT IMPLMENT HERE!! */
|
18 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
classes/class.logging.php
CHANGED
@@ -1,7 +1,12 @@
|
|
1 |
<?php
|
|
|
|
|
|
|
2 |
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
3 |
// Exit if accessed directly
|
4 |
-
if (!defined('DUPLICATOR_VERSION'))
|
|
|
|
|
5 |
|
6 |
/**
|
7 |
* Helper Class for logging
|
@@ -9,9 +14,9 @@ if (!defined('DUPLICATOR_VERSION')) exit;
|
|
9 |
*/
|
10 |
abstract class Dup_ErrorBehavior
|
11 |
{
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
}
|
16 |
|
17 |
class DUP_Log
|
@@ -27,14 +32,14 @@ class DUP_Log
|
|
27 |
private static $traceEnabled = false;
|
28 |
|
29 |
public static $profileLogs = null;
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
|
39 |
/**
|
40 |
* Open a log file connection for writing to the package log file
|
@@ -49,7 +54,7 @@ class DUP_Log
|
|
49 |
throw new Exception("A name value is required to open a file log.");
|
50 |
}
|
51 |
self::Close();
|
52 |
-
if ((self::$logFileHandle = @fopen(DUP_Settings::getSsdirPath()."/{$nameHash}.log", "a+")) === false) {
|
53 |
self::$logFileHandle = null;
|
54 |
return false;
|
55 |
} else {
|
@@ -69,7 +74,7 @@ class DUP_Log
|
|
69 |
public static function Close()
|
70 |
{
|
71 |
$result = true;
|
72 |
-
|
73 |
if (!is_null(self::$logFileHandle)) {
|
74 |
$result = @fclose(self::$logFileHandle);
|
75 |
self::$logFileHandle = null;
|
@@ -81,13 +86,13 @@ class DUP_Log
|
|
81 |
|
82 |
/**
|
83 |
* General information send to the package log if opened
|
84 |
-
* @param string $msg
|
85 |
*
|
86 |
* REPLACE TO DEBUG: Memory consumption as script runs
|
87 |
-
*
|
88 |
-
*
|
89 |
*
|
90 |
-
* @param string $msg
|
91 |
*
|
92 |
* @return null
|
93 |
*/
|
@@ -97,160 +102,172 @@ class DUP_Log
|
|
97 |
self::Trace($msg);
|
98 |
}
|
99 |
if (!is_null(self::$logFileHandle)) {
|
100 |
-
@fwrite(self::$logFileHandle, $msg."\n");
|
101 |
}
|
102 |
}
|
103 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
104 |
public static function print_r_info($val, $name = '')
|
105 |
{
|
106 |
-
$msg
|
107 |
$msg .= print_r($val, true);
|
108 |
self::info($msg);
|
109 |
}
|
110 |
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
if (file_exists($file_path)) {
|
133 |
|
|
|
134 |
$filesize = is_file($file_path) ? @filesize($file_path) : 0;
|
135 |
|
136 |
//Its possible mulitple trace log files exist due to size
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
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 |
public static function print_r_trace($val, $name = '', $calling_function_override = null)
|
183 |
{
|
184 |
-
$msg
|
185 |
$msg .= print_r($val, true);
|
186 |
self::trace($msg, $calling_function_override);
|
187 |
}
|
188 |
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
213 |
|
214 |
/**
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
* Deletes the trace log and backup trace log files
|
242 |
-
*
|
243 |
-
* @return null
|
244 |
-
*/
|
245 |
-
public static function DeleteTraceLog()
|
246 |
-
{
|
247 |
-
$file_path = self::GetTraceFilepath();
|
248 |
-
$backup_path = self::GetBackupTraceFilepath();
|
249 |
-
self::trace("deleting $file_path");
|
250 |
-
@unlink($file_path);
|
251 |
-
self::trace("deleting $backup_path");
|
252 |
-
@unlink($backup_path);
|
253 |
-
}
|
254 |
|
255 |
/**
|
256 |
* Called when an error is detected and no further processing should occur
|
@@ -259,84 +276,83 @@ class DUP_Log
|
|
259 |
* @param int $behavior
|
260 |
* @throws Exception
|
261 |
*/
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
DUP_LOG::trace("throwing exception");
|
284 |
throw new Exception($msg);
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
}
|
341 |
|
342 |
class DUP_Handler
|
@@ -415,18 +431,18 @@ class DUP_Handler
|
|
415 |
}
|
416 |
break;
|
417 |
case self::MODE_VAR:
|
418 |
-
self::$varModeLog .= self::getMessage($errno, $errstr, $errfile, $errline)."\n";
|
419 |
break;
|
420 |
case self::MODE_LOG:
|
421 |
default:
|
422 |
switch ($errno) {
|
423 |
-
case E_ERROR
|
424 |
$log_message = self::getMessage($errno, $errstr, $errfile, $errline);
|
425 |
DUP_Log::error($log_message);
|
426 |
break;
|
427 |
-
case E_NOTICE
|
428 |
-
case E_WARNING
|
429 |
-
default
|
430 |
$log_message = self::getMessage($errno, $errstr, $errfile, $errline);
|
431 |
DUP_Log::Info($log_message);
|
432 |
break;
|
@@ -441,16 +457,16 @@ class DUP_Handler
|
|
441 |
if (self::$errPrefix) {
|
442 |
$result = '[PHP ERR]';
|
443 |
switch ($errno) {
|
444 |
-
case E_ERROR
|
445 |
$result .= '[FATAL]';
|
446 |
break;
|
447 |
-
case E_WARNING
|
448 |
$result .= '[WARN]';
|
449 |
break;
|
450 |
-
case E_NOTICE
|
451 |
$result .= '[NOTICE]';
|
452 |
break;
|
453 |
-
default
|
454 |
$result .= '[ISSUE]';
|
455 |
break;
|
456 |
}
|
@@ -460,8 +476,8 @@ class DUP_Handler
|
|
460 |
$result .= $errstr;
|
461 |
|
462 |
if (self::$codeReference) {
|
463 |
-
$result .= ' [CODE:'
|
464 |
-
$result .= "\n".wp_debug_backtrace_summary();
|
465 |
}
|
466 |
|
467 |
return $result;
|
1 |
<?php
|
2 |
+
|
3 |
+
use Duplicator\Libs\Snap\SnapUtil;
|
4 |
+
|
5 |
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
6 |
// Exit if accessed directly
|
7 |
+
if (!defined('DUPLICATOR_VERSION')) {
|
8 |
+
exit;
|
9 |
+
}
|
10 |
|
11 |
/**
|
12 |
* Helper Class for logging
|
14 |
*/
|
15 |
abstract class Dup_ErrorBehavior
|
16 |
{
|
17 |
+
const LogOnly = 0;
|
18 |
+
const ThrowException = 1;
|
19 |
+
const Quit = 2;
|
20 |
}
|
21 |
|
22 |
class DUP_Log
|
32 |
private static $traceEnabled = false;
|
33 |
|
34 |
public static $profileLogs = null;
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Init this static object
|
38 |
+
*/
|
39 |
+
public static function Init()
|
40 |
+
{
|
41 |
+
self::$traceEnabled = (DUP_Settings::Get('trace_log_enabled') == 1);
|
42 |
+
}
|
43 |
|
44 |
/**
|
45 |
* Open a log file connection for writing to the package log file
|
54 |
throw new Exception("A name value is required to open a file log.");
|
55 |
}
|
56 |
self::Close();
|
57 |
+
if ((self::$logFileHandle = @fopen(DUP_Settings::getSsdirPath() . "/{$nameHash}.log", "a+")) === false) {
|
58 |
self::$logFileHandle = null;
|
59 |
return false;
|
60 |
} else {
|
74 |
public static function Close()
|
75 |
{
|
76 |
$result = true;
|
77 |
+
|
78 |
if (!is_null(self::$logFileHandle)) {
|
79 |
$result = @fclose(self::$logFileHandle);
|
80 |
self::$logFileHandle = null;
|
86 |
|
87 |
/**
|
88 |
* General information send to the package log if opened
|
89 |
+
* @param string $msg The message to log
|
90 |
*
|
91 |
* REPLACE TO DEBUG: Memory consumption as script runs
|
92 |
+
* $results = DUP_Util::byteSize(memory_get_peak_usage(true)) . "\t" . $msg;
|
93 |
+
* @fwrite(self::$logFileHandle, "{$results} \n");
|
94 |
*
|
95 |
+
* @param string $msg The message to log
|
96 |
*
|
97 |
* @return null
|
98 |
*/
|
102 |
self::Trace($msg);
|
103 |
}
|
104 |
if (!is_null(self::$logFileHandle)) {
|
105 |
+
@fwrite(self::$logFileHandle, $msg . "\n");
|
106 |
}
|
107 |
}
|
108 |
|
109 |
+
/**
|
110 |
+
* General information send to the package log and trace log
|
111 |
+
*
|
112 |
+
* @param string $msg The message to log
|
113 |
+
*
|
114 |
+
* @return null
|
115 |
+
*/
|
116 |
+
public static function infoTrace($msg, $calling_function_override = null)
|
117 |
+
{
|
118 |
+
self::Info($msg);
|
119 |
+
self::Trace($msg, $calling_function_override);
|
120 |
+
}
|
121 |
+
|
122 |
public static function print_r_info($val, $name = '')
|
123 |
{
|
124 |
+
$msg = empty($name) ? '' : 'VALUE ' . $name . ': ';
|
125 |
$msg .= print_r($val, true);
|
126 |
self::info($msg);
|
127 |
}
|
128 |
|
129 |
+
/**
|
130 |
+
* Does the trace file exists
|
131 |
+
*
|
132 |
+
* @return bool Returns true if an active trace file exists
|
133 |
+
*/
|
134 |
+
public static function TraceFileExists()
|
135 |
+
{
|
136 |
+
$file_path = self::getTraceFilepath();
|
137 |
+
return file_exists($file_path);
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Gets the current file size of the active trace file
|
142 |
+
*
|
143 |
+
* @return string Returns a human readable file size of the active trace file
|
144 |
+
*/
|
145 |
+
public static function getTraceStatus()
|
146 |
+
{
|
147 |
+
$file_path = DUP_Log::getTraceFilepath();
|
148 |
+
$backup_path = DUP_Log::getBackupTraceFilepath();
|
|
|
|
|
149 |
|
150 |
+
if (file_exists($file_path)) {
|
151 |
$filesize = is_file($file_path) ? @filesize($file_path) : 0;
|
152 |
|
153 |
//Its possible mulitple trace log files exist due to size
|
154 |
+
if (is_file($backup_path)) {
|
155 |
+
$filesize += @filesize($backup_path);
|
156 |
+
}
|
157 |
+
|
158 |
+
$message = sprintf('%1$s', DUP_Util::byteSize($filesize));
|
159 |
+
} else {
|
160 |
+
$message = esc_html__('No Log', 'duplicator');
|
161 |
+
}
|
162 |
+
|
163 |
+
return $message;
|
164 |
+
}
|
165 |
+
|
166 |
+
// RSR TODO: Swap trace logic out for real trace later
|
167 |
+
public static function Trace($message, $calling_function_override = null)
|
168 |
+
{
|
169 |
+
|
170 |
+
if (self::$traceEnabled) {
|
171 |
+
$unique_id = sprintf("%08x", abs(crc32($_SERVER['REMOTE_ADDR'] . $_SERVER['REQUEST_TIME'] . $_SERVER['REMOTE_PORT'])));
|
172 |
+
|
173 |
+
if ($calling_function_override == null) {
|
174 |
+
$calling_function = SnapUtil::getCallingFunctionName();
|
175 |
+
} else {
|
176 |
+
$calling_function = $calling_function_override;
|
177 |
+
}
|
178 |
+
|
179 |
+
if (is_object($message)) {
|
180 |
+
$ov = get_object_vars($message);
|
181 |
+
$message = print_r($ov, true);
|
182 |
+
} elseif (is_array($message)) {
|
183 |
+
$message = print_r($message, true);
|
184 |
+
}
|
185 |
+
|
186 |
+
$logging_message = "{$unique_id}|{$calling_function} | {$message}";
|
187 |
+
$ticks = time() + ((int) get_option('gmt_offset') * 3600);
|
188 |
+
$formatted_time = date('d-m-H:i:s', $ticks);
|
189 |
+
$formatted_logging_message = "{$formatted_time}|DUP|{$logging_message} \r\n";
|
190 |
+
|
191 |
+
// Always write to error log - if they don't want the info they can turn off WordPress error logging or tracing
|
192 |
+
self::ErrLog($logging_message);
|
193 |
+
|
194 |
+
// Everything goes to the plugin log, whether it's part of package generation or not.
|
195 |
+
self::WriteToTrace($formatted_logging_message);
|
196 |
+
}
|
197 |
+
}
|
198 |
|
199 |
public static function print_r_trace($val, $name = '', $calling_function_override = null)
|
200 |
{
|
201 |
+
$msg = empty($name) ? '' : 'VALUE ' . $name . ': ';
|
202 |
$msg .= print_r($val, true);
|
203 |
self::trace($msg, $calling_function_override);
|
204 |
}
|
205 |
|
206 |
+
public static function errLog($message)
|
207 |
+
{
|
208 |
+
$message = 'DUP:' . $message;
|
209 |
+
error_log($message);
|
210 |
+
}
|
211 |
+
|
212 |
+
public static function TraceObject($msg, $o, $log_private_members = true)
|
213 |
+
{
|
214 |
+
if (self::$traceEnabled) {
|
215 |
+
if (!$log_private_members) {
|
216 |
+
$o = get_object_vars($o);
|
217 |
+
}
|
218 |
+
self::Trace($msg . ':' . print_r($o, true));
|
219 |
+
}
|
220 |
+
}
|
221 |
+
|
222 |
+
public static function GetDefaultKey()
|
223 |
+
{
|
224 |
+
$auth_key = defined('AUTH_KEY') ? AUTH_KEY : 'atk';
|
225 |
+
$auth_key .= defined('DB_HOST') ? DB_HOST : 'dbh';
|
226 |
+
$auth_key .= defined('DB_NAME') ? DB_NAME : 'dbn';
|
227 |
+
$auth_key .= defined('DB_USER') ? DB_USER : 'dbu';
|
228 |
+
return hash('md5', $auth_key);
|
229 |
+
}
|
230 |
+
|
231 |
+
/**
|
232 |
+
* Gets the current file size of the old trace file "1"
|
233 |
+
*
|
234 |
+
* @return string Returns a human readable file size of the active trace file
|
235 |
+
*/
|
236 |
+
public static function GetBackupTraceFilepath()
|
237 |
+
{
|
238 |
+
$default_key = self::getDefaultKey();
|
239 |
+
$backup_log_filename = "dup_$default_key.log1";
|
240 |
+
$backup_path = DUP_Settings::getSsdirPath() . "/" . $backup_log_filename;
|
241 |
+
return $backup_path;
|
242 |
+
}
|
243 |
|
244 |
/**
|
245 |
+
* Gets the active trace file path
|
246 |
+
*
|
247 |
+
* @return string Returns the full path to the active trace file (i.e. dup-pro_hash.log)
|
248 |
+
*/
|
249 |
+
public static function GetTraceFilepath()
|
250 |
+
{
|
251 |
+
$default_key = self::getDefaultKey();
|
252 |
+
$log_filename = "dup_$default_key.log";
|
253 |
+
$file_path = DUP_Settings::getSsdirPath() . "/" . $log_filename;
|
254 |
+
return $file_path;
|
255 |
+
}
|
256 |
+
|
257 |
+
/**
|
258 |
+
* Deletes the trace log and backup trace log files
|
259 |
+
*
|
260 |
+
* @return null
|
261 |
+
*/
|
262 |
+
public static function DeleteTraceLog()
|
263 |
+
{
|
264 |
+
$file_path = self::GetTraceFilepath();
|
265 |
+
$backup_path = self::GetBackupTraceFilepath();
|
266 |
+
self::trace("deleting $file_path");
|
267 |
+
@unlink($file_path);
|
268 |
+
self::trace("deleting $backup_path");
|
269 |
+
@unlink($backup_path);
|
270 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
271 |
|
272 |
/**
|
273 |
* Called when an error is detected and no further processing should occur
|
276 |
* @param int $behavior
|
277 |
* @throws Exception
|
278 |
*/
|
279 |
+
public static function error($msg, $detail = '', $behavior = Dup_ErrorBehavior::Quit)
|
280 |
+
{
|
281 |
+
|
282 |
+
error_log($msg . ' DETAIL:' . $detail);
|
283 |
+
$source = self::getStack(debug_backtrace());
|
284 |
+
|
285 |
+
$err_msg = "\n==================================================================================\n";
|
286 |
+
$err_msg .= "DUPLICATOR ERROR\n";
|
287 |
+
$err_msg .= "Please try again! If the error persists see the Duplicator 'Help' menu.\n";
|
288 |
+
$err_msg .= "---------------------------------------------------------------------------------\n";
|
289 |
+
$err_msg .= "MESSAGE:\n\t{$msg}\n";
|
290 |
+
if (strlen($detail)) {
|
291 |
+
$err_msg .= "DETAILS:\n\t{$detail}\n";
|
292 |
+
}
|
293 |
+
$err_msg .= "TRACE:\n{$source}";
|
294 |
+
$err_msg .= "==================================================================================\n\n";
|
295 |
+
@fwrite(self::$logFileHandle, "{$err_msg}");
|
296 |
+
|
297 |
+
switch ($behavior) {
|
298 |
+
case Dup_ErrorBehavior::ThrowException:
|
299 |
+
DUP_LOG::trace("throwing exception");
|
|
|
300 |
throw new Exception($msg);
|
301 |
+
break;
|
302 |
+
|
303 |
+
case Dup_ErrorBehavior::Quit:
|
304 |
+
DUP_LOG::trace("quitting");
|
305 |
+
die("DUPLICATOR ERROR: Please see the 'Package Log' file link below.");
|
306 |
+
break;
|
307 |
+
|
308 |
+
default:
|
309 |
+
// Nothing
|
310 |
+
}
|
311 |
+
}
|
312 |
+
|
313 |
+
/**
|
314 |
+
* The current stack trace of a PHP call
|
315 |
+
* @param $stacktrace The current debug stack
|
316 |
+
* @return string
|
317 |
+
*/
|
318 |
+
public static function getStack($stacktrace)
|
319 |
+
{
|
320 |
+
$output = "";
|
321 |
+
$i = 1;
|
322 |
+
foreach ($stacktrace as $node) {
|
323 |
+
$output .= "\t $i. " . basename($node['file']) . " : " . $node['function'] . " (" . $node['line'] . ")\n";
|
324 |
+
$i++;
|
325 |
+
}
|
326 |
+
return $output;
|
327 |
+
}
|
328 |
+
|
329 |
+
/**
|
330 |
+
* Manages writing the active or backup log based on the size setting
|
331 |
+
*
|
332 |
+
* @return null
|
333 |
+
*/
|
334 |
+
private static function WriteToTrace($formatted_logging_message)
|
335 |
+
{
|
336 |
+
$log_filepath = self::GetTraceFilepath();
|
337 |
+
|
338 |
+
if (@filesize($log_filepath) > DUPLICATOR_MAX_LOG_SIZE) {
|
339 |
+
$backup_log_filepath = self::GetBackupTraceFilepath();
|
340 |
+
|
341 |
+
if (file_exists($backup_log_filepath)) {
|
342 |
+
if (@unlink($backup_log_filepath) === false) {
|
343 |
+
self::errLog("Couldn't delete backup log $backup_log_filepath");
|
344 |
+
}
|
345 |
+
}
|
346 |
+
|
347 |
+
if (@rename($log_filepath, $backup_log_filepath) === false) {
|
348 |
+
self::errLog("Couldn't rename log $log_filepath to $backup_log_filepath");
|
349 |
+
}
|
350 |
+
}
|
351 |
+
|
352 |
+
if (@file_put_contents($log_filepath, $formatted_logging_message, FILE_APPEND) === false) {
|
353 |
+
// Not en error worth reporting
|
354 |
+
}
|
355 |
+
}
|
356 |
}
|
357 |
|
358 |
class DUP_Handler
|
431 |
}
|
432 |
break;
|
433 |
case self::MODE_VAR:
|
434 |
+
self::$varModeLog .= self::getMessage($errno, $errstr, $errfile, $errline) . "\n";
|
435 |
break;
|
436 |
case self::MODE_LOG:
|
437 |
default:
|
438 |
switch ($errno) {
|
439 |
+
case E_ERROR:
|
440 |
$log_message = self::getMessage($errno, $errstr, $errfile, $errline);
|
441 |
DUP_Log::error($log_message);
|
442 |
break;
|
443 |
+
case E_NOTICE:
|
444 |
+
case E_WARNING:
|
445 |
+
default:
|
446 |
$log_message = self::getMessage($errno, $errstr, $errfile, $errline);
|
447 |
DUP_Log::Info($log_message);
|
448 |
break;
|
457 |
if (self::$errPrefix) {
|
458 |
$result = '[PHP ERR]';
|
459 |
switch ($errno) {
|
460 |
+
case E_ERROR:
|
461 |
$result .= '[FATAL]';
|
462 |
break;
|
463 |
+
case E_WARNING:
|
464 |
$result .= '[WARN]';
|
465 |
break;
|
466 |
+
case E_NOTICE:
|
467 |
$result .= '[NOTICE]';
|
468 |
break;
|
469 |
+
default:
|
470 |
$result .= '[ISSUE]';
|
471 |
break;
|
472 |
}
|
476 |
$result .= $errstr;
|
477 |
|
478 |
if (self::$codeReference) {
|
479 |
+
$result .= ' [CODE:' . $errno . '|FILE:' . $errfile . '|LINE:' . $errline . ']';
|
480 |
+
$result .= "\n" . wp_debug_backtrace_summary();
|
481 |
}
|
482 |
|
483 |
return $result;
|
classes/class.password.php
CHANGED
@@ -1,229 +1,235 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
|
4 |
-
#
|
5 |
-
#
|
6 |
-
#
|
7 |
-
#
|
8 |
-
#
|
9 |
-
#
|
10 |
-
#
|
11 |
-
#
|
12 |
-
#
|
13 |
-
#
|
14 |
-
#
|
15 |
-
#
|
16 |
-
#
|
17 |
-
#
|
18 |
-
#
|
19 |
-
#
|
20 |
-
#
|
21 |
-
#
|
22 |
-
#
|
23 |
-
#
|
24 |
-
#
|
25 |
-
#
|
26 |
-
#
|
27 |
-
#
|
28 |
-
|
29 |
-
|
30 |
-
if
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
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 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
4 |
+
#
|
5 |
+
# Portable PHP password hashing framework.
|
6 |
+
#
|
7 |
+
# Version 0.5 / genuine.
|
8 |
+
#
|
9 |
+
# Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in
|
10 |
+
# the public domain. Revised in subsequent years, still public domain.
|
11 |
+
#
|
12 |
+
# There's absolutely no warranty.
|
13 |
+
#
|
14 |
+
# The homepage URL for this framework is:
|
15 |
+
#
|
16 |
+
# http://www.openwall.com/phpass/
|
17 |
+
#
|
18 |
+
# Please be sure to update the Version line if you edit this file in any way.
|
19 |
+
# It is suggested that you leave the main version number intact, but indicate
|
20 |
+
# your project name (after the slash) and add your own revision information.
|
21 |
+
#
|
22 |
+
# Please do not change the "private" password hashing method implemented in
|
23 |
+
# here, thereby making your hashes incompatible. However, if you must, please
|
24 |
+
# change the hash type identifier (the "$P$") to something different.
|
25 |
+
#
|
26 |
+
# Obviously, since this code is in the public domain, the above are not
|
27 |
+
# requirements (there can be none), but merely suggestions.
|
28 |
+
#
|
29 |
+
|
30 |
+
// Exit if accessed directly
|
31 |
+
if (! defined('DUPLICATOR_VERSION')) {
|
32 |
+
exit;
|
33 |
+
}
|
34 |
+
|
35 |
+
class DUP_PasswordHash
|
36 |
+
{
|
37 |
+
public $itoa64;
|
38 |
+
public $iteration_count_log2;
|
39 |
+
public $portable_hashes;
|
40 |
+
public $random_state;
|
41 |
+
|
42 |
+
public function __construct($iteration_count_log2, $portable_hashes)
|
43 |
+
{
|
44 |
+
$this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
45 |
+
if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31) {
|
46 |
+
$iteration_count_log2 = 8;
|
47 |
+
}
|
48 |
+
$this->iteration_count_log2 = $iteration_count_log2;
|
49 |
+
$this->portable_hashes = $portable_hashes;
|
50 |
+
$this->random_state = microtime();
|
51 |
+
if (function_exists('getmypid')) {
|
52 |
+
$this->random_state .= getmypid();
|
53 |
+
}
|
54 |
+
}
|
55 |
+
|
56 |
+
public function PasswordHash($iteration_count_log2, $portable_hashes)
|
57 |
+
{
|
58 |
+
self::__construct($iteration_count_log2, $portable_hashes);
|
59 |
+
}
|
60 |
+
|
61 |
+
public function get_random_bytes($count)
|
62 |
+
{
|
63 |
+
$output = '';
|
64 |
+
if (
|
65 |
+
@is_readable('/dev/urandom') &&
|
66 |
+
($fh = @fopen('/dev/urandom', 'rb'))
|
67 |
+
) {
|
68 |
+
$output = fread($fh, $count);
|
69 |
+
fclose($fh);
|
70 |
+
}
|
71 |
+
|
72 |
+
if (strlen($output) < $count) {
|
73 |
+
$output = '';
|
74 |
+
for ($i = 0; $i < $count; $i += 16) {
|
75 |
+
$this->random_state =
|
76 |
+
md5(microtime() . $this->random_state);
|
77 |
+
$output .= md5($this->random_state, true);
|
78 |
+
}
|
79 |
+
$output = substr($output, 0, $count);
|
80 |
+
}
|
81 |
+
|
82 |
+
return $output;
|
83 |
+
}
|
84 |
+
|
85 |
+
public function encode64($input, $count)
|
86 |
+
{
|
87 |
+
$output = '';
|
88 |
+
$i = 0;
|
89 |
+
do {
|
90 |
+
$value = ord($input[$i++]);
|
91 |
+
$output .= $this->itoa64[$value & 0x3f];
|
92 |
+
if ($i < $count) {
|
93 |
+
$value |= ord($input[$i]) << 8;
|
94 |
+
}
|
95 |
+
$output .= $this->itoa64[($value >> 6) & 0x3f];
|
96 |
+
if ($i++ >= $count) {
|
97 |
+
break;
|
98 |
+
}
|
99 |
+
if ($i < $count) {
|
100 |
+
$value |= ord($input[$i]) << 16;
|
101 |
+
}
|
102 |
+
$output .= $this->itoa64[($value >> 12) & 0x3f];
|
103 |
+
if ($i++ >= $count) {
|
104 |
+
break;
|
105 |
+
}
|
106 |
+
$output .= $this->itoa64[($value >> 18) & 0x3f];
|
107 |
+
} while ($i < $count);
|
108 |
+
return $output;
|
109 |
+
}
|
110 |
+
|
111 |
+
public function gensalt_private($input)
|
112 |
+
{
|
113 |
+
$output = '$P$';
|
114 |
+
$output .= $this->itoa64[min($this->iteration_count_log2 +
|
115 |
+
((PHP_VERSION >= '5') ? 5 : 3), 30)];
|
116 |
+
$output .= $this->encode64($input, 6);
|
117 |
+
return $output;
|
118 |
+
}
|
119 |
+
|
120 |
+
public function crypt_private($password, $setting)
|
121 |
+
{
|
122 |
+
$output = '*0';
|
123 |
+
if (substr($setting, 0, 2) === $output) {
|
124 |
+
$output = '*1';
|
125 |
+
}
|
126 |
+
|
127 |
+
$id = substr($setting, 0, 3);
|
128 |
+
# We use "$P$", phpBB3 uses "$H$" for the same thing
|
129 |
+
if ($id !== '$P$' && $id !== '$H$') {
|
130 |
+
return $output;
|
131 |
+
}
|
132 |
+
|
133 |
+
$count_log2 = strpos($this->itoa64, $setting[3]);
|
134 |
+
if ($count_log2 < 7 || $count_log2 > 30) {
|
135 |
+
return $output;
|
136 |
+
}
|
137 |
+
|
138 |
+
$count = 1 << $count_log2;
|
139 |
+
$salt = substr($setting, 4, 8);
|
140 |
+
if (strlen($salt) !== 8) {
|
141 |
+
return $output;
|
142 |
+
}
|
143 |
+
|
144 |
+
# We were kind of forced to use MD5 here since it's the only
|
145 |
+
# cryptographic primitive that was available in all versions
|
146 |
+
# of PHP in use. To implement our own low-level crypto in PHP
|
147 |
+
# would have resulted in much worse performance and
|
148 |
+
# consequently in lower iteration counts and hashes that are
|
149 |
+
# quicker to crack (by non-PHP code).
|
150 |
+
$hash = md5($salt . $password, true);
|
151 |
+
do {
|
152 |
+
$hash = md5($hash . $password, true);
|
153 |
+
} while (--$count);
|
154 |
+
$output = substr($setting, 0, 12);
|
155 |
+
$output .= $this->encode64($hash, 16);
|
156 |
+
return $output;
|
157 |
+
}
|
158 |
+
|
159 |
+
public function gensalt_blowfish($input)
|
160 |
+
{
|
161 |
+
# This one needs to use a different order of characters and a
|
162 |
+
# different encoding scheme from the one in encode64() above.
|
163 |
+
# We care because the last character in our encoded string will
|
164 |
+
# only represent 2 bits. While two known implementations of
|
165 |
+
# bcrypt will happily accept and correct a salt string which
|
166 |
+
# has the 4 unused bits set to non-zero, we do not want to take
|
167 |
+
# chances and we also do not want to waste an additional byte
|
168 |
+
# of entropy.
|
169 |
+
$itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
170 |
+
$output = '$2a$';
|
171 |
+
$output .= chr(ord('0') + (int)($this->iteration_count_log2 / 10));
|
172 |
+
$output .= chr(ord('0') + $this->iteration_count_log2 % 10);
|
173 |
+
$output .= '$';
|
174 |
+
$i = 0;
|
175 |
+
do {
|
176 |
+
$c1 = ord($input[$i++]);
|
177 |
+
$output .= $itoa64[$c1 >> 2];
|
178 |
+
$c1 = ($c1 & 0x03) << 4;
|
179 |
+
if ($i >= 16) {
|
180 |
+
$output .= $itoa64[$c1];
|
181 |
+
break;
|
182 |
+
}
|
183 |
+
|
184 |
+
$c2 = ord($input[$i++]);
|
185 |
+
$c1 |= $c2 >> 4;
|
186 |
+
$output .= $itoa64[$c1];
|
187 |
+
$c1 = ($c2 & 0x0f) << 2;
|
188 |
+
$c2 = ord($input[$i++]);
|
189 |
+
$c1 |= $c2 >> 6;
|
190 |
+
$output .= $itoa64[$c1];
|
191 |
+
$output .= $itoa64[$c2 & 0x3f];
|
192 |
+
} while (1);
|
193 |
+
return $output;
|
194 |
+
}
|
195 |
+
|
196 |
+
public function HashPassword($password)
|
197 |
+
{
|
198 |
+
$random = '';
|
199 |
+
if (CRYPT_BLOWFISH === 1 && !$this->portable_hashes) {
|
200 |
+
$random = $this->get_random_bytes(16);
|
201 |
+
$hash =
|
202 |
+
crypt($password, $this->gensalt_blowfish($random));
|
203 |
+
if (strlen($hash) === 60) {
|
204 |
+
return $hash;
|
205 |
+
}
|
206 |
+
}
|
207 |
+
|
208 |
+
if (strlen($random) < 6) {
|
209 |
+
$random = $this->get_random_bytes(6);
|
210 |
+
}
|
211 |
+
$hash =
|
212 |
+
$this->crypt_private(
|
213 |
+
$password,
|
214 |
+
$this->gensalt_private($random)
|
215 |
+
);
|
216 |
+
if (strlen($hash) === 34) {
|
217 |
+
return $hash;
|
218 |
+
}
|
219 |
+
|
220 |
+
# Returning '*' on error is safe here, but would _not_ be safe
|
221 |
+
# in a crypt(3)-like function used _both_ for generating new
|
222 |
+
# hashes and for validating passwords against existing hashes.
|
223 |
+
return '*';
|
224 |
+
}
|
225 |
+
|
226 |
+
public function CheckPassword($password, $stored_hash)
|
227 |
+
{
|
228 |
+
$hash = $this->crypt_private($password, $stored_hash);
|
229 |
+
if ($hash[0] === '*') {
|
230 |
+
$hash = crypt($password, $stored_hash);
|
231 |
+
}
|
232 |
+
|
233 |
+
return $hash === $stored_hash;
|
234 |
+
}
|
235 |
+
}
|
classes/class.plugin.upgrade.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
3 |
|
4 |
/**
|
@@ -7,7 +8,6 @@ defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
|
7 |
*/
|
8 |
class DUP_LITE_Plugin_Upgrade
|
9 |
{
|
10 |
-
|
11 |
const DUP_VERSION_OPT_KEY = 'duplicator_version_plugin';
|
12 |
|
13 |
/**
|
@@ -24,7 +24,7 @@ class DUP_LITE_Plugin_Upgrade
|
|
24 |
self::updateInstallation($oldDupVersion);
|
25 |
}
|
26 |
|
27 |
-
//
|
28 |
self::updateDatabase();
|
29 |
DUP_Util::initSnapshotDirectory();
|
30 |
}
|
@@ -52,15 +52,15 @@ class DUP_LITE_Plugin_Upgrade
|
|
52 |
//PRE 1.3.35
|
53 |
//Do not update to new wp-content storage till after
|
54 |
if (version_compare($oldVersion, '1.3.35', '<')) {
|
55 |
-
DUP_Settings::Set('storage_position', DUP_Settings::
|
56 |
DUP_Settings::Save();
|
57 |
}
|
58 |
-
|
59 |
//WordPress Options Hooks
|
60 |
update_option(self::DUP_VERSION_OPT_KEY, DUPLICATOR_VERSION);
|
61 |
}
|
62 |
|
63 |
-
|
64 |
* Runs for both new and update installs and creates the database tables
|
65 |
*
|
66 |
* @return void
|
@@ -69,7 +69,7 @@ class DUP_LITE_Plugin_Upgrade
|
|
69 |
{
|
70 |
global $wpdb;
|
71 |
|
72 |
-
$table_name = $wpdb->prefix."duplicator_packages";
|
73 |
|
74 |
//PRIMARY KEY must have 2 spaces before for dbDelta to work
|
75 |
//see: https://codex.wordpress.org/Creating_Tables_with_Plugins
|
@@ -85,7 +85,7 @@ class DUP_LITE_Plugin_Upgrade
|
|
85 |
KEY hash (hash))";
|
86 |
|
87 |
$abs_path = duplicator_get_abs_path();
|
88 |
-
require_once($abs_path.'/wp-admin/includes/upgrade.php');
|
89 |
@dbDelta($sql);
|
90 |
}
|
91 |
-
}
|
1 |
<?php
|
2 |
+
|
3 |
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
4 |
|
5 |
/**
|
8 |
*/
|
9 |
class DUP_LITE_Plugin_Upgrade
|
10 |
{
|
|
|
11 |
const DUP_VERSION_OPT_KEY = 'duplicator_version_plugin';
|
12 |
|
13 |
/**
|
24 |
self::updateInstallation($oldDupVersion);
|
25 |
}
|
26 |
|
27 |
+
//Init Database & Backup Directories
|
28 |
self::updateDatabase();
|
29 |
DUP_Util::initSnapshotDirectory();
|
30 |
}
|
52 |
//PRE 1.3.35
|
53 |
//Do not update to new wp-content storage till after
|
54 |
if (version_compare($oldVersion, '1.3.35', '<')) {
|
55 |
+
DUP_Settings::Set('storage_position', DUP_Settings::STORAGE_POSITION_LEGACY);
|
56 |
DUP_Settings::Save();
|
57 |
}
|
58 |
+
|
59 |
//WordPress Options Hooks
|
60 |
update_option(self::DUP_VERSION_OPT_KEY, DUPLICATOR_VERSION);
|
61 |
}
|
62 |
|
63 |
+
/**
|
64 |
* Runs for both new and update installs and creates the database tables
|
65 |
*
|
66 |
* @return void
|
69 |
{
|
70 |
global $wpdb;
|
71 |
|
72 |
+
$table_name = $wpdb->prefix . "duplicator_packages";
|
73 |
|
74 |
//PRIMARY KEY must have 2 spaces before for dbDelta to work
|
75 |
//see: https://codex.wordpress.org/Creating_Tables_with_Plugins
|
85 |
KEY hash (hash))";
|
86 |
|
87 |
$abs_path = duplicator_get_abs_path();
|
88 |
+
require_once($abs_path . '/wp-admin/includes/upgrade.php');
|
89 |
@dbDelta($sql);
|
90 |
}
|
91 |
+
}
|
classes/class.server.php
CHANGED
@@ -1,6 +1,9 @@
|
|
1 |
<?php
|
|
|
|
|
|
|
2 |
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
3 |
-
require_once
|
4 |
|
5 |
/**
|
6 |
* Used to get various pieces of information about the server environment
|
@@ -16,356 +19,356 @@ require_once (DUPLICATOR_PLUGIN_PATH.'classes/utilities/class.u.php');
|
|
16 |
|
17 |
class DUP_Server
|
18 |
{
|
|
|
19 |
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
return $success;
|
48 |
-
}
|
49 |
|
50 |
public static function mysqlEscapeIsOk()
|
51 |
{
|
52 |
-
$escape_test_string
|
53 |
-
$escape_expected_result
|
54 |
-
$escape_actual_result
|
55 |
-
$result
|
56 |
|
57 |
if (!$result) {
|
58 |
-
$msg = "mysqli_real_escape_string test results\n".
|
59 |
-
"Expected escape result: "
|
60 |
-
"Actual escape result: "
|
61 |
DUP_Log::trace($msg);
|
62 |
-
|
63 |
}
|
64 |
|
65 |
return $result;
|
66 |
}
|
67 |
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
|
91 |
-
|
92 |
-
|
93 |
|
94 |
-
|
95 |
-
|
96 |
|
97 |
-
|
98 |
-
|
99 |
|
100 |
-
|
101 |
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
|
109 |
-
|
110 |
-
|
111 |
|
112 |
-
|
113 |
-
|
114 |
|
115 |
-
|
116 |
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
|
121 |
//mysqli_real_escape_string test
|
122 |
$dup_tests['SRV']['MYSQL_ESC'] = self::mysqlEscapeIsOk() ? 'Pass' : 'Fail';
|
123 |
self::logRequirementFail($dup_tests['SRV']['MYSQL_ESC'], "The function mysqli_real_escape_string is not escaping strings as expected.");
|
124 |
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
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 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
//MANAGED HOST
|
214 |
$checks['SRV']['SYS']['managedHost'] = !DUP_Custom_Host_Manager::getInstance()->isManaged();
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
|
|
|
1 |
<?php
|
2 |
+
|
3 |
+
use Duplicator\Libs\Snap\SnapWP;
|
4 |
+
|
5 |
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
6 |
+
require_once(DUPLICATOR_PLUGIN_PATH . 'classes/utilities/class.u.php');
|
7 |
|
8 |
/**
|
9 |
* Used to get various pieces of information about the server environment
|
19 |
|
20 |
class DUP_Server
|
21 |
{
|
22 |
+
const LockFileName = 'lockfile.txt';
|
23 |
|
24 |
+
// Possibly use in the future if we want to prevent double building
|
25 |
+
public static function isEngineLocked()
|
26 |
+
{
|
27 |
+
if (self::setEngineLock(true)) {
|
28 |
+
self::setEngineLock(false);
|
29 |
+
$locked = false;
|
30 |
+
} else {
|
31 |
+
$locked = true;
|
32 |
+
}
|
33 |
+
}
|
34 |
+
|
35 |
+
// Possibly use in the future if we want to prevent double building
|
36 |
+
public static function setEngineLock($shouldLock)
|
37 |
+
{
|
38 |
+
$success = false;
|
39 |
+
$locking_file = @fopen(self::LockFileName, 'c+');
|
40 |
+
if ($locking_file != false) {
|
41 |
+
if ($shouldLock) {
|
42 |
+
$success = @flock($locking_file, LOCK_EX | LOCK_NB);
|
43 |
+
} else {
|
44 |
+
$success = @flock($locking_file, LOCK_UN);
|
45 |
+
}
|
46 |
+
|
47 |
+
@fclose($locking_file);
|
48 |
+
}
|
49 |
+
return $success;
|
50 |
+
}
|
|
|
|
|
51 |
|
52 |
public static function mysqlEscapeIsOk()
|
53 |
{
|
54 |
+
$escape_test_string = chr(0) . chr(26) . "\r\n'\"\\";
|
55 |
+
$escape_expected_result = "\"\\0\Z\\r\\n\\'\\\"\\\\\"";
|
56 |
+
$escape_actual_result = DUP_DB::escValueToQueryString($escape_test_string);
|
57 |
+
$result = $escape_expected_result === $escape_actual_result;
|
58 |
|
59 |
if (!$result) {
|
60 |
+
$msg = "mysqli_real_escape_string test results\n" .
|
61 |
+
"Expected escape result: " . $escape_expected_result . "\n" .
|
62 |
+
"Actual escape result: " . $escape_actual_result;
|
63 |
DUP_Log::trace($msg);
|
|
|
64 |
}
|
65 |
|
66 |
return $result;
|
67 |
}
|
68 |
|
69 |
+
/**
|
70 |
+
* Gets the system requirements which must pass to build a package
|
71 |
+
*
|
72 |
+
* @return array An array of requirements
|
73 |
+
*/
|
74 |
+
public static function getRequirements()
|
75 |
+
{
|
76 |
+
$dup_tests = array();
|
77 |
|
78 |
+
//PHP SUPPORT
|
79 |
+
$safe_ini = strtolower(ini_get('safe_mode'));
|
80 |
+
$dup_tests['PHP']['SAFE_MODE'] = $safe_ini != 'on' || $safe_ini != 'yes' || $safe_ini != 'true' || ini_get("safe_mode") != 1 ? 'Pass' : 'Fail';
|
81 |
+
self::logRequirementFail($dup_tests['PHP']['SAFE_MODE'], 'SAFE_MODE is on.');
|
82 |
|
83 |
+
$dup_tests['PHP']['VERSION'] = DUP_Util::$on_php_529_plus ? 'Pass' : 'Fail';
|
84 |
+
$phpversion = phpversion();
|
85 |
+
self::logRequirementFail($dup_tests['PHP']['VERSION'], 'PHP version(' . $phpversion . ') is lower than 5.2.9');
|
86 |
|
87 |
+
if (DUP_Settings::Get('archive_build_mode') == DUP_Archive_Build_Mode::ZipArchive) {
|
88 |
+
$dup_tests['PHP']['ZIP'] = class_exists('ZipArchive') ? 'Pass' : 'Fail';
|
89 |
+
self::logRequirementFail($dup_tests['PHP']['ZIP'], 'ZipArchive class doesn\'t exist.');
|
90 |
+
}
|
91 |
|
92 |
+
$dup_tests['PHP']['FUNC_1'] = function_exists("file_get_contents") ? 'Pass' : 'Fail';
|
93 |
+
self::logRequirementFail($dup_tests['PHP']['FUNC_1'], 'file_get_contents function doesn\'t exist.');
|
94 |
|
95 |
+
$dup_tests['PHP']['FUNC_2'] = function_exists("file_put_contents") ? 'Pass' : 'Fail';
|
96 |
+
self::logRequirementFail($dup_tests['PHP']['FUNC_2'], 'file_put_contents function doesn\'t exist.');
|
97 |
|
98 |
+
$dup_tests['PHP']['FUNC_3'] = function_exists("mb_strlen") ? 'Pass' : 'Fail';
|
99 |
+
self::logRequirementFail($dup_tests['PHP']['FUNC_3'], 'mb_strlen function doesn\'t exist.');
|
100 |
|
101 |
+
$dup_tests['PHP']['ALL'] = !in_array('Fail', $dup_tests['PHP']) ? 'Pass' : 'Fail';
|
102 |
|
103 |
+
//REQUIRED PATHS
|
104 |
+
$abs_path = duplicator_get_abs_path();
|
105 |
+
$handle_test = @opendir($abs_path);
|
106 |
+
$dup_tests['IO']['WPROOT'] = is_writeable($abs_path) && $handle_test ? 'Pass' : 'Warn';
|
107 |
+
@closedir($handle_test);
|
108 |
+
self::logRequirementFail($dup_tests['IO']['WPROOT'], $abs_path . ' (abs path) can\'t be opened.');
|
109 |
|
110 |
+
$dup_tests['IO']['SSDIR'] = is_writeable(DUP_Settings::getSsdirPath()) ? 'Pass' : 'Fail';
|
111 |
+
self::logRequirementFail($dup_tests['IO']['SSDIR'], DUP_Settings::getSsdirPath() . ' (DUPLICATOR_SSDIR_PATH) can\'t be writeable.');
|
112 |
|
113 |
+
$dup_tests['IO']['SSTMP'] = is_writeable(DUP_Settings::getSsdirTmpPath()) ? 'Pass' : 'Fail';
|
114 |
+
self::logRequirementFail($dup_tests['IO']['SSTMP'], DUP_Settings::getSsdirTmpPath() . ' (DUPLICATOR_SSDIR_PATH_TMP) can\'t be writeable.');
|
115 |
|
116 |
+
$dup_tests['IO']['ALL'] = !in_array('Fail', $dup_tests['IO']) ? 'Pass' : 'Fail';
|
117 |
|
118 |
+
//SERVER SUPPORT
|
119 |
+
$dup_tests['SRV']['MYSQLi'] = function_exists('mysqli_connect') ? 'Pass' : 'Fail';
|
120 |
+
self::logRequirementFail($dup_tests['SRV']['MYSQLi'], 'mysqli_connect function doesn\'t exist.');
|
121 |
|
122 |
//mysqli_real_escape_string test
|
123 |
$dup_tests['SRV']['MYSQL_ESC'] = self::mysqlEscapeIsOk() ? 'Pass' : 'Fail';
|
124 |
self::logRequirementFail($dup_tests['SRV']['MYSQL_ESC'], "The function mysqli_real_escape_string is not escaping strings as expected.");
|
125 |
|
126 |
+
$db_version = DUP_DB::getVersion();
|
127 |
+
$dup_tests['SRV']['MYSQL_VER'] = version_compare($db_version, '5.0', '>=') ? 'Pass' : 'Fail';
|
128 |
+
self::logRequirementFail($dup_tests['SRV']['MYSQL_VER'], 'MySQL version ' . $db_version . ' is lower than 5.0.');
|
129 |
+
|
130 |
+
$dup_tests['SRV']['ALL'] = !in_array('Fail', $dup_tests['SRV']) ? 'Pass' : 'Fail';
|
131 |
+
|
132 |
+
//RESERVED FILES
|
133 |
+
$dup_tests['RES']['INSTALL'] = !(self::hasInstallerFiles()) ? 'Pass' : 'Fail';
|
134 |
+
self::logRequirementFail($dup_tests['RES']['INSTALL'], 'Installer file(s) are exist on the server.');
|
135 |
+
$dup_tests['Success'] = $dup_tests['PHP']['ALL'] == 'Pass' && $dup_tests['IO']['ALL'] == 'Pass' && $dup_tests['SRV']['ALL'] == 'Pass' && $dup_tests['RES']['INSTALL'] == 'Pass';
|
136 |
+
|
137 |
+
$dup_tests['Warning'] = $dup_tests['IO']['WPROOT'] == 'Warn';
|
138 |
+
|
139 |
+
return $dup_tests;
|
140 |
+
}
|
141 |
+
|
142 |
+
/**
|
143 |
+
* Logs requirement fail status informative message
|
144 |
+
*
|
145 |
+
* @param string $testStatus Either it is Pass or Fail
|
146 |
+
* @param string $errorMessage Error message which should be logged
|
147 |
+
* @return void
|
148 |
+
*/
|
149 |
+
private static function logRequirementFail($testStatus, $errorMessage)
|
150 |
+
{
|
151 |
+
if (empty($testStatus)) {
|
152 |
+
throw new Exception('Exception: Empty $testStatus [File: ' . __FILE__ . ', Ln: ' . __LINE__);
|
153 |
+
}
|
154 |
+
|
155 |
+
if (empty($errorMessage)) {
|
156 |
+
throw new Exception('Exception: Empty $errorMessage [File: ' . __FILE__ . ', Ln: ' . __LINE__);
|
157 |
+
}
|
158 |
+
|
159 |
+
$validTestStatuses = array('Pass', 'Fail', 'Warn');
|
160 |
+
|
161 |
+
if (!in_array($testStatus, $validTestStatuses)) {
|
162 |
+
throw new Exception('Exception: Invalid $testStatus value: ' . $testStatus . ' [File: ' . __FILE__ . ', Ln: ' . __LINE__);
|
163 |
+
}
|
164 |
+
|
165 |
+
if ('Fail' == $testStatus) {
|
166 |
+
DUP_LOG::trace($errorMessage);
|
167 |
+
}
|
168 |
+
}
|
169 |
+
|
170 |
+
/**
|
171 |
+
* Gets the system checks which are not required
|
172 |
+
*
|
173 |
+
* @return array An array of system checks
|
174 |
+
*/
|
175 |
+
public static function getChecks()
|
176 |
+
{
|
177 |
+
$checks = array();
|
178 |
+
|
179 |
+
//PHP/SYSTEM SETTINGS
|
180 |
+
//Web Server
|
181 |
+
$php_test0 = false;
|
182 |
+
foreach ($GLOBALS['DUPLICATOR_SERVER_LIST'] as $value) {
|
183 |
+
if (stristr($_SERVER['SERVER_SOFTWARE'], $value)) {
|
184 |
+
$php_test0 = true;
|
185 |
+
break;
|
186 |
+
}
|
187 |
+
}
|
188 |
+
self::logCheckFalse($php_test0, 'Any out of server software (' . implode(', ', $GLOBALS['DUPLICATOR_SERVER_LIST']) . ') doesn\'t exist.');
|
189 |
+
|
190 |
+
$php_test1 = ini_get("open_basedir");
|
191 |
+
$php_test1 = empty($php_test1) ? true : false;
|
192 |
+
self::logCheckFalse($php_test1, 'open_basedir is enabled.');
|
193 |
+
|
194 |
+
$max_execution_time = ini_get("max_execution_time");
|
195 |
+
$php_test2 = ($max_execution_time > DUPLICATOR_SCAN_TIMEOUT) || (strcmp($max_execution_time, 'Off') == 0 || $max_execution_time == 0) ? true : false;
|
196 |
+
if (strcmp($max_execution_time, 'Off') == 0) {
|
197 |
+
$max_execution_time_error_message = '$max_execution_time should not be' . $max_execution_time;
|
198 |
+
} else {
|
199 |
+
$max_execution_time_error_message = '$max_execution_time (' . $max_execution_time . ') should not be lower than the DUPLICATOR_SCAN_TIMEOUT' . DUPLICATOR_SCAN_TIMEOUT;
|
200 |
+
}
|
201 |
+
self::logCheckFalse($php_test2, $max_execution_time_error_message);
|
202 |
+
|
203 |
+
$php_test3 = function_exists('mysqli_connect');
|
204 |
+
self::logCheckFalse($php_test3, 'mysqli_connect function doesn\'t exist.');
|
205 |
+
|
206 |
+
$php_test4 = DUP_Util::$on_php_53_plus ? true : false;
|
207 |
+
self::logCheckFalse($php_test4, 'PHP Version is lower than 5.3.');
|
208 |
+
|
209 |
+
$checks['SRV']['PHP']['websrv'] = $php_test0;
|
210 |
+
$checks['SRV']['PHP']['openbase'] = $php_test1;
|
211 |
+
$checks['SRV']['PHP']['maxtime'] = $php_test2;
|
212 |
+
$checks['SRV']['PHP']['mysqli'] = $php_test3;
|
213 |
+
$checks['SRV']['PHP']['version'] = $php_test4;
|
214 |
//MANAGED HOST
|
215 |
$checks['SRV']['SYS']['managedHost'] = !DUP_Custom_Host_Manager::getInstance()->isManaged();
|
216 |
+
$checks['SRV']['SYS']['ALL'] = ($php_test0 && $php_test1 && $php_test2 && $php_test3 && $php_test4 && $checks['SRV']['SYS']['managedHost']) ? 'Good' : 'Warn';
|
217 |
+
|
218 |
+
//WORDPRESS SETTINGS
|
219 |
+
global $wp_version;
|
220 |
+
$wp_test1 = version_compare($wp_version, DUPLICATOR_SCAN_MIN_WP) >= 0 ? true : false;
|
221 |
+
self::logCheckFalse($wp_test1, 'WP version (' . $wp_version . ') is lower than the DUPLICATOR_SCAN_MIN_WP (' . DUPLICATOR_SCAN_MIN_WP . ').');
|
222 |
+
|
223 |
+
//Core Files
|
224 |
+
$files = array();
|
225 |
+
$proper_wp_config_file_path = SnapWP::getWPConfigPath();
|
226 |
+
$files['wp-config.php'] = file_exists($proper_wp_config_file_path);
|
227 |
+
self::logCheckFalse($files['wp-config.php'], 'The wp-config.php file doesn\'t exist on the ' . $proper_wp_config_file_path);
|
228 |
+
|
229 |
+
/** searching wp-config in working word press is not worthy
|
230 |
+
* if this script is executing that means wp-config.php exists :)
|
231 |
+
* we need to know the core folders and files added by the user at this point
|
232 |
+
* retaining old logic as else for the case if its used some where else
|
233 |
+
*/
|
234 |
+
//Core dir and files logic
|
235 |
+
if (isset($_POST['file_notice']) && isset($_POST['dir_notice'])) {
|
236 |
+
//means if there are core directories excluded or core files excluded return false
|
237 |
+
if ((bool) $_POST['file_notice'] || (bool) $_POST['dir_notice']) {
|
238 |
+
$wp_test2 = false;
|
239 |
+
} else {
|
240 |
+
$wp_test2 = true;
|
241 |
+
}
|
242 |
+
} else {
|
243 |
+
$wp_test2 = $files['wp-config.php'];
|
244 |
+
}
|
245 |
+
|
246 |
+
//Cache
|
247 |
+
/*
|
248 |
+
$Package = DUP_Package::getActive();
|
249 |
+
$cache_path = DUP_Util::safePath(WP_CONTENT_DIR) . '/cache';
|
250 |
+
$dirEmpty = DUP_Util::isDirectoryEmpty($cache_path);
|
251 |
+
$dirSize = DUP_Util::getDirectorySize($cache_path);
|
252 |
+
$cach_filtered = in_array($cache_path, explode(';', $Package->Archive->FilterDirs));
|
253 |
+
$wp_test3 = ($cach_filtered || $dirEmpty || $dirSize < DUPLICATOR_SCAN_CACHESIZE ) ? true : false;
|
254 |
+
*/
|
255 |
+
$wp_test3 = is_multisite();
|
256 |
+
self::logCheckFalse($wp_test3, 'WP is multi-site setup.');
|
257 |
+
|
258 |
+
$checks['SRV']['WP']['version'] = $wp_test1;
|
259 |
+
$checks['SRV']['WP']['core'] = $wp_test2;
|
260 |
+
$checks['SRV']['WP']['ismu'] = $wp_test3;
|
261 |
+
$checks['SRV']['WP']['ALL'] = $wp_test1 && $wp_test2 && !$wp_test3 ? 'Good' : 'Warn';
|
262 |
+
|
263 |
+
return $checks;
|
264 |
+
}
|
265 |
+
|
266 |
+
/**
|
267 |
+
* Logs checks false informative message
|
268 |
+
*
|
269 |
+
* @param boolean $check Either it is true or false
|
270 |
+
* @param string $errorMessage Error message which should be logged when check is false
|
271 |
+
* @return void
|
272 |
+
*/
|
273 |
+
private static function logCheckFalse($check, $errorMessage)
|
274 |
+
{
|
275 |
+
if (empty($errorMessage)) {
|
276 |
+
throw new Exception('Exception: Empty $errorMessage variable [File: ' . __FILE__ . ', Ln: ' . __LINE__);
|
277 |
+
}
|
278 |
+
|
279 |
+
if (filter_var($check, FILTER_VALIDATE_BOOLEAN) === false) {
|
280 |
+
DUP_LOG::trace($errorMessage);
|
281 |
+
}
|
282 |
+
}
|
283 |
+
|
284 |
+
/**
|
285 |
+
* Check to see if duplicator installer files are present
|
286 |
+
*
|
287 |
+
* @return bool True if any reserved files are found
|
288 |
+
*/
|
289 |
+
public static function hasInstallerFiles()
|
290 |
+
{
|
291 |
+
$files = self::getInstallerFiles();
|
292 |
+
foreach ($files as $file => $path) {
|
293 |
+
if (false !== strpos($path, '*')) {
|
294 |
+
$glob_files = glob($path);
|
295 |
+
if (!empty($glob_files)) {
|
296 |
+
return true;
|
297 |
+
}
|
298 |
+
} elseif (file_exists($path)) {
|
299 |
+
return true;
|
300 |
+
}
|
301 |
+
}
|
302 |
+
return false;
|
303 |
+
}
|
304 |
+
|
305 |
+
/**
|
306 |
+
* Gets a list of all the installer files by name and full path
|
307 |
+
*
|
308 |
+
* @remarks
|
309 |
+
* FILES: installer.php, installer-backup.php, dup-installer-bootlog__[HASH].txt
|
310 |
+
* DIRS: dup-installer
|
311 |
+
* DEV FILES: wp-config.orig
|
312 |
+
* Last set is for lazy developer cleanup files that a developer may have
|
313 |
+
* accidently left around lets be proactive for the user just in case.
|
314 |
+
*
|
315 |
+
* @return array [file_name, file_path]
|
316 |
+
*/
|
317 |
+
public static function getInstallerFiles()
|
318 |
+
{
|
319 |
+
// alphanumeric 7 time, then -(dash), then 8 digits
|
320 |
+
$abs_path = duplicator_get_abs_path();
|
321 |
+
$four_digit_glob_pattern = '[0-9][0-9][0-9][0-9]';
|
322 |
+
$retArr = array(
|
323 |
+
basename(DUPLICATOR_INSTALLER_DIRECTORY) . ' ' . esc_html__('(directory)', 'duplicator') => DUPLICATOR_INSTALLER_DIRECTORY,
|
324 |
+
DUPLICATOR_INSTALL_PHP => $abs_path . '/' . DUPLICATOR_INSTALL_PHP,
|
325 |
+
'[HASH]' . '_' . DUPLICATOR_INSTALL_PHP => $abs_path . '/*_*' . $four_digit_glob_pattern . '_' . DUPLICATOR_INSTALL_PHP,
|
326 |
+
DUPLICATOR_INSTALL_BAK => $abs_path . '/' . DUPLICATOR_INSTALL_BAK,
|
327 |
+
'[HASH]' . '_' . DUPLICATOR_INSTALL_BAK => $abs_path . '/*_*' . $four_digit_glob_pattern . '_' . DUPLICATOR_INSTALL_BAK,
|
328 |
+
'[HASH]_archive.zip|daf' => $abs_path . '/*_*' . $four_digit_glob_pattern . '_archive.[zd][ia][pf]',
|
329 |
+
'dup-installer-bootlog__[HASH].txt' => $abs_path . '/dup-installer-bootlog__' . DUPLICATOR_INSTALLER_HASH_PATTERN . '.txt',
|
330 |
+
);
|
331 |
+
|
332 |
+
// legacy package
|
333 |
+
$retArr['dup-wp-config-arc__[HASH].txt'] = $abs_path . '/dup-wp-config-arc__' . DUPLICATOR_INSTALLER_HASH_PATTERN . '.txt';
|
334 |
+
return $retArr;
|
335 |
+
}
|
336 |
+
|
337 |
+
/**
|
338 |
+
* Get the IP of a client machine
|
339 |
+
*
|
340 |
+
* @return string IP of the client machine
|
341 |
+
*/
|
342 |
+
public static function getClientIP()
|
343 |
+
{
|
344 |
+
if (array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER)) {
|
345 |
+
return $_SERVER["HTTP_X_FORWARDED_FOR"];
|
346 |
+
} elseif (array_key_exists('REMOTE_ADDR', $_SERVER)) {
|
347 |
+
return $_SERVER["REMOTE_ADDR"];
|
348 |
+
} elseif (array_key_exists('HTTP_CLIENT_IP', $_SERVER)) {
|
349 |
+
return $_SERVER["HTTP_CLIENT_IP"];
|
350 |
+
}
|
351 |
+
return '';
|
352 |
+
}
|
353 |
+
|
354 |
+
/**
|
355 |
+
* Get PHP memory usage
|
356 |
+
*
|
357 |
+
* @return string Returns human readable memory usage.
|
358 |
+
*/
|
359 |
+
public static function getPHPMemory($peak = false)
|
360 |
+
{
|
361 |
+
if ($peak) {
|
362 |
+
$result = 'Unable to read PHP peak memory usage';
|
363 |
+
if (function_exists('memory_get_peak_usage')) {
|
364 |
+
$result = DUP_Util::byteSize(memory_get_peak_usage(true));
|
365 |
+
}
|
366 |
+
} else {
|
367 |
+
$result = 'Unable to read PHP memory usage';
|
368 |
+
if (function_exists('memory_get_usage')) {
|
369 |
+
$result = DUP_Util::byteSize(memory_get_usage(true));
|
370 |
+
}
|
371 |
+
}
|
372 |
+
return $result;
|
373 |
+
}
|
374 |
+
}
|
classes/class.settings.php
CHANGED
@@ -1,268 +1,254 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
const
|
11 |
-
const
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
const
|
22 |
-
const
|
23 |
-
const
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
self::$
|
126 |
-
}
|
127 |
-
|
128 |
-
/**
|
129 |
-
*
|
130 |
-
* @return True if option value has changed, false if not or if update failed.
|
131 |
-
*/
|
132 |
-
public static function
|
133 |
-
{
|
134 |
-
return
|
135 |
-
}
|
136 |
-
|
137 |
-
/**
|
138 |
-
*
|
139 |
-
* @return True if option value has changed, false if not or if update failed.
|
140 |
-
*/
|
141 |
-
public static function
|
142 |
-
{
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
$default
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
$default['
|
175 |
-
|
176 |
-
$default['
|
177 |
-
|
178 |
-
$default['
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
$default['
|
194 |
-
|
195 |
-
|
196 |
-
$default['
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
$
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
return
|
214 |
-
}
|
215 |
-
|
216 |
-
public static function
|
217 |
-
{
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
self::$
|
240 |
-
}
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
}
|
256 |
-
return self::$ssDirUrl;
|
257 |
-
}
|
258 |
-
|
259 |
-
public static function getSsdirTmpPath()
|
260 |
-
{
|
261 |
-
return self::getSsdirPath().'/tmp';
|
262 |
-
}
|
263 |
-
|
264 |
-
public static function getSsdirInstallerPath()
|
265 |
-
{
|
266 |
-
return self::getSsdirPath().'/installer';
|
267 |
-
}
|
268 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
use Duplicator\Libs\Snap\SnapIO;
|
4 |
+
|
5 |
+
abstract class DUP_Archive_Build_Mode
|
6 |
+
{
|
7 |
+
const Unconfigured = -1;
|
8 |
+
const Auto = 0;
|
9 |
+
// should no longer be used
|
10 |
+
// const Shell_Exec = 1;
|
11 |
+
const ZipArchive = 2;
|
12 |
+
const DupArchive = 3;
|
13 |
+
}
|
14 |
+
|
15 |
+
class DUP_Settings
|
16 |
+
{
|
17 |
+
const OPT_SETTINGS = 'duplicator_settings';
|
18 |
+
const INSTALLER_NAME_MODE_WITH_HASH = 'withhash';
|
19 |
+
const INSTALLER_NAME_MODE_SIMPLE = 'simple';
|
20 |
+
const STORAGE_POSITION_LEGACY = 'legacy';
|
21 |
+
const STORAGE_POSITION_WP_CONTENT = 'wpcont';
|
22 |
+
const SSDIR_NAME_LEGACY = 'wp-snapshots';
|
23 |
+
const SSDIR_NAME_NEW = 'backups-dup-lite';
|
24 |
+
protected static $Data;
|
25 |
+
protected static $ssDirPath = null;
|
26 |
+
protected static $ssDirUrl = null;
|
27 |
+
/**
|
28 |
+
* Class used to manage all the settings for the plugin
|
29 |
+
*/
|
30 |
+
public static function init()
|
31 |
+
{
|
32 |
+
self::$Data = get_option(self::OPT_SETTINGS);
|
33 |
+
//when the plugin updated, this will be true
|
34 |
+
if (empty(self::$Data) || empty(self::$Data['version']) || version_compare(DUPLICATOR_VERSION, self::$Data['version'], '>')) {
|
35 |
+
self::SetDefaults();
|
36 |
+
}
|
37 |
+
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Find the setting value
|
41 |
+
* @param string $key The name of the key to find
|
42 |
+
* @return string The value stored in the key returns null if key does not exist
|
43 |
+
*/
|
44 |
+
public static function Get($key = '')
|
45 |
+
{
|
46 |
+
$result = null;
|
47 |
+
if (isset(self::$Data[$key])) {
|
48 |
+
$result = self::$Data[$key];
|
49 |
+
} else {
|
50 |
+
$defaults = self::GetAllDefaults();
|
51 |
+
if (isset($defaults[$key])) {
|
52 |
+
$result = $defaults[$key];
|
53 |
+
}
|
54 |
+
}
|
55 |
+
return $result;
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Set the settings value in memory only
|
60 |
+
* @param string $key The name of the key to find
|
61 |
+
* @param string $value The value to set
|
62 |
+
* remarks: The Save() method must be called to write the Settings object to the DB
|
63 |
+
*/
|
64 |
+
public static function Set($key, $value)
|
65 |
+
{
|
66 |
+
if (isset(self::$Data[$key])) {
|
67 |
+
self::$Data[$key] = ($value == null) ? '' : $value;
|
68 |
+
} elseif (!empty($key)) {
|
69 |
+
self::$Data[$key] = ($value == null) ? '' : $value;
|
70 |
+
}
|
71 |
+
}
|
72 |
+
|
73 |
+
public static function setStoragePosition($newPosition)
|
74 |
+
{
|
75 |
+
$legacyPath = self::getSsdirPathLegacy();
|
76 |
+
$wpContPath = self::getSsdirPathWpCont();
|
77 |
+
$oldStoragePost = self::Get('storage_position');
|
78 |
+
self::resetPositionVars();
|
79 |
+
switch ($newPosition) {
|
80 |
+
case self::STORAGE_POSITION_LEGACY:
|
81 |
+
self::$Data['storage_position'] = self::STORAGE_POSITION_LEGACY;
|
82 |
+
if (!DUP_Util::initSnapshotDirectory()) {
|
83 |
+
self::resetPositionVars();
|
84 |
+
self::$Data['storage_position'] = $oldStoragePost;
|
85 |
+
return false;
|
86 |
+
}
|
87 |
+
if (is_dir($wpContPath)) {
|
88 |
+
if (SnapIO::rcopy($wpContPath, $legacyPath)) {
|
89 |
+
SnapIO::rrmdir($wpContPath);
|
90 |
+
}
|
91 |
+
}
|
92 |
+
break;
|
93 |
+
case self::STORAGE_POSITION_WP_CONTENT:
|
94 |
+
self::$Data['storage_position'] = self::STORAGE_POSITION_WP_CONTENT;
|
95 |
+
if (!DUP_Util::initSnapshotDirectory()) {
|
96 |
+
self::resetPositionVars();
|
97 |
+
self::$Data['storage_position'] = $oldStoragePost;
|
98 |
+
return false;
|
99 |
+
}
|
100 |
+
if (is_dir($legacyPath)) {
|
101 |
+
if (SnapIO::rcopy($legacyPath, $wpContPath)) {
|
102 |
+
SnapIO::rrmdir($legacyPath);
|
103 |
+
}
|
104 |
+
}
|
105 |
+
break;
|
106 |
+
default:
|
107 |
+
throw new Exception('Unknown storage position');
|
108 |
+
}
|
109 |
+
|
110 |
+
return true;
|
111 |
+
}
|
112 |
+
|
113 |
+
protected static function resetPositionVars()
|
114 |
+
{
|
115 |
+
self::$ssDirPath = null;
|
116 |
+
self::$ssDirUrl = null;
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* Saves all the setting values to the database
|
121 |
+
* @return True if option value has changed, false if not or if update failed.
|
122 |
+
*/
|
123 |
+
public static function Save()
|
124 |
+
{
|
125 |
+
return update_option(self::OPT_SETTINGS, self::$Data);
|
126 |
+
}
|
127 |
+
|
128 |
+
/**
|
129 |
+
* Deletes all the setting values to the database
|
130 |
+
* @return True if option value has changed, false if not or if update failed.
|
131 |
+
*/
|
132 |
+
public static function Delete()
|
133 |
+
{
|
134 |
+
return delete_option(self::OPT_SETTINGS);
|
135 |
+
}
|
136 |
+
|
137 |
+
/**
|
138 |
+
* Sets the defaults if they have not been set
|
139 |
+
* @return True if option value has changed, false if not or if update failed.
|
140 |
+
*/
|
141 |
+
public static function SetDefaults()
|
142 |
+
{
|
143 |
+
$defaults = self::GetAllDefaults();
|
144 |
+
self::$Data = apply_filters('duplicator_defaults_settings', $defaults);
|
145 |
+
return self::Save();
|
146 |
+
}
|
147 |
+
|
148 |
+
/**
|
149 |
+
* DeleteWPOption: Cleans up legacy data
|
150 |
+
*/
|
151 |
+
public static function DeleteWPOption($optionName)
|
152 |
+
{
|
153 |
+
if (in_array($optionName, $GLOBALS['DUPLICATOR_OPTS_DELETE'])) {
|
154 |
+
return delete_option($optionName);
|
155 |
+
}
|
156 |
+
return false;
|
157 |
+
}
|
158 |
+
|
159 |
+
public static function GetAllDefaults()
|
160 |
+
{
|
161 |
+
$default = array();
|
162 |
+
$default['version'] = DUPLICATOR_VERSION;
|
163 |
+
//Flag used to remove the wp_options value duplicator_settings which are all the settings in this class
|
164 |
+
$default['uninstall_settings'] = isset(self::$Data['uninstall_settings']) ? self::$Data['uninstall_settings'] : true;
|
165 |
+
//Flag used to remove entire wp-snapshot directory
|
166 |
+
$default['uninstall_files'] = isset(self::$Data['uninstall_files']) ? self::$Data['uninstall_files'] : true;
|
167 |
+
//Flag used to remove all tables
|
168 |
+
$default['uninstall_tables'] = isset(self::$Data['uninstall_tables']) ? self::$Data['uninstall_tables'] : true;
|
169 |
+
//Flag used to show debug info
|
170 |
+
$default['package_debug'] = isset(self::$Data['package_debug']) ? self::$Data['package_debug'] : false;
|
171 |
+
//Flag used to enable mysqldump
|
172 |
+
$default['package_mysqldump'] = isset(self::$Data['package_mysqldump']) ? self::$Data['package_mysqldump'] : true;
|
173 |
+
//Optional mysqldump search path
|
174 |
+
$default['package_mysqldump_path'] = isset(self::$Data['package_mysqldump_path']) ? self::$Data['package_mysqldump_path'] : '';
|
175 |
+
//Optional mysql limit size
|
176 |
+
$default['package_phpdump_qrylimit'] = isset(self::$Data['package_phpdump_qrylimit']) ? self::$Data['package_phpdump_qrylimit'] : "100";
|
177 |
+
//Optional mysqldump search path
|
178 |
+
$default['package_zip_flush'] = isset(self::$Data['package_zip_flush']) ? self::$Data['package_zip_flush'] : false;
|
179 |
+
//Optional mysqldump search path
|
180 |
+
$default['installer_name_mode'] = isset(self::$Data['installer_name_mode']) ? self::$Data['installer_name_mode'] : self::INSTALLER_NAME_MODE_SIMPLE;
|
181 |
+
// storage position
|
182 |
+
$default['storage_position'] = isset(self::$Data['storage_position']) ? self::$Data['storage_position'] : self::STORAGE_POSITION_WP_CONTENT;
|
183 |
+
//Flag for .htaccess file
|
184 |
+
$default['storage_htaccess_off'] = isset(self::$Data['storage_htaccess_off']) ? self::$Data['storage_htaccess_off'] : false;
|
185 |
+
// Initial archive build mode
|
186 |
+
if (isset(self::$Data['archive_build_mode'])) {
|
187 |
+
$default['archive_build_mode'] = self::$Data['archive_build_mode'];
|
188 |
+
} else {
|
189 |
+
$is_ziparchive_available = apply_filters('duplicator_is_ziparchive_available', class_exists('ZipArchive'));
|
190 |
+
$default['archive_build_mode'] = $is_ziparchive_available ? DUP_Archive_Build_Mode::ZipArchive : DUP_Archive_Build_Mode::DupArchive;
|
191 |
+
}
|
192 |
+
|
193 |
+
// $default['package_zip_flush'] = apply_filters('duplicator_package_zip_flush_default_setting', '0');
|
194 |
+
//Skip scan archive
|
195 |
+
$default['skip_archive_scan'] = isset(self::$Data['skip_archive_scan']) ? self::$Data['skip_archive_scan'] : false;
|
196 |
+
$default['unhook_third_party_js'] = isset(self::$Data['unhook_third_party_js']) ? self::$Data['unhook_third_party_js'] : false;
|
197 |
+
$default['unhook_third_party_css'] = isset(self::$Data['unhook_third_party_css']) ? self::$Data['unhook_third_party_css'] : false;
|
198 |
+
$default['active_package_id'] = -1;
|
199 |
+
return $default;
|
200 |
+
}
|
201 |
+
|
202 |
+
public static function get_create_date_format()
|
203 |
+
{
|
204 |
+
static $ui_create_frmt = null;
|
205 |
+
if (is_null($ui_create_frmt)) {
|
206 |
+
$ui_create_frmt = is_numeric(self::Get('package_ui_created')) ? self::Get('package_ui_created') : 1;
|
207 |
+
}
|
208 |
+
return $ui_create_frmt;
|
209 |
+
}
|
210 |
+
|
211 |
+
public static function getSsdirPathLegacy()
|
212 |
+
{
|
213 |
+
return SnapIO::safePathTrailingslashit(duplicator_get_abs_path()) . self::SSDIR_NAME_LEGACY;
|
214 |
+
}
|
215 |
+
|
216 |
+
public static function getSsdirPathWpCont()
|
217 |
+
{
|
218 |
+
return SnapIO::safePathTrailingslashit(WP_CONTENT_DIR) . self::SSDIR_NAME_NEW;
|
219 |
+
}
|
220 |
+
|
221 |
+
public static function getSsdirPath()
|
222 |
+
{
|
223 |
+
if (is_null(self::$ssDirPath)) {
|
224 |
+
if (self::Get('storage_position') === self::STORAGE_POSITION_LEGACY) {
|
225 |
+
self::$ssDirPath = self::getSsdirPathLegacy();
|
226 |
+
} else {
|
227 |
+
self::$ssDirPath = self::getSsdirPathWpCont();
|
228 |
+
}
|
229 |
+
}
|
230 |
+
return self::$ssDirPath;
|
231 |
+
}
|
232 |
+
|
233 |
+
public static function getSsdirUrl()
|
234 |
+
{
|
235 |
+
if (is_null(self::$ssDirUrl)) {
|
236 |
+
if (self::Get('storage_position') === self::STORAGE_POSITION_LEGACY) {
|
237 |
+
self::$ssDirUrl = SnapIO::trailingslashit(DUPLICATOR_SITE_URL) . self::SSDIR_NAME_LEGACY;
|
238 |
+
} else {
|
239 |
+
self::$ssDirUrl = SnapIO::trailingslashit(content_url()) . self::SSDIR_NAME_NEW;
|
240 |
+
}
|
241 |
+
}
|
242 |
+
return self::$ssDirUrl;
|
243 |
+
}
|
244 |
+
|
245 |
+
public static function getSsdirTmpPath()
|
246 |
+
{
|
247 |
+
return self::getSsdirPath() . '/tmp';
|
248 |
+
}
|
249 |
+
|
250 |
+
public static function getSsdirInstallerPath()
|
251 |
+
{
|
252 |
+
return self::getSsdirPath() . '/installer';
|
253 |
+
}
|
254 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
classes/host/class.custom.host.manager.php
CHANGED
@@ -10,15 +10,16 @@
|
|
10 |
* @link http://www.php-fig.org/psr/psr-2/
|
11 |
*
|
12 |
*/
|
|
|
13 |
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
14 |
|
15 |
-
require_once
|
16 |
-
require_once
|
17 |
-
require_once
|
18 |
-
require_once
|
19 |
-
require_once
|
20 |
-
require_once
|
21 |
-
require_once
|
22 |
|
23 |
class DUP_Custom_Host_Manager
|
24 |
{
|
@@ -60,7 +61,7 @@ class DUP_Custom_Host_Manager
|
|
60 |
public static function getInstance()
|
61 |
{
|
62 |
if (is_null(self::$instance)) {
|
63 |
-
self::$instance = new self;
|
64 |
}
|
65 |
return self::$instance;
|
66 |
}
|
@@ -142,17 +143,17 @@ class DUP_Custom_Host_Manager
|
|
142 |
|
143 |
public function WPConfigIsNotWriteable()
|
144 |
{
|
145 |
-
$wpConfigPath = duplicator_get_abs_path()."/wp-config.php";
|
146 |
|
147 |
return file_exists($wpConfigPath) && !is_writeable($wpConfigPath);
|
148 |
}
|
149 |
|
150 |
public function notAccessibleCoreDirPresent()
|
151 |
{
|
152 |
-
$absPath
|
153 |
$coreDirs = array(
|
154 |
-
$absPath.'/wp-admin',
|
155 |
-
$absPath.'/wp-includes',
|
156 |
WP_CONTENT_DIR
|
157 |
);
|
158 |
|
10 |
* @link http://www.php-fig.org/psr/psr-2/
|
11 |
*
|
12 |
*/
|
13 |
+
|
14 |
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
15 |
|
16 |
+
require_once(DUPLICATOR_PLUGIN_PATH . '/classes/host/interface.host.php');
|
17 |
+
require_once(DUPLICATOR_PLUGIN_PATH . '/classes/host/class.godaddy.host.php');
|
18 |
+
require_once(DUPLICATOR_PLUGIN_PATH . '/classes/host/class.wpengine.host.php');
|
19 |
+
require_once(DUPLICATOR_PLUGIN_PATH . '/classes/host/class.wordpresscom.host.php');
|
20 |
+
require_once(DUPLICATOR_PLUGIN_PATH . '/classes/host/class.liquidweb.host.php');
|
21 |
+
require_once(DUPLICATOR_PLUGIN_PATH . '/classes/host/class.pantheon.host.php');
|
22 |
+
require_once(DUPLICATOR_PLUGIN_PATH . '/classes/host/class.flywheel.host.php');
|
23 |
|
24 |
class DUP_Custom_Host_Manager
|
25 |
{
|
61 |
public static function getInstance()
|
62 |
{
|
63 |
if (is_null(self::$instance)) {
|
64 |
+
self::$instance = new self();
|
65 |
}
|
66 |
return self::$instance;
|
67 |
}
|
143 |
|
144 |
public function WPConfigIsNotWriteable()
|
145 |
{
|
146 |
+
$wpConfigPath = duplicator_get_abs_path() . "/wp-config.php";
|
147 |
|
148 |
return file_exists($wpConfigPath) && !is_writeable($wpConfigPath);
|
149 |
}
|
150 |
|
151 |
public function notAccessibleCoreDirPresent()
|
152 |
{
|
153 |
+
$absPath = duplicator_get_abs_path();
|
154 |
$coreDirs = array(
|
155 |
+
$absPath . '/wp-admin',
|
156 |
+
$absPath . '/wp-includes',
|
157 |
WP_CONTENT_DIR
|
158 |
);
|
159 |
|
classes/host/class.flywheel.host.php
CHANGED
@@ -9,11 +9,11 @@
|
|
9 |
* @link http://www.php-fig.org/psr/psr-2/
|
10 |
*
|
11 |
*/
|
|
|
12 |
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
13 |
|
14 |
class DUP_Flywheel_Host implements DUP_Host_interface
|
15 |
{
|
16 |
-
|
17 |
public static function getIdentifier()
|
18 |
{
|
19 |
return DUP_Custom_Host_Manager::HOST_FLYWHEEL;
|
@@ -21,12 +21,11 @@ class DUP_Flywheel_Host implements DUP_Host_interface
|
|
21 |
|
22 |
public function isHosting()
|
23 |
{
|
24 |
-
$path = duplicator_get_home_path().'/.fw-config.php';
|
25 |
return apply_filters('duplicator_host_check', file_exists($path), self::getIdentifier());
|
26 |
}
|
27 |
|
28 |
public function init()
|
29 |
{
|
30 |
-
|
31 |
}
|
32 |
}
|
9 |
* @link http://www.php-fig.org/psr/psr-2/
|
10 |
*
|
11 |
*/
|
12 |
+
|
13 |
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
14 |
|
15 |
class DUP_Flywheel_Host implements DUP_Host_interface
|
16 |
{
|
|
|
17 |
public static function getIdentifier()
|
18 |
{
|
19 |
return DUP_Custom_Host_Manager::HOST_FLYWHEEL;
|
21 |
|
22 |
public function isHosting()
|
23 |
{
|
24 |
+
$path = duplicator_get_home_path() . '/.fw-config.php';
|
25 |
return apply_filters('duplicator_host_check', file_exists($path), self::getIdentifier());
|
26 |
}
|
27 |
|
28 |
public function init()
|
29 |
{
|
|
|
30 |
}
|
31 |
}
|
classes/host/class.godaddy.host.php
CHANGED
@@ -1,9 +1,9 @@
|
|
1 |
<?php
|
2 |
-
|
|
|
3 |
|
4 |
class DUP_GoDaddy_Host implements DUP_Host_interface
|
5 |
{
|
6 |
-
|
7 |
public static function getIdentifier()
|
8 |
{
|
9 |
return DUP_Custom_Host_Manager::HOST_GODADDY;
|
@@ -11,7 +11,10 @@ class DUP_GoDaddy_Host implements DUP_Host_interface
|
|
11 |
|
12 |
public function isHosting()
|
13 |
{
|
14 |
-
return apply_filters(
|
|
|
|
|
|
|
15 |
}
|
16 |
|
17 |
public function init()
|
@@ -24,4 +27,4 @@ class DUP_GoDaddy_Host implements DUP_Host_interface
|
|
24 |
$defaults['archive_build_mode'] = DUP_Archive_Build_Mode::DupArchive;
|
25 |
return $defaults;
|
26 |
}
|
27 |
-
}
|
1 |
<?php
|
2 |
+
|
3 |
+
use Duplicator\Libs\Snap\SnapIO;
|
4 |
|
5 |
class DUP_GoDaddy_Host implements DUP_Host_interface
|
6 |
{
|
|
|
7 |
public static function getIdentifier()
|
8 |
{
|
9 |
return DUP_Custom_Host_Manager::HOST_GODADDY;
|
11 |
|
12 |
public function isHosting()
|
13 |
{
|
14 |
+
return apply_filters(
|
15 |
+
'duplicator_godaddy_host_check',
|
16 |
+
file_exists(SnapIO::safePathUntrailingslashit(WPMU_PLUGIN_DIR) . '/gd-system-plugin.php')
|
17 |
+
);
|
18 |
}
|
19 |
|
20 |
public function init()
|
27 |
$defaults['archive_build_mode'] = DUP_Archive_Build_Mode::DupArchive;
|
28 |
return $defaults;
|
29 |
}
|
30 |
+
}
|
classes/host/class.liquidweb.host.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* wpengine custom hosting class
|
4 |
*
|
@@ -8,10 +9,14 @@
|
|
8 |
* @link http://www.php-fig.org/psr/psr-2/
|
9 |
*
|
10 |
*/
|
|
|
|
|
|
|
11 |
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
12 |
|
13 |
class DUP_Liquidweb_Host implements DUP_Host_interface
|
14 |
{
|
|
|
15 |
|
16 |
public static function getIdentifier()
|
17 |
{
|
@@ -20,11 +25,13 @@ class DUP_Liquidweb_Host implements DUP_Host_interface
|
|
20 |
|
21 |
public function isHosting()
|
22 |
{
|
23 |
-
return apply_filters(
|
|
|
|
|
|
|
24 |
}
|
25 |
|
26 |
public function init()
|
27 |
{
|
28 |
-
|
29 |
}
|
30 |
-
}
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* wpengine custom hosting class
|
5 |
*
|
9 |
* @link http://www.php-fig.org/psr/psr-2/
|
10 |
*
|
11 |
*/
|
12 |
+
|
13 |
+
use Duplicator\Libs\Snap\SnapIO;
|
14 |
+
|
15 |
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
16 |
|
17 |
class DUP_Liquidweb_Host implements DUP_Host_interface
|
18 |
{
|
19 |
+
const TEST = 0;
|
20 |
|
21 |
public static function getIdentifier()
|
22 |
{
|
25 |
|
26 |
public function isHosting()
|
27 |
{
|
28 |
+
return apply_filters(
|
29 |
+
'duplicator_liquidweb_host_check',
|
30 |
+
file_exists(SnapIO::safePathUntrailingslashit(WPMU_PLUGIN_DIR) . '/liquid-web.php')
|
31 |
+
);
|
32 |
}
|
33 |
|
34 |
public function init()
|
35 |
{
|
|
|
36 |
}
|
37 |
+
}
|
classes/host/class.pantheon.host.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* godaddy custom hosting class
|
4 |
*
|
@@ -8,11 +9,13 @@
|
|
8 |
* @link http://www.php-fig.org/psr/psr-2/
|
9 |
*
|
10 |
*/
|
|
|
|
|
|
|
11 |
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
12 |
|
13 |
class DUP_Pantheon_Host implements DUP_Host_interface
|
14 |
{
|
15 |
-
|
16 |
public static function getIdentifier()
|
17 |
{
|
18 |
return DUP_Custom_Host_Manager::HOST_PANTHEON;
|
@@ -20,11 +23,13 @@ class DUP_Pantheon_Host implements DUP_Host_interface
|
|
20 |
|
21 |
public function isHosting()
|
22 |
{
|
23 |
-
return apply_filters(
|
|
|
|
|
|
|
24 |
}
|
25 |
|
26 |
public function init()
|
27 |
{
|
28 |
-
|
29 |
}
|
30 |
-
}
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* godaddy custom hosting class
|
5 |
*
|
9 |
* @link http://www.php-fig.org/psr/psr-2/
|
10 |
*
|
11 |
*/
|
12 |
+
|
13 |
+
use Duplicator\Libs\Snap\SnapIO;
|
14 |
+
|
15 |
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
16 |
|
17 |
class DUP_Pantheon_Host implements DUP_Host_interface
|
18 |
{
|
|
|
19 |
public static function getIdentifier()
|
20 |
{
|
21 |
return DUP_Custom_Host_Manager::HOST_PANTHEON;
|
23 |
|
24 |
public function isHosting()
|
25 |
{
|
26 |
+
return apply_filters(
|
27 |
+
'duplicator_pantheon_host_check',
|
28 |
+
file_exists(SnapIO::safePathUntrailingslashit(WPMU_PLUGIN_DIR) . '/pantheon.php')
|
29 |
+
);
|
30 |
}
|
31 |
|
32 |
public function init()
|
33 |
{
|
|
|
34 |
}
|
35 |
+
}
|
classes/host/class.wordpresscom.host.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* godaddy custom hosting class
|
4 |
*
|
@@ -8,11 +9,13 @@
|
|
8 |
* @link http://www.php-fig.org/psr/psr-2/
|
9 |
*
|
10 |
*/
|
|
|
|
|
|
|
11 |
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
12 |
|
13 |
class DUP_WordpressCom_Host implements DUP_Host_interface
|
14 |
{
|
15 |
-
|
16 |
public static function getIdentifier()
|
17 |
{
|
18 |
return DUP_Custom_Host_Manager::HOST_WORDPRESSCOM;
|
@@ -20,11 +23,13 @@ class DUP_WordpressCom_Host implements DUP_Host_interface
|
|
20 |
|
21 |
public function isHosting()
|
22 |
{
|
23 |
-
return apply_filters(
|
|
|
|
|
|
|
24 |
}
|
25 |
|
26 |
public function init()
|
27 |
{
|
28 |
-
|
29 |
}
|
30 |
-
}
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* godaddy custom hosting class
|
5 |
*
|
9 |
* @link http://www.php-fig.org/psr/psr-2/
|
10 |
*
|
11 |
*/
|
12 |
+
|
13 |
+
use Duplicator\Libs\Snap\SnapIO;
|
14 |
+
|
15 |
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
16 |
|
17 |
class DUP_WordpressCom_Host implements DUP_Host_interface
|
18 |
{
|
|
|
19 |
public static function getIdentifier()
|
20 |
{
|
21 |
return DUP_Custom_Host_Manager::HOST_WORDPRESSCOM;
|
23 |
|
24 |
public function isHosting()
|
25 |
{
|
26 |
+
return apply_filters(
|
27 |
+
'duplicator_pro_wordpress_host_check',
|
28 |
+
file_exists(SnapIO::safePathUntrailingslashit(WPMU_PLUGIN_DIR) . '/wpcomsh-loader.php')
|
29 |
+
);
|
30 |
}
|
31 |
|
32 |
public function init()
|
33 |
{
|
|
|
34 |
}
|
35 |
+
}
|
classes/host/class.wpengine.host.php
CHANGED
@@ -1,5 +1,6 @@
|
|
1 |
<?php
|
2 |
-
|
|
|
3 |
|
4 |
// New encryption class
|
5 |
|
@@ -21,7 +22,10 @@ class DUP_WPEngine_Host implements DUP_Host_interface
|
|
21 |
|
22 |
public function isHosting()
|
23 |
{
|
24 |
-
return apply_filters(
|
|
|
|
|
|
|
25 |
}
|
26 |
|
27 |
public static function installerFilePath($path)
|
@@ -36,7 +40,7 @@ class DUP_WPEngine_Host implements DUP_Host_interface
|
|
36 |
|
37 |
public static function globalFileFilters($files)
|
38 |
{
|
39 |
-
$files[] = wp_normalize_path(WP_CONTENT_DIR).'/mysql.sql';
|
40 |
return $files;
|
41 |
}
|
42 |
|
@@ -45,5 +49,4 @@ class DUP_WPEngine_Host implements DUP_Host_interface
|
|
45 |
$defaults['package_zip_flush'] = '1';
|
46 |
return $defaults;
|
47 |
}
|
48 |
-
|
49 |
-
}
|
1 |
<?php
|
2 |
+
|
3 |
+
use Duplicator\Libs\Snap\SnapIO;
|
4 |
|
5 |
// New encryption class
|
6 |
|
22 |
|
23 |
public function isHosting()
|
24 |
{
|
25 |
+
return apply_filters(
|
26 |
+
'duplicator_wp_engine_host_check',
|
27 |
+
file_exists(SnapIO::safePathUntrailingslashit(WPMU_PLUGIN_DIR) . '/wpengine-security-auditor.php')
|
28 |
+
);
|
29 |
}
|
30 |
|
31 |
public static function installerFilePath($path)
|
40 |
|
41 |
public static function globalFileFilters($files)
|
42 |
{
|
43 |
+
$files[] = wp_normalize_path(WP_CONTENT_DIR) . '/mysql.sql';
|
44 |
return $files;
|
45 |
}
|
46 |
|
49 |
$defaults['package_zip_flush'] = '1';
|
50 |
return $defaults;
|
51 |
}
|
52 |
+
}
|
|
classes/host/interface.host.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* interface for specific hostings class
|
4 |
*
|
@@ -8,11 +9,11 @@
|
|
8 |
* @link http://www.php-fig.org/psr/psr-2/
|
9 |
*
|
10 |
*/
|
|
|
11 |
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
12 |
|
13 |
interface DUP_Host_interface
|
14 |
{
|
15 |
-
|
16 |
/**
|
17 |
* return the current host itentifier
|
18 |
*
|
@@ -32,4 +33,4 @@ interface DUP_Host_interface
|
|
32 |
* @return void
|
33 |
*/
|
34 |
public function init();
|
35 |
-
}
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* interface for specific hostings class
|
5 |
*
|
9 |
* @link http://www.php-fig.org/psr/psr-2/
|
10 |
*
|
11 |
*/
|
12 |
+
|
13 |
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
14 |
|
15 |
interface DUP_Host_interface
|
16 |
{
|
|
|
17 |
/**
|
18 |
* return the current host itentifier
|
19 |
*
|
33 |
* @return void
|
34 |
*/
|
35 |
public function init();
|
36 |
+
}
|
classes/index.php
CHANGED
@@ -1,2 +1,3 @@
|
|
1 |
<?php
|
2 |
-
|
|
1 |
<?php
|
2 |
+
|
3 |
+
//silent
|
classes/package/class.pack.archive.file.list.php
ADDED
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* archive path file list object
|
5 |
+
*
|
6 |
+
* Standard: PSR-2
|
7 |
+
* @link http://www.php-fig.org/psr/psr-2
|
8 |
+
*
|
9 |
+
* @package name
|
10 |
+
* @copyright (c) 2019, Snapcreek LLC
|
11 |
+
* @license https://opensource.org/licenses/GPL-3.0 GNU Public License
|
12 |
+
*
|
13 |
+
*/
|
14 |
+
|
15 |
+
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
16 |
+
|
17 |
+
use Duplicator\Libs\Snap\SnapIO;
|
18 |
+
use Duplicator\Libs\Snap\SnapJson;
|
19 |
+
|
20 |
+
class DUP_Archive_File_List
|
21 |
+
{
|
22 |
+
protected $path = null;
|
23 |
+
protected $handle = null;
|
24 |
+
protected $cache = null;
|
25 |
+
|
26 |
+
public function __construct($path)
|
27 |
+
{
|
28 |
+
if (empty($path)) {
|
29 |
+
throw new Exception('path can\'t be empty');
|
30 |
+
}
|
31 |
+
|
32 |
+
$this->path = SnapIO::safePath($path);
|
33 |
+
}
|
34 |
+
|
35 |
+
public function __destruct()
|
36 |
+
{
|
37 |
+
$this->close();
|
38 |
+
}
|
39 |
+
|
40 |
+
public function getPath()
|
41 |
+
{
|
42 |
+
return $this->path;
|
43 |
+
}
|
44 |
+
|
45 |
+
public function open($truncate = false)
|
46 |
+
{
|
47 |
+
if (is_null($this->handle)) {
|
48 |
+
if (($this->handle = fopen($this->path, 'a+')) === false) {
|
49 |
+
DUP_Log::trace('Can\'t open ' . $this->path);
|
50 |
+
$this->handle = null;
|
51 |
+
return false;
|
52 |
+
}
|
53 |
+
}
|
54 |
+
if ($truncate) {
|
55 |
+
$this->emptyFile();
|
56 |
+
}
|
57 |
+
return true;
|
58 |
+
}
|
59 |
+
|
60 |
+
public function emptyFile()
|
61 |
+
{
|
62 |
+
if (!$this->open(false)) {
|
63 |
+
return false;
|
64 |
+
}
|
65 |
+
if (($res = ftruncate($this->handle, 0)) === false) {
|
66 |
+
DUP_Log::trace('Can\'t truncate file ' . $this->path);
|
67 |
+
return false;
|
68 |
+
}
|
69 |
+
return true;
|
70 |
+
}
|
71 |
+
|
72 |
+
public function close()
|
73 |
+
{
|
74 |
+
if (!is_null($this->handle)) {
|
75 |
+
if (($res = @fclose($this->handle)) === false) {
|
76 |
+
DUP_Log::trace('Can\'t close ' . $this->path);
|
77 |
+
return false;
|
78 |
+
}
|
79 |
+
$this->handle = null;
|
80 |
+
}
|
81 |
+
return true;
|
82 |
+
}
|
83 |
+
|
84 |
+
public function addEntry($path, $size, $nodes)
|
85 |
+
{
|
86 |
+
|
87 |
+
if (is_null($this->handle)) { // check to generate less overhead
|
88 |
+
if (!$this->open()) {
|
89 |
+
return false;
|
90 |
+
}
|
91 |
+
}
|
92 |
+
$entry = array('p' => $path, 's' => $size, 'n' => $nodes);
|
93 |
+
fwrite($this->handle, SnapJson::jsonEncode($entry) . "\n");
|
94 |
+
}
|
95 |
+
|
96 |
+
/**
|
97 |
+
*
|
98 |
+
* @param bool $pathOnly if true return only payth
|
99 |
+
* @return boolean|array|string return false if is end of filer.
|
100 |
+
*/
|
101 |
+
public function getEntry($pathOnly = false)
|
102 |
+
{
|
103 |
+
if (is_null($this->handle)) { // check to generate less overhead
|
104 |
+
if (!$this->open()) {
|
105 |
+
return false;
|
106 |
+
}
|
107 |
+
}
|
108 |
+
|
109 |
+
if (($json = fgets($this->handle, 4196)) === false) {
|
110 |
+
// end of file return false
|
111 |
+
return false;
|
112 |
+
}
|
113 |
+
|
114 |
+
$result = json_decode($json, true);
|
115 |
+
if ($pathOnly) {
|
116 |
+
return $result['p'];
|
117 |
+
} else {
|
118 |
+
return $result;
|
119 |
+
}
|
120 |
+
}
|
121 |
+
|
122 |
+
protected function cleanCache()
|
123 |
+
{
|
124 |
+
$this->cache = null;
|
125 |
+
return true;
|
126 |
+
}
|
127 |
+
|
128 |
+
protected function loadCache($refreshCache = false)
|
129 |
+
{
|
130 |
+
if ($refreshCache || is_null($this->cache)) {
|
131 |
+
if (!$this->open()) {
|
132 |
+
return false;
|
133 |
+
}
|
134 |
+
$this->cache = array();
|
135 |
+
if (@fseek($this->handle, 0) === -1) {
|
136 |
+
DUP_Log::trace('Can\'t seek at 0 pos for file ' . $this->path);
|
137 |
+
$this->cleanCache();
|
138 |
+
return false;
|
139 |
+
}
|
140 |
+
while (($entry = $this->getEntry()) !== false) {
|
141 |
+
$this->cache[$entry['p']] = $entry;
|
142 |
+
}
|
143 |
+
if (!feof($this->handle)) {
|
144 |
+
DUP_Log::trace('Error: unexpected fgets() fail', '', false);
|
145 |
+
}
|
146 |
+
}
|
147 |
+
return true;
|
148 |
+
}
|
149 |
+
|
150 |
+
public function getEntryFromPath($path, $refreshCache = false)
|
151 |
+
{
|
152 |
+
if (!$this->loadCache($refreshCache)) {
|
153 |
+
return false;
|
154 |
+
}
|
155 |
+
|
156 |
+
if (array_key_exists($path, $this->cache)) {
|
157 |
+
return $this->cache[$path];
|
158 |
+
} else {
|
159 |
+
return false;
|
160 |
+
}
|
161 |
+
}
|
162 |
+
|
163 |
+
public function getEntriesFormPath($path, $refreshCache = false)
|
164 |
+
{
|
165 |
+
if (!$this->loadCache($refreshCache)) {
|
166 |
+
return false;
|
167 |
+
}
|
168 |
+
|
169 |
+
if (array_key_exists($path, $this->cache)) {
|
170 |
+
$result = array();
|
171 |
+
foreach ($this->cache as $current => $entry) {
|
172 |
+
if (preg_match('/^' . preg_quote($path, '/') . '\/[^\/]+$/', $current)) {
|
173 |
+
$result[] = $entry;
|
174 |
+
}
|
175 |
+
}
|
176 |
+
return $result;
|
177 |
+
} else {
|
178 |
+
return false;
|
179 |
+
}
|
180 |
+
}
|
181 |
+
|
182 |
+
public function getArrayPaths($pathPrefix = '')
|
183 |
+
{
|
184 |
+
if (!$this->open()) {
|
185 |
+
return false;
|
186 |
+
}
|
187 |
+
|
188 |
+
$result = array();
|
189 |
+
if (@fseek($this->handle, 0) === -1) {
|
190 |
+
DUP_Log::trace('Can\'t seek at 0 pos for file ' . $this->path);
|
191 |
+
return false;
|
192 |
+
}
|
193 |
+
$safePrefix = SnapIO::safePathUntrailingslashit($pathPrefix);
|
194 |
+
while (($path = $this->getEntry(true)) !== false) {
|
195 |
+
$result[] = $safePrefix . '/' . $path;
|
196 |
+
}
|
197 |
+
if (!feof($this->handle)) {
|
198 |
+
DUP_Log::trace('Error: unexpected fgets() fail', '', false);
|
199 |
+
}
|
200 |
+
return $result;
|
201 |
+
}
|
202 |
+
}
|
classes/package/class.pack.archive.filters.php
CHANGED
@@ -1,96 +1,96 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
*
|
6 |
-
*
|
7 |
-
* @
|
8 |
-
*
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
if
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
*
|
29 |
-
*
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
public $
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
*
|
45 |
-
*
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
*
|
58 |
-
*
|
59 |
-
*
|
60 |
-
*
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
public $
|
68 |
-
|
69 |
-
public $
|
70 |
-
|
71 |
-
public $
|
72 |
-
public $
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
$this->
|
91 |
-
$this->
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
}
|
96 |
-
|
1 |
+
<?php
|
2 |
+
|
3 |
+
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
4 |
+
/**
|
5 |
+
* The base class for all filter types Directories/Files/Extentions
|
6 |
+
*
|
7 |
+
* @package Duplicator
|
8 |
+
* @subpackage classes/package
|
9 |
+
*
|
10 |
+
*/
|
11 |
+
|
12 |
+
// Exit if accessed directly
|
13 |
+
if (! defined('DUPLICATOR_VERSION')) {
|
14 |
+
exit;
|
15 |
+
}
|
16 |
+
|
17 |
+
class DUP_Archive_Filter_Scope_Base
|
18 |
+
{
|
19 |
+
//All internal storage items that duplicator decides to filter
|
20 |
+
public $Core = array();
|
21 |
+
//Global filter items added from settings
|
22 |
+
public $Global = array();
|
23 |
+
//Items when creating a package or template that a user decides to filter
|
24 |
+
public $Instance = array();
|
25 |
+
}
|
26 |
+
|
27 |
+
/**
|
28 |
+
* The filter types that belong to directories
|
29 |
+
*
|
30 |
+
* @package Duplicator
|
31 |
+
* @subpackage classes/package
|
32 |
+
*
|
33 |
+
*/
|
34 |
+
class DUP_Archive_Filter_Scope_Directory extends DUP_Archive_Filter_Scope_Base
|
35 |
+
{
|
36 |
+
//Items that are not readable
|
37 |
+
public $Warning = array();
|
38 |
+
//Items that are not readable
|
39 |
+
public $Unreadable = array();
|
40 |
+
public $AddonSites = array();
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* The filter types that belong to files
|
45 |
+
*
|
46 |
+
* @package Duplicator
|
47 |
+
* @subpackage classes/package
|
48 |
+
*
|
49 |
+
*/
|
50 |
+
class DUP_Archive_Filter_Scope_File extends DUP_Archive_Filter_Scope_Directory
|
51 |
+
{
|
52 |
+
//Items that are too large
|
53 |
+
public $Size = array();
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* The filter information object which store all information about the filtered
|
58 |
+
* data that is gathered to the execution of a scan process
|
59 |
+
*
|
60 |
+
* @package Duplicator
|
61 |
+
* @subpackage classes/package
|
62 |
+
*
|
63 |
+
*/
|
64 |
+
class DUP_Archive_Filter_Info
|
65 |
+
{
|
66 |
+
/** @var DUP_Archive_Filter_Scope_Directory Contains all folder filter info */
|
67 |
+
public $Dirs = null;
|
68 |
+
/** @var DUP_Archive_Filter_Scope_File Contains all file filter info */
|
69 |
+
public $Files = null;
|
70 |
+
/** @var DUP_Archive_Filter_Scope_Base Contains all extensions filter info */
|
71 |
+
public $Exts = null;
|
72 |
+
public $UDirCount = 0;
|
73 |
+
public $UFileCount = 0;
|
74 |
+
public $UExtCount = 0;
|
75 |
+
public $TreeSize;
|
76 |
+
public $TreeWarning;
|
77 |
+
/**
|
78 |
+
* Init this object
|
79 |
+
*/
|
80 |
+
public function __construct()
|
81 |
+
{
|
82 |
+
$this->reset();
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* reset and clean all object
|
87 |
+
*/
|
88 |
+
public function reset()
|
89 |
+
{
|
90 |
+
$this->Dirs = new DUP_Archive_Filter_Scope_Directory();
|
91 |
+
$this->Files = new DUP_Archive_Filter_Scope_File();
|
92 |
+
$this->Exts = new DUP_Archive_Filter_Scope_Base();
|
93 |
+
$this->TreeSize = array();
|
94 |
+
$this->TreeWarning = array();
|
95 |
+
}
|
96 |
+
}
|
classes/package/class.pack.archive.php
CHANGED
@@ -1,805 +1,1014 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
*
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
public $
|
39 |
-
public $
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
public $
|
46 |
-
public $
|
47 |
-
public $
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
/**
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
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 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
/**
|
214 |
-
*
|
215 |
-
*
|
216 |
-
*
|
217 |
-
* /
|
218 |
-
*
|
219 |
-
*
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
$
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
$
|
422 |
-
|
423 |
-
|
424 |
-
if (
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
-
|
463 |
-
|
464 |
-
|
465 |
-
|
466 |
-
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
$
|
477 |
-
$
|
478 |
-
|
479 |
-
|
480 |
-
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
|
524 |
-
|
525 |
-
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
-
|
530 |
-
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
$
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
|
597 |
-
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
|
605 |
-
|
606 |
-
|
607 |
-
|
608 |
-
|
609 |
-
|
610 |
-
|
611 |
-
|
612 |
-
|
613 |
-
|
614 |
-
|
615 |
-
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
-
|
620 |
-
|
621 |
-
|
622 |
-
|
623 |
-
|
624 |
-
|
625 |
-
|
626 |
-
|
627 |
-
|
628 |
-
|
629 |
-
|
630 |
-
|
631 |
-
|
632 |
-
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
|
637 |
-
|
638 |
-
|
639 |
-
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
-
|
645 |
-
|
646 |
-
|
647 |
-
|
648 |
-
|
649 |
-
|
650 |
-
|
651 |
-
|
652 |
-
|
653 |
-
|
654 |
-
|
655 |
-
|
656 |
-
|
657 |
-
|
658 |
-
|
659 |
-
|
660 |
-
|
661 |
-
|
662 |
-
|
663 |
-
|
664 |
-
|
665 |
-
|
666 |
-
|
667 |
-
|
668 |
-
|
669 |
-
|
670 |
-
|
671 |
-
|
672 |
-
|
673 |
-
|
674 |
-
|
675 |
-
|
676 |
-
|
677 |
-
|
678 |
-
|
679 |
-
|
680 |
-
|
681 |
-
|
682 |
-
|
683 |
-
|
684 |
-
|
685 |
-
|
686 |
-
|
687 |
-
|
688 |
-
|
689 |
-
|
690 |
-
|
691 |
-
|
692 |
-
|
693 |
-
|
694 |
-
|
695 |
-
|
696 |
-
|
697 |
-
|
698 |
-
|
699 |
-
|
700 |
-
|
701 |
-
|
702 |
-
|
703 |
-
|
704 |
-
|
705 |
-
|
706 |
-
|
707 |
-
|
708 |
-
|
709 |
-
|
710 |
-
|
711 |
-
|
712 |
-
|
713 |
-
|
714 |
-
|
715 |
-
|
716 |
-
|
717 |
-
|
718 |
-
|
719 |
-
|
720 |
-
|
721 |
-
|
722 |
-
|
723 |
-
|
724 |
-
|
725 |
-
|
726 |
-
|
727 |
-
|
728 |
-
|
729 |
-
|
730 |
-
|
731 |
-
|
732 |
-
|
733 |
-
|
734 |
-
|
735 |
-
|
736 |
-
|
737 |
-
|
738 |
-
|
739 |
-
|
740 |
-
|
741 |
-
|
742 |
-
|
743 |
-
|
744 |
-
|
745 |
-
|
746 |
-
|
747 |
-
|
748 |
-
|
749 |
-
|
750 |
-
|
751 |
-
|
752 |
-
|
753 |
-
|
754 |
-
|
755 |
-
|
756 |
-
|
757 |
-
|
758 |
-
|
759 |
-
|
760 |
-
|
761 |
-
|
762 |
-
|
763 |
-
|
764 |
-
|
765 |
-
|
766 |
-
|
767 |
-
|
768 |
-
|
769 |
-
|
770 |
-
|
771 |
-
|
772 |
-
|
773 |
-
|
774 |
-
|
775 |
-
|
776 |
-
|
777 |
-
|
778 |
-
|
779 |
-
|
780 |
-
|
781 |
-
|
782 |
-
|
783 |
-
|
784 |
-
|
785 |
-
|
786 |
-
|
787 |
-
|
788 |
-
|
789 |
-
|
790 |
-
|
791 |
-
|
792 |
-
|
793 |
-
|
794 |
-
|
795 |
-
|
796 |
-
|
797 |
-
|
798 |
-
|
799 |
-
|
800 |
-
|
801 |
-
|
802 |
-
|
803 |
-
|
804 |
-
|
805 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
use Duplicator\Libs\Snap\SnapIO;
|
4 |
+
use Duplicator\Libs\Snap\SnapOS;
|
5 |
+
use Duplicator\Libs\Snap\SnapURL;
|
6 |
+
use Duplicator\Libs\Snap\SnapUtil;
|
7 |
+
use Duplicator\Libs\Snap\SnapWP;
|
8 |
+
|
9 |
+
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
10 |
+
// Exit if accessed directly
|
11 |
+
if (!defined('DUPLICATOR_VERSION')) {
|
12 |
+
exit;
|
13 |
+
}
|
14 |
+
|
15 |
+
require_once(DUPLICATOR_PLUGIN_PATH . 'classes/package/duparchive/class.pack.archive.duparchive.php');
|
16 |
+
require_once(DUPLICATOR_PLUGIN_PATH . 'classes/package/class.pack.archive.file.list.php');
|
17 |
+
require_once(DUPLICATOR_PLUGIN_PATH . 'classes/package/class.pack.archive.filters.php');
|
18 |
+
require_once(DUPLICATOR_PLUGIN_PATH . 'classes/package/class.pack.archive.zip.php');
|
19 |
+
require_once(DUPLICATOR_PLUGIN_PATH . 'lib/forceutf8/Encoding.php');
|
20 |
+
/**
|
21 |
+
* Class for handling archive setup and build process
|
22 |
+
*
|
23 |
+
* Standard: PSR-2 (almost)
|
24 |
+
* @link http://www.php-fig.org/psr/psr-2
|
25 |
+
*
|
26 |
+
* @package DUP
|
27 |
+
* @subpackage classes/package
|
28 |
+
* @copyright (c) 2017, Snapcreek LLC
|
29 |
+
* @license https://opensource.org/licenses/GPL-3.0 GNU Public License
|
30 |
+
*
|
31 |
+
*/
|
32 |
+
class DUP_Archive
|
33 |
+
{
|
34 |
+
const DIRS_LIST_FILE_NAME_SUFFIX = '_dirs.txt';
|
35 |
+
const FILES_LIST_FILE_NAME_SUFFIX = '_files.txt';
|
36 |
+
|
37 |
+
//PUBLIC
|
38 |
+
public $FilterDirs;
|
39 |
+
public $FilterFiles;
|
40 |
+
public $FilterExts;
|
41 |
+
public $FilterDirsAll = array();
|
42 |
+
public $FilterFilesAll = array();
|
43 |
+
public $FilterExtsAll = array();
|
44 |
+
public $FilterOn;
|
45 |
+
public $ExportOnlyDB;
|
46 |
+
public $File;
|
47 |
+
public $Format;
|
48 |
+
public $PackDir = '';
|
49 |
+
public $Size = 0;
|
50 |
+
public $Dirs = array();
|
51 |
+
public $dirsCount = 0;
|
52 |
+
public $Files = array();
|
53 |
+
public $filesCount = 0;
|
54 |
+
/** @var DUP_Archive_Filter_Info */
|
55 |
+
public $FilterInfo = null;
|
56 |
+
public $RecursiveLinks = array();
|
57 |
+
public $file_count = -1;
|
58 |
+
/** @var DUP_Package */
|
59 |
+
protected $Package;
|
60 |
+
private $tmpFilterDirsAll = array();
|
61 |
+
private $wpCorePaths = array();
|
62 |
+
private $wpCoreExactPaths = array();
|
63 |
+
private $relativeFiltersDir = array();
|
64 |
+
|
65 |
+
/** @var DUP_Archive_File_List */
|
66 |
+
private $listFileObj = null;
|
67 |
+
/** @var DUP_Archive_File_List */
|
68 |
+
private $listDirObj = null;
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Class constructor
|
72 |
+
*
|
73 |
+
* @param DUP_Package $package
|
74 |
+
*/
|
75 |
+
public function __construct(DUP_Package $package)
|
76 |
+
{
|
77 |
+
$this->Package = $package;
|
78 |
+
$this->FilterOn = false;
|
79 |
+
$this->ExportOnlyDB = false;
|
80 |
+
$this->FilterInfo = new DUP_Archive_Filter_Info();
|
81 |
+
$this->PackDir = $this->getTargetRootPath();
|
82 |
+
|
83 |
+
$paths = self::getArchiveListPaths();
|
84 |
+
$this->wpCorePaths[] = $paths['abs'] . '/wp-admin';
|
85 |
+
$this->wpCorePaths[] = $paths['abs'] . '/wp-includes';
|
86 |
+
$this->wpCorePaths[] = $paths['wpcontent'] . '/languages';
|
87 |
+
$this->wpCoreExactPaths[] = $paths['home'];
|
88 |
+
$this->wpCoreExactPaths[] = $paths['abs'];
|
89 |
+
$this->wpCoreExactPaths[] = $paths['wpcontent'];
|
90 |
+
$this->wpCoreExactPaths[] = $paths['uploads'];
|
91 |
+
$this->wpCoreExactPaths[] = $paths['plugins'];
|
92 |
+
$this->wpCoreExactPaths[] = $paths['muplugins'];
|
93 |
+
$this->wpCoreExactPaths[] = $paths['themes'];
|
94 |
+
|
95 |
+
$this->relativeFiltersDir = array(DUP_Settings::getSsdirTmpPath(), 'backups-dup-pro');
|
96 |
+
}
|
97 |
+
|
98 |
+
/**
|
99 |
+
* Builds the archive based on the archive type
|
100 |
+
*
|
101 |
+
* @param obj $package The package object that started this process
|
102 |
+
*
|
103 |
+
* @return null
|
104 |
+
*/
|
105 |
+
public function build($package, $rethrow_exception = false)
|
106 |
+
{
|
107 |
+
DUP_LOG::trace("b1");
|
108 |
+
$this->Package = $package;
|
109 |
+
if (!isset($this->PackDir) && !is_dir($this->PackDir)) {
|
110 |
+
throw new Exception("The 'PackDir' property must be a valid directory.");
|
111 |
+
}
|
112 |
+
if (!isset($this->File)) {
|
113 |
+
throw new Exception("A 'File' property must be set.");
|
114 |
+
}
|
115 |
+
|
116 |
+
DUP_LOG::trace("b2");
|
117 |
+
$completed = false;
|
118 |
+
|
119 |
+
switch ($this->Format) {
|
120 |
+
case 'TAR':
|
121 |
+
break;
|
122 |
+
case 'TAR-GZIP':
|
123 |
+
break;
|
124 |
+
case 'DAF':
|
125 |
+
$completed = DUP_DupArchive::create($this, $this->Package->BuildProgress, $this->Package);
|
126 |
+
$this->Package->Update();
|
127 |
+
break;
|
128 |
+
default:
|
129 |
+
if (class_exists('ZipArchive')) {
|
130 |
+
$this->Format = 'ZIP';
|
131 |
+
DUP_Zip::create($this, $this->Package->BuildProgress);
|
132 |
+
$completed = true;
|
133 |
+
}
|
134 |
+
|
135 |
+
break;
|
136 |
+
}
|
137 |
+
|
138 |
+
DUP_LOG::Trace("Completed build or build thread");
|
139 |
+
if ($this->Package->BuildProgress === null) {
|
140 |
+
// Zip path
|
141 |
+
DUP_LOG::Trace("Completed Zip");
|
142 |
+
$storePath = DUP_Settings::getSsdirTmpPath() . "/{$this->File}";
|
143 |
+
$this->Size = @filesize($storePath);
|
144 |
+
$this->Package->setStatus(DUP_PackageStatus::ARCDONE);
|
145 |
+
} elseif ($completed) {
|
146 |
+
// Completed DupArchive path
|
147 |
+
DUP_LOG::Trace("Completed DupArchive build");
|
148 |
+
if ($this->Package->BuildProgress->failed) {
|
149 |
+
DUP_LOG::Trace("Error building DupArchive");
|
150 |
+
$this->Package->setStatus(DUP_PackageStatus::ERROR);
|
151 |
+
} else {
|
152 |
+
$filepath = DUP_Settings::getSsdirTmpPath() . "/{$this->File}";
|
153 |
+
$this->Size = @filesize($filepath);
|
154 |
+
$this->Package->setStatus(DUP_PackageStatus::ARCDONE);
|
155 |
+
DUP_LOG::Trace("Done building archive");
|
156 |
+
}
|
157 |
+
} else {
|
158 |
+
DUP_Log::trace("DupArchive chunk done but package not completed yet");
|
159 |
+
}
|
160 |
+
}
|
161 |
+
|
162 |
+
/**
|
163 |
+
*
|
164 |
+
* @return int return DUP_Archive_Build_Mode
|
165 |
+
*/
|
166 |
+
public function getBuildMode()
|
167 |
+
{
|
168 |
+
switch ($this->Format) {
|
169 |
+
case 'TAR':
|
170 |
+
break;
|
171 |
+
case 'TAR-GZIP':
|
172 |
+
break;
|
173 |
+
case 'DAF':
|
174 |
+
return DUP_Archive_Build_Mode::DupArchive;
|
175 |
+
default:
|
176 |
+
if (class_exists('ZipArchive')) {
|
177 |
+
return DUP_Archive_Build_Mode::ZipArchive;
|
178 |
+
} else {
|
179 |
+
return DUP_Archive_Build_Mode::Unconfigured;
|
180 |
+
}
|
181 |
+
|
182 |
+
break;
|
183 |
+
}
|
184 |
+
}
|
185 |
+
|
186 |
+
/**
|
187 |
+
* Initializes the file list handles. Handles are set-up as properties for
|
188 |
+
* performance improvement. Otherwise each handle would be opened and closed
|
189 |
+
* with each path added.
|
190 |
+
*/
|
191 |
+
private function initFileListHandles()
|
192 |
+
{
|
193 |
+
DUP_Log::trace('Inif list files');
|
194 |
+
if (is_null($this->listDirObj)) {
|
195 |
+
$this->listDirObj = new DUP_Archive_File_List(DUP_Settings::getSsdirTmpPath() . '/' . $this->Package->get_dirs_list_filename());
|
196 |
+
}
|
197 |
+
if (is_null($this->listFileObj)) {
|
198 |
+
$this->listFileObj = new DUP_Archive_File_List(DUP_Settings::getSsdirTmpPath() . '/' . $this->Package->get_files_list_filename());
|
199 |
+
}
|
200 |
+
$this->listDirObj->open(true);
|
201 |
+
$this->listFileObj->open(true);
|
202 |
+
}
|
203 |
+
|
204 |
+
/**
|
205 |
+
* Closes file and dir list handles
|
206 |
+
*/
|
207 |
+
private function closeFileListHandles()
|
208 |
+
{
|
209 |
+
$this->listDirObj->close();
|
210 |
+
$this->listFileObj->close();
|
211 |
+
}
|
212 |
+
|
213 |
+
/**
|
214 |
+
* Builds a list of files and directories to be included in the archive
|
215 |
+
*
|
216 |
+
* Get the directory size recursively, but don't calc the snapshot directory, exclusion directories
|
217 |
+
* @link http://msdn.microsoft.com/en-us/library/aa365247%28VS.85%29.aspx Windows filename restrictions
|
218 |
+
*
|
219 |
+
* @return obj Returns a DUP_Archive object
|
220 |
+
*/
|
221 |
+
public function getScannerData()
|
222 |
+
{
|
223 |
+
$this->initFileListHandles();
|
224 |
+
$this->createFilterInfo();
|
225 |
+
$rootPath = $this->getTargetRootPath();
|
226 |
+
if (strlen($rootPath) == 0) {
|
227 |
+
$rootPath = '/';
|
228 |
+
}
|
229 |
+
|
230 |
+
$this->RecursiveLinks = array();
|
231 |
+
// If the root directory is a filter then skip it all
|
232 |
+
if (in_array($rootPath, $this->FilterDirsAll) || $this->Package->Archive->ExportOnlyDB) {
|
233 |
+
$this->Dirs = array();
|
234 |
+
$this->dirsCount = 0;
|
235 |
+
$this->Files = array();
|
236 |
+
$this->filesCount = 0;
|
237 |
+
} else {
|
238 |
+
$mainSize = 0;
|
239 |
+
$mainNodes = 0;
|
240 |
+
$pathsToScan = self::getScanPaths();
|
241 |
+
foreach ($pathsToScan as $path) {
|
242 |
+
DUP_Log::trace('START SCAN PATH: ' . $path);
|
243 |
+
$relativePath = SnapIO::getRelativePath($path, $rootPath);
|
244 |
+
if (($result = $this->getFileLists($path, $relativePath)) != false) {
|
245 |
+
$this->addToDirList($path, $relativePath, $result['size'], $result['nodes'] + 1);
|
246 |
+
$mainSize += $result['size'];
|
247 |
+
$mainNodes += $result['nodes'] + 1;
|
248 |
+
} else {
|
249 |
+
DUP_Log::trace('Can\'t scan the folder ' . $rootPath);
|
250 |
+
}
|
251 |
+
}
|
252 |
+
|
253 |
+
if (!in_array($rootPath, $pathsToScan)) {
|
254 |
+
$this->addToDirList($rootPath, '', $mainSize, $mainNodes + 1);
|
255 |
+
}
|
256 |
+
|
257 |
+
$this->setTreeFilters();
|
258 |
+
}
|
259 |
+
|
260 |
+
$this->closeFileListHandles();
|
261 |
+
|
262 |
+
$this->FilterDirsAll = array_merge($this->FilterDirsAll, $this->FilterInfo->Dirs->Unreadable);
|
263 |
+
$this->FilterFilesAll = array_merge($this->FilterFilesAll, $this->FilterInfo->Files->Unreadable);
|
264 |
+
sort($this->FilterDirsAll);
|
265 |
+
return $this;
|
266 |
+
}
|
267 |
+
|
268 |
+
/**
|
269 |
+
* Save any property of this class through reflection
|
270 |
+
*
|
271 |
+
* @param $property A valid public property in this class
|
272 |
+
* @param $value The value for the new dynamic property
|
273 |
+
*
|
274 |
+
* @return bool Returns true if the value has changed.
|
275 |
+
*/
|
276 |
+
public function saveActiveItem($package, $property, $value)
|
277 |
+
{
|
278 |
+
$package = DUP_Package::getActive();
|
279 |
+
$reflectionClass = new ReflectionClass($package->Archive);
|
280 |
+
$reflectionClass->getProperty($property)->setValue($package->Archive, $value);
|
281 |
+
return update_option(DUP_Package::OPT_ACTIVE, $package);
|
282 |
+
}
|
283 |
+
|
284 |
+
/**
|
285 |
+
* Properly creates the directory filter list that is used for filtering directories
|
286 |
+
*
|
287 |
+
* @param string $dirs A semi-colon list of dir paths
|
288 |
+
* /path1_/path/;/path1_/path2/;
|
289 |
+
*
|
290 |
+
* @returns string A cleaned up list of directory filters
|
291 |
+
* @return string
|
292 |
+
*/
|
293 |
+
public function parseDirectoryFilter($dirs = "")
|
294 |
+
{
|
295 |
+
$filters = "";
|
296 |
+
$dir_array = array_unique(explode(";", $dirs));
|
297 |
+
$clean_array = array();
|
298 |
+
foreach ($dir_array as $val) {
|
299 |
+
$val = SnapIO::safePathUntrailingslashit(SnapUtil::sanitizeNSCharsNewlineTrim($val));
|
300 |
+
if (strlen($val) >= 2 && is_dir($val)) {
|
301 |
+
$clean_array[] = $val;
|
302 |
+
}
|
303 |
+
}
|
304 |
+
|
305 |
+
if (count($clean_array)) {
|
306 |
+
$clean_array = array_unique($clean_array);
|
307 |
+
sort($clean_array);
|
308 |
+
$filters = implode(';', $clean_array) . ';';
|
309 |
+
}
|
310 |
+
return $filters;
|
311 |
+
}
|
312 |
+
|
313 |
+
/**
|
314 |
+
* Properly creates the file filter list that is used for filtering files
|
315 |
+
*
|
316 |
+
* @param string $files A semi-colon list of file paths
|
317 |
+
* /path1_/path/file1.ext;/path1_/path2/file2.ext;
|
318 |
+
*
|
319 |
+
* @returns string A cleaned up list of file filters
|
320 |
+
* @return string
|
321 |
+
*/
|
322 |
+
public function parseFileFilter($files = "")
|
323 |
+
{
|
324 |
+
$filters = "";
|
325 |
+
$file_array = array_unique(explode(";", $files));
|
326 |
+
$clean_array = array();
|
327 |
+
foreach ($file_array as $val) {
|
328 |
+
$val = SnapIO::safePathUntrailingslashit(SnapUtil::sanitizeNSCharsNewlineTrim($val));
|
329 |
+
if (strlen($val) >= 2 && file_exists($val)) {
|
330 |
+
$clean_array[] = $val;
|
331 |
+
}
|
332 |
+
}
|
333 |
+
|
334 |
+
if (count($clean_array)) {
|
335 |
+
$clean_array = array_unique($clean_array);
|
336 |
+
sort($clean_array);
|
337 |
+
$filters = implode(';', $clean_array) . ';';
|
338 |
+
}
|
339 |
+
return $filters;
|
340 |
+
}
|
341 |
+
|
342 |
+
/**
|
343 |
+
* Properly creates the extension filter list that is used for filtering extensions
|
344 |
+
*
|
345 |
+
* @param string $dirs A semi-colon list of dir paths
|
346 |
+
* .jpg;.zip;.gif;
|
347 |
+
*
|
348 |
+
* @returns string A cleaned up list of extension filters
|
349 |
+
*/
|
350 |
+
public function parseExtensionFilter($extensions = "")
|
351 |
+
{
|
352 |
+
$filter_exts = "";
|
353 |
+
if (strlen($extensions) >= 1 && $extensions != ";") {
|
354 |
+
$filter_exts = str_replace(array(' ', '.'), '', $extensions);
|
355 |
+
$filter_exts = str_replace(",", ";", $filter_exts);
|
356 |
+
$filter_exts = DUP_Util::appendOnce($extensions, ";");
|
357 |
+
}
|
358 |
+
return $filter_exts;
|
359 |
+
}
|
360 |
+
|
361 |
+
/**
|
362 |
+
* Creates the filter info setup data used for filtering the archive
|
363 |
+
*
|
364 |
+
* @return null
|
365 |
+
*/
|
366 |
+
private function createFilterInfo()
|
367 |
+
{
|
368 |
+
//FILTER: INSTANCE ITEMS
|
369 |
+
//Add the items generated at create time
|
370 |
+
if ($this->FilterOn) {
|
371 |
+
$this->FilterInfo->Dirs->Instance = array_map('DUP_Util::safePath', explode(";", $this->FilterDirs, -1));
|
372 |
+
$this->FilterInfo->Files->Instance = array_map('DUP_Util::safePath', explode(";", $this->FilterFiles, -1));
|
373 |
+
$this->FilterInfo->Exts->Instance = explode(";", $this->FilterExts, -1);
|
374 |
+
}
|
375 |
+
|
376 |
+
//FILTER: CORE ITMES
|
377 |
+
//Filters Duplicator free packages & All pro local directories
|
378 |
+
$wp_root = duplicator_get_abs_path();
|
379 |
+
$upload_dir = wp_upload_dir();
|
380 |
+
$upload_dir = isset($upload_dir['basedir']) ? basename($upload_dir['basedir']) : 'uploads';
|
381 |
+
$wp_content = str_replace("\\", "/", WP_CONTENT_DIR);
|
382 |
+
$wp_content_upload = "{$wp_content}/{$upload_dir}";
|
383 |
+
$this->FilterInfo->Dirs->Core = array(
|
384 |
+
//WP-ROOT
|
385 |
+
DUP_Settings::getSsdirPathLegacy(),
|
386 |
+
DUP_Settings::getSsdirPathWpCont(),
|
387 |
+
$wp_root . '/.opcache',
|
388 |
+
//WP-CONTENT
|
389 |
+
$wp_content . '/backups-dup-pro',
|
390 |
+
$wp_content . '/ai1wm-backups',
|
391 |
+
$wp_content . '/backupwordpress',
|
392 |
+
$wp_content . '/content/cache',
|
393 |
+
$wp_content . '/contents/cache',
|
394 |
+
$wp_content . '/infinitewp/backups',
|
395 |
+
$wp_content . '/managewp/backups',
|
396 |
+
$wp_content . '/old-cache',
|
397 |
+
$wp_content . '/plugins/all-in-one-wp-migration/storage',
|
398 |
+
$wp_content . '/updraft',
|
399 |
+
$wp_content . '/wishlist-backup',
|
400 |
+
$wp_content . '/wfcache',
|
401 |
+
$wp_content . '/bps-backup', // BulletProof Security backup folder
|
402 |
+
$wp_content . '/cache',
|
403 |
+
//WP-CONTENT-UPLOADS
|
404 |
+
$wp_content_upload . '/aiowps_backups',
|
405 |
+
$wp_content_upload . '/backupbuddy_temp',
|
406 |
+
$wp_content_upload . '/backupbuddy_backups',
|
407 |
+
$wp_content_upload . '/ithemes-security/backups',
|
408 |
+
$wp_content_upload . '/mainwp/backup',
|
409 |
+
$wp_content_upload . '/pb_backupbuddy',
|
410 |
+
$wp_content_upload . '/snapshots',
|
411 |
+
$wp_content_upload . '/sucuri',
|
412 |
+
$wp_content_upload . '/wp-clone',
|
413 |
+
$wp_content_upload . '/wp_all_backup',
|
414 |
+
$wp_content_upload . '/wpbackitup_backups'
|
415 |
+
);
|
416 |
+
if (class_exists('BackWPup')) {
|
417 |
+
$upload_dir = wp_upload_dir(null, false, true);
|
418 |
+
$this->FilterInfo->Dirs->Core[] = trailingslashit(str_replace(
|
419 |
+
'\\',
|
420 |
+
'/',
|
421 |
+
$upload_dir['basedir']
|
422 |
+
)) . 'backwpup-' . BackWPup::get_plugin_data('hash') . '-backups/';
|
423 |
+
$backwpup_cfg_logfolder = get_site_option('backwpup_cfg_logfolder');
|
424 |
+
if (false !== $backwpup_cfg_logfolder) {
|
425 |
+
$this->FilterInfo->Dirs->Core[] = $wp_content . '/' . $backwpup_cfg_logfolder;
|
426 |
+
}
|
427 |
+
}
|
428 |
+
if ($GLOBALS['DUPLICATOR_GLOBAL_FILE_FILTERS_ON']) {
|
429 |
+
$duplicator_global_file_filters = apply_filters('duplicator_global_file_filters', $GLOBALS['DUPLICATOR_GLOBAL_FILE_FILTERS']);
|
430 |
+
$this->FilterInfo->Files->Global = $duplicator_global_file_filters;
|
431 |
+
}
|
432 |
+
|
433 |
+
$this->FilterDirsAll = array_merge($this->FilterInfo->Dirs->Instance, $this->FilterInfo->Dirs->Core);
|
434 |
+
$this->FilterExtsAll = array_merge($this->FilterInfo->Exts->Instance, $this->FilterInfo->Exts->Core);
|
435 |
+
$this->FilterFilesAll = array_merge($this->FilterInfo->Files->Instance, $this->FilterInfo->Files->Global);
|
436 |
+
$abs_path = duplicator_get_abs_path();
|
437 |
+
$this->FilterFilesAll[] = $abs_path . '/.htaccess';
|
438 |
+
$this->FilterFilesAll[] = $abs_path . '/web.config';
|
439 |
+
$this->FilterFilesAll[] = $abs_path . '/wp-config.php';
|
440 |
+
$this->tmpFilterDirsAll = $this->FilterDirsAll;
|
441 |
+
//PHP 5 on windows decode patch
|
442 |
+
if (!DUP_Util::$PHP7_plus && DUP_Util::isWindows()) {
|
443 |
+
foreach ($this->tmpFilterDirsAll as $key => $value) {
|
444 |
+
if (preg_match('/[^\x20-\x7f]/', $value)) {
|
445 |
+
$this->tmpFilterDirsAll[$key] = utf8_decode($value);
|
446 |
+
}
|
447 |
+
}
|
448 |
+
}
|
449 |
+
}
|
450 |
+
|
451 |
+
/**
|
452 |
+
* Recursive function to get all directories in a wp install
|
453 |
+
*
|
454 |
+
* @notes:
|
455 |
+
* Older PHP logic which is more stable on older version of PHP
|
456 |
+
* NOTE RecursiveIteratorIterator is problematic on some systems issues include:
|
457 |
+
* - error 'too many files open' for recursion
|
458 |
+
* - $file->getExtension() is not reliable as it silently fails at least in php 5.2.17
|
459 |
+
* - issues with when a file has a permission such as 705 and trying to get info (had to fallback to path-info)
|
460 |
+
* - basic conclusion wait on the SPL libs until after php 5.4 is a requirments
|
461 |
+
* - tight recursive loop use caution for speed
|
462 |
+
*
|
463 |
+
* @return array Returns an array of directories to include in the archive
|
464 |
+
*/
|
465 |
+
private function getFileLists($path, $relativePath)
|
466 |
+
{
|
467 |
+
if (($handle = opendir((strlen($path) === 0 ? '/' : $path))) === false) {
|
468 |
+
DUP_Log::trace('Can\'t open dir: ' . $path);
|
469 |
+
return false;
|
470 |
+
}
|
471 |
+
|
472 |
+
$result = array(
|
473 |
+
'size' => 0,
|
474 |
+
'nodes' => 1
|
475 |
+
);
|
476 |
+
$trimmedRelativePath = ltrim($relativePath . '/', '/');
|
477 |
+
while (($currentName = readdir($handle)) !== false) {
|
478 |
+
if ($currentName == '.' || $currentName == '..') {
|
479 |
+
continue;
|
480 |
+
}
|
481 |
+
$currentPath = $path . '/' . $currentName;
|
482 |
+
//DUP_Log::trace(' ANALIZE PATH: '.$currentPath);
|
483 |
+
|
484 |
+
if (is_dir($currentPath)) {
|
485 |
+
DUP_Log::trace(' Scan dir: ' . $currentPath);
|
486 |
+
$add = true;
|
487 |
+
if (is_link($currentPath)) {
|
488 |
+
//Get real path of link
|
489 |
+
//trailing slash is essential!
|
490 |
+
$realPath = SnapIO::safePathTrailingslashit($currentPath, true);
|
491 |
+
//if $currentPath starts with $realPath and is link
|
492 |
+
//=> $currentPath is located in $realPath and points back to it
|
493 |
+
if (strpos($currentPath, $realPath) === 0) {
|
494 |
+
$this->RecursiveLinks[] = $currentPath;
|
495 |
+
continue;
|
496 |
+
}
|
497 |
+
}
|
498 |
+
|
499 |
+
if (in_array($currentName, $this->relativeFiltersDir)) {
|
500 |
+
$add = false;
|
501 |
+
} else {
|
502 |
+
foreach ($this->tmpFilterDirsAll as $filteredDir) {
|
503 |
+
if (SnapIO::isChildPath($currentPath, $filteredDir)) {
|
504 |
+
$add = false;
|
505 |
+
break;
|
506 |
+
}
|
507 |
+
}
|
508 |
+
}
|
509 |
+
|
510 |
+
if ($add) {
|
511 |
+
$childResult = $this->getFileLists($currentPath, $trimmedRelativePath . $currentName);
|
512 |
+
$result['size'] += $childResult['size'];
|
513 |
+
$result['nodes'] += $childResult['nodes'];
|
514 |
+
$this->addToDirList($currentPath, $trimmedRelativePath . $currentName, $childResult['size'], $childResult['nodes']);
|
515 |
+
}
|
516 |
+
} else {
|
517 |
+
// Note: The last clause is present to perform just a filename check
|
518 |
+
if (
|
519 |
+
!(in_array(pathinfo($currentName, PATHINFO_EXTENSION), $this->FilterExtsAll) ||
|
520 |
+
in_array($currentPath, $this->FilterFilesAll) ||
|
521 |
+
in_array($currentName, $this->FilterFilesAll))
|
522 |
+
) {
|
523 |
+
$fileSize = (int) @filesize($currentPath);
|
524 |
+
$result['size'] += $fileSize;
|
525 |
+
$result['nodes'] += 1;
|
526 |
+
$this->addToFileList($currentPath, $trimmedRelativePath . $currentName, $fileSize);
|
527 |
+
}
|
528 |
+
}
|
529 |
+
}
|
530 |
+
closedir($handle);
|
531 |
+
return $result;
|
532 |
+
}
|
533 |
+
|
534 |
+
public static function isValidEncoding($string)
|
535 |
+
{
|
536 |
+
return !preg_match('/([\/\*\?><\:\\\\\|]|[^\x20-\x7f])/', $string);
|
537 |
+
}
|
538 |
+
|
539 |
+
private function addToDirList($dirPath, $relativePath, $size, $nodes)
|
540 |
+
{
|
541 |
+
//Dir is not readble remove and flag
|
542 |
+
if (!SnapOS::isWindows() && !is_readable($dirPath)) {
|
543 |
+
$this->FilterInfo->Dirs->Unreadable[] = $dirPath;
|
544 |
+
return;
|
545 |
+
}
|
546 |
+
|
547 |
+
// is relative path is empty is the root path
|
548 |
+
if (strlen($relativePath) && !DUP_Settings::Get('skip_archive_scan')) {
|
549 |
+
$name = basename($dirPath);
|
550 |
+
// Locate invalid directories and warn
|
551 |
+
$invalid_encoding = !self::isValidEncoding($name);
|
552 |
+
if ($invalid_encoding) {
|
553 |
+
$dirPath = DUP_Encoding::toUTF8($dirPath);
|
554 |
+
}
|
555 |
+
$trimmedName = trim($name);
|
556 |
+
$invalid_name = (
|
557 |
+
$invalid_encoding ||
|
558 |
+
(defined('PHP_MAXPATHLEN') && strlen($dirPath) > PHP_MAXPATHLEN) ||
|
559 |
+
strlen($trimmedName) === 0 ||
|
560 |
+
$name[strlen($name) - 1] === '.'
|
561 |
+
);
|
562 |
+
if ($invalid_name) {
|
563 |
+
$this->FilterInfo->Dirs->Warning[] = $dirPath;
|
564 |
+
}
|
565 |
+
|
566 |
+
if ($size > DUPLICATOR_SCAN_WARN_DIR_SIZE) {
|
567 |
+
$dirData = array(
|
568 |
+
'ubytes' => $size,
|
569 |
+
'bytes' => DUP_Util::byteSize($size, 0),
|
570 |
+
'nodes' => $nodes,
|
571 |
+
'name' => $name,
|
572 |
+
'dir' => pathinfo($dirPath, PATHINFO_DIRNAME),
|
573 |
+
'path' => $dirPath
|
574 |
+
);
|
575 |
+
$this->FilterInfo->Dirs->Size[] = $dirData;
|
576 |
+
DUP_Log::traceObject('ADD DIR SIZE:', $dirData);
|
577 |
+
}
|
578 |
+
|
579 |
+
//Check for other WordPress installs
|
580 |
+
if (!self::isCurrentWordpressInstallPath($dirPath) && SnapWP::isWpHomeFolder($dirPath)) {
|
581 |
+
$this->FilterInfo->Dirs->AddonSites[] = $dirPath;
|
582 |
+
}
|
583 |
+
}
|
584 |
+
|
585 |
+
$this->listDirObj->addEntry($relativePath, $size, $nodes);
|
586 |
+
$this->Dirs[] = $dirPath;
|
587 |
+
$this->dirsCount ++;
|
588 |
+
}
|
589 |
+
|
590 |
+
private function addToFileList($filePath, $relativePath, $fileSize)
|
591 |
+
{
|
592 |
+
if (!is_readable($filePath)) {
|
593 |
+
$this->FilterInfo->Files->Unreadable[] = $filePath;
|
594 |
+
return;
|
595 |
+
}
|
596 |
+
|
597 |
+
if (!DUP_Settings::Get('skip_archive_scan')) {
|
598 |
+
$fileName = basename($filePath);
|
599 |
+
//File Warnings
|
600 |
+
$invalid_encoding = !self::isValidEncoding($fileName);
|
601 |
+
$trimmed_name = trim($fileName);
|
602 |
+
$invalid_name = $invalid_encoding || (defined('PHP_MAXPATHLEN') && strlen($filePath) > PHP_MAXPATHLEN) || strlen($trimmed_name) === 0;
|
603 |
+
if ($invalid_encoding) {
|
604 |
+
$filePath = DUP_Encoding::toUTF8($filePath);
|
605 |
+
$fileName = DUP_Encoding::toUTF8($fileName);
|
606 |
+
}
|
607 |
+
|
608 |
+
if ($invalid_name) {
|
609 |
+
$this->FilterInfo->Files->Warning[] = array(
|
610 |
+
'name' => $fileName,
|
611 |
+
'dir' => pathinfo($filePath, PATHINFO_DIRNAME),
|
612 |
+
'path' => $filePath
|
613 |
+
);
|
614 |
+
}
|
615 |
+
|
616 |
+
if ($fileSize > DUPLICATOR_SCAN_WARNFILESIZE) {
|
617 |
+
$this->FilterInfo->Files->Size[] = array(
|
618 |
+
'ubytes' => $fileSize,
|
619 |
+
'bytes' => DUP_Util::byteSize($fileSize, 0),
|
620 |
+
'nodes' => 1,
|
621 |
+
'name' => $fileName,
|
622 |
+
'dir' => pathinfo($filePath, PATHINFO_DIRNAME),
|
623 |
+
'path' => $filePath
|
624 |
+
);
|
625 |
+
}
|
626 |
+
}
|
627 |
+
|
628 |
+
|
629 |
+
$this->Size += $fileSize;
|
630 |
+
$this->listFileObj->addEntry($relativePath, $fileSize, 1);
|
631 |
+
$this->Files[] = $filePath;
|
632 |
+
$this->filesCount++;
|
633 |
+
}
|
634 |
+
|
635 |
+
/**
|
636 |
+
* Builds a tree for both file size warnings and name check warnings
|
637 |
+
* The trees are used to apply filters from the scan screen
|
638 |
+
*
|
639 |
+
* @return null
|
640 |
+
*/
|
641 |
+
private function setTreeFilters()
|
642 |
+
{
|
643 |
+
//-------------------------
|
644 |
+
//SIZE TREE
|
645 |
+
//BUILD: File Size tree
|
646 |
+
$dir_group = DUP_Util::array_group_by($this->FilterInfo->Files->Size, "dir");
|
647 |
+
ksort($dir_group);
|
648 |
+
foreach ($dir_group as $dir => $files) {
|
649 |
+
$sum = 0;
|
650 |
+
foreach ($files as $key => $value) {
|
651 |
+
$sum += $value['ubytes'];
|
652 |
+
}
|
653 |
+
|
654 |
+
//Locate core paths, wp-admin, wp-includes, etc.
|
655 |
+
$iscore = 0;
|
656 |
+
foreach ($this->wpCorePaths as $core_dir) {
|
657 |
+
if (strpos(DUP_Util::safePath($dir), DUP_Util::safePath($core_dir)) !== false) {
|
658 |
+
$iscore = 1;
|
659 |
+
break;
|
660 |
+
}
|
661 |
+
}
|
662 |
+
// Check root and content exact dir
|
663 |
+
if (!$iscore) {
|
664 |
+
if (in_array($dir, $this->wpCoreExactPaths)) {
|
665 |
+
$iscore = 1;
|
666 |
+
}
|
667 |
+
}
|
668 |
+
|
669 |
+
$this->FilterInfo->TreeSize[] = array(
|
670 |
+
'size' => DUP_Util::byteSize($sum, 0),
|
671 |
+
'dir' => $dir,
|
672 |
+
'sdir' => str_replace(duplicator_get_abs_path(), '/', $dir),
|
673 |
+
'iscore' => $iscore,
|
674 |
+
'files' => $files
|
675 |
+
);
|
676 |
+
}
|
677 |
+
|
678 |
+
//-------------------------
|
679 |
+
//NAME TREE
|
680 |
+
//BUILD: Warning tree for file names
|
681 |
+
$dir_group = DUP_Util::array_group_by($this->FilterInfo->Files->Warning, "dir");
|
682 |
+
ksort($dir_group);
|
683 |
+
foreach ($dir_group as $dir => $files) {
|
684 |
+
//Locate core paths, wp-admin, wp-includes, etc.
|
685 |
+
$iscore = 0;
|
686 |
+
foreach ($this->wpCorePaths as $core_dir) {
|
687 |
+
if (strpos($dir, $core_dir) !== false) {
|
688 |
+
$iscore = 1;
|
689 |
+
break;
|
690 |
+
}
|
691 |
+
}
|
692 |
+
// Check root and content exact dir
|
693 |
+
if (!$iscore) {
|
694 |
+
if (in_array($dir, $this->wpCoreExactPaths)) {
|
695 |
+
$iscore = 1;
|
696 |
+
}
|
697 |
+
}
|
698 |
+
|
699 |
+
$this->FilterInfo->TreeWarning[] = array(
|
700 |
+
'dir' => $dir,
|
701 |
+
'sdir' => str_replace(duplicator_get_abs_path(), '/', $dir),
|
702 |
+
'iscore' => $iscore,
|
703 |
+
'count' => count($files),
|
704 |
+
'files' => $files);
|
705 |
+
}
|
706 |
+
|
707 |
+
//BUILD: Warning tree for dir names
|
708 |
+
foreach ($this->FilterInfo->Dirs->Warning as $dir) {
|
709 |
+
$add_dir = true;
|
710 |
+
foreach ($this->FilterInfo->TreeWarning as $key => $value) {
|
711 |
+
if ($value['dir'] == $dir) {
|
712 |
+
$add_dir = false;
|
713 |
+
break;
|
714 |
+
}
|
715 |
+
}
|
716 |
+
if ($add_dir) {
|
717 |
+
//Locate core paths, wp-admin, wp-includes, etc.
|
718 |
+
$iscore = 0;
|
719 |
+
foreach ($this->wpCorePaths as $core_dir) {
|
720 |
+
if (strpos(DUP_Util::safePath($dir), DUP_Util::safePath($core_dir)) !== false) {
|
721 |
+
$iscore = 1;
|
722 |
+
break;
|
723 |
+
}
|
724 |
+
}
|
725 |
+
// Check root and content exact dir
|
726 |
+
if (!$iscore) {
|
727 |
+
if (in_array($dir, $this->wpCoreExactPaths)) {
|
728 |
+
$iscore = 1;
|
729 |
+
}
|
730 |
+
}
|
731 |
+
|
732 |
+
$this->FilterInfo->TreeWarning[] = array(
|
733 |
+
'dir' => $dir,
|
734 |
+
'sdir' => str_replace(duplicator_get_abs_path(), '/', $dir),
|
735 |
+
'iscore' => $iscore,
|
736 |
+
'count' => 0);
|
737 |
+
}
|
738 |
+
}
|
739 |
+
|
740 |
+
function _sortDir($a, $b)
|
741 |
+
{
|
742 |
+
return strcmp($a["dir"], $b["dir"]);
|
743 |
+
}
|
744 |
+
usort($this->FilterInfo->TreeWarning, "_sortDir");
|
745 |
+
}
|
746 |
+
|
747 |
+
public function getWPConfigFilePath()
|
748 |
+
{
|
749 |
+
$wpconfig_filepath = '';
|
750 |
+
$abs_path = duplicator_get_abs_path();
|
751 |
+
if (file_exists($abs_path . '/wp-config.php')) {
|
752 |
+
$wpconfig_filepath = $abs_path . '/wp-config.php';
|
753 |
+
} elseif (@file_exists(dirname($abs_path) . '/wp-config.php') && !@file_exists(dirname($abs_path) . '/wp-settings.php')) {
|
754 |
+
$wpconfig_filepath = dirname($abs_path) . '/wp-config.php';
|
755 |
+
}
|
756 |
+
return $wpconfig_filepath;
|
757 |
+
}
|
758 |
+
|
759 |
+
/**
|
760 |
+
* get the main target root path to make archive
|
761 |
+
*
|
762 |
+
* @staticvar type $targerRoorPath
|
763 |
+
* @return string
|
764 |
+
*/
|
765 |
+
public static function getTargetRootPath()
|
766 |
+
{
|
767 |
+
static $targetRoorPath = null;
|
768 |
+
if (is_null($targetRoorPath)) {
|
769 |
+
$paths = self::getArchiveListPaths();
|
770 |
+
unset($paths['wpconfig']);
|
771 |
+
$targetRoorPath = SnapIO::getCommonPath($paths);
|
772 |
+
}
|
773 |
+
return $targetRoorPath;
|
774 |
+
}
|
775 |
+
|
776 |
+
/**
|
777 |
+
* @param null|string $urlKey if set will only return the url identified by that key
|
778 |
+
* @return array|string|bool
|
779 |
+
*/
|
780 |
+
public static function getOriginalUrls($urlKey = null)
|
781 |
+
{
|
782 |
+
static $origUrls = null;
|
783 |
+
if (is_null($origUrls)) {
|
784 |
+
$restoreMultisite = false;
|
785 |
+
if (is_multisite() && SnapWP::getMainSiteId() !== get_current_blog_id()) {
|
786 |
+
$restoreMultisite = true;
|
787 |
+
restore_current_blog();
|
788 |
+
switch_to_blog(SnapWP::getMainSiteId());
|
789 |
+
}
|
790 |
+
|
791 |
+
$updDirs = wp_upload_dir(null, false, true);
|
792 |
+
if (($wpConfigDir = SnapWP::getWPConfigPath()) !== false) {
|
793 |
+
$wpConfigDir = dirname($wpConfigDir);
|
794 |
+
}
|
795 |
+
|
796 |
+
$homeUrl = home_url();
|
797 |
+
$homeParse = SnapURL::parseUrl(home_url());
|
798 |
+
$absParse = SnapURL::parseUrl(site_url());
|
799 |
+
if ($homeParse['host'] === $absParse['host'] && SnapIO::isChildPath($homeParse['path'], $absParse['path'], false, false)) {
|
800 |
+
$homeParse['path'] = $absParse['path'];
|
801 |
+
$homeUrl = SnapURL::buildUrl($homeParse);
|
802 |
+
}
|
803 |
+
|
804 |
+
$origUrls = array(
|
805 |
+
'home' => $homeUrl,
|
806 |
+
'abs' => site_url(),
|
807 |
+
'wpcontent' => content_url(),
|
808 |
+
'uploads' => $updDirs['baseurl'],
|
809 |
+
'plugins' => plugins_url(),
|
810 |
+
'muplugins' => WPMU_PLUGIN_URL,
|
811 |
+
'themes' => get_theme_root_uri()
|
812 |
+
);
|
813 |
+
if ($restoreMultisite) {
|
814 |
+
restore_current_blog();
|
815 |
+
}
|
816 |
+
}
|
817 |
+
|
818 |
+
if ($urlKey === null) {
|
819 |
+
return $origUrls;
|
820 |
+
}
|
821 |
+
|
822 |
+
if (isset($origUrls[$urlKey])) {
|
823 |
+
return $origUrls[$urlKey];
|
824 |
+
} else {
|
825 |
+
return false;
|
826 |
+
}
|
827 |
+
}
|
828 |
+
|
829 |
+
/**
|
830 |
+
* return all paths to scan
|
831 |
+
*
|
832 |
+
* @return string[]
|
833 |
+
*/
|
834 |
+
public static function getScanPaths()
|
835 |
+
{
|
836 |
+
static $scanPaths = null;
|
837 |
+
if (is_null($scanPaths)) {
|
838 |
+
$paths = self::getArchiveListPaths();
|
839 |
+
// The folder that contains wp-config must not be scanned in full but only added
|
840 |
+
unset($paths['wpconfig']);
|
841 |
+
$scanPaths = array(
|
842 |
+
$paths['home']
|
843 |
+
);
|
844 |
+
unset($paths['home']);
|
845 |
+
foreach ($paths as $path) {
|
846 |
+
$addPath = true;
|
847 |
+
foreach ($scanPaths as $resPath) {
|
848 |
+
if (SnapIO::getRelativePath($path, $resPath) !== false) {
|
849 |
+
$addPath = false;
|
850 |
+
break;
|
851 |
+
}
|
852 |
+
}
|
853 |
+
if ($addPath) {
|
854 |
+
$scanPaths[] = $path;
|
855 |
+
}
|
856 |
+
}
|
857 |
+
$scanPaths = array_values(array_unique($scanPaths));
|
858 |
+
}
|
859 |
+
return $scanPaths;
|
860 |
+
}
|
861 |
+
|
862 |
+
/**
|
863 |
+
* return the wordpress original dir paths
|
864 |
+
*
|
865 |
+
* @staticvar string[] $origPaths if is null retur the array of paths or the single key path
|
866 |
+
* @param string|null $pathKey
|
867 |
+
*
|
868 |
+
* @return string[]|string|bool return false if key doesn\'t exist
|
869 |
+
*/
|
870 |
+
public static function getOriginalPaths($pathKey = null)
|
871 |
+
{
|
872 |
+
static $origPaths = null;
|
873 |
+
if (is_null($origPaths)) {
|
874 |
+
$restoreMultisite = false;
|
875 |
+
if (is_multisite() && SnapWP::getMainSiteId() !== get_current_blog_id()) {
|
876 |
+
$restoreMultisite = true;
|
877 |
+
restore_current_blog();
|
878 |
+
switch_to_blog(SnapWP::getMainSiteId());
|
879 |
+
}
|
880 |
+
|
881 |
+
$updDirs = wp_upload_dir(null, false, true);
|
882 |
+
// fix for old network installation
|
883 |
+
$baseDir = preg_replace('/^(.+\/blogs\.dir)\/\d+\/files$/', '$1', $updDirs['basedir']);
|
884 |
+
if (($wpConfigDir = SnapWP::getWPConfigPath()) !== false) {
|
885 |
+
$wpConfigDir = dirname($wpConfigDir);
|
886 |
+
}
|
887 |
+
$origPaths = array(
|
888 |
+
'home' => SnapWP::getHomePath(),
|
889 |
+
'abs' => ABSPATH,
|
890 |
+
'wpconfig' => $wpConfigDir,
|
891 |
+
'wpcontent' => WP_CONTENT_DIR,
|
892 |
+
'uploads' => $baseDir,
|
893 |
+
'plugins' => WP_PLUGIN_DIR,
|
894 |
+
'muplugins' => WPMU_PLUGIN_DIR,
|
895 |
+
'themes' => get_theme_root()
|
896 |
+
);
|
897 |
+
if ($restoreMultisite) {
|
898 |
+
restore_current_blog();
|
899 |
+
}
|
900 |
+
}
|
901 |
+
|
902 |
+
if (!empty($pathKey)) {
|
903 |
+
if (array_key_exists($pathKey, $origPaths)) {
|
904 |
+
return $origPaths[$pathKey];
|
905 |
+
} else {
|
906 |
+
return false;
|
907 |
+
}
|
908 |
+
} else {
|
909 |
+
return $origPaths;
|
910 |
+
}
|
911 |
+
}
|
912 |
+
|
913 |
+
/**
|
914 |
+
* return the wordpress original dir paths
|
915 |
+
*
|
916 |
+
* @staticvar string[] $paths if is null retur the array of paths or the single key path
|
917 |
+
* @param string|null $pathKey
|
918 |
+
*
|
919 |
+
* @return string[]|string|bool return false if key doesn\'t exist
|
920 |
+
*/
|
921 |
+
public static function getArchiveListPaths($pathKey = null)
|
922 |
+
{
|
923 |
+
static $archivePaths = null;
|
924 |
+
if (is_null($archivePaths)) {
|
925 |
+
$archivePaths = array();
|
926 |
+
$originalPaths = self::getOriginalPaths();
|
927 |
+
|
928 |
+
$archivePaths = array(
|
929 |
+
'home' => SnapIO::safePathUntrailingslashit($originalPaths['home'], true)
|
930 |
+
);
|
931 |
+
unset($originalPaths['home']);
|
932 |
+
|
933 |
+
foreach ($originalPaths as $key => $originalPath) {
|
934 |
+
$path = SnapIO::safePathUntrailingslashit($originalPath, false);
|
935 |
+
$realPath = SnapIO::safePathUntrailingslashit($originalPath, true);
|
936 |
+
|
937 |
+
if ($path == $realPath) {
|
938 |
+
$archivePaths[$key] = $path;
|
939 |
+
} elseif (
|
940 |
+
!SnapIO::isChildPath($realPath, $archivePaths['home']) &&
|
941 |
+
SnapIO::isChildPath($path, $archivePaths['home'])
|
942 |
+
) {
|
943 |
+
$archivePaths[$key] = $path;
|
944 |
+
} else {
|
945 |
+
$archivePaths[$key] = $realPath;
|
946 |
+
}
|
947 |
+
}
|
948 |
+
}
|
949 |
+
|
950 |
+
if (!empty($pathKey)) {
|
951 |
+
if (array_key_exists($pathKey, $archivePaths)) {
|
952 |
+
return $archivePaths[$pathKey];
|
953 |
+
} else {
|
954 |
+
return false;
|
955 |
+
}
|
956 |
+
} else {
|
957 |
+
return $archivePaths;
|
958 |
+
}
|
959 |
+
}
|
960 |
+
|
961 |
+
/**
|
962 |
+
* return true if path is child of duplicator backup path
|
963 |
+
*
|
964 |
+
* @param string $path
|
965 |
+
* @return boolean
|
966 |
+
*/
|
967 |
+
public static function isBackupPathChild($path)
|
968 |
+
{
|
969 |
+
return (preg_match('/[\/]' . preg_quote(DUP_Settings::getSsdirTmpPath(), '/') . '[\/][^\/]+$/', $path) === 1);
|
970 |
+
}
|
971 |
+
|
972 |
+
/**
|
973 |
+
*
|
974 |
+
* @param string $path
|
975 |
+
*
|
976 |
+
* @return bool return true if path is a path of current wordpress installation
|
977 |
+
*/
|
978 |
+
public static function isCurrentWordpressInstallPath($path)
|
979 |
+
{
|
980 |
+
static $currentWpPaths = null;
|
981 |
+
|
982 |
+
if (is_null($currentWpPaths)) {
|
983 |
+
$currentWpPaths = array_merge(self::getOriginalPaths(), self::getArchiveListPaths());
|
984 |
+
$currentWpPaths = array_map('trailingslashit', $currentWpPaths);
|
985 |
+
$currentWpPaths = array_values(array_unique($currentWpPaths));
|
986 |
+
}
|
987 |
+
return in_array(trailingslashit($path), $currentWpPaths);
|
988 |
+
}
|
989 |
+
|
990 |
+
public function wpContentDirNormalizePath()
|
991 |
+
{
|
992 |
+
if (!isset($this->wpContentDirNormalizePath)) {
|
993 |
+
$this->wpContentDirNormalizePath = trailingslashit(wp_normalize_path(WP_CONTENT_DIR));
|
994 |
+
}
|
995 |
+
return $this->wpContentDirNormalizePath;
|
996 |
+
}
|
997 |
+
|
998 |
+
public function getUrl()
|
999 |
+
{
|
1000 |
+
return DUP_Settings::getSsdirUrl() . "/" . $this->File;
|
1001 |
+
}
|
1002 |
+
|
1003 |
+
public function getLocalDirPath($dir, $basePath = '')
|
1004 |
+
{
|
1005 |
+
$safeDir = SnapIO::safePathUntrailingslashit($dir);
|
1006 |
+
return ltrim($basePath . preg_replace('/^' . preg_quote($this->PackDir, '/') . '(.*)/m', '$1', $safeDir), '/');
|
1007 |
+
}
|
1008 |
+
|
1009 |
+
public function getLocalFilePath($file, $basePath = '')
|
1010 |
+
{
|
1011 |
+
$safeFile = SnapIO::safePathUntrailingslashit($file);
|
1012 |
+
return ltrim($basePath . preg_replace('/^' . preg_quote($this->PackDir, '/') . '(.*)/m', '$1', $safeFile), '/');
|
1013 |
+
}
|
1014 |
+
}
|
classes/package/class.pack.archive.zip.php
CHANGED
@@ -1,9 +1,14 @@
|
|
1 |
<?php
|
|
|
|
|
|
|
2 |
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
3 |
// Exit if accessed directly
|
4 |
-
if (! defined('DUPLICATOR_VERSION'))
|
|
|
|
|
5 |
|
6 |
-
require_once
|
7 |
|
8 |
/**
|
9 |
* Creates a zip file using the built in PHP ZipArchive class
|
@@ -32,55 +37,55 @@ class DUP_Zip extends DUP_Archive
|
|
32 |
$package_zip_flush = DUP_Settings::Get('package_zip_flush');
|
33 |
|
34 |
self::$compressDir = rtrim(wp_normalize_path(DUP_Util::safePath($archive->PackDir)), '/');
|
35 |
-
self::$sqlPath = DUP_Settings::getSsdirTmpPath()."/{$archive->Package->Database->File}";
|
36 |
-
self::$zipPath = DUP_Settings::getSsdirTmpPath()."/{$archive->File}";
|
37 |
self::$zipArchive = new ZipArchive();
|
38 |
self::$networkFlush = empty($package_zip_flush) ? false : $package_zip_flush;
|
39 |
|
40 |
-
$filterDirs
|
41 |
-
$filterExts
|
42 |
-
|
43 |
-
$filterOn
|
44 |
$filterDirsFormat = rtrim(str_replace(';', "\n\t", $filterDirs));
|
45 |
-
|
46 |
-
$lastDirSuccess
|
47 |
|
48 |
//LOAD SCAN REPORT
|
49 |
-
$json = file_get_contents(DUP_Settings::getSsdirTmpPath()."/{$archive->Package->NameHash}_scan.json");
|
50 |
self::$scanReport = json_decode($json);
|
51 |
|
52 |
DUP_Log::Info("\n********************************************************************************");
|
53 |
DUP_Log::Info("ARCHIVE (ZIP):");
|
54 |
DUP_Log::Info("********************************************************************************");
|
55 |
-
$isZipOpen = (self::$zipArchive->open(self::$zipPath, ZIPARCHIVE::CREATE) ===
|
56 |
if (!$isZipOpen) {
|
57 |
$error_message = "Cannot open zip file with PHP ZipArchive.";
|
58 |
$buildProgress->set_failed($error_message);
|
59 |
-
DUP_Log::error($error_message, "Path location [".self::$zipPath."]", Dup_ErrorBehavior::LogOnly);
|
60 |
$archive->Package->setStatus(DUP_PackageStatus::ERROR);
|
61 |
return;
|
62 |
}
|
63 |
-
DUP_Log::Info("ARCHIVE DIR: ".self::$compressDir);
|
64 |
-
DUP_Log::Info("ARCHIVE FILE: ".basename(self::$zipPath));
|
65 |
DUP_Log::Info("FILTERS: *{$filterOn}*");
|
66 |
DUP_Log::Info("DIRS:\n\t{$filterDirsFormat}");
|
67 |
-
|
68 |
DUP_Log::Info("EXTS: {$filterExts}");
|
69 |
|
70 |
DUP_Log::Info("----------------------------------------");
|
71 |
DUP_Log::Info("COMPRESSING");
|
72 |
-
DUP_Log::Info("SIZE:\t".self::$scanReport->ARC->Size);
|
73 |
-
DUP_Log::Info("STATS:\tDirs ".self::$scanReport->ARC->DirCount." | Files ".self::$scanReport->ARC->FileCount);
|
74 |
|
75 |
//ADD SQL
|
76 |
$sql_ark_file_path = $archive->Package->getSqlArkFilePath();
|
77 |
-
$isSQLInZip
|
78 |
|
79 |
if ($isSQLInZip) {
|
80 |
-
DUP_Log::Info("SQL ADDED: ".basename(self::$sqlPath));
|
81 |
} else {
|
82 |
$error_message = "Unable to add database.sql to archive.";
|
83 |
-
DUP_Log::error($error_message, "SQL File Path [".self::$sqlPath."]", Dup_ErrorBehavior::LogOnly);
|
84 |
$buildProgress->set_failed($error_message);
|
85 |
$archive->Package->setStatus(DUP_PackageStatus::ERROR);
|
86 |
return;
|
@@ -100,7 +105,7 @@ class DUP_Zip extends DUP_Archive
|
|
100 |
//Don't warn when dirtory is the root path
|
101 |
if (strcmp($dir, rtrim(self::$compressDir, '/')) != 0) {
|
102 |
$dir_path = strlen($dir) ? "[{$dir}]" : "[Read Error] - last successful read was: [{$lastDirSuccess}]";
|
103 |
-
$info
|
104 |
}
|
105 |
}
|
106 |
}
|
@@ -114,20 +119,25 @@ class DUP_Zip extends DUP_Archive
|
|
114 |
/**
|
115 |
* count update for integrity check
|
116 |
*/
|
117 |
-
$sumItems
|
118 |
|
119 |
/* ZIP FILES: Network Flush
|
120 |
* This allows the process to not timeout on fcgi
|
121 |
* setups that need a response every X seconds */
|
122 |
$totalFileCount = count(self::$scanReport->ARC->Files);
|
123 |
-
$info
|
124 |
if (self::$networkFlush) {
|
125 |
foreach (self::$scanReport->ARC->Files as $file) {
|
126 |
-
$file_size
|
127 |
$localFileName = $archive->getLocalFilePath($file);
|
128 |
|
129 |
if (is_readable($file)) {
|
130 |
-
if (
|
|
|
|
|
|
|
|
|
|
|
131 |
Dup_Log::Info("Adding {$file} to zip");
|
132 |
self::$limitItems++;
|
133 |
self::$countFiles++;
|
@@ -150,21 +160,25 @@ class DUP_Zip extends DUP_Archive
|
|
150 |
DUP_Log::Info("Items archived [{$sumItems}] flushing response.");
|
151 |
}
|
152 |
|
153 |
-
if(self::$countFiles % 500 == 0) {
|
154 |
// Every so many files update the status so the UI can display
|
155 |
-
$archive->Package->Status =
|
156 |
$archive->Package->update();
|
157 |
}
|
158 |
}
|
159 |
-
}
|
160 |
-
|
161 |
-
else {
|
162 |
foreach (self::$scanReport->ARC->Files as $file) {
|
163 |
-
$file_size
|
164 |
$localFileName = $archive->getLocalFilePath($file);
|
165 |
|
166 |
if (is_readable($file)) {
|
167 |
-
if (
|
|
|
|
|
|
|
|
|
|
|
168 |
self::$countFiles++;
|
169 |
} elseif (self::$zipArchive->addFile($file, $localFileName)) {
|
170 |
self::$countFiles++;
|
@@ -175,9 +189,9 @@ class DUP_Zip extends DUP_Archive
|
|
175 |
$info .= "FILE: [{$file}]\n";
|
176 |
}
|
177 |
|
178 |
-
if(self::$countFiles % 500 == 0) {
|
179 |
// Every so many files update the status so the UI can display
|
180 |
-
$archive->Package->Status =
|
181 |
$archive->Package->update();
|
182 |
}
|
183 |
}
|
@@ -196,22 +210,24 @@ class DUP_Zip extends DUP_Archive
|
|
196 |
* count update for integrity check
|
197 |
*/
|
198 |
$archive->file_count = self::$countDirs + self::$countFiles;
|
199 |
-
DUP_Log::Info("FILE ADDED TO ZIP: "
|
200 |
|
201 |
|
202 |
//--------------------------------
|
203 |
//LOG FINAL RESULTS
|
204 |
DUP_Util::fcgiFlush();
|
205 |
$zipCloseResult = self::$zipArchive->close();
|
206 |
-
if($zipCloseResult) {
|
207 |
DUP_Log::Info("COMPRESSION RESULT: '{$zipCloseResult}'");
|
208 |
} else {
|
209 |
$error_message = "ZipArchive close failure.";
|
210 |
-
DUP_Log::error(
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
|
|
|
|
215 |
$buildProgress->set_failed($error_message);
|
216 |
$archive->Package->setStatus(DUP_PackageStatus::ERROR);
|
217 |
return;
|
@@ -221,12 +237,9 @@ class DUP_Zip extends DUP_Archive
|
|
221 |
$timerAllSum = DUP_Util::elapsedTime($timerAllEnd, $timerAllStart);
|
222 |
|
223 |
self::$zipFileSize = @filesize(self::$zipPath);
|
224 |
-
DUP_Log::Info("COMPRESSED SIZE: ".DUP_Util::byteSize(self::$zipFileSize));
|
225 |
DUP_Log::Info("ARCHIVE RUNTIME: {$timerAllSum}");
|
226 |
-
DUP_Log::Info("MEMORY STACK: ".DUP_Server::getPHPMemory());
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
} catch (Exception $e) {
|
231 |
$error_message = "Runtime error in class.pack.archive.zip.php constructor.";
|
232 |
DUP_Log::error($error_message, "Exception: {$e}", Dup_ErrorBehavior::LogOnly);
|
@@ -235,4 +248,4 @@ class DUP_Zip extends DUP_Archive
|
|
235 |
return;
|
236 |
}
|
237 |
}
|
238 |
-
}
|
1 |
<?php
|
2 |
+
|
3 |
+
use Duplicator\Libs\Snap\SnapUtil;
|
4 |
+
|
5 |
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
6 |
// Exit if accessed directly
|
7 |
+
if (! defined('DUPLICATOR_VERSION')) {
|
8 |
+
exit;
|
9 |
+
}
|
10 |
|
11 |
+
require_once(DUPLICATOR_PLUGIN_PATH . 'classes/package/class.pack.archive.php');
|
12 |
|
13 |
/**
|
14 |
* Creates a zip file using the built in PHP ZipArchive class
|
37 |
$package_zip_flush = DUP_Settings::Get('package_zip_flush');
|
38 |
|
39 |
self::$compressDir = rtrim(wp_normalize_path(DUP_Util::safePath($archive->PackDir)), '/');
|
40 |
+
self::$sqlPath = DUP_Settings::getSsdirTmpPath() . "/{$archive->Package->Database->File}";
|
41 |
+
self::$zipPath = DUP_Settings::getSsdirTmpPath() . "/{$archive->File}";
|
42 |
self::$zipArchive = new ZipArchive();
|
43 |
self::$networkFlush = empty($package_zip_flush) ? false : $package_zip_flush;
|
44 |
|
45 |
+
$filterDirs = empty($archive->FilterDirs) ? 'not set' : $archive->FilterDirs;
|
46 |
+
$filterExts = empty($archive->FilterExts) ? 'not set' : $archive->FilterExts;
|
47 |
+
$filterFiles = empty($archive->FilterFiles) ? 'not set' : $archive->FilterFiles;
|
48 |
+
$filterOn = ($archive->FilterOn) ? 'ON' : 'OFF';
|
49 |
$filterDirsFormat = rtrim(str_replace(';', "\n\t", $filterDirs));
|
50 |
+
$filterFilesFormat = rtrim(str_replace(';', "\n\t", $filterFiles));
|
51 |
+
$lastDirSuccess = self::$compressDir;
|
52 |
|
53 |
//LOAD SCAN REPORT
|
54 |
+
$json = file_get_contents(DUP_Settings::getSsdirTmpPath() . "/{$archive->Package->NameHash}_scan.json");
|
55 |
self::$scanReport = json_decode($json);
|
56 |
|
57 |
DUP_Log::Info("\n********************************************************************************");
|
58 |
DUP_Log::Info("ARCHIVE (ZIP):");
|
59 |
DUP_Log::Info("********************************************************************************");
|
60 |
+
$isZipOpen = (self::$zipArchive->open(self::$zipPath, ZIPARCHIVE::CREATE) === true);
|
61 |
if (!$isZipOpen) {
|
62 |
$error_message = "Cannot open zip file with PHP ZipArchive.";
|
63 |
$buildProgress->set_failed($error_message);
|
64 |
+
DUP_Log::error($error_message, "Path location [" . self::$zipPath . "]", Dup_ErrorBehavior::LogOnly);
|
65 |
$archive->Package->setStatus(DUP_PackageStatus::ERROR);
|
66 |
return;
|
67 |
}
|
68 |
+
DUP_Log::Info("ARCHIVE DIR: " . self::$compressDir);
|
69 |
+
DUP_Log::Info("ARCHIVE FILE: " . basename(self::$zipPath));
|
70 |
DUP_Log::Info("FILTERS: *{$filterOn}*");
|
71 |
DUP_Log::Info("DIRS:\n\t{$filterDirsFormat}");
|
72 |
+
DUP_Log::Info("FILES:\n\t{$filterFilesFormat}");
|
73 |
DUP_Log::Info("EXTS: {$filterExts}");
|
74 |
|
75 |
DUP_Log::Info("----------------------------------------");
|
76 |
DUP_Log::Info("COMPRESSING");
|
77 |
+
DUP_Log::Info("SIZE:\t" . self::$scanReport->ARC->Size);
|
78 |
+
DUP_Log::Info("STATS:\tDirs " . self::$scanReport->ARC->DirCount . " | Files " . self::$scanReport->ARC->FileCount);
|
79 |
|
80 |
//ADD SQL
|
81 |
$sql_ark_file_path = $archive->Package->getSqlArkFilePath();
|
82 |
+
$isSQLInZip = self::$zipArchive->addFile(self::$sqlPath, $sql_ark_file_path);
|
83 |
|
84 |
if ($isSQLInZip) {
|
85 |
+
DUP_Log::Info("SQL ADDED: " . basename(self::$sqlPath));
|
86 |
} else {
|
87 |
$error_message = "Unable to add database.sql to archive.";
|
88 |
+
DUP_Log::error($error_message, "SQL File Path [" . self::$sqlPath . "]", Dup_ErrorBehavior::LogOnly);
|
89 |
$buildProgress->set_failed($error_message);
|
90 |
$archive->Package->setStatus(DUP_PackageStatus::ERROR);
|
91 |
return;
|
105 |
//Don't warn when dirtory is the root path
|
106 |
if (strcmp($dir, rtrim(self::$compressDir, '/')) != 0) {
|
107 |
$dir_path = strlen($dir) ? "[{$dir}]" : "[Read Error] - last successful read was: [{$lastDirSuccess}]";
|
108 |
+
$info .= "DIR: {$dir_path}\n";
|
109 |
}
|
110 |
}
|
111 |
}
|
119 |
/**
|
120 |
* count update for integrity check
|
121 |
*/
|
122 |
+
$sumItems = (self::$countDirs + self::$countFiles);
|
123 |
|
124 |
/* ZIP FILES: Network Flush
|
125 |
* This allows the process to not timeout on fcgi
|
126 |
* setups that need a response every X seconds */
|
127 |
$totalFileCount = count(self::$scanReport->ARC->Files);
|
128 |
+
$info = '';
|
129 |
if (self::$networkFlush) {
|
130 |
foreach (self::$scanReport->ARC->Files as $file) {
|
131 |
+
$file_size = @filesize($file);
|
132 |
$localFileName = $archive->getLocalFilePath($file);
|
133 |
|
134 |
if (is_readable($file)) {
|
135 |
+
if (
|
136 |
+
defined('DUPLICATOR_ZIP_ARCHIVE_ADD_FROM_STR') &&
|
137 |
+
DUPLICATOR_ZIP_ARCHIVE_ADD_FROM_STR &&
|
138 |
+
$file_size < DUP_Constants::ZIP_STRING_LIMIT &&
|
139 |
+
self::$zipArchive->addFromString($localFileName, file_get_contents($file))
|
140 |
+
) {
|
141 |
Dup_Log::Info("Adding {$file} to zip");
|
142 |
self::$limitItems++;
|
143 |
self::$countFiles++;
|
160 |
DUP_Log::Info("Items archived [{$sumItems}] flushing response.");
|
161 |
}
|
162 |
|
163 |
+
if (self::$countFiles % 500 == 0) {
|
164 |
// Every so many files update the status so the UI can display
|
165 |
+
$archive->Package->Status = SnapUtil::getWorkPercent(DUP_PackageStatus::ARCSTART, DUP_PackageStatus::ARCVALIDATION, $totalFileCount, self::$countFiles);
|
166 |
$archive->Package->update();
|
167 |
}
|
168 |
}
|
169 |
+
} else {
|
170 |
+
//Normal
|
|
|
171 |
foreach (self::$scanReport->ARC->Files as $file) {
|
172 |
+
$file_size = @filesize($file);
|
173 |
$localFileName = $archive->getLocalFilePath($file);
|
174 |
|
175 |
if (is_readable($file)) {
|
176 |
+
if (
|
177 |
+
defined('DUPLICATOR_ZIP_ARCHIVE_ADD_FROM_STR') &&
|
178 |
+
DUPLICATOR_ZIP_ARCHIVE_ADD_FROM_STR &&
|
179 |
+
$file_size < DUP_Constants::ZIP_STRING_LIMIT &&
|
180 |
+
self::$zipArchive->addFromString($localFileName, file_get_contents($file))
|
181 |
+
) {
|
182 |
self::$countFiles++;
|
183 |
} elseif (self::$zipArchive->addFile($file, $localFileName)) {
|
184 |
self::$countFiles++;
|
189 |
$info .= "FILE: [{$file}]\n";
|
190 |
}
|
191 |
|
192 |
+
if (self::$countFiles % 500 == 0) {
|
193 |
// Every so many files update the status so the UI can display
|
194 |
+
$archive->Package->Status = SnapUtil::getWorkPercent(DUP_PackageStatus::ARCSTART, DUP_PackageStatus::ARCVALIDATION, $totalFileCount, self::$countFiles);
|
195 |
$archive->Package->update();
|
196 |
}
|
197 |
}
|
210 |
* count update for integrity check
|
211 |
*/
|
212 |
$archive->file_count = self::$countDirs + self::$countFiles;
|
213 |
+
DUP_Log::Info("FILE ADDED TO ZIP: " . $archive->file_count);
|
214 |
|
215 |
|
216 |
//--------------------------------
|
217 |
//LOG FINAL RESULTS
|
218 |
DUP_Util::fcgiFlush();
|
219 |
$zipCloseResult = self::$zipArchive->close();
|
220 |
+
if ($zipCloseResult) {
|
221 |
DUP_Log::Info("COMPRESSION RESULT: '{$zipCloseResult}'");
|
222 |
} else {
|
223 |
$error_message = "ZipArchive close failure.";
|
224 |
+
DUP_Log::error(
|
225 |
+
$error_message,
|
226 |
+
"The ZipArchive engine is having issues zipping up the files on this server. For more details visit the FAQ\n"
|
227 |
+
. "I'm getting a ZipArchive close failure when building. How can I resolve this?\n"
|
228 |
+
. "[https://snapcreek.com/duplicator/docs/faqs-tech/#faq-package-165-q]",
|
229 |
+
Dup_ErrorBehavior::LogOnly
|
230 |
+
);
|
231 |
$buildProgress->set_failed($error_message);
|
232 |
$archive->Package->setStatus(DUP_PackageStatus::ERROR);
|
233 |
return;
|
237 |
$timerAllSum = DUP_Util::elapsedTime($timerAllEnd, $timerAllStart);
|
238 |
|
239 |
self::$zipFileSize = @filesize(self::$zipPath);
|
240 |
+
DUP_Log::Info("COMPRESSED SIZE: " . DUP_Util::byteSize(self::$zipFileSize));
|
241 |
DUP_Log::Info("ARCHIVE RUNTIME: {$timerAllSum}");
|
242 |
+
DUP_Log::Info("MEMORY STACK: " . DUP_Server::getPHPMemory());
|
|
|
|
|
|
|
243 |
} catch (Exception $e) {
|
244 |
$error_message = "Runtime error in class.pack.archive.zip.php constructor.";
|
245 |
DUP_Log::error($error_message, "Exception: {$e}", Dup_ErrorBehavior::LogOnly);
|
248 |
return;
|
249 |
}
|
250 |
}
|
251 |
+
}
|
classes/package/class.pack.database.php
CHANGED
@@ -1,779 +1,787 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
/**
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
/**
|
43 |
-
* The
|
44 |
-
*/
|
45 |
-
public $
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
/**
|
53 |
-
|
54 |
-
|
55 |
-
public $
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
*
|
64 |
-
*
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
*
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
*
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
/**
|
79 |
-
* The full text version number of the database server
|
80 |
-
* @exmaple: 10.2 mariadb.org binary distribution
|
81 |
-
*/
|
82 |
-
public $versionComment;
|
83 |
-
|
84 |
-
/**
|
85 |
-
*
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
public function
|
113 |
-
{
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
);
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
public $
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
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 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
$
|
198 |
-
$
|
199 |
-
$
|
200 |
-
$
|
201 |
-
|
202 |
-
$
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
DUP_Log::Info(
|
216 |
-
$
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
}
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
$
|
262 |
-
$
|
263 |
-
|
264 |
-
$
|
265 |
-
$
|
266 |
-
$info
|
267 |
-
$info['Status']['
|
268 |
-
|
269 |
-
$info['Status']['
|
270 |
-
|
271 |
-
$info['
|
272 |
-
$info['
|
273 |
-
$info['
|
274 |
-
$info['
|
275 |
-
$
|
276 |
-
$
|
277 |
-
$
|
278 |
-
|
279 |
-
|
280 |
-
$
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
$
|
296 |
-
$
|
297 |
-
$info['
|
298 |
-
$info['
|
299 |
-
$
|
300 |
-
$
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
}
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
$
|
336 |
-
$info
|
337 |
-
$info['
|
338 |
-
$info['
|
339 |
-
$info['
|
340 |
-
|
341 |
-
$
|
342 |
-
$
|
343 |
-
$this->info->
|
344 |
-
$
|
345 |
-
$
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
$
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
$
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
$
|
424 |
-
$
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
$
|
434 |
-
$
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
}
|
442 |
-
}
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
-
|
463 |
-
|
464 |
-
|
465 |
-
|
466 |
-
|
467 |
-
|
468 |
-
|
469 |
-
$cmd
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
-
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
}
|
490 |
-
}
|
491 |
-
|
492 |
-
$
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
|
524 |
-
|
525 |
-
|
526 |
-
$
|
527 |
-
|
528 |
-
|
529 |
-
|
530 |
-
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
|
597 |
-
}
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
|
605 |
-
|
606 |
-
|
607 |
-
|
608 |
-
|
609 |
-
|
610 |
-
|
611 |
-
|
612 |
-
|
613 |
-
|
614 |
-
|
615 |
-
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
-
|
620 |
-
|
621 |
-
|
622 |
-
|
623 |
-
|
624 |
-
|
625 |
-
|
626 |
-
|
627 |
-
|
628 |
-
|
629 |
-
|
630 |
-
|
631 |
-
|
632 |
-
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
|
637 |
-
|
638 |
-
|
639 |
-
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
-
|
645 |
-
|
646 |
-
|
647 |
-
|
648 |
-
|
649 |
-
$
|
650 |
-
|
651 |
-
@fwrite($handle, "
|
652 |
-
|
653 |
-
|
654 |
-
|
655 |
-
|
656 |
-
|
657 |
-
|
658 |
-
|
659 |
-
@fwrite($handle, "
|
660 |
-
|
661 |
-
|
662 |
-
|
663 |
-
|
664 |
-
|
665 |
-
|
666 |
-
|
667 |
-
|
668 |
-
|
669 |
-
|
670 |
-
|
671 |
-
|
672 |
-
|
673 |
-
|
674 |
-
|
675 |
-
|
676 |
-
|
677 |
-
|
678 |
-
|
679 |
-
|
680 |
-
|
681 |
-
|
682 |
-
|
683 |
-
|
684 |
-
$
|
685 |
-
}
|
686 |
-
|
687 |
-
|
688 |
-
|
689 |
-
|
690 |
-
|
691 |
-
|
692 |
-
|
693 |
-
$
|
694 |
-
|
695 |
-
|
696 |
-
|
697 |
-
$
|
698 |
-
|
699 |
-
|
700 |
-
|
701 |
-
|
702 |
-
|
703 |
-
|
704 |
-
|
705 |
-
|
706 |
-
|
707 |
-
|
708 |
-
|
709 |
-
|
710 |
-
|
711 |
-
|
712 |
-
|
713 |
-
|
714 |
-
|
715 |
-
|
716 |
-
|
717 |
-
|
718 |
-
|
719 |
-
|
720 |
-
|
721 |
-
|
722 |
-
|
723 |
-
$
|
724 |
-
|
725 |
-
|
726 |
-
|
727 |
-
|
728 |
-
|
729 |
-
|
730 |
-
|
731 |
-
|
732 |
-
|
733 |
-
|
734 |
-
|
735 |
-
|
736 |
-
|
737 |
-
|
738 |
-
|
739 |
-
|
740 |
-
|
741 |
-
|
742 |
-
|
743 |
-
|
744 |
-
|
745 |
-
|
746 |
-
|
747 |
-
$
|
748 |
-
|
749 |
-
|
750 |
-
|
751 |
-
|
752 |
-
|
753 |
-
|
754 |
-
|
755 |
-
|
756 |
-
|
757 |
-
|
758 |
-
|
759 |
-
|
760 |
-
|
761 |
-
|
762 |
-
|
763 |
-
|
764 |
-
|
765 |
-
|
766 |
-
|
767 |
-
|
768 |
-
|
769 |
-
|
770 |
-
|
771 |
-
|
772 |
-
|
773 |
-
|
774 |
-
|
775 |
-
|
776 |
-
|
777 |
-
|
778 |
-
|
779 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
use Duplicator\Libs\Snap\SnapDB;
|
4 |
+
use Duplicator\Libs\Snap\SnapIO;
|
5 |
+
use Duplicator\Libs\Snap\SnapURL;
|
6 |
+
use Duplicator\Libs\Snap\SnapUtil;
|
7 |
+
use Duplicator\Libs\Snap\SnapWP;
|
8 |
+
|
9 |
+
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
10 |
+
// Exit if accessed directly
|
11 |
+
if (!defined('DUPLICATOR_VERSION')) {
|
12 |
+
exit;
|
13 |
+
}
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Class for gathering system information about a database
|
17 |
+
*
|
18 |
+
* Standard: PSR-2
|
19 |
+
* @link http://www.php-fig.org/psr/psr-2
|
20 |
+
*
|
21 |
+
*/
|
22 |
+
class DUP_DatabaseInfo
|
23 |
+
{
|
24 |
+
/**
|
25 |
+
* The SQL file was built with mysqldump or PHP
|
26 |
+
*/
|
27 |
+
public $buildMode = 'PHP';
|
28 |
+
/** @var string[] A unique list of all charsets table types used in the database */
|
29 |
+
public $charSetList = array();
|
30 |
+
/** @var string[] A unique list of all the collation table types used in the database */
|
31 |
+
public $collationList = array();
|
32 |
+
/** @var string[] engile list used in database tables */
|
33 |
+
public $engineList = array();
|
34 |
+
/**
|
35 |
+
* Does any filtered table have an upper case character in it
|
36 |
+
*/
|
37 |
+
public $isTablesUpperCase = false;
|
38 |
+
/**
|
39 |
+
* Does the database name have any filtered characters in it
|
40 |
+
*/
|
41 |
+
public $isNameUpperCase = false;
|
42 |
+
/**
|
43 |
+
* The real name of the database
|
44 |
+
*/
|
45 |
+
public $name = '';
|
46 |
+
/** @var int The full count of all tables in the database */
|
47 |
+
public $tablesBaseCount = 0;
|
48 |
+
/** @var int The count of tables after the tables filter has been applied */
|
49 |
+
public $tablesFinalCount = 0;
|
50 |
+
/** @var int multisite tables filtered count */
|
51 |
+
public $muFilteredTableCount = 0;
|
52 |
+
/** @var int The number of rows from all filtered tables in the database */
|
53 |
+
public $tablesRowCount = 0;
|
54 |
+
/** @var int The estimated data size on disk from all filtered tables in the database */
|
55 |
+
public $tablesSizeOnDisk = 0;
|
56 |
+
/** @var array */
|
57 |
+
public $tablesList = array();
|
58 |
+
/**
|
59 |
+
* Gets the server variable lower_case_table_names
|
60 |
+
*
|
61 |
+
* 0 store=lowercase; compare=sensitive (works only on case sensitive file systems )
|
62 |
+
* 1 store=lowercase; compare=insensitive
|
63 |
+
* 2 store=exact; compare=insensitive (works only on case INsensitive file systems )
|
64 |
+
* default is 0/Linux ; 1/Windows
|
65 |
+
*/
|
66 |
+
public $varLowerCaseTables = false;
|
67 |
+
/**
|
68 |
+
* The database engine (MySQL/MariaDB/Percona)
|
69 |
+
* @var string
|
70 |
+
* @example MariaDB
|
71 |
+
*/
|
72 |
+
public $dbEngine = '';
|
73 |
+
/**
|
74 |
+
* The simple numeric version number of the database server
|
75 |
+
* @exmaple: 5.5
|
76 |
+
*/
|
77 |
+
public $version = 0;
|
78 |
+
/**
|
79 |
+
* The full text version number of the database server
|
80 |
+
* @exmaple: 10.2 mariadb.org binary distribution
|
81 |
+
*/
|
82 |
+
public $versionComment = 0;
|
83 |
+
|
84 |
+
/**
|
85 |
+
* @var int Number of VIEWs in the database
|
86 |
+
*/
|
87 |
+
public $viewCount = 0;
|
88 |
+
|
89 |
+
/**
|
90 |
+
* @var int Number of PROCEDUREs in the database
|
91 |
+
*/
|
92 |
+
public $procCount = 0;
|
93 |
+
|
94 |
+
/**
|
95 |
+
* @var int Number of PROCEDUREs in the database
|
96 |
+
*/
|
97 |
+
public $funcCount = 0;
|
98 |
+
|
99 |
+
/**
|
100 |
+
* @var array List of triggers included in the database
|
101 |
+
*/
|
102 |
+
public $triggerList = array();
|
103 |
+
/**
|
104 |
+
* Integer field file structure of table, table name as key
|
105 |
+
*/
|
106 |
+
private $intFieldsStruct = array();
|
107 |
+
/**
|
108 |
+
* $currentIndex => processedSchemaSize
|
109 |
+
*/
|
110 |
+
private $indexProcessedSchemaSize = array();
|
111 |
+
//CONSTRUCTOR
|
112 |
+
public function __construct()
|
113 |
+
{
|
114 |
+
}
|
115 |
+
|
116 |
+
public function addTriggers()
|
117 |
+
{
|
118 |
+
global $wpdb;
|
119 |
+
if (!is_array($triggers = $wpdb->get_results("SHOW TRIGGERS", ARRAY_A))) {
|
120 |
+
return;
|
121 |
+
}
|
122 |
+
|
123 |
+
foreach ($triggers as $trigger) {
|
124 |
+
$name = $trigger["Trigger"];
|
125 |
+
$create = $wpdb->get_row("SHOW CREATE TRIGGER `{$name}`", ARRAY_N);
|
126 |
+
$this->triggerList[$name] = array(
|
127 |
+
"create" => "DELIMITER ;;\n" . $create[2] . ";;\nDELIMITER ;"
|
128 |
+
);
|
129 |
+
}
|
130 |
+
}
|
131 |
+
|
132 |
+
/**
|
133 |
+
*
|
134 |
+
* @param stirng $name // table name
|
135 |
+
* @param int $inaccurateRows // This data is intended as a preliminary count and therefore not necessarily accurate
|
136 |
+
* @param int $size // This data is intended as a preliminary count and therefore not necessarily accurate
|
137 |
+
* @param int|bool $insertedRows // This value, if other than false, is the exact line value inserted into the dump file
|
138 |
+
*/
|
139 |
+
public function addTableInList($name, $inaccurateRows, $size, $insertedRows = false)
|
140 |
+
{
|
141 |
+
$this->tablesList[$name] = array(
|
142 |
+
'inaccurateRows' => (int) $inaccurateRows,
|
143 |
+
'insertedRows' => (int) $insertedRows,
|
144 |
+
'size' => (int) $size
|
145 |
+
);
|
146 |
+
}
|
147 |
+
}
|
148 |
+
|
149 |
+
class DUP_Database
|
150 |
+
{
|
151 |
+
const TABLE_CREATION_END_MARKER = "/***** TABLE CREATION END *****/\n";
|
152 |
+
|
153 |
+
//PUBLIC
|
154 |
+
public $Type = 'MySQL';
|
155 |
+
public $Size;
|
156 |
+
public $File;
|
157 |
+
public $Path;
|
158 |
+
public $FilterTables;
|
159 |
+
public $FilterOn;
|
160 |
+
public $Name;
|
161 |
+
public $Compatible;
|
162 |
+
public $Comments;
|
163 |
+
/**
|
164 |
+
*
|
165 |
+
* @var DUP_DatabaseInfo
|
166 |
+
*/
|
167 |
+
public $info = null;
|
168 |
+
//PROTECTED
|
169 |
+
protected $Package;
|
170 |
+
//PRIVATE
|
171 |
+
private $tempDbPath;
|
172 |
+
private $EOFMarker;
|
173 |
+
private $networkFlush;
|
174 |
+
/**
|
175 |
+
* Init this object
|
176 |
+
*/
|
177 |
+
public function __construct($package)
|
178 |
+
{
|
179 |
+
$this->Package = $package;
|
180 |
+
$this->EOFMarker = "";
|
181 |
+
$package_zip_flush = DUP_Settings::Get('package_zip_flush');
|
182 |
+
$this->networkFlush = empty($package_zip_flush) ? false : $package_zip_flush;
|
183 |
+
$this->info = new DUP_DatabaseInfo();
|
184 |
+
$this->info->varLowerCaseTables = DUP_Util::isWindows() ? 1 : 0;
|
185 |
+
}
|
186 |
+
|
187 |
+
/**
|
188 |
+
* Build the database script
|
189 |
+
*
|
190 |
+
* @param DUP_Package $package A reference to the package that this database object belongs in
|
191 |
+
*
|
192 |
+
* @return null
|
193 |
+
*/
|
194 |
+
public function build($package, $errorBehavior = Dup_ErrorBehavior::ThrowException)
|
195 |
+
{
|
196 |
+
try {
|
197 |
+
$this->Package = $package;
|
198 |
+
do_action('duplicator_lite_build_database_before_start', $package);
|
199 |
+
$time_start = DUP_Util::getMicrotime();
|
200 |
+
$this->Package->setStatus(DUP_PackageStatus::DBSTART);
|
201 |
+
$this->tempDbPath = DUP_Settings::getSsdirTmpPath() . "/{$this->File}";
|
202 |
+
$package_mysqldump = DUP_Settings::Get('package_mysqldump');
|
203 |
+
$package_phpdump_qrylimit = DUP_Settings::Get('package_phpdump_qrylimit');
|
204 |
+
$mysqlDumpPath = DUP_DB::getMySqlDumpPath();
|
205 |
+
$mode = DUP_DB::getBuildMode();
|
206 |
+
$reserved_db_filepath = duplicator_get_abs_path() . '/database.sql';
|
207 |
+
$log = "\n********************************************************************************\n";
|
208 |
+
$log .= "DATABASE:\n";
|
209 |
+
$log .= "********************************************************************************\n";
|
210 |
+
$log .= "BUILD MODE: {$mode}";
|
211 |
+
$log .= ($mode == 'PHP') ? "(query limit - {$package_phpdump_qrylimit})\n" : "\n";
|
212 |
+
$log .= "MYSQLTIMEOUT: " . DUPLICATOR_DB_MAX_TIME . "\n";
|
213 |
+
$log .= "MYSQLDUMP: ";
|
214 |
+
$log .= ($mysqlDumpPath) ? "Is Supported" : "Not Supported";
|
215 |
+
DUP_Log::Info($log);
|
216 |
+
$log = null;
|
217 |
+
do_action('duplicator_lite_build_database_start', $package);
|
218 |
+
switch ($mode) {
|
219 |
+
case 'MYSQLDUMP':
|
220 |
+
$this->mysqlDump($mysqlDumpPath);
|
221 |
+
break;
|
222 |
+
case 'PHP':
|
223 |
+
$this->phpDump($package);
|
224 |
+
break;
|
225 |
+
}
|
226 |
+
|
227 |
+
DUP_Log::Info("SQL CREATED: {$this->File}");
|
228 |
+
$time_end = DUP_Util::getMicrotime();
|
229 |
+
$time_sum = DUP_Util::elapsedTime($time_end, $time_start);
|
230 |
+
//File below 10k considered incomplete
|
231 |
+
$sql_file_size = is_file($this->tempDbPath) ? @filesize($this->tempDbPath) : 0;
|
232 |
+
DUP_Log::Info("SQL FILE SIZE: " . DUP_Util::byteSize($sql_file_size) . " ({$sql_file_size})");
|
233 |
+
if ($sql_file_size < 1350) {
|
234 |
+
$error_message = "SQL file size too low.";
|
235 |
+
$package->BuildProgress->set_failed($error_message);
|
236 |
+
$package->Status = DUP_PackageStatus::ERROR;
|
237 |
+
$package->Update();
|
238 |
+
DUP_Log::error($error_message, "File does not look complete. Check permission on file and parent directory at [{$this->tempDbPath}]", $errorBehavior);
|
239 |
+
do_action('duplicator_lite_build_database_fail', $package);
|
240 |
+
} else {
|
241 |
+
do_action('duplicator_lite_build_database_completed', $package);
|
242 |
+
}
|
243 |
+
|
244 |
+
DUP_Log::Info("SQL FILE TIME: " . date("Y-m-d H:i:s"));
|
245 |
+
DUP_Log::Info("SQL RUNTIME: {$time_sum}");
|
246 |
+
$this->Size = is_file($this->tempDbPath) ? @filesize($this->tempDbPath) : 0;
|
247 |
+
$this->Package->setStatus(DUP_PackageStatus::DBDONE);
|
248 |
+
} catch (Exception $e) {
|
249 |
+
do_action('duplicator_lite_build_database_fail', $package);
|
250 |
+
DUP_Log::error("Runtime error in DUP_Database::Build. " . $e->getMessage(), "Exception: {$e}", $errorBehavior);
|
251 |
+
}
|
252 |
+
}
|
253 |
+
|
254 |
+
/**
|
255 |
+
* Get the database meta-data such as tables as all there details
|
256 |
+
*
|
257 |
+
* @return array Returns an array full of meta-data about the database
|
258 |
+
*/
|
259 |
+
public function getScannerData()
|
260 |
+
{
|
261 |
+
global $wpdb;
|
262 |
+
$filterTables = isset($this->FilterTables) ? explode(',', $this->FilterTables) : array();
|
263 |
+
$tblBaseCount = 0;
|
264 |
+
$tblCount = 0;
|
265 |
+
$tables = $this->getBaseTables();
|
266 |
+
$info = array();
|
267 |
+
$info['Status']['Success'] = is_null($tables) ? false : true;
|
268 |
+
//DB_Case for the database name is never checked on
|
269 |
+
$info['Status']['DB_Case'] = 'Good';
|
270 |
+
$info['Status']['DB_Rows'] = 'Good';
|
271 |
+
$info['Status']['DB_Size'] = 'Good';
|
272 |
+
$info['Status']['TBL_Case'] = 'Good';
|
273 |
+
$info['Status']['TBL_Rows'] = 'Good';
|
274 |
+
$info['Status']['TBL_Size'] = 'Good';
|
275 |
+
$info['Size'] = 0;
|
276 |
+
$info['Rows'] = 0;
|
277 |
+
$info['TableCount'] = 0;
|
278 |
+
$info['TableList'] = array();
|
279 |
+
$tblCaseFound = 0;
|
280 |
+
$tblRowsFound = 0;
|
281 |
+
$tblSizeFound = 0;
|
282 |
+
//Grab Table Stats
|
283 |
+
$filteredTables = array();
|
284 |
+
$this->info->tablesList = array();
|
285 |
+
|
286 |
+
foreach ($tables as $table) {
|
287 |
+
$tblBaseCount++;
|
288 |
+
$name = $table["name"];
|
289 |
+
if ($this->FilterOn && is_array($filterTables)) {
|
290 |
+
if (in_array($name, $filterTables)) {
|
291 |
+
continue;
|
292 |
+
}
|
293 |
+
}
|
294 |
+
|
295 |
+
$size = $table['size'];
|
296 |
+
$rows = empty($table["rows"]) ? '0' : $table["rows"];
|
297 |
+
$info['Size'] += $size;
|
298 |
+
$info['Rows'] += $rows;
|
299 |
+
$info['TableList'][$name]['Case'] = preg_match('/[A-Z]/', $name) ? 1 : 0;
|
300 |
+
$info['TableList'][$name]['Rows'] = number_format($rows);
|
301 |
+
$info['TableList'][$name]['Size'] = DUP_Util::byteSize($size);
|
302 |
+
$info['TableList'][$name]['USize'] = $size;
|
303 |
+
$filteredTables[] = $name;
|
304 |
+
|
305 |
+
if (($qRes = $GLOBALS['wpdb']->get_var("SELECT Count(*) FROM `{$name}`")) === null) {
|
306 |
+
$qRes = $rows;
|
307 |
+
}
|
308 |
+
|
309 |
+
$row_count = (int) $qRes;
|
310 |
+
$this->info->addTableInList($name, $rows, $size, $row_count);
|
311 |
+
$tblCount++;
|
312 |
+
|
313 |
+
// Table Uppercase
|
314 |
+
if ($info['TableList'][$name]['Case']) {
|
315 |
+
if (!$tblCaseFound) {
|
316 |
+
$tblCaseFound = 1;
|
317 |
+
}
|
318 |
+
}
|
319 |
+
|
320 |
+
//Table Row Count
|
321 |
+
if ($rows > DUPLICATOR_SCAN_DB_TBL_ROWS) {
|
322 |
+
if (!$tblRowsFound) {
|
323 |
+
$tblRowsFound = 1;
|
324 |
+
}
|
325 |
+
}
|
326 |
+
|
327 |
+
//Table Size
|
328 |
+
if ($size > DUPLICATOR_SCAN_DB_TBL_SIZE) {
|
329 |
+
if (!$tblSizeFound) {
|
330 |
+
$tblSizeFound = 1;
|
331 |
+
}
|
332 |
+
}
|
333 |
+
}
|
334 |
+
|
335 |
+
$this->setInfoObj($filteredTables);
|
336 |
+
$this->info->addTriggers();
|
337 |
+
$info['Status']['DB_Case'] = preg_match('/[A-Z]/', $wpdb->dbname) ? 'Warn' : 'Good';
|
338 |
+
$info['Status']['DB_Rows'] = ($info['Rows'] > DUPLICATOR_SCAN_DB_ALL_ROWS) ? 'Warn' : 'Good';
|
339 |
+
$info['Status']['DB_Size'] = ($info['Size'] > DUPLICATOR_SCAN_DB_ALL_SIZE) ? 'Warn' : 'Good';
|
340 |
+
$info['Status']['TBL_Case'] = ($tblCaseFound) ? 'Warn' : 'Good';
|
341 |
+
$info['Status']['TBL_Rows'] = ($tblRowsFound) ? 'Warn' : 'Good';
|
342 |
+
$info['Status']['TBL_Size'] = ($tblSizeFound) ? 'Warn' : 'Good';
|
343 |
+
$info['Status']['Triggers'] = count($this->info->triggerList) > 0 ? 'Warn' : 'Good';
|
344 |
+
$info['RawSize'] = $info['Size'];
|
345 |
+
$info['TableList'] = $info['TableList'] or "unknown";
|
346 |
+
$info['TableCount'] = $tblCount;
|
347 |
+
$this->info->isTablesUpperCase = $tblCaseFound;
|
348 |
+
$this->info->tablesBaseCount = $tblBaseCount;
|
349 |
+
$this->info->tablesFinalCount = $tblCount;
|
350 |
+
$this->info->tablesRowCount = (int) $info['Rows'];
|
351 |
+
$this->info->tablesSizeOnDisk = (int) $info['Size'];
|
352 |
+
$this->info->dbEngine = SnapDB::getDBEngine($wpdb->dbh);
|
353 |
+
$info['EasySize'] = DUP_Util::byteSize($info['Size']) or "unknown";
|
354 |
+
|
355 |
+
$this->info->viewCount = count($wpdb->get_results("SHOW FULL TABLES WHERE Table_Type = 'VIEW'", ARRAY_A));
|
356 |
+
$this->info->procCount = count($wpdb->get_results("SHOW PROCEDURE STATUS WHERE `Db`='" . DB_NAME . "'", ARRAY_A));
|
357 |
+
$this->info->funcCount = count($wpdb->get_results("SHOW FUNCTION STATUS WHERE `Db`='" . DB_NAME . "'", ARRAY_A));
|
358 |
+
|
359 |
+
return $info;
|
360 |
+
}
|
361 |
+
|
362 |
+
/**
|
363 |
+
* @param array &$filteredTables Filtered names of tables to include in collation search.
|
364 |
+
* Parameter does not change in the function, is passed by reference only to avoid copying.
|
365 |
+
*
|
366 |
+
* @return void
|
367 |
+
*/
|
368 |
+
public function setInfoObj($filteredTables)
|
369 |
+
{
|
370 |
+
global $wpdb;
|
371 |
+
$this->info->buildMode = DUP_DB::getBuildMode();
|
372 |
+
$this->info->version = DUP_DB::getVersion();
|
373 |
+
$this->info->versionComment = DUP_DB::getVariable('version_comment');
|
374 |
+
$this->info->varLowerCaseTables = DUP_DB::getVariable('lower_case_table_names');
|
375 |
+
$this->info->name = $wpdb->dbname;
|
376 |
+
$this->info->isNameUpperCase = preg_match('/[A-Z]/', $wpdb->dbname) ? 1 : 0;
|
377 |
+
$this->info->charSetList = DUP_DB::getTableCharSetList($filteredTables);
|
378 |
+
$this->info->collationList = DUP_DB::getTableCollationList($filteredTables);
|
379 |
+
$this->info->engineList = DUP_DB::getTableEngineList($filteredTables);
|
380 |
+
}
|
381 |
+
|
382 |
+
/**
|
383 |
+
* Return list of base tables to dump
|
384 |
+
*
|
385 |
+
* @return array
|
386 |
+
*/
|
387 |
+
protected function getBaseTables($nameOnly = false)
|
388 |
+
{
|
389 |
+
/** @var \wpdb $wpdb */
|
390 |
+
global $wpdb;
|
391 |
+
|
392 |
+
// (TABLE_NAME REGEXP '^rte4ed_(2|6)_' OR TABLE_NAME NOT REGEXP '^rte4ed_[0-9]+_')
|
393 |
+
$query = 'SELECT `TABLE_NAME` as `name`, `TABLE_ROWS` as `rows`, DATA_LENGTH + INDEX_LENGTH as `size` FROM `information_schema`.`tables`';
|
394 |
+
|
395 |
+
$where = array(
|
396 |
+
'TABLE_SCHEMA = "' . esc_sql($wpdb->dbname) . '"',
|
397 |
+
'TABLE_TYPE != "VIEW"'
|
398 |
+
);
|
399 |
+
|
400 |
+
$query .= ' WHERE ' . implode(' AND ', $where);
|
401 |
+
$query .= ' ORDER BY TABLE_NAME';
|
402 |
+
|
403 |
+
if ($nameOnly) {
|
404 |
+
return $wpdb->get_col($query, 0);
|
405 |
+
} else {
|
406 |
+
return $wpdb->get_results($query, ARRAY_A);
|
407 |
+
}
|
408 |
+
}
|
409 |
+
|
410 |
+
/**
|
411 |
+
* Build the database script using mysqldump
|
412 |
+
*
|
413 |
+
* @return bool Returns true if the sql script was successfully created
|
414 |
+
*/
|
415 |
+
private function mysqlDump($exePath)
|
416 |
+
{
|
417 |
+
global $wpdb;
|
418 |
+
require_once(DUPLICATOR_PLUGIN_PATH . 'classes/utilities/class.u.shell.php');
|
419 |
+
$host = SnapURL::parseUrl(DB_HOST, PHP_URL_HOST);
|
420 |
+
if (($port = SnapURL::parseUrl(DB_HOST, PHP_URL_PORT)) == false) {
|
421 |
+
$port = '';
|
422 |
+
}
|
423 |
+
$name = DB_NAME;
|
424 |
+
$mysqlcompat_on = isset($this->Compatible) && strlen($this->Compatible);
|
425 |
+
//Build command
|
426 |
+
$cmd = escapeshellarg($exePath);
|
427 |
+
$cmd .= ' --no-create-db';
|
428 |
+
$cmd .= ' --single-transaction';
|
429 |
+
$cmd .= ' --hex-blob';
|
430 |
+
$cmd .= ' --skip-add-drop-table';
|
431 |
+
$cmd .= ' --routines';
|
432 |
+
$cmd .= ' --quote-names';
|
433 |
+
$cmd .= ' --skip-comments';
|
434 |
+
$cmd .= ' --skip-set-charset';
|
435 |
+
$cmd .= ' --skip-triggers';
|
436 |
+
$cmd .= ' --allow-keywords';
|
437 |
+
$cmd .= ' --no-tablespaces';
|
438 |
+
//Compatibility mode
|
439 |
+
if ($mysqlcompat_on) {
|
440 |
+
DUP_Log::Info("COMPATIBLE: [{$this->Compatible}]");
|
441 |
+
$cmd .= " --compatible={$this->Compatible}";
|
442 |
+
}
|
443 |
+
|
444 |
+
//Filter tables
|
445 |
+
$res = $wpdb->get_results('SHOW FULL TABLES', ARRAY_N);
|
446 |
+
$tables = array();
|
447 |
+
$baseTables = array();
|
448 |
+
foreach ($res as $row) {
|
449 |
+
if (DUP_Util::isTableExists($row[0])) {
|
450 |
+
$tables[] = $row[0];
|
451 |
+
if ('BASE TABLE' == $row[1]) {
|
452 |
+
$baseTables[] = $row[0];
|
453 |
+
}
|
454 |
+
}
|
455 |
+
}
|
456 |
+
$filterTables = isset($this->FilterTables) ? explode(',', $this->FilterTables) : null;
|
457 |
+
$tblAllCount = count($tables);
|
458 |
+
|
459 |
+
//$tblFilterOn = ($this->FilterOn) ? 'ON' : 'OFF';
|
460 |
+
if (is_array($filterTables) && $this->FilterOn) {
|
461 |
+
foreach ($tables as $key => $val) {
|
462 |
+
if (in_array($tables[$key], $filterTables)) {
|
463 |
+
$cmd .= " --ignore-table={$name}.{$tables[$key]} ";
|
464 |
+
unset($tables[$key]);
|
465 |
+
}
|
466 |
+
}
|
467 |
+
}
|
468 |
+
|
469 |
+
$cmd .= ' -u ' . escapeshellarg(DB_USER);
|
470 |
+
$cmd .= (DB_PASSWORD) ?
|
471 |
+
' -p' . DUP_Shell_U::escapeshellargWindowsSupport(DB_PASSWORD) : '';
|
472 |
+
$cmd .= ' -h ' . escapeshellarg($host);
|
473 |
+
$cmd .= (!empty($port) && is_numeric($port) ) ?
|
474 |
+
' -P ' . $port : '';
|
475 |
+
$isPopenEnabled = DUP_Shell_U::isPopenEnabled();
|
476 |
+
if (!$isPopenEnabled) {
|
477 |
+
$cmd .= ' -r ' . escapeshellarg($this->tempDbPath);
|
478 |
+
}
|
479 |
+
|
480 |
+
$cmd .= ' ' . escapeshellarg(DB_NAME);
|
481 |
+
$cmd .= ' 2>&1';
|
482 |
+
if ($isPopenEnabled) {
|
483 |
+
$needToRewrite = false;
|
484 |
+
foreach ($tables as $tableName) {
|
485 |
+
$rewriteTableAs = $this->rewriteTableNameAs($tableName);
|
486 |
+
if ($tableName != $rewriteTableAs) {
|
487 |
+
$needToRewrite = true;
|
488 |
+
break;
|
489 |
+
}
|
490 |
+
}
|
491 |
+
|
492 |
+
if ($needToRewrite) {
|
493 |
+
$findReplaceTableNames = array();
|
494 |
+
// orignal table name => rewrite table name
|
495 |
+
|
496 |
+
foreach ($tables as $tableName) {
|
497 |
+
$rewriteTableAs = $this->rewriteTableNameAs($tableName);
|
498 |
+
if ($tableName != $rewriteTableAs) {
|
499 |
+
$findReplaceTableNames[$tableName] = $rewriteTableAs;
|
500 |
+
}
|
501 |
+
}
|
502 |
+
}
|
503 |
+
|
504 |
+
$firstLine = '';
|
505 |
+
DUP_LOG::trace("Executing mysql dump command by popen: $cmd");
|
506 |
+
$handle = popen($cmd, "r");
|
507 |
+
if ($handle) {
|
508 |
+
$sql_header = "/* DUPLICATOR-LITE (MYSQL-DUMP BUILD MODE) MYSQL SCRIPT CREATED ON : " . @date("Y-m-d H:i:s") . " */\n\n";
|
509 |
+
file_put_contents($this->tempDbPath, $sql_header, FILE_APPEND);
|
510 |
+
while (!feof($handle)) {
|
511 |
+
$line = fgets($handle);
|
512 |
+
//get ony one line
|
513 |
+
if ($line) {
|
514 |
+
if (empty($firstLine)) {
|
515 |
+
$firstLine = $line;
|
516 |
+
if (false !== stripos($line, 'Using a password on the command line interface can be insecure')) {
|
517 |
+
continue;
|
518 |
+
}
|
519 |
+
}
|
520 |
+
|
521 |
+
if ($needToRewrite) {
|
522 |
+
$replaceCount = 1;
|
523 |
+
if (preg_match('/CREATE TABLE `(.*?)`/', $line, $matches)) {
|
524 |
+
$tableName = $matches[1];
|
525 |
+
if (isset($findReplaceTableNames[$tableName])) {
|
526 |
+
$rewriteTableAs = $findReplaceTableNames[$tableName];
|
527 |
+
$line = str_replace('CREATE TABLE `' . $tableName . '`', 'CREATE TABLE `' . $rewriteTableAs . '`', $line, $replaceCount);
|
528 |
+
}
|
529 |
+
} elseif (preg_match('/INSERT INTO `(.*?)`/', $line, $matches)) {
|
530 |
+
$tableName = $matches[1];
|
531 |
+
if (isset($findReplaceTableNames[$tableName])) {
|
532 |
+
$rewriteTableAs = $findReplaceTableNames[$tableName];
|
533 |
+
$line = str_replace('INSERT INTO `' . $tableName . '`', 'INSERT INTO `' . $rewriteTableAs . '`', $line, $replaceCount);
|
534 |
+
}
|
535 |
+
} elseif (preg_match('/LOCK TABLES `(.*?)`/', $line, $matches)) {
|
536 |
+
$tableName = $matches[1];
|
537 |
+
if (isset($findReplaceTableNames[$tableName])) {
|
538 |
+
$rewriteTableAs = $findReplaceTableNames[$tableName];
|
539 |
+
$line = str_replace('LOCK TABLES `' . $tableName . '`', 'LOCK TABLES `' . $rewriteTableAs . '`', $line, $replaceCount);
|
540 |
+
}
|
541 |
+
}
|
542 |
+
}
|
543 |
+
|
544 |
+
file_put_contents($this->tempDbPath, $line, FILE_APPEND);
|
545 |
+
$output = "Ran from {$exePath}";
|
546 |
+
}
|
547 |
+
}
|
548 |
+
$mysqlResult = pclose($handle);
|
549 |
+
} else {
|
550 |
+
$output = '';
|
551 |
+
}
|
552 |
+
|
553 |
+
// Password bug > 5.6 (@see http://bugs.mysql.com/bug.php?id=66546)
|
554 |
+
if (empty($output) && trim($firstLine) === 'Warning: Using a password on the command line interface can be insecure.') {
|
555 |
+
$output = '';
|
556 |
+
}
|
557 |
+
} else {
|
558 |
+
DUP_LOG::trace("Executing mysql dump command $cmd");
|
559 |
+
exec($cmd, $output, $mysqlResult);
|
560 |
+
$output = implode("\n", $output);
|
561 |
+
// Password bug > 5.6 (@see http://bugs.mysql.com/bug.php?id=66546)
|
562 |
+
if (trim($output) === 'Warning: Using a password on the command line interface can be insecure.') {
|
563 |
+
$output = '';
|
564 |
+
}
|
565 |
+
$output = (strlen($output)) ? $output : "Ran from {$exePath}";
|
566 |
+
$tblCreateCount = count($tables);
|
567 |
+
$tblFilterCount = $tblAllCount - $tblCreateCount;
|
568 |
+
//DEBUG
|
569 |
+
//DUP_Log::Info("COMMAND: {$cmd}");
|
570 |
+
DUP_Log::Info("FILTERED: [{$this->FilterTables}]");
|
571 |
+
DUP_Log::Info("RESPONSE: {$output}");
|
572 |
+
DUP_Log::Info("TABLES: total:{$tblAllCount} | filtered:{$tblFilterCount} | create:{$tblCreateCount}");
|
573 |
+
}
|
574 |
+
|
575 |
+
$sql_footer = "\n\n/* Duplicator WordPress Timestamp: " . date("Y-m-d H:i:s") . "*/\n";
|
576 |
+
$sql_footer .= "/* " . DUPLICATOR_DB_EOF_MARKER . " */\n";
|
577 |
+
file_put_contents($this->tempDbPath, $sql_footer, FILE_APPEND);
|
578 |
+
if ($mysqlResult !== 0) {
|
579 |
+
/**
|
580 |
+
* -1 error command shell
|
581 |
+
* mysqldump return
|
582 |
+
* 0 - Success
|
583 |
+
* 1 - Warning
|
584 |
+
* 2 - Exception
|
585 |
+
*/
|
586 |
+
DUP_Log::Info('MYSQL DUMP ERROR ' . print_r($mysqlResult, true));
|
587 |
+
DUP_Log::error(
|
588 |
+
__('Shell mysql dump error. Change SQL Mode to the "PHP Code" in the Duplicator > Settings > Packages.', 'duplicator'),
|
589 |
+
implode("\n", SnapIO::getLastLinesOfFile(
|
590 |
+
$this->tempDbPath,
|
591 |
+
DUPLICATOR_DB_MYSQLDUMP_ERROR_CONTAINING_LINE_COUNT,
|
592 |
+
DUPLICATOR_DB_MYSQLDUMP_ERROR_CHARS_IN_LINE_COUNT
|
593 |
+
)),
|
594 |
+
Dup_ErrorBehavior::ThrowException
|
595 |
+
);
|
596 |
+
return false;
|
597 |
+
}
|
598 |
+
|
599 |
+
return true;
|
600 |
+
}
|
601 |
+
|
602 |
+
/**
|
603 |
+
* Build the database script using php
|
604 |
+
*
|
605 |
+
* @return bool Returns true if the sql script was successfully created
|
606 |
+
*/
|
607 |
+
private function phpDump($package)
|
608 |
+
{
|
609 |
+
global $wpdb;
|
610 |
+
$wpdb->query("SET session wait_timeout = " . DUPLICATOR_DB_MAX_TIME);
|
611 |
+
if (($handle = fopen($this->tempDbPath, 'w+')) == false) {
|
612 |
+
DUP_Log::error('[PHP DUMP] ERROR Can\'t open sbStorePath "' . $this->tempDbPath . '"', Dup_ErrorBehavior::ThrowException);
|
613 |
+
}
|
614 |
+
$tables = $wpdb->get_col("SHOW FULL TABLES WHERE Table_Type != 'VIEW'");
|
615 |
+
$filterTables = isset($this->FilterTables) ? explode(',', $this->FilterTables) : null;
|
616 |
+
$tblAllCount = count($tables);
|
617 |
+
//$tblFilterOn = ($this->FilterOn) ? 'ON' : 'OFF';
|
618 |
+
$qryLimit = DUP_Settings::Get('package_phpdump_qrylimit');
|
619 |
+
if (is_array($filterTables) && $this->FilterOn) {
|
620 |
+
foreach ($tables as $key => $val) {
|
621 |
+
if (in_array($tables[$key], $filterTables)) {
|
622 |
+
unset($tables[$key]);
|
623 |
+
}
|
624 |
+
}
|
625 |
+
}
|
626 |
+
$tblCreateCount = count($tables);
|
627 |
+
$tblFilterCount = $tblAllCount - $tblCreateCount;
|
628 |
+
DUP_Log::Info("TABLES: total:{$tblAllCount} | filtered:{$tblFilterCount} | create:{$tblCreateCount}");
|
629 |
+
DUP_Log::Info("FILTERED: [{$this->FilterTables}]");
|
630 |
+
//Added 'NO_AUTO_VALUE_ON_ZERO' at plugin version 1.2.12 to fix :
|
631 |
+
//**ERROR** database error write 'Invalid default value for for older mysql versions
|
632 |
+
$sql_header = "/* DUPLICATOR-LITE (PHP BUILD MODE) MYSQL SCRIPT CREATED ON : " . @date("Y-m-d H:i:s") . " */\n\n";
|
633 |
+
$sql_header .= "/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;\n\n";
|
634 |
+
$sql_header .= "SET FOREIGN_KEY_CHECKS = 0;\n\n";
|
635 |
+
fwrite($handle, $sql_header);
|
636 |
+
//BUILD CREATES:
|
637 |
+
//All creates must be created before inserts do to foreign key constraints
|
638 |
+
foreach ($tables as $table) {
|
639 |
+
$rewrite_table_as = $this->rewriteTableNameAs($table);
|
640 |
+
$create = $wpdb->get_row("SHOW CREATE TABLE `{$table}`", ARRAY_N);
|
641 |
+
$count = 1;
|
642 |
+
$create_table_query = str_replace($table, $rewrite_table_as, $create[1], $count);
|
643 |
+
@fwrite($handle, "{$create_table_query};\n\n");
|
644 |
+
}
|
645 |
+
|
646 |
+
$procedures = $wpdb->get_col("SHOW PROCEDURE STATUS WHERE `Db` = '{$wpdb->dbname}'", 1);
|
647 |
+
if (count($procedures)) {
|
648 |
+
foreach ($procedures as $procedure) {
|
649 |
+
@fwrite($handle, "DELIMITER ;;\n");
|
650 |
+
$create = $wpdb->get_row("SHOW CREATE PROCEDURE `{$procedure}`", ARRAY_N);
|
651 |
+
@fwrite($handle, "{$create[2]} ;;\n");
|
652 |
+
@fwrite($handle, "DELIMITER ;\n\n");
|
653 |
+
}
|
654 |
+
}
|
655 |
+
|
656 |
+
$functions = $wpdb->get_col("SHOW FUNCTION STATUS WHERE `Db` = '{$wpdb->dbname}'", 1);
|
657 |
+
if (count($functions)) {
|
658 |
+
foreach ($functions as $function) {
|
659 |
+
@fwrite($handle, "DELIMITER ;;\n");
|
660 |
+
$create = $wpdb->get_row("SHOW CREATE FUNCTION `{$function}`", ARRAY_N);
|
661 |
+
@fwrite($handle, "{$create[2]} ;;\n");
|
662 |
+
@fwrite($handle, "DELIMITER ;\n\n");
|
663 |
+
}
|
664 |
+
}
|
665 |
+
|
666 |
+
$views = $wpdb->get_col("SHOW FULL TABLES WHERE Table_Type = 'VIEW'");
|
667 |
+
if (count($views)) {
|
668 |
+
foreach ($views as $view) {
|
669 |
+
$create = $wpdb->get_row("SHOW CREATE VIEW `{$view}`", ARRAY_N);
|
670 |
+
@fwrite($handle, "{$create[1]};\n\n");
|
671 |
+
}
|
672 |
+
}
|
673 |
+
|
674 |
+
@fwrite($handle, self::TABLE_CREATION_END_MARKER . "\n");
|
675 |
+
|
676 |
+
$table_count = count($tables);
|
677 |
+
$table_number = 0;
|
678 |
+
//BUILD INSERTS:
|
679 |
+
//Create Insert in 100 row increments to better handle memory
|
680 |
+
foreach ($tables as $table) {
|
681 |
+
$table_number++;
|
682 |
+
if ($table_number % 2 == 0) {
|
683 |
+
$this->Package->Status = SnapUtil::getWorkPercent(DUP_PackageStatus::DBSTART, DUP_PackageStatus::DBDONE, $table_count, $table_number);
|
684 |
+
$this->Package->update();
|
685 |
+
}
|
686 |
+
|
687 |
+
$row_count = $wpdb->get_var("SELECT Count(*) FROM `{$table}`");
|
688 |
+
$rewrite_table_as = $this->rewriteTableNameAs($table);
|
689 |
+
|
690 |
+
if ($row_count > $qryLimit) {
|
691 |
+
$row_count = ceil($row_count / $qryLimit);
|
692 |
+
} elseif ($row_count > 0) {
|
693 |
+
$row_count = 1;
|
694 |
+
}
|
695 |
+
|
696 |
+
if ($row_count >= 1) {
|
697 |
+
fwrite($handle, "\n/* INSERT TABLE DATA: {$table} */\n");
|
698 |
+
}
|
699 |
+
|
700 |
+
for ($i = 0; $i < $row_count; $i++) {
|
701 |
+
$sql = "";
|
702 |
+
$limit = $i * $qryLimit;
|
703 |
+
$query = "SELECT * FROM `{$table}` LIMIT {$limit}, {$qryLimit}";
|
704 |
+
$rows = $wpdb->get_results($query, ARRAY_A);
|
705 |
+
$select_last_error = $wpdb->last_error;
|
706 |
+
if ('' !== $select_last_error) {
|
707 |
+
$fix = esc_html__('Please contact your DataBase administrator to fix the error.', 'duplicator');
|
708 |
+
$errorMessage = $select_last_error . ' ' . $fix . '.';
|
709 |
+
$package->BuildProgress->set_failed($errorMessage);
|
710 |
+
$package->BuildProgress->failed = true;
|
711 |
+
$package->failed = true;
|
712 |
+
$package->Status = DUP_PackageStatus::ERROR;
|
713 |
+
$package->Update();
|
714 |
+
DUP_Log::error($select_last_error, $fix, Dup_ErrorBehavior::ThrowException);
|
715 |
+
return;
|
716 |
+
}
|
717 |
+
|
718 |
+
if (is_array($rows)) {
|
719 |
+
foreach ($rows as $row) {
|
720 |
+
$sql .= "INSERT INTO `{$rewrite_table_as}` VALUES(";
|
721 |
+
$num_values = count($row);
|
722 |
+
$num_counter = 1;
|
723 |
+
foreach ($row as $value) {
|
724 |
+
if (is_null($value) || !isset($value)) {
|
725 |
+
($num_values == $num_counter) ? $sql .= 'NULL' : $sql .= 'NULL, ';
|
726 |
+
} else {
|
727 |
+
($num_values == $num_counter) ? $sql .= '"' . DUP_DB::escSQL($value, true) . '"' : $sql .= '"' . DUP_DB::escSQL($value, true) . '", ';
|
728 |
+
}
|
729 |
+
$num_counter++;
|
730 |
+
}
|
731 |
+
$sql .= ");\n";
|
732 |
+
}
|
733 |
+
fwrite($handle, $sql);
|
734 |
+
}
|
735 |
+
}
|
736 |
+
|
737 |
+
//Flush buffer if enabled
|
738 |
+
if ($this->networkFlush) {
|
739 |
+
DUP_Util::fcgiFlush();
|
740 |
+
}
|
741 |
+
$sql = null;
|
742 |
+
$rows = null;
|
743 |
+
}
|
744 |
+
|
745 |
+
$sql_footer = "\nSET FOREIGN_KEY_CHECKS = 1; \n\n";
|
746 |
+
$sql_footer .= "/* Duplicator WordPress Timestamp: " . date("Y-m-d H:i:s") . "*/\n";
|
747 |
+
$sql_footer .= "/* " . DUPLICATOR_DB_EOF_MARKER . " */\n";
|
748 |
+
fwrite($handle, $sql_footer);
|
749 |
+
$wpdb->flush();
|
750 |
+
fclose($handle);
|
751 |
+
}
|
752 |
+
|
753 |
+
private function rewriteTableNameAs($table)
|
754 |
+
{
|
755 |
+
$table_prefix = $this->getTablePrefix();
|
756 |
+
if (!isset($this->sameNameTableExists)) {
|
757 |
+
global $wpdb;
|
758 |
+
$this->sameNameTableExists = false;
|
759 |
+
$all_tables = $wpdb->get_col("SHOW FULL TABLES WHERE Table_Type != 'VIEW'");
|
760 |
+
foreach ($all_tables as $table_name) {
|
761 |
+
if (strtolower($table_name) != $table_name && in_array(strtolower($table_name), $all_tables)) {
|
762 |
+
$this->sameNameTableExists = true;
|
763 |
+
break;
|
764 |
+
}
|
765 |
+
}
|
766 |
+
}
|
767 |
+
if (false === $this->sameNameTableExists && 0 === stripos($table, $table_prefix) && 0 !== strpos($table, $table_prefix)) {
|
768 |
+
$post_fix = substr($table, strlen($table_prefix));
|
769 |
+
$rewrite_table_name = $table_prefix . $post_fix;
|
770 |
+
} else {
|
771 |
+
$rewrite_table_name = $table;
|
772 |
+
}
|
773 |
+
return $rewrite_table_name;
|
774 |
+
}
|
775 |
+
|
776 |
+
private function getTablePrefix()
|
777 |
+
{
|
778 |
+
global $wpdb;
|
779 |
+
$table_prefix = (is_multisite() && !defined('MULTISITE')) ? $wpdb->base_prefix : $wpdb->get_blog_prefix(0);
|
780 |
+
return $table_prefix;
|
781 |
+
}
|
782 |
+
|
783 |
+
public function getUrl()
|
784 |
+
{
|
785 |
+
return DUP_Settings::getSsdirUrl() . "/" . $this->File;
|
786 |
+
}
|
787 |
+
}
|
classes/package/class.pack.installer.php
CHANGED
@@ -1,551 +1,1043 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
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 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
-
|
463 |
-
|
464 |
-
|
465 |
-
|
466 |
-
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
-
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
|
524 |
-
|
525 |
-
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
-
|
530 |
-
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
use Duplicator\Libs\DupArchive\DupArchiveEngine;
|
4 |
+
use Duplicator\Libs\Snap\SnapCode;
|
5 |
+
use Duplicator\Libs\Snap\SnapIO;
|
6 |
+
use Duplicator\Libs\Snap\SnapJson;
|
7 |
+
use Duplicator\Libs\Snap\SnapOrigFileManager;
|
8 |
+
use Duplicator\Libs\Snap\SnapWP;
|
9 |
+
use Duplicator\Libs\WpConfig\WPConfigTransformer;
|
10 |
+
|
11 |
+
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
12 |
+
// Exit if accessed directly
|
13 |
+
/* @var $global DUP_Global_Entity */
|
14 |
+
|
15 |
+
require_once(DUPLICATOR_PLUGIN_PATH . '/classes/class.archive.config.php');
|
16 |
+
require_once(DUPLICATOR_PLUGIN_PATH . '/classes/utilities/class.u.zip.php');
|
17 |
+
require_once(DUPLICATOR_PLUGIN_PATH . '/classes/utilities/class.u.multisite.php');
|
18 |
+
require_once(DUPLICATOR_PLUGIN_PATH . '/classes/class.password.php');
|
19 |
+
class DUP_Installer
|
20 |
+
{
|
21 |
+
const INSTALLER_SERVER_EXTENSION = '.php.bak';
|
22 |
+
const DEFAULT_INSTALLER_FILE_NAME_WITHOUT_HASH = 'installer';
|
23 |
+
const CONFIG_ORIG_FILE_FOLDER_PREFIX = 'source_site_';
|
24 |
+
const CONFIG_ORIG_FILE_USERINI_ID = 'userini';
|
25 |
+
const CONFIG_ORIG_FILE_HTACCESS_ID = 'htaccess';
|
26 |
+
const CONFIG_ORIG_FILE_WPCONFIG_ID = 'wpconfig';
|
27 |
+
const CONFIG_ORIG_FILE_PHPINI_ID = 'phpini';
|
28 |
+
const CONFIG_ORIG_FILE_WEBCONFIG_ID = 'webconfig';
|
29 |
+
|
30 |
+
//PUBLIC
|
31 |
+
public $File;
|
32 |
+
public $Size = 0;
|
33 |
+
public $OptsDBHost;
|
34 |
+
public $OptsDBPort;
|
35 |
+
public $OptsDBName;
|
36 |
+
public $OptsDBUser;
|
37 |
+
public $OptsDBCharset;
|
38 |
+
public $OptsDBCollation;
|
39 |
+
public $OptsSecureOn = 0;
|
40 |
+
public $OptsSecurePass;
|
41 |
+
public $numFilesAdded = 0;
|
42 |
+
public $numDirsAdded = 0;
|
43 |
+
|
44 |
+
/** @var DUP_Package */
|
45 |
+
protected $Package = null;
|
46 |
+
/** @var SnapOrigFileManager */
|
47 |
+
protected $origFileManger = null;
|
48 |
+
|
49 |
+
/** @var WPConfigTransformer */
|
50 |
+
private $configTransformer = null;
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Class contructor
|
54 |
+
*/
|
55 |
+
public function __construct($package)
|
56 |
+
{
|
57 |
+
$this->Package = $package;
|
58 |
+
|
59 |
+
if (($wpConfigPath = SnapWP::getWPConfigPath()) !== false) {
|
60 |
+
$this->configTransformer = new WPConfigTransformer($wpConfigPath);
|
61 |
+
}
|
62 |
+
}
|
63 |
+
|
64 |
+
public function build($package, $error_behavior = Dup_ErrorBehavior::Quit)
|
65 |
+
{
|
66 |
+
DUP_Log::Info("building installer");
|
67 |
+
$this->Package = $package;
|
68 |
+
$success = false;
|
69 |
+
if ($this->create_enhanced_installer_files()) {
|
70 |
+
$success = $this->add_extra_files($package);
|
71 |
+
} else {
|
72 |
+
DUP_Log::Info("error creating enhanced installer files");
|
73 |
+
}
|
74 |
+
|
75 |
+
|
76 |
+
if ($success) {
|
77 |
+
$package->BuildProgress->installer_built = true;
|
78 |
+
} else {
|
79 |
+
$error_message = 'Error adding installer';
|
80 |
+
$package->BuildProgress->set_failed($error_message);
|
81 |
+
$package->Status = DUP_PackageStatus::ERROR;
|
82 |
+
$package->Update();
|
83 |
+
DUP_Log::error($error_message, "Marking build progress as failed because couldn't add installer files", $error_behavior);
|
84 |
+
//$package->BuildProgress->failed = true;
|
85 |
+
//$package->setStatus(DUP_PackageStatus::ERROR);
|
86 |
+
}
|
87 |
+
|
88 |
+
return $success;
|
89 |
+
}
|
90 |
+
|
91 |
+
private function create_enhanced_installer_files()
|
92 |
+
{
|
93 |
+
$success = false;
|
94 |
+
if ($this->create_enhanced_installer()) {
|
95 |
+
$success = $this->create_archive_config_file();
|
96 |
+
} else {
|
97 |
+
DUP_Log::infoTrace("Error in create_enhanced_installer, set build failed");
|
98 |
+
}
|
99 |
+
|
100 |
+
return $success;
|
101 |
+
}
|
102 |
+
|
103 |
+
private function create_enhanced_installer()
|
104 |
+
{
|
105 |
+
$success = true;
|
106 |
+
$archive_filepath = DUP_Settings::getSsdirTmpPath() . "/{$this->Package->Archive->File}";
|
107 |
+
$installer_filepath = apply_filters(
|
108 |
+
'duplicator_installer_file_path',
|
109 |
+
DUP_Settings::getSsdirTmpPath() . "/{$this->Package->NameHash}_installer" . self::INSTALLER_SERVER_EXTENSION
|
110 |
+
);
|
111 |
+
$template_filepath = DUPLICATOR_PLUGIN_PATH . '/installer/installer.tpl';
|
112 |
+
// Replace the @@ARCHIVE@@ token
|
113 |
+
$header = <<<HEADER
|
114 |
+
<?php
|
115 |
+
/* ------------------------------ NOTICE ----------------------------------
|
116 |
+
|
117 |
+
If you're seeing this text when browsing to the installer, it means your
|
118 |
+
web server is not set up properly.
|
119 |
+
|
120 |
+
Please contact your host and ask them to enable "PHP" processing on your
|
121 |
+
account.
|
122 |
+
----------------------------- NOTICE --------------------------------- */
|
123 |
+
?>
|
124 |
+
HEADER;
|
125 |
+
$installer_contents = $header . SnapCode::getSrcClassCode($template_filepath, false) . "\n/* DUPLICATOR_INSTALLER_EOF */";
|
126 |
+
// $installer_contents = file_get_contents($template_filepath);
|
127 |
+
// $csrf_class_contents = file_get_contents($csrf_class_filepath);
|
128 |
+
|
129 |
+
if (DUP_Settings::Get('archive_build_mode') == DUP_Archive_Build_Mode::DupArchive) {
|
130 |
+
$dupLib = DUPLICATOR_PLUGIN_PATH . '/src/Libs/DupArchive/';
|
131 |
+
$dupExpanderCoder = '';
|
132 |
+
$dupExpanderCoder .= SnapCode::getSrcClassCode($dupLib . 'DupArchive.php') . "\n";
|
133 |
+
$dupExpanderCoder .= SnapCode::getSrcClassCode($dupLib . 'DupArchiveExpandBasicEngine.php') . "\n";
|
134 |
+
$dupExpanderCoder .= SnapCode::getSrcClassCode($dupLib . 'Headers/DupArchiveReaderDirectoryHeader.php') . "\n";
|
135 |
+
$dupExpanderCoder .= SnapCode::getSrcClassCode($dupLib . 'Headers/DupArchiveReaderFileHeader.php') . "\n";
|
136 |
+
$dupExpanderCoder .= SnapCode::getSrcClassCode($dupLib . 'Headers/DupArchiveReaderGlobHeader.php') . "\n";
|
137 |
+
$dupExpanderCoder .= SnapCode::getSrcClassCode($dupLib . 'Headers/DupArchiveReaderHeader.php') . "\n";
|
138 |
+
$dupExpanderCoder .= SnapCode::getSrcClassCode($dupLib . 'Headers/DupArchiveHeaderU.php') . "\n";
|
139 |
+
$dupExpanderCoder .= SnapCode::getSrcClassCode($dupLib . 'Info/DupArchiveExpanderInfo.php') . "\n";
|
140 |
+
if (strlen($dupExpanderCoder) == 0) {
|
141 |
+
DUP_Log::error(__('Error reading DupArchive expander', 'Duplicator'), __('Error reading DupArchive expander', 'duplicator'), false);
|
142 |
+
return false;
|
143 |
+
}
|
144 |
+
} else {
|
145 |
+
$dupExpanderCoder = '';
|
146 |
+
}
|
147 |
+
|
148 |
+
$search_array = array('@@ARCHIVE@@', '@@VERSION@@', '@@ARCHIVE_SIZE@@', '@@PACKAGE_HASH@@', '@@SECONDARY_PACKAGE_HASH@@', '@@DUPARCHIVE_MINI_EXPANDER@@');
|
149 |
+
$package_hash = $this->Package->getPackageHash();
|
150 |
+
$secondary_package_hash = $this->Package->getSecondaryPackageHash();
|
151 |
+
$replace_array = array($this->Package->Archive->File, DUPLICATOR_VERSION, @filesize($archive_filepath), $package_hash, $secondary_package_hash, $dupExpanderCoder);
|
152 |
+
$installer_contents = str_replace($search_array, $replace_array, $installer_contents);
|
153 |
+
if (@file_put_contents($installer_filepath, $installer_contents) === false) {
|
154 |
+
DUP_Log::error(__('Error writing installer contents', 'duplicator'), __("Couldn't write to $installer_filepath", 'duplicator'), false);
|
155 |
+
$success = false;
|
156 |
+
}
|
157 |
+
|
158 |
+
if ($success) {
|
159 |
+
$this->Size = @filesize($installer_filepath);
|
160 |
+
}
|
161 |
+
|
162 |
+
return $success;
|
163 |
+
}
|
164 |
+
|
165 |
+
/**
|
166 |
+
* Create archive.txt file */
|
167 |
+
private function create_archive_config_file()
|
168 |
+
{
|
169 |
+
global $wpdb;
|
170 |
+
$success = true;
|
171 |
+
$archive_config_filepath = DUP_Settings::getSsdirTmpPath() . "/{$this->Package->NameHash}_archive.txt";
|
172 |
+
$ac = new DUP_Archive_Config();
|
173 |
+
$extension = strtolower($this->Package->Archive->Format);
|
174 |
+
$hasher = new DUP_PasswordHash(8, false);
|
175 |
+
$pass_hash = $hasher->HashPassword($this->Package->Installer->OptsSecurePass);
|
176 |
+
$this->Package->Database->getScannerData();
|
177 |
+
//READ-ONLY: COMPARE VALUES
|
178 |
+
$ac->created = $this->Package->Created;
|
179 |
+
$ac->version_dup = DUPLICATOR_VERSION;
|
180 |
+
$ac->version_wp = $this->Package->VersionWP;
|
181 |
+
$ac->version_db = $this->Package->VersionDB;
|
182 |
+
$ac->version_php = $this->Package->VersionPHP;
|
183 |
+
$ac->version_os = $this->Package->VersionOS;
|
184 |
+
$ac->dbInfo = $this->Package->Database->info;
|
185 |
+
$ac->packInfo = array(
|
186 |
+
'packageId' => $this->Package->ID,
|
187 |
+
'packageName' => $this->Package->Name,
|
188 |
+
'packageHash' => $this->Package->getPackageHash(),
|
189 |
+
'secondaryHash' => $this->Package->getSecondaryPackageHash()
|
190 |
+
);
|
191 |
+
$ac->fileInfo = array(
|
192 |
+
'dirCount' => $this->Package->Archive->dirsCount,
|
193 |
+
'fileCount' => $this->Package->Archive->filesCount,
|
194 |
+
'size' => $this->Package->Archive->Size
|
195 |
+
);
|
196 |
+
$ac->wpInfo = $this->getWpInfo();
|
197 |
+
|
198 |
+
$ac->installer_base_name = 'installer' . self::INSTALLER_SERVER_EXTENSION;
|
199 |
+
$ac->installer_backup_name = $this->Package->NameHash . '_installer-backup.php';
|
200 |
+
$ac->package_name = "{$this->Package->NameHash}_archive.{$extension}";
|
201 |
+
$ac->package_hash = $this->Package->getPackageHash();
|
202 |
+
$ac->package_notes = $this->Package->Notes;
|
203 |
+
$ac->opts_delete = SnapJson::jsonEncode($GLOBALS['DUPLICATOR_OPTS_DELETE']);
|
204 |
+
$ac->blogname = esc_html(get_option('blogname'));
|
205 |
+
|
206 |
+
$ac->exportOnlyDB = $this->Package->Archive->ExportOnlyDB;
|
207 |
+
|
208 |
+
// PRE-FILLED: GENERAL
|
209 |
+
$ac->secure_on = filter_var($this->Package->Installer->OptsSecureOn, FILTER_VALIDATE_BOOLEAN);
|
210 |
+
$ac->secure_pass = $pass_hash;
|
211 |
+
$ac->dbhost = (strlen($this->Package->Installer->OptsDBHost) ? $this->Package->Installer->OptsDBHost : null);
|
212 |
+
$ac->dbname = (strlen($this->Package->Installer->OptsDBName) ? $this->Package->Installer->OptsDBName : null);
|
213 |
+
$ac->dbuser = (strlen($this->Package->Installer->OptsDBUser) ? $this->Package->Installer->OptsDBUser : null);
|
214 |
+
$ac->dbpass = null;
|
215 |
+
|
216 |
+
$ac->mu_mode = DUP_MU::getMode();
|
217 |
+
$ac->wp_tableprefix = $wpdb->base_prefix;
|
218 |
+
$ac->mu_generation = DUP_MU::getGeneration();
|
219 |
+
$ac->mu_is_filtered = !empty($this->Package->Multisite->FilterSites) ? true : false;
|
220 |
+
|
221 |
+
$ac->mu_siteadmins = array_values(get_super_admins());
|
222 |
+
$filteredTables = ($this->Package->Database->FilterOn && isset($this->Package->Database->FilterTables)) ? explode(',', $this->Package->Database->FilterTables) : array();
|
223 |
+
$ac->subsites = DUP_MU::getSubsites(array(), $filteredTables, $this->Package->Archive->FilterInfo->Dirs->Instance);
|
224 |
+
if ($ac->subsites === false) {
|
225 |
+
DUP_Log::error("Error get subsites", "Couldn't get subisites", false);
|
226 |
+
$success = false;
|
227 |
+
}
|
228 |
+
$ac->main_site_id = SnapWP::getMainSiteId();
|
229 |
+
$ac->brand = array(
|
230 |
+
'name' => "Duplicator",
|
231 |
+
'isDefault' => true,
|
232 |
+
'logo' => '<i class="fa fa-bolt fa-sm"></i> Duplicator',
|
233 |
+
'enabled' => false,
|
234 |
+
'style' => array()
|
235 |
+
);
|
236 |
+
$ac->wp_tableprefix = $wpdb->base_prefix;
|
237 |
+
$ac->mu_mode = DUP_MU::getMode();
|
238 |
+
|
239 |
+
$ac->overwriteInstallerParams = apply_filters('duplicator_overwrite_params_data', $this->getPrefillParams());
|
240 |
+
$json = SnapJson::jsonEncodePPrint($ac);
|
241 |
+
DUP_Log::TraceObject('json', $json);
|
242 |
+
if (file_put_contents($archive_config_filepath, $json) === false) {
|
243 |
+
DUP_Log::error("Error writing archive config", "Couldn't write archive config at $archive_config_filepath", Dup_ErrorBehavior::LogOnly);
|
244 |
+
$success = false;
|
245 |
+
}
|
246 |
+
|
247 |
+
return $success;
|
248 |
+
}
|
249 |
+
|
250 |
+
private function getPrefillParams()
|
251 |
+
{
|
252 |
+
$result = array();
|
253 |
+
if (strlen($this->OptsDBHost) > 0) {
|
254 |
+
$result['dbhost'] = array('value' => $this->OptsDBHost);
|
255 |
+
}
|
256 |
+
|
257 |
+
if (strlen($this->OptsDBName) > 0) {
|
258 |
+
$result['dbname'] = array('value' => $this->OptsDBName);
|
259 |
+
}
|
260 |
+
|
261 |
+
if (strlen($this->OptsDBUser) > 0) {
|
262 |
+
$result['dbuser'] = array('value' => $this->OptsDBUser);
|
263 |
+
}
|
264 |
+
|
265 |
+
$result['blogname'] = array('value' => esc_html(get_option('blogname')));
|
266 |
+
|
267 |
+
return $result;
|
268 |
+
}
|
269 |
+
|
270 |
+
/**
|
271 |
+
* get wpInfo object
|
272 |
+
*
|
273 |
+
* @return \stdClass
|
274 |
+
*/
|
275 |
+
private function getWpInfo()
|
276 |
+
{
|
277 |
+
$wpInfo = new stdClass();
|
278 |
+
$wpInfo->version = $this->Package->VersionWP;
|
279 |
+
$wpInfo->is_multisite = is_multisite();
|
280 |
+
if (function_exists('get_current_network_id')) {
|
281 |
+
$wpInfo->network_id = get_current_network_id();
|
282 |
+
} else {
|
283 |
+
$wpInfo->network_id = 1;
|
284 |
+
}
|
285 |
+
|
286 |
+
$wpInfo->targetRoot = DUP_Archive::getTargetRootPath();
|
287 |
+
$wpInfo->targetPaths = DUP_Archive::getScanPaths();
|
288 |
+
$wpInfo->adminUsers = SnapWP::getAdminUserLists();
|
289 |
+
$wpInfo->configs = new stdClass();
|
290 |
+
$wpInfo->configs->defines = new stdClass();
|
291 |
+
$wpInfo->configs->realValues = new stdClass();
|
292 |
+
$wpInfo->plugins = $this->getPluginsInfo();
|
293 |
+
$wpInfo->themes = $this->getThemesInfo();
|
294 |
+
|
295 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'ABSPATH');
|
296 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'DB_CHARSET');
|
297 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'DB_COLLATE');
|
298 |
+
$this->addDefineIfExists(
|
299 |
+
$wpInfo->configs->defines,
|
300 |
+
'MYSQL_CLIENT_FLAGS',
|
301 |
+
array('Duplicator\\Libs\\Snap\\SnapDB', 'getMysqlConnectFlagsFromMaskVal')
|
302 |
+
);
|
303 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'AUTH_KEY');
|
304 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'SECURE_AUTH_KEY');
|
305 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'LOGGED_IN_KEY');
|
306 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'NONCE_KEY');
|
307 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'AUTH_SALT');
|
308 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'SECURE_AUTH_SALT');
|
309 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'LOGGED_IN_SALT');
|
310 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'NONCE_SALT');
|
311 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'WP_SITEURL');
|
312 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'WP_HOME');
|
313 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'WP_CONTENT_DIR');
|
314 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'WP_CONTENT_URL');
|
315 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'WP_PLUGIN_DIR');
|
316 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'WP_PLUGIN_URL');
|
317 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'PLUGINDIR');
|
318 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'UPLOADS');
|
319 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'AUTOSAVE_INTERVAL');
|
320 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'WP_POST_REVISIONS');
|
321 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'COOKIE_DOMAIN');
|
322 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'WP_ALLOW_MULTISITE');
|
323 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'ALLOW_MULTISITE');
|
324 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'MULTISITE');
|
325 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'DOMAIN_CURRENT_SITE');
|
326 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'PATH_CURRENT_SITE');
|
327 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'SITE_ID_CURRENT_SITE');
|
328 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'BLOG_ID_CURRENT_SITE');
|
329 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'SUBDOMAIN_INSTALL');
|
330 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'VHOST');
|
331 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'SUNRISE');
|
332 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'NOBLOGREDIRECT');
|
333 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'WP_DEBUG');
|
334 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'SCRIPT_DEBUG');
|
335 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'CONCATENATE_SCRIPTS');
|
336 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'WP_DEBUG_LOG');
|
337 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'WP_DEBUG_DISPLAY');
|
338 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'WP_MEMORY_LIMIT');
|
339 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'WP_MAX_MEMORY_LIMIT');
|
340 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'WP_CACHE');
|
341 |
+
|
342 |
+
// wp super cache define
|
343 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'WPCACHEHOME');
|
344 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'WP_TEMP_DIR');
|
345 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'CUSTOM_USER_TABLE');
|
346 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'CUSTOM_USER_META_TABLE');
|
347 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'WPLANG');
|
348 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'WP_LANG_DIR');
|
349 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'SAVEQUERIES');
|
350 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'FS_CHMOD_DIR');
|
351 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'FS_CHMOD_FILE');
|
352 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'FS_METHOD');
|
353 |
+
/**
|
354 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'FTP_BASE');
|
355 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'FTP_CONTENT_DIR');
|
356 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'FTP_PLUGIN_DIR');
|
357 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'FTP_PUBKEY');
|
358 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'FTP_PRIKEY');
|
359 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'FTP_USER');
|
360 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'FTP_PASS');
|
361 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'FTP_HOST');
|
362 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'FTP_SSL');
|
363 |
+
* */
|
364 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'ALTERNATE_WP_CRON');
|
365 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'DISABLE_WP_CRON');
|
366 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'WP_CRON_LOCK_TIMEOUT');
|
367 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'COOKIEPATH');
|
368 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'SITECOOKIEPATH');
|
369 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'ADMIN_COOKIE_PATH');
|
370 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'PLUGINS_COOKIE_PATH');
|
371 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'TEMPLATEPATH');
|
372 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'STYLESHEETPATH');
|
373 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'EMPTY_TRASH_DAYS');
|
374 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'WP_ALLOW_REPAIR');
|
375 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'DO_NOT_UPGRADE_GLOBAL_TABLES');
|
376 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'DISALLOW_FILE_EDIT');
|
377 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'DISALLOW_FILE_MODS');
|
378 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'FORCE_SSL_ADMIN');
|
379 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'WP_HTTP_BLOCK_EXTERNAL');
|
380 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'WP_ACCESSIBLE_HOSTS');
|
381 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'AUTOMATIC_UPDATER_DISABLED');
|
382 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'WP_AUTO_UPDATE_CORE');
|
383 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'IMAGE_EDIT_OVERWRITE');
|
384 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'WPMU_PLUGIN_DIR');
|
385 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'WPMU_PLUGIN_URL');
|
386 |
+
$this->addDefineIfExists($wpInfo->configs->defines, 'MUPLUGINDIR');
|
387 |
+
|
388 |
+
$originalUrls = DUP_Archive::getOriginalUrls();
|
389 |
+
$wpInfo->configs->realValues->siteUrl = $originalUrls['abs'];
|
390 |
+
$wpInfo->configs->realValues->homeUrl = $originalUrls['home'];
|
391 |
+
$wpInfo->configs->realValues->contentUrl = $originalUrls['wpcontent'];
|
392 |
+
$wpInfo->configs->realValues->uploadBaseUrl = $originalUrls['uploads'];
|
393 |
+
$wpInfo->configs->realValues->pluginsUrl = $originalUrls['plugins'];
|
394 |
+
$wpInfo->configs->realValues->mupluginsUrl = $originalUrls['muplugins'];
|
395 |
+
$wpInfo->configs->realValues->themesUrl = $originalUrls['themes'];
|
396 |
+
$wpInfo->configs->realValues->originalPaths = array();
|
397 |
+
$originalpaths = DUP_Archive::getOriginalPaths();
|
398 |
+
foreach ($originalpaths as $key => $val) {
|
399 |
+
$wpInfo->configs->realValues->originalPaths[$key] = rtrim($val, '\\/');
|
400 |
+
}
|
401 |
+
$wpInfo->configs->realValues->archivePaths = array_merge($wpInfo->configs->realValues->originalPaths, DUP_Archive::getArchiveListPaths());
|
402 |
+
return $wpInfo;
|
403 |
+
}
|
404 |
+
|
405 |
+
|
406 |
+
/**
|
407 |
+
* Check if $define is defined and add a prop to $obj
|
408 |
+
*
|
409 |
+
* @param object $obj
|
410 |
+
* @param string $define
|
411 |
+
* @param null|callable $transformCallback if it is different from null the function is applied to the value
|
412 |
+
*
|
413 |
+
* @return boolean return true if define is added of false
|
414 |
+
*/
|
415 |
+
private function addDefineIfExists($obj, $define, $transformCallback = null)
|
416 |
+
{
|
417 |
+
if (!defined($define)) {
|
418 |
+
return false;
|
419 |
+
}
|
420 |
+
|
421 |
+
$obj->{$define} = new StdClass();
|
422 |
+
|
423 |
+
if (is_callable($transformCallback)) {
|
424 |
+
$obj->{$define}->value = call_user_func($transformCallback, constant($define));
|
425 |
+
} else {
|
426 |
+
if ($transformCallback !== null) {
|
427 |
+
throw new Exception('transformCallback isn\'t callable');
|
428 |
+
}
|
429 |
+
$obj->{$define}->value = constant($define);
|
430 |
+
}
|
431 |
+
|
432 |
+
if (!is_null($this->configTransformer)) {
|
433 |
+
$obj->{$define}->inWpConfig = $this->configTransformer->exists('constant', $define);
|
434 |
+
} else {
|
435 |
+
$obj->{$define}->inWpConfig = false;
|
436 |
+
}
|
437 |
+
|
438 |
+
return true;
|
439 |
+
}
|
440 |
+
|
441 |
+
|
442 |
+
/**
|
443 |
+
* get plugins array info with multisite, must-use and drop-ins
|
444 |
+
*
|
445 |
+
* @return array
|
446 |
+
*/
|
447 |
+
public function getPluginsInfo()
|
448 |
+
{
|
449 |
+
if (!function_exists('get_plugins')) {
|
450 |
+
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
451 |
+
}
|
452 |
+
|
453 |
+
// parse all plugins
|
454 |
+
$result = array();
|
455 |
+
foreach (get_plugins() as $path => $plugin) {
|
456 |
+
$result[$path] = self::getPluginArrayData($path, $plugin);
|
457 |
+
$result[$path]['networkActive'] = is_plugin_active_for_network($path);
|
458 |
+
if (!is_multisite()) {
|
459 |
+
$result[$path]['active'] = is_plugin_active($path);
|
460 |
+
} else {
|
461 |
+
// if is _multisite the active value is an array with the blog ids list where the plugin is active
|
462 |
+
$result[$path]['active'] = array();
|
463 |
+
}
|
464 |
+
}
|
465 |
+
|
466 |
+
// if is _multisite the active value is an array with the blog ids list where the plugin is active
|
467 |
+
if (is_multisite()) {
|
468 |
+
foreach (SnapWP::getSitesIds() as $siteId) {
|
469 |
+
switch_to_blog($siteId);
|
470 |
+
foreach ($result as $path => $plugin) {
|
471 |
+
if (!$result[$path]['networkActive'] && is_plugin_active($path)) {
|
472 |
+
$result[$path]['active'][] = $siteId;
|
473 |
+
}
|
474 |
+
}
|
475 |
+
restore_current_blog();
|
476 |
+
}
|
477 |
+
}
|
478 |
+
|
479 |
+
// parse all must use plugins
|
480 |
+
foreach (get_mu_plugins() as $path => $plugin) {
|
481 |
+
$result[$path] = self::getPluginArrayData($path, $plugin);
|
482 |
+
$result[$path]['mustUse'] = true;
|
483 |
+
}
|
484 |
+
|
485 |
+
// parse all dropins plugins
|
486 |
+
foreach (get_dropins() as $path => $plugin) {
|
487 |
+
$result[$path] = self::getPluginArrayData($path, $plugin);
|
488 |
+
$result[$path]['dropIns'] = true;
|
489 |
+
}
|
490 |
+
|
491 |
+
return $result;
|
492 |
+
}
|
493 |
+
|
494 |
+
/**
|
495 |
+
* return plugin formatted data from plugin info
|
496 |
+
* plugin info = Array (
|
497 |
+
* [Name] => Hello Dolly
|
498 |
+
* [PluginURI] => http://wordpress.org/extend/plugins/hello-dolly/
|
499 |
+
* [Version] => 1.6
|
500 |
+
* [Description] => This is not just ...
|
501 |
+
* [Author] => Matt Mullenweg
|
502 |
+
* [AuthorURI] => http://ma.tt/
|
503 |
+
* [TextDomain] =>
|
504 |
+
* [DomainPath] =>
|
505 |
+
* [Network] =>
|
506 |
+
* [Title] => Hello Dolly
|
507 |
+
* [AuthorName] => Matt Mullenweg
|
508 |
+
* )
|
509 |
+
*
|
510 |
+
* @param string $slug // plugin slug
|
511 |
+
* @param array $plugin // pluhin info from get_plugins function
|
512 |
+
* @return array
|
513 |
+
*/
|
514 |
+
protected static function getPluginArrayData($slug, $plugin)
|
515 |
+
{
|
516 |
+
return array(
|
517 |
+
'slug' => $slug,
|
518 |
+
'name' => $plugin['Name'],
|
519 |
+
'version' => $plugin['Version'],
|
520 |
+
'pluginURI' => $plugin['PluginURI'],
|
521 |
+
'author' => $plugin['Author'],
|
522 |
+
'authorURI' => $plugin['AuthorURI'],
|
523 |
+
'description' => $plugin['Description'],
|
524 |
+
'title' => $plugin['Title'],
|
525 |
+
'networkActive' => false,
|
526 |
+
'active' => false,
|
527 |
+
'mustUse' => false,
|
528 |
+
'dropIns' => false
|
529 |
+
);
|
530 |
+
}
|
531 |
+
|
532 |
+
/**
|
533 |
+
* get themes array info with active template, stylesheet
|
534 |
+
*
|
535 |
+
* @return array
|
536 |
+
*/
|
537 |
+
public function getThemesInfo()
|
538 |
+
{
|
539 |
+
if (!function_exists('wp_get_themes')) {
|
540 |
+
require_once ABSPATH . 'wp-admin/includes/theme.php';
|
541 |
+
}
|
542 |
+
|
543 |
+
foreach (wp_get_themes() as $slug => $theme) {
|
544 |
+
$result[$slug] = self::getThemeArrayData($theme);
|
545 |
+
}
|
546 |
+
|
547 |
+
if (is_multisite()) {
|
548 |
+
foreach (SnapWP::getSitesIds() as $siteId) {
|
549 |
+
switch_to_blog($siteId);
|
550 |
+
$stylesheet = get_stylesheet();
|
551 |
+
if (isset($result[$stylesheet])) {
|
552 |
+
$result[$stylesheet]['isActive'][] = $siteId;
|
553 |
+
}
|
554 |
+
restore_current_blog();
|
555 |
+
}
|
556 |
+
} else {
|
557 |
+
$stylesheet = get_stylesheet();
|
558 |
+
if (isset($result[$stylesheet])) {
|
559 |
+
$result[$stylesheet]['isActive'] = true;
|
560 |
+
}
|
561 |
+
}
|
562 |
+
|
563 |
+
return $result;
|
564 |
+
}
|
565 |
+
|
566 |
+
/**
|
567 |
+
* return plugin formatted data from plugin info
|
568 |
+
*
|
569 |
+
* @param WP_Theme $theme instance of WP Core class WP_Theme. theme info from get_themes function
|
570 |
+
* @return array
|
571 |
+
*/
|
572 |
+
protected static function getThemeArrayData(WP_Theme $theme)
|
573 |
+
{
|
574 |
+
$slug = $theme->get_stylesheet();
|
575 |
+
$parent = $theme->parent();
|
576 |
+
return array(
|
577 |
+
'slug' => $slug,
|
578 |
+
'themeName' => $theme->get('Name'),
|
579 |
+
'version' => $theme->get('Version'),
|
580 |
+
'themeURI' => $theme->get('ThemeURI'),
|
581 |
+
'parentTheme' => (false === $parent) ? false : $parent->get_stylesheet(),
|
582 |
+
'template' => $theme->get_template(),
|
583 |
+
'stylesheet' => $theme->get_stylesheet(),
|
584 |
+
'description' => $theme->get('Description'),
|
585 |
+
'author' => $theme->get('Author'),
|
586 |
+
"authorURI" => $theme->get('AuthorURI'),
|
587 |
+
'tags' => $theme->get('Tags'),
|
588 |
+
'isAllowed' => $theme->is_allowed(),
|
589 |
+
'isActive' => (is_multisite() ? array() : false),
|
590 |
+
'defaultTheme' => (defined('WP_DEFAULT_THEME') && WP_DEFAULT_THEME == $slug),
|
591 |
+
);
|
592 |
+
}
|
593 |
+
|
594 |
+
/**
|
595 |
+
* return list of extra files to att to archive
|
596 |
+
*
|
597 |
+
* @param bool $checkExists
|
598 |
+
* @return array
|
599 |
+
* @throws Exception
|
600 |
+
*/
|
601 |
+
private function getExtraFilesLists($checkExists = true)
|
602 |
+
{
|
603 |
+
$result = array();
|
604 |
+
|
605 |
+
$installerFilepath = apply_filters(
|
606 |
+
'duplicator_installer_file_path',
|
607 |
+
DUP_Settings::getSsdirTmpPath() . "/{$this->Package->NameHash}_installer" . self::INSTALLER_SERVER_EXTENSION
|
608 |
+
);
|
609 |
+
|
610 |
+
$result[] = array(
|
611 |
+
'sourcePath' => $installerFilepath,
|
612 |
+
'archivePath' => $this->getInstallerBackupName(),
|
613 |
+
'label' => 'installer backup file'
|
614 |
+
);
|
615 |
+
|
616 |
+
$result[] = array(
|
617 |
+
'sourcePath' => DUPLICATOR_LITE_PATH . '/installer/dup-installer',
|
618 |
+
'archivePath' => '/',
|
619 |
+
'label' => 'dup installer folder'
|
620 |
+
);
|
621 |
+
|
622 |
+
$result[] = array(
|
623 |
+
'sourcePath' => DUPLICATOR_LITE_PATH . '/src/Libs/Snap',
|
624 |
+
'archivePath' => 'dup-installer/libs/',
|
625 |
+
'label' => 'dup snaplib folder'
|
626 |
+
);
|
627 |
+
|
628 |
+
$result[] = array(
|
629 |
+
'sourcePath' => DUPLICATOR_LITE_PATH . '/src/Libs/DupArchive',
|
630 |
+
'archivePath' => 'dup-installer/libs/',
|
631 |
+
'label' => 'dup snaplib folder'
|
632 |
+
);
|
633 |
+
|
634 |
+
$result[] = array(
|
635 |
+
'sourcePath' => DUPLICATOR_LITE_PATH . '/src/Libs/WpConfig',
|
636 |
+
'archivePath' => 'dup-installer/libs/',
|
637 |
+
'label' => 'lib config folder'
|
638 |
+
);
|
639 |
+
|
640 |
+
$result[] = array(
|
641 |
+
'sourcePath' => DUPLICATOR_LITE_PATH . '/src/Libs/Certificates',
|
642 |
+
'archivePath' => 'dup-installer/libs/',
|
643 |
+
'label' => 'SSL certificates'
|
644 |
+
);
|
645 |
+
|
646 |
+
$result[] = array(
|
647 |
+
'sourcePath' => DUPLICATOR_LITE_PATH . '/vendor/requests',
|
648 |
+
'archivePath' => 'dup-installer/vendor/',
|
649 |
+
'label' => 'Requests library'
|
650 |
+
);
|
651 |
+
|
652 |
+
$result[] = array(
|
653 |
+
'sourcePath' => DUPLICATOR_LITE_PATH . '/assets/js/duplicator-tooltip.js',
|
654 |
+
'archivePath' => 'dup-installer/assets/js/duplicator-tooltip.js',
|
655 |
+
'label' => 'Duplicator tooltip script'
|
656 |
+
);
|
657 |
+
|
658 |
+
$result[] = array(
|
659 |
+
'sourcePath' => DUPLICATOR_LITE_PATH . '/assets/js/popper',
|
660 |
+
'archivePath' => 'dup-installer/assets/js/',
|
661 |
+
'label' => 'popper js'
|
662 |
+
);
|
663 |
+
|
664 |
+
$result[] = array(
|
665 |
+
'sourcePath' => DUPLICATOR_LITE_PATH . '/assets/js/tippy',
|
666 |
+
'archivePath' => 'dup-installer/assets/js/',
|
667 |
+
'label' => 'tippy js'
|
668 |
+
);
|
669 |
+
|
670 |
+
$result[] = array(
|
671 |
+
'sourcePath' => $this->origFileManger->getMainFolder(),
|
672 |
+
'archivePath' => 'dup-installer/',
|
673 |
+
'label' => 'original files folder'
|
674 |
+
);
|
675 |
+
|
676 |
+
$result[] = array(
|
677 |
+
'sourcePath' => DUP_Settings::getSsdirTmpPath() . "/{$this->Package->NameHash}_archive.txt",
|
678 |
+
'archivePath' => $this->getArchiveTxtFilePath(),
|
679 |
+
'label' => 'archive descriptor file'
|
680 |
+
);
|
681 |
+
|
682 |
+
$result[] = array(
|
683 |
+
'sourcePath' => DUP_Settings::getSsdirTmpPath() . "/{$this->Package->NameHash}_scan.json",
|
684 |
+
'archivePath' => $this->getEmbeddedScanFilePath(),
|
685 |
+
'label' => 'scan file'
|
686 |
+
);
|
687 |
+
|
688 |
+
$result[] = array(
|
689 |
+
'sourcePath' => DUP_Settings::getSsdirTmpPath() . '/' . $this->Package->NameHash . DUP_Archive::FILES_LIST_FILE_NAME_SUFFIX,
|
690 |
+
'archivePath' => $this->getEmbeddedScanFileList(),
|
691 |
+
'label' => 'files list file'
|
692 |
+
);
|
693 |
+
|
694 |
+
$result[] = array(
|
695 |
+
'sourcePath' => DUP_Settings::getSsdirTmpPath() . '/' . $this->Package->NameHash . DUP_Archive::DIRS_LIST_FILE_NAME_SUFFIX,
|
696 |
+
'archivePath' => $this->getEmbeddedScanDirList(),
|
697 |
+
'label' => 'folders list file'
|
698 |
+
);
|
699 |
+
|
700 |
+
$result[] = array(
|
701 |
+
'sourcePath' => $this->getManualExtractFilePath(),
|
702 |
+
'archivePath' => $this->getEmbeddedManualExtractFilePath(),
|
703 |
+
'label' => 'manual extract file'
|
704 |
+
);
|
705 |
+
|
706 |
+
|
707 |
+
if ($checkExists) {
|
708 |
+
foreach ($result as $item) {
|
709 |
+
if (!is_readable($item['sourcePath'])) {
|
710 |
+
throw new Exception('INSTALLER FILES: "' . $item['label'] . '" doesn\'t exist ' . $item['sourcePath']);
|
711 |
+
}
|
712 |
+
}
|
713 |
+
}
|
714 |
+
|
715 |
+
return $result;
|
716 |
+
}
|
717 |
+
|
718 |
+
public function getInstallerBackupName()
|
719 |
+
{
|
720 |
+
return $this->Package->NameHash . '_installer-backup.php';
|
721 |
+
}
|
722 |
+
|
723 |
+
private function getEmbeddedScanFileList()
|
724 |
+
{
|
725 |
+
return 'dup-installer/dup-scanned-files__' . $this->Package->getPackageHash() . '.txt';
|
726 |
+
}
|
727 |
+
|
728 |
+
private function getEmbeddedScanDirList()
|
729 |
+
{
|
730 |
+
return 'dup-installer/dup-scanned-dirs__' . $this->Package->getPackageHash() . '.txt';
|
731 |
+
}
|
732 |
+
|
733 |
+
/**
|
734 |
+
* createZipBackup
|
735 |
+
* Puts an installer zip file in the archive for backup purposes.
|
736 |
+
*/
|
737 |
+
private function add_extra_files()
|
738 |
+
{
|
739 |
+
$success = false;
|
740 |
+
$archive_filepath = DUP_Settings::getSsdirTmpPath() . "/{$this->Package->Archive->File}";
|
741 |
+
|
742 |
+
$this->initConfigFiles();
|
743 |
+
$this->createManualExtractCheckFile();
|
744 |
+
|
745 |
+
if ($this->Package->Archive->file_count != 2) {
|
746 |
+
DUP_Log::trace("Doing archive file check");
|
747 |
+
// Only way it's 2 is if the root was part of the filter in which case the archive won't be there
|
748 |
+
if (file_exists($archive_filepath) == false) {
|
749 |
+
$error_text = sprintf(__("Zip archive %1s not present.", 'dup;icator'), $archive_filepath);
|
750 |
+
DUP_Log::error($error_text, '', Dup_ErrorBehavior::LogOnly);
|
751 |
+
return false;
|
752 |
+
}
|
753 |
+
}
|
754 |
+
|
755 |
+
if ($this->Package->Archive->Format == 'DAF') {
|
756 |
+
$success = $this->dupArchiveAddExtra();
|
757 |
+
} else {
|
758 |
+
$success = $this->zipArchiveAddExtra();
|
759 |
+
}
|
760 |
+
|
761 |
+
try {
|
762 |
+
$archive_config_filepath = DUP_Settings::getSsdirTmpPath() . "/{$this->Package->NameHash}_archive.txt";
|
763 |
+
// No sense keeping these files
|
764 |
+
@unlink($archive_config_filepath);
|
765 |
+
$this->origFileManger->deleteMainFolder();
|
766 |
+
$this->deleteManualExtractCheckFile();
|
767 |
+
} catch (Exception $e) {
|
768 |
+
DUP_Log::infoTrace("Error clean temp installer file, but continue. Message: " . $e->getMessage());
|
769 |
+
}
|
770 |
+
|
771 |
+
$this->Package->Archive->Size = @filesize($archive_filepath);
|
772 |
+
return $success;
|
773 |
+
}
|
774 |
+
|
775 |
+
/**
|
776 |
+
*
|
777 |
+
* @return boolean
|
778 |
+
* @throws \Exception
|
779 |
+
*/
|
780 |
+
private function zipArchiveAddExtra()
|
781 |
+
{
|
782 |
+
$zipArchive = new ZipArchive();
|
783 |
+
$isCompressed = true;
|
784 |
+
|
785 |
+
if ($zipArchive->open($this->getTmpArchiveFullPath(), ZipArchive::CREATE) !== true) {
|
786 |
+
throw new \Exception("Couldn't open zip archive ");
|
787 |
+
}
|
788 |
+
|
789 |
+
DUP_Log::trace("Successfully opened zip");
|
790 |
+
|
791 |
+
foreach ($this->getExtraFilesLists() as $extraItem) {
|
792 |
+
if (is_dir($extraItem['sourcePath'])) {
|
793 |
+
if (!DUP_Zip_U::addDirWithZipArchive($zipArchive, $extraItem['sourcePath'], true, $extraItem['archivePath'], $isCompressed)) {
|
794 |
+
throw new \Exception('INSTALLER FILES: zip add ' . $extraItem['label'] . ' folder error on folder ' . $extraItem['sourcePath']);
|
795 |
+
}
|
796 |
+
} else {
|
797 |
+
if (!DUP_Zip_U::addFileToZipArchive($zipArchive, $extraItem['sourcePath'], $extraItem['archivePath'], $isCompressed)) {
|
798 |
+
throw new \Exception('INSTALLER FILES: zip add ' . $extraItem['label'] . ' file error on file ' . $extraItem['sourcePath']);
|
799 |
+
}
|
800 |
+
}
|
801 |
+
}
|
802 |
+
|
803 |
+
if ($zipArchive->close() === false) {
|
804 |
+
throw new \Exception("Couldn't close zip archive ");
|
805 |
+
}
|
806 |
+
|
807 |
+
DUP_Log::trace('After ziparchive close when adding installer');
|
808 |
+
|
809 |
+
$this->zipArchiveCheck();
|
810 |
+
return true;
|
811 |
+
}
|
812 |
+
|
813 |
+
private function zipArchiveCheck()
|
814 |
+
{
|
815 |
+
/* ------ ZIP CONSISTENCY CHECK ------ */
|
816 |
+
DUP_Log::trace("Running ZipArchive consistency check");
|
817 |
+
$zip = new ZipArchive();
|
818 |
+
|
819 |
+
// ZipArchive::CHECKCONS will enforce additional consistency checks
|
820 |
+
$res = $zip->open($this->getTmpArchiveFullPath(), ZipArchive::CHECKCONS);
|
821 |
+
if ($res !== true) {
|
822 |
+
$consistency_error = sprintf(__('ERROR: Cannot open created archive. Error code = %1$s', 'duplicator'), $res);
|
823 |
+
DUP_Log::trace($consistency_error);
|
824 |
+
switch ($res) {
|
825 |
+
case ZipArchive::ER_NOZIP:
|
826 |
+
$consistency_error = __('ERROR: Archive is not valid zip archive.', 'duplicator');
|
827 |
+
break;
|
828 |
+
case ZipArchive::ER_INCONS:
|
829 |
+
$consistency_error = __("ERROR: Archive doesn't pass consistency check.", 'duplicator');
|
830 |
+
break;
|
831 |
+
case ZipArchive::ER_CRC:
|
832 |
+
$consistency_error = __("ERROR: Archive checksum is bad.", 'duplicator');
|
833 |
+
break;
|
834 |
+
}
|
835 |
+
|
836 |
+
throw new \Exception($consistency_error);
|
837 |
+
}
|
838 |
+
|
839 |
+
$failed = false;
|
840 |
+
foreach ($this->getInstallerPathsForIntegrityCheck() as $path) {
|
841 |
+
if ($zip->locateName($path) === false) {
|
842 |
+
$failed = true;
|
843 |
+
DUP_Log::infoTrace(__("Couldn't find $path in archive", 'duplicator'));
|
844 |
+
}
|
845 |
+
}
|
846 |
+
|
847 |
+
if ($failed) {
|
848 |
+
DUP_Log::info(__('ARCHIVE CONSISTENCY TEST: FAIL', 'duplicator'));
|
849 |
+
throw new \Exception("Zip for package " . $this->Package->ID . " didn't passed consistency test");
|
850 |
+
} else {
|
851 |
+
DUP_Log::info(__('ARCHIVE CONSISTENCY TEST: PASS', 'duplicator'));
|
852 |
+
DUP_Log::trace("Zip for package " . $this->Package->ID . " passed consistency test");
|
853 |
+
}
|
854 |
+
|
855 |
+
$zip->close();
|
856 |
+
}
|
857 |
+
|
858 |
+
public function getInstallerPathsForIntegrityCheck()
|
859 |
+
{
|
860 |
+
$filesToValidate = array(
|
861 |
+
'dup-installer/assets/index.php',
|
862 |
+
'dup-installer/classes/index.php',
|
863 |
+
'dup-installer/ctrls/index.php',
|
864 |
+
'dup-installer/src/Utils/Autoloader.php',
|
865 |
+
'dup-installer/templates/default/page-help.php',
|
866 |
+
'dup-installer/main.installer.php',
|
867 |
+
);
|
868 |
+
|
869 |
+
foreach ($this->getExtraFilesLists() as $extraItem) {
|
870 |
+
if (is_file($extraItem['sourcePath'])) {
|
871 |
+
$filesToValidate[] = $extraItem['archivePath'];
|
872 |
+
} else {
|
873 |
+
if (file_exists(trailingslashit($extraItem['sourcePath']) . '/index.php')) {
|
874 |
+
$filesToValidate[] = ltrim(trailingslashit($extraItem['archivePath']), '\\/') . basename($extraItem['sourcePath']) . '/index.php';
|
875 |
+
} else {
|
876 |
+
// SKIP CHECK
|
877 |
+
}
|
878 |
+
}
|
879 |
+
}
|
880 |
+
|
881 |
+
return array_unique($filesToValidate);
|
882 |
+
}
|
883 |
+
|
884 |
+
private function dupArchiveAddExtra()
|
885 |
+
{
|
886 |
+
|
887 |
+
$logger = new DUP_DupArchive_Logger();
|
888 |
+
DupArchiveEngine::init($logger, null);
|
889 |
+
|
890 |
+
$archivePath = $this->getTmpArchiveFullPath();
|
891 |
+
$extraPoistion = filesize($archivePath);
|
892 |
+
|
893 |
+
foreach ($this->getExtraFilesLists() as $extraItem) {
|
894 |
+
if (is_dir($extraItem['sourcePath'])) {
|
895 |
+
$basePath = dirname($extraItem['sourcePath']);
|
896 |
+
$destPath = ltrim(trailingslashit($extraItem['archivePath']), '\\/');
|
897 |
+
$result = DupArchiveEngine::addDirectoryToArchiveST($archivePath, $extraItem['sourcePath'], $basePath, true, $destPath);
|
898 |
+
|
899 |
+
$this->numFilesAdded += $result->numFilesAdded;
|
900 |
+
$this->numDirsAdded += $result->numDirsAdded;
|
901 |
+
} else {
|
902 |
+
DupArchiveEngine::addRelativeFileToArchiveST($archivePath, $extraItem['sourcePath'], $extraItem['archivePath']);
|
903 |
+
$this->numFilesAdded++;
|
904 |
+
}
|
905 |
+
}
|
906 |
+
|
907 |
+
// store extra files position
|
908 |
+
$src = json_encode(array(DupArchiveEngine::EXTRA_FILES_POS_KEY => $extraPoistion));
|
909 |
+
$src .= str_repeat("\0", DupArchiveEngine::INDEX_FILE_SIZE - strlen($src));
|
910 |
+
DupArchiveEngine::replaceFileContent($archivePath, $src, DupArchiveEngine::INDEX_FILE_NAME, 0, 3000);
|
911 |
+
|
912 |
+
return true;
|
913 |
+
}
|
914 |
+
|
915 |
+
/**
|
916 |
+
*
|
917 |
+
* @return string
|
918 |
+
*/
|
919 |
+
public function getTmpArchiveFullPath()
|
920 |
+
{
|
921 |
+
return DUP_Settings::getSsdirTmpPath() . '/' . $this->Package->Archive->File;
|
922 |
+
}
|
923 |
+
|
924 |
+
/**
|
925 |
+
* Clear out sensitive database connection information
|
926 |
+
*
|
927 |
+
* @param $temp_conf_ark_file_path Temp config file path
|
928 |
+
* @throws Exception
|
929 |
+
*/
|
930 |
+
private static function cleanTempWPConfArkFilePath($temp_conf_ark_file_path)
|
931 |
+
{
|
932 |
+
try {
|
933 |
+
if (function_exists('token_get_all')) {
|
934 |
+
$transformer = new WPConfigTransformer($temp_conf_ark_file_path);
|
935 |
+
$constants = array('DB_NAME', 'DB_USER', 'DB_PASSWORD', 'DB_HOST');
|
936 |
+
foreach ($constants as $constant) {
|
937 |
+
if ($transformer->exists('constant', $constant)) {
|
938 |
+
$transformer->update('constant', $constant, '');
|
939 |
+
}
|
940 |
+
}
|
941 |
+
}
|
942 |
+
} catch (Exception $e) {
|
943 |
+
DUP_Log::infoTrace("Can\'t inizialize wp-config transformer Message: " . $e->getMessage());
|
944 |
+
} catch (Error $e) {
|
945 |
+
DUP_Log::infoTrace("Can\'t inizialize wp-config transformer Message: " . $e->getMessage());
|
946 |
+
}
|
947 |
+
}
|
948 |
+
|
949 |
+
/**
|
950 |
+
* Get scan.json file path along with name in archive file
|
951 |
+
*/
|
952 |
+
private function getEmbeddedScanFilePath()
|
953 |
+
{
|
954 |
+
$package_hash = $this->Package->getPackageHash();
|
955 |
+
$embedded_scan_ark_file_path = 'dup-installer/dup-scan__' . $package_hash . '.json';
|
956 |
+
return $embedded_scan_ark_file_path;
|
957 |
+
}
|
958 |
+
|
959 |
+
/**
|
960 |
+
* Get archive.txt file path along with name in archive file
|
961 |
+
*/
|
962 |
+
private function getArchiveTxtFilePath()
|
963 |
+
{
|
964 |
+
$package_hash = $this->Package->getPackageHash();
|
965 |
+
$archive_txt_file_path = 'dup-installer/dup-archive__' . $package_hash . '.txt';
|
966 |
+
return $archive_txt_file_path;
|
967 |
+
}
|
968 |
+
|
969 |
+
/**
|
970 |
+
* Creates the original_files_ folder in the tmp directory where all config files are saved
|
971 |
+
* to be later added to the archives
|
972 |
+
*
|
973 |
+
* @throws Exception
|
974 |
+
*/
|
975 |
+
public function initConfigFiles()
|
976 |
+
{
|
977 |
+
$this->origFileManger = new SnapOrigFileManager(
|
978 |
+
DUP_Archive::getArchiveListPaths('home'),
|
979 |
+
DUP_Settings::getSsdirTmpPath(),
|
980 |
+
$this->Package->getPackageHash()
|
981 |
+
);
|
982 |
+
$this->origFileManger->init();
|
983 |
+
$configFilePaths = $this->getConfigFilePaths();
|
984 |
+
foreach ($configFilePaths as $identifier => $path) {
|
985 |
+
if ($path !== false) {
|
986 |
+
try {
|
987 |
+
$this->origFileManger->addEntry($identifier, $path, SnapOrigFileManager::MODE_COPY, self::CONFIG_ORIG_FILE_FOLDER_PREFIX . $identifier);
|
988 |
+
} catch (Exception $ex) {
|
989 |
+
DUP_Log::Info("Error while handling config files: " . $ex->getMessage());
|
990 |
+
}
|
991 |
+
}
|
992 |
+
}
|
993 |
+
|
994 |
+
//Clean sensitive information from wp-config.php file.
|
995 |
+
self::cleanTempWPConfArkFilePath($this->origFileManger->getEntryStoredPath(self::CONFIG_ORIG_FILE_WPCONFIG_ID));
|
996 |
+
}
|
997 |
+
|
998 |
+
/**
|
999 |
+
* Gets config files path
|
1000 |
+
*
|
1001 |
+
* @return string[] array of config files in identifier => path format
|
1002 |
+
*/
|
1003 |
+
public function getConfigFilePaths()
|
1004 |
+
{
|
1005 |
+
$home = DUP_Archive::getArchiveListPaths('home');
|
1006 |
+
$configFiles = array(
|
1007 |
+
self::CONFIG_ORIG_FILE_USERINI_ID => $home . '/.user.ini',
|
1008 |
+
self::CONFIG_ORIG_FILE_PHPINI_ID => $home . '/php.ini',
|
1009 |
+
self::CONFIG_ORIG_FILE_WEBCONFIG_ID => $home . '/web.config',
|
1010 |
+
self::CONFIG_ORIG_FILE_HTACCESS_ID => $home . '/.htaccess',
|
1011 |
+
self::CONFIG_ORIG_FILE_WPCONFIG_ID => SnapWP::getWPConfigPath()
|
1012 |
+
);
|
1013 |
+
foreach ($configFiles as $identifier => $path) {
|
1014 |
+
if (!file_exists($path)) {
|
1015 |
+
unset($configFiles[$identifier]);
|
1016 |
+
}
|
1017 |
+
}
|
1018 |
+
|
1019 |
+
return $configFiles;
|
1020 |
+
}
|
1021 |
+
|
1022 |
+
private function createManualExtractCheckFile()
|
1023 |
+
{
|
1024 |
+
$file_path = $this->getManualExtractFilePath();
|
1025 |
+
return SnapIO::filePutContents($file_path, '');
|
1026 |
+
}
|
1027 |
+
|
1028 |
+
private function getManualExtractFilePath()
|
1029 |
+
{
|
1030 |
+
return DUP_Settings::getSsdirTmpPath() . '/dup-manual-extract__' . $this->Package->getPackageHash();
|
1031 |
+
}
|
1032 |
+
|
1033 |
+
private function getEmbeddedManualExtractFilePath()
|
1034 |
+
{
|
1035 |
+
$embedded_filepath = 'dup-installer/dup-manual-extract__' . $this->Package->getPackageHash();
|
1036 |
+
return $embedded_filepath;
|
1037 |
+
}
|
1038 |
+
|
1039 |
+
private function deleteManualExtractCheckFile()
|
1040 |
+
{
|
1041 |
+
SnapIO::rm($this->getManualExtractFilePath());
|
1042 |
+
}
|
1043 |
+
}
|
classes/package/class.pack.php
CHANGED
@@ -1,1805 +1,1858 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
public $
|
25 |
-
public $
|
26 |
-
public $
|
27 |
-
public $
|
28 |
-
public $
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
/**
|
37 |
-
*
|
38 |
-
* @
|
39 |
-
*/
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
const
|
121 |
-
const
|
122 |
-
const
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
public $
|
171 |
-
public $
|
172 |
-
public $
|
173 |
-
public $
|
174 |
-
|
175 |
-
public $
|
176 |
-
public $
|
177 |
-
public $
|
178 |
-
public $
|
179 |
-
public $
|
180 |
-
|
181 |
-
public $
|
182 |
-
public $
|
183 |
-
public $
|
184 |
-
|
185 |
-
public $
|
186 |
-
public $
|
187 |
-
public $
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
$
|
226 |
-
$report
|
227 |
-
|
228 |
-
|
229 |
-
$
|
230 |
-
$
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
$report['
|
235 |
-
|
236 |
-
|
237 |
-
$
|
238 |
-
$
|
239 |
-
$
|
240 |
-
$
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
$report['ARC']['
|
246 |
-
$report['ARC']['
|
247 |
-
$report['ARC']['
|
248 |
-
$report['ARC']['
|
249 |
-
$report['ARC']['
|
250 |
-
$report['ARC']['
|
251 |
-
|
252 |
-
$
|
253 |
-
$
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
$
|
259 |
-
|
260 |
-
$
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
$
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
$
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
$
|
290 |
-
$report['
|
291 |
-
|
292 |
-
|
293 |
-
$
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
$validator
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
'errmsg' => __('
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
);
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
-
|
463 |
-
|
464 |
-
|
465 |
-
|
466 |
-
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
-
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
|
490 |
-
|
491 |
-
$
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
@
|
524 |
-
@
|
525 |
-
@
|
526 |
-
@
|
527 |
-
|
528 |
-
@
|
529 |
-
|
530 |
-
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
|
547 |
-
|
548 |
-
$
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
|
554 |
-
}
|
555 |
-
|
556 |
-
/**
|
557 |
-
*
|
558 |
-
*
|
559 |
-
*
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
*
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
|
597 |
-
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
|
605 |
-
|
606 |
-
|
607 |
-
|
608 |
-
|
609 |
-
|
610 |
-
|
611 |
-
|
612 |
-
|
613 |
-
|
614 |
-
|
615 |
-
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
-
|
620 |
-
|
621 |
-
|
622 |
-
|
623 |
-
|
624 |
-
|
625 |
-
|
626 |
-
|
627 |
-
|
628 |
-
|
629 |
-
|
630 |
-
|
631 |
-
|
632 |
-
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
|
637 |
-
|
638 |
-
|
639 |
-
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
-
|
645 |
-
|
646 |
-
|
647 |
-
|
648 |
-
|
649 |
-
|
650 |
-
|
651 |
-
|
652 |
-
$
|
653 |
-
|
654 |
-
|
655 |
-
|
656 |
-
|
657 |
-
|
658 |
-
|
659 |
-
|
660 |
-
|
661 |
-
|
662 |
-
|
663 |
-
|
664 |
-
|
665 |
-
|
666 |
-
|
667 |
-
|
668 |
-
|
669 |
-
|
670 |
-
|
671 |
-
|
672 |
-
|
673 |
-
|
674 |
-
|
675 |
-
|
676 |
-
|
677 |
-
|
678 |
-
|
679 |
-
|
680 |
-
|
681 |
-
|
682 |
-
|
683 |
-
|
684 |
-
|
685 |
-
|
686 |
-
|
687 |
-
|
688 |
-
|
689 |
-
|
690 |
-
|
691 |
-
|
692 |
-
|
693 |
-
|
694 |
-
|
695 |
-
|
696 |
-
|
697 |
-
|
698 |
-
|
699 |
-
|
700 |
-
|
701 |
-
|
702 |
-
|
703 |
-
|
704 |
-
|
705 |
-
|
706 |
-
*
|
707 |
-
*
|
708 |
-
*
|
709 |
-
*
|
710 |
-
*
|
711 |
-
*
|
712 |
-
*
|
713 |
-
*
|
714 |
-
*
|
715 |
-
*
|
716 |
-
* @param int $
|
717 |
-
* @param
|
718 |
-
*
|
719 |
-
*
|
720 |
-
|
721 |
-
|
722 |
-
|
723 |
-
|
724 |
-
|
725 |
-
|
726 |
-
|
727 |
-
|
728 |
-
*
|
729 |
-
*
|
730 |
-
* @param array
|
731 |
-
* relation = 'AND',
|
732 |
-
* [ 'op' => '>=' ,
|
733 |
-
* 'status' => DUP_PackageStatus::START ]
|
734 |
-
* [ 'op' => '<' ,
|
735 |
-
* 'status' => DUP_PackageStatus::COMPLETED ]
|
736 |
-
* ]
|
737 |
-
*
|
738 |
-
|
739 |
-
|
740 |
-
|
741 |
-
|
742 |
-
|
743 |
-
|
744 |
-
|
745 |
-
|
746 |
-
$
|
747 |
-
|
748 |
-
|
749 |
-
|
750 |
-
|
751 |
-
*
|
752 |
-
*
|
753 |
-
*
|
754 |
-
*
|
755 |
-
*
|
756 |
-
*
|
757 |
-
* [ 'op' => '
|
758 |
-
* 'status' => DUP_PackageStatus::
|
759 |
-
*
|
760 |
-
*
|
761 |
-
|
762 |
-
|
763 |
-
|
764 |
-
|
765 |
-
|
766 |
-
|
767 |
-
|
768 |
-
|
769 |
-
|
770 |
-
|
771 |
-
|
772 |
-
|
773 |
-
|
774 |
-
|
775 |
-
|
776 |
-
|
777 |
-
|
778 |
-
|
779 |
-
|
780 |
-
|
781 |
-
|
782 |
-
|
783 |
-
|
784 |
-
|
785 |
-
|
786 |
-
|
787 |
-
|
788 |
-
|
789 |
-
|
790 |
-
|
791 |
-
|
792 |
-
|
793 |
-
|
794 |
-
|
795 |
-
|
796 |
-
|
797 |
-
|
798 |
-
|
799 |
-
|
800 |
-
|
801 |
-
|
802 |
-
|
803 |
-
|
804 |
-
|
805 |
-
|
806 |
-
|
807 |
-
|
808 |
-
|
809 |
-
|
810 |
-
|
811 |
-
|
812 |
-
|
813 |
-
|
814 |
-
|
815 |
-
|
816 |
-
|
817 |
-
|
818 |
-
$
|
819 |
-
|
820 |
-
|
821 |
-
|
822 |
-
|
823 |
-
|
824 |
-
|
825 |
-
|
826 |
-
|
827 |
-
|
828 |
-
|
829 |
-
|
830 |
-
{
|
831 |
-
|
832 |
-
|
833 |
-
|
834 |
-
|
835 |
-
|
836 |
-
|
837 |
-
|
838 |
-
|
839 |
-
|
840 |
-
|
841 |
-
|
842 |
-
|
843 |
-
|
844 |
-
|
845 |
-
|
846 |
-
|
847 |
-
|
848 |
-
|
849 |
-
|
850 |
-
|
851 |
-
|
852 |
-
|
853 |
-
|
854 |
-
|
855 |
-
|
856 |
-
|
857 |
-
DUP_Log::
|
858 |
-
DUP_Log::
|
859 |
-
|
860 |
-
|
861 |
-
|
862 |
-
|
863 |
-
|
864 |
-
$
|
865 |
-
$
|
866 |
-
$
|
867 |
-
|
868 |
-
|
869 |
-
|
870 |
-
|
871 |
-
|
872 |
-
|
873 |
-
|
874 |
-
|
875 |
-
|
876 |
-
|
877 |
-
|
878 |
-
|
879 |
-
|
880 |
-
|
881 |
-
|
882 |
-
|
883 |
-
|
884 |
-
|
885 |
-
|
886 |
-
|
887 |
-
|
888 |
-
|
889 |
-
|
890 |
-
|
891 |
-
|
892 |
-
|
893 |
-
|
894 |
-
|
895 |
-
|
896 |
-
|
897 |
-
|
898 |
-
|
899 |
-
|
900 |
-
|
901 |
-
|
902 |
-
|
903 |
-
|
904 |
-
|
905 |
-
|
906 |
-
|
907 |
-
|
908 |
-
|
909 |
-
|
910 |
-
|
911 |
-
|
912 |
-
|
913 |
-
|
914 |
-
|
915 |
-
|
916 |
-
|
917 |
-
|
918 |
-
|
919 |
-
|
920 |
-
|
921 |
-
|
922 |
-
|
923 |
-
|
924 |
-
|
925 |
-
|
926 |
-
|
927 |
-
|
928 |
-
|
929 |
-
$
|
930 |
-
$
|
931 |
-
|
932 |
-
|
933 |
-
|
934 |
-
|
935 |
-
|
936 |
-
|
937 |
-
$
|
938 |
-
|
939 |
-
|
940 |
-
|
941 |
-
|
942 |
-
|
943 |
-
|
944 |
-
|
945 |
-
|
946 |
-
|
947 |
-
|
948 |
-
|
949 |
-
|
950 |
-
|
951 |
-
|
952 |
-
|
953 |
-
|
954 |
-
|
955 |
-
|
956 |
-
|
957 |
-
|
958 |
-
|
959 |
-
|
960 |
-
|
961 |
-
|
962 |
-
|
963 |
-
|
964 |
-
|
965 |
-
|
966 |
-
|
967 |
-
|
968 |
-
|
969 |
-
|
970 |
-
|
971 |
-
|
972 |
-
|
973 |
-
|
974 |
-
|
975 |
-
|
976 |
-
|
977 |
-
|
978 |
-
|
979 |
-
|
980 |
-
|
981 |
-
|
982 |
-
|
983 |
-
|
984 |
-
|
985 |
-
$
|
986 |
-
|
987 |
-
|
988 |
-
|
989 |
-
|
990 |
-
|
991 |
-
|
992 |
-
|
993 |
-
|
994 |
-
|
995 |
-
|
996 |
-
|
997 |
-
|
998 |
-
|
999 |
-
|
1000 |
-
|
1001 |
-
|
1002 |
-
|
1003 |
-
|
1004 |
-
|
1005 |
-
|
1006 |
-
|
1007 |
-
|
1008 |
-
|
1009 |
-
|
1010 |
-
|
1011 |
-
|
1012 |
-
|
1013 |
-
|
1014 |
-
|
1015 |
-
|
1016 |
-
|
1017 |
-
|
1018 |
-
|
1019 |
-
|
1020 |
-
|
1021 |
-
|
1022 |
-
|
1023 |
-
|
1024 |
-
|
1025 |
-
|
1026 |
-
|
1027 |
-
|
1028 |
-
|
1029 |
-
|
1030 |
-
|
1031 |
-
|
1032 |
-
|
1033 |
-
|
1034 |
-
|
1035 |
-
|
1036 |
-
|
1037 |
-
|
1038 |
-
|
1039 |
-
|
1040 |
-
|
1041 |
-
|
1042 |
-
|
1043 |
-
|
1044 |
-
|
1045 |
-
|
1046 |
-
|
1047 |
-
|
1048 |
-
|
1049 |
-
|
1050 |
-
|
1051 |
-
|
1052 |
-
|
1053 |
-
|
1054 |
-
|
1055 |
-
|
1056 |
-
|
1057 |
-
|
1058 |
-
|
1059 |
-
|
1060 |
-
|
1061 |
-
|
1062 |
-
|
1063 |
-
|
1064 |
-
|
1065 |
-
|
1066 |
-
|
1067 |
-
|
1068 |
-
|
1069 |
-
|
1070 |
-
|
1071 |
-
|
1072 |
-
|
1073 |
-
public function
|
1074 |
-
{
|
1075 |
-
|
1076 |
-
|
1077 |
-
|
1078 |
-
|
1079 |
-
|
1080 |
-
|
1081 |
-
|
1082 |
-
|
1083 |
-
|
1084 |
-
|
1085 |
-
|
1086 |
-
|
1087 |
-
|
1088 |
-
|
1089 |
-
|
1090 |
-
|
1091 |
-
|
1092 |
-
|
1093 |
-
|
1094 |
-
|
1095 |
-
|
1096 |
-
|
1097 |
-
|
1098 |
-
|
1099 |
-
|
1100 |
-
|
1101 |
-
|
1102 |
-
|
1103 |
-
|
1104 |
-
|
1105 |
-
|
1106 |
-
|
1107 |
-
|
1108 |
-
|
1109 |
-
|
1110 |
-
|
1111 |
-
|
1112 |
-
|
1113 |
-
|
1114 |
-
|
1115 |
-
|
1116 |
-
|
1117 |
-
|
1118 |
-
|
1119 |
-
|
1120 |
-
|
1121 |
-
|
1122 |
-
|
1123 |
-
|
1124 |
-
|
1125 |
-
|
1126 |
-
|
1127 |
-
|
1128 |
-
|
1129 |
-
|
1130 |
-
|
1131 |
-
|
1132 |
-
|
1133 |
-
|
1134 |
-
|
1135 |
-
|
1136 |
-
|
1137 |
-
|
1138 |
-
|
1139 |
-
|
1140 |
-
|
1141 |
-
|
1142 |
-
|
1143 |
-
|
1144 |
-
|
1145 |
-
}
|
1146 |
-
|
1147 |
-
|
1148 |
-
|
1149 |
-
|
1150 |
-
|
1151 |
-
|
1152 |
-
|
1153 |
-
|
1154 |
-
|
1155 |
-
|
1156 |
-
|
1157 |
-
|
1158 |
-
|
1159 |
-
|
1160 |
-
|
1161 |
-
|
1162 |
-
|
1163 |
-
|
1164 |
-
|
1165 |
-
|
1166 |
-
|
1167 |
-
|
1168 |
-
|
1169 |
-
|
1170 |
-
|
1171 |
-
|
1172 |
-
|
1173 |
-
|
1174 |
-
|
1175 |
-
|
1176 |
-
|
1177 |
-
|
1178 |
-
|
1179 |
-
|
1180 |
-
|
1181 |
-
$
|
1182 |
-
|
1183 |
-
|
1184 |
-
|
1185 |
-
|
1186 |
-
|
1187 |
-
|
1188 |
-
|
1189 |
-
|
1190 |
-
|
1191 |
-
|
1192 |
-
|
1193 |
-
|
1194 |
-
|
1195 |
-
|
1196 |
-
|
1197 |
-
|
1198 |
-
|
1199 |
-
|
1200 |
-
|
1201 |
-
|
1202 |
-
|
1203 |
-
|
1204 |
-
|
1205 |
-
|
1206 |
-
|
1207 |
-
|
1208 |
-
|
1209 |
-
|
1210 |
-
|
1211 |
-
|
1212 |
-
|
1213 |
-
|
1214 |
-
|
1215 |
-
|
1216 |
-
|
1217 |
-
|
1218 |
-
|
1219 |
-
|
1220 |
-
|
1221 |
-
|
1222 |
-
|
1223 |
-
|
1224 |
-
|
1225 |
-
|
1226 |
-
|
1227 |
-
|
1228 |
-
|
1229 |
-
|
1230 |
-
|
1231 |
-
|
1232 |
-
|
1233 |
-
|
1234 |
-
|
1235 |
-
|
1236 |
-
|
1237 |
-
|
1238 |
-
|
1239 |
-
|
1240 |
-
|
1241 |
-
|
1242 |
-
|
1243 |
-
|
1244 |
-
|
1245 |
-
$
|
1246 |
-
$
|
1247 |
-
|
1248 |
-
|
1249 |
-
|
1250 |
-
|
1251 |
-
|
1252 |
-
|
1253 |
-
|
1254 |
-
|
1255 |
-
$
|
1256 |
-
|
1257 |
-
$
|
1258 |
-
|
1259 |
-
|
1260 |
-
|
1261 |
-
|
1262 |
-
|
1263 |
-
|
1264 |
-
|
1265 |
-
|
1266 |
-
|
1267 |
-
}
|
1268 |
-
|
1269 |
-
|
1270 |
-
|
1271 |
-
|
1272 |
-
|
1273 |
-
|
1274 |
-
|
1275 |
-
|
1276 |
-
|
1277 |
-
|
1278 |
-
|
1279 |
-
|
1280 |
-
|
1281 |
-
|
1282 |
-
$this->
|
1283 |
-
|
1284 |
-
|
1285 |
-
|
1286 |
-
|
1287 |
-
|
1288 |
-
|
1289 |
-
|
1290 |
-
|
1291 |
-
|
1292 |
-
|
1293 |
-
|
1294 |
-
|
1295 |
-
|
1296 |
-
|
1297 |
-
|
1298 |
-
|
1299 |
-
|
1300 |
-
|
1301 |
-
|
1302 |
-
|
1303 |
-
|
1304 |
-
|
1305 |
-
|
1306 |
-
|
1307 |
-
|
1308 |
-
|
1309 |
-
|
1310 |
-
|
1311 |
-
|
1312 |
-
|
1313 |
-
|
1314 |
-
|
1315 |
-
|
1316 |
-
|
1317 |
-
|
1318 |
-
|
1319 |
-
|
1320 |
-
|
1321 |
-
|
1322 |
-
|
1323 |
-
|
1324 |
-
|
1325 |
-
|
1326 |
-
|
1327 |
-
|
1328 |
-
|
1329 |
-
|
1330 |
-
|
1331 |
-
|
1332 |
-
|
1333 |
-
|
1334 |
-
|
1335 |
-
|
1336 |
-
|
1337 |
-
|
1338 |
-
|
1339 |
-
|
1340 |
-
|
1341 |
-
|
1342 |
-
|
1343 |
-
|
1344 |
-
|
1345 |
-
|
1346 |
-
|
1347 |
-
|
1348 |
-
|
1349 |
-
|
1350 |
-
$
|
1351 |
-
|
1352 |
-
|
1353 |
-
|
1354 |
-
|
1355 |
-
|
1356 |
-
|
1357 |
-
|
1358 |
-
|
1359 |
-
|
1360 |
-
|
1361 |
-
|
1362 |
-
|
1363 |
-
|
1364 |
-
|
1365 |
-
|
1366 |
-
|
1367 |
-
|
1368 |
-
|
1369 |
-
|
1370 |
-
|
1371 |
-
|
1372 |
-
|
1373 |
-
|
1374 |
-
|
1375 |
-
|
1376 |
-
|
1377 |
-
|
1378 |
-
|
1379 |
-
|
1380 |
-
|
1381 |
-
|
1382 |
-
|
1383 |
-
|
1384 |
-
|
1385 |
-
|
1386 |
-
|
1387 |
-
|
1388 |
-
|
1389 |
-
|
1390 |
-
|
1391 |
-
|
1392 |
-
|
1393 |
-
|
1394 |
-
|
1395 |
-
|
1396 |
-
|
1397 |
-
|
1398 |
-
|
1399 |
-
|
1400 |
-
|
1401 |
-
|
1402 |
-
|
1403 |
-
|
1404 |
-
|
1405 |
-
|
1406 |
-
|
1407 |
-
|
1408 |
-
|
1409 |
-
|
1410 |
-
|
1411 |
-
|
1412 |
-
|
1413 |
-
|
1414 |
-
|
1415 |
-
|
1416 |
-
|
1417 |
-
|
1418 |
-
$
|
1419 |
-
|
1420 |
-
|
1421 |
-
|
1422 |
-
$
|
1423 |
-
$
|
1424 |
-
$
|
1425 |
-
|
1426 |
-
|
1427 |
-
|
1428 |
-
|
1429 |
-
|
1430 |
-
|
1431 |
-
|
1432 |
-
|
1433 |
-
|
1434 |
-
|
1435 |
-
|
1436 |
-
|
1437 |
-
|
1438 |
-
|
1439 |
-
|
1440 |
-
|
1441 |
-
|
1442 |
-
|
1443 |
-
|
1444 |
-
|
1445 |
-
|
1446 |
-
|
1447 |
-
$
|
1448 |
-
|
1449 |
-
|
1450 |
-
|
1451 |
-
|
1452 |
-
|
1453 |
-
|
1454 |
-
|
1455 |
-
|
1456 |
-
|
1457 |
-
|
1458 |
-
|
1459 |
-
|
1460 |
-
|
1461 |
-
|
1462 |
-
|
1463 |
-
|
1464 |
-
|
1465 |
-
|
1466 |
-
|
1467 |
-
|
1468 |
-
|
1469 |
-
|
1470 |
-
|
1471 |
-
|
1472 |
-
|
1473 |
-
|
1474 |
-
|
1475 |
-
|
1476 |
-
|
1477 |
-
|
1478 |
-
|
1479 |
-
|
1480 |
-
|
1481 |
-
|
1482 |
-
|
1483 |
-
|
1484 |
-
|
1485 |
-
|
1486 |
-
|
1487 |
-
|
1488 |
-
|
1489 |
-
|
1490 |
-
|
1491 |
-
|
1492 |
-
|
1493 |
-
|
1494 |
-
|
1495 |
-
|
1496 |
-
|
1497 |
-
|
1498 |
-
|
1499 |
-
|
1500 |
-
|
1501 |
-
|
1502 |
-
|
1503 |
-
|
1504 |
-
|
1505 |
-
|
1506 |
-
|
1507 |
-
|
1508 |
-
|
1509 |
-
|
1510 |
-
|
1511 |
-
|
1512 |
-
|
1513 |
-
|
1514 |
-
|
1515 |
-
|
1516 |
-
|
1517 |
-
|
1518 |
-
|
1519 |
-
|
1520 |
-
|
1521 |
-
$
|
1522 |
-
$this->
|
1523 |
-
|
1524 |
-
|
1525 |
-
|
1526 |
-
|
1527 |
-
|
1528 |
-
|
1529 |
-
|
1530 |
-
|
1531 |
-
|
1532 |
-
|
1533 |
-
|
1534 |
-
|
1535 |
-
|
1536 |
-
|
1537 |
-
|
1538 |
-
|
1539 |
-
|
1540 |
-
|
1541 |
-
|
1542 |
-
|
1543 |
-
|
1544 |
-
|
1545 |
-
|
1546 |
-
|
1547 |
-
|
1548 |
-
|
1549 |
-
|
1550 |
-
*
|
1551 |
-
|
1552 |
-
|
1553 |
-
|
1554 |
-
|
1555 |
-
|
1556 |
-
|
1557 |
-
|
1558 |
-
|
1559 |
-
|
1560 |
-
|
1561 |
-
|
1562 |
-
|
1563 |
-
|
1564 |
-
|
1565 |
-
|
1566 |
-
|
1567 |
-
*
|
1568 |
-
*
|
1569 |
-
*
|
1570 |
-
*
|
1571 |
-
*
|
1572 |
-
|
1573 |
-
|
1574 |
-
|
1575 |
-
|
1576 |
-
|
1577 |
-
|
1578 |
-
$
|
1579 |
-
|
1580 |
-
|
1581 |
-
|
1582 |
-
|
1583 |
-
|
1584 |
-
|
1585 |
-
|
1586 |
-
}
|
1587 |
-
|
1588 |
-
/**
|
1589 |
-
*
|
1590 |
-
*
|
1591 |
-
* @
|
1592 |
-
|
1593 |
-
|
1594 |
-
|
1595 |
-
|
1596 |
-
|
1597 |
-
|
1598 |
-
|
1599 |
-
|
1600 |
-
|
1601 |
-
|
1602 |
-
|
1603 |
-
|
1604 |
-
|
1605 |
-
|
1606 |
-
|
1607 |
-
|
1608 |
-
|
1609 |
-
|
1610 |
-
|
1611 |
-
|
1612 |
-
|
1613 |
-
|
1614 |
-
|
1615 |
-
|
1616 |
-
|
1617 |
-
|
1618 |
-
|
1619 |
-
|
1620 |
-
|
1621 |
-
|
1622 |
-
|
1623 |
-
|
1624 |
-
|
1625 |
-
|
1626 |
-
|
1627 |
-
|
1628 |
-
|
1629 |
-
|
1630 |
-
|
1631 |
-
|
1632 |
-
*
|
1633 |
-
*
|
1634 |
-
*
|
1635 |
-
|
1636 |
-
|
1637 |
-
|
1638 |
-
|
1639 |
-
|
1640 |
-
|
1641 |
-
|
1642 |
-
|
1643 |
-
|
1644 |
-
|
1645 |
-
|
1646 |
-
|
1647 |
-
|
1648 |
-
|
1649 |
-
|
1650 |
-
|
1651 |
-
|
1652 |
-
|
1653 |
-
|
1654 |
-
|
1655 |
-
|
1656 |
-
|
1657 |
-
|
1658 |
-
|
1659 |
-
|
1660 |
-
|
1661 |
-
|
1662 |
-
|
1663 |
-
|
1664 |
-
|
1665 |
-
|
1666 |
-
|
1667 |
-
|
1668 |
-
$
|
1669 |
-
|
1670 |
-
|
1671 |
-
|
1672 |
-
|
1673 |
-
|
1674 |
-
|
1675 |
-
|
1676 |
-
|
1677 |
-
|
1678 |
-
|
1679 |
-
|
1680 |
-
|
1681 |
-
|
1682 |
-
|
1683 |
-
|
1684 |
-
|
1685 |
-
|
1686 |
-
|
1687 |
-
|
1688 |
-
|
1689 |
-
|
1690 |
-
|
1691 |
-
|
1692 |
-
|
1693 |
-
|
1694 |
-
|
1695 |
-
|
1696 |
-
|
1697 |
-
|
1698 |
-
|
1699 |
-
|
1700 |
-
|
1701 |
-
|
1702 |
-
|
1703 |
-
|
1704 |
-
*
|
1705 |
-
*/
|
1706 |
-
public function
|
1707 |
-
{
|
1708 |
-
$
|
1709 |
-
$
|
1710 |
-
|
1711 |
-
|
1712 |
-
|
1713 |
-
|
1714 |
-
|
1715 |
-
|
1716 |
-
|
1717 |
-
|
1718 |
-
|
1719 |
-
|
1720 |
-
|
1721 |
-
|
1722 |
-
|
1723 |
-
|
1724 |
-
|
1725 |
-
|
1726 |
-
|
1727 |
-
|
1728 |
-
|
1729 |
-
|
1730 |
-
|
1731 |
-
|
1732 |
-
|
1733 |
-
|
1734 |
-
|
1735 |
-
|
1736 |
-
|
1737 |
-
|
1738 |
-
|
1739 |
-
|
1740 |
-
|
1741 |
-
|
1742 |
-
|
1743 |
-
|
1744 |
-
|
1745 |
-
|
1746 |
-
|
1747 |
-
|
1748 |
-
|
1749 |
-
|
1750 |
-
|
1751 |
-
|
1752 |
-
|
1753 |
-
}
|
1754 |
-
|
1755 |
-
|
1756 |
-
|
1757 |
-
|
1758 |
-
|
1759 |
-
|
1760 |
-
$
|
1761 |
-
$
|
1762 |
-
|
1763 |
-
$
|
1764 |
-
|
1765 |
-
|
1766 |
-
|
1767 |
-
|
1768 |
-
|
1769 |
-
|
1770 |
-
|
1771 |
-
|
1772 |
-
|
1773 |
-
|
1774 |
-
|
1775 |
-
|
1776 |
-
|
1777 |
-
|
1778 |
-
|
1779 |
-
|
1780 |
-
|
1781 |
-
|
1782 |
-
|
1783 |
-
|
1784 |
-
|
1785 |
-
|
1786 |
-
|
1787 |
-
|
1788 |
-
|
1789 |
-
|
1790 |
-
|
1791 |
-
|
1792 |
-
|
1793 |
-
|
1794 |
-
|
1795 |
-
|
1796 |
-
|
1797 |
-
|
1798 |
-
|
1799 |
-
$
|
1800 |
-
$
|
1801 |
-
|
1802 |
-
|
1803 |
-
$
|
1804 |
-
|
1805 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
use Duplicator\Libs\Snap\SnapJson;
|
4 |
+
use Duplicator\Libs\Snap\SnapUtil;
|
5 |
+
|
6 |
+
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
7 |
+
// Exit if accessed directly
|
8 |
+
if (! defined('DUPLICATOR_VERSION')) {
|
9 |
+
exit;
|
10 |
+
}
|
11 |
+
|
12 |
+
require_once(DUPLICATOR_PLUGIN_PATH . 'classes/utilities/class.u.php');
|
13 |
+
require_once(DUPLICATOR_PLUGIN_PATH . 'classes/package/class.pack.archive.php');
|
14 |
+
require_once(DUPLICATOR_PLUGIN_PATH . 'classes/package/class.pack.installer.php');
|
15 |
+
require_once(DUPLICATOR_PLUGIN_PATH . 'classes/package/class.pack.database.php');
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Class used to keep track of the build progress
|
19 |
+
*
|
20 |
+
* @package Duplicator\classes
|
21 |
+
*/
|
22 |
+
class DUP_Build_Progress
|
23 |
+
{
|
24 |
+
public $thread_start_time;
|
25 |
+
public $initialized = false;
|
26 |
+
public $installer_built = false;
|
27 |
+
public $archive_started = false;
|
28 |
+
public $archive_has_database = false;
|
29 |
+
public $archive_built = false;
|
30 |
+
public $database_script_built = false;
|
31 |
+
public $failed = false;
|
32 |
+
public $retries = 0;
|
33 |
+
public $build_failures = array();
|
34 |
+
public $validation_failures = array();
|
35 |
+
|
36 |
+
/**
|
37 |
+
*
|
38 |
+
* @var DUP_Package
|
39 |
+
*/
|
40 |
+
private $package;
|
41 |
+
|
42 |
+
/**
|
43 |
+
*
|
44 |
+
* @param DUP_Package $package
|
45 |
+
*/
|
46 |
+
public function __construct($package)
|
47 |
+
{
|
48 |
+
$this->package = $package;
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
*
|
53 |
+
* @return bool
|
54 |
+
*/
|
55 |
+
public function has_completed()
|
56 |
+
{
|
57 |
+
return $this->failed || ($this->installer_built && $this->archive_built && $this->database_script_built);
|
58 |
+
}
|
59 |
+
|
60 |
+
public function timed_out($max_time)
|
61 |
+
{
|
62 |
+
if ($max_time > 0) {
|
63 |
+
$time_diff = time() - $this->thread_start_time;
|
64 |
+
return ($time_diff >= $max_time);
|
65 |
+
} else {
|
66 |
+
return false;
|
67 |
+
}
|
68 |
+
}
|
69 |
+
|
70 |
+
public function start_timer()
|
71 |
+
{
|
72 |
+
$this->thread_start_time = time();
|
73 |
+
}
|
74 |
+
|
75 |
+
public function set_validation_failures($failures)
|
76 |
+
{
|
77 |
+
$this->validation_failures = array();
|
78 |
+
|
79 |
+
foreach ($failures as $failure) {
|
80 |
+
$this->validation_failures[] = $failure;
|
81 |
+
}
|
82 |
+
}
|
83 |
+
|
84 |
+
public function set_build_failures($failures)
|
85 |
+
{
|
86 |
+
$this->build_failures = array();
|
87 |
+
|
88 |
+
foreach ($failures as $failure) {
|
89 |
+
$this->build_failures[] = $failure->description;
|
90 |
+
}
|
91 |
+
}
|
92 |
+
|
93 |
+
public function set_failed($failure_message = null)
|
94 |
+
{
|
95 |
+
if ($failure_message !== null) {
|
96 |
+
$failure = new StdClass();
|
97 |
+
$failure->type = 0;
|
98 |
+
$failure->subject = '';
|
99 |
+
$failure->description = $failure_message;
|
100 |
+
$failure->isCritical = true;
|
101 |
+
$this->build_failures[] = $failure;
|
102 |
+
}
|
103 |
+
|
104 |
+
$this->failed = true;
|
105 |
+
$this->package->Status = DUP_PackageStatus::ERROR;
|
106 |
+
}
|
107 |
+
}
|
108 |
+
|
109 |
+
/**
|
110 |
+
* Class used to emulate and ENUM to give the status of a package from 0 to 100%
|
111 |
+
*
|
112 |
+
* @package Duplicator\classes
|
113 |
+
*/
|
114 |
+
final class DUP_PackageStatus
|
115 |
+
{
|
116 |
+
private function __construct()
|
117 |
+
{
|
118 |
+
}
|
119 |
+
|
120 |
+
const ERROR = -1;
|
121 |
+
const CREATED = 0;
|
122 |
+
const START = 10;
|
123 |
+
const DBSTART = 20;
|
124 |
+
const DBDONE = 30;
|
125 |
+
const ARCSTART = 40;
|
126 |
+
const ARCVALIDATION = 60;
|
127 |
+
const ARCDONE = 65;
|
128 |
+
const COMPLETE = 100;
|
129 |
+
}
|
130 |
+
|
131 |
+
/**
|
132 |
+
* Class used to emulate and ENUM to determine how the package was made.
|
133 |
+
* For lite only the MANUAL type is used.
|
134 |
+
*
|
135 |
+
* @package Duplicator\classes
|
136 |
+
*/
|
137 |
+
final class DUP_PackageType
|
138 |
+
{
|
139 |
+
const MANUAL = 0;
|
140 |
+
const SCHEDULED = 1;
|
141 |
+
}
|
142 |
+
|
143 |
+
/**
|
144 |
+
* Class used to emulate and ENUM to determine the various file types used in a package
|
145 |
+
*
|
146 |
+
* @package Duplicator\classes
|
147 |
+
*/
|
148 |
+
abstract class DUP_PackageFileType
|
149 |
+
{
|
150 |
+
const Installer = 0;
|
151 |
+
const Archive = 1;
|
152 |
+
const SQL = 2;
|
153 |
+
const Log = 3;
|
154 |
+
const Scan = 4;
|
155 |
+
}
|
156 |
+
|
157 |
+
/**
|
158 |
+
* Class used to store and process all Package logic
|
159 |
+
*
|
160 |
+
* Standard: PSR-2
|
161 |
+
* @link http://www.php-fig.org/psr/psr-2
|
162 |
+
*
|
163 |
+
* @package Duplicator\classes
|
164 |
+
*/
|
165 |
+
class DUP_Package
|
166 |
+
{
|
167 |
+
const OPT_ACTIVE = 'duplicator_package_active';
|
168 |
+
|
169 |
+
//Properties
|
170 |
+
public $Created;
|
171 |
+
public $Version;
|
172 |
+
public $VersionWP;
|
173 |
+
public $VersionDB;
|
174 |
+
public $VersionPHP;
|
175 |
+
public $VersionOS;
|
176 |
+
public $ID;
|
177 |
+
public $Name;
|
178 |
+
public $Hash;
|
179 |
+
public $NameHash;
|
180 |
+
//Set to DUP_PackageType
|
181 |
+
public $Type;
|
182 |
+
public $Notes;
|
183 |
+
public $ScanFile;
|
184 |
+
public $TimerStart = -1;
|
185 |
+
public $Runtime;
|
186 |
+
public $ExeSize;
|
187 |
+
public $ZipSize;
|
188 |
+
public $Status;
|
189 |
+
public $WPUser;
|
190 |
+
/** @var DUP_Archive */
|
191 |
+
public $Archive;
|
192 |
+
/** @var DUP_Installer */
|
193 |
+
public $Installer;
|
194 |
+
/** @var DUP_Database */
|
195 |
+
public $Database;
|
196 |
+
/** @var DUP_Build_Progress */
|
197 |
+
public $BuildProgress;
|
198 |
+
|
199 |
+
/**
|
200 |
+
* Manages the Package Process
|
201 |
+
*/
|
202 |
+
public function __construct()
|
203 |
+
{
|
204 |
+
$this->ID = null;
|
205 |
+
$this->Version = DUPLICATOR_VERSION;
|
206 |
+
$this->Type = DUP_PackageType::MANUAL;
|
207 |
+
$this->Name = self::getDefaultName();
|
208 |
+
$this->Notes = null;
|
209 |
+
$this->Database = new DUP_Database($this);
|
210 |
+
$this->Archive = new DUP_Archive($this);
|
211 |
+
$this->Installer = new DUP_Installer($this);
|
212 |
+
$this->BuildProgress = new DUP_Build_Progress($this);
|
213 |
+
$this->Status = DUP_PackageStatus::CREATED;
|
214 |
+
}
|
215 |
+
|
216 |
+
/**
|
217 |
+
* Generates a JSON scan report
|
218 |
+
*
|
219 |
+
* @return array of scan results
|
220 |
+
*
|
221 |
+
* @notes: Testing = /wp-admin/admin-ajax.php?action=duplicator_package_scan
|
222 |
+
*/
|
223 |
+
public function runScanner()
|
224 |
+
{
|
225 |
+
$timerStart = DUP_Util::getMicrotime();
|
226 |
+
$report = array();
|
227 |
+
$this->ScanFile = "{$this->NameHash}_scan.json";
|
228 |
+
|
229 |
+
$report['RPT']['ScanTime'] = "0";
|
230 |
+
$report['RPT']['ScanFile'] = $this->ScanFile;
|
231 |
+
|
232 |
+
//SERVER
|
233 |
+
$srv = DUP_Server::getChecks();
|
234 |
+
$report['SRV'] = $srv['SRV'];
|
235 |
+
|
236 |
+
//FILES
|
237 |
+
$this->Archive->getScannerData();
|
238 |
+
$dirCount = count($this->Archive->Dirs);
|
239 |
+
$fileCount = count($this->Archive->Files);
|
240 |
+
$fullCount = $dirCount + $fileCount;
|
241 |
+
|
242 |
+
$report['ARC']['Size'] = DUP_Util::byteSize($this->Archive->Size) or "unknown";
|
243 |
+
$report['ARC']['DirCount'] = number_format($dirCount);
|
244 |
+
$report['ARC']['FileCount'] = number_format($fileCount);
|
245 |
+
$report['ARC']['FullCount'] = number_format($fullCount);
|
246 |
+
$report['ARC']['WarnFileCount'] = count($this->Archive->FilterInfo->Files->Warning);
|
247 |
+
$report['ARC']['WarnDirCount'] = count($this->Archive->FilterInfo->Dirs->Warning);
|
248 |
+
$report['ARC']['UnreadableDirCount'] = count($this->Archive->FilterInfo->Dirs->Unreadable);
|
249 |
+
$report['ARC']['UnreadableFileCount'] = count($this->Archive->FilterInfo->Files->Unreadable);
|
250 |
+
$report['ARC']['FilterDirsAll'] = $this->Archive->FilterDirsAll;
|
251 |
+
$report['ARC']['FilterFilesAll'] = $this->Archive->FilterFilesAll;
|
252 |
+
$report['ARC']['FilterExtsAll'] = $this->Archive->FilterExtsAll;
|
253 |
+
$report['ARC']['FilterInfo'] = $this->Archive->FilterInfo;
|
254 |
+
$report['ARC']['RecursiveLinks'] = $this->Archive->RecursiveLinks;
|
255 |
+
$report['ARC']['UnreadableItems'] = array_merge($this->Archive->FilterInfo->Files->Unreadable, $this->Archive->FilterInfo->Dirs->Unreadable);
|
256 |
+
$report['ARC']['Status']['Size'] = ($this->Archive->Size > DUPLICATOR_SCAN_SIZE_DEFAULT) ? 'Warn' : 'Good';
|
257 |
+
$report['ARC']['Status']['Names'] = (count($this->Archive->FilterInfo->Files->Warning) + count($this->Archive->FilterInfo->Dirs->Warning)) ? 'Warn' : 'Good';
|
258 |
+
$report['ARC']['Status']['UnreadableItems'] = !empty($this->Archive->RecursiveLinks) || !empty($report['ARC']['UnreadableItems']) ? 'Warn' : 'Good';
|
259 |
+
/*
|
260 |
+
$overwriteInstallerParams = apply_filters('duplicator_overwrite_params_data', array());
|
261 |
+
$package_can_be_migrate = !(isset($overwriteInstallerParams['mode_chunking']['value'])
|
262 |
+
&& $overwriteInstallerParams['mode_chunking']['value'] == 3
|
263 |
+
&& isset($overwriteInstallerParams['mode_chunking']['formStatus'])
|
264 |
+
&& $overwriteInstallerParams['mode_chunking']['formStatus'] == 'st_infoonly');
|
265 |
+
*/
|
266 |
+
$package_can_be_migrate = true;
|
267 |
+
$report['ARC']['Status']['MigratePackage'] = $package_can_be_migrate ? 'Good' : 'Warn';
|
268 |
+
$report['ARC']['Status']['CanbeMigratePackage'] = $package_can_be_migrate;
|
269 |
+
|
270 |
+
$privileges_to_show_create_proc_func = true;
|
271 |
+
$procedures = $GLOBALS['wpdb']->get_col("SHOW PROCEDURE STATUS WHERE `Db` = '" . $GLOBALS['wpdb']->dbname . "'", 1);
|
272 |
+
if (count($procedures)) {
|
273 |
+
$create = $GLOBALS['wpdb']->get_row("SHOW CREATE PROCEDURE `" . $procedures[0] . "`", ARRAY_N);
|
274 |
+
$privileges_to_show_create_proc_func = isset($create[2]);
|
275 |
+
}
|
276 |
+
|
277 |
+
$functions = $GLOBALS['wpdb']->get_col("SHOW FUNCTION STATUS WHERE `Db` = '" . $GLOBALS['wpdb']->dbname . "'", 1);
|
278 |
+
if (count($functions)) {
|
279 |
+
$create = $GLOBALS['wpdb']->get_row("SHOW CREATE FUNCTION `" . $functions[0] . "`", ARRAY_N);
|
280 |
+
$privileges_to_show_create_proc_func = $privileges_to_show_create_proc_func && isset($create[2]);
|
281 |
+
}
|
282 |
+
|
283 |
+
$privileges_to_show_create_proc_func = apply_filters('duplicator_privileges_to_show_create_proc_func', $privileges_to_show_create_proc_func);
|
284 |
+
$report['ARC']['Status']['showCreateProcFuncStatus'] = $privileges_to_show_create_proc_func ? 'Good' : 'Warn';
|
285 |
+
$report['ARC']['Status']['showCreateProcFunc'] = $privileges_to_show_create_proc_func;
|
286 |
+
|
287 |
+
//$report['ARC']['Status']['Big'] = count($this->Archive->FilterInfo->Files->Size) ? 'Warn' : 'Good';
|
288 |
+
$report['ARC']['Dirs'] = $this->Archive->Dirs;
|
289 |
+
$report['ARC']['Files'] = $this->Archive->Files;
|
290 |
+
$report['ARC']['Status']['AddonSites'] = count($this->Archive->FilterInfo->Dirs->AddonSites) ? 'Warn' : 'Good';
|
291 |
+
|
292 |
+
//DATABASE
|
293 |
+
$db = $this->Database->getScannerData();
|
294 |
+
$report['DB'] = $db;
|
295 |
+
|
296 |
+
//Lite Limits
|
297 |
+
$rawTotalSize = $this->Archive->Size + $report['DB']['RawSize'];
|
298 |
+
$report['LL']['TotalSize'] = DUP_Util::byteSize($rawTotalSize);
|
299 |
+
$report['LL']['Status']['TotalSize'] = ($rawTotalSize > DUPLICATOR_MAX_DUPARCHIVE_SIZE) ? 'Fail' : 'Good';
|
300 |
+
|
301 |
+
$warnings = array(
|
302 |
+
$report['SRV']['SYS']['ALL'],
|
303 |
+
$report['SRV']['WP']['ALL'],
|
304 |
+
$report['ARC']['Status']['Size'],
|
305 |
+
$report['ARC']['Status']['Names'],
|
306 |
+
$db['Status']['DB_Size'],
|
307 |
+
$db['Status']['DB_Rows']
|
308 |
+
);
|
309 |
+
|
310 |
+
//array_count_values will throw a warning message if it has null values,
|
311 |
+
//so lets replace all nulls with empty string
|
312 |
+
foreach ($warnings as $i => $value) {
|
313 |
+
if (is_null($value)) {
|
314 |
+
$warnings[$i] = '';
|
315 |
+
}
|
316 |
+
}
|
317 |
+
|
318 |
+
$warn_counts = is_array($warnings) ? array_count_values($warnings) : 0;
|
319 |
+
$report['RPT']['Warnings'] = is_null($warn_counts['Warn']) ? 0 : $warn_counts['Warn'];
|
320 |
+
$report['RPT']['Success'] = is_null($warn_counts['Good']) ? 0 : $warn_counts['Good'];
|
321 |
+
$report['RPT']['ScanTime'] = DUP_Util::elapsedTime(DUP_Util::getMicrotime(), $timerStart);
|
322 |
+
$fp = fopen(DUP_Settings::getSsdirTmpPath() . "/{$this->ScanFile}", 'w');
|
323 |
+
|
324 |
+
fwrite($fp, SnapJson::jsonEncodePPrint($report));
|
325 |
+
fclose($fp);
|
326 |
+
|
327 |
+
return $report;
|
328 |
+
}
|
329 |
+
|
330 |
+
/**
|
331 |
+
* Validates the inputs from the UI for correct data input
|
332 |
+
*
|
333 |
+
* @return DUP_Validator
|
334 |
+
*/
|
335 |
+
public function validateInputs()
|
336 |
+
{
|
337 |
+
$validator = new DUP_Validator();
|
338 |
+
|
339 |
+
$validator->filter_custom(
|
340 |
+
$this->Name,
|
341 |
+
DUP_Validator::FILTER_VALIDATE_NOT_EMPTY,
|
342 |
+
array( 'valkey' => 'Name' ,
|
343 |
+
'errmsg' => __('Package name can\'t be empty', 'duplicator'),
|
344 |
+
)
|
345 |
+
);
|
346 |
+
|
347 |
+
$validator->explode_filter_custom(
|
348 |
+
$this->Archive->FilterDirs,
|
349 |
+
';',
|
350 |
+
DUP_Validator::FILTER_VALIDATE_FOLDER,
|
351 |
+
array( 'valkey' => 'FilterDirs' ,
|
352 |
+
'errmsg' => __('Directories: <b>%1$s</b> isn\'t a valid path', 'duplicator'),
|
353 |
+
)
|
354 |
+
);
|
355 |
+
|
356 |
+
$validator->explode_filter_custom(
|
357 |
+
$this->Archive->FilterExts,
|
358 |
+
';',
|
359 |
+
DUP_Validator::FILTER_VALIDATE_FILE_EXT,
|
360 |
+
array( 'valkey' => 'FilterExts' ,
|
361 |
+
'errmsg' => __('File extension: <b>%1$s</b> isn\'t a valid extension', 'duplicator'),
|
362 |
+
)
|
363 |
+
);
|
364 |
+
|
365 |
+
$validator->explode_filter_custom(
|
366 |
+
$this->Archive->FilterFiles,
|
367 |
+
';',
|
368 |
+
DUP_Validator::FILTER_VALIDATE_FILE,
|
369 |
+
array( 'valkey' => 'FilterFiles' ,
|
370 |
+
'errmsg' => __('Files: <b>%1$s</b> isn\'t a valid file name', 'duplicator'),
|
371 |
+
)
|
372 |
+
);
|
373 |
+
|
374 |
+
//FILTER_VALIDATE_DOMAIN throws notice message on PHP 5.6
|
375 |
+
if (defined('FILTER_VALIDATE_DOMAIN')) {
|
376 |
+
$validator->filter_var($this->Installer->OptsDBHost, FILTER_VALIDATE_DOMAIN, array(
|
377 |
+
'valkey' => 'OptsDBHost' ,
|
378 |
+
'errmsg' => __('MySQL Server Host: <b>%1$s</b> isn\'t a valid host', 'duplicator'),
|
379 |
+
'acc_vals' => array(
|
380 |
+
'' ,
|
381 |
+
'localhost'
|
382 |
+
)
|
383 |
+
));
|
384 |
+
}
|
385 |
+
|
386 |
+
$validator->filter_var($this->Installer->OptsDBPort, FILTER_VALIDATE_INT, array(
|
387 |
+
'valkey' => 'OptsDBPort' ,
|
388 |
+
'errmsg' => __('MySQL Server Port: <b>%1$s</b> isn\'t a valid port', 'duplicator'),
|
389 |
+
'acc_vals' => array(
|
390 |
+
''
|
391 |
+
),
|
392 |
+
'options' => array(
|
393 |
+
'min_range' => 0
|
394 |
+
)
|
395 |
+
));
|
396 |
+
|
397 |
+
return $validator;
|
398 |
+
}
|
399 |
+
|
400 |
+
/**
|
401 |
+
*
|
402 |
+
* @return string
|
403 |
+
*/
|
404 |
+
public function getInstDownloadName($onlySecureName = false)
|
405 |
+
{
|
406 |
+
$mode = ($onlySecureName)
|
407 |
+
? DUP_Settings::INSTALLER_NAME_MODE_WITH_HASH
|
408 |
+
: DUP_Settings::Get('installer_name_mode');
|
409 |
+
|
410 |
+
switch ($mode) {
|
411 |
+
case DUP_Settings::INSTALLER_NAME_MODE_SIMPLE:
|
412 |
+
$name = DUP_Installer::DEFAULT_INSTALLER_FILE_NAME_WITHOUT_HASH . DUP_Installer::INSTALLER_SERVER_EXTENSION;
|
413 |
+
break;
|
414 |
+
case DUP_Settings::INSTALLER_NAME_MODE_WITH_HASH:
|
415 |
+
default:
|
416 |
+
$name = basename($this->getLocalPackageFile(DUP_PackageFileType::Installer));
|
417 |
+
break;
|
418 |
+
}
|
419 |
+
$info = pathinfo($name);
|
420 |
+
return $info['filename'];
|
421 |
+
}
|
422 |
+
|
423 |
+
/**
|
424 |
+
*
|
425 |
+
* @return bool return true if package is a active_package_id and status is bewteen 0 and 100
|
426 |
+
*/
|
427 |
+
public function isRunning()
|
428 |
+
{
|
429 |
+
return DUP_Settings::Get('active_package_id') == $this->ID && $this->Status >= 0 && $this->Status < 100;
|
430 |
+
}
|
431 |
+
|
432 |
+
protected function cleanObjectBeforeSave()
|
433 |
+
{
|
434 |
+
$this->Archive->FilterInfo->reset();
|
435 |
+
}
|
436 |
+
|
437 |
+
/**
|
438 |
+
* Saves the active package to the package table
|
439 |
+
*
|
440 |
+
* @return void
|
441 |
+
*/
|
442 |
+
public function save($extension)
|
443 |
+
{
|
444 |
+
global $wpdb;
|
445 |
+
|
446 |
+
$this->Archive->Format = strtoupper($extension);
|
447 |
+
$this->Archive->File = "{$this->NameHash}_archive.{$extension}";
|
448 |
+
$this->Installer->File = apply_filters(
|
449 |
+
'duplicator_installer_file_path',
|
450 |
+
"{$this->NameHash}_installer" . DUP_Installer::INSTALLER_SERVER_EXTENSION
|
451 |
+
);
|
452 |
+
$this->Database->File = "{$this->NameHash}_database.sql";
|
453 |
+
$current_user = wp_get_current_user();
|
454 |
+
$this->WPUser = isset($current_user->user_login) ? $current_user->user_login : 'unknown';
|
455 |
+
|
456 |
+
//START LOGGING
|
457 |
+
DUP_Log::Open($this->NameHash);
|
458 |
+
|
459 |
+
do_action('duplicator_lite_build_before_start', $this);
|
460 |
+
|
461 |
+
$this->writeLogHeader();
|
462 |
+
|
463 |
+
//CREATE DB RECORD
|
464 |
+
$this->cleanObjectBeforeSave();
|
465 |
+
$packageObj = serialize($this);
|
466 |
+
if (!$packageObj) {
|
467 |
+
DUP_Log::error("Unable to serialize package object while building record.");
|
468 |
+
}
|
469 |
+
|
470 |
+
$this->ID = $this->getHashKey($this->Hash);
|
471 |
+
|
472 |
+
if ($this->ID != 0) {
|
473 |
+
DUP_LOG::Trace("ID non zero so setting to start");
|
474 |
+
$this->setStatus(DUP_PackageStatus::START);
|
475 |
+
} else {
|
476 |
+
DUP_LOG::Trace("ID IS zero so creating another package");
|
477 |
+
$tablePrefix = DUP_Util::getTablePrefix();
|
478 |
+
$results = $wpdb->insert($tablePrefix . "duplicator_packages", array(
|
479 |
+
'name' => $this->Name,
|
480 |
+
'hash' => $this->Hash,
|
481 |
+
'status' => DUP_PackageStatus::START,
|
482 |
+
'created' => current_time('mysql', get_option('gmt_offset', 1)),
|
483 |
+
'owner' => isset($current_user->user_login) ? $current_user->user_login : 'unknown',
|
484 |
+
'package' => $packageObj));
|
485 |
+
if ($results === false) {
|
486 |
+
$wpdb->print_error();
|
487 |
+
DUP_LOG::Trace("Problem inserting package: {$wpdb->last_error}");
|
488 |
+
|
489 |
+
DUP_Log::error("Duplicator is unable to insert a package record into the database table.", "'{$wpdb->last_error}'");
|
490 |
+
}
|
491 |
+
$this->ID = $wpdb->insert_id;
|
492 |
+
}
|
493 |
+
|
494 |
+
do_action('duplicator_lite_build_start', $this);
|
495 |
+
}
|
496 |
+
|
497 |
+
/**
|
498 |
+
* Delete all files associated with this package ID
|
499 |
+
*
|
500 |
+
* @return void
|
501 |
+
*/
|
502 |
+
public function delete()
|
503 |
+
{
|
504 |
+
global $wpdb;
|
505 |
+
|
506 |
+
$tablePrefix = DUP_Util::getTablePrefix();
|
507 |
+
$tblName = $tablePrefix . 'duplicator_packages';
|
508 |
+
$getResult = $wpdb->get_results($wpdb->prepare("SELECT name, hash FROM `{$tblName}` WHERE id = %d", $this->ID), ARRAY_A);
|
509 |
+
|
510 |
+
if ($getResult) {
|
511 |
+
$row = $getResult[0];
|
512 |
+
$nameHash = "{$row['name']}_{$row['hash']}";
|
513 |
+
$delResult = $wpdb->query($wpdb->prepare("DELETE FROM `{$tblName}` WHERE id = %d", $this->ID));
|
514 |
+
|
515 |
+
if ($delResult != 0) {
|
516 |
+
$tmpPath = DUP_Settings::getSsdirTmpPath();
|
517 |
+
$ssdPath = DUP_Settings::getSsdirPath();
|
518 |
+
|
519 |
+
$archiveFile = $this->getArchiveFilename();
|
520 |
+
$wpConfigFile = "{$this->NameHash}_wp-config.txt";
|
521 |
+
|
522 |
+
//Perms
|
523 |
+
@chmod($tmpPath . "/{$archiveFile}", 0644);
|
524 |
+
@chmod($tmpPath . "/{$nameHash}_database.sql", 0644);
|
525 |
+
@chmod($tmpPath . "/{$nameHash}_installer" . DUP_Installer::INSTALLER_SERVER_EXTENSION, 0644);
|
526 |
+
@chmod($tmpPath . "/{$nameHash}_scan.json", 0644);
|
527 |
+
@chmod($tmpPath . "/{$wpConfigFile}", 0644);
|
528 |
+
@chmod($tmpPath . "/{$nameHash}.log", 0644);
|
529 |
+
|
530 |
+
@chmod($ssdPath . "/{$archiveFile}", 0644);
|
531 |
+
@chmod($ssdPath . "/{$nameHash}_database.sql", 0644);
|
532 |
+
@chmod($ssdPath . "/{$nameHash}_installer" . DUP_Installer::INSTALLER_SERVER_EXTENSION, 0644);
|
533 |
+
@chmod($ssdPath . "/{$nameHash}_scan.json", 0644);
|
534 |
+
// In older version, The plugin was storing [HASH]_wp-config.txt in main storage area. The below line code is for backward compatibility
|
535 |
+
@chmod($ssdPath . "/{$wpConfigFile}", 0644);
|
536 |
+
@chmod($ssdPath . "/{$nameHash}.log", 0644);
|
537 |
+
//Remove
|
538 |
+
@unlink($tmpPath . "/{$archiveFile}");
|
539 |
+
@unlink($tmpPath . "/{$nameHash}_database.sql");
|
540 |
+
@unlink($tmpPath . "/{$nameHash}_installer" . DUP_Installer::INSTALLER_SERVER_EXTENSION);
|
541 |
+
@unlink($tmpPath . "/{$nameHash}_scan.json");
|
542 |
+
@unlink($tmpPath . "/{$wpConfigFile}");
|
543 |
+
@unlink($tmpPath . "/{$nameHash}.log");
|
544 |
+
|
545 |
+
@unlink($ssdPath . "/{$archiveFile}");
|
546 |
+
@unlink($ssdPath . "/{$nameHash}_database.sql");
|
547 |
+
@unlink($ssdPath . "/{$nameHash}_installer" . DUP_Installer::INSTALLER_SERVER_EXTENSION);
|
548 |
+
@unlink($ssdPath . "/{$nameHash}_scan.json");
|
549 |
+
// In older version, The plugin was storing [HASH]_wp-config.txt in main storage area. The below line code is for backward compatibility
|
550 |
+
@unlink($ssdPath . "/{$wpConfigFile}");
|
551 |
+
@unlink($ssdPath . "/{$nameHash}.log");
|
552 |
+
}
|
553 |
+
}
|
554 |
+
}
|
555 |
+
|
556 |
+
/**
|
557 |
+
* Get package archive size.
|
558 |
+
* If package isn't complete it get size from sum of temp files.
|
559 |
+
*
|
560 |
+
* @return int size in byte
|
561 |
+
*/
|
562 |
+
public function getArchiveSize()
|
563 |
+
{
|
564 |
+
$size = 0;
|
565 |
+
|
566 |
+
if ($this->Status >= DUP_PackageStatus::COMPLETE) {
|
567 |
+
$size = $this->Archive->Size;
|
568 |
+
} else {
|
569 |
+
$tmpSearch = glob(DUP_Settings::getSsdirTmpPath() . "/{$this->NameHash}_*");
|
570 |
+
if (is_array($tmpSearch)) {
|
571 |
+
$result = array_map('filesize', $tmpSearch);
|
572 |
+
$size = array_sum($result);
|
573 |
+
}
|
574 |
+
}
|
575 |
+
|
576 |
+
return $size;
|
577 |
+
}
|
578 |
+
|
579 |
+
/**
|
580 |
+
* Return true if active package exist and have an active status
|
581 |
+
*
|
582 |
+
* @return bool
|
583 |
+
*/
|
584 |
+
public static function is_active_package_present()
|
585 |
+
{
|
586 |
+
$activePakcs = self::get_ids_by_status(array(
|
587 |
+
array('op' => '>=', 'status' => DUP_PackageStatus::CREATED),
|
588 |
+
array('op' => '<', 'status' => DUP_PackageStatus::COMPLETE)
|
589 |
+
), true);
|
590 |
+
|
591 |
+
return in_array(DUP_Settings::Get('active_package_id'), $activePakcs);
|
592 |
+
}
|
593 |
+
|
594 |
+
/**
|
595 |
+
*
|
596 |
+
* @param array $conditions es. [
|
597 |
+
* relation = 'AND',
|
598 |
+
* [ 'op' => '>=' ,
|
599 |
+
* 'status' => DUP_PackageStatus::START ]
|
600 |
+
* [ 'op' => '<' ,
|
601 |
+
* 'status' => DUP_PackageStatus::COMPLETED ]
|
602 |
+
* ]
|
603 |
+
* @return string
|
604 |
+
*/
|
605 |
+
protected static function statusContitionsToWhere($conditions = array())
|
606 |
+
{
|
607 |
+
if (empty($conditions)) {
|
608 |
+
return '';
|
609 |
+
} else {
|
610 |
+
$accepted_op = array('<', '>', '=', '<>', '>=', '<=');
|
611 |
+
$relation = (isset($conditions['relation']) && strtoupper($conditions['relation']) == 'OR') ? ' OR ' : ' AND ';
|
612 |
+
unset($conditions['relation']);
|
613 |
+
|
614 |
+
$str_conds = array();
|
615 |
+
|
616 |
+
foreach ($conditions as $cond) {
|
617 |
+
$op = (isset($cond['op']) && in_array($cond['op'], $accepted_op)) ? $cond['op'] : '=';
|
618 |
+
$status = isset($cond['status']) ? (int) $cond['status'] : 0;
|
619 |
+
$str_conds[] = 'status ' . $op . ' ' . $status;
|
620 |
+
}
|
621 |
+
|
622 |
+
return ' WHERE ' . implode($relation, $str_conds) . ' ';
|
623 |
+
}
|
624 |
+
}
|
625 |
+
|
626 |
+
/**
|
627 |
+
* Get packages with status conditions and/or pagination
|
628 |
+
*
|
629 |
+
* @global wpdb $wpdb
|
630 |
+
*
|
631 |
+
* @param array // $conditions es. [
|
632 |
+
* relation = 'AND',
|
633 |
+
* [ 'op' => '>=' ,
|
634 |
+
* 'status' => DUP_PackageStatus::START ]
|
635 |
+
* [ 'op' => '<' ,
|
636 |
+
* 'status' => DUP_PackageStatus::COMPLETED ]
|
637 |
+
* ]
|
638 |
+
* if empty get all pacages
|
639 |
+
* @param int $limit // max row numbers fi false the limit is PHP_INT_MAX
|
640 |
+
* @param int $offset // offset 0 is at begin
|
641 |
+
* @param string $orderBy // default `id` ASC if empty no order
|
642 |
+
* @param string $resultType // ids => int[]
|
643 |
+
* row => row without backage blob
|
644 |
+
* fullRow => row with package blob
|
645 |
+
* objs => array of DUP_Package objects
|
646 |
+
*
|
647 |
+
* @return DUP_Package[]|object[]|int[]
|
648 |
+
*/
|
649 |
+
public static function get_packages_by_status($conditions = array(), $limit = false, $offset = 0, $orderBy = '`id` ASC', $resultType = 'obj')
|
650 |
+
{
|
651 |
+
global $wpdb;
|
652 |
+
$table = $wpdb->base_prefix . "duplicator_packages";
|
653 |
+
$where = self::statusContitionsToWhere($conditions);
|
654 |
+
|
655 |
+
$packages = array();
|
656 |
+
$offsetStr = ' OFFSET ' . (int) $offset;
|
657 |
+
$limitStr = ' LIMIT ' . ($limit !== false ? max(0, $limit) : PHP_INT_MAX);
|
658 |
+
$orderByStr = empty($orderBy) ? '' : ' ORDER BY ' . $orderBy . ' ';
|
659 |
+
switch ($resultType) {
|
660 |
+
case 'ids':
|
661 |
+
$cols = '`id`';
|
662 |
+
break;
|
663 |
+
case 'row':
|
664 |
+
$cols = '`id`,`name`,`hash`,`status`,`created`,`owner`';
|
665 |
+
break;
|
666 |
+
case 'fullRow':
|
667 |
+
$cols = '*';
|
668 |
+
break;
|
669 |
+
case 'objs':
|
670 |
+
default:
|
671 |
+
$cols = '`status`,`package`';
|
672 |
+
break;
|
673 |
+
}
|
674 |
+
|
675 |
+
$rows = $wpdb->get_results('SELECT ' . $cols . ' FROM `' . $table . '` ' . $where . $orderByStr . $limitStr . $offsetStr);
|
676 |
+
if ($rows != null) {
|
677 |
+
switch ($resultType) {
|
678 |
+
case 'ids':
|
679 |
+
foreach ($rows as $row) {
|
680 |
+
$packages[] = $row->id;
|
681 |
+
}
|
682 |
+
break;
|
683 |
+
case 'row':
|
684 |
+
case 'fullRow':
|
685 |
+
$packages = $rows;
|
686 |
+
break;
|
687 |
+
case 'objs':
|
688 |
+
default:
|
689 |
+
foreach ($rows as $row) {
|
690 |
+
$Package = unserialize($row->package);
|
691 |
+
if ($Package) {
|
692 |
+
// We was not storing Status in Lite 1.2.52, so it is for backward compatibility
|
693 |
+
if (!isset($Package->Status)) {
|
694 |
+
$Package->Status = $row->status;
|
695 |
+
}
|
696 |
+
|
697 |
+
$packages[] = $Package;
|
698 |
+
}
|
699 |
+
}
|
700 |
+
}
|
701 |
+
}
|
702 |
+
return $packages;
|
703 |
+
}
|
704 |
+
|
705 |
+
/**
|
706 |
+
* Get packages row db with status conditions and/or pagination
|
707 |
+
*
|
708 |
+
* @param array // $conditions es. [
|
709 |
+
* relation = 'AND',
|
710 |
+
* [ 'op' => '>=' ,
|
711 |
+
* 'status' => DUP_PackageStatus::START ]
|
712 |
+
* [ 'op' => '<' ,
|
713 |
+
* 'status' => DUP_PackageStatus::COMPLETED ]
|
714 |
+
* ]
|
715 |
+
* if empty get all pacages
|
716 |
+
* @param int $limit // max row numbers
|
717 |
+
* @param int $offset // offset 0 is at begin
|
718 |
+
* @param string $orderBy // default `id` ASC if empty no order
|
719 |
+
*
|
720 |
+
* @return object[] // return row database without package blob
|
721 |
+
*/
|
722 |
+
public static function get_row_by_status($conditions = array(), $limit = false, $offset = 0, $orderBy = '`id` ASC')
|
723 |
+
{
|
724 |
+
return self::get_packages_by_status($conditions, $limit, $offset, $orderBy, 'row');
|
725 |
+
}
|
726 |
+
|
727 |
+
/**
|
728 |
+
* Get packages ids with status conditions and/or pagination
|
729 |
+
*
|
730 |
+
* @param array // $conditions es. [
|
731 |
+
* relation = 'AND',
|
732 |
+
* [ 'op' => '>=' ,
|
733 |
+
* 'status' => DUP_PackageStatus::START ]
|
734 |
+
* [ 'op' => '<' ,
|
735 |
+
* 'status' => DUP_PackageStatus::COMPLETED ]
|
736 |
+
* ]
|
737 |
+
* if empty get all pacages
|
738 |
+
* @param int $limit // max row numbers
|
739 |
+
* @param int $offset // offset 0 is at begin
|
740 |
+
* @param string $orderBy // default `id` ASC if empty no order
|
741 |
+
*
|
742 |
+
* @return array[] // return row database without package blob
|
743 |
+
*/
|
744 |
+
public static function get_ids_by_status($conditions = array(), $limit = false, $offset = 0, $orderBy = '`id` ASC')
|
745 |
+
{
|
746 |
+
return self::get_packages_by_status($conditions, $limit, $offset, $orderBy, 'ids');
|
747 |
+
}
|
748 |
+
|
749 |
+
/**
|
750 |
+
* count package with status condition
|
751 |
+
*
|
752 |
+
* @global wpdb $wpdb
|
753 |
+
* @param array $conditions es. [
|
754 |
+
* relation = 'AND',
|
755 |
+
* [ 'op' => '>=' ,
|
756 |
+
* 'status' => DUP_PackageStatus::START ]
|
757 |
+
* [ 'op' => '<' ,
|
758 |
+
* 'status' => DUP_PackageStatus::COMPLETED ]
|
759 |
+
* ]
|
760 |
+
* @return int
|
761 |
+
*/
|
762 |
+
public static function count_by_status($conditions = array())
|
763 |
+
{
|
764 |
+
global $wpdb;
|
765 |
+
|
766 |
+
$table = $wpdb->base_prefix . "duplicator_packages";
|
767 |
+
$where = self::statusContitionsToWhere($conditions);
|
768 |
+
|
769 |
+
$count = $wpdb->get_var("SELECT count(id) FROM `{$table}` " . $where);
|
770 |
+
return $count;
|
771 |
+
}
|
772 |
+
|
773 |
+
/**
|
774 |
+
* Execute $callback function foreach package result
|
775 |
+
* For each iteration the memory is released
|
776 |
+
*
|
777 |
+
* @param callable $callback // function callback(DUP_Package $package)
|
778 |
+
* @param array // $conditions es. [
|
779 |
+
* relation = 'AND',
|
780 |
+
* [ 'op' => '>=' ,
|
781 |
+
* 'status' => DUP_PackageStatus::START ]
|
782 |
+
* [ 'op' => '<' ,
|
783 |
+
* 'status' => DUP_PackageStatus::COMPLETED ]
|
784 |
+
* ]
|
785 |
+
* if empty get all pacages
|
786 |
+
* @param int $limit // max row numbers
|
787 |
+
* @param int $offset // offset 0 is at begin
|
788 |
+
* @param string $orderBy // default `id` ASC if empty no order
|
789 |
+
*
|
790 |
+
* @return void
|
791 |
+
*/
|
792 |
+
public static function by_status_callback($callback, $conditions = array(), $limit = false, $offset = 0, $orderBy = '`id` ASC')
|
793 |
+
{
|
794 |
+
if (!is_callable($callback)) {
|
795 |
+
throw new Exception('No callback function passed');
|
796 |
+
}
|
797 |
+
|
798 |
+
$offset = max(0, $offset);
|
799 |
+
$numPackages = self::count_by_status($conditions);
|
800 |
+
$maxLimit = $offset + ($limit !== false ? max(0, $limit) : PHP_INT_MAX - $offset);
|
801 |
+
$numPackages = min($maxLimit, $numPackages);
|
802 |
+
$orderByStr = empty($orderBy) ? '' : ' ORDER BY ' . $orderBy . ' ';
|
803 |
+
|
804 |
+
global $wpdb;
|
805 |
+
$table = $wpdb->base_prefix . "duplicator_packages";
|
806 |
+
$where = self::statusContitionsToWhere($conditions);
|
807 |
+
$sql = 'SELECT * FROM `' . $table . '` ' . $where . $orderByStr . ' LIMIT 1 OFFSET ';
|
808 |
+
|
809 |
+
for (; $offset < $numPackages; $offset++) {
|
810 |
+
$rows = $wpdb->get_results($sql . $offset);
|
811 |
+
if ($rows != null) {
|
812 |
+
$Package = @unserialize($rows[0]->package);
|
813 |
+
if ($Package) {
|
814 |
+
if (empty($Package->ID)) {
|
815 |
+
$Package->ID = $rows[0]->id;
|
816 |
+
}
|
817 |
+
// We was not storing Status in Lite 1.2.52, so it is for backward compatibility
|
818 |
+
if (!isset($Package->Status)) {
|
819 |
+
$Package->Status = $rows[0]->status;
|
820 |
+
}
|
821 |
+
call_user_func($callback, $Package);
|
822 |
+
unset($Package);
|
823 |
+
}
|
824 |
+
unset($rows);
|
825 |
+
}
|
826 |
+
}
|
827 |
+
}
|
828 |
+
|
829 |
+
public static function purge_incomplete_package()
|
830 |
+
{
|
831 |
+
$packages = self::get_packages_by_status(array(
|
832 |
+
'relation' => 'AND',
|
833 |
+
array('op' => '>=', 'status' => DUP_PackageStatus::CREATED),
|
834 |
+
array('op' => '<', 'status' => DUP_PackageStatus::COMPLETE)
|
835 |
+
), 1, 0, '`id` ASC');
|
836 |
+
|
837 |
+
|
838 |
+
if (count($packages) > 0) {
|
839 |
+
foreach ($packages as $package) {
|
840 |
+
if (!$package->isRunning()) {
|
841 |
+
$package->delete();
|
842 |
+
}
|
843 |
+
}
|
844 |
+
}
|
845 |
+
}
|
846 |
+
|
847 |
+
/**
|
848 |
+
* Check the DupArchive build to make sure it is good
|
849 |
+
*
|
850 |
+
* @return void
|
851 |
+
*/
|
852 |
+
public function runDupArchiveBuildIntegrityCheck()
|
853 |
+
{
|
854 |
+
//INTEGRITY CHECKS
|
855 |
+
//We should not rely on data set in the serlized object, we need to manually check each value
|
856 |
+
//indepentantly to have a true integrity check.
|
857 |
+
DUP_Log::info("\n********************************************************************************");
|
858 |
+
DUP_Log::info("INTEGRITY CHECKS:");
|
859 |
+
DUP_Log::info("********************************************************************************");
|
860 |
+
|
861 |
+
//------------------------
|
862 |
+
//SQL CHECK: File should be at minimum 5K. A base WP install with only Create tables is about 9K
|
863 |
+
$sql_temp_path = DUP_Settings::getSsdirTmpPath() . '/' . $this->Database->File;
|
864 |
+
$sql_temp_size = @filesize($sql_temp_path);
|
865 |
+
$sql_easy_size = DUP_Util::byteSize($sql_temp_size);
|
866 |
+
$sql_done_txt = DUP_Util::tailFile($sql_temp_path, 3);
|
867 |
+
DUP_Log::Trace('[DUP ARCHIVE] ' . __FUNCTION__ . ' ' . __LINE__);
|
868 |
+
|
869 |
+
// Note: Had to add extra size check of 800 since observed bad sql when filter was on
|
870 |
+
if (
|
871 |
+
!strstr($sql_done_txt, 'DUPLICATOR_MYSQLDUMP_EOF') ||
|
872 |
+
(
|
873 |
+
!$this->Database->FilterOn && $sql_temp_size < 5120) ||
|
874 |
+
($this->Database->FilterOn && $this->Database->info->tablesFinalCount > 0 &&
|
875 |
+
$sql_temp_size < 800
|
876 |
+
)
|
877 |
+
) {
|
878 |
+
DUP_Log::Trace('[DUP ARCHIVE] ' . __FUNCTION__ . ' ' . __LINE__);
|
879 |
+
|
880 |
+
$error_text = "ERROR: SQL file not complete. The file {$sql_temp_path} looks too small ($sql_temp_size bytes) or the end of file marker was not found.";
|
881 |
+
$this->BuildProgress->set_failed($error_text);
|
882 |
+
$this->setStatus(DUP_PackageStatus::ERROR);
|
883 |
+
DUP_Log::error("$error_text", '', Dup_ErrorBehavior::LogOnly);
|
884 |
+
return;
|
885 |
+
}
|
886 |
+
|
887 |
+
DUP_Log::Trace('[DUP ARCHIVE] ' . __FUNCTION__ . ' ' . __LINE__);
|
888 |
+
DUP_Log::Info("SQL FILE: {$sql_easy_size}");
|
889 |
+
|
890 |
+
//------------------------
|
891 |
+
//INSTALLER CHECK:
|
892 |
+
$exe_temp_path = DUP_Settings::getSsdirTmpPath() . '/' . $this->Installer->File;
|
893 |
+
|
894 |
+
$exe_temp_size = @filesize($exe_temp_path);
|
895 |
+
$exe_easy_size = DUP_Util::byteSize($exe_temp_size);
|
896 |
+
$exe_done_txt = DUP_Util::tailFile($exe_temp_path, 10);
|
897 |
+
|
898 |
+
if (!strstr($exe_done_txt, 'DUPLICATOR_INSTALLER_EOF') && !$this->BuildProgress->failed) {
|
899 |
+
//$this->BuildProgress->failed = true;
|
900 |
+
$error_message = 'ERROR: Installer file not complete. The end of file marker was not found. Please try to re-create the package.';
|
901 |
+
|
902 |
+
$this->BuildProgress->set_failed($error_message);
|
903 |
+
$this->Status = DUP_PackageStatus::ERROR;
|
904 |
+
$this->update();
|
905 |
+
DUP_Log::error($error_message, '', Dup_ErrorBehavior::LogOnly);
|
906 |
+
return;
|
907 |
+
}
|
908 |
+
DUP_Log::info("INSTALLER FILE: {$exe_easy_size}");
|
909 |
+
|
910 |
+
//------------------------
|
911 |
+
//ARCHIVE CHECK:
|
912 |
+
DUP_LOG::trace("Archive file count is " . $this->Archive->file_count);
|
913 |
+
|
914 |
+
if ($this->Archive->file_count != -1) {
|
915 |
+
$zip_easy_size = DUP_Util::byteSize($this->Archive->Size);
|
916 |
+
if (!($this->Archive->Size)) {
|
917 |
+
//$this->BuildProgress->failed = true;
|
918 |
+
$error_message = "ERROR: The archive file contains no size.";
|
919 |
+
|
920 |
+
$this->BuildProgress->set_failed($error_message);
|
921 |
+
$this->setStatus(DUP_PackageStatus::ERROR);
|
922 |
+
DUP_Log::error($error_message, "Archive Size: {$zip_easy_size}", Dup_ErrorBehavior::LogOnly);
|
923 |
+
return;
|
924 |
+
}
|
925 |
+
|
926 |
+
$scan_filepath = DUP_Settings::getSsdirTmpPath() . "/{$this->NameHash}_scan.json";
|
927 |
+
$json = '';
|
928 |
+
|
929 |
+
DUP_LOG::Trace("***********Does $scan_filepath exist?");
|
930 |
+
if (file_exists($scan_filepath)) {
|
931 |
+
$json = file_get_contents($scan_filepath);
|
932 |
+
} else {
|
933 |
+
$error_message = sprintf(__("Can't find Scanfile %s. Please ensure there no non-English characters in the package or schedule name.", 'duplicator'), $scan_filepath);
|
934 |
+
|
935 |
+
//$this->BuildProgress->failed = true;
|
936 |
+
//$this->setStatus(DUP_PackageStatus::ERROR);
|
937 |
+
$this->BuildProgress->set_failed($error_message);
|
938 |
+
$this->setStatus(DUP_PackageStatus::ERROR);
|
939 |
+
|
940 |
+
DUP_Log::error($error_message, '', Dup_ErrorBehavior::LogOnly);
|
941 |
+
return;
|
942 |
+
}
|
943 |
+
|
944 |
+
$scanReport = json_decode($json);
|
945 |
+
|
946 |
+
//RSR TODO: rework/simplify the validateion of duparchive
|
947 |
+
$dirCount = count($scanReport->ARC->Dirs);
|
948 |
+
$numInstallerDirs = $this->Installer->numDirsAdded;
|
949 |
+
$fileCount = count($scanReport->ARC->Files);
|
950 |
+
$numInstallerFiles = $this->Installer->numFilesAdded;
|
951 |
+
|
952 |
+
$expected_filecount = $dirCount + $numInstallerDirs + $fileCount + $numInstallerFiles + 1 - 1; // Adding database.sql but subtracting the root dir
|
953 |
+
//Dup_Log::trace("#### a:{$dirCount} b:{$numInstallerDirs} c:{$fileCount} d:{$numInstallerFiles} = {$expected_filecount}");
|
954 |
+
|
955 |
+
DUP_Log::info("ARCHIVE FILE: {$zip_easy_size} ");
|
956 |
+
DUP_Log::info(sprintf(__('EXPECTED FILE/DIRECTORY COUNT: %1$s', 'duplicator'), number_format($expected_filecount)));
|
957 |
+
DUP_Log::info(sprintf(__('ACTUAL FILE/DIRECTORY COUNT: %1$s', 'duplicator'), number_format($this->Archive->file_count)));
|
958 |
+
|
959 |
+
$this->ExeSize = $exe_easy_size;
|
960 |
+
$this->ZipSize = $zip_easy_size;
|
961 |
+
|
962 |
+
/* ------- ZIP Filecount Check -------- */
|
963 |
+
// Any zip of over 500 files should be within 2% - this is probably too loose but it will catch gross errors
|
964 |
+
DUP_LOG::trace("Expected filecount = $expected_filecount and archive filecount=" . $this->Archive->file_count);
|
965 |
+
|
966 |
+
if ($expected_filecount > 500) {
|
967 |
+
$straight_ratio = (float) $expected_filecount / (float) $this->Archive->file_count;
|
968 |
+
|
969 |
+
$warning_count = $scanReport->ARC->WarnFileCount + $scanReport->ARC->WarnDirCount + $scanReport->ARC->UnreadableFileCount + $scanReport->ARC->UnreadableDirCount;
|
970 |
+
DUP_LOG::trace(
|
971 |
+
"Warn/unread counts) warnfile:{$scanReport->ARC->WarnFileCount} warndir:{$scanReport->ARC->WarnDirCount}" .
|
972 |
+
" unreadfile:{$scanReport->ARC->UnreadableFileCount} unreaddir:{$scanReport->ARC->UnreadableDirCount}"
|
973 |
+
);
|
974 |
+
$warning_ratio = ((float) ($expected_filecount + $warning_count)) / (float) $this->Archive->file_count;
|
975 |
+
DUP_LOG::trace(
|
976 |
+
"Straight ratio is $straight_ratio and warning ratio is $warning_ratio. # Expected=$expected_filecount " .
|
977 |
+
"# Warning=$warning_count and #Archive File {$this->Archive->file_count}"
|
978 |
+
);
|
979 |
+
|
980 |
+
// Allow the real file count to exceed the expected by 10% but only allow 1% the other way
|
981 |
+
if (($straight_ratio < 0.90) || ($straight_ratio > 1.01)) {
|
982 |
+
// Has to exceed both the straight as well as the warning ratios
|
983 |
+
if (($warning_ratio < 0.90) || ($warning_ratio > 1.01)) {
|
984 |
+
$error_message = sprintf('ERROR: File count in archive vs expected suggests a bad archive (%1$d vs %2$d).', $this->Archive->file_count, $expected_filecount);
|
985 |
+
$this->BuildProgress->set_failed($error_message);
|
986 |
+
$this->Status = DUP_PackageStatus::ERROR;
|
987 |
+
$this->update();
|
988 |
+
|
989 |
+
DUP_Log::error($error_message, '');
|
990 |
+
return;
|
991 |
+
}
|
992 |
+
}
|
993 |
+
}
|
994 |
+
}
|
995 |
+
|
996 |
+
/* ------ ZIP CONSISTENCY CHECK ------ */
|
997 |
+
if ($this->Archive->getBuildMode() == DUP_Archive_Build_Mode::ZipArchive) {
|
998 |
+
DUP_LOG::trace("Running ZipArchive consistency check");
|
999 |
+
$zipPath = DUP_Settings::getSsdirTmpPath() . "/{$this->Archive->File}";
|
1000 |
+
|
1001 |
+
$zip = new ZipArchive();
|
1002 |
+
|
1003 |
+
// ZipArchive::CHECKCONS will enforce additional consistency checks
|
1004 |
+
$res = $zip->open($zipPath, ZipArchive::CHECKCONS);
|
1005 |
+
|
1006 |
+
if ($res !== true) {
|
1007 |
+
$consistency_error = sprintf(__('ERROR: Cannot open created archive. Error code = %1$s', 'duplicator'), $res);
|
1008 |
+
|
1009 |
+
DUP_LOG::trace($consistency_error);
|
1010 |
+
switch ($res) {
|
1011 |
+
case ZipArchive::ER_NOZIP:
|
1012 |
+
$consistency_error = __('ERROR: Archive is not valid zip archive.', 'duplicator');
|
1013 |
+
break;
|
1014 |
+
|
1015 |
+
case ZipArchive::ER_INCONS:
|
1016 |
+
$consistency_error = __("ERROR: Archive doesn't pass consistency check.", 'duplicator');
|
1017 |
+
break;
|
1018 |
+
|
1019 |
+
|
1020 |
+
case ZipArchive::ER_CRC:
|
1021 |
+
$consistency_error = __("ERROR: Archive checksum is bad.", 'duplicator');
|
1022 |
+
break;
|
1023 |
+
}
|
1024 |
+
|
1025 |
+
$this->BuildProgress->set_failed($consistency_error);
|
1026 |
+
$this->Status = DUP_PackageStatus::ERROR;
|
1027 |
+
$this->update();
|
1028 |
+
|
1029 |
+
DUP_LOG::trace($consistency_error);
|
1030 |
+
DUP_Log::error($consistency_error, '');
|
1031 |
+
} else {
|
1032 |
+
DUP_Log::info(__('ARCHIVE CONSISTENCY TEST: Pass', 'duplicator'));
|
1033 |
+
DUP_LOG::trace("Zip for package $this->ID passed consistency test");
|
1034 |
+
}
|
1035 |
+
|
1036 |
+
$zip->close();
|
1037 |
+
}
|
1038 |
+
}
|
1039 |
+
|
1040 |
+
public function getLocalPackageFile($file_type)
|
1041 |
+
{
|
1042 |
+
$file_path = null;
|
1043 |
+
|
1044 |
+
if ($file_type == DUP_PackageFileType::Installer) {
|
1045 |
+
DUP_Log::Trace("Installer requested");
|
1046 |
+
$file_name = apply_filters('duplicator_installer_file_path', $this->getInstallerFilename());
|
1047 |
+
} elseif ($file_type == DUP_PackageFileType::Archive) {
|
1048 |
+
DUP_Log::Trace("Archive requested");
|
1049 |
+
$file_name = $this->getArchiveFilename();
|
1050 |
+
} elseif ($file_type == DUP_PackageFileType::SQL) {
|
1051 |
+
DUP_Log::Trace("SQL requested");
|
1052 |
+
$file_name = $this->getDatabaseFilename();
|
1053 |
+
} else {
|
1054 |
+
DUP_Log::Trace("Log requested");
|
1055 |
+
$file_name = $this->getLogFilename();
|
1056 |
+
}
|
1057 |
+
|
1058 |
+
$file_path = DUP_Settings::getSsdirPath() . "/$file_name";
|
1059 |
+
DUP_Log::Trace("File path $file_path");
|
1060 |
+
|
1061 |
+
if (file_exists($file_path)) {
|
1062 |
+
return $file_path;
|
1063 |
+
} else {
|
1064 |
+
return null;
|
1065 |
+
}
|
1066 |
+
}
|
1067 |
+
|
1068 |
+
public function getScanFilename()
|
1069 |
+
{
|
1070 |
+
return $this->NameHash . '_scan.json';
|
1071 |
+
}
|
1072 |
+
|
1073 |
+
public function getScanUrl()
|
1074 |
+
{
|
1075 |
+
return DUP_Settings::getSsdirUrl() . "/" . $this->getScanFilename();
|
1076 |
+
}
|
1077 |
+
|
1078 |
+
public function getLogFilename()
|
1079 |
+
{
|
1080 |
+
return $this->NameHash . '.log';
|
1081 |
+
}
|
1082 |
+
|
1083 |
+
public function getLogUrl()
|
1084 |
+
{
|
1085 |
+
return DUP_Settings::getSsdirUrl() . "/" . $this->getLogFilename();
|
1086 |
+
}
|
1087 |
+
|
1088 |
+
public function getArchiveFilename()
|
1089 |
+
{
|
1090 |
+
$extension = strtolower($this->Archive->Format);
|
1091 |
+
|
1092 |
+
return "{$this->NameHash}_archive.{$extension}";
|
1093 |
+
}
|
1094 |
+
|
1095 |
+
public function getInstallerFilename()
|
1096 |
+
{
|
1097 |
+
return "{$this->NameHash}_installer" . DUP_Installer::INSTALLER_SERVER_EXTENSION;
|
1098 |
+
}
|
1099 |
+
|
1100 |
+
public function getDatabaseFilename()
|
1101 |
+
{
|
1102 |
+
return $this->NameHash . '_database.sql';
|
1103 |
+
}
|
1104 |
+
|
1105 |
+
public function get_files_list_filename()
|
1106 |
+
{
|
1107 |
+
return $this->NameHash . DUP_Archive::FILES_LIST_FILE_NAME_SUFFIX;
|
1108 |
+
}
|
1109 |
+
|
1110 |
+
public function get_dirs_list_filename()
|
1111 |
+
{
|
1112 |
+
return $this->NameHash . DUP_Archive::DIRS_LIST_FILE_NAME_SUFFIX;
|
1113 |
+
}
|
1114 |
+
|
1115 |
+
/**
|
1116 |
+
* @param int $type
|
1117 |
+
* @return array
|
1118 |
+
*/
|
1119 |
+
public function getPackageFileDownloadInfo($type)
|
1120 |
+
{
|
1121 |
+
$result = array(
|
1122 |
+
"filename" => "",
|
1123 |
+
"url" => ""
|
1124 |
+
);
|
1125 |
+
|
1126 |
+
switch ($type) {
|
1127 |
+
case DUP_PackageFileType::Archive:
|
1128 |
+
$result["filename"] = $this->Archive->File;
|
1129 |
+
$result["url"] = $this->Archive->getURL();
|
1130 |
+
break;
|
1131 |
+
case DUP_PackageFileType::SQL:
|
1132 |
+
$result["filename"] = $this->Database->File;
|
1133 |
+
$result["url"] = $this->Database->getURL();
|
1134 |
+
break;
|
1135 |
+
case DUP_PackageFileType::Log:
|
1136 |
+
$result["filename"] = $this->getLogFilename();
|
1137 |
+
$result["url"] = $this->getLogUrl();
|
1138 |
+
break;
|
1139 |
+
case DUP_PackageFileType::Scan:
|
1140 |
+
$result["filename"] = $this->getScanFilename();
|
1141 |
+
$result["url"] = $this->getScanUrl();
|
1142 |
+
break;
|
1143 |
+
default:
|
1144 |
+
break;
|
1145 |
+
}
|
1146 |
+
|
1147 |
+
return $result;
|
1148 |
+
}
|
1149 |
+
|
1150 |
+
public function getInstallerDownloadInfo()
|
1151 |
+
{
|
1152 |
+
return array(
|
1153 |
+
"id" => $this->ID,
|
1154 |
+
"hash" => $this->Hash
|
1155 |
+
);
|
1156 |
+
}
|
1157 |
+
|
1158 |
+
/**
|
1159 |
+
* Removes all files except those of active packages
|
1160 |
+
*/
|
1161 |
+
public static function not_active_files_tmp_cleanup()
|
1162 |
+
{
|
1163 |
+
//Check for the 'tmp' folder just for safe measures
|
1164 |
+
if (! is_dir(DUP_Settings::getSsdirTmpPath()) && (strpos(DUP_Settings::getSsdirTmpPath(), 'tmp') !== false)) {
|
1165 |
+
return;
|
1166 |
+
}
|
1167 |
+
|
1168 |
+
$globs = glob(DUP_Settings::getSsdirTmpPath() . '/*.*');
|
1169 |
+
if (! is_array($globs) || $globs === false) {
|
1170 |
+
return;
|
1171 |
+
}
|
1172 |
+
|
1173 |
+
// RUNNING PACKAGES
|
1174 |
+
$active_pack = self::get_row_by_status(array(
|
1175 |
+
'relation' => 'AND',
|
1176 |
+
array('op' => '>=' , 'status' => DUP_PackageStatus::CREATED ),
|
1177 |
+
array('op' => '<' , 'status' => DUP_PackageStatus::COMPLETE )
|
1178 |
+
));
|
1179 |
+
$active_files = array();
|
1180 |
+
foreach ($active_pack as $row) {
|
1181 |
+
$active_files[] = $row->name . '_' . $row->hash;
|
1182 |
+
}
|
1183 |
+
|
1184 |
+
// ERRORS PACKAGES
|
1185 |
+
$err_pack = self::get_row_by_status(array(
|
1186 |
+
array('op' => '<' , 'status' => DUP_PackageStatus::CREATED )
|
1187 |
+
));
|
1188 |
+
$force_del_files = array();
|
1189 |
+
foreach ($err_pack as $row) {
|
1190 |
+
$force_del_files[] = $row->name . '_' . $row->hash;
|
1191 |
+
}
|
1192 |
+
|
1193 |
+
// Don't remove json file;
|
1194 |
+
$extension_filter = array('json', 'txt');
|
1195 |
+
|
1196 |
+
// Calculate delta time for old files
|
1197 |
+
$oldTimeToClean = time() - DUPLICATOR_TEMP_CLEANUP_SECONDS;
|
1198 |
+
|
1199 |
+
foreach ($globs as $glob_full_path) {
|
1200 |
+
// Don't remove sub dir
|
1201 |
+
if (is_dir($glob_full_path)) {
|
1202 |
+
continue;
|
1203 |
+
}
|
1204 |
+
|
1205 |
+
$file_name = basename($glob_full_path);
|
1206 |
+
// skip all active packages
|
1207 |
+
foreach ($active_files as $c_nameHash) {
|
1208 |
+
if (strpos($file_name, $c_nameHash) === 0) {
|
1209 |
+
continue 2;
|
1210 |
+
}
|
1211 |
+
}
|
1212 |
+
|
1213 |
+
// Remove all old files
|
1214 |
+
if (filemtime($glob_full_path) <= $oldTimeToClean) {
|
1215 |
+
@unlink($glob_full_path);
|
1216 |
+
continue;
|
1217 |
+
}
|
1218 |
+
|
1219 |
+
// remove all error packages files
|
1220 |
+
foreach ($force_del_files as $c_nameHash) {
|
1221 |
+
if (strpos($file_name, $c_nameHash) === 0) {
|
1222 |
+
@unlink($glob_full_path);
|
1223 |
+
continue 2;
|
1224 |
+
}
|
1225 |
+
}
|
1226 |
+
|
1227 |
+
$file_info = pathinfo($glob_full_path);
|
1228 |
+
// skip json file for pre build packages
|
1229 |
+
if (in_array($file_info['extension'], $extension_filter) || in_array($file_name, $active_files)) {
|
1230 |
+
continue;
|
1231 |
+
}
|
1232 |
+
|
1233 |
+
@unlink($glob_full_path);
|
1234 |
+
}
|
1235 |
+
}
|
1236 |
+
|
1237 |
+
/**
|
1238 |
+
* Cleans up the temp storage folder have a time interval
|
1239 |
+
*
|
1240 |
+
* @return void
|
1241 |
+
*/
|
1242 |
+
public static function safeTmpCleanup($purge_temp_archives = false)
|
1243 |
+
{
|
1244 |
+
if ($purge_temp_archives) {
|
1245 |
+
$dir = DUP_Settings::getSsdirTmpPath() . "/*_archive.zip.*";
|
1246 |
+
foreach (glob($dir) as $file_path) {
|
1247 |
+
unlink($file_path);
|
1248 |
+
}
|
1249 |
+
$dir = DUP_Settings::getSsdirTmpPath() . "/*_archive.daf.*";
|
1250 |
+
foreach (glob($dir) as $file_path) {
|
1251 |
+
unlink($file_path);
|
1252 |
+
}
|
1253 |
+
} else {
|
1254 |
+
//Remove all temp files that are 24 hours old
|
1255 |
+
$dir = DUP_Settings::getSsdirTmpPath() . "/*";
|
1256 |
+
|
1257 |
+
$files = glob($dir);
|
1258 |
+
|
1259 |
+
if ($files !== false) {
|
1260 |
+
foreach ($files as $file_path) {
|
1261 |
+
// Cut back to keeping things around for just an hour 15 min
|
1262 |
+
if (filemtime($file_path) <= time() - DUPLICATOR_TEMP_CLEANUP_SECONDS) {
|
1263 |
+
unlink($file_path);
|
1264 |
+
}
|
1265 |
+
}
|
1266 |
+
}
|
1267 |
+
}
|
1268 |
+
}
|
1269 |
+
|
1270 |
+
/**
|
1271 |
+
* Starts the package DupArchive progressive build process - always assumed to only run off active package, NOT one in the package table
|
1272 |
+
*
|
1273 |
+
* @return obj Returns a DUP_Package object
|
1274 |
+
*/
|
1275 |
+
public function runDupArchiveBuild()
|
1276 |
+
{
|
1277 |
+
$this->BuildProgress->start_timer();
|
1278 |
+
DUP_Log::Trace('Called');
|
1279 |
+
|
1280 |
+
if ($this->BuildProgress->failed) {
|
1281 |
+
DUP_LOG::Trace("build progress failed so setting package to failed");
|
1282 |
+
$this->setStatus(DUP_PackageStatus::ERROR);
|
1283 |
+
$message = "Package creation failed.";
|
1284 |
+
DUP_Log::Trace($message);
|
1285 |
+
return true;
|
1286 |
+
}
|
1287 |
+
|
1288 |
+
if ($this->BuildProgress->initialized == false) {
|
1289 |
+
DUP_Log::Trace('[DUP ARCHIVE] INIZIALIZE');
|
1290 |
+
$this->BuildProgress->initialized = true;
|
1291 |
+
$this->TimerStart = Dup_Util::getMicrotime();
|
1292 |
+
$this->update();
|
1293 |
+
}
|
1294 |
+
|
1295 |
+
//START BUILD
|
1296 |
+
if (!$this->BuildProgress->database_script_built) {
|
1297 |
+
DUP_Log::Info('[DUP ARCHIVE] BUILDING DATABASE');
|
1298 |
+
$this->Database->build($this, Dup_ErrorBehavior::ThrowException);
|
1299 |
+
DUP_Log::Info('[DUP ARCHIVE] VALIDATING DATABASE');
|
1300 |
+
$this->BuildProgress->database_script_built = true;
|
1301 |
+
$this->update();
|
1302 |
+
DUP_Log::Info('[DUP ARCHIVE] DONE DATABASE');
|
1303 |
+
} elseif (!$this->BuildProgress->archive_built) {
|
1304 |
+
DUP_Log::Info('[DUP ARCHIVE] BUILDING ARCHIVE');
|
1305 |
+
$this->Archive->build($this);
|
1306 |
+
$this->update();
|
1307 |
+
DUP_Log::Info('[DUP ARCHIVE] DONE ARCHIVE');
|
1308 |
+
} elseif (!$this->BuildProgress->installer_built) {
|
1309 |
+
DUP_Log::Info('[DUP ARCHIVE] BUILDING INSTALLER');
|
1310 |
+
// Installer being built is stuffed into the archive build phase
|
1311 |
+
}
|
1312 |
+
|
1313 |
+
if ($this->BuildProgress->has_completed()) {
|
1314 |
+
DUP_Log::Info('[DUP ARCHIVE] HAS COMPLETED CLOSING');
|
1315 |
+
|
1316 |
+
if (!$this->BuildProgress->failed) {
|
1317 |
+
DUP_LOG::Info("[DUP ARCHIVE] DUP ARCHIVE INTEGRITY CHECK");
|
1318 |
+
// Only makees sense to perform build integrity check on completed archives
|
1319 |
+
$this->runDupArchiveBuildIntegrityCheck();
|
1320 |
+
} else {
|
1321 |
+
DUP_LOG::trace("top of loop build progress failed");
|
1322 |
+
}
|
1323 |
+
|
1324 |
+
$timerEnd = DUP_Util::getMicrotime();
|
1325 |
+
$timerSum = DUP_Util::elapsedTime($timerEnd, $this->TimerStart);
|
1326 |
+
$this->Runtime = $timerSum;
|
1327 |
+
|
1328 |
+
//FINAL REPORT
|
1329 |
+
$info = "\n********************************************************************************\n";
|
1330 |
+
$info .= "RECORD ID:[{$this->ID}]\n";
|
1331 |
+
$info .= "TOTAL PROCESS RUNTIME: {$timerSum}\n";
|
1332 |
+
$info .= "PEAK PHP MEMORY USED: " . DUP_Server::getPHPMemory(true) . "\n";
|
1333 |
+
$info .= "DONE PROCESSING => {$this->Name} " . @date("Y-m-d H:i:s") . "\n";
|
1334 |
+
|
1335 |
+
DUP_Log::info($info);
|
1336 |
+
DUP_LOG::trace("Done package building");
|
1337 |
+
|
1338 |
+
if (!$this->BuildProgress->failed) {
|
1339 |
+
DUP_Log::Trace('[DUP ARCHIVE] HAS COMPLETED DONE');
|
1340 |
+
$this->setStatus(DUP_PackageStatus::COMPLETE);
|
1341 |
+
DUP_LOG::Trace("Cleaning up duparchive temp files");
|
1342 |
+
//File Cleanup
|
1343 |
+
$this->buildCleanup();
|
1344 |
+
do_action('duplicator_lite_build_completed', $this);
|
1345 |
+
} else {
|
1346 |
+
DUP_Log::Trace('[DUP ARCHIVE] HAS COMPLETED ERROR');
|
1347 |
+
}
|
1348 |
+
}
|
1349 |
+
DUP_Log::Close();
|
1350 |
+
return $this->BuildProgress->has_completed();
|
1351 |
+
}
|
1352 |
+
|
1353 |
+
/**
|
1354 |
+
* Starts the package build process
|
1355 |
+
*
|
1356 |
+
* @return obj Returns a DUP_Package object
|
1357 |
+
*/
|
1358 |
+
public function runZipBuild()
|
1359 |
+
{
|
1360 |
+
$timerStart = DUP_Util::getMicrotime();
|
1361 |
+
|
1362 |
+
DUP_Log::Trace('#### start of zip build');
|
1363 |
+
//START BUILD
|
1364 |
+
//PHPs serialze method will return the object, but the ID above is not passed
|
1365 |
+
//for one reason or another so passing the object back in seems to do the trick
|
1366 |
+
$this->Database->build($this, Dup_ErrorBehavior::ThrowException);
|
1367 |
+
$this->Archive->build($this);
|
1368 |
+
$this->Installer->build($this);
|
1369 |
+
|
1370 |
+
//INTEGRITY CHECKS
|
1371 |
+
/*DUP_Log::Info("\n********************************************************************************");
|
1372 |
+
DUP_Log::Info("INTEGRITY CHECKS:");
|
1373 |
+
DUP_Log::Info("********************************************************************************");*/
|
1374 |
+
$this->runDupArchiveBuildIntegrityCheck();
|
1375 |
+
$dbSizeRead = DUP_Util::byteSize($this->Database->Size);
|
1376 |
+
$zipSizeRead = DUP_Util::byteSize($this->Archive->Size);
|
1377 |
+
$exeSizeRead = DUP_Util::byteSize($this->Installer->Size);
|
1378 |
+
|
1379 |
+
$timerEnd = DUP_Util::getMicrotime();
|
1380 |
+
$timerSum = DUP_Util::elapsedTime($timerEnd, $timerStart);
|
1381 |
+
|
1382 |
+
$this->Runtime = $timerSum;
|
1383 |
+
$this->ExeSize = $exeSizeRead;
|
1384 |
+
$this->ZipSize = $zipSizeRead;
|
1385 |
+
|
1386 |
+
|
1387 |
+
$this->buildCleanup();
|
1388 |
+
|
1389 |
+
//FINAL REPORT
|
1390 |
+
$info = "\n********************************************************************************\n";
|
1391 |
+
$info .= "RECORD ID:[{$this->ID}]\n";
|
1392 |
+
$info .= "TOTAL PROCESS RUNTIME: {$timerSum}\n";
|
1393 |
+
$info .= "PEAK PHP MEMORY USED: " . DUP_Server::getPHPMemory(true) . "\n";
|
1394 |
+
$info .= "DONE PROCESSING => {$this->Name} " . @date(get_option('date_format') . " " . get_option('time_format')) . "\n";
|
1395 |
+
|
1396 |
+
|
1397 |
+
DUP_Log::Info($info);
|
1398 |
+
DUP_Log::Close();
|
1399 |
+
|
1400 |
+
$this->setStatus(DUP_PackageStatus::COMPLETE);
|
1401 |
+
return $this;
|
1402 |
+
}
|
1403 |
+
|
1404 |
+
/**
|
1405 |
+
* Saves the active options associted with the active(latest) package.
|
1406 |
+
*
|
1407 |
+
* @see DUP_Package::getActive
|
1408 |
+
*
|
1409 |
+
* @param $_POST $post The Post server object
|
1410 |
+
*
|
1411 |
+
* @return null
|
1412 |
+
*/
|
1413 |
+
public function saveActive($post = null)
|
1414 |
+
{
|
1415 |
+
global $wp_version;
|
1416 |
+
|
1417 |
+
if (isset($post)) {
|
1418 |
+
$post = stripslashes_deep($post);
|
1419 |
+
|
1420 |
+
$name = isset($post['package-name']) ? trim($post['package-name']) : self::getDefaultName();
|
1421 |
+
$name = str_replace(array(' ', '-'), '_', $name);
|
1422 |
+
$name = str_replace(array('.', ';', ':', "'", '"'), '', $name);
|
1423 |
+
$name = sanitize_file_name($name);
|
1424 |
+
$name = substr(trim($name), 0, 40);
|
1425 |
+
|
1426 |
+
if (isset($post['filter-dirs'])) {
|
1427 |
+
$post_filter_dirs = sanitize_text_field($post['filter-dirs']);
|
1428 |
+
$filter_dirs = $this->Archive->parseDirectoryFilter($post_filter_dirs);
|
1429 |
+
} else {
|
1430 |
+
$filter_dirs = '';
|
1431 |
+
}
|
1432 |
+
|
1433 |
+
if (isset($post['filter-files'])) {
|
1434 |
+
$post_filter_files = sanitize_text_field($post['filter-files']);
|
1435 |
+
$filter_files = $this->Archive->parseFileFilter($post_filter_files);
|
1436 |
+
} else {
|
1437 |
+
$filter_files = '';
|
1438 |
+
}
|
1439 |
+
|
1440 |
+
if (isset($post['filter-exts'])) {
|
1441 |
+
$post_filter_exts = sanitize_text_field($post['filter-exts']);
|
1442 |
+
$filter_exts = $this->Archive->parseExtensionFilter($post_filter_exts);
|
1443 |
+
} else {
|
1444 |
+
$filter_exts = '';
|
1445 |
+
}
|
1446 |
+
|
1447 |
+
$tablelist = '';
|
1448 |
+
if (isset($post['dbtables'])) {
|
1449 |
+
$tablelist = implode(',', $post['dbtables']);
|
1450 |
+
}
|
1451 |
+
|
1452 |
+
if (isset($post['dbcompat'])) {
|
1453 |
+
$compatlist = is_array($post['dbcompat']) ? implode(',', $post['dbcompat']) : sanitize_text_field($post['dbcompat']);
|
1454 |
+
} else {
|
1455 |
+
$compatlist = '';
|
1456 |
+
}
|
1457 |
+
|
1458 |
+
$dbversion = DUP_DB::getVersion();
|
1459 |
+
$dbversion = is_null($dbversion) ? '- unknown -' : sanitize_text_field($dbversion);
|
1460 |
+
$dbcomments = sanitize_text_field(DUP_DB::getVariable('version_comment'));
|
1461 |
+
$dbcomments = is_null($dbcomments) ? '- unknown -' : sanitize_text_field($dbcomments);
|
1462 |
+
|
1463 |
+
//PACKAGE
|
1464 |
+
$this->Created = gmdate("Y-m-d H:i:s");
|
1465 |
+
$this->Version = DUPLICATOR_VERSION;
|
1466 |
+
$this->VersionOS = defined('PHP_OS') ? PHP_OS : 'unknown';
|
1467 |
+
$this->VersionWP = $wp_version;
|
1468 |
+
$this->VersionPHP = phpversion();
|
1469 |
+
$this->VersionDB = sanitize_text_field($dbversion);
|
1470 |
+
$this->Name = sanitize_text_field($name);
|
1471 |
+
$this->Hash = $this->makeHash();
|
1472 |
+
$this->NameHash = sanitize_text_field("{$this->Name}_{$this->Hash}");
|
1473 |
+
|
1474 |
+
$this->Notes = sanitize_textarea_field($post['package-notes']);
|
1475 |
+
//ARCHIVE
|
1476 |
+
$this->Archive->Format = 'ZIP';
|
1477 |
+
$this->Archive->FilterOn = isset($post['filter-on']) ? 1 : 0;
|
1478 |
+
$this->Archive->ExportOnlyDB = isset($post['export-onlydb']) ? 1 : 0;
|
1479 |
+
$this->Archive->FilterDirs = sanitize_textarea_field($filter_dirs);
|
1480 |
+
$this->Archive->FilterFiles = sanitize_textarea_field($filter_files);
|
1481 |
+
$this->Archive->FilterExts = str_replace(array('.', ' '), '', $filter_exts);
|
1482 |
+
//INSTALLER
|
1483 |
+
$this->Installer->OptsDBHost = sanitize_text_field($post['dbhost']);
|
1484 |
+
$this->Installer->OptsDBPort = sanitize_text_field($post['dbport']);
|
1485 |
+
$this->Installer->OptsDBName = sanitize_text_field($post['dbname']);
|
1486 |
+
$this->Installer->OptsDBUser = sanitize_text_field($post['dbuser']);
|
1487 |
+
$this->Installer->OptsDBCharset = sanitize_text_field($post['dbcharset']);
|
1488 |
+
$this->Installer->OptsDBCollation = sanitize_text_field($post['dbcollation']);
|
1489 |
+
$this->Installer->OptsSecureOn = isset($post['secure-on']) ? 1 : 0;
|
1490 |
+
$post_secure_pass = sanitize_text_field($post['secure-pass']);
|
1491 |
+
$this->Installer->OptsSecurePass = DUP_Util::installerScramble($post_secure_pass);
|
1492 |
+
//DATABASE
|
1493 |
+
$this->Database->FilterOn = isset($post['dbfilter-on']) ? 1 : 0;
|
1494 |
+
$this->Database->FilterTables = sanitize_text_field($tablelist);
|
1495 |
+
$this->Database->Compatible = $compatlist;
|
1496 |
+
$this->Database->Comments = sanitize_text_field($dbcomments);
|
1497 |
+
|
1498 |
+
update_option(self::OPT_ACTIVE, $this);
|
1499 |
+
}
|
1500 |
+
}
|
1501 |
+
|
1502 |
+
/**
|
1503 |
+
* Update the serialized package and status in the database
|
1504 |
+
*
|
1505 |
+
* @return void
|
1506 |
+
*/
|
1507 |
+
public function update()
|
1508 |
+
{
|
1509 |
+
global $wpdb;
|
1510 |
+
|
1511 |
+
$this->Status = number_format($this->Status, 1, '.', '');
|
1512 |
+
$this->cleanObjectBeforeSave();
|
1513 |
+
$packageObj = serialize($this);
|
1514 |
+
|
1515 |
+
if (!$packageObj) {
|
1516 |
+
DUP_Log::error("Package SetStatus was unable to serialize package object while updating record.");
|
1517 |
+
}
|
1518 |
+
|
1519 |
+
$wpdb->flush();
|
1520 |
+
$tablePrefix = DUP_Util::getTablePrefix();
|
1521 |
+
$table = $tablePrefix . "duplicator_packages";
|
1522 |
+
$sql = "UPDATE `{$table}` SET status = {$this->Status},";
|
1523 |
+
$sql .= "package = '" . esc_sql($packageObj) . "'";
|
1524 |
+
$sql .= "WHERE ID = {$this->ID}";
|
1525 |
+
|
1526 |
+
DUP_Log::Trace("UPDATE PACKAGE ID = {$this->ID} STATUS = {$this->Status}");
|
1527 |
+
|
1528 |
+
//DUP_Log::Trace('####Executing SQL' . $sql . '-----------');
|
1529 |
+
$wpdb->query($sql);
|
1530 |
+
}
|
1531 |
+
|
1532 |
+
/**
|
1533 |
+
* Save any property of this class through reflection
|
1534 |
+
*
|
1535 |
+
* @param $property A valid public property in this class
|
1536 |
+
* @param $value The value for the new dynamic property
|
1537 |
+
*
|
1538 |
+
* @return null
|
1539 |
+
*/
|
1540 |
+
public function saveActiveItem($property, $value)
|
1541 |
+
{
|
1542 |
+
$package = self::getActive();
|
1543 |
+
|
1544 |
+
$reflectionClass = new ReflectionClass($package);
|
1545 |
+
$reflectionClass->getProperty($property)->setValue($package, $value);
|
1546 |
+
update_option(self::OPT_ACTIVE, $package);
|
1547 |
+
}
|
1548 |
+
|
1549 |
+
/**
|
1550 |
+
* Sets the status to log the state of the build
|
1551 |
+
* The status level for where the package is
|
1552 |
+
*
|
1553 |
+
* @param int $status
|
1554 |
+
*
|
1555 |
+
* @return void
|
1556 |
+
*/
|
1557 |
+
public function setStatus($status)
|
1558 |
+
{
|
1559 |
+
if (!isset($status)) {
|
1560 |
+
DUP_Log::error("Package SetStatus did not receive a proper code.");
|
1561 |
+
}
|
1562 |
+
$this->Status = $status;
|
1563 |
+
$this->update();
|
1564 |
+
}
|
1565 |
+
|
1566 |
+
/**
|
1567 |
+
* Does a hash already exists
|
1568 |
+
* Returns 0 if no hash is found, if found returns the table ID
|
1569 |
+
*
|
1570 |
+
* @param string $hash An existing hash value
|
1571 |
+
*
|
1572 |
+
* @return int
|
1573 |
+
*/
|
1574 |
+
public function getHashKey($hash)
|
1575 |
+
{
|
1576 |
+
global $wpdb;
|
1577 |
+
|
1578 |
+
$tablePrefix = DUP_Util::getTablePrefix();
|
1579 |
+
$table = $tablePrefix . "duplicator_packages";
|
1580 |
+
$qry = $wpdb->get_row("SELECT ID, hash FROM `{$table}` WHERE hash = '{$hash}'");
|
1581 |
+
if (is_null($qry) || strlen($qry->hash) == 0) {
|
1582 |
+
return 0;
|
1583 |
+
} else {
|
1584 |
+
return $qry->ID;
|
1585 |
+
}
|
1586 |
+
}
|
1587 |
+
|
1588 |
+
/**
|
1589 |
+
* Makes the hashkey for the package files
|
1590 |
+
*
|
1591 |
+
* @return string // A unique hashkey
|
1592 |
+
*/
|
1593 |
+
public function makeHash()
|
1594 |
+
{
|
1595 |
+
try {
|
1596 |
+
if (function_exists('random_bytes') && DUP_Util::PHP53()) {
|
1597 |
+
return bin2hex(random_bytes(8)) . mt_rand(1000, 9999) . '_' . date("YmdHis");
|
1598 |
+
} else {
|
1599 |
+
return strtolower(md5(uniqid(rand(), true))) . '_' . date("YmdHis");
|
1600 |
+
}
|
1601 |
+
} catch (Exception $exc) {
|
1602 |
+
return strtolower(md5(uniqid(rand(), true))) . '_' . date("YmdHis");
|
1603 |
+
}
|
1604 |
+
}
|
1605 |
+
|
1606 |
+
/**
|
1607 |
+
* Gets the active package which is defined as the package that was lasted saved.
|
1608 |
+
* Do to cache issues with the built in WP function get_option moved call to a direct DB call.
|
1609 |
+
*
|
1610 |
+
* @see DUP_Package::saveActive
|
1611 |
+
*
|
1612 |
+
* @return DUP_Package // A copy of the DUP_Package object
|
1613 |
+
*/
|
1614 |
+
public static function getActive()
|
1615 |
+
{
|
1616 |
+
global $wpdb;
|
1617 |
+
|
1618 |
+
$obj = new DUP_Package();
|
1619 |
+
$row = $wpdb->get_row($wpdb->prepare("SELECT option_value FROM `{$wpdb->options}` WHERE option_name = %s LIMIT 1", self::OPT_ACTIVE));
|
1620 |
+
if (is_object($row)) {
|
1621 |
+
$obj = @unserialize($row->option_value);
|
1622 |
+
}
|
1623 |
+
//Incase unserilaize fails
|
1624 |
+
$obj = (is_object($obj)) ? $obj : new DUP_Package();
|
1625 |
+
|
1626 |
+
return $obj;
|
1627 |
+
}
|
1628 |
+
|
1629 |
+
/**
|
1630 |
+
* Gets the Package by ID
|
1631 |
+
*
|
1632 |
+
* @param int $id A valid package id form the duplicator_packages table
|
1633 |
+
*
|
1634 |
+
* @return DUP_Package // A copy of the DUP_Package object
|
1635 |
+
*/
|
1636 |
+
public static function getByID($id)
|
1637 |
+
{
|
1638 |
+
global $wpdb;
|
1639 |
+
$obj = new DUP_Package();
|
1640 |
+
$tablePrefix = DUP_Util::getTablePrefix();
|
1641 |
+
$sql = $wpdb->prepare("SELECT * FROM `{$tablePrefix}duplicator_packages` WHERE ID = %d", $id);
|
1642 |
+
$row = $wpdb->get_row($sql);
|
1643 |
+
if (is_object($row)) {
|
1644 |
+
$obj = @unserialize($row->package);
|
1645 |
+
// We was not storing Status in Lite 1.2.52, so it is for backward compatibility
|
1646 |
+
if (!isset($obj->Status)) {
|
1647 |
+
$obj->Status = $row->status;
|
1648 |
+
}
|
1649 |
+
}
|
1650 |
+
//Incase unserilaize fails
|
1651 |
+
$obj = (is_object($obj)) ? $obj : null;
|
1652 |
+
return $obj;
|
1653 |
+
}
|
1654 |
+
|
1655 |
+
/**
|
1656 |
+
* Gets a default name for the package
|
1657 |
+
*
|
1658 |
+
* @return string // A default package name such as 20170218_blogname
|
1659 |
+
*/
|
1660 |
+
public static function getDefaultName($preDate = true)
|
1661 |
+
{
|
1662 |
+
//Remove specail_chars from final result
|
1663 |
+
$special_chars = array(".", "-");
|
1664 |
+
$name = ($preDate)
|
1665 |
+
? date('Ymd') . '_' . sanitize_title(get_bloginfo('name', 'display'))
|
1666 |
+
: sanitize_title(get_bloginfo('name', 'display')) . '_' . date('Ymd');
|
1667 |
+
$name = substr(sanitize_file_name($name), 0, 40);
|
1668 |
+
$name = str_replace($special_chars, '', $name);
|
1669 |
+
return $name;
|
1670 |
+
}
|
1671 |
+
|
1672 |
+
/**
|
1673 |
+
* Cleanup all tmp files
|
1674 |
+
*
|
1675 |
+
* @param all empty all contents
|
1676 |
+
*
|
1677 |
+
* @return null
|
1678 |
+
*/
|
1679 |
+
public static function tempFileCleanup($all = false)
|
1680 |
+
{
|
1681 |
+
if ($all) {
|
1682 |
+
//Delete all files now
|
1683 |
+
$dir = DUP_Settings::getSsdirTmpPath() . "/*";
|
1684 |
+
foreach (glob($dir) as $file) {
|
1685 |
+
@unlink($file);
|
1686 |
+
}
|
1687 |
+
} else {
|
1688 |
+
//Remove scan files that are 24 hours old
|
1689 |
+
$dir = DUP_Settings::getSsdirTmpPath() . "/*_scan.json";
|
1690 |
+
foreach (glob($dir) as $file) {
|
1691 |
+
if (filemtime($file) <= time() - 86400) {
|
1692 |
+
@unlink($file);
|
1693 |
+
}
|
1694 |
+
}
|
1695 |
+
}
|
1696 |
+
}
|
1697 |
+
|
1698 |
+
/**
|
1699 |
+
* Provides various date formats
|
1700 |
+
*
|
1701 |
+
* @param $utcDate created date in the GMT timezone
|
1702 |
+
* @param $format Various date formats to apply
|
1703 |
+
*
|
1704 |
+
* @return string // a formated date based on the $format
|
1705 |
+
*/
|
1706 |
+
public static function getCreatedDateFormat($utcDate, $format = 1)
|
1707 |
+
{
|
1708 |
+
$date = get_date_from_gmt($utcDate);
|
1709 |
+
$date = new DateTime($date);
|
1710 |
+
switch ($format) {
|
1711 |
+
//YEAR
|
1712 |
+
case 1:
|
1713 |
+
return $date->format('Y-m-d H:i');
|
1714 |
+
break;
|
1715 |
+
case 2:
|
1716 |
+
return $date->format('Y-m-d H:i:s');
|
1717 |
+
break;
|
1718 |
+
case 3:
|
1719 |
+
return $date->format('y-m-d H:i');
|
1720 |
+
break;
|
1721 |
+
case 4:
|
1722 |
+
return $date->format('y-m-d H:i:s');
|
1723 |
+
break;
|
1724 |
+
//MONTH
|
1725 |
+
case 5:
|
1726 |
+
return $date->format('m-d-Y H:i');
|
1727 |
+
break;
|
1728 |
+
case 6:
|
1729 |
+
return $date->format('m-d-Y H:i:s');
|
1730 |
+
break;
|
1731 |
+
case 7:
|
1732 |
+
return $date->format('m-d-y H:i');
|
1733 |
+
break;
|
1734 |
+
case 8:
|
1735 |
+
return $date->format('m-d-y H:i:s');
|
1736 |
+
break;
|
1737 |
+
//DAY
|
1738 |
+
case 9:
|
1739 |
+
return $date->format('d-m-Y H:i');
|
1740 |
+
break;
|
1741 |
+
case 10:
|
1742 |
+
return $date->format('d-m-Y H:i:s');
|
1743 |
+
break;
|
1744 |
+
case 11:
|
1745 |
+
return $date->format('d-m-y H:i');
|
1746 |
+
break;
|
1747 |
+
case 12:
|
1748 |
+
return $date->format('d-m-y H:i:s');
|
1749 |
+
break;
|
1750 |
+
default:
|
1751 |
+
return $date->format('Y-m-d H:i');
|
1752 |
+
}
|
1753 |
+
}
|
1754 |
+
|
1755 |
+
/**
|
1756 |
+
* Cleans up all the tmp files as part of the package build process
|
1757 |
+
*/
|
1758 |
+
public function buildCleanup()
|
1759 |
+
{
|
1760 |
+
$files = DUP_Util::listFiles(DUP_Settings::getSsdirTmpPath());
|
1761 |
+
$newPath = DUP_Settings::getSsdirPath();
|
1762 |
+
|
1763 |
+
$filesToStore = array(
|
1764 |
+
$this->Installer->File,
|
1765 |
+
$this->Archive->File,
|
1766 |
+
);
|
1767 |
+
|
1768 |
+
foreach ($files as $file) {
|
1769 |
+
$fileName = basename($file);
|
1770 |
+
if (!strstr($fileName, $this->NameHash)) {
|
1771 |
+
continue;
|
1772 |
+
}
|
1773 |
+
|
1774 |
+
if (in_array($fileName, $filesToStore)) {
|
1775 |
+
if (function_exists('rename')) {
|
1776 |
+
rename($file, "{$newPath}/{$fileName}");
|
1777 |
+
} elseif (function_exists('copy')) {
|
1778 |
+
copy($file, "{$newPath}/{$fileName}");
|
1779 |
+
} else {
|
1780 |
+
throw new Exception('PHP copy and rename functions not found! Contact hosting provider!');
|
1781 |
+
}
|
1782 |
+
}
|
1783 |
+
|
1784 |
+
if (file_exists($file)) {
|
1785 |
+
unlink($file);
|
1786 |
+
}
|
1787 |
+
}
|
1788 |
+
}
|
1789 |
+
|
1790 |
+
|
1791 |
+
|
1792 |
+
/**
|
1793 |
+
* Get package hash
|
1794 |
+
*
|
1795 |
+
* @return string package hash
|
1796 |
+
*/
|
1797 |
+
public function getPackageHash()
|
1798 |
+
{
|
1799 |
+
$hashParts = explode('_', $this->Hash);
|
1800 |
+
$firstPart = substr($hashParts[0], 0, 7);
|
1801 |
+
$secondPart = substr($hashParts[1], -8);
|
1802 |
+
$package_hash = $firstPart . '-' . $secondPart;
|
1803 |
+
return $package_hash;
|
1804 |
+
}
|
1805 |
+
|
1806 |
+
public function getSecondaryPackageHash()
|
1807 |
+
{
|
1808 |
+
$newHash = $this->makeHash();
|
1809 |
+
$hashParts = explode('_', $newHash);
|
1810 |
+
$firstPart = substr($hashParts[0], 0, 7);
|
1811 |
+
|
1812 |
+
$hashParts = explode('_', $this->Hash);
|
1813 |
+
$secondPart = substr($hashParts[1], -8);
|
1814 |
+
|
1815 |
+
$package_hash = $firstPart . '-' . $secondPart;
|
1816 |
+
return $package_hash;
|
1817 |
+
}
|
1818 |
+
|
1819 |
+
/**
|
1820 |
+
* Provides the full sql file path in archive
|
1821 |
+
*
|
1822 |
+
* @return the full sql file path in archive
|
1823 |
+
*/
|
1824 |
+
public function getSqlArkFilePath()
|
1825 |
+
{
|
1826 |
+
$package_hash = $this->getPackageHash();
|
1827 |
+
$sql_ark_file_Path = 'dup-installer/dup-database__' . $package_hash . '.sql';
|
1828 |
+
return $sql_ark_file_Path;
|
1829 |
+
}
|
1830 |
+
|
1831 |
+
private function writeLogHeader()
|
1832 |
+
{
|
1833 |
+
$php_max_time = @ini_get("max_execution_time");
|
1834 |
+
if (SnapUtil::isIniValChangeable('memory_limit')) {
|
1835 |
+
$php_max_memory = @ini_set('memory_limit', DUPLICATOR_PHP_MAX_MEMORY);
|
1836 |
+
} else {
|
1837 |
+
$php_max_memory = @ini_get('memory_limit');
|
1838 |
+
}
|
1839 |
+
|
1840 |
+
$php_max_time = ($php_max_time == 0) ? "(0) no time limit imposed" : "[{$php_max_time}] not allowed";
|
1841 |
+
$php_max_memory = ($php_max_memory === false) ? "Unabled to set php memory_limit" : DUPLICATOR_PHP_MAX_MEMORY . " ({$php_max_memory} default)";
|
1842 |
+
|
1843 |
+
$info = "********************************************************************************\n";
|
1844 |
+
$info .= "DUPLICATOR-LITE PACKAGE-LOG: " . @date(get_option('date_format') . " " . get_option('time_format')) . "\n";
|
1845 |
+
$info .= "NOTICE: Do NOT post to public sites or forums \n";
|
1846 |
+
$info .= "********************************************************************************\n";
|
1847 |
+
$info .= "VERSION:\t" . DUPLICATOR_VERSION . "\n";
|
1848 |
+
$info .= "WORDPRESS:\t{$GLOBALS['wp_version']}\n";
|
1849 |
+
$info .= "PHP INFO:\t" . phpversion() . ' | ' . 'SAPI: ' . php_sapi_name() . "\n";
|
1850 |
+
$info .= "SERVER:\t\t{$_SERVER['SERVER_SOFTWARE']} \n";
|
1851 |
+
$info .= "PHP TIME LIMIT: {$php_max_time} \n";
|
1852 |
+
$info .= "PHP MAX MEMORY: {$php_max_memory} \n";
|
1853 |
+
$info .= "MEMORY STACK: " . DUP_Server::getPHPMemory();
|
1854 |
+
DUP_Log::Info($info);
|
1855 |
+
|
1856 |
+
$info = null;
|
1857 |
+
}
|
1858 |
+
}
|
classes/package/duparchive/class.pack.archive.duparchive.php
CHANGED
@@ -1,313 +1,268 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
require_once
|
9 |
-
require_once
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
$
|
45 |
-
;
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
$
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
$
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
$
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
$
|
139 |
-
$
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
if ($
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
$
|
212 |
-
|
213 |
-
$
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
$
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
$expandState->
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
$
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
// Fail immediately if critical failure present - even if havent completed processing the entire archive.
|
270 |
-
$error_message = __('Critical failure present in validation', 'duplicator');
|
271 |
-
DUP_Log::error($error_message, $expandState->getFailureSummary(), Dup_ErrorBehavior::LogOnly);
|
272 |
-
$buildProgress->set_failed($error_message);
|
273 |
-
return true;
|
274 |
-
} else if (!$expandState->working) {
|
275 |
-
|
276 |
-
$buildProgress->archive_built = true;
|
277 |
-
$buildProgress->retries = 0;
|
278 |
-
$package->update();
|
279 |
-
|
280 |
-
$timerAllEnd = DUP_Util::getMicrotime();
|
281 |
-
$timerAllSum = DUP_Util::elapsedTime($timerAllEnd, $package->TimerStart);
|
282 |
-
|
283 |
-
DUP_LOG::traceObject("create state", $createState);
|
284 |
-
|
285 |
-
$archiveFileSize = @filesize($archivePath);
|
286 |
-
DUP_Log::info("COMPRESSED SIZE: ".DUP_Util::byteSize($archiveFileSize));
|
287 |
-
DUP_Log::info("ARCHIVE RUNTIME: {$timerAllSum}");
|
288 |
-
DUP_Log::info("MEMORY STACK: ".DUP_Server::getPHPMemory());
|
289 |
-
DUP_Log::info("CREATE WARNINGS: ".$createState->getFailureSummary(false, true));
|
290 |
-
DUP_Log::info("VALIDATION WARNINGS: ".$expandState->getFailureSummary(false, true));
|
291 |
-
|
292 |
-
$archive->file_count = $expandState->fileWriteCount + $expandState->directoryWriteCount;
|
293 |
-
$package->update();
|
294 |
-
$done = true;
|
295 |
-
} else {
|
296 |
-
$expandState->save();
|
297 |
-
}
|
298 |
-
}
|
299 |
-
}
|
300 |
-
}
|
301 |
-
catch (Exception $ex) {
|
302 |
-
// Have to have a catchall since the main system that calls this function is not prepared to handle exceptions
|
303 |
-
DUP_Log::trace('Top level create Exception:'.$ex->getMessage().':'.$ex->getTraceAsString());
|
304 |
-
//$buildProgress->failed = true;
|
305 |
-
$buildProgress->set_failed('Error encoundtered creating archive. See package log');
|
306 |
-
return true;
|
307 |
-
}
|
308 |
-
|
309 |
-
$buildProgress->retries = 0;
|
310 |
-
|
311 |
-
return $done;
|
312 |
-
}
|
313 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
use Duplicator\Libs\DupArchive\DupArchiveEngine;
|
4 |
+
use Duplicator\Libs\DupArchive\DupArchiveLoggerBase;
|
5 |
+
use Duplicator\Libs\DupArchive\States\DupArchiveExpandState;
|
6 |
+
use Duplicator\Libs\Snap\SnapUtil;
|
7 |
+
|
8 |
+
require_once(DUPLICATOR_PLUGIN_PATH . 'classes/package/duparchive/class.pack.archive.duparchive.state.expand.php');
|
9 |
+
require_once(DUPLICATOR_PLUGIN_PATH . 'classes/package/duparchive/class.pack.archive.duparchive.state.create.php');
|
10 |
+
|
11 |
+
class DUP_DupArchive_Logger extends DupArchiveLoggerBase
|
12 |
+
{
|
13 |
+
public function log($s, $flush = false, $callingFunctionOverride = null)
|
14 |
+
{
|
15 |
+
DUP_Log::Trace($s, true, $callingFunctionOverride);
|
16 |
+
}
|
17 |
+
}
|
18 |
+
|
19 |
+
class DUP_DupArchive
|
20 |
+
{
|
21 |
+
// Using a worker time override since evidence shorter time works much
|
22 |
+
const WORKER_TIME_IN_SEC = 10;
|
23 |
+
/**
|
24 |
+
* CREATE
|
25 |
+
* Creates the zip file and adds the SQL file to the archive
|
26 |
+
*/
|
27 |
+
public static function create(DUP_Archive $archive, $buildProgress, $package)
|
28 |
+
{
|
29 |
+
/* @var $buildProgress DUP_Build_Progress */
|
30 |
+
|
31 |
+
DUP_LOG::trace("start");
|
32 |
+
try {
|
33 |
+
DUP_Log::Open($package->NameHash);
|
34 |
+
if ($buildProgress->retries > DUPLICATOR_MAX_BUILD_RETRIES) {
|
35 |
+
$error_msg = __('Package build appears stuck so marking package as failed. Is the Max Worker Time set too high?.', 'duplicator');
|
36 |
+
DUP_Log::error(esc_html__('Build Failure', 'duplicator'), esc_html($error_msg), Dup_ErrorBehavior::LogOnly);
|
37 |
+
//$buildProgress->failed = true;
|
38 |
+
$buildProgress->set_failed($error_msg);
|
39 |
+
$package->setStatus(DUP_PackageStatus::ERROR);
|
40 |
+
;
|
41 |
+
return true;
|
42 |
+
} else {
|
43 |
+
// If all goes well retries will be reset to 0 at the end of this function.
|
44 |
+
$buildProgress->retries++;
|
45 |
+
$package->update();
|
46 |
+
}
|
47 |
+
|
48 |
+
$done = false;
|
49 |
+
DupArchiveEngine::init(new DUP_DupArchive_Logger(), $archive->getTargetRootPath());
|
50 |
+
DUP_Package::safeTmpCleanup(true);
|
51 |
+
$compressDir = rtrim(DUP_Util::safePath($archive->PackDir), '/');
|
52 |
+
$sqlPath = DUP_Settings::getSsdirTmpPath() . "/{$package->Database->File}";
|
53 |
+
$archivePath = DUP_Settings::getSsdirTmpPath() . "/{$archive->File}";
|
54 |
+
$scanFilepath = DUP_Settings::getSsdirTmpPath() . "/{$package->NameHash}_scan.json";
|
55 |
+
$skipArchiveFinalization = false;
|
56 |
+
$json = '';
|
57 |
+
if (file_exists($scanFilepath)) {
|
58 |
+
$json = file_get_contents($scanFilepath);
|
59 |
+
if (empty($json)) {
|
60 |
+
$errorText = __("Scan file $scanFilepath is empty!", 'duplicator');
|
61 |
+
$fixText = __("Click on \"Resolve This\" button to fix the JSON settings.", 'duplicator');
|
62 |
+
DUP_Log::Trace($errorText);
|
63 |
+
DUP_Log::error(esc_html($errorText) . " **RECOMMENDATION: " . esc_html($fixText) . ".", '', Dup_ErrorBehavior::LogOnly);
|
64 |
+
//$buildProgress->failed = true;
|
65 |
+
$buildProgress->set_failed($errorText);
|
66 |
+
$package->setStatus(DUP_PackageStatus::ERROR);
|
67 |
+
return true;
|
68 |
+
}
|
69 |
+
} else {
|
70 |
+
DUP_Log::trace("**** scan file $scanFilepath doesn't exist!!");
|
71 |
+
$errorMessage = sprintf(__("ERROR: Can't find Scanfile %s. Please ensure there no non-English characters in the package or schedule name.", 'duplicator'), $scanFilepath);
|
72 |
+
DUP_Log::error($errorMessage, '', Dup_ErrorBehavior::LogOnly);
|
73 |
+
//$buildProgress->failed = true;
|
74 |
+
$buildProgress->set_failed($errorMessage);
|
75 |
+
$package->setStatus(DUP_PackageStatus::ERROR);
|
76 |
+
return true;
|
77 |
+
}
|
78 |
+
|
79 |
+
Dup_Log::TraceObject("buildprogress object", $buildProgress, false);
|
80 |
+
$scanReport = json_decode($json);
|
81 |
+
if ($buildProgress->archive_started == false) {
|
82 |
+
$filterDirs = empty($archive->FilterDirs) ? 'not set' : $archive->FilterDirs;
|
83 |
+
$filterExts = empty($archive->FilterExts) ? 'not set' : $archive->FilterExts;
|
84 |
+
$filterFiles = empty($archive->FilterFiles) ? 'not set' : $archive->FilterFiles;
|
85 |
+
$filterOn = ($archive->FilterOn) ? 'ON' : 'OFF';
|
86 |
+
$filterDirsFormat = rtrim(str_replace(';', "\n\t", $filterDirs));
|
87 |
+
$filterFilesFormat = rtrim(str_replace(';', "\n\t", $filterFiles));
|
88 |
+
DUP_Log::info("\n********************************************************************************");
|
89 |
+
DUP_Log::info("ARCHIVE Type=DUP Mode=DupArchive");
|
90 |
+
DUP_Log::info("********************************************************************************");
|
91 |
+
DUP_Log::info("ARCHIVE DIR: " . $compressDir);
|
92 |
+
DUP_Log::info("ARCHIVE FILE: " . basename($archivePath));
|
93 |
+
DUP_Log::info("FILTERS: *{$filterOn}*");
|
94 |
+
DUP_Log::Info("DIRS:\n\t{$filterDirsFormat}");
|
95 |
+
DUP_Log::Info("FILES:\n\t{$filterFilesFormat}");
|
96 |
+
DUP_Log::info("EXTS: {$filterExts}");
|
97 |
+
DUP_Log::info("----------------------------------------");
|
98 |
+
DUP_Log::info("COMPRESSING");
|
99 |
+
DUP_Log::info("SIZE:\t" . $scanReport->ARC->Size);
|
100 |
+
DUP_Log::info("STATS:\tDirs " . $scanReport->ARC->DirCount . " | Files " . $scanReport->ARC->FileCount . " | Total " . $scanReport->ARC->FullCount);
|
101 |
+
if (($scanReport->ARC->DirCount == '') || ($scanReport->ARC->FileCount == '') || ($scanReport->ARC->FullCount == '')) {
|
102 |
+
$error_message = 'Invalid Scan Report Detected';
|
103 |
+
DUP_Log::error($error_message, 'Invalid Scan Report Detected', Dup_ErrorBehavior::LogOnly);
|
104 |
+
$buildProgress->set_failed($error_message);
|
105 |
+
$package->setStatus(DUP_PackageStatus::ERROR);
|
106 |
+
return true;
|
107 |
+
}
|
108 |
+
|
109 |
+
try {
|
110 |
+
DupArchiveEngine::createArchive($archivePath, true);
|
111 |
+
$sql_ark_file_path = $package->getSqlArkFilePath();
|
112 |
+
DupArchiveEngine::addRelativeFileToArchiveST($archivePath, $sqlPath, $sql_ark_file_path);
|
113 |
+
} catch (Exception $ex) {
|
114 |
+
$error_message = 'Error adding database.sql to archive';
|
115 |
+
DUP_Log::error($error_message, $ex->getMessage(), Dup_ErrorBehavior::LogOnly);
|
116 |
+
$buildProgress->set_failed($error_message);
|
117 |
+
$package->setStatus(DUP_PackageStatus::ERROR);
|
118 |
+
return true;
|
119 |
+
}
|
120 |
+
|
121 |
+
$buildProgress->archive_started = true;
|
122 |
+
$buildProgress->retries = 0;
|
123 |
+
$createState = DUP_DupArchive_Create_State::createNew($archivePath, $compressDir, self::WORKER_TIME_IN_SEC, true, true);
|
124 |
+
$createState->throttleDelayInUs = 0;
|
125 |
+
$createState->save();
|
126 |
+
$package->Update();
|
127 |
+
}
|
128 |
+
|
129 |
+
try {
|
130 |
+
$createState = DUP_DupArchive_Create_State::get_instance();
|
131 |
+
if ($buildProgress->retries > 1) {
|
132 |
+
$createState->isRobust = true;
|
133 |
+
$createState->save();
|
134 |
+
}
|
135 |
+
|
136 |
+
if ($createState->working) {
|
137 |
+
DUP_LOG::Trace("Create state is working");
|
138 |
+
DupArchiveEngine::addItemsToArchive($createState, $scanReport->ARC);
|
139 |
+
$buildProgress->set_build_failures($createState->failures);
|
140 |
+
if ($createState->isCriticalFailurePresent()) {
|
141 |
+
throw new Exception($createState->getFailureSummary());
|
142 |
+
}
|
143 |
+
|
144 |
+
$totalFileCount = count($scanReport->ARC->Files);
|
145 |
+
$package->Status = SnapUtil::getWorkPercent(DUP_PackageStatus::ARCSTART, DUP_PackageStatus::ARCVALIDATION, $totalFileCount, $createState->currentFileIndex);
|
146 |
+
$buildProgress->retries = 0;
|
147 |
+
$createState->save();
|
148 |
+
DUP_LOG::TraceObject("Stored Create State", $createState);
|
149 |
+
DUP_LOG::TraceObject('Stored build_progress', $package->BuildProgress);
|
150 |
+
if ($createState->working == false) {
|
151 |
+
// Want it to do the final cleanup work in an entirely new thread so return immediately
|
152 |
+
$skipArchiveFinalization = true;
|
153 |
+
DUP_LOG::TraceObject("Done build phase. Create State=", $createState);
|
154 |
+
}
|
155 |
+
}
|
156 |
+
} catch (Exception $ex) {
|
157 |
+
$message = __('Problem adding items to archive.', 'duplicator') . ' ' . $ex->getMessage();
|
158 |
+
DUP_Log::error(__('Problems adding items to archive.', 'duplicator'), $message, Dup_ErrorBehavior::LogOnly);
|
159 |
+
DUP_Log::TraceObject($message . " EXCEPTION:", $ex);
|
160 |
+
//$buildProgress->failed = true;
|
161 |
+
$buildProgress->set_failed($message);
|
162 |
+
$package->setStatus(DUP_PackageStatus::ERROR);
|
163 |
+
return true;
|
164 |
+
}
|
165 |
+
|
166 |
+
//-- Final Wrapup of the Archive
|
167 |
+
if ((!$skipArchiveFinalization) && ($createState->working == false)) {
|
168 |
+
DUP_LOG::Trace("Create state is not working and not skip archive finalization");
|
169 |
+
if (!$buildProgress->installer_built) {
|
170 |
+
if ($package->Installer->build($package, false)) {
|
171 |
+
$package->Runtime = -1;
|
172 |
+
$package->ExeSize = DUP_Util::byteSize($package->Installer->Size);
|
173 |
+
$package->ZipSize = DUP_Util::byteSize($package->Archive->Size);
|
174 |
+
$package->update();
|
175 |
+
} else {
|
176 |
+
$package->update();
|
177 |
+
return;
|
178 |
+
}
|
179 |
+
|
180 |
+
DUP_Log::Trace("Installer has been built so running expand now");
|
181 |
+
$expandState = DUP_DupArchive_Expand_State::getInstance(true);
|
182 |
+
$expandState->archivePath = $archivePath;
|
183 |
+
$expandState->working = true;
|
184 |
+
$expandState->timeSliceInSecs = self::WORKER_TIME_IN_SEC;
|
185 |
+
$expandState->basePath = DUP_Settings::getSsdirTmpPath() . '/validate';
|
186 |
+
$expandState->throttleDelayInUs = 0;
|
187 |
+
// RSR TODO
|
188 |
+
$expandState->validateOnly = true;
|
189 |
+
$expandState->validationType = DupArchiveExpandState::VALIDATION_NONE;
|
190 |
+
$expandState->working = true;
|
191 |
+
$expandState->expectedDirectoryCount = count($scanReport->ARC->Dirs) - $createState->skippedDirectoryCount + $package->Installer->numDirsAdded;
|
192 |
+
$expandState->expectedFileCount = 1 + count($scanReport->ARC->Files) + 1 - $createState->skippedFileCount + $package->Installer->numFilesAdded;
|
193 |
+
// database.sql will be in there
|
194 |
+
|
195 |
+
$expandState->save();
|
196 |
+
$sfc = count($scanReport->ARC->Files);
|
197 |
+
$nfa = $package->Installer->numFilesAdded;
|
198 |
+
Dup_Log::trace("####scan files {$sfc} skipped files {$createState->skippedFileCount} num files added {$nfa}");
|
199 |
+
DUP_LOG::traceObject("EXPAND STATE AFTER SAVE", $expandState);
|
200 |
+
} else {
|
201 |
+
try {
|
202 |
+
$expandState = DUP_DupArchive_Expand_State::getInstance();
|
203 |
+
if ($buildProgress->retries > 1) {
|
204 |
+
// Indicates it had problems before so move into robustness mode
|
205 |
+
$expandState->isRobust = true;
|
206 |
+
$expandState->save();
|
207 |
+
}
|
208 |
+
|
209 |
+
DUP_Log::traceObject('Resumed validation expand state', $expandState);
|
210 |
+
DupArchiveEngine::expandArchive($expandState);
|
211 |
+
$buildProgress->set_validation_failures($expandState->failures);
|
212 |
+
$totalFileCount = count($scanReport->ARC->Files);
|
213 |
+
$archiveSize = @filesize($expandState->archivePath);
|
214 |
+
|
215 |
+
$package->Status = SnapUtil::getWorkPercent(
|
216 |
+
DUP_PackageStatus::ARCVALIDATION,
|
217 |
+
DUP_PackageStatus::COMPLETE,
|
218 |
+
$archiveSize,
|
219 |
+
$expandState->archiveOffset
|
220 |
+
);
|
221 |
+
DUP_LOG::TraceObject("package status after expand=", $package->Status);
|
222 |
+
DUP_LOG::Trace("archive size:{$archiveSize} expand offset:{$expandState->archiveOffset}");
|
223 |
+
} catch (Exception $ex) {
|
224 |
+
DUP_Log::Trace('Exception:' . $ex->getMessage() . ':' . $ex->getTraceAsString());
|
225 |
+
$buildProgress->set_failed('Error validating archive');
|
226 |
+
$package->setStatus(DUP_PackageStatus::ERROR);
|
227 |
+
return true;
|
228 |
+
}
|
229 |
+
|
230 |
+
if ($expandState->isCriticalFailurePresent()) {
|
231 |
+
// Fail immediately if critical failure present - even if havent completed processing the entire archive.
|
232 |
+
$error_message = __('Critical failure present in validation', 'duplicator');
|
233 |
+
DUP_Log::error($error_message, $expandState->getFailureSummary(), Dup_ErrorBehavior::LogOnly);
|
234 |
+
$buildProgress->set_failed($error_message);
|
235 |
+
return true;
|
236 |
+
} elseif (!$expandState->working) {
|
237 |
+
$buildProgress->archive_built = true;
|
238 |
+
$buildProgress->retries = 0;
|
239 |
+
$package->update();
|
240 |
+
$timerAllEnd = DUP_Util::getMicrotime();
|
241 |
+
$timerAllSum = DUP_Util::elapsedTime($timerAllEnd, $package->TimerStart);
|
242 |
+
DUP_LOG::traceObject("create state", $createState);
|
243 |
+
$archiveFileSize = @filesize($archivePath);
|
244 |
+
DUP_Log::info("COMPRESSED SIZE: " . DUP_Util::byteSize($archiveFileSize));
|
245 |
+
DUP_Log::info("ARCHIVE RUNTIME: {$timerAllSum}");
|
246 |
+
DUP_Log::info("MEMORY STACK: " . DUP_Server::getPHPMemory());
|
247 |
+
DUP_Log::info("CREATE WARNINGS: " . $createState->getFailureSummary(false, true));
|
248 |
+
DUP_Log::info("VALIDATION WARNINGS: " . $expandState->getFailureSummary(false, true));
|
249 |
+
$archive->file_count = $expandState->fileWriteCount + $expandState->directoryWriteCount;
|
250 |
+
$package->update();
|
251 |
+
$done = true;
|
252 |
+
} else {
|
253 |
+
$expandState->save();
|
254 |
+
}
|
255 |
+
}
|
256 |
+
}
|
257 |
+
} catch (Exception $ex) {
|
258 |
+
// Have to have a catchall since the main system that calls this function is not prepared to handle exceptions
|
259 |
+
DUP_Log::trace('Top level create Exception:' . $ex->getMessage() . ':' . $ex->getTraceAsString());
|
260 |
+
//$buildProgress->failed = true;
|
261 |
+
$buildProgress->set_failed('Error encoundtered creating archive. See package log');
|
262 |
+
return true;
|
263 |
+
}
|
264 |
+
|
265 |
+
$buildProgress->retries = 0;
|
266 |
+
return $done;
|
267 |
+
}
|
268 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
classes/package/duparchive/class.pack.archive.duparchive.state.create.php
CHANGED
@@ -1,80 +1,66 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
{
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
$instance
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
$instance
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
$instance->
|
51 |
-
$instance->
|
52 |
-
$instance
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
$
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
$
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
public function addFailure($type, $subject, $description, $isCritical = false)
|
69 |
-
{
|
70 |
-
parent::addFailure($type, $subject, $description, $isCritical);
|
71 |
-
}
|
72 |
-
|
73 |
-
public function save()
|
74 |
-
{
|
75 |
-
DUP_Log::TraceObject("Saving create state", $this);
|
76 |
-
DUP_Settings::Set('duparchive_create_state', $this);
|
77 |
-
|
78 |
-
DUP_Settings::Save();
|
79 |
-
}
|
80 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
use Duplicator\Libs\DupArchive\States\DupArchiveCreateState;
|
4 |
+
|
5 |
+
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
6 |
+
|
7 |
+
class DUP_DupArchive_Create_State extends DupArchiveCreateState
|
8 |
+
{
|
9 |
+
/**
|
10 |
+
* Class constructor
|
11 |
+
*/
|
12 |
+
public function __construct()
|
13 |
+
{
|
14 |
+
parent::__construct();
|
15 |
+
$this->throttleDelayInUs = 10;
|
16 |
+
}
|
17 |
+
|
18 |
+
// Only one active package so straightforward
|
19 |
+
// public static function createFromPackage(&$package)
|
20 |
+
public static function get_instance()
|
21 |
+
{
|
22 |
+
$instance = new DUP_DupArchive_Create_State();
|
23 |
+
$data = DUP_Settings::Get('duparchive_create_state');
|
24 |
+
DUP_Util::objectCopy($data, $instance);
|
25 |
+
$instance->startTimestamp = time();
|
26 |
+
DUP_Log::TraceObject("retrieving create state", $instance);
|
27 |
+
return $instance;
|
28 |
+
}
|
29 |
+
|
30 |
+
public static function createNew($archivePath, $basePath, $timeSliceInSecs, $isCompressed, $setArchiveOffsetToEndOfArchive)
|
31 |
+
{
|
32 |
+
$instance = new DUP_DupArchive_Create_State();
|
33 |
+
if ($setArchiveOffsetToEndOfArchive) {
|
34 |
+
$instance->archiveOffset = filesize($archivePath);
|
35 |
+
} else {
|
36 |
+
$instance->archiveOffset = 0;
|
37 |
+
}
|
38 |
+
|
39 |
+
$instance->archivePath = $archivePath;
|
40 |
+
$instance->basePath = $basePath;
|
41 |
+
$instance->currentDirectoryIndex = 0;
|
42 |
+
$instance->currentFileOffset = 0;
|
43 |
+
$instance->currentFileIndex = 0;
|
44 |
+
$instance->failures = array();
|
45 |
+
$instance->globSize = DupArchiveCreateState::DEFAULT_GLOB_SIZE;
|
46 |
+
$instance->isCompressed = $isCompressed;
|
47 |
+
$instance->timeSliceInSecs = $timeSliceInSecs;
|
48 |
+
$instance->working = true;
|
49 |
+
$instance->skippedDirectoryCount = 0;
|
50 |
+
$instance->skippedFileCount = 0;
|
51 |
+
$instance->startTimestamp = time();
|
52 |
+
return $instance;
|
53 |
+
}
|
54 |
+
|
55 |
+
public function addFailure($type, $subject, $description, $isCritical = false)
|
56 |
+
{
|
57 |
+
parent::addFailure($type, $subject, $description, $isCritical);
|
58 |
+
}
|
59 |
+
|
60 |
+
public function save()
|
61 |
+
{
|
62 |
+
DUP_Log::TraceObject("Saving create state", $this);
|
63 |
+
DUP_Settings::Set('duparchive_create_state', $this);
|
64 |
+
DUP_Settings::Save();
|
65 |
+
}
|
66 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
classes/package/duparchive/class.pack.archive.duparchive.state.expand.php
CHANGED
@@ -1,133 +1,88 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
//
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
$
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
// Object members auto skipped
|
90 |
-
DUP_Util::objectCopy($this, $data);
|
91 |
-
|
92 |
-
// $data->archiveOffset = $this->archiveOffset;
|
93 |
-
// $data->archivePath = $this->archivePath;
|
94 |
-
// $data->basePath = $this->basePath;
|
95 |
-
// $data->currentFileOffset = $this->currentFileOffset;
|
96 |
-
// $data->failures = $this->failures;
|
97 |
-
// $data->isCompressed = $this->isCompressed;
|
98 |
-
// $data->startTimestamp = $this->startTimestamp;
|
99 |
-
// $data->timeSliceInSecs = $this->timeSliceInSecs;
|
100 |
-
// $data->fileWriteCount = $this->fileWriteCount;
|
101 |
-
// $data->directoryWriteCount = $this->directoryWriteCount;
|
102 |
-
// $data->working = $this->working;
|
103 |
-
// $data->directoryModeOverride = $this->directoryModeOverride;
|
104 |
-
// $data->fileModeOverride = $this->fileModeOverride;
|
105 |
-
// $data->throttleDelayInUs = $this->throttleDelayInUs;
|
106 |
-
// $data->validateOnly = $this->validateOnly;
|
107 |
-
// $data->validationType = $this->validationType;
|
108 |
-
|
109 |
-
DUP_LOG::traceObject("****SAVING EXPAND STATE****", $this);
|
110 |
-
DUP_LOG::traceObject("****SERIALIZED STATE****", $data);
|
111 |
-
DUP_Settings::Set('duparchive_expand_state', $data);
|
112 |
-
DUP_Settings::Save();
|
113 |
-
}
|
114 |
-
|
115 |
-
private function initMembers()
|
116 |
-
{
|
117 |
-
$this->currentFileHeader = null;
|
118 |
-
$this->archiveOffset = 0;
|
119 |
-
$this->archiveHeader = null;
|
120 |
-
$this->archivePath = null;
|
121 |
-
$this->basePath = null;
|
122 |
-
$this->currentFileOffset = 0;
|
123 |
-
$this->failures = array();
|
124 |
-
$this->isCompressed = false;
|
125 |
-
$this->startTimestamp = time();
|
126 |
-
$this->timeSliceInSecs = -1;
|
127 |
-
$this->working = false;
|
128 |
-
$this->validateOnly = false;
|
129 |
-
$this->directoryModeOverride = -1;
|
130 |
-
$this->fileModeOverride = -1;
|
131 |
-
$this->throttleDelayInUs = 0;
|
132 |
-
}
|
133 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
use Duplicator\Libs\DupArchive\States\DupArchiveExpandState;
|
4 |
+
use Duplicator\Libs\Snap\SnapJson;
|
5 |
+
|
6 |
+
class DUP_DupArchive_Expand_State extends DupArchiveExpandState
|
7 |
+
{
|
8 |
+
public static function getInstance($reset = false)
|
9 |
+
{
|
10 |
+
$instance = new DUP_DupArchive_Expand_State();
|
11 |
+
if ($reset) {
|
12 |
+
$instance->initMembers();
|
13 |
+
} else {
|
14 |
+
$instance->loadMembers();
|
15 |
+
}
|
16 |
+
|
17 |
+
return $instance;
|
18 |
+
}
|
19 |
+
|
20 |
+
private function loadMembers()
|
21 |
+
{
|
22 |
+
/** @var object $data */
|
23 |
+
$data = DUP_Settings::Get('duparchive_expand_state');
|
24 |
+
DUP_LOG::traceObject("****RAW EXPAND STATE LOADED****", $data);
|
25 |
+
if ($data->currentFileHeaderString != null) {
|
26 |
+
$this->currentFileHeader = DUP_JSON::decode($data->currentFileHeaderString);
|
27 |
+
} else {
|
28 |
+
$this->currentFileHeader = null;
|
29 |
+
}
|
30 |
+
|
31 |
+
if ($data->archiveHeaderString != null) {
|
32 |
+
$this->archiveHeader = DUP_JSON::decode($data->archiveHeaderString);
|
33 |
+
} else {
|
34 |
+
$this->archiveHeader = null;
|
35 |
+
}
|
36 |
+
|
37 |
+
if ($data->failuresString) {
|
38 |
+
$this->failures = DUP_JSON::decode($data->failuresString);
|
39 |
+
} else {
|
40 |
+
$this->failures = array();
|
41 |
+
}
|
42 |
+
|
43 |
+
DUP_Util::objectCopy($data, $this, array('archiveHeaderString', 'currentFileHeaderString', 'failuresString'));
|
44 |
+
}
|
45 |
+
|
46 |
+
public function save()
|
47 |
+
{
|
48 |
+
$data = new stdClass();
|
49 |
+
if ($this->currentFileHeader != null) {
|
50 |
+
$data->currentFileHeaderString = SnapJson::jsonEncode($this->currentFileHeader);
|
51 |
+
} else {
|
52 |
+
$data->currentFileHeaderString = null;
|
53 |
+
}
|
54 |
+
|
55 |
+
if ($this->archiveHeader != null) {
|
56 |
+
$data->archiveHeaderString = SnapJson::jsonEncode($this->archiveHeader);
|
57 |
+
} else {
|
58 |
+
$data->archiveHeaderString = null;
|
59 |
+
}
|
60 |
+
|
61 |
+
$data->failuresString = SnapJson::jsonEncode($this->failures);
|
62 |
+
// Object members auto skipped
|
63 |
+
DUP_Util::objectCopy($this, $data);
|
64 |
+
DUP_LOG::traceObject("****SAVING EXPAND STATE****", $this);
|
65 |
+
DUP_LOG::traceObject("****SERIALIZED STATE****", $data);
|
66 |
+
DUP_Settings::Set('duparchive_expand_state', $data);
|
67 |
+
DUP_Settings::Save();
|
68 |
+
}
|
69 |
+
|
70 |
+
private function initMembers()
|
71 |
+
{
|
72 |
+
$this->currentFileHeader = null;
|
73 |
+
$this->archiveOffset = 0;
|
74 |
+
$this->archiveHeader = null;
|
75 |
+
$this->archivePath = null;
|
76 |
+
$this->basePath = null;
|
77 |
+
$this->currentFileOffset = 0;
|
78 |
+
$this->failures = array();
|
79 |
+
$this->isCompressed = false;
|
80 |
+
$this->startTimestamp = time();
|
81 |
+
$this->timeSliceInSecs = -1;
|
82 |
+
$this->working = false;
|
83 |
+
$this->validateOnly = false;
|
84 |
+
$this->directoryModeOverride = -1;
|
85 |
+
$this->fileModeOverride = -1;
|
86 |
+
$this->throttleDelayInUs = 0;
|
87 |
+
}
|
88 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
classes/package/duparchive/index.php
CHANGED
@@ -1,2 +1,3 @@
|
|
1 |
<?php
|
2 |
-
|
|
1 |
<?php
|
2 |
+
|
3 |
+
//silent
|
classes/package/index.php
CHANGED
@@ -1,2 +1,3 @@
|
|
1 |
<?php
|
2 |
-
|
|
1 |
<?php
|
2 |
+
|
3 |
+
//silent
|
classes/ui/class.ui.dialog.php
CHANGED
@@ -1,235 +1,219 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
*
|
6 |
-
*
|
7 |
-
*
|
8 |
-
*
|
9 |
-
*
|
10 |
-
* @
|
11 |
-
* @
|
12 |
-
*
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
if
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
*
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
* The height of the dialog the default is used if not set
|
38 |
-
* Alert = 125px (default) | Confirm = 150px (default)
|
39 |
-
*/
|
40 |
-
public $height;
|
41 |
-
|
42 |
-
|
43 |
-
*
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
*
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
*
|
68 |
-
* @var
|
69 |
-
*/
|
70 |
-
public $
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
*
|
81 |
-
*/
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
$this->
|
100 |
-
}
|
101 |
-
|
102 |
-
/**
|
103 |
-
* Gets the unique id that is assigned to each instance of a
|
104 |
-
*
|
105 |
-
* @return int The unique ID of
|
106 |
-
*/
|
107 |
-
public function
|
108 |
-
{
|
109 |
-
return $this->id;
|
110 |
-
}
|
111 |
-
|
112 |
-
/**
|
113 |
-
*
|
114 |
-
*
|
115 |
-
* @return
|
116 |
-
*/
|
117 |
-
public function
|
118 |
-
{
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
$
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
$html
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
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 |
-
|
184 |
-
|
185 |
-
|
186 |
-
if ($this->
|
187 |
-
$
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
</
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
echo $html;
|
218 |
-
}
|
219 |
-
|
220 |
-
/**
|
221 |
-
* Shows the confirm base JS code used to display when needed
|
222 |
-
*
|
223 |
-
* @return string The javascript content used for the confirm dialog
|
224 |
-
*/
|
225 |
-
public function showConfirm()
|
226 |
-
{
|
227 |
-
$this->width = is_numeric($this->width) ? $this->width : 500;
|
228 |
-
$this->height = is_numeric($this->height) ? $this->height : 225;
|
229 |
-
$html = "tb_show('".esc_js($this->title)."', '#TB_inline?width=".esc_js($this->width)."&height=".esc_js($this->height)."&inlineId=".esc_js($this->id)."');\n" .
|
230 |
-
"var styleData = jQuery('#TB_window').attr('style') + 'height: ".esc_js($this->height)."px !important';\n" .
|
231 |
-
"jQuery('#TB_window').attr('style', styleData);";
|
232 |
-
|
233 |
-
echo $html;
|
234 |
-
}
|
235 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
4 |
+
/**
|
5 |
+
* Used to generate a thick-box inline dialog such as an alert or confirm pop-up
|
6 |
+
*
|
7 |
+
* Standard: PSR-2
|
8 |
+
* @link http://www.php-fig.org/psr/psr-2
|
9 |
+
*
|
10 |
+
* @package Duplicator
|
11 |
+
* @subpackage classes/ui
|
12 |
+
* @copyright (c) 2017, Snapcreek LLC
|
13 |
+
*
|
14 |
+
*/
|
15 |
+
|
16 |
+
// Exit if accessed directly
|
17 |
+
if (! defined('DUPLICATOR_VERSION')) {
|
18 |
+
exit;
|
19 |
+
}
|
20 |
+
|
21 |
+
class DUP_UI_Dialog
|
22 |
+
{
|
23 |
+
/**
|
24 |
+
* The title that shows up in the dialog
|
25 |
+
*/
|
26 |
+
public $title;
|
27 |
+
/**
|
28 |
+
* The message displayed in the body of the dialog
|
29 |
+
*/
|
30 |
+
public $message;
|
31 |
+
/**
|
32 |
+
* The width of the dialog the default is used if not set
|
33 |
+
* Alert = 475px (default) | Confirm = 500px (default)
|
34 |
+
*/
|
35 |
+
public $width;
|
36 |
+
/**
|
37 |
+
* The height of the dialog the default is used if not set
|
38 |
+
* Alert = 125px (default) | Confirm = 150px (default)
|
39 |
+
*/
|
40 |
+
public $height;
|
41 |
+
/**
|
42 |
+
* When the progress meter is running show this text
|
43 |
+
* Available only on confirm dialogs
|
44 |
+
*/
|
45 |
+
public $progressText;
|
46 |
+
/**
|
47 |
+
* When true a progress meter will run until page is reloaded
|
48 |
+
* Available only on confirm dialogs
|
49 |
+
*/
|
50 |
+
public $progressOn = true;
|
51 |
+
/**
|
52 |
+
* The javascript call back method to call when the 'Yes' button is clicked
|
53 |
+
* Available only on confirm dialogs
|
54 |
+
*/
|
55 |
+
public $jscallback;
|
56 |
+
/**
|
57 |
+
*
|
58 |
+
* @var string
|
59 |
+
*/
|
60 |
+
public $okText;
|
61 |
+
/**
|
62 |
+
*
|
63 |
+
* @var string
|
64 |
+
*/
|
65 |
+
public $cancelText;
|
66 |
+
/**
|
67 |
+
* If true close dialog on confirm
|
68 |
+
* @var bool
|
69 |
+
*/
|
70 |
+
public $closeOnConfirm = false;
|
71 |
+
/**
|
72 |
+
* The id given to the full dialog
|
73 |
+
*/
|
74 |
+
private $id;
|
75 |
+
/**
|
76 |
+
* A unique id that is added to all id elements
|
77 |
+
*/
|
78 |
+
private $uniqid;
|
79 |
+
/**
|
80 |
+
* Init this object when created
|
81 |
+
*/
|
82 |
+
public function __construct()
|
83 |
+
{
|
84 |
+
add_thickbox();
|
85 |
+
$this->progressText = __('Processing please wait...', 'duplicator');
|
86 |
+
$this->uniqid = substr(uniqid('', true), 0, 14) . rand();
|
87 |
+
$this->id = 'dup-dlg-' . $this->uniqid;
|
88 |
+
$this->okText = __('OK', 'duplicator');
|
89 |
+
$this->cancelText = __('Cancel', 'duplicator');
|
90 |
+
}
|
91 |
+
|
92 |
+
/**
|
93 |
+
* Gets the unique id that is assigned to each instance of a dialog
|
94 |
+
*
|
95 |
+
* @return int The unique ID of this dialog
|
96 |
+
*/
|
97 |
+
public function getID()
|
98 |
+
{
|
99 |
+
return $this->id;
|
100 |
+
}
|
101 |
+
|
102 |
+
/**
|
103 |
+
* Gets the unique id that is assigned to each instance of a dialogs message text
|
104 |
+
*
|
105 |
+
* @return int The unique ID of the message
|
106 |
+
*/
|
107 |
+
public function getMessageID()
|
108 |
+
{
|
109 |
+
return "{$this->id}_message";
|
110 |
+
}
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Initialize the alert base html code used to display when needed
|
114 |
+
*
|
115 |
+
* @return string The html content used for the alert dialog
|
116 |
+
*/
|
117 |
+
public function initAlert()
|
118 |
+
{
|
119 |
+
$onClickClose = '';
|
120 |
+
if (!is_null($this->jscallback)) {
|
121 |
+
$onClickClose .= $this->jscallback . ';';
|
122 |
+
}
|
123 |
+
$onClickClose .= 'tb_remove();';
|
124 |
+
$hideButton = "";
|
125 |
+
if (strlen($this->okText) == 0) {
|
126 |
+
$hideButton = "style='display:none'";
|
127 |
+
}
|
128 |
+
|
129 |
+
$html = '
|
130 |
+
<div id="' . esc_attr($this->id) . '" style="display:none">
|
131 |
+
<div class="dup-dlg-alert-txt">
|
132 |
+
' . $this->message . '
|
133 |
+
<br/><br/>
|
134 |
+
</div>
|
135 |
+
<div class="dup-dlg-alert-btns">
|
136 |
+
<input type="button" class="button button-large" value="' . esc_attr($this->okText) . '" onclick="' . $onClickClose . '" ' . $hideButton . '/>
|
137 |
+
</div>
|
138 |
+
</div>';
|
139 |
+
echo $html;
|
140 |
+
}
|
141 |
+
|
142 |
+
/**
|
143 |
+
* Shows the alert base JS code used to display when needed
|
144 |
+
*
|
145 |
+
* @return string The javascript content used for the alert dialog
|
146 |
+
*/
|
147 |
+
public function showAlert()
|
148 |
+
{
|
149 |
+
$this->width = is_numeric($this->width) ? $this->width : 500;
|
150 |
+
$this->height = is_numeric($this->height) ? $this->height : 175;
|
151 |
+
$html = "tb_show('" . esc_js($this->title) . "', '#TB_inline?width=" . esc_js($this->width) . "&height=" . esc_js($this->height) . "&inlineId=" . esc_js($this->id) . "');\n" .
|
152 |
+
"var styleData = jQuery('#TB_window').attr('style') + 'height: " . esc_js($this->height) . "px !important';\n" .
|
153 |
+
"jQuery('#TB_window').attr('style', styleData);";
|
154 |
+
echo $html;
|
155 |
+
}
|
156 |
+
|
157 |
+
/**
|
158 |
+
* Shows the confirm base JS code used to display when needed
|
159 |
+
*
|
160 |
+
* @return string The javascript content used for the confirm dialog
|
161 |
+
*/
|
162 |
+
public function initConfirm()
|
163 |
+
{
|
164 |
+
$progress_data = '';
|
165 |
+
$progress_func2 = '';
|
166 |
+
$onClickConfirm = '';
|
167 |
+
if (!is_null($this->jscallback)) {
|
168 |
+
$onClickConfirm .= $this->jscallback . ';';
|
169 |
+
}
|
170 |
+
|
171 |
+
//Enable the progress spinner
|
172 |
+
if ($this->progressOn) {
|
173 |
+
$progress_func1 = "__DUP_UI_Dialog_" . $this->uniqid;
|
174 |
+
$progress_func2 = ";{$progress_func1}(this)";
|
175 |
+
$progress_data = "<div class='dup-dlg-confirm-progress'><i class='fas fa-circle-notch fa-spin fa-lg fa-fw'></i> " . esc_js($this->progressText) . "</div>
|
176 |
+
<script>
|
177 |
+
function {$progress_func1}(obj)
|
178 |
+
{
|
179 |
+
jQuery(obj).parent().parent().find('.dup-dlg-confirm-progress').show();
|
180 |
+
jQuery(obj).closest('.dup-dlg-confirm-btns').find('input').attr('disabled', 'true');
|
181 |
+
}
|
182 |
+
</script>";
|
183 |
+
$onClickConfirm .= $progress_func2 . ';';
|
184 |
+
}
|
185 |
+
|
186 |
+
if ($this->closeOnConfirm) {
|
187 |
+
$onClickConfirm .= 'tb_remove();';
|
188 |
+
}
|
189 |
+
|
190 |
+
$html =
|
191 |
+
'<div id="' . esc_attr($this->id) . '" style="display:none">
|
192 |
+
<div class="dup-dlg-confirm-txt">
|
193 |
+
<span id="' . esc_attr($this->id) . '_message">' . esc_html($this->message) . '</span>
|
194 |
+
<br/><br/>
|
195 |
+
' . $progress_data . '
|
196 |
+
</div>
|
197 |
+
<div class="dup-dlg-confirm-btns">
|
198 |
+
<input type="button" class="button button-large" value="' . esc_attr($this->okText) . '" onclick="' . $onClickConfirm . '" />
|
199 |
+
<input type="button" class="button button-large" value="' . esc_attr($this->cancelText) . '" onclick="tb_remove()" />
|
200 |
+
</div>
|
201 |
+
</div>';
|
202 |
+
echo $html;
|
203 |
+
}
|
204 |
+
|
205 |
+
/**
|
206 |
+
* Shows the confirm base JS code used to display when needed
|
207 |
+
*
|
208 |
+
* @return string The javascript content used for the confirm dialog
|
209 |
+
*/
|
210 |
+
public function showConfirm()
|
211 |
+
{
|
212 |
+
$this->width = is_numeric($this->width) ? $this->width : 500;
|
213 |
+
$this->height = is_numeric($this->height) ? $this->height : 225;
|
214 |
+
$html = "tb_show('" . esc_js($this->title) . "', '#TB_inline?width=" . esc_js($this->width) . "&height=" . esc_js($this->height) . "&inlineId=" . esc_js($this->id) . "');\n" .
|
215 |
+
"var styleData = jQuery('#TB_window').attr('style') + 'height: " . esc_js($this->height) . "px !important';\n" .
|
216 |
+
"jQuery('#TB_window').attr('style', styleData);";
|
217 |
+
echo $html;
|
218 |
+
}
|
219 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
classes/ui/class.ui.messages.php
CHANGED
@@ -1,117 +1,111 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
|
4 |
-
/**
|
5 |
-
* Used to generate a thick box inline dialog such as an alert or confirm pop-up
|
6 |
-
*
|
7 |
-
*
|
8 |
-
* Standard: PSR-2
|
9 |
-
* @link http://www.php-fig.org/psr/psr-2
|
10 |
-
*
|
11 |
-
* @package Duplicator
|
12 |
-
* @subpackage classes/ui
|
13 |
-
* @copyright (c) 2017, Snapcreek LLC
|
14 |
-
*/
|
15 |
-
|
16 |
-
class DUP_UI_Messages
|
17 |
-
{
|
18 |
-
const UNIQUE_ID_PREFIX
|
19 |
-
const NOTICE
|
20 |
-
const WARNING
|
21 |
-
const ERROR
|
22 |
-
|
23 |
-
private
|
24 |
-
|
25 |
-
public $
|
26 |
-
public $
|
27 |
-
public $
|
28 |
-
public $
|
29 |
-
|
30 |
-
|
31 |
-
*
|
32 |
-
|
33 |
-
|
34 |
-
public $
|
35 |
-
public $
|
36 |
-
public $
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
$this->
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
$classes =
|
51 |
-
}
|
52 |
-
|
53 |
-
|
54 |
-
$classes =
|
55 |
-
}
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
echo $result;
|
113 |
-
} else {
|
114 |
-
return $result;
|
115 |
-
}
|
116 |
-
}
|
117 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
4 |
+
/**
|
5 |
+
* Used to generate a thick box inline dialog such as an alert or confirm pop-up
|
6 |
+
*
|
7 |
+
*
|
8 |
+
* Standard: PSR-2
|
9 |
+
* @link http://www.php-fig.org/psr/psr-2
|
10 |
+
*
|
11 |
+
* @package Duplicator
|
12 |
+
* @subpackage classes/ui
|
13 |
+
* @copyright (c) 2017, Snapcreek LLC
|
14 |
+
*/
|
15 |
+
|
16 |
+
class DUP_UI_Messages
|
17 |
+
{
|
18 |
+
const UNIQUE_ID_PREFIX = 'dup_ui_msg_';
|
19 |
+
const NOTICE = 'updated';
|
20 |
+
const WARNING = 'update-nag';
|
21 |
+
const ERROR = 'error';
|
22 |
+
private static $unique_id = 0;
|
23 |
+
private $id;
|
24 |
+
public $type = self::NOTICE;
|
25 |
+
public $content = '';
|
26 |
+
public $wrap_cont_tag = 'p';
|
27 |
+
public $hide_on_init = true;
|
28 |
+
public $is_dismissible = false;
|
29 |
+
/**
|
30 |
+
*
|
31 |
+
* @var int delay in milliseconds
|
32 |
+
*/
|
33 |
+
public $auto_hide_delay = 0;
|
34 |
+
public $callback_on_show = null;
|
35 |
+
public $callback_on_hide = null;
|
36 |
+
public function __construct($content = '', $type = self::NOTICE)
|
37 |
+
{
|
38 |
+
self::$unique_id++;
|
39 |
+
$this->id = self::UNIQUE_ID_PREFIX . self::$unique_id;
|
40 |
+
$this->content = (string) $content;
|
41 |
+
$this->type = $type;
|
42 |
+
}
|
43 |
+
|
44 |
+
protected function get_notice_classes($classes = array())
|
45 |
+
{
|
46 |
+
if (is_string($classes)) {
|
47 |
+
$classes = explode(' ', $classes);
|
48 |
+
} elseif (is_array($classes)) {
|
49 |
+
} else {
|
50 |
+
$classes = array();
|
51 |
+
}
|
52 |
+
|
53 |
+
if ($this->is_dismissible) {
|
54 |
+
$classes[] = 'is-dismissible';
|
55 |
+
}
|
56 |
+
|
57 |
+
$result = array_merge(array('notice', $this->type), $classes);
|
58 |
+
return trim(implode(' ', $result));
|
59 |
+
}
|
60 |
+
|
61 |
+
public function initMessage()
|
62 |
+
{
|
63 |
+
$classes = array();
|
64 |
+
if ($this->hide_on_init) {
|
65 |
+
$classes[] = 'no_display';
|
66 |
+
}
|
67 |
+
|
68 |
+
$this->wrap_tag = empty($this->wrap_tag) ? 'p' : $this->wrap_tag;
|
69 |
+
echo '<div id="' . $this->id . '" class="' . $this->get_notice_classes($classes) . '">' .
|
70 |
+
'<' . $this->wrap_cont_tag . ' class="msg-content">' .
|
71 |
+
$this->content .
|
72 |
+
'</' . $this->wrap_cont_tag . '>' .
|
73 |
+
'</div>';
|
74 |
+
}
|
75 |
+
|
76 |
+
public function updateMessage($jsVarName, $echo = true)
|
77 |
+
{
|
78 |
+
$result = 'jQuery("#' . $this->id . ' > .msg-content").html(' . $jsVarName . ');';
|
79 |
+
if ($echo) {
|
80 |
+
echo $result;
|
81 |
+
} else {
|
82 |
+
return $result;
|
83 |
+
}
|
84 |
+
}
|
85 |
+
|
86 |
+
public function showMessage($echo = true)
|
87 |
+
{
|
88 |
+
$callStr = !empty($this->callback_on_show) ? $this->callback_on_show . ';' : '';
|
89 |
+
$result = 'jQuery("#' . $this->id . '").fadeIn( "slow", function() { $(this).removeClass("no_display");' . $callStr . ' });';
|
90 |
+
if ($this->auto_hide_delay > 0) {
|
91 |
+
$result .= 'setTimeout(function () { ' . $this->hideMessage(false) . ' }, ' . $this->auto_hide_delay . ');';
|
92 |
+
}
|
93 |
+
|
94 |
+
if ($echo) {
|
95 |
+
echo $result;
|
96 |
+
} else {
|
97 |
+
return $result;
|
98 |
+
}
|
99 |
+
}
|
100 |
+
|
101 |
+
public function hideMessage($echo = true)
|
102 |
+
{
|
103 |
+
$callStr = !empty($this->callback_on_hide) ? $this->callback_on_hide . ';' : '';
|
104 |
+
$result = 'jQuery("#' . $this->id . '").fadeOut( "slow", function() { $(this).addClass("no_display");' . $callStr . ' });';
|
105 |
+
if ($echo) {
|
106 |
+
echo $result;
|
107 |
+
} else {
|
108 |
+
return $result;
|
109 |
+
}
|
110 |
+
}
|
111 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
classes/ui/class.ui.notice.php
CHANGED
@@ -1,4 +1,8 @@
|
|
1 |
<?php
|
|
|
|
|
|
|
|
|
2 |
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
3 |
/**
|
4 |
* Used to display notices in the WordPress Admin area
|
@@ -15,13 +19,13 @@ defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
|
15 |
|
16 |
class DUP_UI_Notice
|
17 |
{
|
18 |
-
|
19 |
-
const OPTION_KEY_ACTIVATE_PLUGINS_AFTER_INSTALL = '
|
20 |
|
21 |
//TEMPLATE VALUE: This is a just a simple example for setting up quick notices
|
22 |
-
const OPTION_KEY_NEW_NOTICE_TEMPLATE
|
23 |
-
const
|
24 |
-
const OPTION_KEY_IS_MU_NOTICE_DISMISSED
|
25 |
|
26 |
const GEN_INFO_NOTICE = 0;
|
27 |
const GEN_SUCCESS_NOTICE = 1;
|
@@ -33,49 +37,83 @@ class DUP_UI_Notice
|
|
33 |
*/
|
34 |
public static function init()
|
35 |
{
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
}
|
47 |
}
|
48 |
|
49 |
-
|
50 |
-
|
51 |
-
|
|
|
52 |
*/
|
53 |
-
public static function
|
54 |
{
|
55 |
-
|
|
|
56 |
return;
|
57 |
}
|
58 |
|
59 |
-
|
60 |
-
if (
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
61 |
return;
|
62 |
}
|
63 |
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
echo sprintf(__('Example for passing dynamic data to notice message <i>%s</i> to <i>%s</i>', 'duplicator'),
|
70 |
-
esc_html("test 1"),
|
71 |
-
esc_html(time()));
|
72 |
-
?>
|
73 |
-
</p>
|
74 |
-
<p>
|
75 |
-
<?php echo sprintf(__('More info here: Goto <a href="%s">General Settings</a>', 'duplicator'), 'admin.php?page=duplicator-settings'); ?>
|
76 |
-
</p>
|
77 |
-
</div>
|
78 |
-
<?php
|
79 |
}
|
80 |
|
81 |
/**
|
@@ -88,16 +126,18 @@ class DUP_UI_Notice
|
|
88 |
//Show only on Duplicator pages and Dashboard when plugin is active
|
89 |
$dup_active = is_plugin_active('duplicator/duplicator.php');
|
90 |
$dup_perm = current_user_can('manage_options');
|
91 |
-
if (!$dup_active || !$dup_perm)
|
92 |
return;
|
|
|
93 |
|
94 |
$screen = get_current_screen();
|
95 |
-
if (!isset($screen))
|
96 |
return;
|
|
|
97 |
|
98 |
$is_installer_cleanup_req = ($screen->id == 'duplicator_page_duplicator-tools' && isset($_GET['action']) && $_GET['action'] == 'installer');
|
99 |
if (DUP_Server::hasInstallerFiles() && !$is_installer_cleanup_req) {
|
100 |
-
|
101 |
|
102 |
$on_active_tab = isset($_GET['section']) ? $_GET['section'] : '';
|
103 |
echo '<div class="dup-updated notice notice-success dup-global-error-reserved-files" id="message"><p>';
|
@@ -113,26 +153,24 @@ class DUP_UI_Notice
|
|
113 |
|
114 |
//On Tools > Cleanup Page
|
115 |
if ($screen->id == 'duplicator_page_duplicator-tools' && ($on_active_tab == "info" || $on_active_tab == '')) {
|
116 |
-
|
117 |
$title = __('This site has been successfully migrated!', 'duplicator');
|
118 |
$msg1 = __('Final step(s):', 'duplicator');
|
119 |
$msg2 = __('This message will be removed after all installer files are removed. Installer files must be removed to maintain a secure site. '
|
120 |
-
.'Click the link above or button below to remove all installer files and complete the migration.', 'duplicator');
|
121 |
|
122 |
-
echo "<b class='pass-msg'><i class='fa fa-check-circle'></i> ".esc_html($title)."</b> <br/> {$safe_html} <b>".esc_html($msg1)."</b> <br/>";
|
123 |
printf("1. <a href='javascript:void(0)' onclick='jQuery(\"#dup-remove-installer-files-btn\").click()'>%s</a><br/>", esc_html__('Remove Installation Files Now!', 'duplicator'));
|
124 |
printf("2. <a href='https://wordpress.org/support/plugin/duplicator/reviews/?filter=5' target='wporg'>%s</a> <br/> ", esc_html__('Optionally, Review Duplicator at WordPress.org...', 'duplicator'));
|
125 |
-
echo "<div class='pass-msg'>".esc_html($msg2)."</div>";
|
126 |
|
127 |
//All other Pages
|
128 |
} else {
|
129 |
-
|
130 |
$title = __('Migration Almost Complete!', 'duplicator');
|
131 |
$msg = __('Reserved Duplicator installation files have been detected in the root directory. Please delete these installation files to '
|
132 |
-
.'avoid security issues. <br/> Go to:Duplicator > Tools > Information >Stored Data and click the "Remove Installation Files" button', 'duplicator');
|
133 |
|
134 |
$nonce = wp_create_nonce('duplicator_cleanup_page');
|
135 |
-
$url = self_admin_url('admin.php?page=duplicator-tools&tab=diagnostics§ion=info&_wpnonce='
|
136 |
echo "<b>{$title}</b><br/> {$safe_html} {$msg}";
|
137 |
@printf("<br/><a href='{$url}'>%s</a>", __('Take me there now!', 'duplicator'));
|
138 |
}
|
@@ -160,40 +198,56 @@ class DUP_UI_Notice
|
|
160 |
public static function installAutoDeactivatePlugins()
|
161 |
{
|
162 |
$reactivatePluginsAfterInstallation = get_option(self::OPTION_KEY_ACTIVATE_PLUGINS_AFTER_INSTALL, false);
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
|
|
|
|
|
|
|
|
|
|
170 |
}
|
171 |
|
172 |
-
|
173 |
-
|
|
|
|
|
174 |
} else {
|
175 |
-
$
|
176 |
-
foreach ($shouldBeActivated as $slug => $title) {
|
177 |
-
$activateURL = wp_nonce_url(admin_url('plugins.php?action=activate&plugin='.$slug), 'activate-plugin_'.$slug);
|
178 |
-
$anchorTitle = sprintf(esc_html__('Activate %s', 'duplicator'), $title);
|
179 |
-
$activatePluginsAnchors[] = '<a href="'.$activateURL.'"
|
180 |
-
title="'.esc_attr($anchorTitle).'">'.
|
181 |
-
$title.'</a>';
|
182 |
-
}
|
183 |
-
?>
|
184 |
-
<div class="update-nag duplicator-plugin-activation-admin-notice notice notice-warning duplicator-admin-notice is-dismissible"
|
185 |
-
data-to-dismiss="<?php echo esc_attr(self::OPTION_KEY_ACTIVATE_PLUGINS_AFTER_INSTALL); ?>" >
|
186 |
-
<p>
|
187 |
-
<?php
|
188 |
-
echo "<b>".esc_html__("Warning!", "duplicator")."</b> ".esc_html__("Migration Almost Complete!", "duplicator")." <br/>";
|
189 |
-
echo esc_html__("Plugin(s) listed here have been deactivated during installation to help prevent issues. Please activate them to finish this migration: ", "duplicator")."<br/>";
|
190 |
-
echo implode(' ,', $activatePluginsAnchors);
|
191 |
-
?>
|
192 |
-
</p>
|
193 |
-
</div>
|
194 |
-
<?php
|
195 |
}
|
196 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
197 |
}
|
198 |
|
199 |
/**
|
@@ -224,8 +278,8 @@ class DUP_UI_Notice
|
|
224 |
}
|
225 |
|
226 |
// not using DUP_Util::getTablePrefix() in place of $tablePrefix because DUP_UI_Notice included initially (Duplicator\Lite\Requirement is depended on the DUP_UI_Notice)
|
227 |
-
$tablePrefix
|
228 |
-
$packagesCount = $GLOBALS['wpdb']->get_var('SELECT count(id) FROM '
|
229 |
|
230 |
if ($packagesCount < DUPLICATOR_FEEDBACK_NOTICE_SHOW_AFTER_NO_PACKAGE) {
|
231 |
return;
|
@@ -245,7 +299,7 @@ class DUP_UI_Notice
|
|
245 |
<div class="notice updated duplicator-message duplicator-message-dismissed" data-notice_id="<?php echo esc_attr($notice_id); ?>">
|
246 |
<div class="duplicator-message-inner">
|
247 |
<div class="duplicator-message-icon">
|
248 |
-
<img src="<?php echo esc_url(DUPLICATOR_PLUGIN_URL."assets/img/logo.png"); ?>" style="text-align:top; margin:0; height:60px; width:60px;" alt="Duplicator">
|
249 |
</div>
|
250 |
<div class="duplicator-message-content">
|
251 |
<p><strong><?php echo __('Congrats!', 'duplicator'); ?></strong> <?php printf(esc_html__('You created over %d packages with Duplicator. Great job! If you can spare a minute, please help us by leaving a five star review on WordPress.org.', 'duplicator'), DUPLICATOR_FEEDBACK_NOTICE_SHOW_AFTER_NO_PACKAGE); ?></p>
|
@@ -267,8 +321,8 @@ class DUP_UI_Notice
|
|
267 |
public static function showNoExportCapabilityNotice()
|
268 |
{
|
269 |
if (is_admin() && in_array('administrator', $GLOBALS['current_user']->roles) && !current_user_can('export')) {
|
270 |
-
$errorMessage = __('<strong>Duplicator</strong><hr> Your logged-in user role does not have export capability so you don\'t have access to Duplicator functionality.', 'duplicator').
|
271 |
-
"<br>".
|
272 |
sprintf(__('<strong>RECOMMENDATION:</strong> Add export capability to your role. See FAQ: <a target="_blank" href="%s">%s</a>', 'duplicator'), 'https://snapcreek.com/duplicator/docs/faqs-tech/#faq-licensing-040-q', __('Why is the Duplicator/Packages menu missing from my admin menu?', 'duplicator'));
|
273 |
DUP_UI_Notice::displayGeneralAdminNotice($errorMessage, self::GEN_ERROR_NOTICE, true);
|
274 |
}
|
@@ -279,7 +333,7 @@ class DUP_UI_Notice
|
|
279 |
*
|
280 |
* @param string $htmlMsg html code to be printed
|
281 |
* @param integer $noticeType constant value of SELF::GEN_
|
282 |
-
* @param boolean $isDismissible whether the notice is dismissable or not. Default is true
|
283 |
* @param array|string $extraClasses add more classes to the notice div
|
284 |
* @return void
|
285 |
*/
|
1 |
<?php
|
2 |
+
|
3 |
+
use Duplicator\Core\MigrationMng;
|
4 |
+
use Duplicator\Libs\Snap\SnapUtil;
|
5 |
+
|
6 |
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
7 |
/**
|
8 |
* Used to display notices in the WordPress Admin area
|
19 |
|
20 |
class DUP_UI_Notice
|
21 |
{
|
22 |
+
const OPTION_KEY_MIGRATION_SUCCESS_NOTICE = 'duplicator_migration_success';
|
23 |
+
const OPTION_KEY_ACTIVATE_PLUGINS_AFTER_INSTALL = 'duplicator_activate_plugins_after_installation';
|
24 |
|
25 |
//TEMPLATE VALUE: This is a just a simple example for setting up quick notices
|
26 |
+
const OPTION_KEY_NEW_NOTICE_TEMPLATE = 'duplicator_new_template_notice';
|
27 |
+
const OPTION_KEY_IS_ENABLE_NOTICE_DISMISSED = 'duplicator_is_enable_notice_dismissed';
|
28 |
+
const OPTION_KEY_IS_MU_NOTICE_DISMISSED = 'duplicator_is_mu_notice_dismissed';
|
29 |
|
30 |
const GEN_INFO_NOTICE = 0;
|
31 |
const GEN_SUCCESS_NOTICE = 1;
|
37 |
*/
|
38 |
public static function init()
|
39 |
{
|
40 |
+
add_action('admin_init', array(__CLASS__, 'adminInit'));
|
41 |
+
}
|
42 |
+
|
43 |
+
public static function adminInit()
|
44 |
+
{
|
45 |
+
$notices = array();
|
46 |
+
if (is_multisite()) {
|
47 |
+
$noCapabilitiesNotice = is_super_admin() && !current_user_can('export');
|
48 |
+
} else {
|
49 |
+
$noCapabilitiesNotice = in_array('administrator', $GLOBALS['current_user']->roles) && !current_user_can('export');
|
50 |
+
}
|
51 |
+
|
52 |
+
if ($noCapabilitiesNotice) {
|
53 |
+
$notices[] = array(__CLASS__, 'showNoExportCapabilityNotice');
|
54 |
+
}
|
55 |
+
|
56 |
+
if (is_multisite()) {
|
57 |
+
$displayNotices = is_super_admin() && current_user_can('export');
|
58 |
+
} else {
|
59 |
+
$displayNotices = current_user_can('export');
|
60 |
+
}
|
61 |
+
|
62 |
+
if ($displayNotices) {
|
63 |
+
$notices[] = array(__CLASS__, 'clearInstallerFilesAction'); // BEFORE MIGRATION SUCCESS NOTICE
|
64 |
+
$notices[] = array(__CLASS__, 'migrationSuccessNotice');
|
65 |
+
$notices[] = array(__CLASS__, 'installAutoDeactivatePlugins');
|
66 |
+
}
|
67 |
+
|
68 |
+
$action = is_multisite() ? 'network_admin_notices' : 'admin_notices';
|
69 |
+
foreach ($notices as $notice) {
|
70 |
+
add_action($action, $notice);
|
71 |
}
|
72 |
}
|
73 |
|
74 |
+
/**
|
75 |
+
* Clear installer file action
|
76 |
+
*
|
77 |
+
* @return void
|
78 |
*/
|
79 |
+
public static function clearInstallerFilesAction()
|
80 |
{
|
81 |
+
|
82 |
+
if (!DUP_CTRL_Tools::isDiagnosticPage() || get_option(self::OPTION_KEY_MIGRATION_SUCCESS_NOTICE) == true) {
|
83 |
return;
|
84 |
}
|
85 |
|
86 |
+
|
87 |
+
if (SnapUtil::filterInputRequest('action', FILTER_DEFAULT) === 'installer') {
|
88 |
+
if (! wp_verify_nonce($_REQUEST['_wpnonce'], 'duplicator_cleanup_page')) {
|
89 |
+
echo '<p>' . __('Security issue', 'duplicator') . '</p>';
|
90 |
+
exit; // Get out of here bad nounce!
|
91 |
+
}
|
92 |
+
|
93 |
+
?>
|
94 |
+
<div id="message" class="notice notice-success">
|
95 |
+
<?php require DUPLICATOR_LITE_PATH . '/views/parts/migration-clean-installation-files.php'; ?>
|
96 |
+
</div>
|
97 |
+
<?php
|
98 |
+
}
|
99 |
+
}
|
100 |
+
|
101 |
+
/**
|
102 |
+
* Shows a display message in the wp-admin if any reserved files are found
|
103 |
+
*
|
104 |
+
* @return void
|
105 |
+
*/
|
106 |
+
public static function migrationSuccessNotice()
|
107 |
+
{
|
108 |
+
if (get_option(self::OPTION_KEY_MIGRATION_SUCCESS_NOTICE) != true) {
|
109 |
return;
|
110 |
}
|
111 |
|
112 |
+
if (DUP_CTRL_Tools::isDiagnosticPage()) {
|
113 |
+
require DUPLICATOR_LITE_PATH . '/views/parts/migration-message.php';
|
114 |
+
} else {
|
115 |
+
require DUPLICATOR_LITE_PATH . '/views/parts/migration-almost-complete.php';
|
116 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
117 |
}
|
118 |
|
119 |
/**
|
126 |
//Show only on Duplicator pages and Dashboard when plugin is active
|
127 |
$dup_active = is_plugin_active('duplicator/duplicator.php');
|
128 |
$dup_perm = current_user_can('manage_options');
|
129 |
+
if (!$dup_active || !$dup_perm) {
|
130 |
return;
|
131 |
+
}
|
132 |
|
133 |
$screen = get_current_screen();
|
134 |
+
if (!isset($screen)) {
|
135 |
return;
|
136 |
+
}
|
137 |
|
138 |
$is_installer_cleanup_req = ($screen->id == 'duplicator_page_duplicator-tools' && isset($_GET['action']) && $_GET['action'] == 'installer');
|
139 |
if (DUP_Server::hasInstallerFiles() && !$is_installer_cleanup_req) {
|
140 |
+
MigrationMng::renameInstallersPhpFiles();
|
141 |
|
142 |
$on_active_tab = isset($_GET['section']) ? $_GET['section'] : '';
|
143 |
echo '<div class="dup-updated notice notice-success dup-global-error-reserved-files" id="message"><p>';
|
153 |
|
154 |
//On Tools > Cleanup Page
|
155 |
if ($screen->id == 'duplicator_page_duplicator-tools' && ($on_active_tab == "info" || $on_active_tab == '')) {
|
|
|
156 |
$title = __('This site has been successfully migrated!', 'duplicator');
|
157 |
$msg1 = __('Final step(s):', 'duplicator');
|
158 |
$msg2 = __('This message will be removed after all installer files are removed. Installer files must be removed to maintain a secure site. '
|
159 |
+
. 'Click the link above or button below to remove all installer files and complete the migration.', 'duplicator');
|
160 |
|
161 |
+
echo "<b class='pass-msg'><i class='fa fa-check-circle'></i> " . esc_html($title) . "</b> <br/> {$safe_html} <b>" . esc_html($msg1) . "</b> <br/>";
|
162 |
printf("1. <a href='javascript:void(0)' onclick='jQuery(\"#dup-remove-installer-files-btn\").click()'>%s</a><br/>", esc_html__('Remove Installation Files Now!', 'duplicator'));
|
163 |
printf("2. <a href='https://wordpress.org/support/plugin/duplicator/reviews/?filter=5' target='wporg'>%s</a> <br/> ", esc_html__('Optionally, Review Duplicator at WordPress.org...', 'duplicator'));
|
164 |
+
echo "<div class='pass-msg'>" . esc_html($msg2) . "</div>";
|
165 |
|
166 |
//All other Pages
|
167 |
} else {
|
|
|
168 |
$title = __('Migration Almost Complete!', 'duplicator');
|
169 |
$msg = __('Reserved Duplicator installation files have been detected in the root directory. Please delete these installation files to '
|
170 |
+
. 'avoid security issues. <br/> Go to:Duplicator > Tools > Information >Stored Data and click the "Remove Installation Files" button', 'duplicator');
|
171 |
|
172 |
$nonce = wp_create_nonce('duplicator_cleanup_page');
|
173 |
+
$url = self_admin_url('admin.php?page=duplicator-tools&tab=diagnostics§ion=info&_wpnonce=' . $nonce);
|
174 |
echo "<b>{$title}</b><br/> {$safe_html} {$msg}";
|
175 |
@printf("<br/><a href='{$url}'>%s</a>", __('Take me there now!', 'duplicator'));
|
176 |
}
|
198 |
public static function installAutoDeactivatePlugins()
|
199 |
{
|
200 |
$reactivatePluginsAfterInstallation = get_option(self::OPTION_KEY_ACTIVATE_PLUGINS_AFTER_INSTALL, false);
|
201 |
+
|
202 |
+
$pluginsToActive = get_option(self::OPTION_KEY_ACTIVATE_PLUGINS_AFTER_INSTALL, false);
|
203 |
+
if (!is_array($pluginsToActive) || empty($pluginsToActive)) {
|
204 |
+
return false;
|
205 |
+
}
|
206 |
+
|
207 |
+
$shouldBeActivated = array();
|
208 |
+
$allPlugins = get_plugins();
|
209 |
+
foreach ($pluginsToActive as $index => $pluginSlug) {
|
210 |
+
if (!isset($allPlugins[$pluginSlug])) {
|
211 |
+
unset($pluginsToActive[$index]);
|
212 |
+
continue;
|
213 |
}
|
214 |
|
215 |
+
$isActive = is_plugin_active($pluginSlug);
|
216 |
+
|
217 |
+
if (!$isActive && isset($allPlugins[$pluginSlug])) {
|
218 |
+
$shouldBeActivated[$pluginSlug] = $allPlugins[$pluginSlug]['Name'];
|
219 |
} else {
|
220 |
+
unset($pluginsToActive[$index]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
221 |
}
|
222 |
}
|
223 |
+
|
224 |
+
if (empty($shouldBeActivated)) {
|
225 |
+
delete_option(self::OPTION_KEY_ACTIVATE_PLUGINS_AFTER_INSTALL);
|
226 |
+
return;
|
227 |
+
} else {
|
228 |
+
update_option(self::OPTION_KEY_ACTIVATE_PLUGINS_AFTER_INSTALL, $pluginsToActive);
|
229 |
+
}
|
230 |
+
|
231 |
+
$activatePluginsAnchors = array();
|
232 |
+
foreach ($shouldBeActivated as $slug => $title) {
|
233 |
+
$activateURL = wp_nonce_url(admin_url('plugins.php?action=activate&plugin=' . $slug), 'activate-plugin_' . $slug);
|
234 |
+
$anchorTitle = sprintf(esc_html__('Activate %s', 'duplicator'), $title);
|
235 |
+
$activatePluginsAnchors[] = '<a href="' . $activateURL . '"
|
236 |
+
title="' . esc_attr($anchorTitle) . '">' .
|
237 |
+
$title . '</a>';
|
238 |
+
}
|
239 |
+
?>
|
240 |
+
<div class="update-nag duplicator-plugin-activation-admin-notice notice notice-warning duplicator-admin-notice is-dismissible"
|
241 |
+
data-to-dismiss="<?php echo esc_attr(self::OPTION_KEY_ACTIVATE_PLUGINS_AFTER_INSTALL); ?>" >
|
242 |
+
<p>
|
243 |
+
<?php
|
244 |
+
echo "<b>" . esc_html__("Warning!", "duplicator") . "</b> " . esc_html__("Migration Almost Complete!", "duplicator") . " <br/>";
|
245 |
+
echo esc_html__("Plugin(s) listed here have been deactivated during installation to help prevent issues. Please activate them to finish this migration: ", "duplicator") . "<br/>";
|
246 |
+
echo implode(' ,', $activatePluginsAnchors);
|
247 |
+
?>
|
248 |
+
</p>
|
249 |
+
</div>
|
250 |
+
<?php
|
251 |
}
|
252 |
|
253 |
/**
|
278 |
}
|
279 |
|
280 |
// not using DUP_Util::getTablePrefix() in place of $tablePrefix because DUP_UI_Notice included initially (Duplicator\Lite\Requirement is depended on the DUP_UI_Notice)
|
281 |
+
$tablePrefix = (is_multisite() && is_plugin_active_for_network('duplicator/duplicator.php')) ? $GLOBALS['wpdb']->base_prefix : $GLOBALS['wpdb']->prefix;
|
282 |
+
$packagesCount = $GLOBALS['wpdb']->get_var('SELECT count(id) FROM ' . $tablePrefix . 'duplicator_packages WHERE status=100');
|
283 |
|
284 |
if ($packagesCount < DUPLICATOR_FEEDBACK_NOTICE_SHOW_AFTER_NO_PACKAGE) {
|
285 |
return;
|
299 |
<div class="notice updated duplicator-message duplicator-message-dismissed" data-notice_id="<?php echo esc_attr($notice_id); ?>">
|
300 |
<div class="duplicator-message-inner">
|
301 |
<div class="duplicator-message-icon">
|
302 |
+
<img src="<?php echo esc_url(DUPLICATOR_PLUGIN_URL . "assets/img/logo.png"); ?>" style="text-align:top; margin:0; height:60px; width:60px;" alt="Duplicator">
|
303 |
</div>
|
304 |
<div class="duplicator-message-content">
|
305 |
<p><strong><?php echo __('Congrats!', 'duplicator'); ?></strong> <?php printf(esc_html__('You created over %d packages with Duplicator. Great job! If you can spare a minute, please help us by leaving a five star review on WordPress.org.', 'duplicator'), DUPLICATOR_FEEDBACK_NOTICE_SHOW_AFTER_NO_PACKAGE); ?></p>
|
321 |
public static function showNoExportCapabilityNotice()
|
322 |
{
|
323 |
if (is_admin() && in_array('administrator', $GLOBALS['current_user']->roles) && !current_user_can('export')) {
|
324 |
+
$errorMessage = __('<strong>Duplicator</strong><hr> Your logged-in user role does not have export capability so you don\'t have access to Duplicator functionality.', 'duplicator') .
|
325 |
+
"<br>" .
|
326 |
sprintf(__('<strong>RECOMMENDATION:</strong> Add export capability to your role. See FAQ: <a target="_blank" href="%s">%s</a>', 'duplicator'), 'https://snapcreek.com/duplicator/docs/faqs-tech/#faq-licensing-040-q', __('Why is the Duplicator/Packages menu missing from my admin menu?', 'duplicator'));
|
327 |
DUP_UI_Notice::displayGeneralAdminNotice($errorMessage, self::GEN_ERROR_NOTICE, true);
|
328 |
}
|
333 |
*
|
334 |
* @param string $htmlMsg html code to be printed
|
335 |
* @param integer $noticeType constant value of SELF::GEN_
|
336 |
+
* @param boolean $isDismissible whether the notice is dismissable or not. Default is true
|
337 |
* @param array|string $extraClasses add more classes to the notice div
|
338 |
* @return void
|
339 |
*/
|
classes/ui/class.ui.screen.base.php
CHANGED
@@ -1,4 +1,7 @@
|
|
1 |
<?php
|
|
|
|
|
|
|
2 |
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
3 |
/**
|
4 |
* The base class for all screen.php files. This class is used to control items that are common
|
@@ -14,12 +17,12 @@ defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
|
14 |
*
|
15 |
*/
|
16 |
// Exit if accessed directly
|
17 |
-
if (!defined('DUPLICATOR_VERSION'))
|
18 |
exit;
|
|
|
19 |
|
20 |
class DUP_UI_Screen
|
21 |
{
|
22 |
-
|
23 |
/**
|
24 |
* Used as a placeholder for the current screen object
|
25 |
*/
|
@@ -30,17 +33,18 @@ class DUP_UI_Screen
|
|
30 |
*/
|
31 |
public function __construct()
|
32 |
{
|
33 |
-
|
34 |
}
|
35 |
|
36 |
public static function getCustomCss()
|
37 |
{
|
38 |
$screen = get_current_screen();
|
39 |
-
if (
|
|
|
40 |
'toplevel_page_duplicator',
|
41 |
'duplicator_page_duplicator-tools',
|
42 |
'duplicator_page_duplicator-settings',
|
43 |
-
'duplicator_page_duplicator-gopro'))
|
|
|
44 |
return;
|
45 |
}
|
46 |
|
@@ -63,7 +67,7 @@ class DUP_UI_Screen
|
|
63 |
public static function getCurrentColorScheme()
|
64 |
{
|
65 |
global $_wp_admin_css_colors;
|
66 |
-
if(!isset($_wp_admin_css_colors) || !is_array($_wp_admin_css_colors)){
|
67 |
return false;
|
68 |
}
|
69 |
|
@@ -72,32 +76,31 @@ class DUP_UI_Screen
|
|
72 |
if (isset($_wp_admin_css_colors[$colorScheme])) {
|
73 |
return $_wp_admin_css_colors[$colorScheme];
|
74 |
} else {
|
75 |
-
return $_wp_admin_css_colors[
|
76 |
}
|
77 |
}
|
78 |
|
79 |
/**
|
80 |
* Get the help support tab view content shown in the help system
|
81 |
*
|
82 |
-
* @param string $guide
|
83 |
-
* @param string $faq
|
84 |
*
|
85 |
* @return null
|
86 |
*/
|
87 |
public function getSupportTab($guide, $faq)
|
88 |
{
|
89 |
$content = __("<b>Need Help?</b> Please check out these resources first:"
|
90 |
-
."<ul>"
|
91 |
-
."<li><a href='https://snapcreek.com/duplicator/docs/guide{$guide}' target='_sc-faq'>Full Online User Guide</a></li>"
|
92 |
-
."<li><a href='https://snapcreek.com/duplicator/docs/faqs-tech{$faq}' target='_sc-faq'>Frequently Asked Questions</a></li>"
|
93 |
-
."</ul>", 'duplicator');
|
94 |
|
95 |
$this->screen->add_help_tab(array(
|
96 |
'id' => 'dup_help_tab_callback',
|
97 |
'title' => esc_html__('Support', 'duplicator'),
|
98 |
'content' => "<p>{$content}</p>"
|
99 |
-
)
|
100 |
-
);
|
101 |
}
|
102 |
|
103 |
/**
|
@@ -113,11 +116,11 @@ class DUP_UI_Screen
|
|
113 |
$txt_faq = __("Technical FAQs", 'duplicator');
|
114 |
$txt_sets = __("Package Settings", 'duplicator');
|
115 |
$this->screen->set_help_sidebar(
|
116 |
-
"<div class='dup-screen-hlp-info'><b>".esc_html($txt_title).":</b> <br/>"
|
117 |
-
."<i class='fa fa-home'></i> <a href='https://snapcreek.com/duplicator/docs/' target='_sc-home'>".esc_html($txt_home)."</a> <br/>"
|
118 |
-
."<i class='fa fa-book'></i> <a href='https://snapcreek.com/duplicator/docs/guide/' target='_sc-guide'>".esc_html($txt_guide)."</a> <br/>"
|
119 |
-
."<i class='far fa-file-code'></i> <a href='https://snapcreek.com/duplicator/docs/faqs-tech/' target='_sc-faq'>".esc_html($txt_faq)."</a> <br/>"
|
120 |
-
."<i class='fa fa-cog'></i> <a href='admin.php?page=duplicator-settings&tab=package'>".esc_html($txt_sets)."</a></div>"
|
121 |
);
|
122 |
}
|
123 |
}
|
1 |
<?php
|
2 |
+
|
3 |
+
use Duplicator\Libs\Snap\SnapUtil;
|
4 |
+
|
5 |
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
6 |
/**
|
7 |
* The base class for all screen.php files. This class is used to control items that are common
|
17 |
*
|
18 |
*/
|
19 |
// Exit if accessed directly
|
20 |
+
if (!defined('DUPLICATOR_VERSION')) {
|
21 |
exit;
|
22 |
+
}
|
23 |
|
24 |
class DUP_UI_Screen
|
25 |
{
|
|
|
26 |
/**
|
27 |
* Used as a placeholder for the current screen object
|
28 |
*/
|
33 |
*/
|
34 |
public function __construct()
|
35 |
{
|
|
|
36 |
}
|
37 |
|
38 |
public static function getCustomCss()
|
39 |
{
|
40 |
$screen = get_current_screen();
|
41 |
+
if (
|
42 |
+
!in_array($screen->id, array(
|
43 |
'toplevel_page_duplicator',
|
44 |
'duplicator_page_duplicator-tools',
|
45 |
'duplicator_page_duplicator-settings',
|
46 |
+
'duplicator_page_duplicator-gopro'))
|
47 |
+
) {
|
48 |
return;
|
49 |
}
|
50 |
|
67 |
public static function getCurrentColorScheme()
|
68 |
{
|
69 |
global $_wp_admin_css_colors;
|
70 |
+
if (!isset($_wp_admin_css_colors) || !is_array($_wp_admin_css_colors)) {
|
71 |
return false;
|
72 |
}
|
73 |
|
76 |
if (isset($_wp_admin_css_colors[$colorScheme])) {
|
77 |
return $_wp_admin_css_colors[$colorScheme];
|
78 |
} else {
|
79 |
+
return $_wp_admin_css_colors[SnapUtil::arrayKeyFirst($_wp_admin_css_colors)];
|
80 |
}
|
81 |
}
|
82 |
|
83 |
/**
|
84 |
* Get the help support tab view content shown in the help system
|
85 |
*
|
86 |
+
* @param string $guide The target URL to navigate to on the online user guide
|
87 |
+
* @param string $faq The target URL to navigate to on the online user tech FAQ
|
88 |
*
|
89 |
* @return null
|
90 |
*/
|
91 |
public function getSupportTab($guide, $faq)
|
92 |
{
|
93 |
$content = __("<b>Need Help?</b> Please check out these resources first:"
|
94 |
+
. "<ul>"
|
95 |
+
. "<li><a href='https://snapcreek.com/duplicator/docs/guide{$guide}' target='_sc-faq'>Full Online User Guide</a></li>"
|
96 |
+
. "<li><a href='https://snapcreek.com/duplicator/docs/faqs-tech{$faq}' target='_sc-faq'>Frequently Asked Questions</a></li>"
|
97 |
+
. "</ul>", 'duplicator');
|
98 |
|
99 |
$this->screen->add_help_tab(array(
|
100 |
'id' => 'dup_help_tab_callback',
|
101 |
'title' => esc_html__('Support', 'duplicator'),
|
102 |
'content' => "<p>{$content}</p>"
|
103 |
+
));
|
|
|
104 |
}
|
105 |
|
106 |
/**
|
116 |
$txt_faq = __("Technical FAQs", 'duplicator');
|
117 |
$txt_sets = __("Package Settings", 'duplicator');
|
118 |
$this->screen->set_help_sidebar(
|
119 |
+
"<div class='dup-screen-hlp-info'><b>" . esc_html($txt_title) . ":</b> <br/>"
|
120 |
+
. "<i class='fa fa-home'></i> <a href='https://snapcreek.com/duplicator/docs/' target='_sc-home'>" . esc_html($txt_home) . "</a> <br/>"
|
121 |
+
. "<i class='fa fa-book'></i> <a href='https://snapcreek.com/duplicator/docs/guide/' target='_sc-guide'>" . esc_html($txt_guide) . "</a> <br/>"
|
122 |
+
. "<i class='far fa-file-code'></i> <a href='https://snapcreek.com/duplicator/docs/faqs-tech/' target='_sc-faq'>" . esc_html($txt_faq) . "</a> <br/>"
|
123 |
+
. "<i class='fa fa-cog'></i> <a href='admin.php?page=duplicator-settings&tab=package'>" . esc_html($txt_sets) . "</a></div>"
|
124 |
);
|
125 |
}
|
126 |
}
|
classes/ui/class.ui.viewstate.php
CHANGED
@@ -1,83 +1,85 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
*
|
6 |
-
*
|
7 |
-
*
|
8 |
-
*
|
9 |
-
*
|
10 |
-
* @
|
11 |
-
* @
|
12 |
-
*
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
if
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
*
|
31 |
-
*
|
32 |
-
*
|
33 |
-
* @
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
$view_state
|
40 |
-
$
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
*
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
*
|
58 |
-
* @
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
*
|
69 |
-
*
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
4 |
+
/**
|
5 |
+
* Gets the view state of UI elements to remember its viewable state
|
6 |
+
*
|
7 |
+
* Standard: PSR-2
|
8 |
+
* @link http://www.php-fig.org/psr/psr-2
|
9 |
+
*
|
10 |
+
* @package Duplicator
|
11 |
+
* @subpackage classes/ui
|
12 |
+
* @copyright (c) 2017, Snapcreek LLC
|
13 |
+
*
|
14 |
+
*/
|
15 |
+
|
16 |
+
// Exit if accessed directly
|
17 |
+
if (! defined('DUPLICATOR_VERSION')) {
|
18 |
+
exit;
|
19 |
+
}
|
20 |
+
|
21 |
+
class DUP_UI_ViewState
|
22 |
+
{
|
23 |
+
/**
|
24 |
+
* The key used in the wp_options table
|
25 |
+
*
|
26 |
+
* @var string
|
27 |
+
*/
|
28 |
+
private static $optionsViewStateKey = 'duplicator_ui_view_state';
|
29 |
+
/**
|
30 |
+
* Save the view state of UI elements
|
31 |
+
*
|
32 |
+
* @param string $key A unique key to define the UI element
|
33 |
+
* @param string $value A generic value to use for the view state
|
34 |
+
*
|
35 |
+
* @return bool Returns true if the value was successfully saved
|
36 |
+
*/
|
37 |
+
public static function save($key, $value)
|
38 |
+
{
|
39 |
+
$view_state = array();
|
40 |
+
$view_state = get_option(self::$optionsViewStateKey);
|
41 |
+
$view_state[$key] = $value;
|
42 |
+
$success = update_option(self::$optionsViewStateKey, $view_state);
|
43 |
+
return $success;
|
44 |
+
}
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Gets all the values from the settings array
|
48 |
+
*
|
49 |
+
* @return array Returns and array of all the values stored in the settings array
|
50 |
+
*/
|
51 |
+
public static function getArray()
|
52 |
+
{
|
53 |
+
return get_option(self::$optionsViewStateKey);
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Sets all the values from the settings array
|
58 |
+
* @param array $view_state states
|
59 |
+
*
|
60 |
+
* @return boolean Returns whether updated or not
|
61 |
+
*/
|
62 |
+
public static function setArray($view_state)
|
63 |
+
{
|
64 |
+
return update_option(self::$optionsViewStateKey, $view_state);
|
65 |
+
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Return the value of the of view state item
|
69 |
+
*
|
70 |
+
* @param type $searchKey The key to search on
|
71 |
+
* @return string Returns the value of the key searched or null if key is not found
|
72 |
+
*/
|
73 |
+
public static function getValue($searchKey)
|
74 |
+
{
|
75 |
+
$view_state = get_option(self::$optionsViewStateKey);
|
76 |
+
if (is_array($view_state)) {
|
77 |
+
foreach ($view_state as $key => $value) {
|
78 |
+
if ($key == $searchKey) {
|
79 |
+
return $value;
|
80 |
+
}
|
81 |
+
}
|
82 |
+
}
|
83 |
+
return null;
|
84 |
+
}
|
85 |
+
}
|
classes/ui/index.php
CHANGED
@@ -1,2 +1,3 @@
|
|
1 |
<?php
|
2 |
-
|
|
1 |
<?php
|
2 |
+
|
3 |
+
//silent
|
classes/utilities/class.u.json.php
CHANGED
@@ -1,167 +1,166 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
*
|
8 |
-
*
|
9 |
-
*
|
10 |
-
* @
|
11 |
-
*
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
{
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
$jsonString =
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
use Duplicator\Libs\Snap\SnapJson;
|
4 |
+
|
5 |
+
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
6 |
+
/**
|
7 |
+
* Utility class for working with JSON data
|
8 |
+
*
|
9 |
+
* Standard: PSR-2
|
10 |
+
* @link http://www.php-fig.org/psr/psr-2
|
11 |
+
*
|
12 |
+
* @subpackage classes/utilities
|
13 |
+
* @copyright (c) 2017, Snapcreek LLC
|
14 |
+
* @license https://opensource.org/licenses/GPL-3.0 GNU Public License
|
15 |
+
*/
|
16 |
+
|
17 |
+
// Exit if accessed directly
|
18 |
+
if (! defined('DUPLICATOR_VERSION')) {
|
19 |
+
exit;
|
20 |
+
}
|
21 |
+
|
22 |
+
class DUP_JSON
|
23 |
+
{
|
24 |
+
// PHP 5.3 doesn't allow concating of strings within an array initializer so accepting long string
|
25 |
+
protected static $messages = array(
|
26 |
+
JSON_ERROR_NONE => 'No error has occurred',
|
27 |
+
JSON_ERROR_DEPTH => 'The maximum stack depth has been exceeded',
|
28 |
+
JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON',
|
29 |
+
JSON_ERROR_CTRL_CHAR => 'Control character error, possibly incorrectly encoded',
|
30 |
+
JSON_ERROR_SYNTAX => 'Syntax error',
|
31 |
+
JSON_ERROR_UTF8 => 'Malformed UTF-8 characters. To resolve see https://snapcreek.com/duplicator/docs/faqs-tech/?utm_source=duplicator_free&utm_medium=wordpress_plugin&utm_campaign=problem_resolution&utm_content=malformed_utf8#faq-package-170-q'
|
32 |
+
);
|
33 |
+
/**
|
34 |
+
* Used on PHP 5.3+ to better handle calling the json_encode method
|
35 |
+
*
|
36 |
+
* Returns a string containing the JSON representation of the supplied value
|
37 |
+
*
|
38 |
+
* @return string
|
39 |
+
*/
|
40 |
+
public static function customEncode($value, $iteration = 1)
|
41 |
+
{
|
42 |
+
if (DUP_Util::$on_php_53_plus) {
|
43 |
+
$encoded = SnapJson::jsonEncodePPrint($value);
|
44 |
+
switch (json_last_error()) {
|
45 |
+
case JSON_ERROR_NONE:
|
46 |
+
return $encoded;
|
47 |
+
case JSON_ERROR_DEPTH:
|
48 |
+
throw new RuntimeException('Maximum stack depth exceeded');
|
49 |
+
case JSON_ERROR_STATE_MISMATCH:
|
50 |
+
throw new RuntimeException('Underflow or the modes mismatch');
|
51 |
+
case JSON_ERROR_CTRL_CHAR:
|
52 |
+
throw new RuntimeException('Unexpected control character found');
|
53 |
+
case JSON_ERROR_SYNTAX:
|
54 |
+
throw new RuntimeException('Syntax error, malformed JSON');
|
55 |
+
case JSON_ERROR_UTF8:
|
56 |
+
if ($iteration == 1) {
|
57 |
+
$clean = self::makeUTF8($value);
|
58 |
+
return self::customEncode($clean, $iteration + 1);
|
59 |
+
} else {
|
60 |
+
throw new RuntimeException('UTF-8 error loop');
|
61 |
+
}
|
62 |
+
default:
|
63 |
+
throw new RuntimeException('Unknown error');
|
64 |
+
}
|
65 |
+
} else {
|
66 |
+
return self::oldCustomEncode($value);
|
67 |
+
}
|
68 |
+
}
|
69 |
+
|
70 |
+
public static function safeEncode($data, $options = 0, $depth = 512)
|
71 |
+
{
|
72 |
+
try {
|
73 |
+
$jsonString = SnapJson::jsonEncode($data, $options, $depth);
|
74 |
+
} catch (Exception $e) {
|
75 |
+
$jsonString = false;
|
76 |
+
}
|
77 |
+
|
78 |
+
if (($jsonString === false) || trim($jsonString) == '') {
|
79 |
+
$jsonString = self::customEncode($value);
|
80 |
+
if (($jsonString === false) || trim($jsonString) == '') {
|
81 |
+
throw new Exception('Unable to generate JSON from object');
|
82 |
+
}
|
83 |
+
}
|
84 |
+
return $jsonString;
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Attempts to only call the json_decode method directly
|
89 |
+
*
|
90 |
+
* Returns the value encoded in json in appropriate PHP type. Values true, false and null are returned as TRUE, FALSE and NULL respectively.
|
91 |
+
* NULL is returned if the json cannot be decoded or if the encoded data is deeper than the recursion limit.
|
92 |
+
*
|
93 |
+
* @return object
|
94 |
+
*/
|
95 |
+
public static function decode($json, $assoc = false)
|
96 |
+
{
|
97 |
+
$result = json_decode($json, $assoc);
|
98 |
+
if ($result !== null) {
|
99 |
+
return $result;
|
100 |
+
}
|
101 |
+
|
102 |
+
if (function_exists('json_last_error')) {
|
103 |
+
throw new RuntimeException(self::$messages[json_last_error()]);
|
104 |
+
} else {
|
105 |
+
throw new RuntimeException("DUP_JSON decode error");
|
106 |
+
}
|
107 |
+
}
|
108 |
+
|
109 |
+
private static function makeUTF8($mixed)
|
110 |
+
{
|
111 |
+
if (is_array($mixed)) {
|
112 |
+
foreach ($mixed as $key => $value) {
|
113 |
+
$mixed[$key] = self::makeUTF8($value);
|
114 |
+
}
|
115 |
+
} elseif (is_string($mixed)) {
|
116 |
+
return utf8_encode($mixed);
|
117 |
+
}
|
118 |
+
return $mixed;
|
119 |
+
}
|
120 |
+
|
121 |
+
private static function escapeString($str)
|
122 |
+
{
|
123 |
+
return addcslashes($str, "\v\t\n\r\f\"\\/");
|
124 |
+
}
|
125 |
+
|
126 |
+
private static function oldCustomEncode($in)
|
127 |
+
{
|
128 |
+
$out = "";
|
129 |
+
if (is_object($in)) {
|
130 |
+
$arr[$key] = "\"" . self::escapeString($key) . "\":\"{$val}\"";
|
131 |
+
$in = get_object_vars($in);
|
132 |
+
}
|
133 |
+
|
134 |
+
if (is_array($in)) {
|
135 |
+
$obj = false;
|
136 |
+
$arr = array();
|
137 |
+
foreach ($in as $key => $val) {
|
138 |
+
if (!is_numeric($key)) {
|
139 |
+
$obj = true;
|
140 |
+
}
|
141 |
+
$arr[$key] = self::oldCustomEncode($val);
|
142 |
+
}
|
143 |
+
|
144 |
+
if ($obj) {
|
145 |
+
foreach ($arr as $key => $val) {
|
146 |
+
$arr[$key] = "\"" . self::escapeString($key) . "\":{$val}";
|
147 |
+
}
|
148 |
+
$val = implode(',', $arr);
|
149 |
+
$out .= "{{$val}}";
|
150 |
+
} else {
|
151 |
+
$val = implode(',', $arr);
|
152 |
+
$out .= "[{$val}]";
|
153 |
+
}
|
154 |
+
} elseif (is_bool($in)) {
|
155 |
+
$out .= $in ? 'true' : 'false';
|
156 |
+
} elseif (is_null($in)) {
|
157 |
+
$out .= 'null';
|
158 |
+
} elseif (is_string($in)) {
|
159 |
+
$out .= "\"" . self::escapeString($in) . "\"";
|
160 |
+
} else {
|
161 |
+
$out .= $in;
|
162 |
+
}
|
163 |
+
|
164 |
+
return "{$out}";
|
165 |
+
}
|
166 |
+
}
|
|
classes/utilities/class.u.migration.php
DELETED
@@ -1,137 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Utility class managing th emigration data
|
5 |
-
*
|
6 |
-
* Standard: PSR_2
|
7 |
-
* @link http://www.php_fig.org/psr/psr_2
|
8 |
-
* @copyright (c) 2017, Snapcreek LLC
|
9 |
-
* @license https://opensource.org/licenses/GPL_3.0 GNU Public License
|
10 |
-
*
|
11 |
-
*/
|
12 |
-
|
13 |
-
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
14 |
-
|
15 |
-
class DUP_Migration
|
16 |
-
{
|
17 |
-
const CLEAN_INSTALL_REPORT_OPTION = 'duplicator_clean_install_report';
|
18 |
-
const ARCHIVE_REGEX_PATTERN = '/^(.+_[a-z0-9]{7,}_[0-9]{14})_archive\.(?:zip|daf)$/';
|
19 |
-
|
20 |
-
/**
|
21 |
-
* messages to be displayed in the successful migration box
|
22 |
-
*
|
23 |
-
* @var array
|
24 |
-
*/
|
25 |
-
protected static $migrationCleanupReport = array(
|
26 |
-
'removed' => array(),
|
27 |
-
'stored' => array(),
|
28 |
-
'instFile' => array()
|
29 |
-
);
|
30 |
-
|
31 |
-
/**
|
32 |
-
* Check the root path and in case there are installer files without hashes rename them.
|
33 |
-
*
|
34 |
-
* @return void
|
35 |
-
*/
|
36 |
-
public static function renameInstallersPhpFiles()
|
37 |
-
{
|
38 |
-
$pathsTocheck = array(
|
39 |
-
DupLiteSnapLibIOU::safePathTrailingslashit(ABSPATH),
|
40 |
-
DupLiteSnapLibIOU::safePathTrailingslashit(DupLiteSnapLibUtilWp::getHomePath()),
|
41 |
-
DupLiteSnapLibIOU::safePathTrailingslashit(WP_CONTENT_DIR)
|
42 |
-
);
|
43 |
-
|
44 |
-
$pathsTocheck = array_unique($pathsTocheck);
|
45 |
-
|
46 |
-
$filesToCheck = array();
|
47 |
-
foreach ($pathsTocheck as $cFolder) {
|
48 |
-
if (
|
49 |
-
!is_dir($cFolder) ||
|
50 |
-
!is_writable($cFolder) // rename permissions
|
51 |
-
) {
|
52 |
-
continue;
|
53 |
-
}
|
54 |
-
$cFile = $cFolder . 'installer.php';
|
55 |
-
if (
|
56 |
-
!is_file($cFile) ||
|
57 |
-
!DupLiteSnapLibIOU::chmod($cFile, 'u+rw') ||
|
58 |
-
!is_readable($cFile)
|
59 |
-
) {
|
60 |
-
continue;
|
61 |
-
}
|
62 |
-
$filesToCheck[] = $cFile;
|
63 |
-
}
|
64 |
-
|
65 |
-
$installerTplCheck = '/class DUPX_Bootstrap.+const\s+ARCHIVE_FILENAME\s*=\s*[\'"](.+?)[\'"]\s*;.*const\s+PACKAGE_HASH\s*=\s*[\'"](.+?)[\'"];/s';
|
66 |
-
|
67 |
-
foreach ($filesToCheck as $file) {
|
68 |
-
$fileName = basename($file);
|
69 |
-
if (($content = @file_get_contents($file, false, null, 0, 5000)) === false) {
|
70 |
-
continue;
|
71 |
-
}
|
72 |
-
$matches = null;
|
73 |
-
if (preg_match($installerTplCheck, $content, $matches) !== 1) {
|
74 |
-
continue;
|
75 |
-
}
|
76 |
-
|
77 |
-
$archiveName = $matches[1];
|
78 |
-
$hash = $matches[2];
|
79 |
-
$matches = null;
|
80 |
-
|
81 |
-
if (preg_match(self::ARCHIVE_REGEX_PATTERN, $archiveName, $matches) !== 1) {
|
82 |
-
if (DupLiteSnapLibIOU::unlink($file)) {
|
83 |
-
self::$migrationCleanupReport['instFile'][] = "<div class='failure'>"
|
84 |
-
. "<i class='fa fa-check green'></i> "
|
85 |
-
. sprintf(__('Installer file <b>%s</b> removed for secority reasons', 'duplicator'), esc_html($fileName))
|
86 |
-
. "</div>";
|
87 |
-
} else {
|
88 |
-
self::$migrationCleanupReport['instFile'][] = "<div class='success'>"
|
89 |
-
. '<i class="fa fa-exclamation-triangle red"></i> '
|
90 |
-
. sprintf(__('Can\'t remove installer file <b>%s</b>, please remove it for security reasons', 'duplicator'), esc_html($fileName))
|
91 |
-
. '</div>';
|
92 |
-
}
|
93 |
-
continue;
|
94 |
-
}
|
95 |
-
|
96 |
-
$archiveHash = $matches[1];
|
97 |
-
if (strpos($file, $archiveHash) === false) {
|
98 |
-
if (DupLiteSnapLibIOU::rename($file, dirname($file) . '/' . $archiveHash . '_installer.php', true)) {
|
99 |
-
self::$migrationCleanupReport['instFile'][] = "<div class='failure'>"
|
100 |
-
. "<i class='fa fa-check green'></i> "
|
101 |
-
. sprintf(__('Installer file <b>%s</b> renamed with HASH', 'duplicator'), esc_html($fileName))
|
102 |
-
. "</div>";
|
103 |
-
} else {
|
104 |
-
self::$migrationCleanupReport['instFile'][] = "<div class='success'>"
|
105 |
-
. '<i class="fa fa-exclamation-triangle red"></i> '
|
106 |
-
. sprintf(__('Can\'t rename installer file <b>%s</b> with HASH, please remove it for security reasons', 'duplicator'), esc_html($fileName))
|
107 |
-
. '</div>';
|
108 |
-
}
|
109 |
-
}
|
110 |
-
}
|
111 |
-
}
|
112 |
-
|
113 |
-
/**
|
114 |
-
* return cleanup report
|
115 |
-
*
|
116 |
-
* @return array
|
117 |
-
*/
|
118 |
-
public static function getCleanupReport()
|
119 |
-
{
|
120 |
-
$option = get_option(self::CLEAN_INSTALL_REPORT_OPTION);
|
121 |
-
if (is_array($option)) {
|
122 |
-
self::$migrationCleanupReport = array_merge(self::$migrationCleanupReport, $option);
|
123 |
-
}
|
124 |
-
|
125 |
-
return self::$migrationCleanupReport;
|
126 |
-
}
|
127 |
-
|
128 |
-
/**
|
129 |
-
* save clean up report in wordpress options
|
130 |
-
*
|
131 |
-
* @return boolean
|
132 |
-
*/
|
133 |
-
public static function saveCleanupReport()
|
134 |
-
{
|
135 |
-
return add_option(self::CLEAN_INSTALL_REPORT_OPTION, self::$migrationCleanupReport, '', 'no');
|
136 |
-
}
|
137 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
classes/utilities/class.u.multisite.php
CHANGED
@@ -1,39 +1,264 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
use Duplicator\Libs\Snap\SnapDB;
|
4 |
+
use Duplicator\Libs\Snap\SnapIO;
|
5 |
+
use Duplicator\Libs\Snap\SnapURL;
|
6 |
+
use Duplicator\Libs\Snap\SnapWP;
|
7 |
+
|
8 |
+
class DUP_MU
|
9 |
+
{
|
10 |
+
public static function networkMenuPageUrl($menu_slug, $echo = true)
|
11 |
+
{
|
12 |
+
global $_parent_pages;
|
13 |
+
|
14 |
+
if (isset($_parent_pages[$menu_slug])) {
|
15 |
+
$parent_slug = $_parent_pages[$menu_slug];
|
16 |
+
if ($parent_slug && !isset($_parent_pages[$parent_slug])) {
|
17 |
+
$url = network_admin_url(add_query_arg('page', $menu_slug, $parent_slug));
|
18 |
+
} else {
|
19 |
+
$url = network_admin_url('admin.php?page=' . $menu_slug);
|
20 |
+
}
|
21 |
+
} else {
|
22 |
+
$url = '';
|
23 |
+
}
|
24 |
+
|
25 |
+
$url = esc_url($url);
|
26 |
+
|
27 |
+
if ($echo) {
|
28 |
+
echo esc_url($url);
|
29 |
+
}
|
30 |
+
|
31 |
+
return $url;
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* return multisite mode
|
36 |
+
* 0 = single site
|
37 |
+
* 1 = multisite subdomain
|
38 |
+
* 2 = multisite subdirectory
|
39 |
+
*
|
40 |
+
* @return int
|
41 |
+
*/
|
42 |
+
public static function getMode()
|
43 |
+
{
|
44 |
+
if (is_multisite()) {
|
45 |
+
if (defined('SUBDOMAIN_INSTALL') && SUBDOMAIN_INSTALL) {
|
46 |
+
return 1;
|
47 |
+
} else {
|
48 |
+
return 2;
|
49 |
+
}
|
50 |
+
} else {
|
51 |
+
return 0;
|
52 |
+
}
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* This function is wrong because it assumes that if the folder sites exist, blogs.dir cannot exist.
|
57 |
+
* This is not true because if the network is old but a new site is created after the WordPress update both blogs.dir and sites folders exist.
|
58 |
+
*
|
59 |
+
* @deprecated since version 3.8.4
|
60 |
+
*
|
61 |
+
* @return int
|
62 |
+
*/
|
63 |
+
public static function getGeneration()
|
64 |
+
{
|
65 |
+
if (self::getMode() == 0) {
|
66 |
+
return 0;
|
67 |
+
} else {
|
68 |
+
$sitesDir = WP_CONTENT_DIR . '/uploads/sites';
|
69 |
+
|
70 |
+
if (file_exists($sitesDir)) {
|
71 |
+
return 2;
|
72 |
+
} else {
|
73 |
+
return 1;
|
74 |
+
}
|
75 |
+
}
|
76 |
+
}
|
77 |
+
|
78 |
+
/**
|
79 |
+
*
|
80 |
+
* @param array $filteredSites
|
81 |
+
* @param array $filteredTables
|
82 |
+
* @param array $filteredPaths
|
83 |
+
*
|
84 |
+
* @return array
|
85 |
+
*/
|
86 |
+
public static function getSubsites($filteredSites = array(), $filteredTables = array(), $filteredPaths = array())
|
87 |
+
{
|
88 |
+
if (!is_multisite()) {
|
89 |
+
return array(
|
90 |
+
self::getSubsiteInfo(1, $filteredTables, $filteredPaths)
|
91 |
+
);
|
92 |
+
}
|
93 |
+
|
94 |
+
$site_array = array();
|
95 |
+
$filteredSites = is_array($filteredSites) ? $filteredSites : array();
|
96 |
+
|
97 |
+
DUP_Log::trace("NETWORK SITES");
|
98 |
+
|
99 |
+
foreach (SnapWP::getSitesIds() as $siteId) {
|
100 |
+
if (in_array($siteId, $filteredSites)) {
|
101 |
+
continue;
|
102 |
+
}
|
103 |
+
if (($siteInfo = self::getSubsiteInfo($siteId, $filteredTables, $filteredPaths)) == false) {
|
104 |
+
continue;
|
105 |
+
}
|
106 |
+
array_push($site_array, $siteInfo);
|
107 |
+
DUP_Log::trace("Multisite subsite detected. ID={$siteInfo->id} Domain={$siteInfo->domain} Path={$siteInfo->path} Blogname={$siteInfo->blogname}");
|
108 |
+
}
|
109 |
+
|
110 |
+
return $site_array;
|
111 |
+
}
|
112 |
+
|
113 |
+
/**
|
114 |
+
*
|
115 |
+
* @param int $subsiteId
|
116 |
+
*
|
117 |
+
* @return stdClass|bool false on failure
|
118 |
+
*/
|
119 |
+
public static function getSubsiteInfoById($subsiteId)
|
120 |
+
{
|
121 |
+
if (!is_multisite()) {
|
122 |
+
$subsiteId = 1;
|
123 |
+
}
|
124 |
+
return self::getSubsiteInfo($subsiteId);
|
125 |
+
}
|
126 |
+
|
127 |
+
/**
|
128 |
+
* Get subsite info
|
129 |
+
*
|
130 |
+
* @param int $siteId
|
131 |
+
* @param array $filteredTables
|
132 |
+
* @param array|false $filteredPaths return
|
133 |
+
*
|
134 |
+
* @return stdClass|bool false on failure
|
135 |
+
*/
|
136 |
+
public static function getSubsiteInfo($siteId = 1, $filteredTables = array(), $filteredPaths = array())
|
137 |
+
{
|
138 |
+
if (is_multisite()) {
|
139 |
+
if (($siteDetails = get_blog_details($siteId)) == false) {
|
140 |
+
return false;
|
141 |
+
}
|
142 |
+
} else {
|
143 |
+
$siteId = 1;
|
144 |
+
$siteDetails = new stdClass();
|
145 |
+
$home = DUP_Archive::getOriginalUrls('home');
|
146 |
+
$parsedHome = SnapURL::parseUrl($home);
|
147 |
+
$siteDetails->domain = $parsedHome['host'];
|
148 |
+
$siteDetails->path = trailingslashit($parsedHome['path']);
|
149 |
+
$siteDetails->blogname = sanitize_text_field(get_option('blogname'));
|
150 |
+
}
|
151 |
+
|
152 |
+
$subsiteID = $siteId;
|
153 |
+
$siteInfo = new stdClass();
|
154 |
+
$siteInfo->id = $subsiteID;
|
155 |
+
$siteInfo->domain = $siteDetails->domain;
|
156 |
+
$siteInfo->path = $siteDetails->path;
|
157 |
+
$siteInfo->blogname = $siteDetails->blogname;
|
158 |
+
$siteInfo->blog_prefix = $GLOBALS['wpdb']->get_blog_prefix($subsiteID);
|
159 |
+
if (count($filteredTables) > 0) {
|
160 |
+
$siteInfo->filteredTables = array_values(array_intersect(self::getSubsiteTables($subsiteID), $filteredTables));
|
161 |
+
} else {
|
162 |
+
$siteInfo->filteredTables = array();
|
163 |
+
}
|
164 |
+
$siteInfo->adminUsers = SnapWP::getAdminUserLists($siteInfo->id);
|
165 |
+
$siteInfo->fullHomeUrl = get_home_url($siteId);
|
166 |
+
$siteInfo->fullSiteUrl = get_site_url($siteId);
|
167 |
+
|
168 |
+
if ($siteId > 1) {
|
169 |
+
switch_to_blog($siteId);
|
170 |
+
}
|
171 |
+
|
172 |
+
$uploadData = wp_upload_dir();
|
173 |
+
$uploadPath = $uploadData['basedir'];
|
174 |
+
$siteInfo->uploadPath = SnapIO::getRelativePath($uploadPath, DUP_Archive::getTargetRootPath(), true);
|
175 |
+
$siteInfo->fullUploadPath = untrailingslashit($uploadPath);
|
176 |
+
$siteInfo->fullUploadSafePath = SnapIO::safePathUntrailingslashit($uploadPath);
|
177 |
+
$siteInfo->fullUploadUrl = $uploadData['baseurl'];
|
178 |
+
if (count($filteredPaths)) {
|
179 |
+
$globalDirFilters = apply_filters('duplicator_global_file_filters', $GLOBALS['DUPLICATOR_GLOBAL_FILE_FILTERS']);
|
180 |
+
$siteInfo->filteredPaths = array_values(array_filter($filteredPaths, function ($path) use ($uploadPath, $subsiteID, $globalDirFilters) {
|
181 |
+
if (
|
182 |
+
($relativeUpload = SnapIO::getRelativePath($path, $uploadPath)) === false ||
|
183 |
+
in_array($path, $globalDirFilters)
|
184 |
+
) {
|
185 |
+
return false;
|
186 |
+
}
|
187 |
+
|
188 |
+
if ($subsiteID > 1) {
|
189 |
+
return true;
|
190 |
+
} else {
|
191 |
+
// no check on blogs.dir because in wp-content/blogs.dir not in upload folder
|
192 |
+
return !(strpos($relativeUpload, 'sites') === 0);
|
193 |
+
}
|
194 |
+
}));
|
195 |
+
} else {
|
196 |
+
$siteInfo->filteredPaths = array();
|
197 |
+
}
|
198 |
+
|
199 |
+
if ($siteId > 1) {
|
200 |
+
restore_current_blog();
|
201 |
+
}
|
202 |
+
return $siteInfo;
|
203 |
+
}
|
204 |
+
|
205 |
+
/**
|
206 |
+
* @param int $subsiteID
|
207 |
+
*
|
208 |
+
* @return array List of tables belonging to subsite
|
209 |
+
*/
|
210 |
+
public static function getSubsiteTables($subsiteID)
|
211 |
+
{
|
212 |
+
/** @var \wpdb $wpdb */
|
213 |
+
global $wpdb;
|
214 |
+
|
215 |
+
$basePrefix = $wpdb->base_prefix;
|
216 |
+
$subsitePrefix = $wpdb->get_blog_prefix($subsiteID);
|
217 |
+
|
218 |
+
$sharedTables = array(
|
219 |
+
$basePrefix . 'users',
|
220 |
+
$basePrefix . 'usermeta'
|
221 |
+
);
|
222 |
+
$multisiteOnlyTables = array(
|
223 |
+
$basePrefix . 'blogmeta',
|
224 |
+
$basePrefix . 'blogs',
|
225 |
+
$basePrefix . 'blog_versions',
|
226 |
+
$basePrefix . 'registration_log',
|
227 |
+
$basePrefix . 'signups',
|
228 |
+
$basePrefix . 'site',
|
229 |
+
$basePrefix . 'sitemeta'
|
230 |
+
);
|
231 |
+
|
232 |
+
$subsiteTables = array();
|
233 |
+
$sql = "";
|
234 |
+
$dbnameSafe = esc_sql(DB_NAME);
|
235 |
+
|
236 |
+
if ($subsiteID != 1) {
|
237 |
+
$regex = '^' . SnapDB::quoteRegex($subsitePrefix);
|
238 |
+
$regexpSafe = esc_sql($regex);
|
239 |
+
|
240 |
+
$sharedTablesSafe = "'" . implode(
|
241 |
+
"', '",
|
242 |
+
esc_sql($sharedTables)
|
243 |
+
) . "'";
|
244 |
+
$sql = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '$dbnameSafe' AND ";
|
245 |
+
$sql .= "(TABLE_NAME REGEXP '$regexpSafe' OR TABLE_NAME IN ($sharedTablesSafe))";
|
246 |
+
} else {
|
247 |
+
$regexMain = '^' . SnapDB::quoteRegex($basePrefix);
|
248 |
+
$regexpMainSafe = esc_sql($regexMain);
|
249 |
+
$regexNotSub = '^' . SnapDB::quoteRegex($basePrefix) . '[0-9]+_';
|
250 |
+
$regexpNotSubSafe = esc_sql($regexNotSub);
|
251 |
+
|
252 |
+
$multisiteOnlyTablesSafe = "'" . implode(
|
253 |
+
"', '",
|
254 |
+
esc_sql($multisiteOnlyTables)
|
255 |
+
) . "'";
|
256 |
+
$sql = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '$dbnameSafe' AND ";
|
257 |
+
$sql .= "TABLE_NAME REGEXP '$regexpMainSafe' AND ";
|
258 |
+
$sql .= "TABLE_NAME NOT REGEXP '$regexpNotSubSafe' AND ";
|
259 |
+
$sql .= "TABLE_NAME NOT IN ($multisiteOnlyTablesSafe)";
|
260 |
+
}
|
261 |
+
$subsiteTables = $wpdb->get_col($sql);
|
262 |
+
return $subsiteTables;
|
263 |
+
}
|
264 |
+
}
|
classes/utilities/class.u.patch.php
CHANGED
@@ -34,15 +34,46 @@ class DUP_Patch
|
|
34 |
{
|
35 |
$backupDir = $this->DupLiteBackupDir;
|
36 |
|
37 |
-
foreach (glob("{$backupDir}/*_installer"
|
38 |
-
if (strstr($file, '_installer'
|
39 |
$content = "<?php \n";
|
40 |
$content .= " /** PATCH_MARKER_START:V.0001 **/ \n";
|
41 |
$content .= " //TODO ADD PHP CODE HERE";
|
42 |
$content .= " /** PATCH_MARKER_END **/ \n";
|
43 |
$content .= "?>\n";
|
44 |
-
|
45 |
}
|
46 |
}
|
47 |
}
|
48 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
{
|
35 |
$backupDir = $this->DupLiteBackupDir;
|
36 |
|
37 |
+
foreach (glob("{$backupDir}/*_installer.php") as $file) {
|
38 |
+
if (strstr($file, '_installer.php')) {
|
39 |
$content = "<?php \n";
|
40 |
$content .= " /** PATCH_MARKER_START:V.0001 **/ \n";
|
41 |
$content .= " //TODO ADD PHP CODE HERE";
|
42 |
$content .= " /** PATCH_MARKER_END **/ \n";
|
43 |
$content .= "?>\n";
|
44 |
+
$this->fwritePrepend($file, $content);
|
45 |
}
|
46 |
}
|
47 |
}
|
48 |
+
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Prepends data to an existing file
|
52 |
+
*
|
53 |
+
* @param string $file The full file path to the file
|
54 |
+
* @param string $content The content to prepend to the file
|
55 |
+
*
|
56 |
+
* @return TRUE on success or if file does not exist. FALSE on failure
|
57 |
+
*/
|
58 |
+
private function fwritePrepend($file, $prepend)
|
59 |
+
{
|
60 |
+
if (!file_exists($file) || !is_writable($file)) {
|
61 |
+
return false;
|
62 |
+
}
|
63 |
+
|
64 |
+
$handle = fopen($file, "r+");
|
65 |
+
$len = strlen($prepend);
|
66 |
+
$final_len = filesize($file) + $len;
|
67 |
+
$cache_old = fread($handle, $len);
|
68 |
+
rewind($handle);
|
69 |
+
$i = 1;
|
70 |
+
while (ftell($handle) < $final_len) {
|
71 |
+
fwrite($handle, $prepend);
|
72 |
+
$prepend = $cache_old;
|
73 |
+
$cache_old = fread($handle, $len);
|
74 |
+
fseek($handle, $i * $len);
|
75 |
+
$i++;
|
76 |
+
}
|
77 |
+
}
|
78 |
+
|
79 |
+
}
|
classes/utilities/class.u.php
CHANGED
@@ -1,10 +1,9 @@
|
|
1 |
<?php
|
2 |
-
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
3 |
|
4 |
-
|
5 |
|
6 |
/**
|
7 |
-
*
|
8 |
*
|
9 |
* Standard: PSR-2
|
10 |
* @link http://www.php-fig.org/psr/psr-2
|
@@ -13,14 +12,11 @@ require_once(DUPLICATOR_PLUGIN_PATH . '/classes/class.io.php');
|
|
13 |
* @subpackage classes/utilities
|
14 |
* @copyright (c) 2017, Snapcreek LLC
|
15 |
*
|
|
|
16 |
*/
|
|
|
17 |
class DUP_Util
|
18 |
{
|
19 |
-
|
20 |
-
const SECURE_ISSUE_DIE = 'die';
|
21 |
-
const SECURE_ISSUE_THROW = 'throw';
|
22 |
-
const SECURE_ISSUE_RETURN = 'return';
|
23 |
-
|
24 |
/**
|
25 |
* Is PHP 5.2.9 or better running
|
26 |
*/
|
@@ -53,17 +49,16 @@ class DUP_Util
|
|
53 |
*/
|
54 |
public static function init()
|
55 |
{
|
56 |
-
|
57 |
-
self::$
|
58 |
-
self::$
|
59 |
-
self::$
|
|
|
60 |
}
|
61 |
|
62 |
public static function getArchitectureString()
|
63 |
{
|
64 |
-
|
65 |
-
|
66 |
-
switch ($php_int_size) {
|
67 |
case 4:
|
68 |
return esc_html__('32-bit', 'duplicator');
|
69 |
break;
|
@@ -87,18 +82,18 @@ class DUP_Util
|
|
87 |
|
88 |
public static function getWPCoreDirs()
|
89 |
{
|
90 |
-
$wp_core_dirs = array(get_home_path().'wp-admin', get_home_path().'wp-includes');
|
91 |
|
92 |
//if wp_content is overrided
|
93 |
-
$wp_path = get_home_path()."wp-content";
|
94 |
-
if (get_home_path().'wp-content' != WP_CONTENT_DIR) {
|
95 |
$wp_path = WP_CONTENT_DIR;
|
96 |
}
|
97 |
$wp_path = str_replace("\\", "/", $wp_path);
|
98 |
|
99 |
$wp_core_dirs[] = $wp_path;
|
100 |
-
$wp_core_dirs[] = $wp_path.'/plugins';
|
101 |
-
$wp_core_dirs[] = $wp_path.'/themes';
|
102 |
|
103 |
return $wp_core_dirs;
|
104 |
}
|
@@ -109,7 +104,7 @@ class DUP_Util
|
|
109 |
*/
|
110 |
public static function getWPCoreFiles()
|
111 |
{
|
112 |
-
$wp_cored_dirs = array(get_home_path().'wp-config.php');
|
113 |
return $wp_cored_dirs;
|
114 |
}
|
115 |
|
@@ -133,8 +128,8 @@ class DUP_Util
|
|
133 |
trigger_error('array_group_by(): The key should be a string, an integer, or a callback', E_USER_ERROR);
|
134 |
return null;
|
135 |
}
|
136 |
-
$func
|
137 |
-
$_key
|
138 |
// Load the new array, splitting by the target key
|
139 |
$grouped = array();
|
140 |
foreach ($array as $value) {
|
@@ -193,8 +188,9 @@ class DUP_Util
|
|
193 |
{
|
194 |
// Open file
|
195 |
$f = @fopen($filepath, "rb");
|
196 |
-
if ($f === false)
|
197 |
return false;
|
|
|
198 |
|
199 |
// Sets buffer size
|
200 |
$buffer = 256;
|
@@ -204,8 +200,9 @@ class DUP_Util
|
|
204 |
|
205 |
// Read it and adjust line number if necessary
|
206 |
// (Otherwise the result would be wrong if file doesn't end with a blank line)
|
207 |
-
if (fread($f, 1) != "\n")
|
208 |
$lines -= 1;
|
|
|
209 |
|
210 |
// Start reading
|
211 |
$output = '';
|
@@ -214,15 +211,15 @@ class DUP_Util
|
|
214 |
// While we would like more
|
215 |
while (ftell($f) > 0 && $lines >= 0) {
|
216 |
// Figure out how far back we should jump
|
217 |
-
$seek
|
218 |
// Do the jump (backwards, relative to where we are)
|
219 |
fseek($f, -$seek, SEEK_CUR);
|
220 |
// Read a chunk and prepend it to our output
|
221 |
-
$output = ($chunk = fread($f, $seek))
|
222 |
// Jump back to where we started reading
|
223 |
fseek($f, -mb_strlen($chunk, '8bit'), SEEK_CUR);
|
224 |
// Decrease our line counter
|
225 |
-
$lines
|
226 |
}
|
227 |
|
228 |
// While we have too many lines
|
@@ -249,9 +246,8 @@ class DUP_Util
|
|
249 |
for ($i = 0; $size >= 1024 && $i < 4; $i++) {
|
250 |
$size /= 1024;
|
251 |
}
|
252 |
-
return round($size, $roundBy)
|
253 |
-
}
|
254 |
-
catch (Exception $e) {
|
255 |
return "n/a";
|
256 |
}
|
257 |
}
|
@@ -262,7 +258,7 @@ class DUP_Util
|
|
262 |
* uni: /home/path/file.txt
|
263 |
* win: D:/home/path/file.txt
|
264 |
*
|
265 |
-
* @param string $path
|
266 |
*
|
267 |
* @return string A path with all slashes facing "/"
|
268 |
*/
|
@@ -293,7 +289,7 @@ class DUP_Util
|
|
293 |
*/
|
294 |
public static function appendOnce($string, $value)
|
295 |
{
|
296 |
-
return $string.(substr($string, -1) == $value ? '' : $value);
|
297 |
}
|
298 |
|
299 |
/**
|
@@ -319,8 +315,8 @@ class DUP_Util
|
|
319 |
* @return array of all files in that path
|
320 |
*
|
321 |
* Notes:
|
322 |
-
*
|
323 |
-
*
|
324 |
* - Scandir will not crash on unreadable items, but will not return results
|
325 |
*/
|
326 |
public static function listFiles($path = '.')
|
@@ -329,21 +325,21 @@ class DUP_Util
|
|
329 |
$files = array();
|
330 |
if ($dh = opendir($path)) {
|
331 |
while (($file = readdir($dh)) !== false) {
|
332 |
-
if ($file == '.' || $file == '..')
|
333 |
continue;
|
334 |
-
|
|
|
335 |
$files[] = str_replace("\\", '/', $full_file_path);
|
336 |
}
|
337 |
@closedir($dh);
|
338 |
}
|
339 |
return $files;
|
340 |
-
}
|
341 |
-
catch (Exception $exc) {
|
342 |
$result = array();
|
343 |
$files = @scandir($path);
|
344 |
if (is_array($files)) {
|
345 |
foreach ($files as $file) {
|
346 |
-
$result[] = str_replace("\\", '/', $path)
|
347 |
}
|
348 |
}
|
349 |
return $result;
|
@@ -378,8 +374,9 @@ class DUP_Util
|
|
378 |
*/
|
379 |
public static function isDirectoryEmpty($path)
|
380 |
{
|
381 |
-
if (!is_readable($path))
|
382 |
-
return
|
|
|
383 |
return (count(scandir($path)) == 2);
|
384 |
}
|
385 |
|
@@ -393,16 +390,19 @@ class DUP_Util
|
|
393 |
*/
|
394 |
public static function getDirectorySize($path)
|
395 |
{
|
396 |
-
if (!file_exists($path))
|
397 |
return 0;
|
398 |
-
|
|
|
399 |
return filesize($path);
|
|
|
400 |
|
401 |
$size = 0;
|
402 |
-
$list = glob($path."/*");
|
403 |
if (!empty($list)) {
|
404 |
-
foreach ($list as $file)
|
405 |
$size += self::getDirectorySize($file);
|
|
|
406 |
}
|
407 |
return $size;
|
408 |
}
|
@@ -418,24 +418,27 @@ class DUP_Util
|
|
418 |
$cmds = array('shell_exec', 'escapeshellarg', 'escapeshellcmd', 'extension_loaded');
|
419 |
|
420 |
//Function disabled at server level
|
421 |
-
if (array_intersect($cmds, array_map('trim', explode(',', @ini_get('disable_functions')))))
|
422 |
return apply_filters('duplicator_is_shellzip_available', false);
|
|
|
423 |
|
424 |
//Suhosin: http://www.hardened-php.net/suhosin/
|
425 |
//Will cause PHP to silently fail
|
426 |
if (extension_loaded('suhosin')) {
|
427 |
$suhosin_ini = @ini_get("suhosin.executor.func.blacklist");
|
428 |
-
if (array_intersect($cmds, array_map('trim', explode(',', $suhosin_ini))))
|
429 |
return apply_filters('duplicator_is_shellzip_available', false);
|
|
|
430 |
}
|
431 |
|
432 |
if (! function_exists('shell_exec')) {
|
433 |
-
|
434 |
-
|
435 |
|
436 |
// Can we issue a simple echo command?
|
437 |
-
if (!@shell_exec('echo duplicator'))
|
438 |
return apply_filters('duplicator_is_shellzip_available', false);
|
|
|
439 |
|
440 |
return apply_filters('duplicator_is_shellzip_available', true);
|
441 |
}
|
@@ -479,6 +482,9 @@ class DUP_Util
|
|
479 |
{
|
480 |
return base64_encode($string);
|
481 |
}
|
|
|
|
|
|
|
482 |
|
483 |
/**
|
484 |
* Does the current user have the capability
|
@@ -499,7 +505,7 @@ class DUP_Util
|
|
499 |
|
500 |
if (!current_user_can($capability)) {
|
501 |
$exitMsg = __('You do not have sufficient permissions to access this page.', 'duplicator');
|
502 |
-
DUP_LOG::Trace('You do not have sufficient permissions to access this page. PERMISSION: '
|
503 |
|
504 |
switch ($exit) {
|
505 |
case self::SECURE_ISSUE_THROW:
|
@@ -549,8 +555,7 @@ class DUP_Util
|
|
549 |
}
|
550 |
|
551 |
return strlen($user) ? $user : $unreadable;
|
552 |
-
}
|
553 |
-
catch (Exception $ex) {
|
554 |
return $unreadable;
|
555 |
}
|
556 |
}
|
@@ -562,44 +567,42 @@ class DUP_Util
|
|
562 |
*/
|
563 |
public static function initSnapshotDirectory()
|
564 |
{
|
565 |
-
$error
|
|
|
566 |
$path_wproot = duplicator_get_abs_path();
|
567 |
-
$
|
568 |
-
$path_plugin = DUP_Util::safePath(DUPLICATOR_PLUGIN_PATH);
|
569 |
|
570 |
-
|
571 |
-
//DIRCTORY CREATION
|
572 |
-
//Seupt the main directory and tmp build dir
|
573 |
-
if (!file_exists($backupsDir)) {
|
574 |
$old_root_perm = @fileperms($path_wproot);
|
575 |
|
|
|
576 |
//CHMOD DIRECTORY ACCESS
|
577 |
//wordpress root directory
|
578 |
-
|
579 |
|
580 |
//snapshot directory
|
581 |
-
if (
|
582 |
$error = true;
|
583 |
}
|
584 |
|
585 |
// restore original root perms
|
586 |
-
|
587 |
|
588 |
if ($error) {
|
589 |
return false;
|
590 |
}
|
591 |
}
|
592 |
|
593 |
-
|
594 |
-
|
595 |
|
596 |
//--------------------------------
|
597 |
//FILE CREATION & HARDEN PROCESS
|
598 |
//index.php, .htaccess, robots.txt
|
599 |
-
self::setupBackupDirIndexFile(
|
600 |
-
self::setupBackupDirRobotsFile(
|
601 |
-
self::setupBackupDirHtaccess(
|
602 |
-
self::performHardenProcesses(
|
603 |
|
604 |
return true;
|
605 |
}
|
@@ -607,50 +610,50 @@ class DUP_Util
|
|
607 |
/**
|
608 |
* Attempts to create a secure .htaccess file in the download directory
|
609 |
*
|
610 |
-
* @return
|
611 |
*/
|
612 |
-
|
613 |
{
|
614 |
try {
|
615 |
$storageHtaccessOff = DUP_Settings::Get('storage_htaccess_off');
|
616 |
-
$fileName =
|
617 |
|
618 |
if ($storageHtaccessOff) {
|
619 |
@unlink($fileName);
|
620 |
-
}
|
621 |
-
$
|
622 |
-
|
623 |
-
|
624 |
-
|
625 |
-
|
626 |
-
|
627 |
}
|
628 |
-
}
|
629 |
} catch (Exception $ex) {
|
630 |
-
|
631 |
}
|
632 |
}
|
633 |
|
634 |
/**
|
635 |
* Attempts to create an index.php file in the backups directory
|
636 |
*
|
637 |
-
* @return
|
638 |
*/
|
639 |
-
|
640 |
{
|
641 |
try {
|
642 |
-
$fileName
|
643 |
if (!file_exists($fileName)) {
|
644 |
-
$
|
645 |
-
|
646 |
-
|
647 |
-
|
648 |
-
|
649 |
-
|
650 |
}
|
651 |
}
|
652 |
} catch (Exception $ex) {
|
653 |
-
|
654 |
}
|
655 |
}
|
656 |
|
@@ -658,48 +661,50 @@ class DUP_Util
|
|
658 |
* Attempts to create a robots.txt file in the backups directory
|
659 |
* to prevent search engines
|
660 |
*
|
661 |
-
* @return
|
662 |
*/
|
663 |
-
|
664 |
{
|
665 |
try {
|
666 |
-
$fileName
|
667 |
if (!file_exists($fileName)) {
|
668 |
-
$
|
669 |
-
|
670 |
-
|
671 |
-
|
672 |
-
|
673 |
-
|
674 |
-
@fclose($robotfile);
|
675 |
}
|
676 |
}
|
677 |
} catch (Exception $ex) {
|
678 |
-
|
679 |
}
|
680 |
}
|
681 |
|
|
|
682 |
/**
|
683 |
* Run various secure processes to harden the backups dir
|
684 |
*
|
685 |
-
* @return
|
686 |
*/
|
687 |
-
public static function performHardenProcesses(
|
688 |
{
|
689 |
-
|
|
|
|
|
690 |
$dupInstallFile = "{$backupsDir}/dup-installer/main.installer.php";
|
691 |
if (file_exists($dupInstallFile) ) {
|
692 |
-
|
693 |
-
|
694 |
}
|
695 |
|
696 |
//Rename installer php files to .bak
|
697 |
-
|
698 |
$backupsDir,
|
699 |
function ($path) {
|
700 |
-
$parts
|
701 |
$newPath = $parts['dirname'] . '/' . $parts['filename'] . DUP_Installer::INSTALLER_SERVER_EXTENSION;
|
702 |
-
|
703 |
},
|
704 |
array(
|
705 |
'regexFile' => '/^.+_installer.*\.php$/',
|
@@ -719,7 +724,7 @@ class DUP_Util
|
|
719 |
$filepath = null;
|
720 |
|
721 |
if (self::hasShellExec()) {
|
722 |
-
if (shell_exec('hash zip 2>&1') ==
|
723 |
$filepath = 'zip';
|
724 |
} else {
|
725 |
$possible_paths = array(
|
@@ -760,7 +765,7 @@ class DUP_Util
|
|
760 |
global $wpdb;
|
761 |
$result = array();
|
762 |
foreach (self::getWPCoreTablesEnd() as $tend) {
|
763 |
-
$result[] = $wpdb->prefix
|
764 |
}
|
765 |
return $result;
|
766 |
}
|
@@ -803,7 +808,7 @@ class DUP_Util
|
|
803 |
|
804 |
if (in_array($subTName, $coreEnds)) {
|
805 |
return true;
|
806 |
-
}
|
807 |
$exTable = explode('_', $subTName);
|
808 |
if (count($exTable) >= 2 && is_numeric($exTable[0])) {
|
809 |
$tChekc = implode('_', array_slice($exTable, 1));
|
@@ -835,7 +840,7 @@ class DUP_Util
|
|
835 |
|
836 |
/**
|
837 |
* Check given table is exist in real
|
838 |
-
*
|
839 |
* @param $table string Table name
|
840 |
* @return booleam
|
841 |
*/
|
@@ -843,10 +848,11 @@ class DUP_Util
|
|
843 |
{
|
844 |
// It will clear the $GLOBALS['wpdb']->last_error var
|
845 |
$GLOBALS['wpdb']->flush();
|
846 |
-
$sql = "SELECT 1 FROM `".esc_sql($table)."` LIMIT 1;";
|
847 |
$ret = $GLOBALS['wpdb']->get_var($sql);
|
848 |
-
if (empty($GLOBALS['wpdb']->last_error))
|
849 |
return true;
|
|
|
850 |
return false;
|
851 |
}
|
852 |
|
@@ -859,8 +865,9 @@ class DUP_Util
|
|
859 |
*/
|
860 |
public static function isExecutable($cmd)
|
861 |
{
|
862 |
-
if (strlen($cmd) < 1)
|
863 |
return false;
|
|
|
864 |
|
865 |
if (@is_executable($cmd)) {
|
866 |
return true;
|
@@ -871,7 +878,7 @@ class DUP_Util
|
|
871 |
return true;
|
872 |
}
|
873 |
|
874 |
-
$output = shell_exec($cmd.' -?');
|
875 |
if (!is_null($output)) {
|
876 |
return true;
|
877 |
}
|
@@ -882,7 +889,7 @@ class DUP_Util
|
|
882 |
/**
|
883 |
* Display human readable byte sizes
|
884 |
*
|
885 |
-
* @param string $size
|
886 |
*
|
887 |
* @return string Human readable bytes such as 50MB, 1GB
|
888 |
*/
|
@@ -890,11 +897,11 @@ class DUP_Util
|
|
890 |
{
|
891 |
try {
|
892 |
$units = array('B', 'KB', 'MB', 'GB', 'TB');
|
893 |
-
for ($i = 0; $size >= 1024 && $i < 4; $i++)
|
894 |
-
$size
|
895 |
-
|
896 |
-
|
897 |
-
catch (Exception $e) {
|
898 |
return "n/a";
|
899 |
}
|
900 |
}
|
@@ -935,22 +942,4 @@ class DUP_Util
|
|
935 |
{
|
936 |
return function_exists($function_name) && !in_array($function_name, self::getIniDisableFuncs());
|
937 |
}
|
938 |
-
|
939 |
-
/**
|
940 |
-
* Is the web server IIS
|
941 |
-
*
|
942 |
-
* @return bool Returns true if web server is IIS
|
943 |
-
*/
|
944 |
-
public static function isIISRunning()
|
945 |
-
{
|
946 |
-
if (isset($_SERVER["SERVER_SOFTWARE"])) {
|
947 |
-
$sSoftware = strtolower($_SERVER["SERVER_SOFTWARE"]);
|
948 |
-
if ( strpos($sSoftware, "microsoft-iis") !== false ) {
|
949 |
-
return true;
|
950 |
-
} else {
|
951 |
-
return false;
|
952 |
-
}
|
953 |
-
}
|
954 |
-
return false;
|
955 |
-
}
|
956 |
-
}
|
1 |
<?php
|
|
|
2 |
|
3 |
+
use Duplicator\Libs\Snap\SnapIO;
|
4 |
|
5 |
/**
|
6 |
+
* Recursivly scans a directory and finds all sym-links and unreadable files
|
7 |
*
|
8 |
* Standard: PSR-2
|
9 |
* @link http://www.php-fig.org/psr/psr-2
|
12 |
* @subpackage classes/utilities
|
13 |
* @copyright (c) 2017, Snapcreek LLC
|
14 |
*
|
15 |
+
* @todo Refactor out IO methods into class.io.php file
|
16 |
*/
|
17 |
+
|
18 |
class DUP_Util
|
19 |
{
|
|
|
|
|
|
|
|
|
|
|
20 |
/**
|
21 |
* Is PHP 5.2.9 or better running
|
22 |
*/
|
49 |
*/
|
50 |
public static function init()
|
51 |
{
|
52 |
+
/** @todo Remove the static init method in favor of always consistent values */
|
53 |
+
self::$on_php_529_plus = version_compare(PHP_VERSION, '5.2.9') >= 0;
|
54 |
+
self::$on_php_53_plus = version_compare(PHP_VERSION, '5.3.0') >= 0;
|
55 |
+
self::$on_php_54_plus = version_compare(PHP_VERSION, '5.4.0') >= 0;
|
56 |
+
self::$PHP7_plus = version_compare(PHP_VERSION, '7.0.0', '>=');
|
57 |
}
|
58 |
|
59 |
public static function getArchitectureString()
|
60 |
{
|
61 |
+
switch (PHP_INT_SIZE) {
|
|
|
|
|
62 |
case 4:
|
63 |
return esc_html__('32-bit', 'duplicator');
|
64 |
break;
|
82 |
|
83 |
public static function getWPCoreDirs()
|
84 |
{
|
85 |
+
$wp_core_dirs = array(get_home_path() . 'wp-admin', get_home_path() . 'wp-includes');
|
86 |
|
87 |
//if wp_content is overrided
|
88 |
+
$wp_path = get_home_path() . "wp-content";
|
89 |
+
if (get_home_path() . 'wp-content' != WP_CONTENT_DIR) {
|
90 |
$wp_path = WP_CONTENT_DIR;
|
91 |
}
|
92 |
$wp_path = str_replace("\\", "/", $wp_path);
|
93 |
|
94 |
$wp_core_dirs[] = $wp_path;
|
95 |
+
$wp_core_dirs[] = $wp_path . '/plugins';
|
96 |
+
$wp_core_dirs[] = $wp_path . '/themes';
|
97 |
|
98 |
return $wp_core_dirs;
|
99 |
}
|
104 |
*/
|
105 |
public static function getWPCoreFiles()
|
106 |
{
|
107 |
+
$wp_cored_dirs = array(get_home_path() . 'wp-config.php');
|
108 |
return $wp_cored_dirs;
|
109 |
}
|
110 |
|
128 |
trigger_error('array_group_by(): The key should be a string, an integer, or a callback', E_USER_ERROR);
|
129 |
return null;
|
130 |
}
|
131 |
+
$func = (!is_string($key) && is_callable($key) ? $key : null);
|
132 |
+
$_key = $key;
|
133 |
// Load the new array, splitting by the target key
|
134 |
$grouped = array();
|
135 |
foreach ($array as $value) {
|
188 |
{
|
189 |
// Open file
|
190 |
$f = @fopen($filepath, "rb");
|
191 |
+
if ($f === false) {
|
192 |
return false;
|
193 |
+
}
|
194 |
|
195 |
// Sets buffer size
|
196 |
$buffer = 256;
|
200 |
|
201 |
// Read it and adjust line number if necessary
|
202 |
// (Otherwise the result would be wrong if file doesn't end with a blank line)
|
203 |
+
if (fread($f, 1) != "\n") {
|
204 |
$lines -= 1;
|
205 |
+
}
|
206 |
|
207 |
// Start reading
|
208 |
$output = '';
|
211 |
// While we would like more
|
212 |
while (ftell($f) > 0 && $lines >= 0) {
|
213 |
// Figure out how far back we should jump
|
214 |
+
$seek = min(ftell($f), $buffer);
|
215 |
// Do the jump (backwards, relative to where we are)
|
216 |
fseek($f, -$seek, SEEK_CUR);
|
217 |
// Read a chunk and prepend it to our output
|
218 |
+
$output = ($chunk = fread($f, $seek)) . $output;
|
219 |
// Jump back to where we started reading
|
220 |
fseek($f, -mb_strlen($chunk, '8bit'), SEEK_CUR);
|
221 |
// Decrease our line counter
|
222 |
+
$lines -= substr_count($chunk, "\n");
|
223 |
}
|
224 |
|
225 |
// While we have too many lines
|
246 |
for ($i = 0; $size >= 1024 && $i < 4; $i++) {
|
247 |
$size /= 1024;
|
248 |
}
|
249 |
+
return round($size, $roundBy) . $units[$i];
|
250 |
+
} catch (Exception $e) {
|
|
|
251 |
return "n/a";
|
252 |
}
|
253 |
}
|
258 |
* uni: /home/path/file.txt
|
259 |
* win: D:/home/path/file.txt
|
260 |
*
|
261 |
+
* @param string $path The path to make safe
|
262 |
*
|
263 |
* @return string A path with all slashes facing "/"
|
264 |
*/
|
289 |
*/
|
290 |
public static function appendOnce($string, $value)
|
291 |
{
|
292 |
+
return $string . (substr($string, -1) == $value ? '' : $value);
|
293 |
}
|
294 |
|
295 |
/**
|
315 |
* @return array of all files in that path
|
316 |
*
|
317 |
* Notes:
|
318 |
+
* - Avoid using glob() as GLOB_BRACE is not an option on some operating systems
|
319 |
+
* - Pre PHP 5.3 DirectoryIterator will crash on unreadable files
|
320 |
* - Scandir will not crash on unreadable items, but will not return results
|
321 |
*/
|
322 |
public static function listFiles($path = '.')
|
325 |
$files = array();
|
326 |
if ($dh = opendir($path)) {
|
327 |
while (($file = readdir($dh)) !== false) {
|
328 |
+
if ($file == '.' || $file == '..') {
|
329 |
continue;
|
330 |
+
}
|
331 |
+
$full_file_path = trailingslashit($path) . $file;
|
332 |
$files[] = str_replace("\\", '/', $full_file_path);
|
333 |
}
|
334 |
@closedir($dh);
|
335 |
}
|
336 |
return $files;
|
337 |
+
} catch (Exception $exc) {
|
|
|
338 |
$result = array();
|
339 |
$files = @scandir($path);
|
340 |
if (is_array($files)) {
|
341 |
foreach ($files as $file) {
|
342 |
+
$result[] = str_replace("\\", '/', $path) . $file;
|
343 |
}
|
344 |
}
|
345 |
return $result;
|
374 |
*/
|
375 |
public static function isDirectoryEmpty($path)
|
376 |
{
|
377 |
+
if (!is_readable($path)) {
|
378 |
+
return null;
|
379 |
+
}
|
380 |
return (count(scandir($path)) == 2);
|
381 |
}
|
382 |
|
390 |
*/
|
391 |
public static function getDirectorySize($path)
|
392 |
{
|
393 |
+
if (!file_exists($path)) {
|
394 |
return 0;
|
395 |
+
}
|
396 |
+
if (is_file($path)) {
|
397 |
return filesize($path);
|
398 |
+
}
|
399 |
|
400 |
$size = 0;
|
401 |
+
$list = glob($path . "/*");
|
402 |
if (!empty($list)) {
|
403 |
+
foreach ($list as $file) {
|
404 |
$size += self::getDirectorySize($file);
|
405 |
+
}
|
406 |
}
|
407 |
return $size;
|
408 |
}
|
418 |
$cmds = array('shell_exec', 'escapeshellarg', 'escapeshellcmd', 'extension_loaded');
|
419 |
|
420 |
//Function disabled at server level
|
421 |
+
if (array_intersect($cmds, array_map('trim', explode(',', @ini_get('disable_functions'))))) {
|
422 |
return apply_filters('duplicator_is_shellzip_available', false);
|
423 |
+
}
|
424 |
|
425 |
//Suhosin: http://www.hardened-php.net/suhosin/
|
426 |
//Will cause PHP to silently fail
|
427 |
if (extension_loaded('suhosin')) {
|
428 |
$suhosin_ini = @ini_get("suhosin.executor.func.blacklist");
|
429 |
+
if (array_intersect($cmds, array_map('trim', explode(',', $suhosin_ini)))) {
|
430 |
return apply_filters('duplicator_is_shellzip_available', false);
|
431 |
+
}
|
432 |
}
|
433 |
|
434 |
if (! function_exists('shell_exec')) {
|
435 |
+
return apply_filters('duplicator_is_shellzip_available', false);
|
436 |
+
}
|
437 |
|
438 |
// Can we issue a simple echo command?
|
439 |
+
if (!@shell_exec('echo duplicator')) {
|
440 |
return apply_filters('duplicator_is_shellzip_available', false);
|
441 |
+
}
|
442 |
|
443 |
return apply_filters('duplicator_is_shellzip_available', true);
|
444 |
}
|
482 |
{
|
483 |
return base64_encode($string);
|
484 |
}
|
485 |
+
const SECURE_ISSUE_DIE = 'die';
|
486 |
+
const SECURE_ISSUE_THROW = 'throw';
|
487 |
+
const SECURE_ISSUE_RETURN = 'return';
|
488 |
|
489 |
/**
|
490 |
* Does the current user have the capability
|
505 |
|
506 |
if (!current_user_can($capability)) {
|
507 |
$exitMsg = __('You do not have sufficient permissions to access this page.', 'duplicator');
|
508 |
+
DUP_LOG::Trace('You do not have sufficient permissions to access this page. PERMISSION: ' . $permission);
|
509 |
|
510 |
switch ($exit) {
|
511 |
case self::SECURE_ISSUE_THROW:
|
555 |
}
|
556 |
|
557 |
return strlen($user) ? $user : $unreadable;
|
558 |
+
} catch (Exception $ex) {
|
|
|
559 |
return $unreadable;
|
560 |
}
|
561 |
}
|
567 |
*/
|
568 |
public static function initSnapshotDirectory()
|
569 |
{
|
570 |
+
$error = false;
|
571 |
+
|
572 |
$path_wproot = duplicator_get_abs_path();
|
573 |
+
$path_ssdir = DUP_Settings::getSsdirPath();
|
|
|
574 |
|
575 |
+
if (!file_exists($path_ssdir)) {
|
|
|
|
|
|
|
576 |
$old_root_perm = @fileperms($path_wproot);
|
577 |
|
578 |
+
//--------------------------------
|
579 |
//CHMOD DIRECTORY ACCESS
|
580 |
//wordpress root directory
|
581 |
+
SnapIO::chmod($path_wproot, 'u+rwx');
|
582 |
|
583 |
//snapshot directory
|
584 |
+
if (SnapIO::dirWriteCheckOrMkdir($path_ssdir, 'u+rwx,go+rx') == false) {
|
585 |
$error = true;
|
586 |
}
|
587 |
|
588 |
// restore original root perms
|
589 |
+
SnapIO::chmod($path_wproot, $old_root_perm);
|
590 |
|
591 |
if ($error) {
|
592 |
return false;
|
593 |
}
|
594 |
}
|
595 |
|
596 |
+
SnapIO::chmod($path_ssdir, 'u+rwx,go+rx');
|
597 |
+
SnapIO::dirWriteCheckOrMkdir(DUP_Settings::getSsdirTmpPath(), 'u+rwx');
|
598 |
|
599 |
//--------------------------------
|
600 |
//FILE CREATION & HARDEN PROCESS
|
601 |
//index.php, .htaccess, robots.txt
|
602 |
+
self::setupBackupDirIndexFile();
|
603 |
+
self::setupBackupDirRobotsFile();
|
604 |
+
self::setupBackupDirHtaccess();
|
605 |
+
self::performHardenProcesses();
|
606 |
|
607 |
return true;
|
608 |
}
|
610 |
/**
|
611 |
* Attempts to create a secure .htaccess file in the download directory
|
612 |
*
|
613 |
+
* @return void
|
614 |
*/
|
615 |
+
protected static function setupBackupDirHtaccess()
|
616 |
{
|
617 |
try {
|
618 |
$storageHtaccessOff = DUP_Settings::Get('storage_htaccess_off');
|
619 |
+
$fileName = DUP_Settings::getSsdirPath() . '/.htaccess';
|
620 |
|
621 |
if ($storageHtaccessOff) {
|
622 |
@unlink($fileName);
|
623 |
+
} elseif (!file_exists($fileName)) {
|
624 |
+
$fileContent = <<<HTACCESS
|
625 |
+
Options -Indexes
|
626 |
+
<Files *.php>\n deny from all\n</Files>
|
627 |
+
HTACCESS;
|
628 |
+
if (file_put_contents($fileName, $fileContent) === false) {
|
629 |
+
throw new Exception('Can\'t create .haccess');
|
630 |
}
|
631 |
+
}
|
632 |
} catch (Exception $ex) {
|
633 |
+
DUP_Log::Trace("Unable create file htaccess {$fileName} msg:" . $ex->getMessage());
|
634 |
}
|
635 |
}
|
636 |
|
637 |
/**
|
638 |
* Attempts to create an index.php file in the backups directory
|
639 |
*
|
640 |
+
* @return void
|
641 |
*/
|
642 |
+
protected static function setupBackupDirIndexFile()
|
643 |
{
|
644 |
try {
|
645 |
+
$fileName = DUP_Settings::getSsdirPath() . '/index.php';
|
646 |
if (!file_exists($fileName)) {
|
647 |
+
$fileContent = <<<HTACCESS
|
648 |
+
<?php
|
649 |
+
// silence;
|
650 |
+
HTACCESS;
|
651 |
+
if (file_put_contents($fileName, $fileContent) === false) {
|
652 |
+
throw new Exception('Can\'t create .haccess');
|
653 |
}
|
654 |
}
|
655 |
} catch (Exception $ex) {
|
656 |
+
DUP_Log::Trace("Unable create index.php {$fileName} msg:" . $ex->getMessage());
|
657 |
}
|
658 |
}
|
659 |
|
661 |
* Attempts to create a robots.txt file in the backups directory
|
662 |
* to prevent search engines
|
663 |
*
|
664 |
+
* @return void
|
665 |
*/
|
666 |
+
protected static function setupBackupDirRobotsFile()
|
667 |
{
|
668 |
try {
|
669 |
+
$fileName = DUP_Settings::getSsdirPath() . '/robots.txt';
|
670 |
if (!file_exists($fileName)) {
|
671 |
+
$fileContent = <<<HTACCESS
|
672 |
+
User-agent: *
|
673 |
+
Disallow: /
|
674 |
+
HTACCESS;
|
675 |
+
if (file_put_contents($fileName, $fileContent) === false) {
|
676 |
+
throw new Exception('Can\'t create .haccess');
|
|
|
677 |
}
|
678 |
}
|
679 |
} catch (Exception $ex) {
|
680 |
+
DUP_Log::Trace("Unable create robots.txt {$fileName} msg:" . $ex->getMessage());
|
681 |
}
|
682 |
}
|
683 |
|
684 |
+
|
685 |
/**
|
686 |
* Run various secure processes to harden the backups dir
|
687 |
*
|
688 |
+
* @return void
|
689 |
*/
|
690 |
+
public static function performHardenProcesses()
|
691 |
{
|
692 |
+
$backupsDir = DUP_Settings::getSsdirPath();
|
693 |
+
|
694 |
+
//Edge Case: Remove any main.installer.php files
|
695 |
$dupInstallFile = "{$backupsDir}/dup-installer/main.installer.php";
|
696 |
if (file_exists($dupInstallFile) ) {
|
697 |
+
SnapIO::chmod($dupInstallFile, "a+w");
|
698 |
+
SnapIO::unlink("{$dupInstallFile}");
|
699 |
}
|
700 |
|
701 |
//Rename installer php files to .bak
|
702 |
+
SnapIO::regexGlobCallback(
|
703 |
$backupsDir,
|
704 |
function ($path) {
|
705 |
+
$parts = pathinfo($path);
|
706 |
$newPath = $parts['dirname'] . '/' . $parts['filename'] . DUP_Installer::INSTALLER_SERVER_EXTENSION;
|
707 |
+
SnapIO::rename($path, $newPath);
|
708 |
},
|
709 |
array(
|
710 |
'regexFile' => '/^.+_installer.*\.php$/',
|
724 |
$filepath = null;
|
725 |
|
726 |
if (self::hasShellExec()) {
|
727 |
+
if (shell_exec('hash zip 2>&1') == null) {
|
728 |
$filepath = 'zip';
|
729 |
} else {
|
730 |
$possible_paths = array(
|
765 |
global $wpdb;
|
766 |
$result = array();
|
767 |
foreach (self::getWPCoreTablesEnd() as $tend) {
|
768 |
+
$result[] = $wpdb->prefix . $tend;
|
769 |
}
|
770 |
return $result;
|
771 |
}
|
808 |
|
809 |
if (in_array($subTName, $coreEnds)) {
|
810 |
return true;
|
811 |
+
} elseif (is_multisite()) {
|
812 |
$exTable = explode('_', $subTName);
|
813 |
if (count($exTable) >= 2 && is_numeric($exTable[0])) {
|
814 |
$tChekc = implode('_', array_slice($exTable, 1));
|
840 |
|
841 |
/**
|
842 |
* Check given table is exist in real
|
843 |
+
*
|
844 |
* @param $table string Table name
|
845 |
* @return booleam
|
846 |
*/
|
848 |
{
|
849 |
// It will clear the $GLOBALS['wpdb']->last_error var
|
850 |
$GLOBALS['wpdb']->flush();
|
851 |
+
$sql = "SELECT 1 FROM `" . esc_sql($table) . "` LIMIT 1;";
|
852 |
$ret = $GLOBALS['wpdb']->get_var($sql);
|
853 |
+
if (empty($GLOBALS['wpdb']->last_error)) {
|
854 |
return true;
|
855 |
+
}
|
856 |
return false;
|
857 |
}
|
858 |
|
865 |
*/
|
866 |
public static function isExecutable($cmd)
|
867 |
{
|
868 |
+
if (strlen($cmd) < 1) {
|
869 |
return false;
|
870 |
+
}
|
871 |
|
872 |
if (@is_executable($cmd)) {
|
873 |
return true;
|
878 |
return true;
|
879 |
}
|
880 |
|
881 |
+
$output = shell_exec($cmd . ' -?');
|
882 |
if (!is_null($output)) {
|
883 |
return true;
|
884 |
}
|
889 |
/**
|
890 |
* Display human readable byte sizes
|
891 |
*
|
892 |
+
* @param string $size The size in bytes
|
893 |
*
|
894 |
* @return string Human readable bytes such as 50MB, 1GB
|
895 |
*/
|
897 |
{
|
898 |
try {
|
899 |
$units = array('B', 'KB', 'MB', 'GB', 'TB');
|
900 |
+
for ($i = 0; $size >= 1024 && $i < 4; $i++) {
|
901 |
+
$size /= 1024;
|
902 |
+
}
|
903 |
+
return round($size, 2) . $units[$i];
|
904 |
+
} catch (Exception $e) {
|
905 |
return "n/a";
|
906 |
}
|
907 |
}
|
942 |
{
|
943 |
return function_exists($function_name) && !in_array($function_name, self::getIniDisableFuncs());
|
944 |
}
|
945 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
classes/utilities/class.u.scancheck.php
CHANGED
@@ -1,181 +1,169 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
*
|
6 |
-
*
|
7 |
-
*
|
8 |
-
*
|
9 |
-
*
|
10 |
-
* @
|
11 |
-
* @
|
12 |
-
*
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
if
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
* The maximum count of directories before the recursive function stops
|
37 |
-
*/
|
38 |
-
public $maxDirs = 75000;
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
* Stores a list of
|
57 |
-
*/
|
58 |
-
public $
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
/**
|
76 |
-
*
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
$
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
$
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
*/
|
171 |
-
private function isLink($target)
|
172 |
-
{
|
173 |
-
//Currently Windows does not support sym-link detection
|
174 |
-
if ($this->isWindows) {
|
175 |
-
return false;
|
176 |
-
} elseif (is_link($target)) {
|
177 |
-
return true;
|
178 |
-
}
|
179 |
-
return false;
|
180 |
-
}
|
181 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
4 |
+
/**
|
5 |
+
* Recursivly scans a directory and finds all sym-links and unreadable files
|
6 |
+
*
|
7 |
+
* Standard: PSR-2
|
8 |
+
* @link http://www.php-fig.org/psr/psr-2
|
9 |
+
*
|
10 |
+
* @package Duplicator
|
11 |
+
* @subpackage classes/utilities
|
12 |
+
* @copyright (c) 2017, Snapcreek LLC
|
13 |
+
*
|
14 |
+
*/
|
15 |
+
|
16 |
+
// Exit if accessed directly
|
17 |
+
if (! defined('DUPLICATOR_VERSION')) {
|
18 |
+
exit;
|
19 |
+
}
|
20 |
+
|
21 |
+
class DUP_ScanCheck
|
22 |
+
{
|
23 |
+
/**
|
24 |
+
* The number of files scanned
|
25 |
+
*/
|
26 |
+
public $fileCount = 0;
|
27 |
+
/**
|
28 |
+
* The number of directories scanned
|
29 |
+
*/
|
30 |
+
public $dirCount = 0;
|
31 |
+
/**
|
32 |
+
* The maximum count of files before the recursive function stops
|
33 |
+
*/
|
34 |
+
public $maxFiles = 1000000;
|
35 |
+
/**
|
36 |
+
* The maximum count of directories before the recursive function stops
|
37 |
+
*/
|
38 |
+
public $maxDirs = 75000;
|
39 |
+
/**
|
40 |
+
* Recursivly scan the root directory provided
|
41 |
+
*/
|
42 |
+
public $recursion = true;
|
43 |
+
/**
|
44 |
+
* Stores a list of symbolic link files
|
45 |
+
*/
|
46 |
+
public $symLinks = array();
|
47 |
+
/**
|
48 |
+
* Stores a list of files unreadable by PHP
|
49 |
+
*/
|
50 |
+
public $unreadable = array();
|
51 |
+
/**
|
52 |
+
* Stores a list of dirs with utf8 settings
|
53 |
+
*/
|
54 |
+
public $nameTestDirs = array();
|
55 |
+
/**
|
56 |
+
* Stores a list of files with utf8 settings
|
57 |
+
*/
|
58 |
+
public $nameTestFiles = array();
|
59 |
+
/**
|
60 |
+
* If the maxFiles or maxDirs limit is reached then true
|
61 |
+
*/
|
62 |
+
protected $limitReached = false;
|
63 |
+
/**
|
64 |
+
* Is the server running on Windows
|
65 |
+
*/
|
66 |
+
private $isWindows = false;
|
67 |
+
/**
|
68 |
+
* Init this instance of the object
|
69 |
+
*/
|
70 |
+
public function __construct()
|
71 |
+
{
|
72 |
+
$this->isWindows = defined('PHP_WINDOWS_VERSION_BUILD');
|
73 |
+
}
|
74 |
+
|
75 |
+
/**
|
76 |
+
* Start the scan process
|
77 |
+
*
|
78 |
+
* @param string $dir A valid directory path where the scan will run
|
79 |
+
* @param array $results Used for recursion, do not pass in value with calling
|
80 |
+
*
|
81 |
+
* @return obj The scan check object with the results of the scan
|
82 |
+
*/
|
83 |
+
public function run($dir, &$results = array())
|
84 |
+
{
|
85 |
+
//Stop Recursion if Max search is reached
|
86 |
+
if ($this->fileCount > $this->maxFiles || $this->dirCount > $this->maxDirs) {
|
87 |
+
$this->limitReached = true;
|
88 |
+
return $results;
|
89 |
+
}
|
90 |
+
|
91 |
+
$files = @scandir($dir);
|
92 |
+
if (is_array($files)) {
|
93 |
+
foreach ($files as $key => $value) {
|
94 |
+
$path = realpath($dir . DIRECTORY_SEPARATOR . $value);
|
95 |
+
if ($path) {
|
96 |
+
//Files
|
97 |
+
if (!is_dir($path)) {
|
98 |
+
if (!is_readable($path)) {
|
99 |
+
$results[] = $path;
|
100 |
+
$this->unreadable[] = $path;
|
101 |
+
} elseif ($this->isLink($path)) {
|
102 |
+
$results[] = $path;
|
103 |
+
$this->symLinks[] = $path;
|
104 |
+
} else {
|
105 |
+
$name = basename($path);
|
106 |
+
$invalid_test = preg_match('/(\/|\*|\?|\>|\<|\:|\\|\|)/', $name)
|
107 |
+
|| trim($name) == ''
|
108 |
+
|| (strrpos($name, '.') == strlen($name) - 1 && substr($name, -1) == '.')
|
109 |
+
|| preg_match('/[^\x20-\x7f]/', $name);
|
110 |
+
if ($invalid_test) {
|
111 |
+
if (! DUP_Util::$PHP7_plus && DUP_Util::isWindows()) {
|
112 |
+
$this->nameTestFiles[] = utf8_decode($path);
|
113 |
+
} else {
|
114 |
+
$this->nameTestFiles[] = $path;
|
115 |
+
}
|
116 |
+
}
|
117 |
+
}
|
118 |
+
$this->fileCount++;
|
119 |
+
} elseif ($value != "." && $value != "..") {
|
120 |
+
//Dirs
|
121 |
+
if (!$this->isLink($path) && $this->recursion) {
|
122 |
+
$this->Run($path, $results);
|
123 |
+
}
|
124 |
+
|
125 |
+
if (!is_readable($path)) {
|
126 |
+
$results[] = $path;
|
127 |
+
$this->unreadable[] = $path;
|
128 |
+
} elseif ($this->isLink($path)) {
|
129 |
+
$results[] = $path;
|
130 |
+
$this->symLinks[] = $path;
|
131 |
+
} else {
|
132 |
+
$invalid_test = strlen($path) > 244
|
133 |
+
|| trim($path) == ''
|
134 |
+
|| preg_match('/[^\x20-\x7f]/', $path);
|
135 |
+
if ($invalid_test) {
|
136 |
+
if (! DUP_Util::$PHP7_plus && DUP_Util::isWindows()) {
|
137 |
+
$this->nameTestDirs[] = utf8_decode($path);
|
138 |
+
} else {
|
139 |
+
$this->nameTestDirs[] = $path;
|
140 |
+
}
|
141 |
+
}
|
142 |
+
}
|
143 |
+
|
144 |
+
$this->dirCount++;
|
145 |
+
}
|
146 |
+
}
|
147 |
+
}
|
148 |
+
}
|
149 |
+
return $this;
|
150 |
+
}
|
151 |
+
|
152 |
+
/**
|
153 |
+
* Separation logic for supporting how different operating systems work
|
154 |
+
*
|
155 |
+
* @param string $target A valid file path
|
156 |
+
*
|
157 |
+
* @return bool Is the target a sym link
|
158 |
+
*/
|
159 |
+
private function isLink($target)
|
160 |
+
{
|
161 |
+
//Currently Windows does not support sym-link detection
|
162 |
+
if ($this->isWindows) {
|
163 |
+
return false;
|
164 |
+
} elseif (is_link($target)) {
|
165 |
+
return true;
|
166 |
+
}
|
167 |
+
return false;
|
168 |
+
}
|
169 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
classes/utilities/class.u.shell.php
CHANGED
@@ -1,45 +1,49 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
|
4 |
-
if
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
*
|
13 |
-
*
|
14 |
-
*
|
15 |
-
*
|
16 |
-
*
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
4 |
+
// Exit if accessed directly
|
5 |
+
if (! defined('DUPLICATOR_VERSION')) {
|
6 |
+
exit;
|
7 |
+
}
|
8 |
+
|
9 |
+
class DUP_Shell_U
|
10 |
+
{
|
11 |
+
/**
|
12 |
+
* Escape a string to be used as a shell argument with bypass support for Windows
|
13 |
+
*
|
14 |
+
* NOTES:
|
15 |
+
* Provides a way to support shell args on Windows OS and allows %,! on Windows command line
|
16 |
+
* Safe if input is know such as a defined constant and not from user input escape shellarg
|
17 |
+
* on Windows with turn %,! into spaces
|
18 |
+
*
|
19 |
+
* @return string
|
20 |
+
*/
|
21 |
+
public static function escapeshellargWindowsSupport($string)
|
22 |
+
{
|
23 |
+
if (strncasecmp(PHP_OS, 'WIN', 3) == 0) {
|
24 |
+
if (strstr($string, '%') || strstr($string, '!')) {
|
25 |
+
$result = '"' . str_replace('"', '', $string) . '"';
|
26 |
+
return $result;
|
27 |
+
}
|
28 |
+
}
|
29 |
+
return escapeshellarg($string);
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
*
|
34 |
+
* @return boolean
|
35 |
+
*
|
36 |
+
*/
|
37 |
+
public static function isPopenEnabled()
|
38 |
+
{
|
39 |
+
|
40 |
+
if (!DUP_Util::isIniFunctionEnalbe('popen') || !DUP_Util::isIniFunctionEnalbe('proc_open')) {
|
41 |
+
$ret = false;
|
42 |
+
} else {
|
43 |
+
$ret = true;
|
44 |
+
}
|
45 |
+
|
46 |
+
$ret = apply_filters('duplicator_is_popen_enabled', $ret);
|
47 |
+
return $ret;
|
48 |
+
}
|
49 |
+
}
|
classes/utilities/class.u.string.php
CHANGED
@@ -1,99 +1,96 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
*
|
6 |
-
*
|
7 |
-
*
|
8 |
-
*
|
9 |
-
*
|
10 |
-
* @
|
11 |
-
* @
|
12 |
-
* @
|
13 |
-
*
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
/**
|
19 |
-
* Append the value to the string if it doesn't already exist
|
20 |
-
*
|
21 |
-
* @param string $string The string to append to
|
22 |
-
* @param string $value The string to append to the $string
|
23 |
-
*
|
24 |
-
* @return string Returns the string with the $value appended once
|
25 |
-
*/
|
26 |
-
public static function appendOnce($string, $value)
|
27 |
-
{
|
28 |
-
return $string.(substr($string, -1) == $value ? '' : $value);
|
29 |
-
}
|
30 |
-
|
31 |
-
/**
|
32 |
-
* Returns true if the string contains UTF8 characters
|
33 |
-
* @see http://php.net/manual/en/function.mb-detect-encoding.php
|
34 |
-
*
|
35 |
-
* @param string $string The class name where the $destArray exists
|
36 |
-
*
|
37 |
-
* @return null
|
38 |
-
*/
|
39 |
-
public static function hasUTF8($string)
|
40 |
-
{
|
41 |
-
return preg_match('%(?:
|
42 |
-
[\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
|
43 |
-
|\xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
|
44 |
-
|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
|
45 |
-
|\xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
|
46 |
-
|\xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
|
47 |
-
|[\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
|
48 |
-
|\xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
|
49 |
-
)+%xs', $string);
|
50 |
-
}
|
51 |
-
|
52 |
-
/**
|
53 |
-
* Returns true if the $needle is found in the $haystack
|
54 |
-
*
|
55 |
-
* @param string $haystack The full string to search in
|
56 |
-
* @param string $needle The string to for
|
57 |
-
*
|
58 |
-
* @return bool
|
59 |
-
*/
|
60 |
-
public static function contains($haystack, $needle)
|
61 |
-
{
|
62 |
-
$pos = strpos($haystack, $needle);
|
63 |
-
return ($pos !== false);
|
64 |
-
}
|
65 |
-
|
66 |
-
/**
|
67 |
-
* Returns true if the $haystack string starts with the $needle
|
68 |
-
*
|
69 |
-
* @param string $haystack The full string to search in
|
70 |
-
* @param string $needle The string to for
|
71 |
-
*
|
72 |
-
* @return bool Returns true if the $haystack string starts with the $needle
|
73 |
-
*/
|
74 |
-
public static function startsWith($haystack, $needle)
|
75 |
-
{
|
76 |
-
$length = strlen($needle);
|
77 |
-
return (substr($haystack, 0, $length) === $needle);
|
78 |
-
}
|
79 |
-
|
80 |
-
/**
|
81 |
-
* Returns true if the $haystack string ends with the $needle
|
82 |
-
*
|
83 |
-
* @param string $haystack The full string to search in
|
84 |
-
* @param string $needle The string to for
|
85 |
-
*
|
86 |
-
* @return bool Returns true if the $haystack string ends with the $needle
|
87 |
-
*/
|
88 |
-
public static function endsWith($haystack, $needle)
|
89 |
-
{
|
90 |
-
$length = strlen($needle);
|
91 |
-
if ($length == 0) {
|
92 |
-
return true;
|
93 |
-
}
|
94 |
-
return (substr($haystack, -$length) === $needle);
|
95 |
-
}
|
96 |
-
|
97 |
-
|
98 |
-
}
|
99 |
-
|
1 |
+
<?php
|
2 |
+
|
3 |
+
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
4 |
+
/**
|
5 |
+
* Utility class working with strings
|
6 |
+
*
|
7 |
+
* Standard: PSR-2
|
8 |
+
* @link http://www.php-fig.org/psr/psr-2
|
9 |
+
*
|
10 |
+
* @package DUP
|
11 |
+
* @subpackage classes/utilities
|
12 |
+
* @copyright (c) 2017, Snapcreek LLC
|
13 |
+
* @license https://opensource.org/licenses/GPL-3.0 GNU Public License
|
14 |
+
*
|
15 |
+
*/
|
16 |
+
class DUP_STR
|
17 |
+
{
|
18 |
+
/**
|
19 |
+
* Append the value to the string if it doesn't already exist
|
20 |
+
*
|
21 |
+
* @param string $string The string to append to
|
22 |
+
* @param string $value The string to append to the $string
|
23 |
+
*
|
24 |
+
* @return string Returns the string with the $value appended once
|
25 |
+
*/
|
26 |
+
public static function appendOnce($string, $value)
|
27 |
+
{
|
28 |
+
return $string . (substr($string, -1) == $value ? '' : $value);
|
29 |
+
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Returns true if the string contains UTF8 characters
|
33 |
+
* @see http://php.net/manual/en/function.mb-detect-encoding.php
|
34 |
+
*
|
35 |
+
* @param string $string The class name where the $destArray exists
|
36 |
+
*
|
37 |
+
* @return null
|
38 |
+
*/
|
39 |
+
public static function hasUTF8($string)
|
40 |
+
{
|
41 |
+
return preg_match('%(?:
|
42 |
+
[\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
|
43 |
+
|\xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
|
44 |
+
|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
|
45 |
+
|\xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
|
46 |
+
|\xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
|
47 |
+
|[\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
|
48 |
+
|\xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
|
49 |
+
)+%xs', $string);
|
50 |
+
}
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Returns true if the $needle is found in the $haystack
|
54 |
+
*
|
55 |
+
* @param string $haystack The full string to search in
|
56 |
+
* @param string $needle The string to for
|
57 |
+
*
|
58 |
+
* @return bool
|
59 |
+
*/
|
60 |
+
public static function contains($haystack, $needle)
|
61 |
+
{
|
62 |
+
$pos = strpos($haystack, $needle);
|
63 |
+
return ($pos !== false);
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Returns true if the $haystack string starts with the $needle
|
68 |
+
*
|
69 |
+
* @param string $haystack The full string to search in
|
70 |
+
* @param string $needle The string to for
|
71 |
+
*
|
72 |
+
* @return bool Returns true if the $haystack string starts with the $needle
|
73 |
+
*/
|
74 |
+
public static function startsWith($haystack, $needle)
|
75 |
+
{
|
76 |
+
$length = strlen($needle);
|
77 |
+
return (substr($haystack, 0, $length) === $needle);
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* Returns true if the $haystack string ends with the $needle
|
82 |
+
*
|
83 |
+
* @param string $haystack The full string to search in
|
84 |
+
* @param string $needle The string to for
|
85 |
+
*
|
86 |
+
* @return bool Returns true if the $haystack string ends with the $needle
|
87 |
+
*/
|
88 |
+
public static function endsWith($haystack, $needle)
|
89 |
+
{
|
90 |
+
$length = strlen($needle);
|
91 |
+
if ($length == 0) {
|
92 |
+
return true;
|
93 |
+
}
|
94 |
+
return (substr($haystack, -$length) === $needle);
|
95 |
+
}
|
96 |
+
}
|
|
|
|
|
|
classes/utilities/class.u.validator.php
CHANGED
@@ -1,231 +1,220 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
*
|
6 |
-
*
|
7 |
-
*
|
8 |
-
*
|
9 |
-
*
|
10 |
-
* @
|
11 |
-
* @
|
12 |
-
*
|
13 |
-
|
14 |
-
|
15 |
-
if
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
'
|
27 |
-
'
|
28 |
-
'
|
29 |
-
'
|
30 |
-
'
|
31 |
-
|
32 |
-
|
33 |
-
const FILTER_VALIDATE_IS_EMPTY = 'empty';
|
34 |
-
const FILTER_VALIDATE_NOT_EMPTY = 'nempty';
|
35 |
-
const FILTER_VALIDATE_FILE = 'ffile';
|
36 |
-
const FILTER_VALIDATE_FOLDER = 'fdir';
|
37 |
-
const FILTER_VALIDATE_FILE_EXT = 'fext';
|
38 |
-
const FILTER_VALIDATE_EMAIL = 'email';
|
39 |
-
|
40 |
-
|
41 |
-
*
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
*
|
95 |
-
* @
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
$msgs
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
*
|
130 |
-
*
|
131 |
-
*
|
132 |
-
*
|
133 |
-
*
|
134 |
-
*
|
135 |
-
*
|
136 |
-
* @
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
if (
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
$result = filter_var($variable, $filter, $options);
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
if ($
|
162 |
-
$
|
163 |
-
}
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
$key
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
$vals = explode($delimiter, trim($variable, $delimiter));
|
223 |
-
$res = array();
|
224 |
-
|
225 |
-
foreach ($vals as $val) {
|
226 |
-
$res[] = $this->filter_custom($val, $filter, $options);
|
227 |
-
}
|
228 |
-
|
229 |
-
return $res;
|
230 |
-
}
|
231 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
4 |
+
/**
|
5 |
+
* Validate variables
|
6 |
+
*
|
7 |
+
* Standard: PSR-2
|
8 |
+
* @link http://www.php-fig.org/psr/psr-2
|
9 |
+
*
|
10 |
+
* @package Duplicator
|
11 |
+
* @subpackage classes/utilities
|
12 |
+
* @copyright (c) 2017, Snapcreek LLC
|
13 |
+
*
|
14 |
+
*/
|
15 |
+
// Exit if accessed directly
|
16 |
+
if (!defined('DUPLICATOR_VERSION')) {
|
17 |
+
exit;
|
18 |
+
}
|
19 |
+
|
20 |
+
class DUP_Validator
|
21 |
+
{
|
22 |
+
/**
|
23 |
+
* @var array $patterns
|
24 |
+
*/
|
25 |
+
private static $patterns = array(
|
26 |
+
'fdir' => '/^([a-zA-Z]:[\\\\\/]|\/|\\\\\\\\|\/\/)[^<>\0]+$/',
|
27 |
+
'ffile' => '/^([a-zA-Z]:[\\\\\/]|\/|\\\\\\\\|\/\/)[^<>\0]+$/',
|
28 |
+
'fext' => '/^\.?[^\\\\\/*:<>\0?"|\s\.]+$/',
|
29 |
+
'email' => '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_\`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/',
|
30 |
+
'empty' => '/^$/',
|
31 |
+
'nempty' => '/^.+$/',
|
32 |
+
);
|
33 |
+
const FILTER_VALIDATE_IS_EMPTY = 'empty';
|
34 |
+
const FILTER_VALIDATE_NOT_EMPTY = 'nempty';
|
35 |
+
const FILTER_VALIDATE_FILE = 'ffile';
|
36 |
+
const FILTER_VALIDATE_FOLDER = 'fdir';
|
37 |
+
const FILTER_VALIDATE_FILE_EXT = 'fext';
|
38 |
+
const FILTER_VALIDATE_EMAIL = 'email';
|
39 |
+
/**
|
40 |
+
* @var array $errors [ ['key' => string field key,
|
41 |
+
* 'msg' => error message ] , [] ]
|
42 |
+
*/
|
43 |
+
private $errors = array();
|
44 |
+
/**
|
45 |
+
*
|
46 |
+
*/
|
47 |
+
public function __construct()
|
48 |
+
{
|
49 |
+
$this->errors = array();
|
50 |
+
}
|
51 |
+
|
52 |
+
/**
|
53 |
+
*
|
54 |
+
*/
|
55 |
+
public function reset()
|
56 |
+
{
|
57 |
+
$this->errors = array();
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
*
|
62 |
+
* @return bool
|
63 |
+
*/
|
64 |
+
public function isSuccess()
|
65 |
+
{
|
66 |
+
return empty($this->errors);
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
*
|
71 |
+
* @return array
|
72 |
+
*/
|
73 |
+
public function getErrors()
|
74 |
+
{
|
75 |
+
return $this->errors;
|
76 |
+
}
|
77 |
+
|
78 |
+
/**
|
79 |
+
*
|
80 |
+
* @return array return errors messages
|
81 |
+
*/
|
82 |
+
public function getErrorsMsg()
|
83 |
+
{
|
84 |
+
$result = array();
|
85 |
+
foreach ($this->errors as $err) {
|
86 |
+
$result[] = $err['msg'];
|
87 |
+
}
|
88 |
+
return $result;
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
*
|
93 |
+
* @param string $format printf format message where %s is the variable content default "%s\n"
|
94 |
+
* @param bool $echo if false return string
|
95 |
+
* @return void|string
|
96 |
+
*/
|
97 |
+
public function getErrorsFormat($format = "%s\n", $echo = true)
|
98 |
+
{
|
99 |
+
$msgs = $this->getErrorsMsg();
|
100 |
+
ob_start();
|
101 |
+
foreach ($msgs as $msg) {
|
102 |
+
printf($format, $msg);
|
103 |
+
}
|
104 |
+
|
105 |
+
if ($echo) {
|
106 |
+
ob_end_flush();
|
107 |
+
} else {
|
108 |
+
return ob_get_clean();
|
109 |
+
}
|
110 |
+
}
|
111 |
+
|
112 |
+
/**
|
113 |
+
*
|
114 |
+
* @param string $key
|
115 |
+
* @param string $msg
|
116 |
+
*/
|
117 |
+
protected function addError($key, $msg)
|
118 |
+
{
|
119 |
+
$this->errors[] = array(
|
120 |
+
'key' => $key,
|
121 |
+
'msg' => $msg
|
122 |
+
);
|
123 |
+
}
|
124 |
+
|
125 |
+
/**
|
126 |
+
* filter_var function wrapper see http://php.net/manual/en/function.filter-var.php
|
127 |
+
*
|
128 |
+
* additional options
|
129 |
+
* valkey => key of field
|
130 |
+
* errmsg => error message; % s will be replaced with the contents of the variable es. "<b>%s</b> isn't a valid field"
|
131 |
+
* acc_vals => array of accepted values that skip validation
|
132 |
+
*
|
133 |
+
* @param mixed $variable
|
134 |
+
* @param int $filter
|
135 |
+
* @param array $options
|
136 |
+
* @return mixed
|
137 |
+
*/
|
138 |
+
public function filter_var($variable, $filter = FILTER_DEFAULT, $options = array())
|
139 |
+
{
|
140 |
+
$success = true;
|
141 |
+
$result = null;
|
142 |
+
if (isset($options['acc_vals']) && in_array($variable, $options['acc_vals'])) {
|
143 |
+
return $variable;
|
144 |
+
}
|
145 |
+
|
146 |
+
if ($filter === FILTER_VALIDATE_BOOLEAN) {
|
147 |
+
$options['flags'] = FILTER_NULL_ON_FAILURE;
|
148 |
+
$result = filter_var($variable, $filter, $options);
|
149 |
+
if (is_null($result)) {
|
150 |
+
$success = false;
|
151 |
+
}
|
152 |
+
} else {
|
153 |
+
$result = filter_var($variable, $filter, $options);
|
154 |
+
if ($result === false) {
|
155 |
+
$success = false;
|
156 |
+
}
|
157 |
+
}
|
158 |
+
|
159 |
+
if (!$success) {
|
160 |
+
$key = isset($options['valkey']) ? $options['valkey'] : '';
|
161 |
+
if (isset($options['errmsg'])) {
|
162 |
+
$msg = sprintf($options['errmsg'], $variable);
|
163 |
+
} else {
|
164 |
+
$msg = sprintf('%1$s isn\'t a valid value', $variable);
|
165 |
+
}
|
166 |
+
|
167 |
+
$this->addError($key, $msg);
|
168 |
+
}
|
169 |
+
|
170 |
+
return $result;
|
171 |
+
}
|
172 |
+
|
173 |
+
/**
|
174 |
+
* validation of predefined regular expressions
|
175 |
+
*
|
176 |
+
* @param mixed $variable
|
177 |
+
* @param string $filter
|
178 |
+
* @param array $options
|
179 |
+
* @return type
|
180 |
+
* @throws Exception
|
181 |
+
*/
|
182 |
+
public function filter_custom($variable, $filter, $options = array())
|
183 |
+
{
|
184 |
+
|
185 |
+
if (!isset(self::$patterns[$filter])) {
|
186 |
+
throw new Exception('Filter not valid');
|
187 |
+
}
|
188 |
+
|
189 |
+
$options = array_merge($options, array(
|
190 |
+
'options' => array(
|
191 |
+
'regexp' => self::$patterns[$filter])
|
192 |
+
));
|
193 |
+
//$options['regexp'] = self::$patterns[$filter];
|
194 |
+
|
195 |
+
return $this->filter_var($variable, FILTER_VALIDATE_REGEXP, $options);
|
196 |
+
}
|
197 |
+
|
198 |
+
/**
|
199 |
+
* it explodes a string with a delimiter and validates every element of the array
|
200 |
+
*
|
201 |
+
* @param string $variable
|
202 |
+
* @param string $delimiter
|
203 |
+
* @param string $filter
|
204 |
+
* @param array $options
|
205 |
+
*/
|
206 |
+
public function explode_filter_custom($variable, $delimiter, $filter, $options = array())
|
207 |
+
{
|
208 |
+
if (empty($variable)) {
|
209 |
+
return array();
|
210 |
+
}
|
211 |
+
|
212 |
+
$vals = explode($delimiter, trim($variable, $delimiter));
|
213 |
+
$res = array();
|
214 |
+
foreach ($vals as $val) {
|
215 |
+
$res[] = $this->filter_custom($val, $filter, $options);
|
216 |
+
}
|
217 |
+
|
218 |
+
return $res;
|
219 |
+
}
|
220 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
classes/utilities/class.u.zip.php
CHANGED
@@ -1,154 +1,160 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
*
|
6 |
-
*
|
7 |
-
*
|
8 |
-
*
|
9 |
-
*
|
10 |
-
* @
|
11 |
-
* @
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
if
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
*
|
24 |
-
*
|
25 |
-
*
|
26 |
-
* @
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
$
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
$
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
}
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
return
|
153 |
-
}
|
154 |
-
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
4 |
+
/**
|
5 |
+
* Utility class for zipping up content
|
6 |
+
*
|
7 |
+
* Standard: PSR-2
|
8 |
+
* @link http://www.php-fig.org/psr/psr-2
|
9 |
+
*
|
10 |
+
* @subpackage classes/utilities
|
11 |
+
* @copyright (c) 2017, Snapcreek LLC
|
12 |
+
* @license https://opensource.org/licenses/GPL-3.0 GNU Public License
|
13 |
+
*/
|
14 |
+
|
15 |
+
// Exit if accessed directly
|
16 |
+
if (! defined('DUPLICATOR_VERSION')) {
|
17 |
+
exit;
|
18 |
+
}
|
19 |
+
|
20 |
+
class DUP_Zip_U
|
21 |
+
{
|
22 |
+
/**
|
23 |
+
* Add a directory to an existing ZipArchive object
|
24 |
+
*
|
25 |
+
* @param ZipArchive $zipArchive An existing ZipArchive object
|
26 |
+
* @param string $directoryPath The full directory path to add to the ZipArchive
|
27 |
+
* @param bool $retainDirectory Should the full directory path be retained in the archive
|
28 |
+
*
|
29 |
+
* @return bool Returns true if the directory was added to the object
|
30 |
+
*/
|
31 |
+
public static function addDirWithZipArchive($zipArchive, $directoryPath, $retainDirectory, $localPrefix, $isCompressed)
|
32 |
+
{
|
33 |
+
$directoryPath = rtrim(str_replace("\\", '/', $directoryPath), '/') . '/';
|
34 |
+
if (!is_dir($directoryPath) || !is_readable($directoryPath)) {
|
35 |
+
$success = false;
|
36 |
+
} elseif (!$fp = @opendir($directoryPath)) {
|
37 |
+
$success = false;
|
38 |
+
} else {
|
39 |
+
$success = true;
|
40 |
+
while (false !== ($file = readdir($fp))) {
|
41 |
+
if ($file === '.' || $file === '..') {
|
42 |
+
continue;
|
43 |
+
}
|
44 |
+
$objectPath = $directoryPath . $file;
|
45 |
+
// Not used SnapIO::safePath(), because I would like to decrease max_nest_level
|
46 |
+
// Otherwise we will get the error:
|
47 |
+
// PHP Fatal error: Uncaught Error: Maximum function nesting level of '512' reached, aborting! in ...
|
48 |
+
// $objectPath = SnapIO::safePath($objectPath);
|
49 |
+
$localName = ltrim(str_replace($directoryPath, '', $objectPath), '/');
|
50 |
+
if ($retainDirectory) {
|
51 |
+
$localName = basename($directoryPath) . "/$localName";
|
52 |
+
}
|
53 |
+
$localName = ltrim($localPrefix . $localName, '/');
|
54 |
+
if (is_readable($objectPath)) {
|
55 |
+
if (is_dir($objectPath)) {
|
56 |
+
$localPrefixArg = substr($localName, 0, strrpos($localName, '/')) . '/';
|
57 |
+
$added = self::addDirWithZipArchive($zipArchive, $objectPath, $retainDirectory, $localPrefixArg, $isCompressed);
|
58 |
+
} else {
|
59 |
+
$added = self::addFileToZipArchive($zipArchive, $objectPath, $localName, $isCompressed);
|
60 |
+
}
|
61 |
+
} else {
|
62 |
+
$added = false;
|
63 |
+
}
|
64 |
+
|
65 |
+
if (!$added) {
|
66 |
+
DUP_Log::error("Couldn't add file $objectPath to archive", '', false);
|
67 |
+
$success = false;
|
68 |
+
break;
|
69 |
+
}
|
70 |
+
}
|
71 |
+
@closedir($fp);
|
72 |
+
}
|
73 |
+
|
74 |
+
if ($success) {
|
75 |
+
return true;
|
76 |
+
} else {
|
77 |
+
DUP_Log::error("Couldn't add folder $directoryPath to archive", '', false);
|
78 |
+
return false;
|
79 |
+
}
|
80 |
+
}
|
81 |
+
|
82 |
+
|
83 |
+
public static function extractFiles($archiveFilepath, $relativeFilesToExtract, $destinationDirectory, $useShellUnZip)
|
84 |
+
{
|
85 |
+
// TODO: Unzip using either shell unzip or ziparchive
|
86 |
+
if ($useShellUnZip) {
|
87 |
+
$shellExecPath = DUPX_Server::get_unzip_filepath();
|
88 |
+
$filenameString = implode(' ', $relativeFilesToExtract);
|
89 |
+
$command = "{$shellExecPath} -o -qq \"{$archiveFilepath}\" {$filenameString} -d {$destinationDirectory} 2>&1";
|
90 |
+
$stderr = shell_exec($command);
|
91 |
+
if ($stderr != '') {
|
92 |
+
$errorMessage = __("Error extracting {$archiveFilepath}): {$stderr}", 'duplicator');
|
93 |
+
throw new Exception($errorMessage);
|
94 |
+
}
|
95 |
+
} else {
|
96 |
+
$zipArchive = new ZipArchive();
|
97 |
+
$result = $zipArchive->open($archiveFilepath);
|
98 |
+
if ($result !== true) {
|
99 |
+
throw new Exception("Error opening {$archiveFilepath} when extracting.");
|
100 |
+
}
|
101 |
+
|
102 |
+
$result = $zipArchive->extractTo($destinationDirectory, $relativeFilesToExtract);
|
103 |
+
if ($result === false) {
|
104 |
+
throw new Exception("Error extracting {$archiveFilepath}.");
|
105 |
+
}
|
106 |
+
}
|
107 |
+
}
|
108 |
+
|
109 |
+
/**
|
110 |
+
* Add a directory to an existing ZipArchive object
|
111 |
+
*
|
112 |
+
* @param string $sourceFilePath The file to add to the zip file
|
113 |
+
* @param string $zipFilePath The zip file to be added to
|
114 |
+
* @param bool $deleteOld Delete the zip file before adding a file
|
115 |
+
* @param string $newName Rename the $sourceFile if needed
|
116 |
+
*
|
117 |
+
* @return bool Returns true if the file was added to the zip file
|
118 |
+
*/
|
119 |
+
public static function zipFile($sourceFilePath, $zipFilePath, $deleteOld, $newName, $isCompressed)
|
120 |
+
{
|
121 |
+
if ($deleteOld && file_exists($zipFilePath)) {
|
122 |
+
SnapIO::unlink($zipFilePath);
|
123 |
+
}
|
124 |
+
|
125 |
+
if (file_exists($sourceFilePath)) {
|
126 |
+
$zip_archive = new ZipArchive();
|
127 |
+
$is_zip_open = ($zip_archive->open($zipFilePath, ZIPARCHIVE::CREATE) === true);
|
128 |
+
if ($is_zip_open === false) {
|
129 |
+
DUP_Log::error("Cannot create zip archive {$zipFilePath}");
|
130 |
+
} else {
|
131 |
+
//ADD SQL
|
132 |
+
if ($newName == null) {
|
133 |
+
$source_filename = basename($sourceFilePath);
|
134 |
+
DUP_Log::Info("adding {$source_filename}");
|
135 |
+
} else {
|
136 |
+
$source_filename = $newName;
|
137 |
+
DUP_Log::Info("new name added {$newName}");
|
138 |
+
}
|
139 |
+
|
140 |
+
$in_zip = DUP_Zip_U::addFileToZipArchive($zip_archive, $sourceFilePath, $source_filename, $isCompressed);
|
141 |
+
if ($in_zip === false) {
|
142 |
+
DUP_Log::error("Unable to add {$sourceFilePath} to $zipFilePath");
|
143 |
+
}
|
144 |
+
|
145 |
+
$zip_archive->close();
|
146 |
+
return true;
|
147 |
+
}
|
148 |
+
} else {
|
149 |
+
DUP_Log::error("Trying to add {$sourceFilePath} to a zip but it doesn't exist!");
|
150 |
+
}
|
151 |
+
|
152 |
+
return false;
|
153 |
+
}
|
154 |
+
|
155 |
+
public static function addFileToZipArchive($zipArchive, $filepath, $localName, $isCompressed)
|
156 |
+
{
|
157 |
+
$added = $zipArchive->addFile($filepath, $localName);
|
158 |
+
return $added;
|
159 |
+
}
|
160 |
+
}
|
ctrls/class.web.services.php
CHANGED
@@ -1,9 +1,11 @@
|
|
1 |
<?php
|
|
|
|
|
|
|
2 |
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
3 |
|
4 |
class DUP_Web_Services
|
5 |
{
|
6 |
-
|
7 |
/**
|
8 |
* init ajax actions
|
9 |
*/
|
@@ -61,8 +63,7 @@ class DUP_Web_Services
|
|
61 |
DUP_Package::not_active_files_tmp_cleanup();
|
62 |
|
63 |
//throw new Exception('force error test');
|
64 |
-
}
|
65 |
-
catch (Exception $e) {
|
66 |
$error = true;
|
67 |
$result['message'] = $e->getMessage();
|
68 |
}
|
@@ -125,9 +126,9 @@ class DUP_Web_Services
|
|
125 |
$fileName = $package->getInstDownloadName();
|
126 |
$realFileName = $package->Installer->File;
|
127 |
$backupDir = DUP_Settings::getSsdirPath();
|
128 |
-
|
129 |
-
if(DUP_STR::endsWith($realFileName, '.php')) {
|
130 |
-
$realFileName = basename($realFileName, '.php').DUP_Installer::INSTALLER_SERVER_EXTENSION;
|
131 |
}
|
132 |
$filepath = "{$backupDir}/{$realFileName}";
|
133 |
|
@@ -137,35 +138,33 @@ class DUP_Web_Services
|
|
137 |
}
|
138 |
|
139 |
// Clean output buffer
|
140 |
-
if (ob_get_level() !== 0 && @ob_end_clean() ===
|
141 |
@ob_clean();
|
142 |
}
|
143 |
|
144 |
header('Content-Description: File Transfer');
|
145 |
header('Content-Type: application/octet-stream');
|
146 |
-
header('Content-Disposition: attachment; filename="'
|
147 |
header('Expires: 0');
|
148 |
header('Cache-Control: must-revalidate');
|
149 |
header('Pragma: public');
|
150 |
-
header('Content-Length: '.filesize($filepath));
|
151 |
flush(); // Flush system output buffer
|
152 |
|
153 |
try {
|
154 |
$fp = @fopen($filepath, 'r');
|
155 |
if (false === $fp) {
|
156 |
-
throw new Exception('Fail to open the file '
|
157 |
}
|
158 |
-
while (!feof($fp) && ($data = fread($fp, DUPLICATOR_BUFFER_READ_WRITE_SIZE)) !==
|
159 |
echo $data;
|
160 |
}
|
161 |
@fclose($fp);
|
162 |
-
}
|
163 |
-
catch (Exception $e) {
|
164 |
readfile($filepath);
|
165 |
}
|
166 |
exit;
|
167 |
-
}
|
168 |
-
catch (Exception $ex) {
|
169 |
//Prevent brute force
|
170 |
sleep(2);
|
171 |
wp_die($ex->getMessage());
|
@@ -176,7 +175,7 @@ class DUP_Web_Services
|
|
176 |
{
|
177 |
DUP_Handler::init_error_handler();
|
178 |
|
179 |
-
try{
|
180 |
DUP_Util::hasCapability('export', DUP_Util::SECURE_ISSUE_THROW);
|
181 |
|
182 |
if (!wp_verify_nonce($_REQUEST['nonce'], 'duplicator_set_admin_notice_viewed')) {
|
@@ -184,7 +183,7 @@ class DUP_Web_Services
|
|
184 |
throw new Exception('Security issue');
|
185 |
}
|
186 |
|
187 |
-
$notice_id =
|
188 |
|
189 |
if (empty($notice_id)) {
|
190 |
throw new Exception(__('Invalid Request', 'duplicator'));
|
@@ -201,8 +200,7 @@ class DUP_Web_Services
|
|
201 |
|
202 |
$notices[$notice_id] = 'true';
|
203 |
update_user_meta(get_current_user_id(), DUPLICATOR_ADMIN_NOTICES_USER_META_KEY, $notices);
|
204 |
-
}
|
205 |
-
catch (Exception $ex) {
|
206 |
wp_die($ex->getMessage());
|
207 |
}
|
208 |
}
|
@@ -224,15 +222,14 @@ class DUP_Web_Services
|
|
224 |
case DUP_UI_Notice::OPTION_KEY_NEW_NOTICE_TEMPLATE:
|
225 |
delete_option($noticeToDismiss);
|
226 |
break;
|
227 |
-
case DUP_UI_Notice::
|
228 |
case DUP_UI_Notice::OPTION_KEY_IS_MU_NOTICE_DISMISSED:
|
229 |
update_option($noticeToDismiss, true);
|
230 |
break;
|
231 |
default:
|
232 |
throw new Exception('Notice invalid');
|
233 |
}
|
234 |
-
}
|
235 |
-
catch (Exception $e) {
|
236 |
wp_send_json_error($e->getMessage());
|
237 |
}
|
238 |
|
1 |
<?php
|
2 |
+
|
3 |
+
use Duplicator\Libs\Snap\SnapUtil;
|
4 |
+
|
5 |
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
6 |
|
7 |
class DUP_Web_Services
|
8 |
{
|
|
|
9 |
/**
|
10 |
* init ajax actions
|
11 |
*/
|
63 |
DUP_Package::not_active_files_tmp_cleanup();
|
64 |
|
65 |
//throw new Exception('force error test');
|
66 |
+
} catch (Exception $e) {
|
|
|
67 |
$error = true;
|
68 |
$result['message'] = $e->getMessage();
|
69 |
}
|
126 |
$fileName = $package->getInstDownloadName();
|
127 |
$realFileName = $package->Installer->File;
|
128 |
$backupDir = DUP_Settings::getSsdirPath();
|
129 |
+
|
130 |
+
if (DUP_STR::endsWith($realFileName, '.php')) {
|
131 |
+
$realFileName = basename($realFileName, '.php') . DUP_Installer::INSTALLER_SERVER_EXTENSION;
|
132 |
}
|
133 |
$filepath = "{$backupDir}/{$realFileName}";
|
134 |
|
138 |
}
|
139 |
|
140 |
// Clean output buffer
|
141 |
+
if (ob_get_level() !== 0 && @ob_end_clean() === false) {
|
142 |
@ob_clean();
|
143 |
}
|
144 |
|
145 |
header('Content-Description: File Transfer');
|
146 |
header('Content-Type: application/octet-stream');
|
147 |
+
header('Content-Disposition: attachment; filename="' . $fileName . '"');
|
148 |
header('Expires: 0');
|
149 |
header('Cache-Control: must-revalidate');
|
150 |
header('Pragma: public');
|
151 |
+
header('Content-Length: ' . filesize($filepath));
|
152 |
flush(); // Flush system output buffer
|
153 |
|
154 |
try {
|
155 |
$fp = @fopen($filepath, 'r');
|
156 |
if (false === $fp) {
|
157 |
+
throw new Exception('Fail to open the file ' . $filepath);
|
158 |
}
|
159 |
+
while (!feof($fp) && ($data = fread($fp, DUPLICATOR_BUFFER_READ_WRITE_SIZE)) !== false) {
|
160 |
echo $data;
|
161 |
}
|
162 |
@fclose($fp);
|
163 |
+
} catch (Exception $e) {
|
|
|
164 |
readfile($filepath);
|
165 |
}
|
166 |
exit;
|
167 |
+
} catch (Exception $ex) {
|
|
|
168 |
//Prevent brute force
|
169 |
sleep(2);
|
170 |
wp_die($ex->getMessage());
|
175 |
{
|
176 |
DUP_Handler::init_error_handler();
|
177 |
|
178 |
+
try {
|
179 |
DUP_Util::hasCapability('export', DUP_Util::SECURE_ISSUE_THROW);
|
180 |
|
181 |
if (!wp_verify_nonce($_REQUEST['nonce'], 'duplicator_set_admin_notice_viewed')) {
|
183 |
throw new Exception('Security issue');
|
184 |
}
|
185 |
|
186 |
+
$notice_id = SnapUtil::filterInputRequest('notice_id', FILTER_UNSAFE_RAW);
|
187 |
|
188 |
if (empty($notice_id)) {
|
189 |
throw new Exception(__('Invalid Request', 'duplicator'));
|
200 |
|
201 |
$notices[$notice_id] = 'true';
|
202 |
update_user_meta(get_current_user_id(), DUPLICATOR_ADMIN_NOTICES_USER_META_KEY, $notices);
|
203 |
+
} catch (Exception $ex) {
|
|
|
204 |
wp_die($ex->getMessage());
|
205 |
}
|
206 |
}
|
222 |
case DUP_UI_Notice::OPTION_KEY_NEW_NOTICE_TEMPLATE:
|
223 |
delete_option($noticeToDismiss);
|
224 |
break;
|
225 |
+
case DUP_UI_Notice::OPTION_KEY_IS_ENABLE_NOTICE_DISMISSED:
|
226 |
case DUP_UI_Notice::OPTION_KEY_IS_MU_NOTICE_DISMISSED:
|
227 |
update_option($noticeToDismiss, true);
|
228 |
break;
|
229 |
default:
|
230 |
throw new Exception('Notice invalid');
|
231 |
}
|
232 |
+
} catch (Exception $e) {
|
|
|
233 |
wp_send_json_error($e->getMessage());
|
234 |
}
|
235 |
|
ctrls/ctrl.base.php
CHANGED
@@ -1,153 +1,152 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
*
|
45 |
-
*
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
*
|
60 |
-
*
|
61 |
-
*
|
62 |
-
*
|
63 |
-
*
|
64 |
-
*
|
65 |
-
*
|
66 |
-
* @
|
67 |
-
* @
|
68 |
-
*
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
use Duplicator\Libs\Snap\SnapJson;
|
4 |
+
|
5 |
+
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
6 |
+
// Exit if accessed directly
|
7 |
+
if (! defined('DUPLICATOR_VERSION')) {
|
8 |
+
exit;
|
9 |
+
}
|
10 |
+
|
11 |
+
require_once(DUPLICATOR_PLUGIN_PATH . '/classes/utilities/class.u.php');
|
12 |
+
//Enum used to define the various test statues
|
13 |
+
final class DUP_CTRL_Status
|
14 |
+
{
|
15 |
+
const ERROR = -2;
|
16 |
+
const FAILED = -1;
|
17 |
+
const UNDEFINED = 0;
|
18 |
+
const SUCCESS = 1;
|
19 |
+
}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Base class for all controllers
|
23 |
+
*
|
24 |
+
* @package Duplicator
|
25 |
+
* @subpackage classes/ctrls
|
26 |
+
*/
|
27 |
+
class DUP_CTRL_Base
|
28 |
+
{
|
29 |
+
//Represents the name of the Nonce Action
|
30 |
+
public $Action;
|
31 |
+
//The return type valiad options: PHP, JSON-AJAX, JSON
|
32 |
+
public $returnType = 'JSON-AJAX';
|
33 |
+
public function setResponseType($type)
|
34 |
+
{
|
35 |
+
$opts = array('PHP', 'JSON-AJAX', 'JSON');
|
36 |
+
if (!in_array($type, $opts)) {
|
37 |
+
throw new Exception('The $type param must be one of the following: ' . implode(',', $opts) . ' for the following function [' . __FUNCTION__ . ']');
|
38 |
+
}
|
39 |
+
$this->returnType = $type;
|
40 |
+
}
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* A class structer used to report on controller methods
|
45 |
+
*
|
46 |
+
* @package Duplicator
|
47 |
+
* @subpackage classes/ctrls
|
48 |
+
*/
|
49 |
+
class DUP_CTRL_Report
|
50 |
+
{
|
51 |
+
//Properties
|
52 |
+
public $runTime;
|
53 |
+
public $returnType;
|
54 |
+
public $results;
|
55 |
+
public $status;
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* A class used format all controller responses in a consistent format. Every controller response will
|
60 |
+
* have a Report and Payload structer. The Payload is an array of the result response. The Report is used
|
61 |
+
* report on the overall status of the controller method
|
62 |
+
*
|
63 |
+
* Standard: PSR-2
|
64 |
+
* @link http://www.php-fig.org/psr/psr-2
|
65 |
+
*
|
66 |
+
* @package Duplicator
|
67 |
+
* @subpackage classes/ctrls
|
68 |
+
* @copyright (c) 2017, Snapcreek LLC
|
69 |
+
*
|
70 |
+
*/
|
71 |
+
class DUP_CTRL_Result
|
72 |
+
{
|
73 |
+
//Properties
|
74 |
+
public $report;
|
75 |
+
public $payload;
|
76 |
+
private $timeStart;
|
77 |
+
private $timeEnd;
|
78 |
+
private $CTRL;
|
79 |
+
|
80 |
+
public function __construct(DUP_CTRL_Base $CTRL_OBJ)
|
81 |
+
{
|
82 |
+
DUP_Util::hasCapability('export');
|
83 |
+
$this->timeStart = $this->microtimeFloat();
|
84 |
+
$this->CTRL = $CTRL_OBJ;
|
85 |
+
//Report Data
|
86 |
+
$this->report = new DUP_CTRL_Report();
|
87 |
+
$this->report->returnType = $CTRL_OBJ->returnType;
|
88 |
+
}
|
89 |
+
|
90 |
+
/**
|
91 |
+
* Used to process a controller request
|
92 |
+
*
|
93 |
+
* @param object $payload The response object that will be returned
|
94 |
+
* @param enum $test The status of a response
|
95 |
+
*
|
96 |
+
* @return object || JSON Returns a PHP object or json encoded object
|
97 |
+
*/
|
98 |
+
public function process($payload, $test = DUP_CTRL_Status::UNDEFINED)
|
99 |
+
{
|
100 |
+
if (is_array($this->payload)) {
|
101 |
+
$this->payload[] = $payload;
|
102 |
+
$this->report->results = count($this->payload);
|
103 |
+
} else {
|
104 |
+
$this->payload = $payload;
|
105 |
+
$this->report->results = (is_array($payload)) ? count($payload) : 1;
|
106 |
+
}
|
107 |
+
|
108 |
+
$this->report->status = $test;
|
109 |
+
$this->getProcessTime();
|
110 |
+
switch ($this->CTRL->returnType) {
|
111 |
+
case 'JSON':
|
112 |
+
return SnapJson::jsonEncode($this);
|
113 |
+
break;
|
114 |
+
case 'PHP':
|
115 |
+
return $this;
|
116 |
+
break;
|
117 |
+
default:
|
118 |
+
wp_send_json($this);
|
119 |
+
break;
|
120 |
+
}
|
121 |
+
}
|
122 |
+
|
123 |
+
/**
|
124 |
+
* Used to process an error response
|
125 |
+
*
|
126 |
+
* @param object $exception The PHP exception object
|
127 |
+
*
|
128 |
+
* @return object || JSON Returns a PHP object or json encoded object
|
129 |
+
*/
|
130 |
+
public function processError($exception)
|
131 |
+
{
|
132 |
+
$payload = array();
|
133 |
+
$payload['Message'] = $exception->getMessage();
|
134 |
+
$payload['File'] = $exception->getFile();
|
135 |
+
$payload['Line'] = $exception->getLine();
|
136 |
+
$payload['Trace'] = $exception->getTraceAsString();
|
137 |
+
$this->process($payload, DUP_CTRL_Status::ERROR);
|
138 |
+
die(SnapJson::jsonEncode($this));
|
139 |
+
}
|
140 |
+
|
141 |
+
private function getProcessTime()
|
142 |
+
{
|
143 |
+
$this->timeEnd = $this->microtimeFloat();
|
144 |
+
$this->report->runTime = $this->timeEnd - $this->timeStart;
|
145 |
+
}
|
146 |
+
|
147 |
+
private function microtimeFloat()
|
148 |
+
{
|
149 |
+
list($usec, $sec) = explode(" ", microtime());
|
150 |
+
return ((float) $usec + (float) $sec);
|
151 |
+
}
|
152 |
+
}
|
|
ctrls/ctrl.package.php
CHANGED
@@ -1,479 +1,430 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
require_once(DUPLICATOR_PLUGIN_PATH.'/classes/
|
13 |
-
require_once(DUPLICATOR_PLUGIN_PATH.'/classes/
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
*
|
34 |
-
*
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
@set_time_limit(0);
|
49 |
-
$errLevel = error_reporting();
|
50 |
-
error_reporting(E_ERROR);
|
51 |
-
DUP_Util::initSnapshotDirectory();
|
52 |
-
|
53 |
-
$
|
54 |
-
$
|
55 |
-
|
56 |
-
$package->saveActiveItem('
|
57 |
-
$json_response = DUP_JSON::safeEncode($report);
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
*
|
66 |
-
*
|
67 |
-
*
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
$Package->
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
$Package->
|
99 |
-
|
100 |
-
|
101 |
-
//
|
102 |
-
|
103 |
-
$
|
104 |
-
|
105 |
-
|
106 |
-
$json
|
107 |
-
$json['
|
108 |
-
$json['
|
109 |
-
$json['
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
//
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
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 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
DUP_Log::Info('[CTRL DUP ARCIVE] ERROR
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
'
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
$
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
}
|
287 |
-
|
288 |
-
$
|
289 |
-
$
|
290 |
-
$
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
$
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
$
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
$payload['files-in'] = esc_html(sanitize_text_field($inputData['file_paths']));
|
432 |
-
$payload['files-out'] = esc_html($package->Archive->FilterFiles);
|
433 |
-
|
434 |
-
//RETURN RESULT
|
435 |
-
$test = ($changed) ? DUP_CTRL_Status::SUCCESS : DUP_CTRL_Status::FAILED;
|
436 |
-
$result->process($payload, $test);
|
437 |
-
}
|
438 |
-
catch (Exception $exc) {
|
439 |
-
$result->processError($exc);
|
440 |
-
}
|
441 |
-
}
|
442 |
-
|
443 |
-
/**
|
444 |
-
* Get active package status
|
445 |
-
*
|
446 |
-
* <code>
|
447 |
-
* //JavaScript Ajax Request
|
448 |
-
* Duplicator.Package.getActivePackageStatus()
|
449 |
-
* </code>
|
450 |
-
*/
|
451 |
-
public function getActivePackageStatus()
|
452 |
-
{
|
453 |
-
DUP_Handler::init_error_handler();
|
454 |
-
check_ajax_referer('DUP_CTRL_Package_getActivePackageStatus', 'nonce');
|
455 |
-
|
456 |
-
$result = new DUP_CTRL_Result($this);
|
457 |
-
|
458 |
-
try {
|
459 |
-
DUP_Util::hasCapability('export', DUP_Util::SECURE_ISSUE_THROW);
|
460 |
-
//CONTROLLER LOGIC
|
461 |
-
$active_package_id = DUP_Settings::Get('active_package_id');
|
462 |
-
$package = DUP_Package::getByID($active_package_id);
|
463 |
-
$payload = array();
|
464 |
-
|
465 |
-
if ($package != null) {
|
466 |
-
$test = DUP_CTRL_Status::SUCCESS;
|
467 |
-
$payload['status'] = $package->Status;
|
468 |
-
} else {
|
469 |
-
$test = DUP_CTRL_Status::FAILED;
|
470 |
-
}
|
471 |
-
|
472 |
-
//RETURN RESULT
|
473 |
-
return $result->process($payload, $test);
|
474 |
-
}
|
475 |
-
catch (Exception $exc) {
|
476 |
-
$result->processError($exc);
|
477 |
-
}
|
478 |
-
}
|
479 |
-
}
|
1 |
+
<?php
|
2 |
+
|
3 |
+
use Duplicator\Libs\Snap\SnapJson;
|
4 |
+
|
5 |
+
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
6 |
+
// Exit if accessed directly
|
7 |
+
if (!defined('DUPLICATOR_VERSION')) {
|
8 |
+
exit;
|
9 |
+
}
|
10 |
+
|
11 |
+
require_once(DUPLICATOR_PLUGIN_PATH . '/ctrls/ctrl.base.php');
|
12 |
+
require_once(DUPLICATOR_PLUGIN_PATH . '/classes/utilities/class.u.scancheck.php');
|
13 |
+
require_once(DUPLICATOR_PLUGIN_PATH . '/classes/utilities/class.u.json.php');
|
14 |
+
require_once(DUPLICATOR_PLUGIN_PATH . '/classes/package/class.pack.php');
|
15 |
+
require_once(DUPLICATOR_PLUGIN_PATH . '/classes/package/duparchive/class.pack.archive.duparchive.state.create.php');
|
16 |
+
require_once(DUPLICATOR_PLUGIN_PATH . '/classes/package/duparchive/class.pack.archive.duparchive.php');
|
17 |
+
/* @var $package DUP_Package */
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Display error if any fatal error occurs occurs while scan ajax call
|
21 |
+
*
|
22 |
+
* @return void
|
23 |
+
*/
|
24 |
+
function duplicator_package_scan_shutdown()
|
25 |
+
{
|
26 |
+
$logMessage = DUP_Handler::getVarLog();
|
27 |
+
if (!empty($logMessage)) {
|
28 |
+
echo nl2br($logMessage);
|
29 |
+
}
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* DUPLICATOR_PACKAGE_SCAN
|
34 |
+
* Returns a JSON scan report object which contains data about the system
|
35 |
+
*
|
36 |
+
* @return json JSON report object
|
37 |
+
* @example to test: /wp-admin/admin-ajax.php?action=duplicator_package_scan
|
38 |
+
*/
|
39 |
+
function duplicator_package_scan()
|
40 |
+
{
|
41 |
+
DUP_Handler::init_error_handler();
|
42 |
+
DUP_Handler::setMode(DUP_Handler::MODE_VAR);
|
43 |
+
register_shutdown_function('duplicator_package_scan_shutdown');
|
44 |
+
check_ajax_referer('duplicator_package_scan', 'nonce');
|
45 |
+
DUP_Util::hasCapability('export');
|
46 |
+
header('Content-Type: application/json;');
|
47 |
+
@ob_flush();
|
48 |
+
@set_time_limit(0);
|
49 |
+
$errLevel = error_reporting();
|
50 |
+
error_reporting(E_ERROR);
|
51 |
+
DUP_Util::initSnapshotDirectory();
|
52 |
+
$package = DUP_Package::getActive();
|
53 |
+
$report = $package->runScanner();
|
54 |
+
$package->saveActiveItem('ScanFile', $package->ScanFile);
|
55 |
+
$package->Archive->saveActiveItem($package, 'dirsCount', $package->Archive->dirsCount);
|
56 |
+
$package->Archive->saveActiveItem($package, 'filesCount', $package->Archive->filesCount);
|
57 |
+
$json_response = DUP_JSON::safeEncode($report);
|
58 |
+
DUP_Package::tempFileCleanup();
|
59 |
+
error_reporting($errLevel);
|
60 |
+
die($json_response);
|
61 |
+
}
|
62 |
+
|
63 |
+
/**
|
64 |
+
* duplicator_package_build
|
65 |
+
* Returns the package result status
|
66 |
+
*
|
67 |
+
* @return json JSON object of package results
|
68 |
+
*/
|
69 |
+
function duplicator_package_build()
|
70 |
+
{
|
71 |
+
DUP_Handler::init_error_handler();
|
72 |
+
check_ajax_referer('duplicator_package_build', 'nonce');
|
73 |
+
header('Content-Type: application/json');
|
74 |
+
$Package = null;
|
75 |
+
try {
|
76 |
+
DUP_Util::hasCapability('export', DUP_Util::SECURE_ISSUE_THROW);
|
77 |
+
@set_time_limit(0);
|
78 |
+
$errLevel = error_reporting();
|
79 |
+
error_reporting(E_ERROR);
|
80 |
+
DUP_Util::initSnapshotDirectory();
|
81 |
+
$Package = DUP_Package::getActive();
|
82 |
+
$Package->save('zip');
|
83 |
+
DUP_Settings::Set('active_package_id', $Package->ID);
|
84 |
+
DUP_Settings::Save();
|
85 |
+
if (!is_readable(DUP_Settings::getSsdirTmpPath() . "/{$Package->ScanFile}")) {
|
86 |
+
die("The scan result file was not found. Please run the scan step before building the package.");
|
87 |
+
}
|
88 |
+
|
89 |
+
$Package->runZipBuild();
|
90 |
+
//JSON:Debug Response
|
91 |
+
//Pass = 1, Warn = 2, Fail = 3
|
92 |
+
$json = array();
|
93 |
+
$json['status'] = 1;
|
94 |
+
$json['error'] = '';
|
95 |
+
$json['package'] = $Package;
|
96 |
+
$json['instDownloadName'] = $Package->getInstDownloadName();
|
97 |
+
$json['runtime'] = $Package->Runtime;
|
98 |
+
$json['exeSize'] = $Package->ExeSize;
|
99 |
+
$json['archiveSize'] = $Package->ZipSize;
|
100 |
+
//Simulate a Host Build Interrupt
|
101 |
+
//die(0);
|
102 |
+
} catch (Exception $e) {
|
103 |
+
$Package->setStatus(DUP_PackageStatus::ERROR);
|
104 |
+
//JSON:Debug Response
|
105 |
+
//Pass = 1, Warn = 2, Fail = 3
|
106 |
+
$json = array();
|
107 |
+
$json['status'] = 3;
|
108 |
+
$json['error'] = $e->getMessage() . "\n" . "FILE: " . $e->getFile() . "[" . $e->getLine() . "]\n" . $e->getTraceAsString();
|
109 |
+
$json['package'] = $Package;
|
110 |
+
$json['instDownloadName'] = null;
|
111 |
+
$json['runtime'] = null;
|
112 |
+
$json['exeSize'] = null;
|
113 |
+
$json['archiveSize'] = null;
|
114 |
+
}
|
115 |
+
$json_response = SnapJson::jsonEncode($json);
|
116 |
+
error_reporting($errLevel);
|
117 |
+
die($json_response);
|
118 |
+
}
|
119 |
+
|
120 |
+
/**
|
121 |
+
* Returns the package result status
|
122 |
+
*
|
123 |
+
* @return json JSON object of package results
|
124 |
+
*/
|
125 |
+
function duplicator_duparchive_package_build()
|
126 |
+
{
|
127 |
+
DUP_Handler::init_error_handler();
|
128 |
+
DUP_Log::Info('[CTRL DUP ARCIVE] CALL TO ' . __FUNCTION__);
|
129 |
+
check_ajax_referer('duplicator_duparchive_package_build', 'nonce');
|
130 |
+
DUP_Util::hasCapability('export');
|
131 |
+
header('Content-Type: application/json');
|
132 |
+
@set_time_limit(0);
|
133 |
+
$errLevel = error_reporting();
|
134 |
+
error_reporting(E_ERROR);
|
135 |
+
// The DupArchive build process always works on a saved package so the first time through save the active package to the package table.
|
136 |
+
// After that, just retrieve it.
|
137 |
+
$active_package_id = DUP_Settings::Get('active_package_id');
|
138 |
+
DUP_Log::Info('[CTRL DUP ARCIVE] CURRENT PACKAGE ACTIVE ' . $active_package_id);
|
139 |
+
if ($active_package_id == -1) {
|
140 |
+
$package = DUP_Package::getActive();
|
141 |
+
$package->save('daf');
|
142 |
+
DUP_Log::Info('[CTRL DUP ARCIVE] PACKAGE AS NEW ID ' . $package->ID . ' SAVED | STATUS:' . $package->Status);
|
143 |
+
//DUP_Log::TraceObject("[CTRL DUP ARCIVE] PACKAGE SAVED:", $package);
|
144 |
+
DUP_Settings::Set('active_package_id', $package->ID);
|
145 |
+
DUP_Settings::Save();
|
146 |
+
} else {
|
147 |
+
if (($package = DUP_Package::getByID($active_package_id)) == null) {
|
148 |
+
DUP_Log::Info('[CTRL DUP ARCIVE] ERROR: Get package by id ' . $active_package_id . ' FAILED');
|
149 |
+
die('Get package by id ' . $active_package_id . ' FAILED');
|
150 |
+
}
|
151 |
+
DUP_Log::Info('[CTRL DUP ARCIVE] PACKAGE GET BY ID ' . $active_package_id . ' | STATUS:' . $package->Status);
|
152 |
+
// DUP_Log::TraceObject("getting active package by id {$active_package_id}", $package);
|
153 |
+
}
|
154 |
+
|
155 |
+
if (!is_readable(DUP_Settings::getSsdirTmpPath() . "/{$package->ScanFile}")) {
|
156 |
+
DUP_Log::Info('[CTRL DUP ARCIVE] ERROR: The scan result file was not found. Please run the scan step before building the package.');
|
157 |
+
die("The scan result file was not found. Please run the scan step before building the package.");
|
158 |
+
}
|
159 |
+
|
160 |
+
if ($package === null) {
|
161 |
+
DUP_Log::Info('[CTRL DUP ARCIVE] There is no active package.');
|
162 |
+
die("There is no active package.");
|
163 |
+
}
|
164 |
+
|
165 |
+
if ($package->Status == DUP_PackageStatus::ERROR) {
|
166 |
+
$package->setStatus(DUP_PackageStatus::ERROR);
|
167 |
+
$hasCompleted = true;
|
168 |
+
} else {
|
169 |
+
try {
|
170 |
+
$hasCompleted = $package->runDupArchiveBuild();
|
171 |
+
} catch (Exception $ex) {
|
172 |
+
DUP_Log::Info('[CTRL DUP ARCIVE] ERROR: caught exception');
|
173 |
+
Dup_Log::error('[CTRL DUP ARCIVE] Caught exception', $ex->getMessage(), Dup_ErrorBehavior::LogOnly);
|
174 |
+
DUP_Log::Info('[CTRL DUP ARCIVE] ERROR: after log');
|
175 |
+
$package->setStatus(DUP_PackageStatus::ERROR);
|
176 |
+
$hasCompleted = true;
|
177 |
+
}
|
178 |
+
}
|
179 |
+
|
180 |
+
$json = array();
|
181 |
+
$json['failures'] = array_merge($package->BuildProgress->build_failures, $package->BuildProgress->validation_failures);
|
182 |
+
if (!empty($json['failures'])) {
|
183 |
+
DUP_Log::Info('[CTRL DUP ARCIVE] FAILURES ' . print_r($json['failures'], true));
|
184 |
+
}
|
185 |
+
|
186 |
+
//JSON:Debug Response
|
187 |
+
//Pass = 1, Warn = 2, 3 = Failure, 4 = Not Done
|
188 |
+
if ($hasCompleted) {
|
189 |
+
DUP_Log::Info('[CTRL DUP ARCIVE] COMPLETED PACKAGE STATUS: ' . $package->Status);
|
190 |
+
if ($package->Status == DUP_PackageStatus::ERROR) {
|
191 |
+
DUP_Log::Info('[CTRL DUP ARCIVE] ERROR');
|
192 |
+
$error_message = __('Error building DupArchive package') . '<br/>';
|
193 |
+
foreach ($json['failures'] as $failure) {
|
194 |
+
$error_message .= implode(',', $failure->description);
|
195 |
+
}
|
196 |
+
|
197 |
+
Dup_Log::error("Build failed so sending back error", esc_html($error_message), Dup_ErrorBehavior::LogOnly);
|
198 |
+
DUP_Log::Info('[CTRL DUP ARCIVE] ERROR AFTER LOG 2');
|
199 |
+
$json['status'] = 3;
|
200 |
+
} else {
|
201 |
+
Dup_Log::Info("sending back success status");
|
202 |
+
$json['status'] = 1;
|
203 |
+
}
|
204 |
+
|
205 |
+
Dup_Log::Trace('#### json package');
|
206 |
+
$json['package'] = $package;
|
207 |
+
$json['instDownloadName'] = $package->getInstDownloadName();
|
208 |
+
$json['runtime'] = $package->Runtime;
|
209 |
+
$json['exeSize'] = $package->ExeSize;
|
210 |
+
$json['archiveSize'] = $package->ZipSize;
|
211 |
+
DUP_Log::Trace('[CTRL DUP ARCIVE] JSON PACKAGE');
|
212 |
+
} else {
|
213 |
+
DUP_Log::Info('[CTRL DUP ARCIVE] sending back continue status PACKAGE STATUS: ' . $package->Status);
|
214 |
+
$json['status'] = 4;
|
215 |
+
}
|
216 |
+
|
217 |
+
$json_response = SnapJson::jsonEncode($json);
|
218 |
+
Dup_Log::TraceObject('json response', $json_response);
|
219 |
+
error_reporting($errLevel);
|
220 |
+
die($json_response);
|
221 |
+
}
|
222 |
+
|
223 |
+
/**
|
224 |
+
* DUPLICATOR_PACKAGE_DELETE
|
225 |
+
* Deletes the files and database record entries
|
226 |
+
*
|
227 |
+
* @return json A JSON message about the action.
|
228 |
+
* Use console.log to debug from client
|
229 |
+
*/
|
230 |
+
function duplicator_package_delete()
|
231 |
+
{
|
232 |
+
DUP_Handler::init_error_handler();
|
233 |
+
check_ajax_referer('duplicator_package_delete', 'nonce');
|
234 |
+
$json = array(
|
235 |
+
'success' => false,
|
236 |
+
'message' => ''
|
237 |
+
);
|
238 |
+
$package_ids = filter_input(INPUT_POST, 'package_ids', FILTER_VALIDATE_INT, array(
|
239 |
+
'flags' => FILTER_REQUIRE_ARRAY,
|
240 |
+
'options' => array(
|
241 |
+
'default' => false
|
242 |
+
)
|
243 |
+
));
|
244 |
+
$delCount = 0;
|
245 |
+
try {
|
246 |
+
DUP_Util::hasCapability('export', DUP_Util::SECURE_ISSUE_THROW);
|
247 |
+
if ($package_ids === false || in_array(false, $package_ids)) {
|
248 |
+
throw new Exception('Invalid Request.', 'duplicator');
|
249 |
+
}
|
250 |
+
|
251 |
+
foreach ($package_ids as $id) {
|
252 |
+
$package = DUP_Package::getByID($id);
|
253 |
+
if ($package === null) {
|
254 |
+
throw new Exception('Invalid Request.', 'duplicator');
|
255 |
+
}
|
256 |
+
|
257 |
+
$package->delete();
|
258 |
+
$delCount++;
|
259 |
+
}
|
260 |
+
|
261 |
+
$json['success'] = true;
|
262 |
+
$json['ids'] = $package_ids;
|
263 |
+
$json['removed'] = $delCount;
|
264 |
+
} catch (Exception $ex) {
|
265 |
+
$json['message'] = $ex->getMessage();
|
266 |
+
}
|
267 |
+
|
268 |
+
die(SnapJson::jsonEncode($json));
|
269 |
+
}
|
270 |
+
|
271 |
+
/**
|
272 |
+
* Active package info
|
273 |
+
* Returns a JSON scan report active package info or
|
274 |
+
* active_package_present == false if no active package is present.
|
275 |
+
*
|
276 |
+
* @return json
|
277 |
+
*/
|
278 |
+
function duplicator_active_package_info()
|
279 |
+
{
|
280 |
+
ob_start();
|
281 |
+
try {
|
282 |
+
DUP_Handler::init_error_handler();
|
283 |
+
DUP_Util::hasCapability('export', DUP_Util::SECURE_ISSUE_THROW);
|
284 |
+
if (!check_ajax_referer('duplicator_active_package_info', 'nonce', false)) {
|
285 |
+
throw new Exception(__('An unauthorized security request was made to this page. Please try again!', 'duplicator'));
|
286 |
+
}
|
287 |
+
|
288 |
+
global $wpdb;
|
289 |
+
$error = false;
|
290 |
+
$result = array(
|
291 |
+
'active_package' => array(
|
292 |
+
'present' => false,
|
293 |
+
'status' => 0,
|
294 |
+
'size' => 0
|
295 |
+
),
|
296 |
+
'html' => '',
|
297 |
+
'message' => ''
|
298 |
+
);
|
299 |
+
$result['active_package']['present'] = DUP_Package::is_active_package_present();
|
300 |
+
if ($result['active_package']['present']) {
|
301 |
+
$id = DUP_Settings::Get('active_package_id');
|
302 |
+
$package = DUP_Package::getByID($id);
|
303 |
+
if (is_null($package)) {
|
304 |
+
throw new Exception(__('Active package object error', 'duplicator'));
|
305 |
+
}
|
306 |
+
$result['active_package']['status'] = $package->Status;
|
307 |
+
$result['active_package']['size'] = $package->getArchiveSize();
|
308 |
+
$result['active_package']['size_format'] = DUP_Util::byteSize($package->getArchiveSize());
|
309 |
+
}
|
310 |
+
} catch (Exception $e) {
|
311 |
+
$error = true;
|
312 |
+
$result['message'] = $e->getMessage();
|
313 |
+
}
|
314 |
+
|
315 |
+
$result['html'] = ob_get_clean();
|
316 |
+
if ($error) {
|
317 |
+
wp_send_json_error($result);
|
318 |
+
} else {
|
319 |
+
wp_send_json_success($result);
|
320 |
+
}
|
321 |
+
}
|
322 |
+
|
323 |
+
/**
|
324 |
+
* Controller for Tools
|
325 |
+
* @package Duplicator\ctrls
|
326 |
+
*/
|
327 |
+
class DUP_CTRL_Package extends DUP_CTRL_Base
|
328 |
+
{
|
329 |
+
/**
|
330 |
+
* Init this instance of the object
|
331 |
+
*/
|
332 |
+
public function __construct()
|
333 |
+
{
|
334 |
+
add_action('wp_ajax_DUP_CTRL_Package_addQuickFilters', array($this, 'addQuickFilters'));
|
335 |
+
add_action('wp_ajax_DUP_CTRL_Package_getPackageFile', array($this, 'getPackageFile'));
|
336 |
+
add_action('wp_ajax_DUP_CTRL_Package_getActivePackageStatus', array($this, 'getActivePackageStatus'));
|
337 |
+
}
|
338 |
+
|
339 |
+
/**
|
340 |
+
* Removed all reserved installer files names
|
341 |
+
*
|
342 |
+
* @param string $_POST['dir_paths'] A semi-colon separated list of directory paths
|
343 |
+
*
|
344 |
+
* @return string Returns all of the active directory filters as a ";" separated string
|
345 |
+
*/
|
346 |
+
public function addQuickFilters()
|
347 |
+
{
|
348 |
+
DUP_Handler::init_error_handler();
|
349 |
+
check_ajax_referer('DUP_CTRL_Package_addQuickFilters', 'nonce');
|
350 |
+
$result = new DUP_CTRL_Result($this);
|
351 |
+
$inputData = filter_input_array(INPUT_POST, array(
|
352 |
+
'dir_paths' => array(
|
353 |
+
'filter' => FILTER_DEFAULT,
|
354 |
+
'flags' => FILTER_REQUIRE_SCALAR,
|
355 |
+
'options' => array(
|
356 |
+
'default' => ''
|
357 |
+
)
|
358 |
+
),
|
359 |
+
'file_paths' => array(
|
360 |
+
'filter' => FILTER_DEFAULT,
|
361 |
+
'flags' => FILTER_REQUIRE_SCALAR,
|
362 |
+
'options' => array(
|
363 |
+
'default' => ''
|
364 |
+
)
|
365 |
+
),
|
366 |
+
));
|
367 |
+
try {
|
368 |
+
DUP_Util::hasCapability('export', DUP_Util::SECURE_ISSUE_THROW);
|
369 |
+
//CONTROLLER LOGIC
|
370 |
+
$package = DUP_Package::getActive();
|
371 |
+
//DIRS
|
372 |
+
$dir_filters = ($package->Archive->FilterOn) ? $package->Archive->FilterDirs . ';' . $inputData['dir_paths'] : $inputData['dir_paths'];
|
373 |
+
$dir_filters = $package->Archive->parseDirectoryFilter($dir_filters);
|
374 |
+
$changed = $package->Archive->saveActiveItem($package, 'FilterDirs', $dir_filters);
|
375 |
+
//FILES
|
376 |
+
$file_filters = ($package->Archive->FilterOn) ? $package->Archive->FilterFiles . ';' . $inputData['file_paths'] : $inputData['file_paths'];
|
377 |
+
$file_filters = $package->Archive->parseFileFilter($file_filters);
|
378 |
+
$changed = $package->Archive->saveActiveItem($package, 'FilterFiles', $file_filters);
|
379 |
+
if (!$package->Archive->FilterOn && !empty($package->Archive->FilterExts)) {
|
380 |
+
$changed = $package->Archive->saveActiveItem($package, 'FilterExts', '');
|
381 |
+
}
|
382 |
+
|
383 |
+
$changed = $package->Archive->saveActiveItem($package, 'FilterOn', 1);
|
384 |
+
//Result
|
385 |
+
$package = DUP_Package::getActive();
|
386 |
+
$payload['dirs-in'] = esc_html(sanitize_text_field($inputData['dir_paths']));
|
387 |
+
$payload['dir-out'] = esc_html($package->Archive->FilterDirs);
|
388 |
+
$payload['files-in'] = esc_html(sanitize_text_field($inputData['file_paths']));
|
389 |
+
$payload['files-out'] = esc_html($package->Archive->FilterFiles);
|
390 |
+
//RETURN RESULT
|
391 |
+
$test = ($changed) ? DUP_CTRL_Status::SUCCESS : DUP_CTRL_Status::FAILED;
|
392 |
+
$result->process($payload, $test);
|
393 |
+
} catch (Exception $exc) {
|
394 |
+
$result->processError($exc);
|
395 |
+
}
|
396 |
+
}
|
397 |
+
|
398 |
+
/**
|
399 |
+
* Get active package status
|
400 |
+
*
|
401 |
+
* <code>
|
402 |
+
* //JavaScript Ajax Request
|
403 |
+
* Duplicator.Package.getActivePackageStatus()
|
404 |
+
* </code>
|
405 |
+
*/
|
406 |
+
public function getActivePackageStatus()
|
407 |
+
{
|
408 |
+
DUP_Handler::init_error_handler();
|
409 |
+
check_ajax_referer('DUP_CTRL_Package_getActivePackageStatus', 'nonce');
|
410 |
+
$result = new DUP_CTRL_Result($this);
|
411 |
+
try {
|
412 |
+
DUP_Util::hasCapability('export', DUP_Util::SECURE_ISSUE_THROW);
|
413 |
+
//CONTROLLER LOGIC
|
414 |
+
$active_package_id = DUP_Settings::Get('active_package_id');
|
415 |
+
$package = DUP_Package::getByID($active_package_id);
|
416 |
+
$payload = array();
|
417 |
+
if ($package != null) {
|
418 |
+
$test = DUP_CTRL_Status::SUCCESS;
|
419 |
+
$payload['status'] = $package->Status;
|
420 |
+
} else {
|
421 |
+
$test = DUP_CTRL_Status::FAILED;
|
422 |
+
}
|
423 |
+
|
424 |
+
//RETURN RESULT
|
425 |
+
return $result->process($payload, $test);
|
426 |
+
} catch (Exception $exc) {
|
427 |
+
$result->processError($exc);
|
428 |
+
}
|
429 |
+
}
|
430 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ctrls/ctrl.tools.php
CHANGED
@@ -1,132 +1,183 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
$
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
use Duplicator\Core\Controllers\ControllersManager;
|
4 |
+
use Duplicator\Libs\Snap\SnapURL;
|
5 |
+
use Duplicator\Libs\Snap\SnapUtil;
|
6 |
+
|
7 |
+
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
8 |
+
// Exit if accessed directly
|
9 |
+
if (! defined('DUPLICATOR_VERSION')) {
|
10 |
+
exit;
|
11 |
+
}
|
12 |
+
|
13 |
+
require_once(DUPLICATOR_PLUGIN_PATH . '/ctrls/ctrl.base.php');
|
14 |
+
require_once(DUPLICATOR_PLUGIN_PATH . '/classes/utilities/class.u.scancheck.php');
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Controller for Tools
|
18 |
+
* @package Duplicator\ctrls
|
19 |
+
*/
|
20 |
+
class DUP_CTRL_Tools extends DUP_CTRL_Base
|
21 |
+
{
|
22 |
+
/**
|
23 |
+
* Init this instance of the object
|
24 |
+
*/
|
25 |
+
public function __construct()
|
26 |
+
{
|
27 |
+
add_action('wp_ajax_DUP_CTRL_Tools_runScanValidator', array($this, 'runScanValidator'));
|
28 |
+
add_action('wp_ajax_DUP_CTRL_Tools_getTraceLog', array($this, 'getTraceLog'));
|
29 |
+
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
*
|
33 |
+
* @return boolean
|
34 |
+
*/
|
35 |
+
public static function isToolPage()
|
36 |
+
{
|
37 |
+
return ControllersManager::isCurrentPage('duplicator-tools');
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
*
|
42 |
+
* @return boolean
|
43 |
+
*/
|
44 |
+
public static function isDiagnosticPage()
|
45 |
+
{
|
46 |
+
return ControllersManager::isCurrentPage('duplicator-tools', 'diagnostics');
|
47 |
+
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Return diagnostic URL
|
51 |
+
*
|
52 |
+
* @param bool $relative if true return relative URL else absolute
|
53 |
+
*
|
54 |
+
* @return string
|
55 |
+
*/
|
56 |
+
public static function getDiagnosticURL($relative = true)
|
57 |
+
{
|
58 |
+
return ControllersManager::getMenuLink(
|
59 |
+
'duplicator-tools',
|
60 |
+
'diagnostics',
|
61 |
+
'',
|
62 |
+
array(),
|
63 |
+
$relative
|
64 |
+
);
|
65 |
+
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Return clean installer files action URL
|
69 |
+
*
|
70 |
+
* @param bool $relative if true return relative URL else absolute
|
71 |
+
*
|
72 |
+
* @return string
|
73 |
+
*/
|
74 |
+
public static function getCleanFilesAcrtionUrl($relative = true)
|
75 |
+
{
|
76 |
+
return ControllersManager::getMenuLink(
|
77 |
+
'duplicator-tools',
|
78 |
+
'diagnostics',
|
79 |
+
'',
|
80 |
+
array(
|
81 |
+
'action' => 'installer',
|
82 |
+
'_wpnonce' => wp_create_nonce('duplicator_cleanup_page')
|
83 |
+
),
|
84 |
+
$relative
|
85 |
+
);
|
86 |
+
}
|
87 |
+
|
88 |
+
/**
|
89 |
+
* Calls the ScanValidator and returns a JSON result
|
90 |
+
*
|
91 |
+
* @notes: Testing = /wp-admin/admin-ajax.php?action=DUP_CTRL_Tools_runScanValidator
|
92 |
+
*/
|
93 |
+
public function runScanValidator()
|
94 |
+
{
|
95 |
+
DUP_Handler::init_error_handler();
|
96 |
+
check_ajax_referer('DUP_CTRL_Tools_runScanValidator', 'nonce');
|
97 |
+
@set_time_limit(0);
|
98 |
+
$isValid = true;
|
99 |
+
$inputData = filter_input_array(INPUT_POST, array(
|
100 |
+
'recursive_scan' => array(
|
101 |
+
'filter' => FILTER_VALIDATE_BOOLEAN,
|
102 |
+
'flags' => FILTER_NULL_ON_FAILURE
|
103 |
+
)
|
104 |
+
));
|
105 |
+
if (is_null($inputData['recursive_scan'])) {
|
106 |
+
$isValid = false;
|
107 |
+
}
|
108 |
+
|
109 |
+
$result = new DUP_CTRL_Result($this);
|
110 |
+
try {
|
111 |
+
DUP_Util::hasCapability('export', DUP_Util::SECURE_ISSUE_THROW);
|
112 |
+
if (!$isValid) {
|
113 |
+
throw new Exception(__('Invalid Request.', 'duplicator'));
|
114 |
+
}
|
115 |
+
//CONTROLLER LOGIC
|
116 |
+
$path = duplicator_get_abs_path();
|
117 |
+
if (!is_dir($path)) {
|
118 |
+
throw new Exception("Invalid directory provided '{$path}'!");
|
119 |
+
}
|
120 |
+
|
121 |
+
$scanner = new DUP_ScanCheck();
|
122 |
+
$scanner->recursion = $inputData['recursive_scan'];
|
123 |
+
$payload = $scanner->run($path);
|
124 |
+
//RETURN RESULT
|
125 |
+
$test = ($payload->fileCount > 0)
|
126 |
+
? DUP_CTRL_Status::SUCCESS
|
127 |
+
: DUP_CTRL_Status::FAILED;
|
128 |
+
$result->process($payload, $test);
|
129 |
+
} catch (Exception $exc) {
|
130 |
+
$result->processError($exc);
|
131 |
+
}
|
132 |
+
}
|
133 |
+
|
134 |
+
public function getTraceLog()
|
135 |
+
{
|
136 |
+
DUP_Log::Trace("enter");
|
137 |
+
check_ajax_referer('DUP_CTRL_Tools_getTraceLog', 'nonce');
|
138 |
+
Dup_Util::hasCapability('export');
|
139 |
+
$file_path = DUP_Log::GetTraceFilepath();
|
140 |
+
$backup_path = DUP_Log::GetBackupTraceFilepath();
|
141 |
+
$zip_path = DUP_Settings::getSsdirPath() . "/" . DUPLICATOR_ZIPPED_LOG_FILENAME;
|
142 |
+
$zipped = DUP_Zip_U::zipFile($file_path, $zip_path, true, null, true);
|
143 |
+
if ($zipped && file_exists($backup_path)) {
|
144 |
+
$zipped = DUP_Zip_U::zipFile($backup_path, $zip_path, false, null, true);
|
145 |
+
}
|
146 |
+
|
147 |
+
header("Pragma: public");
|
148 |
+
header("Expires: 0");
|
149 |
+
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
|
150 |
+
header("Cache-Control: private", false);
|
151 |
+
header("Content-Transfer-Encoding: binary");
|
152 |
+
$fp = fopen($zip_path, 'rb');
|
153 |
+
if (($fp !== false) && $zipped) {
|
154 |
+
$zip_filename = basename($zip_path);
|
155 |
+
header("Content-Type: application/octet-stream");
|
156 |
+
header("Content-Disposition: attachment; filename=\"$zip_filename\";");
|
157 |
+
// required or large files wont work
|
158 |
+
if (ob_get_length()) {
|
159 |
+
ob_end_clean();
|
160 |
+
}
|
161 |
+
|
162 |
+
DUP_Log::trace("streaming $zip_path");
|
163 |
+
if (fpassthru($fp) === false) {
|
164 |
+
DUP_Log::trace("Error with fpassthru for $zip_path");
|
165 |
+
}
|
166 |
+
|
167 |
+
fclose($fp);
|
168 |
+
@unlink($zip_path);
|
169 |
+
} else {
|
170 |
+
header("Content-Type: text/plain");
|
171 |
+
header("Content-Disposition: attachment; filename=\"error.txt\";");
|
172 |
+
if ($zipped === false) {
|
173 |
+
$message = "Couldn't create zip file.";
|
174 |
+
} else {
|
175 |
+
$message = "Couldn't open $file_path.";
|
176 |
+
}
|
177 |
+
DUP_Log::trace($message);
|
178 |
+
echo esc_html($message);
|
179 |
+
}
|
180 |
+
|
181 |
+
exit;
|
182 |
+
}
|
183 |
+
}
|
ctrls/ctrl.ui.php
CHANGED
@@ -1,41 +1,43 @@
|
|
1 |
<?php
|
|
|
2 |
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
3 |
// Exit if accessed directly
|
4 |
-
if (! defined('DUPLICATOR_VERSION'))
|
|
|
|
|
5 |
|
6 |
-
require_once(DUPLICATOR_PLUGIN_PATH . '/ctrls/ctrl.base.php');
|
7 |
require_once(DUPLICATOR_PLUGIN_PATH . '/classes/ui/class.ui.viewstate.php');
|
8 |
|
9 |
/**
|
10 |
-
* Controller for Tools
|
11 |
* @package Duplicator\ctrls
|
12 |
*/
|
13 |
class DUP_CTRL_UI extends DUP_CTRL_Base
|
14 |
-
{
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
}
|
20 |
|
21 |
|
22 |
-
|
23 |
* Calls the SaveViewState and returns a JSON result
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
*/
|
40 |
public function SaveViewState()
|
41 |
{
|
@@ -133,33 +135,30 @@ class DUP_CTRL_UI extends DUP_CTRL_Base
|
|
133 |
$result->processError($exc);
|
134 |
}
|
135 |
}
|
136 |
-
|
137 |
-
|
138 |
* Returns a JSON list of all saved view state items
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
*/
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
$result->processError($exc);
|
163 |
-
}
|
164 |
-
}
|
165 |
}
|
1 |
<?php
|
2 |
+
|
3 |
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
4 |
// Exit if accessed directly
|
5 |
+
if (! defined('DUPLICATOR_VERSION')) {
|
6 |
+
exit;
|
7 |
+
}
|
8 |
|
9 |
+
require_once(DUPLICATOR_PLUGIN_PATH . '/ctrls/ctrl.base.php');
|
10 |
require_once(DUPLICATOR_PLUGIN_PATH . '/classes/ui/class.ui.viewstate.php');
|
11 |
|
12 |
/**
|
13 |
+
* Controller for Tools
|
14 |
* @package Duplicator\ctrls
|
15 |
*/
|
16 |
class DUP_CTRL_UI extends DUP_CTRL_Base
|
17 |
+
{
|
18 |
+
public function __construct()
|
19 |
+
{
|
20 |
+
add_action('wp_ajax_DUP_CTRL_UI_SaveViewState', array($this, 'SaveViewState'));
|
21 |
+
}
|
|
|
22 |
|
23 |
|
24 |
+
/**
|
25 |
* Calls the SaveViewState and returns a JSON result
|
26 |
+
*
|
27 |
+
* @param string $_POST['key'] A unique key that identifies the state of the UI element
|
28 |
+
* @param bool $_POST['value'] The value to store for the state of the UI element
|
29 |
+
*
|
30 |
+
* @notes: Testing: See Testing Interface
|
31 |
+
* URL = /wp-admin/admin-ajax.php?action=DUP_CTRL_UI_SaveViewState
|
32 |
+
*
|
33 |
+
* <code>
|
34 |
+
* //JavaScript Ajax Request
|
35 |
+
* Duplicator.UI.SaveViewState('dup-pack-archive-panel', 1);
|
36 |
+
*
|
37 |
+
* //Call PHP Code
|
38 |
+
* $view_state = DUP_UI_ViewState::getValue('dup-pack-archive-panel');
|
39 |
+
* $ui_css_archive = ($view_state == 1) ? 'display:block' : 'display:none';
|
40 |
+
* </code>
|
41 |
*/
|
42 |
public function SaveViewState()
|
43 |
{
|
135 |
$result->processError($exc);
|
136 |
}
|
137 |
}
|
138 |
+
|
139 |
+
/**
|
140 |
* Returns a JSON list of all saved view state items
|
141 |
+
*
|
142 |
+
*
|
143 |
+
* <code>
|
144 |
+
* See SaveViewState()
|
145 |
+
* </code>
|
146 |
*/
|
147 |
+
public function GetViewStateList()
|
148 |
+
{
|
149 |
+
$result = new DUP_CTRL_Result($this);
|
150 |
+
|
151 |
+
try {
|
152 |
+
//CONTROLLER LOGIC
|
153 |
+
$payload = DUP_UI_ViewState::getArray();
|
154 |
+
|
155 |
+
//RETURN RESULT
|
156 |
+
$test = (is_array($payload) && count($payload))
|
157 |
+
? DUP_CTRL_Status::SUCCESS
|
158 |
+
: DUP_CTRL_Status::FAILED;
|
159 |
+
return $result->process($payload, $test);
|
160 |
+
} catch (Exception $exc) {
|
161 |
+
$result->processError($exc);
|
162 |
+
}
|
163 |
+
}
|
|
|
|
|
|
|
164 |
}
|
ctrls/index.php
CHANGED
@@ -1,2 +1,3 @@
|
|
1 |
<?php
|
2 |
-
|
|
1 |
<?php
|
2 |
+
|
3 |
+
//silent
|
deactivation.php
CHANGED
@@ -1,440 +1,434 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
|
4 |
-
*
|
5 |
-
*
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
if ('plugins.php' == $hook_suffix && !defined('DOING_AJAX')) {
|
24 |
-
duplicator_add_deactivation_feedback_dialog_box();
|
25 |
-
}
|
26 |
-
}
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
/**
|
31 |
-
* Displays a confirmation and feedback dialog box when the user clicks on the "Deactivate" link on the plugins
|
32 |
-
* page.
|
33 |
-
*
|
34 |
-
* @since 2.1.3
|
35 |
-
*/
|
36 |
-
if (!function_exists('duplicator_add_deactivation_feedback_dialog_box')) {
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
$
|
43 |
-
|
44 |
-
|
45 |
-
$
|
46 |
-
$
|
47 |
-
|
48 |
-
$
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
'
|
53 |
-
'
|
54 |
-
'
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
'
|
60 |
-
'
|
61 |
-
'
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
'
|
67 |
-
'
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
'
|
73 |
-
'
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
'
|
79 |
-
'
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
'
|
85 |
-
'
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
'
|
92 |
-
'
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
)
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
'<
|
126 |
-
+ '
|
127 |
-
+ '
|
128 |
-
+ '
|
129 |
-
+ '
|
130 |
-
+
|
131 |
-
+ '
|
132 |
-
+ '
|
133 |
-
+ '
|
134 |
-
+ '
|
135 |
-
+ '
|
136 |
-
+ '
|
137 |
-
+ '
|
138 |
-
+ '
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
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 |
-
|
184 |
-
if (
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
'
|
230 |
-
'
|
231 |
-
'
|
232 |
-
},
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
$
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
}
|
332 |
-
|
333 |
-
function
|
334 |
-
$modal.find('.duplicator-modal-button-deactivate').
|
335 |
-
}
|
336 |
-
|
337 |
-
function
|
338 |
-
$modal.find('.duplicator-modal-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
'
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
'
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
)
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
}
|
436 |
-
exit;
|
437 |
-
}
|
438 |
-
}
|
439 |
-
|
440 |
-
add_action('wp_ajax_duplicator_submit_uninstall_reason_action', 'duplicator_submit_uninstall_reason_action');
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Standard: PSR-2
|
5 |
+
* @link http://www.php-fig.org/psr/psr-2 Full Documentation
|
6 |
+
*
|
7 |
+
*/
|
8 |
+
|
9 |
+
use Duplicator\Libs\Snap\SnapJson;
|
10 |
+
|
11 |
+
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
12 |
+
function duplicator_deactivation_enqueue_scripts($hook)
|
13 |
+
{
|
14 |
+
if ('plugins.php' == $hook && !defined('DOING_AJAX')) {
|
15 |
+
wp_enqueue_style('duplicator-deactivation-modal', DUPLICATOR_PLUGIN_URL . 'assets/css/modal.css', array(), '1.0.0');
|
16 |
+
}
|
17 |
+
}
|
18 |
+
add_action('admin_enqueue_scripts', 'duplicator_deactivation_enqueue_scripts');
|
19 |
+
if (!function_exists('duplicator_plugins_admin_footer')) {
|
20 |
+
function duplicator_plugins_admin_footer()
|
21 |
+
{
|
22 |
+
global $hook_suffix;
|
23 |
+
if ('plugins.php' == $hook_suffix && !defined('DOING_AJAX')) {
|
24 |
+
duplicator_add_deactivation_feedback_dialog_box();
|
25 |
+
}
|
26 |
+
}
|
27 |
+
|
28 |
+
}
|
29 |
+
add_action('admin_footer', 'duplicator_plugins_admin_footer');
|
30 |
+
/**
|
31 |
+
* Displays a confirmation and feedback dialog box when the user clicks on the "Deactivate" link on the plugins
|
32 |
+
* page.
|
33 |
+
*
|
34 |
+
* @since 2.1.3
|
35 |
+
*/
|
36 |
+
if (!function_exists('duplicator_add_deactivation_feedback_dialog_box')) {
|
37 |
+
function duplicator_add_deactivation_feedback_dialog_box()
|
38 |
+
{
|
39 |
+
$basename = 'duplicator/duplicator.php';
|
40 |
+
/*
|
41 |
+
$slug = dirname( $basename );
|
42 |
+
$plugin_id = sanitize_title( $plugin_data['Name'] );
|
43 |
+
*/
|
44 |
+
$slug = 'duplicator';
|
45 |
+
$plugin_id = 'duplicator';
|
46 |
+
$contact_support_template = __('Need help? We are ready to answer your questions.', 'duplicator') .
|
47 |
+
' <a href="https://snapcreek.com/ticket/" target="_blank">' . __('Contact Support', 'duplicator') . '</a>';
|
48 |
+
$reasons = array(
|
49 |
+
array(
|
50 |
+
'id' => 'NOT_WORKING',
|
51 |
+
'text' => __("It's not working on my server.", 'duplicator'),
|
52 |
+
'input_type' => 'textarea',
|
53 |
+
'input_placeholder' => __("Kindly share what didn't work so we can fix it in future updates...", 'duplicator'),
|
54 |
+
'internal_message' => $contact_support_template
|
55 |
+
),
|
56 |
+
array(
|
57 |
+
'id' => 'CONFUSING_TO_UNDERSTAND',
|
58 |
+
'text' => __("It's too confusing to understand.", 'duplicator'),
|
59 |
+
'input_type' => 'textarea',
|
60 |
+
'input_placeholder' => __('Please tell us what is not clear so that we can improve it.', 'duplicator'),
|
61 |
+
'internal_message' => $contact_support_template
|
62 |
+
),
|
63 |
+
array(
|
64 |
+
'id' => 'FOUND_A_DIFFERENT_PLUGIN',
|
65 |
+
'text' => __('I found a different plugin that I like better.', 'duplicator'),
|
66 |
+
'input_type' => 'textfield',
|
67 |
+
'input_placeholder' => __("What's the plugin name?", 'duplicator')
|
68 |
+
),
|
69 |
+
array(
|
70 |
+
'id' => 'NOT_DO_WHAT_I_NEED',
|
71 |
+
'text' => __("It does not do what I need.", 'duplicator'),
|
72 |
+
'input_type' => 'textarea',
|
73 |
+
'input_placeholder' => __('What does it need to do?', 'duplicator')
|
74 |
+
),
|
75 |
+
array(
|
76 |
+
'id' => 'TEMPORARY_DEACTIVATION',
|
77 |
+
'text' => __("It's a temporary deactivation, I use the plugin all the time.", 'duplicator'),
|
78 |
+
'input_type' => '',
|
79 |
+
'input_placeholder' => ''
|
80 |
+
),
|
81 |
+
array(
|
82 |
+
'id' => 'SWITCHING_PRO_VERSION',
|
83 |
+
'text' => sprintf(__("I'm switching over to the %s", 'duplicator'), '<a href="https://snapcreek.com/duplicator/" target="_blank">' . __('Pro version', 'duplicator') . '</a>'),
|
84 |
+
'input_type' => '',
|
85 |
+
'input_placeholder' => ''
|
86 |
+
),
|
87 |
+
/*
|
88 |
+
array(
|
89 |
+
'id' => 'OTHER',
|
90 |
+
'text' => __('Other', 'duplicator'),
|
91 |
+
'input_type' => 'textarea',
|
92 |
+
'input_placeholder' => __('Please tell us the reason so we can improve it.', 'duplicator')
|
93 |
+
)
|
94 |
+
*/
|
95 |
+
);
|
96 |
+
$reasons_list_items_html = '';
|
97 |
+
foreach ($reasons as $reason) {
|
98 |
+
$list_item_classes = 'duplicator-modal-reason' . (!empty($reason['input_type']) ? ' has-input' : '' );
|
99 |
+
if (!empty($reason['internal_message'])) {
|
100 |
+
$list_item_classes .= ' has-internal-message';
|
101 |
+
$reason_internal_message = $reason['internal_message'];
|
102 |
+
} else {
|
103 |
+
$reason_internal_message = '';
|
104 |
+
}
|
105 |
+
|
106 |
+
$reasons_list_items_html .= '<li class="' . $list_item_classes . '" data-input-type="' . $reason['input_type'] . '" data-input-placeholder="' . $reason['input_placeholder'] . '">
|
107 |
+
<label>
|
108 |
+
<span>
|
109 |
+
<input type="radio" name="selected-reason" value="' . $reason['id'] . '"/>
|
110 |
+
</span>
|
111 |
+
<span>' . $reason['text'] . '</span>
|
112 |
+
</label>
|
113 |
+
<div class="duplicator-modal-internal-message">' . $reason_internal_message . '</div>
|
114 |
+
</li>';
|
115 |
+
}
|
116 |
+
?>
|
117 |
+
<script type="text/javascript">
|
118 |
+
(function ($) {
|
119 |
+
var modalHtml =
|
120 |
+
'<div class="duplicator-modal duplicator-modal-deactivation-feedback">'
|
121 |
+
+ ' <div class="duplicator-modal-dialog">'
|
122 |
+
+ ' <div class="duplicator-modal-body">'
|
123 |
+
+ ' <h2><?php _e('Quick Feedback', 'duplicator'); ?></h2>'
|
124 |
+
+ ' <div class="duplicator-modal-panel active"><p><?php _e('If you have a moment, please let us know why you are deactivating', 'duplicator'); ?>:</p>'
|
125 |
+
+ '<ul>' + <?php echo SnapJson::jsonEncode($reasons_list_items_html); ?> + '</ul>'
|
126 |
+
+ ' </div>'
|
127 |
+
+ ' </div>'
|
128 |
+
+ ' <div class="duplicator-modal-footer">'
|
129 |
+
+ ' <div>'
|
130 |
+
+ ' <a href="#" class="button button-secondary duplicator-modal-button-close"><?php _e('Cancel', 'duplicator'); ?></a>'
|
131 |
+
+ ' <a href="#" class="button button-secondary duplicator-modal-button-skip"><?php _e('Skip & Deactivate', 'duplicator'); ?></a>'
|
132 |
+
+ ' <a href="#" class="button button-primary duplicator-modal-button-deactivate" disabled="disabled" ><?php _e('Send & Deactivate', 'duplicator'); ?></a>'
|
133 |
+
+ ' </div>'
|
134 |
+
+ ' <div class="clear"></div>'
|
135 |
+
+ ' <div><small class="duplicator-modal-resp-msg" ><i><?php _e('Your response is sent anonymously.', 'duplicator'); ?></i></small></div>'
|
136 |
+
+ ' </div>'
|
137 |
+
+ ' </div>'
|
138 |
+
+ '</div>',
|
139 |
+
$modal = $(modalHtml),
|
140 |
+
$deactivateLink = $('#the-list .active[data-plugin="<?php echo $basename; ?>"] .deactivate a'),
|
141 |
+
selectedReasonID = false;
|
142 |
+
|
143 |
+
/* WP added data-plugin attr after 4.5 version/ In prev version was id attr */
|
144 |
+
if (0 == $deactivateLink.length)
|
145 |
+
$deactivateLink = $('#the-list .active#<?php echo $plugin_id; ?> .deactivate a');
|
146 |
+
|
147 |
+
$modal.appendTo($('body'));
|
148 |
+
|
149 |
+
DuplicatorModalRegisterEventHandlers();
|
150 |
+
|
151 |
+
function DuplicatorModalRegisterEventHandlers() {
|
152 |
+
$deactivateLink.click(function (evt) {
|
153 |
+
evt.preventDefault();
|
154 |
+
|
155 |
+
/* Display the dialog box.*/
|
156 |
+
DuplicatorModalReset();
|
157 |
+
$modal.addClass('active');
|
158 |
+
$('body').addClass('has-duplicator-modal');
|
159 |
+
});
|
160 |
+
|
161 |
+
$modal.on('input propertychange', '.duplicator-modal-reason-input input', function () {
|
162 |
+
if (!DuplicatorModalIsReasonSelected('OTHER')) {
|
163 |
+
return;
|
164 |
+
}
|
165 |
+
|
166 |
+
var reason = $(this).val().trim();
|
167 |
+
|
168 |
+
/* If reason is not empty, remove the error-message class of the message container to change the message color back to default. */
|
169 |
+
if (reason.length > 0) {
|
170 |
+
$modal.find('.message').removeClass('error-message');
|
171 |
+
DuplicatorModalEnableDeactivateButton();
|
172 |
+
}
|
173 |
+
});
|
174 |
+
|
175 |
+
$modal.on('blur', '.duplicator-modal-reason-input input', function () {
|
176 |
+
var $userReason = $(this);
|
177 |
+
|
178 |
+
setTimeout(function () {
|
179 |
+
if (!DuplicatorModalIsReasonSelected('OTHER')) {
|
180 |
+
return;
|
181 |
+
}
|
182 |
+
|
183 |
+
/* If reason is empty, add the error-message class to the message container to change the message color to red. */
|
184 |
+
if (0 === $userReason.val().trim().length) {
|
185 |
+
$modal.find('.message').addClass('error-message');
|
186 |
+
DuplicatorModalDisableDeactivateButton();
|
187 |
+
}
|
188 |
+
}, 150);
|
189 |
+
});
|
190 |
+
|
191 |
+
$modal.on('click', '.duplicator-modal-footer .button', function (evt) {
|
192 |
+
evt.preventDefault();
|
193 |
+
|
194 |
+
if ($(this).hasClass('disabled')) {
|
195 |
+
return;
|
196 |
+
}
|
197 |
+
|
198 |
+
var _parent = $(this).parents('.duplicator-modal:first'),
|
199 |
+
_this = $(this);
|
200 |
+
|
201 |
+
if (_this.hasClass('allow-deactivate')) {
|
202 |
+
var $radio = $modal.find('input[type="radio"]:checked');
|
203 |
+
|
204 |
+
if (0 === $radio.length) {
|
205 |
+
/* If no selected reason, just deactivate the plugin. */
|
206 |
+
window.location.href = $deactivateLink.attr('href');
|
207 |
+
return;
|
208 |
+
}
|
209 |
+
|
210 |
+
var $selected_reason = $radio.parents('li:first'),
|
211 |
+
$input = $selected_reason.find('textarea, input[type="text"]'),
|
212 |
+
userReason = (0 !== $input.length) ? $input.val().trim() : '';
|
213 |
+
|
214 |
+
if (DuplicatorModalIsReasonSelected('OTHER') && '' === userReason) {
|
215 |
+
return;
|
216 |
+
}
|
217 |
+
|
218 |
+
$.ajax({
|
219 |
+
url: ajaxurl,
|
220 |
+
method: 'POST',
|
221 |
+
data: {
|
222 |
+
'action': 'duplicator_submit_uninstall_reason_action',
|
223 |
+
'plugin': '<?php echo $basename; ?>',
|
224 |
+
'reason_id': $radio.val(),
|
225 |
+
'reason_info': userReason,
|
226 |
+
'duplicator_ajax_nonce': '<?php echo wp_create_nonce('duplicator_ajax_nonce'); ?>'
|
227 |
+
},
|
228 |
+
beforeSend: function () {
|
229 |
+
_parent.find('.duplicator-modal-footer .button').addClass('disabled');
|
230 |
+
// _parent.find( '.duplicator-modal-footer .button-secondary' ).text( '<?php _e('Processing', 'duplicator'); ?>' + '...' );
|
231 |
+
_parent.find('.duplicator-modal-footer .duplicator-modal-button-deactivate').text('<?php _e('Processing', 'duplicator'); ?>' + '...');
|
232 |
+
},
|
233 |
+
complete: function (message) {
|
234 |
+
/* Do not show the dialog box, deactivate the plugin. */
|
235 |
+
window.location.href = $deactivateLink.attr('href');
|
236 |
+
}
|
237 |
+
});
|
238 |
+
} else if (_this.hasClass('duplicator-modal-button-deactivate')) {
|
239 |
+
/* Change the Deactivate button's text and show the reasons panel. */
|
240 |
+
_parent.find('.duplicator-modal-button-deactivate').addClass('allow-deactivate');
|
241 |
+
DuplicatorModalShowPanel();
|
242 |
+
} else if (_this.hasClass('duplicator-modal-button-skip')) {
|
243 |
+
window.location.href = $deactivateLink.attr('href');
|
244 |
+
return;
|
245 |
+
}
|
246 |
+
});
|
247 |
+
|
248 |
+
$modal.on('click', 'input[type="radio"]', function () {
|
249 |
+
var $selectedReasonOption = $(this);
|
250 |
+
|
251 |
+
/* If the selection has not changed, do not proceed. */
|
252 |
+
if (selectedReasonID === $selectedReasonOption.val())
|
253 |
+
return;
|
254 |
+
|
255 |
+
selectedReasonID = $selectedReasonOption.val();
|
256 |
+
|
257 |
+
var _parent = $(this).parents('li:first');
|
258 |
+
|
259 |
+
$modal.find('.duplicator-modal-reason-input').remove();
|
260 |
+
$modal.find('.duplicator-modal-internal-message').hide();
|
261 |
+
$modal.find('.duplicator-modal-button-deactivate').removeAttr( 'disabled' );
|
262 |
+
//$modal.find('.duplicator-modal-button-skip').css('display', 'inline-block');
|
263 |
+
$modal.find('.duplicator-modal-resp-msg').show();
|
264 |
+
|
265 |
+
DuplicatorModalEnableDeactivateButton();
|
266 |
+
|
267 |
+
if (_parent.hasClass('has-internal-message')) {
|
268 |
+
_parent.find('.duplicator-modal-internal-message').show();
|
269 |
+
}
|
270 |
+
|
271 |
+
if (_parent.hasClass('has-input')) {
|
272 |
+
var reasonInputHtml = '<div class="duplicator-modal-reason-input"><span class="message"></span>' +
|
273 |
+
(('textfield' === _parent.data('input-type')) ? '<input type="text" />' : '<textarea rows="5" maxlength="200"></textarea>') + '</div>';
|
274 |
+
|
275 |
+
_parent.append($(reasonInputHtml));
|
276 |
+
_parent.find('input, textarea').attr('placeholder', _parent.data('input-placeholder')).focus();
|
277 |
+
|
278 |
+
/*if (DuplicatorModalIsReasonSelected('OTHER')) {
|
279 |
+
$modal.find('.message').text('<?php _e('Please tell us the reason so we can improve it.', 'duplicator'); ?>').show();
|
280 |
+
DuplicatorModalDisableDeactivateButton();
|
281 |
+
}*/
|
282 |
+
}
|
283 |
+
});
|
284 |
+
|
285 |
+
/* If the user has clicked outside the window, cancel it. */
|
286 |
+
$modal.on('click', function (evt) {
|
287 |
+
var $target = $(evt.target);
|
288 |
+
|
289 |
+
/* If the user has clicked anywhere in the modal dialog, just return. */
|
290 |
+
if ($target.hasClass('duplicator-modal-body') || $target.hasClass('duplicator-modal-footer')) {
|
291 |
+
return;
|
292 |
+
}
|
293 |
+
|
294 |
+
/* If the user has not clicked the close button and the clicked element is inside the modal dialog, just return. */
|
295 |
+
if (!$target.hasClass('duplicator-modal-button-close') && ($target.parents('.duplicator-modal-body').length > 0 || $target.parents('.duplicator-modal-footer').length > 0)) {
|
296 |
+
return;
|
297 |
+
}
|
298 |
+
|
299 |
+
/* Close the modal dialog */
|
300 |
+
$modal.removeClass('active');
|
301 |
+
$('body').removeClass('has-duplicator-modal');
|
302 |
+
|
303 |
+
return false;
|
304 |
+
});
|
305 |
+
}
|
306 |
+
|
307 |
+
function DuplicatorModalIsReasonSelected(reasonID) {
|
308 |
+
/* Get the selected radio input element.*/
|
309 |
+
return (reasonID == $modal.find('input[type="radio"]:checked').val());
|
310 |
+
}
|
311 |
+
|
312 |
+
function DuplicatorModalReset() {
|
313 |
+
selectedReasonID = false;
|
314 |
+
|
315 |
+
DuplicatorModalEnableDeactivateButton();
|
316 |
+
|
317 |
+
/* Uncheck all radio buttons.*/
|
318 |
+
$modal.find('input[type="radio"]').prop('checked', false);
|
319 |
+
|
320 |
+
/* Remove all input fields ( textfield, textarea ).*/
|
321 |
+
$modal.find('.duplicator-modal-reason-input').remove();
|
322 |
+
|
323 |
+
$modal.find('.message').hide();
|
324 |
+
var $deactivateButton = $modal.find('.duplicator-modal-button-deactivate');
|
325 |
+
$deactivateButton.addClass('allow-deactivate');
|
326 |
+
DuplicatorModalShowPanel();
|
327 |
+
}
|
328 |
+
|
329 |
+
function DuplicatorModalEnableDeactivateButton() {
|
330 |
+
$modal.find('.duplicator-modal-button-deactivate').removeClass('disabled');
|
331 |
+
}
|
332 |
+
|
333 |
+
function DuplicatorModalDisableDeactivateButton() {
|
334 |
+
$modal.find('.duplicator-modal-button-deactivate').addClass('disabled');
|
335 |
+
}
|
336 |
+
|
337 |
+
function DuplicatorModalShowPanel() {
|
338 |
+
$modal.find('.duplicator-modal-panel').addClass('active');
|
339 |
+
/* Update the deactivate button's text */
|
340 |
+
//$modal.find('.duplicator-modal-button-deactivate').text('<?php _e('Skip & Deactivate', 'duplicator'); ?>');
|
341 |
+
//$modal.find('.duplicator-modal-button-skip, .duplicator-modal-resp-msg').css('display', 'none');
|
342 |
+
}
|
343 |
+
})(jQuery);
|
344 |
+
</script>
|
345 |
+
<?php
|
346 |
+
}
|
347 |
+
|
348 |
+
}
|
349 |
+
|
350 |
+
/**
|
351 |
+
* Called after the user has submitted his reason for deactivating the plugin.
|
352 |
+
*
|
353 |
+
*/
|
354 |
+
if (!function_exists('duplicator_submit_uninstall_reason_action')) {
|
355 |
+
function duplicator_submit_uninstall_reason_action()
|
356 |
+
{
|
357 |
+
DUP_Handler::init_error_handler();
|
358 |
+
$isValid = true;
|
359 |
+
$inputData = filter_input_array(INPUT_POST, array(
|
360 |
+
'reason_id' => array(
|
361 |
+
'filter' => FILTER_UNSAFE_RAW,
|
362 |
+
'flags' => FILTER_REQUIRE_SCALAR,
|
363 |
+
'options' => array(
|
364 |
+
'default' => false
|
365 |
+
)
|
366 |
+
),
|
367 |
+
'plugin' => array(
|
368 |
+
'filter' => FILTER_UNSAFE_RAW,
|
369 |
+
'flags' => FILTER_REQUIRE_SCALAR,
|
370 |
+
'options' => array(
|
371 |
+
'default' => false
|
372 |
+
)
|
373 |
+
),
|
374 |
+
'reason_info' => array(
|
375 |
+
'filter' => FILTER_UNSAFE_RAW,
|
376 |
+
'flags' => FILTER_REQUIRE_SCALAR,
|
377 |
+
'options' => array(
|
378 |
+
'default' => ''
|
379 |
+
)
|
380 |
+
)
|
381 |
+
));
|
382 |
+
$reason_id = $inputData['reason_id'];
|
383 |
+
$basename = $inputData['plugin'];
|
384 |
+
if (!$reason_id || !$basename) {
|
385 |
+
$isValid = false;
|
386 |
+
}
|
387 |
+
|
388 |
+
try {
|
389 |
+
if (!wp_verify_nonce($_POST['duplicator_ajax_nonce'], 'duplicator_ajax_nonce')) {
|
390 |
+
throw new Exception(__('Security issue', 'duplicator'));
|
391 |
+
}
|
392 |
+
|
393 |
+
DUP_Util::hasCapability('export', DUP_Util::SECURE_ISSUE_THROW);
|
394 |
+
if (!$isValid) {
|
395 |
+
throw new Exception(__('Invalid Request.', 'duplicator'));
|
396 |
+
}
|
397 |
+
|
398 |
+
$reason_info = isset($_REQUEST['reason_info']) ? stripcslashes(esc_html($_REQUEST['reason_info'])) : '';
|
399 |
+
if (!empty($reason_info)) {
|
400 |
+
$reason_info = substr($reason_info, 0, 255);
|
401 |
+
}
|
402 |
+
|
403 |
+
$options = array(
|
404 |
+
'product' => $basename,
|
405 |
+
'reason_id' => $reason_id,
|
406 |
+
'reason_info' => $reason_info,
|
407 |
+
);
|
408 |
+
|
409 |
+
/* send data */
|
410 |
+
$raw_response = wp_remote_post(
|
411 |
+
'https://snapcreekanalytics.com/wp-content/plugins/duplicator-statistics-plugin/deactivation-feedback/',
|
412 |
+
array(
|
413 |
+
'method' => 'POST',
|
414 |
+
'body' => $options,
|
415 |
+
'timeout' => 15,
|
416 |
+
// 'sslverify' => FALSE
|
417 |
+
)
|
418 |
+
);
|
419 |
+
if (!is_wp_error($raw_response) && 200 == wp_remote_retrieve_response_code($raw_response)) {
|
420 |
+
echo 'done';
|
421 |
+
} else {
|
422 |
+
$error_msg = $raw_response->get_error_code() . ': ' . $raw_response->get_error_message();
|
423 |
+
error_log($error_msg);
|
424 |
+
throw new Exception($error_msg);
|
425 |
+
}
|
426 |
+
} catch (Exception $ex) {
|
427 |
+
echo $ex->getMessage();
|
428 |
+
}
|
429 |
+
exit;
|
430 |
+
}
|
431 |
+
|
432 |
+
}
|
433 |
+
|
434 |
+
add_action('wp_ajax_duplicator_submit_uninstall_reason_action', 'duplicator_submit_uninstall_reason_action');
|
|
|
|
|
|
|
|
|
|
|
|
define.php
CHANGED
@@ -1,109 +1,131 @@
|
|
1 |
<?php
|
|
|
2 |
//Prevent directly browsing to the file
|
3 |
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
4 |
|
5 |
|
6 |
-
if (function_exists('plugin_dir_url'))
|
7 |
-
|
8 |
-
define('
|
9 |
-
|
10 |
-
define('
|
11 |
-
|
12 |
-
|
13 |
/* Paths should ALWAYS read "/"
|
14 |
uni: /home/path/file.txt
|
15 |
win: D:/home/path/file.txt
|
16 |
SSDIR = SnapShot Directory */
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
64 |
'duplicator_ui_view_state',
|
65 |
'duplicator_package_active',
|
66 |
'duplicator_settings',
|
67 |
'duplicator_is_pro_enable_notice_dismissed'
|
68 |
);
|
69 |
-
$GLOBALS['DUPLICATOR_GLOBAL_FILE_FILTERS_ON'] = true;
|
70 |
-
$GLOBALS['DUPLICATOR_GLOBAL_FILE_FILTERS'] = array(
|
71 |
-
'error_log',
|
72 |
-
'error.log',
|
73 |
-
'debug_log',
|
74 |
-
'ws_ftp.log',
|
75 |
-
'dbcache',
|
76 |
-
'pgcache',
|
77 |
-
'objectcache',
|
78 |
-
'.DS_Store'
|
79 |
-
);
|
80 |
-
|
81 |
-
|
82 |
-
/* Used to flush a response every N items.
|
83 |
-
* Note: This value will cause the Zip file to double in size durning the creation process only*/
|
84 |
-
define("DUPLICATOR_ZIP_FLUSH_TRIGGER", 1000);
|
85 |
-
|
86 |
-
/* Let's setup few things to cover all PHP versions */
|
87 |
-
if(!defined('PHP_VERSION'))
|
88 |
-
{
|
89 |
-
define('PHP_VERSION', phpversion());
|
90 |
-
}
|
91 |
-
if (!defined('PHP_VERSION_ID')) {
|
92 |
-
$version = expl
|
1 |
<?php
|
2 |
+
|
3 |
//Prevent directly browsing to the file
|
4 |
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
5 |
|
6 |
|
7 |
+
if (function_exists('plugin_dir_url')) {
|
8 |
+
define('DUPLICATOR_VERSION', '1.5.0');
|
9 |
+
define('DUPLICATOR_VERSION_BUILD', '2022-08-28_19:00');
|
10 |
+
define('DUPLICATOR_PLUGIN_URL', plugin_dir_url(__FILE__));
|
11 |
+
define('DUPLICATOR_SITE_URL', get_site_url());
|
12 |
+
|
|
|
13 |
/* Paths should ALWAYS read "/"
|
14 |
uni: /home/path/file.txt
|
15 |
win: D:/home/path/file.txt
|
16 |
SSDIR = SnapShot Directory */
|
17 |
+
if (!defined('ABSPATH')) {
|
18 |
+
define('ABSPATH', dirname(__FILE__));
|
19 |
+
}
|
20 |
+
|
21 |
+
//PATH CONSTANTS
|
22 |
+
if (! defined('DUPLICATOR_WPROOTPATH')) {
|
23 |
+
define('DUPLICATOR_WPROOTPATH', str_replace('\\', '/', ABSPATH));
|
24 |
+
}
|
25 |
+
|
26 |
+
define('DUPLICATOR_PLUGIN_PATH', str_replace("\\", "/", plugin_dir_path(__FILE__)));
|
27 |
+
define('DUPLICATOR_ZIPPED_LOG_FILENAME', 'duplicator_lite_log.zip');
|
28 |
+
define('DUPLICATOR_INSTALL_PHP', 'installer.php');
|
29 |
+
define('DUPLICATOR_INSTALL_BAK', 'installer-backup.php');
|
30 |
+
define('DUPLICATOR_INSTALLER_HASH_PATTERN', '[a-z0-9][a-z0-9][a-z0-9][a-z0-9][a-z0-9][a-z0-9][a-z0-9]-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]');
|
31 |
+
define('DUPLICATOR_INSTALL_SITE_OVERWRITE_ON', true);
|
32 |
+
|
33 |
+
//GENERAL CONSTRAINTS
|
34 |
+
define('DUPLICATOR_PHP_MAX_MEMORY', 4294967296); // 4096MB
|
35 |
+
define('DUPLICATOR_DB_MAX_TIME', 5000);
|
36 |
+
define('DUPLICATOR_DB_EOF_MARKER', 'DUPLICATOR_MYSQLDUMP_EOF');
|
37 |
+
define("DUPLICATOR_DB_MYSQLDUMP_ERROR_CONTAINING_LINE_COUNT", 10);
|
38 |
+
define("DUPLICATOR_DB_MYSQLDUMP_ERROR_CHARS_IN_LINE_COUNT", 1000);
|
39 |
+
//SCANNER CONSTRAINTS
|
40 |
+
define('DUPLICATOR_SCAN_SIZE_DEFAULT', 157286400); //150MB
|
41 |
+
define('DUPLICATOR_SCAN_WARNFILESIZE', 3145728); //3MB
|
42 |
+
define('DUPLICATOR_SCAN_WARN_DIR_SIZE', 104857600);//100MB
|
43 |
+
|
44 |
+
define('DUPLICATOR_SCAN_CACHESIZE', 1048576); //1MB
|
45 |
+
define('DUPLICATOR_SCAN_DB_ALL_ROWS', 500000); //500k per DB
|
46 |
+
define('DUPLICATOR_SCAN_DB_ALL_SIZE', 52428800); //50MB DB
|
47 |
+
define('DUPLICATOR_SCAN_DB_TBL_ROWS', 100000); //100K rows per table
|
48 |
+
define('DUPLICATOR_SCAN_DB_TBL_SIZE', 10485760); //10MB Table
|
49 |
+
define('DUPLICATOR_SCAN_TIMEOUT', 150); //Seconds
|
50 |
+
define('DUPLICATOR_SCAN_MIN_WP', '4.7.0');
|
51 |
+
define('DUPLICATOR_MAX_DUPARCHIVE_SIZE', 524288000); // 500 GB
|
52 |
+
|
53 |
+
define('DUPLICATOR_TEMP_CLEANUP_SECONDS', 900); // 15 min = How many seconds to keep temp files around when delete is requested
|
54 |
+
define('DUPLICATOR_MAX_BUILD_RETRIES', 10); // Max times to try a part of progressive build work
|
55 |
+
define('DUPLICATOR_WEBCONFIG_ORIG_FILENAME', 'web.config.orig');
|
56 |
+
define("DUPLICATOR_INSTALLER_DIRECTORY", duplicator_get_abs_path() . '/dup-installer');
|
57 |
+
define('DUPLICATOR_MAX_LOG_SIZE', 400000); // The higher this is the more overhead
|
58 |
+
define('DUPLICATOR_ZIP_ARCHIVE_ADD_FROM_STR', false);
|
59 |
+
define('DUPLICATOR_DEACTIVATION_FEEDBACK', false);
|
60 |
+
define("DUPLICATOR_BUFFER_READ_WRITE_SIZE", 4377);
|
61 |
+
define("DUPLICATOR_ADMIN_NOTICES_USER_META_KEY", 'duplicator_admin_notices');
|
62 |
+
define("DUPLICATOR_FEEDBACK_NOTICE_SHOW_AFTER_NO_PACKAGE", 5);
|
63 |
+
|
64 |
+
define('DUPLICATOR_LOCAL_OVERWRITE_PARAMS', 'duplicator_pro_params_overwrite');
|
65 |
+
|
66 |
+
// MATCH archive pattern, matches[1] is archive name and hash
|
67 |
+
define('DUPLICATOR_ARCHIVE_REGEX_PATTERN', '/^(.+_[a-z0-9]{7,}_[0-9]{14})_archive\.(?:zip|daf)$/');
|
68 |
+
// MATCH installer.php installer-backup.php and full installer with hash
|
69 |
+
define('DUPLICATOR_INSTALLER_REGEX_PATTERN', '/^(?:.+_[a-z0-9]{7,}_[0-9]{14}_)?installer(?:-backup)?\.php$/');
|
70 |
+
// MATCH dup-installer and dup-installer-[HASH]
|
71 |
+
define('DUPLICATOR_DUP_INSTALLER_FOLDER_REGEX_PATTERN', '/^dup-installer(?:-[a-z0-9]{7,}-[0-9]{8})?$/');
|
72 |
+
define('DUPLICATOR_DUP_INSTALLER_BOOTLOG_REGEX_PATTERN', '/^dup-installer-bootlog__[a-z0-9]{7,}-[0-9]{8}.txt$/');
|
73 |
+
define('DUPLICATOR_DUP_INSTALLER_OWRPARAM_REGEX_PATTERN', '/^' . DUPLICATOR_LOCAL_OVERWRITE_PARAMS . '_[a-z0-9]{7,}-[0-9]{8}.json$/');
|
74 |
+
define("DUPLICATOR_ORIG_FOLDER_PREFIX", 'original_files_');
|
75 |
+
define('DUPLICATOR_CERT_PATH', apply_filters('duplicator_pro_certificate_path', DUPLICATOR_LITE_PATH . '/src/Libs/Certificates/cacert.pem'));
|
76 |
+
|
77 |
+
|
78 |
+
$GLOBALS['DUPLICATOR_SERVER_LIST'] = array('Apache','LiteSpeed', 'Nginx', 'Lighttpd', 'IIS', 'WebServerX', 'uWSGI');
|
79 |
+
$GLOBALS['DUPLICATOR_OPTS_DELETE'] = array(
|
80 |
'duplicator_ui_view_state',
|
81 |
'duplicator_package_active',
|
82 |
'duplicator_settings',
|
83 |
'duplicator_is_pro_enable_notice_dismissed'
|
84 |
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|