PageRenderTime 83ms CodeModel.GetById 18ms RepoModel.GetById 3ms app.codeStats 0ms

/wp-content/plugins/the-events-calendar/src/Tribe/Importer/File_Importer.php

https://gitlab.com/ezgonzalez/integral
PHP | 330 lines | 215 code | 53 blank | 62 comment | 30 complexity | 5697563b6ef24e7921fa2ead3c092499 MD5 | raw file
  1. <?php
  2. /**
  3. * Class Tribe__Events__Importer__File_Importer
  4. */
  5. abstract class Tribe__Events__Importer__File_Importer {
  6. protected $required_fields = array();
  7. /** @var Tribe__Events__Importer__File_Reader */
  8. private $reader = null;
  9. private $map = array();
  10. private $inverted_map = array();
  11. private $type = '';
  12. private $limit = 100;
  13. private $offset = 0;
  14. private $errors = array();
  15. private $updated = 0;
  16. private $created = 0;
  17. private $skipped = array();
  18. private $encoding = array();
  19. private $log = array();
  20. /**
  21. * @var Tribe__Events__Importer__Featured_Image_Uploader
  22. */
  23. protected $featured_image_uploader;
  24. /**
  25. * @param string $type
  26. * @param Tribe__Events__Importer__File_Reader $file_reader
  27. *
  28. * @return Tribe__Events__Importer__File_Importer
  29. * @throws InvalidArgumentException
  30. */
  31. public static function get_importer( $type, Tribe__Events__Importer__File_Reader $file_reader ) {
  32. switch ( $type ) {
  33. case 'events':
  34. return new Tribe__Events__Importer__File_Importer_Events( $file_reader );
  35. case 'venues':
  36. return new Tribe__Events__Importer__File_Importer_Venues( $file_reader );
  37. case 'organizers':
  38. return new Tribe__Events__Importer__File_Importer_Organizers( $file_reader );
  39. default:
  40. /**
  41. * Allows developers to return an importer instance to use for unsupported import types.
  42. *
  43. * @param bool|mixed An importer instance or `false` if not found or not supported.
  44. * @param Tribe__Events__Importer__File_Reader $file_reader
  45. */
  46. $importer = apply_filters( "tribe_events_import_{$type}_importer", false, $file_reader );
  47. if ( false === $importer ) {
  48. throw new InvalidArgumentException( sprintf( esc_html__( 'No importer defined for %s', 'the-events-calendar' ), $type ) );
  49. }
  50. return $importer;
  51. }
  52. }
  53. /**
  54. * @param Tribe__Events__Importer__File_Reader $file_reader
  55. */
  56. public function __construct( Tribe__Events__Importer__File_Reader $file_reader, Tribe__Events__Importer__Featured_Image_Uploader $featured_image_uploader = null ) {
  57. $this->reader = $file_reader;
  58. $this->featured_image_uploader = $featured_image_uploader;
  59. }
  60. public function set_map( array $map_array ) {
  61. $this->map = $map_array;
  62. $this->inverted_map = array_flip( $this->map );
  63. }
  64. public function set_type( $type ) {
  65. $this->type = $type;
  66. }
  67. public function set_limit( $limit ) {
  68. $this->limit = (int) $limit;
  69. }
  70. public function set_offset( $offset ) {
  71. $this->offset = (int) $offset;
  72. }
  73. public function do_import() {
  74. $this->reader->set_row( $this->offset );
  75. for ( $i = 0; $i < $this->limit && ! $this->import_complete(); $i ++ ) {
  76. set_time_limit( 30 );
  77. $this->import_next_row();
  78. }
  79. }
  80. public function get_last_completed_row() {
  81. return $this->reader->get_last_line_number_read() + 1;
  82. }
  83. public function import_complete() {
  84. return $this->reader->at_end_of_file();
  85. }
  86. public function get_updated_post_count() {
  87. return $this->updated;
  88. }
  89. public function get_new_post_count() {
  90. return $this->created;
  91. }
  92. public function get_skipped_row_count() {
  93. return count( $this->skipped );
  94. }
  95. public function get_skipped_row_numbers() {
  96. return $this->skipped;
  97. }
  98. public function get_encoding_changes_row_count() {
  99. return count( $this->encoding );
  100. }
  101. public function get_encoding_changes_row_numbers() {
  102. return $this->encoding;
  103. }
  104. public function get_log_messages() {
  105. return $this->log;
  106. }
  107. public function get_required_fields() {
  108. return $this->required_fields;
  109. }
  110. public function get_type() {
  111. return $this->type;
  112. }
  113. public function import_next_row( $throw = false ) {
  114. $post_id = null;
  115. $record = $this->reader->read_next_row();
  116. $row = $this->reader->get_last_line_number_read() + 1;
  117. //Check if option to encode is active
  118. $encoding_option = Tribe__Events__Importer__Options::getOption( 'imported_encoding_status', array( 'csv' => 'encode' ) );
  119. if ( isset( $encoding_option['csv'] ) && 'encode' == $encoding_option['csv'] ) {
  120. $encoded = ForceUTF8__Encoding::toUTF8( $record );
  121. $encoding_diff = array_diff( $encoded, $record );
  122. if ( ! empty( $encoding_diff ) ) {
  123. $this->encoding[] = $row;
  124. }
  125. $record = $encoded;
  126. }
  127. if ( ! $this->is_valid_record( $record ) ) {
  128. if ( ! $throw ) {
  129. $this->log[ $row ] = $this->get_skipped_row_message( $row );
  130. $this->skipped[] = $row;
  131. return false;
  132. } else {
  133. throw new RuntimeException( sprintf( 'Missing required fields in row %d', $row ) );
  134. }
  135. }
  136. try {
  137. $post_id = $this->update_or_create_post( $record );
  138. } catch ( Exception $e ) {
  139. $this->log[ $row ] = sprintf( esc_html__( 'Failed to import record in row %d.', 'the-events-calendar' ), $row );
  140. $this->skipped[] = $row;
  141. }
  142. return $post_id;
  143. }
  144. protected function update_or_create_post( array $record ) {
  145. if ( $id = $this->match_existing_post( $record ) ) {
  146. $this->update_post( $id, $record );
  147. $this->updated ++;
  148. $this->log[ $this->reader->get_last_line_number_read() + 1 ] = sprintf( esc_html__( '%s (post ID %d) updated.', 'the-events-calendar' ), get_the_title( $id ), $id );
  149. } else {
  150. $id = $this->create_post( $record );
  151. $this->created ++;
  152. $this->log[ $this->reader->get_last_line_number_read() + 1 ] = sprintf( esc_html__( '%s (post ID %d) created.', 'the-events-calendar' ), get_the_title( $id ), $id );
  153. }
  154. return $id;
  155. }
  156. abstract protected function match_existing_post( array $record );
  157. abstract protected function update_post( $post_id, array $record );
  158. abstract protected function create_post( array $record );
  159. protected function is_valid_record( array $record ) {
  160. foreach ( $this->get_required_fields() as $field ) {
  161. if ( $this->get_value_by_key( $record, $field ) == '' ) {
  162. return false;
  163. }
  164. }
  165. return true;
  166. }
  167. /**
  168. * Retrieves a value from the record.
  169. *
  170. * @param array $record
  171. * @param string $key
  172. *
  173. * @return mixed|string Either the value or an empty string if the value was not found.
  174. */
  175. public function get_value_by_key( array $record, $key ) {
  176. if ( ! isset( $this->inverted_map[ $key ] ) ) {
  177. return '';
  178. }
  179. if ( ! isset( $record[ $this->inverted_map[ $key ] ] ) ) {
  180. return '';
  181. }
  182. return $record[ $this->inverted_map[ $key ] ];
  183. }
  184. protected function find_matching_post_id( $name, $post_type ) {
  185. if ( empty( $name ) ) {
  186. return 0;
  187. }
  188. if ( is_numeric( $name ) && intval( $name ) == $name ) {
  189. $found = get_post( $name );
  190. if ( $found && $found->post_type == $post_type ) {
  191. return $name;
  192. }
  193. }
  194. $query_args = array(
  195. 'post_type' => $post_type,
  196. 'post_status' => 'publish',
  197. 'post_title' => $name,
  198. 'fields' => 'ids',
  199. 'suppress_filters' => false,
  200. );
  201. add_filter( 'posts_search', array( $this, 'filter_query_for_title_search' ), 10, 2 );
  202. $ids = get_posts( $query_args );
  203. remove_filter( 'posts_search', array( $this, 'filter_query_for_title_search' ), 10 );
  204. return empty( $ids ) ? 0 : reset( $ids );
  205. }
  206. public function filter_query_for_title_search( $search, WP_Query $wp_query ) {
  207. $title = $wp_query->get( 'post_title' );
  208. if ( ! empty( $title ) ) {
  209. global $wpdb;
  210. $search .= $wpdb->prepare( " AND {$wpdb->posts}.post_title=%s", $title );
  211. }
  212. return $search;
  213. }
  214. /**
  215. * @param string|int $featured_image Either an absolute path to an image or an attachment ID.
  216. *
  217. * @return Tribe__Events__Importer__Featured_Image_Uploader
  218. */
  219. protected function featured_image_uploader( $featured_image ) {
  220. // Remove any leading/trailing whitespace (if the string is a URL, extra whitespace
  221. // could result in URL validation fail)
  222. if ( is_string( $featured_image ) ) {
  223. $featured_image = trim( $featured_image );
  224. }
  225. return empty( $this->featured_image_uploader )
  226. ? new Tribe__Events__Importer__Featured_Image_Uploader( $featured_image )
  227. : $this->featured_image_uploader;
  228. }
  229. /**
  230. * Returns a boolean value from the record.
  231. *
  232. * @param array $record
  233. * @param string $key
  234. * @param string $return_true_value The value to return if the value was found and is truthy.
  235. * @param string $return_false_value The value to return if the value was not found or is not truthy;
  236. * defaults to the original value.
  237. * @param array $accepted_true_values An array of values considered truthy.
  238. *
  239. * @return string
  240. */
  241. public function get_boolean_value_by_key( $record, $key, $return_true_value = '1', $return_false_value = null, $accepted_true_values = array( 'yes', 'true', '1' ) ) {
  242. $value = strtolower( $this->get_value_by_key( $record, $key ) );
  243. if ( in_array( $value, $accepted_true_values ) ) {
  244. return $return_true_value;
  245. }
  246. return is_null( $return_false_value ) ? $value : $return_false_value;
  247. }
  248. /**
  249. * @param $row
  250. *
  251. * @return string
  252. */
  253. protected function get_skipped_row_message( $row ) {
  254. return sprintf( esc_html__( 'Missing required fields in row %d.', 'the-events-calendar' ), $row );
  255. }
  256. /**
  257. * @param $event_id
  258. * @param array $record
  259. *
  260. * @return bool|int|mixed|null
  261. */
  262. protected function get_featured_image( $event_id, array $record ) {
  263. $featured_image_content = $this->get_value_by_key( $record, 'featured_image' );
  264. $featured_image = null;
  265. if ( ! empty( $event_id ) ) {
  266. $featured_image = get_post_meta( $event_id, '_wp_attached_file', true );
  267. if ( empty( $featured_image ) ) {
  268. $featured_image = $this->featured_image_uploader( $featured_image_content )->upload_and_get_attachment();
  269. return $featured_image;
  270. }
  271. return $featured_image;
  272. } else {
  273. $featured_image = $this->featured_image_uploader( $featured_image_content )->upload_and_get_attachment();
  274. return $featured_image;
  275. }
  276. }
  277. }