Skip to content
Merged

Dev #16

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/scheduled-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ on:

jobs:
scheduled-ci:
uses: builtnorth/.github/.github/workflows/composer-package-ci.yml@main
uses: builtnorth/.github/.github/workflows/composer-package-ci.yml@main
secrets: inherit
1 change: 1 addition & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ on:
jobs:
ci:
uses: builtnorth/.github/.github/workflows/composer-package-ci.yml@main
secrets: inherit
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
}
],
"require": {
"php": ">=8.1"
"php": ">=8.0"
},
"autoload": {
"psr-4": {
Expand Down
5 changes: 3 additions & 2 deletions docs/hooks-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ add_filter('wp_schema_framework_output_enabled', function($enabled) {
Override the detected page context.

**Parameters:**
- `$context` (string) - Detected context ('home', 'singular', 'archive', 'search', 'error')
- `$context` (string) - Detected context ('home', 'singular', 'attachment', 'archive', 'search', '404', 'unknown')

**Usage:**
```php
Expand Down Expand Up @@ -167,7 +167,8 @@ Modify a specific schema piece by ID.

**Usage:**
```php
add_filter('wp_schema_framework_piece_id_#organization', function($piece, $context) {
// Note: '#' is stripped from IDs when building the hook name, so '#organization' becomes 'organization'
add_filter('wp_schema_framework_piece_id_organization', function($piece, $context) {
$piece->set('telephone', '+1234567890');
return $piece;
}, 10, 2);
Expand Down
24 changes: 18 additions & 6 deletions inc/App.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace BuiltNorth\WPSchema;

use BuiltNorth\WPSchema\Contracts\SchemaProviderInterface;
use BuiltNorth\WPSchema\Services\ProviderRegistry;
use BuiltNorth\WPSchema\Services\GraphBuilder;
use BuiltNorth\WPSchema\Services\OutputService;
Expand Down Expand Up @@ -60,12 +61,14 @@ public function init(): void

// Initialize output hooks
$this->output_service->init();


// Mark initialized before firing the action so register_provider() works
// during wp_schema_framework_register_providers callbacks.
$this->initialized = true;

// Allow plugins to register providers
do_action('wp_schema_framework_register_providers', $this);

$this->initialized = true;

// Framework is ready
do_action('wp_schema_framework_ready', $this);

Expand Down Expand Up @@ -120,16 +123,25 @@ private function register_core_providers(): void
public static function register_provider(string $name, string $class_name): bool
{
$instance = self::instance();


// Registry is only available after init(); return false instead of crashing.
if (!$instance->initialized) {
return false;
}

if (!class_exists($class_name)) {
return false;
}


if (!is_a($class_name, SchemaProviderInterface::class, true)) {
return false;
}

try {
$provider = new $class_name();
$instance->registry->register($name, $provider);
return true;
} catch (\Exception $e) {
} catch (\Throwable $e) {
return false;
}
}
Expand Down
2 changes: 1 addition & 1 deletion inc/Graph/SchemaGraph.php
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ public function to_json(): string

return json_encode(
$schema_array,
JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE
JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT
) ?: '';
}

Expand Down
62 changes: 46 additions & 16 deletions inc/Providers/ArticleProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -113,42 +113,72 @@ public function get_pieces(string $context): array
if (!$post) {
return [];
}

// Get schema type
$default_type = $this->get_default_schema_type($post->post_type);
$schema_type = apply_filters('wp_schema_framework_post_type_override', $default_type, $post->ID, $post->post_type, $post);

// Create article piece

$article = new SchemaPiece('#article', $schema_type);

$article
->set('headline', $post->post_title)
->set('name', $post->post_title)
->set('url', get_permalink($post->ID))
->set('datePublished', get_the_date('c', $post->ID))
->set('dateModified', get_the_modified_date('c', $post->ID))
->set('mainEntityOfPage', ['@type' => 'WebPage', '@id' => get_permalink($post->ID)])
->set('inLanguage', get_bloginfo('language'))
->add_reference('author', '#author')
->add_reference('publisher', '#organization');

// Add description from filter or excerpt
->add_reference('publisher', '#organization')
->add_reference('isPartOf', '#website');

// Description from filter or excerpt
$description = apply_filters('wp_schema_framework_post_description', '', $post->ID, $post);
if ($description) {
$article->set('description', $description);
} elseif ($post->post_excerpt) {
$article->set('description', wp_strip_all_tags($post->post_excerpt));
}

// Add featured image
if (has_post_thumbnail($post->ID)) {
$image_url = get_the_post_thumbnail_url($post->ID, 'full');

// Featured image with dimensions
$thumb_id = get_post_thumbnail_id($post->ID);
if ($thumb_id) {
$image_url = wp_get_attachment_image_url($thumb_id, 'full');
if ($image_url) {
$article->set('image', [
'@type' => 'ImageObject',
'url' => $image_url,
]);
$image_data = ['@type' => 'ImageObject', 'url' => $image_url];
$metadata = wp_get_attachment_metadata($thumb_id);
if (!empty($metadata['width'])) {
$image_data['width'] = $metadata['width'];
}
if (!empty($metadata['height'])) {
$image_data['height'] = $metadata['height'];
}
$article->set('image', $image_data);
}
}


// Keywords from post tags
$tags = get_the_tags($post->ID);
if ($tags) {
$article->set('keywords', implode(', ', array_map(fn($tag) => $tag->name, $tags)));
}

// Article section from primary category
$categories = get_the_category($post->ID);
if ($categories) {
$article->set('articleSection', $categories[0]->name);
}

// Word count
$word_count = str_word_count(wp_strip_all_tags($post->post_content));
if ($word_count > 0) {
$article->set('wordCount', $word_count);
}

// Allow filtering — also gives polaris-seo a chance to add image fallback etc.
$data = apply_filters('wp_schema_framework_article_data', $article->to_array(), $post->ID, $post);
$article->from_array($data);

return [$article];
}

Expand Down
13 changes: 6 additions & 7 deletions inc/Providers/MediaProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ class MediaProvider implements SchemaProviderInterface
{
public function can_provide(string $context): bool
{
// Only provide for singular attachment pages
return $context === 'singular' && is_attachment();
return $context === 'attachment';
}

public function get_pieces(string $context): array
Expand Down Expand Up @@ -309,19 +308,19 @@ private function format_duration(int $seconds): string
{
$hours = floor($seconds / 3600);
$minutes = floor(($seconds % 3600) / 60);
$seconds = $seconds % 60;
$secs = $seconds % 60;

$duration = 'PT';
if ($hours > 0) {
$duration .= $hours . 'H';
}
if ($minutes > 0) {
$duration .= $minutes . 'M';
}
if ($seconds > 0) {
$duration .= $seconds . 'S';
if ($secs > 0 || $duration === 'PT') {
$duration .= $secs . 'S';
}

return $duration;
}
}
70 changes: 44 additions & 26 deletions inc/Providers/WebPageProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,47 +53,56 @@ public function get_pieces(string $context): array
->set('name', get_bloginfo('name'))
->set('headline', get_bloginfo('name'))
->set('url', home_url())
->set('inLanguage', get_bloginfo('language'))
->add_reference('publisher', '#organization')
->add_reference('isPartOf', '#website');

// Add description
$description = get_bloginfo('description');
if ($description) {
$webpage->set('description', $description);
}

// If front page is a static page
if (get_option('show_on_front') === 'page') {
$page_id = get_option('page_on_front');
if ($page_id) {
$post = get_post($page_id);
if ($post) {
$webpage->set('headline', $post->post_title);

if ($post->post_excerpt) {
$webpage->set('description', wp_strip_all_tags($post->post_excerpt));
}

$webpage
->set('datePublished', get_the_date('c', $page_id))
->set('dateModified', get_the_modified_date('c', $page_id));

if (has_post_thumbnail($page_id)) {
$image_url = get_the_post_thumbnail_url($page_id, 'full');

$thumb_id = get_post_thumbnail_id($page_id);
if ($thumb_id) {
$image_url = wp_get_attachment_image_url($thumb_id, 'full');
if ($image_url) {
$webpage->set('image', [
'@type' => 'ImageObject',
'url' => $image_url,
]);
$image_data = ['@type' => 'ImageObject', 'url' => $image_url];
$metadata = wp_get_attachment_metadata($thumb_id);
if (!empty($metadata['width'])) {
$image_data['width'] = $metadata['width'];
}
if (!empty($metadata['height'])) {
$image_data['height'] = $metadata['height'];
}
$webpage->set('image', $image_data);
}
}
}
}
}

// Add breadcrumb reference
$webpage->add_reference('breadcrumb', '#breadcrumb');

// Only add breadcrumb reference if a BreadcrumbList will be in the graph
if (apply_filters('wp_schema_framework_has_breadcrumb', false)) {
$webpage->add_reference('breadcrumb', '#breadcrumb');
}

// Allow filtering of homepage data
$data = apply_filters('wp_schema_framework_homepage_data', $webpage->to_array());
$data = apply_filters('wp_schema_framework_webpage_data', $data, 0, null);
Expand Down Expand Up @@ -121,32 +130,41 @@ public function get_pieces(string $context): array
->set('url', get_permalink($post->ID))
->set('datePublished', get_the_date('c', $post->ID))
->set('dateModified', get_the_modified_date('c', $post->ID))
->set('inLanguage', get_bloginfo('language'))
->add_reference('author', '#author')
->add_reference('publisher', '#organization')
->add_reference('isPartOf', '#website');

// Add description from filter or excerpt
$description = apply_filters('wp_schema_framework_post_description', '', $post->ID, $post);
if ($description) {
$webpage->set('description', $description);
} elseif ($post->post_excerpt) {
$webpage->set('description', wp_strip_all_tags($post->post_excerpt));
}

// Add featured image
if (has_post_thumbnail($post->ID)) {
$image_url = get_the_post_thumbnail_url($post->ID, 'full');

// Featured image with dimensions
$thumb_id = get_post_thumbnail_id($post->ID);
if ($thumb_id) {
$image_url = wp_get_attachment_image_url($thumb_id, 'full');
if ($image_url) {
$webpage->set('image', [
'@type' => 'ImageObject',
'url' => $image_url,
]);
$image_data = ['@type' => 'ImageObject', 'url' => $image_url];
$metadata = wp_get_attachment_metadata($thumb_id);
if (!empty($metadata['width'])) {
$image_data['width'] = $metadata['width'];
}
if (!empty($metadata['height'])) {
$image_data['height'] = $metadata['height'];
}
$webpage->set('image', $image_data);
}
}

// Add breadcrumb reference
$webpage->add_reference('breadcrumb', '#breadcrumb');

// Only add breadcrumb reference if a BreadcrumbList will be in the graph
if (apply_filters('wp_schema_framework_has_breadcrumb', false)) {
$webpage->add_reference('breadcrumb', '#breadcrumb');
}

// Allow filtering of webpage data
$data = apply_filters('wp_schema_framework_webpage_data', $webpage->to_array(), $post->ID, $post);
$webpage->from_array($data);
Expand Down
8 changes: 7 additions & 1 deletion inc/Providers/WebsiteProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,18 @@ public function get_pieces(string $context): array
->set('name', get_bloginfo('name'))
->set('url', home_url('/'))
->add_reference('publisher', '#organization');

// Add description if available
$description = get_bloginfo('description');
if ($description) {
$website->set('description', $description);
}

// Language
$language = get_bloginfo('language');
if ($language) {
$website->set('inLanguage', $language);
}

// Add search action on all pages for Google Sitelinks Searchbox
$search_url = home_url('/?s={search_term_string}');
Expand Down
Loading
Loading