641 $this->skin->feedback('maintenance_end'); |
822 $this->skin->feedback('maintenance_end'); |
642 $wp_filesystem->delete($file); |
823 $wp_filesystem->delete($file); |
643 } |
824 } |
644 } |
825 } |
645 |
826 |
|
827 /** |
|
828 * Creates a lock using WordPress options. |
|
829 * |
|
830 * @since 4.5.0 |
|
831 * @static |
|
832 * |
|
833 * @param string $lock_name The name of this unique lock. |
|
834 * @param int $release_timeout Optional. The duration in seconds to respect an existing lock. |
|
835 * Default: 1 hour. |
|
836 * @return bool False if a lock couldn't be created or if the lock is still valid. True otherwise. |
|
837 */ |
|
838 public static function create_lock( $lock_name, $release_timeout = null ) { |
|
839 global $wpdb; |
|
840 if ( ! $release_timeout ) { |
|
841 $release_timeout = HOUR_IN_SECONDS; |
|
842 } |
|
843 $lock_option = $lock_name . '.lock'; |
|
844 |
|
845 // Try to lock. |
|
846 $lock_result = $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO `$wpdb->options` ( `option_name`, `option_value`, `autoload` ) VALUES (%s, %s, 'no') /* LOCK */", $lock_option, time() ) ); |
|
847 |
|
848 if ( ! $lock_result ) { |
|
849 $lock_result = get_option( $lock_option ); |
|
850 |
|
851 // If a lock couldn't be created, and there isn't a lock, bail. |
|
852 if ( ! $lock_result ) { |
|
853 return false; |
|
854 } |
|
855 |
|
856 // Check to see if the lock is still valid. If it is, bail. |
|
857 if ( $lock_result > ( time() - $release_timeout ) ) { |
|
858 return false; |
|
859 } |
|
860 |
|
861 // There must exist an expired lock, clear it and re-gain it. |
|
862 WP_Upgrader::release_lock( $lock_name ); |
|
863 |
|
864 return WP_Upgrader::create_lock( $lock_name, $release_timeout ); |
|
865 } |
|
866 |
|
867 // Update the lock, as by this point we've definitely got a lock, just need to fire the actions. |
|
868 update_option( $lock_option, time() ); |
|
869 |
|
870 return true; |
|
871 } |
|
872 |
|
873 /** |
|
874 * Releases an upgrader lock. |
|
875 * |
|
876 * @since 4.5.0 |
|
877 * @static |
|
878 * |
|
879 * @see WP_Upgrader::create_lock() |
|
880 * |
|
881 * @param string $lock_name The name of this unique lock. |
|
882 * @return bool True if the lock was successfully released. False on failure. |
|
883 */ |
|
884 public static function release_lock( $lock_name ) { |
|
885 return delete_option( $lock_name . '.lock' ); |
|
886 } |
|
887 |
646 } |
888 } |
647 |
889 |
648 /** |
890 /** Plugin_Upgrader class */ |
649 * Plugin Upgrader class for WordPress Plugins, It is designed to upgrade/install plugins from a local zip, remote zip URL, or uploaded zip file. |
891 require_once ABSPATH . 'wp-admin/includes/class-plugin-upgrader.php'; |
650 * |
892 |
651 * @package WordPress |
893 /** Theme_Upgrader class */ |
652 * @subpackage Upgrader |
894 require_once ABSPATH . 'wp-admin/includes/class-theme-upgrader.php'; |
653 * @since 2.8.0 |
895 |
654 */ |
896 /** Language_Pack_Upgrader class */ |
655 class Plugin_Upgrader extends WP_Upgrader { |
897 require_once ABSPATH . 'wp-admin/includes/class-language-pack-upgrader.php'; |
656 |
898 |
657 /** |
899 /** Core_Upgrader class */ |
658 * Plugin upgrade result. |
900 require_once ABSPATH . 'wp-admin/includes/class-core-upgrader.php'; |
659 * |
901 |
660 * @since 2.8.0 |
902 /** File_Upload_Upgrader class */ |
661 * @var array|WP_Error $result |
903 require_once ABSPATH . 'wp-admin/includes/class-file-upload-upgrader.php'; |
662 * @see WP_Upgrader::$result |
904 |
663 */ |
905 /** WP_Automatic_Updater class */ |
664 public $result; |
906 require_once ABSPATH . 'wp-admin/includes/class-wp-automatic-updater.php'; |
665 |
|
666 /** |
|
667 * Whether a bulk upgrade/install is being performed. |
|
668 * |
|
669 * @since 2.9.0 |
|
670 * @var bool $bulk |
|
671 */ |
|
672 public $bulk = false; |
|
673 |
|
674 /** |
|
675 * Initialize the upgrade strings. |
|
676 * |
|
677 * @since 2.8.0 |
|
678 */ |
|
679 public function upgrade_strings() { |
|
680 $this->strings['up_to_date'] = __('The plugin is at the latest version.'); |
|
681 $this->strings['no_package'] = __('Update package not available.'); |
|
682 $this->strings['downloading_package'] = __('Downloading update from <span class="code">%s</span>…'); |
|
683 $this->strings['unpack_package'] = __('Unpacking the update…'); |
|
684 $this->strings['remove_old'] = __('Removing the old version of the plugin…'); |
|
685 $this->strings['remove_old_failed'] = __('Could not remove the old plugin.'); |
|
686 $this->strings['process_failed'] = __('Plugin update failed.'); |
|
687 $this->strings['process_success'] = __('Plugin updated successfully.'); |
|
688 $this->strings['process_bulk_success'] = __('Plugins updated successfully.'); |
|
689 } |
|
690 |
|
691 /** |
|
692 * Initialize the install strings. |
|
693 * |
|
694 * @since 2.8.0 |
|
695 */ |
|
696 public function install_strings() { |
|
697 $this->strings['no_package'] = __('Install package not available.'); |
|
698 $this->strings['downloading_package'] = __('Downloading install package from <span class="code">%s</span>…'); |
|
699 $this->strings['unpack_package'] = __('Unpacking the package…'); |
|
700 $this->strings['installing_package'] = __('Installing the plugin…'); |
|
701 $this->strings['no_files'] = __('The plugin contains no files.'); |
|
702 $this->strings['process_failed'] = __('Plugin install failed.'); |
|
703 $this->strings['process_success'] = __('Plugin installed successfully.'); |
|
704 } |
|
705 |
|
706 /** |
|
707 * Install a plugin package. |
|
708 * |
|
709 * @since 2.8.0 |
|
710 * @since 3.7.0 The `$args` parameter was added, making clearing the plugin update cache optional. |
|
711 * |
|
712 * @param string $package The full local path or URI of the package. |
|
713 * @param array $args { |
|
714 * Optional. Other arguments for installing a plugin package. Default empty array. |
|
715 * |
|
716 * @type bool $clear_update_cache Whether to clear the plugin updates cache if successful. |
|
717 * Default true. |
|
718 * } |
|
719 * |
|
720 * @return bool|WP_Error True if the install was successful, false or a WP_Error otherwise. |
|
721 */ |
|
722 public function install( $package, $args = array() ) { |
|
723 |
|
724 $defaults = array( |
|
725 'clear_update_cache' => true, |
|
726 ); |
|
727 $parsed_args = wp_parse_args( $args, $defaults ); |
|
728 |
|
729 $this->init(); |
|
730 $this->install_strings(); |
|
731 |
|
732 add_filter('upgrader_source_selection', array($this, 'check_package') ); |
|
733 |
|
734 $this->run( array( |
|
735 'package' => $package, |
|
736 'destination' => WP_PLUGIN_DIR, |
|
737 'clear_destination' => false, // Do not overwrite files. |
|
738 'clear_working' => true, |
|
739 'hook_extra' => array( |
|
740 'type' => 'plugin', |
|
741 'action' => 'install', |
|
742 ) |
|
743 ) ); |
|
744 |
|
745 remove_filter('upgrader_source_selection', array($this, 'check_package') ); |
|
746 |
|
747 if ( ! $this->result || is_wp_error($this->result) ) |
|
748 return $this->result; |
|
749 |
|
750 // Force refresh of plugin update information |
|
751 wp_clean_plugins_cache( $parsed_args['clear_update_cache'] ); |
|
752 |
|
753 return true; |
|
754 } |
|
755 |
|
756 /** |
|
757 * Upgrade a plugin. |
|
758 * |
|
759 * @since 2.8.0 |
|
760 * @since 3.7.0 The `$args` parameter was added, making clearing the plugin update cache optional. |
|
761 * |
|
762 * @param string $plugin The basename path to the main plugin file. |
|
763 * @param array $args { |
|
764 * Optional. Other arguments for upgrading a plugin package. Defualt empty array. |
|
765 * |
|
766 * @type bool $clear_update_cache Whether to clear the plugin updates cache if successful. |
|
767 * Default true. |
|
768 * } |
|
769 * @return bool|WP_Error True if the upgrade was successful, false or a {@see WP_Error} object otherwise. |
|
770 */ |
|
771 public function upgrade( $plugin, $args = array() ) { |
|
772 |
|
773 $defaults = array( |
|
774 'clear_update_cache' => true, |
|
775 ); |
|
776 $parsed_args = wp_parse_args( $args, $defaults ); |
|
777 |
|
778 $this->init(); |
|
779 $this->upgrade_strings(); |
|
780 |
|
781 $current = get_site_transient( 'update_plugins' ); |
|
782 if ( !isset( $current->response[ $plugin ] ) ) { |
|
783 $this->skin->before(); |
|
784 $this->skin->set_result(false); |
|
785 $this->skin->error('up_to_date'); |
|
786 $this->skin->after(); |
|
787 return false; |
|
788 } |
|
789 |
|
790 // Get the URL to the zip file |
|
791 $r = $current->response[ $plugin ]; |
|
792 |
|
793 add_filter('upgrader_pre_install', array($this, 'deactivate_plugin_before_upgrade'), 10, 2); |
|
794 add_filter('upgrader_clear_destination', array($this, 'delete_old_plugin'), 10, 4); |
|
795 //'source_selection' => array($this, 'source_selection'), //there's a trac ticket to move up the directory for zip's which are made a bit differently, useful for non-.org plugins. |
|
796 |
|
797 $this->run( array( |
|
798 'package' => $r->package, |
|
799 'destination' => WP_PLUGIN_DIR, |
|
800 'clear_destination' => true, |
|
801 'clear_working' => true, |
|
802 'hook_extra' => array( |
|
803 'plugin' => $plugin, |
|
804 'type' => 'plugin', |
|
805 'action' => 'update', |
|
806 ), |
|
807 ) ); |
|
808 |
|
809 // Cleanup our hooks, in case something else does a upgrade on this connection. |
|
810 remove_filter('upgrader_pre_install', array($this, 'deactivate_plugin_before_upgrade')); |
|
811 remove_filter('upgrader_clear_destination', array($this, 'delete_old_plugin')); |
|
812 |
|
813 if ( ! $this->result || is_wp_error($this->result) ) |
|
814 return $this->result; |
|
815 |
|
816 // Force refresh of plugin update information |
|
817 wp_clean_plugins_cache( $parsed_args['clear_update_cache'] ); |
|
818 |
|
819 return true; |
|
820 } |
|
821 |
|
822 /** |
|
823 * Bulk upgrade several plugins at once. |
|
824 * |
|
825 * @since 2.8.0 |
|
826 * @since 3.7.0 The `$args` parameter was added, making clearing the plugin update cache optional. |
|
827 * |
|
828 * @param array $plugins Array of the basename paths of the plugins' main files. |
|
829 * @param array $args { |
|
830 * Optional. Other arguments for upgrading several plugins at once. Default empty array. |
|
831 * |
|
832 * @type bool $clear_update_cache Whether to clear the plugin updates cache if successful. |
|
833 * Default true. |
|
834 * } |
|
835 * |
|
836 * @return array|false An array of results indexed by plugin file, or false if unable to connect to the filesystem. |
|
837 */ |
|
838 public function bulk_upgrade( $plugins, $args = array() ) { |
|
839 |
|
840 $defaults = array( |
|
841 'clear_update_cache' => true, |
|
842 ); |
|
843 $parsed_args = wp_parse_args( $args, $defaults ); |
|
844 |
|
845 $this->init(); |
|
846 $this->bulk = true; |
|
847 $this->upgrade_strings(); |
|
848 |
|
849 $current = get_site_transient( 'update_plugins' ); |
|
850 |
|
851 add_filter('upgrader_clear_destination', array($this, 'delete_old_plugin'), 10, 4); |
|
852 |
|
853 $this->skin->header(); |
|
854 |
|
855 // Connect to the Filesystem first. |
|
856 $res = $this->fs_connect( array(WP_CONTENT_DIR, WP_PLUGIN_DIR) ); |
|
857 if ( ! $res ) { |
|
858 $this->skin->footer(); |
|
859 return false; |
|
860 } |
|
861 |
|
862 $this->skin->bulk_header(); |
|
863 |
|
864 // Only start maintenance mode if: |
|
865 // - running Multisite and there are one or more plugins specified, OR |
|
866 // - a plugin with an update available is currently active. |
|
867 // @TODO: For multisite, maintenance mode should only kick in for individual sites if at all possible. |
|
868 $maintenance = ( is_multisite() && ! empty( $plugins ) ); |
|
869 foreach ( $plugins as $plugin ) |
|
870 $maintenance = $maintenance || ( is_plugin_active( $plugin ) && isset( $current->response[ $plugin] ) ); |
|
871 if ( $maintenance ) |
|
872 $this->maintenance_mode(true); |
|
873 |
|
874 $results = array(); |
|
875 |
|
876 $this->update_count = count($plugins); |
|
877 $this->update_current = 0; |
|
878 foreach ( $plugins as $plugin ) { |
|
879 $this->update_current++; |
|
880 $this->skin->plugin_info = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin, false, true); |
|
881 |
|
882 if ( !isset( $current->response[ $plugin ] ) ) { |
|
883 $this->skin->set_result('up_to_date'); |
|
884 $this->skin->before(); |
|
885 $this->skin->feedback('up_to_date'); |
|
886 $this->skin->after(); |
|
887 $results[$plugin] = true; |
|
888 continue; |
|
889 } |
|
890 |
|
891 // Get the URL to the zip file |
|
892 $r = $current->response[ $plugin ]; |
|
893 |
|
894 $this->skin->plugin_active = is_plugin_active($plugin); |
|
895 |
|
896 $result = $this->run( array( |
|
897 'package' => $r->package, |
|
898 'destination' => WP_PLUGIN_DIR, |
|
899 'clear_destination' => true, |
|
900 'clear_working' => true, |
|
901 'is_multi' => true, |
|
902 'hook_extra' => array( |
|
903 'plugin' => $plugin |
|
904 ) |
|
905 ) ); |
|
906 |
|
907 $results[$plugin] = $this->result; |
|
908 |
|
909 // Prevent credentials auth screen from displaying multiple times |
|
910 if ( false === $result ) |
|
911 break; |
|
912 } //end foreach $plugins |
|
913 |
|
914 $this->maintenance_mode(false); |
|
915 |
|
916 /** |
|
917 * Fires when the bulk upgrader process is complete. |
|
918 * |
|
919 * @since 3.6.0 |
|
920 * |
|
921 * @param Plugin_Upgrader $this Plugin_Upgrader instance. In other contexts, $this, might |
|
922 * be a Theme_Upgrader or Core_Upgrade instance. |
|
923 * @param array $data { |
|
924 * Array of bulk item update data. |
|
925 * |
|
926 * @type string $action Type of action. Default 'update'. |
|
927 * @type string $type Type of update process. Accepts 'plugin', 'theme', or 'core'. |
|
928 * @type bool $bulk Whether the update process is a bulk update. Default true. |
|
929 * @type array $packages Array of plugin, theme, or core packages to update. |
|
930 * } |
|
931 */ |
|
932 do_action( 'upgrader_process_complete', $this, array( |
|
933 'action' => 'update', |
|
934 'type' => 'plugin', |
|
935 'bulk' => true, |
|
936 'plugins' => $plugins, |
|
937 ) ); |
|
938 |
|
939 $this->skin->bulk_footer(); |
|
940 |
|
941 $this->skin->footer(); |
|
942 |
|
943 // Cleanup our hooks, in case something else does a upgrade on this connection. |
|
944 remove_filter('upgrader_clear_destination', array($this, 'delete_old_plugin')); |
|
945 |
|
946 // Force refresh of plugin update information |
|
947 wp_clean_plugins_cache( $parsed_args['clear_update_cache'] ); |
|
948 |
|
949 return $results; |
|
950 } |
|
951 |
|
952 /** |
|
953 * Check a source package to be sure it contains a plugin. |
|
954 * |
|
955 * This function is added to the {@see 'upgrader_source_selection'} filter by |
|
956 * {@see Plugin_Upgrader::install()}. |
|
957 * |
|
958 * @since 3.3.0 |
|
959 * |
|
960 * @param string $source The path to the downloaded package source. |
|
961 * @return string|WP_Error The source as passed, or a {@see WP_Error} object if no plugins were found. |
|
962 */ |
|
963 public function check_package($source) { |
|
964 global $wp_filesystem; |
|
965 |
|
966 if ( is_wp_error($source) ) |
|
967 return $source; |
|
968 |
|
969 $working_directory = str_replace( $wp_filesystem->wp_content_dir(), trailingslashit(WP_CONTENT_DIR), $source); |
|
970 if ( ! is_dir($working_directory) ) // Sanity check, if the above fails, let's not prevent installation. |
|
971 return $source; |
|
972 |
|
973 // Check the folder contains at least 1 valid plugin. |
|
974 $plugins_found = false; |
|
975 foreach ( glob( $working_directory . '*.php' ) as $file ) { |
|
976 $info = get_plugin_data($file, false, false); |
|
977 if ( !empty( $info['Name'] ) ) { |
|
978 $plugins_found = true; |
|
979 break; |
|
980 } |
|
981 } |
|
982 |
|
983 if ( ! $plugins_found ) |
|
984 return new WP_Error( 'incompatible_archive_no_plugins', $this->strings['incompatible_archive'], __( 'No valid plugins were found.' ) ); |
|
985 |
|
986 return $source; |
|
987 } |
|
988 |
|
989 /** |
|
990 * Retrieve the path to the file that contains the plugin info. |
|
991 * |
|
992 * This isn't used internally in the class, but is called by the skins. |
|
993 * |
|
994 * @since 2.8.0 |
|
995 * |
|
996 * @return string|false The full path to the main plugin file, or false. |
|
997 */ |
|
998 public function plugin_info() { |
|
999 if ( ! is_array($this->result) ) |
|
1000 return false; |
|
1001 if ( empty($this->result['destination_name']) ) |
|
1002 return false; |
|
1003 |
|
1004 $plugin = get_plugins('/' . $this->result['destination_name']); //Ensure to pass with leading slash |
|
1005 if ( empty($plugin) ) |
|
1006 return false; |
|
1007 |
|
1008 $pluginfiles = array_keys($plugin); //Assume the requested plugin is the first in the list |
|
1009 |
|
1010 return $this->result['destination_name'] . '/' . $pluginfiles[0]; |
|
1011 } |
|
1012 |
|
1013 /** |
|
1014 * Deactivates a plugin before it is upgraded. |
|
1015 * |
|
1016 * Hooked to the {@see 'upgrader_pre_install'} filter by {@see Plugin_Upgrader::upgrade()}. |
|
1017 * |
|
1018 * @since 2.8.0 |
|
1019 * @since 4.1.0 Added a return value. |
|
1020 * |
|
1021 * @param bool|WP_Error $return Upgrade offer return. |
|
1022 * @param array $plugin Plugin package arguments. |
|
1023 * @return bool|WP_Error The passed in $return param or {@see WP_Error}. |
|
1024 */ |
|
1025 public function deactivate_plugin_before_upgrade($return, $plugin) { |
|
1026 |
|
1027 if ( is_wp_error($return) ) //Bypass. |
|
1028 return $return; |
|
1029 |
|
1030 // When in cron (background updates) don't deactivate the plugin, as we require a browser to reactivate it |
|
1031 if ( defined( 'DOING_CRON' ) && DOING_CRON ) |
|
1032 return $return; |
|
1033 |
|
1034 $plugin = isset($plugin['plugin']) ? $plugin['plugin'] : ''; |
|
1035 if ( empty($plugin) ) |
|
1036 return new WP_Error('bad_request', $this->strings['bad_request']); |
|
1037 |
|
1038 if ( is_plugin_active($plugin) ) { |
|
1039 //Deactivate the plugin silently, Prevent deactivation hooks from running. |
|
1040 deactivate_plugins($plugin, true); |
|
1041 } |
|
1042 |
|
1043 return $return; |
|
1044 } |
|
1045 |
|
1046 /** |
|
1047 * Delete the old plugin during an upgrade. |
|
1048 * |
|
1049 * Hooked to the {@see 'upgrader_clear_destination'} filter by |
|
1050 * {@see Plugin_Upgrader::upgrade()} and {@see Plugin_Upgrader::bulk_upgrade()}. |
|
1051 * |
|
1052 * @since 2.8.0 |
|
1053 */ |
|
1054 public function delete_old_plugin($removed, $local_destination, $remote_destination, $plugin) { |
|
1055 global $wp_filesystem; |
|
1056 |
|
1057 if ( is_wp_error($removed) ) |
|
1058 return $removed; //Pass errors through. |
|
1059 |
|
1060 $plugin = isset($plugin['plugin']) ? $plugin['plugin'] : ''; |
|
1061 if ( empty($plugin) ) |
|
1062 return new WP_Error('bad_request', $this->strings['bad_request']); |
|
1063 |
|
1064 $plugins_dir = $wp_filesystem->wp_plugins_dir(); |
|
1065 $this_plugin_dir = trailingslashit( dirname($plugins_dir . $plugin) ); |
|
1066 |
|
1067 if ( ! $wp_filesystem->exists($this_plugin_dir) ) //If it's already vanished. |
|
1068 return $removed; |
|
1069 |
|
1070 // If plugin is in its own directory, recursively delete the directory. |
|
1071 if ( strpos($plugin, '/') && $this_plugin_dir != $plugins_dir ) //base check on if plugin includes directory separator AND that it's not the root plugin folder |
|
1072 $deleted = $wp_filesystem->delete($this_plugin_dir, true); |
|
1073 else |
|
1074 $deleted = $wp_filesystem->delete($plugins_dir . $plugin); |
|
1075 |
|
1076 if ( ! $deleted ) |
|
1077 return new WP_Error('remove_old_failed', $this->strings['remove_old_failed']); |
|
1078 |
|
1079 return true; |
|
1080 } |
|
1081 } |
|
1082 |
|
1083 /** |
|
1084 * Theme Upgrader class for WordPress Themes, It is designed to upgrade/install themes from a local zip, remote zip URL, or uploaded zip file. |
|
1085 * |
|
1086 * @package WordPress |
|
1087 * @subpackage Upgrader |
|
1088 * @since 2.8.0 |
|
1089 */ |
|
1090 class Theme_Upgrader extends WP_Upgrader { |
|
1091 |
|
1092 /** |
|
1093 * Result of the theme upgrade offer. |
|
1094 * |
|
1095 * @since 2.8.0 |
|
1096 * @var array|WP_Erorr $result |
|
1097 * @see WP_Upgrader::$result |
|
1098 */ |
|
1099 public $result; |
|
1100 |
|
1101 /** |
|
1102 * Whether multiple plugins are being upgraded/installed in bulk. |
|
1103 * |
|
1104 * @since 2.9.0 |
|
1105 * @var bool $bulk |
|
1106 */ |
|
1107 public $bulk = false; |
|
1108 |
|
1109 /** |
|
1110 * Initialize the upgrade strings. |
|
1111 * |
|
1112 * @since 2.8.0 |
|
1113 */ |
|
1114 public function upgrade_strings() { |
|
1115 $this->strings['up_to_date'] = __('The theme is at the latest version.'); |
|
1116 $this->strings['no_package'] = __('Update package not available.'); |
|
1117 $this->strings['downloading_package'] = __('Downloading update from <span class="code">%s</span>…'); |
|
1118 $this->strings['unpack_package'] = __('Unpacking the update…'); |
|
1119 $this->strings['remove_old'] = __('Removing the old version of the theme…'); |
|
1120 $this->strings['remove_old_failed'] = __('Could not remove the old theme.'); |
|
1121 $this->strings['process_failed'] = __('Theme update failed.'); |
|
1122 $this->strings['process_success'] = __('Theme updated successfully.'); |
|
1123 } |
|
1124 |
|
1125 /** |
|
1126 * Initialize the install strings. |
|
1127 * |
|
1128 * @since 2.8.0 |
|
1129 */ |
|
1130 public function install_strings() { |
|
1131 $this->strings['no_package'] = __('Install package not available.'); |
|
1132 $this->strings['downloading_package'] = __('Downloading install package from <span class="code">%s</span>…'); |
|
1133 $this->strings['unpack_package'] = __('Unpacking the package…'); |
|
1134 $this->strings['installing_package'] = __('Installing the theme…'); |
|
1135 $this->strings['no_files'] = __('The theme contains no files.'); |
|
1136 $this->strings['process_failed'] = __('Theme install failed.'); |
|
1137 $this->strings['process_success'] = __('Theme installed successfully.'); |
|
1138 /* translators: 1: theme name, 2: version */ |
|
1139 $this->strings['process_success_specific'] = __('Successfully installed the theme <strong>%1$s %2$s</strong>.'); |
|
1140 $this->strings['parent_theme_search'] = __('This theme requires a parent theme. Checking if it is installed…'); |
|
1141 /* translators: 1: theme name, 2: version */ |
|
1142 $this->strings['parent_theme_prepare_install'] = __('Preparing to install <strong>%1$s %2$s</strong>…'); |
|
1143 /* translators: 1: theme name, 2: version */ |
|
1144 $this->strings['parent_theme_currently_installed'] = __('The parent theme, <strong>%1$s %2$s</strong>, is currently installed.'); |
|
1145 /* translators: 1: theme name, 2: version */ |
|
1146 $this->strings['parent_theme_install_success'] = __('Successfully installed the parent theme, <strong>%1$s %2$s</strong>.'); |
|
1147 $this->strings['parent_theme_not_found'] = __('<strong>The parent theme could not be found.</strong> You will need to install the parent theme, <strong>%s</strong>, before you can use this child theme.'); |
|
1148 } |
|
1149 |
|
1150 /** |
|
1151 * Check if a child theme is being installed and we need to install its parent. |
|
1152 * |
|
1153 * Hooked to the {@see 'upgrader_post_install'} filter by {@see Theme_Upgrader::install()}. |
|
1154 * |
|
1155 * @since 3.4.0 |
|
1156 */ |
|
1157 public function check_parent_theme_filter( $install_result, $hook_extra, $child_result ) { |
|
1158 // Check to see if we need to install a parent theme |
|
1159 $theme_info = $this->theme_info(); |
|
1160 |
|
1161 if ( ! $theme_info->parent() ) |
|
1162 return $install_result; |
|
1163 |
|
1164 $this->skin->feedback( 'parent_theme_search' ); |
|
1165 |
|
1166 if ( ! $theme_info->parent()->errors() ) { |
|
1167 $this->skin->feedback( 'parent_theme_currently_installed', $theme_info->parent()->display('Name'), $theme_info->parent()->display('Version') ); |
|
1168 // We already have the theme, fall through. |
|
1169 return $install_result; |
|
1170 } |
|
1171 |
|
1172 // We don't have the parent theme, let's install it. |
|
1173 $api = themes_api('theme_information', array('slug' => $theme_info->get('Template'), 'fields' => array('sections' => false, 'tags' => false) ) ); //Save on a bit of bandwidth. |
|
1174 |
|
1175 if ( ! $api || is_wp_error($api) ) { |
|
1176 $this->skin->feedback( 'parent_theme_not_found', $theme_info->get('Template') ); |
|
1177 // Don't show activate or preview actions after install |
|
1178 add_filter('install_theme_complete_actions', array($this, 'hide_activate_preview_actions') ); |
|
1179 return $install_result; |
|
1180 } |
|
1181 |
|
1182 // Backup required data we're going to override: |
|
1183 $child_api = $this->skin->api; |
|
1184 $child_success_message = $this->strings['process_success']; |
|
1185 |
|
1186 // Override them |
|
1187 $this->skin->api = $api; |
|
1188 $this->strings['process_success_specific'] = $this->strings['parent_theme_install_success'];//, $api->name, $api->version); |
|
1189 |
|
1190 $this->skin->feedback('parent_theme_prepare_install', $api->name, $api->version); |
|
1191 |
|
1192 add_filter('install_theme_complete_actions', '__return_false', 999); // Don't show any actions after installing the theme. |
|
1193 |
|
1194 // Install the parent theme |
|
1195 $parent_result = $this->run( array( |
|
1196 'package' => $api->download_link, |
|
1197 'destination' => get_theme_root(), |
|
1198 'clear_destination' => false, //Do not overwrite files. |
|
1199 'clear_working' => true |
|
1200 ) ); |
|
1201 |
|
1202 if ( is_wp_error($parent_result) ) |
|
1203 add_filter('install_theme_complete_actions', array($this, 'hide_activate_preview_actions') ); |
|
1204 |
|
1205 // Start cleaning up after the parents installation |
|
1206 remove_filter('install_theme_complete_actions', '__return_false', 999); |
|
1207 |
|
1208 // Reset child's result and data |
|
1209 $this->result = $child_result; |
|
1210 $this->skin->api = $child_api; |
|
1211 $this->strings['process_success'] = $child_success_message; |
|
1212 |
|
1213 return $install_result; |
|
1214 } |
|
1215 |
|
1216 /** |
|
1217 * Don't display the activate and preview actions to the user. |
|
1218 * |
|
1219 * Hooked to the {@see 'install_theme_complete_actions'} filter by |
|
1220 * {@see Theme_Upgrader::check_parent_theme_filter()} when installing |
|
1221 * a child theme and installing the parent theme fails. |
|
1222 * |
|
1223 * @since 3.4.0 |
|
1224 * |
|
1225 * @param array $actions Preview actions. |
|
1226 */ |
|
1227 public function hide_activate_preview_actions( $actions ) { |
|
1228 unset($actions['activate'], $actions['preview']); |
|
1229 return $actions; |
|
1230 } |
|
1231 |
|
1232 /** |
|
1233 * Install a theme package. |
|
1234 * |
|
1235 * @since 2.8.0 |
|
1236 * @since 3.7.0 The `$args` parameter was added, making clearing the update cache optional. |
|
1237 * |
|
1238 * @param string $package The full local path or URI of the package. |
|
1239 * @param array $args { |
|
1240 * Optional. Other arguments for installing a theme package. Default empty array. |
|
1241 * |
|
1242 * @type bool $clear_update_cache Whether to clear the updates cache if successful. |
|
1243 * Default true. |
|
1244 * } |
|
1245 * |
|
1246 * @return bool|WP_Error True if the install was successful, false or a {@see WP_Error} object otherwise. |
|
1247 */ |
|
1248 public function install( $package, $args = array() ) { |
|
1249 |
|
1250 $defaults = array( |
|
1251 'clear_update_cache' => true, |
|
1252 ); |
|
1253 $parsed_args = wp_parse_args( $args, $defaults ); |
|
1254 |
|
1255 $this->init(); |
|
1256 $this->install_strings(); |
|
1257 |
|
1258 add_filter('upgrader_source_selection', array($this, 'check_package') ); |
|
1259 add_filter('upgrader_post_install', array($this, 'check_parent_theme_filter'), 10, 3); |
|
1260 |
|
1261 $this->run( array( |
|
1262 'package' => $package, |
|
1263 'destination' => get_theme_root(), |
|
1264 'clear_destination' => false, //Do not overwrite files. |
|
1265 'clear_working' => true, |
|
1266 'hook_extra' => array( |
|
1267 'type' => 'theme', |
|
1268 'action' => 'install', |
|
1269 ), |
|
1270 ) ); |
|
1271 |
|
1272 remove_filter('upgrader_source_selection', array($this, 'check_package') ); |
|
1273 remove_filter('upgrader_post_install', array($this, 'check_parent_theme_filter')); |
|
1274 |
|
1275 if ( ! $this->result || is_wp_error($this->result) ) |
|
1276 return $this->result; |
|
1277 |
|
1278 // Refresh the Theme Update information |
|
1279 wp_clean_themes_cache( $parsed_args['clear_update_cache'] ); |
|
1280 |
|
1281 return true; |
|
1282 } |
|
1283 |
|
1284 /** |
|
1285 * Upgrade a theme. |
|
1286 * |
|
1287 * @since 2.8.0 |
|
1288 * @since 3.7.0 The `$args` parameter was added, making clearing the update cache optional. |
|
1289 * |
|
1290 * @param string $theme The theme slug. |
|
1291 * @param array $args { |
|
1292 * Optional. Other arguments for upgrading a theme. Default empty array. |
|
1293 * |
|
1294 * @type bool $clear_update_cache Whether to clear the update cache if successful. |
|
1295 * Default true. |
|
1296 * } |
|
1297 * @return bool|WP_Error True if the upgrade was successful, false or a {@see WP_Error} object otherwise. |
|
1298 */ |
|
1299 public function upgrade( $theme, $args = array() ) { |
|
1300 |
|
1301 $defaults = array( |
|
1302 'clear_update_cache' => true, |
|
1303 ); |
|
1304 $parsed_args = wp_parse_args( $args, $defaults ); |
|
1305 |
|
1306 $this->init(); |
|
1307 $this->upgrade_strings(); |
|
1308 |
|
1309 // Is an update available? |
|
1310 $current = get_site_transient( 'update_themes' ); |
|
1311 if ( !isset( $current->response[ $theme ] ) ) { |
|
1312 $this->skin->before(); |
|
1313 $this->skin->set_result(false); |
|
1314 $this->skin->error( 'up_to_date' ); |
|
1315 $this->skin->after(); |
|
1316 return false; |
|
1317 } |
|
1318 |
|
1319 $r = $current->response[ $theme ]; |
|
1320 |
|
1321 add_filter('upgrader_pre_install', array($this, 'current_before'), 10, 2); |
|
1322 add_filter('upgrader_post_install', array($this, 'current_after'), 10, 2); |
|
1323 add_filter('upgrader_clear_destination', array($this, 'delete_old_theme'), 10, 4); |
|
1324 |
|
1325 $this->run( array( |
|
1326 'package' => $r['package'], |
|
1327 'destination' => get_theme_root( $theme ), |
|
1328 'clear_destination' => true, |
|
1329 'clear_working' => true, |
|
1330 'hook_extra' => array( |
|
1331 'theme' => $theme, |
|
1332 'type' => 'theme', |
|
1333 'action' => 'update', |
|
1334 ), |
|
1335 ) ); |
|
1336 |
|
1337 remove_filter('upgrader_pre_install', array($this, 'current_before')); |
|
1338 remove_filter('upgrader_post_install', array($this, 'current_after')); |
|
1339 remove_filter('upgrader_clear_destination', array($this, 'delete_old_theme')); |
|
1340 |
|
1341 if ( ! $this->result || is_wp_error($this->result) ) |
|
1342 return $this->result; |
|
1343 |
|
1344 wp_clean_themes_cache( $parsed_args['clear_update_cache'] ); |
|
1345 |
|
1346 return true; |
|
1347 } |
|
1348 |
|
1349 /** |
|
1350 * Upgrade several themes at once. |
|
1351 * |
|
1352 * @since 3.0.0 |
|
1353 * @since 3.7.0 The `$args` parameter was added, making clearing the update cache optional. |
|
1354 * |
|
1355 * @param array $themes The theme slugs. |
|
1356 * @param array $args { |
|
1357 * Optional. Other arguments for upgrading several themes at once. Default empty array. |
|
1358 * |
|
1359 * @type bool $clear_update_cache Whether to clear the update cache if successful. |
|
1360 * Default true. |
|
1361 * } |
|
1362 * @return array[]|false An array of results, or false if unable to connect to the filesystem. |
|
1363 */ |
|
1364 public function bulk_upgrade( $themes, $args = array() ) { |
|
1365 |
|
1366 $defaults = array( |
|
1367 'clear_update_cache' => true, |
|
1368 ); |
|
1369 $parsed_args = wp_parse_args( $args, $defaults ); |
|
1370 |
|
1371 $this->init(); |
|
1372 $this->bulk = true; |
|
1373 $this->upgrade_strings(); |
|
1374 |
|
1375 $current = get_site_transient( 'update_themes' ); |
|
1376 |
|
1377 add_filter('upgrader_pre_install', array($this, 'current_before'), 10, 2); |
|
1378 add_filter('upgrader_post_install', array($this, 'current_after'), 10, 2); |
|
1379 add_filter('upgrader_clear_destination', array($this, 'delete_old_theme'), 10, 4); |
|
1380 |
|
1381 $this->skin->header(); |
|
1382 |
|
1383 // Connect to the Filesystem first. |
|
1384 $res = $this->fs_connect( array(WP_CONTENT_DIR) ); |
|
1385 if ( ! $res ) { |
|
1386 $this->skin->footer(); |
|
1387 return false; |
|
1388 } |
|
1389 |
|
1390 $this->skin->bulk_header(); |
|
1391 |
|
1392 // Only start maintenance mode if: |
|
1393 // - running Multisite and there are one or more themes specified, OR |
|
1394 // - a theme with an update available is currently in use. |
|
1395 // @TODO: For multisite, maintenance mode should only kick in for individual sites if at all possible. |
|
1396 $maintenance = ( is_multisite() && ! empty( $themes ) ); |
|
1397 foreach ( $themes as $theme ) |
|
1398 $maintenance = $maintenance || $theme == get_stylesheet() || $theme == get_template(); |
|
1399 if ( $maintenance ) |
|
1400 $this->maintenance_mode(true); |
|
1401 |
|
1402 $results = array(); |
|
1403 |
|
1404 $this->update_count = count($themes); |
|
1405 $this->update_current = 0; |
|
1406 foreach ( $themes as $theme ) { |
|
1407 $this->update_current++; |
|
1408 |
|
1409 $this->skin->theme_info = $this->theme_info($theme); |
|
1410 |
|
1411 if ( !isset( $current->response[ $theme ] ) ) { |
|
1412 $this->skin->set_result(true); |
|
1413 $this->skin->before(); |
|
1414 $this->skin->feedback( 'up_to_date' ); |
|
1415 $this->skin->after(); |
|
1416 $results[$theme] = true; |
|
1417 continue; |
|
1418 } |
|
1419 |
|
1420 // Get the URL to the zip file |
|
1421 $r = $current->response[ $theme ]; |
|
1422 |
|
1423 $result = $this->run( array( |
|
1424 'package' => $r['package'], |
|
1425 'destination' => get_theme_root( $theme ), |
|
1426 'clear_destination' => true, |
|
1427 'clear_working' => true, |
|
1428 'is_multi' => true, |
|
1429 'hook_extra' => array( |
|
1430 'theme' => $theme |
|
1431 ), |
|
1432 ) ); |
|
1433 |
|
1434 $results[$theme] = $this->result; |
|
1435 |
|
1436 // Prevent credentials auth screen from displaying multiple times |
|
1437 if ( false === $result ) |
|
1438 break; |
|
1439 } //end foreach $plugins |
|
1440 |
|
1441 $this->maintenance_mode(false); |
|
1442 |
|
1443 /** This action is documented in wp-admin/includes/class-wp-upgrader.php */ |
|
1444 do_action( 'upgrader_process_complete', $this, array( |
|
1445 'action' => 'update', |
|
1446 'type' => 'theme', |
|
1447 'bulk' => true, |
|
1448 'themes' => $themes, |
|
1449 ) ); |
|
1450 |
|
1451 $this->skin->bulk_footer(); |
|
1452 |
|
1453 $this->skin->footer(); |
|
1454 |
|
1455 // Cleanup our hooks, in case something else does a upgrade on this connection. |
|
1456 remove_filter('upgrader_pre_install', array($this, 'current_before')); |
|
1457 remove_filter('upgrader_post_install', array($this, 'current_after')); |
|
1458 remove_filter('upgrader_clear_destination', array($this, 'delete_old_theme')); |
|
1459 |
|
1460 // Refresh the Theme Update information |
|
1461 wp_clean_themes_cache( $parsed_args['clear_update_cache'] ); |
|
1462 |
|
1463 return $results; |
|
1464 } |
|
1465 |
|
1466 /** |
|
1467 * Check that the package source contains a valid theme. |
|
1468 * |
|
1469 * Hooked to the {@see 'upgrader_source_selection'} filter by {@see Theme_Upgrader::install()}. |
|
1470 * It will return an error if the theme doesn't have style.css or index.php |
|
1471 * files. |
|
1472 * |
|
1473 * @since 3.3.0 |
|
1474 * |
|
1475 * @param string $source The full path to the package source. |
|
1476 * @return string|WP_Error The source or a WP_Error. |
|
1477 */ |
|
1478 public function check_package( $source ) { |
|
1479 global $wp_filesystem; |
|
1480 |
|
1481 if ( is_wp_error($source) ) |
|
1482 return $source; |
|
1483 |
|
1484 // Check the folder contains a valid theme |
|
1485 $working_directory = str_replace( $wp_filesystem->wp_content_dir(), trailingslashit(WP_CONTENT_DIR), $source); |
|
1486 if ( ! is_dir($working_directory) ) // Sanity check, if the above fails, let's not prevent installation. |
|
1487 return $source; |
|
1488 |
|
1489 // A proper archive should have a style.css file in the single subdirectory |
|
1490 if ( ! file_exists( $working_directory . 'style.css' ) ) |
|
1491 return new WP_Error( 'incompatible_archive_theme_no_style', $this->strings['incompatible_archive'], __( 'The theme is missing the <code>style.css</code> stylesheet.' ) ); |
|
1492 |
|
1493 $info = get_file_data( $working_directory . 'style.css', array( 'Name' => 'Theme Name', 'Template' => 'Template' ) ); |
|
1494 |
|
1495 if ( empty( $info['Name'] ) ) |
|
1496 return new WP_Error( 'incompatible_archive_theme_no_name', $this->strings['incompatible_archive'], __( "The <code>style.css</code> stylesheet doesn't contain a valid theme header." ) ); |
|
1497 |
|
1498 // If it's not a child theme, it must have at least an index.php to be legit. |
|
1499 if ( empty( $info['Template'] ) && ! file_exists( $working_directory . 'index.php' ) ) |
|
1500 return new WP_Error( 'incompatible_archive_theme_no_index', $this->strings['incompatible_archive'], __( 'The theme is missing the <code>index.php</code> file.' ) ); |
|
1501 |
|
1502 return $source; |
|
1503 } |
|
1504 |
|
1505 /** |
|
1506 * Turn on maintenance mode before attempting to upgrade the current theme. |
|
1507 * |
|
1508 * Hooked to the {@see 'upgrader_pre_install'} filter by {@see Theme_Upgrader::upgrade()} and |
|
1509 * {@see Theme_Upgrader::bulk_upgrade()}. |
|
1510 * |
|
1511 * @since 2.8.0 |
|
1512 */ |
|
1513 public function current_before($return, $theme) { |
|
1514 |
|
1515 if ( is_wp_error($return) ) |
|
1516 return $return; |
|
1517 |
|
1518 $theme = isset($theme['theme']) ? $theme['theme'] : ''; |
|
1519 |
|
1520 if ( $theme != get_stylesheet() ) //If not current |
|
1521 return $return; |
|
1522 //Change to maintenance mode now. |
|
1523 if ( ! $this->bulk ) |
|
1524 $this->maintenance_mode(true); |
|
1525 |
|
1526 return $return; |
|
1527 } |
|
1528 |
|
1529 /** |
|
1530 * Turn off maintenance mode after upgrading the current theme. |
|
1531 * |
|
1532 * Hooked to the {@see 'upgrader_post_install'} filter by {@see Theme_Upgrader::upgrade()} |
|
1533 * and {@see Theme_Upgrader::bulk_upgrade()}. |
|
1534 * |
|
1535 * @since 2.8.0 |
|
1536 */ |
|
1537 public function current_after($return, $theme) { |
|
1538 if ( is_wp_error($return) ) |
|
1539 return $return; |
|
1540 |
|
1541 $theme = isset($theme['theme']) ? $theme['theme'] : ''; |
|
1542 |
|
1543 if ( $theme != get_stylesheet() ) // If not current |
|
1544 return $return; |
|
1545 |
|
1546 // Ensure stylesheet name hasn't changed after the upgrade: |
|
1547 if ( $theme == get_stylesheet() && $theme != $this->result['destination_name'] ) { |
|
1548 wp_clean_themes_cache(); |
|
1549 $stylesheet = $this->result['destination_name']; |
|
1550 switch_theme( $stylesheet ); |
|
1551 } |
|
1552 |
|
1553 //Time to remove maintenance mode |
|
1554 if ( ! $this->bulk ) |
|
1555 $this->maintenance_mode(false); |
|
1556 return $return; |
|
1557 } |
|
1558 |
|
1559 /** |
|
1560 * Delete the old theme during an upgrade. |
|
1561 * |
|
1562 * Hooked to the {@see 'upgrader_clear_destination'} filter by {@see Theme_Upgrader::upgrade()} |
|
1563 * and {@see Theme_Upgrader::bulk_upgrade()}. |
|
1564 * |
|
1565 * @since 2.8.0 |
|
1566 */ |
|
1567 public function delete_old_theme( $removed, $local_destination, $remote_destination, $theme ) { |
|
1568 global $wp_filesystem; |
|
1569 |
|
1570 if ( is_wp_error( $removed ) ) |
|
1571 return $removed; // Pass errors through. |
|
1572 |
|
1573 if ( ! isset( $theme['theme'] ) ) |
|
1574 return $removed; |
|
1575 |
|
1576 $theme = $theme['theme']; |
|
1577 $themes_dir = trailingslashit( $wp_filesystem->wp_themes_dir( $theme ) ); |
|
1578 if ( $wp_filesystem->exists( $themes_dir . $theme ) ) { |
|
1579 if ( ! $wp_filesystem->delete( $themes_dir . $theme, true ) ) |
|
1580 return false; |
|
1581 } |
|
1582 |
|
1583 return true; |
|
1584 } |
|
1585 |
|
1586 /** |
|
1587 * Get the WP_Theme object for a theme. |
|
1588 * |
|
1589 * @since 2.8.0 |
|
1590 * @since 3.0.0 The `$theme` argument was added. |
|
1591 * |
|
1592 * @param string $theme The directory name of the theme. This is optional, and if not supplied, |
|
1593 * the directory name from the last result will be used. |
|
1594 * @return WP_Theme|false The theme's info object, or false `$theme` is not supplied |
|
1595 * and the last result isn't set. |
|
1596 */ |
|
1597 public function theme_info($theme = null) { |
|
1598 |
|
1599 if ( empty($theme) ) { |
|
1600 if ( !empty($this->result['destination_name']) ) |
|
1601 $theme = $this->result['destination_name']; |
|
1602 else |
|
1603 return false; |
|
1604 } |
|
1605 return wp_get_theme( $theme ); |
|
1606 } |
|
1607 |
|
1608 } |
|
1609 |
|
1610 add_action( 'upgrader_process_complete', array( 'Language_Pack_Upgrader', 'async_upgrade' ), 20 ); |
|
1611 |
|
1612 /** |
|
1613 * Language pack upgrader, for updating translations of plugins, themes, and core. |
|
1614 * |
|
1615 * @package WordPress |
|
1616 * @subpackage Upgrader |
|
1617 * @since 3.7.0 |
|
1618 */ |
|
1619 class Language_Pack_Upgrader extends WP_Upgrader { |
|
1620 |
|
1621 /** |
|
1622 * Result of the language pack upgrade. |
|
1623 * |
|
1624 * @since 3.7.0 |
|
1625 * @var array|WP_Error $result |
|
1626 * @see WP_Upgrader::$result |
|
1627 */ |
|
1628 public $result; |
|
1629 |
|
1630 /** |
|
1631 * Whether a bulk upgrade/install is being performed. |
|
1632 * |
|
1633 * @since 3.7.0 |
|
1634 * @var bool $bulk |
|
1635 */ |
|
1636 public $bulk = true; |
|
1637 |
|
1638 /** |
|
1639 * Asynchronously upgrade language packs after other upgrades have been made. |
|
1640 * |
|
1641 * Hooked to the {@see 'upgrader_process_complete'} action by default. |
|
1642 * |
|
1643 * @since 3.7.0 |
|
1644 */ |
|
1645 public static function async_upgrade( $upgrader = false ) { |
|
1646 // Avoid recursion. |
|
1647 if ( $upgrader && $upgrader instanceof Language_Pack_Upgrader ) { |
|
1648 return; |
|
1649 } |
|
1650 |
|
1651 // Nothing to do? |
|
1652 $language_updates = wp_get_translation_updates(); |
|
1653 if ( ! $language_updates ) { |
|
1654 return; |
|
1655 } |
|
1656 |
|
1657 // Avoid messing with VCS installs, at least for now. |
|
1658 // Noted: this is not the ideal way to accomplish this. |
|
1659 $check_vcs = new WP_Automatic_Updater; |
|
1660 if ( $check_vcs->is_vcs_checkout( WP_CONTENT_DIR ) ) { |
|
1661 return; |
|
1662 } |
|
1663 |
|
1664 foreach ( $language_updates as $key => $language_update ) { |
|
1665 $update = ! empty( $language_update->autoupdate ); |
|
1666 |
|
1667 /** |
|
1668 * Filter whether to asynchronously update translation for core, a plugin, or a theme. |
|
1669 * |
|
1670 * @since 4.0.0 |
|
1671 * |
|
1672 * @param bool $update Whether to update. |
|
1673 * @param object $language_update The update offer. |
|
1674 */ |
|
1675 $update = apply_filters( 'async_update_translation', $update, $language_update ); |
|
1676 |
|
1677 if ( ! $update ) { |
|
1678 unset( $language_updates[ $key ] ); |
|
1679 } |
|
1680 } |
|
1681 |
|
1682 if ( empty( $language_updates ) ) { |
|
1683 return; |
|
1684 } |
|
1685 |
|
1686 $skin = new Language_Pack_Upgrader_Skin( array( |
|
1687 'skip_header_footer' => true, |
|
1688 ) ); |
|
1689 |
|
1690 $lp_upgrader = new Language_Pack_Upgrader( $skin ); |
|
1691 $lp_upgrader->bulk_upgrade( $language_updates ); |
|
1692 } |
|
1693 |
|
1694 /** |
|
1695 * Initialize the upgrade strings. |
|
1696 * |
|
1697 * @since 3.7.0 |
|
1698 */ |
|
1699 public function upgrade_strings() { |
|
1700 $this->strings['starting_upgrade'] = __( 'Some of your translations need updating. Sit tight for a few more seconds while we update them as well.' ); |
|
1701 $this->strings['up_to_date'] = __( 'The translation is up to date.' ); // We need to silently skip this case |
|
1702 $this->strings['no_package'] = __( 'Update package not available.' ); |
|
1703 $this->strings['downloading_package'] = __( 'Downloading translation from <span class="code">%s</span>…' ); |
|
1704 $this->strings['unpack_package'] = __( 'Unpacking the update…' ); |
|
1705 $this->strings['process_failed'] = __( 'Translation update failed.' ); |
|
1706 $this->strings['process_success'] = __( 'Translation updated successfully.' ); |
|
1707 } |
|
1708 |
|
1709 /** |
|
1710 * Upgrade a language pack. |
|
1711 * |
|
1712 * @since 3.7.0 |
|
1713 * |
|
1714 * @param string|false $update Optional. Whether an update offer is available. Default false. |
|
1715 * @param array $args Optional. Other optional arguments, see |
|
1716 * {@see Language_Pack_Upgrader::bulk_upgrade()}. Default empty array. |
|
1717 * @return array|WP_Error The result of the upgrade, or a {@see wP_Error} object instead. |
|
1718 */ |
|
1719 public function upgrade( $update = false, $args = array() ) { |
|
1720 if ( $update ) { |
|
1721 $update = array( $update ); |
|
1722 } |
|
1723 |
|
1724 $results = $this->bulk_upgrade( $update, $args ); |
|
1725 |
|
1726 if ( ! is_array( $results ) ) { |
|
1727 return $results; |
|
1728 } |
|
1729 |
|
1730 return $results[0]; |
|
1731 } |
|
1732 |
|
1733 /** |
|
1734 * Bulk upgrade language packs. |
|
1735 * |
|
1736 * @since 3.7.0 |
|
1737 * |
|
1738 * @param array $language_updates Optional. Language pack updates. Default empty array. |
|
1739 * @param array $args { |
|
1740 * Optional. Other arguments for upgrading multiple language packs. Default empty array |
|
1741 * |
|
1742 * @type bool $clear_update_cache Whether to clear the update cache when done. |
|
1743 * Default true. |
|
1744 * } |
|
1745 * @return array|true|false|WP_Error Will return an array of results, or true if there are no updates, |
|
1746 * false or WP_Error for initial errors. |
|
1747 */ |
|
1748 public function bulk_upgrade( $language_updates = array(), $args = array() ) { |
|
1749 global $wp_filesystem; |
|
1750 |
|
1751 $defaults = array( |
|
1752 'clear_update_cache' => true, |
|
1753 ); |
|
1754 $parsed_args = wp_parse_args( $args, $defaults ); |
|
1755 |
|
1756 $this->init(); |
|
1757 $this->upgrade_strings(); |
|
1758 |
|
1759 if ( ! $language_updates ) |
|
1760 $language_updates = wp_get_translation_updates(); |
|
1761 |
|
1762 if ( empty( $language_updates ) ) { |
|
1763 $this->skin->header(); |
|
1764 $this->skin->before(); |
|
1765 $this->skin->set_result( true ); |
|
1766 $this->skin->feedback( 'up_to_date' ); |
|
1767 $this->skin->after(); |
|
1768 $this->skin->bulk_footer(); |
|
1769 $this->skin->footer(); |
|
1770 return true; |
|
1771 } |
|
1772 |
|
1773 if ( 'upgrader_process_complete' == current_filter() ) |
|
1774 $this->skin->feedback( 'starting_upgrade' ); |
|
1775 |
|
1776 // Remove any existing upgrade filters from the plugin/theme upgraders #WP29425 & #WP29230 |
|
1777 remove_all_filters( 'upgrader_pre_install' ); |
|
1778 remove_all_filters( 'upgrader_clear_destination' ); |
|
1779 remove_all_filterS( 'upgrader_post_install' ); |
|
1780 remove_all_filters( 'upgrader_source_selection' ); |
|
1781 |
|
1782 add_filter( 'upgrader_source_selection', array( $this, 'check_package' ), 10, 2 ); |
|
1783 |
|
1784 $this->skin->header(); |
|
1785 |
|
1786 // Connect to the Filesystem first. |
|
1787 $res = $this->fs_connect( array( WP_CONTENT_DIR, WP_LANG_DIR ) ); |
|
1788 if ( ! $res ) { |
|
1789 $this->skin->footer(); |
|
1790 return false; |
|
1791 } |
|
1792 |
|
1793 $results = array(); |
|
1794 |
|
1795 $this->update_count = count( $language_updates ); |
|
1796 $this->update_current = 0; |
|
1797 |
|
1798 /* |
|
1799 * The filesystem's mkdir() is not recursive. Make sure WP_LANG_DIR exists, |
|
1800 * as we then may need to create a /plugins or /themes directory inside of it. |
|
1801 */ |
|
1802 $remote_destination = $wp_filesystem->find_folder( WP_LANG_DIR ); |
|
1803 if ( ! $wp_filesystem->exists( $remote_destination ) ) |
|
1804 if ( ! $wp_filesystem->mkdir( $remote_destination, FS_CHMOD_DIR ) ) |
|
1805 return new WP_Error( 'mkdir_failed_lang_dir', $this->strings['mkdir_failed'], $remote_destination ); |
|
1806 |
|
1807 foreach ( $language_updates as $language_update ) { |
|
1808 |
|
1809 $this->skin->language_update = $language_update; |
|
1810 |
|
1811 $destination = WP_LANG_DIR; |
|
1812 if ( 'plugin' == $language_update->type ) |
|
1813 $destination .= '/plugins'; |
|
1814 elseif ( 'theme' == $language_update->type ) |
|
1815 $destination .= '/themes'; |
|
1816 |
|
1817 $this->update_current++; |
|
1818 |
|
1819 $options = array( |
|
1820 'package' => $language_update->package, |
|
1821 'destination' => $destination, |
|
1822 'clear_destination' => false, |
|
1823 'abort_if_destination_exists' => false, // We expect the destination to exist. |
|
1824 'clear_working' => true, |
|
1825 'is_multi' => true, |
|
1826 'hook_extra' => array( |
|
1827 'language_update_type' => $language_update->type, |
|
1828 'language_update' => $language_update, |
|
1829 ) |
|
1830 ); |
|
1831 |
|
1832 $result = $this->run( $options ); |
|
1833 |
|
1834 $results[] = $this->result; |
|
1835 |
|
1836 // Prevent credentials auth screen from displaying multiple times. |
|
1837 if ( false === $result ) |
|
1838 break; |
|
1839 } |
|
1840 |
|
1841 $this->skin->bulk_footer(); |
|
1842 |
|
1843 $this->skin->footer(); |
|
1844 |
|
1845 // Clean up our hooks, in case something else does an upgrade on this connection. |
|
1846 remove_filter( 'upgrader_source_selection', array( $this, 'check_package' ) ); |
|
1847 |
|
1848 if ( $parsed_args['clear_update_cache'] ) { |
|
1849 wp_clean_update_cache(); |
|
1850 } |
|
1851 |
|
1852 return $results; |
|
1853 } |
|
1854 |
|
1855 /** |
|
1856 * Check the package source to make sure there are .mo and .po files. |
|
1857 * |
|
1858 * Hooked to the {@see 'upgrader_source_selection'} filter by |
|
1859 * {@see Language_Pack_Upgrader::bulk_upgrade()}. |
|
1860 * |
|
1861 * @since 3.7.0 |
|
1862 */ |
|
1863 public function check_package( $source, $remote_source ) { |
|
1864 global $wp_filesystem; |
|
1865 |
|
1866 if ( is_wp_error( $source ) ) |
|
1867 return $source; |
|
1868 |
|
1869 // Check that the folder contains a valid language. |
|
1870 $files = $wp_filesystem->dirlist( $remote_source ); |
|
1871 |
|
1872 // Check to see if a .po and .mo exist in the folder. |
|
1873 $po = $mo = false; |
|
1874 foreach ( (array) $files as $file => $filedata ) { |
|
1875 if ( '.po' == substr( $file, -3 ) ) |
|
1876 $po = true; |
|
1877 elseif ( '.mo' == substr( $file, -3 ) ) |
|
1878 $mo = true; |
|
1879 } |
|
1880 |
|
1881 if ( ! $mo || ! $po ) |
|
1882 return new WP_Error( 'incompatible_archive_pomo', $this->strings['incompatible_archive'], |
|
1883 __( 'The language pack is missing either the <code>.po</code> or <code>.mo</code> files.' ) ); |
|
1884 |
|
1885 return $source; |
|
1886 } |
|
1887 |
|
1888 /** |
|
1889 * Get the name of an item being updated. |
|
1890 * |
|
1891 * @since 3.7.0 |
|
1892 * |
|
1893 * @param object The data for an update. |
|
1894 * @return string The name of the item being updated. |
|
1895 */ |
|
1896 public function get_name_for_update( $update ) { |
|
1897 switch ( $update->type ) { |
|
1898 case 'core': |
|
1899 return 'WordPress'; // Not translated |
|
1900 |
|
1901 case 'theme': |
|
1902 $theme = wp_get_theme( $update->slug ); |
|
1903 if ( $theme->exists() ) |
|
1904 return $theme->Get( 'Name' ); |
|
1905 break; |
|
1906 case 'plugin': |
|
1907 $plugin_data = get_plugins( '/' . $update->slug ); |
|
1908 $plugin_data = reset( $plugin_data ); |
|
1909 if ( $plugin_data ) |
|
1910 return $plugin_data['Name']; |
|
1911 break; |
|
1912 } |
|
1913 return ''; |
|
1914 } |
|
1915 |
|
1916 } |
|
1917 |
|
1918 /** |
|
1919 * Core Upgrader class for WordPress. It allows for WordPress to upgrade itself in combination with the wp-admin/includes/update-core.php file |
|
1920 * |
|
1921 * @package WordPress |
|
1922 * @subpackage Upgrader |
|
1923 * @since 2.8.0 |
|
1924 */ |
|
1925 class Core_Upgrader extends WP_Upgrader { |
|
1926 |
|
1927 /** |
|
1928 * Initialize the upgrade strings. |
|
1929 * |
|
1930 * @since 2.8.0 |
|
1931 */ |
|
1932 public function upgrade_strings() { |
|
1933 $this->strings['up_to_date'] = __('WordPress is at the latest version.'); |
|
1934 $this->strings['no_package'] = __('Update package not available.'); |
|
1935 $this->strings['downloading_package'] = __('Downloading update from <span class="code">%s</span>…'); |
|
1936 $this->strings['unpack_package'] = __('Unpacking the update…'); |
|
1937 $this->strings['copy_failed'] = __('Could not copy files.'); |
|
1938 $this->strings['copy_failed_space'] = __('Could not copy files. You may have run out of disk space.' ); |
|
1939 $this->strings['start_rollback'] = __( 'Attempting to roll back to previous version.' ); |
|
1940 $this->strings['rollback_was_required'] = __( 'Due to an error during updating, WordPress has rolled back to your previous version.' ); |
|
1941 } |
|
1942 |
|
1943 /** |
|
1944 * Upgrade WordPress core. |
|
1945 * |
|
1946 * @since 2.8.0 |
|
1947 * |
|
1948 * @param object $current Response object for whether WordPress is current. |
|
1949 * @param array $args { |
|
1950 * Optional. Arguments for upgrading WordPress core. Default empty array. |
|
1951 * |
|
1952 * @type bool $pre_check_md5 Whether to check the file checksums before |
|
1953 * attempting the upgrade. Default true. |
|
1954 * @type bool $attempt_rollback Whether to attempt to rollback the chances if |
|
1955 * there is a problem. Default false. |
|
1956 * @type bool $do_rollback Whether to perform this "upgrade" as a rollback. |
|
1957 * Default false. |
|
1958 * } |
|
1959 * @return null|false|WP_Error False or WP_Error on failure, null on success. |
|
1960 */ |
|
1961 public function upgrade( $current, $args = array() ) { |
|
1962 global $wp_filesystem; |
|
1963 |
|
1964 include( ABSPATH . WPINC . '/version.php' ); // $wp_version; |
|
1965 |
|
1966 $start_time = time(); |
|
1967 |
|
1968 $defaults = array( |
|
1969 'pre_check_md5' => true, |
|
1970 'attempt_rollback' => false, |
|
1971 'do_rollback' => false, |
|
1972 'allow_relaxed_file_ownership' => false, |
|
1973 ); |
|
1974 $parsed_args = wp_parse_args( $args, $defaults ); |
|
1975 |
|
1976 $this->init(); |
|
1977 $this->upgrade_strings(); |
|
1978 |
|
1979 // Is an update available? |
|
1980 if ( !isset( $current->response ) || $current->response == 'latest' ) |
|
1981 return new WP_Error('up_to_date', $this->strings['up_to_date']); |
|
1982 |
|
1983 $res = $this->fs_connect( array( ABSPATH, WP_CONTENT_DIR ), $parsed_args['allow_relaxed_file_ownership'] ); |
|
1984 if ( ! $res || is_wp_error( $res ) ) { |
|
1985 return $res; |
|
1986 } |
|
1987 |
|
1988 $wp_dir = trailingslashit($wp_filesystem->abspath()); |
|
1989 |
|
1990 $partial = true; |
|
1991 if ( $parsed_args['do_rollback'] ) |
|
1992 $partial = false; |
|
1993 elseif ( $parsed_args['pre_check_md5'] && ! $this->check_files() ) |
|
1994 $partial = false; |
|
1995 |
|
1996 /* |
|
1997 * If partial update is returned from the API, use that, unless we're doing |
|
1998 * a reinstall. If we cross the new_bundled version number, then use |
|
1999 * the new_bundled zip. Don't though if the constant is set to skip bundled items. |
|
2000 * If the API returns a no_content zip, go with it. Finally, default to the full zip. |
|
2001 */ |
|
2002 if ( $parsed_args['do_rollback'] && $current->packages->rollback ) |
|
2003 $to_download = 'rollback'; |
|
2004 elseif ( $current->packages->partial && 'reinstall' != $current->response && $wp_version == $current->partial_version && $partial ) |
|
2005 $to_download = 'partial'; |
|
2006 elseif ( $current->packages->new_bundled && version_compare( $wp_version, $current->new_bundled, '<' ) |
|
2007 && ( ! defined( 'CORE_UPGRADE_SKIP_NEW_BUNDLED' ) || ! CORE_UPGRADE_SKIP_NEW_BUNDLED ) ) |
|
2008 $to_download = 'new_bundled'; |
|
2009 elseif ( $current->packages->no_content ) |
|
2010 $to_download = 'no_content'; |
|
2011 else |
|
2012 $to_download = 'full'; |
|
2013 |
|
2014 $download = $this->download_package( $current->packages->$to_download ); |
|
2015 if ( is_wp_error($download) ) |
|
2016 return $download; |
|
2017 |
|
2018 $working_dir = $this->unpack_package( $download ); |
|
2019 if ( is_wp_error($working_dir) ) |
|
2020 return $working_dir; |
|
2021 |
|
2022 // Copy update-core.php from the new version into place. |
|
2023 if ( !$wp_filesystem->copy($working_dir . '/wordpress/wp-admin/includes/update-core.php', $wp_dir . 'wp-admin/includes/update-core.php', true) ) { |
|
2024 $wp_filesystem->delete($working_dir, true); |
|
2025 return new WP_Error( 'copy_failed_for_update_core_file', __( 'The update cannot be installed because we will be unable to copy some files. This is usually due to inconsistent file permissions.' ), 'wp-admin/includes/update-core.php' ); |
|
2026 } |
|
2027 $wp_filesystem->chmod($wp_dir . 'wp-admin/includes/update-core.php', FS_CHMOD_FILE); |
|
2028 |
|
2029 require_once( ABSPATH . 'wp-admin/includes/update-core.php' ); |
|
2030 |
|
2031 if ( ! function_exists( 'update_core' ) ) |
|
2032 return new WP_Error( 'copy_failed_space', $this->strings['copy_failed_space'] ); |
|
2033 |
|
2034 $result = update_core( $working_dir, $wp_dir ); |
|
2035 |
|
2036 // In the event of an issue, we may be able to roll back. |
|
2037 if ( $parsed_args['attempt_rollback'] && $current->packages->rollback && ! $parsed_args['do_rollback'] ) { |
|
2038 $try_rollback = false; |
|
2039 if ( is_wp_error( $result ) ) { |
|
2040 $error_code = $result->get_error_code(); |
|
2041 /* |
|
2042 * Not all errors are equal. These codes are critical: copy_failed__copy_dir, |
|
2043 * mkdir_failed__copy_dir, copy_failed__copy_dir_retry, and disk_full. |
|
2044 * do_rollback allows for update_core() to trigger a rollback if needed. |
|
2045 */ |
|
2046 if ( false !== strpos( $error_code, 'do_rollback' ) ) |
|
2047 $try_rollback = true; |
|
2048 elseif ( false !== strpos( $error_code, '__copy_dir' ) ) |
|
2049 $try_rollback = true; |
|
2050 elseif ( 'disk_full' === $error_code ) |
|
2051 $try_rollback = true; |
|
2052 } |
|
2053 |
|
2054 if ( $try_rollback ) { |
|
2055 /** This filter is documented in wp-admin/includes/update-core.php */ |
|
2056 apply_filters( 'update_feedback', $result ); |
|
2057 |
|
2058 /** This filter is documented in wp-admin/includes/update-core.php */ |
|
2059 apply_filters( 'update_feedback', $this->strings['start_rollback'] ); |
|
2060 |
|
2061 $rollback_result = $this->upgrade( $current, array_merge( $parsed_args, array( 'do_rollback' => true ) ) ); |
|
2062 |
|
2063 $original_result = $result; |
|
2064 $result = new WP_Error( 'rollback_was_required', $this->strings['rollback_was_required'], (object) array( 'update' => $original_result, 'rollback' => $rollback_result ) ); |
|
2065 } |
|
2066 } |
|
2067 |
|
2068 /** This action is documented in wp-admin/includes/class-wp-upgrader.php */ |
|
2069 do_action( 'upgrader_process_complete', $this, array( 'action' => 'update', 'type' => 'core' ) ); |
|
2070 |
|
2071 // Clear the current updates |
|
2072 delete_site_transient( 'update_core' ); |
|
2073 |
|
2074 if ( ! $parsed_args['do_rollback'] ) { |
|
2075 $stats = array( |
|
2076 'update_type' => $current->response, |
|
2077 'success' => true, |
|
2078 'fs_method' => $wp_filesystem->method, |
|
2079 'fs_method_forced' => defined( 'FS_METHOD' ) || has_filter( 'filesystem_method' ), |
|
2080 'fs_method_direct' => !empty( $GLOBALS['_wp_filesystem_direct_method'] ) ? $GLOBALS['_wp_filesystem_direct_method'] : '', |
|
2081 'time_taken' => time() - $start_time, |
|
2082 'reported' => $wp_version, |
|
2083 'attempted' => $current->version, |
|
2084 ); |
|
2085 |
|
2086 if ( is_wp_error( $result ) ) { |
|
2087 $stats['success'] = false; |
|
2088 // Did a rollback occur? |
|
2089 if ( ! empty( $try_rollback ) ) { |
|
2090 $stats['error_code'] = $original_result->get_error_code(); |
|
2091 $stats['error_data'] = $original_result->get_error_data(); |
|
2092 // Was the rollback successful? If not, collect its error too. |
|
2093 $stats['rollback'] = ! is_wp_error( $rollback_result ); |
|
2094 if ( is_wp_error( $rollback_result ) ) { |
|
2095 $stats['rollback_code'] = $rollback_result->get_error_code(); |
|
2096 $stats['rollback_data'] = $rollback_result->get_error_data(); |
|
2097 } |
|
2098 } else { |
|
2099 $stats['error_code'] = $result->get_error_code(); |
|
2100 $stats['error_data'] = $result->get_error_data(); |
|
2101 } |
|
2102 } |
|
2103 |
|
2104 wp_version_check( $stats ); |
|
2105 } |
|
2106 |
|
2107 return $result; |
|
2108 } |
|
2109 |
|
2110 /** |
|
2111 * Determines if this WordPress Core version should update to an offered version or not. |
|
2112 * |
|
2113 * @since 3.7.0 |
|
2114 * |
|
2115 * @param string $offered_ver The offered version, of the format x.y.z. |
|
2116 * @return bool True if we should update to the offered version, otherwise false. |
|
2117 */ |
|
2118 public static function should_update_to_version( $offered_ver ) { |
|
2119 include( ABSPATH . WPINC . '/version.php' ); // $wp_version; // x.y.z |
|
2120 |
|
2121 $current_branch = implode( '.', array_slice( preg_split( '/[.-]/', $wp_version ), 0, 2 ) ); // x.y |
|
2122 $new_branch = implode( '.', array_slice( preg_split( '/[.-]/', $offered_ver ), 0, 2 ) ); // x.y |
|
2123 $current_is_development_version = (bool) strpos( $wp_version, '-' ); |
|
2124 |
|
2125 // Defaults: |
|
2126 $upgrade_dev = true; |
|
2127 $upgrade_minor = true; |
|
2128 $upgrade_major = false; |
|
2129 |
|
2130 // WP_AUTO_UPDATE_CORE = true (all), 'minor', false. |
|
2131 if ( defined( 'WP_AUTO_UPDATE_CORE' ) ) { |
|
2132 if ( false === WP_AUTO_UPDATE_CORE ) { |
|
2133 // Defaults to turned off, unless a filter allows it |
|
2134 $upgrade_dev = $upgrade_minor = $upgrade_major = false; |
|
2135 } elseif ( true === WP_AUTO_UPDATE_CORE ) { |
|
2136 // ALL updates for core |
|
2137 $upgrade_dev = $upgrade_minor = $upgrade_major = true; |
|
2138 } elseif ( 'minor' === WP_AUTO_UPDATE_CORE ) { |
|
2139 // Only minor updates for core |
|
2140 $upgrade_dev = $upgrade_major = false; |
|
2141 $upgrade_minor = true; |
|
2142 } |
|
2143 } |
|
2144 |
|
2145 // 1: If we're already on that version, not much point in updating? |
|
2146 if ( $offered_ver == $wp_version ) |
|
2147 return false; |
|
2148 |
|
2149 // 2: If we're running a newer version, that's a nope |
|
2150 if ( version_compare( $wp_version, $offered_ver, '>' ) ) |
|
2151 return false; |
|
2152 |
|
2153 $failure_data = get_site_option( 'auto_core_update_failed' ); |
|
2154 if ( $failure_data ) { |
|
2155 // If this was a critical update failure, cannot update. |
|
2156 if ( ! empty( $failure_data['critical'] ) ) |
|
2157 return false; |
|
2158 |
|
2159 // Don't claim we can update on update-core.php if we have a non-critical failure logged. |
|
2160 if ( $wp_version == $failure_data['current'] && false !== strpos( $offered_ver, '.1.next.minor' ) ) |
|
2161 return false; |
|
2162 |
|
2163 // Cannot update if we're retrying the same A to B update that caused a non-critical failure. |
|
2164 // Some non-critical failures do allow retries, like download_failed. |
|
2165 // 3.7.1 => 3.7.2 resulted in files_not_writable, if we are still on 3.7.1 and still trying to update to 3.7.2. |
|
2166 if ( empty( $failure_data['retry'] ) && $wp_version == $failure_data['current'] && $offered_ver == $failure_data['attempted'] ) |
|
2167 return false; |
|
2168 } |
|
2169 |
|
2170 // 3: 3.7-alpha-25000 -> 3.7-alpha-25678 -> 3.7-beta1 -> 3.7-beta2 |
|
2171 if ( $current_is_development_version ) { |
|
2172 |
|
2173 /** |
|
2174 * Filter whether to enable automatic core updates for development versions. |
|
2175 * |
|
2176 * @since 3.7.0 |
|
2177 * |
|
2178 * @param bool $upgrade_dev Whether to enable automatic updates for |
|
2179 * development versions. |
|
2180 */ |
|
2181 if ( ! apply_filters( 'allow_dev_auto_core_updates', $upgrade_dev ) ) |
|
2182 return false; |
|
2183 // Else fall through to minor + major branches below. |
|
2184 } |
|
2185 |
|
2186 // 4: Minor In-branch updates (3.7.0 -> 3.7.1 -> 3.7.2 -> 3.7.4) |
|
2187 if ( $current_branch == $new_branch ) { |
|
2188 |
|
2189 /** |
|
2190 * Filter whether to enable minor automatic core updates. |
|
2191 * |
|
2192 * @since 3.7.0 |
|
2193 * |
|
2194 * @param bool $upgrade_minor Whether to enable minor automatic core updates. |
|
2195 */ |
|
2196 return apply_filters( 'allow_minor_auto_core_updates', $upgrade_minor ); |
|
2197 } |
|
2198 |
|
2199 // 5: Major version updates (3.7.0 -> 3.8.0 -> 3.9.1) |
|
2200 if ( version_compare( $new_branch, $current_branch, '>' ) ) { |
|
2201 |
|
2202 /** |
|
2203 * Filter whether to enable major automatic core updates. |
|
2204 * |
|
2205 * @since 3.7.0 |
|
2206 * |
|
2207 * @param bool $upgrade_major Whether to enable major automatic core updates. |
|
2208 */ |
|
2209 return apply_filters( 'allow_major_auto_core_updates', $upgrade_major ); |
|
2210 } |
|
2211 |
|
2212 // If we're not sure, we don't want it |
|
2213 return false; |
|
2214 } |
|
2215 |
|
2216 /** |
|
2217 * Compare the disk file checksums agains the expected checksums. |
|
2218 * |
|
2219 * @since 3.7.0 |
|
2220 * |
|
2221 * @return bool True if the checksums match, otherwise false. |
|
2222 */ |
|
2223 public function check_files() { |
|
2224 global $wp_version, $wp_local_package; |
|
2225 |
|
2226 $checksums = get_core_checksums( $wp_version, isset( $wp_local_package ) ? $wp_local_package : 'en_US' ); |
|
2227 |
|
2228 if ( ! is_array( $checksums ) ) |
|
2229 return false; |
|
2230 |
|
2231 foreach ( $checksums as $file => $checksum ) { |
|
2232 // Skip files which get updated |
|
2233 if ( 'wp-content' == substr( $file, 0, 10 ) ) |
|
2234 continue; |
|
2235 if ( ! file_exists( ABSPATH . $file ) || md5_file( ABSPATH . $file ) !== $checksum ) |
|
2236 return false; |
|
2237 } |
|
2238 |
|
2239 return true; |
|
2240 } |
|
2241 } |
|
2242 |
|
2243 /** |
|
2244 * Upgrade Skin helper for File uploads. This class handles the upload process and passes it as if it's a local file to the Upgrade/Installer functions. |
|
2245 * |
|
2246 * @package WordPress |
|
2247 * @subpackage Upgrader |
|
2248 * @since 2.8.0 |
|
2249 */ |
|
2250 class File_Upload_Upgrader { |
|
2251 |
|
2252 /** |
|
2253 * The full path to the file package. |
|
2254 * |
|
2255 * @since 2.8.0 |
|
2256 * @var string $package |
|
2257 */ |
|
2258 public $package; |
|
2259 |
|
2260 /** |
|
2261 * The name of the file. |
|
2262 * |
|
2263 * @since 2.8.0 |
|
2264 * @var string $filename |
|
2265 */ |
|
2266 public $filename; |
|
2267 |
|
2268 /** |
|
2269 * The ID of the attachment post for this file. |
|
2270 * |
|
2271 * @since 3.3.0 |
|
2272 * @var int $id |
|
2273 */ |
|
2274 public $id = 0; |
|
2275 |
|
2276 /** |
|
2277 * Construct the upgrader for a form. |
|
2278 * |
|
2279 * @since 2.8.0 |
|
2280 * |
|
2281 * @param string $form The name of the form the file was uploaded from. |
|
2282 * @param string $urlholder The name of the `GET` parameter that holds the filename. |
|
2283 */ |
|
2284 public function __construct( $form, $urlholder ) { |
|
2285 |
|
2286 if ( empty($_FILES[$form]['name']) && empty($_GET[$urlholder]) ) |
|
2287 wp_die(__('Please select a file')); |
|
2288 |
|
2289 //Handle a newly uploaded file, Else assume it's already been uploaded |
|
2290 if ( ! empty($_FILES) ) { |
|
2291 $overrides = array( 'test_form' => false, 'test_type' => false ); |
|
2292 $file = wp_handle_upload( $_FILES[$form], $overrides ); |
|
2293 |
|
2294 if ( isset( $file['error'] ) ) |
|
2295 wp_die( $file['error'] ); |
|
2296 |
|
2297 $this->filename = $_FILES[$form]['name']; |
|
2298 $this->package = $file['file']; |
|
2299 |
|
2300 // Construct the object array |
|
2301 $object = array( |
|
2302 'post_title' => $this->filename, |
|
2303 'post_content' => $file['url'], |
|
2304 'post_mime_type' => $file['type'], |
|
2305 'guid' => $file['url'], |
|
2306 'context' => 'upgrader', |
|
2307 'post_status' => 'private' |
|
2308 ); |
|
2309 |
|
2310 // Save the data. |
|
2311 $this->id = wp_insert_attachment( $object, $file['file'] ); |
|
2312 |
|
2313 // Schedule a cleanup for 2 hours from now in case of failed install. |
|
2314 wp_schedule_single_event( time() + 2 * HOUR_IN_SECONDS, 'upgrader_scheduled_cleanup', array( $this->id ) ); |
|
2315 |
|
2316 } elseif ( is_numeric( $_GET[$urlholder] ) ) { |
|
2317 // Numeric Package = previously uploaded file, see above. |
|
2318 $this->id = (int) $_GET[$urlholder]; |
|
2319 $attachment = get_post( $this->id ); |
|
2320 if ( empty($attachment) ) |
|
2321 wp_die(__('Please select a file')); |
|
2322 |
|
2323 $this->filename = $attachment->post_title; |
|
2324 $this->package = get_attached_file( $attachment->ID ); |
|
2325 } else { |
|
2326 // Else, It's set to something, Back compat for plugins using the old (pre-3.3) File_Uploader handler. |
|
2327 if ( ! ( ( $uploads = wp_upload_dir() ) && false === $uploads['error'] ) ) |
|
2328 wp_die( $uploads['error'] ); |
|
2329 |
|
2330 $this->filename = $_GET[$urlholder]; |
|
2331 $this->package = $uploads['basedir'] . '/' . $this->filename; |
|
2332 } |
|
2333 } |
|
2334 |
|
2335 /** |
|
2336 * Delete the attachment/uploaded file. |
|
2337 * |
|
2338 * @since 3.2.2 |
|
2339 * |
|
2340 * @return bool Whether the cleanup was successful. |
|
2341 */ |
|
2342 public function cleanup() { |
|
2343 if ( $this->id ) |
|
2344 wp_delete_attachment( $this->id ); |
|
2345 |
|
2346 elseif ( file_exists( $this->package ) ) |
|
2347 return @unlink( $this->package ); |
|
2348 |
|
2349 return true; |
|
2350 } |
|
2351 } |
|
2352 |
|
2353 /** |
|
2354 * The WordPress automatic background updater. |
|
2355 * |
|
2356 * @package WordPress |
|
2357 * @subpackage Upgrader |
|
2358 * @since 3.7.0 |
|
2359 */ |
|
2360 class WP_Automatic_Updater { |
|
2361 |
|
2362 /** |
|
2363 * Tracks update results during processing. |
|
2364 * |
|
2365 * @var array |
|
2366 */ |
|
2367 protected $update_results = array(); |
|
2368 |
|
2369 /** |
|
2370 * Whether the entire automatic updater is disabled. |
|
2371 * |
|
2372 * @since 3.7.0 |
|
2373 */ |
|
2374 public function is_disabled() { |
|
2375 // Background updates are disabled if you don't want file changes. |
|
2376 if ( defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS ) |
|
2377 return true; |
|
2378 |
|
2379 if ( defined( 'WP_INSTALLING' ) ) |
|
2380 return true; |
|
2381 |
|
2382 // More fine grained control can be done through the WP_AUTO_UPDATE_CORE constant and filters. |
|
2383 $disabled = defined( 'AUTOMATIC_UPDATER_DISABLED' ) && AUTOMATIC_UPDATER_DISABLED; |
|
2384 |
|
2385 /** |
|
2386 * Filter whether to entirely disable background updates. |
|
2387 * |
|
2388 * There are more fine-grained filters and controls for selective disabling. |
|
2389 * This filter parallels the AUTOMATIC_UPDATER_DISABLED constant in name. |
|
2390 * |
|
2391 * This also disables update notification emails. That may change in the future. |
|
2392 * |
|
2393 * @since 3.7.0 |
|
2394 * |
|
2395 * @param bool $disabled Whether the updater should be disabled. |
|
2396 */ |
|
2397 return apply_filters( 'automatic_updater_disabled', $disabled ); |
|
2398 } |
|
2399 |
|
2400 /** |
|
2401 * Check for version control checkouts. |
|
2402 * |
|
2403 * Checks for Subversion, Git, Mercurial, and Bazaar. It recursively looks up the |
|
2404 * filesystem to the top of the drive, erring on the side of detecting a VCS |
|
2405 * checkout somewhere. |
|
2406 * |
|
2407 * ABSPATH is always checked in addition to whatever $context is (which may be the |
|
2408 * wp-content directory, for example). The underlying assumption is that if you are |
|
2409 * using version control *anywhere*, then you should be making decisions for |
|
2410 * how things get updated. |
|
2411 * |
|
2412 * @since 3.7.0 |
|
2413 * |
|
2414 * @param string $context The filesystem path to check, in addition to ABSPATH. |
|
2415 */ |
|
2416 public function is_vcs_checkout( $context ) { |
|
2417 $context_dirs = array( untrailingslashit( $context ) ); |
|
2418 if ( $context !== ABSPATH ) |
|
2419 $context_dirs[] = untrailingslashit( ABSPATH ); |
|
2420 |
|
2421 $vcs_dirs = array( '.svn', '.git', '.hg', '.bzr' ); |
|
2422 $check_dirs = array(); |
|
2423 |
|
2424 foreach ( $context_dirs as $context_dir ) { |
|
2425 // Walk up from $context_dir to the root. |
|
2426 do { |
|
2427 $check_dirs[] = $context_dir; |
|
2428 |
|
2429 // Once we've hit '/' or 'C:\', we need to stop. dirname will keep returning the input here. |
|
2430 if ( $context_dir == dirname( $context_dir ) ) |
|
2431 break; |
|
2432 |
|
2433 // Continue one level at a time. |
|
2434 } while ( $context_dir = dirname( $context_dir ) ); |
|
2435 } |
|
2436 |
|
2437 $check_dirs = array_unique( $check_dirs ); |
|
2438 |
|
2439 // Search all directories we've found for evidence of version control. |
|
2440 foreach ( $vcs_dirs as $vcs_dir ) { |
|
2441 foreach ( $check_dirs as $check_dir ) { |
|
2442 if ( $checkout = @is_dir( rtrim( $check_dir, '\\/' ) . "/$vcs_dir" ) ) |
|
2443 break 2; |
|
2444 } |
|
2445 } |
|
2446 |
|
2447 /** |
|
2448 * Filter whether the automatic updater should consider a filesystem |
|
2449 * location to be potentially managed by a version control system. |
|
2450 * |
|
2451 * @since 3.7.0 |
|
2452 * |
|
2453 * @param bool $checkout Whether a VCS checkout was discovered at $context |
|
2454 * or ABSPATH, or anywhere higher. |
|
2455 * @param string $context The filesystem context (a path) against which |
|
2456 * filesystem status should be checked. |
|
2457 */ |
|
2458 return apply_filters( 'automatic_updates_is_vcs_checkout', $checkout, $context ); |
|
2459 } |
|
2460 |
|
2461 /** |
|
2462 * Tests to see if we can and should update a specific item. |
|
2463 * |
|
2464 * @since 3.7.0 |
|
2465 * |
|
2466 * @param string $type The type of update being checked: 'core', 'theme', |
|
2467 * 'plugin', 'translation'. |
|
2468 * @param object $item The update offer. |
|
2469 * @param string $context The filesystem context (a path) against which filesystem |
|
2470 * access and status should be checked. |
|
2471 */ |
|
2472 public function should_update( $type, $item, $context ) { |
|
2473 // Used to see if WP_Filesystem is set up to allow unattended updates. |
|
2474 $skin = new Automatic_Upgrader_Skin; |
|
2475 |
|
2476 if ( $this->is_disabled() ) |
|
2477 return false; |
|
2478 |
|
2479 // Only relax the filesystem checks when the update doesn't include new files |
|
2480 $allow_relaxed_file_ownership = false; |
|
2481 if ( 'core' == $type && isset( $item->new_files ) && ! $item->new_files ) { |
|
2482 $allow_relaxed_file_ownership = true; |
|
2483 } |
|
2484 |
|
2485 // If we can't do an auto core update, we may still be able to email the user. |
|
2486 if ( ! $skin->request_filesystem_credentials( false, $context, $allow_relaxed_file_ownership ) || $this->is_vcs_checkout( $context ) ) { |
|
2487 if ( 'core' == $type ) |
|
2488 $this->send_core_update_notification_email( $item ); |
|
2489 return false; |
|
2490 } |
|
2491 |
|
2492 // Next up, is this an item we can update? |
|
2493 if ( 'core' == $type ) |
|
2494 $update = Core_Upgrader::should_update_to_version( $item->current ); |
|
2495 else |
|
2496 $update = ! empty( $item->autoupdate ); |
|
2497 |
|
2498 /** |
|
2499 * Filter whether to automatically update core, a plugin, a theme, or a language. |
|
2500 * |
|
2501 * The dynamic portion of the hook name, `$type`, refers to the type of update |
|
2502 * being checked. Can be 'core', 'theme', 'plugin', or 'translation'. |
|
2503 * |
|
2504 * Generally speaking, plugins, themes, and major core versions are not updated |
|
2505 * by default, while translations and minor and development versions for core |
|
2506 * are updated by default. |
|
2507 * |
|
2508 * See the {@see 'allow_dev_auto_core_updates', {@see 'allow_minor_auto_core_updates'}, |
|
2509 * and {@see 'allow_major_auto_core_updates'} filters for a more straightforward way to |
|
2510 * adjust core updates. |
|
2511 * |
|
2512 * @since 3.7.0 |
|
2513 * |
|
2514 * @param bool $update Whether to update. |
|
2515 * @param object $item The update offer. |
|
2516 */ |
|
2517 $update = apply_filters( 'auto_update_' . $type, $update, $item ); |
|
2518 |
|
2519 if ( ! $update ) { |
|
2520 if ( 'core' == $type ) |
|
2521 $this->send_core_update_notification_email( $item ); |
|
2522 return false; |
|
2523 } |
|
2524 |
|
2525 // If it's a core update, are we actually compatible with its requirements? |
|
2526 if ( 'core' == $type ) { |
|
2527 global $wpdb; |
|
2528 |
|
2529 $php_compat = version_compare( phpversion(), $item->php_version, '>=' ); |
|
2530 if ( file_exists( WP_CONTENT_DIR . '/db.php' ) && empty( $wpdb->is_mysql ) ) |
|
2531 $mysql_compat = true; |
|
2532 else |
|
2533 $mysql_compat = version_compare( $wpdb->db_version(), $item->mysql_version, '>=' ); |
|
2534 |
|
2535 if ( ! $php_compat || ! $mysql_compat ) |
|
2536 return false; |
|
2537 } |
|
2538 |
|
2539 return true; |
|
2540 } |
|
2541 |
|
2542 /** |
|
2543 * Notifies an administrator of a core update. |
|
2544 * |
|
2545 * @since 3.7.0 |
|
2546 * |
|
2547 * @param object $item The update offer. |
|
2548 */ |
|
2549 protected function send_core_update_notification_email( $item ) { |
|
2550 $notified = get_site_option( 'auto_core_update_notified' ); |
|
2551 |
|
2552 // Don't notify if we've already notified the same email address of the same version. |
|
2553 if ( $notified && $notified['email'] == get_site_option( 'admin_email' ) && $notified['version'] == $item->current ) |
|
2554 return false; |
|
2555 |
|
2556 // See if we need to notify users of a core update. |
|
2557 $notify = ! empty( $item->notify_email ); |
|
2558 |
|
2559 /** |
|
2560 * Filter whether to notify the site administrator of a new core update. |
|
2561 * |
|
2562 * By default, administrators are notified when the update offer received |
|
2563 * from WordPress.org sets a particular flag. This allows some discretion |
|
2564 * in if and when to notify. |
|
2565 * |
|
2566 * This filter is only evaluated once per release. If the same email address |
|
2567 * was already notified of the same new version, WordPress won't repeatedly |
|
2568 * email the administrator. |
|
2569 * |
|
2570 * This filter is also used on about.php to check if a plugin has disabled |
|
2571 * these notifications. |
|
2572 * |
|
2573 * @since 3.7.0 |
|
2574 * |
|
2575 * @param bool $notify Whether the site administrator is notified. |
|
2576 * @param object $item The update offer. |
|
2577 */ |
|
2578 if ( ! apply_filters( 'send_core_update_notification_email', $notify, $item ) ) |
|
2579 return false; |
|
2580 |
|
2581 $this->send_email( 'manual', $item ); |
|
2582 return true; |
|
2583 } |
|
2584 |
|
2585 /** |
|
2586 * Update an item, if appropriate. |
|
2587 * |
|
2588 * @since 3.7.0 |
|
2589 * |
|
2590 * @param string $type The type of update being checked: 'core', 'theme', 'plugin', 'translation'. |
|
2591 * @param object $item The update offer. |
|
2592 */ |
|
2593 public function update( $type, $item ) { |
|
2594 $skin = new Automatic_Upgrader_Skin; |
|
2595 |
|
2596 switch ( $type ) { |
|
2597 case 'core': |
|
2598 // The Core upgrader doesn't use the Upgrader's skin during the actual main part of the upgrade, instead, firing a filter. |
|
2599 add_filter( 'update_feedback', array( $skin, 'feedback' ) ); |
|
2600 $upgrader = new Core_Upgrader( $skin ); |
|
2601 $context = ABSPATH; |
|
2602 break; |
|
2603 case 'plugin': |
|
2604 $upgrader = new Plugin_Upgrader( $skin ); |
|
2605 $context = WP_PLUGIN_DIR; // We don't support custom Plugin directories, or updates for WPMU_PLUGIN_DIR |
|
2606 break; |
|
2607 case 'theme': |
|
2608 $upgrader = new Theme_Upgrader( $skin ); |
|
2609 $context = get_theme_root( $item->theme ); |
|
2610 break; |
|
2611 case 'translation': |
|
2612 $upgrader = new Language_Pack_Upgrader( $skin ); |
|
2613 $context = WP_CONTENT_DIR; // WP_LANG_DIR; |
|
2614 break; |
|
2615 } |
|
2616 |
|
2617 // Determine whether we can and should perform this update. |
|
2618 if ( ! $this->should_update( $type, $item, $context ) ) |
|
2619 return false; |
|
2620 |
|
2621 $upgrader_item = $item; |
|
2622 switch ( $type ) { |
|
2623 case 'core': |
|
2624 $skin->feedback( __( 'Updating to WordPress %s' ), $item->version ); |
|
2625 $item_name = sprintf( __( 'WordPress %s' ), $item->version ); |
|
2626 break; |
|
2627 case 'theme': |
|
2628 $upgrader_item = $item->theme; |
|
2629 $theme = wp_get_theme( $upgrader_item ); |
|
2630 $item_name = $theme->Get( 'Name' ); |
|
2631 $skin->feedback( __( 'Updating theme: %s' ), $item_name ); |
|
2632 break; |
|
2633 case 'plugin': |
|
2634 $upgrader_item = $item->plugin; |
|
2635 $plugin_data = get_plugin_data( $context . '/' . $upgrader_item ); |
|
2636 $item_name = $plugin_data['Name']; |
|
2637 $skin->feedback( __( 'Updating plugin: %s' ), $item_name ); |
|
2638 break; |
|
2639 case 'translation': |
|
2640 $language_item_name = $upgrader->get_name_for_update( $item ); |
|
2641 $item_name = sprintf( __( 'Translations for %s' ), $language_item_name ); |
|
2642 $skin->feedback( sprintf( __( 'Updating translations for %1$s (%2$s)…' ), $language_item_name, $item->language ) ); |
|
2643 break; |
|
2644 } |
|
2645 |
|
2646 $allow_relaxed_file_ownership = false; |
|
2647 if ( 'core' == $type && isset( $item->new_files ) && ! $item->new_files ) { |
|
2648 $allow_relaxed_file_ownership = true; |
|
2649 } |
|
2650 |
|
2651 // Boom, This sites about to get a whole new splash of paint! |
|
2652 $upgrade_result = $upgrader->upgrade( $upgrader_item, array( |
|
2653 'clear_update_cache' => false, |
|
2654 // Always use partial builds if possible for core updates. |
|
2655 'pre_check_md5' => false, |
|
2656 // Only available for core updates. |
|
2657 'attempt_rollback' => true, |
|
2658 // Allow relaxed file ownership in some scenarios |
|
2659 'allow_relaxed_file_ownership' => $allow_relaxed_file_ownership, |
|
2660 ) ); |
|
2661 |
|
2662 // If the filesystem is unavailable, false is returned. |
|
2663 if ( false === $upgrade_result ) { |
|
2664 $upgrade_result = new WP_Error( 'fs_unavailable', __( 'Could not access filesystem.' ) ); |
|
2665 } |
|
2666 |
|
2667 // Core doesn't output this, so let's append it so we don't get confused. |
|
2668 if ( 'core' == $type ) { |
|
2669 if ( is_wp_error( $upgrade_result ) ) { |
|
2670 $skin->error( __( 'Installation Failed' ), $upgrade_result ); |
|
2671 } else { |
|
2672 $skin->feedback( __( 'WordPress updated successfully' ) ); |
|
2673 } |
|
2674 } |
|
2675 |
|
2676 $this->update_results[ $type ][] = (object) array( |
|
2677 'item' => $item, |
|
2678 'result' => $upgrade_result, |
|
2679 'name' => $item_name, |
|
2680 'messages' => $skin->get_upgrade_messages() |
|
2681 ); |
|
2682 |
|
2683 return $upgrade_result; |
|
2684 } |
|
2685 |
|
2686 /** |
|
2687 * Kicks off the background update process, looping through all pending updates. |
|
2688 * |
|
2689 * @since 3.7.0 |
|
2690 */ |
|
2691 public function run() { |
|
2692 global $wpdb, $wp_version; |
|
2693 |
|
2694 if ( $this->is_disabled() ) |
|
2695 return; |
|
2696 |
|
2697 if ( ! is_main_network() || ! is_main_site() ) |
|
2698 return; |
|
2699 |
|
2700 $lock_name = 'auto_updater.lock'; |
|
2701 |
|
2702 // Try to lock |
|
2703 $lock_result = $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO `$wpdb->options` ( `option_name`, `option_value`, `autoload` ) VALUES (%s, %s, 'no') /* LOCK */", $lock_name, time() ) ); |
|
2704 |
|
2705 if ( ! $lock_result ) { |
|
2706 $lock_result = get_option( $lock_name ); |
|
2707 |
|
2708 // If we couldn't create a lock, and there isn't a lock, bail |
|
2709 if ( ! $lock_result ) |
|
2710 return; |
|
2711 |
|
2712 // Check to see if the lock is still valid |
|
2713 if ( $lock_result > ( time() - HOUR_IN_SECONDS ) ) |
|
2714 return; |
|
2715 } |
|
2716 |
|
2717 // Update the lock, as by this point we've definitely got a lock, just need to fire the actions |
|
2718 update_option( $lock_name, time() ); |
|
2719 |
|
2720 // Don't automatically run these thins, as we'll handle it ourselves |
|
2721 remove_action( 'upgrader_process_complete', array( 'Language_Pack_Upgrader', 'async_upgrade' ), 20 ); |
|
2722 remove_action( 'upgrader_process_complete', 'wp_version_check' ); |
|
2723 remove_action( 'upgrader_process_complete', 'wp_update_plugins' ); |
|
2724 remove_action( 'upgrader_process_complete', 'wp_update_themes' ); |
|
2725 |
|
2726 // Next, Plugins |
|
2727 wp_update_plugins(); // Check for Plugin updates |
|
2728 $plugin_updates = get_site_transient( 'update_plugins' ); |
|
2729 if ( $plugin_updates && !empty( $plugin_updates->response ) ) { |
|
2730 foreach ( $plugin_updates->response as $plugin ) { |
|
2731 $this->update( 'plugin', $plugin ); |
|
2732 } |
|
2733 // Force refresh of plugin update information |
|
2734 wp_clean_plugins_cache(); |
|
2735 } |
|
2736 |
|
2737 // Next, those themes we all love |
|
2738 wp_update_themes(); // Check for Theme updates |
|
2739 $theme_updates = get_site_transient( 'update_themes' ); |
|
2740 if ( $theme_updates && !empty( $theme_updates->response ) ) { |
|
2741 foreach ( $theme_updates->response as $theme ) { |
|
2742 $this->update( 'theme', (object) $theme ); |
|
2743 } |
|
2744 // Force refresh of theme update information |
|
2745 wp_clean_themes_cache(); |
|
2746 } |
|
2747 |
|
2748 // Next, Process any core update |
|
2749 wp_version_check(); // Check for Core updates |
|
2750 $core_update = find_core_auto_update(); |
|
2751 |
|
2752 if ( $core_update ) |
|
2753 $this->update( 'core', $core_update ); |
|
2754 |
|
2755 // Clean up, and check for any pending translations |
|
2756 // (Core_Upgrader checks for core updates) |
|
2757 $theme_stats = array(); |
|
2758 if ( isset( $this->update_results['theme'] ) ) { |
|
2759 foreach ( $this->update_results['theme'] as $upgrade ) { |
|
2760 $theme_stats[ $upgrade->item->theme ] = ( true === $upgrade->result ); |
|
2761 } |
|
2762 } |
|
2763 wp_update_themes( $theme_stats ); // Check for Theme updates |
|
2764 |
|
2765 $plugin_stats = array(); |
|
2766 if ( isset( $this->update_results['plugin'] ) ) { |
|
2767 foreach ( $this->update_results['plugin'] as $upgrade ) { |
|
2768 $plugin_stats[ $upgrade->item->plugin ] = ( true === $upgrade->result ); |
|
2769 } |
|
2770 } |
|
2771 wp_update_plugins( $plugin_stats ); // Check for Plugin updates |
|
2772 |
|
2773 // Finally, Process any new translations |
|
2774 $language_updates = wp_get_translation_updates(); |
|
2775 if ( $language_updates ) { |
|
2776 foreach ( $language_updates as $update ) { |
|
2777 $this->update( 'translation', $update ); |
|
2778 } |
|
2779 |
|
2780 // Clear existing caches |
|
2781 wp_clean_update_cache(); |
|
2782 |
|
2783 wp_version_check(); // check for Core updates |
|
2784 wp_update_themes(); // Check for Theme updates |
|
2785 wp_update_plugins(); // Check for Plugin updates |
|
2786 } |
|
2787 |
|
2788 // Send debugging email to all development installs. |
|
2789 if ( ! empty( $this->update_results ) ) { |
|
2790 $development_version = false !== strpos( $wp_version, '-' ); |
|
2791 |
|
2792 /** |
|
2793 * Filter whether to send a debugging email for each automatic background update. |
|
2794 * |
|
2795 * @since 3.7.0 |
|
2796 * |
|
2797 * @param bool $development_version By default, emails are sent if the |
|
2798 * install is a development version. |
|
2799 * Return false to avoid the email. |
|
2800 */ |
|
2801 if ( apply_filters( 'automatic_updates_send_debug_email', $development_version ) ) |
|
2802 $this->send_debug_email(); |
|
2803 |
|
2804 if ( ! empty( $this->update_results['core'] ) ) |
|
2805 $this->after_core_update( $this->update_results['core'][0] ); |
|
2806 |
|
2807 /** |
|
2808 * Fires after all automatic updates have run. |
|
2809 * |
|
2810 * @since 3.8.0 |
|
2811 * |
|
2812 * @param array $update_results The results of all attempted updates. |
|
2813 */ |
|
2814 do_action( 'automatic_updates_complete', $this->update_results ); |
|
2815 } |
|
2816 |
|
2817 // Clear the lock |
|
2818 delete_option( $lock_name ); |
|
2819 } |
|
2820 |
|
2821 /** |
|
2822 * If we tried to perform a core update, check if we should send an email, |
|
2823 * and if we need to avoid processing future updates. |
|
2824 * |
|
2825 * @param object $update_result The result of the core update. Includes the update offer and result. |
|
2826 */ |
|
2827 protected function after_core_update( $update_result ) { |
|
2828 global $wp_version; |
|
2829 |
|
2830 $core_update = $update_result->item; |
|
2831 $result = $update_result->result; |
|
2832 |
|
2833 if ( ! is_wp_error( $result ) ) { |
|
2834 $this->send_email( 'success', $core_update ); |
|
2835 return; |
|
2836 } |
|
2837 |
|
2838 $error_code = $result->get_error_code(); |
|
2839 |
|
2840 // Any of these WP_Error codes are critical failures, as in they occurred after we started to copy core files. |
|
2841 // We should not try to perform a background update again until there is a successful one-click update performed by the user. |
|
2842 $critical = false; |
|
2843 if ( $error_code === 'disk_full' || false !== strpos( $error_code, '__copy_dir' ) ) { |
|
2844 $critical = true; |
|
2845 } elseif ( $error_code === 'rollback_was_required' && is_wp_error( $result->get_error_data()->rollback ) ) { |
|
2846 // A rollback is only critical if it failed too. |
|
2847 $critical = true; |
|
2848 $rollback_result = $result->get_error_data()->rollback; |
|
2849 } elseif ( false !== strpos( $error_code, 'do_rollback' ) ) { |
|
2850 $critical = true; |
|
2851 } |
|
2852 |
|
2853 if ( $critical ) { |
|
2854 $critical_data = array( |
|
2855 'attempted' => $core_update->current, |
|
2856 'current' => $wp_version, |
|
2857 'error_code' => $error_code, |
|
2858 'error_data' => $result->get_error_data(), |
|
2859 'timestamp' => time(), |
|
2860 'critical' => true, |
|
2861 ); |
|
2862 if ( isset( $rollback_result ) ) { |
|
2863 $critical_data['rollback_code'] = $rollback_result->get_error_code(); |
|
2864 $critical_data['rollback_data'] = $rollback_result->get_error_data(); |
|
2865 } |
|
2866 update_site_option( 'auto_core_update_failed', $critical_data ); |
|
2867 $this->send_email( 'critical', $core_update, $result ); |
|
2868 return; |
|
2869 } |
|
2870 |
|
2871 /* |
|
2872 * Any other WP_Error code (like download_failed or files_not_writable) occurs before |
|
2873 * we tried to copy over core files. Thus, the failures are early and graceful. |
|
2874 * |
|
2875 * We should avoid trying to perform a background update again for the same version. |
|
2876 * But we can try again if another version is released. |
|
2877 * |
|
2878 * For certain 'transient' failures, like download_failed, we should allow retries. |
|
2879 * In fact, let's schedule a special update for an hour from now. (It's possible |
|
2880 * the issue could actually be on WordPress.org's side.) If that one fails, then email. |
|
2881 */ |
|
2882 $send = true; |
|
2883 $transient_failures = array( 'incompatible_archive', 'download_failed', 'insane_distro' ); |
|
2884 if ( in_array( $error_code, $transient_failures ) && ! get_site_option( 'auto_core_update_failed' ) ) { |
|
2885 wp_schedule_single_event( time() + HOUR_IN_SECONDS, 'wp_maybe_auto_update' ); |
|
2886 $send = false; |
|
2887 } |
|
2888 |
|
2889 $n = get_site_option( 'auto_core_update_notified' ); |
|
2890 // Don't notify if we've already notified the same email address of the same version of the same notification type. |
|
2891 if ( $n && 'fail' == $n['type'] && $n['email'] == get_site_option( 'admin_email' ) && $n['version'] == $core_update->current ) |
|
2892 $send = false; |
|
2893 |
|
2894 update_site_option( 'auto_core_update_failed', array( |
|
2895 'attempted' => $core_update->current, |
|
2896 'current' => $wp_version, |
|
2897 'error_code' => $error_code, |
|
2898 'error_data' => $result->get_error_data(), |
|
2899 'timestamp' => time(), |
|
2900 'retry' => in_array( $error_code, $transient_failures ), |
|
2901 ) ); |
|
2902 |
|
2903 if ( $send ) |
|
2904 $this->send_email( 'fail', $core_update, $result ); |
|
2905 } |
|
2906 |
|
2907 /** |
|
2908 * Sends an email upon the completion or failure of a background core update. |
|
2909 * |
|
2910 * @since 3.7.0 |
|
2911 * |
|
2912 * @param string $type The type of email to send. Can be one of 'success', 'fail', 'manual', 'critical'. |
|
2913 * @param object $core_update The update offer that was attempted. |
|
2914 * @param mixed $result Optional. The result for the core update. Can be WP_Error. |
|
2915 */ |
|
2916 protected function send_email( $type, $core_update, $result = null ) { |
|
2917 update_site_option( 'auto_core_update_notified', array( |
|
2918 'type' => $type, |
|
2919 'email' => get_site_option( 'admin_email' ), |
|
2920 'version' => $core_update->current, |
|
2921 'timestamp' => time(), |
|
2922 ) ); |
|
2923 |
|
2924 $next_user_core_update = get_preferred_from_update_core(); |
|
2925 // If the update transient is empty, use the update we just performed |
|
2926 if ( ! $next_user_core_update ) |
|
2927 $next_user_core_update = $core_update; |
|
2928 $newer_version_available = ( 'upgrade' == $next_user_core_update->response && version_compare( $next_user_core_update->version, $core_update->version, '>' ) ); |
|
2929 |
|
2930 /** |
|
2931 * Filter whether to send an email following an automatic background core update. |
|
2932 * |
|
2933 * @since 3.7.0 |
|
2934 * |
|
2935 * @param bool $send Whether to send the email. Default true. |
|
2936 * @param string $type The type of email to send. Can be one of |
|
2937 * 'success', 'fail', 'critical'. |
|
2938 * @param object $core_update The update offer that was attempted. |
|
2939 * @param mixed $result The result for the core update. Can be WP_Error. |
|
2940 */ |
|
2941 if ( 'manual' !== $type && ! apply_filters( 'auto_core_update_send_email', true, $type, $core_update, $result ) ) |
|
2942 return; |
|
2943 |
|
2944 switch ( $type ) { |
|
2945 case 'success' : // We updated. |
|
2946 /* translators: 1: Site name, 2: WordPress version number. */ |
|
2947 $subject = __( '[%1$s] Your site has updated to WordPress %2$s' ); |
|
2948 break; |
|
2949 |
|
2950 case 'fail' : // We tried to update but couldn't. |
|
2951 case 'manual' : // We can't update (and made no attempt). |
|
2952 /* translators: 1: Site name, 2: WordPress version number. */ |
|
2953 $subject = __( '[%1$s] WordPress %2$s is available. Please update!' ); |
|
2954 break; |
|
2955 |
|
2956 case 'critical' : // We tried to update, started to copy files, then things went wrong. |
|
2957 /* translators: 1: Site name. */ |
|
2958 $subject = __( '[%1$s] URGENT: Your site may be down due to a failed update' ); |
|
2959 break; |
|
2960 |
|
2961 default : |
|
2962 return; |
|
2963 } |
|
2964 |
|
2965 // If the auto update is not to the latest version, say that the current version of WP is available instead. |
|
2966 $version = 'success' === $type ? $core_update->current : $next_user_core_update->current; |
|
2967 $subject = sprintf( $subject, wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ), $version ); |
|
2968 |
|
2969 $body = ''; |
|
2970 |
|
2971 switch ( $type ) { |
|
2972 case 'success' : |
|
2973 $body .= sprintf( __( 'Howdy! Your site at %1$s has been updated automatically to WordPress %2$s.' ), home_url(), $core_update->current ); |
|
2974 $body .= "\n\n"; |
|
2975 if ( ! $newer_version_available ) |
|
2976 $body .= __( 'No further action is needed on your part.' ) . ' '; |
|
2977 |
|
2978 // Can only reference the About screen if their update was successful. |
|
2979 list( $about_version ) = explode( '-', $core_update->current, 2 ); |
|
2980 $body .= sprintf( __( "For more on version %s, see the About WordPress screen:" ), $about_version ); |
|
2981 $body .= "\n" . admin_url( 'about.php' ); |
|
2982 |
|
2983 if ( $newer_version_available ) { |
|
2984 $body .= "\n\n" . sprintf( __( 'WordPress %s is also now available.' ), $next_user_core_update->current ) . ' '; |
|
2985 $body .= __( 'Updating is easy and only takes a few moments:' ); |
|
2986 $body .= "\n" . network_admin_url( 'update-core.php' ); |
|
2987 } |
|
2988 |
|
2989 break; |
|
2990 |
|
2991 case 'fail' : |
|
2992 case 'manual' : |
|
2993 $body .= sprintf( __( 'Please update your site at %1$s to WordPress %2$s.' ), home_url(), $next_user_core_update->current ); |
|
2994 |
|
2995 $body .= "\n\n"; |
|
2996 |
|
2997 // Don't show this message if there is a newer version available. |
|
2998 // Potential for confusion, and also not useful for them to know at this point. |
|
2999 if ( 'fail' == $type && ! $newer_version_available ) |
|
3000 $body .= __( 'We tried but were unable to update your site automatically.' ) . ' '; |
|
3001 |
|
3002 $body .= __( 'Updating is easy and only takes a few moments:' ); |
|
3003 $body .= "\n" . network_admin_url( 'update-core.php' ); |
|
3004 break; |
|
3005 |
|
3006 case 'critical' : |
|
3007 if ( $newer_version_available ) |
|
3008 $body .= sprintf( __( 'Your site at %1$s experienced a critical failure while trying to update WordPress to version %2$s.' ), home_url(), $core_update->current ); |
|
3009 else |
|
3010 $body .= sprintf( __( 'Your site at %1$s experienced a critical failure while trying to update to the latest version of WordPress, %2$s.' ), home_url(), $core_update->current ); |
|
3011 |
|
3012 $body .= "\n\n" . __( "This means your site may be offline or broken. Don't panic; this can be fixed." ); |
|
3013 |
|
3014 $body .= "\n\n" . __( "Please check out your site now. It's possible that everything is working. If it says you need to update, you should do so:" ); |
|
3015 $body .= "\n" . network_admin_url( 'update-core.php' ); |
|
3016 break; |
|
3017 } |
|
3018 |
|
3019 $critical_support = 'critical' === $type && ! empty( $core_update->support_email ); |
|
3020 if ( $critical_support ) { |
|
3021 // Support offer if available. |
|
3022 $body .= "\n\n" . sprintf( __( "The WordPress team is willing to help you. Forward this email to %s and the team will work with you to make sure your site is working." ), $core_update->support_email ); |
|
3023 } else { |
|
3024 // Add a note about the support forums. |
|
3025 $body .= "\n\n" . __( 'If you experience any issues or need support, the volunteers in the WordPress.org support forums may be able to help.' ); |
|
3026 $body .= "\n" . __( 'https://wordpress.org/support/' ); |
|
3027 } |
|
3028 |
|
3029 // Updates are important! |
|
3030 if ( $type != 'success' || $newer_version_available ) { |
|
3031 $body .= "\n\n" . __( 'Keeping your site updated is important for security. It also makes the internet a safer place for you and your readers.' ); |
|
3032 } |
|
3033 |
|
3034 if ( $critical_support ) { |
|
3035 $body .= " " . __( "If you reach out to us, we'll also ensure you'll never have this problem again." ); |
|
3036 } |
|
3037 |
|
3038 // If things are successful and we're now on the latest, mention plugins and themes if any are out of date. |
|
3039 if ( $type == 'success' && ! $newer_version_available && ( get_plugin_updates() || get_theme_updates() ) ) { |
|
3040 $body .= "\n\n" . __( 'You also have some plugins or themes with updates available. Update them now:' ); |
|
3041 $body .= "\n" . network_admin_url(); |
|
3042 } |
|
3043 |
|
3044 $body .= "\n\n" . __( 'The WordPress Team' ) . "\n"; |
|
3045 |
|
3046 if ( 'critical' == $type && is_wp_error( $result ) ) { |
|
3047 $body .= "\n***\n\n"; |
|
3048 $body .= sprintf( __( 'Your site was running version %s.' ), $GLOBALS['wp_version'] ); |
|
3049 $body .= ' ' . __( 'We have some data that describes the error your site encountered.' ); |
|
3050 $body .= ' ' . __( 'Your hosting company, support forum volunteers, or a friendly developer may be able to use this information to help you:' ); |
|
3051 |
|
3052 // If we had a rollback and we're still critical, then the rollback failed too. |
|
3053 // Loop through all errors (the main WP_Error, the update result, the rollback result) for code, data, etc. |
|
3054 if ( 'rollback_was_required' == $result->get_error_code() ) |
|
3055 $errors = array( $result, $result->get_error_data()->update, $result->get_error_data()->rollback ); |
|
3056 else |
|
3057 $errors = array( $result ); |
|
3058 |
|
3059 foreach ( $errors as $error ) { |
|
3060 if ( ! is_wp_error( $error ) ) |
|
3061 continue; |
|
3062 $error_code = $error->get_error_code(); |
|
3063 $body .= "\n\n" . sprintf( __( "Error code: %s" ), $error_code ); |
|
3064 if ( 'rollback_was_required' == $error_code ) |
|
3065 continue; |
|
3066 if ( $error->get_error_message() ) |
|
3067 $body .= "\n" . $error->get_error_message(); |
|
3068 $error_data = $error->get_error_data(); |
|
3069 if ( $error_data ) |
|
3070 $body .= "\n" . implode( ', ', (array) $error_data ); |
|
3071 } |
|
3072 $body .= "\n"; |
|
3073 } |
|
3074 |
|
3075 $to = get_site_option( 'admin_email' ); |
|
3076 $headers = ''; |
|
3077 |
|
3078 $email = compact( 'to', 'subject', 'body', 'headers' ); |
|
3079 |
|
3080 /** |
|
3081 * Filter the email sent following an automatic background core update. |
|
3082 * |
|
3083 * @since 3.7.0 |
|
3084 * |
|
3085 * @param array $email { |
|
3086 * Array of email arguments that will be passed to wp_mail(). |
|
3087 * |
|
3088 * @type string $to The email recipient. An array of emails |
|
3089 * can be returned, as handled by wp_mail(). |
|
3090 * @type string $subject The email's subject. |
|
3091 * @type string $body The email message body. |
|
3092 * @type string $headers Any email headers, defaults to no headers. |
|
3093 * } |
|
3094 * @param string $type The type of email being sent. Can be one of |
|
3095 * 'success', 'fail', 'manual', 'critical'. |
|
3096 * @param object $core_update The update offer that was attempted. |
|
3097 * @param mixed $result The result for the core update. Can be WP_Error. |
|
3098 */ |
|
3099 $email = apply_filters( 'auto_core_update_email', $email, $type, $core_update, $result ); |
|
3100 |
|
3101 wp_mail( $email['to'], wp_specialchars_decode( $email['subject'] ), $email['body'], $email['headers'] ); |
|
3102 } |
|
3103 |
|
3104 /** |
|
3105 * Prepares and sends an email of a full log of background update results, useful for debugging and geekery. |
|
3106 * |
|
3107 * @since 3.7.0 |
|
3108 */ |
|
3109 protected function send_debug_email() { |
|
3110 $update_count = 0; |
|
3111 foreach ( $this->update_results as $type => $updates ) |
|
3112 $update_count += count( $updates ); |
|
3113 |
|
3114 $body = array(); |
|
3115 $failures = 0; |
|
3116 |
|
3117 $body[] = sprintf( __( 'WordPress site: %s' ), network_home_url( '/' ) ); |
|
3118 |
|
3119 // Core |
|
3120 if ( isset( $this->update_results['core'] ) ) { |
|
3121 $result = $this->update_results['core'][0]; |
|
3122 if ( $result->result && ! is_wp_error( $result->result ) ) { |
|
3123 $body[] = sprintf( __( 'SUCCESS: WordPress was successfully updated to %s' ), $result->name ); |
|
3124 } else { |
|
3125 $body[] = sprintf( __( 'FAILED: WordPress failed to update to %s' ), $result->name ); |
|
3126 $failures++; |
|
3127 } |
|
3128 $body[] = ''; |
|
3129 } |
|
3130 |
|
3131 // Plugins, Themes, Translations |
|
3132 foreach ( array( 'plugin', 'theme', 'translation' ) as $type ) { |
|
3133 if ( ! isset( $this->update_results[ $type ] ) ) |
|
3134 continue; |
|
3135 $success_items = wp_list_filter( $this->update_results[ $type ], array( 'result' => true ) ); |
|
3136 if ( $success_items ) { |
|
3137 $messages = array( |
|
3138 'plugin' => __( 'The following plugins were successfully updated:' ), |
|
3139 'theme' => __( 'The following themes were successfully updated:' ), |
|
3140 'translation' => __( 'The following translations were successfully updated:' ), |
|
3141 ); |
|
3142 |
|
3143 $body[] = $messages[ $type ]; |
|
3144 foreach ( wp_list_pluck( $success_items, 'name' ) as $name ) { |
|
3145 $body[] = ' * ' . sprintf( __( 'SUCCESS: %s' ), $name ); |
|
3146 } |
|
3147 } |
|
3148 if ( $success_items != $this->update_results[ $type ] ) { |
|
3149 // Failed updates |
|
3150 $messages = array( |
|
3151 'plugin' => __( 'The following plugins failed to update:' ), |
|
3152 'theme' => __( 'The following themes failed to update:' ), |
|
3153 'translation' => __( 'The following translations failed to update:' ), |
|
3154 ); |
|
3155 |
|
3156 $body[] = $messages[ $type ]; |
|
3157 foreach ( $this->update_results[ $type ] as $item ) { |
|
3158 if ( ! $item->result || is_wp_error( $item->result ) ) { |
|
3159 $body[] = ' * ' . sprintf( __( 'FAILED: %s' ), $item->name ); |
|
3160 $failures++; |
|
3161 } |
|
3162 } |
|
3163 } |
|
3164 $body[] = ''; |
|
3165 } |
|
3166 |
|
3167 $site_title = wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ); |
|
3168 if ( $failures ) { |
|
3169 $body[] = trim( __( |
|
3170 "BETA TESTING? |
|
3171 ============= |
|
3172 |
|
3173 This debugging email is sent when you are using a development version of WordPress. |
|
3174 |
|
3175 If you think these failures might be due to a bug in WordPress, could you report it? |
|
3176 * Open a thread in the support forums: https://wordpress.org/support/forum/alphabeta |
|
3177 * Or, if you're comfortable writing a bug report: https://core.trac.wordpress.org/ |
|
3178 |
|
3179 Thanks! -- The WordPress Team" ) ); |
|
3180 $body[] = ''; |
|
3181 |
|
3182 $subject = sprintf( __( '[%s] There were failures during background updates' ), $site_title ); |
|
3183 } else { |
|
3184 $subject = sprintf( __( '[%s] Background updates have finished' ), $site_title ); |
|
3185 } |
|
3186 |
|
3187 $body[] = trim( __( |
|
3188 'UPDATE LOG |
|
3189 ==========' ) ); |
|
3190 $body[] = ''; |
|
3191 |
|
3192 foreach ( array( 'core', 'plugin', 'theme', 'translation' ) as $type ) { |
|
3193 if ( ! isset( $this->update_results[ $type ] ) ) |
|
3194 continue; |
|
3195 foreach ( $this->update_results[ $type ] as $update ) { |
|
3196 $body[] = $update->name; |
|
3197 $body[] = str_repeat( '-', strlen( $update->name ) ); |
|
3198 foreach ( $update->messages as $message ) |
|
3199 $body[] = " " . html_entity_decode( str_replace( '…', '...', $message ) ); |
|
3200 if ( is_wp_error( $update->result ) ) { |
|
3201 $results = array( 'update' => $update->result ); |
|
3202 // If we rolled back, we want to know an error that occurred then too. |
|
3203 if ( 'rollback_was_required' === $update->result->get_error_code() ) |
|
3204 $results = (array) $update->result->get_error_data(); |
|
3205 foreach ( $results as $result_type => $result ) { |
|
3206 if ( ! is_wp_error( $result ) ) |
|
3207 continue; |
|
3208 |
|
3209 if ( 'rollback' === $result_type ) { |
|
3210 /* translators: 1: Error code, 2: Error message. */ |
|
3211 $body[] = ' ' . sprintf( __( 'Rollback Error: [%1$s] %2$s' ), $result->get_error_code(), $result->get_error_message() ); |
|
3212 } else { |
|
3213 /* translators: 1: Error code, 2: Error message. */ |
|
3214 $body[] = ' ' . sprintf( __( 'Error: [%1$s] %2$s' ), $result->get_error_code(), $result->get_error_message() ); |
|
3215 } |
|
3216 |
|
3217 if ( $result->get_error_data() ) |
|
3218 $body[] = ' ' . implode( ', ', (array) $result->get_error_data() ); |
|
3219 } |
|
3220 } |
|
3221 $body[] = ''; |
|
3222 } |
|
3223 } |
|
3224 |
|
3225 $email = array( |
|
3226 'to' => get_site_option( 'admin_email' ), |
|
3227 'subject' => $subject, |
|
3228 'body' => implode( "\n", $body ), |
|
3229 'headers' => '' |
|
3230 ); |
|
3231 |
|
3232 /** |
|
3233 * Filter the debug email that can be sent following an automatic |
|
3234 * background core update. |
|
3235 * |
|
3236 * @since 3.8.0 |
|
3237 * |
|
3238 * @param array $email { |
|
3239 * Array of email arguments that will be passed to wp_mail(). |
|
3240 * |
|
3241 * @type string $to The email recipient. An array of emails |
|
3242 * can be returned, as handled by wp_mail(). |
|
3243 * @type string $subject Email subject. |
|
3244 * @type string $body Email message body. |
|
3245 * @type string $headers Any email headers. Default empty. |
|
3246 * } |
|
3247 * @param int $failures The number of failures encountered while upgrading. |
|
3248 * @param mixed $results The results of all attempted updates. |
|
3249 */ |
|
3250 $email = apply_filters( 'automatic_updates_debug_email', $email, $failures, $this->update_results ); |
|
3251 |
|
3252 wp_mail( $email['to'], wp_specialchars_decode( $email['subject'] ), $email['body'], $email['headers'] ); |
|
3253 } |
|
3254 } |
|