1<?php
2/**
3 * Upgrader API: WP_Upgrader_Skin class
4 *
5 * @package WordPress
6 * @subpackage Upgrader
7 * @since 4.6.0
8 */
9
10/**
11 * Generic Skin for the WordPress Upgrader classes. This skin is designed to be extended for specific purposes.
12 *
13 * @since 2.8.0
14 * @since 4.6.0 Moved to its own file from wp-admin/includes/class-wp-upgrader-skins.php.
15 */
16#[AllowDynamicProperties]
17class WP_Upgrader_Skin {
18
19 /**
20 * Holds the upgrader data.
21 *
22 * @since 2.8.0
23 * @var WP_Upgrader
24 */
25 public $upgrader;
26
27 /**
28 * Whether header is done.
29 *
30 * @since 2.8.0
31 * @var bool
32 */
33 public $done_header = false;
34
35 /**
36 * Whether footer is done.
37 *
38 * @since 2.8.0
39 * @var bool
40 */
41 public $done_footer = false;
42
43 /**
44 * Holds the result of an upgrade.
45 *
46 * @since 2.8.0
47 * @var string|bool|WP_Error
48 */
49 public $result = false;
50
51 /**
52 * Holds the options of an upgrade.
53 *
54 * @since 2.8.0
55 * @var array
56 */
57 public $options = array();
58
59 /**
60 * Constructor.
61 *
62 * Sets up the generic skin for the WordPress Upgrader classes.
63 *
64 * @since 2.8.0
65 *
66 * @param array $args Optional. The WordPress upgrader skin arguments to
67 * override default options. Default empty array.
68 */
69 public function __construct( $args = array() ) {
70 $defaults = array(
71 'url' => '',
72 'nonce' => '',
73 'title' => '',
74 'context' => false,
75 );
76 $this->options = wp_parse_args( $args, $defaults );
77 }
78
79 /**
80 * Sets the relationship between the skin being used and the upgrader.
81 *
82 * @since 2.8.0
83 *
84 * @param WP_Upgrader $upgrader
85 */
86 public function set_upgrader( &$upgrader ) {
87 if ( is_object( $upgrader ) ) {
88 $this->upgrader =& $upgrader;
89 }
90 $this->add_strings();
91 }
92
93 /**
94 * Sets up the strings used in the update process.
95 *
96 * @since 3.0.0
97 */
98 public function add_strings() {
99 }
100
101 /**
102 * Sets the result of an upgrade.
103 *
104 * @since 2.8.0
105 *
106 * @param string|bool|WP_Error $result The result of an upgrade.
107 */
108 public function set_result( $result ) {
109 $this->result = $result;
110 }
111
112 /**
113 * Displays a form to the user to request for their FTP/SSH details in order
114 * to connect to the filesystem.
115 *
116 * @since 2.8.0
117 * @since 4.6.0 The `$context` parameter default changed from `false` to an empty string.
118 *
119 * @see request_filesystem_credentials()
120 *
121 * @param bool|WP_Error $error Optional. Whether the current request has failed to connect,
122 * or an error object. Default false.
123 * @param string $context Optional. Full path to the directory that is tested
124 * for being writable. Default empty.
125 * @param bool $allow_relaxed_file_ownership Optional. Whether to allow Group/World writable. Default false.
126 * @return bool True on success, false on failure.
127 */
128 public function request_filesystem_credentials( $error = false, $context = '', $allow_relaxed_file_ownership = false ) {
129 $url = $this->options['url'];
130 if ( ! $context ) {
131 $context = $this->options['context'];
132 }
133 if ( ! empty( $this->options['nonce'] ) ) {
134 $url = wp_nonce_url( $url, $this->options['nonce'] );
135 }
136
137 $extra_fields = array();
138
139 return request_filesystem_credentials( $url, '', $error, $context, $extra_fields, $allow_relaxed_file_ownership );
140 }
141
142 /**
143 * Displays the header before the update process.
144 *
145 * @since 2.8.0
146 */
147 public function header() {
148 if ( $this->done_header ) {
149 return;
150 }
151 $this->done_header = true;
152 echo '<div class="wrap">';
153 echo '<h1>' . $this->options['title'] . '</h1>';
154 }
155
156 /**
157 * Displays the footer following the update process.
158 *
159 * @since 2.8.0
160 */
161 public function footer() {
162 if ( $this->done_footer ) {
163 return;
164 }
165 $this->done_footer = true;
166 echo '</div>';
167 }
168
169 /**
170 * Displays an error message about the update.
171 *
172 * @since 2.8.0
173 *
174 * @param string|WP_Error $errors Errors.
175 */
176 public function error( $errors ) {
177 if ( ! $this->done_header ) {
178 $this->header();
179 }
180 if ( is_string( $errors ) ) {
181 $this->feedback( $errors );
182 } elseif ( is_wp_error( $errors ) && $errors->has_errors() ) {
183 foreach ( $errors->get_error_messages() as $message ) {
184 if ( $errors->get_error_data() && is_string( $errors->get_error_data() ) ) {
185 $this->feedback( $message . ' ' . esc_html( strip_tags( $errors->get_error_data() ) ) );
186 } else {
187 $this->feedback( $message );
188 }
189 }
190 }
191 }
192
193 /**
194 * Displays a message about the update.
195 *
196 * @since 2.8.0
197 * @since 5.9.0 Renamed `$string` (a PHP reserved keyword) to `$feedback` for PHP 8 named parameter support.
198 *
199 * @param string $feedback Message data.
200 * @param mixed ...$args Optional text replacements.
201 */
202 public function feedback( $feedback, ...$args ) {
203 if ( isset( $this->upgrader->strings[ $feedback ] ) ) {
204 $feedback = $this->upgrader->strings[ $feedback ];
205 }
206
207 if ( str_contains( $feedback, '%' ) ) {
208 if ( $args ) {
209 $args = array_map( 'strip_tags', $args );
210 $args = array_map( 'esc_html', $args );
211 $feedback = vsprintf( $feedback, $args );
212 }
213 }
214 if ( empty( $feedback ) ) {
215 return;
216 }
217 show_message( $feedback );
218 }
219
220 /**
221 * Performs an action before an update.
222 *
223 * @since 2.8.0
224 */
225 public function before() {}
226
227 /**
228 * Performs an action following an update.
229 *
230 * @since 2.8.0
231 */
232 public function after() {}
233
234 /**
235 * Outputs JavaScript that calls function to decrement the update counts.
236 *
237 * @since 3.9.0
238 *
239 * @param string $type Type of update count to decrement. Likely values include 'plugin',
240 * 'theme', 'translation', etc.
241 */
242 protected function decrement_update_count( $type ) {
243 if ( ! $this->result || is_wp_error( $this->result ) || 'up_to_date' === $this->result ) {
244 return;
245 }
246
247 if ( defined( 'IFRAME_REQUEST' ) ) {
248 echo '<script type="text/javascript">
249 if ( window.postMessage && JSON ) {
250 window.parent.postMessage(
251 JSON.stringify( {
252 action: "decrementUpdateCount",
253 upgradeType: "' . $type . '"
254 } ),
255 window.location.protocol + "//" + window.location.hostname
256 + ( "" !== window.location.port ? ":" + window.location.port : "" )
257 );
258 }
259 </script>';
260 } else {
261 echo '<script type="text/javascript">
262 (function( wp ) {
263 if ( wp && wp.updates && wp.updates.decrementCount ) {
264 wp.updates.decrementCount( "' . $type . '" );
265 }
266 })( window.wp );
267 </script>';
268 }
269 }
270
271 /**
272 * Displays the header before the bulk update process.
273 *
274 * @since 3.0.0
275 */
276 public function bulk_header() {}
277
278 /**
279 * Displays the footer following the bulk update process.
280 *
281 * @since 3.0.0
282 */
283 public function bulk_footer() {}
284
285 /**
286 * Hides the `process_failed` error message when updating by uploading a zip file.
287 *
288 * @since 5.5.0
289 *
290 * @param WP_Error $wp_error WP_Error object.
291 * @return bool True if the error should be hidden, false otherwise.
292 */
293 public function hide_process_failed( $wp_error ) {
294 return false;
295 }
296}
297