PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 12.5.2
Jetpack – WP Security, Backup, Speed, & Growth v12.5.2
12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 14.4.2 14.5.1 14.6.1 14.7.1 14.8.1 14.9.2 15.0.3 15.1.2 15.2.1 15.3.2 15.4.1 15.5.1 15.6.1 15.7.2 15.8.1 15.9.2 16.0.2 16.1.3 16.2-a.5 16.2-a.3 16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / vendor / nojimage / twitter-text-php / lib / Twitter / Text / Autolink.php
jetpack / vendor / nojimage / twitter-text-php / lib / Twitter / Text Last commit date
Autolink.php 5 years ago Configuration.php 5 years ago EmojiRegex.php 5 years ago Extractor.php 5 years ago HitHighlighter.php 5 years ago ParseResults.php 5 years ago Parser.php 5 years ago Regex.php 5 years ago StringUtils.php 5 years ago TldLists.php 5 years ago Validator.php 5 years ago
Autolink.php
935 lines
1 <?php
2
3 /**
4 * @author Mike Cochrane <mikec@mikenz.geek.nz>
5 * @author Nick Pope <nick@nickpope.me.uk>
6 * @copyright Copyright © 2010, Mike Cochrane, Nick Pope
7 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
8 * @package Twitter.Text
9 */
10
11 namespace Twitter\Text;
12
13 /**
14 * Twitter Autolink Class
15 *
16 * Parses tweets and generates HTML anchor tags around URLs, usernames,
17 * username/list pairs and hashtags.
18 *
19 * Originally written by {@link http://github.com/mikenz Mike Cochrane}, this
20 * is based on code by {@link http://github.com/mzsanford Matt Sanford} and
21 * heavily modified by {@link http://github.com/ngnpope Nick Pope}.
22 *
23 * @author Mike Cochrane <mikec@mikenz.geek.nz>
24 * @author Nick Pope <nick@nickpope.me.uk>
25 * @copyright Copyright © 2010, Mike Cochrane, Nick Pope
26 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
27 * @package Twitter.Text
28 */
29 class Autolink
30 {
31
32 /**
33 * CSS class for auto-linked URLs.
34 *
35 * @var string
36 */
37 protected $class_url = '';
38
39 /**
40 * CSS class for auto-linked username URLs.
41 *
42 * @var string
43 */
44 protected $class_user = 'tweet-url username';
45
46 /**
47 * CSS class for auto-linked list URLs.
48 *
49 * @var string
50 */
51 protected $class_list = 'tweet-url list-slug';
52
53 /**
54 * CSS class for auto-linked hashtag URLs.
55 *
56 * @var string
57 */
58 protected $class_hash = 'tweet-url hashtag';
59
60 /**
61 * CSS class for auto-linked cashtag URLs.
62 *
63 * @var string
64 */
65 protected $class_cash = 'tweet-url cashtag';
66
67 /**
68 * URL base for username links (the username without the @ will be appended).
69 *
70 * @var string
71 */
72 protected $url_base_user = 'https://twitter.com/';
73
74 /**
75 * URL base for list links (the username/list without the @ will be appended).
76 *
77 * @var string
78 */
79 protected $url_base_list = 'https://twitter.com/';
80
81 /**
82 * URL base for hashtag links (the hashtag without the # will be appended).
83 *
84 * @var string
85 */
86 protected $url_base_hash = 'https://twitter.com/search?q=%23';
87
88 /**
89 * URL base for cashtag links (the hashtag without the $ will be appended).
90 *
91 * @var string
92 */
93 protected $url_base_cash = 'https://twitter.com/search?q=%24';
94
95 /**
96 * the 'rel' attribute values.
97 *
98 * @var array
99 */
100 protected $rel = array('external', 'nofollow');
101
102 /**
103 * The scope to open the link in.
104 *
105 * Support for the 'target' attribute was deprecated in HTML 4.01 but has
106 * since been reinstated in HTML 5. To output the 'target' attribute you
107 * must disable the adding of the string 'external' to the 'rel' attribute.
108 *
109 * @var string
110 */
111 protected $target = '_blank';
112
113 /**
114 * attribute for invisible span tag
115 *
116 * @var string
117 */
118 protected $invisibleTagAttrs = "style='position:absolute;left:-9999px;'";
119
120 /**
121 * If the at mark '@' should be included in the link (false by default)
122 *
123 * @var bool
124 * @since 3.0.1
125 */
126 protected $usernameIncludeSymbol = false;
127
128 /**
129 * HTML tag to be applied around #/@/# symbols in hashtags/usernames/lists/cashtag
130 *
131 * @var string
132 * @since 3.0.1
133 */
134 protected $symbolTag = '';
135
136 /**
137 * HTML tag to be applied around text part of hashtags/usernames/lists/cashtag
138 *
139 * @var string
140 * @since 3.0.1
141 */
142 protected $textWithSymbolTag = '';
143
144 /**
145 *
146 * @var Extractor
147 */
148 protected $extractor = null;
149
150 /**
151 * The tweet to be used in parsing.
152 *
153 * @var string
154 * @deprecated will be removed
155 */
156 protected $tweet = '';
157
158 /**
159 * Provides fluent method chaining.
160 *
161 * @param string $tweet [deprecated] The tweet to be converted.
162 * @param bool $full_encode [deprecated] Whether to encode all special characters.
163 *
164 * @see __construct()
165 *
166 * @return Autolink
167 */
168 public static function create($tweet = null, $full_encode = false)
169 {
170 return new static($tweet, $full_encode);
171 }
172
173 /**
174 * Reads in a tweet to be parsed and converted to contain links.
175 *
176 * As the intent is to produce links and output the modified tweet to the
177 * user, we take this opportunity to ensure that we escape user input.
178 *
179 * @see htmlspecialchars()
180 *
181 * @param string $tweet [deprecated] The tweet to be converted.
182 * @param bool $escape [deprecated] Whether to escape the tweet (default: true).
183 * @param bool $full_encode [deprecated] Whether to encode all special characters.
184 */
185 public function __construct($tweet = null, $escape = true, $full_encode = false)
186 {
187 if ($escape && !empty($tweet)) {
188 if ($full_encode) {
189 $this->tweet = htmlentities($tweet, ENT_QUOTES, 'UTF-8', false);
190 } else {
191 $this->tweet = htmlspecialchars($tweet, ENT_QUOTES, 'UTF-8', false);
192 }
193 } else {
194 $this->tweet = $tweet;
195 }
196
197 $this->extractor = Extractor::create();
198 }
199
200 /**
201 * Set CSS class to all link types.
202 *
203 * @param string $v CSS class for links.
204 *
205 * @return Autolink Fluid method chaining.
206 */
207 public function setToAllLinkClasses($v)
208 {
209 $this->setURLClass($v);
210 $this->setUsernameClass($v);
211 $this->setListClass($v);
212 $this->setHashtagClass($v);
213 $this->setCashtagClass($v);
214
215 return $this;
216 }
217
218 /**
219 * CSS class for auto-linked URLs.
220 *
221 * @return string CSS class for URL links.
222 */
223 public function getURLClass()
224 {
225 return $this->class_url;
226 }
227
228 /**
229 * CSS class for auto-linked URLs.
230 *
231 * @param string $v CSS class for URL links.
232 *
233 * @return Autolink Fluid method chaining.
234 */
235 public function setURLClass($v)
236 {
237 $this->class_url = trim($v);
238 return $this;
239 }
240
241 /**
242 * CSS class for auto-linked username URLs.
243 *
244 * @return string CSS class for username links.
245 */
246 public function getUsernameClass()
247 {
248 return $this->class_user;
249 }
250
251 /**
252 * CSS class for auto-linked username URLs.
253 *
254 * @param string $v CSS class for username links.
255 *
256 * @return Autolink Fluid method chaining.
257 */
258 public function setUsernameClass($v)
259 {
260 $this->class_user = trim($v);
261 return $this;
262 }
263
264 /**
265 * CSS class for auto-linked username/list URLs.
266 *
267 * @return string CSS class for username/list links.
268 */
269 public function getListClass()
270 {
271 return $this->class_list;
272 }
273
274 /**
275 * CSS class for auto-linked username/list URLs.
276 *
277 * @param string $v CSS class for username/list links.
278 *
279 * @return Autolink Fluid method chaining.
280 */
281 public function setListClass($v)
282 {
283 $this->class_list = trim($v);
284 return $this;
285 }
286
287 /**
288 * CSS class for auto-linked hashtag URLs.
289 *
290 * @return string CSS class for hashtag links.
291 */
292 public function getHashtagClass()
293 {
294 return $this->class_hash;
295 }
296
297 /**
298 * CSS class for auto-linked hashtag URLs.
299 *
300 * @param string $v CSS class for hashtag links.
301 *
302 * @return Autolink Fluid method chaining.
303 */
304 public function setHashtagClass($v)
305 {
306 $this->class_hash = trim($v);
307 return $this;
308 }
309
310 /**
311 * CSS class for auto-linked cashtag URLs.
312 *
313 * @return string CSS class for cashtag links.
314 */
315 public function getCashtagClass()
316 {
317 return $this->class_cash;
318 }
319
320 /**
321 * CSS class for auto-linked cashtag URLs.
322 *
323 * @param string $v CSS class for cashtag links.
324 *
325 * @return Autolink Fluid method chaining.
326 */
327 public function setCashtagClass($v)
328 {
329 $this->class_cash = trim($v);
330 return $this;
331 }
332
333 /**
334 * Whether to include the value 'nofollow' in the 'rel' attribute.
335 *
336 * @return bool Whether to add 'nofollow' to the 'rel' attribute.
337 */
338 public function getNoFollow()
339 {
340 return in_array('nofollow', $this->rel, true);
341 }
342
343 /**
344 * Whether to include the value 'nofollow' in the 'rel' attribute.
345 *
346 * @param bool $v The value to add to the 'target' attribute.
347 *
348 * @return Autolink Fluid method chaining.
349 */
350 public function setNoFollow($v)
351 {
352 if ($v && !$this->getNoFollow()) {
353 $this->setRel('nofollow', true);
354 }
355 if (!$v && $this->getNoFollow()) {
356 $this->rel = array_filter($this->rel, function ($r) {
357 return $r !== 'nofollow';
358 });
359 }
360
361 return $this;
362 }
363
364 /**
365 * Whether to include the value 'external' in the 'rel' attribute.
366 *
367 * Often this is used to be matched on in JavaScript for dynamically adding
368 * the 'target' attribute which is deprecated in HTML 4.01. In HTML 5 it has
369 * been undeprecated and thus the 'target' attribute can be used. If this is
370 * set to false then the 'target' attribute will be output.
371 *
372 * @return bool Whether to add 'external' to the 'rel' attribute.
373 */
374 public function getExternal()
375 {
376 return in_array('external', $this->rel, true);
377 }
378
379 /**
380 * Whether to include the value 'external' in the 'rel' attribute.
381 *
382 * Often this is used to be matched on in JavaScript for dynamically adding
383 * the 'target' attribute which is deprecated in HTML 4.01. In HTML 5 it has
384 * been undeprecated and thus the 'target' attribute can be used. If this is
385 * set to false then the 'target' attribute will be output.
386 *
387 * @param bool $v The value to add to the 'target' attribute.
388 *
389 * @return Autolink Fluid method chaining.
390 */
391 public function setExternal($v)
392 {
393 if ($v && !$this->getExternal()) {
394 $this->setRel('external', true);
395 }
396 if (!$v && $this->getExternal()) {
397 $this->rel = array_filter($this->rel, function ($r) {
398 return $r !== 'external';
399 });
400 }
401
402 return $this;
403 }
404
405 /**
406 * The scope to open the link in.
407 *
408 * Support for the 'target' attribute was deprecated in HTML 4.01 but has
409 * since been reinstated in HTML 5. To output the 'target' attribute you
410 * must disable the adding of the string 'external' to the 'rel' attribute.
411 *
412 * @return string The value to add to the 'target' attribute.
413 */
414 public function getTarget()
415 {
416 return $this->target;
417 }
418
419 /**
420 * The scope to open the link in.
421 *
422 * Support for the 'target' attribute was deprecated in HTML 4.01 but has
423 * since been reinstated in HTML 5. To output the 'target' attribute you
424 * must disable the adding of the string 'external' to the 'rel' attribute.
425 *
426 * @param string $v The value to add to the 'target' attribute.
427 *
428 * @return Autolink Fluid method chaining.
429 */
430 public function setTarget($v)
431 {
432 $this->target = trim($v);
433 return $this;
434 }
435
436 /**
437 * @return bool
438 * @since 3.0.1
439 */
440 public function isUsernameIncludeSymbol()
441 {
442 return $this->usernameIncludeSymbol;
443 }
444
445 /**
446 * Set if the at mark '@' should be included in the link (false by default)
447 *
448 * @param bool $usernameIncludeSymbol if username includes symbol
449 * @return Autolink
450 * @since 3.0.1
451 */
452 public function setUsernameIncludeSymbol($usernameIncludeSymbol)
453 {
454 $this->usernameIncludeSymbol = $usernameIncludeSymbol;
455
456 return $this;
457 }
458
459 /**
460 * @return string
461 * @since 3.0.1
462 */
463 public function getSymbolTag()
464 {
465 return $this->symbolTag;
466 }
467
468 /**
469 * Set HTML tag to be applied around #/@/# symbols in hashtags/usernames/lists/cashtag
470 *
471 * @param string $symbolTag HTML tag without bracket. e.g., 'b' or 's'
472 * @return Autolink
473 * @since 3.0.1
474 */
475 public function setSymbolTag($symbolTag)
476 {
477 $this->symbolTag = $symbolTag;
478
479 return $this;
480 }
481
482 /**
483 * @return string
484 * @since 3.0.1
485 */
486 public function getTextWithSymbolTag()
487 {
488 return $this->textWithSymbolTag;
489 }
490
491 /**
492 * Set HTML tag to be applied around text part of hashtags/usernames/lists/cashtag
493 *
494 * @param string $textWithSymbolTag HTML tag without bracket. e.g., 'b' or 's'
495 * @return Autolink
496 * @since 3.0.1
497 */
498 public function setTextWithSymbolTag($textWithSymbolTag)
499 {
500 $this->textWithSymbolTag = $textWithSymbolTag;
501
502 return $this;
503 }
504
505 /**
506 * Autolink with entities
507 *
508 * @param string $tweet
509 * @param array $entities
510 * @return string
511 * @since 1.1.0
512 */
513 public function autoLinkEntities($tweet = null, $entities = null)
514 {
515 if ($tweet === null) {
516 $tweet = $this->tweet;
517 }
518
519 $text = '';
520 $beginIndex = 0;
521 foreach ($entities as $entity) {
522 $text .= StringUtils::substr($tweet, $beginIndex, $entity['indices'][0] - $beginIndex);
523
524 if (isset($entity['url'])) {
525 $text .= $this->linkToUrl($entity);
526 } elseif (isset($entity['hashtag'])) {
527 $text .= $this->linkToHashtag($entity, $tweet);
528 } elseif (isset($entity['screen_name'])) {
529 $text .= $this->linkToMentionAndList($entity, $tweet);
530 } elseif (isset($entity['cashtag'])) {
531 $text .= $this->linkToCashtag($entity, $tweet);
532 }
533 $beginIndex = $entity['indices'][1];
534 }
535 $text .= StringUtils::substr($tweet, $beginIndex, StringUtils::strlen($tweet));
536 return $text;
537 }
538
539 /**
540 * Auto-link hashtags, URLs, usernames and lists, with JSON entities.
541 *
542 * @param string The tweet to be converted
543 * @param mixed The entities info
544 * @return string that auto-link HTML added
545 * @since 1.1.0
546 */
547 public function autoLinkWithJson($tweet = null, $json = null)
548 {
549 // concatenate entities
550 $entities = array();
551 if (is_object($json)) {
552 $json = $this->object2array($json);
553 }
554 if (is_array($json)) {
555 $entities = call_user_func_array('array_merge', $json);
556 }
557
558 // map JSON entity to twitter-text entity
559 foreach ($entities as $idx => $entity) {
560 if (!empty($entity['text'])) {
561 $entities[$idx]['hashtag'] = $entity['text'];
562 }
563 }
564
565 $entities = $this->extractor->removeOverlappingEntities($entities);
566 return $this->autoLinkEntities($tweet, $entities);
567 }
568
569 /**
570 * convert Object to Array
571 *
572 * @param mixed $obj
573 * @return array
574 */
575 protected function object2array($obj)
576 {
577 $array = (array) $obj;
578 foreach ($array as $key => $var) {
579 if (is_object($var) || is_array($var)) {
580 $array[$key] = $this->object2array($var);
581 }
582 }
583 return $array;
584 }
585
586 /**
587 * Auto-link hashtags, URLs, usernames and lists.
588 *
589 * @param string The tweet to be converted
590 * @return string that auto-link HTML added
591 * @since 1.1.0
592 */
593 public function autoLink($tweet = null)
594 {
595 if ($tweet === null) {
596 $tweet = $this->tweet;
597 }
598 $entities = $this->extractor->extractURLWithoutProtocol(false)->extractEntitiesWithIndices($tweet);
599 return $this->autoLinkEntities($tweet, $entities);
600 }
601
602 /**
603 * Auto-link the @username and @username/list references in the provided text. Links to @username references will
604 * have the usernameClass CSS classes added. Links to @username/list references will have the listClass CSS class
605 * added.
606 *
607 * @return string that auto-link HTML added
608 * @since 1.1.0
609 */
610 public function autoLinkUsernamesAndLists($tweet = null)
611 {
612 if ($tweet === null) {
613 $tweet = $this->tweet;
614 }
615 $entities = $this->extractor->extractMentionsOrListsWithIndices($tweet);
616 return $this->autoLinkEntities($tweet, $entities);
617 }
618
619 /**
620 * Auto-link #hashtag references in the provided Tweet text. The #hashtag links will have the hashtagClass CSS class
621 * added.
622 *
623 * @return string that auto-link HTML added
624 * @since 1.1.0
625 */
626 public function autoLinkHashtags($tweet = null)
627 {
628 if ($tweet === null) {
629 $tweet = $this->tweet;
630 }
631 $entities = $this->extractor->extractHashtagsWithIndices($tweet);
632 return $this->autoLinkEntities($tweet, $entities);
633 }
634
635 /**
636 * Auto-link URLs in the Tweet text provided.
637 * <p/>
638 * This only auto-links URLs with protocol.
639 *
640 * @return string that auto-link HTML added
641 * @since 1.1.0
642 */
643 public function autoLinkURLs($tweet = null)
644 {
645 if ($tweet === null) {
646 $tweet = $this->tweet;
647 }
648 $entities = $this->extractor->extractURLWithoutProtocol(false)->extractURLsWithIndices($tweet);
649 return $this->autoLinkEntities($tweet, $entities);
650 }
651
652 /**
653 * Auto-link $cashtag references in the provided Tweet text. The $cashtag links will have the cashtagClass CSS class
654 * added.
655 *
656 * @return string that auto-link HTML added
657 * @since 1.1.0
658 */
659 public function autoLinkCashtags($tweet = null)
660 {
661 if ($tweet === null) {
662 $tweet = $this->tweet;
663 }
664 $entities = $this->extractor->extractCashtagsWithIndices($tweet);
665 return $this->autoLinkEntities($tweet, $entities);
666 }
667
668 public function linkToUrl($entity)
669 {
670 if (!empty($this->class_url)) {
671 $attributes['class'] = $this->class_url;
672 }
673 $attributes['href'] = $entity['url'];
674 $linkText = $this->escapeHTML($entity['url']);
675
676 if (!empty($entity['display_url']) && !empty($entity['expanded_url'])) {
677 // Goal: If a user copies and pastes a tweet containing t.co'ed link, the resulting paste
678 // should contain the full original URL (expanded_url), not the display URL.
679 //
680 // Method: Whenever possible, we actually emit HTML that contains expanded_url, and use
681 // font-size:0 to hide those parts that should not be displayed (because they are not part of display_url).
682 // Elements with font-size:0 get copied even though they are not visible.
683 // Note that display:none doesn't work here. Elements with display:none don't get copied.
684 //
685 // Additionally, we want to *display* ellipses, but we don't want them copied. To make this happen we
686 // wrap the ellipses in a tco-ellipsis class and provide an onCopy handler that sets display:none on
687 // everything with the tco-ellipsis class.
688 //
689 // As an example: The user tweets "hi http://longdomainname.com/foo"
690 // This gets shortened to "hi http://t.co/xyzabc", with display_url = "…nname.com/foo"
691 // This will get rendered as:
692 // <span class='tco-ellipsis'> <!-- This stuff should get displayed but not copied -->
693 //
694 // <!-- There's a chance the onCopy event handler might not fire. In case that happens,
695 // we include an &nbsp; here so that the … doesn't bump up against the URL and ruin it.
696 // The &nbsp; is inside the tco-ellipsis span so that when the onCopy handler *does*
697 // fire, it doesn't get copied. Otherwise the copied text would have two spaces in a row,
698 // e.g. "hi http://longdomainname.com/foo".
699 // <span style='font-size:0'>&nbsp;</span>
700 // </span>
701 // <span style='font-size:0'> <!-- This stuff should get copied but not displayed -->
702 // http://longdomai
703 // </span>
704 // <span class='js-display-url'> <!-- This stuff should get displayed *and* copied -->
705 // nname.com/foo
706 // </span>
707 // <span class='tco-ellipsis'> <!-- This stuff should get displayed but not copied -->
708 // <span style='font-size:0'>&nbsp;</span>
709 //
710 // </span>
711 //
712 // Exception: pic.twitter.com images, for which
713 // expandedUrl = "https://twitter.com/#!/username/status/1234/photo/1
714 // For those URLs, display_url is not a substring of expanded_url, so we don't do anything
715 //special to render the elided parts.
716 // For a pic.twitter.com URL, the only elided part will be the "https://", so this is fine.
717 $displayURL = $entity['display_url'];
718 $expandedURL = $entity['expanded_url'];
719 $displayURLSansEllipses = preg_replace('//u', '', $displayURL);
720 $diplayURLIndexInExpandedURL = mb_strpos($expandedURL, $displayURLSansEllipses);
721
722 if ($diplayURLIndexInExpandedURL !== false) {
723 $beforeDisplayURL = mb_substr($expandedURL, 0, $diplayURLIndexInExpandedURL);
724 $afterDisplayURL = mb_substr(
725 $expandedURL,
726 $diplayURLIndexInExpandedURL + mb_strlen($displayURLSansEllipses)
727 );
728 $precedingEllipsis = (preg_match('/\A/u', $displayURL)) ? '' : '';
729 $followingEllipsis = (preg_match('/\z/u', $displayURL)) ? '' : '';
730
731 $invisibleSpan = "<span {$this->invisibleTagAttrs}>";
732
733 $linkText = "<span class='tco-ellipsis'>{$precedingEllipsis}{$invisibleSpan}&nbsp;</span></span>";
734 $linkText .= "{$invisibleSpan}{$this->escapeHTML($beforeDisplayURL)}</span>";
735 $linkText .= "<span class='js-display-url'>{$this->escapeHTML($displayURLSansEllipses)}</span>";
736 $linkText .= "{$invisibleSpan}{$this->escapeHTML($afterDisplayURL)}</span>";
737 $linkText .= "<span class='tco-ellipsis'>{$invisibleSpan}&nbsp;</span>{$followingEllipsis}</span>";
738 } else {
739 $linkText = $entity['display_url'];
740 }
741 $attributes['title'] = $entity['expanded_url'];
742 } elseif (!empty($entity['display_url'])) {
743 $linkText = $entity['display_url'];
744 }
745
746 return $this->linkToText($entity, $linkText, $attributes);
747 }
748
749 /**
750 *
751 * @param array $entity
752 * @param string $tweet
753 * @return string
754 * @since 1.1.0
755 */
756 public function linkToHashtag($entity, $tweet = null)
757 {
758 if ($tweet === null) {
759 $tweet = $this->tweet;
760 }
761
762 $attributes = array();
763 $class = array();
764 $hash = StringUtils::substr($tweet, $entity['indices'][0], 1);
765 $linkText = $entity['hashtag'];
766
767 $attributes['href'] = $this->url_base_hash . $entity['hashtag'];
768 $attributes['title'] = '#' . $entity['hashtag'];
769 if (!empty($this->class_hash)) {
770 $class[] = $this->class_hash;
771 }
772 if (preg_match(Regex::getRtlCharsMatcher(), $linkText)) {
773 $class[] = 'rtl';
774 }
775 if (!empty($class)) {
776 $attributes['class'] = implode(' ', $class);
777 }
778
779 return $this->linkToTextWithSymbol($entity, $hash, $linkText, $attributes);
780 }
781
782 /**
783 *
784 * @param array $entity
785 * @param string $tweet
786 * @return string
787 * @since 1.1.0
788 */
789 public function linkToMentionAndList($entity, $tweet)
790 {
791 $attributes = array();
792 $symbol = StringUtils::substr($tweet, $entity['indices'][0], 1);
793
794 if (!empty($entity['list_slug'])) {
795 # Replace the list and username
796 $linkText = $entity['screen_name'] . $entity['list_slug'];
797 $class = $this->class_list;
798 $url = $this->url_base_list . $linkText;
799 } else {
800 # Replace the username
801 $linkText = $entity['screen_name'];
802 $class = $this->class_user;
803 $url = $this->url_base_user . $linkText;
804 }
805 if (!empty($class)) {
806 $attributes['class'] = $class;
807 }
808 $attributes['href'] = $url;
809
810 return $this->linkToTextWithSymbol($entity, $symbol, $linkText, $attributes);
811 }
812
813 /**
814 *
815 * @param array $entity
816 * @param string $tweet
817 * @return string
818 * @since 1.1.0
819 */
820 public function linkToCashtag($entity, $tweet = null)
821 {
822 if ($tweet === null) {
823 $tweet = $this->tweet;
824 }
825 $attributes = array();
826 $dollar = StringUtils::substr($tweet, $entity['indices'][0], 1);
827 $linkText = $entity['cashtag'];
828 $attributes['href'] = $this->url_base_cash . $entity['cashtag'];
829 $attributes['title'] = '$' . $linkText;
830 if (!empty($this->class_cash)) {
831 $attributes['class'] = $this->class_cash;
832 }
833
834 return $this->linkToTextWithSymbol($entity, $dollar, $linkText, $attributes);
835 }
836
837 /**
838 *
839 * @param array $entity
840 * @param string $text
841 * @param array $attributes
842 * @return string
843 * @since 1.1.0
844 */
845 public function linkToText(array $entity, $text, $attributes = array())
846 {
847 $rel = $this->getRel();
848 if ($rel !== '') {
849 $attributes['rel'] = $rel;
850 }
851 if ($this->target) {
852 $attributes['target'] = $this->target;
853 }
854 $link = '<a';
855 foreach ($attributes as $key => $val) {
856 $link .= ' ' . $key . '="' . $this->escapeHTML($val) . '"';
857 }
858 $link .= '>' . $text . '</a>';
859 return $link;
860 }
861
862 /**
863 *
864 * @param array $entity
865 * @param string $symbol
866 * @param string $linkText
867 * @param array $attributes
868 * @return string
869 * @since 3.0.1
870 */
871 protected function linkToTextWithSymbol(array $entity, $symbol, $linkText, array $attributes)
872 {
873 $includeSymbol = $this->usernameIncludeSymbol || !preg_match('/[@@]/u', $symbol);
874
875 if (!empty($this->symbolTag)) {
876 $symbol = sprintf('<%1$s>%2$s</%1$s>', $this->symbolTag, $symbol);
877 }
878 if (!empty($this->textWithSymbolTag)) {
879 $linkText = sprintf('<%1$s>%2$s</%1$s>', $this->textWithSymbolTag, $linkText);
880 }
881
882 if (!$includeSymbol) {
883 return $symbol . $this->linkToText($entity, $linkText, $attributes);
884 }
885
886 $linkText = $symbol . $linkText;
887
888 return $this->linkToText($entity, $linkText, $attributes);
889 }
890
891 /**
892 * get rel attribute
893 *
894 * @return string
895 */
896 public function getRel()
897 {
898 $rel = $this->rel;
899 $rel = array_unique($rel);
900
901 return implode(' ', $rel);
902 }
903
904 /**
905 * Set rel attribute.
906 *
907 * This method override setExternal/setNoFollow setting.
908 *
909 * @param string[]|string $rel the rel attribute
910 * @param bool $merge if true, merge rel attributes instead replace.
911 * @return $this
912 */
913 public function setRel($rel, $merge = false)
914 {
915 if (is_string($rel)) {
916 $rel = explode(' ', $rel);
917 }
918
919 $this->rel = $merge ? array_unique(array_merge($this->rel, $rel)) : $rel;
920
921 return $this;
922 }
923
924 /**
925 * html escape
926 *
927 * @param string $text
928 * @return string
929 */
930 protected function escapeHTML($text)
931 {
932 return htmlspecialchars($text, ENT_QUOTES, 'UTF-8', false);
933 }
934 }
935