Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[4.1] [RFC] Use schema.org for metadata #25117

Closed
wants to merge 14 commits into from
78 changes: 72 additions & 6 deletions components/com_contact/View/Contact/HtmlView.php
Expand Up @@ -11,6 +11,7 @@

defined('_JEXEC') or die;

use Joomla\CMS\Application\CMSApplicationInterface;
use Joomla\CMS\Categories\Categories;
use Joomla\CMS\Factory;
use Joomla\CMS\Helper\TagsHelper;
Expand All @@ -20,7 +21,12 @@
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Uri\Uri;
use Joomla\CMS\User\User;
use Joomla\Component\Contact\Site\Helper\Route as ContactHelperRoute;
use Spatie\SchemaOrg\Person;
use Spatie\SchemaOrg\PostalAddress;
use Spatie\SchemaOrg\Schema;

/**
* HTML Contact View class for the Contact component
Expand Down Expand Up @@ -107,12 +113,13 @@ class HtmlView extends BaseHtmlView
*
* @param string $tpl The name of the template file to parse; automatically searches through the template paths.
*
* @return mixed A string if successful, otherwise an Error object.
* @return void
* @throws \Exception
*/
public function display($tpl = null)
{
$app = Factory::getApplication();
$user = Factory::getUser();
$user = $app->getIdentity();
$state = $this->get('State');
$item = $this->get('Item');
$this->form = $this->get('Form');
Expand Down Expand Up @@ -181,7 +188,7 @@ public function display($tpl = null)
$app->enqueueMessage(Text::_('JERROR_ALERTNOAUTHOR'), 'error');
$app->setHeader('status', 403, true);

return false;
return;
}

$options['category_id'] = $item->catid;
Expand Down Expand Up @@ -347,7 +354,7 @@ public function display($tpl = null)
$item->text = $item->misc;
}

$app->triggerEvent('onContentPrepare', array ('com_contact.contact', &$item, &$this->params, $offset));
$app->triggerEvent('onContentPrepare', array('com_contact.contact', &$item, &$this->params, $offset));

// Store the events for later
$item->event = new \stdClass;
Expand All @@ -370,7 +377,7 @@ public function display($tpl = null)
if ($item->params->get('show_user_custom_fields') && $item->user_id && $contactUser = Factory::getUser($item->user_id))
{
$contactUser->text = '';
$app->triggerEvent('onContentPrepare', array ('com_users.user', &$contactUser, &$item->params, 0));
$app->triggerEvent('onContentPrepare', array('com_users.user', &$contactUser, &$item->params, 0));

if (!isset($contactUser->jcfields))
{
Expand Down Expand Up @@ -421,8 +428,9 @@ public function display($tpl = null)
}

$this->_prepareDocument();
$this->addJsonSchema();

return parent::display($tpl);
parent::display($tpl);
}

/**
Expand Down Expand Up @@ -535,4 +543,62 @@ protected function _prepareDocument()
}
}
}

/**
* Prepares the document.
*

This comment was marked as abuse.

* @return void
*/
private function addJsonSchema()
{
// Note we don't display tags here as the keywords property isn't valid for a person
$schema = Schema::person()
->if($this->item->params->get('show_name'), function (Person $schema) {
$schema->name($this->item->name);
})
->if($this->item->image && $this->item->params->get('show_image'), function (Person $schema) {
$schema->image(Uri::root() . $this->item->image);
})
->if($this->item->params->get('show_position'), function (Person $schema) {
$schema->jobTitle($this->item->con_position);
})
->if($this->item->params->get('address_check') > 0, function (Person $schema) {
$schema->address(
Schema::postalAddress()
->if($this->item->address && $this->params->get('show_street_address'), function (PostalAddress $schema) {
$schema->streetAddress($this->item->address);
})
->if($this->item->suburb && $this->params->get('show_suburb'), function (PostalAddress $schema) {
$schema->addressLocality($this->item->suburb);
})
->if($this->item->state && $this->params->get('show_state'), function (PostalAddress $schema) {
$schema->addressRegion($this->item->state);
})
->if($this->item->postcode && $this->params->get('show_postcode'), function (PostalAddress $schema) {
$schema->postalCode($this->item->postcode);
})
->if($this->item->country && $this->params->get('show_country'), function (PostalAddress $schema) {
$schema->addressCountry($this->item->country);
})
);
})
// TODO: Should we expose the raw email like this?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we do in the vcard download

->if($this->item->params->get('show_email') === '1', function (Person $schema) {
$schema->email($this->item->email_raw);
})
->if($this->item->telephone && $this->params->get('show_telephone'), function (Person $schema) {
$schema->telephone($this->item->telephone);
})
->if($this->item->fax && $this->params->get('show_fax'), function (Person $schema) {
$schema->faxNumber($this->item->fax);
})
->if($this->item->mobile && $this->params->get('show_mobile'), function (Person $schema) {
$schema->telephone($this->item->mobile);
})
->if($this->item->webpage && $this->params->get('show_webpage'), function (Person $schema) {
$schema->url($this->item->webpage);
});

$this->document->addScriptDeclaration(json_encode($schema, JDEBUG ? JSON_PRETTY_PRINT : 0), 'application/ld+json');
wilsonge marked this conversation as resolved.
Show resolved Hide resolved
}
}
8 changes: 4 additions & 4 deletions components/com_contact/tmpl/contact/default.php
Expand Up @@ -21,7 +21,7 @@
$tparams = $this->item->params;
?>

<div class="com-contact contact" itemscope itemtype="https://schema.org/Person">
<div class="com-contact contact">
<?php if ($tparams->get('show_page_heading')) : ?>
<h1>
<?php echo $this->escape($tparams->get('page_heading')); ?>
Expand All @@ -34,7 +34,7 @@
<?php if ($this->item->published == 0) : ?>
<span class="badge badge-warning"><?php echo Text::_('JUNPUBLISHED'); ?></span>
<?php endif; ?>
<span class="contact-name" itemprop="name"><?php echo $this->item->name; ?></span>
<span class="contact-name"><?php echo $this->item->name; ?></span>
</h2>
</div>
<?php endif; ?>
Expand Down Expand Up @@ -77,14 +77,14 @@

<?php if ($this->item->image && $tparams->get('show_image')) : ?>
<div class="com-contact__thumbnail thumbnail float-right">
<?php echo HTMLHelper::_('image', $this->item->image, htmlspecialchars($this->item->name, ENT_QUOTES, 'UTF-8'), array('itemprop' => 'image')); ?>
<?php echo HTMLHelper::_('image', $this->item->image, htmlspecialchars($this->item->name, ENT_QUOTES, 'UTF-8')); ?>
</div>
<?php endif; ?>

<?php if ($this->item->con_position && $tparams->get('show_position')) : ?>
<dl class="com-contact__position contact-position dl-horizontal">
<dt><?php echo Text::_('COM_CONTACT_POSITION'); ?>:</dt>
<dd itemprop="jobTitle">
<dd>
<?php echo $this->item->con_position; ?>
</dd>
</dl>
Expand Down
22 changes: 11 additions & 11 deletions components/com_contact/tmpl/contact/default_address.php
Expand Up @@ -16,7 +16,7 @@
* jicon-text, jicon-none, jicon-icon
*/
?>
<dl class="com-contact__address contact-address dl-horizontal" itemprop="address" itemscope itemtype="https://schema.org/PostalAddress">
<dl class="com-contact__address contact-address dl-horizontal">
<?php if (($this->params->get('address_check') > 0) &&
($this->item->address || $this->item->suburb || $this->item->state || $this->item->country || $this->item->postcode)) : ?>
<dt>
Expand All @@ -27,7 +27,7 @@

<?php if ($this->item->address && $this->params->get('show_street_address')) : ?>
<dd>
<span class="contact-street" itemprop="streetAddress">
<span class="contact-street">
<?php echo nl2br($this->item->address); ?>
<br>
</span>
Expand All @@ -36,31 +36,31 @@

<?php if ($this->item->suburb && $this->params->get('show_suburb')) : ?>
<dd>
<span class="contact-suburb" itemprop="addressLocality">
<span class="contact-suburb">
<?php echo $this->item->suburb; ?>
<br>
</span>
</dd>
<?php endif; ?>
<?php if ($this->item->state && $this->params->get('show_state')) : ?>
<dd>
<span class="contact-state" itemprop="addressRegion">
<span class="contact-state">
<?php echo $this->item->state; ?>
<br>
</span>
</dd>
<?php endif; ?>
<?php if ($this->item->postcode && $this->params->get('show_postcode')) : ?>
<dd>
<span class="contact-postcode" itemprop="postalCode">
<span class="contact-postcode">
<?php echo $this->item->postcode; ?>
<br>
</span>
</dd>
<?php endif; ?>
<?php if ($this->item->country && $this->params->get('show_country')) : ?>
<dd>
<span class="contact-country" itemprop="addressCountry">
<span class="contact-country">
<?php echo $this->item->country; ?>
<br>
</span>
Expand All @@ -70,7 +70,7 @@

<?php if ($this->item->email_to && $this->params->get('show_email')) : ?>
<dt>
<span class="<?php echo $this->params->get('marker_class'); ?>" itemprop="email">
<span class="<?php echo $this->params->get('marker_class'); ?>">
<?php echo nl2br($this->params->get('marker_email')); ?>
</span>
</dt>
Expand All @@ -88,7 +88,7 @@
</span>
</dt>
<dd>
<span class="contact-telephone" itemprop="telephone">
<span class="contact-telephone">
<?php echo $this->item->telephone; ?>
</span>
</dd>
Expand All @@ -100,7 +100,7 @@
</span>
</dt>
<dd>
<span class="contact-fax" itemprop="faxNumber">
<span class="contact-fax">
<?php echo $this->item->fax; ?>
</span>
</dd>
Expand All @@ -112,7 +112,7 @@
</span>
</dt>
<dd>
<span class="contact-mobile" itemprop="telephone">
<span class="contact-mobile">
<?php echo $this->item->mobile; ?>
</span>
</dd>
Expand All @@ -124,7 +124,7 @@
</dt>
<dd>
<span class="contact-webpage">
<a href="<?php echo $this->item->webpage; ?>" target="_blank" rel="noopener noreferrer" itemprop="url">
<a href="<?php echo $this->item->webpage; ?>" target="_blank" rel="noopener noreferrer">
<?php echo PunycodeHelper::urlToUTF8($this->item->webpage); ?></a>
</span>
</dd>
Expand Down
70 changes: 68 additions & 2 deletions components/com_content/View/Article/HtmlView.php
Expand Up @@ -11,9 +11,11 @@

defined('_JEXEC') or die;

use Joomla\CMS\Application\CMSApplicationInterface;
use Joomla\CMS\Categories\Categories;
use Joomla\CMS\Factory;
use Joomla\CMS\Helper\TagsHelper;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Associations;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\FileLayout;
Expand All @@ -23,6 +25,9 @@
use Joomla\CMS\Router\Route;
use Joomla\CMS\Uri\Uri;
use Joomla\Component\Content\Site\Helper\AssociationHelper;
use Spatie\SchemaOrg\Article;
use Spatie\SchemaOrg\Person;
use Spatie\SchemaOrg\Schema;

/**
* HTML Article View class for the Content component
Expand Down Expand Up @@ -80,13 +85,16 @@ class HtmlView extends BaseHtmlView
*
* @param string $tpl The name of the template file to parse; automatically searches through the template paths.
*
* @return mixed A string if successful, otherwise an Error object.
* @return void
* @throws \Exception
*/
public function display($tpl = null)
{
if ($this->getLayout() == 'pagebreak')
{
return parent::display($tpl);
parent::display($tpl);

return;
}

$app = Factory::getApplication();
Expand Down Expand Up @@ -255,6 +263,7 @@ public function display($tpl = null)
$this->pageclass_sfx = htmlspecialchars($this->item->params->get('pageclass_sfx'));

$this->_prepareDocument();
$this->addJsonSchema($app);

parent::display($tpl);
}
Expand Down Expand Up @@ -387,4 +396,61 @@ protected function _prepareDocument()
$this->document->setMetaData('robots', 'noindex, nofollow');
}
}

/**
* Prepares the document.
*
* @param CMSApplicationInterface $app The application object
*

This comment was marked as abuse.

* @return void
*/
private function addJsonSchema($app)
{
$images = json_decode($this->item->images);
$articleLanguage = ($this->item->language === '*') ? $app->get('language') : $this->item->language;
$authorised = $app->getIdentity()->getAuthorisedViewLevels();
$keywords = [];

foreach ($this->item->tags->itemTags as $tag)
{
if (in_array($tag->access, $authorised)) {
$keywords[] = $this->escape($tag->title);
}
}

// TODO: This requires a publisher to pass google structured data checker
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like it needs an image as well although the error messages are conflicting

image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I saw that - but I'm not sure the best way of achieving this as its mandated for the image to be above 700px width - which clearly may not be the case in an article :/

$schema = Schema::article()
->articleBody($this->item->text)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicating the entire article text into JSON?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup. that's apparently how it should be

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure about that @wilsonge ? I am looking at some pages from the site of another cms that dont

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If true, I stand by my deleted comment - this is probably unsuitable for content. How does one insert this into article and tie it up to specific pieces of content? With Microdata it's simple by using editor button that inserts the tags around selected content.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://jsonld.com/article/

You can paste your entire post in here, and yes it can get really really long

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That looks counter-productive to me. Would not want to bloat up the page like this.

How about the other question? Microdata is not just for automatically generated content. It can be used inside actual content, e.g. inside articles.

->if($this->item->params->get('show_title'), function (Article $schema) {
$schema->headline($this->escape($this->item->title));
})
->inLanguage($articleLanguage)
->dateCreated(HTMLHelper::_('date', $this->item->created, "Y-m-d"))
->dateModified(HTMLHelper::_('date', $this->item->modified, "Y-m-d"))
->datePublished(HTMLHelper::_('date', $this->item->publish_up, "Y-m-d"))
->if($this->item->params->get('show_author') === '1' && !empty($this->item->author), function (Article $schema) {
$schema->author(
Schema::Person()
->name($this->item->created_by_alias ?: $this->item->author)
->if($this->item->params->get('link_author'), function(Person $schema) {
$schema->url($this->item->contact_link);
})
);
});

if (!empty($keywords))
{
$schema->keywords($keywords);
}

if (!empty($images->image_fulltext))
{
$schema->image(
Schema::imageObject()
->url($images->image_fulltext)
);
}

$this->document->addScriptDeclaration(json_encode($schema, JDEBUG ? JSON_PRETTY_PRINT : 0), 'application/ld+json');
}
}