1<?php 2 3declare(strict_types=1); 4 5use Fisharebest\Webtrees\Http\RequestHandlers\ControlPanel; 6use Fisharebest\Webtrees\Http\RequestHandlers\DataFixData; 7use Fisharebest\Webtrees\Http\RequestHandlers\DataFixPage; 8use Fisharebest\Webtrees\Http\RequestHandlers\DataFixUpdateAll; 9use Fisharebest\Webtrees\Http\RequestHandlers\HelpText; 10use Fisharebest\Webtrees\Http\RequestHandlers\ManageTrees; 11use Fisharebest\Webtrees\I18N; 12use Fisharebest\Webtrees\Module\ModuleDataFixInterface; 13use Fisharebest\Webtrees\Tree; 14use Fisharebest\Webtrees\View; 15 16/** 17 * @var ModuleDataFixInterface $data_fix 18 * @var string $latest_version 19 * @var string $title 20 * @var Tree $tree 21 * @var string $pending_url 22 */ 23 24?> 25 26<?= view('components/breadcrumbs', ['links' => [route(ControlPanel::class) => I18N::translate('Control panel'), route(ManageTrees::class, ['tree' => $tree->name()]) => I18N::translate('Manage family trees'), route(DataFixPage::class, ['tree' => $tree->name()]) => view('icons/data-fix') . I18N::translate('Data fixes'), $title]]) ?> 27 28<h1><?= $title ?></h1> 29 30<form action="#" id="data-fix-options"> 31 <p> 32 <?= $data_fix->description() ?> 33 </p> 34 35 <?= $data_fix->fixOptions($tree) ?> 36 37 <div class="row mb-3"> 38 <div class="col-sm-3"> 39 </div> 40 41 <div class="col-sm-9"> 42 <button class="btn btn-primary" type="button" id="btn-search"> 43 <?= view('icons/search') ?> 44 <?= I18N::translate('Search') ?> 45 </button> 46 47 <button class="btn btn-primary" type="button" id="btn-update-all"> 48 <?= view('icons/data-fix') ?> 49 <?= I18N::translate('Update all') ?> 50 </button> 51 </div> 52 </div> 53</form> 54 55<div id="data-fix-table-container" class="d-none"> 56 <table 57 id="data-fix-table" 58 class="table table-bordered table-sm table-hover wt-data-fix-table" 59 <?= view('lists/datatables-attributes') ?> 60 data-server-side="true" 61 data-filter="false" 62 data-processing="true" 63 data-sort="false" 64 > 65 <thead> 66 <tr> 67 <th class="w-75"> 68 <?= I18N::translate('Record') ?> 69 </th> 70 <th class="w-25"> 71 <?= view('icons/data-fix') ?> 72 <?= I18N::translate('Data fix') ?> 73 </th> 74 </tr> 75 </thead> 76 <tbody> 77 </tbody> 78 </table> 79 80 <hr> 81 82 <a href="#" data-bs-toggle="modal" data-bs-backdrop="static" data-bs-target="#wt-ajax-modal" data-wt-href="<?= e(route(HelpText::class, ['topic' => 'data-fixes'])) ?>"> 83 <?= view('icons/help') ?> 84 <?= I18N::translate('Why does this list include records that do not need to be updated?') ?> 85 </a> 86</div> 87 88<div id="data-fix-progress" class="d-none"> 89 <div class="progress" role="progressbar"> 90 <div class="progress-bar"></div> 91 </div> 92</div> 93 94<?= view('modals/ajax') ?> 95 96<?php View::push('javascript') ?> 97<script> 98 (function () { 99 let form = document.getElementById('data-fix-options'); 100 let container = document.getElementById('data-fix-table-container'); 101 let table = document.getElementById('data-fix-table'); 102 let progress = document.getElementById('data-fix-progress'); 103 let progressbar = progress.querySelector('.progress-bar'); 104 let queue = []; 105 106 function getParams () { 107 let formData = new FormData(form); 108 let params = {}; 109 formData.forEach(function (value, key) { 110 params[key] = value; 111 }); 112 113 return params; 114 } 115 116 function addParamsToUrl (u) { 117 let url = new URL(u); 118 let formData = new FormData(form); 119 formData.forEach(function (value, key) { 120 url.searchParams.append(key, value); 121 }); 122 123 return url.toString(); 124 125 } 126 127 form.addEventListener('submit', function (event) { 128 event.preventDefault(); 129 }); 130 131 container.addEventListener('click', function (event) { 132 if ('updateUrl' in event.target.dataset) { 133 event.preventDefault(); 134 135 webtrees.httpPost(event.target.dataset.updateUrl) 136 .then(function (response) { 137 $(table).DataTable().ajax.reload(null, false); 138 }) 139 .catch(function (error) { 140 alert(error); 141 }); 142 } 143 }); 144 145 document.getElementById('btn-search').addEventListener('click', function (event) { 146 event.preventDefault(); 147 148 // If we were in the middle of doing "update all", stop processing. 149 queue = []; 150 151 progress.classList.add('d-none'); 152 153 if ($.fn.dataTable.isDataTable(table)) { 154 $(table).DataTable().ajax.reload(); 155 } else { 156 $(table).DataTable({ 157 'ajax': { 158 'url': <?= json_encode(route(DataFixData::class, ['tree' => $tree->name(), 'data_fix' => $data_fix->name()]), JSON_THROW_ON_ERROR) ?>, 159 'data': function (data) { 160 $.extend(data, getParams()); 161 } 162 } 163 }); 164 } 165 166 container.classList.remove('d-none'); 167 }); 168 169 document.getElementById('btn-update-all').addEventListener('click', function (event) { 170 event.preventDefault(); 171 172 progressbar.innerHTML = ''; 173 progressbar.style.width = '0%'; 174 175 container.classList.add('d-none'); 176 progress.classList.remove('d-none'); 177 178 let url = addParamsToUrl(<?= json_encode(route(DataFixUpdateAll::class, ['tree' => $tree->name(), 'data_fix' => $data_fix->name()]), JSON_THROW_ON_ERROR) ?>); 179 180 webtrees.httpPost(url) 181 .then(function (response) { 182 return response.json(); 183 }) 184 .then(async function (data) { 185 queue = data; 186 while (queue.length > 0) { 187 let datum = queue.shift(); 188 await webtrees.httpPost(datum.url) 189 .then(function () { 190 let progressbar = progress.querySelector('.progress-bar'); 191 progressbar.innerHTML = datum.progress; 192 progressbar.style.width = datum.percent; 193 }); 194 } 195 }) 196 .catch(function (error) { 197 progress.innerHTML = error; 198 }); 199 }); 200 })(); 201</script> 202<?php View::endpush() ?> 203