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
|
|
|
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
|
|
|
$cacheFile = getenv('alfred_workflow_data') . '/' . $service . '_cache.json'; |
157
|
|
|
|
158
|
|
|
if (file_exists($cacheFile)) { |
159
|
|
|
$projects = json_decode(file_get_contents($cacheFile), true); |
160
|
|
|
|
161
|
|
|
/* |
162
|
|
|
* To only show projects that are currently active |
163
|
|
|
* The Toggl API is slightly weird on that |
164
|
|
|
*/ |
165
|
|
|
foreach ($projects['data']['projects'] as $key => $project) { |
166
|
|
|
if (isset($project['server_deleted_at']) === true) { |
167
|
|
|
unset($projects['data']['projects'][$key]); |
168
|
|
|
} |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
$projects = $projects['data']['projects']; |
172
|
|
|
} |
173
|
|
|
} |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
return $projects; |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
/** |
180
|
|
|
* @return mixed |
181
|
|
|
*/ |
182
|
|
|
public function getRecentTimers() |
183
|
|
|
{ |
184
|
|
|
$timers = []; |
185
|
|
|
|
186
|
|
|
if ($this->isServiceActive('toggl') === true) { |
187
|
|
|
$timers = array_merge($timers, $this->getRecentTogglTimers()); |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
return $timers; |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
/** |
194
|
|
|
* @return mixed |
195
|
|
|
*/ |
196
|
|
|
public function getTags() |
197
|
|
|
{ |
198
|
|
|
$tags = []; |
199
|
|
|
|
200
|
|
|
if ($this->isServiceActive('toggl') === true) { |
201
|
|
|
$tags = array_merge($tags, $this->getTogglTags()); |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
return $tags; |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
/** |
208
|
|
|
* @return mixed |
209
|
|
|
*/ |
210
|
|
|
public function getTimerDescription() |
211
|
|
|
{ |
212
|
|
|
return $this->config->get('workflow', 'timer_description'); |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
/** |
216
|
|
|
* @return boolean |
217
|
|
|
*/ |
218
|
|
|
public function hasTimerRunning() |
219
|
|
|
{ |
220
|
|
|
return $this->config->get('workflow', 'is_timer_running') === false ? false : true; |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
/** |
224
|
|
|
* @param string $feature |
225
|
|
|
* @return mixed |
226
|
|
|
*/ |
227
|
|
|
public function implementedServicesForFeature($feature = null) |
228
|
|
|
{ |
229
|
|
|
$services = []; |
230
|
|
|
|
231
|
|
|
if (isset($this->currentImplementation[$feature]) === true) { |
232
|
|
|
$services = $this->currentImplementation[$feature]; |
233
|
|
|
} |
234
|
|
|
|
235
|
|
|
return $services; |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
/** |
239
|
|
|
* @return boolean |
240
|
|
|
*/ |
241
|
|
|
public function isConfigured() |
242
|
|
|
{ |
243
|
|
|
return $this->config === null ? false : true; |
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
/** |
247
|
|
|
* @param $service |
248
|
|
|
* @return mixed |
249
|
|
|
*/ |
250
|
|
|
public function isServiceActive($service) |
251
|
|
|
{ |
252
|
|
|
return $this->config->get($service, 'is_active'); |
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
/** |
256
|
|
|
* @return mixed |
257
|
|
|
*/ |
258
|
|
|
public function servicesToUndo() |
259
|
|
|
{ |
260
|
|
|
$services = []; |
261
|
|
|
|
262
|
|
|
foreach ($this->activatedServices() as $service) { |
263
|
|
|
if ($this->config->get('workflow', 'timer_' . $service . '_id') !== null) { |
264
|
|
|
array_push($services, $service); |
265
|
|
|
} |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
return $services; |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
/** |
272
|
|
|
* @param $description |
273
|
|
|
* @param $projectsDefault |
274
|
|
|
* @param null $tagsDefault |
275
|
|
|
* @param boolean $startDefault |
276
|
|
|
* @return string |
277
|
|
|
*/ |
278
|
|
|
public function startTimer($description = '', $projectsDefault = null, $tagsDefault = null, $startDefault = false) |
279
|
|
|
{ |
280
|
|
|
$message = ''; |
281
|
|
|
$startType = $startDefault === true ? 'start_default' : 'start'; |
282
|
|
|
$atLeastOneServiceStarted = false; |
283
|
|
|
$implementedServices = $this->implementedServicesForFeature($startType); |
284
|
|
|
|
285
|
|
|
/* |
286
|
|
|
* When starting a new timer, all the services timer IDs have to be put to null |
287
|
|
|
* so that when the user uses the UNDO feature, it doesn't delete old previous |
288
|
|
|
* other services timers. The timer IDs are used for the UNDO feature and |
289
|
|
|
* should then contain the IDs of the last starts through the workflow, not |
290
|
|
|
* through each individual sefrvice |
291
|
|
|
*/ |
292
|
|
|
if (empty($implementedServices) === false) { |
293
|
|
|
foreach ($this->activatedServices() as $service) { |
294
|
|
|
$this->config->update('workflow', 'timer_' . $service . '_id', null); |
295
|
|
|
} |
296
|
|
|
|
297
|
|
|
foreach ($implementedServices as $service) { |
298
|
|
|
$defaultProjectId = isset($projectsDefault[$service]) ? $projectsDefault[$service] : null; |
299
|
|
|
$defaultTags = isset($tagsDefault[$service]) ? $tagsDefault[$service] : null; |
300
|
|
|
|
301
|
|
|
$timerId = $this->$service->startTimer($description, $defaultProjectId, $defaultTags); |
302
|
|
|
$this->config->update('workflow', 'timer_' . $service . '_id', $timerId); |
303
|
|
|
|
304
|
|
|
if ($timerId !== null) { |
305
|
|
|
$atLeastOneServiceStarted = true; |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
$message .= $this->$service->getLastMessage() . "\r\n"; |
309
|
|
|
} |
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
if ($atLeastOneServiceStarted === true) { |
313
|
|
|
$this->config->update('workflow', 'timer_description', $description); |
314
|
|
|
$this->config->update('workflow', 'is_timer_running', true); |
315
|
|
|
} |
316
|
|
|
|
317
|
|
|
return $message; |
318
|
|
|
} |
319
|
|
|
|
320
|
|
|
/** |
321
|
|
|
* @param $description |
322
|
|
|
* @return string |
323
|
|
|
*/ |
324
|
|
|
public function startTimerWithDefaultOptions($description) |
325
|
|
|
{ |
326
|
|
|
$projectsDefault = [ |
327
|
|
|
'toggl' => $this->config->get('toggl', 'default_project_id'), |
328
|
|
|
'harvest' => $this->config->get('harvest', 'default_project_id'), |
329
|
|
|
]; |
330
|
|
|
|
331
|
|
|
$tagsDefault = [ |
332
|
|
|
'toggl' => $this->config->get('toggl', 'default_tags'), |
333
|
|
|
'harvest' => $this->config->get('harvest', 'default_task_id'), |
334
|
|
|
]; |
335
|
|
|
|
336
|
|
|
return $this->startTimer($description, $projectsDefault, $tagsDefault, true); |
337
|
|
|
} |
338
|
|
|
|
339
|
|
|
/** |
340
|
|
|
* @return string |
341
|
|
|
*/ |
342
|
|
|
public function stopRunningTimer() |
343
|
|
|
{ |
344
|
|
|
$message = ''; |
345
|
|
|
$atLeastOneServiceStopped = false; |
346
|
|
|
|
347
|
|
View Code Duplication |
foreach ($this->activatedServices() as $service) { |
|
|
|
|
348
|
|
|
$timerId = $this->config->get('workflow', 'timer_' . $service . '_id'); |
349
|
|
|
|
350
|
|
|
if ($this->$service->stopTimer($timerId) === true) { |
351
|
|
|
$atLeastOneServiceStopped = true; |
352
|
|
|
} |
353
|
|
|
|
354
|
|
|
$message .= $this->$service->getLastMessage() . "\r\n"; |
355
|
|
|
} |
356
|
|
|
|
357
|
|
|
if ($atLeastOneServiceStopped === true) { |
358
|
|
|
$this->config->update('workflow', 'is_timer_running', false); |
359
|
|
|
} |
360
|
|
|
|
361
|
|
|
return $message; |
362
|
|
|
} |
363
|
|
|
|
364
|
|
|
/** |
365
|
|
|
* @return string |
366
|
|
|
*/ |
367
|
|
|
public function syncOnlineDataToLocalCache() |
368
|
|
|
{ |
369
|
|
|
$message = ''; |
370
|
|
|
|
371
|
|
|
if ($this->isServiceActive('toggl') === true) { |
372
|
|
|
$message .= $this->syncServiceOnlineDataToLocalCache('toggl'); |
373
|
|
|
} |
374
|
|
|
|
375
|
|
|
return $message; |
376
|
|
|
} |
377
|
|
|
|
378
|
|
|
/** |
379
|
|
|
* @return string |
380
|
|
|
*/ |
381
|
|
|
public function undoTimer() |
382
|
|
|
{ |
383
|
|
|
$message = ''; |
384
|
|
|
|
385
|
|
|
if ($this->hasTimerRunning() === true) { |
386
|
|
|
$this->stopRunningTimer(); |
387
|
|
|
} |
388
|
|
|
|
389
|
|
|
$atLeastOneTimerDeleted = false; |
390
|
|
|
|
391
|
|
View Code Duplication |
foreach ($this->servicesToUndo() as $service) { |
|
|
|
|
392
|
|
|
if ($this->deleteServiceTimer($service, $this->config->get('workflow', 'timer_' . $service . '_id')) === true) { |
393
|
|
|
$atLeastOneTimerDeleted = true; |
394
|
|
|
} |
395
|
|
|
|
396
|
|
|
$message .= $this->$service->getLastMessage() . "\r\n"; |
397
|
|
|
} |
398
|
|
|
|
399
|
|
|
if ($atLeastOneTimerDeleted === true) { |
400
|
|
|
$this->config->update('workflow', 'is_timer_running', false); |
401
|
|
|
} |
402
|
|
|
|
403
|
|
|
return $message; |
404
|
|
|
} |
405
|
|
|
|
406
|
|
|
/** |
407
|
|
|
* @return mixed |
408
|
|
|
*/ |
409
|
|
|
private function getRecentTogglTimers() |
410
|
|
|
{ |
411
|
|
|
return $this->toggl->getRecentTimers(); |
412
|
|
|
} |
413
|
|
|
|
414
|
|
|
/** |
415
|
|
|
* @return mixed |
416
|
|
|
*/ |
417
|
|
|
private function getTogglTags() |
418
|
|
|
{ |
419
|
|
|
$cacheFile = getenv('alfred_workflow_data') . '/toggl_cache.json'; |
420
|
|
|
$cacheData = []; |
421
|
|
|
|
422
|
|
|
if (file_exists($cacheFile)) { |
423
|
|
|
$cacheData = json_decode(file_get_contents($cacheFile), true); |
424
|
|
|
} |
425
|
|
|
|
426
|
|
|
return $cacheData['data']['tags']; |
427
|
|
|
} |
428
|
|
|
|
429
|
|
|
/** |
430
|
|
|
* @param $data |
431
|
|
|
*/ |
432
|
|
|
private function saveServiceDataCache($service, $data) |
433
|
|
|
{ |
434
|
|
|
$cacheFile = getenv('alfred_workflow_data') . '/' . $service . '_cache.json'; |
435
|
|
|
file_put_contents($cacheFile, json_encode($data)); |
436
|
|
|
} |
437
|
|
|
|
438
|
|
|
/** |
439
|
|
|
* @return mixed |
440
|
|
|
*/ |
441
|
|
|
private function syncServiceOnlineDataToLocalCache($service) |
|
|
|
|
442
|
|
|
{ |
443
|
|
|
$tempServices = ['toggl']; |
444
|
|
|
|
445
|
|
|
foreach ($tempServices as $service) { |
446
|
|
|
$data = $this->$service->getOnlineData(); |
447
|
|
|
$this->message .= $this->$service->getLastMessage(); |
448
|
|
|
|
449
|
|
|
if (empty($data) === false) { |
450
|
|
|
$this->saveServiceDataCache($service, $data); |
451
|
|
|
} |
452
|
|
|
} |
453
|
|
|
|
454
|
|
|
return $this->message; |
455
|
|
|
} |
456
|
|
|
} |
457
|
|
|
|
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.