xref: /webtrees/resources/views/admin/trees-privacy.phtml (revision 1b860509f2032c67fc289dfb8ccba11726fbd18a)
1<?php use Fisharebest\Webtrees\Bootstrap4;
2use Fisharebest\Webtrees\Functions\FunctionsEdit;
3use Fisharebest\Webtrees\I18N;
4use Fisharebest\Webtrees\Site;
5use Fisharebest\Webtrees\View; ?>
6<?php ?>
7<?php ?>
8<?php ?>
9<?php ?>
10
11<?= view('components/breadcrumbs', ['links' => [route('admin-control-panel') => I18N::translate('Control panel'), route('admin-trees') => I18N::translate('Manage family trees'), $title]]) ?>
12
13<h1><?= $title ?></h1>
14
15<form method="POST">
16    <input type="hidden" name="route" value="tree-privacy">
17    <input type="hidden" name="ged" value="<?= e($tree->name()) ?>">
18    <?= csrf_field() ?>
19
20    <!-- REQUIRE_AUTHENTICATION -->
21    <div class="row form-group">
22        <div class="col-form-label col-sm-4">
23            <label>
24                <?= /* I18N: A configuration setting */ I18N::translate('Show the family tree') ?>
25            </label>
26            <div class="hidden-xs">
27                <span class="badge visitors"><?= I18N::translate('visitors') ?></span>
28                <span class="badge members"><?= I18N::translate('members') ?></span>
29            </div>
30        </div>
31        <div class="col-sm-8">
32            <?= Bootstrap4::select(['0' => I18N::translate('Show to visitors'), '1' => I18N::translate('Show to members')], $tree->getPreference('REQUIRE_AUTHENTICATION'), ['id' => 'REQUIRE_AUTHENTICATION', 'name' => 'REQUIRE_AUTHENTICATION']) ?>
33            <p class="small text-muted">
34                <?= /* I18N: Help text for the “Family tree” configuration setting */ I18N::translate('Enabling this option will force all visitors to sign in before they can view any data on the website.') ?>
35            </p>
36            <?php if (Site::getPreference('USE_REGISTRATION_MODULE') === '1') : ?>
37                <p class="small text-muted">
38                    <?= I18N::translate('If visitors can not see the family tree, they will not be able to sign up for an account. You will need to add their account manually.') ?>
39                </p>
40            <?php endif ?>
41        </div>
42    </div>
43
44    <!-- SHOW_DEAD_PEOPLE -->
45    <div class="row form-group">
46        <div class="col-form-label col-sm-4">
47            <label for="SHOW_DEAD_PEOPLE">
48                <?= /* I18N: A configuration setting */ I18N::translate('Show dead individuals') ?>
49            </label>
50            <div class="hidden-xs">
51                <span class="badge visitors"><?= I18N::translate('visitors') ?></span>
52                <span class="badge members"><?= I18N::translate('members') ?></span>
53            </div>
54        </div>
55        <div class="col-sm-8">
56            <?= Bootstrap4::select(array_slice(FunctionsEdit::optionsAccessLevels(), 0, 2, true), $tree->getPreference('SHOW_DEAD_PEOPLE'), ['id' => 'SHOW_DEAD_PEOPLE', 'name' => 'SHOW_DEAD_PEOPLE']) ?>
57            <p class="small text-muted">
58                <?= /* I18N: Help text for the “Show dead individuals” configuration setting */ I18N::translate('Set the privacy access level for all dead individuals.') ?>
59            </p>
60        </div>
61    </div>
62
63
64    <!-- MAX_ALIVE_AGE -->
65    <div class="row form-group">
66        <label class="col-form-label col-sm-4" for="MAX_ALIVE_AGE">
67            <?= I18N::translate('Age at which to assume an individual is dead') ?>
68        </label>
69        <div class="col-sm-8">
70            <input
71                class="form-control"
72                id="MAX_ALIVE_AGE"
73                maxlength="5"
74                name="MAX_ALIVE_AGE"
75                type="text"
76                value="<?= e($tree->getPreference('MAX_ALIVE_AGE')) ?>"
77            >
78            <p class="small text-muted">
79                <?= /* I18N: Help text for the “Age at which to assume an individual is dead” configuration setting */ I18N::translate('If this individual has any events other than death, burial, or cremation more recent than this number of years, they are considered to be “alive”. Children’s birth dates are considered to be such events for this purpose.') ?>
80            </p>
81        </div>
82    </div>
83
84    <!-- HIDE_LIVE_PEOPLE -->
85    <fieldset class="form-group">
86        <div class="row">
87            <div class="col-sm-4">
88                <legend class="col-form-label">
89                    <?= /* I18N: A configuration setting */ I18N::translate('Show living individuals') ?>
90                    <div class="hidden-xs">
91                        <span class="badge visitors"><?= I18N::translate('visitors') ?></span>
92                        <span class="badge members"><?= I18N::translate('members') ?></span>
93                    </div>
94                </legend>
95            </div>
96            <div class="col-sm-8">
97                <?= Bootstrap4::select(['0' => I18N::translate('Show to visitors'), '1' => I18N::translate('Show to members')], $tree->getPreference('HIDE_LIVE_PEOPLE'), ['id' => 'HIDE_LIVE_PEOPLE', 'name' => 'HIDE_LIVE_PEOPLE']) ?>
98                <p class="small text-muted">
99                    <?= /* I18N: Help text for the “Show living individuals” configuration setting */ I18N::translate('If you show living individuals to visitors, all other privacy restrictions are ignored. Do this only if all the data in your tree is public.') ?>
100                </p>
101            </div>
102        </div>
103    </fieldset>
104
105    <!-- KEEP_ALIVE_YEARS_BIRTH / KEEP_ALIVE_YEARS_DEATH -->
106    <fieldset class="form-group">
107        <div class="row">
108            <legend class="col-form-label col-sm-4">
109                <?= /* I18N: A configuration setting. …who were born in the last XX years or died in the    last YY years */ I18N::translate('Extend privacy to dead individuals') ?>
110            </legend>
111            <div class="col-sm-8">
112                <?php
113                echo
114                    /* I18N: Extend privacy to dead individuals who were… */ I18N::translate(
115                        'born in the last %1$s years or died in the last %2$s years',
116                        '<input type="text" name="KEEP_ALIVE_YEARS_BIRTH" value="' . $tree->getPreference('KEEP_ALIVE_YEARS_BIRTH') . '" size="5" maxlength="3">',
117                        '<input type="text" name="KEEP_ALIVE_YEARS_DEATH" value="' . $tree->getPreference('KEEP_ALIVE_YEARS_DEATH') . '" size="5" maxlength="3">'
118                    ) ?>
119                <p class="small text-muted">
120                    <?= /* I18N: Help text for the “Extend privacy to dead individuals” configuration setting */ I18N::translate('In some countries, privacy laws apply not only to living individuals, but also to those who have died recently. This option will allow you to extend the privacy rules for living individuals to those who were born or died within a specified number of years. Leave these values empty to disable this feature.') ?>
121                </p>
122            </div>
123        </div>
124    </fieldset>
125
126    <!-- SHOW_LIVING_NAMES -->
127    <div class="row form-group">
128        <div class="col-form-label col-sm-4">
129            <label for="SHOW_LIVING_NAMES">
130                <?= /* I18N: A configuration setting */ I18N::translate('Show names of private individuals') ?>
131            </label>
132            <div class="hidden-xs">
133                <span class="badge visitors"><?= I18N::translate('visitors') ?></span>
134                <span class="badge members"><?= I18N::translate('members') ?></span>
135                <span class="badge managers"><?= I18N::translate('managers') ?></span>
136            </div>
137        </div>
138        <div class="col-sm-8">
139            <?= Bootstrap4::select(array_slice(FunctionsEdit::optionsAccessLevels(), 0, 3, true), $tree->getPreference('SHOW_LIVING_NAMES'), ['id' => 'SHOW_LIVING_NAMES', 'name' => 'SHOW_LIVING_NAMES']) ?>
140            <p class="small text-muted">
141                <?= /* I18N: Help text for the “Show names of private individuals” configuration setting */ I18N::translate('This option will show the names (but no other details) of private individuals. Individuals are private if they are still alive or if a privacy restriction has been added to their individual record. To hide a specific name, add a privacy restriction to that name record.') ?>
142            </p>
143        </div>
144    </div>
145
146    <!-- SHOW_PRIVATE_RELATIONSHIPS -->
147    <div class="row form-group">
148        <div class="col-form-label col-sm-4">
149            <label for="SHOW_PRIVATE_RELATIONSHIPS">
150                <?= /* I18N: A configuration setting */ I18N::translate('Show private relationships') ?>
151            </label>
152            <div class="hidden-xs">
153                <span class="badge visitors"><?= I18N::translate('visitors') ?></span>
154                <span class="badge members"><?= I18N::translate('members') ?></span>
155            </div>
156        </div>
157        <div class="col-sm-8">
158            <?= Bootstrap4::select(['0' => I18N::translate('Hide from everyone'), '1' => I18N::translate('Show to visitors')], $tree->getPreference('SHOW_PRIVATE_RELATIONSHIPS'), ['id' => 'SHOW_PRIVATE_RELATIONSHIPS', 'name' => 'SHOW_PRIVATE_RELATIONSHIPS']) ?>
159            <p class="small text-muted">
160                <?= /* I18N: Help text for the “Show private relationships” configuration setting */ I18N::translate('This option will retain family links in private records. This means that you will see empty “private” boxes on the pedigree chart and on other charts with private individuals.') ?>
161            </p>
162        </div>
163    </div>
164    <h2><?= /* I18N: Privacy restrictions are set by RESN tags in GEDCOM. */ I18N::translate('Privacy restrictions') ?></h2>
165    <p>
166        <?= /* I18N: Privacy restrictions are RESN tags in GEDCOM. */ I18N::translate('You can set the access for a specific record, fact, or event by adding a restriction to it. If a record, fact, or event does not have a restriction, the following default restrictions will be used.') ?>
167    </p>
168
169    <script id="new-resn-template" type="text/html">
170        <tr>
171            <td>
172                <select class="form-control record-type-selector">
173                    <option value="individual"><?= I18N::translate('Individual') ?></option>
174                    <option value="family"><?= I18N::translate('Family') ?></option>
175                    <option value="source"><?= I18N::translate('Source') ?></option>
176                    <option value="repository"><?= I18N::translate('Repository') ?></option>
177                    <option value="note"><?= I18N::translate('Note') ?></option>
178                    <option value="media"><?= I18N::translate('Media object') ?></option>
179                </select>
180                <span class="select-record select-individual">
181                    <?= FunctionsEdit::formControlIndividual($tree, null, ['name' => 'xref[]', 'class' => 'form-control', 'style' => 'width:100%;']) ?>
182                </span>
183                <span class="select-record select-family d-none">
184                    <?= FunctionsEdit::formControlFamily($tree, null, ['name' => 'xref[]', 'class' => 'form-control', 'style' => 'width:100%;', 'disabled' => true]) ?>
185                </span>
186                <span class="select-record select-source d-none">
187                    <?= FunctionsEdit::formControlSource($tree, null, ['name' => 'xref[]', 'class' => 'form-control', 'style' => 'width:100%;', 'disabled' => true]) ?>
188                </span>
189                <span class="select-record select-repository d-none">
190                    <?= FunctionsEdit::formControlRepository($tree, null, ['name' => 'xref[]', 'class' => 'form-control', 'style' => 'width:100%;', 'disabled' => true]) ?>
191                </span>
192                <span class="select-record select-note d-none">
193                    <?= FunctionsEdit::formControlNote($tree, null, ['name' => 'xref[]', 'class' => 'form-control', 'style' => 'width:100%;', 'disabled' => true]) ?>
194                </span>
195                <span class="select-record select-media d-none">
196                    <?= FunctionsEdit::formControlMediaObject($tree, null, ['name' => 'xref[]', 'class' => 'form-control', 'style' => 'width:100%;', 'disabled' => true]) ?>
197                </span>
198            </td>
199            <td>
200                <?= Bootstrap4::select($all_tags, '', ['name' => 'tag_type[]']) ?>
201            </td>
202            <td>
203                <?= Bootstrap4::select($privacy_constants, 'privacy', ['name' => 'resn[]']) ?>
204            </td>
205            <td>
206            </td>
207        </tr>
208    </script>
209
210    <table class="table table-bordered table-sm table-hover" id="default-resn">
211        <caption class="sr-only">
212            <?= I18N::translate('Privacy restrictions - these apply to records and facts that do not contain a GEDCOM RESN tag') ?>
213        </caption>
214        <thead>
215            <tr>
216                <th>
217                    <?= I18N::translate('Record') ?>
218                </th>
219                <th>
220                    <?= I18N::translate('Fact or event') ?>
221                </th>
222                <th>
223                    <?= I18N::translate('Access level') ?>
224                </th>
225                <th>
226                    <button class="btn btn-primary" id="add-resn" type="button">
227                <?= view('icons/add') ?>
228                        <?= /* I18N: A button label. */ I18N::translate('add') ?>
229                    </button>
230                </th>
231            </tr>
232        </thead>
233        <tbody>
234            <?php foreach ($privacy_restrictions as $privacy_restriction) : ?>
235                <tr>
236                    <td>
237                        <?php if ($privacy_restriction->record) : ?>
238                            <a href="<?= e($privacy_restriction->record->url()) ?>"><?= $privacy_restriction->record->fullName() ?></a>
239                        <?php elseif ($privacy_restriction->xref) : ?>
240                            <div class="text-danger">
241                                <?= $privacy_restriction->xref ?><?= I18N::translate('This record does not exist.') ?>
242                            </div>
243                        <?php else : ?>
244                            <div class="text-muted">
245                                <?= I18N::translate('All records') ?>
246                            </div>
247                        <?php endif ?>
248                    </td>
249                    <td>
250                        <?php if ($privacy_restriction->tag_label === '') : ?>
251                            <div class="text-muted">
252                                <?= I18N::translate('All facts and events') ?>
253                            </div>
254                        <?php else : ?>
255                            <?= $privacy_restriction->tag_label ?>
256                        <?php endif ?>
257                    </td>
258                    <td>
259                        <?= FunctionsEdit::optionsRestrictions(false)[$privacy_restriction->resn] ?>
260                    </td>
261                    <td>
262                        <label for="delete-<?= $privacy_restriction->default_resn_id ?>">
263                            <input id="delete-<?= $privacy_restriction->default_resn_id ?>" name="delete[]" type="checkbox" value="<?= $privacy_restriction->default_resn_id ?>">
264                            <?= I18N::translate('Delete') ?>
265                        </label>
266                    </td>
267                </tr>
268            <?php endforeach ?>
269        </tbody>
270    </table>
271
272    <div class="row form-group">
273        <div class="offset-sm-4 col-sm-8">
274            <button type="submit" class="btn btn-primary">
275                <?= view('icons/save') ?>
276                <?= I18N::translate('save') ?>
277            </button>
278            <a class="btn btn-secondary" href="<?= route('admin-trees', ['ged' => $tree->name()]) ?>">
279                <?= view('icons/cancel') ?>
280                <?= I18N::translate('cancel') ?>
281            </a>
282            <!-- Coming soon
283            <div class="form-check">
284                <?php if ($count_trees > 1) : ?>
285                <label>
286                    <input type="checkbox" name="all_trees">
287                    <?= /* I18N: Label for checkbox */ I18N::translate('Apply these preferences to all family trees') ?>
288                </label>
289                <?php endif ?>
290            </div>
291            <div class="form-check">
292                <label>
293                    <input type="checkbox" name="new_trees">
294                    <?= /* I18N: Label for checkbox */ I18N::translate('Apply these preferences to new family trees') ?>
295                </label>
296            </div>
297        </div>
298        -->
299        </div>
300
301</form>
302
303<?php View::push('javascript') ?>
304<script>
305    "use strict";
306
307    /**
308     * Hide/show the feedback labels for a privacy option.
309     *
310     * @param sel    the control to change
311     * @param who    "visitors", "members" or "managers"
312     * @param access true or false
313     */
314    function setPrivacyFeedback(sel, who, access) {
315        var formGroup = $(sel).closest(".form-group");
316
317        if (access) {
318            $("." + who, formGroup).addClass("badge-success").removeClass("badge-secondary");
319            $("." + who + " i", formGroup).addClass("fa-check").removeClass("fa-times");
320        } else {
321            $("." + who, formGroup).addClass("badge-secondary").removeClass("badge-success");
322            $("." + who + " i", formGroup).addClass("fa-times").removeClass("fa-check");
323        }
324    }
325
326    /**
327     * Update all the privacy feedback labels.
328     */
329    function updatePrivacyFeedback() {
330        var requireAuthentication    = parseInt($("[name=REQUIRE_AUTHENTICATION]").val(), 10);
331        var showDeadPeople           = parseInt($("[name=SHOW_DEAD_PEOPLE]").val(), 10);
332        var hideLivePeople           = parseInt($("[name=HIDE_LIVE_PEOPLE]").val(), 10);
333        var showLivingNames          = parseInt($("[name=SHOW_LIVING_NAMES]").val(), 10);
334        var showPrivateRelationships = parseInt($("[name=SHOW_PRIVATE_RELATIONSHIPS]").val(), 10);
335
336        setPrivacyFeedback("[name=REQUIRE_AUTHENTICATION]", "visitors", requireAuthentication === 0);
337        setPrivacyFeedback("[name=REQUIRE_AUTHENTICATION]", "members", true);
338
339        setPrivacyFeedback("[name=SHOW_DEAD_PEOPLE]", "visitors", requireAuthentication === 0 && (showDeadPeople >= 2 || hideLivePeople === 0));
340        setPrivacyFeedback("[name=SHOW_DEAD_PEOPLE]", "members", showDeadPeople >= 1 || hideLivePeople === 0);
341
342        setPrivacyFeedback("[name=HIDE_LIVE_PEOPLE]", "visitors", requireAuthentication === 0 && hideLivePeople === 0);
343        setPrivacyFeedback("[name=HIDE_LIVE_PEOPLE]", "members", true);
344
345        setPrivacyFeedback("[name=SHOW_LIVING_NAMES]", "visitors", requireAuthentication === 0 && showLivingNames >= 2);
346        setPrivacyFeedback("[name=SHOW_LIVING_NAMES]", "members", showLivingNames >= 1);
347        setPrivacyFeedback("[name=SHOW_LIVING_NAMES]", "managers", showLivingNames >= 0);
348
349        setPrivacyFeedback("[name=SHOW_PRIVATE_RELATIONSHIPS]", "visitors", requireAuthentication === 0 && showPrivateRelationships >= 1);
350        setPrivacyFeedback("[name=SHOW_PRIVATE_RELATIONSHIPS]", "members", showPrivateRelationships >= 1);
351    }
352
353    // Activate the privacy feedback labels.
354    updatePrivacyFeedback();
355    $("[name=REQUIRE_AUTHENTICATION], [name=HIDE_LIVE_PEOPLE], [name=SHOW_DEAD_PEOPLE], [name=SHOW_LIVING_NAMES], [name=SHOW_PRIVATE_RELATIONSHIPS]").on("change", function () {
356        updatePrivacyFeedback();
357    });
358
359    // Mute a line when it is marked for deletion
360    $("#default-resn").on("click", "input[type=checkbox]", function () {
361        if ($(this).prop("checked")) {
362            $($(this).closest("tr").addClass("text-muted"));
363        } else {
364            $($(this).closest("tr").removeClass("text-muted"));
365        }
366    });
367
368    // Add a new row to the table
369    $("#add-resn").on("click", function () {
370        $("#default-resn tbody").prepend($("#new-resn-template").html());
371
372        // Select2 - same as webtrees.js
373        $("select.select2").select2({
374            escapeMarkup: function (x) {
375                return x;
376            },
377        })
378        .on("select2:unselect", function (evt) {
379            $(evt.delegateTarget).append("<option value=\"\" selected=\"selected\"></option>");
380        });
381
382        // Record type selector
383        $(".record-type-selector").change(function () {
384            $(".select-record", $(this).parent()).addClass('d-none');
385            $(".select-record select", $(this).parent()).prop( "disabled", true);
386            $(".select-" + $(this).val(), $(this).parent()).removeClass('d-none');
387            $(".select-" + $(this).val() + " select", $(this).parent()).prop( "disabled", false);
388        });
389    });
390</script>
391<?php View::endpush() ?>
392