Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
22.95% covered (danger)
22.95%
42 / 183
16.67% covered (danger)
16.67%
3 / 18
CRAP
0.00% covered (danger)
0.00%
0 / 1
WorkspaceController
22.95% covered (danger)
22.95%
42 / 183
16.67% covered (danger)
16.67%
3 / 18
1193.52
0.00% covered (danger)
0.00%
0 / 1
 get
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 put
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 patch
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 patchUsers
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 getUsers
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getResults
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 deleteResponses
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 getFile
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
12
 postFile
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
7
 getFiles
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 getFilesWithDependencies
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
 deleteFiles
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
3
 getReport
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
6
 getSysCheckReportsOverview
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 deleteSysCheckReports
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 getSysCheck
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
2
 getSysCheckUnitAndPLayer
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
56
 putSysCheckReport
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2
3/** @noinspection PhpUnhandledExceptionInspection */
4declare(strict_types=1);
5
6// TODO unit tests !
7
8use Slim\Exception\HttpBadRequestException;
9use Slim\Exception\HttpNotFoundException;
10use Slim\Http\Response;
11use Slim\Http\ServerRequest as Request;
12use Slim\Psr7\Stream;
13
14class WorkspaceController extends Controller {
15  /**
16   * @deprecated
17   */
18  public static function get(Request $request, Response $response): Response {
19    $workspaceId = (int) $request->getAttribute('ws_id');
20
21    /* @var $authToken AuthToken */
22    $authToken = $request->getAttribute('AuthToken');
23
24    return $response->withJson([
25      "id" => $workspaceId,
26      "name" => self::workspaceDAO($workspaceId)->getWorkspaceName(),
27      "role" => self::adminDAO()->getWorkspaceRole($authToken->getToken(), $workspaceId)
28    ]);
29  }
30
31  public static function put(Request $request, Response $response): Response {
32    $requestBody = JSON::decode($request->getBody()->getContents());
33    if (!isset($requestBody->name)) {
34      throw new HttpBadRequestException($request, "New workspace name missing");
35    }
36
37    $workspaceCreated = self::superAdminDAO()->createWorkspace($requestBody->name);
38
39    $response->getBody()->write(htmlspecialchars($workspaceCreated['id']));
40    return $response->withStatus(201);
41  }
42
43  public static function patch(Request $request, Response $response): Response {
44    $requestBody = JSON::decode($request->getBody()->getContents());
45    $workspaceId = (int) $request->getAttribute('ws_id');
46
47    if (!isset($requestBody->name) or (!$requestBody->name)) {
48      throw new HttpBadRequestException($request, "New name (name) is missing");
49    }
50
51    self::superAdminDAO()->setWorkspaceName($workspaceId, $requestBody->name);
52
53    return $response;
54  }
55
56  public static function patchUsers(Request $request, Response $response): Response {
57    $requestBody = JSON::decode($request->getBody()->getContents());
58    $workspaceId = (int) $request->getAttribute('ws_id');
59
60    if (!isset($requestBody->u) or (!count($requestBody->u))) {
61      throw new HttpBadRequestException($request, "User-list (u) is missing");
62    }
63
64    self::superAdminDAO()->setUserRightsForWorkspace($workspaceId, $requestBody->u);
65
66    return $response->withHeader('Content-type', 'text/plain;charset=UTF-8');
67  }
68
69  public static function getUsers(Request $request, Response $response): Response {
70    $workspaceId = (int) $request->getAttribute('ws_id');
71
72    return $response->withJson(self::superAdminDAO()->getUsersByWorkspace($workspaceId));
73  }
74
75  public static function getResults(Request $request, Response $response): Response {
76    $workspaceId = (int) $request->getAttribute('ws_id');
77    $results = self::adminDAO()->getResultStats($workspaceId);
78
79    return $response->withJson($results);
80  }
81
82  public static function deleteResponses(Request $request, Response $response): Response {
83    $workspaceId = (int) $request->getAttribute('ws_id');
84    $groups = RequestBodyParser::getRequiredElement($request, 'groups');
85
86    foreach ($groups as $group) {
87      self::adminDAO()->deleteResultData($workspaceId, $group);
88    }
89
90    BroadcastService::send('system/clean');
91
92    return $response;
93  }
94
95  public static function getFile(Request $request, Response $response): Response {
96    $workspaceId = $request->getAttribute('ws_id', 0);
97    $fileType = $request->getAttribute('type', '[type missing]');
98    $filename = $request->getAttribute('filename', '[filename missing]');
99
100    $fullFilename = DATA_DIR . "/ws_$workspaceId/$fileType/$filename";
101    if (!file_exists($fullFilename)) {
102      throw new HttpNotFoundException($request, "File not found:" . $fullFilename);
103    }
104
105    $response->withHeader('Content-Description', 'File Transfer');
106    $response->withHeader('Content-Type', ($fileType == 'Resource') ? 'application/octet-stream' : 'text/xml');
107    $response->withHeader('Content-Disposition', 'attachment; filename="' . $filename . '"');
108    $response->withHeader('Expires', '0');
109    $response->withHeader('Cache-Control', 'must-revalidate');
110    $response->withHeader('Pragma', 'public');
111    $response->withHeader('Content-Length', filesize($fullFilename));
112
113    $fileHandle = fopen($fullFilename, 'rb');
114
115    $fileStream = new Stream($fileHandle);
116
117    return $response->withBody($fileStream);
118  }
119
120  public static function postFile(Request $request, Response $response): Response {
121    set_time_limit(600); // because password hashing may take a lot of time if many testtakers are provided
122    $workspaceId = (int) $request->getAttribute('ws_id');
123    $workspace = new Workspace($workspaceId);
124
125    $filesToImport = UploadedFilesHandler::handleUploadedFiles($request, 'fileforvo', $workspace->getWorkspacePath());
126
127    $importedFilesReports = $workspace->importUncategorizedFiles($filesToImport);
128    $workspace->setWorkspaceHash();
129
130    $loginsAffected = false;
131    $containsErrors = false;
132    foreach ($importedFilesReports as $localPath => $report) {
133        $containsErrors = ($containsErrors or !empty($importedFilesReports[$localPath]['error']));
134        $loginsAffected = ($loginsAffected or ($containsErrors and ($importedFilesReports[$localPath]['type'] == 'Testtakers')));
135        $importedFilesReports[$localPath] = array_splice($report, 1); // revert to current structure for output so not to needlessly change the API
136    }
137
138    if ($loginsAffected) {
139      BroadcastService::send('system/clean');
140    }
141
142    return $response->withJson($importedFilesReports)->withStatus($containsErrors ? 207 : 201);
143  }
144
145  public static function getFiles(Request $request, Response $response): Response {
146    $workspaceId = (int) $request->getAttribute('ws_id');
147    $workspace = new Workspace($workspaceId);
148
149    $files = $workspace->workspaceDAO->getAllFiles();
150
151    // TODo change the FE and endpoint to accept it with keys
152    $fileDigestList = [];
153    foreach ($files as $fileType => $fileList) {
154      $fileDigestList[$fileType] = array_values(File::removeDeprecatedValues($fileList));
155    }
156
157    return $response->withJson($fileDigestList);
158  }
159
160  public static function getFilesWithDependencies(Request $request, Response $response) {
161    $workspaceId = (int) $request->getAttribute('ws_id');
162    $names = RequestBodyParser::getRequiredElement($request, 'body');
163
164    $workspace = new Workspace($workspaceId);
165    $files = $workspace->workspaceDAO->getFilesByNames($names);
166
167    $fileDigestList = [];
168    foreach ($files as $fileType => $fileList) {
169      $fileDigestList[$fileType] = array_values(File::removeDeprecatedValues($fileList));
170    }
171
172    return $response->withJson($fileDigestList);
173  }
174
175  /** TODO since only allowed files are in the five main folders, a better syntax for the body would suit
176   * eg [{"type": "Booklet", "name": "SAMPLE_BOOKLET.XML"}]
177   */
178  public static function deleteFiles(Request $request, Response $response): Response {
179    $workspaceId = (int) $request->getAttribute('ws_id');
180    $filesToDelete = RequestBodyParser::getRequiredElement($request, 'f');
181
182    $workspace = new Workspace($workspaceId);
183    $deletionReport = $workspace->deleteFiles($filesToDelete);
184
185    foreach ($deletionReport->deleted as $deletedFile) {
186      list($type) = explode('/', $deletedFile);
187      if ($type == 'Testtakers') {
188        BroadcastService::send('system/clean');
189        self::workspaceDAO($workspaceId)->setSysCheckMode('mixed');
190        break;
191      }
192    }
193
194    $workspace->setWorkspaceHash();
195
196    return $response->withJson($deletionReport)->withStatus(207);
197  }
198
199  public static function getReport(Request $request, Response $response): ?Response {
200    $workspaceId = (int) $request->getAttribute('ws_id');
201
202    $dataIds = $request->getParam('dataIds', '') === ''
203      ? []
204      : explode(',', $request->getParam('dataIds', ''));
205
206    try {
207      $reportType = ReportType::from($request->getAttribute('type'));
208    } catch (ValueError $exception) {
209      throw new HttpNotFoundException($request, "Report type '{$request->getAttribute('type')}' not found.");
210    }
211
212    $reportFormat = $request->getHeaderLine('Accept') == 'text/csv' ? ReportFormat::CSV : ReportFormat::JSON;
213
214    $report = ReportOutputFactory::createReportOutput($workspaceId, $dataIds, $reportType, $reportFormat);
215
216    if ($report->generate($request->getParam('useNewVersion') === 'true')) {
217      $response->getBody()->write($report->asString());
218    }
219    $response = $reportFormat === ReportFormat::CSV
220      ? $response->withHeader('Content-type', 'text/csv;charset=UTF-8')
221      : $response->withHeader('Content-Type', 'application/json');
222
223    return $response;
224  }
225
226  public static function getSysCheckReportsOverview(Request $request, Response $response): Response {
227    $workspaceId = (int) $request->getAttribute('ws_id');
228
229    $sysChecksFolder = new SysChecksFolder($workspaceId);
230    $reports = $sysChecksFolder->getSysCheckReportList();
231
232    return $response->withJson($reports);
233  }
234
235  public static function deleteSysCheckReports(Request $request, Response $response): Response {
236    $workspaceId = (int) $request->getAttribute('ws_id');
237    $checkIds = RequestBodyParser::getElementWithDefault($request, 'checkIds', []);
238
239    $sysChecksFolder = new SysChecksFolder($workspaceId);
240    $fileDeletionReport = $sysChecksFolder->deleteSysCheckReports($checkIds);
241
242    return $response->withJson($fileDeletionReport)->withStatus(207);
243  }
244
245  public static function getSysCheck(Request $request, Response $response): Response {
246    $workspaceId = (int) $request->getAttribute('ws_id');
247    $sysCheckName = $request->getAttribute('sys-check_name');
248
249    $workspaceController = new Workspace($workspaceId);
250    /* @var XMLFileSysCheck $xmlFile */
251    $xmlFile = $workspaceController->getFileById('SysCheck', $sysCheckName);
252
253    return $response->withJson([
254      'name' => $xmlFile->getId(),
255      'label' => $xmlFile->getLabel(),
256      'canSave' => $xmlFile->hasSaveKey(),
257      'hasUnit' => $xmlFile->hasUnit(),
258      'questions' => $xmlFile->getQuestions(),
259      'customTexts' => (object) $xmlFile->getCustomTexts(),
260      'skipNetwork' => $xmlFile->getSkipNetwork(),
261      'downloadSpeed' => $xmlFile->getSpeedtestDownloadParams(),
262      'uploadSpeed' => $xmlFile->getSpeedtestUploadParams(),
263      'workspaceId' => $workspaceId
264    ]);
265  }
266
267  public static function getSysCheckUnitAndPLayer(Request $request, Response $response): Response {
268    $workspaceId = (int) $request->getAttribute('ws_id');
269    $sysCheckName = $request->getAttribute('sys-check_name');
270
271    $workspace = new Workspace($workspaceId);
272
273    /* @var XMLFileSysCheck $sysCheck */
274    $sysCheck = $workspace->getFileById('SysCheck', $sysCheckName);
275
276    $res = [
277      'player_id' => '',
278      'def' => '',
279      'player' => ''
280    ];
281
282    if (!$sysCheck->hasUnit()) {
283      return $response->withJson($res);
284    }
285
286    $unit = $workspace->getFileById('Unit', $sysCheck->getUnitId());
287    /* @var XMLFileUnit $unit */
288    $unitRelations = $workspace->getFileRelations($unit);
289
290    foreach ($unitRelations as $unitRelation) {
291      /* @var FileRelation $unitRelation */
292
293      switch ($unitRelation->getRelationshipType()) {
294        case FileRelationshipType::isDefinedBy:
295
296          $unitDefinitionFile = $workspace->getFileByName('Resource', $unitRelation->getTargetName());
297          $res['def'] = $unitDefinitionFile->getContent();
298          break;
299
300        case FileRelationshipType::usesPlayer:
301
302          $playerFile = $workspace->getFileByName('Resource', $unitRelation->getTargetName());
303          $res['player'] = $playerFile->getContent();
304          $res['player_id'] = $playerFile->getVeronaModuleId();
305          break;
306      }
307    }
308
309    if (!$res['def']) {
310      $unitEmbeddedDefinition = $unit->getDefinition();
311      if ($unitEmbeddedDefinition) {
312        $res['def'] = $unitEmbeddedDefinition;
313      }
314    }
315
316    return $response->withJson($res);
317  }
318
319  public static function putSysCheckReport(Request $request, Response $response): Response {
320    $workspaceId = (int) $request->getAttribute('ws_id');
321    $sysCheckName = $request->getAttribute('sys-check_name');
322
323    $authToken = $request->getAttribute('AuthToken');
324    $bodyContents = JSON::decode($request->getBody()->getContents());
325
326    $sysChecksFolder = new SysChecksFolder($workspaceId);
327    /* @var XMLFileSysCheck $sysCheck */
328    $sysCheck = $sysChecksFolder->getFileById('SysCheck', $sysCheckName);
329
330    if ($authToken) {
331      $session = self::sessionDAO()->getPersonSessionByToken($authToken->getToken());
332      $bodyContents->title = $session->getLoginSession()->getLogin()->getName();
333      $bodyContents->keyPhrase = '';
334      $report = new SysCheckReport($bodyContents);
335    } else {
336      $report = new SysCheckReport($bodyContents);
337      if (strlen((string) $report->keyPhrase) <= 0) {
338        throw new HttpBadRequestException($request, "No key `$report->keyPhrase`");
339      }
340
341      if (strtoupper((string) $report->keyPhrase) !== strtoupper($sysCheck->getSaveKey())) {
342        throw new HttpError("Wrong key `$report->keyPhrase`", 403);
343      }
344    }
345
346    $report->checkId = $sysCheck->getId();
347    $report->checkLabel = $sysCheck->getLabel();
348
349    $sysChecksFolder->saveSysCheckReport($report);
350
351    return $response->withStatus(201);
352  }
353}