Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 55
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
BoardMover
0.00% covered (danger)
0.00%
0 / 55
0.00% covered (danger)
0.00%
0 / 4
182
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 begin
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 move
0.00% covered (danger)
0.00%
0 / 42
0.00% covered (danger)
0.00%
0 / 1
72
 commit
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2
3namespace Flow;
4
5use Flow\Data\ManagerGroup;
6use Flow\Exception\FlowException;
7use Flow\Model\Header;
8use Flow\Model\Workflow;
9use MediaWiki\Title\Title;
10use MediaWiki\User\User;
11use MediaWiki\WikiMap\WikiMap;
12use Wikimedia\Rdbms\IDatabase;
13
14class BoardMover {
15    /**
16     * @var DbFactory
17     */
18    protected $dbFactory;
19
20    /**
21     * @var ManagerGroup
22     */
23    protected $storage;
24
25    /**
26     * @var User
27     */
28    protected $nullEditUser;
29
30    /**
31     * @var IDatabase|null
32     */
33    protected $dbw;
34
35    public function __construct( DbFactory $dbFactory, ManagerGroup $storage, User $nullEditUser ) {
36        $this->dbFactory = $dbFactory;
37        $this->storage = $storage;
38        $this->nullEditUser = $nullEditUser;
39    }
40
41    /**
42     * Starts a transaction on the Flow database.
43     */
44    protected function begin() {
45        // All reads must go through primary to help ensure consistency
46        $this->dbFactory->forcePrimary();
47
48        // Open a transaction, this will be closed from self::commit.
49        $this->dbw = $this->dbFactory->getDB( DB_PRIMARY );
50        $this->dbw->startAtomic( __CLASS__ );
51    }
52
53    /**
54     * Collects the workflow and header (if it exists) and puts them into the database. Does
55     * not commit yet. It is intended for move to be called for each move, and commit
56     * to be called at the end the core transaction, via a hook.
57     *
58     * @param int $oldPageId Page ID before move/change
59     * @param Title $newPage Page after move/change
60     * @throws \Flow\Exception\DataModelException
61     * @throws FlowException
62     */
63    public function move( $oldPageId, Title $newPage ) {
64        if ( $this->dbw === null ) {
65            $this->begin();
66        }
67
68        // @todo this loads every topic workflow this board has ever seen,
69        // would prefer to update db directly but that won't work due to
70        // the caching layer not getting updated.  After dropping Flow\Data\Index\*
71        // revisit this.
72        /** @var Workflow[] $found */
73        $found = $this->storage->find( 'Workflow', [
74            'workflow_wiki' => WikiMap::getCurrentWikiId(),
75            'workflow_page_id' => $oldPageId,
76        ] );
77        if ( !$found ) {
78            throw new FlowException(
79                "Could not locate workflow for page ID {oldPageId}",
80                'default',
81                [ 'oldPageId' => $oldPageId ]
82            );
83        }
84
85        $discussionWorkflow = null;
86        foreach ( $found as $workflow ) {
87            if ( $workflow->getType() === 'discussion' ) {
88                $discussionWorkflow = $workflow;
89            }
90            $workflow->updateFromPageId( $oldPageId, $newPage );
91            $this->storage->put( $workflow, [] );
92        }
93        if ( $discussionWorkflow === null ) {
94            throw new FlowException(
95                "Main discussion workflow for page ID {oldPageId} not found",
96                'default',
97                [ 'oldPageId' => $oldPageId ]
98            );
99        }
100
101        /** @var Header[] $found */
102        $found = $this->storage->find(
103            'Header',
104            [ 'rev_type_id' => $discussionWorkflow->getId() ],
105            [ 'sort' => 'rev_id', 'order' => 'DESC', 'limit' => 1 ]
106        );
107
108        if ( $found ) {
109            $header = reset( $found );
110            $nextHeader = $header->newNextRevision(
111                $this->nullEditUser,
112                $header->getContentRaw(),
113                $header->getContentFormat(),
114                'edit-header',
115                $newPage
116            );
117            if ( $header !== $nextHeader ) {
118                $this->storage->put( $nextHeader, [
119                    'workflow' => $discussionWorkflow,
120                ] );
121            }
122        }
123    }
124
125    /**
126     * @throws Exception\FlowException
127     */
128    public function commit() {
129        if ( $this->dbw === null ) {
130            return;
131        }
132
133        try {
134            $this->dbw->endAtomic( __CLASS__ );
135        } catch ( \Exception $e ) {
136            $this->dbw->rollback( __METHOD__ );
137            throw $e;
138        }
139
140        // reset dbw (which is used to check if a move transaction is already in
141        // progress, which is no longer the case)
142        $this->dbw = null;
143    }
144}