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