* magic_quotes_gpc is now always _OFF_ (even if on in the php config, we revert the changes)
* new validation scheme, more robust, more complete, more powerfull, with comments support (to please OG) :
- only alias are in the new format.
- other validations are following.
* some corrections wrt {iterate}
* XOrgDB tweaks
git-archimport-id: opensource@polytechnique.org--2005/platal--mainline--0.9--patch-165
-[valid_alias]
-from="Equipe Polytechnique.org" <validation+melix@polytechnique.org>
-cc="Equipe Polytechnique.org" <validation+melix@polytechnique.org>
-
-[valid_emploi]
-from="Equipe Polytechnique.org" <validation+emploi@polytechnique.org>
-cc="Equipe Polytechnique.org" <validation+emploi@polytechnique.org>
-
[valid_epouses]
from="Equipe Polytechnique.org" <validation+epouse@polytechnique.org>
cc="Equipe Polytechnique.org" <validation+epouse@polytechnique.org>
if(Env::has('uid') && Env::has('type') && Env::has('stamp')) {
$req = Validate::get_request(Env::get('uid'), Env::get('type'), Env::get('stamp'));
- if($req) {
- $page->assign('mail', $req->handle_formu());
- }
+ if($req) { $req->handle_formu(); }
}
-$it = new ValidateIterator ();
-
-$valids = Array();
-while($valids[] = $it->next());
-array_pop($valids);
-
-$page->assign_by_ref('valids', $valids);
-
+$page->assign_by_ref('vit', new ValidateIterator ());
$page->run();
?>
// {{{ class Validate
/** classe "virtuelle" à dériver pour chaque nouvelle implémentation
- * XXX attention, dans l'implémentation de la classe, il ne faut jamais faire confiance au timestamp
- * de l'objet qui sort du BLOB de la BD, on met donc systématiquement le champt $this->stamp depuis
- * le TIMESTAMP de la BD
- * Par contre, à la sortie de toute fonction il faut que le stamp soit valide !!! XXX
*/
class Validate
{
// {{{ properties
- /** l'uid de la personne faisant la requête */
var $uid;
- /** le time stamp de la requête */
+ var $prenom;
+ var $nom;
+ var $promo;
+ var $bestalias;
+ var $forlife;
+
var $stamp;
- /** indique si la donnée est unique pour un utilisateur donné */
var $unique;
- /** donne le type de l'objet (certes redonant, mais plus pratique) */
var $type;
+ var $comments = Array();
// }}}
// {{{ constructor
* @param $_uid user id
* @param $_unique requête pouvant être multiple ou non
* @param $_type type de la donnée comme dans le champ type de x4dat.requests
- * @param $_stamp stamp de création, 0 si c'estun nouvel objet
*/
- function Validate($_uid, $_unique, $_type, $_stamp=0)
+ function Validate($_uid, $_unique, $_type)
{
- $this->uid = $_uid;
- $this->stamp = $_stamp;
+ global $globals;
+ $this->uid = $_uid;
+ $this->stamp = date('YmdHis');
$this->unique = $_unique;
- $this->type = $_type;
+ $this->type = $_type;
+ $res = $globals->xdb->query(
+ "SELECT u.prenom, u.nom, u.promo, a.alias, b.alias
+ FROM auth_user_md5 AS u
+ INNER JOIN aliases AS a ON ( u.user_id=a.id AND a.type='a_vie' )
+ INNER JOIN aliases AS b ON ( u.user_id=b.id AND b.type!='homonyme' AND FIND_IN_SET('bestalias', b.flags) )
+ WHERE u.user_id={?}", $_uid);
+ list($this->prenom, $this->nom, $this->promo, $this->forlife, $this->bestalias) = $res->fetchOneRow();
+ }
+
+ // }}}
+ // {{{ function submit()
+
+ /** fonction à utiliser pour envoyer les données à la modération
+ * cette fonction supprimme les doublons sur un couple ($user,$type) si $this->unique est vrai
+ */
+ function submit ()
+ {
+ global $globals;
+ if ($this->unique) {
+ $globals->xdb->execute('DELETE FROM requests WHERE user_id={?} AND type={?}', $this->uid, $this->type);
+ }
+
+ $this->stamp = date('YmdHis');
+ $globals->xdb->execute('INSERT INTO requests (user_id, type, data, stamp) VALUES ({?}, {?}, {?}, {?})',
+ $this->uid, $this->type, $this, $this->stamp);
+
+ return true;
+ }
+
+ // }}}
+ // {{{ function update()
+
+ function update ()
+ {
+ global $globals;
+ $globals->xdb->execute('UPDATE requests SET data={?}, stamp=stamp
+ WHERE user_id={?} AND type={?} AND stamp={?}',
+ $this, $this->uid, $this->type, $this->stamp);
+
+ return true;
+ }
+
+ // }}}
+ // {{{ function clean()
+
+ /** fonction à utiliser pour nettoyer l'entrée de la requête dans la table requests
+ * attention, tout est supprimé si c'est un unique
+ */
+ function clean ()
+ {
+ global $globals;
+ if ($this->unique) {
+ return $globals->xdb->execute('DELETE FROM requests WHERE user_id={?} AND type={?}',
+ $this->uid, $this->type);
+ } else {
+ return $globals->xdb->execute('DELETE FROM requests WHERE user_id={?} AND type={?} AND stamp={?}',
+ $this->uid, $this->type, $this->stamp);
+ }
}
+
+ // }}}
+ // {{{ function handle_formu()
+ /** fonction à réaliser en cas de valistion du formulaire
+ */
+ function handle_formu()
+ {
+ if (Env::has('delete')) {
+ $this->clean();
+ $this->trig('requete supprimée');
+ return true;
+ }
+
+ if (Env::has('hold') && Env::has('comm')) {
+ $this->comments[] = Array(Session::get('bestalias'), Env::get('comm'));
+ $this->update();
+ $this->trig('commentaire ajouté');
+ return true;
+ }
+
+ if (Env::has('accept')) {
+ if ($this->commit()) {
+ $this->sendmail(true);
+ $this->clean();
+ $this->trig('mail envoyé');
+ return true;
+ } else {
+ $this->trig('erreur lors de la validation');
+ return false;
+ }
+ }
+
+ if (Env::has('refuse')) {
+ $this->sendmail(false);
+ $this->clean();
+ $this->trig('mail envoyé');
+ return true;
+ }
+
+ return false;
+ }
+
// }}}
+ // {{{ function sendmail
+
+ function sendmail($isok)
+ {
+ global $globals;
+ require_once('diogenes.hermes.inc.php');
+ $mailer = new HermesMailer;
+ $mailer->setSubject($this->_mail_subj());
+ $mailer->setFrom("validations+{$this->type}@{$globals->mail->domain}");
+ $mailer->addTo("\"{$this->prenom} {$this->nom}\" <{$this->bestalias}@{$globals->mail->domain}>");
+ $mailer->addCc("validations+{$this->type}@{$globals->mail->domain}");
+
+ $body = "Cher(e) camarade,\n\n"
+ . $this->_mail_body($isok)
+ . (Env::has('comm') ? "\n\n".Env::get('comm') : '')
+ . "\n\nCordialement,\nL'équipe Polytechnique.org\n";
+
+ $mailer->setTxtBody(wordwrap($body));
+ $mailer->send();
+ }
+
+ // }}}
+ // {{{ function trig()
+
+ function trig($msg) {
+ global $page;
+ $page->trig($msg);
+ }
+
+ // }}}
+ // --- VIRTUAL FUNCTIONS ---
// {{{ function get_unique_request
/** fonction statique qui renvoie la requête dans le cas d'un objet unique de l'utilisateur d'id $uid
* XXX à dériver XXX
* à utiliser uniquement pour récupérer un objet <strong>unique</strong>
*/
- function get_unique_request($uid,$type)
+ function get_unique_request($uid, $type)
{
global $globals;
- $res = $globals->xdb->query('SELECT data,stamp FROM requests WHERE user_id={?} and type={?}', $uid, $type);
- if (list($result, $stamp) = $res->fetchOneRow()) {
+ $res = $globals->xdb->query('SELECT data FROM requests WHERE user_id={?} and type={?}', $uid, $type);
+ if ($result = $res->fetchOneCell()) {
$result = unserialize($result);
- // on ne fait <strong>jamais</strong> confiance au timestamp de l'objet,
- $result->stamp = $stamp;
if (!$result->unique) { // on vérifie que c'est tout de même bien un objet unique
$result = false;
}
global $globals;
$res = $globals->xdb->query("SELECT data, stamp FROM requests WHERE user_id={?} AND type={?} and stamp={?}",
$uid, $type, $stamp);
- if (list($result, $stamp) = $res->fetchOneRow()) {
+ if ($result = $res->fetchOneCell()) {
$result = unserialize($result);
- // on ne fait <strong>jamais</strong> confiance au timestamp de l'objet,
- $result->stamp = $stamp;
} else {
$result = false;
}
-
return($result);
}
// }}}
- // {{{ function submit()
+ // {{{ function _mail_body
- /** fonction à utiliser pour envoyer les données à la modération
- * cette fonction supprimme les doublons sur un couple ($user,$type) si $this->unique est vrai
- */
- function submit ()
+ function _mail_body($isok)
{
- global $globals;
- if ($this->unique) {
- $globals->xdb->execute('DELETE FROM requests WHERE user_id={?} AND type={?}', $this->uid, $this->type);
- }
-
- $globals->xdb->execute('INSERT INTO requests (user_id, type, data) VALUES ({?}, {?}, {?})',
- $this->uid, $this->type, $this);
-
- // au cas où l'objet est réutilisé après un commit, il faut mettre son stamp à jour
- $res = $globals->xdb->query('SELECT MAX(stamp) FROM requests WHERE user_id={?} AND type={?}', $this->uid, $this->type);
- $this->stamp = $res->fetchOneCell();
- return true;
}
-
- // }}}
- // {{{ function clean()
- /** fonction à utiliser pour nettoyer l'entrée de la requête dans la table requests
- * attention, tout est supprimé si c'est un unique
- */
- function clean ()
- {
- global $globals;
- if ($this->unique) {
- return $globals->xdb->execute('DELETE FROM requests WHERE user_id={?} AND type={?}',
- $this->uid, $this->type);
- } else {
- return $globals->xdb->execute('DELETE FROM requests WHERE user_id={?} AND type={?} AND stamp={?}',
- $this->uid, $this->type, $this->stamp);
- }
- }
-
// }}}
- // {{{ function formu()
-
- /** nom du template qui contient le formulaire */
- function formu()
- { return null; }
+ // {{{ function _mail_subj
- // }}}
- // {{{ function handle_formu()
+ function _mail_subj()
+ {
+ }
- /** fonction à réaliser en cas de valistion du formulaire
- * XXX la fonction est "virtuelle" XXX
- */
- function handle_formu()
- { }
-
// }}}
// {{{ function commit()
{ }
// }}}
+ // {{{ function formu()
+
+ /** nom du template qui contient le formulaire */
+ function formu()
+ { return null; }
+
+ // }}}
}
// }}}
// {{{ IMPLEMENTATIONS
-require_once("validations/aliases.inc.php");
-require_once("validations/epouses.inc.php");
-require_once("validations/photos.inc.php");
-require_once("validations/evts.inc.php");
-require_once("validations/listes.inc.php");
+foreach (glob(dirname(__FILE__).'/validations/*.inc.php') as $file) {
+ require_once($file);
+}
// }}}
var $alias;
var $raison;
- var $forlife;
- var $bestalias;
- var $prenom;
- var $nom;
var $old='';
// }}}
{
global $globals;
$this->Validate($_uid, true, 'alias', $_stamp);
- $this->alias = $_alias;
+ $this->alias = $_alias.'@'.$globals->mail->alias_dom;
$this->raison = $_raison;
$res = $globals->xdb->query("
- SELECT l.alias,m.alias,prenom,nom
- FROM auth_user_md5 AS u
- INNER JOIN aliases AS l ON (u.user_id=l.id AND l.type='a_vie')
- INNER JOIN aliases AS m ON (u.user_id=m.id AND FIND_IN_SET('bestalias',m.flags))
- WHERE user_id={?}", $this->uid);
- list($this->forlife,$this->bestalias,$this->prenom,$this->nom) = $res->fetchOneRow();
-
- $res = $globals->xdb->query("
SELECT v.alias
FROM virtual_redirect AS vr
INNER JOIN virtual AS v ON (v.vid=vr.vid AND v.alias LIKE '%@{$globals->mail->alias_dom}')
{ return 'include/form.valid.aliases.tpl'; }
// }}}
- // {{{ function handle_formu()
+ // {{{ function _mail_subj
- function handle_formu()
+ function _mail_subj()
{
- if (Env::get('submit') != "Accepter" && Env::get('submit') != "Refuser") {
- return false;
- }
+ return "[Polytechnique.org/MELIX] Demande de l'alias {$this->alias}";
+ }
- require_once("xorg.mailer.inc.php");
- $mymail = new XOrgMailer('valid.alias.tpl');
- $mymail->assign('alias', $this->alias);
- $mymail->assign('bestalias', $this->bestalias);
+ // }}}
+ // {{{ function _mail_body
- if (Env::get('submit') == "Accepter") {
- $mymail->assign('answer', 'yes');
- $this->commit() ;
+ function _mail_body($isok)
+ {
+ if ($isok) {
+ return " L'adresse mail {$this->alias} que tu avais demandée vient d'être créée, tu peux désormais l'utiliser à ta convenance.";
} else {
- $mymail->assign('answer', 'no');
- $mymail->assign('motif', stripslashes(Env::get('motif')));
+ return " La demande que tu avais faite pour l'alias {$this->alias} a été refusée.";
}
- $mymail->send();
- //Suppression de la demande
- $this->clean();
- return "Mail envoyé";
}
// }}}
global $globals;
if ($this->old) {
- $globals->xdb->execute('UPDATE virtual SET alias={?} WHERE alias={?}',
- $this->alias.'@'.$globals->mail->alias_dom, $this->old);
+ return $globals->xdb->execute('UPDATE virtual SET alias={?} WHERE alias={?}', $this->alias, $this->old);
} else {
- $globals->xdb->execute('INSERT INTO virtual SET alias={?},type="user"',
- $this->alias.'@'.$globals->mail->alias_dom);
+ $globals->xdb->execute('INSERT INTO virtual SET alias={?},type="user"', $this->alias);
$vid = mysql_insert_id();
- require_once('emails.inc.php');
$dom = $globals->mail->shorter_domain();
- $globals->xdb->query('INSERT INTO virtual_redirect (vid,redirect) VALUES ({?}, {?})', $vid, $this->forlife.'@'.$dom);
+ return $globals->xdb->query('INSERT INTO virtual_redirect (vid,redirect) VALUES ({?}, {?})', $vid, $this->forlife.'@'.$dom);
}
}
var $epouse;
var $alias = '';
- var $forlife;
var $oldepouse;
var $oldalias;
- var $prenom;
- var $nom;
var $homonyme;
$this->epouse = $_epouse;
$this->forlife = $_forlife;
list($prenom) = explode('.',$_forlife);
- $this->alias = make_username($prenom,$this->epouse);
+ $this->alias = make_username($prenom, $this->epouse);
$sql = $globals->xdb->query("
- SELECT e.alias, u.epouse, u.prenom, u.nom, a.id
+ SELECT e.alias, u.epouse, a.id
FROM auth_user_md5 as u
LEFT JOIN aliases as e ON(e.type='alias' AND FIND_IN_SET('epouse',e.flags) AND e.id = u.user_id)
LEFT JOIN aliases as a ON(a.alias = {?} AND a.id != u.user_id)
WHERE u.user_id = {?}", $this->alias, $this->uid);
- list($this->oldalias, $this->oldepouse, $this->prenom, $this->nom, $this->homonyme) = $res->fetchOneRow();
+ list($this->oldalias, $this->oldepouse, $this->homonyme) = $res->fetchOneRow();
}
// }}}
var $peremption;
var $comment;
- var $bestalias;
- var $promo;
- var $nom;
- var $prenom;
-
// }}}
// {{{ constructor
$this->pmax = $_pmax;
$this->peremption = $_peremption;
$this->comment = $_comment;
- $res = $globals->xdb->query("
- SELECT a.alias,promo,nom,prenom
- FROM auth_user_md5 AS u
- INNER JOIN aliases AS a ON ( u.user_id=a.id AND FIND_IN_SET('bestalias',a.flags))
- WHERE user_id={?}", $_uid);
- list($this->bestalias,$this->promo,$this->nom,$this->prenom) = $res->fetchOneRow();
}
// }}}
{
// {{{ properties
- var $bestalias;
var $liste;
var $desc;
$this->inslevel = $_inslevel;
$this->owners = $_owners;
$this->members = $_members;
-
- $res = $globals->xdb->query("
- SELECT l.alias
- FROM auth_user_md5 AS u
- INNER JOIN aliases AS l ON (u.user_id=l.id AND FIND_IN_SET('bestalias',l.flags))
- WHERE user_id={?}", $this->uid);
- $this->bestalias = $res->fetchOneCell();
}
// }}}
var $x;
var $y;
- var $bestalias;
- var $prenom;
- var $nom;
-
// }}}
// {{{ constructor
global $erreur, $globals;
$this->Validate($_uid, true, 'photo', $_stamp);
- $res = $globals->xdb->query("
- SELECT a.alias, prenom, nom
- FROM auth_user_md5 AS u
- INNER JOIN aliases AS a ON ( a.id=u.user_id AND FIND_IN_SET('bestalias',a.flags) )
- WHERE user_id={?}", $this->uid);
- list($this->bestalias,$this->prenom,$this->nom) = $res->fetchOneRow();
if (!file_exists($_file)) {
$erreur = "Fichier inexistant";
XorgSession::init();
// }}}
+// {{{ fix magic quotes
+
+function fix_gpc_magic(&$item, $key) {
+ if (is_array($item)) {
+ array_walk($item, 'fix_gpc_magic');
+ } else {
+ $item = stripslashes($item);
+ }
+}
+
+if (ini_get("magic_quotes_gpc")) {
+ array_walk($_GET, 'fix_gpc_magic');
+ array_walk($_POST, 'fix_gpc_magic');
+ array_walk($_COOKIE, 'fix_gpc_magic');
+ array_walk($_REQUEST, 'fix_gpc_magic');
+}
+
+// }}}
// {{{ function _new_page()
function _new_page($type, $tpl_name, $tpl_head, $min_auth, $admin=false)
return $var;
case 'string':
- if (get_magic_quotes_gpc()) {
- return "'".addslashes(stripslashes($var))."'";
- } else {
- return "'".addslashes($var)."'";
- }
+ return "'".addslashes($var)."'";
case 'NULL':
return 'NULL';
$_from = $compiler->_dequote($_params['from']);
$_item = $compiler->_dequote($_params['item']);
- return "while ((\$this->_tpl_vars['$_item'] =& \$this->_tpl_vars['$_from']->next()) !== null):";
+ return "\$_iterate_$_item = $_from;\n"
+ . "while ((\$this->_tpl_vars['$_item'] = \$_iterate_{$_item}->next()) !== null):";
}
/* vim: set expandtab: */
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
***************************************************************************}
-
+<h1>Validation</h1>
+
{dynamic}
-{$mail}
-{foreach item=valid from=$valids}
+
+{if $vit->total()}
+
+{iterate item=valid from=$vit|smarty:nodefaults}
<br />
-{include file=$valid->formu() valid=$valid}
-{/foreach}
+<form action="{$smarty.server.PHP_SELF}" method="post">
+ <table class="bicol">
+ <tr>
+ <th colspan="2">{$valid->type}</th>
+ </tr>
+ <tr>
+ <td class="titre" style="width: 20%">Demandeur :</td>
+ <td>
+ <a href="{rel}/fiche.php?user={$valid->bestalias}" class="popup2">
+ {$valid->prenom} {$valid->nom} (X{$valid->promo})
+ </a>
+ </td>
+ </tr>
+ {include file=$valid->formu()}
+ {if $valid->comments}
+ <tr><th colspan='2'>Commentaires</th></tr>
+ {/if}
+ {foreach from=$valid->comments item=c}
+ <tr class="{cycle values="impair,pair"}">
+ <td class="titre">
+ <a href="{rel}/fiche.php?user={$c[0]}" class="popup2">{$c[0]}</a>
+ </td>
+ <td>{$c[1]}</td>
+ </tr>
+ {/foreach}
+ <tr>
+ <td colspan='2' class='center'>
+ Commentaire:<br />
+ <textarea rows="5" cols="50" name="comm"></textarea><br />
+
+ <input type="hidden" name="uid" value="{$valid->uid}" />
+ <input type="hidden" name="type" value="{$valid->type}" />
+ <input type="hidden" name="stamp" value="{$valid->stamp}" />
+ <input type="submit" name="accept" value="Accepter" />
+ <input type="submit" name="hold" value="Commenter" />
+ <input type="submit" name="refuse" value="Refuser" />
+ <input type="submit" name="delete" value="Supprimer" />
+ </td>
+ </tr>
+ </table>
+</form>
+{/iterate}
+
+{else}
+
+<p>Rien à valider</p>
+
+{/if}
+
{/dynamic}
{* vim:set et sw=2 sts=2 sws=2: *}
<br />
<div class="contact-list">
-{iterate from=citer item=contact}
+{iterate from=$citer item=contact}
{include file=include/minifiche.tpl c=$contact show_action="retirer"}
{/iterate}
</div>
***************************************************************************}
-<form action="{$smarty.server.PHP_SELF}" method="post">
- <table class="bicol" cellpadding="4" summary="Demande d'alias">
- <tr>
- <td>Demandeur :
- </td>
- <td>
- <a href="{"fiche.php"|url}?user={$valid->bestalias}" class='popup2'>
- {$valid->prenom} {$valid->nom}</a> {$valid->old}
- </td>
- </tr>
- <tr>
- <td>Nouvel alias :</td>
- <td>{$valid->alias}@{#globals.mail.alias_dom#}</td>
- </tr>
- <tr>
- <td>Motif :</td>
- <td style="border: 1px dotted inherit">
- {$valid->raison|nl2br}
- </td>
- </tr>
- <tr>
- <td class="middle">
- <input type="hidden" name="uid" value="{$valid->uid}" />
- <input type="hidden" name="type" value="{$valid->type}" />
- <input type="hidden" name="stamp" value="{$valid->stamp}" />
- <input type="submit" name="submit" value="Accepter" />
- <br /><br />
- <input type="submit" name="submit" value="Refuser" />
- </td>
- <td>
- <p>Raison du refus:</p>
- <textarea rows="5" cols="50" name="motif"></textarea>
- </td>
- </tr>
- </table>
-</form>
+<tr class="pair">
+ <td class="titre">Ancien alias</td>
+ <td>{$valid->old|default:"-none-"}</td>
+</tr>
+<tr class="pair">
+ <td class="titre">Nouvel alias :</td>
+ <td>{$valid->alias}</td>
+</tr>
+<tr class="pair">
+ <td class="titre">Motif :</td>
+ <td>{$valid->raison|nl2br}</td>
+</tr>
{* vim:set et sw=2 sts=2 sws=2: *}
+++ /dev/null
-{***************************************************************************
- * Copyright (C) 2003-2004 Polytechnique.org *
- * http://opensource.polytechnique.org/ *
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- * *
- * This program is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU General Public License for more details. *
- * *
- * You should have received a copy of the GNU General Public License *
- * along with this program; if not, write to the Free Software *
- * Foundation, Inc., *
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
- ***************************************************************************}
-
-{config_load file="mails.conf" section="valid_alias"}
-{subject text="[Polytechnique.org/MELIX] Demande de l'alias $alias@melix.net par $bestalias"}
-{from full=#from#}
-{to addr="$bestalias@polytechnique.org"}
-{cc full=#cc#}
-{if $answer eq "yes"}
-Cher(e) camarade,
-
- Les adresses e-mail {$alias}@melix.net et {$alias}@melix.org que tu avais demandées viennent d'être créées, tu peux désormais les utiliser à ta convenance.
-
-Cordialement,
-L'équipe X.org
-{elseif $answer eq 'no'}
-
-Cher(e) camarade,
-
- La demande que tu avais faite pour les alias {$alias}@melix.net et {$alias}@melix.org a été refusée pour la raison suivante :
-{$motif|stripslashes}
-
-Cordialement,
-L'équipe X.org
-{/if}
-{* vim:set et sw=2 sts=2 sws=2: *}