Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 47
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 / 47
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 / 34
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( "Could not locate workflow for page ID $oldPageId" );
79        }
80
81        $discussionWorkflow = null;
82        foreach ( $found as $workflow ) {
83            if ( $workflow->getType() === 'discussion' ) {
84                $discussionWorkflow = $workflow;
85            }
86            $workflow->updateFromPageId( $oldPageId, $newPage );
87            $this->storage->put( $workflow, [] );
88        }
89        if ( $discussionWorkflow === null ) {
90            throw new FlowException( "Main discussion workflow for page ID $oldPageId not found" );
91        }
92
93        /** @var Header[] $found */
94        $found = $this->storage->find(
95            'Header',
96            [ 'rev_type_id' => $discussionWorkflow->getId() ],
97            [ 'sort' => 'rev_id', 'order' => 'DESC', 'limit' => 1 ]
98        );
99
100        if ( $found ) {
101            $header = reset( $found );
102            $nextHeader = $header->newNextRevision(
103                $this->nullEditUser,
104                $header->getContentRaw(),
105                $header->getContentFormat(),
106                'edit-header',
107                $newPage
108            );
109            if ( $header !== $nextHeader ) {
110                $this->storage->put( $nextHeader, [
111                    'workflow' => $discussionWorkflow,
112                ] );
113            }
114        }
115    }
116
117    /**
118     * @throws Exception\FlowException
119     */
120    public function commit() {
121        if ( $this->dbw === null ) {
122            return;
123        }
124
125        try {
126            $this->dbw->endAtomic( __CLASS__ );
127        } catch ( \Exception $e ) {
128            $this->dbw->rollback( __METHOD__ );
129            throw $e;
130        }
131
132        // reset dbw (which is used to check if a move transaction is already in
133        // progress, which is no longer the case)
134        $this->dbw = null;
135    }
136}