├── Capfile ├── README.md ├── class-widget-data.php ├── config └── deploy │ └── production.rb ├── readme.txt ├── screenshot-1.png ├── screenshot-2.png ├── widget-data.css ├── widget-data.js └── widget-data.php /Capfile: -------------------------------------------------------------------------------- 1 | # Includes default deployment tasks 2 | require 'capistrano/deploy' 3 | 4 | # Voce Platforms specific tasks and support 5 | require 'capistrano/platforms/setup' 6 | 7 | # Loads custom tasks from `lib/capistrano/tasks' if you have any defined. 8 | Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Widget Settings Importer/Exporter 2 | Contributors: kevinlangleyjr, smccafferty, markparolisi, voceplatforms 3 | Tags: widget, import, export 4 | Requires at least: 2.8 5 | Tested up to: 4.7.2 6 | Stable tag: 1.5.3 7 | License: GPLv2 or later 8 | License URI: http://www.gnu.org/licenses/gpl-2.0.html 9 | 10 | ## Description 11 | 12 | Gives the user the ability to export the current widget settings and states as a json file. You can then import those settings on a different server or installation of WordPress so you have the same widgets within the same sidebars as the export. The import will not overwrite any data currently within the sidebars, but instead will increment the widgets and add a new instance of the widget instead. 13 | 14 | **Please note that the plugin currently does not import anything if that particular sidebar is unavailable during the import.** 15 | 16 | ## More Information 17 | 18 | For more information, please refer to the [wiki](https://github.com/voceconnect/widget-data/wiki). 19 | -------------------------------------------------------------------------------- /class-widget-data.php: -------------------------------------------------------------------------------- 1 | $widgets ) { 27 | if( is_array( $widgets ) ){ 28 | $inactive = array_merge($inactive, $widgets); 29 | } 30 | 31 | $sidebars[$sidebar] = array(); 32 | } 33 | 34 | $sidebars['wp_inactive_widgets'] = $inactive; 35 | wp_set_sidebars_widgets( $sidebars ); 36 | } 37 | 38 | /** 39 | * Register admin pages 40 | */ 41 | public static function add_admin_menus() { 42 | //import 43 | self::$import_page = add_management_page( 'Widget Settings Import', 'Widget Settings Import', 'manage_options', 'widget-settings-import', array( __CLASS__, 'import_settings_page' ) ); 44 | // export 45 | self::$export_page = add_management_page( 'Widget Settings Export', 'Widget Settings Export', 'manage_options', 'widget-settings-export', array( __CLASS__, 'export_settings_page' ) ); 46 | 47 | add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_admin_scripts' ) ); 48 | } 49 | 50 | public static function enqueue_admin_scripts( $hook ) { 51 | if( !in_array( $hook, array( self::$export_page, self::$import_page ) ) ) 52 | return; 53 | 54 | wp_enqueue_style( 'widget_data', plugins_url( '/widget-data.css', __FILE__ ) ); 55 | wp_enqueue_script( 'widget_data', plugins_url( '/widget-data.js', __FILE__ ), array( 'jquery', 'wp-ajax-response' ) ); 56 | wp_localize_script( 'widget_data', 'widgets_url', get_admin_url( false, 'widgets.php' ) ); 57 | } 58 | 59 | /** 60 | * HTML for export admin page 61 | */ 62 | public static function export_settings_page() { 63 | $sidebar_widgets = self::order_sidebar_widgets( wp_get_sidebars_widgets() ); 64 | ?> 65 |
66 |
67 |

Widget Setting Export

68 | 69 |
70 | 71 | 72 |

73 | Select All Active Widgets 74 | Un-Select All Active Widgets 75 |

76 |
77 |

Sidebars

78 |
79 |
80 | 116 | 117 |
118 |
119 |
120 | 129 |
130 |
131 |

Widget Setting Import

132 | 133 | 134 |
135 |

136 | Select All Active Widgets 137 | Un-Select All Active Widgets 138 |

139 |
140 | get_error_message() ); 146 | 147 | if( !$json || !( $json_data = json_decode( $json[0], true ) ) ) 148 | return; 149 | 150 | $json_file = $json[1]; 151 | ?> 152 | 153 | 154 |
155 |

Please select a widget to continue.

156 |

Sidebars

157 |
158 |
159 | 207 |

208 | 209 |
210 | All active widgets will be moved to inactive 211 |

212 | 213 |
214 |
215 | 216 |
217 |

Select the file that contains widget settings

218 |

219 | 220 | Select a file 221 | 222 |

223 | 224 |
225 | 226 |
227 |
228 | $widgets ) { 240 | if ( !empty( $widgets ) && is_array( $widgets ) ) { 241 | foreach ( $widgets as $sidebar_widget ) { 242 | if ( in_array( $sidebar_widget, array_keys( $posted_array ) ) ) { 243 | $sidebar_export[$sidebar][] = $sidebar_widget; 244 | } 245 | } 246 | } 247 | } 248 | $widgets = array( ); 249 | foreach ( $posted_array as $k => $v ) { 250 | $widget = array( ); 251 | $widget['type'] = trim( substr( $k, 0, strrpos( $k, '-' ) ) ); 252 | $widget['type-index'] = trim( substr( $k, strrpos( $k, '-' ) + 1 ) ); 253 | $widget['export_flag'] = ($v == 'on') ? true : false; 254 | $widgets[] = $widget; 255 | } 256 | $widgets_array = array( ); 257 | foreach ( $widgets as $widget ) { 258 | $widget_val = get_option( 'widget_' . $widget['type'] ); 259 | $widget_val = apply_filters( 'widget_data_export', $widget_val, $widget['type'] ); 260 | $multiwidget_val = $widget_val['_multiwidget']; 261 | $widgets_array[$widget['type']][$widget['type-index']] = $widget_val[$widget['type-index']]; 262 | if ( isset( $widgets_array[$widget['type']]['_multiwidget'] ) ) 263 | unset( $widgets_array[$widget['type']]['_multiwidget'] ); 264 | 265 | $widgets_array[$widget['type']]['_multiwidget'] = $multiwidget_val; 266 | } 267 | unset( $widgets_array['export'] ); 268 | $export_array = array( $sidebar_export, $widgets_array ); 269 | $json = json_encode( $export_array ); 270 | return $json; 271 | } 272 | 273 | /** 274 | * Import widgets 275 | * @param array $import_array 276 | */ 277 | public static function parse_import_data( $import_array ) { 278 | $sidebars_data = $import_array[0]; 279 | $widget_data = $import_array[1]; 280 | $current_sidebars = get_option( 'sidebars_widgets' ); 281 | $new_widgets = array( ); 282 | 283 | foreach ( $sidebars_data as $import_sidebar => $import_widgets ) : 284 | 285 | foreach ( $import_widgets as $import_widget ) : 286 | //if the sidebar exists 287 | if ( array_key_exists( $import_sidebar, $current_sidebars ) ) : 288 | $title = trim( substr( $import_widget, 0, strrpos( $import_widget, '-' ) ) ); 289 | $index = trim( substr( $import_widget, strrpos( $import_widget, '-' ) + 1 ) ); 290 | $current_widget_data = get_option( 'widget_' . $title ); 291 | $new_widget_name = self::get_new_widget_name( $title, $index ); 292 | $new_index = trim( substr( $new_widget_name, strrpos( $new_widget_name, '-' ) + 1 ) ); 293 | 294 | if ( !empty( $new_widgets[ $title ] ) && is_array( $new_widgets[$title] ) ) { 295 | while ( array_key_exists( $new_index, $new_widgets[$title] ) ) { 296 | $new_index++; 297 | } 298 | } 299 | $current_sidebars[$import_sidebar][] = $title . '-' . $new_index; 300 | if ( array_key_exists( $title, $new_widgets ) ) { 301 | $new_widgets[$title][$new_index] = $widget_data[$title][$index]; 302 | $multiwidget = $new_widgets[$title]['_multiwidget']; 303 | unset( $new_widgets[$title]['_multiwidget'] ); 304 | $new_widgets[$title]['_multiwidget'] = $multiwidget; 305 | } else { 306 | $current_widget_data[$new_index] = $widget_data[$title][$index]; 307 | $current_multiwidget = array_key_exists('_multiwidget', $current_widget_data) ? $current_widget_data['_multiwidget'] : false; 308 | $new_multiwidget = array_key_exists('_multiwidget', $widget_data[$title]) ? $widget_data[$title]['_multiwidget'] : false; 309 | $multiwidget = ($current_multiwidget != $new_multiwidget) ? $current_multiwidget : 1; 310 | unset( $current_widget_data['_multiwidget'] ); 311 | $current_widget_data['_multiwidget'] = $multiwidget; 312 | $new_widgets[$title] = $current_widget_data; 313 | } 314 | 315 | endif; 316 | endforeach; 317 | endforeach; 318 | 319 | if ( isset( $new_widgets ) && isset( $current_sidebars ) ) { 320 | update_option( 'sidebars_widgets', $current_sidebars ); 321 | 322 | foreach ( $new_widgets as $title => $content ) { 323 | $content = apply_filters( 'widget_data_import', $content, $title ); 324 | update_option( 'widget_' . $title, $content ); 325 | } 326 | 327 | return true; 328 | } 329 | 330 | return false; 331 | } 332 | 333 | /** 334 | * Output the JSON for download 335 | */ 336 | public static function export_widget_settings() { 337 | // @TODO check something better than just $_POST 338 | if ( isset( $_POST['action'] ) && $_POST['action'] == 'export_widget_settings' ){ 339 | header( "Content-Description: File Transfer" ); 340 | header( "Content-Disposition: attachment; filename=widget_data.json" ); 341 | header( "Content-Type: application/octet-stream" ); 342 | unset($_POST['action']); 343 | unset($_POST['_wpnonce']); 344 | unset($_POST['_wp_http_referer']); 345 | echo self::parse_export_data( $_POST ); 346 | exit; 347 | } 348 | } 349 | 350 | /** 351 | * Parse JSON import file and load 352 | */ 353 | public static function ajax_import_widget_data() { 354 | $response = array( 355 | 'what' => 'widget_import_export', 356 | 'action' => 'import_submit' 357 | ); 358 | 359 | $widgets = isset( $_POST['widgets'] ) ? $_POST['widgets'] : false; 360 | $import_file = isset( $_POST['import_file'] ) ? $_POST['import_file'] : false; 361 | 362 | if( empty($widgets) || empty($import_file) ){ 363 | $response['id'] = new WP_Error('import_widget_data', 'No widget data posted to import'); 364 | $response = new WP_Ajax_Response( $response ); 365 | $response->send(); 366 | } 367 | 368 | $clear_current = isset( $_POST['clear_current'] ); 369 | 370 | if ( $clear_current ) 371 | self::clear_widgets(); 372 | 373 | $json_data = file_get_contents( $import_file ); 374 | $json_data = json_decode( $json_data, true ); 375 | $sidebar_data = $json_data[0]; 376 | $widget_data = $json_data[1]; 377 | foreach ( $sidebar_data as $title => $sidebar ) { 378 | $count = count( $sidebar ); 379 | for ( $i = 0; $i < $count; $i++ ) { 380 | $widget = array( ); 381 | $widget['type'] = trim( substr( $sidebar[$i], 0, strrpos( $sidebar[$i], '-' ) ) ); 382 | $widget['type-index'] = trim( substr( $sidebar[$i], strrpos( $sidebar[$i], '-' ) + 1 ) ); 383 | if ( !isset( $widgets[$widget['type']][$widget['type-index']] ) ) { 384 | unset( $sidebar_data[$title][$i] ); 385 | } 386 | } 387 | $sidebar_data[$title] = array_values( $sidebar_data[$title] ); 388 | } 389 | 390 | foreach ( $widgets as $widget_title => $widget_value ) { 391 | foreach ( $widget_value as $widget_key => $widget_value ) { 392 | $widgets[$widget_title][$widget_key] = $widget_data[$widget_title][$widget_key]; 393 | } 394 | } 395 | 396 | $sidebar_data = array( array_filter( $sidebar_data ), $widgets ); 397 | $response['id'] = ( self::parse_import_data( $sidebar_data ) ) ? true : new WP_Error( 'widget_import_submit', 'Unknown Error' ); 398 | 399 | $response = new WP_Ajax_Response( $response ); 400 | $response->send(); 401 | } 402 | 403 | /** 404 | * Read uploaded JSON file 405 | * @return type 406 | */ 407 | public static function get_widget_settings_json() { 408 | $widget_settings = self::upload_widget_settings_file(); 409 | 410 | if( is_wp_error( $widget_settings ) || ! $widget_settings ) 411 | return false; 412 | 413 | if( isset( $widget_settings['error'] ) ) 414 | return new WP_Error( 'widget_import_upload_error', $widget_settings['error'] ); 415 | 416 | $file_contents = file_get_contents( $widget_settings['file'] ); 417 | return array( $file_contents, $widget_settings['file'] ); 418 | } 419 | 420 | /** 421 | * Upload JSON file 422 | * @return boolean 423 | */ 424 | public static function upload_widget_settings_file() { 425 | if ( isset( $_FILES['widget-upload-file'] ) ) { 426 | add_filter( 'upload_mimes', array( __CLASS__, 'json_upload_mimes' ) ); 427 | 428 | $upload = wp_handle_upload( $_FILES['widget-upload-file'], array( 'test_form' => false ) ); 429 | 430 | remove_filter( 'upload_mimes', array( __CLASS__, 'json_upload_mimes' ) ); 431 | return $upload; 432 | } 433 | 434 | return false; 435 | } 436 | 437 | /** 438 | * 439 | * @param string $widget_name 440 | * @param string $widget_index 441 | * @return string 442 | */ 443 | public static function get_new_widget_name( $widget_name, $widget_index ) { 444 | $current_sidebars = get_option( 'sidebars_widgets' ); 445 | $all_widget_array = array( ); 446 | foreach ( $current_sidebars as $sidebar => $widgets ) { 447 | if ( !empty( $widgets ) && is_array( $widgets ) && $sidebar != 'wp_inactive_widgets' ) { 448 | foreach ( $widgets as $widget ) { 449 | $all_widget_array[] = $widget; 450 | } 451 | } 452 | } 453 | while ( in_array( $widget_name . '-' . $widget_index, $all_widget_array ) ) { 454 | $widget_index++; 455 | } 456 | $new_widget_name = $widget_name . '-' . $widget_index; 457 | return $new_widget_name; 458 | } 459 | 460 | /** 461 | * 462 | * @global type $wp_registered_sidebars 463 | * @param type $sidebar_id 464 | * @return boolean 465 | */ 466 | public static function get_sidebar_info( $sidebar_id ) { 467 | global $wp_registered_sidebars; 468 | 469 | //since wp_inactive_widget is only used in widgets.php 470 | if ( $sidebar_id == 'wp_inactive_widgets' ) 471 | return array( 'name' => 'Inactive Widgets', 'id' => 'wp_inactive_widgets' ); 472 | 473 | foreach ( $wp_registered_sidebars as $sidebar ) { 474 | if ( isset( $sidebar['id'] ) && $sidebar['id'] == $sidebar_id ) 475 | return $sidebar; 476 | } 477 | 478 | return false; 479 | } 480 | 481 | /** 482 | * 483 | * @param array $sidebar_widgets 484 | * @return type 485 | */ 486 | public static function order_sidebar_widgets( $sidebar_widgets ) { 487 | $inactive_widgets = false; 488 | 489 | //seperate inactive widget sidebar from other sidebars so it can be moved to the end of the array, if it exists 490 | if ( isset( $sidebar_widgets['wp_inactive_widgets'] ) ) { 491 | $inactive_widgets = $sidebar_widgets['wp_inactive_widgets']; 492 | unset( $sidebar_widgets['wp_inactive_widgets'] ); 493 | $sidebar_widgets['wp_inactive_widgets'] = $inactive_widgets; 494 | } 495 | 496 | return $sidebar_widgets; 497 | } 498 | 499 | /** 500 | * Add mime type for JSON 501 | * @param array $existing_mimes 502 | * @return string 503 | */ 504 | public static function json_upload_mimes( $existing_mimes = array( ) ) { 505 | $existing_mimes['json'] = 'application/json'; 506 | return $existing_mimes; 507 | } 508 | 509 | } -------------------------------------------------------------------------------- /config/deploy/production.rb: -------------------------------------------------------------------------------- 1 | set :application, 'widget-data' 2 | set :repo_url, "git@github.com:voceconnect/#{fetch(:application)}.git" 3 | 4 | set :scm, 'git-to-svn' 5 | set :type, 'plugin' 6 | 7 | set :svn_repository, "http://plugins.svn.wordpress.org/widget-settings-importexport/" 8 | set :svn_deploy_to, "trunk" 9 | 10 | set :build_folders, ( 11 | fetch(:build_folders) << %w{ 12 | config 13 | } 14 | ).flatten -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === Widget Settings Importer/Exporter === 2 | Contributors: kevinlangleyjr, smccafferty, markparolisi, voceplatforms 3 | Tags: widget, import, export 4 | Requires at least: 2.8 5 | Tested up to: 4.7.2 6 | Stable tag: 1.5.3 7 | License: GPLv2 or later 8 | License URI: http://www.gnu.org/licenses/gpl-2.0.html 9 | 10 | Allows you to export and import widgets settings. 11 | 12 | == Description == 13 | 14 | Gives the user the ability to export the current widget settings and states as a json file. You can then import those settings on a different server or installation of WordPress so you have the same widgets within the same sidebars as the export. The import will not overwrite any data currently within the sidebars, but instead will increment the widgets and add a new instance of the widget instead. 15 | 16 | ** Please note that the plugin currently does not import anything if that particular sidebar is unavailable during the import. 17 | 18 | *** This plugin requires at least PHP 5.2.0 19 | 20 | Please refer to full documentation at https://github.com/voceconnect/widget-data/wiki. 21 | 22 | == Installation == 23 | 24 | Please refer to full installation instructions at https://github.com/voceconnect/widget-data/wiki. 25 | 26 | == Frequently Asked Questions == 27 | 28 | If you have any issues with this plugin, please log them at the Github repo for this plugin. 29 | This is done to centralize our issues and make sure nothing goes unnoticed. 30 | 31 | The URL to log an issue is https://github.com/voceconnect/widget-data/issues. 32 | 33 | See Frequently Asked Questions here: https://github.com/voceconnect/widget-data/wiki/Frequently-Asked-Questions. 34 | 35 | == Screenshots == 36 | 37 | 1. Choose which widgets to export 38 | 2. Upload JSON export file 39 | 40 | == Changelog == 41 | 42 | Please refer to full changelog at https://github.com/voceconnect/widget-data/releases. 43 | -------------------------------------------------------------------------------- /screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voceconnect/widget-data/8bf0adfb244cb04a681c73d1262ae23d5c394550/screenshot-1.png -------------------------------------------------------------------------------- /screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voceconnect/widget-data/8bf0adfb244cb04a681c73d1262ae23d5c394550/screenshot-2.png -------------------------------------------------------------------------------- /widget-data.css: -------------------------------------------------------------------------------- 1 | .widget-data .title { border: solid 1px #DFDFDF; background-color: #F1F1F1; } 2 | .widget-data .title h3 { margin: 0; padding: 8px 10px; } 3 | .widget-data .title .widget-selection-error { display:none; margin: 0; float: right; color: #FF0000; padding: 10px 10px 0 0; } 4 | .widget-data .sidebars { border: solid 1px #DFDFDF; } 5 | .widget-data .sidebars .sidebar h4 { margin: 0; padding: 8px 20px; background:#eee; } 6 | .widget-data .sidebars .sidebar .widgets input[type="checkbox"] { margin-right:3px; } 7 | .widget-data .sidebars .sidebar .widgets .import-form-row { padding-left:30px; background-color:#FFF; } 8 | .widget-data .sidebars .sidebar .widgets .import-form-row:nth-child(even){ background-color:#F9F9F9; } 9 | .widget-data form#upload-widget-data span.file-name { min-width:300px; display:inline-block; height:20px; top:7px; border: solid 1px #bbb; position: relative; } 10 | .widget-data form#upload-widget-data #upload-button { margin:4px 0 0 10px; } 11 | .widget-data .button-bottom { margin-top: 10px; } -------------------------------------------------------------------------------- /widget-data.js: -------------------------------------------------------------------------------- 1 | !function ($) { 2 | "use strict"; 3 | 4 | /** 5 | * Display the notification div, populate a message, and set a CSS class 6 | * @param String message Message to display 7 | * @param String className CSS class of error or success 8 | */ 9 | var show_notification = function(message, className){ 10 | var notification = $('div#notifier').empty().removeClass('error updated'); 11 | notification.html('

' + message + '

'); 12 | notification.addClass(className); 13 | 14 | notification.fadeIn('slow'); 15 | jQuery('body,html').animate({ 16 | scrollTop: 0 17 | }, 800); 18 | }, 19 | wrapper = $('
').css({ 20 | height:0, 21 | width:0, 22 | 'overflow':'hidden' 23 | }); 24 | $(function () { 25 | var fileInput = $('#widget-upload-file').wrap(wrapper), 26 | widgetCheckboxes = $('.widget-data .widget-checkbox'), 27 | widgetSelectionError = $('.widget-data p.widget-selection-error'); 28 | 29 | /** 30 | * Handle click events for widget-data to select all checkboxes on click, to uncheck all 31 | * checkboxes on click, and to activate the file upload when the file upload button is clicked. 32 | * @param Object e Event object 33 | */ 34 | $('.widget-data').on('click', '.select-all, .unselect-all, .upload-button', function(e){ 35 | e.preventDefault(); 36 | if( $(this).hasClass('select-all') ){ 37 | widgetCheckboxes.not(":checked").each(function(){ 38 | $(this).attr( 'checked', true ); 39 | }); 40 | } else if( $(this).hasClass('unselect-all') ){ 41 | widgetCheckboxes.filter(":checked").each(function(){ 42 | $(this).attr( 'checked', false ); 43 | }); 44 | } else if( $(this).hasClass('upload-button') ){ 45 | fileInput.click(); 46 | } 47 | }); 48 | 49 | /** 50 | * Handle the export form submission 51 | * @param Object e Event object 52 | */ 53 | $('form#widget-export-settings').submit(function(e) { 54 | // return and show notification if no widgets are selected 55 | if (widgetCheckboxes.filter(':checked').length === 0) { 56 | e.preventDefault(); 57 | show_notification('Please select a widget to continue.', 'error'); 58 | return; 59 | } 60 | var message = 'All of the requested widgets have been exported.'; 61 | $('form#widget-export-settings').fadeOut('slow'); 62 | window.setTimeout(function () { 63 | window.location.replace(widgets_url); 64 | }, 4000); 65 | show_notification(message, 'updated'); 66 | }); 67 | 68 | /*** 69 | * Handle imports 70 | * @param Object e Event object 71 | */ 72 | $('form#import-widget-data').submit(function(e){ 73 | e.preventDefault(); 74 | 75 | if (widgetCheckboxes.filter(':checked').length === 0) { 76 | widgetSelectionError.fadeIn('slow').delay(2000).fadeOut('slow'); 77 | return false; 78 | } 79 | var message, newClass; 80 | $.post( ajaxurl, $("#import-widget-data").serialize(), function(r){ 81 | var res = wpAjax.parseAjaxResponse(r, 'notifier'); 82 | if( ! res ) 83 | return; 84 | 85 | $('.import-wrapper').fadeOut('slow'); 86 | show_notification('All widgets with registered sidebars have been imported successfully.', 'updated'); 87 | // window.setTimeout(function () { 88 | // window.location.replace(widgets_url); 89 | // }, 4000); 90 | }); 91 | }); 92 | 93 | /** 94 | * 95 | */ 96 | fileInput.change(function(){ 97 | var outputText = $('#upload-widget-data .file-name'), 98 | sub = $(this).val().lastIndexOf('\\') + 1, 99 | filename = $(this).val().substring(sub); 100 | 101 | outputText.val(filename); 102 | }); 103 | 104 | }); 105 | }(window.jQuery); -------------------------------------------------------------------------------- /widget-data.php: -------------------------------------------------------------------------------- 1 | . 23 | * ****************************************************************** 24 | */ 25 | 26 | require( dirname(__FILE__) . '/class-widget-data.php' ); 27 | add_action( 'init', array( 'Widget_Data', 'init' ) ); --------------------------------------------------------------------------------