Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
48.21% covered (danger)
48.21%
27 / 56
43.75% covered (danger)
43.75%
7 / 16
CRAP
0.00% covered (danger)
0.00%
0 / 1
AbstractCollection
48.21% covered (danger)
48.21%
27 / 56
43.75% covered (danger)
43.75%
7 / 16
119.88
0.00% covered (danger)
0.00%
0 / 1
 getRevisionClass
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getWorkflowId
n/a
0 / 0
n/a
0 / 0
0
 getBoardWorkflowId
n/a
0 / 0
n/a
0 / 0
0
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 newFromId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 newFromRevisionId
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 newFromRevision
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getStorage
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 getAllRevisions
69.23% covered (warning)
69.23%
9 / 13
0.00% covered (danger)
0.00%
0 / 1
4.47
 getRevision
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getFirstRevision
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getLastRevision
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getPrevRevision
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 getNextRevision
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 getTitle
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getWorkflow
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
3.04
 getBoardWorkflow
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace Flow\Collection;
4
5use Flow\Container;
6use Flow\Data\ManagerGroup;
7use Flow\Data\ObjectManager;
8use Flow\Exception\FlowException;
9use Flow\Exception\InvalidDataException;
10use Flow\Model\AbstractRevision;
11use Flow\Model\UUID;
12use Flow\Model\Workflow;
13use MediaWiki\Title\Title;
14
15abstract class AbstractCollection {
16    /**
17     * Id of the collection object.
18     *
19     * @var UUID
20     */
21    protected $uuid;
22
23    /**
24     * Array of revisions for this object.
25     *
26     * @var AbstractRevision[]
27     */
28    protected $revisions = [];
29
30    /**
31     * @var Workflow
32     */
33    protected $workflow;
34
35    /**
36     * Returns the revision class name for this specific object (e.g. Header,
37     * PostRevision)
38     *
39     * @return string
40     */
41    public static function getRevisionClass() {
42        // Workaround to allow static inheritance without strict standards warning from this being abstract
43        throw new FlowException( 'getRevisionClass() must be implemented in the subclass', 'not-implemented' );
44    }
45
46    /**
47     * Returns the id of the workflow this collection is associated with.
48     *
49     * @return UUID
50     */
51    abstract public function getWorkflowId();
52
53    /**
54     * Returns the id of the workflow of the board this collection is on.
55     *
56     * @return UUID
57     */
58    abstract public function getBoardWorkflowId();
59
60    /**
61     * Use the static methods to load an object from a given revision.
62     *
63     * @see AbstractCollection::newFromId
64     * @see AbstractCollection::newFromRevision
65     * @see AbstractCollection::newFromRevisionId
66     *
67     * @param UUID $uuid
68     */
69    protected function __construct( UUID $uuid ) {
70        $this->uuid = $uuid;
71    }
72
73    /**
74     * Instantiate a new object based on its type id
75     * (post ID, header ID, etc.)
76     *
77     * @param UUID $uuid
78     * @return static
79     * @suppress PhanTypeInstantiateAbstractStatic Phan is right, though
80     */
81    public static function newFromId( UUID $uuid ) {
82        return new static( $uuid );
83    }
84
85    /**
86     * Instantiate a new collection based on a revision ID
87     *
88     * @param UUID $revId Revision ID
89     * @return AbstractCollection
90     */
91    public static function newFromRevisionId( UUID $revId ) {
92        $revision = static::getStorage()->get( $revId );
93
94        if ( $revision === null ) {
95            throw new InvalidDataException(
96                'Revisions for ' . $revId->getAlphadecimal() . ' could not be found',
97                'invalid-revision-id'
98            );
99        }
100
101        return static::newFromRevision( $revision );
102    }
103
104    /**
105     * Instantiate a new object based off of an AbstractRevision object.
106     *
107     * @param AbstractRevision $revision
108     * @return AbstractCollection
109     */
110    public static function newFromRevision( AbstractRevision $revision ) {
111        return static::newFromId( $revision->getCollectionId() );
112    }
113
114    /**
115     * @return UUID
116     */
117    public function getId() {
118        return $this->uuid;
119    }
120
121    /**
122     * @param string|null $class Storage class - defaults to getRevisionClass()
123     * @return ObjectManager
124     */
125    public static function getStorage( $class = null ) {
126        if ( !$class ) {
127            $class = static::getRevisionClass();
128        }
129
130        /** @var ManagerGroup $storage */
131        $storage = Container::get( 'storage' );
132        return $storage->getStorage( $class );
133    }
134
135    /**
136     * @return AbstractRevision[]
137     * @throws InvalidDataException When no revisions can be found
138     */
139    public function getAllRevisions() {
140        if ( !$this->revisions ) {
141            /** @var AbstractRevision[] $revisions */
142            $revisions = self::getStorage()->find(
143                [ 'rev_type_id' => $this->uuid ],
144                [ 'sort' => 'rev_id', 'order' => 'DESC' ]
145            );
146
147            if ( !$revisions ) {
148                throw new InvalidDataException(
149                    'Revisions for ' . $this->uuid->getAlphadecimal() . ' could not be found',
150                    'invalid-type-id'
151                );
152            }
153
154            foreach ( $revisions as $revision ) {
155                $this->revisions[$revision->getRevisionId()->getAlphadecimal()] = $revision;
156            }
157        }
158
159        return $this->revisions;
160    }
161
162    /**
163     * Returns the revision with the given id.
164     *
165     * @param UUID $uuid
166     * @return AbstractRevision|null null if there is no such revision in the collection
167     */
168    public function getRevision( UUID $uuid ) {
169        // make sure all revisions have been loaded
170        $this->getAllRevisions();
171
172        if ( !isset( $this->revisions[$uuid->getAlphadecimal()] ) ) {
173            return null;
174        }
175
176        // find requested id, based on given revision
177        return $this->revisions[$uuid->getAlphadecimal()];
178    }
179
180    /**
181     * Returns the oldest revision.
182     *
183     * @return AbstractRevision
184     */
185    public function getFirstRevision() {
186        $revisions = $this->getAllRevisions();
187        return array_pop( $revisions );
188    }
189
190    /**
191     * Returns the most recent revision.
192     *
193     * @return AbstractRevision
194     */
195    public function getLastRevision() {
196        $revisions = $this->getAllRevisions();
197        return array_shift( $revisions );
198    }
199
200    /**
201     * Given a certain revision, returns the previous revision.
202     *
203     * @param AbstractRevision $revision
204     * @return AbstractRevision|null null if there is no previous revision
205     */
206    public function getPrevRevision( AbstractRevision $revision ) {
207        $previousRevisionId = $revision->getPrevRevisionId();
208        if ( !$previousRevisionId ) {
209            return null;
210        }
211
212        return $this->getRevision( $previousRevisionId );
213    }
214
215    /**
216     * Given a certain revision, returns the next revision.
217     *
218     * @param AbstractRevision $revision
219     * @return AbstractRevision|null null if there is no next revision
220     */
221    public function getNextRevision( AbstractRevision $revision ) {
222        // make sure all revisions have been loaded
223        $this->getAllRevisions();
224
225        // find requested id, based on given revision
226        $ids = array_keys( $this->revisions );
227        $current = array_search( $revision->getRevisionId()->getAlphadecimal(), $ids );
228        $next = $current - 1;
229
230        if ( $next < 0 ) {
231            return null;
232        }
233
234        return $this->getRevision( UUID::create( $ids[$next] ) );
235    }
236
237    /**
238     * Returns the Title object this revision is associated with.
239     *
240     * @return Title
241     */
242    public function getTitle() {
243        return $this->getWorkflow()->getArticleTitle();
244    }
245
246    /**
247     * Returns the workflow object this collection is associated with.
248     *
249     * @return Workflow
250     * @throws InvalidDataException
251     */
252    public function getWorkflow() {
253        if ( !$this->workflow ) {
254            $uuid = $this->getWorkflowId();
255
256            $this->workflow = self::getStorage( Workflow::class )->get( $uuid );
257            if ( !$this->workflow ) {
258                throw new InvalidDataException( 'Invalid workflow: ' . $uuid->getAlphadecimal(), 'invalid-workflow' );
259            }
260        }
261
262        return $this->workflow;
263    }
264
265    public function getBoardWorkflow() {
266        return self::getStorage( Workflow::class )->get( $this->getBoardWorkflowId() );
267    }
268}