PluginProbe
Parse.ly / 3.18.1
Parse.ly v3.18.1
3.24.1 3.24.0 3.23.7 3.23.6 3.23.5 3.23.4 3.23.3 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.17.0 3.18.0 3.18.1 3.19.0 3.19.1 3.19.2 3.19.3 3.2.0 3.2.1 3.20.0 3.20.1 3.20.2 3.20.3 All 105 releases
wp-parsely / src / Models / class-inbound-smart-link.php

class-inbound-smart-link.php in Parse.ly 3.18.1, at src/Models/class-inbound-smart-link.php

237 lines 6.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Model for Inbound Smart Link.
4 *
5 * @package Parsely
6 * @since 3.16.0
7 */
8
9 declare( strict_types = 1 );
10
11 namespace Parsely\Models;
12
13 use DOMDocument;
14 use ReflectionClass;
15 use WP_Post;
16
17 /**
18 * Model for Inbound Smart Link.
19 *
20 * @since 3.16.0
21 */
22 class Inbound_Smart_Link extends Smart_Link {
23
24 /**
25 * The source post object.
26 *
27 * @since 3.16.0
28 *
29 * @var WP_Post|null The source post.
30 */
31 private $source_post;
32
33 /**
34 * The paragraph data.
35 *
36 * @since 3.16.0
37 *
38 * @var array<string,mixed>|null The paragraph data.
39 * @phpstan-var array{paragraph: string, is_first_paragraph: bool, is_last_paragraph: bool}|null
40 */
41 private $paragraph_data;
42
43 /**
44 * The post data.
45 *
46 * @since 3.16.0
47 *
48 * @var array<string,mixed>|null The post data.
49 */
50 private $post_data;
51
52 /**
53 * Serializes the model to a JSON string, and adds extra data.
54 *
55 * @since 3.16.0
56 *
57 * @return array<mixed> The serialized model.
58 */
59 public function to_array(): array {
60 $data = parent::to_array();
61
62 $data['post_data'] = $this->get_post_data();
63
64 return $data;
65 }
66
67 /**
68 * Checks if the Smart Link is linked to a post.
69 *
70 * @since 3.16.0
71 *
72 * @return bool True if the Smart Link is linked to a post, false otherwise.
73 */
74 public function is_linked(): bool {
75 $object_exists = parent::exists();
76 $paragraph_data = $this->get_post_data();
77
78 // If the paragraph is empty, we assume that the Smart Link is not linked to a post.
79 return $object_exists && '' !== $paragraph_data['paragraph'];
80 }
81
82 /**
83 * Gets the post data for the smart link.
84 *
85 * @since 3.16.0
86 *
87 * @return array<mixed> The post data.
88 */
89 private function get_post_data(): array {
90 if ( null !== $this->post_data ) {
91 return $this->post_data;
92 }
93
94 if ( null === $this->source_post ) {
95 $this->source_post = get_post( $this->source_post_id );
96 }
97
98 $post = $this->source_post;
99
100 if ( ! $post instanceof WP_Post ) {
101 return array();
102 }
103
104 // Get the post content.
105 $content = $post->post_content;
106 // Get the paragraph that has the smart link UID.
107 $paragraph = $this->get_paragraph( $content );
108
109 $author_name = get_the_author();
110 if ( '' === $author_name ) {
111 // If the author name is empty, use the author login name.
112 $author_name = get_the_author_meta( 'user_login', intval( $post->post_author ) );
113 }
114
115 $post_type = get_post_type_object( $post->post_type );
116 $post_type_label = '';
117 if ( null !== $post_type ) {
118 $post_type_label = $post_type->labels->singular_name;
119 }
120
121 $this->post_data = array(
122 'id' => $post->ID,
123 'title' => $post->post_title,
124 'type' => $post_type_label,
125 'paragraph' => $paragraph['paragraph'],
126 'permalink' => get_permalink( $post ),
127 'edit_link' => get_edit_post_link( $post, 'html' ),
128 'is_first_paragraph' => $paragraph['is_first_paragraph'],
129 'is_last_paragraph' => $paragraph['is_last_paragraph'],
130 'author' => $author_name,
131 'date' => get_the_date( '', $post ),
132 'image' => get_the_post_thumbnail_url( $post, 'medium' ),
133 );
134
135 return $this->post_data;
136 }
137
138 /**
139 * Get the HTML paragraph that has the smart link UID.
140 *
141 * @since 3.16.0
142 *
143 * @param string $content The post content.
144 * @return array The paragraph that has the smart link uid, and if it is the first or last paragraph.
145 * @phpstan-return array{paragraph: string, is_first_paragraph: bool, is_last_paragraph: bool}
146 */
147 private function get_paragraph( string $content ): array {
148 if ( null !== $this->paragraph_data ) {
149 return $this->paragraph_data;
150 }
151
152 $paragraph = '';
153 if ( ! class_exists( 'DOMDocument' ) ) {
154 return array(
155 'paragraph' =>
156 '<p>' . __( 'Unable to fetch paragraph. DOMDocument is not available.', 'wp-parsely' ) . '</p>',
157 'is_first_paragraph' => true,
158 'is_last_paragraph' => true,
159 );
160 }
161
162 libxml_use_internal_errors( true );
163
164 $dom = new DOMDocument();
165 $dom->loadHTML( mb_convert_encoding( $content, 'HTML-ENTITIES', 'UTF-8' ), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD );
166
167 $errors = libxml_get_errors();
168 if ( count( $errors ) > 0 ) {
169 libxml_clear_errors();
170 return array(
171 'paragraph' =>
172 '<p>' . __( 'Unable to fetch paragraph. Error loading HTML.', 'wp-parsely' ) . '</p>',
173 'is_first_paragraph' => true,
174 'is_last_paragraph' => true,
175 );
176 }
177
178 // Fetch all paragraph tags.
179 $paragraphs = $dom->getElementsByTagName( 'p' );
180
181 $is_first_paragraph = true;
182 $is_last_paragraph = false;
183
184 foreach ( $paragraphs as $p ) {
185 // Check each anchor tag within the paragraph.
186 $anchors = $p->getElementsByTagName( 'a' );
187 foreach ( $anchors as $anchor ) {
188 // Check if the data-smartlink attribute contains the UID.
189 if ( $anchor->hasAttribute( 'data-smartlink' ) && stripos( $anchor->getAttribute( 'data-smartlink' ), $this->uid ) !== false ) {
190 // Save the outer HTML of the paragraph.
191 $is_first_paragraph = $p === $paragraphs->item( 0 );
192 $is_last_paragraph = $p === $paragraphs->item( $paragraphs->length - 1 );
193 $paragraph = $dom->saveHTML( $p );
194 break 2;
195 }
196 }
197 }
198
199 if ( false === $paragraph ) {
200 $paragraph = '<p>' . __( 'Unable to fetch paragraph.', 'wp-parsely' ) . '</p>';
201 }
202
203 $this->paragraph_data = array(
204 'paragraph' => $paragraph,
205 'is_first_paragraph' => $is_first_paragraph,
206 'is_last_paragraph' => $is_last_paragraph,
207 );
208
209 return $this->paragraph_data;
210 }
211
212 /**
213 * Creates a new instance of an Inbound Smart Link from a Smart Link object.
214 *
215 * This is used to convert a Smart Link object to an Inbound Smart Link object.
216 *
217 * @since 3.16.0
218 *
219 * @param Smart_Link $smart_link The Smart Link object.
220 * @return Inbound_Smart_Link The Inbound Smart Link object.
221 */
222 public static function from_smart_link( Smart_Link $smart_link ): Inbound_Smart_Link {
223 $inbound_smart_link = new self( '', '', '', 0 );
224 $reflection_class = new ReflectionClass( $smart_link );
225
226 foreach ( $reflection_class->getProperties() as $property ) {
227 // Make the property accessible.
228 $property->setAccessible( true );
229 $value = $property->getValue( $smart_link );
230 // Copy the property value.
231 $property->setValue( $inbound_smart_link, $value );
232 }
233
234 return $inbound_smart_link;
235 }
236 }
237