-
Notifications
You must be signed in to change notification settings - Fork 0
/
log-time.php
189 lines (162 loc) · 5.96 KB
/
log-time.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
<?php namespace Vanderbilt\DataCoreCustomizationsModule;
if($_SERVER['REQUEST_METHOD'] === 'GET'){
?>
<script>
/**
* Securely retrieve the entries from Assembla via postMessage(),
* and post them back to this page so they can be processed via PHP.
*/
const assemblaUrl = 'https://app.assembla.com'
window.opener.postMessage('loaded', assemblaUrl)
window.addEventListener("message", (event) => {
if(event.origin !== assemblaUrl){
return
}
const form = document.createElement('form')
form.style.display = 'none'
form.method = 'POST'
const csrfToken = document.createElement('input')
csrfToken.name = 'redcap_csrf_token'
csrfToken.value = <?=json_encode($module->getCSRFToken())?>;
form.append(csrfToken)
const data = document.createElement('textarea')
data.name = 'data'
data.innerHTML = event.data
form.append(data)
document.body.append(form)
form.submit()
}, false);
</script>
<?php
return;
}
?>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.3/css/bootstrap.min.css" integrity="sha512-SbiR/eusphKoMVVXysTKG/7VseWii+Y3FdHrt0EpKgpToZeemhqHeZeLWLhJutz/2ut2Vw1uQEj2MbRF+TVBUA==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<style>
body{
padding: 10px;
max-width: 1000px;
margin: auto;
}
#log-new-entries{
margin: auto;
margin-top: 30px;
display: block;
}
</style>
<h3>Assembla Time Sync</h3>
<?php
$payload = json_decode($_POST['data'], true);
$payloadAge = time() - $payload['time'];
if($payloadAge > 15){
die('This request has expired. Please retry from Assembla.');
}
$pid = $module->getSystemSetting('hours-survey-pid');
if(empty($pid)){
die('Please set the "CORE - Hourly Billing- NEW" system setting.');
}
$programmerId = $module->getProgrammerId($pid);
$assemblaEntries = $payload['entries'];
$totalAssembalHours = 0;
foreach($assemblaEntries as &$entry){
$entry['programmer_name'] = (string) $programmerId;
$entry['billing_year'] = $payload['billing_year'];
$entry['billing_month'] = $payload['billing_month'];
$hoursSurveyProject = $entry['hours_survey_project'];
unset($entry['hours_survey_project']);
$entry['project_name_2'] = $module->parseHoursSurveyProjectId($hoursSurveyProject);
foreach([
'project_hours',
'project_notes',
'project_hours_2',
'project_notes_2',
] as $field){
$value = $entry[$field] ?? '';
$entry[$field] = $value;
if(str_contains($field, 'hours')){
$totalAssembalHours += (int) $value;
}
}
}
$existing = \REDCap::getData([
'project_id' => $pid,
'return_format' => 'json-array',
'fields' => array_merge($module->getUniqueCheckFields(), [
'project_name_2',
'project_hours',
'project_notes',
'project_hours_2',
'project_notes_2',
]),
'filterLogic' => "
[programmer_name] = '$programmerId'
and [billing_year] = '{$payload['billing_year']}'
and [billing_month] = '{$payload['billing_month']}'
"
]);
?>
<script>
(() => {
// For debugging sync issues
console.log('Assembla Entries', <?=json_encode($assemblaEntries)?>)
console.log('Existing REDCap Entries', <?=json_encode($existing)?>)
})()
</script>
<?php
[$unmatched, $new, $incomplete] = $module->compareTimeLogs($assemblaEntries, $existing);
if(!empty($unmatched)){
// Only show unmatched logs, since new & incomplete logs can't be correctly determined when unmatched logs exist.
$module->displayTimeLogs("
The following time logs already existed in REDCap, but do not have matching entries in Assembla.<br>
If you've logged any time entries manually this month, you will not be able to use this tool.<br>
If you have not logged any time entries manually, please send a copy of the following to Mark:<br>
", $unmatched);
}
else if(!empty($incomplete)){
foreach($incomplete as $error=>$tickets){
echo "<h6>$error</h6>";
echo $module->getTicketLinks($tickets);
}
}
else if(!empty($new)){
$module->displayTimeLogs("
The following new entries will be logged.
Please review them for accuracy before continuing:
", $new);
?>
<button id='log-new-entries'>Log New Entries</button>
<script>
document.querySelector("#log-new-entries").addEventListener('click', (e) => {
const button = e.target
button.disabled = true
const pageLoadTime = <?=json_encode(time())?>;
const secondsSinceLoad = Date.now()/1000 - pageLoadTime
if(secondsSinceLoad > 60*60){
alert('This page is an hour old. Please close it and reopen it from Assembla to make sure the time logs are up to date.')
return
}
const data = new URLSearchParams()
data.append('redcap_csrf_token', <?=json_encode($module->getCSRFToken())?>)
data.append('data', JSON.stringify(<?=json_encode($new)?>))
fetch(<?=json_encode($module->getUrl('save-new-time-logs.php'))?>, {
method: 'POST',
body: data,
})
.then((response) => response.text())
.then((text) => {
if(text === 'success'){
alert('New entries have been successfully logged!')
close()
}
else{
alert("An error occurred. See the browser console for details.")
console.error(text)
}
});
})
</script>
<?php
}
else{
echo "Your Assembla time entries totaling $totalAssembalHours hours have already been synced with the REDCap Hours Survey for this month.";
}