WidgetImporter.php (13225B)
1 <?php 2 /** 3 * Class for the widget importer used in the One Click Demo Import plugin. 4 * 5 * Code is mostly from the Widget Importer & Exporter plugin. 6 * 7 * @see https://wordpress.org/plugins/widget-importer-exporter/ 8 * @package ocdi 9 */ 10 11 namespace OCDI; 12 13 class WidgetImporter { 14 /** 15 * Import widgets from WIE or JSON file. 16 * 17 * @param string $widget_import_file_path path to the widget import file. 18 */ 19 public static function import( $widget_import_file_path ) { 20 $results = array(); 21 $ocdi = OneClickDemoImport::get_instance(); 22 $log_file_path = $ocdi->get_log_file_path(); 23 24 // Import widgets and return result. 25 if ( ! empty( $widget_import_file_path ) ) { 26 $results = self::import_widgets( $widget_import_file_path ); 27 } 28 29 // Check for errors, else write the results to the log file. 30 if ( is_wp_error( $results ) ) { 31 $error_message = $results->get_error_message(); 32 33 // Add any error messages to the frontend_error_messages variable in OCDI main class. 34 $ocdi->append_to_frontend_error_messages( $error_message ); 35 36 // Write error to log file. 37 Helpers::append_to_file( 38 $error_message, 39 $log_file_path, 40 esc_html__( 'Importing widgets', 'pt-ocdi' ) 41 ); 42 } 43 else { 44 ob_start(); 45 self::format_results_for_log( $results ); 46 $message = ob_get_clean(); 47 48 // Add this message to log file. 49 $log_added = Helpers::append_to_file( 50 $message, 51 $log_file_path, 52 esc_html__( 'Importing widgets' , 'pt-ocdi' ) 53 ); 54 } 55 56 } 57 58 59 /** 60 * Imports widgets from a json file. 61 * 62 * @param string $data_file path to json file with WordPress widget export data. 63 */ 64 private static function import_widgets( $data_file ) { 65 // Get widgets data from file. 66 $data = self::process_import_file( $data_file ); 67 68 // Return from this function if there was an error. 69 if ( is_wp_error( $data ) ) { 70 return $data; 71 } 72 73 // Import the widget data and save the results. 74 return self::import_data( $data ); 75 } 76 77 /** 78 * Process import file - this parses the widget data and returns it. 79 * 80 * @param string $file path to json file. 81 * @return object $data decoded JSON string 82 */ 83 private static function process_import_file( $file ) { 84 // File exists? 85 if ( ! file_exists( $file ) ) { 86 return new \WP_Error( 87 'widget_import_file_not_found', 88 __( 'Error: Widget import file could not be found.', 'pt-ocdi' ) 89 ); 90 } 91 92 // Get file contents and decode. 93 $data = Helpers::data_from_file( $file ); 94 preg_match_all("!https?:[^?#]+\.(?:jpe?g|png|gif)!Ui",$data , $remote_url_new); 95 foreach($remote_url_new[0] as $remote_url){ 96 if(strpos($remote_url,"wp-content")!==false){ 97 $urlpath=substr_replace($remote_url, "", 0, strpos($remote_url,"wp-content")+11); 98 $site_url=rtrim(site_url(), '/'); 99 $remote_url_rep= $site_url."/wp-content/".$urlpath; 100 $data=str_replace($remote_url,$remote_url_rep,$data); 101 } 102 } 103 104 105 106 // Return from this function if there was an error. 107 if ( is_wp_error( $data ) ) { 108 return $data; 109 } 110 111 return json_decode( $data ); 112 } 113 114 115 /** 116 * Import widget JSON data 117 * 118 * @global array $wp_registered_sidebars 119 * @param object $data JSON widget data. 120 * @return array $results 121 */ 122 private static function import_data( $data ) { 123 global $wp_registered_sidebars; 124 125 // Have valid data? If no data or could not decode. 126 if ( empty( $data ) || ! is_object( $data ) ) { 127 return new \WP_Error( 128 'corrupted_widget_import_data', 129 __( 'Error: Widget import data could not be read. Please try a different file.', 'pt-ocdi' ) 130 ); 131 } 132 133 // Hook before import. 134 do_action( 'pt-ocdi/widget_importer_before_widgets_import' ); 135 $data = apply_filters( 'pt-ocdi/before_widgets_import_data', $data ); 136 137 // Get all available widgets site supports. 138 $available_widgets = self::available_widgets(); 139 140 // Get all existing widget instances. 141 $widget_instances = array(); 142 143 foreach ( $available_widgets as $widget_data ) { 144 $widget_instances[ $widget_data['id_base'] ] = get_option( 'widget_' . $widget_data['id_base'] ); 145 } 146 147 // Begin results. 148 $results = array(); 149 150 // Loop import data's sidebars. 151 foreach ( $data as $sidebar_id => $widgets ) { 152 // Skip inactive widgets (should not be in export file). 153 if ( 'wp_inactive_widgets' == $sidebar_id ) { 154 continue; 155 } 156 157 // Check if sidebar is available on this site. Otherwise add widgets to inactive, and say so. 158 if ( isset( $wp_registered_sidebars[ $sidebar_id ] ) ) { 159 $sidebar_available = true; 160 $use_sidebar_id = $sidebar_id; 161 $sidebar_message_type = 'success'; 162 $sidebar_message = ''; 163 } 164 else { 165 $sidebar_available = false; 166 $use_sidebar_id = 'wp_inactive_widgets'; // Add to inactive if sidebar does not exist in theme. 167 $sidebar_message_type = 'error'; 168 $sidebar_message = __( 'Sidebar does not exist in theme (moving widget to Inactive)', 'pt-ocdi' ); 169 } 170 171 // Result for sidebar. 172 $results[ $sidebar_id ]['name'] = ! empty( $wp_registered_sidebars[ $sidebar_id ]['name'] ) ? $wp_registered_sidebars[ $sidebar_id ]['name'] : $sidebar_id; // Sidebar name if theme supports it; otherwise ID. 173 $results[ $sidebar_id ]['message_type'] = $sidebar_message_type; 174 $results[ $sidebar_id ]['message'] = $sidebar_message; 175 $results[ $sidebar_id ]['widgets'] = array(); 176 177 // Loop widgets. 178 foreach ( $widgets as $widget_instance_id => $widget ) { 179 $fail = false; 180 181 // Get id_base (remove -# from end) and instance ID number. 182 $id_base = preg_replace( '/-[0-9]+$/', '', $widget_instance_id ); 183 $instance_id_number = str_replace( $id_base . '-', '', $widget_instance_id ); 184 185 // Does site support this widget? 186 if ( ! $fail && ! isset( $available_widgets[ $id_base ] ) ) { 187 $fail = true; 188 $widget_message_type = 'error'; 189 $widget_message = __( 'Site does not support widget', 'pt-ocdi' ); // Explain why widget not imported. 190 } 191 192 // Filter to modify settings object before conversion to array and import. 193 // Leave this filter here for backwards compatibility with manipulating objects (before conversion to array below). 194 // Ideally the newer wie_widget_settings_array below will be used instead of this. 195 $widget = apply_filters( 'pt-ocdi/widget_settings', $widget ); // Object. 196 197 // Convert multidimensional objects to multidimensional arrays. 198 // Some plugins like Jetpack Widget Visibility store settings as multidimensional arrays. 199 // Without this, they are imported as objects and cause fatal error on Widgets page. 200 // If this creates problems for plugins that do actually intend settings in objects then may need to consider other approach: https://wordpress.org/support/topic/problem-with-array-of-arrays. 201 // It is probably much more likely that arrays are used than objects, however. 202 $widget = json_decode( json_encode( $widget ), true ); 203 204 // Filter to modify settings array. 205 // This is preferred over the older wie_widget_settings filter above. 206 // Do before identical check because changes may make it identical to end result (such as URL replacements). 207 $widget = apply_filters( 'pt-ocdi/widget_settings_array', $widget ); 208 209 // Does widget with identical settings already exist in same sidebar? 210 if ( ! $fail && isset( $widget_instances[ $id_base ] ) ) { 211 // Get existing widgets in this sidebar. 212 $sidebars_widgets = get_option( 'sidebars_widgets' ); 213 $sidebar_widgets = isset( $sidebars_widgets[ $use_sidebar_id ] ) ? $sidebars_widgets[ $use_sidebar_id ] : array(); // Check Inactive if that's where will go. 214 215 // Loop widgets with ID base. 216 $single_widget_instances = ! empty( $widget_instances[ $id_base ] ) ? $widget_instances[ $id_base ] : array(); 217 foreach ( $single_widget_instances as $check_id => $check_widget ) { 218 // Is widget in same sidebar and has identical settings? 219 if ( in_array( "$id_base-$check_id", $sidebar_widgets ) && (array) $widget == $check_widget ) { 220 $fail = true; 221 $widget_message_type = 'warning'; 222 $widget_message = __( 'Widget already exists', 'pt-ocdi' ); // Explain why widget not imported. 223 224 break; 225 } 226 } 227 } 228 229 // No failure. 230 if ( ! $fail ) { 231 // Add widget instance. 232 $single_widget_instances = get_option( 'widget_' . $id_base ); // All instances for that widget ID base, get fresh every time. 233 $single_widget_instances = ! empty( $single_widget_instances ) ? $single_widget_instances : array( '_multiwidget' => 1 ); // Start fresh if have to. 234 $single_widget_instances[] = $widget; // Add it. 235 236 // Get the key it was given. 237 end( $single_widget_instances ); 238 $new_instance_id_number = key( $single_widget_instances ); 239 240 // If key is 0, make it 1. 241 // When 0, an issue can occur where adding a widget causes data from other widget to load, and the widget doesn't stick (reload wipes it). 242 if ( '0' === strval( $new_instance_id_number ) ) { 243 $new_instance_id_number = 1; 244 $single_widget_instances[ $new_instance_id_number ] = $single_widget_instances[0]; 245 unset( $single_widget_instances[0] ); 246 } 247 248 // Move _multiwidget to end of array for uniformity. 249 if ( isset( $single_widget_instances['_multiwidget'] ) ) { 250 $multiwidget = $single_widget_instances['_multiwidget']; 251 unset( $single_widget_instances['_multiwidget'] ); 252 $single_widget_instances['_multiwidget'] = $multiwidget; 253 } 254 255 // Update option with new widget. 256 update_option( 'widget_' . $id_base, $single_widget_instances ); 257 258 // Assign widget instance to sidebar. 259 $sidebars_widgets = get_option( 'sidebars_widgets' ); // Which sidebars have which widgets, get fresh every time. 260 $new_instance_id = $id_base . '-' . $new_instance_id_number; // Use ID number from new widget instance. 261 $sidebars_widgets[ $use_sidebar_id ][] = $new_instance_id; // Add new instance to sidebar. 262 update_option( 'sidebars_widgets', $sidebars_widgets ); // Save the amended data. 263 264 // After widget import action. 265 $after_widget_import = array( 266 'sidebar' => $use_sidebar_id, 267 'sidebar_old' => $sidebar_id, 268 'widget' => $widget, 269 'widget_type' => $id_base, 270 'widget_id' => $new_instance_id, 271 'widget_id_old' => $widget_instance_id, 272 'widget_id_num' => $new_instance_id_number, 273 'widget_id_num_old' => $instance_id_number, 274 ); 275 do_action( 'pt-ocdi/widget_importer_after_single_widget_import', $after_widget_import ); 276 277 // Success message. 278 if ( $sidebar_available ) { 279 $widget_message_type = 'success'; 280 $widget_message = __( 'Imported', 'pt-ocdi' ); 281 } 282 else { 283 $widget_message_type = 'warning'; 284 $widget_message = __( 'Imported to Inactive', 'pt-ocdi' ); 285 } 286 } 287 288 // Result for widget instance. 289 $results[ $sidebar_id ]['widgets'][ $widget_instance_id ]['name'] = isset( $available_widgets[ $id_base ]['name'] ) ? $available_widgets[ $id_base ]['name'] : $id_base; // Widget name or ID if name not available (not supported by site). 290 $results[ $sidebar_id ]['widgets'][ $widget_instance_id ]['title'] = ! empty( $widget['title'] ) ? $widget['title'] : __( 'No Title', 'pt-ocdi' ); // Show "No Title" if widget instance is untitled. 291 $results[ $sidebar_id ]['widgets'][ $widget_instance_id ]['message_type'] = $widget_message_type; 292 $results[ $sidebar_id ]['widgets'][ $widget_instance_id ]['message'] = $widget_message; 293 294 } 295 } 296 297 // Hook after import. 298 do_action( 'pt-ocdi/widget_importer_after_widgets_import' ); 299 300 // Return results. 301 return apply_filters( 'pt-ocdi/widget_import_results', $results ); 302 } 303 304 305 /** 306 * Available widgets. 307 * 308 * Gather site's widgets into array with ID base, name, etc. 309 * 310 * @global array $wp_registered_widget_controls 311 * @return array $available_widgets, Widget information 312 */ 313 private static function available_widgets() { 314 global $wp_registered_widget_controls; 315 316 $widget_controls = $wp_registered_widget_controls; 317 $available_widgets = array(); 318 319 foreach ( $widget_controls as $widget ) { 320 if ( ! empty( $widget['id_base'] ) && ! isset( $available_widgets[ $widget['id_base'] ] ) ) { 321 $available_widgets[ $widget['id_base'] ]['id_base'] = $widget['id_base']; 322 $available_widgets[ $widget['id_base'] ]['name'] = $widget['name']; 323 } 324 } 325 326 return apply_filters( 'pt-ocdi/available_widgets', $available_widgets ); 327 } 328 329 330 /** 331 * Format results for log file 332 * 333 * @param array $results widget import results. 334 */ 335 private static function format_results_for_log( $results ) { 336 if ( empty( $results ) ) { 337 esc_html_e( 'No results for widget import!', 'pt-ocdi' ); 338 } 339 340 // Loop sidebars. 341 foreach ( $results as $sidebar ) { 342 echo esc_html( $sidebar['name'] ) . ' : ' . esc_html( $sidebar['message'] ) . PHP_EOL . PHP_EOL; 343 // Loop widgets. 344 foreach ( $sidebar['widgets'] as $widget ) { 345 echo esc_html( $widget['name'] ) . ' - ' . esc_html( $widget['title'] ) . ' - ' . esc_html( $widget['message'] ) . PHP_EOL; 346 } 347 echo PHP_EOL; 348 } 349 } 350 }