Passed
Push — master ( 8f2f39...434123 )
by y
01:50
created

Project::getEvents()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Helix\Asana;
4
5
use Helix\Asana\Base\AbstractEntity;
6
use Helix\Asana\Base\AbstractEntity\CrudTrait;
7
use Helix\Asana\Base\AbstractEntity\DateTrait;
8
use Helix\Asana\Base\AbstractEntity\PostMutatorTrait;
9
use Helix\Asana\Base\AbstractEntity\SyncTrait;
10
use Helix\Asana\CustomField\FieldSetting;
11
use Helix\Asana\CustomField\FieldSettingsTrait;
12
use Helix\Asana\Project\Section;
13
use Helix\Asana\Project\Status;
14
use Helix\Asana\Project\TaskCounts;
15
use Helix\Asana\Webhook\ProjectWebhook;
16
use Helix\Asana\Workspace\WorkspaceTrait;
17
use IteratorAggregate;
18
use Traversable;
19
20
/**
21
 * A project.
22
 *
23
 * @see https://developers.asana.com/docs/asana-projects
24
 * @see https://developers.asana.com/docs/project
25
 *
26
 * @method bool         isArchived          ()
27
 * @method string       getColor            () @depends after-create
28
 * @method string       getCreatedAt        () RFC3339x
29
 * @method null|Status  getCurrentStatus    ()
30
 * @method User[]       getFollowers        ()
31
 * @method string       getIcon             ()
32
 * @method string       getLayout           ()
33
 * @method User[]       getMembers          ()
34
 * @method string       getModifiedAt       () RFC3339x
35
 * @method string       getName             ()
36
 * @method string       getNotes            ()
37
 * @method null|User    getOwner            ()
38
 * @method bool         isPublic            ()
39
 * @method null|Team    getTeam             ()
40
 *
41
 * @method bool         hasFollowers        ()
42
 * @method bool         hasMembers          ()
43
 * @method bool         hasOwner            ()
44
 * @method bool         hasTeam             ()
45
 *
46
 * @method $this        setArchived         (bool $archived)
47
 * @method $this        setColor            (string $color)
48
 * @method $this        setLayout           (string $layout)
49
 * @method $this        setName             (string $name)
50
 * @method $this        setNotes            (string $notes)
51
 * @method $this        setOwner            (User $owner)
52
 * @method $this        setPublic           (bool $public)
53
 *
54
 * @method User[]       selectFollowers     (callable $filter) `fn( User $user ): bool`
55
 * @method User[]       selectMembers       (callable $filter) `fn( User $user ): bool`
56
 * @method Section[]    selectSections      (callable $filter) `fn( Section $section ): bool`
57
 * @method Status[]     selectStatuses      (callable $filter) `fn( Status $status ): bool`
58
 * @method Task[]       selectTasks         (callable $fn, array $apiX = Task::GET_INCOMPLETE) `fn( Task $task ): bool`
59
 */
60
class Project extends AbstractEntity implements IteratorAggregate {
61
62
    use CrudTrait;
63
    use DateTrait;
64
    use FieldSettingsTrait;
65
    use PostMutatorTrait;
66
    use SyncTrait;
67
    use WorkspaceTrait;
68
69
    const TYPE = 'project';
70
71
    const LAYOUT_BOARD = 'board';
72
    const LAYOUT_LIST = 'list';
73
74
    const GET_ACTIVE = ['archived' => false];
75
    const GET_ARCHIVED = ['archived' => true];
76
77
    protected const MAP = [
78
        'current_status' => Status::class,
79
        'custom_field_settings' => [FieldSetting::class],
80
        'followers' => [User::class],
81
        'members' => [User::class],
82
        'owner' => User::class,
83
        'team' => Team::class,
84
        'workspace' => Workspace::class
85
    ];
86
87
    /**
88
     * @var Section
89
     */
90
    private $defaultSection;
91
92
    /**
93
     * `projects/{gid}`
94
     *
95
     * @return string
96
     */
97
    final public function __toString (): string {
98
        return "projects/{$this->getGid()}";
99
    }
100
101
    /**
102
     * `projects`
103
     *
104
     * @return string
105
     */
106
    final protected function _getDir (): string {
107
        return 'projects';
108
    }
109
110
    protected function _setData (array $data): void {
111
        // this is always empty. fields are in the settings, values are in tasks.
112
        unset($data['custom_fields']);
113
114
        // deprecated for due_on
115
        unset($data['due_date']);
116
117
        parent::_setData($data);
118
    }
119
120
    /**
121
     * @depends after-create
122
     * @param User $user
123
     * @return $this
124
     */
125
    public function addMember (User $user) {
126
        return $this->addMembers([$user]);
127
    }
128
129
    /**
130
     * @depends after-create
131
     * @param User[] $users
132
     * @return $this
133
     */
134
    public function addMembers (array $users) {
135
        return $this->_addWithPost("{$this}/addMembers", [
136
            'members' => array_column($users, 'gid')
137
        ], 'members', $users);
138
    }
139
140
    /**
141
     * @depends after-create
142
     * @param string $target
143
     * @return ProjectWebhook
144
     */
145
    public function addWebhook (string $target) {
146
        /** @var ProjectWebhook $webhook */
147
        $webhook = $this->api->factory($this, ProjectWebhook::class);
148
        return $webhook->create($this, $target);
149
    }
150
151
    /**
152
     * Duplicates the project.
153
     *
154
     * @see https://developers.asana.com/docs/duplicate-a-project
155
     *
156
     * If `$team` is `null`, the duplicate will inherit the existing team.
157
     *
158
     * If `$schedule` is given:
159
     * - It must have either `due_on` or `start_on`
160
     * - `task_dates` is automatically added to `$include`
161
     * - `should_skip_weekends` defaults to `true` if not given.
162
     *
163
     * @depends after-create
164
     * @param string $name
165
     * @param string[] $include
166
     * @param null|Team $team
167
     * @param array $schedule
168
     * @return Job
169
     */
170
    public function duplicate (string $name, array $include, Team $team = null, array $schedule = []) {
171
        $data = ['name' => $name];
172
        if ($team) {
173
            $data['team'] = $team->getGid();
174
        }
175
        if ($schedule) {
176
            $include[] = 'task_dates';
177
            $schedule += ['should_skip_weekends' => true];
178
            $data['schedule_dates'] = $schedule;
179
        }
180
        $data['include'] = array_values($include);
181
        /** @var array $remote */
182
        $remote = $this->api->post("{$this}/duplicate", $data);
183
        return $this->api->factory($this, Job::class, $remote);
184
    }
185
186
    /**
187
     * @depends after-create
188
     * @return Section
189
     */
190
    public function getDefaultSection () {
191
        return $this->defaultSection ?? $this->defaultSection = $this->getSections(1)[0];
192
    }
193
194
    /**
195
     * Iterates over sections.
196
     *
197
     * @see https://developers.asana.com/docs/get-sections-in-a-project
198
     *
199
     * @param int $limit
200
     * @return Traversable|Section[]
201
     */
202
    public function getIterator (int $limit = PHP_INT_MAX) {
203
        return $this->api->loadEach($this, Section::class, "{$this}/sections", ['limit' => $limit]);
204
    }
205
206
    /**
207
     * @depends after-create
208
     * @param int $limit
209
     * @return Section[]
210
     */
211
    public function getSections (int $limit = PHP_INT_MAX) {
212
        return iterator_to_array($this->getIterator($limit));
213
    }
214
215
    /**
216
     * @depends after-create
217
     * @return Status[]
218
     */
219
    public function getStatuses () {
220
        return $this->api->loadAll($this, Status::class, "{$this}/project_statuses");
221
    }
222
223
    /**
224
     * @depends after-create
225
     * @return TaskCounts
226
     */
227
    public function getTaskCounts () {
228
        /** @var array $remote */
229
        $remote = $this->api->get("{$this}/task_counts", [], TaskCounts::OPT);
230
        return $this->api->factory($this, TaskCounts::class, $remote);
231
    }
232
233
    /**
234
     * The project's tasks.
235
     *
236
     * @depends after-create
237
     * @param array $filter
238
     * @return Task[]
239
     */
240
    public function getTasks (array $filter = Task::GET_INCOMPLETE) {
241
        $filter['project'] = $this->getGid();
242
        return $this->api->loadAll($this, Task::class, "tasks", $filter);
243
    }
244
245
    /**
246
     * @depends after-create
247
     * @return string
248
     */
249
    public function getUrl (): string {
250
        return "https://app.asana.com/0/{$this->getGid()}";
251
    }
252
253
    /**
254
     * @return ProjectWebhook[]
255
     */
256
    public function getWebhooks () {
257
        return $this->api->loadAll($this, ProjectWebhook::class, 'webhooks', [
258
            'workspace' => $this->getWorkspace()->getGid(),
259
            'resource' => $this->getGid()
260
        ]);
261
    }
262
263
    /**
264
     * @return bool
265
     */
266
    final public function isBoard (): bool {
267
        return $this->getLayout() === self::LAYOUT_BOARD;
268
    }
269
270
    /**
271
     * @return bool
272
     */
273
    final public function isList (): bool {
274
        return $this->getLayout() === self::LAYOUT_LIST;
275
    }
276
277
    /**
278
     * Factory.
279
     *
280
     * @depends after-create
281
     * @return Section
282
     */
283
    public function newSection () {
284
        return $this->api->factory($this, Section::class, ['project' => $this]);
285
    }
286
287
    /**
288
     * Factory.
289
     *
290
     * @depends after-create
291
     * @return Status
292
     */
293
    public function newStatus () {
294
        return $this->api->factory($this, Status::class);
295
    }
296
297
    /**
298
     * Factory.
299
     *
300
     * @depends after-create
301
     * @return Task
302
     */
303
    public function newTask () {
304
        return $this->getDefaultSection()->newTask();
305
    }
306
307
    /**
308
     * @depends after-create
309
     * @param User $user
310
     * @return $this
311
     */
312
    public function removeMember (User $user) {
313
        return $this->removeMembers([$user]);
314
    }
315
316
    /**
317
     * @depends after-create
318
     * @param User[] $users
319
     * @return $this
320
     */
321
    public function removeMembers (array $users) {
322
        return $this->_removeWithPost("{$this}/removeMembers", [
323
            'members' => array_column($users, 'gid')
324
        ], 'members', $users);
325
    }
326
327
    /**
328
     * @depends create-only
329
     * @param null|Team $team
330
     * @return $this
331
     */
332
    public function setTeam (?Team $team) {
333
        if ($team and !$this->hasWorkspace()) {
334
            $this->setWorkspace($team->getOrganization());
335
        }
336
        return $this->_set('team', $team);
337
    }
338
}