- md5
- 4782a5aabb3714e0640897ef09e3601a
- sha1
- 05b4e230a8af6b697693e53b29bdda1a7af84112
- sha256
- 7595d8e936618eb189a32e71c6048e1c96f0ae5da3bf4f2d3fc1f421faa21fec
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899
<?php
/**
* Joomla! Content Management System
*
* @copyright Copyright (C) 2005 - 2017 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\CMS\Microdata;
defined('JPATH_PLATFORM') or die;
/**
* Joomla Platform class for interacting with Microdata semantics.
*
* @since 3.2
*/
class Microdata
{
/**
* Array with all available Types and Properties from the http://schema.org vocabulary
*
* @var array
* @since 3.2
*/
protected static $types = null;
/**
* The Type
*
* @var string
* @since 3.2
*/
protected $type = null;
/**
* The Property
*
* @var string
* @since 3.2
*/
protected $property = null;
/**
* The Human content
*
* @var string
* @since 3.2
*/
protected $content = null;
/**
* The Machine content
*
* @var string
* @since 3.2
*/
protected $machineContent = null;
/**
* The Fallback Type
*
* @var string
* @since 3.2
*/
protected $fallbackType = null;
/**
* The Fallback Property
*
* @var string
* @since 3.2
*/
protected $fallbackProperty = null;
/**
* Used for checking if the library output is enabled or disabled
*
* @var boolean
* @since 3.2
*/
protected $enabled = true;
/**
* Initialize the class and setup the default $Type
*
* @param string $type Optional, fallback to 'Thing' Type
* @param boolean $flag Enable or disable the library output
*
* @since 3.2
*/
public function __construct($type = '', $flag = true)
{
if ($this->enabled = (boolean) $flag)
{
// Fallback to 'Thing' Type
if (!$type)
{
$type = 'Thing';
}
$this->setType($type);
}
}
/**
* Load all available Types and Properties from the http://schema.org vocabulary contained in the types.json file
*
* @return void
*
* @since 3.2
*/
protected static function loadTypes()
{
// Load the JSON
if (!static::$types)
{
$path = __DIR__ . '/types.json';
static::$types = json_decode(file_get_contents($path), true);
}
}
/**
* Reset all params
*
* @return void
*
* @since 3.2
*/
protected function resetParams()
{
$this->content = null;
$this->machineContent = null;
$this->property = null;
$this->fallbackProperty = null;
$this->fallbackType = null;
}
/**
* Enable or Disable the library output
*
* @param boolean $flag Enable or disable the library output
*
* @return Microdata Instance of $this
*
* @since 3.2
*/
public function enable($flag = true)
{
$this->enabled = (boolean) $flag;
return $this;
}
/**
* Return 'true' if the library output is enabled
*
* @return boolean
*
* @since 3.2
*/
public function isEnabled()
{
return $this->enabled;
}
/**
* Set a new http://schema.org Type
*
* @param string $type The $Type to be setup
*
* @return Microdata Instance of $this
*
* @since 3.2
*/
public function setType($type)
{
if (!$this->enabled)
{
return $this;
}
// Sanitize the Type
$this->type = static::sanitizeType($type);
// If the given $Type isn't available, fallback to 'Thing' Type
if (!static::isTypeAvailable($this->type))
{
$this->type = 'Thing';
}
return $this;
}
/**
* Return the current $Type name
*
* @return string
*
* @since 3.2
*/
public function getType()
{
return $this->type;
}
/**
* Setup a $Property
*
* @param string $property The Property
*
* @return Microdata Instance of $this
*
* @since 3.2
*/
public function property($property)
{
if (!$this->enabled)
{
return $this;
}
// Sanitize the $Property
$property = static::sanitizeProperty($property);
// Control if the $Property exists in the given $Type and setup it, otherwise leave it 'NULL'
if (static::isPropertyInType($this->type, $property))
{
$this->property = $property;
}
return $this;
}
/**
* Return the current $Property name
*
* @return string
*
* @since 3.2
*/
public function getProperty()
{
return $this->property;
}
/**
* Setup a Human content or content for the Machines
*
* @param string $content The human content or machine content to be used
* @param string $machineContent The machine content
*
* @return Microdata Instance of $this
*
* @since 3.2
*/
public function content($content, $machineContent = null)
{
$this->content = $content;
$this->machineContent = $machineContent;
return $this;
}
/**
* Return the current $content
*
* @return string
*
* @since 3.2
*/
public function getContent()
{
return $this->content;
}
/**
* Return the current $machineContent
*
* @return string
*
* @since 3.3
*/
public function getMachineContent()
{
return $this->machineContent;
}
/**
* Setup a Fallback Type and Property
*
* @param string $type The Fallback Type
* @param string $property The Fallback Property
*
* @return Microdata Instance of $this
*
* @since 3.2
*/
public function fallback($type, $property)
{
if (!$this->enabled)
{
return $this;
}
// Sanitize the $Type
$this->fallbackType = static::sanitizeType($type);
// If the given $Type isn't available, fallback to 'Thing' Type
if (!static::isTypeAvailable($this->fallbackType))
{
$this->fallbackType = 'Thing';
}
// Control if the $Property exist in the given $Type and setup it, otherwise leave it 'NULL'
if (static::isPropertyInType($this->fallbackType, $property))
{
$this->fallbackProperty = $property;
}
else
{
$this->fallbackProperty = null;
}
return $this;
}
/**
* Return the current $fallbackType
*
* @return string
*
* @since 3.2
*/
public function getFallbackType()
{
return $this->fallbackType;
}
/**
* Return the current $fallbackProperty
*
* @return string
*
* @since 3.2
*/
public function getFallbackProperty()
{
return $this->fallbackProperty;
}
/**
* This function handles the display logic.
* It checks if the Type, Property are available, if not check for a Fallback,
* then reset all params for the next use and return the HTML.
*
* @param string $displayType Optional, 'inline', available options ['inline'|'span'|'div'|meta]
* @param boolean $emptyOutput Return an empty string if the library output is disabled and there is a $content value
*
* @return string
*
* @since 3.2
*/
public function display($displayType = '', $emptyOutput = false)
{
// Initialize the HTML to output
$html = ($this->content !== null && !$emptyOutput) ? $this->content : '';
// Control if the library output is enabled, otherwise return the $content or an empty string
if (!$this->enabled)
{
// Reset params
$this->resetParams();
return $html;
}
// If the $property is wrong for the current $Type check if a Fallback is available, otherwise return an empty HTML
if ($this->property)
{
// Process and return the HTML the way the user expects to
if ($displayType)
{
switch ($displayType)
{
case 'span':
$html = static::htmlSpan($html, $this->property);
break;
case 'div':
$html = static::htmlDiv($html, $this->property);
break;
case 'meta':
$html = ($this->machineContent !== null) ? $this->machineContent : $html;
$html = static::htmlMeta($html, $this->property);
break;
default:
// Default $displayType = 'inline'
$html = static::htmlProperty($this->property);
break;
}
}
else
{
/*
* Process and return the HTML in an automatic way,
* with the $Property expected Types and display everything in the right way,
* check if the $Property is 'normal', 'nested' or must be rendered in a metadata tag
*/
switch (static::getExpectedDisplayType($this->type, $this->property))
{
case 'nested':
// Retrieve the expected 'nested' Type of the $Property
$nestedType = static::getExpectedTypes($this->type, $this->property);
$nestedProperty = '';
// If there is a Fallback Type then probably it could be the expectedType
if (in_array($this->fallbackType, $nestedType))
{
$nestedType = $this->fallbackType;
if ($this->fallbackProperty)
{
$nestedProperty = $this->fallbackProperty;
}
}
else
{
$nestedType = $nestedType[0];
}
// Check if a $content is available, otherwise fallback to an 'inline' display type
if ($this->content !== null)
{
if ($nestedProperty)
{
$html = static::htmlSpan(
$this->content,
$nestedProperty
);
}
$html = static::htmlSpan(
$html,
$this->property,
$nestedType,
true
);
}
else
{
$html = static::htmlProperty($this->property) . ' ' . static::htmlScope($nestedType);
if ($nestedProperty)
{
$html .= ' ' . static::htmlProperty($nestedProperty);
}
}
break;
case 'meta':
// Check if a $content is available, otherwise fallback to an 'inline' display type
if ($this->content !== null)
{
$html = ($this->machineContent !== null) ? $this->machineContent : $this->content;
$html = static::htmlMeta($html, $this->property) . $this->content;
}
else
{
$html = static::htmlProperty($this->property);
}
break;
default:
/*
* Default expected display type = 'normal'
* Check if a $content is available,
* otherwise fallback to an 'inline' display type
*/
if ($this->content !== null)
{
$html = static::htmlSpan($this->content, $this->property);
}
else
{
$html = static::htmlProperty($this->property);
}
break;
}
}
}
elseif ($this->fallbackProperty)
{
// Process and return the HTML the way the user expects to
if ($displayType)
{
switch ($displayType)
{
case 'span':
$html = static::htmlSpan($html, $this->fallbackProperty, $this->fallbackType);
break;
case 'div':
$html = static::htmlDiv($html, $this->fallbackProperty, $this->fallbackType);
break;
case 'meta':
$html = ($this->machineContent !== null) ? $this->machineContent : $html;
$html = static::htmlMeta($html, $this->fallbackProperty, $this->fallbackType);
break;
default:
// Default $displayType = 'inline'
$html = static::htmlScope($this->fallbackType) . ' ' . static::htmlProperty($this->fallbackProperty);
break;
}
}
else
{
/*
* Process and return the HTML in an automatic way,
* with the $Property expected Types an display everything in the right way,
* check if the Property is 'nested' or must be rendered in a metadata tag
*/
switch (static::getExpectedDisplayType($this->fallbackType, $this->fallbackProperty))
{
case 'meta':
// Check if a $content is available, otherwise fallback to an 'inline' display Type
if ($this->content !== null)
{
$html = ($this->machineContent !== null) ? $this->machineContent : $this->content;
$html = static::htmlMeta($html, $this->fallbackProperty, $this->fallbackType);
}
else
{
$html = static::htmlScope($this->fallbackType) . ' ' . static::htmlProperty($this->fallbackProperty);
}
break;
default:
/*
* Default expected display type = 'normal'
* Check if a $content is available,
* otherwise fallback to an 'inline' display Type
*/
if ($this->content !== null)
{
$html = static::htmlSpan($this->content, $this->fallbackProperty);
$html = static::htmlSpan($html, '', $this->fallbackType);
}
else
{
$html = static::htmlScope($this->fallbackType) . ' ' . static::htmlProperty($this->fallbackProperty);
}
break;
}
}
}
elseif (!$this->fallbackProperty && $this->fallbackType !== null)
{
$html = static::htmlScope($this->fallbackType);
}
// Reset params
$this->resetParams();
return $html;
}
/**
* Return the HTML of the current Scope
*
* @return string
*
* @since 3.2
*/
public function displayScope()
{
// Control if the library output is enabled, otherwise return the $content or empty string
if (!$this->enabled)
{
return '';
}
return static::htmlScope($this->type);
}
/**
* Return the sanitized $Type
*
* @param string $type The Type to sanitize
*
* @return string
*
* @since 3.2
*/
public static function sanitizeType($type)
{
return ucfirst(trim($type));
}
/**
* Return the sanitized $Property
*
* @param string $property The Property to sanitize
*
* @return string
*
* @since 3.2
*/
public static function sanitizeProperty($property)
{
return lcfirst(trim($property));
}
/**
* Return an array with all available Types and Properties from the http://schema.org vocabulary
*
* @return array
*
* @since 3.2
*/
public static function getTypes()
{
static::loadTypes();
return static::$types;
}
/**
* Return an array with all available Types from the http://schema.org vocabulary
*
* @return array
*
* @since 3.2
*/
public static function getAvailableTypes()
{
static::loadTypes();
return array_keys(static::$types);
}
/**
* Return the expected Types of the given Property
*
* @param string $type The Type to process
* @param string $property The Property to process
*
* @return array
*
* @since 3.2
*/
public static function getExpectedTypes($type, $property)
{
static::loadTypes();
$tmp = static::$types[$type]['properties'];
// Check if the $Property is in the $Type
if (isset($tmp[$property]))
{
return $tmp[$property]['expectedTypes'];
}
// Check if the $Property is inherit
$extendedType = static::$types[$type]['extends'];
// Recursive
if (!empty($extendedType))
{
return static::getExpectedTypes($extendedType, $property);
}
return array();
}
/**
* Return the expected display type: [normal|nested|meta]
* In which way to display the Property:
* normal -> itemprop="name"
* nested -> itemprop="director" itemscope itemtype="https://schema.org/Person"
* meta -> `<meta itemprop="datePublished" content="1991-05-01">`
*
* @param string $type The Type where to find the Property
* @param string $property The Property to process
*
* @return string
*
* @since 3.2
*/
protected static function getExpectedDisplayType($type, $property)
{
$expectedTypes = static::getExpectedTypes($type, $property);
// Retrieve the first expected type
$type = $expectedTypes[0];
// Check if it's a 'meta' display
if ($type === 'Date' || $type === 'DateTime' || $property === 'interactionCount')
{
return 'meta';
}
// Check if it's a 'normal' display
if ($type === 'Text' || $type === 'URL' || $type === 'Boolean' || $type === 'Number')
{
return 'normal';
}
// Otherwise it's a 'nested' display
return 'nested';
}
/**
* Recursive function, control if the given Type has the given Property
*
* @param string $type The Type where to check
* @param string $property The Property to check
*
* @return boolean
*
* @since 3.2
*/
public static function isPropertyInType($type, $property)
{
if (!static::isTypeAvailable($type))
{
return false;
}
// Control if the $Property exists, and return 'true'
if (array_key_exists($property, static::$types[$type]['properties']))
{
return true;
}
// Recursive: Check if the $Property is inherit
$extendedType = static::$types[$type]['extends'];
if (!empty($extendedType))
{
return static::isPropertyInType($extendedType, $property);
}
return false;
}
/**
* Control if the given Type class is available
*
* @param string $type The Type to check
*
* @return boolean
*
* @since 3.2
*/
public static function isTypeAvailable($type)
{
static::loadTypes();
return (array_key_exists($type, static::$types)) ? true : false;
}
/**
* Return Microdata semantics in a `<meta>` tag with content for machines.
*
* @param string $content The machine content to display
* @param string $property The Property
* @param string $scope Optional, the Type scope to display
* @param boolean $invert Optional, default = false, invert the $scope with the $property
*
* @return string
*
* @since 3.2
*/
public static function htmlMeta($content, $property, $scope = '', $invert = false)
{
return static::htmlTag('meta', $content, $property, $scope, $invert);
}
/**
* Return Microdata semantics in a `<span>` tag.
*
* @param string $content The human content
* @param string $property Optional, the human content to display
* @param string $scope Optional, the Type scope to display
* @param boolean $invert Optional, default = false, invert the $scope with the $property
*
* @return string
*
* @since 3.2
*/
public static function htmlSpan($content, $property = '', $scope = '', $invert = false)
{
return static::htmlTag('span', $content, $property, $scope, $invert);
}
/**
* Return Microdata semantics in a `<div>` tag.
*
* @param string $content The human content
* @param string $property Optional, the human content to display
* @param string $scope Optional, the Type scope to display
* @param boolean $invert Optional, default = false, invert the $scope with the $property
*
* @return string
*
* @since 3.2
*/
public static function htmlDiv($content, $property = '', $scope = '', $invert = false)
{
return static::htmlTag('div', $content, $property, $scope, $invert);
}
/**
* Return Microdata semantics in a specified tag.
*
* @param string $tag The HTML tag
* @param string $content The human content
* @param string $property Optional, the human content to display
* @param string $scope Optional, the Type scope to display
* @param boolean $invert Optional, default = false, invert the $scope with the $property
*
* @return string
*
* @since 3.3
*/
public static function htmlTag($tag, $content, $property = '', $scope = '', $invert = false)
{
// Control if the $Property has already the 'itemprop' prefix
if (!empty($property) && stripos($property, 'itemprop') !== 0)
{
$property = static::htmlProperty($property);
}
// Control if the $Scope have already the 'itemscope' prefix
if (!empty($scope) && stripos($scope, 'itemscope') !== 0)
{
$scope = static::htmlScope($scope);
}
// Depending on the case, the $scope must precede the $property, or otherwise
if ($invert)
{
$tmp = implode(' ', array($property, $scope));
}
else
{
$tmp = implode(' ', array($scope, $property));
}
$tmp = trim($tmp);
$tmp = ($tmp) ? ' ' . $tmp : '';
// Control if it is an empty element without a closing tag
if ($tag === 'meta')
{
return "<meta$tmp content='$content'/>";
}
return '<' . $tag . $tmp . '>' . $content . '</' . $tag . '>';
}
/**
* Return the HTML Scope
*
* @param string $scope The Scope to process
*
* @return string
*
* @since 3.2
*/
public static function htmlScope($scope)
{
return "itemscope itemtype='https://schema.org/" . static::sanitizeType($scope) . "'";
}
/**
* Return the HTML Property
*
* @param string $property The Property to process
*
* @return string
*
* @since 3.2
*/
public static function htmlProperty($property)
{
return "itemprop='$property'";
}
}
The same content is in 12 releases, Joomla 3.8.0-beta1 to 3.8.3. See them all, or every version of this file.