Refactoring of the wizard based profile edit with a new structure that
authorFlorent Bruneau <florent.bruneau@polytechnique.org>
Sun, 26 Aug 2007 09:31:40 +0000 (11:31 +0200)
committerFlorent Bruneau <florent.bruneau@polytechnique.org>
Sun, 26 Aug 2007 09:31:40 +0000 (11:31 +0200)
handle data changes and page switch

Signed-off-by: Florent Bruneau <florent.bruneau@polytechnique.org>
classes/plwizard.php
include/xorg/session.inc.php
modules/profile/general.inc.php [new file with mode: 0644]
modules/profile/page.inc.php
templates/core/plwizard.tpl
templates/profile/base.tpl [new file with mode: 0644]
templates/profile/general.tpl

index 7b6721d..23a47e1 100644 (file)
@@ -71,6 +71,7 @@ class PlWizard
     protected $pages;
     protected $titles;
     protected $lookup;
+    protected $inv_lookup;
 
     public function __construct($name, $layout, $stateless = false)
     {
@@ -82,18 +83,20 @@ class PlWizard
         $this->titles = array();
         if (!isset($_SESSION[$this->name])) {
             $_SESSION[$this->name] = array();
+            $_SESSION[$this->name . '_page']  = null;
+            $_SESSION[$this->name . '_stack'] = array();
         }
-        $_SESSION[$this->name . '_page']  = null;
-        $_SESSION[$this->name . '_stack'] = array();
     }
 
-    public function addPage($class, $title,$id = null)
+    public function addPage($class, $title, $id = null)
     {
-        if ($id != null) {
-            $this->lookup[$id] = count($this->pages);
+        if ($id == null) {
+            $id = count($this->pages);
         }
-        $this->pages[]  = $class;
-        $this->titles[] = $title;
+        $this->lookup[$id]  = count($this->pages);
+        $this->inv_lookup[] = $id;
+        $this->pages[]      = $class;
+        $this->titles[]     = $title;
     }
 
     public function set($varname, $value)
@@ -135,7 +138,14 @@ class PlWizard
 
     public function apply(PlatalPage &$smarty, $baseurl, $pgid = null)
     {
-        $curpage =& $_SESSION[$this->name . '_page'];
+        if ($this->stateless && (isset($this->lookup[$pgid]) || isset($this->pages[$pgid]))) { 
+            $curpage = is_numeric($pgid) ? $pgid : $this->lookup[$pgid]; 
+        } else if ($this->stateless && is_null($pgid)) {
+            $curpage = 0;
+        } else {
+            $curpage = $_SESSION[$this->name . '_page'];
+        }
+        $oldpage = $curpage;
 
         // Process the previous page
         if (!is_null($curpage)) {
@@ -165,25 +175,28 @@ class PlWizard
                 break;
               case PlWizard::CURRENT_PAGE: break; // don't change the page
               default:
-                $curpage = is_numeric($next) ? $next : $this->lookup[$curpage];
+                $curpage = is_numeric($next) ? $next : $this->lookup[$next];
                 break;
             }
             if (!$this->stateless) {
                 array_push($_SESSION[$this->name . '_stack'], $last);
             }
         }
-        if ($this->stateless && (in_array($pgid, $this->lookup) || isset($this->pages[$pgid]))) {
-            $curpage = $pgid;
-        }
         if (is_null($curpage)) {
             $curpage = 0;
         }
 
         // Prepare the page
-        $page = $this->getPage($curpage);
+        $_SESSION[$this->name . '_page'] = $curpage;
+        if ($curpage != $oldpage) {
+            pl_redirect($baseurl . '/' . $this->inv_lookup[$curpage]);
+        } else if (!isset($page)) {
+            $page = $this->getPage($curpage);
+        }
         $smarty->changeTpl($this->layout);
         $smarty->assign('pages', $this->titles);
         $smarty->assign('current', $curpage);
+        $smarty->assign('lookup', $this->inv_lookup);
         $smarty->assign('stateless', $this->stateless);
         $smarty->assign('wiz_baseurl', $baseurl);
         $smarty->assign('tab_width', (int)(99 / count($this->pages)));
index 265d2c0..a0467b9 100644 (file)
@@ -239,7 +239,8 @@ function try_cookie()
 function start_connexion ($uid, $identified)
 {
     $res  = XDB::query("
-        SELECT  u.user_id AS uid, prenom, nom, nom_usage, perms, promo, matricule, password, FIND_IN_SET('femme', u.flags) AS femme,
+        SELECT  u.user_id AS uid, prenom, prenom_ini, nom, nom_ini, nom_usage, perms, promo, promo_sortie,
+                matricule, password, FIND_IN_SET('femme', u.flags) AS femme,
                 UNIX_TIMESTAMP(s.start) AS lastlogin, s.host, a.alias AS forlife, a2.alias AS bestalias,
                 q.core_mail_fmt AS mail_fmt, UNIX_TIMESTAMP(q.banana_last) AS banana_last, q.watch_last, q.core_rss_hash,
                 FIND_IN_SET('watch', u.flags) AS watch_account, q.last_version
diff --git a/modules/profile/general.inc.php b/modules/profile/general.inc.php
new file mode 100644 (file)
index 0000000..7d5c873
--- /dev/null
@@ -0,0 +1,89 @@
+<?php
+/***************************************************************************
+ *  Copyright (C) 2003-2007 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                *
+ ***************************************************************************/
+
+class ProfileNom implements ProfileSetting
+{
+    private function matchWord($old, $new, $newLen) {
+        return ($i = strpos($ancien, $nouveau)) !== false
+            && ($i == 0 || $old{$i-1} == ' ')
+            && ($i + $newLen == strlen($old) || $old{$i + $newLen} == ' ');
+    }
+
+    private function prepareField($value)
+    {
+        $value = strtoupper(replace_accent($value));
+        return preg_replace('/[^A-Z]/', ' ', $value);
+    }
+
+    public function value(ProfilePage &$page, $field, $value, &$success)
+    {
+        $success = true;
+        $current = S::v($field);
+        $init    = S::v($field . '_ini');
+        if (is_null($value)) {
+            return $current;
+        }
+        if ($value == $current || $value == $init) {
+            return $value;
+        }
+        $ini = $this->prepareField($init);
+        $old = $this->prepareField($current);
+        $new = $this->prepareField($value);
+        $newLen = strlen($new);
+        $success = $this->matchWord($old, $new, $newLen)
+                || $this->matchWord($ini, $new, $newLen);
+        if (!$success) {
+            global $page;
+            $page->trig("Le $field que tu as choisi ($value) est trop loin de ton $field initial ($init)"
+                       . (($init == $current)? "" : " et de ton prénom précédent ($current)"));
+        }
+        return $success ? $value : $current;
+    }
+
+    public function save(ProfilePage &$page, $field, $new_value)
+    {
+        $_SESSION[$field] = $new_value;
+    }
+}
+
+class ProfileGeneral extends ProfilePage
+{
+    protected $pg_template = 'profile/general.tpl';
+
+    public function __construct(PlWizard &$wiz)
+    {
+        parent::__construct($wiz);
+        $this->settings['nom'] = $this->settings['prenom']
+                               = new ProfileNom();
+        $this->settings['promo']  = $this->settings['promo_sortie']
+                                  = $this->settings['nom_usage']
+                                  = new ProfileFixed();
+    }
+
+    public function prepare(PlatalPage &$page)
+    {
+        parent::prepare($page);
+        require_once "applis.func.inc.php";
+    }
+}
+
+// vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
+?>
index a4b2d3c..f7e2f3e 100644 (file)
  *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA                *
  ***************************************************************************/
 
-class ProfileGeneral implements PlWizardPage
+interface ProfileSetting
+{
+    /** Get a field and a value, check that the given value is
+     * valid, if not, return a corrected value. If no valid value can be
+     * computed from the input data, the success flag is set to false.
+     *
+     * If value is null, the default value should be returned.
+     * TODO: check this does not conflict with some possible values.
+     *
+     * Whatever happen, this function must always returns the function to
+     * show on the page to the user.
+     */
+    public function value(ProfilePage &$page, $field, $value, &$success);
+
+    /** Save the new value for the given field.
+     */
+    public function save(ProfilePage &$page, $field, $new_value);
+}
+
+abstract class ProfileNoSave implements ProfileSetting
+{
+    public function save(ProfilePage &$page, $field, $new_value) { }
+}
+
+class ProfileFixed extends ProfileNoSave
+{
+    public function value(ProfilePage &$page, $field, $value, &$success)
+    {
+        $success = true;
+        return isset($page->values[$field]) ? $page->values[$field] : S::v($field);
+    }
+}
+
+class ProfileWeb extends ProfileNoSave
+{
+    public function value(ProfilePage &$page, $field, $value, &$success)
+    {
+        if (is_null($value)) {
+            return isset($page->values[$field]) ? $page->values[$field] : S::v($field);
+        }
+        $success = preg_match("{^(https?|ftp)://[a-zA-Z0-9._%#+/?=&~-]+$}i", $value);
+        if (!$success) {
+            global $page;
+            $page->trig('URL Incorrecte : une url doit commencer par http:// ou https:// ou ftp://'
+                      . ' et ne pas contenir de caractères interdits');
+        }
+        return $value;
+    }
+}
+
+class ProfileTel extends ProfileNoSave
+{
+    public function value(ProfilePage &$page, $field, $value, &$success)
+    {
+        if (is_null($value)) {
+            return isset($page->values[$field]) ? $page->values[$field] : S::v($field);
+        }
+        $success = strlen(strtok($value, '<>{}@&#~\/:;?,!§*_`[]|%$^=')) < strlen($value);
+        if (!$success) {
+            global $page;
+            $page->trig('Le numéro de téléphone contient un caractère interdit.');
+        }
+        return $value;
+    }
+}
+
+abstract class ProfilePage implements PlWizardPage
 {
     protected $wizard;
+    protected $pg_template;
+    protected $settings = array();  // A set ProfileSetting objects
+
+    public $values   = array();
 
     public function __construct(PlWizard &$wiz)
     {
         $this->wizard =& $wiz;
     }
 
+    protected function fetchData()
+    {
+    }
+
+    protected function saveData()
+    {
+    }
+
     public function template()
     {
-        return 'profile/general.tpl';
+        return 'profile/base.tpl';
     }
 
     public function prepare(PlatalPage &$page)
     {
+        if (count($this->values) == 0) {
+            $this->fetchData();
+            foreach ($this->settings as $field=>&$setting) {
+                $success = false;
+                $this->values[$field] = $setting->value($this, $field, null, $success);
+            }
+        }
+        foreach ($this->values as $field=>&$value) {
+            $page->assign($field, $value);
+        }
+        $page->assign('profile_page', $this->pg_template);
     }
 
     public function process()
     {
+        $global_success = true;
+        $this->fetchData();
+        foreach ($this->settings as $field=>&$setting) {
+            $success = false;
+            $this->values[$field] = $setting->value($this, $field, Post::v($field), $success);
+            $global_success = $global_success && $success;
+        }
+        if ($global_success) {
+            foreach ($this->settings as $field=>&$setting) {
+                $setting->save($this, $field, $this->values[$field]);
+            }
+            $this->saveData();
+            return Post::has('valid_and_next') ? PlWizard::NEXT_PAGE : PlWizard::CURRENT_PAGE;
+        }
+        global $page;
+        $page->trig("Certains champs n'ont pas pu être validés, merci de corriger les infos "
+                  . "de ton profil et de revalider ta demande");
         return PlWizard::CURRENT_PAGE;
     }
 }
 
+require_once dirname(__FILE__) . '/general.inc.php';
 
 // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
 ?>
index 808e7c7..1ea6727 100644 (file)
     {foreach from=$pages item=title key=id}
     <div class="wiz_tab {if $id eq $current}active{/if} {if !$stateless && $current gt $id}grayed{/if}"
          style="float: left; width: {$tab_width}%">
-      {if $stateless || $id gt $current}<a href="{$wiz_baseurl}/{$id}">{/if}
+      {if $stateless || $id gt $current}<a href="{$wiz_baseurl}/{$lookup[$id]}">{/if}
       {$title}
       {if $stateless || $id gt $current}</a>{/if}
     </div>
     {/foreach}
     <div style="clear: both"></div>
   </div>
-  <div class="wiz_content" style="clear: both">
+  <div class="wiz_content" style="clear: both" class="center">
     {include file=$wiz_page}
   </div>
 </div>
diff --git a/templates/profile/base.tpl b/templates/profile/base.tpl
new file mode 100644 (file)
index 0000000..765205f
--- /dev/null
@@ -0,0 +1,33 @@
+{**************************************************************************}
+{*                                                                        *}
+{*  Copyright (C) 2003-2007 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               *}
+{*                                                                        *}
+{**************************************************************************}
+
+<form action="{$wiz_baseurl}/{$lookup[$current]}" method="post">
+  <div>
+    {include file=$profile_page}
+  </div>
+  <div style="clear: both">
+    <input type="submit" name="valid" value="Valider les modifications" />
+    <input type="submit" name="valid_and_next" value="Valider et passer à la page suivante" />
+  </div>
+</form>
+
+{* vim:set et sw=2 sts=2 sws=2 enc=utf-8: *}
index bd754c6..b515d4d 100644 (file)
@@ -21,7 +21,7 @@
 {**************************************************************************}
 
 
-{* include file="profile/applis.js.tpl" *}
+{include file="profile/applis.js.tpl"}
 <div class="blocunite_tab">
   <table class="bicol" cellspacing="0" cellpadding="0" 
     summary="Profil : Informations générales">