Completed
Push — master ( 5db201...585add )
by David
08:20
created

Wordlift_Schema_Service   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 1284
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 5

Importance

Changes 0
Metric Value
dl 0
loc 1284
rs 9.2173
c 0
b 0
f 0
wmc 22
lcom 2
cbo 5

15 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 27 1
A get_instance() 0 4 1
B get_field() 0 19 5
A get_schema() 0 9 2
A get_schema_by_uri() 0 10 3
B get_thing_schema() 0 97 1
B get_creative_work_schema() 0 44 1
A get_event_schema() 0 64 1
B get_organization_schema() 0 125 1
B get_person_schema() 0 102 1
B get_place_schema() 0 91 1
B get_local_business_schema() 0 27 1
B get_recipe_schema() 0 142 1
A get_article_schema() 0 18 1
A get_all_predicates() 0 17 1
1
<?php
2
/**
3
 * Services: WordLift Schema Service.
4
 *
5
 * This file defines the Wordlift_Schema_Service class.
6
 *
7
 * @since      3.1.0
8
 * @package    Wordlift
9
 * @subpackage Wordlift/includes
10
 */
11
12
/**
13
 * Provides constants and methods related to WordLift's schema.
14
 *
15
 * @since      3.1.0
16
 * @package    Wordlift
17
 * @subpackage Wordlift/includes
18
 */
19
class Wordlift_Schema_Service {
20
21
	/**
22
	 * The 'location created' field name.
23
	 *
24
	 * @since 3.5.0
25
	 */
26
	const FIELD_LOCATION_CREATED = 'wl_location_created';
27
28
	/**
29
	 * The 'topic' field name.
30
	 *
31
	 * @since 3.5.0
32
	 */
33
	const FIELD_TOPIC = 'wl_topic';
34
35
	/**
36
	 * The 'author' field name.
37
	 *
38
	 * @since 3.1.0
39
	 */
40
	const FIELD_AUTHOR = 'wl_author';
41
42
	/**
43
	 * The 'same as' field name.
44
	 *
45
	 * @since 3.1.0
46
	 */
47
	const FIELD_SAME_AS = 'entity_same_as';
48
49
	/**
50
	 * The 'date start' field name.
51
	 *
52
	 * @since 3.1.0
53
	 */
54
	const FIELD_DATE_START = 'wl_cal_date_start';
55
56
	/**
57
	 * The 'date end' field name.
58
	 *
59
	 * @since 3.1.0
60
	 */
61
	const FIELD_DATE_END = 'wl_cal_date_end';
62
63
	/**
64
	 * The 'location' field name.
65
	 *
66
	 * @since 3.1.0
67
	 */
68
	const FIELD_LOCATION = 'wl_location';
69
70
	/**
71
	 * The 'founder' field name.
72
	 *
73
	 * @since 3.1.0
74
	 */
75
	const FIELD_FOUNDER = 'wl_founder';
76
77
	/**
78
	 * The 'knows' field name.
79
	 *
80
	 * @since 3.1.0
81
	 */
82
	const FIELD_KNOWS = 'wl_knows';
83
84
	/**
85
	 * The 'birth date' field name.
86
	 *
87
	 * @since 3.1.0
88
	 */
89
	const FIELD_BIRTH_DATE = 'wl_birth_date';
90
91
	/**
92
	 * The 'birth place' field name.
93
	 *
94
	 * @since 3.1.0
95
	 */
96
	const FIELD_BIRTH_PLACE = 'wl_birth_place';
97
98
	/**
99
	 * The 'latitude' field name.
100
	 *
101
	 * @since 3.1.0
102
	 */
103
	const FIELD_GEO_LATITUDE = 'wl_geo_latitude';
104
105
	/**
106
	 * The 'longitude' field name.
107
	 *
108
	 * @since 3.1.0
109
	 */
110
	const FIELD_GEO_LONGITUDE = 'wl_geo_longitude';
111
112
	/**
113
	 * The 'streetAddress' field name.
114
	 *
115
	 * @since 3.1.0
116
	 */
117
	const FIELD_ADDRESS = 'wl_address';
118
119
	/**
120
	 * The 'postOfficeBoxNumber' field name.
121
	 *
122
	 * @since 3.3.0
123
	 */
124
	const FIELD_ADDRESS_PO_BOX = 'wl_address_post_office_box';
125
126
	/**
127
	 * The 'postalCode' field name.
128
	 *
129
	 * @since 3.3.0
130
	 */
131
	const FIELD_ADDRESS_POSTAL_CODE = 'wl_address_postal_code';
132
133
	/**
134
	 * The 'addressLocality' field name.
135
	 *
136
	 * @since 3.3.0
137
	 */
138
	const FIELD_ADDRESS_LOCALITY = 'wl_address_locality';
139
	/**
140
	 * The 'addressRegion' field name.
141
	 *
142
	 * @since 3.3.0
143
	 */
144
	const FIELD_ADDRESS_REGION = 'wl_address_region';
145
146
	/**
147
	 * The 'addressCountry' field name.
148
	 *
149
	 * @since 3.3.0
150
	 */
151
	const FIELD_ADDRESS_COUNTRY = 'wl_address_country';
152
153
	/**
154
	 * The 'entity type' field name.
155
	 *
156
	 * @since 3.1.0
157
	 */
158
	const FIELD_ENTITY_TYPE = 'wl_entity_type_uri';
159
160
	/**
161
	 * The 'email' field name.
162
	 *
163
	 * @since 3.2.0
164
	 */
165
	const FIELD_EMAIL = 'wl_email';
166
167
	/**
168
	 * The 'affiliation' field name.
169
	 *
170
	 * @since 3.2.0
171
	 */
172
	const FIELD_AFFILIATION = 'wl_affiliation';
173
174
	/**
175
	 * The 'telephone' field name.
176
	 *
177
	 * @since 3.8.0
178
	 */
179
	const FIELD_TELEPHONE = 'wl_schema_telephone';
180
181
	/**
182
	 * The 'legalName' field name.
183
	 *
184
	 * @since 3.12.0
185
	 */
186
	const FIELD_LEGAL_NAME = 'wl_schema_legal_name';
187
188
	/**
189
	 * The 'recipeCuisine' field name.
190
	 *
191
	 * @since 3.14.0
192
	 */
193
	const FIELD_RECIPE_CUISINE = 'wl_schema_recipe_cuisine';
194
195
	/**
196
	 * The 'recipeIngredient' field name.
197
	 *
198
	 * @since 3.14.0
199
	 */
200
	const FIELD_RECIPE_INGREDIENT = 'wl_schema_recipe_ingredient';
201
202
	/**
203
	 * The 'calories' field name.
204
	 *
205
	 * @since 3.14.0
206
	 */
207
	const FIELD_NUTRITION_INFO_CALORIES = 'wl_schema_nutrition_information_calories';
208
209
	/**
210
	 * The 'recipeInstructions' field name.
211
	 *
212
	 * @since 3.14.0
213
	 */
214
	const FIELD_RECIPE_INSTRUCTIONS = 'wl_schema_recipe_instructions';
215
216
	/**
217
	 * The 'recipeYield' field name.
218
	 *
219
	 * @since 3.14.0
220
	 */
221
	const FIELD_RECIPE_YIELD = 'wl_schema_recipe_yield';
222
223
	/**
224
	 * The 'prepTime' field name.
225
	 *
226
	 * @since 3.14.0
227
	 */
228
	const FIELD_PREP_TIME = 'wl_schema_prep_time';
229
230
	/**
231
	 * The 'cookTime' field name.
232
	 *
233
	 * @since 3.14.0
234
	 */
235
	const FIELD_COOK_TIME = 'wl_schema_cook_time';
236
237
	/**
238
	 * The 'totalTime' field name.
239
	 *
240
	 * @since 3.14.0
241
	 */
242
	const FIELD_TOTAL_TIME = 'wl_schema_total_time';
243
244
	/**
245
	 * The 'URI' data type name.
246
	 *
247
	 * @since 3.1.0
248
	 */
249
	const DATA_TYPE_URI = 'uri';
250
251
	/**
252
	 * The 'date' data type name.
253
	 *
254
	 * @since 3.1.0
255
	 */
256
	const DATA_TYPE_DATE = 'date';
257
258
	/**
259
	 * The 'dateTime' data type name.
260
	 *
261
	 * @since 3.15.0
262
	 */
263
	const DATA_TYPE_DATE_TIME = 'dateTime';
264
265
	/**
266
	 * The 'time' data type name.
267
	 *
268
	 * @since 3.14.0
269
	 */
270
	const DATA_TYPE_DURATION = 'duration';
271
272
	/**
273
	 * The 'double' data type name.
274
	 *
275
	 * @since 3.1.0
276
	 */
277
	const DATA_TYPE_DOUBLE = 'double';
278
279
	/**
280
	 * The 'string' data type name.
281
	 *
282
	 * @since 3.1.0
283
	 */
284
	const DATA_TYPE_STRING = 'string';
285
286
	/**
287
	 * The multiline text data type name.
288
	 *
289
	 * @since 3.14.0
290
	 */
291
	const DATA_TYPE_MULTILINE = 'multiline';
292
293
	/**
294
	 * The 'integer' data type name.
295
	 *
296
	 * @since 3.1.0
297
	 */
298
	const DATA_TYPE_INTEGER = 'int';
299
300
	/**
301
	 * The 'boolean' data type name.
302
	 *
303
	 * @since 3.1.0
304
	 */
305
	const DATA_TYPE_BOOLEAN = 'bool';
306
307
	/**
308
	 * The schema.org Event type URI.
309
	 *
310
	 * @since 3.1.0
311
	 */
312
	const SCHEMA_EVENT_TYPE = 'http://schema.org/Event';
313
314
	/**
315
	 * The Schema service singleton instance.
316
	 *
317
	 * @since  3.1.0
318
	 * @access private
319
	 * @var \Wordlift_Schema_Service $instance The Schema service singleton instance.
320
	 */
321
	private static $instance;
322
323
	/**
324
	 * WordLift's schema.
325
	 *
326
	 * @since  3.1.0
327
	 * @access private
328
	 * @var array $schema WordLift's schema.
329
	 */
330
	private $schema;
331
332
	/**
333
	 * The Log service.
334
	 *
335
	 * @since  3.1.0
336
	 * @access private
337
	 * @var \Wordlift_Log_Service $log The Log service.
338
	 */
339
	private $log;
340
341
	/**
342
	 * The {@link Wordlift_Post_Property_Storage_Factory} instance.
343
	 *
344
	 * @since  3.15.0
345
	 * @access private
346
	 * @var \Wordlift_Storage_Factory $storage_factory The {@link Wordlift_Post_Property_Storage_Factory} instance.
347
	 */
348
	private $storage_factory;
349
350
	/**
351
	 * The {@link Wordlift_Sparql_Tuple_Rendition_Factory} instance.
352
	 *
353
	 * @since  3.15.0
354
	 * @access private
355
	 * @var \Wordlift_Sparql_Tuple_Rendition_Factory $rendition_factory The {@link Wordlift_Sparql_Tuple_Rendition_Factory} instance.
356
	 */
357
	private $rendition_factory;
358
359
	/**
360
	 * The {@link Wordlift_Configuration_Service} instance.
361
	 *
362
	 * @since  3.15.0
363
	 * @access private
364
	 * @var \Wordlift_Configuration_Service $configuration_service The {@link Wordlift_Configuration_Service} instance.
365
	 */
366
	private $configuration_service;
367
368
	/**
369
	 * The web site configured language code.
370
	 *
371
	 * @since  3.15.0
372
	 * @access private
373
	 * @var string $language_code The web site configured language code.
374
	 */
375
	private $language_code;
376
377
	/**
378
	 * Wordlift_Schema_Service constructor.
379
	 *
380
	 * @since 3.1.0
381
	 *
382
	 * @param \Wordlift_Storage_Factory                $storage_factory       The {@link Wordlift_Post_Property_Storage_Factory} instance.
383
	 * @param \Wordlift_Sparql_Tuple_Rendition_Factory $rendition_factory     The {@link Wordlift_Sparql_Tuple_Rendition_Factory} instance.
384
	 * @param \Wordlift_Configuration_Service          $configuration_service The {@link Wordlift_Configuration_Service} instance.
385
	 */
386
	public function __construct( $storage_factory, $rendition_factory, $configuration_service ) {
387
388
		$this->log = Wordlift_Log_Service::get_logger( 'Wordlift_Schema_Service' );
389
390
		$this->storage_factory       = $storage_factory;
391
		$this->rendition_factory     = $rendition_factory;
392
		$this->configuration_service = $configuration_service;
393
		$this->language_code         = $this->configuration_service->get_language_code();
394
395
		// Set the taxonomy data.
396
		// Note: parent types must be defined before child types.
397
		$this->schema = array(
398
			'article'       => $this->get_article_schema(),
399
			'thing'         => $this->get_thing_schema(),
400
			'creative-work' => $this->get_creative_work_schema(),
401
			'event'         => $this->get_event_schema(),
402
			'organization'  => $this->get_organization_schema(),
403
			'person'        => $this->get_person_schema(),
404
			'place'         => $this->get_place_schema(),
405
			'localbusiness' => $this->get_local_business_schema(),
406
			'recipe'        => $this->get_recipe_schema(),
407
		);
408
409
		// Create a singleton instance of the Schema service, useful to provide static functions to global functions.
410
		self::$instance = $this;
411
412
	}
413
414
	/**
415
	 * Get a reference to the Schema service.
416
	 *
417
	 * @since 3.1.0
418
	 *
419
	 * @return Wordlift_Schema_Service A reference to the Schema service.
420
	 */
421
	public static function get_instance() {
422
423
		return self::$instance;
424
	}
425
426
	/**
427
	 * Get the properties for a field with the specified key. The key is used as
428
	 * meta key when the field's value is stored in WordPress meta data table.
429
	 *
430
	 * @since 3.6.0
431
	 *
432
	 * @param string $key The field's key.
433
	 *
434
	 * @return null|array An array of field's properties or null if the field is not found.
435
	 */
436
	public function get_field( $key ) {
437
438
		// Parse each schema's fields until we find the one we're looking for, then
439
		// return its properties.
440
		foreach ( $this->schema as $_ => $schema ) {
441
442
			if ( ! isset( $schema['custom_fields'] ) ) {
443
				break;
444
			}
445
446
			foreach ( $schema['custom_fields'] as $field => $props ) {
447
				if ( $key === $field ) {
448
					return $props;
449
				}
450
			}
451
		}
452
453
		return null;
454
	}
455
456
	/**
457
	 * Get the WordLift's schema.
458
	 *
459
	 * @param string $name The schema name.
460
	 *
461
	 * @return array|null An array with the schema configuration or NULL if the schema is not found.
462
	 *
463
	 * @since 3.1.0
464
	 */
465
	public function get_schema( $name ) {
466
		// Check if the schema exists and, if not, return NULL.
467
		if ( ! isset( $this->schema[ $name ] ) ) {
468
			return null;
469
		}
470
471
		// Return the requested schema.
472
		return $this->schema[ $name ];
473
	}
474
475
	/**
476
	 * Get the WordLift's schema trough schema type uri.
477
	 *
478
	 * @param string $uri The schema uri.
479
	 *
480
	 * @return array|null An array with the schema configuration or NULL if the schema is not found.
481
	 *
482
	 * @since 3.3.0
483
	 */
484
	public function get_schema_by_uri( $uri ) {
485
486
		foreach ( $this->schema as $name => $schema ) {
487
			if ( $schema['uri'] === $uri ) {
488
				return $schema;
489
			}
490
		}
491
492
		return null;
493
	}
494
495
	/**
496
	 * Get the 'thing' schema.
497
	 *
498
	 * @return array An array with the schema configuration.
499
	 *
500
	 * @since 3.1.0
501
	 */
502
	private function get_thing_schema() {
503
504
		return array(
505
			'css_class'     => 'wl-thing',
506
			'uri'           => 'http://schema.org/Thing',
507
			'same_as'       => array( '*' ),
508
			// set as default.
509
			'custom_fields' => array(
510
				self::FIELD_SAME_AS                            => array(
511
					'predicate'   => 'http://schema.org/sameAs',
512
					'type'        => self::DATA_TYPE_URI,
513
					'export_type' => 'http://schema.org/Thing',
514
					'constraints' => array(
515
						'cardinality' => INF,
516
					),
517
					// We need a custom metabox.
518
					'input_field' => 'sameas',
519
				),
520
				// Add the schema:url property.
521
				Wordlift_Schema_Url_Property_Service::META_KEY => Wordlift_Schema_Url_Property_Service::get_instance()
0 ignored issues
show
Deprecated Code introduced by
The method Wordlift_Property_Service::get_compat_definition() has been deprecated.

This method has been deprecated.

Loading history...
522
																									  ->get_compat_definition(),
523
			),
524
			// {{sameAs}} not present in the microdata template,
525
			// because it is treated separately in *wl_content_embed_item_microdata*
526
			'templates'     => array(
527
				'subtitle' => '{{id}}',
528
			),
529
			'linked_data'   => array(
530
				// ### Title to rdfs:label.
531
				$this->rendition_factory->create(
532
					$this->storage_factory->post_title(),
533
					Wordlift_Query_Builder::RDFS_LABEL_URI,
534
					null,
535
					$this->language_code
536
				),
537
				// ### Title to dct:title.
538
				$this->rendition_factory->create(
539
					$this->storage_factory->post_title(),
540
					'http://purl.org/dc/terms/title',
541
					null,
542
					$this->language_code
543
				),
544
				// ### Alternative title to rdfs:label.
545
				$this->rendition_factory->create(
546
					$this->storage_factory->post_meta( Wordlift_Entity_Service::ALTERNATIVE_LABEL_META_KEY ),
547
					Wordlift_Query_Builder::RDFS_LABEL_URI,
548
					null,
549
					$this->language_code
550
				),
551
				// ### Alternative title to dct:title.
552
				$this->rendition_factory->create(
553
					$this->storage_factory->post_meta( Wordlift_Entity_Service::ALTERNATIVE_LABEL_META_KEY ),
554
					'http://purl.org/dc/terms/title',
555
					null,
556
					$this->language_code
557
				),
558
				// ### schema:url.
559
				$this->rendition_factory->create(
560
					$this->storage_factory->url_property(),
561
					Wordlift_Query_Builder::SCHEMA_URL_URI,
562
					self::DATA_TYPE_URI
563
				),
564
				// ### schema:description.
565
				$this->rendition_factory->create(
566
					$this->storage_factory->post_description_no_tags_no_shortcodes(),
567
					'http://schema.org/description',
568
					null,
569
					$this->language_code
570
				),
571
				// ### owl:sameAs.
572
				$this->rendition_factory->create(
573
					$this->storage_factory->post_meta( self::FIELD_SAME_AS ),
574
					'http://www.w3.org/2002/07/owl#sameAs',
575
					self::DATA_TYPE_URI
576
				),
577
				// ### rdf:type.
578
				$this->rendition_factory->create(
579
					$this->storage_factory->schema_class( $this ),
580
					Wordlift_Query_Builder::RDFS_TYPE_URI,
581
					self::DATA_TYPE_URI
582
				),
583
				// ### schema:image.
584
				$this->rendition_factory->create(
585
					$this->storage_factory->post_images(),
586
					Wordlift_Query_Builder::SCHEMA_IMAGE_URI,
587
					self::DATA_TYPE_URI
588
				),
589
				// ### dct:relation.
590
				$this->rendition_factory->create(
591
					$this->storage_factory->relations(),
592
					Wordlift_Query_Builder::DCTERMS_RELATION_URI,
593
					self::DATA_TYPE_URI
594
				),
595
			),
596
		);
597
598
	}
599
600
	/**
601
	 * Get the 'creative work' schema.
602
	 *
603
	 * @return array An array with the schema configuration.
604
	 *
605
	 * @since 3.1.0
606
	 */
607
	private function get_creative_work_schema() {
608
609
		$schema = array(
610
			'label'         => 'CreativeWork',
611
			'description'   => 'A creative work (or a Music Album).',
612
			'parents'       => array( 'thing' ),
613
			// Give term slug as parent.
614
			'css_class'     => 'wl-creative-work',
615
			'uri'           => 'http://schema.org/CreativeWork',
616
			'same_as'       => array(
617
				'http://schema.org/MusicAlbum',
618
				'http://schema.org/Product',
619
			),
620
			'custom_fields' => array(
621
				self::FIELD_AUTHOR => array(
622
					'predicate'   => 'http://schema.org/author',
623
					'type'        => self::DATA_TYPE_URI,
624
					'export_type' => 'http://schema.org/Person',
625
					'constraints' => array(
626
						'uri_type'    => array( 'Person', 'Organization' ),
627
						'cardinality' => INF,
628
					),
629
				),
630
			),
631
			'linked_data'   => array(
632
				// ### schema:author.
633
				$this->rendition_factory->create(
634
					$this->storage_factory->author_uri(),
635
					Wordlift_Query_Builder::SCHEMA_AUTHOR_URI,
636
					self::DATA_TYPE_URI
637
				),
638
			),
639
			'templates'     => array(
640
				'subtitle' => '{{id}}',
641
			),
642
		);
643
644
		// Merge the custom fields with those provided by the thing schema.
645
		$parent_schema           = $this->get_thing_schema();
646
		$schema['custom_fields'] = array_merge( $schema['custom_fields'], $parent_schema['custom_fields'] );
647
		$schema['linked_data']   = array_merge( $schema['linked_data'], $parent_schema['linked_data'] );
648
649
		return $schema;
650
	}
651
652
	/**
653
	 * Get the 'event' schema.
654
	 *
655
	 * @return array An array with the schema configuration.
656
	 *
657
	 * @since 3.1.0
658
	 */
659
	private function get_event_schema() {
660
661
		$schema = array(
662
			'label'         => 'Event',
663
			'description'   => 'An event . ',
664
			'parents'       => array( 'thing' ),
665
			'css_class'     => 'wl-event',
666
			'uri'           => self::SCHEMA_EVENT_TYPE,
667
			'same_as'       => array( 'http://dbpedia.org/ontology/Event' ),
668
			'custom_fields' => array(
669
				self::FIELD_DATE_START => array(
670
					'predicate'   => 'http://schema.org/startDate',
671
					'type'        => self::DATA_TYPE_DATE,
672
					'export_type' => 'xsd:dateTime',
673
					'constraints' => '',
674
				),
675
				self::FIELD_DATE_END   => array(
676
					'predicate'   => 'http://schema.org/endDate',
677
					'type'        => self::DATA_TYPE_DATE,
678
					'export_type' => 'xsd:dateTime',
679
					'constraints' => '',
680
				),
681
				self::FIELD_LOCATION   => array(
682
					'predicate'   => 'http://schema.org/location',
683
					'type'        => self::DATA_TYPE_URI,
684
					'export_type' => 'http://schema.org/PostalAddress',
685
					'constraints' => array(
686
						'uri_type'    => array( 'Place', 'LocalBusiness' ),
687
						'cardinality' => INF,
688
					),
689
				),
690
			),
691
			'linked_data'   => array(
692
				// ### schema:startDate.
693
				$this->rendition_factory->create(
694
					$this->storage_factory->post_meta( self::FIELD_DATE_END ),
695
					'http://schema.org/startDate',
696
					self::DATA_TYPE_DATE_TIME
697
				),
698
				// ### schema:endDate.
699
				$this->rendition_factory->create(
700
					$this->storage_factory->post_meta( self::FIELD_DATE_END ),
701
					'http://schema.org/endDate',
702
					self::DATA_TYPE_DATE_TIME
703
				),
704
				// ### schema:location.
705
				$this->rendition_factory->create(
706
					$this->storage_factory->post_meta_to_uri( self::FIELD_LOCATION ),
707
					'http://schema.org/location',
708
					self::DATA_TYPE_URI
709
				),
710
			),
711
			'templates'     => array(
712
				'subtitle' => '{{id}}',
713
			),
714
		);
715
716
		// Merge the custom fields with those provided by the thing schema.
717
		$parent_schema           = $this->get_thing_schema();
718
		$schema['custom_fields'] = array_merge( $schema['custom_fields'], $parent_schema['custom_fields'] );
719
		$schema['linked_data']   = array_merge( $schema['linked_data'], $parent_schema['linked_data'] );
720
721
		return $schema;
722
	}
723
724
	/**
725
	 * Get the 'organization' schema.
726
	 *
727
	 * @return array An array with the schema configuration.
728
	 *
729
	 * @since 3.1.0
730
	 */
731
	private function get_organization_schema() {
732
733
		$schema = array(
734
			'label'         => 'Organization',
735
			'description'   => 'An organization, including a government or a newspaper.',
736
			'parents'       => array( 'thing' ),
737
			'css_class'     => 'wl-organization',
738
			'uri'           => 'http://schema.org/Organization',
739
			'same_as'       => array(
740
				'http://rdf.freebase.com/ns/organization.organization',
741
				'http://rdf.freebase.com/ns/government.government',
742
				'http://schema.org/Newspaper',
743
			),
744
			'custom_fields' => array(
745
				self::FIELD_LEGAL_NAME          => array(
746
					'predicate'   => 'http://schema.org/legalName',
747
					'type'        => self::DATA_TYPE_STRING,
748
					'export_type' => 'xsd:string',
749
					'constraints' => '',
750
				),
751
				self::FIELD_FOUNDER             => array(
752
					'predicate'   => 'http://schema.org/founder',
753
					'type'        => self::DATA_TYPE_URI,
754
					'export_type' => 'http://schema.org/Person',
755
					'constraints' => array(
756
						'uri_type'    => 'Person',
757
						'cardinality' => INF,
758
					),
759
				),
760
				self::FIELD_ADDRESS             => array(
761
					'predicate'   => 'http://schema.org/streetAddress',
762
					'type'        => self::DATA_TYPE_STRING,
763
					'export_type' => 'xsd:string',
764
					'constraints' => '',
765
					// To build custom metabox.
766
					'input_field' => 'address',
767
				),
768
				self::FIELD_ADDRESS_PO_BOX      => array(
769
					'predicate'   => 'http://schema.org/postOfficeBoxNumber',
770
					'type'        => self::DATA_TYPE_STRING,
771
					'export_type' => 'xsd:string',
772
					'constraints' => '',
773
					// To build custom metabox.
774
					'input_field' => 'address',
775
				),
776
				self::FIELD_ADDRESS_POSTAL_CODE => array(
777
					'predicate'   => 'http://schema.org/postalCode',
778
					'type'        => self::DATA_TYPE_STRING,
779
					'export_type' => 'xsd:string',
780
					'constraints' => '',
781
					// To build custom metabox.
782
					'input_field' => 'address',
783
				),
784
				self::FIELD_ADDRESS_LOCALITY    => array(
785
					'predicate'   => 'http://schema.org/addressLocality',
786
					'type'        => self::DATA_TYPE_STRING,
787
					'export_type' => 'xsd:string',
788
					'constraints' => '',
789
					// To build custom metabox.
790
					'input_field' => 'address',
791
				),
792
				self::FIELD_ADDRESS_REGION      => array(
793
					'predicate'   => 'http://schema.org/addressRegion',
794
					'type'        => self::DATA_TYPE_STRING,
795
					'export_type' => 'xsd:string',
796
					'constraints' => '',
797
					// To build custom metabox.
798
					'input_field' => 'address',
799
				),
800
				self::FIELD_ADDRESS_COUNTRY     => array(
801
					'predicate'   => 'http://schema.org/addressCountry',
802
					'type'        => self::DATA_TYPE_STRING,
803
					'export_type' => 'xsd:string',
804
					'constraints' => '',
805
					// To build custom metabox.
806
					'input_field' => 'address',
807
				),
808
				self::FIELD_EMAIL               => array(
809
					'predicate'   => 'http://schema.org/email',
810
					'type'        => self::DATA_TYPE_STRING,
811
					'export_type' => 'xsd:string',
812
					'constraints' => '',
813
				),
814
				self::FIELD_TELEPHONE           => array(
815
					'predicate'   => 'http://schema.org/telephone',
816
					'type'        => self::DATA_TYPE_STRING,
817
					'export_type' => 'xsd:string',
818
					'constraints' => '',
819
				),
820
			),
821
			'linked_data'   => array(
822
				// ### schema:legalName.
823
				$this->rendition_factory->create(
824
					$this->storage_factory->post_meta( self::FIELD_LEGAL_NAME ),
825
					'http://schema.org/legalName'
826
				),
827
				// ### schema:founder.
828
				$this->rendition_factory->create(
829
					$this->storage_factory->post_meta_to_uri( self::FIELD_FOUNDER ),
830
					'http://schema.org/founder',
831
					self::DATA_TYPE_URI
832
				),
833
				// ### schema:email.
834
				$this->rendition_factory->create(
835
					$this->storage_factory->post_meta( self::FIELD_EMAIL ),
836
					'http://schema.org/email'
837
				),
838
				// ### schema:telephone.
839
				$this->rendition_factory->create(
840
					$this->storage_factory->post_meta( self::FIELD_TELEPHONE ),
841
					'http://schema.org/telephone'
842
				),
843
			),
844
			'templates'     => array(
845
				'subtitle' => '{{id}}',
846
			),
847
		);
848
849
		// Merge the custom fields with those provided by the thing schema.
850
		$parent_schema           = $this->get_thing_schema();
851
		$schema['custom_fields'] = array_merge( $schema['custom_fields'], $parent_schema['custom_fields'] );
852
		$schema['linked_data']   = array_merge( $schema['linked_data'], $parent_schema['linked_data'] );
853
854
		return $schema;
855
	}
856
857
	/**
858
	 * Get the 'person' schema.
859
	 *
860
	 * @return array An array with the schema configuration.
861
	 *
862
	 * @since 3.1.0
863
	 */
864
	private function get_person_schema() {
865
866
		$schema = array(
867
			'label'         => 'Person',
868
			'description'   => 'A person (or a music artist).',
869
			'parents'       => array( 'thing' ),
870
			'css_class'     => 'wl-person',
871
			'uri'           => 'http://schema.org/Person',
872
			'same_as'       => array(
873
				'http://rdf.freebase.com/ns/people.person',
874
				'http://rdf.freebase.com/ns/music.artist',
875
				'http://dbpedia.org/class/yago/LivingPeople',
876
			),
877
			'custom_fields' => array(
878
				self::FIELD_KNOWS       => array(
879
					'predicate'   => 'http://schema.org/knows',
880
					'type'        => self::DATA_TYPE_URI,
881
					'export_type' => 'http://schema.org/Person',
882
					'constraints' => array(
883
						'uri_type'    => 'Person',
884
						'cardinality' => INF,
885
					),
886
				),
887
				self::FIELD_BIRTH_DATE  => array(
888
					'predicate'   => 'http://schema.org/birthDate',
889
					'type'        => self::DATA_TYPE_DATE,
890
					'export_type' => 'xsd:date',
891
					'constraints' => '',
892
				),
893
				self::FIELD_BIRTH_PLACE => array(
894
					'predicate'   => 'http://schema.org/birthPlace',
895
					'type'        => self::DATA_TYPE_URI,
896
					'export_type' => 'http://schema.org/Place',
897
					'constraints' => array(
898
						'uri_type' => 'Place',
899
					),
900
				),
901
				self::FIELD_AFFILIATION => array(
902
					'predicate'   => 'http://schema.org/affiliation',
903
					'type'        => self::DATA_TYPE_URI,
904
					'export_type' => 'http://schema.org/Organization',
905
					'constraints' => array(
906
						'uri_type'    => array(
907
							'Organization',
908
							'LocalBusiness',
909
						),
910
						'cardinality' => INF,
911
					),
912
				),
913
				self::FIELD_EMAIL       => array(
914
					'predicate'   => 'http://schema.org/email',
915
					'type'        => self::DATA_TYPE_STRING,
916
					'export_type' => 'xsd:string',
917
					'constraints' => array(
918
						'cardinality' => INF,
919
					),
920
				),
921
			),
922
			'linked_data'   => array(
923
				// ### schema:knows.
924
				$this->rendition_factory->create(
925
					$this->storage_factory->post_meta_to_uri( self::FIELD_KNOWS ),
926
					'http://schema.org/knows',
927
					self::DATA_TYPE_URI
928
				),
929
				// ### schema:birthDate.
930
				$this->rendition_factory->create(
931
					$this->storage_factory->post_meta( self::FIELD_BIRTH_DATE ),
932
					'http://schema.org/birthDate',
933
					self::DATA_TYPE_DATE
934
				),
935
				// ### schema:birthPlace.
936
				$this->rendition_factory->create(
937
					$this->storage_factory->post_meta_to_uri( self::FIELD_BIRTH_PLACE ),
938
					'http://schema.org/birthPlace',
939
					self::DATA_TYPE_URI
940
				),
941
				// ### schema:affiliation.
942
				$this->rendition_factory->create(
943
					$this->storage_factory->post_meta_to_uri( self::FIELD_AFFILIATION ),
944
					'http://schema.org/affiliation',
945
					self::DATA_TYPE_URI
946
				),
947
				// ### schema:email.
948
				$this->rendition_factory->create(
949
					$this->storage_factory->post_meta( self::FIELD_EMAIL ),
950
					'http://schema.org/email'
951
				),
952
			),
953
			'templates'     => array(
954
				'subtitle' => '{{id}}',
955
			),
956
		);
957
958
		// Merge the custom fields with those provided by the thing schema.
959
		$parent_schema           = $this->get_thing_schema();
960
		$schema['custom_fields'] = array_merge( $schema['custom_fields'], $parent_schema['custom_fields'] );
961
		$schema['linked_data']   = array_merge( $schema['linked_data'], $parent_schema['linked_data'] );
962
963
		return $schema;
964
965
	}
966
967
	/**
968
	 * Get the 'place' schema.
969
	 *
970
	 * @return array An array with the schema configuration.
971
	 *
972
	 * @since 3.1.0
973
	 */
974
	private function get_place_schema() {
975
976
		$schema = array(
977
			'label'         => 'Place',
978
			'description'   => 'A place.',
979
			'parents'       => array( 'thing' ),
980
			'css_class'     => 'wl-place',
981
			'uri'           => 'http://schema.org/Place',
982
			'same_as'       => array(
983
				'http://rdf.freebase.com/ns/location.location',
984
				'http://www.opengis.net/gml/_Feature',
985
			),
986
			'custom_fields' => array(
987
				self::FIELD_GEO_LATITUDE        => array(
988
					'predicate'   => 'http://schema.org/latitude',
989
					'type'        => self::DATA_TYPE_DOUBLE,
990
					'export_type' => 'xsd:double',
991
					'constraints' => '',
992
					// To build custom metabox.
993
					'input_field' => 'coordinates',
994
				),
995
				self::FIELD_GEO_LONGITUDE       => array(
996
					'predicate'   => 'http://schema.org/longitude',
997
					'type'        => self::DATA_TYPE_DOUBLE,
998
					'export_type' => 'xsd:double',
999
					'constraints' => '',
1000
					// To build custom metabox.
1001
					'input_field' => 'coordinates',
1002
				),
1003
				self::FIELD_ADDRESS             => array(
1004
					'predicate'   => 'http://schema.org/streetAddress',
1005
					'type'        => self::DATA_TYPE_STRING,
1006
					'export_type' => 'xsd:string',
1007
					'constraints' => '',
1008
					// To build custom metabox.
1009
					'input_field' => 'address',
1010
				),
1011
				self::FIELD_ADDRESS_PO_BOX      => array(
1012
					'predicate'   => 'http://schema.org/postOfficeBoxNumber',
1013
					'type'        => self::DATA_TYPE_STRING,
1014
					'export_type' => 'xsd:string',
1015
					'constraints' => '',
1016
					// To build custom metabox.
1017
					'input_field' => 'address',
1018
				),
1019
				self::FIELD_ADDRESS_POSTAL_CODE => array(
1020
					'predicate'   => 'http://schema.org/postalCode',
1021
					'type'        => self::DATA_TYPE_STRING,
1022
					'export_type' => 'xsd:string',
1023
					'constraints' => '',
1024
					// To build custom metabox.
1025
					'input_field' => 'address',
1026
				),
1027
				self::FIELD_ADDRESS_LOCALITY    => array(
1028
					'predicate'   => 'http://schema.org/addressLocality',
1029
					'type'        => self::DATA_TYPE_STRING,
1030
					'export_type' => 'xsd:string',
1031
					'constraints' => '',
1032
					// To build custom metabox.
1033
					'input_field' => 'address',
1034
				),
1035
				self::FIELD_ADDRESS_REGION      => array(
1036
					'predicate'   => 'http://schema.org/addressRegion',
1037
					'type'        => self::DATA_TYPE_STRING,
1038
					'export_type' => 'xsd:string',
1039
					'constraints' => '',
1040
					// To build custom metabox.
1041
					'input_field' => 'address',
1042
				),
1043
				self::FIELD_ADDRESS_COUNTRY     => array(
1044
					'predicate'   => 'http://schema.org/addressCountry',
1045
					'type'        => self::DATA_TYPE_STRING,
1046
					'export_type' => 'xsd:string',
1047
					'constraints' => '',
1048
					// To build custom metabox.
1049
					'input_field' => 'address',
1050
				),
1051
			),
1052
			'linked_data'   => array(),
1053
			'templates'     => array(
1054
				'subtitle' => '{{id}}',
1055
			),
1056
		);
1057
1058
		// Merge the custom fields with those provided by the thing schema.
1059
		$parent_schema           = $this->get_thing_schema();
1060
		$schema['custom_fields'] = array_merge( $schema['custom_fields'], $parent_schema['custom_fields'] );
1061
		$schema['linked_data']   = array_merge( $schema['linked_data'], $parent_schema['linked_data'] );
1062
1063
		return $schema;
1064
	}
1065
1066
	/**
1067
	 * Get the 'local business' schema.
1068
	 *
1069
	 * @return array An array with the schema configuration.
1070
	 *
1071
	 * @since 3.1.0
1072
	 */
1073
	private function get_local_business_schema() {
1074
1075
		$schema = array(
1076
			'label'         => 'LocalBusiness',
1077
			'description'   => 'A local business.',
1078
			'parents'       => array( 'place', 'organization' ),
1079
			'css_class'     => 'wl-local-business',
1080
			'uri'           => 'http://schema.org/LocalBusiness',
1081
			'same_as'       => array(
1082
				'http://rdf.freebase.com/ns/business/business_location',
1083
				'https://schema.org/Store',
1084
			),
1085
			'custom_fields' => array(),
1086
			'linked_data'   => array(),
1087
			'templates'     => array(
1088
				'subtitle' => '{{id}}',
1089
			),
1090
		);
1091
1092
		// Merge the custom fields with those provided by the place and organization schema.
1093
		$place_schema            = $this->get_place_schema();
1094
		$organization_schema     = $this->get_organization_schema();
1095
		$schema['custom_fields'] = array_merge( $schema['custom_fields'], $place_schema['custom_fields'], $organization_schema['custom_fields'] );
1096
		$schema['linked_data']   = array_merge( $schema['linked_data'], $place_schema['linked_data'], $organization_schema['linked_data'] );
1097
1098
		return $schema;
1099
	}
1100
1101
	/**
1102
	 * Get the 'recipe' schema.
1103
	 *
1104
	 * @return array An array with the schema configuration.
1105
	 *
1106
	 * @since 3.14.0
1107
	 */
1108
	private function get_recipe_schema() {
1109
1110
		$schema = array(
1111
			'label'         => 'Recipe',
1112
			'description'   => 'A Recipe.',
1113
			'parents'       => array( 'CreativeWork' ),
1114
			'css_class'     => 'wl-recipe',
1115
			'uri'           => 'http://schema.org/Recipe',
1116
			'same_as'       => array(),
1117
			'templates'     => array(
1118
				'subtitle' => '{{id}}',
1119
			),
1120
			'custom_fields' => array(
1121
				self::FIELD_RECIPE_CUISINE          => array(
1122
					'predicate'   => 'http://schema.org/recipeCuisine',
1123
					'type'        => self::DATA_TYPE_STRING,
1124
					'export_type' => 'xsd:string',
1125
					'constraints' => '',
1126
					'metabox'     => array(
1127
						'label' => __( 'Recipe cuisine', 'wordlift' ),
1128
					),
1129
				),
1130
				self::FIELD_RECIPE_INGREDIENT       => array(
1131
					'predicate'   => 'http://schema.org/recipeIngredient',
1132
					'type'        => self::DATA_TYPE_STRING,
1133
					'export_type' => 'xsd:string',
1134
					'constraints' => array(
1135
						'cardinality' => INF,
1136
					),
1137
					'metabox'     => array(
1138
						'label' => __( 'Recipe ingredient', 'wordlift' ),
1139
					),
1140
				),
1141
				self::FIELD_RECIPE_INSTRUCTIONS     => array(
1142
					'predicate'   => 'http://schema.org/recipeInstructions',
1143
					'type'        => self::DATA_TYPE_MULTILINE,
1144
					'export_type' => 'xsd:string',
1145
					'constraints' => '',
1146
					'metabox'     => array(
1147
						'class' => 'Wordlift_Metabox_Field_Multiline',
1148
						'label' => __( 'Recipe instructions', 'wordlift' ),
1149
					),
1150
				),
1151
				self::FIELD_RECIPE_YIELD            => array(
1152
					'predicate'   => 'http://schema.org/recipeYield',
1153
					'type'        => self::DATA_TYPE_STRING,
1154
					'export_type' => 'xsd:string',
1155
					'constraints' => '',
1156
					'metabox'     => array(
1157
						'label' => __( 'Recipe number of servings', 'wordlift' ),
1158
					),
1159
				),
1160
				self::FIELD_RECIPE_INGREDIENT       => array(
1161
					'predicate'   => 'http://schema.org/recipeIngredient',
1162
					'type'        => self::DATA_TYPE_STRING,
1163
					'export_type' => 'xsd:string',
1164
					'constraints' => array(
1165
						'cardinality' => INF,
1166
					),
1167
					'metabox'     => array(
1168
						'label' => __( 'Recipe ingredient', 'wordlift' ),
1169
					),
1170
				),
1171
				self::FIELD_NUTRITION_INFO_CALORIES => array(
1172
					'predicate'   => 'http://schema.org/calories',
1173
					'type'        => self::DATA_TYPE_STRING,
1174
					'export_type' => 'xsd:string',
1175
					'constraints' => '',
1176
					'metabox'     => array(
1177
						'label' => __( 'Calories (e.g. 240 calories)', 'wordlift' ),
1178
					),
1179
				),
1180
				self::FIELD_PREP_TIME               => array(
1181
					'predicate'   => 'http://schema.org/prepTime',
1182
					'type'        => self::DATA_TYPE_DURATION,
1183
					'export_type' => 'xsd:time',
1184
					'constraints' => '',
1185
					'metabox'     => array(
1186
						'class' => 'Wordlift_Metabox_Field_Duration',
1187
						'label' => __( 'Recipe preparation time (e.g. 1:30)', 'wordlift' ),
1188
					),
1189
				),
1190
				self::FIELD_COOK_TIME               => array(
1191
					'predicate'   => 'http://schema.org/cookTime',
1192
					'type'        => self::DATA_TYPE_DURATION,
1193
					'export_type' => 'xsd:time',
1194
					'constraints' => '',
1195
					'metabox'     => array(
1196
						'class' => 'Wordlift_Metabox_Field_Duration',
1197
						'label' => __( 'Recipe cook time (e.g. 1:30)', 'wordlift' ),
1198
					),
1199
				),
1200
				self::FIELD_TOTAL_TIME              => array(
1201
					'predicate'   => 'http://schema.org/totalTime',
1202
					'type'        => self::DATA_TYPE_DURATION,
1203
					'export_type' => 'xsd:time',
1204
					'constraints' => '',
1205
					'metabox'     => array(
1206
						'class' => 'Wordlift_Metabox_Field_Duration',
1207
						'label' => __( 'Recipe total time (e.g. 1:30)', 'wordlift' ),
1208
					),
1209
				),
1210
			),
1211
			'linked_data'   => array(
1212
				// ### schema:recipeCuisine.
1213
				$this->rendition_factory->create(
1214
					$this->storage_factory->post_meta( self::FIELD_RECIPE_CUISINE ),
1215
					'http://schema.org/recipeCuisine'
1216
				),
1217
				// ### schema:recipeIngredient.
1218
				$this->rendition_factory->create(
1219
					$this->storage_factory->post_meta( self::FIELD_RECIPE_INGREDIENT ),
1220
					'http://schema.org/recipeIngredient'
1221
				),
1222
				// ### schema:prepTime.
1223
				$this->rendition_factory->create(
1224
					$this->storage_factory->post_meta( self::FIELD_PREP_TIME ),
1225
					'http://schema.org/prepTime',
1226
					self::DATA_TYPE_DURATION
1227
				),
1228
				// ### schema:cookTime.
1229
				$this->rendition_factory->create(
1230
					$this->storage_factory->post_meta( self::FIELD_COOK_TIME ),
1231
					'http://schema.org/cookTime',
1232
					self::DATA_TYPE_DURATION
1233
				),
1234
				// ### schema:totalTime.
1235
				$this->rendition_factory->create(
1236
					$this->storage_factory->post_meta( self::FIELD_TOTAL_TIME ),
1237
					'http://schema.org/totalTime',
1238
					self::DATA_TYPE_DURATION
1239
				),
1240
			),
1241
		);
1242
1243
		// Merge the custom fields with those provided by the parent schema.
1244
		$parent_schema           = $this->get_creative_work_schema();
1245
		$schema['custom_fields'] = array_merge( $schema['custom_fields'], $parent_schema['custom_fields'] );
1246
		$schema['linked_data']   = array_merge( $schema['linked_data'], $parent_schema['linked_data'] );
1247
1248
		return $schema;
1249
	}
1250
1251
	/**
1252
	 * Get the 'article' schema.
1253
	 *
1254
	 * @return array An array with the schema configuration.
1255
	 *
1256
	 * @since 3.15.0
1257
	 */
1258
	private function get_article_schema() {
1259
1260
		$schema = array(
1261
			'label'         => 'Article',
1262
			'description'   => 'An Article.',
1263
			'parents'       => array(),
1264
			'css_class'     => 'wl-article',
1265
			'uri'           => 'http://schema.org/Article',
1266
			'same_as'       => array(),
1267
			'templates'     => array(
1268
				'subtitle' => '{{id}}',
1269
			),
1270
			'custom_fields' => array(),
1271
			'linked_data'   => array(),
1272
		);
1273
1274
		return $schema;
1275
	}
1276
1277
	/**
1278
	 * Get all the predicates.
1279
	 *
1280
	 * @since 3.15.0
1281
	 *
1282
	 * @return array An array of predicates.
1283
	 */
1284
	public function get_all_predicates() {
1285
1286
		// Get the custom fields.
1287
		$renditions = array_reduce( $this->schema, function ( $carry, $item ) {
1288
			return array_merge( $carry, $item['linked_data'] );
1289
		}, array() );
1290
1291
		// Create a new array of predicates from the custom fields. The initial
1292
		// array contains just the `http://www.w3.org/1999/02/22-rdf-syntax-ns#type`
1293
		// (a, rdf:type) predicate (use the full URI).
1294
		$predicates = array_unique( array_reduce( $renditions, function ( $carry, $item ) {
1295
			return array_merge( $carry, (array) $item->get_predicate() );
1296
		}, array() ) );
1297
1298
		// Finally return the predicates array.
1299
		return $predicates;
1300
	}
1301
1302
}
1303