PluginProbe
ActivityPub / 7.7.0
ActivityPub v7.7.0
9.3.1 9.3.0 9.2.2 9.2.1 9.2.0 9.1.0 9.0.2 9.0.1 9.0.0 8.3.0 8.2.1 8.2.0 8.1.1 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.2.0 1.3.0 2.0.0 2.0.1 2.1.0 2.1.1 All 160 releases
activitypub / includes / model / class-blog.php

class-blog.php in ActivityPub 7.7.0, at includes/model/class-blog.php

561 lines 11.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Blog model file.
4 *
5 * @package Activitypub
6 */
7
8 namespace Activitypub\Model;
9
10 use Activitypub\Activity\Actor;
11 use Activitypub\Collection\Actors;
12 use Activitypub\Collection\Extra_Fields;
13
14 use function Activitypub\esc_hashtag;
15 use function Activitypub\get_attribution_domains;
16 use function Activitypub\get_rest_url_by_path;
17 use function Activitypub\is_blog_public;
18 use function Activitypub\is_single_user;
19
20 /**
21 * Blog class.
22 *
23 * @method int get__id() Gets the internal user ID for the blog (always returns BLOG_USER_ID).
24 */
25 class Blog extends Actor {
26 /**
27 * The User-ID
28 *
29 * @var int
30 */
31 protected $_id = Actors::BLOG_USER_ID; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
32
33 /**
34 * The generator of the object.
35 *
36 * @see https://www.w3.org/TR/activitypub/#generator
37 * @see https://codeberg.org/fediverse/fep/src/branch/main/fep/844e/fep-844e.md#discovery-through-an-actor
38 *
39 * @var array
40 */
41 protected $generator = array(
42 'type' => 'Application',
43 'implements' => array(
44 array(
45 'href' => 'https://datatracker.ietf.org/doc/html/rfc9421',
46 'name' => 'RFC-9421: HTTP Message Signatures',
47 ),
48 ),
49 );
50
51 /**
52 * Constructor.
53 */
54 public function __construct() {
55 /**
56 * Fires when a model actor is constructed.
57 *
58 * @param Blog $this The Blog model.
59 */
60 \do_action( 'activitypub_construct_model_actor', $this );
61 }
62
63 /**
64 * Whether the User manually approves followers.
65 *
66 * @return false
67 */
68 public function get_manually_approves_followers() {
69 return false;
70 }
71
72 /**
73 * Whether the User is discoverable.
74 *
75 * @return boolean
76 */
77 public function get_discoverable() {
78 return true;
79 }
80
81 /**
82 * Get the User ID.
83 *
84 * @return string The User ID.
85 */
86 public function get_id() {
87 $id = parent::get_id();
88
89 if ( $id ) {
90 return $id;
91 }
92
93 $permalink = \get_option( 'activitypub_use_permalink_as_id_for_blog', false );
94
95 if ( $permalink ) {
96 return \esc_url( \trailingslashit( get_home_url() ) . '@' . $this->get_preferred_username() );
97 }
98
99 return \add_query_arg( 'author', $this->_id, \home_url( '/' ) );
100 }
101
102 /**
103 * Get the type of the object.
104 *
105 * If the Blog is in "single user" mode, return "Person" instead of "Group".
106 *
107 * @return string The type of the object.
108 */
109 public function get_type() {
110 if ( is_single_user() ) {
111 return 'Person';
112 } else {
113 return 'Group';
114 }
115 }
116
117 /**
118 * Get the Username.
119 *
120 * @return string The Username.
121 */
122 public function get_name() {
123 return \wp_strip_all_tags(
124 \html_entity_decode(
125 \get_bloginfo( 'name' ),
126 \ENT_QUOTES,
127 'UTF-8'
128 )
129 );
130 }
131
132 /**
133 * Get the User description.
134 *
135 * @return string The User description.
136 */
137 public function get_summary() {
138 $summary = \get_option( 'activitypub_blog_description', null );
139
140 if ( ! $summary ) {
141 $summary = \get_bloginfo( 'description' );
142 }
143
144 return \wpautop(
145 \wp_kses(
146 $summary,
147 'default'
148 )
149 );
150 }
151
152 /**
153 * Get the User url.
154 *
155 * @return string The User url.
156 */
157 public function get_url() {
158 return \get_bloginfo( 'url' );
159 }
160
161 /**
162 * Get blog's homepage URL.
163 *
164 * @return string The User-Url.
165 */
166 public function get_alternate_url() {
167 return \esc_url( \trailingslashit( get_home_url() ) );
168 }
169
170 /**
171 * Generate a default Username.
172 *
173 * @return string The auto-generated Username.
174 */
175 public static function get_default_username() {
176 // Check if domain host has a subdomain.
177 $host = \wp_parse_url( \get_home_url(), \PHP_URL_HOST );
178 $host = \preg_replace( '/^www\./i', '', $host );
179
180 /**
181 * Filters the default blog username.
182 *
183 * This filter allows developers to modify the default username that is
184 * generated for the blog, which by default is the site's host name
185 * without the 'www.' prefix.
186 *
187 * @param string $host The default username (site's host name).
188 */
189 return apply_filters( 'activitypub_default_blog_username', $host );
190 }
191
192 /**
193 * Get the preferred Username.
194 *
195 * @return string The Username.
196 */
197 public function get_preferred_username() {
198 $username = \get_option( 'activitypub_blog_identifier' );
199
200 if ( $username ) {
201 return $username;
202 }
203
204 return self::get_default_username();
205 }
206
207 /**
208 * Get the User icon.
209 *
210 * @return string[] The User icon.
211 */
212 public function get_icon() {
213 // Try site_logo, falling back to site_icon, first.
214 $icon_id = get_option( 'site_icon' );
215
216 // Try custom logo second.
217 if ( ! $icon_id ) {
218 $icon_id = get_theme_mod( 'custom_logo' );
219 }
220
221 $icon_url = false;
222
223 if ( $icon_id ) {
224 $icon = wp_get_attachment_image_src( $icon_id, 'full' );
225 if ( $icon ) {
226 $icon_url = $icon[0];
227 }
228 }
229
230 if ( ! $icon_url ) {
231 // Fallback to default icon.
232 $icon_url = plugins_url( '/assets/img/wp-logo.png', ACTIVITYPUB_PLUGIN_FILE );
233 }
234
235 return array(
236 'type' => 'Image',
237 'url' => esc_url( $icon_url ),
238 );
239 }
240
241 /**
242 * Get the User-Header-Image.
243 *
244 * @return string[]|null The User-Header-Image.
245 */
246 public function get_image() {
247 $header_image = get_option( 'activitypub_header_image' );
248 $image_url = null;
249
250 if ( $header_image ) {
251 $image_url = \wp_get_attachment_url( $header_image );
252 }
253
254 if ( ! $image_url && \has_header_image() ) {
255 $image_url = \get_header_image();
256 }
257
258 if ( $image_url ) {
259 return array(
260 'type' => 'Image',
261 'url' => esc_url( $image_url ),
262 );
263 }
264
265 return null;
266 }
267
268 /**
269 * Get the published date.
270 *
271 * @return string The published date.
272 */
273 public function get_published() {
274 $first_post = new \WP_Query(
275 array(
276 'orderby' => 'date',
277 'order' => 'ASC',
278 'number' => 1,
279 )
280 );
281
282 if ( ! empty( $first_post->posts[0] ) ) {
283 $time = \strtotime( $first_post->posts[0]->post_date_gmt );
284 } else {
285 $time = \time();
286 }
287
288 return \gmdate( ACTIVITYPUB_DATE_TIME_RFC3339, $time );
289 }
290
291 /**
292 * Get the canonical URL.
293 *
294 * @return string|null The canonical URL.
295 */
296 public function get_canonical_url() {
297 return \home_url();
298 }
299
300 /**
301 * Get the Moderators endpoint.
302 *
303 * @return string|null The Moderators endpoint.
304 */
305 public function get_moderators() {
306 if ( is_single_user() || 'Group' !== $this->get_type() ) {
307 return null;
308 }
309
310 return get_rest_url_by_path( 'collections/moderators' );
311 }
312
313 /**
314 * Get attributedTo value.
315 *
316 * @return string|null The attributedTo value.
317 */
318 public function get_attributed_to() {
319 if ( is_single_user() || 'Group' !== $this->get_type() ) {
320 return null;
321 }
322
323 return get_rest_url_by_path( 'collections/moderators' );
324 }
325
326 /**
327 * Get the public key information.
328 *
329 * @return string[] The public key.
330 */
331 public function get_public_key() {
332 return array(
333 'id' => $this->get_id() . '#main-key',
334 'owner' => $this->get_id(),
335 'publicKeyPem' => Actors::get_public_key( $this->get__id() ),
336 );
337 }
338
339 /**
340 * Returns whether posting is restricted to mods.
341 *
342 * @return bool|null True if posting is restricted to mods, null if not applicable.
343 */
344 public function get_posting_restricted_to_mods() {
345 if ( 'Group' === $this->get_type() ) {
346 return true;
347 }
348
349 return null;
350 }
351
352 /**
353 * Returns the Inbox-API-Endpoint.
354 *
355 * @return string The Inbox-Endpoint.
356 */
357 public function get_inbox() {
358 return get_rest_url_by_path( sprintf( 'actors/%d/inbox', $this->get__id() ) );
359 }
360
361 /**
362 * Returns the Outbox-API-Endpoint.
363 *
364 * @return string The Outbox-Endpoint.
365 */
366 public function get_outbox() {
367 return get_rest_url_by_path( sprintf( 'actors/%d/outbox', $this->get__id() ) );
368 }
369
370 /**
371 * Returns the Followers-API-Endpoint.
372 *
373 * @return string The Followers-Endpoint.
374 */
375 public function get_followers() {
376 return get_rest_url_by_path( sprintf( 'actors/%d/followers', $this->get__id() ) );
377 }
378
379 /**
380 * Returns the Following-API-Endpoint.
381 *
382 * @return string The Following-Endpoint.
383 */
384 public function get_following() {
385 return get_rest_url_by_path( sprintf( 'actors/%d/following', $this->get__id() ) );
386 }
387
388 /**
389 * Returns endpoints.
390 *
391 * @return string[]|null The endpoints.
392 */
393 public function get_endpoints() {
394 return array(
395 'sharedInbox' => get_rest_url_by_path( 'inbox' ),
396 );
397 }
398
399 /**
400 * Returns a user@domain type of identifier for the user.
401 *
402 * @return string The Webfinger-Identifier.
403 */
404 public function get_webfinger() {
405 return $this->get_preferred_username() . '@' . \wp_parse_url( \home_url(), \PHP_URL_HOST );
406 }
407
408 /**
409 * Returns the Featured-API-Endpoint.
410 *
411 * @return string The Featured-Endpoint.
412 */
413 public function get_featured() {
414 return get_rest_url_by_path( sprintf( 'actors/%d/collections/featured', $this->get__id() ) );
415 }
416
417 /**
418 * Returns the Featured-Tags-API-Endpoint.
419 *
420 * @return string The Featured-Tags-Endpoint.
421 */
422 public function get_featured_tags() {
423 return get_rest_url_by_path( sprintf( 'actors/%d/collections/tags', $this->get__id() ) );
424 }
425
426 /**
427 * Returns whether the site is indexable.
428 *
429 * @return bool Whether the site is indexable.
430 */
431 public function get_indexable() {
432 if ( is_blog_public() ) {
433 return true;
434 } else {
435 return false;
436 }
437 }
438
439 /**
440 * Update the Username.
441 *
442 * @param mixed $value The new value.
443 * @return bool True if the attribute was updated, false otherwise.
444 */
445 public function update_name( $value ) {
446 return \update_option( 'blogname', $value );
447 }
448
449 /**
450 * Update the User description.
451 *
452 * @param mixed $value The new value.
453 * @return bool True if the attribute was updated, false otherwise.
454 */
455 public function update_summary( $value ) {
456 return \update_option( 'blogdescription', $value );
457 }
458
459 /**
460 * Update the User icon.
461 *
462 * @param mixed $value The new value.
463 * @return bool True if the attribute was updated, false otherwise.
464 */
465 public function update_icon( $value ) {
466 if ( ! wp_attachment_is_image( $value ) ) {
467 return false;
468 }
469 return \update_option( 'site_icon', $value );
470 }
471
472 /**
473 * Update the User-Header-Image.
474 *
475 * @param mixed $value The new value.
476 * @return bool True if the attribute was updated, false otherwise.
477 */
478 public function update_header( $value ) {
479 if ( ! wp_attachment_is_image( $value ) ) {
480 return false;
481 }
482 return \update_option( 'activitypub_header_image', $value );
483 }
484
485 /**
486 * Get the User - Hashtags.
487 *
488 * @see https://docs.joinmastodon.org/spec/activitypub/#Hashtag
489 *
490 * @return string[] The User - Hashtags.
491 */
492 public function get_tag() {
493 $hashtags = array();
494
495 $args = array(
496 'orderby' => 'count',
497 'order' => 'DESC',
498 'number' => 10,
499 );
500
501 $tags = get_tags( $args );
502
503 foreach ( $tags as $tag ) {
504 $hashtags[] = array(
505 'type' => 'Hashtag',
506 'href' => \get_tag_link( $tag->term_id ),
507 'name' => esc_hashtag( $tag->name ),
508 );
509 }
510
511 return $hashtags;
512 }
513
514 /**
515 * Extend the User-Output with Attachments.
516 *
517 * @return array The extended User-Output.
518 */
519 public function get_attachment() {
520 $extra_fields = Extra_Fields::get_actor_fields( $this->_id );
521 return Extra_Fields::fields_to_attachments( $extra_fields );
522 }
523
524 /**
525 * Returns the website hosts allowed to credit this blog.
526 *
527 * @return string[]|null The attribution domains or null if not found.
528 */
529 public function get_attribution_domains() {
530 return get_attribution_domains();
531 }
532
533 /**
534 * Returns the alsoKnownAs.
535 *
536 * @return string[] The alsoKnownAs.
537 */
538 public function get_also_known_as() {
539 $also_known_as = array(
540 \add_query_arg( 'author', $this->_id, \home_url( '/' ) ),
541 $this->get_url(),
542 $this->get_alternate_url(),
543 );
544
545 $also_known_as = array_merge( $also_known_as, \get_option( 'activitypub_blog_user_also_known_as', array() ) );
546
547 return array_unique( $also_known_as );
548 }
549
550 /**
551 * Returns the movedTo.
552 *
553 * @return string The movedTo.
554 */
555 public function get_moved_to() {
556 $moved_to = \get_option( 'activitypub_blog_user_moved_to' );
557
558 return $moved_to && $moved_to !== $this->get_id() ? $moved_to : null;
559 }
560 }
561