Overview
Comment:Implement drag and drop ordering of fields
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | dev
Files: files | file ages | folders
SHA3-256: 9f76a46aa156ee924a38478a752629128e3625a566dbd864ff54a406306b4b42
User & Date: bohwaz on 2022-03-11 04:12:55
Other Links: branch diff | manifest | tags
Context
2022-03-12
02:08
Implement saving of dynamic fields sort order, improve JS drag and sort check-in: 0abf9de2e4 user: bohwaz tags: dev
2022-03-11
04:12
Implement drag and drop ordering of fields check-in: 9f76a46aa1 user: bohwaz tags: dev
02:04
Upgrade new user form check-in: 2591fb802e user: bohwaz tags: dev
Changes

Modified src/templates/admin/config/_menu.tpl from [fffca7d347] to [ab9107d0d2].

1
2
3
4
5
6
7
8
9
10
11
12
13
<nav class="tabs">
	<ul>
		<li{if $current == 'index'} class="current"{/if}><a href="{$admin_url}config/">Général</a></li>
		<li{if $current == 'custom'} class="current"{/if}><a href="{$admin_url}config/custom.php">Personnalisation</a></li>
		<li{if $current == 'categories'} class="current"{/if}><a href="{$admin_url}config/categories/">Catégories de membres</a></li>
		<li{if $current == 'fiches_membres'} class="current"{/if}><a href="{$admin_url}config/membres.php">Fiche des membres</a></li>
		<li{if $current == 'backup'} class="current"{/if}><a href="{$admin_url}config/backup/">Sauvegardes</a></li>
		<li{if $current == 'plugins'} class="current"{/if}><a href="{$admin_url}config/plugins.php">Extensions</a></li>
		<li{if $current == 'advanced'} class="current"{/if}><a href="{$admin_url}config/advanced/">Fonctions avancées</a></li>
	</ul>

	{if $current == 'advanced'}
	<ul class="sub">





|







1
2
3
4
5
6
7
8
9
10
11
12
13
<nav class="tabs">
	<ul>
		<li{if $current == 'index'} class="current"{/if}><a href="{$admin_url}config/">Général</a></li>
		<li{if $current == 'custom'} class="current"{/if}><a href="{$admin_url}config/custom.php">Personnalisation</a></li>
		<li{if $current == 'categories'} class="current"{/if}><a href="{$admin_url}config/categories/">Catégories de membres</a></li>
		<li{if $current == 'fields'} class="current"{/if}><a href="{$admin_url}config/fields/">Fiche des membres</a></li>
		<li{if $current == 'backup'} class="current"{/if}><a href="{$admin_url}config/backup/">Sauvegardes</a></li>
		<li{if $current == 'plugins'} class="current"{/if}><a href="{$admin_url}config/plugins.php">Extensions</a></li>
		<li{if $current == 'advanced'} class="current"{/if}><a href="{$admin_url}config/advanced/">Fonctions avancées</a></li>
	</ul>

	{if $current == 'advanced'}
	<ul class="sub">

Modified src/templates/admin/config/categories/index.tpl from [7b5889b61d] to [885e1ad798].

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
			<tr>
				<th>{$cat.name}</th>
				<td class="num">{$cat.count}</td>
				<td class="permissions">
					{display_permissions permissions=$cat}
				</td>
				<td class="actions">
					{if $cat.id != $user.id_category}
						{linkbutton shape="delete" label="Supprimer" href="supprimer.php?id=%d"|args:$cat.id}
					{/if}
					{linkbutton shape="edit" label="Modifier" href="modifier.php?id=%d"|args:$cat.id}
					{linkbutton shape="users" label="Liste des membres" href="!membres/?cat=%d"|args:$cat.id}
				</td>
			</tr>
		{/foreach}







|







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
			<tr>
				<th>{$cat.name}</th>
				<td class="num">{$cat.count}</td>
				<td class="permissions">
					{display_permissions permissions=$cat}
				</td>
				<td class="actions">
					{if $cat.id != $logged_user.id_category}
						{linkbutton shape="delete" label="Supprimer" href="supprimer.php?id=%d"|args:$cat.id}
					{/if}
					{linkbutton shape="edit" label="Modifier" href="modifier.php?id=%d"|args:$cat.id}
					{linkbutton shape="users" label="Liste des membres" href="!membres/?cat=%d"|args:$cat.id}
				</td>
			</tr>
		{/foreach}

Modified src/templates/admin/config/fields/index.tpl from [e15825a3fa] to [5abe1156d8].

1
2
3
4






5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
{include file="admin/_head.tpl" current="config" custom_css=['config.css']}

{include file="admin/config/_menu.tpl" current="fiches_membres"}







{if isset($status) && $status == 'OK'}
    <p class="block confirm">
        La configuration a bien été enregistrée.
    </p>
{elseif isset($status) && $status == 'ADDED'}
    <p class="block alert">
        Le champ a été ajouté à la fin de la liste. Pour vérifier et sauvegarder les modifications de la fiche membre cliquer sur le bouton «&nbsp;Vérifier les changements&nbsp;» en base de page.
    </p>
{/if}

{form_errors}

{if $review}
    <p class="help">
        Voici ce à quoi ressemblera la nouvelle fiche de membre, vérifiez vos modifications avant d'enregistrer les changements.
    </p>
    <p class="block alert">
        Attention&nbsp;! Si vous avez supprimé un champ, les données liées à celui-ci seront supprimées de toutes les fiches de tous les membres.
    </p>
    <fieldset>
        <legend>Fiche membre exemple</legend>
        <dl>
            {foreach from=$champs item="champ" key="nom"}
                {if $nom == 'passe'}{continue}{/if}
                {html_champ_membre config=$champ name=$nom disabled=true}
                {if empty($champ.editable) || !empty($champ.private)}
                <dd>
                    {if !empty($champ.private)}
                        (Champ caché)
                    {elseif empty($champ.editable)}
                        (Non-modifiable par les membres)
                    {/if}
                </dd>
                {/if}
            {/foreach}
        </dl>
    </fieldset>

    <fieldset id="f_passe">
        <legend>Connexion</legend>
        <dl>
            <dt><label for="f_passe">Mot de passe</label>{if !empty($champs.passe.mandatory)} <b title="(Champ obligatoire)">obligatoire</b>{/if}</dt>
            <dd><input type="password" id="f_passe" disabled="disabled" /></dd>
            {if empty($champs.passe.editable) || !empty($champs.passe.private)}
            <dd>
                {if !empty($champs.passe.private)}
                    (Champ caché)
                {elseif empty($champs.passe.editable)}
                    (Non-modifiable par les membres)
                {/if}
            </dd>
            {/if}
        </dl>
    </fieldset>

    <form method="post" action="{$admin_url}config/membres.php">
        <p class="submit">
            {csrf_field key="config_membres"}
            <input type="hidden" name="champs" value="{$champs|escape:json|escape}" />
            <input type="submit" name="back" value="&larr; Retour à l'édition" class="minor" />
            <input type="submit" name="reset" value="Annuler les changements" class="minor" />
            {button type="submit" name="save" label="Enregistrer" shape="right" class="main"}
        </p>
    </form>
{else}
    <p class="help">
        Cette page vous permet de personnaliser les fiches d'information des membres de l'association.<br />
        <strong>Attention :</strong> Les champs supprimés de la fiche seront effacés de toutes les fiches de tous les membres, et les données qu'ils contenaient seront perdues.
    </p>

    {if !empty($presets)}
    <form method="post" action="{$self_url}">
    <fieldset>
        <legend>Ajouter un champ pré-défini</legend>
        <p>
            <select name="preset" required="required">
                <option></option>
                {foreach from=$presets key="name" item="preset"}
                <option value="{$name}">{$name} &mdash; {$preset.title}</option>
                {/foreach}
            </select>
            {csrf_field key="config_membres"}
            {button type="submit" name="add" label="Ajouter ce champ à la fiche membre" shape="plus"}
        </p>
    </fieldset>
    </form>
    {/if}

<form method="post" action="{$self_url}">
    <fieldset>
        <legend>Ajouter un champ personnalisé</legend>
        <dl>
            <dt><label for="f_name">Nom unique</label> <b title="(Champ obligatoire)">obligatoire</b></dt>
            <dd class="help">Ne peut comporter que des lettres minuscules et des tirets bas.</dd>
            <dd><input type="text" name="new" id="f_name" value="{form_field name=new}" required="required" /></dd>
            <dt><label for="f_title">Titre</label> <b title="(Champ obligatoire)">obligatoire</b></dt>
            <dd><input type="text" name="new_title" id="f_title" value="{form_field name=new_title}" required="required" /></dd>
            <dt><label for="f_type">Type de champ</label> <b title="(Champ obligatoire)">obligatoire</b></dt>
            <dd>
                <select name="new_type" id="f_type" required="required">
                    {foreach from=$types key="type" item="nom"}
                    <option value="{$type}" {form_field name=new_type selected=$type}>{$nom}</option>
                    {/foreach}
                </select>
            </dd>
        </dl>
        <p>
            {csrf_field key="config_membres"}
            {button type="submit" name="add" label="Ajouter ce champ à la fiche membre" shape="plus"}
            <input type="hidden" name="champs" value="{$champs|escape:json|escape}" />
        </p>
    </fieldset>
</form>

<form method="post" action="{$self_url}">
    <div id="orderFields">
        {foreach from=$champs item="champ" key="nom"}
        {if $nom == 'passe'}{continue}{/if}
        <fieldset id="f_{$nom}">
            <legend>{$nom}</legend>
            <dl>
                <dt><label>Type</label></dt>
                <dd><input type="hidden" name="champs[{$nom}][type]" value="{$champ.type}" />{$champ.type|get_type}</dd>
                <dt><label for="f_{$nom}_title">Titre</label> <b title="(Champ obligatoire)">obligatoire</b></dt>
                <dd><input type="text" name="champs[{$nom}][title]" id="f_{$nom}_title" value="{form_field data=$champ name=title}" required="required" /></dd>
                <dt><label for="f_{$nom}_help">Aide</label></dt>
                <dd><input type="text" name="champs[{$nom}][help]" id="f_{$nom}_help" value="{form_field data=$champ name=help}" /></dd>

                <dt><input type="checkbox" name="champs[{$nom}][private]" value="1" {form_field data=$champ name=private checked="1"} id="f_{$nom}_private"/> <label for="f_{$nom}_private">Caché pour les membres</label></dt>
                <dd class="help">Si coché, ce champ ne sera pas visible par les membres dans leur espace personnel. Attention, il apparaîtra quand même sur l'export de données RGPD que le membre peut télécharger, et qui contiendra toutes les données concernant ce membre.</dd>
                <dt><input type="checkbox" name="champs[{$nom}][editable]" value="1" {form_field data=$champ name=editable checked="1"} id="f_{$nom}_editable" /> <label for="f_{$nom}_editable">Modifiable par les membres</label></dt>
                <dd class="help">Si coché, les membres pourront changer cette information depuis leur espace personnel.</dd>
                <dt><label><input type="checkbox" name="champs[{$nom}][mandatory]" value="1" {form_field data=$champ name=mandatory checked="1"} id="f_{$nom}_mandatory" /> <label for="f_{$nom}_mandatory">Champ obligatoire</label></dt>
                <dd class="help">Si coché, ce champ ne pourra rester vide.</dd>

                {if $champ.type == 'select' || $champ.type == 'multiple'}
                    <dt><label>Options disponibles</label></dt>
                    {if $champ.type == 'multiple'}
                        <dd class="help">Attention changer l'ordre des options peut avoir des effets indésirables.</dd>
                    {else}
                        <dd class="help">Attention renommer ou supprimer une option n'affecte pas ce qui a déjà été enregistré dans les fiches des membres.</dd>
                    {/if}
                    <dd>
                        <{if $champ.type == 'multiple'}ol{else}ul{/if} class="options">
                        {if !empty($champ.options)}
                            {foreach from=$champ.options key="key" item="opt"}
                                <li><input type="text" name="champs[{$nom}][options][]" value="{$opt}" /></li>
                            {/foreach}
                        {/if}
                        {if $champ.type == 'select' || empty($champ.options) || count($champ.options) < 32}
                            <li><input type="text" name="champs[{$nom}][options][]" value="" /></li>
                        {/if}
                    </dd>
                {/if}
                <dt><label for="f_{$nom}_list_row">Numéro de colonne dans la liste des membres</label></dt>
                <dd class="help">Laisser vide ou indiquer le chiffre zéro pour que ce champ n'apparaisse pas dans la liste des membres. Inscrire un chiffre entre 1 et 10 pour indiquer l'ordre d'affichage du champ dans le tableau de la liste des membres.</dd>
                <dd><input type="number" id="f_{$nom}_list_row" name="champs[{$nom}][list_row]" min="0" max="10" value="{form_field data=$champ name=list_row}" /></dd>
            </dl>
        </fieldset>
        {/foreach}
    </div>

    <fieldset id="f_passe">
        <legend>Mot de passe</legend>
        <dl>
            {input type="checkbox" name="champs[passe][private]" value="1" default=$champs.passe.private label="Caché pour les membres" help="Si coché, ce champ ne sera pas visible par les membres dans leur espace personnel"}
            {input type="checkbox" name="champs[passe][editable]" value="1" default=$champs.passe.editable label="Modifiable par les membres" help="Si coché, les membres pourront changer cette information depuis leur espace personnel"}
            {input type="checkbox" name="champs[passe][mandatory]" value="1" default=$champs.passe.mandatory label="Champ obligatoire" help="Si coché, ce champ ne pourra rester vide lors de la création d'un membre"}
        </dl>
    </fieldset>

    <p class="submit">
        {csrf_field key="config_membres"}
        {button type="submit" name="reset" label="Annuler les changements" shape="left"}
        {button type="submit" name="review" label="Vérifier les changements" shape="right" class="main"}
        <em class="help">(un récapitulatif sera présenté et une confirmation sera demandée)</em>
    </p>
</form>

<script type="text/javascript">
var champ_identifiant = "f_{$config.champ_identifiant|escape:'js'}";
var champ_identite = "f_{$config.champ_identite|escape:'js'}";

{literal}
(function () {
    if (!document.querySelector || !document.querySelectorAll)
    {
        return false;
    }

    var fields = document.querySelectorAll('#orderFields fieldset');

    for (i = 0; i < fields.length; i++)
    {
        var field = fields[i];
        field.querySelector('dl').classList.toggle('hidden');

        var legend = field.querySelector('legend');

        legend.onclick = function () {
            this.parentNode.querySelector('dl').classList.toggle('hidden');
        }

        legend.className = 'interactive';
        legend.title = 'Cliquer pour modifier ce champ';

        var actions = document.createElement('div');
        actions.className = 'actions';
        field.appendChild(actions);

        var up = document.createElement('a');
        up.className = 'icn up';
        up.innerHTML = '&uarr;';
        up.title = 'Déplacer vers le haut';
        up.onclick = function (e) {
            var field = this.parentNode.parentNode;
            var p = field.previousSibling;
            while (p.nodeType == 3) { p = p.previousSibling; }
            field.parentNode.insertBefore(field, p);
            return false;
        };
        actions.appendChild(up);

        var down = document.createElement('a');
        down.className = 'icn down';
        down.innerHTML = '&darr;';
        down.title = 'Déplacer vers le bas';
        down.onclick = function (e) {
            var field = this.parentNode.parentNode;
            var p = field.nextSibling;

            if (!p.nextSibling)
            {
                field.parentNode.appendChild(field);
            }
            else
            {
                while (p.nodeType == 3) { p = p.nextSibling; }
                p = p.nextSibling;
                while (p.nodeType == 3) { p = p.nextSibling; }
                field.parentNode.insertBefore(field, p);
            }
            return false;
        };
        actions.appendChild(down);

        var edit = document.createElement('a');
        edit.className = 'icn edit';
        edit.innerHTML = '&#x270e;';
        edit.title = 'Modifier ce champ';
        edit.onclick = function (e) {
            this.parentNode.parentNode.querySelector('dl').classList.toggle('hidden');
            return false;
        };
        actions.appendChild(edit);

        if (field.id != champ_identifiant && field.id != 'f_passe' && field.id != champ_identite && field.id != 'f_numero' && field.id != 'f_email')
        {
            var rem = document.createElement('a');
            rem.className = 'icn remove';
            rem.innerHTML = '✘';
            rem.title = 'Enlever ce champ de la fiche';
            rem.onclick = function (e) {
                if (!window.confirm('Êtes-vous sûr de supprimer ce champ des fiches de membre ?'))
                {
                    return false;
                }

                var field = this.parentNode.parentNode;
                this.parentNode.parentNode.querySelector('dl').classList.add('hidden');
                field.classList.toggle('removed');
                window.setTimeout(function () { field.parentNode.removeChild(field); }, 800);
                return false;
            };
            actions.appendChild(rem);
        }

        if (field.querySelector('.options'))
        {
            var options = field.querySelectorAll('.options li');
            var options_nb = options.length;

            if (options[0].parentNode.tagName.toLowerCase() == 'ul')
            {
                // champ select
                for (j = 0; j < options_nb; j++)
                {
                    var remove = document.createElement('input');
                    remove.type = 'button';
                    remove.className = 'icn';
                    remove.value = '-';
                    remove.title = 'Enlever cette option';
                    remove.onclick = function (e) {
                        var p = this.parentNode;
                        p.parentNode.removeChild(p);
                    };
                    options[j].appendChild(remove);
                }
            }

            var add = document.createElement('input');
            add.type = 'button';
            add.className = 'icn add';
            add.value = '+';
            add.title = 'Ajouter une option';
            add.onclick = function (e) {
                var p = this.parentNode.parentNode;
                var options = p.querySelectorAll('li');
                var new_option = this.parentNode.cloneNode(true);
                var btn = new_option.querySelector('input.add');
                new_option.getElementsByTagName('input')[0].value = '';

                if (options.length >= 30)
                {
                    new_option.removeChild(btn);
                }
                else
                {
                    btn.onclick = this.onclick;
                }

                p.appendChild(new_option);
                this.parentNode.removeChild(this);
            };

            options[options_nb - 1].appendChild(add);
        }
    }
}());
{/literal}
</script>
{/if}

{include file="admin/_foot.tpl"}
|

|

>
>
>
>
>
>

|
|
|

|
|
|




<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
|
<
<
<
|
<
<
<
<
|
<
<
<
<
<
<
<
<
|
<
|
<
|
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
|
<
|
|
<
<
|
<
<
<
|
<
<
<
<
<
<
|
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
|
<
<
<
|
<
|
<
<
<
<
|
<
|
<
|
<
<
|
<
|
<
<
<
|
<
<
|
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
|
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


<


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22











































23









24



25




26








27

28

29










30











31

32
33


34



35






36






37


























38






39









40



41

42




43

44

45


46

47



48


49



50












51







52
























53











54








55




56

















57





























58
59

60
61
{include file="admin/_head.tpl" current="config" title="Fiche des membres"}

{include file="admin/config/_menu.tpl" current="fields"}

<nav class="tabs">
	<aside>
		{linkbutton shape="plus" label="Ajouter un champ" href="new.php"}
	</aside>
</nav>

{if isset($status) && $status == 'OK'}
	<p class="block confirm">
		La configuration a bien été enregistrée.
	</p>
{elseif isset($status) && $status == 'ADDED'}
	<p class="block alert">
		Le champ a été ajouté à la fin de la liste. Pour vérifier et sauvegarder les modifications de la fiche membre cliquer sur le bouton «&nbsp;Vérifier les changements&nbsp;» en base de page.
	</p>
{/if}

{form_errors}












































<form method="post" action="{$self_url_no_qs}">









	<table class="list">



		<thead>




			<tr>








				<td>Ordre</td>

				<th>Libellé</th>

				<td>Liste des membres</td>










				<td></td>











			</tr>

		</thead>
		<tbody>


		{foreach from=$fields item="field"}



			<tr>






				<td>






					{button shape="menu" title="Cliquer, glisser et déposer pour modifier l'ordre"}


























					<input type="hidden" name="sort_order[]" value="{$field.id}" />






				</td>









				<th>{$field.label}</th>



				<td>{if $field.list_row}Oui{else}Non{/if}</td>

				<td class="actions">




					{if !$field.system || ($field.system && !($field.system | $field::PRESET))}

						{linkbutton shape="delete" label="Supprimer" href="delete.php?id=%d"|args:$field.id}

					{/if}


					{linkbutton shape="edit" label="Modifier" href="edit.php?id=%d"|args:$field.id}

				</td>



			</tr>


		{/foreach}



		</tbody>












	</table>







</form>




































<script type="text/javascript" src="{$admin_url}static/scripts/dragdrop-table.js"></script>








<script type="text/javascript">




{literal}

















enableTableDragAndDrop(document.querySelector('table'));





























{/literal}
</script>


{include file="admin/_foot.tpl"}

Modified src/templates/admin/config/index.tpl from [0b8c620213] to [cf8abf6a45].

69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
		</dl>
	</fieldset>

	<fieldset>
		<legend>Membres</legend>
		<dl>
			{input type="select" name="categorie_membres" source=$config options=$membres_cats required=true label="Catégorie par défaut des nouveaux membres"}
			{input type="select" name="champ_identite" source=$config options=$champs required=true label="Champ utilisé pour définir l'identité des membres" help="Ce champ des fiches membres sera utilisé comme identité du membre dans les emails, les fiches, les pages, etc."}
			{input type="select" name="champ_identifiant" source=$config options=$champs required=true label="Champ utilisé comme identifiant de connexion" help="Ce champ des fiches membres sera utilisé comme identifiant pour se connecter à Garradin. Ce champ doit être unique (il ne peut pas contenir deux membres ayant la même valeur dans ce champ)."}
		</dl>
	</fieldset>

	<p class="submit">
		{csrf_field key="config"}
		{button type="submit" name="save" label="Enregistrer" shape="right" class="main"}
	</p>







<
<







69
70
71
72
73
74
75


76
77
78
79
80
81
82
		</dl>
	</fieldset>

	<fieldset>
		<legend>Membres</legend>
		<dl>
			{input type="select" name="categorie_membres" source=$config options=$membres_cats required=true label="Catégorie par défaut des nouveaux membres"}


		</dl>
	</fieldset>

	<p class="submit">
		{csrf_field key="config"}
		{button type="submit" name="save" label="Enregistrer" shape="right" class="main"}
	</p>

Modified src/www/admin/config/fields/index.php from [0aca5c7236] to [775ccccf5d].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
<?php
namespace Garradin;

require_once __DIR__ . '/_inc.php';

$membres = new Membres;

// Restauration de ce qui était 
if ($champs = f('champs'))
{
    if (is_string($champs))
    {
        $champs = json_decode($champs, true);
    }

    try {
        $champs = new Membres\Champs($champs);
    }
    catch (UserException $e)
    {
        $champs = new Membres\Champs($config->get('champs_membres'));
        unset($_POST['review']);
        $form->addError($e->getMessage());
    }
}
else
{
    // Il est nécessaire de créer une nouvelle instance ici, sinon
    // l'enregistrement des modifs ne marchera pas car les deux instances seront identiques.
    // Càd si on utilise directement l'instance de $config, elle sera modifiée directement
    // du coup quand on essaiera de comparer si ça a changé ça comparera deux fois la même chose
    // donc ça n'aura pas changé forcément.
    $champs = new Membres\Champs($config->get('champs_membres'));
}

if (f('save') || f('add') || f('review') || f('reset'))
{
    $form->check('config_membres');

    if (!$form->hasErrors())
    {
        if (f('reset'))
        {
            Utils::redirect(ADMIN_URL . 'config/membres.php');
        }
        elseif (f('add'))
        {
            try {
                if (f('preset'))
                {
                    $presets = Membres\Champs::listUnusedPresets($champs);
                    if (!array_key_exists(f('preset'), $presets))
                    {
                        throw new UserException('Le champ pré-défini demandé ne fait pas partie des champs disponibles.');
                    }

                    $champs->add(f('preset'), $presets[f('preset')]);
                }
                elseif (f('new'))
                {
                    $new = f('new');

                    $champs->checkCustomFieldName($new);

                    $config = [
                        'type'  =>  f('new_type'),
                        'title' =>  f('new_title'),
                        'editable'  =>  true,
                        'mandatory' =>  false,
                    ];

                    if ($config['type'] == 'select' || $config['type'] == 'multiple')
                    {
                        $config['options'] = ['Première option'];
                    }

                    $champs->add($new, $config);
                }

                $tpl->assign('status', 'ADDED');
            }
            catch (UserException $e)
            {
                $form->addError($e->getMessage());
            }
        }
        elseif (f('save'))
        {
            try {
                $champs->save();
                Utils::redirect(ADMIN_URL . 'config/membres.php?ok');
            }
            catch (UserException $e)
            {
                $form->addError($e->getMessage());
            }
        }
    }
}
else
{
    $tpl->assign('status', null !== qg('ok'));
}

$tpl->assign('review', (bool) f('review'));

$types = $champs->getTypes();

$tpl->assign('champs', $champs->getAll());
$tpl->assign('types', $types);
$tpl->assign('presets', Membres\Champs::listUnusedPresets($champs));
$tpl->assign('new', f('new'));

$tpl->register_modifier('get_type', function ($type) use ($types) {
    return $types[$type];
});

$tpl->assign('title', 'Configuration — ' . (null !== qg('review') ? 'Confirmer les changements' : 'Fiche membres'));

$tpl->display('admin/config/membres.tpl');



<
|
<

<
<
<
<
<
<
<
|
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
1
2
3

4

5







6


7
















8




















































































9
<?php
namespace Garradin;


use Garradin\Users\DynamicFields;









require_once __DIR__ . '/../_inc.php';



















$tpl->assign('fields', DynamicFields::getInstance()->all());




















































































$tpl->display('admin/config/fields/index.tpl');

Modified src/www/admin/config/index.php from [746271d6a0] to [2ca815358a].

1
2
3
4

5
6
7
8
9
10
11
<?php
namespace Garradin;

use Garradin\Users\Categories;

use Garradin\Files\Files;
use Garradin\Entities\Files\File;

require_once __DIR__ . '/_inc.php';

if (qg('check_version') !== null) {
	echo json_encode(Upgrade::fetchLatestVersion());




>







1
2
3
4
5
6
7
8
9
10
11
12
<?php
namespace Garradin;

use Garradin\Users\Categories;
use Garradin\Users\DynamicFields;
use Garradin\Files\Files;
use Garradin\Entities\Files\File;

require_once __DIR__ . '/_inc.php';

if (qg('check_version') !== null) {
	echo json_encode(Upgrade::fetchLatestVersion());
30
31
32
33
34
35
36
37
38


39
40
41
	'new_version'      => $latest,
	'php_version'      => phpversion(),
	'has_gpg_support'  => \KD2\Security::canUseEncryption(),
	'server_time'      => time(),
	'sqlite_version'   => \SQLite3::version()['versionString'],
	'countries'        => Utils::getCountryList(),
	'membres_cats'     => Categories::listSimple(),
	'champs'           => $config->get('champs_membres')->listAssocNames(),
	'garradin_website' => WEBSITE,


]);

$tpl->display('admin/config/index.tpl');







<

>
>



31
32
33
34
35
36
37

38
39
40
41
42
43
	'new_version'      => $latest,
	'php_version'      => phpversion(),
	'has_gpg_support'  => \KD2\Security::canUseEncryption(),
	'server_time'      => time(),
	'sqlite_version'   => \SQLite3::version()['versionString'],
	'countries'        => Utils::getCountryList(),
	'membres_cats'     => Categories::listSimple(),

	'garradin_website' => WEBSITE,
	'login_field'      => DynamicFields::getLoginField(),
	'name_field'       => DynamicFields::getNameFields()[0],
]);

$tpl->display('admin/config/index.tpl');

Added src/www/admin/static/scripts/dragdrop-table.js version [53e281cf53].











































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
window.enableTableDragAndDrop = function (table) {
	var items = table.querySelectorAll('tbody tr');

	items.forEach(function (row) {
		const btn = row.querySelector('button');
		row.draggable = true;
		btn.classList.add('draggable');

		row.dataset.label = row.querySelector('th').textContent;

		row.addEventListener('dragstart', handleDragStart, false);
		row.addEventListener('dragenter', handleDragEnter, false);
		row.addEventListener('dragover', handleDragOver, false);
		row.addEventListener('dragleave', handleDragLeave, false);
		row.addEventListener('drop', handleDrop, false);
		row.addEventListener('dragend', handleDragEnd, false);
	});

	var dragSrcEl = null;

	function handleDragStart(e) {
		this.parentNode.parentNode.classList.add('dragging');
		this.classList.add('dragging');

		dragSrcEl = this;

		e.dataTransfer.effectAllowed = 'move';
		e.dataTransfer.setData('text/html', this.innerHTML);

		// Hide ghost image
		var i = new Image;
		i.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==';
		e.dataTransfer.setDragImage(i, 0, 0);
	}

	function handleDragOver(e) {
		if (e.preventDefault) {
			e.preventDefault();
		}

		e.dataTransfer.dropEffect = 'move';

		return false;
	}

	function handleDragEnter(e) {
		if (this == dragSrcEl) {
			dragSrcEl.querySelector('th').textContent = dragSrcEl.dataset.label;
			return;
		}

		changeLabel(this);

		this.classList.add('placeholder');
	}

	function handleDragLeave(e) {
		if (this == dragSrcEl) {
			return;
		}

		// Restore label, but only of current element
		this.querySelector('th').textContent = this.dataset.label;

		this.classList.remove('placeholder');
	}

	function handleDrop(e) {
		if (e.stopPropagation) {
			e.stopPropagation(); // stops the browser from redirecting.
		}

		if (dragSrcEl != this) {
			var src_label = dragSrcEl.dataset.label;
			var target_label = this.dataset.label;

			dragSrcEl.innerHTML = this.innerHTML;
			dragSrcEl.querySelector('th').textContent = target_label;
			dragSrcEl.dataset.label = target_label;

			this.dataset.label = src_label;
			this.innerHTML = e.dataTransfer.getData('text/html');
		}

		return false;
	}

	function changeLabel(elm) {
		dragSrcEl.querySelector('th').textContent = elm.dataset.label;
		elm.querySelector('th').textContent = dragSrcEl.dataset.label;
	}

	function handleDragEnd(e) {
		this.classList.remove('dragging');
		this.parentNode.parentNode.classList.remove('dragging');

		items.forEach(function (item) {
			item.classList.remove('placeholder');
		});
	}
};

Modified src/www/admin/static/styles/03-forms.css from [2ce7e6ae49] to [43a5eca13c].

399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
    line-height: 1.5rem;
    color: var(--gBorderColor);
}

p.submit {
    margin: 1em;
}


form .checkUncheck {
    float: left;
}

form span.password_check {
    margin-left: 1em;







<







399
400
401
402
403
404
405

406
407
408
409
410
411
412
    line-height: 1.5rem;
    color: var(--gBorderColor);
}

p.submit {
    margin: 1em;
}


form .checkUncheck {
    float: left;
}

form span.password_check {
    margin-left: 1em;

Modified src/www/admin/static/styles/06-tables.css from [3a60ef01c4] to [94df5f62df].

146
147
148
149
150
151
152



















    width: 1.5em;
    color: rgba(var(--gTextColor), 0.3);
}

table.list .folder .icon {
    color: rgba(var(--gTextColor), 0.5);
}


























>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
    width: 1.5em;
    color: rgba(var(--gTextColor), 0.3);
}

table.list .folder .icon {
    color: rgba(var(--gTextColor), 0.5);
}

/** Draggable table rows */
table .draggable {
    cursor: move;
    user-select: none;
}

table tr.placeholder {
    outline: 4px solid rgb(var(--gSecondColor)); !important;
}

table tr.dragging {
    opacity: 0.5;
    outline: 4px solid var(--gBorderColor) !important;
}

table.dragging td {
    opacity: 0;
}

Modified src/www/admin/static/styles/config.css from [cd8a35a686] to [0adb6dba86].

38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
	padding: .2em .5em;
}

.error .event table {
	margin: .5em;
}


#orderFields fieldset {
    position: relative;
    min-height: 2em;
    transition: all 1s;
    overflow: hidden;
    max-height: 5000px;
}

#orderFields fieldset legend {
    font-size: 1.2em;
    line-height: .8em;
    color: #666;
}

#orderFields fieldset .actions {
    display: block;
    position: absolute;
    top: 1em;
    right: 1em;
}

#orderFields fieldset .actions .icn {
    position: absolute;
}

#orderFields fieldset dl {
    overflow: hidden;
    transition: all .5s;
    opacity: 1;
    display: block;
    max-height: 5000px;
}

#orderFields fieldset dl.hidden {
    opacity: 0;
    max-height: 0;
}

#orderFields fieldset.removed {
    max-height: 0;
    opacity: 0;
    border-color: red;
    min-height: 0;
    height: 0;
}

#orderFields fieldset .actions .remove { right: 0em; }
#orderFields fieldset .actions .edit { right: 1.5em; }
#orderFields fieldset .actions .down { right: 3em; }
#orderFields fieldset .actions .up { right: 4.5em; }

#orderFields fieldset:nth-child(1) .actions .up, #orderFields fieldset:nth-last-child(1) .actions .down {
    display: none;
}

#orderFields fieldset .actions .icn {
    cursor: pointer;
}

#orderFields fieldset .interactive:hover {
    cursor: pointer;
    text-decoration: underline;
}

.image-preview img, .image-preview figure {
    vertical-align: middle;
}

.image-preview img {
    max-width: 150px;
}







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







38
39
40
41
42
43
44

































































45
46
47
48
49
50
51
	padding: .2em .5em;
}

.error .event table {
	margin: .5em;
}


































































.image-preview img, .image-preview figure {
    vertical-align: middle;
}

.image-preview img {
    max-width: 150px;
}