Overview
Comment:Fix install and other forms with passwords
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | dev
Files: files | file ages | folders
SHA1: 14966850df283333a31eecbd2dfee13cac6c801b
User & Date: bohwaz on 2020-11-08 03:24:57
Other Links: branch diff | manifest | tags
Context
2020-11-08
03:25
Remove unused code check-in: 91ffebb0bf user: bohwaz tags: dev
03:24
Fix install and other forms with passwords check-in: 14966850df user: bohwaz tags: dev
03:24
Improve login form messages check-in: cb81b4f743 user: bohwaz tags: dev
Changes

Modified src/include/lib/Garradin/Install.php from [703fdadf14] to [b589e9ae71].

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
..
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
..
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
...
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
			// Force l'installation de plugin système
			Plugin::checkAndInstallSystemPlugins();
		}

		return $ok;
	}

	static public function install($nom_asso, $adresse_asso, $email_asso, $nom_categorie, $nom_membre, $email_membre, $passe_membre, $site_asso = WWW_URL)
	{
		$db = DB::getInstance(true);

		// Taille de la page de DB, on force à 4096 (défaut dans les dernières
		// versions de SQLite mais pas les vieilles)
		$db->exec('PRAGMA page_size = 4096;');
		$db->exec('VACUUM;');
................................................................................
		$db->exec(file_get_contents(DB_SCHEMA));
		$db->commit();

		// Configuration de base
		// c'est dans Config::set que sont vérifiées les données utilisateur (renvoie UserException)
		$config = Config::getInstance();
		$config->set('nom_asso', $nom_asso);
		$config->set('adresse_asso', $adresse_asso);
		$config->set('email_asso', $email_asso);
		$config->set('site_asso', $site_asso);
		$config->set('monnaie', '€');
		$config->set('pays', 'FR');
		$config->setVersion(garradin_version());

		$champs = Membres\Champs::importInstall();
		$champs->save(false); // Pas de copie car pas de table membres existante

................................................................................
			'droit_compta' => Membres::DROIT_AUCUN,
			'droit_config' => Membres::DROIT_AUCUN,
			'droit_connexion' => Membres::DROIT_AUCUN,
			'cacher' => 1,
		]);

		$id = $cats->add([
			'nom' => ucfirst($nom_categorie),
			'droit_inscription' => Membres::DROIT_AUCUN,
			'droit_wiki' => Membres::DROIT_ADMIN,
			'droit_membres' => Membres::DROIT_ADMIN,
			'droit_compta' => Membres::DROIT_ADMIN,
			'droit_config' => Membres::DROIT_ADMIN,
		]);

................................................................................
        	'position' => Account::ASSET_OR_LIABILITY,
        	'id_chart' => $chart->id(),
        	'user' => 1,
        ]);
        $account->save();

		// Ajout d'une recherche avancée en exemple
		$query = [
			'query' => [[
				'operator' => 'AND',
				'conditions' => [
					[
						'column'   => 'lettre_infos',
						'operator' => '= 1',
						'values'   => [],







|







 







<
|
|







 







|







 







|







36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
..
55
56
57
58
59
60
61

62
63
64
65
66
67
68
69
70
..
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
...
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
			// Force l'installation de plugin système
			Plugin::checkAndInstallSystemPlugins();
		}

		return $ok;
	}

	static public function install($nom_asso, $nom_membre, $email_membre, $passe_membre)
	{
		$db = DB::getInstance(true);

		// Taille de la page de DB, on force à 4096 (défaut dans les dernières
		// versions de SQLite mais pas les vieilles)
		$db->exec('PRAGMA page_size = 4096;');
		$db->exec('VACUUM;');
................................................................................
		$db->exec(file_get_contents(DB_SCHEMA));
		$db->commit();

		// Configuration de base
		// c'est dans Config::set que sont vérifiées les données utilisateur (renvoie UserException)
		$config = Config::getInstance();
		$config->set('nom_asso', $nom_asso);

		$config->set('email_asso', $email_membre);
		$config->set('site_asso', WWW_URL);
		$config->set('monnaie', '€');
		$config->set('pays', 'FR');
		$config->setVersion(garradin_version());

		$champs = Membres\Champs::importInstall();
		$champs->save(false); // Pas de copie car pas de table membres existante

................................................................................
			'droit_compta' => Membres::DROIT_AUCUN,
			'droit_config' => Membres::DROIT_AUCUN,
			'droit_connexion' => Membres::DROIT_AUCUN,
			'cacher' => 1,
		]);

		$id = $cats->add([
			'nom' => 'Administrateurs',
			'droit_inscription' => Membres::DROIT_AUCUN,
			'droit_wiki' => Membres::DROIT_ADMIN,
			'droit_membres' => Membres::DROIT_ADMIN,
			'droit_compta' => Membres::DROIT_ADMIN,
			'droit_config' => Membres::DROIT_ADMIN,
		]);

................................................................................
        	'position' => Account::ASSET_OR_LIABILITY,
        	'id_chart' => $chart->id(),
        	'user' => 1,
        ]);
        $account->save();

		// Ajout d'une recherche avancée en exemple
		$query = (object) [
			'query' => [[
				'operator' => 'AND',
				'conditions' => [
					[
						'column'   => 'lettre_infos',
						'operator' => '= 1',
						'values'   => [],

Modified src/templates/admin/install.tpl from [b50bf2de06] to [1fb95b991f].

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
..
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
    <form method="post" action="{$self_url}">

    <fieldset>
        <legend>Informations sur l'association</legend>
        <dl>
            <dt><label for="f_nom_asso">Nom</label> <b title="(Champ obligatoire)">obligatoire</b></dt>
            <dd><input type="text" name="nom_asso" id="f_nom_asso" required="required" value="{form_field name=nom_asso}" /></dd>
            <dt><label for="f_email_asso">Adresse E-Mail</label> <b title="(Champ obligatoire)">obligatoire</b></dt>
            <dd><input type="email" name="email_asso" id="f_email_asso" required="required" value="{form_field name=email_asso}" /></dd>
            <dt><label for="f_adresse_asso">Adresse postale</label></dt>
            <dd><textarea cols="50" rows="5" name="adresse_asso" id="f_adresse_asso">{form_field name=adresse_asso}</textarea></dd>
        </dl>
    </fieldset>

    <fieldset>
        <legend>Informations sur le premier membre</legend>
        <dl>
            <dt><label for="f_nom_membre">Nom et prénom</label> <b title="(Champ obligatoire)">obligatoire</b></dt>
            <dd><input type="text" name="nom_membre" id="f_nom_membre" required="required" value="{form_field name=nom_membre}" /></dd>
            <dt><label for="f_cat_membre">Catégorie du membre</label> <b title="(Champ obligatoire)">obligatoire</b></dt>
            <dd class="tip">Par exemple : bureau, conseil d'administration, présidente, trésorier, etc.</dd>
            <dd><input type="text" name="cat_membre" id="f_cat_membre" required="required" value="{form_field name=cat_membre}" /></dd>
            <dt><label for="f_email_membre">Adresse E-Mail</label> <b title="(Champ obligatoire)">obligatoire</b></dt>
            <dd><input type="email" name="email_membre" id="f_email_membre" required="required" value="{form_field name=email_membre}" /></dd>
            <dt><label for="f_passe_membre">Mot de passe</label> (minimum {$password_length} caractères) <b title="(Champ obligatoire)">obligatoire</b></dt>
            <dd class="help">
                Astuce : un mot de passe de quatre mots choisis au hasard dans le dictionnaire est plus sûr 
                et plus simple à retenir qu'un mot de passe composé de 10 lettres et chiffres.
            </dd>
            <dd class="help">
                Pas d'idée&nbsp;? Voici une suggestion choisie au hasard :
                <input type="text" readonly="readonly" title="Cliquer pour utiliser cette suggestion comme mot de passe" id="pw_suggest" value="{$passphrase}" autocomplete="off" />
            </dd>
            <dd><input type="password" name="passe" id="f_passe_membre" value="{form_field name=passe}" pattern="{$password_pattern}" required="required" autocomplete="off" /></dd>
................................................................................
        <input type="submit" id="f_submit" name="save" value="Terminer l'installation &rarr;" />
    </p>

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

    <script type="text/javascript">
    {literal}
    g.script('scripts/password.js').onload = function () {
        initPasswordField('pw_suggest', 'f_passe_membre', 'f_repasse_membre');
    };
    
    var form = $('form')[0];
    form.onsubmit = function () {
        $('#f_submit').style.opacity = 0;
        var loader = document.createElement('div');
        loader.className = 'loader install';
        loader.innerHTML = '<b>Garradin est en cours d\'installation…</b>';
        $('#f_submit').parentNode.appendChild(loader);







<
<
<
<




|



<
<
<




|







 







|

|
|







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
..
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
    <form method="post" action="{$self_url}">

    <fieldset>
        <legend>Informations sur l'association</legend>
        <dl>
            <dt><label for="f_nom_asso">Nom</label> <b title="(Champ obligatoire)">obligatoire</b></dt>
            <dd><input type="text" name="nom_asso" id="f_nom_asso" required="required" value="{form_field name=nom_asso}" /></dd>




        </dl>
    </fieldset>

    <fieldset>
        <legend>Création du compte administrateur</legend>
        <dl>
            <dt><label for="f_nom_membre">Nom et prénom</label> <b title="(Champ obligatoire)">obligatoire</b></dt>
            <dd><input type="text" name="nom_membre" id="f_nom_membre" required="required" value="{form_field name=nom_membre}" /></dd>



            <dt><label for="f_email_membre">Adresse E-Mail</label> <b title="(Champ obligatoire)">obligatoire</b></dt>
            <dd><input type="email" name="email_membre" id="f_email_membre" required="required" value="{form_field name=email_membre}" /></dd>
            <dt><label for="f_passe_membre">Mot de passe</label> (minimum {$password_length} caractères) <b title="(Champ obligatoire)">obligatoire</b></dt>
            <dd class="help">
                Astuce : un mot de passe de quatre mots choisis au hasard dans le dictionnaire est plus sûr
                et plus simple à retenir qu'un mot de passe composé de 10 lettres et chiffres.
            </dd>
            <dd class="help">
                Pas d'idée&nbsp;? Voici une suggestion choisie au hasard :
                <input type="text" readonly="readonly" title="Cliquer pour utiliser cette suggestion comme mot de passe" id="pw_suggest" value="{$passphrase}" autocomplete="off" />
            </dd>
            <dd><input type="password" name="passe" id="f_passe_membre" value="{form_field name=passe}" pattern="{$password_pattern}" required="required" autocomplete="off" /></dd>
................................................................................
        <input type="submit" id="f_submit" name="save" value="Terminer l'installation &rarr;" />
    </p>

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

    <script type="text/javascript">
    {literal}
    g.script('scripts/password.js', () => {
        initPasswordField('pw_suggest', 'f_passe_membre', 'f_repasse_membre');
    });

    var form = $('form')[0];
    form.onsubmit = function () {
        $('#f_submit').style.opacity = 0;
        var loader = document.createElement('div');
        loader.className = 'loader install';
        loader.innerHTML = '<b>Garradin est en cours d\'installation…</b>';
        $('#f_submit').parentNode.appendChild(loader);

Modified src/templates/admin/mes_infos_securite.tpl from [6a374e6bef] to [4acd33e052].

131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
            <input type="submit" name="save" value="Enregistrer &rarr;" />
        </p>

    </form>

    <script type="text/javascript">
    {literal}
    g.script('scripts/password.js').onload = function () {
        initPasswordField('pw_suggest', 'f_passe', 'f_passe_confirmed');
    };
    {/literal}
    </script>
{/if}

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







|
|
|





131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
            <input type="submit" name="save" value="Enregistrer &rarr;" />
        </p>

    </form>

    <script type="text/javascript">
    {literal}
    g.script('scripts/password.js', () => {
        initPasswordField('pw_suggest', 'f_passe', 'f_repasse');
    });
    {/literal}
    </script>
{/if}

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

Modified src/templates/admin/password_change.tpl from [bfd4bc96b0] to [6734a9400d].

27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
		{csrf_field key="changePassword"}
		<input type="submit" name="change" value="Modifier le mot de passe &rarr;" />
	</p>


	<script type="text/javascript">
	{literal}
	g.script('scripts/password.js').onload = function () {
		initPasswordField('pw_suggest', 'f_passe_membre', 'f_repasse_membre');
	};
	{/literal}
	</script>
</form>


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







|
|
|






27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
		{csrf_field key="changePassword"}
		<input type="submit" name="change" value="Modifier le mot de passe &rarr;" />
	</p>


	<script type="text/javascript">
	{literal}
	g.script('scripts/password.js', () => {
		initPasswordField('pw_suggest', 'f_passe', 'f_repasse');
	});
	{/literal}
	</script>
</form>


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

Modified src/www/admin/install.php from [016887ae94] to [d0c2a835bd].

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
else
{
    $tpl->assign('disabled', false);

    if (f('save'))
    {
        $form->check('install', [
            'nom_asso'     => 'required',
            'email_asso'   => 'required|email',
            'nom_membre'   => 'required',
            'email_membre' => 'required|email',
            'passe'        => 'confirmed|required',
            'cat_membre'   => 'required',
        ]);

        if (!$form->hasErrors())
        {
            try {
            	Install::install(f('nom_asso'), f('adresse_asso'), f('email_asso'),
            		f('cat_membre'), f('nom_membre'), f('email_membre'), f('passe'),
            		WWW_URL);

            	Utils::redirect(ADMIN_URL . 'login.php');
            }
            catch (UserException $e)
            {
                @unlink(DB_FILE);








<
<



<





<
|
<







26
27
28
29
30
31
32


33
34
35

36
37
38
39
40

41

42
43
44
45
46
47
48
else
{
    $tpl->assign('disabled', false);

    if (f('save'))
    {
        $form->check('install', [


            'nom_membre'   => 'required',
            'email_membre' => 'required|email',
            'passe'        => 'confirmed|required',

        ]);

        if (!$form->hasErrors())
        {
            try {

            	Install::install(f('nom_asso'), f('nom_membre'), f('email_membre'), f('passe'));


            	Utils::redirect(ADMIN_URL . 'login.php');
            }
            catch (UserException $e)
            {
                @unlink(DB_FILE);

Modified src/www/admin/static/scripts/global.js from [4d73132811] to [7e15ba263a].

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
...
284
285
286
287
288
289
290

291
292
293
294
295
296
297
				elm.onchange({target: elm});
			}
		}

		return true;
	};























	g.enhancePasswordField = function (field, repeat_field = null)
	{
		var show_password = document.createElement('input');
		show_password.type = 'button';
		show_password.className = 'icn action showPassword';
		show_password.title = 'Voir/cacher le mot de passe';
		show_password.value = '👁';





		show_password.onclick = function (e) {
			var pos = field.selectionStart;
			var hidden = field.type.match(/pass/i);
			field.type = hidden ? 'text' : 'password';
			this.value = !hidden ? '👁' : '⤫';
			field.classList.toggle('clearTextPassword');

			if (null !== repeat_field)
			{
				repeat_field.type = field.type;
				repeat_field.classList.toggle('clearTextPassword');
			}

			// Remettre le focus sur le champ mot de passe
			// on ne peut pas vraiment remettre le focus sur le champ
			// précis qui était utilisé avant de cliquer sur le bouton 
			// car il faudrait enregistrer les actions onfocus de tous
			// les champs de la page
			field.focus();
			field.selectionStart = field.selectionEnd = pos;
		};

		field.parentNode.insertBefore(show_password, field.nextSibling);
	};

	g.enhanceDateField = (input) => {
		var span = document.createElement('span');
		span.className = 'datepicker-parent';
		var btn = document.createElement('button');
		var cal = null;
................................................................................

	g.onload(() => {
		document.querySelectorAll('input[data-input="date"]').forEach((e) => {
			g.enhanceDateField(e);
		});
	});


	g.onload(function () {
		var tableActions = document.querySelectorAll('form table tfoot .actions select');

		for (var i = 0; i < tableActions.length; i++)
		{
			tableActions[i].onchange = function () {
				if (!this.form.querySelector('table tbody input[type=checkbox]:checked'))







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|

|

|
<
<
>
>
>
>
>


<
<
<
<

|
<
<
<
<



|





<
<







 







>







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
...
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
				elm.onchange({target: elm});
			}
		}

		return true;
	};

	g.togglePasswordVisibility = (field, repeat, show) => {
		if (typeof show == 'undefined') {
			show = field.type.toLowerCase() == 'password';
		}

		var btn = field.nextSibling;

		if (!btn) {
			throw Error('button not found');
		}

		field.type = show ? 'text' : 'password';
		btn.dataset.icon = !show ? '👁' : '⤫';
		btn.innerHTML = !show ? 'Voir le mot de passe' : 'Cacher le mot de passe';
		field.classList.toggle('clearTextPassword', !show);

		if (repeat) {
			repeat.type = field.type;
			repeat.classList.toggle('clearTextPassword', !show);
		}
	};

	g.enhancePasswordField = function (field, repeat_field)
	{
		var show_password = document.createElement('button');
		show_password.type = 'button';
		show_password.className = 'icn-btn';



		field.parentNode.insertBefore(show_password, field.nextSibling);

		g.togglePasswordVisibility(field, repeat_field, false);

		show_password.onclick = function (e) {
			var pos = field.selectionStart;





			g.togglePasswordVisibility(field, repeat_field);





			// Remettre le focus sur le champ mot de passe
			// on ne peut pas vraiment remettre le focus sur le champ
			// précis qui était utilisé avant de cliquer sur le bouton
			// car il faudrait enregistrer les actions onfocus de tous
			// les champs de la page
			field.focus();
			field.selectionStart = field.selectionEnd = pos;
		};


	};

	g.enhanceDateField = (input) => {
		var span = document.createElement('span');
		span.className = 'datepicker-parent';
		var btn = document.createElement('button');
		var cal = null;
................................................................................

	g.onload(() => {
		document.querySelectorAll('input[data-input="date"]').forEach((e) => {
			g.enhanceDateField(e);
		});
	});

	// To be able to select a whole table line just by clicking the row
	g.onload(function () {
		var tableActions = document.querySelectorAll('form table tfoot .actions select');

		for (var i = 0; i < tableActions.length; i++)
		{
			tableActions[i].onchange = function () {
				if (!this.form.querySelector('table tbody input[type=checkbox]:checked'))

Modified src/www/admin/static/scripts/password.js from [ae6eb9a86f] to [7acb018c50].

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
	{
		pw_elm = (typeof password == 'string') ? document.getElementById(password) : password;
		pw2_elm = (typeof password2 == 'string') ? document.getElementById(password2) : password2;
		suggest_elm = (typeof suggest == 'string') ? document.getElementById(suggest) : suggest;

		g.enhancePasswordField(pw_elm, pw2_elm);

		suggest_elm.size = suggest_elm.value.length;

		suggest_elm.onclick = function () {
	        pw_elm.value = this.value;
	        pw2_elm.value = this.value;
	        this.select();
	        checkPasswordStrength();
	        checkPasswordMatch();







|







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
	{
		pw_elm = (typeof password == 'string') ? document.getElementById(password) : password;
		pw2_elm = (typeof password2 == 'string') ? document.getElementById(password2) : password2;
		suggest_elm = (typeof suggest == 'string') ? document.getElementById(suggest) : suggest;

		g.enhancePasswordField(pw_elm, pw2_elm);

		suggest_elm.size = suggest_elm.value.length + 3;

		suggest_elm.onclick = function () {
	        pw_elm.value = this.value;
	        pw2_elm.value = this.value;
	        this.select();
	        checkPasswordStrength();
	        checkPasswordMatch();