Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
83.33% covered (warning)
83.33%
30 / 36
50.00% covered (danger)
50.00%
4 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
AbstractUndoAction
83.33% covered (warning)
83.33%
30 / 36
50.00% covered (danger)
50.00%
4 / 8
12.67
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 onView
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 needsReadRights
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getRestriction
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getDiffFromRequest
72.73% covered (warning)
72.73%
8 / 11
0.00% covered (danger)
0.00%
0 / 1
4.32
 tryPatching
90.00% covered (success)
90.00%
9 / 10
0.00% covered (danger)
0.00%
0 / 1
2.00
 showUndoErrorPage
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare( strict_types = 1 );
4
5namespace EntitySchema\MediaWiki\Actions;
6
7use Diff\DiffOp\Diff\Diff;
8use DomainException;
9use EntitySchema\MediaWiki\Content\EntitySchemaContent;
10use EntitySchema\MediaWiki\UndoHandler;
11use MediaWiki\Actions\FormlessAction;
12use MediaWiki\Context\IContextSource;
13use MediaWiki\MediaWikiServices;
14use MediaWiki\Page\Article;
15use MediaWiki\Request\WebRequest;
16use MediaWiki\Revision\RevisionStore;
17use MediaWiki\Revision\SlotRecord;
18use MediaWiki\Status\Status;
19
20/**
21 * @license GPL-2.0-or-later
22 */
23abstract class AbstractUndoAction extends FormlessAction {
24
25    protected RevisionStore $revisionStore;
26
27    public function __construct(
28        Article $article,
29        IContextSource $context,
30        RevisionStore $revisionStore
31    ) {
32        parent::__construct( $article, $context );
33        $this->revisionStore = $revisionStore;
34    }
35
36    /** @inheritDoc */
37    public function getName() {
38        return 'view';
39    }
40
41    /** @inheritDoc */
42    public function onView() {
43        return null;
44    }
45
46    /** @inheritDoc */
47    public function needsReadRights() {
48        // Pages in $wgWhitelistRead can be viewed without having the 'read'
49        // right. We rely on Article::view() to properly check read access.
50        return false;
51    }
52
53    public function getRestriction(): string {
54        return $this->getName();
55    }
56
57    protected function getDiffFromRequest( WebRequest $req ): Status {
58        $newerRevision = $this->revisionStore->getRevisionById( $req->getInt( 'undo' ) );
59        $olderRevision = $this->revisionStore->getRevisionById( $req->getInt( 'undoafter' ) );
60
61        if ( $newerRevision === null || $olderRevision === null ) {
62            return Status::newFatal( 'entityschema-undo-bad-revisions' );
63        }
64
65        /** @var EntitySchemaContent $undoFromContent */
66        $undoFromContent = $newerRevision->getContent( SlotRecord::MAIN );
67        '@phan-var EntitySchemaContent $undoFromContent';
68        /** @var EntitySchemaContent $undoToContent */
69        $undoToContent = $olderRevision->getContent( SlotRecord::MAIN );
70        '@phan-var EntitySchemaContent $undoToContent';
71        $undoHandler = new UndoHandler();
72        try {
73            $undoHandler->validateContentIds( $undoFromContent, $undoToContent );
74        } catch ( DomainException $e ) {
75            return Status::newFatal( 'entityschema-error-inconsistent-id' );
76        }
77
78        return $undoHandler->getDiffFromContents( $undoFromContent, $undoToContent );
79    }
80
81    /**
82     * Try applying the diff to the latest revision of this page.
83     *
84     * @param Diff $diff
85     *
86     * @return Status contains array of the patched schema data and the revision that was patched
87     */
88    protected function tryPatching( Diff $diff ): Status {
89        $revStore = MediaWikiServices::getInstance()->getRevisionStore();
90        $baseRevId = $this->getTitle()->getLatestRevID();
91        /** @var EntitySchemaContent $baseContent */
92        $baseContent = $revStore
93            ->getRevisionById( $baseRevId )
94            ->getContent( SlotRecord::MAIN );
95        '@phan-var EntitySchemaContent $baseContent';
96        $undoHandler = new UndoHandler();
97        $status = $undoHandler->tryPatching( $diff, $baseContent );
98        if ( $status->isGood() ) {
99            return Status::newGood( [ $status->getValue(), $baseRevId ] );
100        }
101
102        return $status;
103    }
104
105    /**
106     * Output an error page showing the given status
107     *
108     * @param Status $status The status to report.
109     */
110    protected function showUndoErrorPage( Status $status ): void {
111        $this->getOutput()->prepareErrorPage();
112        $this->getOutput()->setPageTitleMsg(
113            $this->msg( 'entityschema-undo-heading-failed' )
114        );
115        $this->getOutput()->setHTMLTitle(
116            $this->msg( 'errorpagetitle' )
117        );
118
119        $this->getOutput()->addHTML( $status->getMessage()->parse() );
120
121        $this->getOutput()->returnToMain();
122    }
123
124}