Current Directory: /home/astoriaah/www/components/com_jce/editor/tiny_mce/plugins/microdata
Viewing File: /home/astoriaah/www/components/com_jce/editor/tiny_mce/plugins/microdata/microdata.php
<?php
/**
* @copyright Copyright (c) 2009-2022 Ryan Demmer. All rights reserved
* @license GNU/GPL 2 or later - http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* JCE is free software. This version may have been modified pursuant
* to the GNU General Public License, and as distributed it includes or
* is derivative of works licensed under the GNU General Public License or
* other free or open source software licenses
*/
defined('JPATH_PLATFORM') or die;
// Load class dependencies
// Link Plugin Controller
class WFMicrodataPlugin extends WFEditorPlugin
{
protected static $_url = 'https://schema.org/version/latest/schemaorg-current-https.jsonld';
//protected static $_url = 'https://schema.org/docs/schema_org_rdfa.html';
protected static $_schema = null;
/**
* Constructor activating the default information of the class.
*/
public function __construct()
{
parent::__construct();
$request = WFRequest::getInstance();
$request->setRequest(array($this, 'getSchema'));
$request->setRequest(array($this, 'getTypeList'));
$request->setRequest(array($this, 'getPropertyList'));
}
public function display()
{
parent::display();
$document = WFDocument::getInstance();
$settings = $this->getSettings();
$document->addScriptDeclaration('MicrodataDialog.settings=' . json_encode($settings) . ';');
$tabs = WFTabs::getInstance(array(
'base_path' => WF_EDITOR_PLUGIN,
));
// Add tabs
$tabs->addTab('microdata', 1);
// add link stylesheet
$document->addStyleSheet(array('microdata'), 'plugins');
// add link scripts last
$document->addScript(array('microdata'), 'plugins');
}
public function getSettings($settings = array())
{
return parent::getSettings($settings);
}
public function getPropertyList($type)
{
$schema = $this->getSchema('types');
if (isset($schema->types->$type)) {
return $schema->types->$type;
}
return false;
}
/**
* array_merge_recursive does indeed merge arrays, but it converts values with duplicate
* keys to arrays rather than overwriting the value in the first array with the duplicate
* value in the second array, as array_merge does. I.e., with array_merge_recursive,
* this happens (documented behavior):.
*
* array_merge_recursive(array('key' => 'org value'), array('key' => 'new value'));
* => array('key' => array('org value', 'new value'));
*
* array_merge_recursive_distinct does not change the datatypes of the values in the arrays.
* Matching keys' values in the second array overwrite those in the first array, as is the
* case with array_merge, i.e.:
*
* array_merge_recursive_distinct(array('key' => 'org value'), array('key' => 'new value'));
* => array('key' => array('new value'));
*
* Parameters are passed by reference, though only for performance reasons. They're not
* altered by this function.
*
* @param array $array1
* @param array $array2
*
* @return array
*
* @author Daniel <daniel (at) danielsmedegaardbuus (dot) dk>
* @author Gabriel Sobrinho <gabriel (dot) sobrinho (at) gmail (dot) com>
*/
private static function array_merge_recursive_distinct(array &$array1, array &$array2)
{
$merged = $array1;
foreach ($array2 as $key => &$value) {
if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
$merged[$key] = self::array_merge_recursive_distinct($merged[$key], $value);
} else {
$merged[$key] = $value;
}
}
return $merged;
}
private static function findClassPositionByType($needle, $array)
{
foreach ($array as $pos => $item) {
if ($needle == $item['resource']) {
return $pos;
}
}
return false;
}
private static function extractKey($value)
{
if (is_array($value) && array_key_exists('@value', $value)) {
$value = $value['@value'];
}
// key is in url format, eg: 'https://schema.org/value'
if (strpos($value, '://') !== false) {
$key = parse_url($value, PHP_URL_PATH);
if (!$key) {
return $value;
}
return trim($key, "/");
}
// key is in format 'schema:value'
if (substr($value, 0, 7) === 'schema:') {
return substr($value, 7);
}
return $value;
}
private static function buildListFromJson($nodes)
{
$data = array();
$nodes = isset($nodes['@graph']) ? $nodes['@graph'] : array();
foreach ($nodes as $node) {
$id = self::extractKey($node['@id']);
if ($node['@type'] != 'rdfs:Class') {
continue;
}
if (isset($node['rdfs:subClassOf'])) {
continue;
}
$comment = '';
if (isset($node['rdfs:comment'])) {
$comment = self::extractKey($node['rdfs:comment']);
}
$values = array(
'resource' => $id,
'comment' => $comment,
'domainIncludes' => array(),
'rangeIncludes' => array(),
);
$data[] = $values;
}
$count = count($nodes);
do {
foreach ($nodes as $node) {
if ($node['@type'] != 'rdfs:Class') {
$count--;
continue;
}
if (!isset($node['rdfs:subClassOf'])) {
$count--;
continue;
}
$id = self::extractKey($node['@id']);
if (self::findClassPositionByType($id, $data)) {
$count--;
continue;
}
$comment = '';
if (isset($node['rdfs:comment'])) {
$comment = self::extractKey($node['rdfs:comment']);
}
$values = array(
'resource' => $id,
'comment' => $comment,
'subClassOf' => array(),
'domainIncludes' => array(),
'rangeIncludes' => array(),
);
$items = (array) $node['rdfs:subClassOf'];
foreach ($items as $item) {
if (is_string($item)) {
$values['subClassOf'][] = self::extractKey($item);
} else {
$values['subClassOf'][] = self::extractKey($item['@id']);
}
}
$pos = false;
foreach ($values['subClassOf'] as $cls) {
$pos = self::findClassPositionByType($cls, $data);
if ($pos !== false) {
array_splice($data, $pos + 1, 0, array($values));
}
}
if ($pos) {
$count--;
}
}
} while ($count > 0);
foreach ($nodes as $node) {
if ($node['@type'] != 'rdf:Property') {
continue;
}
$id = self::extractKey($node['@id']);
$comment = '';
if (isset($node['rdfs:comment'])) {
$comment = self::extractKey($node['rdfs:comment']);
}
$entry = array(
'label' => $id,
'comment' => $comment,
);
foreach (['domainIncludes', 'rangeIncludes'] as $prop) {
$key = 'schema:' . $prop;
if (!isset($node[$key])) {
continue;
}
$val = $node[$key];
if (!is_array($val)) {
$subclass = self::extractKey($val['@id']);
array_walk($data, function (&$item) use ($subclass, $prop, $entry) {
if ($item['resource'] == $subclass) {
$item[$prop] = array_merge($item[$prop], array($entry));
}
});
} else {
foreach ($val as $subclass) {
if (!is_string($subclass)) {
$subclass = $subclass['@id'];
}
$subclass = self::extractKey($subclass);
array_walk($data, function (&$item) use ($subclass, $prop, $entry) {
if ($item['resource'] == $subclass) {
$item[$prop] = array_merge($item[$prop], array($entry));
}
});
}
}
}
}
return $data;
}
private static function buildListFromDom($html)
{
$dom = new \DOMDocument();
$dom->loadHTML($html);
$data = array();
// dom is empty, return cache
if (empty($dom)) {
return $data;
}
$nodes = $dom->getElementsByTagName('div');
// no div nodes, return cache
if ($nodes->length === 0) {
return $data;
}
foreach ($nodes as $node) {
$value = self::extractKey($node->getAttribute('resource'));
if ($node->getAttribute('typeof') === 'rdfs:Class') {
$subclass = array();
$comment = '';
foreach ($node->getElementsByTagName('a') as $item) {
if ($item->getAttribute('property') === 'rdfs:subClassOf') {
$subclass[] = self::extractKey($item->nodeValue);
}
}
foreach ($node->getElementsByTagName('span') as $item) {
$prop = $item->getAttribute('property');
if ($prop === 'rdfs:comment') {
$comment = self::extractKey($item->nodeValue);
}
}
$arr = array($value => array('resource' => $value, 'comment' => $comment, 'subClassOf' => $subclass, 'domainIncludes' => array(), 'rangeIncludes' => array()));
$data = self::array_merge_recursive_distinct($data, $arr);
}
if ($node->getAttribute('typeof') === 'rdf:Property') {
$comment = '';
foreach ($node->getElementsByTagName('span') as $item) {
$prop = self::extractKey($item->getAttribute('property'));
if ($prop === 'rdfs:comment') {
$comment = self::extractKey($item->nodeValue);
}
}
foreach ($node->getElementsByTagName('a') as $item) {
$prop = self::extractKey($item->getAttribute('property'));
if ($prop === 'domainIncludes' || $prop === 'rangeIncludes') {
$subclass = self::extractKey($item->nodeValue);
if ($value) {
$entry = array('label' => $value, 'comment' => $comment);
$arr = array($subclass => array($prop => array($entry)));
$data = array_merge_recursive($data, $arr);
}
}
}
}
}
return $data;
}
private function getData($url)
{
$http = JHttpFactory::getHttp();
try {
$response = $http->get($url);
} catch (\RuntimeException $e) {
$response = null;
}
if ($response === null || $response->code !== 200) {
return array('error' => JText::_('Unable to load schema - Invalid response from ' . $url));
}
/*if ($data = gzdecode($response->body)) {
return $data;
}*/
return $response->body;
}
public function getSchema()
{
jimport('joomla.filesystem.file');
// get the url from parameters or default
$url = $this->getParam('schema_url', self::$_url);
if (empty(self::$_schema)) {
// create cache file path
$cache = JPATH_SITE . '/cache/com_jce/' . md5($url) . '.json';
// get refresh time
$ttl = (int) $this->getParam('cache_ttl', 7);
// load data from cache file
if (JFile::exists($cache)) {
$data = file_get_contents($cache);
self::$_schema = json_decode($data);
}
if (empty(self::$_schema) || !$ttl || (JFile::exists($cache) && filemtime($cache) >= strtotime($ttl . ' days ago'))) {
if (pathinfo($url, PATHINFO_EXTENSION) === 'jsonld') {
$jsonld = $this->getData($url);
if (!is_string($jsonld)) {
return self::$_schema;
}
$json = json_decode($jsonld, true);
if (empty($json)) {
return self::$_schema;
}
$data = self::buildListFromJson($json);
} else {
$html = $this->getData($url);
// result should be string, otherwise an error
if (!is_string($html)) {
return $html;
}
// error getting data, return cache
if (empty($html)) {
return self::$_schema;
}
$data = self::buildListFromDom($html);
}
// no new list created, return cache
if (empty($data)) {
return empty(self::$_schema) ? 'Unable to build schema list' : self::$_schema;
}
// set schema and write to cache
self::$_schema = $data;
JFile::write($cache, json_encode($data));
}
}
return self::$_schema;
}
public function getTypeList()
{
$schema = $this->getSchema();
$options = array();
foreach ($schema as $key => $value) {
$options[] = array('key' => $key, 'value' => $value->resource);
}
return $options;
}
}