|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/** |
|
4
|
|
|
* Provide entity-related services. |
|
5
|
|
|
* |
|
6
|
|
|
* @since 3.1.0 |
|
7
|
|
|
*/ |
|
8
|
|
|
class Wordlift_Entity_Service { |
|
9
|
|
|
|
|
10
|
|
|
/** |
|
11
|
|
|
* The Log service. |
|
12
|
|
|
* |
|
13
|
|
|
* @since 3.2.0 |
|
14
|
|
|
* @access private |
|
15
|
|
|
* @var \Wordlift_Log_Service $log_service The Log service. |
|
16
|
|
|
*/ |
|
17
|
|
|
private $log_service; |
|
18
|
|
|
|
|
19
|
|
|
/** |
|
20
|
|
|
* The UI service. |
|
21
|
|
|
* |
|
22
|
|
|
* @since 3.2.0 |
|
23
|
|
|
* @access private |
|
24
|
|
|
* @var \Wordlift_UI_Service $ui_service The UI service. |
|
25
|
|
|
*/ |
|
26
|
|
|
private $ui_service; |
|
27
|
|
|
|
|
28
|
|
|
/** |
|
29
|
|
|
* The Schema service. |
|
30
|
|
|
* |
|
31
|
|
|
* @since 3.3.0 |
|
32
|
|
|
* @access private |
|
33
|
|
|
* @var \Wordlift_Schema_Service $schema_service The Schema service. |
|
34
|
|
|
*/ |
|
35
|
|
|
private $schema_service; |
|
36
|
|
|
|
|
37
|
|
|
/** |
|
38
|
|
|
* The Notice service. |
|
39
|
|
|
* |
|
40
|
|
|
* @since 3.3.0 |
|
41
|
|
|
* @access private |
|
42
|
|
|
* @var \Wordlift_Notice_Service $notice_service The Notice service. |
|
43
|
|
|
*/ |
|
44
|
|
|
private $notice_service; |
|
45
|
|
|
|
|
46
|
|
|
/** |
|
47
|
|
|
* The entity post type name. |
|
48
|
|
|
* |
|
49
|
|
|
* @since 3.1.0 |
|
50
|
|
|
*/ |
|
51
|
|
|
const TYPE_NAME = 'entity'; |
|
52
|
|
|
|
|
53
|
|
|
/** |
|
54
|
|
|
* Entity rating max. |
|
55
|
|
|
* |
|
56
|
|
|
* @since 3.3.0 |
|
57
|
|
|
*/ |
|
58
|
|
|
const RATING_MAX = 7; |
|
59
|
|
|
|
|
60
|
|
|
/** |
|
61
|
|
|
* Entity rating score meta key. |
|
62
|
|
|
* |
|
63
|
|
|
* @since 3.3.0 |
|
64
|
|
|
*/ |
|
65
|
|
|
const RATING_RAW_SCORE_META_KEY = '_wl_entity_rating_raw_score'; |
|
66
|
|
|
|
|
67
|
|
|
/** |
|
68
|
|
|
* Entity rating warnings meta key. |
|
69
|
|
|
* |
|
70
|
|
|
* @since 3.3.0 |
|
71
|
|
|
*/ |
|
72
|
|
|
const RATING_WARNINGS_META_KEY = '_wl_entity_rating_warnings'; |
|
73
|
|
|
|
|
74
|
|
|
/** |
|
75
|
|
|
* Entity warning has related post identifier. |
|
76
|
|
|
* |
|
77
|
|
|
* @since 3.3.0 |
|
78
|
|
|
*/ |
|
79
|
|
|
const RATING_WARNING_HAS_RELATED_POSTS = 'There are no related posts for the current entity.'; |
|
80
|
|
|
|
|
81
|
|
|
/** |
|
82
|
|
|
* Entity warning has content post identifier. |
|
83
|
|
|
* |
|
84
|
|
|
* @since 3.3.0 |
|
85
|
|
|
*/ |
|
86
|
|
|
const RATING_WARNING_HAS_CONTENT_POST = 'This entity has not description.'; |
|
87
|
|
|
|
|
88
|
|
|
/** |
|
89
|
|
|
* Entity warning has related entities identifier. |
|
90
|
|
|
* |
|
91
|
|
|
* @since 3.3.0 |
|
92
|
|
|
*/ |
|
93
|
|
|
const RATING_WARNING_HAS_RELATED_ENTITIES = 'There are no related entities for the current entity.'; |
|
94
|
|
|
|
|
95
|
|
|
/** |
|
96
|
|
|
* Entity warning is published identifier. |
|
97
|
|
|
* |
|
98
|
|
|
* @since 3.3.0 |
|
99
|
|
|
*/ |
|
100
|
|
|
const RATING_WARNING_IS_PUBLISHED = 'This entity is not published. It will not appear within analysis results.'; |
|
101
|
|
|
|
|
102
|
|
|
/** |
|
103
|
|
|
* Entity warning has thumbnail identifier. |
|
104
|
|
|
* |
|
105
|
|
|
* @since 3.3.0 |
|
106
|
|
|
*/ |
|
107
|
|
|
const RATING_WARNING_HAS_THUMBNAIL = 'This entity has no featured image yet.'; |
|
108
|
|
|
|
|
109
|
|
|
/** |
|
110
|
|
|
* Entity warning has same as identifier. |
|
111
|
|
|
* |
|
112
|
|
|
* @since 3.3.0 |
|
113
|
|
|
*/ |
|
114
|
|
|
const RATING_WARNING_HAS_SAME_AS = 'There are no sameAs configured for this entity.'; |
|
115
|
|
|
|
|
116
|
|
|
/** |
|
117
|
|
|
* Entity warning has completed metadata identifier. |
|
118
|
|
|
* |
|
119
|
|
|
* @since 3.3.0 |
|
120
|
|
|
*/ |
|
121
|
|
|
const RATING_WARNING_HAS_COMPLETED_METADATA = 'Schema.org metadata for this entity are not completed.'; |
|
122
|
|
|
|
|
123
|
|
|
/** |
|
124
|
|
|
* The alternative label meta key. |
|
125
|
|
|
* |
|
126
|
|
|
* @since 3.2.0 |
|
127
|
|
|
*/ |
|
128
|
|
|
const ALTERNATIVE_LABEL_META_KEY = '_wl_alt_label'; |
|
129
|
|
|
|
|
130
|
|
|
/** |
|
131
|
|
|
* The alternative label input template. |
|
132
|
|
|
* |
|
133
|
|
|
* @since 3.2.0 |
|
134
|
|
|
*/ |
|
135
|
|
|
// TODO: this should be moved to a class that deals with HTML code. |
|
136
|
|
|
const ALTERNATIVE_LABEL_INPUT_TEMPLATE = '<div class="wl-alternative-label"> |
|
137
|
|
|
<label class="screen-reader-text" id="wl-alternative-label-prompt-text" for="wl-alternative-label">Enter alternative label here</label> |
|
138
|
|
|
<input name="wl_alternative_label[]" size="30" value="%s" id="wl-alternative-label" type="text"> |
|
139
|
|
|
<button class="button wl-delete-button">%s</button> |
|
140
|
|
|
</div>'; |
|
141
|
|
|
|
|
142
|
|
|
/** |
|
143
|
|
|
* A singleton instance of the Entity service. |
|
144
|
|
|
* |
|
145
|
|
|
* @since 3.2.0 |
|
146
|
|
|
* @access private |
|
147
|
|
|
* @var \Wordlift_Entity_Service $instance A singleton instance of the Entity service. |
|
148
|
|
|
*/ |
|
149
|
|
|
private static $instance; |
|
150
|
|
|
|
|
151
|
|
|
/** |
|
152
|
|
|
* Create a Wordlift_Entity_Service instance. |
|
153
|
|
|
* |
|
154
|
|
|
* @since 3.2.0 |
|
155
|
|
|
* |
|
156
|
|
|
* @param \Wordlift_UI_Service $ui_service The UI service. |
|
157
|
|
|
*/ |
|
158
|
|
|
public function __construct( $ui_service, $schema_service, $notice_service ) { |
|
159
|
|
|
|
|
160
|
|
|
$this->log_service = Wordlift_Log_Service::get_logger( 'Wordlift_Entity_Service' ); |
|
161
|
|
|
|
|
162
|
|
|
// Set the UI service. |
|
163
|
|
|
$this->ui_service = $ui_service; |
|
164
|
|
|
|
|
165
|
|
|
// Set the Schema service. |
|
166
|
|
|
$this->schema_service = $schema_service; |
|
167
|
|
|
|
|
168
|
|
|
// Set the Schema service. |
|
169
|
|
|
$this->notice_service = $notice_service; |
|
170
|
|
|
|
|
171
|
|
|
// Set the singleton instance. |
|
172
|
|
|
self::$instance = $this; |
|
173
|
|
|
|
|
174
|
|
|
} |
|
175
|
|
|
|
|
176
|
|
|
/** |
|
177
|
|
|
* Get the singleton instance of the Entity service. |
|
178
|
|
|
* |
|
179
|
|
|
* @since 3.2.0 |
|
180
|
|
|
* @return \Wordlift_Entity_Service The singleton instance of the Entity service. |
|
181
|
|
|
*/ |
|
182
|
|
|
public static function get_instance() { |
|
183
|
|
|
|
|
184
|
|
|
return self::$instance; |
|
185
|
|
|
} |
|
186
|
|
|
|
|
187
|
|
|
/** |
|
188
|
|
|
* Get rating max |
|
189
|
|
|
* |
|
190
|
|
|
* @since 3.3.0 |
|
191
|
|
|
* |
|
192
|
|
|
* @return int Max rating according to performed checks. |
|
193
|
|
|
*/ |
|
194
|
|
|
public static function get_rating_max() { |
|
195
|
|
|
return self::RATING_MAX; |
|
196
|
|
|
} |
|
197
|
|
|
|
|
198
|
|
|
/** |
|
199
|
|
|
* Get the entities related to the last 50 posts published on this blog (we're keeping a long function name due to |
|
200
|
|
|
* its specific function). |
|
201
|
|
|
* |
|
202
|
|
|
* @since 3.1.0 |
|
203
|
|
|
* |
|
204
|
|
|
* @return array An array of post IDs. |
|
205
|
|
|
*/ |
|
206
|
|
|
public function get_all_related_to_last_50_published_posts() { |
|
207
|
|
|
|
|
208
|
|
|
// Global timeline. Get entities from the latest posts. |
|
209
|
|
|
$latest_posts_ids = get_posts( array( |
|
210
|
|
|
'numberposts' => 50, |
|
211
|
|
|
'fields' => 'ids', //only get post IDs |
|
212
|
|
|
'post_type' => 'post', |
|
213
|
|
|
'post_status' => 'publish', |
|
214
|
|
|
) ); |
|
215
|
|
|
|
|
216
|
|
|
if ( empty( $latest_posts_ids ) ) { |
|
217
|
|
|
// There are no posts. |
|
218
|
|
|
return array(); |
|
219
|
|
|
} |
|
220
|
|
|
|
|
221
|
|
|
// Collect entities related to latest posts |
|
222
|
|
|
$entity_ids = array(); |
|
223
|
|
|
foreach ( $latest_posts_ids as $id ) { |
|
224
|
|
|
$entity_ids = array_merge( $entity_ids, wl_core_get_related_entity_ids( $id, array( |
|
225
|
|
|
'status' => 'publish', |
|
226
|
|
|
) ) ); |
|
227
|
|
|
} |
|
228
|
|
|
|
|
229
|
|
|
return $entity_ids; |
|
230
|
|
|
} |
|
231
|
|
|
|
|
232
|
|
|
/** |
|
233
|
|
|
* Determines whether a post is an entity or not. |
|
234
|
|
|
* |
|
235
|
|
|
* @since 3.1.0 |
|
236
|
|
|
* |
|
237
|
|
|
* @param int $post_id A post id. |
|
238
|
|
|
* |
|
239
|
|
|
* @return bool Return true if the post is an entity otherwise false. |
|
240
|
|
|
*/ |
|
241
|
|
|
public function is_entity( $post_id ) { |
|
242
|
|
|
|
|
243
|
|
|
return ( self::TYPE_NAME === get_post_type( $post_id ) ); |
|
244
|
|
|
} |
|
245
|
|
|
|
|
246
|
|
|
/** |
|
247
|
|
|
* Get the proper classification scope for a given entity post |
|
248
|
|
|
* |
|
249
|
|
|
* @since 3.5.0 |
|
250
|
|
|
* |
|
251
|
|
|
* @param integer $post_id An entity post id. |
|
252
|
|
|
* |
|
253
|
|
|
* @return string Returns an uri. |
|
254
|
|
|
*/ |
|
255
|
|
|
public function get_classification_scope_for( $post_id ) { |
|
256
|
|
|
|
|
257
|
|
|
if ( FALSE === $this->is_entity( $post_id ) ) { |
|
258
|
|
|
return NULL; |
|
259
|
|
|
} |
|
260
|
|
|
// Retrieve the entity type |
|
261
|
|
|
$entity_type_arr = wl_entity_type_taxonomy_get_type( $post_id ); |
|
|
|
|
|
|
262
|
|
|
$entity_type = str_replace( 'wl-', '', $entity_type_arr['css_class'] ); |
|
263
|
|
|
// Retrieve classification boxes configuration |
|
264
|
|
|
$classification_boxes = unserialize( WL_CORE_POST_CLASSIFICATION_BOXES ); |
|
265
|
|
|
foreach ( $classification_boxes as $cb ) { |
|
266
|
|
|
if ( in_array( $entity_type, $cb['registeredTypes'] ) ) { |
|
267
|
|
|
return $cb['id']; |
|
268
|
|
|
} |
|
269
|
|
|
} |
|
270
|
|
|
|
|
271
|
|
|
// or null |
|
272
|
|
|
return NULL; |
|
273
|
|
|
|
|
274
|
|
|
} |
|
275
|
|
|
|
|
276
|
|
|
/** |
|
277
|
|
|
* Build an entity uri for a given title |
|
278
|
|
|
* The uri is composed using a given post_type and a title |
|
279
|
|
|
* If already exists an entity e2 with a given uri a numeric suffix is added |
|
280
|
|
|
* If a schema type is given entities with same label and same type are overridden |
|
281
|
|
|
* |
|
282
|
|
|
* @since 3.5.0 |
|
283
|
|
|
* |
|
284
|
|
|
* @param string $title A post title. |
|
285
|
|
|
* @param string $post_type A post type. Default value is 'entity' |
|
286
|
|
|
* @param string $schema_type A schema org type. |
|
287
|
|
|
* @param integer $increment_digit A digit used to call recursively the same function. |
|
288
|
|
|
* |
|
289
|
|
|
* @return string Returns an uri. |
|
290
|
|
|
*/ |
|
291
|
|
|
public function build_uri( $title, $post_type, $schema_type = NULL, $increment_digit = 0 ) { |
|
292
|
|
|
|
|
293
|
|
|
// Get the entity slug suffix digit |
|
294
|
|
|
$suffix_digit = $increment_digit + 1; |
|
295
|
|
|
// Get a sanitized uri for a given title |
|
296
|
|
|
$entity_slug = ( 0 == $increment_digit ) ? |
|
297
|
|
|
wl_sanitize_uri_path( $title ) : |
|
|
|
|
|
|
298
|
|
|
wl_sanitize_uri_path( $title . '_' . $suffix_digit ); |
|
|
|
|
|
|
299
|
|
|
|
|
300
|
|
|
// Compose a candidated uri |
|
301
|
|
|
$new_entity_uri = sprintf( '%s/%s/%s', |
|
302
|
|
|
wl_configuration_get_redlink_dataset_uri(), |
|
303
|
|
|
$post_type, |
|
304
|
|
|
$entity_slug |
|
305
|
|
|
); |
|
306
|
|
|
|
|
307
|
|
|
$this->log_service->trace( "Going to check if uri is used [ new_entity_uri :: $new_entity_uri ] [ increment_digit :: $increment_digit ]" ); |
|
308
|
|
|
|
|
309
|
|
|
global $wpdb; |
|
|
|
|
|
|
310
|
|
|
// Check if the candidated uri already is used |
|
311
|
|
|
$stmt = $wpdb->prepare( |
|
312
|
|
|
"SELECT post_id FROM $wpdb->postmeta WHERE meta_key = %s AND meta_value = %s LIMIT 1", |
|
313
|
|
|
WL_ENTITY_URL_META_NAME, |
|
314
|
|
|
$new_entity_uri |
|
315
|
|
|
); |
|
316
|
|
|
|
|
317
|
|
|
// Perform the query |
|
318
|
|
|
$post_id = $wpdb->get_var( $stmt ); |
|
319
|
|
|
|
|
320
|
|
|
// If the post does not exist, then the new uri is returned |
|
|
|
|
|
|
321
|
|
|
if ( ! is_numeric( $post_id ) ) { |
|
322
|
|
|
$this->log_service->trace( "Going to return uri [ new_entity_uri :: $new_entity_uri ]" ); |
|
323
|
|
|
|
|
324
|
|
|
return $new_entity_uri; |
|
325
|
|
|
} |
|
326
|
|
|
// If schema_type is equal to schema org type of post x, then the new uri is returned |
|
327
|
|
|
$schema_post_type = wl_entity_type_taxonomy_get_type( $post_id ); |
|
|
|
|
|
|
328
|
|
|
|
|
329
|
|
|
if ( $schema_type === $schema_post_type['css_class'] ) { |
|
330
|
|
|
$this->log_service->trace( "An entity with the same title and type already exists! Return uri [ new_entity_uri :: $new_entity_uri ]" ); |
|
331
|
|
|
|
|
332
|
|
|
return $new_entity_uri; |
|
333
|
|
|
} |
|
334
|
|
|
|
|
335
|
|
|
// Otherwise the same function is called recorsively |
|
336
|
|
|
return $this->build_uri( $title, $post_type, $schema_type, ++ $increment_digit ); |
|
337
|
|
|
} |
|
338
|
|
|
|
|
339
|
|
|
public function is_used( $post_id ) { |
|
340
|
|
|
|
|
341
|
|
|
if ( FALSE === $this->is_entity( $post_id ) ) { |
|
342
|
|
|
return NULL; |
|
343
|
|
|
} |
|
344
|
|
|
// Retrieve the post |
|
345
|
|
|
$entity = get_post( $post_id ); |
|
346
|
|
|
|
|
347
|
|
|
global $wpdb; |
|
|
|
|
|
|
348
|
|
|
// Retrieve Wordlift relation instances table name |
|
349
|
|
|
$table_name = wl_core_get_relation_instances_table_name(); |
|
350
|
|
|
|
|
351
|
|
|
// Check is it's referenced / related to another post / entity |
|
352
|
|
|
$stmt = $wpdb->prepare( |
|
353
|
|
|
"SELECT COUNT(*) FROM $table_name WHERE object_id = %d", |
|
354
|
|
|
$entity->ID |
|
355
|
|
|
); |
|
356
|
|
|
|
|
357
|
|
|
// Perform the query |
|
358
|
|
|
$relation_instances = (int) $wpdb->get_var( $stmt ); |
|
359
|
|
|
// If there is at least one relation instance for the current entity, then it's used |
|
360
|
|
|
if ( 0 < $relation_instances ) { |
|
361
|
|
|
return TRUE; |
|
362
|
|
|
} |
|
363
|
|
|
|
|
364
|
|
|
// Check if the entity uri is used as meta_value |
|
365
|
|
|
$stmt = $wpdb->prepare( |
|
366
|
|
|
"SELECT COUNT(*) FROM $wpdb->postmeta WHERE post_id != %d AND meta_value = %s", |
|
367
|
|
|
$entity->ID, |
|
368
|
|
|
wl_get_entity_uri( $entity->ID ) |
|
|
|
|
|
|
369
|
|
|
); |
|
370
|
|
|
// Perform the query |
|
371
|
|
|
$meta_instances = (int) $wpdb->get_var( $stmt ); |
|
372
|
|
|
|
|
373
|
|
|
// If there is at least one meta that refers the current entity uri, then current entity is used |
|
374
|
|
|
if ( 0 < $meta_instances ) { |
|
375
|
|
|
return TRUE; |
|
376
|
|
|
} |
|
377
|
|
|
|
|
378
|
|
|
// If we are here, it means the current entity is not used at the moment |
|
379
|
|
|
return FALSE; |
|
380
|
|
|
} |
|
381
|
|
|
|
|
382
|
|
|
/** |
|
383
|
|
|
* Determines whether a given uri is an internal uri or not. |
|
384
|
|
|
* |
|
385
|
|
|
* @since 3.3.2 |
|
386
|
|
|
* |
|
387
|
|
|
* @param int $uri An uri. |
|
388
|
|
|
* |
|
389
|
|
|
* @return true if the uri internal to the current dataset otherwise false. |
|
390
|
|
|
*/ |
|
391
|
|
|
public function is_internal_uri( $uri ) { |
|
392
|
|
|
|
|
393
|
|
|
return ( 0 === strrpos( $uri, wl_configuration_get_redlink_dataset_uri() ) ); |
|
394
|
|
|
} |
|
395
|
|
|
|
|
396
|
|
|
/** |
|
397
|
|
|
* Find entity posts by the entity URI. Entity as searched by their entity URI or same as. |
|
398
|
|
|
* |
|
399
|
|
|
* @since 3.2.0 |
|
400
|
|
|
* |
|
401
|
|
|
* @param string $uri The entity URI. |
|
402
|
|
|
* |
|
403
|
|
|
* @return WP_Post|null A WP_Post instance or null if not found. |
|
404
|
|
|
*/ |
|
405
|
|
|
public function get_entity_post_by_uri( $uri ) { |
|
406
|
|
|
|
|
407
|
|
|
// Check if we've been provided with a value otherwise return null. |
|
408
|
|
|
if ( empty( $uri ) ) { |
|
409
|
|
|
return NULL; |
|
410
|
|
|
} |
|
411
|
|
|
|
|
412
|
|
|
$query_args = array( |
|
413
|
|
|
'posts_per_page' => 1, |
|
414
|
|
|
'post_status' => 'any', |
|
415
|
|
|
'post_type' => self::TYPE_NAME, |
|
416
|
|
|
'meta_query' => array( |
|
417
|
|
|
array( |
|
418
|
|
|
'key' => WL_ENTITY_URL_META_NAME, |
|
419
|
|
|
'value' => $uri, |
|
420
|
|
|
'compare' => '=', |
|
421
|
|
|
), |
|
422
|
|
|
), |
|
423
|
|
|
); |
|
424
|
|
|
|
|
425
|
|
|
// Only if the current uri is not an internal uri |
|
426
|
|
|
// entity search is performed also looking at sameAs values |
|
427
|
|
|
// This solve issues like https://github.com/insideout10/wordlift-plugin/issues/237 |
|
428
|
|
|
if ( ! $this->is_internal_uri( $uri ) ) { |
|
429
|
|
|
|
|
430
|
|
|
$query_args['meta_query']['relation'] = 'OR'; |
|
431
|
|
|
$query_args['meta_query'][] = array( |
|
432
|
|
|
'key' => Wordlift_Schema_Service::FIELD_SAME_AS, |
|
433
|
|
|
'value' => $uri, |
|
434
|
|
|
'compare' => '=', |
|
435
|
|
|
); |
|
436
|
|
|
} |
|
437
|
|
|
|
|
438
|
|
|
$query = new WP_Query( $query_args ); |
|
439
|
|
|
|
|
440
|
|
|
// Get the matching entity posts. |
|
441
|
|
|
$posts = $query->get_posts(); |
|
442
|
|
|
|
|
443
|
|
|
// Return null if no post is found. |
|
444
|
|
|
if ( 0 === count( $posts ) ) { |
|
445
|
|
|
return NULL; |
|
446
|
|
|
} |
|
447
|
|
|
|
|
448
|
|
|
// Return the found post. |
|
449
|
|
|
return $posts[0]; |
|
450
|
|
|
} |
|
451
|
|
|
|
|
452
|
|
|
/** |
|
453
|
|
|
* Fires once a post has been saved. This function uses the $_REQUEST, therefore |
|
454
|
|
|
* we check that the post we're saving is the current post. |
|
455
|
|
|
* |
|
456
|
|
|
* @see https://github.com/insideout10/wordlift-plugin/issues/363 |
|
457
|
|
|
* |
|
458
|
|
|
* @since 3.2.0 |
|
459
|
|
|
* |
|
460
|
|
|
* @param int $post_id Post ID. |
|
461
|
|
|
* @param WP_Post $post Post object. |
|
462
|
|
|
* @param bool $update Whether this is an existing post being updated or not. |
|
463
|
|
|
*/ |
|
464
|
|
|
public function save_post( $post_id, $post, $update ) { |
|
|
|
|
|
|
465
|
|
|
|
|
466
|
|
|
// We're setting the alternative label that have been provided via the UI |
|
467
|
|
|
// (in fact we're using $_REQUEST), while save_post may be also called |
|
468
|
|
|
// programmatically by some other function: we need to check therefore if |
|
469
|
|
|
// the $post_id in the save_post call matches the post id set in the request. |
|
470
|
|
|
// |
|
471
|
|
|
// If this is not the current post being saved or if it's not an entity, return. |
|
472
|
|
|
if ( ! isset( $_REQUEST['post_ID'] ) || $_REQUEST['post_ID'] != $post_id || ! $this->is_entity( $post_id ) ) { |
|
473
|
|
|
return; |
|
474
|
|
|
} |
|
475
|
|
|
|
|
476
|
|
|
// Get the alt labels from the request (or empty array). |
|
477
|
|
|
$alt_labels = isset( $_REQUEST['wl_alternative_label'] ) ? $_REQUEST['wl_alternative_label'] : array(); |
|
478
|
|
|
|
|
479
|
|
|
// Set the alternative labels. |
|
480
|
|
|
$this->set_alternative_labels( $post_id, $alt_labels ); |
|
481
|
|
|
|
|
482
|
|
|
} |
|
483
|
|
|
|
|
484
|
|
|
/** |
|
485
|
|
|
* Set the alternative labels. |
|
486
|
|
|
* |
|
487
|
|
|
* @since 3.2.0 |
|
488
|
|
|
* |
|
489
|
|
|
* @param int $post_id The post id. |
|
490
|
|
|
* @param array $alt_labels An array of labels. |
|
491
|
|
|
*/ |
|
492
|
|
|
public function set_alternative_labels( $post_id, $alt_labels ) { |
|
493
|
|
|
|
|
494
|
|
|
// Force $alt_labels to be an array |
|
495
|
|
|
if ( ! is_array( $alt_labels ) ) { |
|
496
|
|
|
$alt_labels = array( $alt_labels ); |
|
497
|
|
|
} |
|
498
|
|
|
|
|
499
|
|
|
$this->log_service->debug( "Setting alternative labels [ post id :: $post_id ][ alt labels :: " . implode( ',', $alt_labels ) . " ]" ); |
|
500
|
|
|
|
|
501
|
|
|
// Delete all the existing alternate labels. |
|
502
|
|
|
delete_post_meta( $post_id, self::ALTERNATIVE_LABEL_META_KEY ); |
|
503
|
|
|
|
|
504
|
|
|
// Set the alternative labels. |
|
505
|
|
|
foreach ( $alt_labels as $alt_label ) { |
|
506
|
|
|
if ( ! empty( $alt_label ) ) { |
|
507
|
|
|
add_post_meta( $post_id, self::ALTERNATIVE_LABEL_META_KEY, $alt_label ); |
|
508
|
|
|
} |
|
509
|
|
|
} |
|
510
|
|
|
|
|
511
|
|
|
} |
|
512
|
|
|
|
|
513
|
|
|
/** |
|
514
|
|
|
* Retrieve the alternate labels. |
|
515
|
|
|
* |
|
516
|
|
|
* @since 3.2.0 |
|
517
|
|
|
* |
|
518
|
|
|
* @param int $post_id Post id. |
|
519
|
|
|
* |
|
520
|
|
|
* @return mixed An array of alternative labels. |
|
521
|
|
|
*/ |
|
522
|
|
|
public function get_alternative_labels( $post_id ) { |
|
523
|
|
|
|
|
524
|
|
|
return get_post_meta( $post_id, self::ALTERNATIVE_LABEL_META_KEY ); |
|
525
|
|
|
} |
|
526
|
|
|
|
|
527
|
|
|
/** |
|
528
|
|
|
* Fires before the permalink field in the edit form (this event is available in WP from 4.1.0). |
|
529
|
|
|
* |
|
530
|
|
|
* @since 3.2.0 |
|
531
|
|
|
* |
|
532
|
|
|
* @param WP_Post $post Post object. |
|
533
|
|
|
*/ |
|
534
|
|
|
public function edit_form_before_permalink( $post ) { |
|
535
|
|
|
|
|
536
|
|
|
// If it's not an entity, return. |
|
537
|
|
|
if ( ! $this->is_entity( $post->ID ) ) { |
|
538
|
|
|
return; |
|
539
|
|
|
} |
|
540
|
|
|
|
|
541
|
|
|
// Print the input template. |
|
542
|
|
|
$this->ui_service->print_template( 'wl-tmpl-alternative-label-input', $this->get_alternative_label_input() ); |
|
543
|
|
|
|
|
544
|
|
|
// Print all the currently set alternative labels. |
|
545
|
|
|
foreach ( $this->get_alternative_labels( $post->ID ) as $alt_label ) { |
|
546
|
|
|
|
|
547
|
|
|
echo $this->get_alternative_label_input( $alt_label ); |
|
548
|
|
|
|
|
549
|
|
|
}; |
|
550
|
|
|
|
|
551
|
|
|
// Print the button. |
|
552
|
|
|
$this->ui_service->print_button( 'wl-add-alternative-labels-button', __( 'Add more titles', 'wordlift' ) ); |
|
553
|
|
|
|
|
554
|
|
|
} |
|
555
|
|
|
|
|
556
|
|
|
/** |
|
557
|
|
|
* Add admin notices for the current entity depending on the current rating. |
|
558
|
|
|
* |
|
559
|
|
|
* @since 3.3.0 |
|
560
|
|
|
* |
|
561
|
|
|
* @param WP_Post $post Post object. |
|
|
|
|
|
|
562
|
|
|
*/ |
|
563
|
|
|
public function in_admin_header() { |
|
564
|
|
|
|
|
565
|
|
|
// Return safely if get_current_screen() is not defined (yet) |
|
566
|
|
|
if ( FALSE === function_exists( 'get_current_screen' ) ) { |
|
567
|
|
|
return; |
|
568
|
|
|
} |
|
569
|
|
|
|
|
570
|
|
|
$screen = get_current_screen(); |
|
571
|
|
|
// If there is any valid screen nothing to do |
|
572
|
|
|
if ( NULL === $screen ) { |
|
573
|
|
|
return $clauses; |
|
|
|
|
|
|
574
|
|
|
} |
|
575
|
|
|
|
|
576
|
|
|
// If you're not in the entity post edit page, return. |
|
577
|
|
|
if ( self::TYPE_NAME !== $screen->id ) { |
|
578
|
|
|
return; |
|
579
|
|
|
} |
|
580
|
|
|
// Retrieve the current global post |
|
581
|
|
|
global $post; |
|
|
|
|
|
|
582
|
|
|
// If it's not an entity, return. |
|
583
|
|
|
if ( ! $this->is_entity( $post->ID ) ) { |
|
584
|
|
|
return; |
|
585
|
|
|
} |
|
586
|
|
|
// Retrieve an updated rating for the current entity |
|
587
|
|
|
$rating = $this->get_rating_for( $post->ID, TRUE ); |
|
588
|
|
|
// If there is at least 1 warning |
|
589
|
|
|
if ( isset( $rating['warnings'] ) && 0 < count( $rating['warnings'] ) ) { |
|
590
|
|
|
// TODO - Pass Wordlift_Notice_Service trough the service constructor |
|
591
|
|
|
$this->notice_service->add_suggestion( $rating['warnings'] ); |
|
592
|
|
|
} |
|
593
|
|
|
|
|
594
|
|
|
} |
|
595
|
|
|
|
|
596
|
|
|
/** |
|
597
|
|
|
* Set rating for a given entity |
|
598
|
|
|
* |
|
599
|
|
|
* @since 3.3.0 |
|
600
|
|
|
* |
|
601
|
|
|
* @param int $post_id The entity post id. |
|
602
|
|
|
* |
|
603
|
|
|
* @return int An array representing the rating obj. |
|
604
|
|
|
*/ |
|
605
|
|
|
public function set_rating_for( $post_id ) { |
|
606
|
|
|
|
|
607
|
|
|
// Calculate rating for the given post |
|
608
|
|
|
$rating = $this->calculate_rating_for( $post_id ); |
|
609
|
|
|
// Store the rating on db as post meta |
|
610
|
|
|
// Please notice that RATING_RAW_SCORE_META_KEY |
|
611
|
|
|
// is saved on a different meta to allow score sorting |
|
612
|
|
|
// Both meta are managed as unique |
|
613
|
|
|
// https://codex.wordpress.org/Function_Reference/update_post_meta |
|
614
|
|
|
update_post_meta( $post_id, self::RATING_RAW_SCORE_META_KEY, $rating['raw_score'] ); |
|
615
|
|
|
update_post_meta( $post_id, self::RATING_WARNINGS_META_KEY, $rating['warnings'] ); |
|
616
|
|
|
|
|
617
|
|
|
$this->log_service->trace( sprintf( "Rating set for [ post_id :: $post_id ] [ rating :: %s ]", $rating['raw_score'] ) ); |
|
618
|
|
|
|
|
619
|
|
|
// Finally returns the rating |
|
620
|
|
|
return $rating; |
|
621
|
|
|
} |
|
622
|
|
|
|
|
623
|
|
|
/** |
|
624
|
|
|
* Get or calculate rating for a given entity |
|
625
|
|
|
* |
|
626
|
|
|
* @since 3.3.0 |
|
627
|
|
|
* |
|
628
|
|
|
* @param int $post_id The entity post id. |
|
629
|
|
|
* @param $force_reload $warnings_needed If true, detailed warnings collection is provided with the rating obj. |
|
|
|
|
|
|
630
|
|
|
* |
|
631
|
|
|
* @return int An array representing the rating obj. |
|
632
|
|
|
*/ |
|
633
|
|
|
public function get_rating_for( $post_id, $force_reload = FALSE ) { |
|
634
|
|
|
|
|
635
|
|
|
// If forced reload is required or rating is missing .. |
|
636
|
|
|
if ( $force_reload ) { |
|
637
|
|
|
$this->log_service->trace( "Force rating reload [ post_id :: $post_id ]" ); |
|
638
|
|
|
|
|
639
|
|
|
return $this->set_rating_for( $post_id ); |
|
640
|
|
|
} |
|
641
|
|
|
|
|
642
|
|
|
$current_raw_score = get_post_meta( $post_id, self::RATING_RAW_SCORE_META_KEY, TRUE ); |
|
643
|
|
|
|
|
644
|
|
|
if ( ! is_numeric( $current_raw_score ) ) { |
|
645
|
|
|
$this->log_service->trace( "Rating missing for [ post_id :: $post_id ] [ current_raw_score :: $current_raw_score ]" ); |
|
646
|
|
|
|
|
647
|
|
|
return $this->set_rating_for( $post_id ); |
|
648
|
|
|
} |
|
649
|
|
|
$current_warnings = get_post_meta( $post_id, self::RATING_WARNINGS_META_KEY, TRUE ); |
|
650
|
|
|
|
|
651
|
|
|
// Finally return score and warnings |
|
652
|
|
|
return array( |
|
653
|
|
|
'raw_score' => $current_raw_score, |
|
654
|
|
|
'traffic_light_score' => $this->convert_raw_score_to_traffic_light( $current_raw_score ), |
|
655
|
|
|
'percentage_score' => $this->convert_raw_score_to_percentage( $current_raw_score ), |
|
656
|
|
|
'warnings' => $current_warnings, |
|
657
|
|
|
); |
|
658
|
|
|
|
|
659
|
|
|
} |
|
660
|
|
|
|
|
661
|
|
|
/** |
|
662
|
|
|
* Calculate rating for a given entity |
|
663
|
|
|
* Rating depends from following criteria |
|
664
|
|
|
* |
|
665
|
|
|
* 1. Is the current entity related to at least 1 post? |
|
666
|
|
|
* 2. Is the current entity content post not empty? |
|
667
|
|
|
* 3. Is the current entity related to at least 1 entity? |
|
668
|
|
|
* 4. Is the entity published? |
|
669
|
|
|
* 5. There is a a thumbnail associated to the entity? |
|
670
|
|
|
* 6. Has the entity a sameas defined? |
|
671
|
|
|
* 7. Are all schema.org required metadata compiled? |
|
672
|
|
|
* |
|
673
|
|
|
* Each positive check means +1 in terms of rating score |
|
674
|
|
|
* |
|
675
|
|
|
* @since 3.3.0 |
|
676
|
|
|
* |
|
677
|
|
|
* @param int $post_id The entity post id. |
|
678
|
|
|
* |
|
679
|
|
|
* @return int An array representing the rating obj. |
|
680
|
|
|
*/ |
|
681
|
|
|
public function calculate_rating_for( $post_id ) { |
|
682
|
|
|
|
|
683
|
|
|
// If it's not an entity, return. |
|
684
|
|
|
if ( ! $this->is_entity( $post_id ) ) { |
|
685
|
|
|
return; |
|
686
|
|
|
} |
|
687
|
|
|
// Retrieve the post object |
|
688
|
|
|
$post = get_post( $post_id ); |
|
689
|
|
|
// Rating value |
|
690
|
|
|
$score = 0; |
|
691
|
|
|
// Store warning messages |
|
692
|
|
|
$warnings = array(); |
|
693
|
|
|
|
|
694
|
|
|
// Is the current entity related to at least 1 post? |
|
695
|
|
|
( 0 < count( wl_core_get_related_post_ids( $post->ID ) ) ) ? |
|
696
|
|
|
$score ++ : |
|
697
|
|
|
array_push( $warnings, __( self::RATING_WARNING_HAS_RELATED_POSTS, 'wordlift' ) ); |
|
698
|
|
|
|
|
699
|
|
|
// Is the post content not empty? |
|
700
|
|
|
( ! empty( $post->post_content ) ) ? |
|
701
|
|
|
$score ++ : |
|
702
|
|
|
array_push( $warnings, __( self::RATING_WARNING_HAS_CONTENT_POST, 'wordlift' ) ); |
|
703
|
|
|
|
|
704
|
|
|
// Is the current entity related to at least 1 entity? |
|
705
|
|
|
// Was the current entity already disambiguated? |
|
706
|
|
|
( 0 < count( wl_core_get_related_entity_ids( $post->ID ) ) ) ? |
|
707
|
|
|
$score ++ : |
|
708
|
|
|
array_push( $warnings, __( self::RATING_WARNING_HAS_RELATED_ENTITIES, 'wordlift' ) ); |
|
709
|
|
|
|
|
710
|
|
|
// Is the entity published? |
|
711
|
|
|
( 'publish' === get_post_status( $post->ID ) ) ? |
|
712
|
|
|
$score ++ : |
|
713
|
|
|
array_push( $warnings, __( self::RATING_WARNING_IS_PUBLISHED, 'wordlift' ) ); |
|
714
|
|
|
|
|
715
|
|
|
// Has a thumbnail? |
|
716
|
|
|
( has_post_thumbnail( $post->ID ) ) ? |
|
717
|
|
|
$score ++ : |
|
718
|
|
|
array_push( $warnings, __( self::RATING_WARNING_HAS_THUMBNAIL, 'wordlift' ) ); |
|
719
|
|
|
|
|
720
|
|
|
// Get all post meta keys for the current post |
|
721
|
|
|
global $wpdb; |
|
|
|
|
|
|
722
|
|
|
$query = $wpdb->prepare( |
|
723
|
|
|
"SELECT DISTINCT(meta_key) FROM $wpdb->postmeta WHERE post_id = %d", $post->ID |
|
724
|
|
|
); |
|
725
|
|
|
|
|
726
|
|
|
// Check intersection between available meta keys |
|
727
|
|
|
// and expected ones arrays to detect missing values |
|
728
|
|
|
$available_meta_keys = $wpdb->get_col( $query ); |
|
729
|
|
|
|
|
730
|
|
|
// If each expected key is contained in available keys array ... |
|
731
|
|
|
( in_array( Wordlift_Schema_Service::FIELD_SAME_AS, $available_meta_keys ) ) ? |
|
732
|
|
|
$score ++ : |
|
733
|
|
|
array_push( $warnings, __( self::RATING_WARNING_HAS_SAME_AS, 'wordlift' ) ); |
|
734
|
|
|
|
|
735
|
|
|
$schema = wl_entity_type_taxonomy_get_type( $post_id ); |
|
|
|
|
|
|
736
|
|
|
|
|
737
|
|
|
$expected_meta_keys = ( NULL === $schema['custom_fields'] ) ? |
|
738
|
|
|
array() : |
|
739
|
|
|
array_keys( $schema['custom_fields'] ); |
|
740
|
|
|
|
|
741
|
|
|
$intersection = array_intersect( $expected_meta_keys, $available_meta_keys ); |
|
742
|
|
|
// If each expected key is contained in available keys array ... |
|
743
|
|
|
( count( $intersection ) === count( $expected_meta_keys ) ) ? |
|
744
|
|
|
$score ++ : |
|
745
|
|
|
array_push( $warnings, __( self::RATING_WARNING_HAS_COMPLETED_METADATA, 'wordlift' ) ); |
|
746
|
|
|
|
|
747
|
|
|
// Finally return score and warnings |
|
748
|
|
|
return array( |
|
749
|
|
|
'raw_score' => $score, |
|
750
|
|
|
'traffic_light_score' => $this->convert_raw_score_to_traffic_light( $score ), |
|
751
|
|
|
'percentage_score' => $this->convert_raw_score_to_percentage( $score ), |
|
752
|
|
|
'warnings' => $warnings, |
|
753
|
|
|
); |
|
754
|
|
|
|
|
755
|
|
|
} |
|
756
|
|
|
|
|
757
|
|
|
/** |
|
758
|
|
|
* Get the URI for the entity with the specified post id. |
|
759
|
|
|
* |
|
760
|
|
|
* @since 3.6.0 |
|
761
|
|
|
* |
|
762
|
|
|
* @param int $post_id The entity post id. |
|
763
|
|
|
* |
|
764
|
|
|
* @return null|string The entity URI or NULL if not found or the dataset URI is not configured. |
|
765
|
|
|
*/ |
|
766
|
|
|
public function get_uri( $post_id ) { |
|
767
|
|
|
|
|
768
|
|
|
// If a null is given, nothing to do |
|
769
|
|
|
if ( NULL == $post_id ) { |
|
770
|
|
|
return NULL; |
|
771
|
|
|
} |
|
772
|
|
|
|
|
773
|
|
|
$uri = get_post_meta( $post_id, WL_ENTITY_URL_META_NAME, TRUE ); |
|
774
|
|
|
|
|
775
|
|
|
// If the dataset uri is not properly configured, null is returned |
|
776
|
|
|
if ( '' === wl_configuration_get_redlink_dataset_uri() ) { |
|
777
|
|
|
return NULL; |
|
778
|
|
|
} |
|
779
|
|
|
|
|
780
|
|
|
// Set the URI if it isn't set yet. |
|
781
|
|
|
$post_status = get_post_status( $post_id ); |
|
782
|
|
|
if ( empty( $uri ) && 'auto-draft' !== $post_status && 'revision' !== $post_status ) { |
|
783
|
|
|
$uri = wl_build_entity_uri( $post_id ); |
|
784
|
|
|
wl_set_entity_uri( $post_id, $uri ); |
|
785
|
|
|
} |
|
786
|
|
|
|
|
787
|
|
|
return $uri; |
|
788
|
|
|
} |
|
789
|
|
|
|
|
790
|
|
|
/** |
|
791
|
|
|
* Get as rating as input and convert in a traffic-light rating |
|
792
|
|
|
* |
|
793
|
|
|
* @since 3.3.0 |
|
794
|
|
|
* |
|
795
|
|
|
* @param int $score The rating score for a given entity. |
|
796
|
|
|
* |
|
797
|
|
|
* @return string The input HTML code. |
|
798
|
|
|
*/ |
|
799
|
|
|
private function convert_raw_score_to_traffic_light( $score ) { |
|
800
|
|
|
// RATING_MAX : $score = 3 : x |
|
801
|
|
|
// See http://php.net/manual/en/function.round.php |
|
802
|
|
|
$rating = round( ( $score * 3 ) / self::get_rating_max(), 0, PHP_ROUND_HALF_UP ); |
|
803
|
|
|
|
|
804
|
|
|
// If rating is 0, return 1, otherwise return rating |
|
805
|
|
|
return ( 0 == $rating ) ? 1 : $rating; |
|
806
|
|
|
|
|
807
|
|
|
} |
|
808
|
|
|
|
|
809
|
|
|
/** |
|
810
|
|
|
* Get as rating as input and convert in a traffic-light rating |
|
811
|
|
|
* |
|
812
|
|
|
* @since 3.3.0 |
|
813
|
|
|
* |
|
814
|
|
|
* @param int $score The rating score for a given entity. |
|
815
|
|
|
* |
|
816
|
|
|
* @return string The input HTML code. |
|
817
|
|
|
*/ |
|
818
|
|
|
public function convert_raw_score_to_percentage( $score ) { |
|
819
|
|
|
// RATING_MAX : $score = 100 : x |
|
820
|
|
|
return round( ( $score * 100 ) / self::get_rating_max(), 0, PHP_ROUND_HALF_UP ); |
|
821
|
|
|
} |
|
822
|
|
|
|
|
823
|
|
|
/** |
|
824
|
|
|
* Get the alternative label input HTML code. |
|
825
|
|
|
* |
|
826
|
|
|
* @since 3.2.0 |
|
827
|
|
|
* |
|
828
|
|
|
* @param string $value The input value. |
|
829
|
|
|
* |
|
830
|
|
|
* @return string The input HTML code. |
|
831
|
|
|
*/ |
|
832
|
|
|
private function get_alternative_label_input( $value = '' ) { |
|
833
|
|
|
|
|
834
|
|
|
return sprintf( self::ALTERNATIVE_LABEL_INPUT_TEMPLATE, esc_attr( $value ), __( 'Delete', 'wordlift' ) ); |
|
835
|
|
|
} |
|
836
|
|
|
|
|
837
|
|
|
/** |
|
838
|
|
|
* Get the number of entity posts published in this blog. |
|
839
|
|
|
* |
|
840
|
|
|
* @since 3.6.0 |
|
841
|
|
|
* |
|
842
|
|
|
* @return int The number of published entity posts. |
|
843
|
|
|
*/ |
|
844
|
|
|
public function count() { |
|
845
|
|
|
|
|
846
|
|
|
$count = wp_count_posts( self::TYPE_NAME ); |
|
847
|
|
|
|
|
848
|
|
|
return $count->publish; |
|
849
|
|
|
} |
|
850
|
|
|
|
|
851
|
|
|
/** |
|
852
|
|
|
* Create a new entity. |
|
853
|
|
|
* |
|
854
|
|
|
* @since 3.9.0 |
|
855
|
|
|
* |
|
856
|
|
|
* @param string $name The entity name. |
|
857
|
|
|
* @param string $type_uri The entity's type URI. |
|
858
|
|
|
* @param null $logo The entity logo id (or NULL if none). |
|
859
|
|
|
* @param string $status The post status, by default 'publish'. |
|
860
|
|
|
* |
|
861
|
|
|
* @return int|WP_Error The entity post id or a {@link WP_Error} in case the `wp_insert_post` call fails. |
|
862
|
|
|
*/ |
|
863
|
|
|
public function create( $name, $type_uri, $logo = NULL, $status = 'publish' ) { |
|
864
|
|
|
|
|
865
|
|
|
// Create an entity for the publisher. |
|
866
|
|
|
$post_id = wp_insert_post( array( |
|
867
|
|
|
'post_type' => self::TYPE_NAME, |
|
868
|
|
|
'post_title' => $name, |
|
869
|
|
|
'post_status' => $status, |
|
870
|
|
|
'post_content' => '', |
|
871
|
|
|
) ); |
|
872
|
|
|
|
|
873
|
|
|
// Return the error if any. |
|
874
|
|
|
if ( is_wp_error( $post_id ) ) { |
|
875
|
|
|
return $post_id; |
|
876
|
|
|
} |
|
877
|
|
|
|
|
878
|
|
|
// Set the entity logo. |
|
879
|
|
|
if ( $logo && is_numeric( $logo ) ) { |
|
880
|
|
|
set_post_thumbnail( $post_id, $logo ); |
|
881
|
|
|
} |
|
882
|
|
|
|
|
883
|
|
|
// Set the entity type. |
|
884
|
|
|
Wordlift_Entity_Type_Service::get_instance()->set( $post_id, $type_uri ); |
|
885
|
|
|
|
|
886
|
|
|
return $post_id; |
|
887
|
|
|
} |
|
888
|
|
|
|
|
889
|
|
|
} |
|
890
|
|
|
|
This function has been deprecated. The supplier of the file has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.