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