PluginProbe
ElasticPress / 5.3.5
ElasticPress v5.3.5
5.3.5 5.3.4 3.6.5 3.6.6 4.0.0 4.0.1 4.1.0 4.2.0 4.2.1 4.2.2 4.3.0 4.3.1 4.4.0 4.4.1 4.5.0 4.5.1 4.5.2 4.6.0 4.6.1 4.7.0 4.7.1 4.7.2 5.0.0 5.0.1 5.0.2 All 108 releases
← All changes | includes/classes/IndexHelper.php +310 -75 4.5.05.3.5 View file →
@@ -1,17 +1,20 @@
1 1 <?php
2 2 /**
3 3 * Index Helper
4 4 *
5 + * NOTE: As explained in the doc linked below, the dashboard sync exits after each output()
6 + * call, to respond to the AJAX request. That means this script will be called several times
7 + * while syncing via dashboard, relying on the index_meta to pick it up where it stopped.
8 + *
5 9 * @since 4.0.0
6 - * @see docs/indexing-process.md
7 - * @see https://10up.github.io/ElasticPress/tutorial-indexing-process.html
10 + * @see https://www.elasticpress.io/resources/articles/sync-process/
8 11 * @package elasticpress
9 12 */
10 13
11 14 namespace ElasticPress;
12 15
13 -use ElasticPress\Utils as Utils;
16 +use ElasticPress\Utils;
14 17
15 18 /**
16 19 * Index Helper Class.
17 20 *
@@ -81,11 +84,15 @@
81 84 */
82 85 $this->args = apply_filters( 'ep_sync_args', $args, $this->index_meta );
83 86
84 87 if ( false === $this->index_meta ) {
88 + $this->maybe_apply_feature_settings();
85 89 $this->build_index_meta();
86 90 }
87 91
92 + // For the dashboard, this will be called and exit the script until the queue is empty again.
93 + $this->flush_messages_queue();
94 +
88 95 while ( $this->has_items_to_be_processed() ) {
89 96 $this->process_sync_item();
90 97 }
91 98
@@ -136,8 +143,10 @@
136 143 'network_alias' => [],
137 144 'start_time' => microtime( true ),
138 145 'start_date_time' => $start_date_time ? $start_date_time->format( DATE_ATOM ) : false,
139 146 'starting_indices' => $starting_indices,
147 + 'messages_queue' => [],
148 + 'trigger' => ! empty( $this->args['trigger'] ) ? sanitize_text_field( $this->args['trigger'] ) : null,
140 149 'totals' => [
141 150 'total' => 0,
142 151 'synced' => 0,
143 152 'skipped' => 0,
@@ -156,15 +165,11 @@
156 165 if ( ! is_numeric( $this->args['network_wide'] ) ) {
157 166 $this->args['network_wide'] = 0;
158 167 }
159 168
160 - $sites = Utils\get_sites( $this->args['network_wide'] );
169 + $sites = Utils\get_sites( $this->args['network_wide'], true );
161 170
162 171 foreach ( $sites as $site ) {
163 - if ( ! Utils\is_site_indexable( $site['blog_id'] ) ) {
164 - continue;
165 - }
166 -
167 172 switch_to_blog( $site['blog_id'] );
168 173
169 174 foreach ( $non_global_indexables as $indexable ) {
170 175 $this->add_sync_item_to_stack(
@@ -244,9 +249,9 @@
244 249 */
245 250 protected function filter_indexables( $indexables ) {
246 251 return array_filter(
247 252 $indexables,
248 - function( $indexable ) {
253 + function ( $indexable ) {
249 254 return empty( $this->args['indexables'] ) || in_array( $indexable, $this->args['indexables'], true );
250 255 }
251 256 );
252 257 }
@@ -566,9 +571,9 @@
566 571 $queued_items = [];
567 572
568 573 foreach ( $this->current_query['objects'] as $object ) {
569 574 if ( $this->should_skip_object_index( $object, $indexable ) ) {
570 - $this->index_meta['current_sync_item']['skipped']++;
575 + ++$this->index_meta['current_sync_item']['skipped'];
571 576 } else {
572 577 $queued_items[ $object->ID ] = true;
573 578 }
574 579 }
@@ -669,9 +674,9 @@
669 674 $failed_objects = array_merge(
670 675 $failed_objects,
671 676 array_filter(
672 677 $return['items'],
673 - function( $item ) {
678 + function ( $item ) {
674 679 return ! empty( $item['index']['error'] );
675 680 }
676 681 )
677 682 );
@@ -686,19 +691,33 @@
686 691 }
687 692
688 693 if ( is_wp_error( $return ) ) {
689 694 $this->index_meta['current_sync_item']['failed'] += count( $queued_items );
690 - $this->index_meta['current_sync_item']['errors'] = array_merge( $this->index_meta['current_sync_item']['errors'], $return->get_error_messages() );
691 695
692 - $this->output( implode( "\n", $return->get_error_messages() ), 'warning' );
696 + $wp_error_messages = $return->get_error_messages();
697 +
698 + $this->maybe_process_error_limit(
699 + count( $this->index_meta['current_sync_item']['errors'] ) + count( $wp_error_messages ),
700 + count( $this->index_meta['current_sync_item']['errors'] ),
701 + $wp_error_messages
702 + );
703 +
704 + $this->queue_message( $wp_error_messages, 'warning' );
693 705 } elseif ( count( $failed_objects ) ) {
694 706 $errors_output = $this->output_index_errors( $failed_objects );
695 707
696 708 $this->index_meta['current_sync_item']['synced'] += count( $queued_items ) - count( $failed_objects );
709 +
710 + $this->maybe_process_error_limit(
711 + $this->index_meta['current_sync_item']['failed'] + count( $failed_objects ),
712 + $this->index_meta['current_sync_item']['failed'],
713 + $errors_output
714 + );
715 +
697 716 $this->index_meta['current_sync_item']['failed'] += count( $failed_objects );
698 - $this->index_meta['current_sync_item']['errors'] = array_merge( $this->index_meta['current_sync_item']['errors'], $errors_output );
717 + $error_type = ! empty( $this->args['stop_on_error'] ) ? 'error' : 'warning';
699 718
700 - $this->output( $errors_output, 'warning' );
719 + $this->queue_message( $errors_output, $error_type );
701 720 } else {
702 721 $this->index_meta['current_sync_item']['synced'] += count( $queued_items );
703 722 }
704 723 }
@@ -704,24 +723,61 @@
704 723 }
705 724
706 725 $this->index_meta['current_sync_item']['last_processed_object_id'] = end( $this->current_query['objects'] )->ID;
707 726
708 - $this->output(
709 - sprintf(
710 - /* translators: 1. Indexable type 2. Offset start, 3. Offset end, 4. Found items 5. Last object ID */
711 - esc_html__( 'Processed %1$s %2$d - %3$d of %4$d. Last Object ID: %5$d', 'elasticpress' ),
712 - esc_html( strtolower( $indexable->labels['plural'] ) ),
713 - $this->index_meta['from'],
714 - $this->index_meta['offset'],
715 - $this->index_meta['found_items'],
716 - $this->index_meta['current_sync_item']['last_processed_object_id']
717 - ),
718 - 'info',
719 - 'index_next_batch'
727 + $summary = sprintf(
728 + /* translators: 1. Indexable type 2. Offset start, 3. Offset end, 4. Found items 5. Last object ID */
729 + esc_html__( 'Processed %1$s %2$d - %3$d of %4$d. Last Object ID: %5$d', 'elasticpress' ),
730 + esc_html( strtolower( $indexable->labels['plural'] ) ),
731 + $this->index_meta['from'],
732 + $this->index_meta['offset'],
733 + $this->index_meta['found_items'],
734 + $this->index_meta['current_sync_item']['last_processed_object_id']
720 735 );
736 +
737 + $this->queue_message( $summary, 'info', 'index_next_batch' );
738 + $this->flush_messages_queue();
721 739 }
722 740
723 741 /**
742 + * If the number of errors is greater than the limit, slice the array to the limit.
743 + * If the number of errors is less than or equal the limit, add the error message to the array (if it's not there).
744 + * Merges the new errors with the existing errors.
745 + *
746 + * @since 4.5.1
747 + * @param int $count Number of errors.
748 + * @param int $num Number of errors to subtract from $limit.
749 + * @param array $errors Array of errors.
750 + */
751 + protected function maybe_process_error_limit( $count, $num, $errors ) {
752 + $error_store_msg = __( 'Reached maximum number of errors to store', 'elasticpress' );
753 +
754 + /**
755 + * Filter the number of errors of a current sync that should be stored.
756 + *
757 + * @since 4.5.1
758 + * @hook ep_current_sync_number_of_errors_stored
759 + * @param {int} $number Number of errors to be logged.
760 + * @return {int} New value
761 + */
762 + $limit = (int) apply_filters( 'ep_current_sync_number_of_errors_stored', 50 );
763 +
764 + if ( $limit > 0 && $count > $limit ) {
765 + $diff = $limit - $num;
766 + if ( $diff > 0 ) {
767 + $errors = array_slice( $errors, 0, $diff );
768 + } else {
769 + $errors = [];
770 + if ( end( $this->index_meta['current_sync_item']['errors'] ) !== $error_store_msg ) {
771 + $this->index_meta['current_sync_item']['errors'][] = $error_store_msg;
772 + }
773 + }
774 + }
775 +
776 + $this->index_meta['current_sync_item']['errors'] = array_merge( $this->index_meta['current_sync_item']['errors'], $errors );
777 + }
778 +
779 + /**
724 780 * Update the sync info with the totals from the last sync item.
725 781 *
726 782 * @since 4.2.0
727 783 */
@@ -765,8 +821,9 @@
765 821
766 822 $current_sync_item = $this->index_meta['current_sync_item'];
767 823
768 824 $this->index_meta['current_sync_item'] = null;
825 + $this->index_meta['offset'] = 0;
769 826
770 827 if ( $current_sync_item['failed'] ) {
771 828 if ( ! empty( $current_sync_item['blog_id'] ) && defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
772 829 $message = sprintf(
@@ -787,10 +844,8 @@
787 844
788 845 $this->output( $message, 'warning' );
789 846 }
790 847
791 - $this->index_meta['offset'] = 0;
792 -
793 848 if ( ! empty( $current_sync_item['blog_id'] ) && defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
794 849 $message = sprintf(
795 850 /* translators: 1: indexable (plural), 2: Blog ID, 3: number of synced objects */
796 851 esc_html__( 'Number of %1$s indexed on site %2$d: %3$d', 'elasticpress' ),
@@ -813,14 +868,16 @@
813 868 /**
814 869 * Update last sync info.
815 870 *
816 871 * @since 4.2.0
872 + * @param string $final_status Optional final status
817 873 */
818 - protected function update_last_index() {
874 + protected function update_last_index( string $final_status = '' ) {
875 + $is_full_sync = $this->index_meta['put_mapping'];
876 + $method = $this->index_meta['method'];
819 877 $start_time = $this->index_meta['start_time'];
820 878 $totals = $this->index_meta['totals'];
821 - $method = $this->index_meta['method'];
822 - $is_full_sync = $this->index_meta['put_mapping'];
879 + $trigger = $this->index_meta['trigger'];
823 880
824 881 $this->index_meta = null;
825 882
826 883 $end_date_time = date_create( 'now', wp_timezone() );
@@ -825,19 +882,67 @@
825 882
826 883 $end_date_time = date_create( 'now', wp_timezone() );
827 884 $start_time_sec = (int) $start_time;
828 885
886 + // Time related info
829 887 $totals['end_date_time'] = $end_date_time ? $end_date_time->format( DATE_ATOM ) : false;
830 888 $totals['start_date_time'] = $start_time ? wp_date( DATE_ATOM, $start_time_sec ) : false;
831 889 $totals['end_time_gmt'] = time();
832 890 $totals['total_time'] = microtime( true ) - $start_time;
833 - $totals['method'] = $method;
834 - $totals['is_full_sync'] = $is_full_sync;
891 +
892 + // Additional info
893 + $totals['is_full_sync'] = $is_full_sync;
894 + $totals['method'] = $method;
895 + $totals['trigger'] = $trigger;
896 +
897 + // Final status
898 + if ( '' !== $final_status ) {
899 + $totals['final_status'] = $final_status;
900 + } elseif ( ! empty( $totals['failed'] ) ) {
901 + $totals['final_status'] = 'with_errors';
902 + } else {
903 + $totals['final_status'] = 'success';
904 + }
905 +
835 906 Utils\update_option( 'ep_last_cli_index', $totals, false );
836 - Utils\update_option( 'ep_last_index', $totals, false );
907 +
908 + $this->add_last_sync( $totals );
837 909 }
838 910
839 911 /**
912 + * Add a sync to the list of all past syncs
913 + *
914 + * @since 5.0.0
915 + * @param array $last_sync_info The latest sync info to be added to the log
916 + * @return void
917 + */
918 + protected function add_last_sync( array $last_sync_info ) {
919 + // Remove error messages from previous syncs - we only store msgs for the newest one.
920 + $last_syncs = array_map(
921 + function ( $sync ) {
922 + unset( $sync['errors'] );
923 + return $sync;
924 + },
925 + $this->get_sync_history()
926 + );
927 +
928 + /**
929 + * Filter the number of past syncs to keep info
930 + *
931 + * @since 5.0.0
932 + * @hook ep_syncs_to_keep_info
933 + * @param {int} $number Number of past syncs to keep info
934 + * @return {int} New number
935 + */
936 + $syncs_to_keep = (int) apply_filters( 'ep_syncs_to_keep_info', 5 );
937 +
938 + $last_syncs = array_slice( $last_syncs, 0, $syncs_to_keep - 1 );
939 + array_unshift( $last_syncs, $last_sync_info );
940 +
941 + Utils\update_option( 'ep_sync_history', $last_syncs, false );
942 + }
943 +
944 + /**
840 945 * Make the necessary clean up after everything was sync'd.
841 946 *
842 947 * @since 4.0.0
843 948 */
@@ -847,11 +952,12 @@
847 952 /**
848 953 * Fires after executing a reindex
849 954 *
850 955 * @since 4.0.0
956 + * @param array $args Sync arguments.
851 957 * @hook ep_after_sync_index
852 958 */
853 - do_action( 'ep_after_sync_index' );
959 + do_action( 'ep_after_sync_index', $this->args );
854 960
855 961 /**
856 962 * Fires after executing a reindex
857 963 *
@@ -882,16 +988,11 @@
882 988 protected function create_network_alias() {
883 989 $indexes = [];
884 990 $indexable = Indexables::factory()->get( array_shift( $this->index_meta['network_alias'] ) );
885 991
886 - $sites = Utils\get_sites();
992 + $sites = Utils\get_sites( 0, true );
887 993
888 994 foreach ( $sites as $site ) {
889 -
890 - if ( ! Utils\is_site_indexable( $site['blog_id'] ) ) {
891 - continue;
892 - }
893 -
894 995 switch_to_blog( $site['blog_id'] );
895 996 $indexes[] = $indexable->get_index_name();
896 997 restore_current_blog();
897 998 }
@@ -930,9 +1031,9 @@
930 1031 if ( $this->index_meta ) {
931 1032 Utils\update_option( 'ep_index_meta', $this->index_meta );
932 1033 } else {
933 1034 Utils\delete_option( 'ep_index_meta' );
934 - $totals = $this->get_last_index();
1035 + $totals = $this->get_last_sync();
935 1036 }
936 1037
937 1038 $message = [
938 1039 'message' => ( is_array( $message_text ) ) ? implode( "\n", $message_text ) : $message_text,
@@ -940,8 +1041,12 @@
940 1041 'totals' => $totals ?? [],
941 1042 'status' => $type,
942 1043 ];
943 1044
1045 + if ( in_array( $type, [ 'warning', 'error' ], true ) ) {
1046 + $message['errors'] = $this->build_message_errors_data( $message_text );
1047 + }
1048 +
944 1049 if ( is_callable( $this->args['output_method'] ) ) {
945 1050 call_user_func( $this->args['output_method'], $message, $this->args, $this->index_meta, $context );
946 1051 }
947 1052 }
@@ -979,9 +1084,9 @@
979 1084
980 1085 $error_text = [];
981 1086
982 1087 foreach ( $failed_objects as $object ) {
983 - $error_text[] = $object['index']['_id'] . ' (' . $indexable->labels['singular'] . '): [' . $object['index']['error']['type'] . '] ' . $object['index']['error']['reason'];
1088 + $error_text[] = ! empty( $object['index'] ) ? $object['index']['_id'] . ' (' . $indexable->labels['singular'] . '): [' . $object['index']['error']['type'] . '] ' . $object['index']['error']['reason'] : (string) $object;
984 1089 }
985 1090
986 1091 return $error_text;
987 1092 }
@@ -986,9 +1091,9 @@
986 1091 return $error_text;
987 1092 }
988 1093
989 1094 /**
990 - * Utilitary function to check if the indexable is being fully reindexed, i.e.,
1095 + * Utility function to check if the indexable is being fully reindexed, i.e.,
991 1096 * the index was deleted, a new mapping was sent and content is being reindexed.
992 1097 *
993 1098 * @param string $indexable_slug Indexable slug.
994 1099 * @param int|null $blog_id Blog ID
@@ -1034,50 +1139,71 @@
1034 1139 return apply_filters( "ep_is_full_reindexing_{$indexable_slug}", $is_full_reindexing );
1035 1140 }
1036 1141
1037 1142 /**
1038 - * Get the last index/sync meta information.
1143 + * Get the previous syncs meta information.
1039 1144 *
1040 - * @since 4.2.0
1145 + * @since 5.0.0
1041 1146 * @return array
1042 1147 */
1043 - public function get_last_index() {
1044 - return Utils\get_option( 'ep_last_index', [] );
1148 + public function get_sync_history(): array {
1149 + return Utils\get_option( 'ep_sync_history', [] );
1045 1150 }
1046 1151
1047 1152 /**
1153 + * Get the last sync meta information.
1154 + *
1155 + * @since 5.0.0
1156 + * @return array
1157 + */
1158 + public function get_last_sync(): array {
1159 + $syncs = $this->get_sync_history();
1160 + if ( empty( $syncs ) ) {
1161 + return [];
1162 + }
1163 + return array_shift( $syncs );
1164 + }
1165 +
1166 + /**
1048 1167 * Check if an object should be indexed or skipped.
1049 1168 *
1050 1169 * We used to have two different filters for this (one for the dashboard, another for CLI),
1051 1170 * this method combines both.
1052 1171 *
1053 - * @param {stdClass} $object Object to be checked
1172 + * @param {stdClass} $indexable_object Object to be checked
1054 1173 * @param {Indexable} $indexable Indexable
1055 1174 * @return boolean
1056 1175 */
1057 - protected function should_skip_object_index( $object, $indexable ) {
1176 + protected function should_skip_object_index( $indexable_object, $indexable ) {
1058 1177 /**
1059 1178 * Filter whether to not sync specific item in dashboard or not
1060 1179 *
1061 1180 * @since 2.1
1181 + * @deprecated 5.3.3 Use ep_{indexable_slug}_sync_kill instead
1062 1182 * @hook ep_item_sync_kill
1063 1183 * @param {boolean} $kill False means dont sync
1064 - * @param {array} $object Object to sync
1184 + * @param {array} $indexable_object Object to sync
1065 1185 * @return {Indexable} Indexable that object belongs to
1066 1186 */
1067 - $ep_item_sync_kill = apply_filters( 'ep_item_sync_kill', false, $object, $indexable );
1187 + $ep_item_sync_kill = apply_filters_deprecated(
1188 + 'ep_item_sync_kill',
1189 + [ false, $indexable_object, $indexable ],
1190 + 'ElasticPress 5.3.3',
1191 + 'ep_' . $indexable->slug . '_sync_kill'
1192 + );
1068 1193
1069 - /**
1070 - * Conditionally kill indexing for a post
1071 - *
1072 - * @hook ep_{indexable_slug}_index_kill
1073 - * @param {bool} $index True means dont index
1074 - * @param {int} $object_id Object ID
1075 - * @return {bool} New value
1076 - */
1077 - $ep_indexable_sync_kill = apply_filters( 'ep_' . $indexable->slug . '_index_kill', false, $object->ID );
1194 + /** This filter is documented in includes/classes/Indexable.php */
1195 + $ep_indexable_index_kill = apply_filters_deprecated(
1196 + 'ep_' . $indexable->slug . '_index_kill',
1197 + [ false, $indexable_object->ID ],
1198 + 'ElasticPress 5.3.3',
1199 + 'ep_' . $indexable->slug . '_sync_kill'
1200 + );
1078 1201
1079 - return $ep_item_sync_kill || $ep_indexable_sync_kill;
1202 + /** This filter is documented in includes/classes/Indexable.php */
1203 + $ep_indexable_sync_kill = apply_filters( 'ep_' . $indexable->slug . '_sync_kill', false, $indexable_object->ID );
1204 +
1205 + return $ep_item_sync_kill || $ep_indexable_sync_kill || $ep_indexable_index_kill;
1080 1206 }
1081 1207
1082 1208 /**
1083 1209 * Given an array, create a new sync item and add it to the stack.
@@ -1158,16 +1284,14 @@
1158 1284 * in-memory cache for persistent object caches
1159 1285 */
1160 1286 if ( function_exists( 'wp_cache_flush_runtime' ) ) {
1161 1287 wp_cache_flush_runtime();
1162 - } else {
1288 + } elseif ( ! wp_using_ext_object_cache() ) {
1163 1289 /*
1164 1290 * In the case where we're not using an external object cache, we need to call flush on the default
1165 1291 * WordPress object cache class to clear the values from the cache property
1166 1292 */
1167 - if ( ! wp_using_ext_object_cache() ) {
1168 - wp_cache_flush();
1169 - }
1293 + wp_cache_flush();
1170 1294 }
1171 1295
1172 1296 if ( is_object( $wp_object_cache ) ) {
1173 1297 $wp_object_cache->group_ops = [];
@@ -1208,19 +1332,22 @@
1208 1332 do_action( 'ep_stop_the_insanity' );
1209 1333 }
1210 1334
1211 1335 /**
1212 - * Utilitary function to delete the index meta option.
1336 + * Utility function to delete the index meta option.
1213 1337 *
1214 1338 * @since 4.0.0
1215 1339 */
1216 1340 public function clear_index_meta() {
1341 + if ( ! empty( $this->index_meta ) ) {
1342 + $this->update_last_index( 'aborted' );
1343 + }
1217 1344 $this->index_meta = false;
1218 1345 Utils\delete_option( 'ep_index_meta', false );
1219 1346 }
1220 1347
1221 1348 /**
1222 - * Utilitary function to get the index meta option.
1349 + * Utility function to get the index meta option.
1223 1350 *
1224 1351 * @return array
1225 1352 * @since 4.0.0
1226 1353 */
@@ -1272,9 +1399,9 @@
1272 1399 $totals = $this->index_meta['totals'];
1273 1400
1274 1401 $this->index_meta['totals']['errors'][] = $error['message'];
1275 1402 $this->index_meta['totals']['failed'] = $totals['total'] - ( $totals['synced'] + $totals['skipped'] );
1276 - $this->update_last_index();
1403 + $this->update_last_index( 'failed' );
1277 1404
1278 1405 /**
1279 1406 * Fires after a sync failed due to a PHP fatal error.
1280 1407 *
@@ -1285,12 +1412,17 @@
1285 1412 do_action( 'ep_after_sync_error', $error );
1286 1413
1287 1414 switch ( $context ) {
1288 1415 case 'mapping':
1289 - /* translators: Error message */
1290 - $message = sprintf( esc_html__( 'Mapping failed: %s', 'elasticpress' ), $error['message'] );
1291 - $message .= "\n";
1292 - $message .= esc_html__( 'Mapping has failed, which will cause ElasticPress search results to be incorrect. Please click `Delete all Data and Start a Fresh Sync` to retry mapping.', 'elasticpress' );
1416 + $message = sprintf(
1417 + /* translators: Error message */
1418 + esc_html__( 'Mapping failed: %s', 'elasticpress' ),
1419 + Utils\get_elasticsearch_error_reason( $error['message'] )
1420 + );
1421 + if ( $this->should_suggest_retry( $message ) ) {
1422 + $message .= "\n";
1423 + $message .= esc_html__( 'Mapping has failed, which will cause ElasticPress search results to be incorrect. Please click `Delete all Data and Start a Fresh Sync` to retry mapping.', 'elasticpress' );
1424 + }
1293 1425 break;
1294 1426 default:
1295 1427 /* translators: Error message */
1296 1428 $message = sprintf( esc_html__( 'Index failed: %s', 'elasticpress' ), $error['message'] );
@@ -1305,9 +1437,9 @@
1305 1437 *
1306 1438 * @since 4.4.0
1307 1439 * @return integer
1308 1440 */
1309 - public function get_index_default_per_page() : int {
1441 + public function get_index_default_per_page(): int {
1310 1442 /**
1311 1443 * Filter number of items to index per cycle in the dashboard
1312 1444 *
1313 1445 * @since 2.1
@@ -1318,8 +1450,99 @@
1318 1450 return (int) apply_filters( 'ep_index_default_per_page', Utils\get_option( 'ep_bulk_setting', 350 ) );
1319 1451 }
1320 1452
1321 1453 /**
1454 + * Add a message to the queue
1455 + *
1456 + * @since 4.7.0
1457 + * @param string|array $message_text Message to be outputted
1458 + * @param string $type Type of message
1459 + * @param string $context Context of the output
1460 + */
1461 + protected function queue_message( $message_text, string $type, string $context = '' ) {
1462 + $this->index_meta['messages_queue'][] = [
1463 + 'text' => $message_text,
1464 + 'type' => $type,
1465 + 'context' => $context,
1466 + ];
1467 + }
1468 +
1469 + /**
1470 + * Display messages in the queue.
1471 + *
1472 + * NOTE: As the dashboard sync exits after every output call (to respond the AJAX request),
1473 + * this will just output one message. As the method is called every time the script is called,
1474 + * all messages will be displayed but one at a time.
1475 + *
1476 + * @since 4.7.0
1477 + */
1478 + protected function flush_messages_queue() {
1479 + if ( ! is_array( $this->index_meta['messages_queue'] ) ) {
1480 + return;
1481 + }
1482 +
1483 + $messages_count = count( $this->index_meta['messages_queue'] );
1484 + if ( 0 === $messages_count ) {
1485 + return;
1486 + }
1487 +
1488 + for ( $i = 0; $i < $messages_count; $i++ ) {
1489 + $next_message = array_shift( $this->index_meta['messages_queue'] );
1490 + $this->output( $next_message['text'], $next_message['type'], $next_message['context'] );
1491 + }
1492 + }
1493 +
1494 + /**
1495 + * Get data for a given error message(s)
1496 + *
1497 + * @since 5.0.0
1498 + * @param string|array $messages Messages
1499 + * @return array
1500 + */
1501 + protected function build_message_errors_data( $messages ): array {
1502 + $messages = (array) $messages;
1503 + $error_interpreter = new \ElasticPress\ElasticsearchErrorInterpreter();
1504 +
1505 + $errors_list = [];
1506 + foreach ( $messages as $message ) {
1507 + $error = $error_interpreter->maybe_suggest_solution_for_es( $message );
1508 +
1509 + if ( ! isset( $errors_list[ $error['error'] ] ) ) {
1510 + $errors_list[ $error['error'] ] = [
1511 + 'solution' => $error['solution'],
1512 + 'count' => 1,
1513 + ];
1514 + } else {
1515 + ++$errors_list[ $error['error'] ]['count'];
1516 + }
1517 + }
1518 + return $errors_list;
1519 + }
1520 +
1521 + /**
1522 + * If this is a full sync, apply the draft feature settings
1523 + *
1524 + * @since 5.0.0
1525 + */
1526 + protected function maybe_apply_feature_settings() {
1527 + if ( empty( $this->args['put_mapping'] ) ) {
1528 + return;
1529 + }
1530 +
1531 + Features::factory()->apply_draft_feature_settings();
1532 + }
1533 +
1534 + /**
1535 + * Whether to suggest retrying the sync or not.
1536 + *
1537 + * @param string $message The message returned by the hosting server
1538 + * @return boolean
1539 + */
1540 + protected function should_suggest_retry( $message ) {
1541 + return ! preg_match( '/you have reached the limit of indices your plan supports/', $message );
1542 + }
1543 +
1544 + /**
1322 1545 * Return singleton instance of class.
1323 1546 *
1324 1547 * @return self
1325 1548 * @since 4.0.0
@@ -1332,6 +1555,18 @@
1332 1555 $instance->setup();
1333 1556 }
1334 1557
1335 1558 return $instance;
1559 + }
1560 +
1561 + /**
1562 + * DEPRECATED. Get the last index/sync meta information.
1563 + *
1564 + * @since 4.2.0
1565 + * @deprecated 5.0.0
1566 + * @return array
1567 + */
1568 + public function get_last_index() {
1569 + _deprecated_function( __METHOD__, '5.0.0', '\ElasticPress\IndexHelper::get_last_sync' );
1570 + return $this->get_last_sync();
1336 1571 }
1337 1572 }