function testSaveExistingImages()
 {
     $images = array('http://upload.wikimedia.org/wikipedia/commons/f/ff/Tim_Berners-Lee-Knight.jpg', 'http://upload.wikimedia.org/wikipedia/commons/3/3a/Tim_Berners-Lee_closeup.jpg', 'http://upload.wikimedia.org/wikipedia/commons/c/c2/Tim_Berners-Lee_2012.jpg', 'http://upload.wikimedia.org/wikipedia/commons/3/3a/Tim_Berners-Lee_closeup.jpg');
     $entity_post = wl_save_entity(array('uri' => 'http://example.org/entity', 'label' => 'Entity', 'main_type' => 'http://schema.org/Thing', 'description' => 'An example entity.', 'type_uris' => array(), 'related_post_id' => null, 'image' => $images, 'same_as' => array()));
     // Get all the attachments for the entity post.
     $attachments = wl_get_attachments($entity_post->ID);
     // Check that there is one attachment.
     $this->assertEquals(3, count($attachments));
     // Check that the attachments are found by source URL.
     foreach ($images as $image) {
         $image_post = wl_get_attachment_for_source_url($entity_post->ID, $image);
         $this->assertNotNull($image_post);
     }
 }
/**
 * Save the specified data as an entity in WordPress. This method only create new entities. When an existing entity is
 * found (by its URI), then the original post is returned.
 *
 * @param array $entity_properties, associative array containing: 
 * string 'uri' The entity URI.
 * string 'label' The entity label.
 * string 'main_type_uri' The entity type URI.
 * string 'description' The entity description.
 * array 'type_uris' An array of entity type URIs.
 * array 'images' An array of image URLs.
 * int 'related_post_id' A related post ID.
 * array 'same_as' An array of sameAs URLs.
 *
 * @return null|WP_Post A post instance or null in case of failure.
 */
function wl_save_entity($entity_properties)
{
    $uri = $entity_properties['uri'];
    $label = $entity_properties['label'];
    $type_uri = $entity_properties['main_type_uri'];
    $description = $entity_properties['description'];
    $entity_types = $entity_properties['type_uris'];
    $images = $entity_properties['images'];
    $related_post_id = $entity_properties['related_post_id'];
    $same_as = $entity_properties['same_as'];
    // Avoid errors due to null.
    if (is_null($entity_types)) {
        $entity_types = array();
    }
    wl_write_log("[ uri :: {$uri} ][ label :: {$label} ][ type uri :: {$type_uri} ]");
    // Prepare properties of the new entity.
    $params = array('post_status' => is_numeric($related_post_id) ? get_post_status($related_post_id) : 'draft', 'post_type' => WL_ENTITY_TYPE_NAME, 'post_title' => $label, 'post_content' => $description, 'post_excerpt' => '');
    // Check whether an entity already exists with the provided URI.
    $post = wl_get_entity_post_by_uri($uri);
    if (null !== $post) {
        // We insert into the params the entity ID, so it will be updated and not inserted.
        $params['ID'] = $post->ID;
    }
    // create or update the post.
    $post_id = wp_insert_post($params, true);
    // TODO: handle errors.
    if (is_wp_error($post_id)) {
        wl_write_log(': error occurred');
        // inform an error occurred.
        return null;
    }
    wl_set_entity_main_type($post_id, $type_uri);
    // Save the entity types.
    wl_set_entity_rdf_types($post_id, $entity_types);
    // Get a dataset URI for the entity.
    $wl_uri = wl_build_entity_uri($post_id);
    // Save the entity URI.
    wl_set_entity_uri($post_id, $wl_uri);
    // Add the uri to the sameAs data if it's not a local URI.
    if ($wl_uri !== $uri) {
        array_push($same_as, $uri);
    }
    $new_uri = wl_get_entity_uri($post_id);
    // Save the sameAs data for the entity.
    wl_schema_set_value($post_id, 'sameAs', $same_as);
    // Call hooks.
    do_action('wl_save_entity', $post_id);
    wl_write_log("[ post id :: {$post_id} ][ uri :: {$uri} ][ label :: {$label} ][ wl uri :: {$wl_uri} ][ types :: " . implode(',', $entity_types) . " ][ images count :: " . count($images) . " ][ same_as count :: " . count($same_as) . " ]");
    foreach ($images as $image_remote_url) {
        // Check if image is already present in local DB
        if (strpos($image_remote_url, site_url()) !== false) {
            // Do nothing.
            continue;
        }
        // Check if there is an existing attachment for this post ID and source URL.
        $existing_image = wl_get_attachment_for_source_url($post_id, $image_remote_url);
        // Skip if an existing image is found.
        if (null !== $existing_image) {
            continue;
        }
        // Save the image and get the local path.
        $image = wl_save_image($image_remote_url);
        // Get the local URL.
        $filename = $image['path'];
        $url = $image['url'];
        $content_type = $image['content_type'];
        $attachment = array('guid' => $url, 'post_title' => $label, 'post_content' => '', 'post_status' => 'inherit', 'post_mime_type' => $content_type);
        // Create the attachment in WordPress and generate the related metadata.
        $attachment_id = wp_insert_attachment($attachment, $filename, $post_id);
        // Set the source URL for the image.
        wl_set_source_url($attachment_id, $image_remote_url);
        $attachment_data = wp_generate_attachment_metadata($attachment_id, $filename);
        wp_update_attachment_metadata($attachment_id, $attachment_data);
        // Set it as the featured image.
        set_post_thumbnail($post_id, $attachment_id);
    }
    // The entity is pushed to Redlink on save by the function hooked to save_post.
    // save the entity in the triple store.
    wl_linked_data_push_to_redlink($post_id);
    // finally return the entity post.
    return get_post($post_id);
}
/**
 * Save the specified data as an entity in WordPress. This method only create new entities. When an existing entity is
 * found (by its URI), then the original post is returned.
 *
 * @param array $entity_data, associative array containing: 
 * string 'uri'             The entity URI.
 * string 'label'           The entity label.
 * string 'main_type'       The entity type URI.
 * array  'type'            An array of entity type URIs.
 * string 'description'     The entity description.
 * array  'images'          An array of image URLs.
 * int    'related_post_id' A related post ID.
 * array  'same_as'         An array of sameAs URLs.
 *
 * @return null|WP_Post A post instance or null in case of failure.
 */
function wl_save_entity($entity_data)
{
    $uri = $entity_data['uri'];
    $label = $entity_data['label'];
    $type_uri = $entity_data['main_type'];
    $entity_types = isset($entity_data['type']) ? $entity_data['type'] : array();
    $description = $entity_data['description'];
    $images = isset($entity_data['image']) ? wl_force_to_array($entity_data['image']) : array();
    $same_as = isset($entity_data['sameas']) ? wl_force_to_array($entity_data['sameas']) : array();
    $related_post_id = isset($entity_data['related_post_id']) ? $entity_data['related_post_id'] : null;
    $other_properties = isset($entity_data['properties']) ? $entity_data['properties'] : array();
    // wl_write_log( "[ uri :: $uri ][ label :: $label ][ type uri :: $type_uri ]" );
    // Prepare properties of the new entity.
    $params = array('post_status' => is_numeric($related_post_id) ? get_post_status($related_post_id) : 'draft', 'post_type' => Wordlift_Entity_Service::TYPE_NAME, 'post_title' => $label, 'post_content' => $description, 'post_excerpt' => '');
    // Check whether an entity already exists with the provided URI.
    $post = wl_get_entity_post_by_uri($uri);
    if (null !== $post) {
        // We insert into the params the entity ID, so it will be updated and not inserted.
        $params['ID'] = $post->ID;
        // Preserve the current entity status
        if ('public' === $post->post_status) {
            $params['post_status'] = $post->post_status;
        }
        // Preserve the current entity post_content.
        $params['post_content'] = $post->post_content;
        // Preserve the entity post_title to avoid de-synch between WP and RL
        // See: https://github.com/insideout10/wordlift-plugin/issues/221
        $params['post_title'] = $post->post_title;
    }
    // If Yoast is installed and active, we temporary remove the save_postdata hook which causes Yoast to "pass over"
    // the local SEO form values to the created entity (https://github.com/insideout10/wordlift-plugin/issues/156)
    // Same thing applies to SEO Ultimate (https://github.com/insideout10/wordlift-plugin/issues/148)
    // This does NOT affect saving an entity from the entity admin page since this function is called when an entity
    // is created when saving a post.
    global $wpseo_metabox, $seo_ultimate;
    if (isset($wpseo_metabox)) {
        remove_action('wp_insert_post', array($wpseo_metabox, 'save_postdata'));
    }
    if (isset($seo_ultimate)) {
        remove_action('save_post', array($seo_ultimate, 'save_postmeta_box'));
    }
    // The fact that we're calling here wp_insert_post is causing issues with plugins (and ourselves too) that hook to
    // save_post in order to save additional inputs from the edit page. In order to avoid issues, we pop all the hooks
    // to the save_post and restore them after we saved the entity.
    // see https://github.com/insideout10/wordlift-plugin/issues/203
    // see https://github.com/insideout10/wordlift-plugin/issues/156
    // see https://github.com/insideout10/wordlift-plugin/issues/148
    global $wp_filter;
    $save_post_filters = $wp_filter['save_post'];
    $wp_filter['save_post'] = array();
    // create or update the post.
    $post_id = wp_insert_post($params, true);
    // Restore all the existing filters.
    $wp_filter['save_post'] = $save_post_filters;
    // If Yoast is installed and active, we restore the Yoast save_postdata hook (https://github.com/insideout10/wordlift-plugin/issues/156)
    if (isset($wpseo_metabox)) {
        add_action('wp_insert_post', array($wpseo_metabox, 'save_postdata'));
    }
    // If SEO Ultimate is installed, add back the hook we removed a few lines above.
    if (isset($seo_ultimate)) {
        add_action('save_post', array($seo_ultimate, 'save_postmeta_box'), 10, 2);
    }
    // TODO: handle errors.
    if (is_wp_error($post_id)) {
        wl_write_log(': error occurred');
        // inform an error occurred.
        return null;
    }
    wl_set_entity_main_type($post_id, $type_uri);
    // Save the entity types.
    wl_set_entity_rdf_types($post_id, $entity_types);
    // Get a dataset URI for the entity.
    $wl_uri = wl_build_entity_uri($post_id);
    // Save the entity URI.
    wl_set_entity_uri($post_id, $wl_uri);
    // Add the uri to the sameAs data if it's not a local URI.
    if ($wl_uri !== $uri) {
        array_push($same_as, $uri);
    }
    $new_uri = wl_get_entity_uri($post_id);
    // Save the sameAs data for the entity.
    wl_schema_set_value($post_id, 'sameAs', $same_as);
    // Save the other properties (latitude, langitude, startDate, endDate, etc.)
    foreach ($other_properties as $property_name => $property_value) {
        wl_schema_set_value($post_id, $property_name, $property_value);
    }
    // Call hooks.
    do_action('wl_save_entity', $post_id);
    wl_write_log("[ post id :: {$post_id} ][ uri :: {$uri} ][ label :: {$label} ][ wl uri :: {$wl_uri} ][ types :: " . implode(',', $entity_types) . " ][ images count :: " . count($images) . " ][ same_as count :: " . count($same_as) . " ]");
    foreach ($images as $image_remote_url) {
        // Check if image is already present in local DB
        if (strpos($image_remote_url, site_url()) !== false) {
            // Do nothing.
            continue;
        }
        // Check if there is an existing attachment for this post ID and source URL.
        $existing_image = wl_get_attachment_for_source_url($post_id, $image_remote_url);
        // Skip if an existing image is found.
        if (null !== $existing_image) {
            continue;
        }
        // Save the image and get the local path.
        $image = wl_save_image($image_remote_url);
        // Get the local URL.
        $filename = $image['path'];
        $url = $image['url'];
        $content_type = $image['content_type'];
        $attachment = array('guid' => $url, 'post_title' => $label, 'post_content' => '', 'post_status' => 'inherit', 'post_mime_type' => $content_type);
        // Create the attachment in WordPress and generate the related metadata.
        $attachment_id = wp_insert_attachment($attachment, $filename, $post_id);
        // Set the source URL for the image.
        wl_set_source_url($attachment_id, $image_remote_url);
        $attachment_data = wp_generate_attachment_metadata($attachment_id, $filename);
        wp_update_attachment_metadata($attachment_id, $attachment_data);
        // Set it as the featured image.
        set_post_thumbnail($post_id, $attachment_id);
    }
    // The entity is pushed to Redlink on save by the function hooked to save_post.
    // save the entity in the triple store.
    wl_linked_data_push_to_redlink($post_id);
    // finally return the entity post.
    return get_post($post_id);
}