Replace sectors by job terms in profile and search (job and mentoring).
authorPascal Corpet <pascal.corpet@m4x.org>
Sun, 25 Jul 2010 19:26:59 +0000 (21:26 +0200)
committerPascal Corpet <pascal.corpet@m4x.org>
Sun, 29 Aug 2010 22:16:46 +0000 (00:16 +0200)
25 files changed:
classes/direnum.php
classes/jobterms.php [new file with mode: 0644]
classes/profile.php
classes/userfilter.php
htdocs/css/default.css
htdocs/css/jstree.css [new file with mode: 0644]
htdocs/images/jstree.png [new file with mode: 0644]
htdocs/javascript/jobtermstree.js [new file with mode: 0644]
htdocs/javascript/profile.js
include/profil.func.inc.php [deleted file]
include/profilefields.inc.php
include/ufbuilder.inc.php
modules/profile.php
modules/profile/jobs.inc.php
modules/profile/mentor.inc.php
modules/search.php
templates/include/emploi.tpl
templates/include/jobterms.branch.tpl [new file with mode: 0644]
templates/profile/fiche_referent.tpl
templates/profile/jobs.job.tpl
templates/profile/jobs.tpl
templates/profile/mentor.tpl
templates/profile/referent.tpl
templates/search/adv.form.tpl
upgrade/1.0.1/tokenize_job_terms.php

index 8415ee3..1382019 100644 (file)
@@ -51,6 +51,7 @@ class DirEnum
     const COMPANIES      = 'companies';
     const SECTORS        = 'sectors';
     const JOBDESCRIPTION = 'jobdescription';
+    const JOBTERMS       = 'jobterms';
 
     const NETWORKS       = 'networking';
 
@@ -646,6 +647,30 @@ class DE_JobDescription extends DirEnumeration
 }
 // }}}
 
+// {{{ class DE_JobTerms
+class DE_JobTerms extends DirEnumeration
+{
+    // {{{ function getAutoComplete
+    public function getAutoComplete($text)
+    {
+        $tokens = JobTerms::tokenize($text.'%');
+        if (count($tokens) == 0) {
+            return PlIteratorUtils::fromArray(array());
+        }
+        $token_join = JobTerms::token_join_query($tokens, 'e');
+        return XDB::iterator('SELECT  e.jtid AS id, e.full_name AS field, COUNT(DISTINCT p.pid) AS nb
+                                 FROM  profile_job_term_enum AS e
+                           INNER JOIN  profile_job_term_relation AS r ON (r.jtid_1 = e.jtid)
+                           INNER JOIN  profile_job_term AS p ON (r.jtid_2 = p.jtid)
+                           '.$token_join.'
+                             GROUP BY  e.jtid
+                             ORDER BY  nb DESC, field
+                                LIMIT ' . self::AUTOCOMPLETE_LIMIT);
+    }
+    // }}}
+}
+// }}}
+
 /** NETWORKING
  */
 // {{{ class DE_Networking
diff --git a/classes/jobterms.php b/classes/jobterms.php
new file mode 100644 (file)
index 0000000..460b74e
--- /dev/null
@@ -0,0 +1,144 @@
+<?php
+/***************************************************************************
+ *  Copyright (C) 2003-2010 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 JobTerms {
+
+    const ALL = 'all';
+    const ONLY_JOBS = 'jobs';
+    const ONLY_MENTORS = 'mentors';
+
+    static public function getSubTerms($parent_jtid, $filter = self::ALL) {
+        switch ($filter) {
+          case self::ALL:
+          default:
+            $table = '';
+            break;
+          case self::ONLY_JOBS:
+            $table = 'profile_job_term';
+            break;
+          case self::ONLY_MENTORS:
+            $table = 'profile_mentor_term';
+            break;
+        }
+        if ($table) {
+            $count = ', COUNT(DISTINCT j.pid) AS nb';
+            $join = ' INNER JOIN  profile_job_term_relation AS r2 ON (r2.jtid_1 = e.jtid)
+                      INNER JOIN  '.$table.' AS j ON (j.jtid = r2.jtid_2)';
+        } else {
+            $count = $join = '';
+        }
+        return XDB::iterator('SELECT  e.jtid, e.name, e.full_name'.$count.'
+                                FROM  profile_job_term_enum AS e
+                          INNER JOIN  profile_job_term_relation AS r ON (r.jtid_2 = e.jtid)'.$join.'
+                               WHERE  r.jtid_1 = {?} AND r.computed = "original"
+                            GROUP BY  e.jtid
+                            ORDER BY  e.name',
+                       $parent_jtid);
+    }
+
+    /**
+     * Display a JSon page containing the sub-branches of a branch in the job terms tree.
+     * @param $page the Platal page
+     * @param $filter filter helps to display only jobterms that are contained in jobs or in mentors
+     *
+     * @param Env::i('jtid') job term id of the parent branch, if none trunk will be used
+     * @param Env::v('attrfunc') the name of a javascript function that will be called when a branch
+     * is chosen
+     * @param Env::v('treeid') tree id that will be given as first argument of attrfunc function
+     * the second argument will be the chosen job term id and the third one the chosen job's full name.
+     */
+    static public function ajaxGetBranch(&$page, $filter = self::ALL) {
+        pl_content_headers('application/json');
+        $page->changeTpl('include/jobterms.branch.tpl', NO_SKIN);
+        $subTerms = self::getSubTerms(Env::v('jtid'), $filter);
+        $page->assign('subTerms', $subTerms);
+        switch ($filter) {
+          case self::ONLY_JOBS:
+            $page->assign('filter', 'camarade');
+            break;
+          case self::ONLY_MENTORS:
+            $page->assign('filter', 'mentor');
+            break;
+        }
+        $page->assign('attrfunc', Env::v('attrfunc'));
+        $page->assign('treeid', Env::v('treeid'));
+    }
+
+    static public function jsAddTree($platalpage, $domElement = '.term_tree', $treeid = '', $attrfunc = '') {
+        return '$("'.addslashes($domElement).'").jstree({
+            "core" : {"strings":{"loading":"Chargement ..."}},
+            "plugins" : ["themes","json_data"],
+            "themes" : { "url" : platal_baseurl + "css/jstree.css" },
+            "json_data" : { "ajax" : {
+                "url" : platal_baseurl + "'.addslashes($platalpage).'",
+                "data" : function(nod) {
+                    var jtid = 0;
+                    if (nod != -1) {
+                        jtid = nod.attr("id").replace(/^.*_([0-9]+)$/, "$1");
+                    }
+                    return { "jtid" : jtid, "treeid" : "'.addslashes($treeid).'", "attrfunc" : "'.addslashes($attrfunc).'" }
+                }
+            }} });';
+    }
+
+    /**
+     * Extract search token from term
+     * @param $term a utf-8 string that can contain any char
+     * @param an array of elementary tokens
+     */
+    static public function tokenize($term)
+    {
+        $term = mb_strtoupper(replace_accent($term));
+        $term = str_replace(array('/', ',', '(', ')', '"', '&', '»', '«'), ' ', $term);
+        $tokens = explode(' ', $term);
+        static $not_tokens = array('ET','AND','DE','DES','DU','D\'','OU','L\'','LA','LE','LES','PAR','AU','AUX','EN','SUR','UN','UNE','IN');
+        foreach ($tokens as &$t) {
+            $t = preg_replace(array('/^-+/','/-+$/'), '', $t);
+            if (substr($t, 1, 1) == '\'' && in_array(substr($t, 0, 2), $not_tokens)) {
+                $t = substr($t, 2);
+            }
+            if (strlen($t) == 1 || in_array($t, $not_tokens)) {
+                $t = false;
+                continue;
+            }
+        }
+        return array_filter($tokens);
+    }
+
+    /**
+     * Create the INNER JOIN query to restrict search to some job terms
+     * @param $tokens an array of the job terms to look for (LIKE comp)
+     * @param $table_alias the alias or name of the table with a jtid field to restrict
+     * @return a partial SQL query
+     */
+    static public function token_join_query(array $tokens, $table_alias) {
+        $joins = '';
+        $i = 0;
+        foreach ($tokens as $t) {
+            ++$i;
+             $joins .= ' INNER JOIN  profile_job_term_search AS s'.$i.' ON(s'.$i.'.jtid = '.$table_alias.'.jtid AND s'.$i.'.search LIKE '.XDB::escape($t).')';
+        }
+        return $joins;
+    }
+}
+
+// vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
+?>
index 10d625b..a747063 100644 (file)
@@ -155,10 +155,12 @@ class Profile
     const FETCH_MENTOR_SECTOR  = 0x000040;
     const FETCH_MENTOR_COUNTRY = 0x000080;
     const FETCH_PHONES         = 0x000100;
+    const FETCH_JOB_TERMS      = 0x000200;
+    const FETCH_MENTOR_TERMS   = 0x000400;
 
     const FETCH_MINIFICHES   = 0x00012D; // FETCH_ADDRESSES | FETCH_EDU | FETCH_JOBS | FETCH_NETWORKING | FETCH_PHONES
 
-    const FETCH_ALL          = 0x0001FF; // OR of FETCH_*
+    const FETCH_ALL          = 0x0007FF; // OR of FETCH_*
 
     private $fetched_fields  = 0x000000;
 
@@ -481,8 +483,8 @@ class Profile
 
     private function fetched($field)
     {
-        if (!array_key_exists($field, ProfileField::$fields)) {
-            Platal::page()->kill("Invalid field: $field");
+        if (($fields | self::FETCH_ALL) != self::FETCH_ALL) {
+            Platal::page()->kill("Invalid fetched fields: $fields");
         }
 
         return ($this->fetched_fields & $field);
@@ -494,6 +496,9 @@ class Profile
      */
     private function getProfileField($field)
     {
+        if (!array_key_exists($field, ProfileField::$fields)) {
+            Platal::page()->kill("Invalid field: $field");
+        }
         if ($this->fetched($field)) {
             return null;
         } else {
@@ -522,6 +527,9 @@ class Profile
         if ($this->addresses != null && $this->jobs != null) {
             $this->jobs->addAddresses($this->addresses);
         }
+        if ($this->jobs != null && $this->jobterms != null) {
+            $this->jobs->addJobTerms($this->jobterms);
+        }
     }
 
     /* Photo
@@ -732,6 +740,15 @@ class Profile
         return array_pop($job);
     }
 
+    /** JobTerms
+     */
+    private $jobterms = null;
+    public function setJobTerms(ProfileJobTerms $jobterms)
+    {
+        $this->jobterms = $jobterms;
+        $this->consolidateFields();
+    }
+
     /* Mentoring
      */
     private $mentor_sectors = null;
@@ -772,6 +789,34 @@ class Profile
         }
     }
 
+    /** List of job terms to specify mentoring */
+    private $mentor_terms = null;
+    /**
+     * set job terms to specify mentoring
+     * @param $terms a ProfileMentoringTerms object listing terms only for this profile
+     */
+    public function setMentoringTerms(ProfileMentoringTerms $terms)
+    {
+        $this->mentor_terms = $terms;
+    }
+    /**
+     * get all job terms that specify mentoring
+     * @return an array of JobTerms objects
+     */
+    public function getMentoringTerms()
+    {
+        if ($this->mentor_terms == null && !$this->fetched(self::FETCH_MENTOR_TERMS)) {
+            $this->setMentoringTerms($this->getProfileField(self::FETCH_MENTOR_TERMS));
+        }
+
+        if ($this->mentor_terms == null) {
+            return array();
+        } else {
+            return $this->mentor_terms->get();
+        }
+    }
+
+
     /* Binets
      */
     public function getBinets()
@@ -1124,6 +1169,8 @@ class ProfileIterator implements PlIterator
     private $fields;
     private $visibility;
 
+    const FETCH_ALL    = 0x000033F; // FETCH_ADDRESSES | FETCH_CORPS | FETCH_EDU | FETCH_JOBS | FETCH_MEDALS | FETCH_NETWORKING | FETCH_PHONES | FETCH_JOB_TERMS
+
     public function __construct(PlIterator $it, array $pids, $fields = 0x0000, ProfileVisibility $visibility = null)
     {
         require_once 'profilefields.inc.php';
@@ -1142,39 +1189,12 @@ class ProfileIterator implements PlIterator
         $callbacks[0] = PlIteratorUtils::arrayValueCallback('pid');
         $cb = PlIteratorUtils::objectPropertyCallback('pid');
 
-        if ($fields & Profile::FETCH_ADDRESSES) {
-            $callbacks[Profile::FETCH_ADDRESSES] = $cb;
-            $subits[Profile::FETCH_ADDRESSES] = new ProfileFieldIterator('ProfileAddresses', $pids, $visibility);
-        }
-
-        if ($fields & Profile::FETCH_CORPS) {
-            $callbacks[Profile::FETCH_CORPS] = $cb;
-            $subits[Profile::FETCH_CORPS] = new ProfileFieldIterator('ProfileCorps', $pids, $visibility);
-        }
-
-        if ($fields & Profile::FETCH_EDU) {
-            $callbacks[Profile::FETCH_EDU] = $cb;
-            $subits[Profile::FETCH_EDU] = new ProfileFieldIterator('ProfileEducation', $pids, $visibility);
-        }
-
-        if ($fields & Profile::FETCH_JOBS) {
-            $callbacks[Profile::FETCH_JOBS] = $cb;
-            $subits[Profile::FETCH_JOBS] = new ProfileFieldIterator('ProfileJobs', $pids, $visibility);
-        }
-
-        if ($fields & Profile::FETCH_MEDALS) {
-            $callbacks[Profile::FETCH_MEDALS] = $cb;
-            $subits[Profile::FETCH_MEDALS] = new ProfileFieldIterator('ProfileMedals', $pids, $visibility);
-        }
-
-        if ($fields & Profile::FETCH_NETWORKING) {
-            $callbacks[Profile::FETCH_NETWORKING] = $cb;
-            $subits[Profile::FETCH_NETWORKING] = new ProfileFieldIterator('ProfileNetworking', $pids, $visibility);
-        }
-
-        if ($fields & Profile::FETCH_PHONES) {
-            $callbacks[Profile::FETCH_PHONES] = $cb;
-            $subits[Profile::FETCH_PHONES] = new ProfileFieldIterator('ProfilePhones', $pids, $visibility);
+        $fields = $fields & self::FETCH_ALL;
+        for ($field = 1; $field < $fields; $field *= 2) {
+            if (($fields & $field) ) {
+                $callbacks[$field] = $cb;
+                $subits[$field] = new ProfileFieldIterator($field, $pids, $visibility);
+            }
         }
 
         $this->iterator = PlIteratorUtils::parallelIterator($subits, $callbacks, 0);
@@ -1200,6 +1220,9 @@ class ProfileIterator implements PlIterator
         if ($this->hasData(Profile::FETCH_JOBS, $vals)) {
             $pf->setJobs($vals[Profile::FETCH_JOBS]);
         }
+        if ($this->hasData(Profile::FETCH_JOB_TERMS, $vals)) {
+            $pf->setJobTerms($vals[Profile::FETCH_JOB_TERMS]);
+        }
 
         if ($this->hasData(Profile::FETCH_CORPS, $vals)) {
             $pf->setCorps($vals[Profile::FETCH_CORPS]);
index 2a9d077..6e29146 100644 (file)
@@ -980,6 +980,35 @@ class UFC_Job_Sectorization implements UserFilterCondition
 }
 // }}}
 
+// {{{ class UFC_Job_Terms
+/** Filters users based on the job terms they assigned to one of their
+ * jobs.
+ * @param $val The ID of the job term, or an array of such IDs
+ */
+class UFC_Job_Terms implements UserFilterCondition
+{
+    private $val;
+
+    public function __construct($val)
+    {
+        if (!is_array($val)) {
+            $val = array($val);
+        }
+        $this->val = $val;
+    }
+
+    public function buildCondition(PlFilter &$uf)
+    {
+        $sub = $uf->addJobTermsFilter(count($this->val));
+        $conditions = array();
+        foreach ($this->val as $i => $jtid) {
+            $conditions[] = $sub[$i] . ' = ' . XDB::escape($jtid);
+        }
+        return implode(' AND ', $conditions);
+    }
+}
+// }}}
+
 // {{{ class UFC_Job_Description
 /** Filters users based on their job description
  * @param $description The text being searched for
@@ -2473,6 +2502,7 @@ class UserFilter extends PlFilter
      * pjsse => profile_job_subsector_enum
      * pjssse => profile_job_subsubsector_enum
      * pja => profile_job_alternates
+     * pjt => profile_job_terms
      */
     private $with_pj = false;
     private $with_pje = false;
@@ -2480,6 +2510,7 @@ class UserFilter extends PlFilter
     private $with_pjsse = false;
     private $with_pjssse = false;
     private $with_pja = false;
+    private $with_pjt = 0;
 
     public function addJobFilter()
     {
@@ -2513,6 +2544,22 @@ class UserFilter extends PlFilter
         }
     }
 
+    /**
+     * Adds a filter on job terms of profile.
+     * @param $nb the number of job terms to use
+     * @return an array of the fields to filter (one for each term).
+     * Code using this function should used returned field as is (contains table and field name).
+     */
+    public function addJobTermsFilter($nb = 1)
+    {
+        $this->with_pjt = $nb;
+        $jobtermstable = array();
+        for ($i = 1; $i <= $nb; ++$i) {
+            $jobtermstable[] = 'pjtr_'.$i.'.jtid_1';
+        }
+        return $jobtermstable;
+    }
+
     protected function jobJoins()
     {
         $joins = array();
@@ -2534,6 +2581,12 @@ class UserFilter extends PlFilter
         if ($this->with_pja) {
             $joins['pja'] = PlSqlJoin::left('profile_job_alternates', '$ME.subsubsectorid = pj.subsubsectorid');
         }
+        if ($this->with_pjt > 0) {
+            for ($i = 1; $i <= $this->with_pjt; ++$i) {
+                $joins['pjt_'.$i] = PlSqlJoin::left('profile_job_term', '$ME.pid = $PID');
+                $joins['pjtr_'.$i] = PlSqlJoin::left('profile_job_term_relation', '$ME.jtid_2 = pjt_'.$i.'.jtid');
+            }
+        }
         return $joins;
     }
 
index cd673cb..4ae8637 100644 (file)
@@ -484,6 +484,13 @@ div.adresse strong {
     font-size: 90%;
 }
 
+div.adresse ul {
+    margin-top: 0px;
+    margin-bottom: 0px;
+    list-style-type: none;
+    padding-left: 0px;
+}
+
 #fiche .medal_frame {
     float: left;
     width: 33%;
diff --git a/htdocs/css/jstree.css b/htdocs/css/jstree.css
new file mode 100644 (file)
index 0000000..65d3403
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * jsTree default theme 1.0
+ * Supported features: dots/no-dots, icons/no-icons, focused, loading
+ * Supported plugins: ui (hovered, clicked), checkbox, contextmenu, search
+ */
+
+.jstree-default li,
+.jstree-default ins { background-image:url("../images/jstree.png"); background-repeat:no-repeat; background-color:transparent; }
+.jstree-default li { background-position:-90px 0; background-repeat:repeat-y; }
+.jstree-default li.jstree-last { background:transparent; }
+.jstree-default .jstree-open > ins { background-position:-72px 0; }
+.jstree-default .jstree-closed > ins { background-position:-54px 0; }
+.jstree-default .jstree-leaf > ins { background-position:-36px 0; }
+
+.jstree-default .jstree-hovered { background:#e7f4f9; border:1px solid #d8f0fa; padding:0 2px 0 1px; }
+.jstree-default .jstree-clicked { background:#beebff; border:1px solid #99defd; padding:0 2px 0 1px; }
+.jstree-default a .jstree-icon { background-position:-56px -19px; }
+.jstree-default a.jstree-loading .jstree-icon { background:url("../images/wait.gif") center center no-repeat !important; }
+
+.jstree-default .jstree-no-dots li,
+.jstree-default .jstree-no-dots .jstree-leaf > ins { background:transparent; }
+.jstree-default .jstree-no-dots .jstree-open > ins { background-position:-18px 0; }
+.jstree-default .jstree-no-dots .jstree-closed > ins { background-position:0 0; }
+
+.jstree-default .jstree-no-icons a .jstree-icon { display:none; }
+
+.jstree-default .jstree-search { font-style:italic; }
+
+.jstree-default .jstree-no-icons .jstree-checkbox { display:inline-block; }
+.jstree-default .jstree-no-checkboxes .jstree-checkbox { display:none !important; }
+.jstree-default .jstree-checked > a > .jstree-checkbox { background-position:-38px -19px; }
+.jstree-default .jstree-unchecked > a > .jstree-checkbox { background-position:-2px -19px; }
+.jstree-default .jstree-undetermined > a > .jstree-checkbox { background-position:-20px -19px; }
+.jstree-default .jstree-checked > a > .jstree-checkbox:hover { background-position:-38px -37px; }
+.jstree-default .jstree-unchecked > a > .jstree-checkbox:hover { background-position:-2px -37px; }
+.jstree-default .jstree-undetermined > a > .jstree-checkbox:hover { background-position:-20px -37px; }
+
+#vakata-dragged.jstree-default ins { background:transparent !important; }
+#vakata-dragged.jstree-default .jstree-ok { background:url("../images/jstree.png") -2px -53px no-repeat !important; }
+#vakata-dragged.jstree-default .jstree-invalid { background:url("../images/jstree.png") -18px -53px no-repeat !important; }
+#jstree-marker.jstree-default { background:url("../images/jstree.png") -41px -57px no-repeat !important; }
+
+.jstree-default a.jstree-search { color:aqua; }
+
+#vakata-contextmenu.jstree-default-context,
+#vakata-contextmenu.jstree-default-context li ul { background:#f0f0f0; border:1px solid #979797; -moz-box-shadow: 1px 1px 2px #999; -webkit-box-shadow: 1px 1px 2px #999; box-shadow: 1px 1px 2px #999; }
+#vakata-contextmenu.jstree-default-context li { }
+#vakata-contextmenu.jstree-default-context a { color:black; }
+#vakata-contextmenu.jstree-default-context a:hover,
+#vakata-contextmenu.jstree-default-context .vakata-hover > a { padding:0 5px; background:#e8eff7; border:1px solid #aecff7; color:black; -moz-border-radius:2px; -webkit-border-radius:2px; border-radius:2px; }
+#vakata-contextmenu.jstree-default-context li.jstree-contextmenu-disabled a,
+#vakata-contextmenu.jstree-default-context li.jstree-contextmenu-disabled a:hover { color:silver; background:transparent; border:0; padding:1px 4px; }
+#vakata-contextmenu.jstree-default-context li.vakata-separator { background:white; border-top:1px solid #e0e0e0; margin:0; }
+#vakata-contextmenu.jstree-default-context li ul { margin-left:-4px; }
+
+/* IE6 BEGIN */
+.jstree-default li,
+.jstree-default ins,
+#vakata-dragged.jstree-default .jstree-invalid,
+#vakata-dragged.jstree-default .jstree-ok,
+#jstree-marker.jstree-default { _background-image:url("d.gif"); }
+.jstree-default .jstree-open ins { _background-position:-72px 0; }
+.jstree-default .jstree-closed ins { _background-position:-54px 0; }
+.jstree-default .jstree-leaf ins { _background-position:-36px 0; }
+.jstree-default a ins.jstree-icon { _background-position:-56px -19px; }
+#vakata-contextmenu.jstree-default-context ins { _display:none; }
+#vakata-contextmenu.jstree-default-context li { _zoom:1; }
+.jstree-default .jstree-undetermined a .jstree-checkbox { _background-position:-20px -19px; }
+.jstree-default .jstree-checked a .jstree-checkbox { _background-position:-38px -19px; }
+.jstree-default .jstree-unchecked a .jstree-checkbox { _background-position:-2px -19px; }
+/* IE6 END */
diff --git a/htdocs/images/jstree.png b/htdocs/images/jstree.png
new file mode 100644 (file)
index 0000000..8540175
Binary files /dev/null and b/htdocs/images/jstree.png differ
diff --git a/htdocs/javascript/jobtermstree.js b/htdocs/javascript/jobtermstree.js
new file mode 100644 (file)
index 0000000..f3a42eb
--- /dev/null
@@ -0,0 +1,51 @@
+/***************************************************************************
+ *  Copyright (C) 2003-2010 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                *
+ ***************************************************************************/
+
+/**
+ * Creates a job terms tree.
+ * @param domElement the jQuery selector string that defines the DOM element
+ * which should contain the tree.
+ * @param platalpage the base page to query for branches
+ * @param treeid an id unique for the tree in this page that will be used in
+ * clickFunc
+ * @param clickFunc name of a javascript function that will be called when a
+ * term is clicked. The three params of this function will be : treeid, the
+ * id of the job term clicked, and the full name of the job term clicked.
+ */
+function createJobTermsTree(domElement, platalpage, treeid, clickFunc)
+{
+    $(domElement).jstree({
+        "core" : {"strings":{"loading":"Chargement ..."}},
+        "plugins" : ["themes","json_data"],
+        "themes" : { "url" : platal_baseurl + "css/jstree.css" },
+        "json_data" : { "ajax" : {
+            "url" : platal_baseurl + platalpage,
+            "data" : function(nod) {
+                var jtid = 0;
+                if (nod != -1) {
+                    jtid = nod.attr("id").replace(/^.*_([0-9]+)$/, "$1");
+                }
+                return { "jtid" : jtid, "treeid" : treeid, "attrfunc" : clickFunc }
+            }
+        }} });
+}
+
+// vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
+
index cafaba9..6145692 100644 (file)
@@ -566,6 +566,125 @@ function addEntreprise(id)
     $('.entreprise_' + id).toggle();
 }
 
+/**
+ * Adds a job term in job profile page
+ * @param jobid id of profile's job among his different jobs
+ * @param jtid id of job term to add
+ * @param full_name full text of job term
+ * @return false if the term already exist for this job, true otherwise
+ */
+function addJobTerm(jobid, jtid, full_name)
+{
+    var termid = 0;
+    var parentpath;
+    var formvarname;
+    if (jobid < 0) {
+        parentpath = '';
+        jobid = '';
+        formvarname = 'terms';
+    } else {
+        parentpath = '#job_'+jobid+' ';
+        formvarname = 'jobs['+jobid+'][terms]';
+    }
+    var lastJobTerm = $(parentpath + '.job_term:last');
+    if (lastJobTerm.length != 0) {
+        termid = parseInt(lastJobTerm.children('input').attr('name').replace(/^(jobs\[[0-9]+\]\[terms\]|terms)\[([0-9]+)\]\[jtid\]/, '$2')) + 1;
+        if ($('#job'+jobid+'_term'+jtid).length > 0) {
+            return false;
+        }
+    }
+    var newdiv = '<div class="job_term" id="job'+jobid+'_term'+jtid+'">'+
+        '<span>'+full_name+'</span>'+
+        '<input type="hidden" name="'+formvarname+'['+termid+'][jtid]" value="'+jtid+'" />'+
+        '<img title="Retirer ce mot-clef" alt="retirer" src="images/icons/cross.gif" />'+
+        '</div>';
+    if (lastJobTerm.length == 0) {
+        $(parentpath + '.job_terms').prepend(newdiv);
+    } else {
+        lastJobTerm.after(newdiv);
+    }
+    $('#job'+jobid+'_term'+jtid+' img').css('cursor','pointer').click(removeJobTerm);
+    return true;
+}
+
+/**
+ * Remove a job term in job profile page.
+ * Must be called from a button in a div containing the term
+ */
+function removeJobTerm()
+{
+    $(this).parent().remove();
+}
+
+/**
+ * Prepare display for autocomplete suggestions in job terms
+ * @param row an array of (title of term, id of term)
+ * @return text to display
+ * If id is negative, it is because there are too much terms to
+ * be displayed.
+ */
+function displayJobTerm(row)
+{
+    if (row[1] < 0) {
+        return '... <em>précise ta recherche</em> ...';
+    }
+    return row[0];
+}
+
+/**
+ * Function called when a job term has been selected from autocompletion
+ * in search
+ * @param li is the list item (<li>) that has been clicked
+ * The context is the jsquery autocomplete object.
+ */
+function selectJobTerm(li)
+{
+    if (li.extra[0] < 0) {
+        return;
+    }
+    var jobid = this.extraParams.jobid;
+    addJobTerm(jobid,li.extra[0],$(li).text());
+    var search_input;
+    if (jobid < 0) {
+        search_input = $('.term_search')[0];
+    } else {
+        search_input = $('#job_'+jobid+' .term_search')[0];
+    }
+    search_input.value = '';
+    search_input.focus();
+}
+
+/**
+ * Function to show or hide a terms tree in job edition
+ * @param jobid is the id of the job currently edited
+ */
+function toggleJobTermsTree(jobid)
+{
+    var treepath;
+    if (jobid < 0) {
+        treepath = '';
+    } else {
+        treepath = '#job_'+jobid+' ';
+    }
+    treepath += '.term_tree';
+    if ($(treepath + ' ul').length > 0) {
+        $(treepath).empty().removeClass().addClass('term_tree');
+        return;
+    }
+    createJobTermsTree(treepath, 'profile/ajax/tree/jobterms/all', 'job' + jobid, 'chooseJobTerm');
+}
+
+/**
+ * Function called when a job term is chosen from terms tree
+ * @param treeid is the full id of the tree (must look like job3)
+ * @param jtid is the id of the job term chosen
+ * @param fullname is the complete name (understandable without context) of the term
+ */
+function chooseJobTerm(treeid, jtid, fullname)
+{
+    addJobTerm(treeid.replace(/^job(.*)$/, '$1'), jtid, fullname);
+}
+
 // {{{1 Skills
 
 function addSkill(cat)
diff --git a/include/profil.func.inc.php b/include/profil.func.inc.php
deleted file mode 100644 (file)
index e8ecb59..0000000
+++ /dev/null
@@ -1,379 +0,0 @@
-<?php
-/***************************************************************************
- *  Copyright (C) 2003-2010 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                *
- ***************************************************************************/
-
-
-
-function replace_ifset(&$var,$req) {
-    if (Env::has($req)){
-        $var = Env::v($req);
-    }
-}
-
-function replace_ifset_i(&$var,$req,$i) {
-    if (isset($_REQUEST[$req][$i])){
-        $var[$i] = $_REQUEST[$req][$i];
-    }
-}
-
-function replace_ifset_i_j(&$var,$req,$i,$j) {
-    if (isset($_REQUEST[$req][$j])){
-        $var[$i] = $_REQUEST[$req][$j];
-    }
-}
-
-//pour rentrer qqchose dans la base
-function put_in_db($string){
-    return trim($string);
-}
-
-// example of use for diff_user_details : get $b from database, $a from other site
-//  calculate diff $c and add $c in database (with set_user_details)
-function diff_user_details(&$a, &$b, $view = 'private') { // compute $c = $a - $b
-//    if (!isset($b) || !$b || !is_array($b) || count($b) == 0)
-//        return $a;
-//    if (!isset($a) || !$a || !is_array($a))
-//        $c = array();
-//    else
-        $c = $a;
-    foreach ($b as $val => $bvar) {
-        if (isset($a[$val])) {
-            if ($a[$val] == $bvar)
-                unset($c[$val]);
-            else {
-                switch ($val) {
-                    case 'adr' : if (!($c['adr'] = diff_user_addresses($a[$val], $bvar, $view))) unset($c['adr']); break;
-                    case 'adr_pro' : if (!($c['adr_pro'] = diff_user_pros($a[$val], $bvar, $view))) unset($c['adr_pro']); break;
-                    case 'tels' : if (!($c['tels'] = diff_user_tels($a[$val], $bvar, $view))) unset($c['tels']); break;
-                }
-            }
-        }
-    }
-    // don't modify freetext if you don't have the right
-    if (isset($b['freetext_pub']) && !has_user_right($b['freetext_pub'], $view) && isset($c['freetext']))
-        unset($c['freetext']);
-    if (!count($c))
-        return false;
-    return $c;
-}
-
-function same_tel(&$a, &$b) {
-    $numbera = format_phone_number((string) $a);
-    $numberb = format_phone_number((string) $b);
-    return $numbera === $numberb;
-}
-function same_address(&$a, &$b) {
-    return
-        (same_field($a['adr1'],$b['adr1'])) &&
-        (same_field($a['adr2'],$b['adr2'])) &&
-        (same_field($a['adr3'],$b['adr3'])) &&
-        (same_field($a['postcode'],$b['postcode'])) &&
-        (same_field($a['city'],$b['city'])) &&
-        (same_field($a['countrytxt'],$b['countrytxt'])) &&
-        true;
-}
-function same_pro(&$a, &$b) {
-    return
-        (same_field($a['entreprise'],$b['entreprise'])) &&
-        (same_field($a['fonction'],$b['fonction'])) &&
-        true;
-}
-function same_field(&$a, &$b) {
-    if ($a == $b) return true;
-    if (is_array($a)) {
-        if (!is_array($b) || count($a) != count($b)) return false;
-        foreach ($a as $val => $avar)
-            if (!isset($b[$val]) || !same_field($avar, $b[$val])) return false;
-        return true;
-    } elseif (is_string($a))
-        return (mb_strtoupper($a) == mb_strtoupper($b));
-}
-function diff_user_tel(&$a, &$b) {
-    $c = $a;
-    if (isset($b['tel_pub']) && isset($a['tel_pub']) && has_user_right($b['tel_pub'], $a['tel_pub']))
-        $c['tel_pub'] = $b['tel_pub'];
-    foreach ($b as $val => $bvar) {
-        if (isset($a[$val])) {
-            if ($a[$val] == $bvar)
-                unset($c[$val]);
-        }
-    }
-    if (!count($c))
-        return false;
-    $c['telid'] = $a['telid'];
-    return $c;
-}
-
-function diff_user_tels(&$a, &$b)
-{
-    $c = $a;
-    $telids_b = array();
-    foreach ($b as $i => $telb) $telids_b[$telb['telid']] = $i;
-
-    foreach ($a as $j => $tela) {
-        if (isset($tela['telid'])) {
-            // if b has a tel with the same telid, compute diff
-            if (isset($telids_b[$tela['telid']])) {
-                if (!($c[$j] = diff_user_tel($tela, $b[$telids_b[$tela['adrid']]]))) {
-                    unset($c[$j]);
-                }
-                unset($telids_b[$tela['telid']]);
-            }
-        } else {
-            // try to find a match in b
-            foreach ($b as $i => $telb) {
-                if (same_tel($tela['tel'], $telb['tel'])) {
-                    $tela['telid'] = $telb['telid'];
-                    if (!($c[$j] = diff_user_tel($tela, $telb))) {
-                        unset($c[$j]);
-                    }
-                    unset($telids_b[$tela['telid']]);
-                    break;
-                }
-            }
-        }
-    }
-
-    foreach ($telids_b as $telidb => $i)
-        $c[] = array('telid' => $telidb, 'remove' => 1);
-    return $c;
-}
-
-function diff_user_address($a, $b) {
-    if (isset($b['pub']) && isset($a['pub']) && has_user_right($b['pub'], $a['pub']))
-        $a['pub'] = $b['pub'];
-    if (isset($b['tels'])) {
-        if (isset($a['tels'])) {
-            $avar = $a['tels'];
-        } else {
-            $avar = array();
-        }
-        $ctels = diff_user_tels($avar, $b['tels']);
-
-        if (!count($ctels)) {
-            $b['tels'] = $avar;
-        } else {
-            $a['tels'] = $ctels;
-        }
-    }
-
-    foreach ($a as $val => $avar) {
-        if (!isset($b[$val]) || !same_field($avar,$b[$val])) {
-            return $a;
-        }
-    }
-    return false;
-}
-
-// $b need to use adrids
-function diff_user_addresses(&$a, &$b) {
-    $c = $a;
-    $adrids_b = array();
-    foreach ($b as $i => $adrb) $adrids_b[$adrb['adrid']] = $i;
-
-    foreach ($a as $j => $adra) {
-        if (isset($adra['adrid'])) {
-            // if b has an address with the same adrid, compute diff
-            if (isset($adrids_b[$adra['adrid']])) {
-                if (!($c[$j] = diff_user_address($adra, $b[$adrids_b[$adra['adrid']]])))
-                    unset($c[$j]);
-                unset($adrids_b[$adra['adrid']]);
-            }
-        } else {
-            // try to find a match in b
-            foreach ($b as $i => $adrb) {
-                if (same_address($adra, $adrb)) {
-                    $adra['adrid'] = $adrb['adrid'];
-                    if (!($c[$j] = diff_user_address($adra, $adrb)))
-                        unset($c[$j]);
-                    if ($c[$j]) $c[$j]['adrid'] = $adra['adrid'];
-                    unset($adrids_b[$adra['adrid']]);
-                    break;
-                }
-            }
-        }
-    }
-
-    foreach ($adrids_b as $adridb => $i)
-        $c[] = array('adrid' => $adridb, 'remove' => 1);
-
-    if (!count($c)) return false;
-    return $c;
-}
-
-function diff_user_pro($a, &$b, $view = 'private') {
-    if (isset($b['pub']) && isset($a['pub']) && has_user_right($b['pub'], $a['pub']))
-        $a['pub'] = $b['pub'];
-    if (isset($b['adr_pub']) && !has_user_right($b['adr_pub'], $view)) {
-        unset($a['adr1']);
-        unset($a['adr2']);
-        unset($a['adr3']);
-        unset($a['postcode']);
-        unset($a['city']);
-        unset($a['countrytxt']);
-        unset($a['region']);
-    }
-    if (isset($b['adr_pub']) && isset($a['adr_pub']) && has_user_right($b['adr_pub'], $a['adr_pub']))
-        $a['adr_pub'] = $b['adr_pub'];
-    if (isset($b['tels'])) {
-        if (isset($a['tels']))
-            $avar = $a['tels'];
-        else
-            $avar = array();
-        $ctels = diff_user_tels($avar, $b['tels']);
-
-        if (!count($ctels)) {
-            $b['tels'] = $avar;
-        } else
-            $a['tels'] = $ctels;
-    }
-    if (isset($b['email_pub']) && !has_user_right($b['email_pub'], $view))
-        unset($a['email']);
-    if (isset($b['email_pub']) && isset($a['email_pub']) && has_user_right($b['email_pub'], $a['email_pub']))
-        $a['email_pub'] = $b['email_pub'];
-    foreach ($a as $val => $avar) {
-        if (($avar && !isset($b[$val])) || !same_field($avar,$b[$val])) {
-            return $a;
-        }
-    }
-    return false;
-}
-
-// $b need to use entrids
-function diff_user_pros(&$a, &$b, $view = 'private') {
-    $c = $a;
-    $entrids_b = array();
-    foreach ($b as $i => $prob) $entrids_b[$prob['entrid']] = $i;
-
-    foreach ($a as $j => $proa) {
-        if (isset($proa['entrid'])) {
-            // if b has an address with the same adrid, compute diff
-            if (isset($entrids_b[$proa['entrid']])) {
-                if (!($c[$j] = diff_user_pro($proa, $b[$entrids_b[$proa['entrid']]], $view)))
-                    unset($c[$j]);
-                unset($entrids_b[$proa['entrid']]);
-            }
-        } else {
-            // try to find a match in b
-            foreach ($b as $i => $prob) {
-                if (same_pro($proa, $prob)) {
-                    $proa['entrid'] = $prob['entrid'];
-                    if (!($c[$j] = diff_user_pro($proa, $prob, $view)))
-                        unset($c[$j]);
-                    if ($c[$j]) $c[$j]['entrid'] = $proa['entrid'];
-                    unset($entrids_b[$proa['entrid']]);
-                    break;
-                }
-            }
-        }
-    }
-
-    foreach ($entrids_b as $entridb => $i)
-        $c[] = array('entrid' => $entridb, 'remove' => 1);
-
-    if (!count($c)) return false;
-    return $c;
-}
-
-function format_phone_number($tel)
-{
-    $tel = trim($tel);
-    if (substr($tel, 0, 3) === '(0)') {
-        $tel = '33' . $tel;
-    }
-    $tel = preg_replace('/\(0\)/',  '', $tel);
-    $tel = preg_replace('/[^0-9]/', '', $tel);
-    if (substr($tel, 0, 2) === '00') {
-        $tel = substr($tel, 2);
-    } else if(substr($tel, 0, 1) === '0') {
-        $tel = '33' . substr($tel, 1);
-    }
-    return $tel;
-}
-
-function format_display_number($tel, &$error, $format = array('format'=>'','phoneprf'=>''))
-{
-    $error = false;
-    $ret = '';
-    $tel_length = strlen($tel);
-    if((!isset($format['phoneprf'])) || ($format['phoneprf'] == '')) {
-        $res = XDB::query("SELECT phonePrefix AS phoneprf, phoneFormat AS format
-                             FROM geoloc_countries
-                            WHERE phonePrefix = {?} OR phonePrefix = {?} OR phonePrefix = {?}
-                            LIMIT 1",
-                          substr($tel, 0, 1), substr($tel, 0, 2), substr($tel, 0, 3));
-        if ($res->numRows() == 0) {
-            $error = true;
-            return '+' . $tel;
-        }
-        $format = $res->fetchOneAssoc();
-    }
-    if ($format['format'] == '') {
-        $format['format'] = '+p';
-    }
-    $j = 0;
-    $i = strlen($format['phoneprf']);
-    $length_format = strlen($format['format']);
-    while (($i < $tel_length) && ($j < $length_format)){
-        if ($format['format'][$j] == '#'){
-            $ret .= $tel[$i];
-            $i++;
-        } else if ($format['format'][$j] == 'p') {
-            $ret .= $format['phoneprf'];
-        } else {
-            $ret .= $format['format'][$j];
-        }
-        $j++;
-    }
-    for (; $i < $tel_length - 1; $i += 2) {
-        $ret .= ' ' . substr($tel, $i, 2);
-    }
-    //appends last alone number to the last block
-    if ($i < $tel_length) {
-        $ret .= substr($tel, $i);
-    }
-    return $ret;
-}
-
-/**
- * Extract search token from term
- * @param $term a utf-8 string that can contain any char
- * @param an array of elementary tokens
- */
-function tokenize_job_term($term)
-{
-    $term = mb_strtoupper(replace_accent($term));
-    $term = str_replace(array('/', ',', '(', ')', '"', '&', '»', '«'), ' ', $term);
-    $tokens = explode(' ', $term);
-    static $not_tokens = array('ET','AND','DE','DES','DU','D\'','OU','L\'','LA','LE','LES','PAR','AU','AUX','EN','SUR','UN','UNE','IN');
-    foreach ($tokens as &$t) {
-        if (substr($t, 1, 1) == '\'' && in_array(substr($t, 0, 2), $not_tokens)) {
-            $t = substr($t, 2);
-        }
-        if (strlen($t) == 1 || in_array($t, $not_tokens)) {
-            $t = false;
-            continue;
-        }
-    }
-    return array_filter($tokens);
-}
-
-// vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
-?>
index 594e327..506b8ac 100644 (file)
@@ -35,6 +35,8 @@ abstract class ProfileField
         Profile::FETCH_PHONES         => 'ProfilePhones',
         Profile::FETCH_MENTOR_SECTOR  => 'ProfileMentoringSectors',
         Profile::FETCH_MENTOR_COUNTRY => 'ProfileMentoringCountries',
+        Profile::FETCH_JOB_TERMS      => 'ProfileJobTerms',
+        Profile::FETCH_MENTOR_TERMS   => 'ProfileMentoringTerms',
     );
 
     /** The profile to which this field belongs
@@ -92,6 +94,9 @@ class ProfileFieldIterator implements PlIterator
 
     public function __construct($cls, array $pids, ProfileVisibility $visibility)
     {
+        if (is_numeric($cls) && isset(ProfileField::$fields[$cls])) {
+            $cls = ProfileField::$fields[$cls];
+        }
         $this->data = call_user_func(array($cls, 'fetchData'), $pids, $visibility);
         $this->cls = $cls;
     }
@@ -169,6 +174,7 @@ class Job
     public $company = null;
     public $phones = array();
     public $address = null;
+    public $terms = array();
 
     public $jobid;
 
@@ -219,6 +225,30 @@ class Job
             $this->address = $address;
         }
     }
+
+    public function addTerm(JobTerm &$term)
+    {
+        $this->terms[$term->jtid] = $term;
+    }
+}
+// }}}
+// {{{ class JobTerm
+class JobTerm
+{
+    public $jtid;
+    public $full_name;
+    public $pid;
+    public $jid;
+
+    /** Fields are:
+     * pid, jid, jtid, full_name
+     */
+    public function __construct($data)
+    {
+        foreach ($data as $key => $val) {
+            $this->$key = $val;
+        }
+    }
 }
 // }}}
 // {{{ class Address
@@ -776,9 +806,64 @@ class ProfileJobs extends ProfileField
             $this->company = $companies[$job->jobid];
         }
     }
+
+    public function addJobTerms(ProfileJobTerms $jobterms)
+    {
+        $terms = $jobterms->get();
+        foreach ($terms as $term) {
+            if ($this->pid == $term->pid && array_key_exists($term->jid, $this->jobs)) {
+                $this->jobs[$term->jid]->addTerm(&$term);
+            }
+        }
+    }
 }
 // }}}
+// {{{ class ProfileJobTerms                          [ Field ]
+class ProfileJobTerms extends ProfileField
+{
+    private $jobterms = array();
 
+    public function __construct(PlInnerSubIterator $it)
+    {
+        $this->pid = $it->value();
+        while ($term = $it->next()) {
+            $this->jobterms[] = new JobTerm($term);
+        }
+    }
+
+    public function get()
+    {
+        return $this->jobterms;
+    }
+
+    public static function fetchData(array $pids, ProfileVisibility $visibility)
+    {
+        $data = XDB::iterator('SELECT  jt.jtid, jte.full_name, jt.pid, jt.jid
+                                 FROM  profile_job_term AS jt
+                           INNER JOIN  profile_job AS j ON (jt.pid = j.pid AND jt.jid = j.id)
+                            LEFT JOIN  profile_job_term_enum AS jte USING(jtid)
+                                WHERE  jt.pid IN {?} AND j.pub IN {?}
+                             ORDER BY  ' . XDB::formatCustomOrder('jt.pid', $pids),
+                                 $pids, $visibility->levels());
+        return PlIteratorUtils::subIterator($data, PlIteratorUtils::arrayValueCallback('pid'));
+    }
+}
+// }}}
+// {{{ class ProfileMentoringTerms                    [ Field ]
+class ProfileMentoringTerms extends ProfileJobTerms
+{
+    public static function fetchData(array $pids, ProfileVisibility $visibility)
+    {
+        $data = XDB::iterator('SELECT  mt.jtid, jte.full_name, mt.pid
+                                 FROM  profile_mentor_term AS mt
+                            LEFT JOIN  profile_job_term_enum AS jte USING(jtid)
+                                WHERE  mt.pid IN {?}
+                             ORDER BY  ' . XDB::formatCustomOrder('mt.pid', $pids),
+                                $pids);
+        return PlIteratorUtils::subIterator($data, PlIteratorUtils::arrayValueCallback('pid'));
+    }
+}
+// }}}
 // {{{ class CompanyList
 class CompanyList
 {
index 4a734b2..819fd91 100644 (file)
@@ -181,6 +181,7 @@ class UFB_AdvancedSearch extends UserFilterBuilder
             new UFBF_JobSector('sector', 'Poste'),
             new UFBF_JobDescription('jobdescription', 'Fonction'),
             new UFBF_JobCv('cv', 'CV'),
+            new UFBF_JobTerms('jobterm', 'Mots-clefs'),
 
             new UFBF_Nationality('nationaliteTxt', 'nationalite', 'Nationalité'),
             new UFBF_Binet('binetTxt', 'binet', 'Binet'),
@@ -842,6 +843,16 @@ class UFBF_JobSector extends UFBF_Mixed
 }
 // }}}
 
+// {{{ class UFBF_JobTerms
+class UFBF_JobTerms extends UFBF_Index
+{
+    protected function buildUFC(UserFilterBuilder &$ufb)
+    {
+        return new UFC_Job_Terms($this->val);
+    }
+}
+// }}}
+
 // {{{ class UFBF_JobDescription
 class UFBF_JobDescription extends UFBF_Text
 {
index 61d1596..f38fbfd 100644 (file)
@@ -44,6 +44,8 @@ class ProfileModule extends PLModule
             'profile/ajax/skill'         => $this->make_hook('ajax_skill',                 AUTH_COOKIE, 'user', NO_AUTH),
             'profile/ajax/searchname'    => $this->make_hook('ajax_searchname',            AUTH_COOKIE, 'user', NO_AUTH),
             'profile/ajax/buildnames'    => $this->make_hook('ajax_buildnames',            AUTH_COOKIE, 'user', NO_AUTH),
+            'profile/ajax/tree/jobterms' => $this->make_hook('ajax_tree_job_terms',        AUTH_COOKIE, 'user', NO_AUTH),
+            'profile/jobterms'           => $this->make_hook('jobterms',                   AUTH_COOKIE, 'user', NO_AUTH),
             'javascript/education.js'    => $this->make_hook('education_js',               AUTH_COOKIE),
             'javascript/grades.js'       => $this->make_hook('grades_js',                  AUTH_COOKIE),
             'profile/medal'              => $this->make_hook('medal',                      AUTH_PUBLIC),
@@ -55,6 +57,7 @@ class ProfileModule extends PLModule
             'referent/search'            => $this->make_hook('ref_search',                 AUTH_COOKIE),
             'referent/ssect'             => $this->make_hook('ref_sect',                   AUTH_COOKIE, 'user', NO_AUTH),
             'referent/country'           => $this->make_hook('ref_country',                AUTH_COOKIE, 'user', NO_AUTH),
+            'referent/autocomplete'      => $this->make_hook('ref_autocomplete',           AUTH_COOKIE, 'user', NO_AUTH),
 
             'groupes-x'                  => $this->make_hook('xnet',                       AUTH_COOKIE),
             'groupes-x/logo'             => $this->make_hook('xnetlogo',                   AUTH_PUBLIC),
@@ -446,7 +449,6 @@ class ProfileModule extends PLModule
             $page->assign('jobpref', $jobpref);
         }
     }
-
     function handler_ajax_sub_sector(&$page, $id, $ssect, $sssect = -1)
     {
         pl_content_headers("text/html");
@@ -459,6 +461,23 @@ class ProfileModule extends PLModule
         $page->assign('sel', $sssect);
     }
 
+    /**
+     * Page for url "profile/ajax/tree/jobterms". Display a JSon page containing
+     * the sub-branches of a branch in the job terms tree.
+     * @param $page the Platal page
+     * @param $filter filter helps to display only jobterms that are contained in jobs or in mentors
+     *
+     * @param Env::i('jtid') job term id of the parent branch, if none trunk will be used
+     * @param Env::v('attrfunc') the name of a javascript function that will be called when a branch
+     * is chosen
+     * @param Env::v('treeid') tree id that will be given as first argument of attrfunc function
+     * the second argument will be the chosen job term id and the third one the chosen job full name.
+     */
+    function handler_ajax_tree_job_terms(&$page, $filter = JobTerms::ALL)
+    {
+        JobTerms::ajaxGetBranch(&$page, $filter);
+    }
+
     function handler_ajax_alternates(&$page, $id, $sssect)
     {
         pl_content_headers("text/html");
@@ -566,18 +585,12 @@ class ProfileModule extends PLModule
 
         $page->setTitle('Emploi et Carrières');
 
-        // Retrieval of sector names
-        $sectors = XDB::fetchAllAssoc('id', 'SELECT  pjse.id, pjse.name
-                                               FROM  profile_job_sector_enum AS pjse
-                                         INNER JOIN  profile_mentor_sector AS pms ON (pms.sectorid = pjse.id)
-                                           GROUP BY  pjse.id
-                                           ORDER BY  pjse.name');
-        $page->assign_by_ref('sectors', $sectors);
-
         // nb de mentors
-        $res = XDB::query("SELECT count(*) FROM profile_mentor");
+        $res = XDB::query("SELECT count(distinct pid) FROM profile_mentor_term");
         $page->assign('mentors_number', $res->fetchOneCell());
 
+        $page->addJsLink('jquery.autocomplete.js');
+
         // On vient d'un formulaire
         require_once 'ufbuilder.inc.php';
         $ufb = new UFB_MentorSearch();
@@ -627,6 +640,85 @@ class ProfileModule extends PLModule
         $page->assign('list', $it);
     }
 
+    /**
+     * Page for url "referent/autocomplete". Display an "autocomplete" page (plain/text with values
+     * separated by "|" chars) for jobterms in referent (mentor) search.
+     * @see handler_jobterms
+     */
+    function handler_ref_autocomplete(&$page)
+    {
+        $this->handler_jobterms(&$page, 'mentor');
+    }
+
+    /**
+     * Page for url "profile/jobterms" (function also used for "referent/autocomplete" @see
+     * handler_ref_autocomplete). Displays an "autocomplete" page (plain text with values
+     * separated by "|" chars) for jobterms to add in profile.
+     * @param $page the Platal page
+     * @param $type set to 'mentor' to display the number of mentors for each term and order
+     *  by descending number of mentors.
+     *
+     * @param Env::v('q') the text that has been typed and to complete automatically
+     */
+    function handler_jobterms(&$page, $type = 'nomentor')
+    {
+        pl_content_headers("text/plain");
+
+        $q = Env::v('q').'%';
+        $tokens = JobTerms::tokenize($q);
+        if (count($tokens) == 0) {
+            exit;
+        }
+        sort($tokens);
+        $q_normalized = implode(' ', $tokens);
+
+        // try to look in cached results
+        $cache = XDB::query('SELECT  result
+                               FROM  search_autocomplete
+                              WHERE  name = {?} AND
+                                     query = {?} AND
+                                     generated > NOW() - INTERVAL 1 DAY',
+                            $type, $q_normalized);
+        if ($res = $cache->fetchOneCell()) {
+            echo $res;
+            die();
+        }
+
+        $joins = JobTerms::token_join_query($tokens, 'e');
+        if ($type == 'mentor') {
+            $count = ', COUNT(DISTINCT pid) AS nb';
+            $countjoin = ' LEFT JOIN  profile_job_term_relation AS r ON(r.jtid_1 = e.jtid) LEFT JOIN  profile_mentor_term AS m ON(r.jtid_2 = m.jtid)';
+            $countorder = 'nb DESC, ';
+        } else {
+            $count = $countjoin = $countorder = '';
+        }
+        $list = XDB::iterator('SELECT  e.jtid AS id, e.full_name AS field'.$count.'
+                                 FROM  profile_job_term_enum AS e '.$joins.$countjoin.'
+                             GROUP BY  e.jtid
+                             ORDER BY  '.$countorder.'field
+                                LIMIT  11');
+        $nbResults = 0;
+        $res = '';
+        while ($result = $list->next()) {
+            $nbResults++;
+            if ($nbResults == 11) {
+                $res .= $q."|-1\n";
+            } else {
+                $res .= $result['field'].'|';
+                if ($count) {
+                    $res .= $result['nb'].'|';
+                }
+                $res .= $result['id'];
+            }
+            $res .= "\n";
+        }
+        XDB::query('REPLACE INTO  search_autocomplete
+                          VALUES  ({?}, {?}, {?}, NOW())',
+                    $type, $q_normalized, $res);
+        echo $res;
+        exit();
+    }
+
     function handler_xnet(&$page)
     {
         $page->changeTpl('profile/groupesx.tpl');
index ccd7d0d..30cc3f4 100644 (file)
@@ -109,7 +109,8 @@ class ProfileSettingJob extends ProfileSettingGeocoding
                 'tel'     => '',
                 'pub'     => 'private',
                 'comment' => '',
-            )),
+            ),
+            'terms'            => array()),
         );
     }
 
@@ -147,6 +148,26 @@ class ProfileSettingJob extends ProfileSettingGeocoding
                 list($job['sector'], $job['subSector'], $job['subSubSector']) = $res->fetchOneRow();
             }
         }
+        if (count($job['terms'])) {
+            $termsid = array();
+            foreach ($job['terms'] as $term) {
+                if (!$term['full_name']) {
+                    $termsid[] = $term['jtid'];
+                }
+            }
+            if (count($termsid)) {
+                $res = XDB::query("SELECT  jtid, full_name
+                                    FROM  profile_job_term_enum
+                                   WHERE  jtid IN {?}",
+                                 $termsid);
+                $term_id_to_name = $res->fetchAllAssoc('jtid', false);
+                foreach ($job['terms'] as &$term) {
+                    if (!$term['full_name']) {
+                        $term['full_name'] = $term_id_to_name[$term['jtid']];
+                    }
+                }
+            }
+        }
         if ($job['name']) {
             $res = XDB::query("SELECT  id
                                  FROM  profile_job_enum
@@ -233,6 +254,7 @@ class ProfileSettingJob extends ProfileSettingGeocoding
                             WHERE  pid = {?} AND type = 'job'",
                      $page->pid());
         Phone::deletePhones($page->pid(), Phone::LINK_JOB);
+        $terms_values = array();
         foreach ($value as $id => &$job) {
             if (isset($job['name']) && $job['name']) {
                 if (isset($job['jobid']) && $job['jobid']) {
@@ -251,8 +273,17 @@ class ProfileSettingJob extends ProfileSettingGeocoding
                 $address = new ProfileSettingAddress();
                 $address->saveAddress($page->pid(), $id, $job['w_address'], 'job');
                 Phone::savePhones($job['w_phone'], $page->pid(), Phone::LINK_JOB, $id);
+                if (isset($job['terms'])) {
+                    foreach ($job['terms'] as $term) {
+                        $terms_values[] = '('.XDB::escape($page->pid()).', '. XDB::escape($id).', '.XDB::escape($term['jtid']).', "original")';
+                    }
+                }
             }
         }
+        if (count($terms_values) > 0) {
+            XDB::execute('INSERT INTO  profile_job_term (pid, jid, jtid, computed)
+                               VALUES  '.implode(', ', $terms_values));
+        }
     }
 
     public function getText($value) {
@@ -418,6 +449,32 @@ class ProfileSettingJobs extends ProfilePage
             while ($phone = $it->next()) {
                 $this->values['jobs'][$phone->linkId()]['w_phone'][$phone->id()] = $phone->toFormArray();
             }
+            $res = XDB::iterator("SELECT  e.jtid, e.full_name, j.jid AS jobid
+                                    FROM  profile_job_term_enum AS e
+                              INNER JOIN  profile_job_term AS j USING(jtid)
+                                   WHERE  pid = {?}
+                                ORDER BY  j.jid",
+                                 $this->pid());
+            $i = 0;
+            $jobNb = count($this->values['jobs']);
+            while ($term = $res->next()) {
+                $jobid = $term['jobid'];
+                while ($i < $jobNb && $this->values['jobs'][$i]['id'] < $jobid) {
+                    $i++;
+                }
+                if ($i >= $jobNb) {
+                    break;
+                }
+                $job =& $this->values['jobs'][$i];
+                if ($job['id'] != $jobid) {
+                    continue;
+                }
+                if (!isset($job['terms'])) {
+                    $job['terms'] = array();
+                }
+                $job['terms'][] = $term;
+            }
+
             foreach ($this->values['jobs'] as $id => &$job) {
                 $phone = new Phone();
                 if (!isset($job['w_phone'])) {
index 17d9cdd..3aa4140 100644 (file)
  *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA                *
  ***************************************************************************/
 
-class ProfileSettingSectors implements ProfileSetting
+/** Terms associated to profile mentoring */
+class ProfileSettingTerms implements ProfileSetting
 {
     public function value(ProfilePage &$page, $field, $value, &$success)
     {
         $success = true;
         if (is_null($value)) {
             $value = array();
-            $res = XDB::iterRow("SELECT  m.sectorid, m.subsectorid, ss.name
-                                   FROM  profile_mentor_sector      AS m
-                             INNER JOIN  profile_job_sector_enum    AS s  ON (m.sectorid = s.id)
-                             INNER JOIN  profile_job_subsector_enum AS ss ON (s.id = ss.sectorid AND m.subsectorid = ss.id)
-                                  WHERE  m.pid = {?}",
+            $res = XDB::query('SELECT  e.jtid, e.full_name
+                                 FROM  profile_mentor_term   AS m
+                           INNER JOIN  profile_job_term_enum AS e  ON (m.jtid = e.jtid)
+                                WHERE  m.pid = {?}',
                                 $page->pid());
-            while (list($s, $ss, $ssname) = $res->next()) {
-                if (!isset($value[$s])) {
-                    $value[$s] = array($ss => $ssname);
-                } else {
-                    $value[$s][$ss] = $ssname;
-                }
-            }
+            $value = $res->fetchAllAssoc();
         } elseif (!is_array($value)) {
             $value = array();
-        } elseif (count($value) > 10) {
-            Platal::page()->trigError("Le nombre de secteurs d'expertise est limité à 10.");
+        } elseif (count($value) > 20) {
+            Platal::page()->trigError("Le nombre de mots clefs d'expertise est limité à 20.");
             $success = false;
+        } else {
+            $missing_full_names = array();
+            foreach ($value as &$term) if (empty($term['full_name'])) {
+                $missing_full_names[] = $term['jtid'];
+            }
+            if (count($missing_full_names)) {
+                $res = XDB::query('SELECT  jtid, full_name
+                                     FROM  profile_job_term_enum
+                                    WHERE  jtid IN {?}',
+                                    $missing_full_names);
+                $term_id_to_name = $res->fetchAllAssoc('jtid', false);
+                foreach ($value as &$term) {
+                    if (empty($term['full_name'])) {
+                        $term['full_name'] = $term_id_to_name[$term['jtid']];
+                    }
+                }
+            }
         }
         ksort($value);
         foreach ($value as &$sss) {
@@ -55,29 +66,27 @@ class ProfileSettingSectors implements ProfileSetting
     public function save(ProfilePage &$page, $field, $value)
     {
 
-        XDB::execute("DELETE FROM  profile_mentor_sector
+        XDB::execute("DELETE FROM  profile_mentor_term
                             WHERE  pid = {?}",
                      $page->pid());
         if (!count($value)) {
             return;
         }
-        foreach ($value as $id => $sect) {
-            foreach ($sect as $sid => $name) {
-                XDB::execute("INSERT INTO  profile_mentor_sector (pid, sectorid, subsectorid)
-                                   VALUES  ({?}, {?}, {?})",
-                             $page->pid(), $id, $sid);
-            }
+        $mentor_term_values = array();
+        foreach ($value as &$term) {
+            $mentor_term_values[] = '('.XDB::escape($page->pid()).', '.XDB::escape($term['jtid']).')';
         }
+        XDB::execute('INSERT INTO  profile_mentor_term (pid, jtid)
+                           VALUES  '.implode(',', $mentor_term_values));
+
     }
 
     public function getText($value) {
-        $sectors = array();
-        foreach ($value as $sector) {
-            foreach ($sector as $subsector) {
-                $sectors[] = $subsector;
-            }
+        $terms = array();
+        foreach ($value as &$term) {
+            $terms[] = $term['full_name'];
         }
-        return implode(', ', $sectors);
+        return implode(', ', $terms);
     }
 }
 
@@ -132,7 +141,7 @@ class ProfileSettingMentor extends ProfilePage
     {
         parent::__construct($wiz);
         $this->settings['expertise'] = null;
-        $this->settings['sectors'] = new ProfileSettingSectors();
+        $this->settings['terms'] = new ProfileSettingTerms();
         $this->settings['countries'] = new ProfileSettingCountry();
     }
 
@@ -165,9 +174,6 @@ class ProfileSettingMentor extends ProfilePage
 
     public function _prepare(PlPage &$page, $id)
     {
-        $page->assign('sectorList', XDB::iterator('SELECT  id, name
-                                                     FROM  profile_job_sector_enum'));
-
         $page->assign('countryList', XDB::iterator("SELECT  iso_3166_1_a2, countryFR
                                                       FROM  geoloc_countries
                                                   ORDER BY  countryFR"));
index cc12371..2796ed1 100644 (file)
@@ -218,7 +218,7 @@ class SearchModule extends PLModule
             'city'               => DirEnum::LOCALITIES,
             'countryTxt'         => DirEnum::COUNTRIES,
             'entreprise'         => DirEnum::COMPANIES,
-            'secteurTxt'         => DirEnum::SECTORS,
+            'jobtermTxt'         => DirEnum::JOBTERMS,
             'description'        => DirEnum::JOBDESCRIPTION,
             'nationaliteTxt'     => DirEnum::NATIONALITIES,
             'schoolTxt'          => DirEnum::EDUSCHOOLS,
@@ -304,6 +304,22 @@ class SearchModule extends PLModule
           case 'secteur':
             $ids = DirEnum::getOptionsIter(DirEnum::SECTORS);
             break;
+          case 'jobterm':
+            if (Env::has('jtid')) {
+                JobTerms::ajaxGetBranch(&$page, JobTerms::ONLY_JOBS);
+                return;
+            } else {
+                pl_content_headers('text/xml');
+                echo '<div>'; // global container so that response is valid xml
+                echo '<input name="jobtermTxt" type="text" style="display:none" size="32" />';
+                echo '<input name="jobterm" type="hidden"/>';
+                echo '<div class="term_tree"></div>'; // container where to create the tree
+                echo '<script type="text/javascript" src="javascript/jquery.jstree.js"></script>';
+                echo '<script type="text/javascript" src="javascript/jobtermstree.js"></script>';
+                echo '<script type="text/javascript">createJobTermsTree(".term_tree", "search/list/jobterm", "search", "searchForJobTerm");</script>';
+                echo '</div>';
+                exit();
+            }
           default: exit();
         }
         if (isset($idVal)) {
index 66b5614..a322c8d 100644 (file)
           {if $job->user_site} [<a href='{$job->user_site}'>Page perso</a>]{/if}</strong></td>
         </tr>
         {/if}
-        {if $job->sector}
+        {if count($job->terms)}
         <tr>
-          <td><em>Secteur&nbsp;: </em></td>
-          <td><strong>{$job->sector}{if $job->subsector} ({$job->subsector}){/if}</strong></td>
+          <td><em>Mots-clefs&nbsp;: </em></td>
+          <td><ul>
+            {foreach from=$job->terms item=term}
+            <li><strong>{$term->full_name}</strong></li>
+            {/foreach}
+          </ul></td>
         </tr>
         {/if}
 
diff --git a/templates/include/jobterms.branch.tpl b/templates/include/jobterms.branch.tpl
new file mode 100644 (file)
index 0000000..14d6bb9
--- /dev/null
@@ -0,0 +1,41 @@
+{**************************************************************************}
+{*                                                                        *}
+{*  Copyright (C) 2003-2010 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               *}
+{*                                                                        *}
+{**************************************************************************}
+[
+{iterate from=$subTerms item=term}
+  {if $started},{/if}
+  {assign var=started value=1}
+  {ldelim}
+    "data" :
+    {ldelim}
+      "title" : "{$term.name|replace:'"':'\\"'}{if $filter} ({$term.nb} {$filter}{if $term.nb > 1}s{/if}){/if}",
+      "attr" : {ldelim}
+        {if $attrfunc}"href" : "javascript:{$attrfunc}('{$treeid}','{$term.jtid}',\"{$term.full_name|replace:'"':'\\\\\\"'}\")",{/if}
+        "title" : "{$term.full_name|replace:'"':'\\"'}"
+      {rdelim}
+    {rdelim},
+    "attr" : {ldelim} "id" : "job_terms_tree_{$treeid}_{$term.jtid}" {rdelim},
+    "state": "closed"
+  {rdelim}
+{/iterate}
+]
+
+{* vim:set et sw=2 sts=2 sws=2 enc=utf-8: *}
index 0ded2bc..a35e521 100644 (file)
@@ -21,7 +21,7 @@
 {**************************************************************************}
 
 {javascript name=ajax}
-{assign var=sectors value=$profile->getMentoringSectors()}
+{assign var=terms value=$profile->getMentoringTerms()}
 {assign var=countries value=$profile->getMentoringCountries()}
 <div id="fiche">
 <div id="fiche_referent">
@@ -33,7 +33,7 @@
   </div>
   <div class="spacer"></div>
 
-  {if $profile->expertise != '' || $sectors|count || $countries|count }
+  {if $profile->expertise != '' || $terms|count || $countries|count }
   <div id="part">
     <h2>Informations de référent&nbsp;:</h2>
     {if $profile->expertise}
       <span>{$profile->expertise|nl2br}</span>
     </div>
     {/if}
-    {if $sectors|count}
+    {if $terms|count}
     <div class="rubrique_referent">
-      <em>Secteurs&nbsp;:</em><br />
+      <em>Mots-clefs&nbsp;:</em><br />
       <ul>
-        {foreach from=$sectors item="sector" key="i"}
-        <li>{$sector.sector}{if $sector.subsector != ''} ({$sector.subsector}){/if}</li>
+        {foreach from=$terms item="term"}
+        <li>{$term->full_name}</li>
         {/foreach}
       </ul>
     </div>
index 118ad93..97220a7 100644 (file)
@@ -20,6 +20,7 @@
 {*                                                                        *}
 {**************************************************************************}
 
+<script type="text/javascript" src="javascript/jquery.jstree.js"></script>
 {assign var=jobid value="job_"|cat:$i}
 {assign var=jobpref value="jobs[`$i`]"}
 {assign var=sector_text value="sector_text_"|cat:$i}
       <td colspan="2" class="center" style="font-style: italic">Ta place dans l'entreprise</td>
     </tr>
     <tr class="pair {$sector_text}">
-      <td class="titre">Secteur d'activité</td>
-      <td>
-        <input type="text" class="sectorName {if $job.sector_error}error{/if}" size="35" maxlength="100"
-               name="{$jobpref}[subSubSectorName]" value="{$job.subSubSectorName}" />
-        <a href="javascript:displayAllSector({$i})">{icon name="table" title="Tous les secteurs"}</a>
+      <td class="titre">Mots-clefs</td>
+      <td class="job_terms">
+        <input type="text" class="term_search" size="35"/>
+        <a href="javascript:toggleJobTermsTree({$i})">{icon name="table" title="Tous les mots-clefs"}</a>
+        <script type="text/javascript">
+        /* <![CDATA[ */
+        $(function() {ldelim}
+          {foreach from=$job.terms item=term}
+          addJobTerm("{$i}", "{$term.jtid}", "{$term.full_name|replace:'"':'\\"'}");
+          {/foreach}
+          $('#job_{$i} .term_search').autocomplete(platal_baseurl + 'profile/jobterms',
+            {ldelim}
+              "formatItem" : displayJobTerm,
+              "extraParams" : {ldelim} "jobid" : "{$i}" {rdelim},
+              "width" : $('#job_{$i} .term_search').width()*2,
+              "onItemSelect" : selectJobTerm,
+              "matchSubset" : false
+            {rdelim});
+        {rdelim});
+        /* ]]> */
+        </script>
+      </td>
+    </tr>
+    <tr class="pair">
+      <td colspan="2" class="term_tree">
       </td>
     </tr>
     <tr class="pair {$sector}" style="display: none">
index 2139d39..1146519 100644 (file)
@@ -20,6 +20,7 @@
 {*                                                                        *}
 {**************************************************************************}
 
+{javascript name=jobtermstree}
 {foreach from=$jobs item=job key=i}
 {include file="profile/jobs.job.tpl" i=$i job=$job new=false}
 {/foreach}
index 44ea740..56951e6 100644 (file)
@@ -20,6 +20,8 @@
 {*                                                                        *}
 {**************************************************************************}
 
+{javascript name=jobtermstree}
+
 <div>{icon name=information title="Afficher ma fiche référent"}Tu peux consulter ta <a class="popup2" href="referent/{$hrpid}">fiche référent</a> qui n'est accessible que par les X.
 </div>
 {if (!$expertise)||(!($sectors|@count))}
   </tr>
 </table>
 
+<script type="text/javascript" src="javascript/jquery.jstree.js"></script>
+
 <table class="bicol" style="margin-bottom: 1em" summary="Profil&nbsp;: Mentoring">
   <tr>
-    <th>
+    <th colspan="2">
       <div class="flags" style="float: left">
         <input type="checkbox" name="accesX" checked="checked" disabled="disabled" />
         {icon name="flag_red" value="privé"}
       </div>
-      Secteurs d'activité dans lesquels tu as beaucoup exercé
+      Mots clefs qui représentent le mieux ton expérience
     </th>
   </tr>
   <tr>
-    <td id="sectorSelection">
-      <div style="float: left; width: 30%" class="titre">Secteur</div>
-      <select name="sectorSelection" onchange="updateSector()">
-        <option value="">&nbsp;</option>
-        {iterate from=$sectorList item=sector}
-        <option value="{$sector.id}">{$sector.name}</option>
-        {/iterate}
-      </select>
-    </td>
+    <td colspan="2">
+      Il est préférable de mentionner des notions précises : <em>Pizzaïolo</em> plutôt que <em>Hôtellerie</em>.
+      En effet Les recherches sur le mot-clef <em>Hôtellerie</em> te trouveront dans les deux cas mais une
+      recherche sur <em>Production culinaire</em> ou <em>Pizzaïolo</em> non.
+    <td/>
   </tr>
   <tr>
-    <td>
-      <div style="float: left; width: 30%" class="titre">Sous-secteur</div>
-      <span id="subSectorSelection"></span>
+    <td class="titre" style="width:30%">Mots-clefs</td>
+    <td class="job_terms">
+      <input type="text" class="term_search" size="35"/>
+      <a href="javascript:toggleJobTermsTree(-1)">{icon name="table" title="Tous les mots-clefs"}</a>
+      <script type="text/javascript">
+      /* <![CDATA[ */
+      $(function() {ldelim}
+        {foreach from=$terms item=term}
+        addJobTerm(-1, "{$term.jtid}", "{$term.full_name|replace:'"':'\\"'}");
+        {/foreach}
+        $('.term_search').autocomplete(platal_baseurl + 'profile/jobterms',
+          {ldelim}
+            "formatItem" : displayJobTerm,
+            "extraParams" : {ldelim} "jobid" : "-1" {rdelim},
+            "width" : $('.term_search').width()*2,
+            "onItemSelect" : selectJobTerm,
+            "matchSubset" : false
+          {rdelim});
+      {rdelim});
+      /* ]]> */
+      </script>
     </td>
   </tr>
-  <tr class="pair">
-    <td id="sectors">
-      {if $sectors|@count}
-      {foreach from=$sectors item=sector key=s}
-      {foreach from=$sector item=subSector key=ss}
-      <div id="sectors_{$s}_{$ss}" style="clear: both; margin-top: 0.5em" class="titre">
-        <a href="javascript:removeSector('{$s}','{$ss}')" style="display: block; float: right">
-          {icon name=cross title="Supprimer ce secteur"}
-        </a>
-        <input type="hidden" name="sectors[{$s}][{$ss}]" value="{$subSector}" />
-        {$subSector}
-      </div>
-      {/foreach}
-      {/foreach}
-      {/if}
+  <tr>
+    <td colspan="2" class="term_tree">
     </td>
   </tr>
 </table>
index 5c04863..0da1170 100644 (file)
@@ -34,6 +34,8 @@ Actuellement, {$mentors_number} mentors et référents se sont déclarés sur {#
 </p>
 
 {javascript name=ajax}
+{javascript name=jquery.jstree}
+{javascript name=jobtermstree}
 <script type="text/javascript">//<![CDATA[
 
 var baseurl = platal_baseurl + "referent/";
@@ -64,6 +66,12 @@ function setSSectors()
     Ajax2.update_html('country_chg', baseurl + 'country/' + sect + '/' + ssect);
 }
 
+function toggleJobTermsTree()
+{
+  $('#mentoring_terms').closest('tr').toggle();
+  return false;
+}
+
 {/literal}
 //]]></script>
 
@@ -71,35 +79,17 @@ function setSSectors()
   <table cellpadding="0" cellspacing="0" summary="Formulaire de recherche de referents" class="bicol">
     <tr class="impair">
       <td class="titre">
-        Secteur de compétence<br />du référent
+        Mot-clef&nbsp;:
       </td>
       <td>
-        <select name="sector" id="sect_field" onchange="setSector(this.value)">
-          <option name=""></option>
-          {html_options options=$sectors selected=$sectorSelection}
-        </select>
+        <input type="text" name="jobterm_text" id="term_search" size="32"/>
+        <input type="hidden" name="jobterm" />
+        <a id="jobTermsTreeToggle" href="#">{icon name=table title="Tous les mots-clefs"}</a>
       </td>
     </tr>
-    <tr class="impair" style="display: none" id="scat">
-      <td class="titre">
-        Sous-secteur
-      </td>
-      <td id="ssect_chg">
-      </td>
-    </tr>
-    <tr class="pair" style="display: none" id="country">
-      <td class="titre">
-        Pays bien connu du référent
-      </td>
-      <td id="country_chg">
-      </td>
-    </tr>
-    <tr class="impair" style="display: none" id="keywords">
-      <td class="titre">
-        Expertise (rentre un ou plusieurs mots clés)
-      </td>
-      <td >
-        <input type="text" name="expertise" size="30" value="{$expertise_champ}" />
+    <tr class="impair" style="display:none">
+      <td colspan="2">
+        <div id="mentoring_terms"></div>
       </td>
     </tr>
   </table>
@@ -109,6 +99,17 @@ function setSSectors()
 </form>
 
 <script type="text/javascript">
-setSector(document.getElementById('sect_field').value);
+{literal}
+$(function() {
+  createJobTermsTree('#mentoring_terms', 'profile/ajax/tree/jobterms/mentors', 'mentor', 'searchForJobTerm');
+  $("#term_search").autocomplete(baseurl + "autocomplete",
+  {
+    "selectOnly":1,
+    "matchSubset":0,
+    "width":$("#term_search").width()
+  });
+  $('#jobTermsTreeToggle').click(toggleJobTermsTree);
+});
+{/literal}
 </script>
 {* vim:set et sw=2 sts=2 sws=2 enc=utf-8: *}
index ff73a1c..5521a00 100644 (file)
       });
   }
 
+  // when choosing a job term in tree, hide tree and set job term field
+  function searchForJobTerm(treeid, jtid, full_name) {
+    $(".term_tree").remove();
+    $("input[name='jobtermTxt']").val(full_name).addClass("hidden_valid").show();
+    $("input[name='jobterm']").val(jtid);
+  }
+
   // when choosing autocomplete from list, must validate
   function select_autocomplete(name) {
       nameRealField = name.replace(/Txt$/, '');
       <td><input type="text" class="autocomplete" name="description" size="32" value="{$smarty.request.description}" /></td>
     </tr>
     <tr>
-      <td>Secteur</td>
+      <td>Mots-clefs</td>
       <td>
-        <input name="secteurTxt" type="text" class="autocomplete" style="display:none" size="32"
-               value="{$smarty.request.secteurTxt}"/>
-        <input name="secteur" class="autocompleteTarget" type="hidden" value="{$smarty.request.secteur}"/>
-        <a href="secteur" class="autocompleteToSelect">{icon name="table" title="Tous les secteurs"}</a>
+        <input name="jobtermTxt" type="text" class="autocomplete{if $smarty.request.jobterm} hidden_valid{/if}" style="display:none" size="32"
+               value="{$smarty.request.jobtermTxt}"/>
+        <input name="jobterm" class="autocompleteTarget" type="hidden" value="{$smarty.request.jobterm}"/>
+        <a href="jobterm" class="autocompleteToSelect">{icon name="table" title="Tous les mots-clefs"}</a>
       </td>
     </tr>
     <tr>
index 31bea52..bbe2c5f 100755 (executable)
@@ -1,13 +1,13 @@
 #!/usr/bin/php5
 <?php
 require_once 'connect.db.inc.php';
-require_once 'profil.func.inc.php';
+require_once 'class/jobterms.php';
 
 $globals->debug = 0; //do not store backtraces
 
 $terms = XDB::iterator('SELECT `jtid`, `name` FROM `profile_job_term_enum`');
 while ($term = $terms->next()) {
-    $tokens = array_unique(tokenize_job_term($term['name']));
+    $tokens = array_unique(JobTerms::tokenize($term['name']));
     if (!count($tokens)) {
         continue;
     }