xref: /webtrees/resources/views/admin/data-fix-page.phtml (revision 17c6a11dd0f7d834aa76b1703185adf9cae1b8a9)
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()]), JSON_THROW_ON_ERROR) ?>,
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()]), JSON_THROW_ON_ERROR) ?>);
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