1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace AlfredTime; |
4
|
|
|
|
5
|
|
|
use AlfredTime\Toggl; |
6
|
|
|
use AlfredTime\Config; |
7
|
|
|
use AlfredTime\Harvest; |
8
|
|
|
|
9
|
|
|
class Time |
10
|
|
|
{ |
11
|
|
|
/** |
12
|
|
|
* @var mixed |
13
|
|
|
*/ |
14
|
|
|
private $config; |
15
|
|
|
|
16
|
|
|
/** |
17
|
|
|
* @var array |
18
|
|
|
*/ |
19
|
|
|
private $currentImplementation = [ |
20
|
|
|
'start' => ['toggl'], |
21
|
|
|
'start_default' => ['toggl', 'harvest'], |
22
|
|
|
'stop' => ['toggl', 'harvest'], |
23
|
|
|
'delete' => ['toggl'], |
24
|
|
|
]; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* @var mixed |
28
|
|
|
*/ |
29
|
|
|
private $harvest; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* @var mixed |
33
|
|
|
*/ |
34
|
|
|
private $message; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* @var mixed |
38
|
|
|
*/ |
39
|
|
|
private $toggl; |
40
|
|
|
|
41
|
|
|
public function __construct() |
42
|
|
|
{ |
43
|
|
|
$this->config = new Config(getenv('alfred_workflow_data') . '/config.json'); |
44
|
|
|
|
45
|
|
|
$this->harvest = new Harvest($this->config->get('harvest', 'domain'), $this->config->get('harvest', 'api_token')); |
46
|
|
|
$this->toggl = new Toggl($this->config->get('toggl', 'api_token')); |
47
|
|
|
$this->message = ''; |
48
|
|
|
} |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* @return mixed |
52
|
|
|
*/ |
53
|
|
|
public function activatedServices() |
54
|
|
|
{ |
55
|
|
|
$services = []; |
56
|
|
|
|
57
|
|
|
if ($this->isTogglActive() === true) { |
58
|
|
|
array_push($services, 'toggl'); |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
if ($this->isHarvestActive() === true) { |
62
|
|
|
array_push($services, 'harvest'); |
63
|
|
|
} |
64
|
|
|
|
65
|
|
|
return $services; |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* @param $timerId |
70
|
|
|
* @return string |
71
|
|
|
*/ |
72
|
|
|
public function deleteTimer($timerId) |
73
|
|
|
{ |
74
|
|
|
$message = ''; |
75
|
|
|
|
76
|
|
|
$atLeastOneTimerDeleted = false; |
77
|
|
|
|
78
|
|
View Code Duplication |
foreach ($this->implementedServicesForFeature('delete') as $service) { |
|
|
|
|
79
|
|
|
$functionName = 'delete' . ucfirst($service) . 'Timer'; |
80
|
|
|
|
81
|
|
|
if (call_user_func_array(['AlfredTime', $functionName], [$timerId]) === true) { |
82
|
|
|
if ($timerId === $this->config->get('workflow', 'timer_' . $service . '_id')) { |
83
|
|
|
$this->config->update('workflow', 'timer_' . $service . '_id', null); |
84
|
|
|
$atLeastOneTimerDeleted = true; |
85
|
|
|
} |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
$message .= $this->getLastMessage() . "\r\n"; |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
if ($atLeastOneTimerDeleted === true) { |
92
|
|
|
$this->config->update('workflow', 'is_timer_running', false); |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
return $message; |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
public function generateDefaultConfigurationFile() |
99
|
|
|
{ |
100
|
|
|
$this->config->generateDefaultConfigurationFile(); |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* @param $projectId |
105
|
|
|
* @return mixed |
106
|
|
|
*/ |
107
|
|
|
public function getProjectName($projectId) |
108
|
|
|
{ |
109
|
|
|
$projectName = ''; |
110
|
|
|
|
111
|
|
|
$projects = $this->getProjects(); |
112
|
|
|
|
113
|
|
|
foreach ($projects as $project) { |
114
|
|
|
if ($project['id'] === $projectId) { |
115
|
|
|
$projectName = $project['name']; |
116
|
|
|
break; |
117
|
|
|
} |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
return $projectName; |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* @return mixed |
125
|
|
|
*/ |
126
|
|
|
public function getProjects() |
127
|
|
|
{ |
128
|
|
|
$projects = []; |
129
|
|
|
|
130
|
|
|
if ($this->isTogglActive() === true) { |
131
|
|
|
$projects = array_merge($projects, $this->getTogglProjects()); |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
return $projects; |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
/** |
138
|
|
|
* @return mixed |
139
|
|
|
*/ |
140
|
|
|
public function getRecentTimers() |
141
|
|
|
{ |
142
|
|
|
$timers = []; |
143
|
|
|
|
144
|
|
|
if ($this->isTogglActive() === true) { |
145
|
|
|
$timers = array_merge($timers, $this->getRecentTogglTimers()); |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
return $timers; |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
/** |
152
|
|
|
* @return mixed |
153
|
|
|
*/ |
154
|
|
|
public function getTags() |
155
|
|
|
{ |
156
|
|
|
$tags = []; |
157
|
|
|
|
158
|
|
|
if ($this->isTogglActive() === true) { |
159
|
|
|
$tags = array_merge($tags, $this->getTogglTags()); |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
return $tags; |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
/** |
166
|
|
|
* @return mixed |
167
|
|
|
*/ |
168
|
|
|
public function getTimerDescription() |
169
|
|
|
{ |
170
|
|
|
return $this->config->get('workflow', 'timer_description'); |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
/** |
174
|
|
|
* @return mixed |
175
|
|
|
*/ |
176
|
|
|
public function hasTimerRunning() |
177
|
|
|
{ |
178
|
|
|
return $this->config->get('workflow', 'is_timer_running') === false ? false : true; |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
/** |
182
|
|
|
* @param $feature |
183
|
|
|
* @return mixed |
184
|
|
|
*/ |
185
|
|
|
public function implementedServicesForFeature($feature = null) |
186
|
|
|
{ |
187
|
|
|
$services = []; |
188
|
|
|
|
189
|
|
|
if (isset($this->currentImplementation[$feature]) === true) { |
190
|
|
|
$services = $this->currentImplementation[$feature]; |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
return $services; |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
/** |
197
|
|
|
* @return mixed |
198
|
|
|
*/ |
199
|
|
|
public function isConfigured() |
200
|
|
|
{ |
201
|
|
|
return $this->config === null ? false : true; |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
/** |
205
|
|
|
* @return mixed |
206
|
|
|
*/ |
207
|
|
|
public function servicesToUndo() |
208
|
|
|
{ |
209
|
|
|
$services = []; |
210
|
|
|
|
211
|
|
|
foreach ($this->activatedServices() as $service) { |
212
|
|
|
if ($this->config->get('workflow', 'timer_' . $service . '_id') !== null) { |
213
|
|
|
array_push($services, $service); |
214
|
|
|
} |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
return $services; |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
/** |
221
|
|
|
* @param $description |
222
|
|
|
* @param $projectsDefault |
223
|
|
|
* @param null $tagsDefault |
224
|
|
|
* @param boolean $startDefault |
225
|
|
|
* @return mixed |
226
|
|
|
*/ |
227
|
|
|
public function startTimer($description = '', $projectsDefault = null, $tagsDefault = null, $startDefault = false) |
228
|
|
|
{ |
229
|
|
|
$message = ''; |
230
|
|
|
$startType = $startDefault === true ? 'start_default' : 'start'; |
231
|
|
|
$atLeastOneServiceStarted = false; |
232
|
|
|
$implementedServices = $this->implementedServicesForFeature($startType); |
233
|
|
|
|
234
|
|
|
/* |
235
|
|
|
* When starting a new timer, all the services timer IDs have to be put to null |
236
|
|
|
* so that when the user uses the UNDO feature, it doesn't delete old previous |
237
|
|
|
* other services timers. The timer IDs are used for the UNDO feature and |
238
|
|
|
* should then contain the IDs of the last starts through the workflow, not |
239
|
|
|
* through each individual sefrvice |
240
|
|
|
*/ |
241
|
|
|
if (empty($implementedServices) === false) { |
242
|
|
|
foreach ($this->activatedServices() as $service) { |
243
|
|
|
$this->config->update('workflow', 'timer_' . $service . '_id', null); |
244
|
|
|
} |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
foreach ($implementedServices as $service) { |
248
|
|
|
$defaultProjectId = isset($projectsDefault[$service]) ? $projectsDefault[$service] : null; |
249
|
|
|
$defaultTags = isset($tagsDefault[$service]) ? $tagsDefault[$service] : null; |
250
|
|
|
|
251
|
|
|
$functionName = 'start' . ucfirst($service) . 'Timer'; |
252
|
|
|
$timerId = call_user_func_array(['AlfredTime', $functionName], [$description, $defaultProjectId, $defaultTags]); |
253
|
|
|
$this->config->update('workflow', 'timer_' . $service . '_id', $timerId); |
254
|
|
|
|
255
|
|
|
if ($timerId !== null) { |
256
|
|
|
$atLeastOneServiceStarted = true; |
257
|
|
|
} |
258
|
|
|
|
259
|
|
|
$message .= $this->getLastMessage() . "\r\n"; |
260
|
|
|
} |
261
|
|
|
|
262
|
|
|
if ($atLeastOneServiceStarted === true) { |
263
|
|
|
$this->config->update('workflow', 'timer_description', $description); |
264
|
|
|
$this->config->update('workflow', 'is_timer_running', true); |
265
|
|
|
} |
266
|
|
|
|
267
|
|
|
return $message; |
268
|
|
|
} |
269
|
|
|
|
270
|
|
|
/** |
271
|
|
|
* @param $description |
272
|
|
|
* @return mixed |
273
|
|
|
*/ |
274
|
|
|
public function startTimerWithDefaultOptions($description) |
275
|
|
|
{ |
276
|
|
|
$projectsDefault = [ |
277
|
|
|
'toggl' => $this->config->get('toggl', 'default_project_id'), |
278
|
|
|
'harvest' => $this->config->get('harvest', 'default_project_id'), |
279
|
|
|
]; |
280
|
|
|
|
281
|
|
|
$tagsDefault = [ |
282
|
|
|
'toggl' => $this->config->get('toggl', 'default_tags'), |
283
|
|
|
'harvest' => $this->config->get('harvest', 'default_task_id'), |
284
|
|
|
]; |
285
|
|
|
|
286
|
|
|
return $this->startTimer($description, $projectsDefault, $tagsDefault, true); |
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
/** |
290
|
|
|
* @return mixed |
291
|
|
|
*/ |
292
|
|
|
public function stopRunningTimer() |
293
|
|
|
{ |
294
|
|
|
$message = ''; |
295
|
|
|
$atLeastOneServiceStopped = false; |
296
|
|
|
|
297
|
|
|
foreach ($this->activatedServices() as $service) { |
298
|
|
|
$functionName = 'stop' . ucfirst($service) . 'Timer'; |
299
|
|
|
|
300
|
|
|
if (call_user_func(['AlfredTime', $functionName]) === true) { |
301
|
|
|
$atLeastOneServiceStopped = true; |
302
|
|
|
} |
303
|
|
|
|
304
|
|
|
$message .= $this->getLastMessage() . "\r\n"; |
305
|
|
|
} |
306
|
|
|
|
307
|
|
|
if ($atLeastOneServiceStopped === true) { |
308
|
|
|
$this->config->update('workflow', 'is_timer_running', false); |
309
|
|
|
} |
310
|
|
|
|
311
|
|
|
return $message; |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
/** |
315
|
|
|
* @return mixed |
316
|
|
|
*/ |
317
|
|
|
public function syncOnlineDataToLocalCache() |
318
|
|
|
{ |
319
|
|
|
$message = ''; |
320
|
|
|
|
321
|
|
|
if ($this->isTogglActive() === true) { |
322
|
|
|
$message .= $this->syncTogglOnlineDataToLocalCache(); |
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
return $message; |
326
|
|
|
} |
327
|
|
|
|
328
|
|
|
/** |
329
|
|
|
* @return mixed |
330
|
|
|
*/ |
331
|
|
|
public function undoTimer() |
332
|
|
|
{ |
333
|
|
|
$message = ''; |
334
|
|
|
|
335
|
|
|
if ($this->hasTimerRunning() === true) { |
336
|
|
|
$this->stopRunningTimer(); |
337
|
|
|
} |
338
|
|
|
|
339
|
|
|
$atLeastOneTimerDeleted = false; |
340
|
|
|
|
341
|
|
View Code Duplication |
foreach ($this->servicesToUndo() as $service) { |
|
|
|
|
342
|
|
|
$functionName = 'delete' . ucfirst($service) . 'Timer'; |
343
|
|
|
|
344
|
|
|
if (call_user_func_array(['AlfredTime', $functionName], [$this->config->get('workflow', 'timer_' . $service . '_id')]) === true) { |
345
|
|
|
$this->config->update('workflow', 'timer_' . $service . '_id', null); |
346
|
|
|
$atLeastOneTimerDeleted = true; |
347
|
|
|
} |
348
|
|
|
|
349
|
|
|
$message .= $this->getLastMessage() . "\r\n"; |
350
|
|
|
} |
351
|
|
|
|
352
|
|
|
if ($atLeastOneTimerDeleted === true) { |
353
|
|
|
$this->config->update('workflow', 'is_timer_running', false); |
354
|
|
|
} |
355
|
|
|
|
356
|
|
|
return $message; |
357
|
|
|
} |
358
|
|
|
|
359
|
|
|
/** |
360
|
|
|
* @param $harvestId |
361
|
|
|
* @return mixed |
362
|
|
|
*/ |
363
|
|
|
private function deleteHarvestTimer($harvestId) |
364
|
|
|
{ |
365
|
|
|
$res = $this->harvest->deleteTimer($harvestId); |
366
|
|
|
$this->message = $this->harvest->getLastMessage(); |
367
|
|
|
|
368
|
|
|
return $res; |
369
|
|
|
} |
370
|
|
|
|
371
|
|
|
/** |
372
|
|
|
* @param $togglId |
373
|
|
|
* @return mixed |
374
|
|
|
*/ |
375
|
|
|
private function deleteTogglTimer($togglId) |
376
|
|
|
{ |
377
|
|
|
$res = $this->toggl->deleteTimer($togglId); |
378
|
|
|
$this->message = $this->toggl->getLastMessage(); |
379
|
|
|
|
380
|
|
|
return $res; |
381
|
|
|
} |
382
|
|
|
|
383
|
|
|
/** |
384
|
|
|
* @return mixed |
385
|
|
|
*/ |
386
|
|
|
private function getLastMessage() |
387
|
|
|
{ |
388
|
|
|
return $this->message; |
389
|
|
|
} |
390
|
|
|
|
391
|
|
|
/** |
392
|
|
|
* @return mixed |
393
|
|
|
*/ |
394
|
|
|
private function getRecentTogglTimers() |
395
|
|
|
{ |
396
|
|
|
return $this->toggl->getRecentTimers(); |
397
|
|
|
} |
398
|
|
|
|
399
|
|
|
/** |
400
|
|
|
* @return mixed |
401
|
|
|
*/ |
402
|
|
|
private function getTogglProjects() |
403
|
|
|
{ |
404
|
|
|
$cacheData = []; |
405
|
|
|
$cacheFile = getenv('alfred_workflow_data') . '/toggl_cache.json'; |
406
|
|
|
|
407
|
|
|
if (file_exists($cacheFile)) { |
408
|
|
|
$cacheData = json_decode(file_get_contents($cacheFile), true); |
409
|
|
|
} |
410
|
|
|
|
411
|
|
|
/* |
412
|
|
|
* To only show projects that are currently active |
413
|
|
|
* The Toggl API is slightly weird on that |
414
|
|
|
*/ |
415
|
|
|
foreach ($cacheData['data']['projects'] as $key => $project) { |
416
|
|
|
if (isset($project['server_deleted_at']) === true) { |
417
|
|
|
unset($cacheData['data']['projects'][$key]); |
418
|
|
|
} |
419
|
|
|
} |
420
|
|
|
|
421
|
|
|
return $cacheData['data']['projects']; |
422
|
|
|
} |
423
|
|
|
|
424
|
|
|
/** |
425
|
|
|
* @return mixed |
426
|
|
|
*/ |
427
|
|
|
private function getTogglTags() |
428
|
|
|
{ |
429
|
|
|
$cacheFile = getenv('alfred_workflow_data') . '/toggl_cache.json'; |
430
|
|
|
$cacheData = []; |
431
|
|
|
|
432
|
|
|
if (file_exists($cacheFile)) { |
433
|
|
|
$cacheData = json_decode(file_get_contents($cacheFile), true); |
434
|
|
|
} |
435
|
|
|
|
436
|
|
|
return $cacheData['data']['tags']; |
437
|
|
|
} |
438
|
|
|
|
439
|
|
|
/** |
440
|
|
|
* @return mixed |
441
|
|
|
*/ |
442
|
|
|
private function isHarvestActive() |
443
|
|
|
{ |
444
|
|
|
return $this->config->get('harvest', 'is_active'); |
445
|
|
|
} |
446
|
|
|
|
447
|
|
|
/** |
448
|
|
|
* @return mixed |
449
|
|
|
*/ |
450
|
|
|
private function isTogglActive() |
451
|
|
|
{ |
452
|
|
|
return $this->config->get('toggl', 'is_active'); |
453
|
|
|
} |
454
|
|
|
|
455
|
|
|
/** |
456
|
|
|
* @param $data |
457
|
|
|
*/ |
458
|
|
|
private function saveTogglDataCache($data) |
459
|
|
|
{ |
460
|
|
|
$cacheFile = getenv('alfred_workflow_data') . '/toggl_cache.json'; |
461
|
|
|
file_put_contents($cacheFile, json_encode($data)); |
462
|
|
|
} |
463
|
|
|
|
464
|
|
|
/** |
465
|
|
|
* @param $description |
466
|
|
|
* @param $projectId |
467
|
|
|
* @param $taskId |
468
|
|
|
* @return mixed |
469
|
|
|
*/ |
470
|
|
|
private function startHarvestTimer($description, $projectId, $taskId) |
471
|
|
|
{ |
472
|
|
|
$harvestId = $this->harvest->startTimer($description, $projectId, $taskId); |
473
|
|
|
$this->message = $this->harvest->getLastMessage(); |
474
|
|
|
|
475
|
|
|
return $harvestId; |
476
|
|
|
} |
477
|
|
|
|
478
|
|
|
/** |
479
|
|
|
* @param $description |
480
|
|
|
* @param $projectId |
481
|
|
|
* @param $tagNames |
482
|
|
|
* @return mixed |
483
|
|
|
*/ |
484
|
|
|
private function startTogglTimer($description, $projectId, $tagNames) |
485
|
|
|
{ |
486
|
|
|
$togglId = $this->toggl->startTimer($description, $projectId, $tagNames); |
487
|
|
|
$this->message = $this->toggl->getLastMessage(); |
488
|
|
|
|
489
|
|
|
return $togglId; |
490
|
|
|
} |
491
|
|
|
|
492
|
|
|
/** |
493
|
|
|
* @return mixed |
494
|
|
|
*/ |
495
|
|
View Code Duplication |
private function stopHarvestTimer() |
|
|
|
|
496
|
|
|
{ |
497
|
|
|
$harvestId = $this->config->get('workflow', 'timer_harvest_id'); |
498
|
|
|
|
499
|
|
|
$res = $this->harvest->stopTimer($harvestId); |
500
|
|
|
$this->message = $this->harvest->getLastMessage(); |
501
|
|
|
|
502
|
|
|
return $res; |
503
|
|
|
} |
504
|
|
|
|
505
|
|
|
/** |
506
|
|
|
* @return mixed |
507
|
|
|
*/ |
508
|
|
View Code Duplication |
private function stopTogglTimer() |
|
|
|
|
509
|
|
|
{ |
510
|
|
|
$togglId = $this->config->get('workflow', 'timer_toggl_id'); |
511
|
|
|
|
512
|
|
|
$res = $this->toggl->stopTimer($togglId); |
513
|
|
|
$this->message = $this->toggl->getLastMessage(); |
514
|
|
|
|
515
|
|
|
return $res; |
516
|
|
|
} |
517
|
|
|
|
518
|
|
|
/** |
519
|
|
|
* @return mixed |
520
|
|
|
*/ |
521
|
|
|
private function syncTogglOnlineDataToLocalCache() |
522
|
|
|
{ |
523
|
|
|
$data = $this->toggl->getOnlineData(); |
524
|
|
|
|
525
|
|
|
$this->message = $this->toggl->getLastMessage(); |
526
|
|
|
|
527
|
|
|
if (empty($data) === false) { |
528
|
|
|
$this->saveTogglDataCache($data); |
529
|
|
|
} |
530
|
|
|
|
531
|
|
|
return $this->message; |
532
|
|
|
} |
533
|
|
|
} |
534
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.