|
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 array |
|
38
|
|
|
*/ |
|
39
|
|
|
private $services = [ |
|
40
|
|
|
'toggl', |
|
41
|
|
|
'harvest', |
|
42
|
|
|
]; |
|
43
|
|
|
|
|
44
|
|
|
/** |
|
45
|
|
|
* @var mixed |
|
46
|
|
|
*/ |
|
47
|
|
|
private $toggl; |
|
48
|
|
|
|
|
49
|
|
|
public function __construct() |
|
50
|
|
|
{ |
|
51
|
|
|
$this->config = new Config(getenv('alfred_workflow_data') . '/config.json'); |
|
52
|
|
|
|
|
53
|
|
|
$this->harvest = new Harvest($this->config->get('harvest', 'domain'), $this->config->get('harvest', 'api_token')); |
|
54
|
|
|
$this->toggl = new Toggl($this->config->get('toggl', 'api_token')); |
|
55
|
|
|
$this->message = ''; |
|
56
|
|
|
} |
|
57
|
|
|
|
|
58
|
|
|
/** |
|
59
|
|
|
* @return mixed |
|
60
|
|
|
*/ |
|
61
|
|
|
public function activatedServices() |
|
62
|
|
|
{ |
|
63
|
|
|
$activatedServices = []; |
|
64
|
|
|
|
|
65
|
|
|
foreach ($this->services as $service) { |
|
66
|
|
|
if ($this->isServiceActive($service) === true) { |
|
67
|
|
|
array_push($activatedServices, $service); |
|
68
|
|
|
} |
|
69
|
|
|
} |
|
70
|
|
|
|
|
71
|
|
|
return $activatedServices; |
|
72
|
|
|
} |
|
73
|
|
|
|
|
74
|
|
|
/** |
|
75
|
|
|
* @param $service |
|
76
|
|
|
* @param $timerId |
|
77
|
|
|
* @return boolean |
|
78
|
|
|
*/ |
|
79
|
|
|
public function deleteServiceTimer($service, $timerId) |
|
80
|
|
|
{ |
|
81
|
|
|
$res = false; |
|
82
|
|
|
|
|
83
|
|
|
if ($this->$service->deleteTimer($timerId) === true) { |
|
84
|
|
|
if ($timerId === $this->config->get('workflow', 'timer_' . $service . '_id')) { |
|
85
|
|
|
$this->config->update('workflow', 'timer_' . $service . '_id', null); |
|
86
|
|
|
$res = true; |
|
87
|
|
|
} |
|
88
|
|
|
} |
|
89
|
|
|
|
|
90
|
|
|
return $res; |
|
91
|
|
|
} |
|
92
|
|
|
|
|
93
|
|
|
/** |
|
94
|
|
|
* @param $timerId |
|
95
|
|
|
* @return string |
|
96
|
|
|
*/ |
|
97
|
|
|
public function deleteTimer($timerId) |
|
98
|
|
|
{ |
|
99
|
|
|
$message = ''; |
|
100
|
|
|
$atLeastOneTimerDeleted = false; |
|
101
|
|
|
|
|
102
|
|
|
foreach ($this->implementedServicesForFeature('delete') as $service) { |
|
103
|
|
|
if ($this->deleteServiceTimer($service, $timerId) === true) { |
|
104
|
|
|
$atLeastOneTimerDeleted = true; |
|
105
|
|
|
} |
|
106
|
|
|
|
|
107
|
|
|
$message .= $this->$service->getLastMessage() . "\r\n"; |
|
108
|
|
|
} |
|
109
|
|
|
|
|
110
|
|
|
if ($atLeastOneTimerDeleted === true) { |
|
111
|
|
|
$this->config->update('workflow', 'is_timer_running', false); |
|
112
|
|
|
} |
|
113
|
|
|
|
|
114
|
|
|
return $message; |
|
115
|
|
|
} |
|
116
|
|
|
|
|
117
|
|
|
public function generateDefaultConfigurationFile() |
|
118
|
|
|
{ |
|
119
|
|
|
$this->config->generateDefaultConfigurationFile(); |
|
120
|
|
|
} |
|
121
|
|
|
|
|
122
|
|
|
/** |
|
123
|
|
|
* @param $projectId |
|
124
|
|
|
* @return mixed |
|
125
|
|
|
*/ |
|
126
|
|
|
public function getProjectName($projectId) |
|
127
|
|
|
{ |
|
128
|
|
|
$projectName = ''; |
|
129
|
|
|
|
|
130
|
|
|
$projects = $this->getProjects(); |
|
131
|
|
|
|
|
132
|
|
|
foreach ($projects as $project) { |
|
133
|
|
|
if ($project['id'] === $projectId) { |
|
134
|
|
|
$projectName = $project['name']; |
|
135
|
|
|
break; |
|
136
|
|
|
} |
|
137
|
|
|
} |
|
138
|
|
|
|
|
139
|
|
|
return $projectName; |
|
140
|
|
|
} |
|
141
|
|
|
|
|
142
|
|
|
/** |
|
143
|
|
|
* @return mixed |
|
144
|
|
|
*/ |
|
145
|
|
View Code Duplication |
public function getProjects() |
|
|
|
|
|
|
146
|
|
|
{ |
|
147
|
|
|
$tempServices = ['toggl']; |
|
148
|
|
|
$projects = []; |
|
149
|
|
|
|
|
150
|
|
|
/* |
|
151
|
|
|
* Temporary, only get the projects of Toggl |
|
152
|
|
|
* Later, we will get Harvest ones too |
|
153
|
|
|
*/ |
|
154
|
|
|
foreach ($tempServices as $service) { |
|
155
|
|
|
if ($this->isServiceActive($service) === true) { |
|
156
|
|
|
$projects = $this->getServiceProjects($service); |
|
157
|
|
|
} |
|
158
|
|
|
} |
|
159
|
|
|
|
|
160
|
|
|
return $projects; |
|
161
|
|
|
} |
|
162
|
|
|
|
|
163
|
|
|
/** |
|
164
|
|
|
* @return mixed |
|
165
|
|
|
*/ |
|
166
|
|
View Code Duplication |
public function getRecentTimers() |
|
|
|
|
|
|
167
|
|
|
{ |
|
168
|
|
|
$tempServices = ['toggl']; |
|
169
|
|
|
$timers = []; |
|
170
|
|
|
|
|
171
|
|
|
foreach ($tempServices as $service) { |
|
172
|
|
|
if ($this->isServiceActive($service) === true) { |
|
173
|
|
|
$timers = array_merge($timers, $this->getRecentServiceTimers($service)); |
|
174
|
|
|
} |
|
175
|
|
|
} |
|
176
|
|
|
|
|
177
|
|
|
return $timers; |
|
178
|
|
|
} |
|
179
|
|
|
|
|
180
|
|
|
/** |
|
181
|
|
|
* @param $service |
|
182
|
|
|
*/ |
|
183
|
|
|
public function getServiceProjects($service) |
|
184
|
|
|
{ |
|
185
|
|
|
$projects = []; |
|
186
|
|
|
$cacheFile = getenv('alfred_workflow_data') . '/' . $service . '_cache.json'; |
|
187
|
|
|
|
|
188
|
|
|
if (file_exists($cacheFile)) { |
|
189
|
|
|
$projects = json_decode(file_get_contents($cacheFile), true)['data']['projects']; |
|
190
|
|
|
|
|
191
|
|
|
/* |
|
192
|
|
|
* To only show projects that are currently active |
|
193
|
|
|
* The Toggl API is slightly weird on that |
|
194
|
|
|
*/ |
|
195
|
|
|
foreach ($projects as $key => $project) { |
|
196
|
|
|
if (isset($project['server_deleted_at']) === true) { |
|
197
|
|
|
unset($projects[$key]); |
|
198
|
|
|
} |
|
199
|
|
|
} |
|
200
|
|
|
} |
|
201
|
|
|
|
|
202
|
|
|
return $projects; |
|
203
|
|
|
} |
|
204
|
|
|
|
|
205
|
|
|
/** |
|
206
|
|
|
* @return mixed |
|
207
|
|
|
*/ |
|
208
|
|
View Code Duplication |
public function getTags() |
|
|
|
|
|
|
209
|
|
|
{ |
|
210
|
|
|
$tempServices = ['toggl']; |
|
211
|
|
|
$tags = []; |
|
212
|
|
|
|
|
213
|
|
|
foreach ($tempServices as $service) { |
|
214
|
|
|
if ($this->isServiceActive($service) === true) { |
|
215
|
|
|
$tags = array_merge($tags, $this->getServiceTags($service)); |
|
216
|
|
|
} |
|
217
|
|
|
} |
|
218
|
|
|
|
|
219
|
|
|
return $tags; |
|
220
|
|
|
} |
|
221
|
|
|
|
|
222
|
|
|
/** |
|
223
|
|
|
* @return mixed |
|
224
|
|
|
*/ |
|
225
|
|
|
public function getTimerDescription() |
|
226
|
|
|
{ |
|
227
|
|
|
return $this->config->get('workflow', 'timer_description'); |
|
228
|
|
|
} |
|
229
|
|
|
|
|
230
|
|
|
/** |
|
231
|
|
|
* @return boolean |
|
232
|
|
|
*/ |
|
233
|
|
|
public function hasTimerRunning() |
|
234
|
|
|
{ |
|
235
|
|
|
return $this->config->get('workflow', 'is_timer_running') === false ? false : true; |
|
236
|
|
|
} |
|
237
|
|
|
|
|
238
|
|
|
/** |
|
239
|
|
|
* @param string $feature |
|
240
|
|
|
* @return mixed |
|
241
|
|
|
*/ |
|
242
|
|
|
public function implementedServicesForFeature($feature = null) |
|
243
|
|
|
{ |
|
244
|
|
|
$services = []; |
|
245
|
|
|
|
|
246
|
|
|
if (isset($this->currentImplementation[$feature]) === true) { |
|
247
|
|
|
$services = $this->currentImplementation[$feature]; |
|
248
|
|
|
} |
|
249
|
|
|
|
|
250
|
|
|
return $services; |
|
251
|
|
|
} |
|
252
|
|
|
|
|
253
|
|
|
/** |
|
254
|
|
|
* @return boolean |
|
255
|
|
|
*/ |
|
256
|
|
|
public function isConfigured() |
|
257
|
|
|
{ |
|
258
|
|
|
return $this->config === null ? false : true; |
|
259
|
|
|
} |
|
260
|
|
|
|
|
261
|
|
|
/** |
|
262
|
|
|
* @param $service |
|
263
|
|
|
* @return mixed |
|
264
|
|
|
*/ |
|
265
|
|
|
public function isServiceActive($service) |
|
266
|
|
|
{ |
|
267
|
|
|
return $this->config->get($service, 'is_active'); |
|
268
|
|
|
} |
|
269
|
|
|
|
|
270
|
|
|
/** |
|
271
|
|
|
* @return mixed |
|
272
|
|
|
*/ |
|
273
|
|
|
public function servicesToUndo() |
|
274
|
|
|
{ |
|
275
|
|
|
$services = []; |
|
276
|
|
|
|
|
277
|
|
|
foreach ($this->activatedServices() as $service) { |
|
278
|
|
|
if ($this->config->get('workflow', 'timer_' . $service . '_id') !== null) { |
|
279
|
|
|
array_push($services, $service); |
|
280
|
|
|
} |
|
281
|
|
|
} |
|
282
|
|
|
|
|
283
|
|
|
return $services; |
|
284
|
|
|
} |
|
285
|
|
|
|
|
286
|
|
|
/** |
|
287
|
|
|
* @param $description |
|
288
|
|
|
* @param $projectsDefault |
|
289
|
|
|
* @param null $tagsDefault |
|
290
|
|
|
* @param boolean $startDefault |
|
291
|
|
|
* @return string |
|
292
|
|
|
*/ |
|
293
|
|
|
public function startTimer($description = '', $projectsDefault = null, $tagsDefault = null, $startDefault = false) |
|
294
|
|
|
{ |
|
295
|
|
|
$message = ''; |
|
296
|
|
|
$startType = $startDefault === true ? 'start_default' : 'start'; |
|
297
|
|
|
$atLeastOneServiceStarted = false; |
|
298
|
|
|
$implementedServices = $this->implementedServicesForFeature($startType); |
|
299
|
|
|
|
|
300
|
|
|
/* |
|
301
|
|
|
* When starting a new timer, all the services timer IDs have to be put to null |
|
302
|
|
|
* so that when the user uses the UNDO feature, it doesn't delete old previous |
|
303
|
|
|
* other services timers. The timer IDs are used for the UNDO feature and |
|
304
|
|
|
* should then contain the IDs of the last starts through the workflow, not |
|
305
|
|
|
* through each individual sefrvice |
|
306
|
|
|
*/ |
|
307
|
|
|
if (empty($implementedServices) === false) { |
|
308
|
|
|
foreach ($this->activatedServices() as $service) { |
|
309
|
|
|
$this->config->update('workflow', 'timer_' . $service . '_id', null); |
|
310
|
|
|
} |
|
311
|
|
|
|
|
312
|
|
|
foreach ($implementedServices as $service) { |
|
313
|
|
|
$defaultProjectId = isset($projectsDefault[$service]) ? $projectsDefault[$service] : null; |
|
314
|
|
|
$defaultTags = isset($tagsDefault[$service]) ? $tagsDefault[$service] : null; |
|
315
|
|
|
|
|
316
|
|
|
$timerId = $this->$service->startTimer($description, $defaultProjectId, $defaultTags); |
|
317
|
|
|
$this->config->update('workflow', 'timer_' . $service . '_id', $timerId); |
|
318
|
|
|
|
|
319
|
|
|
if ($timerId !== null) { |
|
320
|
|
|
$atLeastOneServiceStarted = true; |
|
321
|
|
|
} |
|
322
|
|
|
|
|
323
|
|
|
$message .= $this->$service->getLastMessage() . "\r\n"; |
|
324
|
|
|
} |
|
325
|
|
|
} |
|
326
|
|
|
|
|
327
|
|
|
if ($atLeastOneServiceStarted === true) { |
|
328
|
|
|
$this->config->update('workflow', 'timer_description', $description); |
|
329
|
|
|
$this->config->update('workflow', 'is_timer_running', true); |
|
330
|
|
|
} |
|
331
|
|
|
|
|
332
|
|
|
return $message; |
|
333
|
|
|
} |
|
334
|
|
|
|
|
335
|
|
|
/** |
|
336
|
|
|
* @param $description |
|
337
|
|
|
* @return string |
|
338
|
|
|
*/ |
|
339
|
|
|
public function startTimerWithDefaultOptions($description) |
|
340
|
|
|
{ |
|
341
|
|
|
$projectsDefault = [ |
|
342
|
|
|
'toggl' => $this->config->get('toggl', 'default_project_id'), |
|
343
|
|
|
'harvest' => $this->config->get('harvest', 'default_project_id'), |
|
344
|
|
|
]; |
|
345
|
|
|
|
|
346
|
|
|
$tagsDefault = [ |
|
347
|
|
|
'toggl' => $this->config->get('toggl', 'default_tags'), |
|
348
|
|
|
'harvest' => $this->config->get('harvest', 'default_task_id'), |
|
349
|
|
|
]; |
|
350
|
|
|
|
|
351
|
|
|
return $this->startTimer($description, $projectsDefault, $tagsDefault, true); |
|
352
|
|
|
} |
|
353
|
|
|
|
|
354
|
|
|
/** |
|
355
|
|
|
* @return string |
|
356
|
|
|
*/ |
|
357
|
|
|
public function stopRunningTimer() |
|
358
|
|
|
{ |
|
359
|
|
|
$message = ''; |
|
360
|
|
|
$atLeastOneServiceStopped = false; |
|
361
|
|
|
|
|
362
|
|
View Code Duplication |
foreach ($this->activatedServices() as $service) { |
|
|
|
|
|
|
363
|
|
|
$timerId = $this->config->get('workflow', 'timer_' . $service . '_id'); |
|
364
|
|
|
|
|
365
|
|
|
if ($this->$service->stopTimer($timerId) === true) { |
|
366
|
|
|
$atLeastOneServiceStopped = true; |
|
367
|
|
|
} |
|
368
|
|
|
|
|
369
|
|
|
$message .= $this->$service->getLastMessage() . "\r\n"; |
|
370
|
|
|
} |
|
371
|
|
|
|
|
372
|
|
|
if ($atLeastOneServiceStopped === true) { |
|
373
|
|
|
$this->config->update('workflow', 'is_timer_running', false); |
|
374
|
|
|
} |
|
375
|
|
|
|
|
376
|
|
|
return $message; |
|
377
|
|
|
} |
|
378
|
|
|
|
|
379
|
|
|
/** |
|
380
|
|
|
* @return string |
|
381
|
|
|
*/ |
|
382
|
|
View Code Duplication |
public function syncOnlineDataToLocalCache() |
|
|
|
|
|
|
383
|
|
|
{ |
|
384
|
|
|
$tempServices = ['toggl']; |
|
385
|
|
|
$message = ''; |
|
386
|
|
|
|
|
387
|
|
|
foreach ($tempServices as $service) { |
|
388
|
|
|
if ($this->isServiceActive($service) === true) { |
|
389
|
|
|
$message .= $this->syncServiceOnlineDataToLocalCache($service); |
|
390
|
|
|
} |
|
391
|
|
|
} |
|
392
|
|
|
|
|
393
|
|
|
return $message; |
|
394
|
|
|
} |
|
395
|
|
|
|
|
396
|
|
|
/** |
|
397
|
|
|
* @return string |
|
398
|
|
|
*/ |
|
399
|
|
|
public function undoTimer() |
|
400
|
|
|
{ |
|
401
|
|
|
$message = ''; |
|
402
|
|
|
|
|
403
|
|
|
if ($this->hasTimerRunning() === true) { |
|
404
|
|
|
$this->stopRunningTimer(); |
|
405
|
|
|
} |
|
406
|
|
|
|
|
407
|
|
|
$atLeastOneTimerDeleted = false; |
|
408
|
|
|
|
|
409
|
|
View Code Duplication |
foreach ($this->servicesToUndo() as $service) { |
|
|
|
|
|
|
410
|
|
|
if ($this->deleteServiceTimer($service, $this->config->get('workflow', 'timer_' . $service . '_id')) === true) { |
|
411
|
|
|
$atLeastOneTimerDeleted = true; |
|
412
|
|
|
} |
|
413
|
|
|
|
|
414
|
|
|
$message .= $this->$service->getLastMessage() . "\r\n"; |
|
415
|
|
|
} |
|
416
|
|
|
|
|
417
|
|
|
if ($atLeastOneTimerDeleted === true) { |
|
418
|
|
|
$this->config->update('workflow', 'is_timer_running', false); |
|
419
|
|
|
} |
|
420
|
|
|
|
|
421
|
|
|
return $message; |
|
422
|
|
|
} |
|
423
|
|
|
|
|
424
|
|
|
/** |
|
425
|
|
|
* @return mixed |
|
426
|
|
|
*/ |
|
427
|
|
|
private function getRecentServiceTimers($service) |
|
428
|
|
|
{ |
|
429
|
|
|
return $this->$service->getRecentTimers(); |
|
430
|
|
|
} |
|
431
|
|
|
|
|
432
|
|
|
/** |
|
433
|
|
|
* @return mixed |
|
434
|
|
|
*/ |
|
435
|
|
|
private function getServiceTags($service) |
|
436
|
|
|
{ |
|
437
|
|
|
$tags = []; |
|
438
|
|
|
|
|
439
|
|
|
$cacheFile = getenv('alfred_workflow_data') . '/' . $service . '_cache.json'; |
|
440
|
|
|
|
|
441
|
|
|
if (file_exists($cacheFile)) { |
|
442
|
|
|
$tags = json_decode(file_get_contents($cacheFile), true)['data']['tags']; |
|
443
|
|
|
} |
|
444
|
|
|
|
|
445
|
|
|
return $tags; |
|
446
|
|
|
} |
|
447
|
|
|
|
|
448
|
|
|
/** |
|
449
|
|
|
* @param $data |
|
450
|
|
|
*/ |
|
451
|
|
|
private function saveServiceDataCache($service, $data) |
|
452
|
|
|
{ |
|
453
|
|
|
$cacheFile = getenv('alfred_workflow_data') . '/' . $service . '_cache.json'; |
|
454
|
|
|
file_put_contents($cacheFile, json_encode($data)); |
|
455
|
|
|
} |
|
456
|
|
|
|
|
457
|
|
|
/** |
|
458
|
|
|
* @return mixed |
|
459
|
|
|
*/ |
|
460
|
|
|
private function syncServiceOnlineDataToLocalCache($service) |
|
461
|
|
|
{ |
|
462
|
|
|
$data = $this->$service->getOnlineData(); |
|
463
|
|
|
$this->message .= $this->$service->getLastMessage(); |
|
464
|
|
|
|
|
465
|
|
|
if (empty($data) === false) { |
|
466
|
|
|
$this->saveServiceDataCache($service, $data); |
|
467
|
|
|
} |
|
468
|
|
|
|
|
469
|
|
|
return $this->message; |
|
470
|
|
|
} |
|
471
|
|
|
} |
|
472
|
|
|
|
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.