public function html_input($default_entity_identifier) { // The entity can be referenced as URI or ID. if (is_numeric($default_entity_identifier)) { $entity = get_post($default_entity_identifier); } else { // It is an URI $entity = wl_get_entity_post_by_uri($default_entity_identifier); } if (!is_null($entity)) { $label = $entity->post_title; $value = $entity->ID; } else { // No ID and no internal uri. Just leave as is. $label = $default_entity_identifier; $value = $default_entity_identifier; } /* * Write saved value in page * The <input> tags host the meta value. * The visible <input> has the human readable value (i.e. entity name or uri) * and is accompained by an hidden <input> tag, passed to the server, * that contains the raw value (i.e. the uri or entity id). */ $html = <<<EOF \t\t\t<div class="wl-input-wrapper wl-autocomplete-wrapper"> \t\t\t\t<input type="text" class="{$this->meta_name} wl-autocomplete" value="{$label}" style="width:88%" /> \t\t\t\t<input type="hidden" class="{$this->meta_name}" name="wl_metaboxes[{$this->meta_name}][]" value="{$value}" /> \t\t\t\t<button class="button wl-remove-input wl-button" type="button" style="width:10%">Remove</button> \t\t\t\t<div class="wl-input-notice"></div> \t\t\t</div> EOF; return $html; }
function testFindByURI() { $entity_post_id = wl_create_post('', 'test_entity', 'Test Entity', 'draft', 'entity'); $entity_uri = wl_get_entity_uri($entity_post_id); wl_schema_set_value($entity_post_id, 'sameAs', 'http://example.org/entity/test_entity'); $same_as_array = wl_schema_get_value($entity_post_id, 'sameAs'); $this->assertTrue(is_array($same_as_array)); $this->assertEquals('http://example.org/entity/test_entity', $same_as_array[0]); wl_schema_set_value($entity_post_id, 'sameAs', array('http://example.org/entity/test_entity', 'http://data.example.org/entity/test_entity')); $same_as_array = wl_schema_get_value($entity_post_id, 'sameAs'); $this->assertTrue(is_array($same_as_array)); $this->assertEquals('http://example.org/entity/test_entity', $same_as_array[0]); $this->assertEquals('http://data.example.org/entity/test_entity', $same_as_array[1]); $post = wl_get_entity_post_by_uri('http://example.org/entity/test_entity'); $this->assertNotNull($post); $post = wl_get_entity_post_by_uri('http://data.example.org/entity/test_entity'); $this->assertNotNull($post); $same_as_uri = 'http://example.org/entity/test_entity2'; $entity_post_id = wl_create_post('', 'test_entity_2', 'Test Entity 2', 'draft', 'entity'); $entity_uri = wl_get_entity_uri($entity_post_id); wl_schema_set_value($entity_post_id, 'sameAs', $same_as_uri); $same_as_array = wl_schema_get_value($entity_post_id, 'sameAs'); $this->assertTrue(is_array($same_as_array)); $this->assertEquals($same_as_uri, $same_as_array[0]); $post = wl_get_entity_post_by_uri('http://example.org/entity/test_entity'); $this->assertNotNull($post); }
function testContentParsing() { // Create the sample entity for testing. $this->createSampleEntity(); $content = '<span class="textannotation highlight organization disambiguated" id="urn:enhancement-16e9b0f6-e792-5b75-ffb7-ec40916d8753" itemid="http://dbpedia.org/resource/Honda" itemscope="itemscope" itemtype="organization">Honda</span> is recalling nearly 900,000 minivans for a defect that could increase fire risk.'; $matches = array(); if (0 < preg_match_all('/ itemid="([^"]+)"/im', $content, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { $item_id = $match[1]; $post = wl_get_entity_post_by_uri($item_id); $this->assertNotNull($post); $uri = wl_get_entity_uri($post->ID); $this->assertNotNull($uri); $this->assertNotEquals($item_id, $uri); } } $this->assertTrue(0 < count($matches)); }
function testEntityMetadataAreProperlyUpdated() { $fake = $this->prepareFakeGlobalPostArrayFromFile('/assets/fake_global_post_array_with_one_entity_linked_as_what.json'); $_POST = $fake; // Retrieve the entity uri (the first key in wl_entities associative aray) $original_entity_uri = current(array_keys($fake['wl_entities'])); // Reference the entity to the post content $content = <<<EOF <span itemid="{$original_entity_uri}">My entity</span> EOF; // Create a post referincing to the created entity $post_id = wl_create_post($content, 'my-post', 'A post', 'draft'); // Here the entity should have been created $original_entity = wl_get_entity_post_by_uri($original_entity_uri); $this->assertNotNull($original_entity); // Store entity type, images and sameAs (needed later) $original_type = wl_schema_get_types($original_entity->ID); $original_thumbnails = $this->getThumbs($original_entity->ID); $original_sameAs = wl_schema_get_value($original_entity->ID, 'sameAs'); // Query the same entity using the Redlink URI $entity_uri = wl_get_entity_uri($original_entity->ID); $e = wl_get_entity_post_by_uri($entity_uri); $this->assertEquals($original_entity, $e); // The entity description should be the same we expect $raw_entity = current(array_values($fake['wl_entities'])); $this->assertEquals($raw_entity['description'], $original_entity->post_content); // The entity is related as what predicate $related_entity_ids = wl_core_get_related_entity_ids($post_id, array("predicate" => "what")); $this->assertCount(1, $related_entity_ids); // Ensure there are no other relation instances $relation_instances = wl_tests_get_relation_instances_for($post_id); $this->assertCount(1, $relation_instances); /* Now Post is saved again with the same mentioned entity: * - with different type * - with different description * - with one more image * - with one modified sameAs * - as WHO instead fo WHAT */ $fake = $this->prepareFakeGlobalPostArrayFromFile('/assets/fake_global_post_array_with_one_entity_linked_as_who_and_modified_data.json'); $_POST = $fake; // The entity url should be the same we expect $raw_entity = current(array_values($fake['wl_entities'])); $raw_entity_uri = $raw_entity['uri']; $new_content = <<<EOF <span itemid="{$raw_entity_uri}">My entity</span> EOF; // Update the post content (to force existing entities update) wp_update_post(array('ID' => $post_id, 'post_content' => $new_content)); // Verify the mentioned entity was already into DB... $updated_entity = wl_get_entity_post_by_uri($raw_entity_uri); $this->assertEquals($original_entity->ID, $updated_entity->ID); $this->assertEquals($original_entity->post_title, $updated_entity->post_title); // ... but some properties changed! $this->assertNotEquals($original_entity, $updated_entity); // Verify entity type has been updated $updated_type = wl_schema_get_types($updated_entity->ID); $this->assertNotEquals($original_type, $updated_type); $this->assertEquals(array('http://schema.org/Organization'), $updated_type); // Verify entity description has been updated $this->assertEquals($raw_entity['description'], $updated_entity->post_content); // Verify entity images have been updated (one was added) $updated_thumbnails = $this->getThumbs($updated_entity->ID); $this->assertNotEquals($original_thumbnails, $updated_thumbnails); $this->assertContains($original_thumbnails[0], $updated_thumbnails); $this->assertCount(2, $updated_thumbnails); // There is one more $this->assertContains('Netherlands_vs_Ivory_Coast', $updated_thumbnails[1]); // ... about soccer // Verify entity sameAs have been updated $updated_sameAs = wl_schema_get_value($updated_entity->ID, 'sameAs'); $this->assertNotEquals($original_sameAs, $updated_sameAs); $this->assertContains($original_sameAs[1], $updated_sameAs); $this->assertNotContains($original_sameAs[0], $updated_sameAs); $this->assertContains('http://sv.dbpedia.org/page/Reason', $updated_sameAs); // Verify the entity is now related as who predicate $related_entity_ids = wl_core_get_related_entity_ids($post_id, array("predicate" => "who")); $this->assertCount(1, $related_entity_ids); // Ensure there are no other relation instances $relation_instances = wl_tests_get_relation_instances_for($post_id); $this->assertCount(1, $relation_instances); }
/** * Saves the values of wordlift metaboxes set in the entity editor page */ function wl_entity_metabox_save($post_id) { if (!isset($_POST['wl_metaboxes'])) { return; } // Loop over the wl_metaboxes array and save metaboxes values foreach ($_POST['wl_metaboxes'] as $meta_name => $meta_values) { // First, verify nonce is set for this meta $nonce_name = 'wordlift_' . $meta_name . '_entity_box_nonce'; $nonce_verify = 'wordlift_' . $meta_name . '_entity_box'; if (!isset($_POST[$nonce_name])) { return $post_id; } // Verify that the nonce is valid. if (!wp_verify_nonce($_POST[$nonce_name], $nonce_verify)) { return $post_id; } // Delete values before updating delete_post_meta($post_id, $meta_name); // Save the property value(s) if (isset($meta_name) && isset($meta_values) && $meta_values !== '') { // There can be one or more property values, so we force to array: if (!is_array($meta_values)) { $meta_values = array($meta_values); } foreach ($meta_values as $meta_value) { // If the meta expects an entity... $expecting_uri = wl_get_meta_type($meta_name) === WL_DATA_TYPE_URI; // ...and the user inputs an entity that is not present in the db... $absent_from_db = is_null(wl_get_entity_post_by_uri($meta_value)); // ...and that is not a external uri $name_is_uri = strpos($meta_value, 'http') === 0; if ($expecting_uri && $absent_from_db && !$name_is_uri) { // ...we create a new entity! $new_entity = wl_save_entity('', $meta_value, WL_ENTITY_TYPE_NAME, ''); // Assign type $constraints = wl_get_meta_constraints($meta_name); $type = 'http://schema.org/' . $constraints['uri_type']; wl_set_entity_main_type($new_entity->ID, $type); // TODO: directly publish the new entity // Update the value that will be saved as meta $meta_value = wl_get_entity_uri($new_entity->ID); } // TODO: use WL methods add_post_meta($post_id, $meta_name, $meta_value); } } } // Push changes on RedLink wl_linked_data_push_to_redlink($post_id); }
/** * Get an array of entities from the *itemid* attributes embedded in the provided content. * * @since 3.0.0 * * @param string $content The content with itemid attributes. * * @return array An array of entity posts. */ function wl_linked_data_content_get_embedded_entities($content) { // Remove quote escapes. $content = str_replace('\\"', '"', $content); // Match all itemid attributes. $pattern = '/<\\w+[^>]*\\sitemid="([^"]+)"[^>]*>/im'; wl_write_log("Getting entities embedded into content [ pattern :: {$pattern} ]"); // Remove the pattern while it is found (match nested annotations). $matches = array(); // In case of errors, return an empty array. if (false === preg_match_all($pattern, $content, $matches)) { wl_write_log("Found no entities embedded in content"); return array(); } // wl_write_log("wl_update_related_entities [ content :: $content ][ data :: " . var_export($data, true). " ][ matches :: " . var_export($matches, true) . " ]"); // Collect the entities. $entities = array(); foreach ($matches[1] as $uri) { $uri_d = html_entity_decode($uri); $entity = wl_get_entity_post_by_uri($uri_d); if (null !== $entity) { array_push($entities, $entity->ID); } } $count = sizeof($entities); wl_write_log("Found {$count} entities embedded in content"); return $entities; }
function testEntityAdditionalPropertiesAreSaved() { $fake = $this->prepareFakeGlobalPostArrayFromFile('/assets/fake_global_post_array_with_a_new_entity_linked_as_where_with_coordinates.json'); $_POST = $fake; // Retrieve the entity uri (the first key in wl_entities associative aray) $entity_uri = current(array_keys($fake['wl_entities'])); // Retrieve the label and compose expected uri $raw_entity = current(array_values($fake['wl_entities'])); $expected_entity_uri = $this->buildEntityUriForLabel($raw_entity['label']); // Reference the entity to the post content $content = <<<EOF <span itemid="{$entity_uri}">My entity</span> EOF; // Create a post referincing to the created entity $post_id = wl_create_post($content, 'my-post', 'A post', 'draft'); // Here the entity should have been created $entity = wl_get_entity_post_by_uri($expected_entity_uri); $this->assertNotNull($entity); // Verify association to post as where $related_entity_ids = wl_core_get_related_entity_ids($post_id, array("predicate" => "where")); $this->assertEquals(array($entity->ID), $related_entity_ids); // Verify schema type $this->assertEquals(array('http://schema.org/Place'), wl_schema_get_types($entity->ID)); // Verify coordinates $this->assertEquals(array(43.21), wl_schema_get_value($entity->ID, 'latitude')); $this->assertEquals(array(12.34), wl_schema_get_value($entity->ID, 'longitude')); }
/** * Replaces the *itemid* attributes URIs with the WordLift URIs. * * @param string $content The post content. * * @return string The updated post content. */ function wl_replace_item_id_with_uri($content) { // wl_write_log( "wl_replace_item_id_with_uri" ); // Strip slashes, see https://core.trac.wordpress.org/ticket/21767 $content = stripslashes($content); // If any match are found. $matches = array(); if (0 < preg_match_all('/ itemid="([^"]+)"/i', $content, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { // Get the item ID. $item_id = $match[1]; // Get the post bound to that item ID (looking both in the 'official' URI and in the 'same-as' . $post = wl_get_entity_post_by_uri($item_id); // If no entity is found, continue to the next one. if (null === $post) { continue; } // Get the URI for that post. $uri = wl_get_entity_uri($post->ID); // wl_write_log( "wl_replace_item_id_with_uri [ item id :: $item_id ][ uri :: $uri ]" ); // If the item ID and the URI differ, replace the item ID with the URI saved in WordPress. if ($item_id !== $uri) { $uri_e = esc_html($uri); $content = str_replace(" itemid=\"{$item_id}\"", " itemid=\"{$uri_e}\"", $content); } } } // Reapply slashes. $content = addslashes($content); return $content; }
/** * Fills up the microdata_template with entity's values. * * @param string $entity_id An entity ID. * @param string $entity_type Entity type stracture. * @param integer $recursion_level Recursion depth level in microdata compiling. Recursion depth limit is defined by WL_MAX_NUM_RECURSIONS_WHEN_PRINTING_MICRODATA constant. * * @return string The content with embedded microdata. */ function wl_content_embed_compile_microdata_template($entity_id, $entity_type, $recursion_level = 0) { wl_write_log("[ entity id :: {$entity_id} ][ entity type :: " . var_export($entity_type, true) . " ][ recursion level :: {$recursion_level} ]"); $regex = '/{{(.*?)}}/'; $matches = array(); if (null === $entity_type) { return ''; } $template = $entity_type['microdata_template']; // Return empty string if template fields have not been found. if (false === preg_match_all($regex, $template, $matches, PREG_SET_ORDER)) { return ''; } foreach ($matches as $match) { $placeholder = $match[0]; $field_name = $match[1]; // Get property value. $meta_collection = wl_schema_get_value($entity_id, $field_name); // If no value is given, just remove the placeholder from the template if (null == $meta_collection) { $template = str_replace($placeholder, '', $template); continue; } // What kind of value is it? // TODO: Performance issue here: meta type retrieving should be centralized $expected_type = wl_get_meta_type($field_name); foreach ($meta_collection as $field_value) { if (WL_DATA_TYPE_URI == $expected_type) { // If is a numeric value we assume it is an ID referencing for an internal entity. if (is_numeric($field_value)) { // Found id, get uri. $field_value = wl_get_entity_uri($field_value); } // Just if the linked entity does exist I can go further with template compiling $nested_entity = wl_get_entity_post_by_uri($field_value); if (!is_null($nested_entity)) { $content = '<span itemid="' . esc_attr($field_value) . '">' . $nested_entity->post_title . '</span>'; $compiled_template = wl_content_embed_item_microdata($content, $field_value, $field_name, ++$recursion_level); $template = str_replace($placeholder, $compiled_template, $template); } else { $template = str_replace($placeholder, '', $template); } continue; } // Standard condition: field containing a raw value $value = '<span itemprop="' . esc_attr($field_name) . '" content="' . esc_attr($field_value) . '"></span>'; $template = str_replace($placeholder, $value, $template); } } return $template; }
/** * Fills up the microdata_template with entity's values. * * @param string $entity_id An entity ID. * @param string $entity_type Entity type structure. * @param integer $recursion_level Recursion depth level in microdata compiling. Recursion depth limit is defined by WL_MAX_NUM_RECURSIONS_WHEN_PRINTING_MICRODATA constant. * * @return string The content with embedded microdata. */ function wl_content_embed_compile_microdata_template($entity_id, $entity_type, $recursion_level = 0) { global $wl_logger; if (WP_DEBUG) { $wl_logger->trace("Embedding microdata [ entity id :: {$entity_id} ][ entity type :: " . var_export($entity_type, true) . " ][ recursion level :: {$recursion_level} ]"); } $regex = '/{{(.*?)}}/'; $matches = array(); if (null === $entity_type) { return ''; } $template = $entity_type['microdata_template']; // Return empty string if template fields have not been found. if (false === preg_match_all($regex, $template, $matches, PREG_SET_ORDER)) { return ''; } foreach ($matches as $match) { $placeholder = $match[0]; $field_name = $match[1]; // Get property value. $meta_collection = wl_schema_get_value($entity_id, $field_name); // If no value is given, just remove the placeholder from the template if (null == $meta_collection) { $template = str_replace($placeholder, '', $template); continue; } // What kind of value is it? // TODO: Performance issue here: meta type retrieving should be centralized $expected_type = wl_get_meta_type($field_name); if (WP_DEBUG) { $wl_logger->trace("Embedding microdata [ placeholder :: {$placeholder} ][ field name :: {$field_name} ][ meta collection :: " . (is_array($meta_collection) ? var_export($meta_collection, true) : $meta_collection) . " ][ expected type :: {$expected_type} ]"); } foreach ($meta_collection as $field_value) { // Quick and dirty patch for #163: // - only apply to URIs, i.e. to properties pointing to another post ( $field_value should be a post ID ), // - check that $field_value is actually a number, // - check that the referenced post is published. // OR // - if the value is empty then we don't display it. if (Wordlift_Schema_Service::DATA_TYPE_URI === $expected_type && is_numeric($field_value) && 'publish' !== ($post_status = get_post_status($field_value)) || empty($field_value)) { if (WP_DEBUG) { $wl_logger->trace("Microdata refers to a non-published post [ field value :: {$field_value} ][ post status :: {$post_status} ]"); } // Remove the placeholder. $template = str_replace($placeholder, '', $template); continue; } if (Wordlift_Schema_Service::DATA_TYPE_URI == $expected_type) { // If is a numeric value we assume it is an ID referencing for an internal entity. if (is_numeric($field_value)) { // Found id, get uri. $field_value = wl_get_entity_uri($field_value); } // Just if the linked entity does exist I can go further with template compiling $nested_entity = wl_get_entity_post_by_uri($field_value); if (!is_null($nested_entity)) { $content = '<span itemid="' . esc_attr($field_value) . '">' . $nested_entity->post_title . '</span>'; $compiled_template = wl_content_embed_item_microdata($content, $field_value, $field_name, ++$recursion_level); $template = str_replace($placeholder, $compiled_template, $template); } else { $template = str_replace($placeholder, '', $template); } continue; } // Standard condition: field containing a raw value // For non visible test, schema.org dictates to use the *meta* tag. // see http://schema.org/docs/gs.html#advanced_missing $value = '<meta itemprop="' . esc_attr($field_name) . '" content="' . esc_attr($field_value) . '" />'; $template = str_replace($placeholder, $value, $template); } } return $template; }