Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
28.57% covered (danger)
28.57%
8 / 28
20.00% covered (danger)
20.00%
1 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
CommentItemTrait
28.57% covered (danger)
28.57%
8 / 28
20.00% covered (danger)
20.00%
1 / 5
64.48
0.00% covered (danger)
0.00%
0 / 1
 getParent
n/a
0 / 0
n/a
0 / 0
0
 getAuthor
n/a
0 / 0
n/a
0 / 0
0
 getTimestamp
n/a
0 / 0
n/a
0 / 0
0
 jsonSerialize
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 jsonSerializeForDiff
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 getTimestampString
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
 getHeading
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 getSubscribableHeading
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2
3namespace MediaWiki\Extension\DiscussionTools\ThreadItem;
4
5use DateTimeImmutable;
6use MediaWiki\MediaWikiServices;
7use RuntimeException;
8
9trait CommentItemTrait {
10    // phpcs:disable Squiz.WhiteSpace, MediaWiki.Commenting
11    // Required ThreadItem methods (listed for Phan)
12    abstract public function getParent(): ?ThreadItem;
13    // Required CommentItem methods (listed for Phan)
14    abstract public function getAuthor(): string;
15    abstract public function getTimestamp(): DateTimeImmutable;
16    // phpcs:enable
17
18    /**
19     * @inheritDoc
20     * @suppress PhanTraitParentReference
21     */
22    public function jsonSerialize( bool $deep = false, ?callable $callback = null ): array {
23        return array_merge( [
24            'timestamp' => $this->getTimestampString(),
25            'author' => $this->getAuthor(),
26        ], parent::jsonSerialize( $deep, $callback ) );
27    }
28
29    /**
30     * @return array JSON-serializable array
31     */
32    public function jsonSerializeForDiff(): array {
33        $data = $this->jsonSerialize();
34
35        $heading = $this->getHeading();
36        $data['headingId'] = $heading->getId();
37        $subscribableHeading = $this->getSubscribableHeading();
38        $data['subscribableHeadingId'] = $subscribableHeading ? $subscribableHeading->getId() : null;
39
40        return $data;
41    }
42
43    /**
44     * Get the comment timestamp in the format used in IDs and names.
45     *
46     * Depending on the date of the comment, this may use one of two formats:
47     *
48     *  - For dates prior to 'DiscussionToolsTimestampFormatSwitchTime' (by default 2022-07-12):
49     *    Uses ISO 8601 date. Almost DateTimeInterface::RFC3339_EXTENDED, but ending with 'Z' instead
50     *    of '+00:00', like Date#toISOString in JavaScript.
51     *
52     *  - For dates on or after 'DiscussionToolsTimestampFormatSwitchTime' (by default 2022-07-12):
53     *    Uses MediaWiki timestamp (TS_MW in MediaWiki PHP code).
54     *
55     * @return string Comment timestamp in standard format
56     */
57    public function getTimestampString(): string {
58        $dtConfig = MediaWikiServices::getInstance()->getConfigFactory()->makeConfig( 'discussiontools' );
59        $switchTime = new DateTimeImmutable(
60            $dtConfig->get( 'DiscussionToolsTimestampFormatSwitchTime' )
61        );
62        $timestamp = $this->getTimestamp();
63        if ( $timestamp < $switchTime ) {
64            return $timestamp->format( 'Y-m-d\TH:i:s.v\Z' );
65        } else {
66            return $timestamp->format( 'YmdHis' );
67        }
68    }
69
70    /**
71     * @return ContentHeadingItem Closest ancestor which is a HeadingItem
72     */
73    public function getHeading(): HeadingItem {
74        $parent = $this;
75        while ( $parent instanceof CommentItem ) {
76            $parent = $parent->getParent();
77        }
78        if ( !( $parent instanceof HeadingItem ) ) {
79            throw new RuntimeException( 'Heading parent not found' );
80        }
81        return $parent;
82    }
83
84    /**
85     * @return ContentHeadingItem|null Closest heading that can be used for topic subscriptions
86     */
87    public function getSubscribableHeading(): ?HeadingItem {
88        $heading = $this->getHeading();
89        while ( $heading instanceof HeadingItem && !$heading->isSubscribable() ) {
90            $heading = $heading->getParent();
91        }
92        return $heading instanceof HeadingItem ? $heading : null;
93    }
94}