Theming

From OSF Wiki
Jump to: navigation, search

Contents

Introduction

Drupal sit on top of a really powerful theming engine. Nearly anything can be templated and themed before being displayed to the end users. What we will focus on in this page are the things in OSF for Drupal that can be themed. There are two kind of templates that can be created:

  • Templates that will be used to theme specific individual resources' pages based on their type
  • Templates that will be used to theme search results based on their type

Creating Templates

Creating a template for a ResourceType entity is not different than creating a template for any other type of Drupal Entities. The same API, methodologies and concepts can be used.

When you create a template, different pre-populated variables are accessible to the person that will create the theme. The main variables in a non-search template are:

  • $resource
    • This is a StructuredDynamics\osf\framework\Resource class instance that can be used to manipulate the entity's content. The Resource class should be the first choice of the themers since it has been specifically created to make the templates easier to create, maintain and read.
  • $element or $entity
    • This is a ResourceType class instance. All the values of the properties follows the internal Drupal array structure conventions
  • $subject
    • This is a StructuredDynamics\osf\framework\Subject class instance that can be used to manipulate the entity's content. The Subject class does have a complete API that is available to the developer to help them manipulating the content of an entity.

The main variables for a search template are:

  • $resource
  • $entity
  • $subject

Simple Template Using $resource

The Resource API is a set of PHP classes that have been created specifically to help OSF for Drupal templetes designers to create, maintain and read templates files. The API is mean to be a simple set of classes and functions that can easily be used to display Entity related information into a Drupal templated page.

Introduction to the Resource API

The Resource API is a set of five classes but only three are used for templating purposes:

  • Resource
  • Property
  • Value

The design of the API focus on these three classes. A Resource is an Entity description. It is composed of:

  • A unique identifier
  • One or multiple types
  • One or multiple Properties

Then a Property is composed of:

  • A unique identifier
  • A type, which is one of:
    • Datatype Property, or
    • Object Property
  • One or multiple Values

Then a Value is composed of:

  • A content (the actual value)
  • An optional language identifer (ISO 639)
  • An optional datatype
  • Optional reification statements (out of the scope of this documentation).

It is as simple as this: a Resource is described using multiple Properties and each of these properties can have one or multiple Values.

What the Resource API does, is just to expose utility functions to help Drupal template designers to use that entity information in the context of a Drupal template.

Overview of the Resource API

In this section, we will cover the main API functions that are available via this API. Note that this document only focus on the classes and functions that are relevant to Drupal template designers. Other classes and functions exists, but they are not relevant for the job at hands.

Resource Class
Function Description Parameters Returns
uri() Get the full URI of the Resource None Return the full URI of this Resource
property($uri) Get a specific property and all its values
Parameter Description
$uri URI of the property to get. The full URI of the property can be used, or its "prefixed" (abridged) version. Such a prefixed version would be "foaf:name". See the section below that discuss abridged vs. unabridged URIs.
Return the Property instance with all its values. If the property is not existing, then an empty Property is returned. An empty property is a property with an empty URI and an empty set of values
properties() Get the list of all the properties defining this Resource None Return an array of Property() instances that define this Resource
type($id) Get a type of this resource
Parameter Description
$id ID of the type to return. The index start at 1, unlike arrays
Return the full URI of the type available for that index. NULL is returned if there is no type available for this index
types() Get the types of the Resource None Return an array of full URIs that refer to the types of this Resource
setLanguage($language) Specify the language to be used when the user get values from this Resource instance. If a language is specified, then all the functions that returns values will only return values of that language. If the specified language is NULL, then the values of all languages will be returned.

Note that the object properties are not affected by this setting.

Parameter Description
$language ISO 639 language tag
Return the Resource itself.

This means that this function can be chained with other function calls.

isLanguageStrict() Specify if the Resource is using the strict language mode None Return the Resource itself.

This means that this function can be chained with other function calls.

strictLanguage() Enables the strict language mode. If this mode is enabled, it means that only the values tagged with that language will be returned for the Datatype properties values. This means that all values that have a NULL (unspecified) language won't be returned by any functions that return values None Return the Resource itself.

This means that this function can be chained with other function calls.

looseLanguage() Enables the "loose language" mode. If this mode is enabled, it means that the values tagged with that language, and the values without any language defined for them (NULL) will be returned for the Datatype properties values.

This is the default behavior.

None Return the Resource itself.

This means that this function can be chained with other function calls.

Property Class
Function Description Parameters Returns
value($id) Return a value defined for a property. If a language is defined for the Resource then the index is the one of the sub-set of values that is composed of all the values of the language specified for the Resource.
Parameter Description
$id ID of the value to return.

The index start at 1, unlike arrays

Return a Value instance that represents that value. An empty Value instance will be returned if there is no Value for that index
values() Get all the values of that Property. If the property is a Datatype property and if a language is defined for the Resource, then all the values that will be returned are values that have this language None Return an array of Value instances. One for each value of that property
uri() Get the URI of this property None Return the full URI of that property
type() Get the type of tihs property. Can be DATATYPE_PROPERTY or OBJECT_PROPERTY None Return the type of the property. It can be de constant DATATYPE_PROPERTY or the constant OBJECT_PROPERTY
exists() Specify if there are values for that property. If the Resource has a specific language specified for it, and if this is a DATATYPE_PROPERTY then exists will return TRUE only if there is a value with that language None TRUE is there is at least a value for that property. Return FALSE otherwise.
Value Class
Function Description Parameters Returns
content() Get the actual value content of this Value None Return the value content of this Value
display() Display the value

The display is used using the "echo" language construct

None Nothing
language() Get the language of this Value None Return the ISO 639 language code of this value. If NULL is returned it means that no language is specified for this Value (so it is considered language neutral).
type() Get the URI of the datatype of the Value None Return the URI of the datatype of the value. If NULL is returned it means that no specific datatype has been defined for this value
reifications() Get the reification properties for the triple composed of that value None Return an array which is composed of the reification statements

Usage Recipes

This section outline a series of Resource API "usage recipes". These are code snippets that covers the most common usage of the API for templating purposes.

Working with URIs

Before starting the list the recipes, we have to cover one of the core concept: URI. URI are unique identifiers that are used to identify Resources, Properties and Types. A URI is normally a standard URL, but it can be different. A full URI looks like this:

http://purl.org/ontology/iron#prefLabel

This unique identifier is not different than any other unique identifiers like a unique number, a hash value, etc. In the recipes below, we will be referring to prefixed URI. A prefixed URI is a URI that his shortened using a prefix. Let's say that we have a prefix called iron that stands for the partial URI http://purl.org/ontology/iron#. This means that the prefixed URI version of the full URI we defined above would be:

iron:prefLabel

Prefixed URIs makes shorter and more human readable URIs. This is perfect in the context of writing templates code since it will make the templates easier to read and maintain and create.

Note that you will be able use the osf_dpm_resource() function when you will be creating new templates to list all the prefixed properties URI that are available for the entity you are templating.

There is a configuration user interface in OSF for Drupal that you can use to see, and manage, all the namespaces that are available to you. See the Managing Namespaces page for more information.

Getting a Property

Getting a reference of one of the property that describes a Resource is pretty easier, the only thing you have to do is to specify the full or the prefixed URI to get using the property() function:

<?
  $label = $resource->property('iron:prefLabel');
?>

Then $label can be used anywhere in your code to display the value(s) of that preferred label.

Getting the First Value of a Property

To get the first value of a property, you have to use the value() function of a Property:

<?
  $labelValue = $resource->property('iron:prefLabel')->value();
?>

If you don't specify any $id for the value() function, then the first value will be returned. As you can see in this example, you can chain the function calls to get a property, and then its value. You could have done this too:

<?
  $label = $resource->property('iron:prefLabel');
  $labelValue = $label->value();
?>
Getting the Content of the First Value of a Property

To get the content of the first value of a property, you have to use the content() function of a Value:

<?
  $actualLabel = $resource->property('iron:prefLabel')->value()->content();
?>

What this does is to return the actual value of the Value instance.

Displaying the Content of the First Value of a Property

To display the content of the first value of a property, you have to use the display() function of a Value:

  <h2><? $resource->property('iron:prefLabel')->value()->display() ?></h2>

What this code does is to directly display the value within the H2 HTML code element. If you want to make you code even simpler and cleaner, you could have done this:

<?
  // Declare all the properties I want to use in my template
  $label = $resource->property('iron:prefLabel')->value();


  // Then I get to the template's code...
?>

  <h2><? $label->display() ?></h2>
Check if a Property Exists in a Resource

Often, you only want to display parts of your template depending if there exists values for some properties of the Resource you are templating. For example, if you have a Resource that may have some latitude/longitude coordinates, than you only want to display a Google Map if there are actual lat/long coordinates for the Resource you are displaying to the users. To make that check, you have to use the exists() function of a Property:

<?
  $lat = $resource->property('geo:latitude');
  $long = $resource->property('geo:longitude');
?>


<? if($lat->exists() && $long->exists()) { ?>
  <script type="text/javascript">
    var latlng = new google.maps.LatLng("<? $lat->value->display() ?>", "<? $long->value->display() ?>");
    var myOptions = {zoom: 15,
                     center: latlng,
                     mapTypeId: google.maps.MapTypeId.ROADMAP};
                   
    var map = new google.maps.Map(document.getElementById("map_canvas_'.$id.'"), myOptions);
                   
    var marker = new google.maps.Marker({position: latlng,
                                         map: map});
  </script>
<? } ?>
Get all the Values of a Property

Sometimes it is essential to be able to iterate all the Values of a Property. It can easily be done using the values() function of a Property:

<?
  $altLabel = $resource->property('iron:altLabel');

  if($altLabel->exists)
  {
    ?>Alternative labels: <?
    foreach($altLabel->values() as $value)
    {
      ?> $value->display() <?
    }
  }  
?>
Get all French Values of a Property

In a multilingual Drupal instance, you may have to deal with multilingual Resources. These are resources that may use multiple languages to describe the same Value. This can easily be done with this API. The only thing you have to do is to specify that you want to access French information from the Resource by using the setLanguage() function call on the Resource. Then when you will use any other functions to get the values from that Resource, everything will be in French:

<?
  $resource->setLanguage('fr');
   
  foreach($resource->property('rdfs:label')->values() as $value)
  {
    // Display all the French values of that property
    // This will also display the values that have no language defined for them
    $value->display();
  }  
?>

Remember that by default, the loose language mode is activated. That means that the code below will display all the French labels, and the labels that doesn't have any language defined for them. If you want to strictly use the French labels, then you could change that code this way to enable the strict language mode:

<?
  $resource->setLanguage('fr')
           ->strictLanguage();
   
  foreach($resource->property('rdfs:label')->values() as $value)
  {
    // Display all the French values of that property
    // This will also display the values that have no language defined for them
    $value->display();
  }  
?>
Working with Object Properties

As we discussed above, a Property can be a DATATYPE_PROPERTY or a OBJECT_PROPERTY. Datatype properties are properties for which their value is textual. The Object properties' value are always URIs. These URIs refer to other entities that should exists in the system.

In this example, we will consider that we have an object property foaf:knows that refer a person entity to another person entity:

<?
  $knows = $resource->property('foaf:knows');

  // Check if our Resource knows someone
  if($knows->exists())
  {
    ?><ul><?
    foreach($knows->values() as $know)
    {
      // Display the name of every person our Resource knows
     
      // First get the description of the entity he knows
      $knownEntity = osf_get_resource($know->content());
     
      // Then display the name of that person
      ?><li><? $knownEntity->property('iron:prefLabel')->value()->display() ?></li><?
    }
    ?></ul><?
  }
?>

In this example, we use the osf_get_resource() OSF for Drupal function call to get the description of the entity that is referenced from our Resource. That other Resource that is returned by this function call can be used like any other Resource that we are manipulating using the Resource API.

Getting Information About Types and Properties

Sometime it may be useful to get information about the type and properties that are used to describe a Resource you are manipulating. A type or a property are Resources as well. What this means is that you can use the Resource API to get additional information about these things that you may want to display into the templated page.

Let's take the usecase where you want to display the preferred label for the type of a Resource that you are templating, in parenthesis in the title of that Resource's page.

What you have to do is to use the osf_get_resource() function to get the description of that type and to display information about that type. Here is how you can do that:

<?

  // Get the Resource object of the Type of the Resource of that page
  $typeResource = osf_get_resource($resource->type())

  // Then display its preferred label in parenthesis

?>

<h3><? $resource->property('iron:prefLabel')->value()->display() ?> (<? $typeResource->property('iron:prefLabel')->value()->display() ?>)</h3>

What this code does is really simple: it get additional information from OSF about the type of the entity being processed in the templated page, and use that information to display the preferred label of that type into the templated page.

Drupal Specific Functions

There are two OSF for Drupal functions that exists that are related to the Resource API:

osf_get_resource($uri)

What the osf_get_resource($uri) call does is to return a Resource instance for the URI you specified as parameter. This function is normally used to get the Resource that describes an entity reference by the Resource being templated. That means that you can template information from related entities to the entity being templated. Like in this code:

<?
  $knows = $resource->property('foaf:knows');

  // Check if our Resource knows someone
  if($knows->exists())
  {
    ?><ul><?
    foreach($knows->values() as $know)
    {
      // Display the name of every person our Resource knows
     
      // First get the description of the entity he knows
      $knownEntity = osf_get_resource($know->content());
     
      // Then display the name of that person
      ?><li><? $knownEntity->property('iron:prefLabel')->value()->display() ?></li><?
    }
    ?></ul><?
  }
?>
osf_dpm_resource(Resource $resource)

The first thing a template designer has to do when he starts templating a new Entity is to check what are the properties/values available to him. What the osf_dpm_resource() function does is to output the structure of the Resource being templated such that you the information you can template, and more importantly, the prefixed URI of the properties you can use in your code. If you use that code, anywhere in the template's code:

<?
  osf_dpm_resource($resource);
?>

You will get that output in your browser the next time you will refresh your page (note: it may takes two refreshes depending on the code sequence in Drupal and if it is a search template an a view template):

Osf dpm resource.PNG

As you can see, this debugging output will show you all the properties that are defining that Resource. Then for each of these properties, you can see their prefixed URI. These same prefixed URI can be used in your template code like what we were doing in the recipes above. You can click to extends all these sections and see the values for each of these properties.

Simple Template Using $entity

Here is a simple template that uses the $entity variable to display information about an entity.

<?php
 
  echo '<h2>'.$entity->label.'</h2>';
 
  // Display alternative labels only if any are defined
  if(!empty($entity->iron_altlabel))
  {
    echo '<em>(';
   
    foreach(current($entity->iron_altlabel) as $altLabel)
    {
      echo $altLabel['value'].',';
    }
   
    echo '<em>)';
  }
 
  if(!empty($entity->foaf_homepage))
  {
    echo '<a href="'.current($entity->foaf_homepage)[0]['value'].'">Homepage</a>';
  }
 
  if(!empty($entity->foaf_topic))
  {
    echo '<h3>Topics</h3>';
    echo '<ul>';
   
    foreach(current($entity->foaf_topic) as $topic)
    {
      $topicURI = current($entity->foaf_topic)[0]['value'];
     
      // Get the label of the URI that is referenced by this property
      // using the entity_load() function call
      $topicEntity = entity_load('resource_type', array($topicURI));
     
      if(!empty($topicEntity))
      {
        $topicEntity = current($topicEntity);
       
        echo '<li>'.$topicEntity->obj->getPrefLabel().'</li>';
      }
    }
   
    echo '</ul>';
  }
 
?>

Once the template is processed, the output of such a simple template would be:

Osf simple template.PNG

Simple Template Using $subject

Here is a simple template that uses the $subject variable to display information about an entity.

<?php
 
  use \StructuredDynamics\osf\framework\Namespaces;
 
  echo '<h2>'.$subject->getPrefLabel().'</h2>';
 
  // Display alternative labels only if any are defined
  if(count($subject->getAltLabels()) > 0)
  {
    echo '<em>(';
   
    foreach($subject->getAltLabels() as $altLabel)
    {
      echo $altLabel.',';
    }
   
    echo '<em>)';
  }
 
  $homepages = $subject->getDataPropertyValues(Namespaces::$foaf.'homepage');
  if(!empty($homepages))
  {
    echo '<a href="'.$homepages[0]['value'].'">Homepage</a>';
  }
 
  $topics = $subject->getDataPropertyValues(Namespaces::$foaf.'topic');
  if(!empty($topics))
  {
    echo '<h3>Topics</h3>';
    echo '<ul>';
   
    foreach($topics as $topic)
    {
      // Get the label of the URI that is referenced by this property
      // using the entity_load() function call
      $topicEntity = entity_load('resource_type', array($topic['uri']));
     
      if(!empty($topicEntity))
      {
        $topicEntity = current($topicEntity);
       
        echo '<li>'.$topicEntity->obj->getPrefLabel().'</li>';
      }
    }
   
    echo '</ul>';
  }
 
?>

Subject Class API

In this section, we cover the relevant API functions that can be used on a $subject variable.

$subject->getAltLabels()

Get all the alternative labels of this subject.

Returns an array of alternative labels. Returns an empty array is there is none.

$subject->getDataPropertyUri()

Get the URI of all the data properties that describes this subject.

Returns an array of all the URI of the datatype properties that describe this subject.

$subject->getDataPropertyValues($propertyUri)

Get the values of a data property.

Return an array of values for the input property URI.

The returned array follow the following conventions:

  Array(
    Array(
      "value" => "some value",
      "lang" => "language string of the value",
      "type" => "type of the value"
      "reify" => Array(
         "reification-attribute-uri" => Array("value of the reification statement"),
         "more-reification-attribute-uri" => ...
       ),
    ),
    Array(
      ...
    )
  )
$subject->getDescription()

Get the description of this subject

Returns the description. Returns an empty string if there is none.

$subject->getObjectPropertiesUri()

Get the URI of all the object properties that describes this subject

Returns an array of all the URI of the object properties that describe this subject.

$subject->getObjectPropertyValues($propertyUri)

Get the values of an object property.

Return an array of values for the input property URI.

The returned array follow the following conventions:

  Array(
    Array(
      "uri" => "some uri",
      "type" => "optional type of the referenced URI",
      "reify" => Array(
        "reification-attribute-uri" => Array("value of the reification statement"),
        "more-reification-attribute-uri" => ...
      ),
    ),
  )
$subject->getPrefLabel($force = TRUE)

Get the preferred label of this subject

Can force a preferred to be returned. If the $force parameter is TRUE (default) then in the worsecase, getPrefLabel() will return the subject's URI fragment as the pref label if nothing else is defined for it.

Returns the preferred label. Returns an empty string if there is none and if $force is FALSE.

$subject->getPrefURL()

Get the preferred URL of this subject

Returns the preferred URL. Returns an empty string if there is none.

$subject->getSubject()

Get the array description of the Subject object.

$subject->getTypes()

Get all the types of this subject.

Returns an array of types URIs. Returns an empty array if there is none.

$subject->getUri()

Get the URI of the subject

Templates Selection

In OSF for Drupal, everything that are manipulated are [resource] entities. Any entity has at least one or multiple types. These types are defined, and hierarchized in an ontology. It is these types that will determine which template will be used by Drupal to theme and display one of these entities.

Let's use this simple, fictive, classes hierarchy that as been defined in one of the loaded ontology to show how the templates are selected by OSF for Drupal:

  • owl:Thing
    • foaf:Agent
      • foaf:Person
        • foo:PersonByOccupationArtist
        • foo:PersonByOccupationDoctor
      • foaf:Organization

Now let's assume that we have a the following template files that have been created, and that are located in the templates folder of the default theme:

  • /templates/resource_type.tpl.php
  • /templates/resource_type__foaf_person.tpl.php

The [resource] entities template selection engine works as follow:

  1. Check the types of the entity to display
  2. Check if there exist a template for one of its types
    1. If a template exists, then the system will select it to theme the content of that entity. The selection process stops there
  3. If no template exists for one of its types, then the system check if there exist a template for one of the parent class of one of the types of the entity
    1. If a template exists, then the system will select it to theme the content of that entity. The selection process stops there
  4. In the case that no template exists, then the default [generic] resource_type.tpl.php template will be used to theme the content of the entity

Given this heuristic, here are the templates that would be loaded depending on the type of the entities that would be selected for all the types of the simple ontology:

  • owl:Thing [resource_type.tpl.php]
    • foaf:Agent [resource_type.tpl.php]
      • foaf:Person [resource_type__foaf_person.tpl.php]
        • foo:PersonByOccupationArtist [resource_type__foaf_person.tpl.php]
        • foo:PersonByOccupationDoctor [resource_type__foaf_person.tpl.php]
      • foaf:Organization [resource_type.tpl.php]

A Few Templates Examples

First Example: foaf:Organization

Now let's assume that a user is accessing an entity page of type foaf:Organization. If we check the classes hierarchy that is currently present in OSF, and if we check the templates that are currently available in Drupal and we if follow the templates selection heuristic defined above, we will see that the resource_type.tpl.php default generic template will be used because there exists no template for that type, and none of its parent classes does have one neither except owl:Thing.

Second Example: foaf:Person

Now let's assume that a user is accessing an entity page of type foaf:Person. If we check the classes hierarchy that is currently present in OSF, and if we check the templates that are currently available in Drupal and we if follow the templates selection heuristic defined above, we will see that the resource_type__foaf_person.tpl.php template will be used because there exists a template for the foaf:Person class.

Third eExample: foo:PersonByOccupationArtist

Now let's assume that a user is accessing an entity page of type foaf:PersonByOccupationArtist. If we check the classes hierarchy that is currently present in OSF, and if we check the templates that are currently available in Drupal and we if follow the templates selection heuristic defined above, we will see that the resource_type__foaf_person.tpl.php template will be used because there exists a template for a parent class: foaf:Person.

Theming Individual Resource Pages

To theme an individual ResourceType entity, you have to create a template file for the type of entity you want to theme. Then you have to save your template file in one of the following two locations:

  • /modules/osf/modules/osf_entities/
  • [default-theme-folder]/templates/

The name of the template file should follow the following specifications:

  • The name of the file as to be lowercase
  • It should start with the string: resource_type
  • It should be followed by a double underscore string to delimit the type of the resource to theme: __
  • It should be followed by the prefix of the URI of the class followed by an underscore: prefix_
  • It should be followed by the type of the resource: type
  • Finally it should ends with: .tpl.php

Here are a few examples of such template names:

  • resource_type__foaf_person.tpl.php
  • resource_type__owl_thing.tpl.php
  • resource_type__foo_personbyoccupationartist.tpl.php
Note: Every time you add or remove a template, you have to clear the Drupal cache in order to take the modifications into account.


Note 2: You *have to make sure* that the base URI of the class you are templating does have a prefix in the list of namespaces/prefixes.


Theming OSF SearchAPI Results

Screencast Tutorial

0.jpg

To theme an individual ResourceType entity as search results, you have to create a template file for the type of entity you want to theme. Then you have to save your template file in one of the following two locations:

  • /modules/osf/modules/osf_entities/
  • [default-theme-folder]/templates/

The name of the template file should follow the following specifications:

  • The name of the file as to be lowercase
  • It should start with the string: resource_type
  • It should be followed by a double underscore string to delimit the type of the resource to theme: __
  • It should be followed by the prefix of the URI of the class followed by an underscore: prefix_
  • It should be followed by the type of the resource: type
  • It should be followed by: __search
  • Finally it should ends with: .tpl.php

Here are a few examples of such template names:

  • resource_type__foaf_person__search.tpl.php
  • resource_type__owl_thing__search.tpl.php
  • resource_type__foo_personbyoccupationartist__search.tpl.php
Note: Every time you add or remove a template, you have to clear the Drupal cache in order to take the modifications into account.


Note 2: You *have to make sure* that the base URI of the class you are templating does have a prefix in the list of namespaces/prefixes.


Theming OSF SearchProfiles Results

Theming search results that will appears in Search Profiles is exactly the same as theming search results that will appear in normal search results pages. The same templates are used whatever if they are displayed in a search profile or not.

Note: Every time you add or remove a template, you have to clear the Drupal cache in order to take the modifications into account.


Note 2: You *have to make sure* that the base URI of the class you are templating does have a prefix in the list of namespaces/prefixes.


External Drupal Theming Resources