1 | <?php |
||
2 | declare(strict_types=1); |
||
3 | |||
4 | /** |
||
5 | * ReportingCloud PHP SDK |
||
6 | * |
||
7 | * PHP SDK for ReportingCloud Web API. Authored and supported by Text Control GmbH. |
||
8 | * |
||
9 | * @link https://www.reporting.cloud to learn more about ReportingCloud |
||
10 | * @link https://tinyurl.com/vmbbh6kd for the canonical source repository |
||
11 | * @license https://tinyurl.com/3pc9am89 |
||
12 | * @copyright © 2023 Text Control GmbH |
||
13 | */ |
||
14 | |||
15 | namespace TextControl\ReportingCloud; |
||
16 | |||
17 | use Ctw\Http\HttpMethod; |
||
18 | use Ctw\Http\HttpStatus; |
||
19 | use GuzzleHttp\RequestOptions; |
||
20 | use JsonException; |
||
21 | use Psr\Http\Message\ResponseInterface; |
||
22 | use TextControl\ReportingCloud\Assert\Assert; |
||
23 | use TextControl\ReportingCloud\Filter\Filter; |
||
24 | use TextControl\ReportingCloud\PropertyMap\AbstractPropertyMap as PropertyMap; |
||
25 | use TextControl\ReportingCloud\PropertyMap\ModifiedDocument as ModifiedDocumentPropertyMap; |
||
26 | use TextControl\ReportingCloud\PropertyMap\TrackedChanges as TrackedChangesPropertyMap; |
||
27 | use TextControl\ReportingCloud\Stdlib\FileUtils; |
||
28 | |||
29 | /** |
||
30 | * Trait PostTrait |
||
31 | */ |
||
32 | trait PostTrait |
||
33 | { |
||
34 | /** |
||
35 | * Upload a base64 encoded template to template storage |
||
36 | * |
||
37 | * @param string $data Template encoded as base64 |
||
38 | * @param string $templateName Template name |
||
39 | */ |
||
40 | 24 | public function uploadTemplateFromBase64(string $data, string $templateName): bool |
|
41 | { |
||
42 | 24 | Assert::assertBase64Data($data); |
|
43 | 24 | Assert::assertTemplateName($templateName); |
|
44 | |||
45 | 24 | $query = [ |
|
46 | 24 | 'templateName' => $templateName, |
|
47 | 24 | ]; |
|
48 | |||
49 | 24 | $result = $this->post('/templates/upload', $query, $data, HttpStatus::STATUS_CREATED); |
|
50 | |||
51 | 24 | return null === $result; |
|
52 | } |
||
53 | |||
54 | /** |
||
55 | * Upload a template to template storage |
||
56 | * |
||
57 | * @param string $templateFilename Template name |
||
58 | */ |
||
59 | 30 | public function uploadTemplate(string $templateFilename): bool |
|
60 | { |
||
61 | 30 | Assert::assertTemplateExtension($templateFilename); |
|
62 | 24 | Assert::assertFilenameExists($templateFilename); |
|
63 | |||
64 | 22 | $templateName = basename($templateFilename); |
|
65 | |||
66 | 22 | $data = FileUtils::read($templateFilename, true); |
|
67 | |||
68 | 22 | return $this->uploadTemplateFromBase64($data, $templateName); |
|
69 | } |
||
70 | |||
71 | /** |
||
72 | * Convert a document on the local file system to a different format |
||
73 | * |
||
74 | * @param string $documentFilename Document filename |
||
75 | * @param string $returnFormat Return format |
||
76 | */ |
||
77 | 14 | public function convertDocument(string $documentFilename, string $returnFormat): string |
|
78 | { |
||
79 | 14 | Assert::assertDocumentExtension($documentFilename); |
|
80 | 8 | Assert::assertFilenameExists($documentFilename); |
|
81 | 6 | Assert::assertReturnFormat($returnFormat); |
|
82 | |||
83 | 4 | $query = [ |
|
84 | 4 | 'returnFormat' => $returnFormat, |
|
85 | 4 | ]; |
|
86 | |||
87 | 4 | $data = FileUtils::read($documentFilename, true); |
|
88 | |||
89 | 4 | $result = $this->post('/document/convert', $query, $data, HttpStatus::STATUS_OK); |
|
90 | |||
91 | 4 | if (is_string($result) && 0 < strlen($result)) { |
|
92 | 4 | $decoded = base64_decode($result, true); |
|
93 | 4 | if (is_string($decoded)) { |
|
0 ignored issues
–
show
introduced
by
![]() |
|||
94 | 4 | return $decoded; |
|
95 | } |
||
96 | } |
||
97 | |||
98 | return ''; |
||
99 | } |
||
100 | |||
101 | /** |
||
102 | * Merge data into a template and return an array of binary data. |
||
103 | * Each record in the array is the binary data of one document |
||
104 | * |
||
105 | * @param array $mergeData Array of merge data |
||
106 | * @param string $returnFormat Return format |
||
107 | * @param string $templateName Template name |
||
108 | * @param string $templateFilename Template filename on local file system |
||
109 | * @param bool $append Append flag |
||
110 | * @param array $mergeSettings Array of merge settings |
||
111 | */ |
||
112 | 24 | public function mergeDocument( |
|
113 | array $mergeData, |
||
114 | string $returnFormat, |
||
115 | string $templateName = '', |
||
116 | string $templateFilename = '', |
||
117 | bool $append = false, |
||
118 | array $mergeSettings = [] |
||
119 | ): array { |
||
120 | |||
121 | 24 | $query = []; |
|
122 | 24 | $json = []; |
|
123 | |||
124 | 24 | $json['mergeData'] = $mergeData; |
|
125 | |||
126 | 24 | Assert::assertReturnFormat($returnFormat); |
|
127 | 22 | $query['returnFormat'] = $returnFormat; |
|
128 | |||
129 | 22 | if (0 < strlen($templateName)) { |
|
130 | 4 | Assert::assertTemplateName($templateName); |
|
131 | 2 | $query['templateName'] = $templateName; |
|
132 | } |
||
133 | |||
134 | 20 | if (0 < strlen($templateFilename)) { |
|
135 | 18 | Assert::assertTemplateExtension($templateFilename); |
|
136 | 12 | Assert::assertFilenameExists($templateFilename); |
|
137 | 10 | $json['template'] = FileUtils::read($templateFilename, true); |
|
138 | } |
||
139 | |||
140 | 12 | if ($append) { |
|
141 | //Assert::assertBoolean($append); |
||
142 | $query['append'] = Filter::filterBooleanToString($append); |
||
143 | } |
||
144 | |||
145 | 12 | if ([] !== $mergeSettings) { |
|
146 | 10 | $json['mergeSettings'] = $this->buildMergeSettingsArray($mergeSettings); |
|
147 | } |
||
148 | |||
149 | 6 | $result = $this->post('/document/merge', $query, $json, HttpStatus::STATUS_OK); |
|
150 | |||
151 | 6 | if (is_array($result)) { |
|
152 | 6 | foreach ($result as $key => $value) { |
|
153 | 6 | $value = base64_decode($value, true); |
|
154 | 6 | assert(is_string($value)); |
|
155 | 6 | $result[$key] = $value; |
|
156 | } |
||
157 | |||
158 | 6 | return $result; |
|
159 | } |
||
160 | |||
161 | return []; |
||
162 | } |
||
163 | |||
164 | /** |
||
165 | * Combine documents by appending them, divided by a new section, paragraph or nothing |
||
166 | */ |
||
167 | 2 | public function appendDocument(array $documentsData, string $returnFormat, array $documentSettings = []): string |
|
168 | { |
||
169 | |||
170 | 2 | $query = []; |
|
171 | 2 | $json = []; |
|
172 | |||
173 | 2 | $json['documents'] = $this->buildDocumentsArray($documentsData); |
|
174 | |||
175 | 2 | Assert::assertReturnFormat($returnFormat); |
|
176 | 2 | $query['returnFormat'] = $returnFormat; |
|
177 | |||
178 | 2 | if ([] !== $documentSettings) { |
|
179 | 2 | $json['documentSettings'] = $this->buildDocumentSettingsArray($documentSettings); |
|
180 | } |
||
181 | |||
182 | 2 | $result = $this->post('/document/append', $query, $json, HttpStatus::STATUS_OK); |
|
183 | |||
184 | 2 | if (is_string($result) && 0 < strlen($result)) { |
|
185 | 2 | $decoded = base64_decode($result, true); |
|
186 | 2 | if (is_string($decoded)) { |
|
0 ignored issues
–
show
|
|||
187 | 2 | return $decoded; |
|
188 | } |
||
189 | } |
||
190 | |||
191 | return ''; |
||
192 | } |
||
193 | |||
194 | /** |
||
195 | * Perform find and replace in document and return binary data. |
||
196 | * |
||
197 | * @param array $findAndReplaceData Array of find and replace data |
||
198 | * @param string $returnFormat Return format |
||
199 | * @param string $templateName Template name |
||
200 | * @param string $templateFilename Template filename on local file system |
||
201 | * @param array $mergeSettings Array of merge settings |
||
202 | */ |
||
203 | 20 | public function findAndReplaceDocument( |
|
204 | array $findAndReplaceData, |
||
205 | string $returnFormat, |
||
206 | string $templateName = '', |
||
207 | string $templateFilename = '', |
||
208 | array $mergeSettings = [] |
||
209 | ): string { |
||
210 | 20 | $query = []; |
|
211 | 20 | $json = []; |
|
212 | |||
213 | 20 | $json['findAndReplaceData'] = $this->buildFindAndReplaceDataArray($findAndReplaceData); |
|
214 | |||
215 | 20 | Assert::assertReturnFormat($returnFormat); |
|
216 | 18 | $query['returnFormat'] = $returnFormat; |
|
217 | |||
218 | 18 | if (0 < strlen($templateName)) { |
|
219 | 4 | Assert::assertTemplateName($templateName); |
|
220 | 2 | $query['templateName'] = $templateName; |
|
221 | } |
||
222 | |||
223 | 16 | if (0 < strlen($templateFilename)) { |
|
224 | 14 | Assert::assertTemplateExtension($templateFilename); |
|
225 | 8 | Assert::assertFilenameExists($templateFilename); |
|
226 | 6 | $json['template'] = FileUtils::read($templateFilename, true); |
|
227 | } |
||
228 | |||
229 | 8 | if ([] !== $mergeSettings) { |
|
230 | 8 | $json['mergeSettings'] = $this->buildMergeSettingsArray($mergeSettings); |
|
231 | } |
||
232 | |||
233 | 4 | $result = $this->post('/document/findandreplace', $query, $json, HttpStatus::STATUS_OK); |
|
234 | |||
235 | 4 | if (is_string($result) && 0 < strlen($result)) { |
|
236 | 4 | $decoded = base64_decode($result, true); |
|
237 | 4 | if (is_string($decoded)) { |
|
0 ignored issues
–
show
|
|||
238 | 4 | return $decoded; |
|
239 | } |
||
240 | } |
||
241 | |||
242 | return ''; |
||
243 | } |
||
244 | |||
245 | /** |
||
246 | * Generate a thumbnail image per page of specified document filename. |
||
247 | * Return an array of binary data with each record containing one thumbnail. |
||
248 | * |
||
249 | * @param string $documentFilename Document filename |
||
250 | * @param int $zoomFactor Zoom factor |
||
251 | * @param int $fromPage From page |
||
252 | * @param int $toPage To page |
||
253 | * @param string $imageFormat Image format |
||
254 | */ |
||
255 | 2 | public function getDocumentThumbnails( |
|
256 | string $documentFilename, |
||
257 | int $zoomFactor, |
||
258 | int $fromPage, |
||
259 | int $toPage, |
||
260 | string $imageFormat |
||
261 | ): array { |
||
262 | |||
263 | 2 | Assert::assertDocumentThumbnailExtension($documentFilename); |
|
264 | 2 | Assert::assertFilenameExists($documentFilename); |
|
265 | 2 | Assert::assertZoomFactor($zoomFactor); |
|
266 | 2 | Assert::assertPage($fromPage); |
|
267 | 2 | Assert::assertPage($toPage); |
|
268 | 2 | Assert::assertImageFormat($imageFormat); |
|
269 | |||
270 | 2 | $query = [ |
|
271 | 2 | 'zoomFactor' => $zoomFactor, |
|
272 | 2 | 'fromPage' => $fromPage, |
|
273 | 2 | 'toPage' => $toPage, |
|
274 | 2 | 'imageFormat' => $imageFormat, |
|
275 | 2 | ]; |
|
276 | |||
277 | 2 | $data = FileUtils::read($documentFilename, true); |
|
278 | |||
279 | 2 | $result = $this->post('/document/thumbnails', $query, $data, HttpStatus::STATUS_OK); |
|
280 | |||
281 | 2 | if (is_array($result)) { |
|
282 | 2 | foreach ($result as $key => $value) { |
|
283 | 2 | $value = base64_decode($value, true); |
|
284 | 2 | assert(is_string($value)); |
|
285 | 2 | $result[$key] = $value; |
|
286 | } |
||
287 | |||
288 | 2 | return $result; |
|
289 | } |
||
290 | |||
291 | return []; |
||
292 | } |
||
293 | |||
294 | /** |
||
295 | * Return the tracked changes in a document. |
||
296 | * |
||
297 | * @param string $documentFilename Document filename |
||
298 | */ |
||
299 | 2 | public function getTrackedChanges(string $documentFilename): array |
|
300 | { |
||
301 | 2 | $ret = []; |
|
302 | |||
303 | 2 | $propertyMap = new TrackedChangesPropertyMap(); |
|
304 | |||
305 | 2 | Assert::assertDocumentExtension($documentFilename); |
|
306 | 2 | Assert::assertFilenameExists($documentFilename); |
|
307 | |||
308 | 2 | $data = FileUtils::read($documentFilename, true); |
|
309 | |||
310 | 2 | $result = $this->post('/processing/review/trackedchanges', [], $data, HttpStatus::STATUS_OK); |
|
311 | |||
312 | 2 | if (is_array($result)) { |
|
313 | 2 | $ret = $this->buildPropertyMapArray($result, $propertyMap); |
|
314 | 2 | array_walk($ret, static function (array &$record): void { |
|
315 | 2 | $key = 'change_time'; |
|
316 | 2 | if (isset($record[$key])) { |
|
317 | //@todo [20190902] return value of backend DateTime in Zulu timezone |
||
318 | //Assert::assertDateTime($record[$key]); |
||
319 | 2 | $record[$key] = Filter::filterDateTimeToTimestamp($record[$key]); |
|
320 | } |
||
321 | 2 | }); |
|
322 | } |
||
323 | |||
324 | 2 | return $ret; |
|
325 | } |
||
326 | |||
327 | /** |
||
328 | * Removes a specific tracked change and returns the resulting document. |
||
329 | * |
||
330 | * @param string $documentFilename Document filename |
||
331 | * @param int $id The ID of the tracked change that needs to be removed |
||
332 | * @param bool $accept Specifies whether the tracked change should be accepted or not (reject) |
||
333 | */ |
||
334 | 2 | public function removeTrackedChange(string $documentFilename, int $id, bool $accept): array |
|
335 | { |
||
336 | 2 | $ret = []; |
|
337 | |||
338 | 2 | $propertyMap = new ModifiedDocumentPropertyMap(); |
|
339 | |||
340 | 2 | Assert::assertDocumentExtension($documentFilename); |
|
341 | 2 | Assert::assertFilenameExists($documentFilename); |
|
342 | |||
343 | 2 | $query = [ |
|
344 | 2 | 'id' => $id, |
|
345 | 2 | 'accept' => Filter::filterBooleanToString($accept), |
|
346 | 2 | ]; |
|
347 | |||
348 | 2 | $data = FileUtils::read($documentFilename, true); |
|
349 | |||
350 | 2 | $result = $this->post('/processing/review/removetrackedchange', $query, $data, HttpStatus::STATUS_OK); |
|
351 | |||
352 | 2 | if (is_array($result)) { |
|
353 | 2 | $ret = $this->buildPropertyMapArray($result, $propertyMap); |
|
354 | 2 | $key = 'document'; |
|
355 | 2 | if (isset($ret[$key]) && is_string($ret[$key])) { |
|
356 | 2 | $ret[$key] = base64_decode($ret[$key], true); |
|
357 | } |
||
358 | } |
||
359 | |||
360 | 2 | return $ret; |
|
361 | } |
||
362 | |||
363 | /** |
||
364 | * Construct URI with version number |
||
365 | * |
||
366 | * @param string $uri URI |
||
367 | */ |
||
368 | abstract protected function uri(string $uri): string; |
||
369 | |||
370 | /** |
||
371 | * Request the URI with options |
||
372 | * |
||
373 | * @param string $method HTTP method |
||
374 | * @param string $uri URI |
||
375 | * @param array $options Options |
||
376 | */ |
||
377 | abstract protected function request(string $method, string $uri, array $options): ResponseInterface; |
||
378 | |||
379 | /** |
||
380 | * Using passed findAndReplaceData associative array (key-value), build array for backend (list of string arrays) |
||
381 | * |
||
382 | * @param array $array FindAndReplaceData array |
||
383 | */ |
||
384 | abstract protected function buildFindAndReplaceDataArray(array $array): array; |
||
385 | |||
386 | /** |
||
387 | * Using passed mergeSettings array, build array for backend |
||
388 | * |
||
389 | * @param array $array MergeSettings array |
||
390 | */ |
||
391 | abstract protected function buildMergeSettingsArray(array $array): array; |
||
392 | |||
393 | /** |
||
394 | * Using passed documentsData array, build array for backend |
||
395 | * |
||
396 | * @param array $array AppendDocument array |
||
397 | */ |
||
398 | abstract protected function buildDocumentsArray(array $array): array; |
||
399 | |||
400 | /** |
||
401 | * Using passed documentsSettings array, build array for backend |
||
402 | */ |
||
403 | abstract protected function buildDocumentSettingsArray(array $array): array; |
||
404 | |||
405 | /** |
||
406 | * Using the passed propertyMap, recursively build array |
||
407 | * |
||
408 | * @param array $array Array |
||
409 | * @param PropertyMap $propertyMap PropertyMap |
||
410 | */ |
||
411 | abstract protected function buildPropertyMapArray(array $array, PropertyMap $propertyMap): array; |
||
412 | |||
413 | /** |
||
414 | * Execute a POST request via REST client |
||
415 | * |
||
416 | * @param string $uri URI |
||
417 | * @param array $query Query |
||
418 | * @param mixed $json JSON |
||
419 | * @param int $statusCode Required HTTP status code for response |
||
420 | */ |
||
421 | 42 | private function post(string $uri, array $query = [], mixed $json = '', int $statusCode = 0): mixed |
|
422 | { |
||
423 | 42 | $ret = null; |
|
424 | |||
425 | 42 | $response = $this->request(HttpMethod::METHOD_POST, $this->uri($uri), [ |
|
426 | 42 | RequestOptions::QUERY => $query, |
|
427 | 42 | RequestOptions::JSON => $json, |
|
428 | 42 | ]); |
|
429 | |||
430 | 42 | if ($statusCode === $response->getStatusCode()) { |
|
431 | try { |
||
432 | 42 | $body = $response->getBody(); |
|
433 | 42 | $content = $body->getContents(); |
|
434 | 42 | $ret = json_decode($content, true, 512, JSON_THROW_ON_ERROR); |
|
435 | 24 | } catch (JsonException) { |
|
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
|
|||
436 | } |
||
437 | } |
||
438 | |||
439 | 42 | return $ret; |
|
440 | } |
||
441 | } |
||
442 |