BaseJsonView.php
1 <?php 2 3 namespace App\Http\Livewire\Jsons; 4 5 use App\Http\Services\JsonImporterService; 6 use App\Models\Dossier; 7 use App\Models\Json; 8 use App\Models\JsonDraft; 9 use Illuminate\Foundation\Auth\Access\AuthorizesRequests; 10 use Illuminate\Pagination\LengthAwarePaginator; 11 use Illuminate\Support\Collection; 12 use Jfcherng\Diff\DiffHelper; 13 use Jfcherng\Diff\Renderer\RendererConstant; 14 use Livewire\Component; 15 use Livewire\Redirector; 16 use Livewire\WithPagination; 17 use WireUi\Traits\Actions; 18 19 abstract class BaseJsonView extends Component 20 { 21 use WithPagination, Actions, AuthorizesRequests; 22 23 /** 24 * @var $json JsonDraft|Json 25 */ 26 public $json; 27 28 public ?Json $oldJson; 29 30 public int $entityId; 31 32 public array $data; 33 34 public bool $isDiffing = false; 35 36 protected $listeners = ['refresh' => '$refresh']; 37 38 public function mount() 39 { 40 $this->authorize('viewAnyAdmin', Dossier::class); 41 $this->data = $this->json->json; 42 43 if (get_class($this->json) === JsonDraft::class) 44 $this->oldJson = JsonImporterService::findJsonFromDraft($this->json); 45 } 46 47 public function updating(): void 48 { 49 $this->resetPage(); 50 } 51 52 public function getChangelogsProperty() 53 { 54 $changelogs = collect($this->json->json['changelog']); 55 return $this->getPaginate($changelogs, 'changelogs'); 56 } 57 58 public function getAcronymsProperty() 59 { 60 $acronyms = collect($this->json->json['acronyms']); 61 return $this->getPaginate($acronyms, 'acronyms'); 62 } 63 64 public function getDocumentReferencesProperty() 65 { 66 $documentReferences = collect($this->json->json['documentReferences']); 67 return $this->getPaginate($documentReferences, 'documentReferences'); 68 } 69 70 public function getNonConformitiesProperty() 71 { 72 $nonConformities = collect($this->json->json['nonconformities']); 73 return $this->getPaginate($nonConformities, 'nonConformities'); 74 } 75 76 public function getCommentsProperty() 77 { 78 $comments = collect($this->json->json['comments']); 79 return $this->getPaginate($comments, 'comments'); 80 } 81 82 public abstract function render(); 83 84 85 public function getDossierCode(Json|JsonDraft $json) 86 { 87 return $json->json['information']['dossierCode']; 88 } 89 90 91 public function getProjectCode(Json|JsonDraft $json) 92 { 93 return $json->json['information']['projectCode']; 94 } 95 96 public function getApprovedBy(Json|JsonDraft $json) 97 { 98 return $json->json['information']['approvedBy']; 99 } 100 101 public function getReviewedBy(Json|JsonDraft $json) 102 { 103 return $json->json['information']['reviewedBy']; 104 } 105 106 public function getAuthors(Json|JsonDraft $json) 107 { 108 $olds = collect($this->oldJson->json['information']['authors']); 109 $news = collect($this->json->json['information']['authors']); 110 $result = []; 111 $used = []; 112 113 foreach ($news as $new) { 114 $used[] = $new; 115 $aux = $olds->contains($new); 116 $result[] = $this->applyDiff($new ?? '', $aux ?? ''); 117 } 118 119 foreach ($olds as $old) { 120 if (!collect($used)->contains($old)) { 121 $result[] = $this->applyDiff('', $old); 122 } 123 } 124 125 return implode('-',$result); 126 } 127 128 public function getAssuranceLevel(Json|JsonDraft $json) 129 { 130 return $json->json['information']['assuranceLevel']; 131 } 132 133 134 const rendererName = 'Combined'; 135 136 const differOptions = [ 137 // show how many neighbor lines 138 // Differ::CONTEXT_ALL can be used to show the whole file 139 'context' => 3, 140 // ignore case difference 141 'ignoreCase' => false, 142 // ignore line ending difference 143 'ignoreLineEnding' => false, 144 // ignore whitespace difference 145 'ignoreWhitespace' => false, 146 // if the input sequence is too long, it will just gives up (especially for char-level diff) 147 'lengthLimit' => 4000, 148 ]; 149 150 const rendererOptions = [ 151 // how detailed the rendered HTML in-line diff is? (none, line, word, char) 152 'detailLevel' => 'line', 153 // renderer language: eng, cht, chs, jpn, ... 154 // or an array which has the same keys with a language file 155 // check the "Custom Language" section in the readme for more advanced usage 156 'language' => 'eng', 157 // show line numbers in HTML renderers 158 'lineNumbers' => false, 159 // show a separator between different diff hunks in HTML renderers 160 'separateBlock' => true, 161 // show the (table) header 162 'showHeader' => false, 163 // the frontend HTML could use CSS "white-space: pre;" to visualize consecutive whitespaces 164 // but if you want to visualize them in the backend with " ", you can set this to true 165 'spacesToNbsp' => false, 166 // HTML renderer tab width (negative = do not convert into spaces) 167 'tabSize' => 4, 168 // this option is currently only for the Combined renderer. 169 // it determines whether a replace-type block should be merged or not 170 // depending on the content changed ratio, which values between 0 and 1. 171 'mergeThreshold' => 0.8, 172 // this option is currently only for the Unified and the Context renderers. 173 // RendererConstant::CLI_COLOR_AUTO = colorize the output if possible (default) 174 // RendererConstant::CLI_COLOR_ENABLE = force to colorize the output 175 // RendererConstant::CLI_COLOR_DISABLE = force not to colorize the output 176 'cliColorization' => RendererConstant::CLI_COLOR_AUTO, 177 // this option is currently only for the Json renderer. 178 // internally, ops (tags) are all int type but this is not good for human reading. 179 // set this to "true" to convert them into string form before outputting. 180 'outputTagAsString' => false, 181 // this option is currently only for the Json renderer. 182 // it controls how the output JSON is formatted. 183 // see available options on https://www.php.net/manual/en/function.json-encode.php 184 'jsonEncodeFlags' => \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE, 185 // this option is currently effective when the "detailLevel" is "word" 186 // characters listed in this array can be used to make diff segments into a whole 187 // for example, making "<del>good</del>-<del>looking</del>" into "<del>good-looking</del>" 188 // this should bring better readability but set this to empty array if you do not want it 189 'wordGlues' => [' ', '-'], 190 // change this value to a string as the returned diff if the two input strings are identical 191 'resultForIdenticals' => null, 192 // extra HTML classes added to the DOM of the diff container 193 'wrapperClasses' => ['diff-wrapper'], 194 ]; 195 196 public function diff(string $function) 197 { 198 $function = 'get' . ucfirst($function); 199 200 if (!$this->isDiffing) 201 return $this->{$function}($this->json); 202 203 return $this->applyDiff($this->{$function}($this->json), $this->{$function}($this->oldJson)); 204 } 205 206 protected function applyDiff(string $draft, string $saved): string 207 { 208 $result = DiffHelper::calculate($saved, $draft, self::rendererName, self::differOptions, self::rendererOptions); 209 210 if (!$result) 211 return $draft; 212 213 return $result; 214 } 215 216 public function enabledDiff() 217 { 218 $this->isDiffing = !$this->isDiffing; 219 } 220 221 public function canDiff(): bool 222 { 223 if (isset($this->oldJson) && !$this->json->completed) 224 return true; 225 226 return false; 227 } 228 229 public function canSave() 230 { 231 if (get_class($this->json) !== JsonDraft::class) 232 return false; 233 234 if ($this->json->completed) 235 return false; 236 237 return true; 238 } 239 240 public function canValidate() 241 { 242 return $this->canSave(); 243 } 244 245 public function gotoValidation() 246 { 247 if ($this->canValidate()) { 248 return redirect()->to(route('validations.jsons.view', ['jsonDraft' => $this->json])); 249 } 250 } 251 252 public function showDialog() 253 { 254 $this->dialog()->confirm([ 255 'title' => __('jsons.dialogs.title'), 256 'description' => __('jsons.dialogs.description'), 257 'acceptLabel' => __('jsons.dialogs.yes'), 258 'rejectLabel' => __('jsons.dialogs.cancel'), 259 'method' => 'save', 260 ]); 261 } 262 263 public function diffedChangelogs() 264 { 265 $olds = collect($this->oldJson->json['changelog']); 266 $news = collect($this->json->json['changelog']); 267 $result = []; 268 $used = []; 269 270 foreach ($news as $new) { 271 272 $used[] = $new['version']; 273 $aux = $olds->where('version', $new['version'])->first(); 274 275 $result[] = [ 276 'version' => $this->applyDiff($new['version'] ?? '', $aux['version'] ?? ''), 277 'date' => $this->applyDiff($new['date'] ?? '', $aux['date'] ?? ''), 278 'authors' => $this->applyDiff(implode(', ', $new['authors'] ?? []), implode(', ', $aux['authors'] ?? [])), 279 'reason' => $this->applyDiff($new['reason'] ?? '', $aux['reason'] ?? ''), 280 'changes' => $this->applyDiff(implode(', ', (array)$new['changes'] ?? []), implode(', ', is_null($aux) ? [] : (array)$aux['changes'])), 281 ]; 282 } 283 284 foreach ($olds->whereNotIn('version', $used) as $old) { 285 $result[] = [ 286 'version' => $this->applyDiff('', $old['version']), 287 'date' => $this->applyDiff('', $old['date']), 288 'authors' => $this->applyDiff('', implode(', ', $old['authors'])), 289 'reason' => $this->applyDiff('', $old['reason']), 290 'changes' => $this->applyDiff('', implode(', ', (array)$old['changes'])), 291 ]; 292 } 293 294 return $this->getPaginate(collect($result), 'changelogs'); 295 } 296 297 public function diffedAcronyms() 298 { 299 $olds = collect($this->oldJson->json['acronyms']); 300 $news = collect($this->json->json['acronyms']); 301 $result = []; 302 $used = []; 303 304 foreach ($news as $new) { 305 306 $used[] = $new['acronym']; 307 $aux = $olds->where('acronym', $new['acronym'])->first(); 308 309 $result[] = [ 310 'acronym' => $this->applyDiff($new['acronym'] ?? '', $aux['acronym'] ?? ''), 311 'meaning' => $this->applyDiff($new['meaning'] ?? '', $aux['meaning'] ?? ''), 312 ]; 313 } 314 315 foreach ($olds->whereNotIn('acronym', $used) as $old) { 316 $result[] = [ 317 'acronym' => $this->applyDiff('', $old['acronym']), 318 'meaning' => $this->applyDiff('', $old['meaning']), 319 ]; 320 } 321 322 return $this->getPaginate(collect($result), 'acronyms'); 323 } 324 325 public function diffedDocumentReferences() 326 { 327 $olds = collect($this->oldJson->json['documentReferences']); 328 $news = collect($this->json->json['documentReferences']); 329 $result = []; 330 $used = []; 331 332 foreach ($news as $new) { 333 334 $used[] = $new['reference']; 335 $aux = $olds->where('reference', $new['reference'])->first(); 336 337 $result[] = [ 338 'reference' => $this->applyDiff($new['reference'] ?? '', $aux['reference'] ?? ''), 339 'document' => $this->applyDiff($new['document'] ?? '', $aux['document'] ?? ''), 340 ]; 341 } 342 343 foreach ($olds->whereNotIn('reference', $used) as $old) { 344 $result[] = [ 345 'reference' => $this->applyDiff('', $old['reference']), 346 'document' => $this->applyDiff('', $old['document']), 347 ]; 348 } 349 350 return $this->getPaginate(collect($result), 'documentReferences'); 351 } 352 353 public function diffedNonConformities() 354 { 355 $olds = collect($this->oldJson->json['nonconformities']); 356 $news = collect($this->json->json['nonconformities']); 357 $result = []; 358 $used = []; 359 360 foreach ($news as $new) { 361 362 $used[] = $new['id']; 363 $aux = $olds->where('id', $new['id'])->first(); 364 365 $result[] = [ 366 'id' => $this->applyDiff($new['id'] ?? '', $aux['id'] ?? ''), 367 'workunit' => $this->applyDiff($new['workunit'] ?? '', $aux['workunit'] ?? ''), 368 'observation' => $this->applyDiff($new['observation'] ?? '', $aux['observation'] ?? ''), 369 'state' => $this->applyDiff($new['state'] ?? '', $aux['state'] ?? ''), 370 ]; 371 } 372 373 foreach ($olds->whereNotIn('id', $used) as $old) { 374 $result[] = [ 375 'id' => $this->applyDiff('', $old['id']), 376 'workunit' => $this->applyDiff('', $old['workunit']), 377 'observation' => $this->applyDiff('', $old['observation']), 378 'state' => $this->applyDiff('', $old['state']), 379 ]; 380 } 381 382 return $this->getPaginate(collect($result), 'nonConformities'); 383 } 384 385 public function diffedComments() 386 { 387 $olds = collect($this->oldJson->json['comments']); 388 $news = collect($this->json->json['comments']); 389 $result = []; 390 $used = []; 391 392 foreach ($news as $new) { 393 394 $used[] = $new['id']; 395 $aux = $olds->where('id', $new['id'])->first(); 396 397 $result[] = [ 398 'id' => $this->applyDiff($new['id'] ?? '', $aux['id'] ?? ''), 399 'workunit' => $this->applyDiff($new['workunit'] ?? '', $aux['workunit'] ?? ''), 400 'observation' => $this->applyDiff($new['observation'] ?? '', $aux['observation'] ?? ''), 401 'state' => $this->applyDiff($new['state'] ?? '', $aux['state'] ?? ''), 402 ]; 403 } 404 405 foreach ($olds->whereNotIn('id', $used) as $old) { 406 $result[] = [ 407 'id' => $this->applyDiff('', $old['id']), 408 'workunit' => $this->applyDiff('', $old['workunit']), 409 'observation' => $this->applyDiff('', $old['observation']), 410 'state' => $this->applyDiff('', $old['state']), 411 ]; 412 } 413 414 return $this->getPaginate(collect($result), 'comments'); 415 } 416 417 418 public function getPaginate(Collection $items, string $pageName, ?bool $isDiffing = false, ?int $perPage = 3): LengthAwarePaginator 419 { 420 $pageName = $pageName . 'Page'; 421 $itemsReverse = $isDiffing ? $items->reverse() : $items; 422 return $itemsReverse->paginate($perPage, $itemsReverse->count(), null, $pageName); 423 } 424 425 public function save(): Redirector 426 { 427 $this->authorize('viewAnyAdmin', Dossier::class); 428 429 JsonImporterService::acceptDraft($this->json); 430 $this->notification()->success( 431 __('jsons.dialogs.notification.title'), 432 __('jsons.dialogs.notification.description'), 433 ); 434 435 $this->emitSelf('refresh'); 436 437 return redirect()->to(route('jsons.index')); 438 } 439 }