Completed
Push — develop ( df3f0b...88eeb8 )
by David
07:56 queued 02:04
created

Wordlift_Entity_Service::save_post()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 19
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 5
nc 3
nop 3
dl 0
loc 19
rs 8.8571
c 0
b 0
f 0
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;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
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 	
0 ignored issues
show
Coding Style introduced by
There is some trailing whitespace on this line which should be avoided as per coding-style.
Loading history...
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;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
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 )
0 ignored issues
show
Deprecated Code introduced by
The function wl_get_entity_uri() has been deprecated with message: use Wordlift_Entity_Service::get_instance()->get_uri( $post_id )

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.

Loading history...
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 ) {
0 ignored issues
show
Unused Code introduced by
The parameter $post is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $update is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Coding Style introduced by
save_post uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
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.
0 ignored issues
show
Bug introduced by
There is no parameter named $post. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
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;
0 ignored issues
show
Bug introduced by
The variable $clauses does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
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;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
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.
0 ignored issues
show
Documentation introduced by
The doc-type $force_reload could not be parsed: Unknown type name "$force_reload" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
Bug introduced by
There is no parameter named $warnings_needed. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
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;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
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