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