ROOTPLOIT
Server: LiteSpeed
System: Linux server71.web-hosting.com 4.18.0-513.18.1.lve.el8.x86_64 #1 SMP Thu Feb 22 12:55:50 UTC 2024 x86_64
User: niphet (1079)
PHP: 5.3.29
Disabled: NONE
Upload Files
File: /home/niphet/public_html/wp-content/plugins/mediavine-create/lib/class-publish.php
<?php
namespace Mediavine\Create;

use Mediavine\MV_DBI;
use Mediavine\WordPress\Support\Arr;
use Mediavine\WordPress\Support\Str;

class Publish extends Plugin {

	public static function publish( \WP_REST_Request $request, \WP_REST_Response $response ) {
		$creation_id = $request->get_param( 'id' );

		if ( ! $creation_id ) {
			return new \WP_Error(
				404, __( 'Entry Not Found', 'mediavine' ), [
					'message' => __( 'The Creation could not be found', 'mediavine' ),
					'class'   => 'Mediavine\Create\Publish',
					'method'  => 'publish',
				]
			);
		}

		$creation                = Creations::publish_creation( (int) $creation_id );
		$creation->thumbnail_uri = wp_get_attachment_url( $creation->thumbnail_id );
		$response                = API_Services::set_response_data( $creation, $response );
		return $response;
	}

	/**
	 * Conditionally Republish Creation at Run Time
	 * @param object $creation Creation DB object
	 * @return object $creation full creation object whether new or original
	 */
	public static function maybe_republish( $creation ) {
		self::do_actions( $creation->id );
		$should_republish = false;

		// $should_republish bool passed on to return prior result, preventing false negatives
		$should_republish = self::list_link_repair( $creation, $should_republish );
		$should_republish = self::remove_associated_post_revisions( $creation, $should_republish );
		$should_republish = self::fix_associated_posts_column( $creation, $should_republish );
		$should_republish = self::fix_imported_ratings_dates( $creation, $should_republish );
		$should_republish = self::fix_canonical_post_id( $creation, $should_republish );
		$should_republish = self::fix_create_slug( $creation, $should_republish );

		$publish_queue        = [];
		$publish_queue_option = get_option( 'mv_publish_queue' );

		if ( ! empty( $publish_queue_option ) ) {
			$publish_queue = json_decode( $publish_queue_option, true );
		}

		if ( in_array( $creation->id, $publish_queue, true ) ) {
			$should_republish = true;
			$publish_queue    = array_values(
				array_filter(
					$publish_queue, function( $item ) use ( $creation ) {
					return $item !== $creation->id;
					}
				)
			);
			update_option( 'mv_publish_queue', wp_json_encode( $publish_queue ) );
		}

		if ( empty( $creation->published ) || ! is_array( json_decode( $creation->published, true ) ) ) {
			$should_republish = true;
		}

		if ( $should_republish ) {
			$creation = \Mediavine\Create\Creations::publish_creation( $creation->id, false );
		}

		return $creation;
	}

	/**
	 * Repairs list links on a Create list
	 *
	 * @param object $creation Create card data
	 * @param bool $should_republish Current $should_republish value to potentially be passed on
	 * @return bool True if data updated and republish should happen, false if it doesn't need to happen
	 */
	private static function list_link_repair( $creation, $should_republish ) {

		if ( 'list' !== $creation->type ) {
			return $should_republish;
		}

		$metadata = [];
		if ( ! empty( $creation->metadata ) ) {
			$metadata = json_decode( $creation->metadata, true );
		}

		if ( ! empty( $metadata['list_link_repaired'] ) ) {
			return $should_republish;
		}

		$items = self::$models_v2->mv_relations->find(
			[
				'where' => [
					'creation' => $creation->id,
				],
			]
		);

		foreach ( $items as &$item ) {
			if ( 'card' !== $item->content_type ) {
				continue;
			}

			if ( $item->relation_id !== $item->canonical_post_id ) {
				continue;
			}

			$found_creation = self::$models_v2->mv_creations->find_one_by_id( $item->relation_id );

			if ( ! empty( $found_creation->canonical_post_id ) ) {
				$item->url = get_permalink( $found_creation->canonical_post_id );

				$updated_item = self::$models_v2->mv_relations->update(
					[
						'id'                => $item->id,
						'url'               => $item->url,
						'canonical_post_id' => $found_creation->canonical_post_id,
					]
				);
			}
		}

		$metadata['list_link_repaired'] = true;
		$updated_creation               = self::$models_v2->mv_creations->update_without_modified_date(
			[
				'id'       => $creation->id,
				'metadata' => wp_json_encode( $metadata ),
			]
		);

		return true;
	}


	private static function fix_associated_posts_column( $creation, $should_republish ) {
		if (
			empty( $creation->associated_posts ) ||
			Str::contains( 'fixed_associated_posts_column', $creation->metadata ) ||
			Str::contains( '""', $creation->associated_posts )
		) {
			return $should_republish;
		}
		$associated_posts = json_decode( $creation->associated_posts, true );
		$associated_posts = wp_json_encode( array_map( 'strval', $associated_posts ) );

		$updated_creation = self::$models_v2->mv_creations->update_without_modified_date(
			[
				'id'               => $creation->id,
				'associated_posts' => $associated_posts,
			]
		);
		if ( is_wp_error( $updated_creation ) ) {
			return $should_republish;
		}

		$metadata                                  = json_decode( $creation->metadata, true );
		$metadata['fixed_associated_posts_column'] = true;
		self::$models_v2->mv_creations->update_without_modified_date(
			[
				'id'       => $creation->id,
				'metadata' => wp_json_encode( $metadata ),
			]
		);
		return true;
	}


	private static function fix_canonical_post_id( $creation, $should_republish ) {
		// return $should_republish;
		if ( empty( $creation->canonical_post_id ) ) {
			return $should_republish;
		}

		$associated_posts = ! empty( $creation->associated_posts ) ? json_decode( $creation->associated_posts, true ) : [];
		if ( empty( $associated_posts ) || in_array( $creation->canonical_post_id, $associated_posts, true ) ) {
			return $should_republish;
		}

		// Use first associated post if found
		$canonical_post_id = Arr::first( $associated_posts );

		// Use original post ID if available and in associated posts
		if (
			! empty( $creation->original_post_id ) &&
			in_array( $creation->original_post_id, $associated_posts, true )
		) {
			$canonical_post_id = $creation->original_post_id;
		}

		if ( empty( $canonical_post_id ) ) {
			return $should_republish;
		}
		// Update creation with new metadata and associated posts
		self::$models_v2->mv_creations->update_without_modified_date(
			[
				'id'                => $creation->id,
				'canonical_post_id' => $canonical_post_id,
			]
		);

		return true;
	}

	/**
	 * Fix the slug for a Create post type if incorrect
	 *
	 * We check for '-creation' at the end of the slug. If it's not there then we will
	 * update the slug to include it. This will prevent Yoast SEO Premium from causing
	 * bad page redirects.
	 *
	 * @param object $creation Full creation data
	 * @param bool $should_republish Previous republish value
	 * @return bool True if we should republish or the previous value if no changes are to be made
	 */
	static public function fix_create_slug( $creation, $should_republish ) {
		$metadata = [];
		if ( ! empty( $creation->metadata ) ) {
			$metadata = json_decode( $creation->metadata, true );
		}

		if ( ! empty( $metadata['slug_repaired'] ) ) {
			return $should_republish;
		}

		$post_slug   = get_post_field( 'post_name', $creation->object_id );
		$update_slug = false;
		if ( ! empty( $post_slug ) ) {
			$end_of_slug = substr( $post_slug, -9 );
			if ( '-creation' !== $end_of_slug ) {
				$update_slug = true;
			}
		}
		if ( $update_slug ) {
			$update_post = wp_update_post(
				[
					'ID'        => $creation->object_id,
					'post_name' => $post_slug . '-creation',
				]
			);
			if ( is_wp_error( $update_post ) ) {
				return $should_republish;
			}

			// Legacy support for old Create post types, and old WP revision support
			global $wpdb;

			// Remove any trailing digits
			// SECURITY CHECKED: This query is properly sanitized. Custom LIKE doesn't work with preparation.
			$trimmed_slug   = preg_replace( '/-[0-9]*$/', '', $post_slug );
			$statement      = "SELECT * FROM {$wpdb->prefix}posts
				WHERE post_name LIKE '{$trimmed_slug}%'
				AND ( post_type = 'mv_create'
					OR post_type = 'mv_creations'
					OR post_type = 'mv_products'
					OR post_type = 'mv_recipes'
				)
			";
			$prepared       = $wpdb->prepare( $statement, [] );
			$matching_posts = $wpdb->get_results( $prepared );
			foreach ( $matching_posts as $matching_post ) {
				if ( ! empty( $matching_post->post_name ) ) {
					$end_of_slug = substr( $matching_post->post_name, -9 );
					if ( '-creation' !== $end_of_slug ) {
						$update_post = wp_update_post(
							[
								'ID'        => $matching_post->ID,
								'post_name' => $matching_post->post_name . '-creation',
							]
						);
						if ( is_wp_error( $update_post ) ) {
							return $should_republish;
						}
					}
				}
			}
		}

		$metadata['slug_repaired'] = true;
		self::$models_v2->mv_creations->update_without_modified_date(
			[
				'id'       => $creation->id,
				'metadata' => wp_json_encode( $metadata ),
			]
		);
		return true;
	}

	/**
	 * Removes previously associated revisions from a Create card
	 *
	 * @param object $creation Create card data
	 * @param bool $should_republish Current $should_republish value to potentially be passed on
	 * @return bool True if data updated and republish should happen, false if it doesn't need to happen
	 */
	private static function remove_associated_post_revisions( $creation, $should_republish ) {
		$metadata = [];
		if ( ! empty( $creation->metadata ) ) {
			$metadata = json_decode( $creation->metadata, true );
		}

		if ( ! empty( $metadata['revisions_removed'] ) ) {
			return $should_republish;
		}

		$associated_posts = [];
		if ( ! empty( $creation->associated_posts ) ) {
			$associated_posts = json_decode( $creation->associated_posts );
		}

		foreach ( $associated_posts as $key => $associated_post ) {
			$post_status      = get_post_status( $associated_post );
			$allowed_statuses = [
				'publish',
				'future',
				'draft',
				'pending',
				'private',
			];

			if ( ! in_array( $post_status, $allowed_statuses, true ) ) {
				unset( $associated_posts[ $key ] );
			}
		}

		$metadata['revisions_removed'] = true;

		// Update creation with new metadata and associated posts
		self::$models_v2->mv_creations->update_without_modified_date(
			[
				'id'               => $creation->id,
				'metadata'         => wp_json_encode( $metadata ),
				'associated_posts' => wp_json_encode( array_values( array_unique( $associated_posts ) ) ),
			]
		);

		return true;
	}

	/**
	 * Fixes review creation dates for imported ratings/reviews.
	 *
	 * Recipe ratings imported previously were assigned a created date on import,
	 * which means all ratings imported for a given recipe had the same date. This function
	 * reassigns any rating creation dates possible.
	 *
	 * Remove December 2019
	 *
	 * @since 1.4.10
	 *
	 * @return void
	 */
	public static function fix_imported_ratings_dates( $creation, $should_republish ) {
		global $wpdb;

		if (
			'recipe' !== $creation->type ||
			! Str::contains( $creation->metadata, 'import' ) ||
			Str::contains( $creation->metadata, 'fixed_ratings_dates' ) ||
			empty( $creation->original_post_id )
		) {
			return $should_republish;
		}

		$dbi       = self::$models_v2->mv_reviews;
		$statement = "SELECT
			r.id as id,
			comment_date AS created,
			comment_date AS modified
			FROM {$wpdb->commentmeta} AS cm
			JOIN {$wpdb->comments} AS c ON (c.comment_ID = cm.comment_id)
			JOIN {$dbi->table_name} AS r ON (c.comment_author_email=r.author_email)
			WHERE c.comment_approved = 1
			AND cm.meta_key IN ('ERRating', 'cookbook_comment_rating', 'recipe_rating', 'wprm-comment-rating')
			AND cm.meta_value != 0
			AND c.comment_post_ID = %d";

		if ( Str::contains( $creation->metadata, [ 'meal_planner', 'recipe_maker' ] ) ) {
			if ( Str::contains( $creation->metadata, 'meal_planner' ) ) {
				$table_name = 'mpprecipe_ratings';
			}
			if ( Str::contains( $creation->metadata, 'recipe_maker' ) ) {
				$table_name = 'wprm_ratings';
			}
			$statement = "SELECT
				r.id as id,
				comment_date AS created,
				comment_date AS modified
				FROM {$wpdb->prefix}{$table_name} AS ir
				JOIN {$wpdb->comments} AS c ON (c.comment_ID = ir.comment_id)
				JOIN {$dbi->table_name} AS r ON (c.comment_author_email=r.author_email)
				WHERE c.comment_approved = 1
				AND c.comment_post_ID = %d";
		}

		// SECURITY CHECKED: This query is properly prepared.
		$prepared = $wpdb->prepare( $statement, [ $creation->original_post_id ] );
		$ratings  = $wpdb->get_results( $prepared, ARRAY_A );

		foreach ( $ratings as $data ) {
			$date             = date( 'Y-m-d H:i:s' );
			$data['created']  = isset( $data['created'] ) ? $data['created'] : $date;
			$data['modified'] = isset( $data['modified'] ) ? $data['modified'] : $date;

			if ( is_wp_error( $data ) ) {
				continue;
			}

			// because our insert, upsert, and update methods overwrite `created` and `modified` dates,
			// we have to manually perform an update here
			$normalized_data = $dbi->normalize_data( $data );
			add_filter( 'query', [ $dbi, 'allow_null' ] );
			$wpdb->update( $dbi->table_name, $normalized_data, [ 'id' => $normalized_data['id'] ] );
			remove_filter( 'query', [ $dbi, 'allow_null' ] );
		}

		$metadata                        = json_decode( $creation->metadata, true );
		$metadata['fixed_ratings_dates'] = true;

		// Update creation with new metadata and associated posts
		self::$models_v2->mv_creations->update_without_modified_date(
			[
				'id'       => $creation->id,
				'metadata' => wp_json_encode( $metadata ),
			]
		);

		return true;
	}

	/**
	 * Add Creations to republish queue.
	 *
	 * @param \WP_REST_Request $request
	 * @param \WP_REST_Response $response
	 *
	 * @return void|bool|array|\WP_REST_Response
	 */
	public static function republish_creations( \WP_REST_Request $request, \WP_REST_Response $response ) {
		$params = $request->get_params();

		if ( 'pubeng' !== $params['auth'] ) {
			return false;
		}

		if ( empty( $params['type'] ) ) {
			return static::add_all_to_publish_queue();
		}

		$query_args   = [
			'where'  => [
				'type' => $params['type'],
			],
			'select' => [
				'id',
			],
			'limit'  => 9999,
		];
		$model        = new MV_DBI( 'mv_creations' );
		$creations    = $model->find( $query_args );
		$creation_ids = [];
		foreach ( $creations as $creation ) {
			$creation_ids[] = $creation->id;
		}
		$publish_queue = static::update_publish_queue( $creation_ids );
		$response      = API_Services::set_response_data( $publish_queue, $response );
		return $response;
	}

	public static function do_actions( $id ) {
		$action_queues = get_option( 'mv_queues', [] );
		if ( empty( $action_queues ) ) {
			return;
		}
		$action_queues = json_decode( $action_queues, true );
		foreach ( $action_queues as $key => $name ) {
			$queue = get_option( 'mv_' . $name . '_queue' );
			if ( false === $queue || empty( $queue ) || 'null' === $queue ) {
				unset( $action_queues[ $key ] );
				continue;
			}
			$queued_ids = json_decode( $queue, true );
			if ( ! in_array( (string) $id, $queued_ids, true ) ) {
				continue;
			}
			do_action( 'mv_' . $name . '_queue_action', $id );
			$queued_ids = array_values(
				array_filter(
					$queued_ids, function( $item ) use ( $id ) {
						return $item !== $id;
					}
				)
			);
			if ( empty( $queued_ids ) ) {
				delete_option( 'mv_' . $name . '_queue' );
				unset( $action_queues[ $key ] );
				continue;
			}
			update_option( 'mv_' . $name . '_queue', wp_json_encode( $queued_ids ) );
		}
		update_option( 'mv_queues', wp_json_encode( $action_queues ) );
	}

	public static function selective_update_queue( $creation_ids = [], $name = '' ) {
		$queue        = [];
		$option       = 'mv_' . $name . '_queue';
		$queue_option = get_option( $option );

		if ( ! empty( $queue_option ) ) {
			$queue = json_decode( $queue_option, true );
		}

		foreach ( $creation_ids as $id ) {
		$queue[] = (string) $id;
		}

		$queue = array_values( array_unique( $queue ) );

		update_option( $option, wp_json_encode( $queue ) );
		static::add_to_queues( $name );

		return $queue;
	}

	public static function add_to_queues( $name ) {
		$queue_option = get_option( 'mv_queues' );
		$queue        = [];

		if ( ! empty( $queue_option ) ) {
			$queue = json_decode( $queue_option, true );
		}
		$queue[] = $name;

		$queue = array_values( array_unique( $queue ) );

		update_option( 'mv_queues', wp_json_encode( $queue ) );
	}

	/**
	 * Manage queue of items that need to be republished
	 * @param array $creation_ids Numeric Array of Creation IDs
	 * @return array $publish_queue Numeric Array of Creation IDs that need republish
	 */
	public static function update_publish_queue( $creation_ids = [] ) {
		$publish_queue        = [];
		$publish_queue_option = get_option( 'mv_publish_queue' );

		if ( ! empty( $publish_queue_option ) ) {
			$publish_queue = json_decode( $publish_queue_option, true );
		}

		foreach ( $creation_ids as $an_id ) {
			$publish_queue[] = (string) $an_id;
		}

		$publish_queue = array_values( array_unique( $publish_queue ) );

		update_option( 'mv_publish_queue', wp_json_encode( $publish_queue ) );
		return $publish_queue;
	}

	public static function add_all_to_publish_queue() {
		$model        = new MV_DBI( 'mv_creations' );
		$creations    = $model->find();
		$creation_ids = [];
		foreach ( $creations as $creation ) {
			$creation_ids[] = $creation->id;
		}
		return static::update_publish_queue( $creation_ids );
	}

	public static function prepare_creation( $creation ) {
		if ( empty( $creation->id ) ) {
			return new \WP_Error( 404, __( 'Entry Not Found', 'mediavine' ), [ 'message' => __( 'The Creation could not be found', 'mediavine' ) ] );
		}

		unset( $creation->published );
		return $creation;
	}

	public static function prepare_jsonld( $creation ) {
		if ( ! $creation->schema_display ) {
			return $creation;
		}

		$JSON_LD = JSON_LD::get_instance();

		if ( ! $creation->author ) {
			$creation->author = \Mediavine\Settings::get_setting( self::$settings_group . '_copyright_attribution' );
		}

		// Pinterest image should not be included in google schema
		if ( empty( $creation->images ) ) {
			$json_ld = $JSON_LD->build_json_ld( (array) $creation, $creation->type );

			if ( ! empty( $json_ld ) ) {
				$creation->json_ld = wp_json_encode( $json_ld );
			}
			return $creation;
		}

		// Clone used because shallow copy was made, so $cleaned_image_creation passing a
		// referenced object of $creation (http://php.net/manual/en/language.oop5.cloning.php)
		$cleaned_image_creation = clone( $creation );
		$cleaned                = [];

		foreach ( $cleaned_image_creation->images as $image ) {
			if ( 'mv_create_vert' === $image['image_size'] ) {
				continue;
			}
			$cleaned[] = $image;
		}

		$cleaned_image_creation->images = $cleaned;
		$json_ld                        = $JSON_LD->build_json_ld( (array) $cleaned_image_creation, $cleaned_image_creation->type );

		if ( ! empty( $json_ld ) ) {
			$creation->json_ld = wp_json_encode( $json_ld );
		}

		return $creation;
	}

	public static function prepare_create_settings( $creation ) {
		$create_settings = [];

		$create_settings = apply_filters( 'mv_publish_create_settings', $create_settings );

		$creation->create_settings = $create_settings;
		return $creation;
	}

	public static function prepare_supplies( $creation ) {

		if ( empty( $creation->supplies ) ) {
			return $creation;
		}

		$supplies = $creation->supplies;
		usort( $supplies, [ 'Mediavine\Create\Supplies', 'sort_supply' ] );

		if ( 'recipe' === $creation->type ) {
			$ingredients = array_filter(
				$supplies, function( $supply ) {
					return 'ingredients' === $supply->type;
				}
			);
			if ( $ingredients ) {
				$creation->ingredients = Supplies::put_supplies_in_groups_array( $ingredients );
			}
		}

		if ( 'diy' === $creation->type ) {
			$materials = array_filter(
				$supplies, function( $supply ) {
					return 'materials' === $supply->type;
				}
			);
			if ( $materials ) {
				$creation->materials = Supplies::put_supplies_in_groups_array( $materials );
			}
			$tools = array_filter(
				$supplies, function( $supply ) {
					return 'tools' === $supply->type;
				}
			);
			if ( $tools ) {
				$creation->tools = Supplies::put_supplies_in_groups_array( $tools );
			}
		}

		unset( $creation->supplies );

		return $creation;
	}

	public static function prepare_relations( $creation ) {
		if ( 'list' === $creation->type ) {
			$relations  = $creation->relations;
			$list_items = array_filter(
				$relations, function( $item ) {
					return 'listItems' === $item->type;
				}
			);
			if ( $list_items ) {
				$creation->list_items = array_values( $list_items );
			}
		}

		unset( $creation->relations );
		return $creation;
	}

	public static function prepare_instructions( $creation ) {
		$creation->instructions = html_entity_decode( $creation->instructions );

		if ( ( 'diy' === $creation->type ) || ( 'recipe' === $creation->type ) ) {
			$dom = new \DOMDocument;
			if ( function_exists( 'libxml_use_internal_errors' ) ) {
				libxml_use_internal_errors( true );
			}
			$load = $dom->loadHTML( mb_convert_encoding( $creation->instructions, 'HTML-ENTITIES', 'UTF-8' ) );
			if ( function_exists( 'libxml_use_internal_errors' ) ) {
				libxml_use_internal_errors( false );
			}
			if ( ! $load ) {
				return $creation;
			}

			$lis = $dom->getElementsByTagName( 'li' );
			$i   = 1; // start at 1 because it's outputting HTML for instructions, so step 1 should be `mv_create_123_1`

			foreach ( $lis as $li ) {
				// the schema outputs the anchor as `mv_create_123_1`, so we do the same here
				$li->setAttribute( 'id', "mv_create_{$creation->id}_{$i}" );
				++$i;
			}

			// Remove the doctype when saving
			$creation->instructions = preg_replace( '~<(?:!DOCTYPE|/?(?:html|body))[^>]*>\s*~i', '', $dom->saveHTML() );
		}

		return $creation;
	}

	public static function prepare_ratings( $creation ) {
		$rating       = Reviews_Models::get_creation_rating( $creation->id );
		$rating_count = Reviews_Models::get_creation_rating_count( $creation->id );

		if ( $rating && $rating_count ) {
			$creation->rating       = $rating;
			$creation->rating_count = $rating_count;
		}

		return $creation;
	}

	public static function prepare_posts( $creation ) {
		$associated_posts = [];
		if ( ! empty( $creation->associated_posts ) ) {
			$associated_posts = json_decode( $creation->associated_posts );
			foreach ( $associated_posts as &$post ) {
				$post = [
					'id'    => $post,
					'title' => get_the_title( $post ),
				];
			}
		}

		$creation->posts = $associated_posts;
		return $creation;
	}

	/**
	 * Prepares the image data for publishing
	 *
	 * @param object $creation Creation data
	 * @return object Updated creation data
	 */
	public static function prepare_images( $creation ) {
		if ( empty( $creation->thumbnail_id ) && empty( $creation->pinterest_img_id ) ) {
			return $creation;
		}
		if ( empty( $creation->thumbnail_id ) ) {
			$creation->thumbnail_id = 0;
		}
		if ( empty( $creation->pinterest_img_id ) ) {
			$creation->pinterest_img_id = 0;
		}
		$images = Creations::add_images_to_creation( $creation, $creation->thumbnail_id, $creation->pinterest_img_id, $creation->type );

		if ( ! empty( $images ) ) {
			// Make sure we generate base image sizes for published data
			$images           = Images::get_available_image_sizes( $images );
			$creation->images = $images;
		}

		return $creation;
	}

	public static function prepare_times( $creation ) {
		$times_to_parse = [
			'prep_time',
			'active_time',
			'additional_time',
			'perform_time',
			'total_time',
		];

		// similar to how juration parses time units
		$units_to_parse = apply_filters(
			'mv_time_units_to_parse', [
				'years'   => 31536000,
				'months'  => 2628000,
				'days'    => 86400,
				'hours'   => 3600,
				'minutes' => 60,
				'seconds' => 1,
			]
		);

		$times_to_parse = apply_filters( 'mv_times_to_parse', $times_to_parse );

		foreach ( $times_to_parse as $time_to_parse ) {
			if ( empty( $creation->{$time_to_parse} ) ) {
				$creation->{$time_to_parse} = null;
				continue;
			}

			$seconds = (int) floor( $creation->{$time_to_parse} );

			$time_array['original'] = $seconds;
			$remaining              = $seconds;
			foreach ( $units_to_parse as $unit => $unit_value ) {
				$value = (int) floor( round( $remaining * 1000 ) / 1000 / $unit_value );

				if ( 0 !== $value ) {
					$time_array[ $unit ] = $value;
					$remaining           = $remaining % $unit_value;
				} else {
					$time_array[ $unit ] = 0;
				}
			}

			$creation->{$time_to_parse} = $time_array;
		}

		return $creation;
	}

	private static function format_supplies( $supplies ) {

		$formatted = [];
		foreach ( $supplies as $supply ) {
			$formatted[ $supply->type ][] = $supply;
		}

		return $formatted;
	}

}