/**
 * Encode places array in geojson compliant format
 * (refer to http://leafletjs.com/examples/geojson.html)
 * Define geomap boundaries according to $places
 * Default boundaries are defined using PHP_INT_MAX value
 *
 * @param array $places An array of place posts.
 *
 * @return array An array of markers and boundaries for Leaflet.
 */
function wl_shortcode_geomap_prepare_map($places)
{
    // Prepare for min/max lat/long in case we need to define a view boundary for the client JavaScript.
    $min_latitude = PHP_INT_MAX;
    $min_longitude = PHP_INT_MAX;
    $max_latitude = ~PHP_INT_MAX;
    $max_longitude = ~PHP_INT_MAX;
    // Prepare an empty array of POIs in geoJSON format.
    $pois = array();
    // And store list of points to allow Leaflet compute the optimal bounding box.
    // The main reason for this is that geoJSON has swapped coordinates (lon. lat)
    $boundaries = array();
    // Add a POI for each entity that has coordinates.
    foreach ($places as $entity) {
        // Get the coordinates.
        $coordinates = wl_get_coordinates($entity->ID);
        // Don't show the widget if the coordinates aren't set.
        if ($coordinates['latitude'] == 0 || $coordinates['longitude'] == 0) {
            continue;
        }
        // TODO Map html rendering should be delegated to the wordlift js ui layer
        // This function should be focused on returning pure data instead
        // Get the title, URL and thumb of the entity.
        $title = esc_attr($entity->post_title);
        $link = esc_attr(get_permalink($entity->ID));
        if ('' !== ($thumbnail_id = get_post_thumbnail_id($entity->ID)) && false !== ($attachment = wp_get_attachment_image_src($thumbnail_id))) {
            $img_src = esc_attr($attachment[0]);
        }
        // Build HTML popup. TODO: move thumb width in css
        $content = "<a href={$link}><h6>{$title}</h6>";
        if (isset($img_src)) {
            $content = $content . "<img src={$img_src} style='width:100%'/>";
        }
        $content = $content . "</a><ul>";
        // Get the related posts (published) and print them in the popup.
        $related_posts = wl_core_get_related_post_ids($entity->ID, array('status' => 'publish'));
        foreach ($related_posts as $rp_id) {
            $rp = get_post($rp_id);
            $title = esc_attr($rp->post_title);
            $link = esc_attr(get_permalink($rp->ID));
            $content = $content . "<li><a href={$link}>{$title}</a></li>";
        }
        $content = $content . "</ul>";
        // Formatting POI in geoJSON.
        // http://leafletjs.com/examples/geojson.html
        $poi = array('type' => 'Feature', 'properties' => array('popupContent' => $content), 'geometry' => array('type' => 'Point', 'coordinates' => array($coordinates['longitude'], $coordinates['latitude'])));
        $pois[] = $poi;
        // Formatting boundaries in a Leaflet-like format (see LatLngBounds).
        // http://leafletjs.com/reference.html#latlngbounds
        $boundaries[] = array($coordinates['latitude'], $coordinates['longitude']);
    }
    $map_data = array();
    $map_data['features'] = $pois;
    $map_data['boundaries'] = $boundaries;
    return $map_data;
}
 /**
  * Render custom columns
  * @see https://codex.wordpress.org/Plugin_API/Action_Reference/manage_$post_type_posts_custom_column
  *
  * @since 3.3.0
  *
  * @param string $column the current column.
  * @param int $entity_id An entity post id.
  *
  * @return true if the post is an entity otherwise false.
  */
 public function render_custom_columns($column, $entity_id)
 {
     switch ($column) {
         case 'wl_column_related_posts':
             echo count(wl_core_get_related_post_ids($entity_id));
             break;
         case 'wl_column_thumbnail':
             $edit_link = get_edit_post_link($entity_id);
             $thumb = get_the_post_thumbnail($entity_id, array(self::THUMB_SIZE, self::THUMB_SIZE));
             if (!$thumb) {
                 $thumb = "<img src='" . WL_DEFAULT_THUMBNAIL_PATH . "' width='" . self::THUMB_SIZE . "' />";
             }
             echo "<a href='{$edit_link}'>{$thumb}</a>";
             break;
         case 'wl_column_rating':
             $rating = $this->entity_service->get_rating_for($entity_id);
             echo '<i class="wl-traffic-light wl-tl-' . $rating['traffic_light_score'] . '">' . $rating['percentage_score'] . '%</i>';
             break;
     }
 }
/**
 * Recursive function used to retrieve related content starting from a post ID.
 *
 * @uses wl_core_get_related_post_ids() to get the list of post ids that reference an entity.
 *
 * @param int $entity_id The entity post ID.
 * @param int $depth Max number of entities in output.
 * @param array $related An existing array of related entities.
 * @return array
 */
function wl_shortcode_chord_get_relations($entity_id, $depth = 2, $related = null)
{
    // Search for more entities only if we did not exceed $depth or $max_size
    $max_size = 30;
    if (!is_null($related)) {
        if (count($related['entities']) > $max_size || $depth <= 0) {
            return $related;
        }
    }
    wl_write_log("wl_shortcode_chord_get_relations [ post id :: {$entity_id} ][ depth :: {$depth} ][ related? :: " . (is_null($related) ? 'yes' : 'no') . " ]");
    // Create a related array which will hold entities and relations.
    if (is_null($related)) {
        $related = array('entities' => array($entity_id), 'relations' => array());
    }
    // Get related entities
    $related_entity_ids = wl_core_get_related_entity_ids($entity_id, array('status' => 'publish'));
    // Get related posts (only id the current node is an entity)
    $related_post_ids = array();
    if (get_post_type($entity_id) == WL_ENTITY_TYPE_NAME) {
        $related_post_ids = wl_core_get_related_post_ids($entity_id, array('status' => 'publish'));
    }
    // Merge results.
    $related_ids = array_merge($related_post_ids, $related_entity_ids);
    $related_ids = array_unique($related_ids);
    // TODO: List of entities ($rel) should be ordered by interest factors.
    shuffle($related_ids);
    // Now we have all the related IDs.
    foreach ($related_ids as $related_id) {
        // TODO: does it make sense to set an array post ID > related ID? The *wl_shortcode_chord_get_graph*
        // method is going anyway to *refactor* the data structure. So here the structure may be optimized in terms
        // of readability and performance.
        $related['relations'][] = array($entity_id, $related_id);
        if (!in_array($related_id, $related['entities'])) {
            //Found new related entity!
            $related['entities'][] = $related_id;
            $related = wl_shortcode_chord_get_relations($related_id, $depth - 1, $related);
        }
    }
    // End condition 2: no more entities to search for.
    return $related;
}
/**
 * Delete the specified post from the triple store.
 *
 * @param array|int $post An array of post data
 */
function rl_delete_post($post)
{
    $post_id = is_numeric($post) ? $post : $post->ID;
    // hide all entities that are not referenced by any published post.
    foreach (wl_core_get_related_entity_ids($post_id) as $entity_id) {
        // check if there is at least one referencing post published.
        $is_published = array_reduce(wl_core_get_related_post_ids($entity_id), function ($carry, $item) {
            $post = get_post($item);
            return $carry || 'publish' === $post->post_status;
        });
        // set the entity to draft if no referencing posts are published.
        if (!$is_published) {
            wl_update_post_status($entity_id, 'draft');
        }
    }
    // get the entity URI (valid also for posts)
    $uri_esc = wl_sparql_escape_uri(wl_get_entity_uri($post_id));
    wl_write_log("rl_delete_post [ post id :: {$post_id} ][ uri esc :: {$uri_esc} ]");
    // create the SPARQL statement by joining the SPARQL prefixes and deleting any known predicate.
    $stmt = rl_sparql_prefixes();
    foreach (wl_predicates() as $predicate) {
        $stmt .= "DELETE { <{$uri_esc}> {$predicate} ?o . } WHERE { <{$uri_esc}> {$predicate} ?o . };\n" . "DELETE { ?s {$predicate} <{$uri_esc}> . } WHERE { ?s {$predicate} <{$uri_esc}> . };\n";
    }
    // if the post is an entity and has exported properties, delete the related predicates.
    if (WL_ENTITY_TYPE_NAME === $post->post_type) {
        $type = wl_entity_type_taxonomy_get_type($post->ID);
        if (isset($type['custom_fields'])) {
            foreach ($type['custom_fields'] as $field => $params) {
                // TODO: enclose in <> only if predicate starts with http(s)://
                $predicate = '<' . $params['predicate'] . '>';
                $stmt .= "DELETE { <{$uri_esc}> {$predicate} ?o . } WHERE { <{$uri_esc}> {$predicate} ?o . };\n";
            }
        }
    }
    // finally execute the query.
    rl_execute_sparql_update_query($stmt);
}
function wl_shortcode_faceted_search_ajax($http_raw_data = null)
{
    // Entity ID must be defined
    if (!isset($_GET['entity_id'])) {
        wp_die('No entity_id given');
        return;
    }
    $entity_id = $_GET['entity_id'];
    // If the current post is not an entity post an exception needs to be raised
    $entity = get_post($entity_id);
    if (Wordlift_Entity_Service::TYPE_NAME !== $entity->post_type) {
        wp_die('Faceted search supports only entity posts');
        return;
    }
    // Which type was requested?
    if (isset($_GET['type'])) {
        $required_type = $_GET['type'];
    } else {
        $required_type = null;
    }
    // Extract filtering conditions
    $filtering_entity_uris = null == $http_raw_data ? file_get_contents("php://input") : $http_raw_data;
    $filtering_entity_uris = json_decode($filtering_entity_uris);
    // Set up data structures
    $referencing_post_ids = wl_core_get_related_post_ids($entity_id, array('status' => 'publish'));
    $results = array();
    if ('posts' == $required_type) {
        // Required filtered posts.
        wl_write_log("Going to find related posts for the current entity [ entity ID :: {$entity_id} ]");
        if (empty($filtering_entity_uris)) {
            // No filter, just get referencing posts
            foreach ($referencing_post_ids as $post_obj_id) {
                $post_obj = get_post($post_obj_id);
                $thumbnail = wp_get_attachment_url(get_post_thumbnail_id($post_obj->ID, 'thumbnail'));
                $post_obj->thumbnail = $thumbnail ? $thumbnail : WL_DEFAULT_THUMBNAIL_PATH;
                $post_obj->permalink = get_post_permalink($post_obj->ID);
                $results[] = $post_obj;
            }
        } else {
            $filtering_entity_ids = wl_get_entity_post_ids_by_uris($filtering_entity_uris);
            // Search posts that reference all the filtering entities.
            $filtered_posts = wl_core_get_posts(array('get' => 'posts', 'post__in' => $referencing_post_ids, 'related_to__in' => $filtering_entity_ids, 'post_type' => 'post', 'as' => 'subject'));
            foreach ($filtered_posts as $post_obj) {
                $thumbnail = wp_get_attachment_url(get_post_thumbnail_id($post_obj->ID, 'thumbnail'));
                $post_obj->thumbnail = $thumbnail ? $thumbnail : WL_DEFAULT_THUMBNAIL_PATH;
                $post_obj->permalink = get_post_permalink($post_obj->ID);
                $results[] = $post_obj;
            }
            $results = $filtered_posts;
        }
    } else {
        global $wpdb;
        wl_write_log("Going to find related entities for the current entity [ entity ID :: {$entity_id} ]");
        // Retrieve Wordlift relation instances table name
        $table_name = wl_core_get_relation_instances_table_name();
        $ids = implode(',', $referencing_post_ids);
        // TODO - if an entity is related with different predicates each predicate impacts on counter
        $query = <<<EOF
            SELECT object_id as ID, count( object_id ) as counter 
            FROM {$table_name} 
            WHERE subject_id IN ({$ids}) and object_id != {$entity_id}
            GROUP BY object_id;
EOF;
        wl_write_log("Going to find related entities for the current entity [ entity ID :: {$entity_id} ] [ query :: {$query} ]");
        $entities = $wpdb->get_results($query, OBJECT);
        wl_write_log("Entities found " . count($entities));
        foreach ($entities as $obj) {
            $entity = get_post($obj->ID);
            $entity = wl_serialize_entity($entity);
            $entity['counter'] = $obj->counter;
            $results[] = $entity;
        }
    }
    wl_core_send_json($results);
}
 /**
  * Calculate rating for a given entity
  * Rating depends from following criteria
  *
  * 1. Is the current entity related to at least 1 post?
  * 2. Is the current entity content post not empty?
  * 3. Is the current entity related to at least 1 entity?
  * 4. Is the entity published? 
  * 5. There is a a thumbnail associated to the entity?
  * 6. Has the entity a sameas defined?
  * 7. Are all schema.org required metadata compiled?
  *
  * Each positive check means +1 in terms of rating score
  *
  * @since 3.3.0
  *
  * @param int $post_id The entity post id.
  *
  * @return int An array representing the rating obj.
  */
 public function calculate_rating_for($post_id)
 {
     // If it's not an entity, return.
     if (!$this->is_entity($post_id)) {
         return;
     }
     // Retrieve the post object
     $post = get_post($post_id);
     // Rating value
     $score = 0;
     // Store warning messages
     $warnings = array();
     // Is the current entity related to at least 1 post?
     0 < count(wl_core_get_related_post_ids($post->ID)) ? $score++ : array_push($warnings, __(self::RATING_WARNING_HAS_RELATED_POSTS, 'wordlift'));
     // Is the post content not empty?
     !empty($post->post_content) ? $score++ : array_push($warnings, __(self::RATING_WARNING_HAS_CONTENT_POST, 'wordlift'));
     // Is the current entity related to at least 1 entity?
     // Was the current entity already disambiguated?
     0 < count(wl_core_get_related_entity_ids($post->ID)) ? $score++ : array_push($warnings, __(self::RATING_WARNING_HAS_RELATED_ENTITIES, 'wordlift'));
     // Is the entity published?
     'publish' === get_post_status($post->ID) ? $score++ : array_push($warnings, __(self::RATING_WARNING_IS_PUBLISHED, 'wordlift'));
     // Has a thumbnail?
     has_post_thumbnail($post->ID) ? $score++ : array_push($warnings, __(self::RATING_WARNING_HAS_THUMBNAIL, 'wordlift'));
     // Get all post meta keys for the current post
     global $wpdb;
     $query = $wpdb->prepare("SELECT DISTINCT(meta_key) FROM {$wpdb->postmeta}  WHERE post_id = %d", $post->ID);
     // Check intersection between available meta keys
     // and expected ones arrays to detect missing values
     $available_meta_keys = $wpdb->get_col($query);
     // If each expected key is contained in available keys array ...
     in_array(Wordlift_Schema_Service::FIELD_SAME_AS, $available_meta_keys) ? $score++ : array_push($warnings, __(self::RATING_WARNING_HAS_SAME_AS, 'wordlift'));
     $schema = wl_entity_type_taxonomy_get_type($post_id);
     $expected_meta_keys = null === $schema['custom_fields'] ? array() : array_keys($schema['custom_fields']);
     $intersection = array_intersect($expected_meta_keys, $available_meta_keys);
     // If each expected key is contained in available keys array ...
     count($intersection) === count($expected_meta_keys) ? $score++ : array_push($warnings, __(self::RATING_WARNING_HAS_COMPLETED_METADATA, 'wordlift'));
     // Finally return score and warnings
     return array('raw_score' => $score, 'traffic_light_score' => $this->convert_raw_score_to_traffic_light($score), 'percentage_score' => $this->convert_raw_score_to_percentage($score), 'warnings' => $warnings);
 }
    function testEntityWithAlternativeLabelIsProperlyOverridden()
    {
        $original_label = uniqid('entity-original', true);
        // Create an entity
        $entity_id = wl_create_post('', 'entity-1', $original_label, 'draft', 'entity');
        // Check that there are no related posts for the entity
        $related_post_ids = wl_core_get_related_post_ids($entity_id, array("predicate" => "what"));
        $this->assertCount(0, $related_post_ids);
        // Generate e label and set it as alternative label for the new entity
        $alternative_label = uniqid('entity-alternative', true);
        Wordlift_Entity_Service::get_instance()->set_alternative_labels($entity_id, $alternative_label);
        // Check that the alternative label is properly set
        $labels = Wordlift_Entity_Service::get_instance()->get_alternative_labels($entity_id);
        $this->assertCount(1, $labels);
        $this->assertContains($alternative_label, $labels);
        // Force post status to publish: this triggers the save_post hook
        wl_update_post_status($entity_id, 'publish');
        // Check that entity label is properly mapped on entity post title
        $this->assertEquals($original_label, get_post($entity_id)->post_title);
        // Notice that the uri is generated trough the original label
        // while the current label is the alternative one
        $fake = $this->prepareFakeGlobalPostArrayFromFile('/assets/fake_global_post_array_with_one_existing_entity_linked_as_what.json', array('CURRENT_URI' => $this->buildEntityUriForLabel($original_label), 'CURRENT_LABEL' => $alternative_label));
        $_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 trough its alternative label
        $content = <<<EOF
    <span itemid="{$original_entity_uri}">{$alternative_label}</span>
EOF;
        // Create a post referincing to the created entity
        $post_id = wl_create_post($content, 'my-post', 'A post', 'draft');
        // Check that entity label is STILL properly mapped on entity post title
        $this->assertEquals($original_label, get_post($entity_id)->post_title);
        $expected_entity_uri = $this->buildEntityUriForLabel($original_label);
        $entity_uri = wl_get_entity_uri($entity_id);
        $this->assertEquals($entity_uri, $expected_entity_uri);
        // And it should be related to the post as what predicate
        $related_entity_ids = wl_core_get_related_entity_ids($post_id, array("predicate" => "what"));
        $this->assertCount(1, $related_entity_ids);
        $this->assertContains($entity_id, $related_entity_ids);
    }
 function testWlCoreGetRelatedPostIdsForAPost()
 {
     // Create 2 posts and 1 entities
     $post_1_id = wl_create_post('', 'post1', 'A post');
     $post_2_id = wl_create_post('', 'post2', 'A post');
     $entity_1_id = wl_create_post('', 'entity1', 'An Entity', 'draft', 'entity');
     // Insert relations
     wl_core_add_relation_instance($post_1_id, WL_WHERE_RELATION, $entity_1_id);
     wl_core_add_relation_instance($post_2_id, WL_WHO_RELATION, $entity_1_id);
     // Check relation are retrieved as expected
     $result = wl_core_get_related_post_ids($post_1_id);
     $this->assertCount(1, $result);
     $this->assertTrue(in_array($post_2_id, $result));
     $result = wl_core_get_related_post_ids($post_1_id, array('predicate' => WL_WHERE_RELATION));
     $this->assertCount(0, $result);
     $result = wl_core_get_related_post_ids($post_1_id, array('predicate' => WL_WHO_RELATION));
     $this->assertCount(1, $result);
     $this->assertTrue(in_array($post_2_id, $result));
 }
Ejemplo n.º 9
0
 /**
  * Test saving entities passed via a metabox.
  */
 function testEntitiesViaArray()
 {
     // Create a post.
     $post_id = $this->createPost();
     $this->assertTrue(is_numeric($post_id));
     $post = get_post($post_id);
     $this->assertNotNull($post);
     // Read the entities from the mock-up analysis.
     $analysis_results = wl_parse_file(dirname(__FILE__) . '/' . self::FILENAME . '.json');
     $this->assertTrue(is_array($analysis_results));
     // For each entity get the label, type, description and thumbnails.
     $this->assertTrue(isset($analysis_results['entities']));
     // Get a reference to the entities.
     $text_annotations = $analysis_results['text_annotations'];
     $best_entities = array();
     foreach ($text_annotations as $id => $text_annotation) {
         $entity_annotation = wl_get_entity_annotation_best_match($text_annotation['entities']);
         $entity = $entity_annotation['entity'];
         $entity_id = $entity->{'@id'};
         if (!array_key_exists($entity_id, $best_entities)) {
             $best_entities[$entity_id] = $entity;
         }
     }
     // Accumulate the entities in an array.
     $entities = array();
     foreach ($best_entities as $uri => $entity) {
         // Label
         if (!isset($entity->{'http://www.w3.org/2000/01/rdf-schema#label'}->{'@value'})) {
             var_dump($entity);
         }
         $this->assertTrue(isset($entity->{'http://www.w3.org/2000/01/rdf-schema#label'}->{'@value'}));
         $label = $entity->{'http://www.w3.org/2000/01/rdf-schema#label'}->{'@value'};
         $this->assertFalse(empty($label));
         // Type
         //            $type = wl_get_entity_type($entity);
         //            $this->assertFalse(empty($type));
         // Description
         $description = wl_get_entity_description($entity);
         $this->assertNotNull($description);
         // Images
         $images = wl_get_entity_thumbnails($entity);
         $this->assertTrue(is_array($images));
         // Save the entity to the entities array.
         $entities = array_merge_recursive($entities, array($uri => array('uri' => $uri, 'label' => $label, 'main_type' => 'http://schema.org/Thing', 'type' => array(), 'description' => $description, 'images' => $images)));
     }
     // Save the entities in the array.
     $entity_posts = wl_save_entities($entities);
     // Publish
     $entity_post_ids = array_map(function ($item) {
         return $item->ID;
     }, $entity_posts);
     foreach ($entity_post_ids as $entity_id) {
         wp_publish_post($entity_id);
     }
     // TODO: need to bind entities with posts.
     wl_core_add_relation_instances($post_id, WL_WHAT_RELATION, $entity_post_ids);
     $this->assertCount(sizeof($entity_post_ids), wl_core_get_related_entity_ids($post_id));
     // TODO: synchronize data.
     // NOTICE: this requires a published post!
     wl_linked_data_push_to_redlink($post_id);
     // Check that the entities are created in WordPress.
     $this->assertCount(count($entities), $entity_posts);
     // Check that each entity is bound to the post.
     $entity_ids = array();
     foreach ($entity_posts as $post) {
         // Store the entity IDs for future checks.
         array_push($entity_ids, $post->ID);
         // Get the related posts IDs.
         $rel_posts = wl_core_get_related_post_ids($post->ID);
         $this->assertCount(1, $rel_posts);
         // The post must be the one the test created.
         $this->assertEquals($post_id, $rel_posts[0]);
     }
     // Check that the post references the entities.
     $rel_entities = wl_core_get_related_entity_ids($post_id);
     $this->assertEquals(count($entity_ids), count($rel_entities));
     foreach ($entity_ids as $id) {
         $this->assertTrue(in_array($id, $rel_entities));
     }
     // Check that the locally saved entities and the remotely saved ones match.
     $this->checkEntities($entity_posts);
     // Check that the locally saved post data match the ones on Redlink.
     $this->checkPost($post_id);
     // Check the post references, that they match between local and remote.
     $this->checkPostReferences($post_id);
     // Delete the post.
     $this->deletePost($post_id);
 }