<repo_name>HuguesGithub/hj-zombicide-v2<file_sep>/core/bean/KeywordBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe KeywordBean
 * @author Hugues
 * @since 1.0.00
 * @version 1.04.27
 */
class KeywordBean extends LocalBean
{
  /**
   * Class Constructor
   * @param Keyword $Keyword
   */
  public function __construct($Keyword='')
  {
    parent::__construct();
    $this->Keyword = ($Keyword=='' ? new Keyword() : $Keyword);
    $this->EquipmentKeywordServices = new EquipmentKeywordServices();
  }
  /**
   * @param string $tBodyButtons Template des Boutons de fin de ligne
   * @return string
   */
  public function getRowForAdminPage($tBodyButtons)
  {
    $Keyword = $this->Keyword;
    $arrF = array(self::FIELD_KEYWORDID=>$Keyword->getId());
    $Equipments = $this->EquipmentKeywordServices->getEquipmentKeywordsWithFilters($arrF);
    $nb = count($Equipments);
    $queryArg = array(
      self::CST_ONGLET=>'parametre',
      self::CST_POSTACTION=>'edit',
      'table'=>'keyword',
      'id'=>$Keyword->getId()
    );
    $urlEdit = $this->getQueryArg($queryArg);
    $queryArg[self::CST_POSTACTION] = 'trash';
    $urlTrash = $this->getQueryArg($queryArg);
    $args = array(
      $nb.' Carte'.($nb>1?'s':'').' Equipement',
      $urlEdit,
      $urlTrash,
    );
    $tBody = '<tr><td>'.$Keyword->getId().'</td><td>'.$Keyword->getName().'</td><td>'.$Keyword->getDescription().'</td>';
    return $tBody.vsprintf($tBodyButtons, $args).'</tr>';
  }
}
<file_sep>/core/bean/OrigineBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe OrigineBean
 * @author Hugues
 * @since 1.07.25
 * @version 1.07.25
 */
class OrigineBean extends LocalBean
{

  /**
   * @param Expansion $Origine
   */
  public function __construct($Origine=null)
  {
    parent::__construct();
    $this->Origine = ($Origine==null ? new Origine() : $Origine);
    $this->OrigineServices = new OrigineServices();
  }

  public static function getStaticSelect($name, $selId=0)
  {
    $OrigineBean = new OrigineBean();
    return $OrigineBean->getSelect($name, $selId);
  }
  public function getSelect($name, $selId)
  {
    $Origines = $this->OrigineServices->getOriginesWithFilters();
    $strOptions = $this->getBalise(self::TAG_OPTION, 'Toutes Origines', array(self::ATTR_VALUE=>0));
    while (!empty($Origines)) {
      $Origine = array_shift($Origines);
      $args = array(self::ATTR_VALUE=>$Origine->getId());
      if ($Origine->getId()==$selId) {
        $args[self::ATTR_SELECTED] = self::CST_SELECTED;
      }
      $strOptions .= $this->getBalise(self::TAG_OPTION, $Origine->getName(), $args);
    }
    return $this->getBalise(self::TAG_SELECT, $strOptions, array(self::ATTR_NAME=>$name, self::ATTR_ID=>'filter-by-'.$name));
  }

}
<file_sep>/core/bean/WpPageEquipmentsBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe WpPageEquipmentsBean
 * @author Hugues
 * @since 1.04.15
 * @version 1.04.28
 */
class WpPageEquipmentsBean extends WpPageBean
{
  protected $urlTemplate = 'web/pages/public/wppage-equipments.php';
  /**
   * Class Constructor
   * @param WpPage $WpPage
   */
  public function __construct($WpPage='')
  {
    parent::__construct($WpPage);
    $this->EquipmentServices          = new EquipmentServices();
    $this->EquipmentExpansionServices = new EquipmentExpansionServices();
    $this->ExpansionServices          = new ExpansionServices();
  }
  /**
   * @return string
   */
  public function getContentPage()
  {
    /////////////////////////////////////////////////////////////////////////////
    // On construit la liste déroulante des Extensions ayant des cartes Equipements
    $Expansions = $this->ExpansionServices->getExpansionsWithFilters(array(), self::FIELD_DISPLAYRANK);
    $strExpansions = '';
    foreach ($Expansions as $Expansion) {
      $id = $Expansion->getId();
      $arrFilters = array(self::FIELD_EXPANSIONID=>$id);
      $EquipmentExpansions = $this->EquipmentExpansionServices->getEquipmentExpansionsWithFilters($arrFilters);
      // Si on n'a pas de carte Equipement rattachée, on n'a pas besoin d'afficher cette extension.
      if (empty($EquipmentExpansions)) {
        continue;
      }
      // On peut ajouter l'Extension au menu pour filtrer.
      $strExpansions .= $this->getBalise(self::TAG_OPTION, $Expansion->getName(), array(self::ATTR_VALUE=>'set-'.$id));

      /////////////////////////////////////////////////////////////////////////////
      // On récupère l'ensemble des cartes de l'extension.
      foreach ($EquipmentExpansions as $EquipmentExpansion) {
        $EquipmentCard = $this->EquipmentServices->selectEquipment($EquipmentExpansion->getEquipmentCardId());
        $niceName = $EquipmentCard->getNiceName().'-'.$EquipmentCard->getId().'-'.$id;
        $EquipmentCard->setExpansionId($id);
        // Par contre, vu qu'on s'appuye sur la tablede jointure, on ne peut pas directement trier les cartes.
        // On les stocke donc temporairement.
        $EquipmentCardsToDisplay[$niceName] = $EquipmentCard;
      }
    }

    /////////////////////////////////////////////////////////////////////////////
    // On construit la liste des cartes à afficher.
    $strCartes = '';
    if (!empty($EquipmentCardsToDisplay)) {
      // On trie les cartes selon leur nom.
      ksort($EquipmentCardsToDisplay);
      foreach ($EquipmentCardsToDisplay as $name => $EquipmentCard) {
        list(, , $id) = explode('-', $name);
        $EquipmentBean = new EquipmentBean($EquipmentCard);
        $strCartes .= $EquipmentBean->displayCard($id);
      }
    }

    /////////////////////////////////////////////////////////////////////////////
    // On construit la liste déroulante pour les filtres "mots-clés" (mais pas que).
    $arr = array(
      'weapon'=>'Armes',
      'melee'=>'Armes de Mêlée',
      'ranged'=>'Armes A distance',
      'pimp'=>'Armes Pimp',
      'dual'=>'Armes Dual',
      'starter'=>'Equipement de départ'
    );
    $strCategories  = '';
    foreach ($arr as $key => $value) {
      $strCategories .= $this->getBalise(self::TAG_OPTION, $value, array(self::ATTR_VALUE=>$key));
    }

    //////////////////////////////////////////////////////////////////
    // On enrichi le template puis on le restitue.
    $args = array(
      // Liste des cartes - 1
      $strCartes,
      // Options de sélection des extensions - 2
      $strExpansions,
      // Options de sélection de catégories. - 3
      $strCategories,
      '', '', '', '', '',
    );
    return $this->getRender($this->urlTemplate, $args);
  }

  /**
   * @param array $post
   */
  public function setFilters($post=null)
  { parent::setBeanFilters($post, self::FIELD_NAME); }

}
<file_sep>/core/domain/MissionRule.class.php

<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe MissionRule
 * @author Hugues.
 * @since 1.04.08
 * @version 1.04.28
 */
class MissionRule extends LocalDomain
{
  /**
   * Id technique de la donnée
   * @var int $id
   */
  protected $id;
  /**
   * Id technique de la Mission
   * @var int $missionId
   */
  protected $missionId;
  /**
   * Id technique de la Règle
   * @var int $ruleId
   */
  protected $ruleId;
  /**
   * titre de la règle
   * @var string $title
   */
  protected $title;
  /**
   * @return int
   */
  public function getId()
  { return $this->id; }
  /**
   * @return int
   */
  public function getMissionId()
  { return $this->missionId; }
  /**
   * @return int
   */
  public function getRuleId()
  { return $this->ruleId; }
  /**
   * @return string
   */
  public function getTitle()
  { return $this->title; }
  /**
   * @param int $id
   */
  public function setId($id)
  { $this->id = $id; }
  /**
   * @param int $missionId
   */
  public function setMissionId($missionId)
  { $this->missionId = $missionId; }
  /**
   * @param int $ruleId
   */
  public function setRuleId($ruleId)
  { $this->ruleId = $ruleId; }
  /**
   * @param string $title
   */
  public function setTitle($title)
  { $this->title = $title; }
  /**
   * @return array
   */
  public function getClassVars()
  { return get_class_vars('MissionRule'); }
  /**
   * @param array $row
   * @param string $a
   * @param string $b
   * @return MissionRule
   */
  public static function convertElement($row, $a='', $b='')
  { return parent::convertElement(new MissionRule(), self::getClassVars(), $row); }

}
<file_sep>/core/bean/SkillBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe SkillBean
 * @author Hugues
 * @since 1.00.00
 * @version 1.05.12
 */
class SkillBean extends LocalBean
{
  protected $urlRowAdmin  = 'web/pages/admin/fragments/skill-row.php';
  protected $urlRowPublic = 'web/pages/public/fragments/skill-row.php';
  /**
   * @param Skill $Skill
   */
  public function __construct($Skill=null)
  {
    parent::__construct();
    $this->Skill = ($Skill==null ? new Skill() : $Skill);
    $this->SurvivorSkillServices = new SurvivorSkillServices();
  }

  //////////////////////////////////////////////////////////////////////////
  // Différentes modes de présentation
  /**
   * @return string
   */
  public function getRowForAdminPage()
  {
    ///////////////////////////////////////////////////////////////
    // On enrichit le template et on le retourne.
    $args = array(
      // Identifiant de la Competence - 1
      $this->Skill->getId(),
      // Code de la Compétence - 2
      $this->Skill->getCode(),
      // Url d'édition du WpPost - 3
      $this->Skill->getWpPostEditUrl(),
      // Url d'édition BDD - 4
      $this->Skill->getEditUrl(self::CST_SKILL),
      // Url publique de l'Article - 5
      $this->Skill->getWpPostUrl(),
      // Nom de la Compétence - 6
      $this->Skill->getName(),
      // Description du Skill - 7
      $this->Skill->getDescription(),
      // Officiel ou non ? - 8
      ($this->Skill->getExpansion()->isOfficial() ? 'Oui' : 'Non'),
    );
    ///////////////////////////////////////////////////////////////
    // Puis on le retourne
    return $this->getRender($this->urlRowAdmin, $args);
  }
  /**
   * @return string
   */
  public function getRowForPublicPage()
  {
    ///////////////////////////////////////////////////////////////
    // On enrichi le template et on le retourne.
    $args = array(
      // Front Url de la Compétence - 1
      $this->Skill->getWpPostUrl(),
      // Nom de la Compétence - 2
      $this->Skill->getName(),
      // Nombre de Compétences possédées par un Survivant, un Zombivant ou un Ultimate par niveau - 3
      $this->getSkillCartouches(),
      // Description de la Compétence - 4
      $this->Skill->getDescription(),
      // Identifiant de la Compétence - 5
      $this->Skill->getId(),
    );
    return $this->getRender($this->urlRowPublic, $args);
  }

  private function getSkillCartouches()
  {
    ///////////////////////////////////////////////////////////////
    // Construction des éléments de la colonne Niveau
    $strSkillsCartouches = '';
    $arrTags = array(
      self::LVL_BLUE   => array(10, 11),
      self::LVL_YELLOW => array(20),
      self::LVL_ORANGE => array(30, 31),
      self::LVL_RED    => array(40, 41, 42),
    );
    $arrLvls = array(1=>'S', 2=>'Z', 3=>'U', 4=>'UZ');
    foreach ($arrTags as $key => $value) {
      foreach ($arrLvls as $k => $v) {
        $nb = $this->getNbSkillsByTag($value, $k);
        if ($nb!=0) {
          $strSkillsCartouches .= $this->getBalise(self::TAG_SPAN, $v.' : '.$nb, array(self::ATTR_CLASS=>'badge badge-'.$key.'-skill'));
        }
      }
    }
    return $strSkillsCartouches;
  }
  /**
   * @param array $arrTags Liste des tags dont on veut le nombre de couples SurvivorSkill
   * @return int
   */
  private function getNbSkillsByTag($arrTags, $type=1)
  {
    $arrFilters = array(
      self::FIELD_SKILLID        => $this->Skill->getId(),
      self::FIELD_SURVIVORTYPEID => $type,
    );
    $nb = 0;
    // Pour chaque tag, on fait une recherche en base et on cumule le nombre que l'on renvoie.
    while (!empty($arrTags)) {
      $arrFilters[self::FIELD_TAGLEVELID] = array_shift($arrTags);
      $SurvivorSkills = $this->SurvivorSkillServices->getSurvivorSkillsWithFilters($arrFilters);
      $nb += count($SurvivorSkills);
    }
    return $nb;
  }
}
<file_sep>/core/domain/MissionExpansion.class.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe MissionExpansion
 * @author Hugues.
 * @since 1.04.27
 * @version 1.05.02
 */
class MissionExpansion extends LocalDomain
{
  /**
   * Id technique de la jointure
   * @var int $id
   */
  protected $id;
  /**
   * Id technique de la Mission
   * @var int $missionId
   */
  protected $missionId;
  /**
   * Id technique de l'Expansion
   * @var int $expansionId
   */
  protected $expansionId;
  /**
   * @return int
   */
  public function getId()
  {return $this->id; }
  /**
   * @ return int
   */
  public function getMissionId()
  { return $this->MissionId; }
  /**
   * @ return int
   */
  public function getExpansionId()
  { return $this->expansionId; }
  /**
   * @param int $id
   */
  public function setId($id)
  { $this->id=$id; }
  /**
   * @param int $missionId
   */
  public function setMissionId($missionId)
  { $this->missionId = $missionId; }
  /**
   * @param int $expansionId
   */
  public function setExpansionId($expansionId)
  { $this->expansionId = $expansionId; }
  /**
   * @return array
   */
  public function getClassVars()
  { return get_class_vars('MissionExpansion'); }
  /**
   * @param array $row
   * @param string $a
   * @param string $b
   * @return MissionExpansion
   */
  public static function convertElement($row, $a='', $b='')
  { return parent::convertElement(new MissionExpansion(), self::getClassVars(), $row); }
  /**
   * @return array EquipmentExpansion
   */
  public function getEquipmentExpansions()
  {
    if ($this->EquipmentExpansions == null) {
      $arrFilters = array(self::FIELD_EXPANSIONID=>$this->expansionId);
      $this->EquipmentExpansions = $this->EquipmentExpansionServices->getEquipmentExpansionsWithFilters($arrFilters);
    }
    return $this->EquipmentExpansions;
  }
  /**
   * @param Expansion $Expansion
   */
  public function setExpansion($Expansion)
  { $this->Expansion=$Expansion; }

}
<file_sep>/core/services/WeaponProfileServices.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe WeaponProfileServices
 * @author Hugues.
 * @since 1.04.27
 * @version 1.04.27
 */
class WeaponProfileServices extends LocalServices
{
  /**
   * L'objet Dao pour faire les requêtes
   * @var DurationDaoImpl $Dao
   */
  protected $Dao;
  /**
   * Class Constructor
   */
  public function __construct()
  {
    parent::__construct();
    $this->Dao = new WeaponProfileDaoImpl();
  }
}
<file_sep>/core/domain/Tile.class.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe Tile
 * @author Hugues.
 * @since 1.04.07
 * @version 1.07.22
 */
class Tile extends LocalDomain
{
  /**
   * Id technique de la donnée
   * @var int $id
   */
  protected $id;
  /**
   * Id technique de l'Expansion
   * @var int $expansionId
   */
  protected $expansionId;
  /**
   * Code de la Dalle
   * @var string $code
   */
  protected $code;
  protected $coordPoly;
  protected $zoneType;
  /**
   * Sans doute à virer...
   * @var string $zoneAcces
   */
  protected $zoneAcces;
  /**
   * La Dalle est elle active ?
   * @var int $active
   */
  protected $activeTile;
  protected $oCode;
  protected $side_top;
  protected $side_right;
  protected $side_bottom;
  protected $side_left;

  /**
   * @return int
   */
  public function getId()
  {return $this->id; }
  /**
   * @return string
   */
  public function getCode()
  { return $this->code; }
  /**
   * @return int
   */
  public function getExpansionId()
  { return $this->expansionId; }
  /**
   * @return string
   */
  public function getZoneAcces()
  { return $this->zoneAcces; }

  public function getOCode()
  { return $this->oCode; }
  public function getSideTop()
  { return $this->side_top; }
  public function getSideRight()
  { return $this->side_right; }
  public function getSideBottom()
  { return $this->side_bottom; }
  public function getSideLeft()
  { return $this->side_left; }

  /**
   * @param int $id
   */
  public function setId($id)
  { $this->id=$id; }
  /**
   * @param string $code
   */
  public function setCode($code)
  { $this->code=$code; }
  /**
   * @param int $expansionId
   */
  public function setExpansionId($expansionId)
  { $this->expansionId=$expansionId; }
  /**
   * @param string $zoneAcces
   */
  public function setZoneAcces($zoneAcces)
  { $this->zoneAcces=$zoneAcces; }
  /**
   * @return array
   */
  public function getClassVars()
  { return get_class_vars('Tile'); }
  /**
   * @param array $row
   * @param string $a
   * @param string $b
   * @return Tile
   */
  public static function convertElement($row, $a='', $b='')
  { return parent::convertElement(new Tile(), self::getClassVars(), $row); }
  /**
   * @return string
   */
  public function getImgUrl()
  { return '/wp-content/plugins/hj-zombicide/web/rsc/img/tiles/'.$this->code.'-500px.png'; }
  /**
   * @return string
   */
  public function getDimensions()
  {
    list($width, $height, ,) = getimagesize('http://zombicide.jhugues.fr'.$this->getImgUrl());
    return $width.'px x '.$height.'px';
  }
  /**
   * @param array $row
   * @return Tile
   */
  public static function convertElementFromPost($row)
  {
    $Obj = new Tile();
    $vars = get_class_vars('Tile');
    if (!empty($vars)) {
      foreach ($vars as $key => $value) {
        $Obj->setField($key, str_replace("\\", '', $row[$key]));
      }
      if ($row['active']=='on') {
        $Obj->setField('active', 1);
      }
    }
    return $Obj;
  }
}
<file_sep>/core/actions/ExpansionActions.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * ExpansionActions
 * @author Hugues
 * @since 1.04.30
 * @version 1.08.01
 */
class ExpansionActions extends LocalActions
{
  /**
   * Constructeur
   */
  public function __construct($post=array())
  {
    parent::__construct();
    $this->post = $post;
    $this->ExpansionServices  = new ExpansionServices();
    $this->SurvivorServices   = new SurvivorServices();
    $this->WpPostServices     = new WpPostServices();
  }
  /**
   * Point d'entrée des méthodes statiques.
   * @param array $post
   * @return string
   **/
  public static function dealWithStatic($post)
  {
    $returned = '';
    $Act = new ExpansionActions($post);
    switch ($post[self::CST_AJAXACTION]) {
      case self::AJAX_EXPANSIONVERIF :
        $returned = $Act->dealWithExpansionVerif(true);
      break;
      case self::AJAX_GETEXPANSIONS  :
        $returned = $Act->dealWithGetExpansions();
      break;
      default :
        $returned  = 'Erreur dans ExpansionActions > dealWithStatic, '.$_POST[self::CST_AJAXACTION].' inconnu.';
      break;
    }
    return $returned;
  }

  /**
   * Récupération du contenu de la page via une requête Ajax.
   * @param array $post
   * @return string
   */
  public function dealWithGetExpansions()
  {
    $Bean = new WpPageExpansionsBean();
    $Bean->setFilters($this->post);
    return $this->jsonString($Bean->getListContentPage(), self::PAGE_EXTENSION, true);
  }

  //////////////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////////
  // Bloc de gestion de vérifications des Extensions en Home Admin
  /**
   * @param boolean $isVerif
   * @return string
   */
  public function dealWithExpansionVerif($isVerif=false)
  {
    // On récupère les articles d'extensions
    $args = array(
      self::WP_CAT         => self::WP_CAT_EXPANSION_ID,
      self::WP_TAXQUERY    => array(),
      self::WP_POSTSTATUS  => self::WP_PUBLISH.', future, pending',
    );
    $this->WpPostExpansions = $this->WpPostServices->getArticles($args);
    $nbWpPostExpansions = count($this->WpPostExpansions);
    // Et les extensions en base
    $this->Expansions = $this->ExpansionServices->getExpansionsWithFilters();
    $nbExpansions = count($this->Expansions);

    if ($isVerif) {
      $this->checkExpansions();
      $strBilan = $this->jsonString($this->strBilan, self::AJAX_EXPANSIONVERIF, true);
    } elseif ($nbWpPostExpansions!=$nbExpansions) {
      $strBilan  = "Le nombre d'articles ($nbWpPostExpansions) ne correspond pas au nombre d'extensions en base ($nbExpansions).<br>";
      $strBilan .= "Une vérification est vivement conseillée.";
    } else {
      $strBilan = "Le nombre d'articles ($nbWpPostExpansions) correspond au nombre d'extensions en base.";
    }
    return $strBilan;
  }
  private function checkExpansions()
  {
    $hasErrors = false;
    $strErrors = '';
    $this->strBilan  = "Début de l'analyse des données relatives aux Extensions.<br>";
    $this->strBilan .= "Il y a ".count($this->WpPostExpansions)." articles d'Extensions.<br>";
    $this->strBilan .= "Il y a ".count($this->Expansions)." entrées en base.<br>";
    /////////////////////////////////////////////////////////////////////
    // On va réorganiser les Expansions pour les retrouver facilement
    $arrExpansions = array();
    while (!empty($this->Expansions)) {
      $Expansion = array_shift($this->Expansions);
      if (isset($arrExpansions[$Expansion->getCode()])) {
        $strErrors .= "Le code <em>".$Expansion->getCode()."</em> semble être utilisé deux fois dans la base de données.<br>";
        $hasErrors = true;
      }
      $arrExpansions[$Expansion->getCode()] = $Expansion;
    }
    /////////////////////////////////////////////////////////////////////

    /////////////////////////////////////////////////////////////////////
    while (!empty($this->WpPostExpansions)) {
      // On regarde les articles créés et on vérifie les données en base, si elles existent et si elles sont cohérentes entre elles.
      // On récupère le WpPost et ses données
      $this->WpPost = array_shift($this->WpPostExpansions);
      $code = $this->WpPost->getPostMeta(self::FIELD_CODE);
      if (!isset($arrExpansions[$code])) {
        // A priori l'article n'a pas de code associé en base. Il faut donc en créé un qui corresponde
        $Expansion = new Expansion();
        $Expansion->setCode($code);
        $name = $this->WpPost->getPostTitle();
        $Expansion->setName($name);
        $Expansion->setDisplayRank($this->WpPost->getPostMeta(self::FIELD_DISPLAYRANK));
        $Expansion->setOfficial($this->WpPost->getPostMeta(self::FIELD_OFFICIAL));
        // On insère la donnée et on log dans le bilan
        $this->ExpansionServices->insertExpansion($Expansion);
        $this->strBilan .= "L'article <em>".$name."</em> a été créé en base.<br>";
        continue;
      }
      $Expansion = $arrExpansions[$code];
      unset($arrExpansions[$code]);
      $this->checkExpansion($Expansion);
    }
    /////////////////////////////////////////////////////////////////////
    // On vérifie que la totalité des Extensions en base ont été utilisées. Si ce n'est pas le cas, il faut créer des articles correspondants.
    if (!empty($arrExpansions)) {
      $this->strBilan .= "On a des données en base qui n'ont pas d'article correspondant.<br>";
      while (!empty($arrExpansions)) {
        $Expansion = array_shift($arrExpansions);
        $this->strBilan .= '<br>Article à créer pour une Extension  : '.$Expansion->getName().' ['.$Expansion->toJson().'].<br>';
      }
    }
    /////////////////////////////////////////////////////////////////////
    $this->strBilan .= "Fin de l'analyse des données relatives aux Extensions.<br>";
    if ($hasErrors) {
      $this->strBilan .= "Anomalies constatées :<br>".$strErrors;
    } else {
      $this->strBilan .= "Aucune anomalie constatée.";
    }
  }
  private function checkExpansion($Expansion)
  {
    $doUpdate = false;
    // On initialise les données de l'article
    $name         = $this->WpPost->getPostTitle();
    $displayRank  = $this->WpPost->getPostMeta(self::FIELD_DISPLAYRANK);
    $official     = $this->WpPost->getPostMeta(self::FIELD_OFFICIAL);
    // On vérifie si la donnée en base correspond à l'article.
    $strError = '';
    if ($Expansion->getName()!=$name) {
      $Expansion->setName($name);
      $strError .= "Le Nom a été mis à jour.<br>";
      $doUpdate = true;
    }
    if ($Expansion->getDisplayRank()!=$displayRank) {
      $Expansion->setDisplayRank($displayRank);
      $strError .= "Le Rang d'affichage a été mis à jour.<br>";
      $doUpdate = true;
    }
    if ($Expansion->isOfficial()!=$official) {
      $Expansion->setOfficial($official);
      $strError .= "Le statut Officiel a été mis à jour.<br>";
      $doUpdate = true;
    }
    // On peut aussi envisager de mettre à jour les chaps nbSurvivants et nbMissions...
    $Survivors = $this->SurvivorServices->getSurvivorsWithFilters(array(self::FIELD_EXPANSIONID=>$Expansion->getId()));
    if ($Expansion->getNbSurvivants()!=count($Survivors)) {
      $Expansion->setNbSurvivants(count($Survivors));
      $strError .= "Le Nombre de Survivants a été mis à jour.<br>";
      $doUpdate = true;
    }
    // Vérifions le nombre de Dalles
    $Tiles = $Expansion->getTiles();
    // On exclus les sets de Dalles. Parce que une Dalle est rattachée à une Extension, et les sets de Dalles proposent les mêmes Dalles pour une autre Extension.
    $doNotCheckTilesForExpansions = array(59, 60, 61, 62, 40);
    if ($Expansion->getNbDalles()!=count($Tiles) && !in_array($Expansion->getId(), $doNotCheckTilesForExpansions)) {
      $this->strBilan .= "Le nombre de Dalles pour l'Extension <em>".$name."</em> ne correspond pas. Nb dans le champ : ".$Expansion->getNbDalles().". Nb donnés par la requête : ".count($Tiles).".<br>";
      // Note : On ne met à jour que si la requête renvoie plus que la donnée du champ.
      if ($Expansion->getNbDalles()<count($Tiles)) {
        $Expansion->setNbDalles(count($Tiles));
        $doUpdate = true;
      }
    }
    // Vérifions le nombre de Missions
    // TODO : Données à mettre à jour dans les articles. Les champs "Dalles" des WpPost de type Missions doivent être renseignés
    $Missions = $Expansion->getMissions();
    if ($Expansion->getNbMissions()!=count($Missions) && $Expansion->getNbMissions()<count($Missions)) {
      //$this->strBilan .= "Le nombre de Missions pour l'Extension <em>".$name."</em> ne correspond pas. Nb dans le champ : ".$Expansion->getNbMissions().". Nb donnés par la requête : ".count($Missions).".<br>";
      // Note : On ne met à jour que si la requête renvoie plus que la donnée du champ.
      $Expansion->setNbMissions(count($Missions));
      $doUpdate = true;
    }
    if ($doUpdate) {
      // Si nécessaire, on update en base.
      $this->ExpansionServices->updateExpansion($Expansion);
      $this->strBilan .= "Les données de l'Extension <em>".$name."</em> ont été mises à jour.<br>".$strError;
    }
  }
  // Fin du bloc relatif à la vérification d'extensions sur la Home Admin.
  //////////////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////////

}
<file_sep>/core/bean/LocalBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe LocalBean
 * @author Hugues
 * @since 1.00.00
 * @version 1.07.21
 */
class LocalBean extends UtilitiesBean implements ConstantsInterface
{

  /**
   * @param array $addArg
   * @param array $remArg
   * @return string
   */
  public function getQueryArg($addArg, $remArg=array())
  {
    $addArg['page'] = 'hj-zombicide/admin_manage.php';
    $remArg[] = 'form';
    $remArg[] = 'id';
    return add_query_arg($addArg, remove_query_arg($remArg, 'http://zombicide.jhugues.fr/wp-admin/admin.php'));
  }
  /**
   * @param array $addArg
   * @param array $remArg
   * @param string $url
   * @return string
   */
  public function getFrontQueryArg($addArg, $remArg=array(), $url='http://zombicide.jhugues.fr/')
  { return add_query_arg($addArg, remove_query_arg($remArg, $url)); }
  /**
   * @return bool
   */
  public static function isAdmin()
  { return current_user_can('manage_options'); }
  /**
   * @return bool
   */
  public static function isLogged()
  { return is_user_logged_in(); }
  /**
   * @return int
   */
  public static function getWpUserId()
  { return get_current_user_id(); }
  /**
   * @param string $id
   * @param string $default
   * @return mixed
   */
  public function initVar($id, $default='')
  {
    if (isset($_POST[$id])) {
      return $_POST[$id];
    }
    if (isset($_GET[$id])) {
      return $_GET[$id];
    }
    return $default;
  }

  public function getPublicBalise($balise, $label='', $attributes=array())
  { return $this->getBalise($balise, $label, $attributes); }
}
<file_sep>/core/domain/Duration.class.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe Duration
 * @author Hugues.
 * @since 1.0.00
 * @version 1.04.28
 */
class Duration extends LocalDomain
{
  /**
   * Id technique de la donnée
   * @var int $id
   */
  protected $id;
  /**
   * Durée minimale estimée
   * @var int $minDuration
   */
  protected $minDuration;
  /**
   * Durée maximal estimée (nulle si une seule durée donnée)
   * @var int $maxDuration
   */
  protected $maxDuration;
  /**
   * @return int
   */
  public function getId()
  { return $this->id; }
  /**
   * @return int
   */
  public function getMinDuration()
  { return $this->minDuration; }
  /**
   * @return int
   */
  public function getMaxDuration()
  { return $this->maxDuration; }
  /**
   * @param int $id
   */
  public function setId($id)
  { $this->id = $id; }
  /**
   * @param int $minDuration
   */
  public function setMinDuration($minDuration)
  { $this->minDuration = $minDuration; }
  /**
   * @param int $maxDuration
   */
  public function setMaxDuration($maxDuration)
  { $this->maxDuration = $maxDuration; }
  /**
   * @return array
   */
  public function getClassVars()
  { return get_class_vars('Duration'); }
  /**
   * @param array $row
   * @param string $a
   * @param string $b
   * @return Duration
   */
  public static function convertElement($row, $a='', $b='')
  { return parent::convertElement(new Duration(), self::getClassVars(), $row); }
  /**
   * @return string
   */
  public function getStrDuree()
  { return $this->minDuration.($this->maxDuration == 0 ? '' : ' à '.$this->maxDuration).' minutes'; }
  /**
   * @return DurationBean
   */
  public function getBean()
  { return new DurationBean($this); }
}
<file_sep>/core/bean/WpPageTilesBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe WpPageTilesBean
 * @author Hugues
 * @since 1.08.30
 */
class WpPageTilesBean extends WpPageBean
{
  protected $urlTemplate = 'web/pages/public/wppage-tiles.php';
  /**
   * Class Constructor
   * @param WpPage $WpPage
   */
  public function __construct($WpPage='')
  {
    parent::__construct($WpPage);
    $this->ExpansionServices = new ExpansionServices();
  }
  /**
   * @return string
   */
  public function getContentPage()
  { return $this->getListContentPage(); }
  /**
   * Retourne la liste des dalles
   * @return string
   */
  public function getListContentPage()
  {
    /////////////////////////////////////////////////////////////////////////////
    // On récupère la liste de toutes les Extensions
    $Expansions = $this->ExpansionServices->getExpansionsWithFilters(array(), self::FIELD_DISPLAYRANK);
    $strFilters = '';
    $strSpawns = '';
    while (!empty($Expansions)) {
      $Expansion = array_shift($Expansions);
      // Si l'extension n'a pas de dalles, on passe à l'extension suivante.
      if ($Expansion->getNbDalles()==0) {
        continue;
      }
      // On en profite aussi pour construire le bloc de filtres.
      $strFilters .= $this->getBalise(self::TAG_OPTION, $Expansion->getName(), array(self::ATTR_VALUE => 'set-'.$Expansion->getId()));
    }

    //////////////////////////////////////////////////////////////////
    // On enrichi le template puis on le restitue.
    $args = array(
      // Les filtres disponibles - 1
      $strFilters,
    );
    return $this->getRender($this->urlTemplate, $args);
  }
}
<file_sep>/core/domain/Expansion.class.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe Expansion
 * @author Hugues.
 * @since 1.04.00
 * @version 1.07.22
 */
class Expansion extends WpPostRelais
{
  /**
   * Id technique de la donnée
   * @var int $id
   */
  protected $id;
  /**
   * Code de la donnée
   * @var string $code
   */
  protected $code;
  /**
   * Nom de la donnée
   * @var string $name
   */
  protected $name;
  /**
   * Rang d'affichage
   * @var int $displayRank
   */
  protected $displayRank;
  /**
   * Nombre de Survivants
   * @var int $nbSurvivants
   */
  protected $nbSurvivants;
  /**
   * Nombre de Missions
   * @var int $nbMissions
   */
  protected $nbMissions;
  /**
   * Nombre de Dalles
   * @var int $nbDalles
   */
  protected $nbDalles;
  /**
   * Est officielle ?
   * @var boolean $official;
   */
  protected $official;

  /**
   * Getter Id
   * @return int
   */
  public function getId()
  {return $this->id; }
  /**
   * Getter Code
   * @return string
   */
  public function getCode()
  { return $this->code; }
  /**
   * Getter Name
   * @return string
   */
  public function getName()
  { return $this->name; }
  /**
   * Getter displayRank
   * @return int
   */
  public function getDisplayRank()
  { return $this->displayRank; }
  /**
   * Getter nbSurvivants
   * @return int
   */
  public function getNbSurvivants()
  { return $this->nbSurvivants; }
  /**
   * Getter nbMissions
   * @return int
   */
  public function getNbMissions()
  { return $this->nbMissions; }
  /**
   * Getter nbDalles
   * @return int
   */
  public function getNbDalles()
  { return $this->nbDalles; }
  /**
   * Getter official
   * @return boolean
   */
  public function isOfficial()
  { return ($this->official==1); }
  /**
   * @param int $id
   */
  public function setId($id)
  { $this->id=$id; }
  /**
   * @param string $code
   */
  public function setCode($code)
  { $this->code=$code; }
  /**
   * @param string $name
   */
  public function setName($name)
  { $this->name=$name; }
  /**
   * @param int $displayRank
   */
  public function setDisplayRank($displayRank)
  { $this->displayRank=$displayRank; }
  /**
   * @param int $nbSurvivants
   */
  public function setNbSurvivants($nbSurvivants)
  { $this->nbSurvivants = $nbSurvivants; }
  /**
   * @param int $nbMissions
   */
  public function setNbMissions($nbMissions)
  { $this->nbMissions = $nbMissions; }
  /**
   * @param int $nbDalles
   */
  public function setNbDalles($nbDalles)
  { $this->nbDalles = $nbDalles; }
  /**
   * @param boolean $official
   */
  public function setOfficial($official)
  { $this->official = $official; }

  ///////////////////////////////////////////////////////////////
  /**
   * Retourne les attributs de la classe
   * @return array
   */
  public function getClassVars()
  { return get_class_vars('Expansion'); }
  /**
   * @param array $row
   * @param string $a
   * @param string $b
   */
  public static function convertElement($row, $a='', $b='')
  { return parent::convertElement(new Expansion(), self::getClassVars(), $row); }
  /**
   * @return ExpansionBean
   */
  public function getBean()
  { return new ExpansionBean($this); }
  ///////////////////////////////////////////////////////////////

  /**
   * @return string
   */
  public function getWpPost()
  { return $this->getMainWpPost(self::FIELD_CODE, $this->code, self::WP_CAT_EXPANSION_ID); }








  /**
   * @param array $row
   */
  public static function convertElementFromPost($row)
  {
    $Obj = new Expansion();
    $vars = get_class_vars('Expansion');
    if (!empty($vars)) {
      foreach ($vars as $key => $value) {
        $Obj->setField($key, $row[$key]);
      }
      if ($row['officielle']=='on') {
        $Obj->setField('officielle', 1);
      }
      if ($row['active']=='on') {
        $Obj->setField('active', 1);
      }
    }
    return $Obj;
  }

  public function getTiles()
  {
    if ($this->Tiles==null) {
      $this->Tiles = $this->TileServices->getTilesWithFilters(array(self::FIELD_EXPANSIONID=>$this->getId()));
    }
    return $this->Tiles;
  }
  public function getMissions()
  {
    if ($this->Missions==null) {
      $this->Missions = $this->MissionServices->getMissionsByExpansionId($this->getId());
    }
    return $this->Missions;
  }
}
<file_sep>/core/bean/WpPageSkillsBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe WpPageSkillsBean
 * @author Hugues
 * @since 1.00.00
 * @version 1.08.01
 */
class WpPageSkillsBean extends WpPageBean
{
  protected $urlTemplate = 'web/pages/public/wppage-skills.php';
  /**
   * Class Constructor
   * @param WpPage $WpPage
   */
  public function __construct($WpPage='')
  {
    parent::__construct($WpPage);
    $this->SkillServices = new SkillServices();
  }
  /**
   * On vérifie si on est ici pour traiter la page des compétences, ou une compétence en particulier.
   * Pour le cas d'une compétence, on retourne une WpPostSkillBean.
   * @return string
   */
  public function getContentPage()
  {
    // On récupère l'éventuel paramètre FIELD_SKILLID
    $skillId = $this->initVar(self::FIELD_SKILLID, -1);
    if ($skillId==-1) {
      // S'il n'est pas défini, on affiche la liste des compétences
      $this->setFilters();
      return $this->getListContentPage();
    } else {
      // S'il est défini, on affiche la compétence associée.
      $Bean = new WpPostSkillBean($skillId);
      return $Bean->getContentPage();
    }
  }
  /**
   * @return string
   */
  public function getListContentPage()
  {
    /////////////////////////////////////////////////////////////////////////////
    // On récupère la liste de compétences puis les éléments nécessaires à la pagination.
    $Skills = $this->SkillServices->getSkillsWithFilters($this->arrFilters, $this->colSort, $this->colOrder);
    $this->nbElements = count($Skills);
    $this->nbPages = ceil($this->nbElements/$this->nbperpage);
    // On slice la liste pour n'avoir que celles à afficher
    $displayedSkills = array_slice($Skills, $this->nbperpage*($this->paged-1), $this->nbperpage);
    // On construit le corps du tableau
    $strBody = '';
    if (!empty($displayedSkills)) {
      foreach ($displayedSkills as $Skill) {
        $strBody .= $Skill->getBean()->getRowForPublicPage();
      }
    }
    /////////////////////////////////////////////////////////////////////////////

    /////////////////////////////////////////////////////////////////////////////
    // Affiche-t-on le filtre ?
    $showFilters = (isset($this->arrFilters[self::FIELD_DESCRIPTION])&&$this->arrFilters[self::FIELD_DESCRIPTION]!='');
    /////////////////////////////////////////////////////////////////////////////

    //////////////////////////////////////////////////////////////////
    // On enrichi le template puis on le restitue.
    $args = array(
      // Les lignes du tableau - 1
      $strBody,
      // On affiche le dropdown par pages - 2
      $this->getDropdownNbPerPages(),
      // On affiche la pagination - 3
      $this->getNavPagination(),
      // Affiche ou non le bloc filtre - 4
      $showFilters ? 'block' : 'none',
      // Filtre sur la Description - 5
      $this->arrFilters[self::FIELD_DESCRIPTION],
    );
    return $this->getRender($this->urlTemplate, $args);
  }
  /**
   * @param array $post
   */
  public function setFilters($post=null)
  {
    $pattern = "/description=(.*)/";
    if (preg_match($pattern, $post[self::CST_FILTERS], $matches)) {
      $post[self::CST_FILTERS] .= '&name='.$matches[1];
    }
    parent::setBeanFilters($post, self::FIELD_NAME);
  }

}
<file_sep>/web/rsc/zombicide.js
/*
 * @version 1.04.26
 */
var $hj = jQuery;
$hj(document).ready(function(){
  /***************
   *** #01 - Home
   *** Si on est sur la Homepage
   ***************/
  if ($hj('#homeSectionArticles').length!=0 ) {
    $hj('#more_news').click(function() {
      var offset = $hj('#homeSectionArticles article').length;
      addMoreNews(offset);
    });
    addPageSurvivantAjaxActions();
  }
  /***************
   *** #02 - Compétences
   *** Si on est sur la page du Listing des Compétences
   ***************/
  if ($hj('#page-competences').length!=0 ) {
    addPageCompetenceAjaxActions();
  }
  /***************
   *** #03 - Missions
   *** Si on est sur la page du Listing des Missions
   ***************/
  if ($hj('#page-missions').length!=0 ) {
    addPageMissionAjaxActions();
  }
  /***************
   *** #04 - Survivants
   *** Si on est sur la page du Listing des Survivants
   ***************/
  if ($hj('#page-survivants').length!=0 ) {
    addPageSurvivantAjaxActions();
  }
  /***************
   *** #05 - Cartes Equipement
   *** Si on est sur la page du Listing des Cartes Equipement
   ***************/
  if ($hj('#page-equipements').length!=0 ) {
    addPageCardEquipmentAjaxActions();
  }
  /***************
   *** #06 - Page Tools
   *** Si on est sur une des sous pages Tools
   ***************/
  if ($hj('#page-tools').length!=0 ) {
    addPageToolsAjaxActions();
  }
  /***************
   *** #07 - Extensions
   *** Si on est sur la page du Listing des Extensions
   ***************/
  if ($hj('#page-extensions').length!=0 ) {
    addPageExpansionAjaxActions();
  }
  /***************
   *** #08 - Dalles
   *** Si on est sur la page du Listing des Dalles
   ***************/
  if ($hj('#page-dalles').length!=0 ) {
    addPageTileAjaxActions();
  }

});

/********
 * Ajax Actions - HomePage
 ********/
function addMoreNews(offset) {
  var obj;
  var data = {'action': 'dealWithAjax', 'ajaxAction': 'addMoreNews', 'value': offset};
  $hj.post(
    ajaxurl,
    data,
    function(response) {
      try {
        obj = JSON.parse(response);
        if (obj['homeSectionArticles'] != '' ) {
          $hj('#homeSectionArticles').append(obj['homeSectionArticles']);
          if ($hj('#homeSectionArticles>article').length%6 != 0 ) {
            $hj('#more_news').remove();
          }
        } else {
          $hj('#more_news').remove();
        }
        addPageSurvivantAjaxActions();
      } catch (e) {
        console.log("error: "+e);
        console.log(response);
      }
    }
  );
}
function addPageTileAjaxActions() {
  $hj('button[data-ajaxaction="reset"]').unbind().click(function(){
    $hj('#filter-expansionId').val('');
    $hj('#tile-container').html("");
  });
  $hj('#filter-expansionId').unbind().change(function(){
    var idSet = $hj(this).val().substr(4);
    if (idSet=='') {
      $hj('#tile-container').html("");
      return false;
    } else {
      var data = {'action': 'dealWithAjax', 'ajaxAction': 'getTiles', 'idSet': idSet};
      resolveCallAjax(data, 'tile-container');
    }
  });
}
function addPageExpansionAjaxActions() {
  // On ajoute une Action sur les actions Ajax
  $hj('#page-extensions .ajaxAction').unbind().click(function(){
    resolvePageExpansionAjaxActions($hj(this));
    return false;
  });
  // On ajoute une Action sur le changement de nombre d'éléments à afficher par page
  $hj('#displayedRows').change(function(){
    resolvePageExpansionAjaxActions($hj(this));
    return false;
  });
  // On ajoute une Action pour afficher/cacher le formulaire de filtre
  $hj('i.fa-filter').unbind().click(function(){
    $hj('#filters input').val('');
    $hj('#filters').toggle();
  });
}
function addPageCompetenceAjaxActions() {
  // On ajoute une Action sur les actions Ajax
  $hj('#page-competences .ajaxAction').unbind().click(function(){
    resolvePageCompetenceAjaxActions($hj(this));
    return false;
  });
  // On ajoute une Action sur le changement de nombre d'éléments à afficher par page
  $hj('#displayedRows').change(function(){
    resolvePageCompetenceAjaxActions($hj(this));
    return false;
  });
  // On ajoute une Action pour afficher/cacher le formulaire de filtre
  $hj('i.fa-filter').unbind().click(function(){
    $hj('#filters input').val('');
    $hj('#filters').toggle();
  });
}
function addPageMissionAjaxActions() {
  // On ajoute une Action sur les actions Ajax
  $hj('#page-missions .ajaxAction').unbind().click(function(){
    resolvePageMissionAjaxActions($hj(this));
    return false;
  });
  // On ajoute une Action sur le changement de nombre d'éléments à afficher par page
  $hj('#displayedRows').change(function(){
    resolvePageMissionAjaxActions($hj(this));
    return false;
  });
  // On ajoute une Action pour afficher/cacher le formulaire de filtre
  $hj('i.fa-filter').unbind().click(function(){
    $hj('#filters input').val('');
    $hj('#filters').toggle();
  });
}
function addPageSurvivantAjaxActions() {
  // On ajoute une Action sur les actions Ajax
  $hj('#page-survivants .ajaxAction').unbind().click(function(){
    resolvePageSurvivantAjaxActions($hj(this));
    return false;
  });
  // On ajoute une Action sur le changement de nombre d'éléments à afficher par page
  $hj('#displayedRows').change(function(){
    resolvePageSurvivantAjaxActions($hj(this));
    return false;
  });
  // On ajoute un action sur les cases à cocher des Profils.
  $hj('.publicSurvivorRow .changeProfile').unbind().click(function(){
      addPageSurvivantLocalActions($hj(this));
      return false;
  });
  // On ajoute une Action pour afficher/cacher le formulaire de filtre
  $hj('i.fa-filter').unbind().click(function(){
    $hj('#filters input').val('');
    $hj('#filters').toggle();
  });
}
function addPageSurvivantLocalActions(clicked) {
  var type = clicked.data('type');
  clicked.parent().parent().parent().parent().parent().removeClass('survivant zombivant ultimate ultimatez').addClass(type);
  clicked.parent().siblings().removeClass('active');
  clicked.parent().addClass('active');
}
function addPageCardEquipmentAjaxActions() {
  $hj('#filters select').unbind().change(function(){
    var set = $hj('#filter-expansionId').val();
    var cType = $hj('#filter-typeId').val();
    console.log(set);
    console.log(cType);
    $hj('#card-container .card').each(function(){
      if ((set=='' || set!=''&&$hj(this).hasClass(set)) && (cType==undefined || cType=='' || cType!=''&&$hj(this).hasClass(cType))) {
        $hj(this).css('display', 'inline-block');
      } else {
        $hj(this).css('display', 'none');
      }
    });
  });
  $hj('button[data-ajaxaction="reset"]').unbind().click(function(){
    $hj('#filter-expansionId').val('');
    $hj('#filter-typeId').val('');
    $hj('#card-container .card').each(function(){
      $hj(this).css('display', 'inline-block');
    });
    return false;
  });
}

function addPageToolsAjaxActions() {
  addSelectionSurvivantActions();
  addThrowDiceActions();
  addGenerationMapActions();
}
function addDownloadMap() {
  $hj('.fa-camera').unbind().click(function(){
    $hj('.overlay').addClass('loading');
    $hj('.fa-unlock').removeClass('fa-unlock').addClass('fa-lock');
    var params = lauchBuildingMapv2();
    var data = {'action': 'dealWithAjax', 'ajaxAction': 'getImageMap', 'params': params};
    $hj.post(
      ajaxurl,
      data,
      function(response) {
        try {
          var win = window.open('http://zombicide.jhugues.fr'+response, '_blank');
          console.log('http://zombicide.jhugues.fr'+response);
          if (win) {
            win.focus();
          }
        } catch (e) {
          console.log("error: "+e);
          console.log(response);
        }
      }
    ).done(
      function() {
        $hj('.overlay').removeClass('loading');
      }
    );
  });
}
function turnFaSquareToCheckBox() {
  $hj('.displayMap i.fakeCb').unbind().click(function(){
    $hj(this).toggleClass('fa-square fa-check-square');
  });
}
function addLockUnlockEffect() {
  $hj('.displayMap i.fakeLock').unbind().click(function(){
    $hj(this).toggleClass('fa-unlock fa-lock');
  });
}
function addCogAction() {
  $hj('.displayMap i.fa-cog').unbind().click(function(){
    var params = '';
    // Liste des extensions sélectionnées
    var expansionIds = $hj('#expansionIds').val();
    if (expansionIds==undefined) {
      params += '&version=2';
      expansionIds = '';
      $hj('.btn-expansion i.fa-check-square').each(function(){
        if (expansionIds!='') {
          expansionIds += ',';
        }
        expansionIds += $hj(this).parent().parent().data('expansion-id');
      });
    }
    params += '&expansionIds='+expansionIds;
    // Liste des Dalles déjà placées
    var locks = '';
    $hj('.displayMap i.fakeLock').each(function() {
      if (locks!='') {
        locks += ',';
      }
      locks += $hj(this).attr('data-lock');
    });
    params += '&locks='+locks;
    // Et on ajoute la case courante
    var current = $hj(this).siblings('.fakeLock').data('lock');
    params += '&current='+current;
    var data = {'action': 'dealWithAjax', 'ajaxAction': 'getNonUsedTiles', 'params': params};

    $hj('.overlay').addClass('loading');
    var idPage = 'page-generation-map';
    $hj.post(
      ajaxurl,
      data,
      function(response) {
        try {
          var obj = JSON.parse(response);
          if (obj[idPage] != '' ) {
            $hj('#'+idPage).prepend(obj[idPage]);

            $hj('.proposals img').unbind().click(function(){
              var row = $hj(this).data('row')*1;
              var col = $hj(this).data('col')*1;
              var orientation = $hj(this).data('orientation');
              var src = $hj(this).data('src');
              var code = $hj(this).data('code');
              $hj('.displayMap .row:nth-child('+(row+1)+') .cell:nth-child('+(col+1)+') img').attr('class', orientation).attr('src', src);
              $hj('.displayMap .row:nth-child('+(row+1)+') .cell:nth-child('+(col+1)+') .fakeLock').attr('data-lock', 'cell_'+row+'_'+col+'_'+code+'_'+orientation);
              $hj('.proposals').remove();
            })
          }
        } catch (e) {
          console.log("error: "+e);
          console.log(response);
        }
      }
    ).done(
      function() {
        $hj('.overlay').removeClass('loading');
      }
    );
  });
}
function addRotateAction() {
  $hj('.displayMap i.fa-undo').unbind().click(function(){
    var orientation = $hj(this).parent().siblings('img').attr('class');
    var newOrientation = '';
    switch (orientation) {
      case 'top' : newOrientation = 'left'; break;
      case 'left' : newOrientation = 'bottom'; break;
      case 'bottom' : newOrientation = 'right'; break;
      case 'right' : newOrientation = 'top'; break;
      default : newOrientation = orientation; break;
    }
    var strToReplace = $hj(this).siblings('.fakeLock').data('lock');
    $hj(this).siblings('.fakeLock').attr('data-lock', strToReplace.replace(orientation, newOrientation));
    $hj(this).parent().siblings('img').attr('class', newOrientation);
  });
  $hj('.displayMap i.fa-redo').unbind().click(function(){
    var orientation = $hj(this).parent().siblings('img').attr('class');
    var newOrientation = '';
    switch (orientation) {
      case 'top' : newOrientation = 'right'; break;
      case 'left' : newOrientation = 'top'; break;
      case 'bottom' : newOrientation = 'left'; break;
      case 'right' : newOrientation = 'bottom'; break;
      default : newOrientation = orientation; break;
    }
    var strToReplace = $hj(this).siblings('.fakeLock').data('lock');
    $hj(this).siblings('.fakeLock').attr('data-lock', strToReplace.replace(orientation, newOrientation));
    $hj(this).parent().siblings('img').attr('class', newOrientation);
  });
}
function lauchBuildingMapv2() {
  var params = '';
  // Largeur et Hauteur
  params  = 'width='+$hj('#width').val();
  params += '&height='+$hj('#height').val();
  // Liste des extensions sélectionnées
  var expansionIds = $hj('#expansionIds').val();
  if (expansionIds==undefined) {
    params += '&version=2';
    expansionIds = '';
    $hj('.btn-expansion i.fa-check-square').each(function(){
      if (expansionIds!='') {
        expansionIds += ',';
      }
      expansionIds += $hj(this).parent().parent().data('expansion-id');
    });
  }
  params += '&expansionIds='+expansionIds;
  // Liste des CheckBoxes non sélectionnées
  var cells = '';
  $hj('.displayMap i.fa-square').each(function() {
    if (cells!='') {
      cells += ',';
    }
    cells += $hj(this).data('cell');
  });
  params += '&cells='+cells;
  // Liste des Dalles déjà placées
  var locks = '';
  $hj('.displayMap i.fa-lock').each(function() {
    if (locks!='') {
      locks += ',';
    }
    locks += $hj(this).attr('data-lock');
  });
  params += '&locks='+locks;
  return params;
}
function addChangeMapSize() {
  if ($hj('.displayMap').length==0) {
    $hj('#proceedRandomMap').trigger('click');
  } else {
    var actualHeight = $hj('.displayMap .row').length;
    var actualWidth = $hj('.displayMap .row:first-child .cell').length;
    var height = $hj('#height').val();
    var width = $hj('#width').val();
    var cellModel = $hj('.cellModel').clone();
    $hj('.displayMap').removeClass('map'+actualWidth+'x'+actualHeight+' mapWidth'+actualWidth);
    $hj('.displayMap').addClass('map'+width+'x'+height+' mapWidth'+width);
    if (height<actualHeight) {
      $hj('.displayMap .row:last-child').remove();
    } else if (width<actualWidth) {
      $hj('.displayMap .row .cell:last-child').remove();
    } else if (height>actualHeight) {
      $hj('.displayMap').append('<div class="row"></div>');
      for (var cpt=0; cpt<width; cpt++) {
        cellModel = $hj('.cellModel').clone();
        cellModel.removeClass('hidden cellModel');
        cellModel.find('i.fakeCb').attr('data-cell', 'cell_'+(height-1)+'_'+cpt);
        cellModel.find('i.fakeLock').attr('data-lock', 'cell_'+(height-1)+'_'+cpt);
        $hj('.displayMap .row:last-child').append(cellModel);
      }
    } else if (width>actualWidth) {
      for (cpt=0; cpt<height; cpt++) {
        cellModel = $hj('.cellModel').clone();
        cellModel.removeClass('hidden cellModel');
        cellModel.find('i.fakeCb').attr('data-cell', 'cell_'+cpt+'_'+(width-1));
        cellModel.find('i.fakeLock').attr('data-lock', 'cell_'+cpt+'_'+(width-1));
        $hj('.displayMap .row:nth-child('+(cpt+1)+')').append(cellModel);
      }
    }
  }
}
function addGenerationMapActions() {
  addDownloadMap();
  $hj('#proceedRandomMap').unbind().click(function() {
    var params = lauchBuildingMapv2();
    var data = {'action': 'dealWithAjax', 'ajaxAction': 'getRandomMap', 'params': params};
    resolveCallAjax(data, 'page-generation-map');
  });

  $hj('#width').unbind().change(function(){
    addChangeMapSize();
  });
  $hj('#height').unbind().change(function(){
    addChangeMapSize();
  });
  /*
  $hj('#width').unbind().change(function(){
    var actualWidth = $hj('.displayMap .row .cell').length;
    var width = $hj(this).val();
    var height = $hj('#height').val();
    if ($hj('.displayMap').length==0) {
      $hj('#proceedRandomMap').trigger('click');
    } else if (width<actualWidth) {
      $hj('.displayMap').removeClass('map'+actualWidth+'x'+height+' mapWidth'+actualWidth).addClass('map'+width+'x'+height+' mapWidth'+width);
      for (var cpt=0; cpt<height; cpt++) {
        $hj('.displayMap .row .cell:last-child').remove();
      }
    } else {
      $hj('.overlay').addClass('loading');
      var data = {'action': 'dealWithAjax', 'ajaxAction': 'getEmptyCell'};
      $hj.post(
        ajaxurl,
        data,
        function(response) {
          try {
            $hj('.displayMap').removeClass('map'+actualWidth+'x'+height+' mapWidth'+actualWidth).addClass('map'+width+'x'+height+' mapWidth'+width);
            var obj = JSON.parse(response);
            var emptyCell = obj['empty-cell'];
            for (var cpt=0; cpt<height; cpt++) {
              var newCell = emptyCell.replace('cell_0_0', 'cell_'+cpt+'_'+(width-1));
              newCell = newCell.replace('cell_0_0', 'cell_'+cpt+'_'+(width-1));
              $hj('.displayMap .row:nth-child('+(cpt+1)+')').append(newCell);
            }
          } catch (e) {
            console.log("error: "+e);
            console.log(response);
          }
        }
      ).done(
        function() {
          $hj('.overlay').removeClass('loading');
          turnFaSquareToCheckBox();
          addLockUnlockEffect();
          addCogAction();
        }
      );
    }
  });
  $hj('#height').unbind().change(function(){
    var actualHeight = $hj('.displayMap .row').length;
    var width = $hj('#width').val();
    var height = $hj(this).val();
    if ($hj('.displayMap').length==0) {
      $hj('#proceedRandomMap').trigger('click');
    } else if (height<actualHeight) {
      $hj('.displayMap').removeClass('map'+width+'x'+actualHeight).addClass('map'+width+'x'+height);
      $hj('.displayMap .row:last-child').remove();
    } else {
      $hj('.overlay').addClass('loading');
      var data = {'action': 'dealWithAjax', 'ajaxAction': 'getEmptyCell'};
      $hj.post(
        ajaxurl,
        data,
        function(response) {
          try {
            $hj('.displayMap').removeClass('map'+width+'x'+actualHeight).addClass('map'+width+'x'+height);
            $hj('.displayMap').append('<div class="row"></div>');
            var obj = JSON.parse(response);
            var emptyCell = obj['empty-cell'];
            for (var cpt=0; cpt<width; cpt++) {
              var newCell = emptyCell.replace('cell_0_0', 'cell_'+(height-1)+'_'+cpt);
              newCell = newCell.replace('cell_0_0', 'cell_'+(height-1)+'_'+cpt);
              $hj('.displayMap .row:last-child').append(newCell);
            }
          } catch (e) {
            console.log("error: "+e);
            console.log(response);
          }
        }
      ).done(
        function() {
          $hj('.overlay').removeClass('loading');
          turnFaSquareToCheckBox();
          addLockUnlockEffect();
          addCogAction();
        }
      );
    }
  });
  */
}

function addThrowDiceActions() {
  $hj('#proceedThrowDice').unbind().click(function() {
    var params = '';
    params  = 'nbDice='+$hj('#diceNumber').val();
    params += '&seuil='+$hj('#accuracy').val();
    params += '&modif='+$hj('#plusAuDe').val();
    params += '&surunsix='+$hj('#surUnSix').val();
    params += '&dual=0';
    params += '&barbauto=0';
    var data = {'action': 'dealWithAjax', 'ajaxAction': 'getThrowDice', 'params': params};
    resolveCallAjax(data, 'page-piste-de-des');
  });
}
function addSelectionSurvivantActions() {
  // Actions sur les boutons de sélections du nombre de survivants.
  $hj('#nbSurvSel button').unbind().click(function(){
    $hj(this).siblings().removeClass('active');
    $hj(this).addClass('active');
    return false;
  });
  // Actions sur les regroupements d'extensions
  $hj('#page-tools .btn-expansion-group').unbind().click(function(){
    $hj(this).nextUntil('.btn-expansion-group').toggleClass('hidden');
    $hj(this).find('i').toggleClass('fa-chevron-circle-down fa-chevron-circle-right');
    return false;
  });
  // Actions sur les boutons d'Extensions pour afficher ou non les Survivants associés.
  $hj('#page-tools .btn-expansion').unbind().click(function(){
    // S'il a la classe fa-square, on doit tous les afficher et les sélectionner.
    var expansionId= $hj(this).data('expansion-id');
    if ($hj(this).find('i').hasClass('fa-check-square')) {
      $hj(this).find('i').addClass('fa-square').removeClass('fa-check-square');
      $hj('#listing button[data-expansion-id="'+expansionId+'"]').each(function(){
        $hj(this).addClass('hidden');
        $hj(this).find('i').addClass('fa-square').removeClass('fa-check-square');
      });
    } else {
      $hj(this).find('i').removeClass('fa-square').addClass('fa-check-square');
      $hj('#listing button[data-expansion-id="'+expansionId+'"]').each(function(){
        $hj(this).removeClass('hidden');
        $hj(this).find('i').removeClass('fa-square').addClass('fa-check-square');
      });
    }
    $hj('#nbSelected').html($hj('#listing .fa-check-square').length);
    var nb = 0;
    $hj('#listing button.btn-survivor').each(function(){ if ($hj(this).is(':visible')) nb++;});
    $hj('#nbDisplayed').html(nb);
    return false;
  });
  // Actions sur la case à cocher du bouton d'un Survivant.
  $hj('#page-tools .btn-survivor').unbind().click(function(){
    // On inverse le statut de la case à cocher
    $hj(this).find('i').toggleClass('fa-square fa-check-square');
    // On récupère l'extension Parent
    var expansionId = $hj(this).data('expansion-id');
    var parentNode = $hj('#page-tools .btn-expansion[data-expansion-id="'+expansionId+'"]');
    // On va parcourir les enfants du Parent pour dénombrer les statuts.
    var children = $hj('#listing button[data-expansion-id="'+expansionId+'"]');
    var childNb = children.length;
    var checkedNb = 0;
    children.each(function(){
      if ($hj(this).find('i').hasClass('fa-check-square') ) { checkedNb++; }
    });
    if (checkedNb==0 ) {
      // Si aucun n'est coché
      parentNode.find('i').removeClass('fa-check-square fa-minus-square').addClass('fa-square');
    } else if (checkedNb==childNb ) {
      // S'ils sont tous cochés
      parentNode.find('i').removeClass('fa-square fa-minus-square').addClass('fa-check-square');
    } else {
      // Si une partie est cochée.
      parentNode.find('i').removeClass('fa-check-square fa-square').addClass('fa-minus-square');
    }
    $hj('#nbSelected').html($hj('#listing .fa-check-square').length);
  });
  // Si on clic sur le bouton Générer.
  $hj('#proceedBuildTeam').unbind().click(function(){
    // On récupère la liste des Survivants sélectionnés.
    var selection = '';
    var nbSurv = 0;
    $hj('#page-tools .btn-survivor').each(function(){
      if ($hj(this).find('i').hasClass('fa-check-square') ) {
        selection += (selection==''?'':',')+$hj(this).data('survivor-id');
        nbSurv++;
      }
    });
    // Et le nombre de Survivants souhaités
    var nbSurvSel = $hj('#nbSurvSel button.active').data('nb');
    // Si le nombre sélectionné est inférieur à celui souhaité, même pas la peine de chercher...
    if (nbSurv<nbSurvSel) {
      console.log('Impossible');
    } else {
      var data = {'action': 'dealWithAjax', 'ajaxAction': 'getRandomTeam', 'nbSurvSel': nbSurvSel, 'value': selection};
      resolveCallAjax(data, 'page-selection-survivants');
    }
  });
  $hj('#loadOwnTeam').unbind().click(function(){
    // On supprime toute sélection précédente
    $hj('#listing button.btn-survivor').addClass('hidden');
    $hj('#listing button.btn-survivor i').removeClass('fa-check-square').addClass('fa-square');
    // On récupère la sélection soumise
    var teamLoader = $hj('#teamLoader').val();
    var arr = teamLoader.split(',');
    var nb = arr.length;
    // On parcour la liste pour cocher les trucs qui vont bien
    for (var i=0; i<nb; i++) {
      var subArr = arr[i].split('-');
      if (subArr.length==1) {
        // un Survivant spécifique
        $hj('button[data-survivor-id="'+subArr[0]+'"]').removeClass('hidden').click();
      } else {
        for (var j=subArr[0]; j<=subArr[1]; j++) {
          // Un intervalle de Survivants
          $hj('button[data-survivor-id="'+j+'"]').removeClass('hidden').click();
        }
      }
    }
    nb = 0;
    $hj('#listing button.btn-survivor').each(function(){ if ($hj(this).is(':visible')) nb++;});
    $hj('#nbDisplayed').html(nb);
  });
  $hj('#saveOwnTeam').unbind().click(function(e){
    $hj('#saveOwnTeam .tooltip').addClass('visible');
    var strSave = '';
    $hj('#listing i.fa-check-square').each(function(){
      strSave += $hj(this).parent().attr('data-survivor-id')+',';
    });

    var textArea = document.createElement("textarea");
    textArea.value = strSave.substring(0,strSave.length-1);

    // Avoid scrolling to bottom
    textArea.style.top = "0";
    textArea.style.left = "0";
    textArea.style.position = "fixed";

    document.body.appendChild(textArea);
    textArea.focus();
    textArea.select();

    try {
      var successful = document.execCommand('copy');
      var msg = successful ? 'successful' : 'unsuccessful';
      console.log('Fallback: Copying text command was ' + msg);
    } catch (err) {
      console.error('Fallback: Oops, unable to copy', err);
    }
    document.body.removeChild(textArea);
  });
  $hj('#saveOwnTeam .tooltip').hover(function(e) {
  }, function(e) {
    $hj('#saveOwnTeam .tooltip').removeClass('visible');
  });
}

function resolvePageExpansionAjaxActions(clicked) {
  var ajaxaction = clicked.data('ajaxaction');
  var callAjax = true;
  // On initialise les données de tri et de filtres.
  var colsort = 'name';
  var colorder = 'asc';
  var paged = 1;
  var nbPerPages = $hj('#displayedRows').val();
  var filters = 'name='+$hj('#filter-name').val();

  switch (ajaxaction) {
    // On change le nombre d'éléments affichés
    case 'display' :
    case 'filter' :
    break;
    // On change la page affichée
    case 'paged' :
      paged = clicked.data('paged');
    break;
    default :
      callAjax = false;
    break;
  }
  if (callAjax) {
    var data = {'action': 'dealWithAjax', 'ajaxAction': 'getExpansions', 'colsort': colsort, 'colorder': colorder, 'nbperpage': nbPerPages, 'paged': paged, 'filters': filters};
    resolveCallAjax(data, 'page-extensions');
  }
}
function resolvePageCompetenceAjaxActions(clicked) {
  var ajaxaction = clicked.data('ajaxaction');
  var callAjax = true;
  // On initialise les données de tri et de filtres.
  var colsort = 'name';
  var colorder = 'asc';
  var paged = 1;
  var nbPerPages = $hj('#displayedRows').val();
  var filters = '';
  if ($hj('#filter-description').val()!=undefined) {
    filters = 'description='+$hj('#filter-description').val();
  }

  switch (ajaxaction) {
    // On change le nombre d'éléments affichés
    case 'display' :
    case 'filter' :
    break;
    // On change la page affichée
    case 'paged' :
      paged = clicked.data('paged');
    break;
    default :
      callAjax = false;
    break;
  }
  if (callAjax) {
    var data = {'action': 'dealWithAjax', 'ajaxAction': 'getSkills', 'colsort': colsort, 'colorder': colorder, 'nbperpage': nbPerPages, 'paged': paged, 'filters': filters};
    resolveCallAjax(data, 'page-competences');
  }
}
function resolvePageMissionAjaxActions(clicked) {
  var ajaxaction = clicked.data('ajaxaction');
  var callAjax = true;
  // On initialise les données de tri et de filtres.
  var colsort = 'title';
  var colorder = 'asc';
  var paged = 1;
  var nbPerPages = $hj('#displayedRows').val();
  var filters = 'title='+$hj('#filter-title').val();
  filters += '&levelId='+$hj('#filter-levelId').val();
  filters += '&playerId='+$hj('#filter-playerId').val();
  filters += '&durationId='+$hj('#filter-durationId').val();
  filters += '&origineId='+$hj('#filter-origineId').val();
  if ($hj('#filter-expansionId').val()!=null) {
    filters += '&expansionId='+$hj('#filter-expansionId').val();
  }

  switch (ajaxaction) {
    // On change le nombre d'éléments affichés
    case 'display' :
    case 'filter' :
    break;
    // On change la page affichée
    case 'paged' :
      paged = clicked.data('paged');
    break;
    default :
      callAjax = false;
    break;
  }
  if (callAjax) {
    var data = {'action': 'dealWithAjax', 'ajaxAction': 'getMissions', 'colsort': colsort, 'colorder': colorder, 'nbperpage': nbPerPages, 'paged': paged, 'filters': filters};
    resolveCallAjax(data, 'page-missions');
  }
}
function resolvePageSurvivantAjaxActions(clicked) {
  var ajaxaction = clicked.data('ajaxaction');
  var callAjax = true;
  // On initialise les données de tri et de filtres.
  var colsort = 'name';
  var colorder = 'asc';
  var paged = 1;
  var nbPerPages = $hj('#displayedRows').val();
  var filters = 'name='+$hj('#filter-name').val();
  if ($hj('#filter-expansionId').val()!=null) {
    filters += '&expansionId='+$hj('#filter-expansionId').val();
  }
  if ($hj('#filter-blue-skill').val()!=null) {
    filters += '&blue-skillId='+$hj('#filter-blue-skill').val();
  }
  if ($hj('#filter-yellow-skill').val()!=null) {
    filters += '&yellow-skillId='+$hj('#filter-yellow-skill').val();
  }
  if ($hj('#filter-orange-skill').val()!=null) {
    filters += '&orange-skillId='+$hj('#filter-orange-skill').val();
  }
  if ($hj('#filter-red-skill').val()!=null) {
    filters += '&red-skillId='+$hj('#filter-red-skill').val();
  }

  switch (ajaxaction) {
    // On change le nombre d'éléments affichés
    case 'display' :
    case 'filter' :
    break;
    // On change la page affichée
    case 'paged' :
      paged = clicked.data('paged');
    break;
    default :
      callAjax = false;
    break;
  }
  if (callAjax) {
    var data = {'action': 'dealWithAjax', 'ajaxAction': 'getSurvivants', 'colsort': colsort, 'colorder': colorder, 'nbperpage': nbPerPages, 'paged': paged, 'filters': filters};
    console.log(data);
    resolveCallAjax(data, 'page-survivants');
  }
}

function resolveCallAjax(data, idPage) {
  $hj('.overlay').addClass('loading');
  $hj.post(
    ajaxurl,
    data,
    function(response) {
      try {
        var obj = JSON.parse(response);
        if (obj[idPage] != '' ) {
          $hj('#'+idPage).replaceWith(obj[idPage]);
          switch (idPage) {
            case 'page-competences' : addPageCompetenceAjaxActions(); break;
            case 'page-missions'    : addPageMissionAjaxActions(); break;
            case 'page-selection-survivants' :
            case 'page-survivants'  : addPageSurvivantAjaxActions(); break;
            case 'page-extensions'  : addPageExpansionAjaxActions(); break;
            case 'page-generation-map' :
              $hj('.displayMap .cell').each(function(){ $hj(this).height($hj(this).width()+'px'); });
              turnFaSquareToCheckBox();
              addLockUnlockEffect();
              addRotateAction();
              addCogAction();
            break;
            default: break;
          }
        }
      } catch (e) {
        console.log("error: "+e);
        console.log(response);
      }
    }
  ).done(
    function() {
      $hj('.overlay').removeClass('loading');
    }
  );
}
<file_sep>/core/domain/Skill.class.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe Skill
 * @author Hugues.
 * @since 1.00.00
 * @version 1.05.12
 */
class Skill extends WpPostRelais
{
  /**
   * Id technique de la donnée
   * @var int $id
   */
  protected $id;
  /**
   * Code de la donnée
   * @var string $code
   */
  protected $code;
  /**
   * Nom de la donnée
   * @var string $name
   */
  protected $name;
  /**
   * Description de la donnée
   * @var string $description
   */
  protected $description;
  /**
   * Extension de la compétence (première apparition ou derni_ère modification)
   * @var int $expansionId
   */
  protected $expansionId;

  /**
   * @return int
   */
  public function getId()
  { return $this->id; }
  /**
   * @return string
   */
  public function getCode()
  { return $this->code; }
  /**
   * @return string
   */
  public function getName()
  { return $this->name; }
  /**
   * @return string
   */
  public function getDescription()
  { return $this->description; }
  /**
   * @return int
   */
  public function getExpansionId()
  { return $this->expansionId; }
  /**
   * @param int $id
   */
  public function setId($id)
  { $this->id=$id; }
  /**
   * @param string $code
   */
  public function setCode($code)
  { $this->code=$code; }
  /**
   * @param string $name
   */
  public function setName($name)
  { $this->name=$name; }
  /**
   * @param string $description
   */
  public function setDescription($description)
  { $this->description=$description; }
  /**
   * @param int $official
   */
  public function setExpansionId($expansionId)
  { $this->expansionId = $expansionId; }

  ///////////////////////////////////////////////////////////////
  /**
   * @return array
   */
  public function getClassVars()
  { return get_class_vars('Skill'); }
  /**
   * @param array $row
   * @param string $a
   * @param string $b
   * @return Skill
   */
  public static function convertElement($row, $a='', $b='')
  { return parent::convertElement(new Skill(), self::getClassVars(), $row); }
  /**
   * @return SkillBean
   */
  public function getBean()
  { return new SkillBean($this); }
  ///////////////////////////////////////////////////////////////

  /**
   * @return string
   */
  public function getWpPost()
  { return $this->getMainWpPost(self::FIELD_CODE, $this->code, self::WP_CAT_SKILL_ID); }










  public function getExpansion()
  {
    if ($this->Expansion==null) {
      $this->Expansion = $this->ExpansionServices->selectExpansion($this->getExpansionId());
    }
    return $this->Expansion;
  }

}
<file_sep>/core/bean/EquipmentBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe EquipmentBean
 * @author Hugues
 * @since 1.04.26
 * @version 1.04.26
 */
class EquipmentBean extends LocalBean
{
  /**
   * Class Constructor
   * @param Equipment $EquipmentCard
   */
  public function __construct($EquipmentCard='')
  {
    parent::__construct();
    $this->EquipmentCard = ($EquipmentCard=='' ? new EquipmentCard() : $EquipmentCard);
  }
  /**
   * @param int $expansionId
   * @param int $equipmentLiveDeckId
   * @return string
   */
  public function displayCard($expansionId='', $equipmentLiveDeckId=-1)
  {
    $EquipmentCard = $this->EquipmentCard;
    if ($expansionId=='') {
      $expansionId = $EquipmentCard->getExpansionId();
    }
    $arrKeyWords = array();
    $strClasse = '';
    if ($EquipmentCard->isRanged()) {
      $strClasse .= ' ranged weapon';
    }
    if ($EquipmentCard->isMelee()) {
      $strClasse .= ' melee weapon';
    }
    if ($EquipmentCard->isPimp()) {
      $strClasse .= ' pimp';
      array_push($arrKeyWords, 'Pimp');
    }
    if ($EquipmentCard->isStarter()) {
      $strClasse .= ' starter';
      array_push($arrKeyWords, 'Starter');
    }
    if ($EquipmentCard->isDual()) {
      $strClasse .= ' dual';
      array_push($arrKeyWords, 'Dual');
    }
    $arrKeyWordsToCheck = array(
        'Embuscade', '9mm', 'Pistolet', 'Précision', 'Vivres', '12mm', 'Recharge', 'Effraction', 'Katana',
        'Munitions', 'Composite'
    );
    foreach ($arrKeyWordsToCheck as $keyWord) {
      if ($EquipmentCard->hasKeyword($keyWord)) {
        array_push($arrKeyWords, $keyWord);
      }
    }
    $args = array(
      // Identifiant de l'extension
      $expansionId,
      // Classe
      $strClasse.(self::isAdmin() ? ' hasTooltip' : ''),
      // Nom de l'équipement
      $EquipmentCard->getName(),
      // Liste des mots-clés pour Debug
      implode(', ', $arrKeyWords),
      // URL de l'image
      $EquipmentCard->getImgUrl($expansionId),
      // On montre le bouton Discard ou non - 6
      ($equipmentLiveDeckId!=-1?'':'hidden'),
      // On a besoin du KeyAccess - 7
      ($equipmentLiveDeckId!=-1?$_SESSION[self::SESSION_DECKKEY]:''),
      // On a besoin de l'id de l'EquipmentExpansion - 8
      ($equipmentLiveDeckId!=-1?$equipmentLiveDeckId:''),
    );
    $str = file_get_contents(PLUGIN_PATH.'web/pages/public/fragments/card-equipment-article.php');
    return vsprintf($str, $args);
  }
}
<file_sep>/core/services/WpPostServices.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe WpPostServices
 * @author Hugues
 * @since 1.04.00
 * @version 1.08.01
 */
class WpPostServices extends GlobalServices implements ConstantsInterface
{

  /**
   * @param array $params
   * @param string $viaWpQuery
   * @param string $wpPostType
   * @return array
   */
  public function getArticles($params=array(), $viaWpQuery=false, $wpPostType='WpPostMission')
  {
    $args = array(
      self::WP_ORDERBY      => self::FIELD_NAME,
      self::WP_ORDER        => self::ORDER_ASC,
      self::WP_POSTSPERPAGE => -1,
      self::WP_POSTTYPE     => self::WP_POST,
      self::WP_TAXQUERY     => array(
        array(
          self::WP_TAXONOMY => self::WP_POSTTAG,
          self::WP_FIELD    => self::WP_SLUG,
          self::WP_TERMS    => array('mission', 'survivant'),
        )
      )
    );
    if (!empty($params)) {
      foreach ($params as $key => $value) {
        $args[$key] = $value;
      }
    }
    if ($viaWpQuery) {
      $wpQuery = new WP_Query($args);
      $posts_array = $wpQuery->posts;
    } else {
      $posts_array = get_posts($args);
    }
    $WpPosts = array();
    if (!empty($posts_array)) {
      foreach ($posts_array as $post) {
        $tags = wp_get_post_tags($post->ID);
        $localWpPostType = $this->getPostTypeFromTags($tags);
        $WpPosts[] = WpPost::convertElement($post, $localWpPostType);
      }
    }
    return $WpPosts;
  }
  private function getPostTypeFromTags($tags)
  {
    $wpPostType = '';
    if (!empty($tags)) {
      foreach ($tags as $WpTerm) {
        if ($WpTerm->slug == 'mission') {
          $wpPostType = 'WpPostMission';
        } elseif ($WpTerm->slug == 'news') {
          $wpPostType = 'WpPostNews';
        } elseif ($WpTerm->slug == 'survivant') {
          $wpPostType = 'WpPostSurvivor';
        }
      }
    }
    return $wpPostType;
  }

  /**
   * @param int $pageId
   * @param int $limit
   * @return array
   */
  public function getChildPagesByParentId($pageId, $limit = -1, $params=array())
  {
    global $post;
    $pages = array();
    $args = array(
      self::WP_ORDERBY      => self::FIELD_NAME,
      self::WP_ORDER        => self::ORDER_ASC,
      self::WP_POSTTYPE     => 'page',
      'post_parent'         => $pageId,
      self::WP_POSTSPERPAGE => $limit
    );
    if ( !empty($params) ) {
      foreach ( $params as $key=>$value ) {
        $args[$key] = $value;
      }
    }
    $the_query = new WP_Query($args);
    while ($the_query->have_posts()) {
      $the_query->the_post();
      $pages[] = WpPost::convertElement($post, 'WpPost');
    }
    wp_reset_postdata();
    return $pages;
  }

  public function getWpPostsByCustomField($name, $value)
  {
    $args = array('numberposts'=>-1, 'post_type'=>'post', 'meta_key'=>$name, 'meta_value'=>$value);
    $posts = get_posts($args);
    $WpPosts = array();
    while (!empty($posts)) {
      array_push($WpPosts, WpPost::convertElement(array_shift($posts), 'WpPost'));
    }
    return $WpPosts;
  }

  public function getWpPostByCategoryId($categoryId=-1)
  {
    $args = array(
      self::WP_TAXQUERY     => array(),
      self::WP_CAT          => $categoryId,
    );
    return $this->getArticles($args);
  }
}
<file_sep>/core/actions/SkillActions.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * SkillActions
 * @author Hugues
 * @since 1.04.00
 * @version 1.08.01
 */
class SkillActions extends LocalActions
{
  /**
   * Constructeur
   */
  public function __construct($post=array())
  {
    parent::__construct();
    $this->post = $post;
    $this->ExpansionServices = new ExpansionServices();
    $this->SkillServices  = new SkillServices();
    $this->WpPostServices = new WpPostServices();
  }
  /**
   * Point d'entrée des méthodes statiques.
   * @param array $post
   * @return string
   **/
  public static function dealWithStatic($post)
  {
    $returned = '';
    $Act = new SkillActions($post);
    switch ($post[self::CST_AJAXACTION]) {
      case self::AJAX_GETSKILLS    :
        $returned = $Act->dealWithGetSkills();
      break;
      case self::AJAX_SKILLVERIF   :
        $returned = $Act->dealWithSkillVerif(true);
      break;
      default :
        $returned  = 'Erreur dans SkillActions > dealWithStatic, '.$_POST[self::CST_AJAXACTION].' inconnu.';
      break;
    }
    return $returned;
  }

  //////////////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////////
  // Bloc de gestion de vérifications des Compétences en Home Admin
  /**
   * @param boolean $isVerif
   * @return string
   */
  public function dealWithSkillVerif($isVerif=false)
  {
    // On récupère les articles de compétences
    $args = array(
      self::WP_CAT         => self::WP_CAT_SKILL_ID,
      self::WP_TAXQUERY    => array(),
      self::WP_POSTSTATUS  => self::WP_PUBLISH.', future',
    );
    $this->WpPostSkills = $this->WpPostServices->getArticles($args);
    $nbWpPostSkills = count($this->WpPostSkills);
    // Et les compétences en base
    $this->Skills = $this->SkillServices->getSkillsWithFilters();
    $nbSkills = count($this->Skills);

    if ($isVerif) {
      $this->checkSkills();
      $strBilan = $this->jsonString($this->strBilan, self::AJAX_SKILLVERIF, true);
    } elseif ($nbWpPostSkills!=$nbSkills) {
      $strBilan  = "Le nombre d'articles ($nbWpPostSkills) ne correspond pas au nombre de compétences en base ($nbSkills).<br>";
      $strBilan .= "Une vérification est vivement conseillée.";
    } else {
      $strBilan = "Le nombre d'articles ($nbWpPostSkills) correspond au nombre de compétences en base.";
    }
    return $strBilan;
  }
  private function checkSkills()
  {
    $hasErrors = false;
    $strErrors = '';
    $this->strBilan  = "Début de l'analyse des données relatives aux Compétences.<br>";
    $this->strBilan .= "Il y a ".count($this->WpPostSkills)." articles de Compétences.<br>";
    $this->strBilan .= "Il y a ".count($this->Skills)." entrées en base.<br>";
    /////////////////////////////////////////////////////////////////////
    // On va réorganiser les Skills pour les retrouver facilement
    $arrSkills = array();
    while (!empty($this->Skills)) {
      $Skill = array_shift($this->Skills);
      if (isset($arrSkills[$Skill->getCode()])) {
        $strErrors .= "Le code <em>".$Skill->getCode()."</em> semble être utilisé deux fois dans la base de données.<br>";
        $hasErrors = true;
      }
      $arrSkills[$Skill->getCode()] = $Skill;
    }
    /////////////////////////////////////////////////////////////////////

    /////////////////////////////////////////////////////////////////////
    while (!empty($this->WpPostSkills)) {
      // On regarde les articles créés et on vérifie les données en base, si elles existent et si elles sont cohérentes entre elles.
      // On récupère le WpPost et ses données
      $this->WpPost = array_shift($this->WpPostSkills);
      $name = $this->WpPost->getPostTitle();
      $code = $this->WpPost->getPostMeta(self::FIELD_CODE);
      if (!isset($arrSkills[$code])) {
        // A priori l'article n'a pas de code associé en base. Il faut donc en créé un qui corresponde
        $Skill = new Skill();
        $Skill->setCode($code);
        $Skill->setName($name);
        $description  = $this->WpPost->getPostContent();
        $Skill->setDescription($description);
        $expansionId = $this->getExpansionId();
        $Skill->setExpansionId($expansionId);
        // On insère la donnée et on log dans le bilan
        $this->SkillServices->insertSkill($Skill);
        $this->strBilan .= "L'article <em>".$name."</em> a été créé en base.<br>";
        continue;
      }
      $Skill = $arrSkills[$code];
      unset($arrSkills[$code]);
      $this->checkSkill($Skill);
    }
    /////////////////////////////////////////////////////////////////////
    // On vérifie que la totalité des Compétences en base ont été utilisées. Si ce n'est pas le cas, il faut créer des articles correspondants.
    if (!empty($arrSkills)) {
      $this->strBilan .= "On a des données en base qui n'ont pas d'article correspondant.<br>";
      while (!empty($arrSkills)) {
        $Skill = array_shift($arrSkills);
        $this->strBilan .= '<br>Article à créer pour une Compétence  : '.$Skill->getName().' ['.$Skill->toJson().'].';
      }
    }
    /////////////////////////////////////////////////////////////////////
    $this->strBilan .= "Fin de l'analyse des données relatives aux Compétences.<br>";
    if ($hasErrors) {
      $this->strBilan .= "Anomalies constatées :<br>".$strErrors;
    } else {
      $this->strBilan .= "Aucune anomalie constatée.";
    }
  }
  private function getExpansionId()
  {
    $postId  = $this->WpPost->getPostMeta(self::FIELD_EXPANSIONID);
    $Wp_post = get_post($postId);
    $WpPost  = WpPost::convertElement($Wp_post);
    $codeExpansion = $WpPost->getPostMeta(self::FIELD_CODE);
    $Expansions = $this->ExpansionServices->getExpansionsWithFilters(array(self::FIELD_CODE=>$codeExpansion));
    $Expansion = array_shift($Expansions);
    return $Expansion->getId();
  }
  private function checkSkill($Skill)
  {
    $doUpdate = false;
    // On initialise les données de l'article
    $name          = $this->WpPost->getPostTitle();
    $description   = $this->WpPost->getPostContent();
    $expansionId   = $this->getExpansionId();
    // On vérifie si la donnée en base correspond à l'article.
    $strError = '';
    if ($Skill->getName()!=$name) {
      $Skill->setName($name);
      $doUpdate = true;
      $strError .= "Le Nom a été mis à jour.<br>";
    }
    if ($Skill->getDescription()!=$description) {
      $Skill->setDescription($description);
      $doUpdate = true;
      $strError .= "La description a été mise à jour.<br>";
    }
    if ($Skill->getExpansionId()!=$expansionId) {
      $Skill->setExpansionId($expansionId);
      $doUpdate = true;
      $strError .= "L'extension a été mise à jour.<br>";
    }
    if ($doUpdate) {
      // Si nécessaire, on update en base.
      $this->SkillServices->updateSkill($Skill);
      $this->strBilan .= "Les données de la Compétence <em>".$name."</em> ont été mises à jour.<br>".$strError;
    }
  }
  // Fin du bloc relatif à la vérification des compétences sur la Home Admin.
  //////////////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////////

  /**
   * Récupération du contenu de la page via une requête Ajax.
   * @param array $post
   * @return string
   */
  public function dealWithGetSkills()
  {
    $Bean = new WpPageSkillsBean();
    $Bean->setFilters($this->post);
    return $this->jsonString($Bean->getListContentPage(), self::PAGE_SKILL, true);
  }
}
<file_sep>/core/domain/Spawn.class.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe Spawn
 * @author Hugues.
 * @version 1.0.00
 * @since 1.0.00
 */
class Spawn extends LocalDomain
{
  /**
   * Id technique de la donnée
   * @var int $id
   */
  protected $id;
  /**
   * Id de l'extension
   * @var int $expansionId
   */
  protected $expansionId;
  /**
   * Numéro de la carte
   * @var int $spawnNumber
   */
  protected $spawnNumber;
  /**
   * Id technique du type de Spawn
   * @var int $spawnTypeId
   */
  protected $spawnTypeId;
  /**
   * Id technique de catégorie du Zombie
   * @var int $zombieCategoryId
   */
  protected $zombieCategoryId;
  /**
   * Type de Zombie Bleu
   * @var int $blueZombieTypeId
   */
  protected $blueZombieTypeId;
  /**
   * Nombre de Zombies apparaissant en Blue
   * @var int $blueQuantity
   */
  protected $blueQuantity;
  /**
   * Type de Zombie Yellow
   * @var int $yellowZombieTypeId
   */
  protected $yellowZombieTypeId;
  /**
   * Nombre de Zombies apparaissant en Blue
   * @var int $yellowQuantity
   */
  protected $yellowQuantity;
  /**
   * Type de Zombie Yellow
   * @var int $orangeZombieTypeId
   */
  protected $orangeZombieTypeId;
  /**
   * Nombre de Zombies apparaissant en Orange
   * @var int $orangeQuantity
   */
  protected $orangeQuantity;
  /**
   * Type de Zombie Red
   * @var int $redZombieTypeId
   */
  protected $redZombieTypeId;
  /**
   * Nombre de Zombies apparaissant en Red
   * @var int $redQuantity
   */
  protected $redQuantity;
  /**
   * @return $id
   */
  public function getId()
  {return $this->id; }
  /**
   * @return $expansionId
   */
  public function getExpansionId()
  { return $this->expansionId; }
  /**
   * @return $spawnNumber
   */
  public function getSpawnNumber()
  { return $this->spawnNumber; }
  /**
   * @return $spawnTypeId
   */
  public function getSpawnTypeId()
  { return $this->spawnTypeId; }
  /**
   * @return $zombieCategoryId
   */
  public function getZombieCategoryId()
  { return $this->zombieCategoryId; }
  /**
   * @return $blueZombieTypeId
   */
  public function getBlueZombieTypeId()
  { return $this->blueZombieTypeId; }
  /**
   * @return $blueQuantity
   */
  public function getBlueQuantity()
  { return $this->blueQuantity; }
  /**
   * @return $yellowZombieTypeId
   */
  public function getYellowZombieTypeId()
  { return $this->yellowZombieTypeId; }
  /**
   * @return $yellowQuantity
   */
  public function getYellowQuantity()
  { return $this->yellowQuantity; }
  /**
   * @return $orangeZombieTypeId
   */
  public function getOrangeZombieTypeId()
  { return $this->orangeZombieTypeId; }
  /**
   * @return $orangeQuantity
   */
  public function getOrangeQuantity()
  { return $this->orangeQuantity; }
  /**
   * @return $redZombieTypeId
   */
  public function getRedZombieTypeId()
  { return $this->redZombieTypeId; }
  /**
   * @return $redQuantity
   */
  public function getRedQuantity()
  { return $this->redQuantity; }
  /**
   * @param int $id
   */
  public function setId($id)
  { $this->id=$id; }
  /**
   * @param int $expansionId
   */
  public function setExpansionId($expansionId)
  { $this->expansionId=$expansionId; }
  /**
   * @param int $spawnNumber
   */
  public function setSpawnNumber($spawnNumber)
  { $this->spawnNumber=$spawnNumber; }
  /**
   * @param int $spawnTypeId
   */
  public function setSpawnTypeId($spawnTypeId)
  { $this->spawnTypeId=$spawnTypeId; }
  /**
   * @param int $zombieCategoryId
   */
  public function setZombieCategoryId($zombieCategoryId)
  { $this->zombieCategoryId=$zombieCategoryId; }
  /**
   * @param int $blueZombieTypeId
   */
  public function setBlueZombieTypeId($blueZombieTypeId)
  { $this->blueZombieTypeId=$blueZombieTypeId; }
  /**
   * @param int $blueQuantity
   */
  public function setBlueQuantity($blueQuantity)
  { $this->blueQuantity=$blueQuantity; }
  /**
   * @param int $yellowZombieTypeId
   */
  public function setYellowZombieTypeId($yellowZombieTypeId)
  { $this->yellowZombieTypeId=$yellowZombieTypeId; }
  /**
   * @param int $yellowQuantity
   */
  public function setYellowQuantity($yellowQuantity)
  { $this->yellowQuantity=$yellowQuantity; }
  /**
   * @param int $orangeZombieTypeId
   */
  public function setOrangeZombieTypeId($orangeZombieTypeId)
  { $this->orangeZombieTypeId=$orangeZombieTypeId; }
  /**
   * @param int $orangeQuantity
   */
  public function setOrangeQuantity($orangeQuantity)
  { $this->orangeQuantity=$orangeQuantity; }
  /**
   * @param int $redZombieTypeId
   */
  public function setRedZombieTypeId($redZombieTypeId)
  { $this->redZombieTypeId=$redZombieTypeId; }
  /**
   * @param int $redQuantity
   */
  public function setRedQuantity($redQuantity)
  { $this->redQuantity=$redQuantity; }
  /**
   * @return array
   */
  public function getClassVars()
  { return get_class_vars('Spawn'); }
  /**
   * @param array $row
   * @param string $a
   * @param string $b
   * @return Spawn
   */
  public static function convertElement($row, $a='', $b='')
  { return parent::convertElement(new Spawn(), self::getClassVars(), $row); }
  /**
   * @return string
   */
  public function getImgUrl()
  { return '/wp-content/plugins/zombicide/web/rsc/images/spawns/'.(str_pad($this->spawnNumber, 4, '0', STR_PAD_LEFT)).'-thumb.jpg'; }

  public function getBean()
  { return new SpawnBean($this); }
}
<file_sep>/core/domain/MapTile.class.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe MapTile
 * @author Hugues.
 * @since 1.08.30
 */
class MapTile extends LocalDomain
{
  /**
   * Tile
   * @var Tile $Tile
   */
  protected $Tile;
  /**
   * Orientation de la Tuile
   * @var string $orientation
   */
  protected $orientation;
  /**
   * Tuile bloquée ou non.
   * @var boolean $locked
   */
  protected $locked;


  public function __construct($Tile, $orientation, $locked=false)
  {
    $this->Tile = $Tile;
    $this->orientation = $orientation;
    $this->locked = $locked;
  }

  public function getTile()
  { return $this->Tile; }

  public function getOrientation()
  { return $this->orientation; }

  public function isLocked()
  { return ($this->locked==1); }

  public function setTile($Tile)
  { $this->Tile = $Tile; }

  public function setOrientaiton($orientation)
  { $this->orientation = $orientation; }

  public function setLocked($locked)
  { $this->locked = $locked; }

  public function isCompatible($OtherMapTile, $side)
  {
    $isCompatible = true;
    if ($side=='left') {
      $sideLeft  = $this->getLeft();
      $sideRight = $OtherMapTile->getRight();
      //echo "[left : ".$this->Tile->getCode()."-$sideLeft;".$OtherMapTile->getTile()->getCode()."-$sideRight]\r\n";
      $isCompatible = $this->compSides($sideLeft, $sideRight);
    } elseif ($side=='right') {
      $thisSide  = $this->getRight();
      $otherSide = $OtherMapTile->getLeft();
      //echo "[top : ".$this->Tile->getCode()."-$sideTop;".$OtherMapTile->getTile()->getCode()."-$sideBottom]\r\n";
      $isCompatible = $this->compSides($thisSide, $otherSide);
    } elseif ($side=='bottom') {
      $sideBottom = $this->getBottom();
      $sideTop    = $OtherMapTile->getTop();
      //echo "[top : ".$this->Tile->getCode()."-$sideTop;".$OtherMapTile->getTile()->getCode()."-$sideBottom]\r\n";
      $isCompatible = $this->compSides($sideBottom, $sideTop);
    } elseif ($side=='top') {
      $sideTop    = $this->getTop();
      $sideBottom = $OtherMapTile->getBottom();
      //echo "[top : ".$this->Tile->getCode()."-$sideTop;".$OtherMapTile->getTile()->getCode()."-$sideBottom]\r\n";
      $isCompatible = $this->compSides($sideTop, $sideBottom);
    }
    return $isCompatible;
  }

  public function isCompatibleV2($MapTiles, $row, $col)
  {
    $isCompatible = true;
    if (isset($MapTiles[$row][$col-1])) {
      $isCompatible = $this->isCompatible($MapTiles[$row][$col-1], 'left');
    }
    if ($isCompatible && isset($MapTiles[$row-1][$col])) {
      $isCompatible = $this->isCompatible($MapTiles[$row-1][$col], 'top');
    }
    if ($isCompatible && isset($MapTiles[$row][$col+1])) {
      $isCompatible = $this->isCompatible($MapTiles[$row][$col+1], 'right');
    }
    if ($isCompatible && isset($MapTiles[$row+1][$col])) {
      $isCompatible = $this->isCompatible($MapTiles[$row+1][$col], 'bottom');
    }
    return $isCompatible;
  }

  private function compSides($sideLeft, $sideRight)
  {
    // B: Building
    // S : Street
    // V : terrain Vague
    // C : Couloir
    // --> P : Prison Outbreak
    // --> M : Toxic City Mall
    // --> H : Rue Morgue
    // --> W : Museum Worricow
    // --> F : Highschool Funeral

    $arrCorridors = array('P', 'M', 'H', 'W', 'F');
    if ($sideLeft[0]==$sideRight[2] && $sideLeft[1]==$sideRight[1] && $sideLeft[2]==$sideRight[0]) {
      return true;
    }
    if (in_array($sideLeft[0], $arrCorridors) && in_array($sideRight[2], $arrCorridors) &&
        in_array($sideLeft[1], $arrCorridors) && in_array($sideRight[1], $arrCorridors) &&
        in_array($sideLeft[2], $arrCorridors) && in_array($sideRight[0], $arrCorridors)) {
      return true;
    }
    return false;
  }

  public function getTop()
  {
    switch ($this->orientation) {
      case 'top' :
        $top = $this->Tile->getSideTop();
      break;
      case 'right' :
        $top = $this->Tile->getSideLeft();
      break;
      case 'bottom' :
        $top = $this->Tile->getSideBottom();
      break;
      default :
        $top = $this->Tile->getSideRight();
      break;
    }
    return $top;
  }
  public function getBottom()
  {
    switch ($this->orientation) {
      case 'top' :
        $bottom = $this->Tile->getSideBottom();
      break;
      case 'right' :
        $bottom = $this->Tile->getSideRight();
      break;
      case 'bottom' :
        $bottom = $this->Tile->getSideTop();
      break;
      default :
        $bottom = $this->Tile->getSideLeft();
      break;
    }
    return $bottom;
  }

  public function getRight()
  {
    switch ($this->orientation) {
      case 'top' :
        $right = $this->Tile->getSideRight();
      break;
      case 'right' :
        $right = $this->Tile->getSideTop();
      break;
      case 'bottom' :
        $right = $this->Tile->getSideLeft();
      break;
      default :
        $right = $this->Tile->getSideBottom();
      break;
    }
    return $right;
  }
  public function getLeft()
  {
    switch ($this->orientation) {
      case 'top' :
        $left = $this->Tile->getSideLeft();
      break;
      case 'right' :
        $left = $this->Tile->getSideBottom();
      break;
      case 'bottom' :
        $left = $this->Tile->getSideRight();
      break;
      default :
        $left = $this->Tile->getSideTop();
      break;
    }
    return $left;
  }
}
<file_sep>/core/daoimpl/SurvivorDaoImpl.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe SurvivorDaoImpl
 * @author Hugues.
 * @since 1.0.00
 * @version 1.05.06
 */
class SurvivorDaoImpl extends LocalDaoImpl
{
  protected $whereFiltersExpansionIn;
  /**
   * Class constructor
   */
  public function __construct()
  { parent::__construct('Survivor'); }
  /**
   * @param array $rows
   * @return array
   */
  protected function convertToArray($rows)
  {
    $Items = array();
    if (!empty($rows)) {
      foreach ($rows as $row) {
        $Items[] = Survivor::convertElement($row);
      }
    }
    return $Items;
  }

  /**
   * @param array $arrParams
   * @param array $filters
   * @return array
   */
  public function selectEntriesWithFiltersIn($arrParams, $filters)
  {
    // On s'appuie sur la requête de base.
    $requete  = $this->selectRequest.$this->fromRequest;
    // Contrainte sur la Compétence Bleue
    if (isset($filters[self::COLOR_BLUE.'-'.self::FIELD_SKILLID])) {
      $requete .= 'INNER JOIN wp_11_zombicide_survivor_skill ss1 ON s.id=ss1.survivorId ';
      $requete .= 'AND ss1.skillId='.$filters[self::COLOR_BLUE.'-'.self::FIELD_SKILLID].' AND ss1.tagLevelId IN (10,11) ';
    }
    // Contrainte sur la Compétence Jaune
    if (isset($filters[self::COLOR_YELLOW.'-'.self::FIELD_SKILLID])) {
      $requete .= 'INNER JOIN wp_11_zombicide_survivor_skill ss2 ON s.id=ss2.survivorId ';
      $requete .= 'AND ss2.skillId='.$filters[self::COLOR_YELLOW.'-'.self::FIELD_SKILLID].' AND ss2.tagLevelId IN (20) ';
    }
    // Contrainte sur la Compétence Orange
    if (isset($filters[self::COLOR_ORANGE.'-'.self::FIELD_SKILLID])) {
      $requete .= 'INNER JOIN wp_11_zombicide_survivor_skill ss3 ON s.id=ss3.survivorId ';
      $requete .= 'AND ss3.skillId='.$filters[self::COLOR_ORANGE.'-'.self::FIELD_SKILLID].' AND ss3.tagLevelId IN (30,31) ';
    }
    // Contrainte sur la Compétence Rouge
    if (isset($filters[self::COLOR_RED.'-'.self::FIELD_SKILLID])) {
      $requete .= 'INNER JOIN wp_11_zombicide_survivor_skill ss4 ON s.id=ss4.survivorId ';
      $requete .= 'AND ss4.skillId='.$filters[self::COLOR_RED.'-'.self::FIELD_SKILLID].' AND ss4.tagLevelId IN (40,41,42) ';
    }
    // On peut aussi trier
    $requete .= $this->orderBy;
    // Et retourner le tableau de résultats.
    return $this->convertToArray($this->selectEntriesAndLogQuery(__FILE__, __LINE__, $requete, $arrParams));
  }

  /**
   * @param string $file
   * @param int $line
   * @param array $arrParams
   * @return array|Survivor
   */
  public function select($file, $line, $arrParams)
  { return parent::localSelect($file, $line, $arrParams, new Survivor()); }
  /**
   * @param array $filters
   * @return array
   */
  public function selectEntriesInExpansions($filters) {
    $requete  = $this->selectRequest.$this->fromRequest;
    $requete .= "WHERE name LIKE '%s' AND zombivor LIKE '%s' AND ultimate LIKE '%s' AND (expansionId LIKE '%s' ";
    $requete .= "OR expansionId IN (".$filters[SQL_PARAMS_WHERE][3].")) ";
    $requete .= "AND background LIKE '%s' AND liveAble LIKE '%s' ";
    $requete .= $this->orderBy.$this->limit;
    return $this->convertToArray($this->selectEntriesAndLogQuery(__FILE__, __LINE__, $requete, $filters));
  }
}
<file_sep>/core/domain/LocalDomain.class.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe LocalDomain
 * @author Hugues.
 * @since 1.00.00
 * @version 1.07.20
 */
class LocalDomain extends GlobalDomain implements ConstantsInterface
{

  public function __construct($attributes=array())
  {
    global $globalExpansions;
    $globalExpansions = array();
    parent::__construct($attributes);
    $this->DurationServices          = new DurationServices();
    $this->EquipmentKeywordServices  = new EquipmentKeywordServices();
    $this->EWProfileServices         = new EquipmentWeaponProfileServices();
    $this->ExpansionServices         = new ExpansionServices();
    $this->KeywordServices           = new KeywordServices();
    $this->LevelServices             = new LevelServices();
    $this->MissionServices           = new MissionServices();
    $this->MissionExpansionServices  = new MissionExpansionServices();
    $this->MissionObjectiveServices  = new MissionObjectiveServices();
    $this->MissionRuleServices       = new MissionRuleServices();
    $this->MissionTileServices       = new MissionTileServices();
    $this->ObjectiveServices         = new ObjectiveServices();
    $this->OrigineServices           = new OrigineServices();
    $this->PlayerServices            = new PlayerServices();
    $this->RuleServices              = new RuleServices();
    $this->SkillServices             = new SkillServices();
    $this->SurvivorSkillServices     = new SurvivorSkillServices();
    $this->TileServices              = new TileServices();
    $this->WeaponProfileServices     = new WeaponProfileServices();
    $this->WpPostServices            = new WpPostServices();
  }
  /**
   * @return Expansion
   */
  public function getExpansion()
  {
    global $globalExpansions;
    if ($this->Expansion==null) {
      if (isset($globalExpansions[$this->expansionId])) {
        $this->Expansion = $globalExpansions[$this->expansionId];
      } else {
        $this->Expansion = $this->ExpansionServices->selectExpansion($this->expansionId);
        $globalExpansions[$this->expansionId] = $this->Expansion;
      }
    }
    return $this->Expansion;
  }













  /**
   * @return string
   */
  public function toJson()
  {
    $classVars = $this->getClassVars();
    $str = '';
    foreach ($classVars as $key => $value) {
      if ($str!='') {
        $str .= ', ';
      }
      $str .= '"'.$key.'":'.json_encode($this->getField($key));
    }
    return '{'.$str.'}';
  }
  /**
   * @param array $post
   * @return bool
   */
  public function updateWithPost($post)
  {
    $classVars = $this->getClassVars();
    unset($classVars['id']);
    $doUpdate = false;
    foreach ($classVars as $key => $value) {
      if (is_array($post[$key])) {
        $value = stripslashes(implode(';', $post[$key]));
      } else {
        $value = stripslashes($post[$key]);
      }
      if ($this->{$key} != $value) {
        $doUpdate = true;
        $this->{$key} = $value;
      }
    }
    return $doUpdate;
  }
  /**
   * @return int
   */
  public static function getWpUserId()
  { return get_current_user_id(); }

  /**
   * @version 1.04.27
   * @param array $addArg
   * @param array $remArg
   * @return string
   */
  public function getQueryArg($addArg, $remArg=array())
  {
    $addArg['page'] = 'hj-zombicide/admin_manage.php';
    $remArg[] = 'form';
    $remArg[] = 'id';
    return add_query_arg($addArg, remove_query_arg($remArg, 'http://zombicide.jhugues.fr/wp-admin/admin.php'));
  }
}
<file_sep>/core/services/WpTagServices.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe WpTagServices
 * @author Hugues.
 * @since 1.04.16
 * @version 1.07.20
 */
class WpTagServices extends GlobalServices {

  public function getTagByName($name, $dbTag='wp_5')
  {
    $requete = "SELECT t.term_id AS termId FROM ".$dbTag."_terms t INNER JOIN ".$dbTag."_term_taxonomy tt ON t.term_id = tt.term_id WHERE name = '$name' AND taxonomy='post_tag';";
    $rows = MySQL::wpdbSelect($requete);
    if (empty($rows)) {
      return new WpTag();
    } else {
      $row = array_shift($rows);
      $tagId = $row->termId;
      return WpTag::convertElement(get_tag($tagId));
    }
  }

  public function getTagBySlug($slug='')
  {
    $requete = "SELECT term_id FROM wp_11_terms WHERE slug = '$slug';";
    $rows = MySQL::wpdbSelect($requete);
    if (empty($rows)) {
      return new WpTag();
    } else {
      $row = array_shift($rows);
      $tagId = $row->term_id;
      return WpTag::convertElement(get_tag($tagId));
    }
  }

  public function getTags()
  {
    $WpTags = array();
    $tags = get_tags();
    while (!empty($tags)) {
      // Qu'on convertit en WpTag
      array_push($WpTags, WpTag::convertElement(array_shift($tags)));
    }
    return $WpTags;
  }
}
?>
<file_sep>/core/bean/WpPostNewsBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe WpPostNewsBean
 * @author Hugues
 * @since 1.05.09
 * @version 1.05.09
 */
class WpPostNewsBean extends WpPostBean
{
  protected $urlTemplate = 'web/pages/public/fragments/article-news-extract.php';
  /**
   * Constructeur
   */
  public function __construct($WpPost='')
  {
    parent::__construct();
    $this->WpPost = $WpPost;
  }

  /**
   * @param string $isHome
   * @return string
   */
  public function displayWpPost()
  {
    $args = array(
      $this->WpPost->getPostContent(),
      $this->WpPost->getPostTitle(),
      '','','','','','','',
    );
    return $this->getRender($this->urlTemplate, $args);
  }
}
<file_sep>/core/bean/WpPageTagBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe WpPageTagBean
 * @author Hugues
 * @since 1.04.16
 * @version 1.08.02
 */
class WpPageTagBean extends WpPageBean
{
  protected $urlTemplate = 'web/pages/public/wppage-spawncards.php';
  /**
   * Class Constructor
   * @param WpPage $WpPage
   */
  public function __construct($scriptUrl='')
  {
    $arr = explode('/', $scriptUrl);
    $tag = $arr[2];
    // On défini les Services
    $this->DurationServices = new DurationServices();
    $this->LevelServices    = new LevelServices();
    $this->WpPostServices   = new WpPostServices();
    $this->WpTagServices    = new WpTagServices();
    // On initialise le Tag
    $this->WpTag = $this->WpTagServices->getTagBySlug($tag);
  }
  /**
   * @return string
   */
  public function getContentPage()
  {
    /////////////////////////////////////////////////////////////////////////////
    // On récupère le slug
    $slug = $this->WpTag->getSlug();
    /////////////////////////////////////////////////////////////////////////////
    // Slugs relatifs à la Durée des Missions
    if (strpos($slug, '-minutes')!==false) {
      $arr = explode('-', $slug);
      $minDuration = $arr[0];
      $Durations = $this->DurationServices->getDurationsWithFilters(array(self::FIELD_MINDURATION=>$minDuration));
      $Duration = array_shift($Durations);
      $WpPage = new WpPageMissionsBean();
      $WpPage->setFilters(array(self::CST_FILTERS=>self::FIELD_DURATIONID.'='.$Duration->getId()));
      $returned = $WpPage->getListContentPage();
    } else {
      switch ($slug) {
        /////////////////////////////////////////////////////////////////////////////
        // Slugs relatifs à la Difficulté des Missions
        case 'tutoriel'   :
        case 'facile'     :
        case 'moyen'      :
        case 'difficile'  :
        case 'hardcore'   :
        case 'competitif' :
          $Levels = $this->LevelServices->getLevelsWithFilters(array(self::FIELD_NAME=>$slug));
          $Level = array_shift($Levels);
          $WpPage = new WpPageMissionsBean();
          $WpPage->setFilters(array(self::CST_FILTERS=>self::FIELD_LEVELID.'='.$Level->getId()));
          $returned = $WpPage->getListContentPage();
        break;
        default :
          $returned = "WIP : Gestion des Tags [[$slug]].";
        break;
      }
    }
    return $returned;
  }
}
<file_sep>/core/services/MissionExpansionServices.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe MissionExpansionServices
 * @author Hugues.
 * @since 1.0.00
 * @version 1.04.27
 */
class MissionExpansionServices extends LocalServices
{
  /**
   * L'objet Dao pour faire les requêtes
   * @var MissionExpansionDaoImpl $Dao
   */
  protected $Dao;
  /**
   * Class Constructor
   */
  public function __construct()
  {
    parent::__construct();
    $this->Dao = new MissionExpansionDaoImpl();
    $this->ExpansionServices = new ExpansionServices();
  }

  /**
   * @param array $arrFilters
   */
  private function buildFilters($arrFilters)
  {
    $this->arrParams[self::SQL_WHERE] = array();
    array_push($this->arrParams[self::SQL_WHERE], $this->addFilter($arrFilters, self::FIELD_MISSIONID));
    array_push($this->arrParams[self::SQL_WHERE], $this->addFilter($arrFilters, self::FIELD_EXPANSIONID));
  }
  /**
   * @param array $arrFilters
   * @param string $orderby
   * @param string $order
   * @return array
   */
  public function getMissionExpansionsWithFilters($arrFilters=array(), $orderby=self::FIELD_ID, $order=self::ORDER_ASC)
  {
    $this->arrParams = $this->buildOrderAndLimit($orderby, $order);
    $this->buildFilters($arrFilters);
    return $this->Dao->selectEntriesWithFilters(__FILE__, __LINE__, $this->arrParams);
  }

  public function deleteMissionExpansion($MissionExpansion)
  { $this->delete(__FILE__, __LINE__, $MissionExpansion); }
  public function insertMissionExpansion($MissionExpansion)
  { $this->insert(__FILE__, __LINE__, $MissionExpansion); }
}
<file_sep>/core/services/LocalServices.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe LocalServices
 * @author Hugues.
 * @since 1.00.00
 * @version 1.05.20
 */
class LocalServices extends GlobalServices implements ConstantsInterface
{
  protected $arrParams = array();
  /**
   * Texte par défaut du Select
   * @var string $labelDefault
   */
  protected $labelDefault = '';
  /**
   * Valeur par défaut de la classe du Select
   * @var string $classe
   */
  protected $classe = 'form-control';
  /**
   * Le Select est-il multiple ?
   * @var boolean $multiple
   */
  protected $multiple = false;

  /**
   * Class Constructor
   */
  public function __construct()
  {
  }

  /**
   * @param array $arrFilters
   * @param string $field
   * @return string
   */
  protected function addFilter($arrFilters, $field)
  {
    return (isset($arrFilters[$field]) && !empty($arrFilters[$field]) ? $arrFilters[$field] : '%');
  }
  /**
   * @param array $arrFilters
   * @param string $field
   * @return string
   */
  protected function addNonArrayFilter($arrFilters, $field, $defaultSearch='%')
  {
    return (isset($arrFilters[$field]) && !empty($arrFilters[$field]) && !is_array($arrFilters[$field]) ? $arrFilters[$field] : $defaultSearch);
  }
  /**
   * @param array $arrFilters
   * @param string $field
   * @return string
   */
  protected function addNonArrayWideFilter($arrFilters, $field, $defaultSearch='%')
  {
    return (isset($arrFilters[$field]) && !empty($arrFilters[$field]) && !is_array($arrFilters[$field]) ? '%'.$arrFilters[$field].'%' : $defaultSearch);
  }


  public function prepObject($Obj, $isUpdate=false) {
    $arr = array();
    $vars = $Obj->getClassVars();
    if ( !empty($vars) ) {
      foreach ( $vars as $key=>$value ) {
        if ( $key=='id' ) { continue; }
        $arr[] = $Obj->getField($key);
      }
      if ( $isUpdate ) { $arr[] = $Obj->getField('id'); }
    }
    return $arr;
  }








  /**
   * @param array $arrSetLabels
   * @param string $name
   * @param string $value
   * @return string
   */
  protected function getSetSelect($arrSetLabels, $name, $value)
  {
    $strSelect = '';
    $selName = $name;
    if ($this->labelDefault!='') {
      $strSelect .= '<label class="screen-reader-text" for="'.$name.'">'.$this->labelDefault.'</label>';
    }
    // On créé la base du select
    $strSelect .= '<select id="'.$name.'" name="'.$selName.'" class="'.$this->classe.'"'.($this->multiple?' multiple':'').'>';
    // S'il n'est pas multiple et qu'il a une valeur par défaut, on la met.
    if (!$this->multiple && $this->labelDefault!='') {
      $strSelect .= '<option value="">'.$this->labelDefault.'</option>';
    }
    // On parcourt l'ensemble des couples $key/$value de la liste
    if (!empty($arrSetLabels)) {
      foreach ($arrSetLabels as $key => $labelValue) {
        // Visiblement, la $key peut parfois être nulle et c'est mal.
        if ($key=='') {
          continue;
        }
        // On construit l'option.
        $strSelect .= '<option value="'.$key.'"';
        $strSelect .= ($this->isKeySelected($key, $value) ? ' selected="selected"' : '');
        $strSelect .= '>'.$labelValue.'</option>';
      }
    }
    return $strSelect.'</select>';
  }
  /**
   * @param string $key
   * @param mixed $values
   * @return boolean
   */
  protected function isKeySelected($key, $values)
  {
    // Si on ne cherche pas dans un tableau, on teste juste l'égalité.
    if (!is_array($values)) {
      return trim($key)==trim($values);
    }
    $isSelected = false;
    // Sinon, on parcourt la liste pour essayer de trouver la valeur cherchée.
    while (!empty($values)) {
      $value = array_shift($values);
      if ($key==$value) {
        $isSelected = true;
      }
    }
    return $isSelected;
  }
  /**
   * Vérifie qu'un élément du tableau n'est ni vide ni un tableau.
   * @param array $arrFilters
   * @param string $tag
   * @return boolean
   */
  protected function isNonEmptyAndNoArray($arrFilters, $tag)
  { return !empty($arrFilters[$tag]) && !is_array($arrFilters[$tag]); }

  /**
   * @return int
   */
  public static function getWpUserId()
  { return get_current_user_id(); }




}
<file_sep>/core/domain/SurvivorSkill.class.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe SurvivorSkill
 * @author Hugues.
 * @since 1.0.00
 * @version 1.05.02
 */
class SurvivorSkill extends LocalDomain
{
  /**
   * Id technique de la donnée
   * @var int $id
   */
  protected $id;
  /**
   * Id technique du Survivant
   * @var int $survivorId
   */
  protected $survivorId;
  /**
   * Id technique du Skill
   * @var int $skillId
   */
  protected $skillId;
  /**
   * Id technique du type
   * @var int $survivorTypeId
   */
  protected $survivorTypeId;
  /**
   * Rang de la compétence sur le profil
   * @var int $tagLevelId
   */
  protected $tagLevelId;

  /**
   * @param array $attributes
   */
  public function __construct($attributes=array())
  {
    parent::__construct($attributes);
    $this->SkillServices    = new SkillServices();
    $this->SurvivorServices = new SurvivorServices();
  }

  public function getBean()
  { return new SurvivorSkillBean($this); }

  /**
   * @return int
   */
  public function getId()
  { return $this->id; }
  /**
   * @return int
   */
  public function getSurvivorId()
  { return $this->survivorId; }
  /**
   * @return int
   */
  public function getSkillId()
  { return $this->skillId; }
  /**
   * @return int
   */
  public function getSurvivorTypeId()
  { return $this->survivorTypeId; }
  /**
   * @return int
   */
  public function getTagLevelId()
  { return $this->tagLevelId; }
  /**
   * @param int $id
   */
  public function setId($id)
  { $this->id=$id; }
  /**
   * @param int $survivorId
   */
  public function setSurvivorId($survivorId)
  { $this->survivorId=$survivorId; }
  /**
   * @param int $skillId
   */
  public function setSkillId($skillId)
  { $this->skillId=$skillId; }
  /**
   * @param int $survivorTypeId
   */
  public function setSurvivorTypeId($survivorTypeId)
  { $this->survivorTypeId=$survivorTypeId; }
  /**
   * @param int $tagLevelId
   */
  public function setTagLevelId($tagLevelId)
  { $this->tagLevelId=$tagLevelId; }
  /**
   * @return array
   */
  public function getClassVars()
  { return get_class_vars('SurvivorSkill'); }
  /**
   * @param array $row
   * @param string $a
   * @param string $b
   * @return SurvivorSkill
   */
  public static function convertElement($row, $a='', $b='')
  { return parent::convertElement(new SurvivorSkill(), self::getClassVars(), $row); }
  /**
   * @return Skill
   */
  public function getSkill()
  {
    if ($this->Skill == null) {
      $this->Skill = $this->SkillServices->selectSkill($this->skillId);
    }
    return $this->Skill;
  }
  /**
   * @return string
   */
  public function getSkillName()
  { return $this->getSkill()->getName(); }
  public function getSurvivor()
  {
    if ($this->Survivor == null) {
      $this->Survivor = $this->SurvivorServices->selectSurvivor($this->survivorId);
    }
    return $this->Survivor;
  }
}
<file_sep>/core/services/LevelServices.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe LevelServices
 * @author Hugues.
 * @since 1.04.16
 * @version 1.04.27
 */
class LevelServices extends LocalServices
{
  /**
   * L'objet Dao pour faire les requêtes
   * @var LevelDaoImpl $Dao
   */
  protected $Dao;
  /**
   * Class Constructor
   */
  public function __construct()
  {
    parent::__construct();
    $this->Dao = new LevelDaoImpl();
  }

  /**
   * @param array $arrFilters
   */
  private function buildFilters($arrFilters)
  {
    $this->arrParams[self::SQL_WHERE] = array();
    array_push($this->arrParams[self::SQL_WHERE], $this->addFilter($arrFilters, self::FIELD_NAME));
  }
  /**
   * @param array $arrFilters
   * @param string $orderby
   * @param string $order
   * @return array
   */
  public function getLevelsWithFilters($arrFilters=array(), $orderby=self::FIELD_ID, $order=self::ORDER_ASC)
  {
    $this->arrParams = $this->buildOrderAndLimit($orderby, $order);
    $this->buildFilters($arrFilters);
    return $this->Dao->selectEntriesWithFilters(__FILE__, __LINE__, $this->arrParams);
  }
  /**
   * @param int $id
   * @return Level
   */
  public function selectLevel($id)
  { return $this->select(__FILE__, __LINE__, $id); }

}
<file_sep>/core/domain/Survivor.class.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe Survivor
 * @author Hugues.
 * @since 1.0.00
 * @version 1.07.21
 */
class Survivor extends WpPostRelais
{
  /**
   * Id technique de la donnée
   * @var int $id
   */
  protected $id;
  /**
   * Nom de la donnée
   * @var string $name
   */
  protected $name;
  /**
   * A un profil Standard
   * @var int $standard
   */
  protected $standard;
  /**
   * A un profil Zombivor
   * @var int $zombivor
   */
  protected $zombivor;
  /**
   * A un profil Ultimate
   * @var int $ultimate
   */
  protected $ultimate;
  /**
   * A un profil Ultimate Zombivor
   * @var int $ultimatez
   */
  protected $ultimatez;
  /**
   * Id de l'extension
   * @var int $expansionId
   */
  protected $expansionId;
  /**
   * Background du Survivant
   * @var string $background
   */
  protected $background;
  /**
   * Eventuelle image alternative
   * @var string $altImgName
   */
  protected $altImgName;
  /**
   * Le Survivant peut-il être joué en ligne ?
   * @var int $liveAble
   */
  protected $liveAble;

  public function __construct($attributes=array())
  {
    parent::__construct($attributes);
    $this->imgBaseUrl = 'http://www.jhugues.fr/wp-content/plugins/hj-zombicide/web/rsc/img/portraits/p';
    $this->SurvivorSkills = array();
  }
  /**
   * @return int
   */
  public function getId()
  {return $this->id; }
  /**
   * @return string
   */
  public function getName()
  { return $this->name; }
  /**
   * @return boolean
   */
  public function isStandard()
  { return ($this->standard==1); }
  /**
   * @return boolean
   */
  public function isZombivor()
  { return ($this->zombivor==1); }
  /**
   * @return boolean
   */
  public function isUltimate()
  { return ($this->ultimate==1); }
  /**
   * @return boolean
   */
  public function isUltimatez()
  { return ($this->ultimatez==1); }
  /**
   * @return int
   */
  public function getExpansionId()
  { return $this->expansionId; }
  /**
   * @return string
   */
  public function getBackground()
  { return $this->background; }
  /**
   * @return string
   */
  public function getAltImgName()
  { return $this->altImgName; }
  /**
   * @return boolean
   */
  public function isLiveAble()
  { return ($this->liveAble==1); }
  /**
   * @param int $id
   */
  public function setId($id)
  { $this->id=$id; }
  /**
   * @param string $name
   */
  public function setName($name)
  { $this->name=$name; }
  /**
   * @param int $standard
   */
  public function setStandard($standard)
  { $this->standard=$standard; }
  /**
   * @param int $zombivor
   */
  public function setZombivor($zombivor)
  { $this->zombivor=$zombivor; }
  /**
   * @param int $ultimate
   */
  public function setUltimate($ultimate)
  { $this->ultimate=$ultimate; }
  /**
   * @param int $ultimatez
   */
  public function setUltimatez($ultimatez)
  { $this->ultimatez=$ultimatez; }
  /**
   * @param int $expansionId
   */
  public function setExpansionId($expansionId)
  { $this->expansionId=$expansionId; }
  /**
   * @param string $background
   */
  public function setBackground($background)
  { $this->background=$background; }
  /**
   * @param string $altImgName
   */
  public function setAltImgName($altImgName)
  { $this->altImgName=$altImgName; }
  /**
   * @param int $liveAble
   */
  public function setLiveAble($liveAble)
  { $this->liveAble=$liveAble; }

  ///////////////////////////////////////////////////////////////
  /**
   * @return array
   */
  public function getClassVars()
  { return get_class_vars('Survivor'); }
  /**
   * @param array $row
   * @param string $a
   * @param string $b
   * @return Survivor
   */
  public static function convertElement($row, $a='', $b='')
  { return parent::convertElement(new Survivor(), self::getClassVars(), $row); }
  /**
   * @return Bean
   */
  public function getBean()
  { return new SurvivorBean($this); }
  ///////////////////////////////////////////////////////////////

  /**
   * @return string
   */
  public function getWpPost()
  { return $this->getMainWpPost(self::FIELD_SURVIVORID, $this->id, self::WP_CAT_SURVIVOR_ID); }











  ////////////////////////////////////////////////////////////////////////////
  // Méthodes relatives aux Portraits
  /**
   * @param string $str
   * @return string
   */
  public function getNiceName($str='')
  {
    if ($str=='') {
      $str = $this->name;
    }
    return str_replace(array(' ', '#'), '', strtolower($str));
  }
  /**
   * @param string $type
   * @return string
   */
  public function getPortraitUrl($type='')
  {
    if (!$this->isStandard() && $type=='') {
      $type = 'u';
    }
    $usedName = ($this->getAltImgName()!='' ? $this->getAltImgName() : $this->name);
    $wholeUrl = $this->imgBaseUrl.$this->getNiceName($usedName).($type!='' ? '-'.$type : '').'.jpg';
    if (self::isAdmin() && @getimagesize($wholeUrl)===false) {
      $wholeUrl = $this->imgBaseUrl.($type!='' ? '-'.$type : '').'.jpg';
    }
    return $wholeUrl;
  }
  ////////////////////////////////////////////////////////////////////////////

  protected function initSurvivorSkills()
  {
    $SurvivorSkills = $this->SurvivorSkillServices->getSurvivorSkillsWithFilters(array(self::FIELD_SURVIVORID=>$this->getId()));
    while (!empty($SurvivorSkills)) {
      $SurvivorSkill = array_shift($SurvivorSkills);
      $survivorTypeId = $SurvivorSkill->getSurvivorTypeId();
      $tagLevelId = $SurvivorSkill->getTagLevelId();
      if (!isset($this->SurvivorSkills[$survivorTypeId])) {
        $this->SurvivorSkills[$survivorTypeId] = array();
      }
      $this->SurvivorSkills[$survivorTypeId][$tagLevelId] = $SurvivorSkill->getSkill();
    }
  }

  public function getSkill($type, $rank)
  {
    if ($this->SurvivorSkills == null) {
      $this->SurvivorSkills = array();
      $this->initSurvivorSkills();
    }
    return ($this->SurvivorSkills[$type][$rank]==null ? new Skill() : $this->SurvivorSkills[$type][$rank]);
  }





  /**
   * @param int $survivorTypeId
   * @return boolean
   */
  public function areDataSkillsOkay($survivorTypeId=1)
  {
    // On récupère les Compétences associées au Survivant pour le profil passé en paramètre.
    $SurvivorSkills = $this->getSurvivorSkills($survivorTypeId);
    $nbSkills = count($SurvivorSkills);
    // On doit avoir 7 (profils standards et zombivants) ou
    // 8 (ultimate survivant et zombivant, ou standard avec Descente en Rappel ou Pilote d'Hélicoptère) compétences de retournées.
    // Si on a ce nombre de compétences, on retourne true. Sinon false.
    return ($nbSkills==7 || $nbSkills==8);
  }
  /**
   * @return array SurvivorSkill
   */
  public function getSurvivorSkills($survivorTypeId='')
  {
    if ($this->SurvivorSkills == null) {
      $arrFilters = array(self::FIELD_SURVIVORID=>$this->id);
      if ($survivorTypeId!='') {
        $arrFilters[self::FIELD_SURVIVORTYPEID] = $survivorTypeId;
      }
      $this->SurvivorSkills = $this->SurvivorSkillServices->getSurvivorSkillsWithFilters($arrFilters);
    }
    return $this->SurvivorSkills;
  }

  public function getAdminUlSkills($survivorTypeId=1)
  {
    $args = array(
      self::FIELD_SURVIVORID     => $this->getId(),
      self::FIELD_SURVIVORTYPEID => $survivorTypeId,
    );
    $SurvivorSkills = $this->SurvivorSkillServices->getSurvivorSkillsWithFilters($args);
    $strReturned = '';
    while (!empty($SurvivorSkills)) {
      $SurvivorSkill = array_shift($SurvivorSkills);
      if ($SurvivorSkill->getSurvivorTypeId()!=$survivorTypeId) {
        continue;
      }
      $strReturned .= '<li><span>'.$SurvivorSkill->getBean()->getBadge().'</span></li>';
    }
    return $this->getBean()->getBalise(self::TAG_UL, $strReturned, array(self::ATTR_CLASS=>'col-12'));
  }





  /**
   * @param bool $isHome
   * @return string
   */
  public function getStrClassFilters()
  { return 'col-12 col-md-6 col-xl-4'; }
  /**
   * Retourne si le type de Survivant associé au SurvivorSkill est bien celui attendu.
   * @param string $type Le type recherché
   * @param SurvivorSkill $SurvivorSkill
   * @return boolean
   */
  public function controlTypeAndSkill($type, $SurvivorSkill)
  {
    return ($type=='' && $SurvivorSkill->getSurvivorTypeId()!=1 ||
        $type=='z' && $SurvivorSkill->getSurvivorTypeId()!=2 ||
        $type=='u' && $SurvivorSkill->getSurvivorTypeId()!=3 ||
        $type=='uz' && $SurvivorSkill->getSurvivorTypeId()!=4);
  }
  /**
   * @param string $type
   * @param boolean $withLink
   * @return string
   */
  public function getUlSkills($type='', $withLink=false, $isHome=false)
  {
    if ($type=='' && !$this->isStandard() && $this->isUltimate()) {
      $type='u';
    }
    $classExtra = 'col-12'.($isHome ? '' : ' col-sm-6 col-lg-3');
    $SurvivorSkills = $this->getSurvivorSkills();
    $str = '';
    $strTmp = '';
    if (!empty($SurvivorSkills)) {
      foreach ($SurvivorSkills as $SurvivorSkill) {
        if ($this->controlTypeAndSkill($type, $SurvivorSkill)) {
          continue;
        }
        switch ($SurvivorSkill->getTagLevelId()) {
          case 20 :
          case 30 :
          case 40 :
            $str .= $this->getBean()->getBalise(self::TAG_UL, $strTmp, array(self::ATTR_CLASS=>$classExtra));
            $strTmp = '';
          break;
          default :
          break;
        }
        $strTmp .= $this->getSkillLi($SurvivorSkill, $withLink);
      }
      $str .= $this->getBean()->getBalise(self::TAG_UL, $strTmp, array(self::ATTR_CLASS=>$classExtra));
    }
    return $str;
  }
  private function getSkillLi($SurvivorSkill, $withLink)
  { return $this->getBean()->getBalise(self::TAG_LI, $SurvivorSkill->getBean()->getBadge($withLink)); }
}
<file_sep>/core/bean/TokenBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe TokenBean
 * @author Hugues
 * @since 1.11.01
 * @version 1.11.01
 */
class TokenBean extends LocalBean
{
  protected $urlDirLiveMissions = '/web/rsc/missions/live/';

  private $addClass = ' token';
  private $color;
  private $coordX;
  private $coordY;
  private $height;
  private $id;
  private $level;
  private $name;
  private $orientation;
  private $quantite;
  private $src;
  private $status;
  private $type;
  private $width;

  private $urlMenuZombiesTemplate = 'web/pages/public/fragments/menu-zombies-creation.php';
  private $urlOnlineDetailSurvivor = 'web/pages/public/fragments/online-detail-survivor.php';

  private $arrTagColors = array(1=>'blue', 2=>'yellow', 3=>'orange', 4=>'red');

  /**
   * @param Expansion $Expansion
   */
  public function __construct($chip=null)
  {
    parent::__construct();
    $this->SurvivorServices = new SurvivorServices();
    $this->SkillServices = new SkillServices();

    if (!is_array($chip)) {
      $this->color       = $chip->attributes()['color'];
      $this->coordX      = $chip->attributes()['coordX'];
      $this->coordY      = $chip->attributes()['coordY'];
      $this->id          = $chip->attributes()['id'];
      $this->level       = $chip->attributes()['level'];
      $this->orientation = $chip->attributes()['orientation'];
      $this->quantite    = $chip->attributes()['quantite'];
      $this->src         = $chip->attributes()['src'];
      $this->status      = $chip->attributes()['status'];
      $this->type        = $chip->attributes()['type'];
      $this->experiencePoints = $chip->attributes()['experiencePoints'];
      $this->actionPoints = $chip->attributes()['actionPoints'];
      $this->hitPoints   = $chip->attributes()['hitPoints'];
    } else {
      $this->color       = $chip[self::XML_ATTRIBUTES]['color'];
      $this->coordX      = $chip[self::XML_ATTRIBUTES]['coordX'];
      $this->coordY      = $chip[self::XML_ATTRIBUTES]['coordY'];
      $this->id          = $chip[self::XML_ATTRIBUTES]['id'];
      $this->level       = $chip[self::XML_ATTRIBUTES]['level'];
      $this->orientation = $chip[self::XML_ATTRIBUTES]['orientation'];
      $this->quantite    = $chip[self::XML_ATTRIBUTES]['quantite'];
      $this->src         = $chip[self::XML_ATTRIBUTES]['src'];
      $this->status      = $chip[self::XML_ATTRIBUTES]['status'];
      $this->type        = $chip[self::XML_ATTRIBUTES]['type'];
      $this->experiencePoints = $chip[self::XML_ATTRIBUTES]['experiencePoints'];
      $this->actionPoints = $chip[self::XML_ATTRIBUTES]['actionPoints'];
      $this->hitPoints   = $chip[self::XML_ATTRIBUTES]['hitPoints'];
    }
    $this->chip = $chip;
    $this->patternZombie = '/z(Walker|Runner|Fatty|Abomination)(Standard)/';
    $this->init();
  }



  public function getJsonModifications($id, $bln_create)
  {
    // On retourne Le Token et son menu
    $args = array(
      // On veut retourner le Tag mis à jour.
      array($id, $this->getTokenBalise()),
      // On veut retourner le Menu associé au Tag mis à jour.
      array('m'.$id, $this->getTokenMenu()),
    );
    // Eventuellement, on ajoute pour certains trucs spéciaux.
    if ($this->type=='Survivor') {
      $args[] = array('portrait-'.($bln_create ? 'new' : $id), $this->getTokenPortrait());
      $args[] = array('detail-survivor-'.($bln_create ? 'new' : $id), $this->getTokenDetail());
    }
    // Puis on retourne le tout
    return $args;
  }






  private function init()
  {
    $this->baliseContent = '';
    switch ($this->type) {
      case 'Noise' :
        $this->width     = 55;
        $this->height    = 50;
        $this->addClass .= ' noise';
        $this->name      = 'noise';
        $this->baliseContent = $this->getBalise(self::TAG_DIV, $this->quantite, array(self::ATTR_CLASS=>'badge'));
      break;
      case 'Door' :
        $this->width     = 56;
        $this->height    = 56;
        $this->name      = 'door_'.strtolower($this->color).'_'.strtolower($this->status);
        $this->addClass .= ' '.$this->orientation;
      break;
      case 'Objective' :
        $this->width     = 50;
        $this->height    = 50;
        $this->name      = 'objective_'.($this->status=='Unveiled' ? 'red' : strtolower($this->color));
      break;
      case 'Spawn' :
        $this->width     = 100;
        $this->height    = 50;
        $this->name      = 'spawn_'.strtolower($this->color);
        $this->addClass .= ' '.$this->orientation.' '.strtolower($this->status);
      break;
      case 'Exit' :
        $this->width     = 100;
        $this->height    = 50;
        $this->name      = 'exit';
        $this->addClass .= ' '.$this->orientation.($this->status=='Unactive' ? ' unactive' : '');
      break;
      case 'Survivor' :
        $this->width     = 50;
        $this->height    = 50;
        $this->addClass .= ' survivor '.$this->level;
        $args = array(
          self::ATTR_SRC=>'/wp-content/plugins/hj-zombicide/web/rsc/img/portraits/'.$this->src.'.jpg'
        );
        $this->baliseContent  = $this->getBalise(self::TAG_IMG, '', $args);
      break;
      case 'Zombie' :
        if (preg_match($this->patternZombie, $this->src, $matches)) {
          $strName       = $matches[1].' '.$matches[2];
        } else {
          $strName       = 'Pattern foireux ('.$this->src.')';
        }
        $this->width     = 50;
        $this->height    = 50;
        $this->type      = 'Zombie';
        $this->addClass .= ' zombie '.$matches[2];
        $args = array(
          self::ATTR_SRC   => '/wp-content/plugins/hj-zombicide/web/rsc/img/zombies/'.$this->src.'.png',
          self::ATTR_TITLE => $strName.' x'.$this->quantite,
        );
        $this->baliseContent  = $this->getBalise(self::TAG_IMG, '', $args);
        $this->baliseContent .= $this->getBalise(self::TAG_DIV, $this->quantite, array(self::ATTR_CLASS=>'badge'));
      break;
      default :
        $this->name      = '';
      break;
    }
  }

  public function getTokenBalise()
  {
    if ($this->status=='Picked') {
      return '';
    }
    $args = array(
      self::ATTR_CLASS   => 'chip'.$this->addClass,
      'data-color'       => $this->color,
      'data-coordX'      => $this->coordX,
      'data-coordY'      => $this->coordY,
      'data-height'      => $this->height,
      self::ATTR_ID      => $this->id,
      'data-orientation' => $this->orientation,
      'data-status'      => $this->status,
      'data-type'        => $this->type,
      'data-width'       => $this->width,
    );
    if (!empty($this->name)) {
      $args['style'] = "background:url('/wp-content/plugins/hj-zombicide/web/rsc/img/tokens/".$this->name.".png');";
    }

    return $this->getBalise(self::TAG_DIV, $this->baliseContent, $args);
  }

  private function getLiMenuSeparator()
  { return $this->getBalise(self::TAG_LI, '', array(self::ATTR_CLASS=>'menu-separator')); }
  private function getLiMenuItem($label, $act, $iCode, $disabled='', $type='')
  {
    $span   = $this->getBalise(self::TAG_SPAN, $label, array(self::ATTR_CLASS=>'menu-text'));
    $i = $this->getBalise(self::TAG_I, '', array(self::ATTR_CLASS=>'fa fa-'.$iCode));
    $button = $this->getBalise(self::TAG_BUTTON, $i.$span, array(self::ATTR_TYPE=>'button', self::ATTR_CLASS=>'menu-btn'));
    $argsLi = array(
      self::ATTR_CLASS   => 'menu-item'.$disabled,
      self::ATTR_ID      => $this->id,
      'data-menu-action' => $act
    );
    if ($type!='') {
      $argsLi['data-quantite'] = 1;
      $argsLi['data-type'] = $type;
    }
    return $this->getBalise(self::TAG_LI, $button, $argsLi);
  }
  private function getDoorMenu()
  {
    // On peut vouloir ouvrir ou fermer une porte.
    $strMenu = $this->getLiMenuItem('Ouvrir', 'open', 'folder-open-o', ($this->status=='Closed' ? '' : ' '.self::CST_DISABLED));
    return $strMenu . $this->getLiMenuItem('Fermer', 'close', 'folder-o', ($this->status=='Opened' ? '' : ' '.self::CST_DISABLED));
  }
  private function getObjectiveMenu()
  {
    // On peut vouloir révéler ou prendre un Objectif
    $strMenu = $this->getLiMenuItem('Révéler', 'reveal', 'share-square-o', ($this->status=='Unveiled' ? '' : ' '.self::CST_DISABLED));
    return $strMenu . $this->getLiMenuItem('Prendre', 'pick', 'check-square-o', ($this->status=='Unactive' ? '' : ' '.self::CST_DISABLED));
  }
  private function getExitMenu()
  {
    $strMenu  = $this->getLiMenuItem('Activer', 'activate', 'thumbs-o-up', ($this->status=='Unactive' ? '' : ' '.self::CST_DISABLED));
    return $strMenu . $this->getLiMenuItem('Désactiver', 'unactivate', 'thumbs-o-down', ($this->status=='Active' ? '' : ' '.self::CST_DISABLED));
  }
  private function getSpawnMenu()
  {
    // On peut vouloir activer ou désactiver un Spawn.
    $strMenu  = $this->getLiMenuItem('Activer', 'activate', 'thumbs-o-up', ($this->status=='Unactive' ? '' : ' '.self::CST_DISABLED));
    $strMenu .= $this->getLiMenuItem('Désactiver', 'unactivate', 'thumbs-o-down', ($this->status=='Active' ? '' : ' '.self::CST_DISABLED));
    $strMenu .= $this->getLiMenuSeparator();
    // On peut vouloir le retirer du plateau. On peut vouloir le déplacer.
    $strMenu .= $this->getLiMenuItem('Déplacer', 'move', 'arrows-alt', ' '.self::CST_DISABLED);
    $strMenu .= $this->getLiMenuItem('Supprimer', 'pick', 'trash');
    $strMenu .= $this->getLiMenuSeparator();
    // On peut vouloir ajouter des Zombies.
    $strMenu .= $this->getLiMenuItem('Piocher', 'draw', 'stack-overflow', ($this->status=='Active' ? '' : ' '.self::CST_DISABLED), 'Spawn');
    $strMenu .= $this->getLiMenuItem('Mélanger', 'shuffle', 'recycle', ($this->status=='Active' ? '' : ' '.self::CST_DISABLED), 'Spawn');
    $args = array(($this->status=='Active' ? '' : ' '.self::CST_DISABLED));
    return $strMenu . $this->getRender($this->urlMenuZombiesTemplate, $args);
  }
  private function getLiSubMenu($faClass, $label, $content)
  {
    return '<li class="menu-item submenu"><button type="button" class="menu-btn"> <i class="fa fa-'.$faClass.'"></i> <span class="menu-text">'.$label.'</span> </button><menu class="menu">'.$content.'</menu></li>';
  }
  private function getSurvivorMenu()
  {
    $strButton = '<button type="button" class="menu-btn"> <span class="menu-text">%1$s</span> </button>';
    $argsLi = array(
      self::ATTR_CLASS   => 'menu-item',
      self::ATTR_ID      => $this->id,
      'data-menu-action' => 'add',
    );
    // On peut ajouter des Zombies
    $subMenu  = '';
    for ($i=1; $i<=5; $i++) {
      $argsLi['data-quantite'] = $i;
      $argsLi['data-type'] = 'xp';
      $subMenu .= $this->getBalise(self::TAG_LI, sprintf($strButton, $i), $argsLi);
    }
    $strMenu  = $this->getLiSubMenu('plus-circle', 'Ajouter XP', $subMenu);
    $strMenu .= $this->getLiMenuItem('Retirer 1 XP', 'del', 'minus-circle', ($this->experiencePoints!=0 ? '' : ' '.self::CST_DISABLED), 'xp');
    $strMenu .= $this->getLiMenuSeparator();
    $strMenu .= $this->getLiMenuItem('Ajouter 1 PA', 'add', 'plus-circle', '', 'pa');
    $strMenu .= $this->getLiMenuItem('Retirer 1 PA', 'del', 'minus-circle', ($this->actionPoints!=0 ? '' : ' '.self::CST_DISABLED), 'pa');
    $strMenu .= $this->getLiMenuSeparator();
    $strMenu .= $this->getLiMenuItem('Ajouter 1 PV', 'add', 'plus-circle', '', 'pv');
    $strMenu .= $this->getLiMenuItem('Retirer 1 PV', 'del', 'minus-circle', ($this->hitPoints!=0 ? '' : ' '.self::CST_DISABLED), 'pv');
    $strMenu .= $this->getLiMenuSeparator();
    return $strMenu . $this->getLiMenuItem('Supprimer', 'pick', 'trash');
  }
  private function getBruitMenu()
  { return $this->getZombieMenu(); }
  private function getZombieMenu()
  {
    $strButton = '<button type="button" class="menu-btn"> <span class="menu-text">%1$s</span> </button>';
    $argsLi = array(
      self::ATTR_CLASS   => 'menu-item',
      self::ATTR_ID      => $this->id,
      'data-menu-action' => 'add',
    );
    // On peut ajouter des Zombies
    $subMenu  = '';
    // De 1 à 5
    for ($i=1; $i<=5; $i++) {
      $argsLi['data-quantite'] = $i;
      $subMenu .= $this->getBalise(self::TAG_LI, sprintf($strButton, $i), $argsLi);
    }
    $strMenu  = $this->getLiSubMenu('plus-circle', 'Ajouter', $subMenu);
    // On peut enlever des Zombies
    // Tous d'un coup
    $argsLi['data-menu-action'] = 'pick';
    unset($argsLi['quantite']);
    $subMenu  = $this->getBalise(self::TAG_LI, sprintf($strButton, 'Tous'), $argsLi);
    // Ou de 1 à 5 ou 1 de moins que le nombre disponible.
    $argsLi['data-menu-action'] = 'del';
    if ($this->quantite>1) {
      $subMenu .= $this->getLiMenuSeparator();
      for ($i=1; $i<min(6, $this->quantite); $i++) {
        $argsLi['data-quantite'] = $i;
        $subMenu .= $this->getBalise(self::TAG_LI, sprintf($strButton, $i), $argsLi);
      }
    }
    $strMenu .= $this->getLiSubMenu('minus-circle', 'Retirer', $subMenu);
    // On peut déplacer des Zombies
    $strMenu .= $this->getLiMenuSeparator();
    return $strMenu . $this->getLiMenuItem('Déplacer', 'move', 'arrows-alt', ' '.self::CST_DISABLED);
  }
  public function getTokenMenu()
  {
    switch ($this->type) {
      case 'Noise' :
        $returned = $this->getBruitMenu();
      break;
      case 'Door' :
        $returned = $this->getDoorMenu();
      break;
      case 'Objective' :
        $returned = $this->getObjectiveMenu();
      break;
      case 'Spawn' :
        $returned = $this->getSpawnMenu();
      break;
      case 'Exit' :
        $returned = $this->getExitMenu();
      break;
      case 'Zombie' :
        $returned = $this->getZombieMenu();
      break;
      case 'Survivor' :
        $returned = $this->getSurvivorMenu();
      break;
      default :
        $returned = 'Bad Type in getTokenMenu : ['.$this->type.'].';
      break;
    }

    return $this->getBalise('menu', $returned, array(self::ATTR_CLASS=>'menu', self::ATTR_ID=>'m'.$this->id));
  }

  public function getTokenPortrait()
  {
    // Retourne le portrait que l'on veut afficher en haut à droite de la sidebar.
    $args = array(
      self::ATTR_ID    => 'portrait-'.$this->id,
      self::ATTR_CLASS => 'known',
      self::ATTR_SRC   => '/wp-content/plugins/hj-zombicide/web/rsc/img/portraits/p'.$this->src.'.jpg',
      self::ATTR_TITLE => '',
    );
    return $this->getBalise(self::TAG_IMG, '', $args);
  }
  public function getTokenDetail()
  {
    $fileName = PLUGIN_PATH.$this->urlDirLiveMissions.$_SESSION['zombieKey'].".mission.xml";
    $this->objXmlDocument = simplexml_load_file($fileName);

    // On récupère l'id et le Survivor associé
    $survivorId = substr($this->id, 1);
    $Survivor = $this->SurvivorServices->selectSurvivor($survivorId);
    // On récupère les Skills stockés dans le fichier
    $skills = $this->objXmlDocument->xPath('//survivor[@id="'.$this->id.'"]/skills/skill');
    $strSkills = '';
    while (!empty($skills)) {
      $skill = array_shift($skills);
      // On récupère l'id du Skill pour aller chercher le nom en base.
      $skillId = $skill->attributes()[self::FIELD_SKILLID];
      $Skill = $this->SkillServices->selectSkill($skillId);
      // On récupère son Id
      $nodeId = $skill->attributes()[self::FIELD_ID];
      list(, $tagLevel) = explode('-', $nodeId);
      $skillColor = $this->arrTagColors[$tagLevel/10];
      // On récupère le Status
      $skillStatus = $skill->attributes()['status'];
      // On construit les tags HTML
      $spanBadge = $this->getBalise(self::TAG_SPAN, $Skill->getName(), array(self::ATTR_CLASS=>'badge badge-'.$skillColor.'-skill'));
      // Et on stack la liste de Skills
      $argsLi = array(
        self::ATTR_ID=>$nodeId,
        self::ATTR_CLASS=>($skillStatus=='Unactive' ? 'disabled' : '')
      );
      $strSkills .= $this->getBalise(self::TAG_LI, $spanBadge, $argsLi);
    }
    // On enrichit le Template et on retourne l'ensemble.
    $args = array(
      // Le rang du Survivant dans la partie
      $this->id,
      // L'url du portrait
      $Survivor->getPortraitUrl(),
      // Le nom du Survivant
      $Survivor->getName(),
      // Niveau du Survivant
      strtolower($this->level),
      // Nombre d'XP - 5
      strtolower($this->experiencePoints),
      // Nombre de PA - 6
      strtolower($this->actionPoints),
      // Nombre de PV - 7
      strtolower($this->hitPoints),
      // Les Compétences du Survivant - 8
      $strSkills
    );
    return $this->getRender($this->urlOnlineDetailSurvivor, $args);
  }
}
<file_sep>/core/actions/LiveMissionActions.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * LiveMissionActions
 * @author Hugues
 * @since 1.10.06
 * @version 1.10.06
 */
class LiveMissionActions extends LocalActions
{
  protected $urlDirSpawns       = '/wp-content/plugins/hj-zombicide/web/rsc/img/spawns/';
  protected $urlDirLiveMissions = 'web/rsc/missions/live/';
  protected $strTokenStyle      = 'background:url("/wp-content/plugins/hj-zombicide/web/rsc/img/tokens/%1$s.png");';
  protected $chipToken          = '<PASSWORD>';
  /**
   * Constructeur
   */
  public function __construct($post=array())
  {
    parent::__construct();
    $this->post = $post;
    $this->SurvivorServices = new SurvivorServices();
    $this->EquipmentExpansionServices = new EquipmentExpansionServices();
  }
  /**
   * Point d'entrée des méthodes statiques.
   * @param array $post
   * @return string
   **/
  public static function dealWithStatic($post)
  {
    $returned = '';
    $Act = new LiveMissionActions($post);
    if ($post[self::CST_AJAXACTION]=='updateLiveMission') {
      $returned = $Act->dealWithUpdateLiveMission();
    } else {
      $returned = '';
    }
    return $returned;
  }

  /**
   * @return string
   */
  public function dealWithUpdateLiveMission()
  {
    $returned = '';
    ////////////////////////////////////////////////////////////////////////
    // On récupère et vérifie les données
    if (!isset($this->post['uniqid'])) {
      return $this->formatErrorMessage('Identifiant fichier non défini.');
    }
    $this->fileId = $this->post['uniqid'];
    $fileName = PLUGIN_PATH.$this->urlDirLiveMissions.$this->fileId.".mission.xml";
    if (!is_file($fileName)) {
      return $this->formatErrorMessage('Le fichier de sauvegarde n\'existe pas.');
    }
    ////////////////////////////////////////////////////////////////////////
    // On a le feu vert, on ouvre le fichier XML
    $this->objXmlDocument = simplexml_load_file($fileName);
    ////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////
    // On est venu pour ça. Analyser l'action passée en paramètre, et traiter.
    $returned = $this->parseAndResolveAction();
    ////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////
    // Et si on en profitait pour purger les Tchats un peu vieux... de l'automate uniquement
    $purgeLimit = time()-1*24*60*60;
    $Tchats = $this->objXmlDocument->xPath('//tchat[@timestamp<"'.$purgeLimit.'" and @author="Automat"]');
    // On parcourt la liste des logs à supprimer.
    foreach ($Tchats as $Tchat) {
      // Et on les supprime un à un.
      unset($Tchat[0]);
    }
    ////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////
    // On sauvegarde les modifications du fichier.
    $this->objXmlDocument->asXML($fileName);
    // Et on retourne le visuel modifié. S'il y en a un.
    if (!empty($returned)) {
      return $returned;
    }
  }

  private function parseAndResolveAction()
  {
    $bln_create = true;
    $this->id   = $this->post['id'];
    if ($this->id!='') {
      $this->node = $this->objXmlDocument->xPath('//*[@id="'.$this->id.'"]')[0];
      $bln_create = false;
    }
    $qte  = $this->post['quantite'];
    $type = $this->post['type'];
    $posX = $this->post['left'];
    $posY = $this->post['top'];

    $needABean = false;
    switch ($this->post['act']) {
      case 'activate' :
        $needABean = $this->activateAction();
      break;
      case 'add'      :
        $needABean = $this->addAction($qte, $type);
      break;
      case 'close'    :
        $needABean = $this->closeAction();
      break;
      case 'del'      :
        $needABean = $this->deleteAction($qte, $type);
      break;
      case 'draw'     :
        $returned = $this->drawAction($type);
        $needABean = false;
      break;
      case 'init'     :
        $returned = $this->initAction($type);
        $needABean = false;
      break;
      case 'move'     :
        $needABean = $this->moveAction($posX, $posY);
      break;
      case 'open'     :
        $needABean = $this->openAction();
      break;
      case 'pick'     :
        $this->pickAction();
      break;
      case 'reveal'   :
        $needABean = $this->revealAction();
      break;
      case 'shuffle'  :
        $this->shuffleAction($type);
      break;
      case 'tchat'  :
        $returned = $this->tchatAction();
        $needABean = false;
      break;
      case 'unactivate' :
        $needABean = $this->unactivateAction();
      break;
      default :
      break;
    }
    if ($needABean) {
      $TokenBean = new TokenBean($this->node);
      $returned = $TokenBean->getJsonModifications($this->id, $bln_create);
      return $this->jsonString($returned, 'lstElements', true);
    } else {
      return $returned;
    }
  }

  private function initAction($type)
  {
    switch ($type) {
      case 'Item'  :
      break;
      case 'Spawn' :
        // On doit supprimer tous les Spawns.
        $Spawns = $this->objXmlDocument->xPath('//spawns/spawn');
        // On vire le noeud Spawns
        foreach ($Spawns as $Spawn) {
          // Et on les supprime un à un.
          unset($Spawn[0]);
        }

        // On récupère l'intervalle à utiliser dorénavant.
        $newInterval = $this->post['interval'];
        $this->insertTchatMessage('Pioche Invasion redéfinie : '.$newInterval);
        // Et on recrée le nouveau, avec le bon intervalle.
        $Spawns = $this->objXmlDocument->xpath('//spawns')[0];
        $Spawns->attributes()['interval'] = $newInterval;

        // On ajoute les nouvelles cartes
        $intervals = explode(',', $newInterval);
        $rank = 1;
        foreach ($intervals as $interval) {
          list($interval, $multi) = explode('x', $interval);
          list($start, $end) = explode('-', $interval);
          if ($multi=='') {
            $multi = 1;
          }
          if ($end=='') {
            $end = $start;
          }
          for ($i=1; $i<=$multi; $i++) {
            for ($j=$start; $j<=$end; $j++) {
              $spawn = $this->objXmlDocument->spawns->addChild('spawn');
              $spawn->addAttribute('id', 'spawn-'.$rank);
              $spawn->addAttribute('src', 'x'.str_pad($j, 3, 0, STR_PAD_LEFT));
              $spawn->addAttribute('rank', $rank);
              $spawn->addAttribute('status', 'deck');
              $rank++;
            }
          }
        }
        // On pense bien à mélanger.
        $this->shuffleAction($type);

        // Et on retourne l'intervalle mis à jour
        $returned = array(
          array('currentInterval', '<input type="text" class="form-control" id="currentInterval" readonly value="'.$newInterval.'"/>'),
        );
        return $this->jsonString($returned, 'lstElements', true);
      break;
      default :
      break;
    }

  }

  private function shuffleAction($type)
  {
    // Spawn, Equipment
    // TODO : Equipment à faire.
    switch ($type) {
      case 'Item'  :
        // On récupère toutes les cartes Items, puis on les mélange
        $Items = $this->objXmlDocument->xpath('//items/item');
        shuffle($Items);
        // On les renumérote en les remettant dans la pioche
        $rank = 1;
        foreach ($Items as $Item) {
          $Item->attributes()['rank'] = $rank;
          if ($Item->attributes()['status']=='discard') {
            $Item->attributes()['status'] = 'deck';
          }
          $rank++;
        }
        // On insère un message et on ne retourne rien.
        $this->insertTchatMessage('Pioche Equipment mélangée');
      break;
      case 'Spawn' :
        // On récupère toutes les cartes Invasions, puis on les mélange
        $Spawns = $this->objXmlDocument->xpath('//spawns/spawn');
        shuffle($Spawns);
        // On les renumérote en les remettant dans la pioche
        $rank = 1;
        foreach ($Spawns as $Spawn) {
          $Spawn->attributes()['rank'] = $rank;
          $Spawn->attributes()['status'] = 'deck';
          $rank++;
        }
        // On insère un message et on ne retourne rien.
        $this->insertTchatMessage('Pioche Invasion mélangée');
      break;
      default :
      break;
    }
  }
  private function drawAction($type)
  {
    // Spawn, Equipment
    // TODO : Equipment à faire.
    // On récupère toutes les cartes Invasions encore dans la pioche
    $Spawns = $this->objXmlDocument->xPath('//spawns/spawn[@status="deck"]');
    if (empty($Spawns)) {
      $this->shuffleAction($type);
      $Spawns = $this->objXmlDocument->xPath('//spawns/spawn[@status="deck"]');
    }
    // On trie par ordre croissant et on récupère le premier élément.
    usort($Spawns, 'sort_trees_rank');
    $Spawn = $Spawns[0];
    // On le défausse, on le trace, puis on retourne le visuel
    $Spawn->attributes()['status'] = 'discard';
    $this->insertTchatMessage('1 Carte Invasion piochée');
    //
    $Bean = new LocalBean();
    $returned = array(
      array("modalBody", $Bean->getBalise(self::TAG_IMG, '', array(self::ATTR_SRC=>$this->urlDirSpawns.$Spawn->attributes()['src'].'-thumb.jpg'))),
    );
    return $this->jsonString($returned, 'lstElements', true);
  }
  private function moveAction($posX, $posY)
  {
    // Zombie, Noise, Survivor
    $this->node->attributes()['coordX'] = $posX;
    $this->node->attributes()['coordY'] = $posY;
    $this->insertTchatMessage($this->node->attributes()['type'].' déplacé');
    return true;
  }
  private function pickAction()
  {
    switch (substr($this->id, 0, 1)) {
      case 'c' :
        // Objective, Noise
        $Elements = $this->objXmlDocument->map->chips->chip;
      break;
      case 'z' :
        // Zombie
        $Elements = $this->objXmlDocument->map->zombies->zombie;

        $type = $this->node->attributes()['src'];
        $oldQte = $this->objXmlDocument->xPath('//pool[@type="'.$type.'"]')[0]->attributes()['current'];
        $this->objXmlDocument->xPath('//pool[@type="'.$type.'"]')[0]->attributes()['current'] = $oldQte - $this->node->attributes()['quantite'];
      break;
      case 's' :
        // Survivor
        $Elements = $this->objXmlDocument->map->survivors->survivor;
      break;
      default :
        $this->insertTchatMessage('Suppression échouée ['.$this->id.'].');
        $Elements = array();
      break;
    }
    $cpt = 0;
    foreach ($Elements as $element) {
      if ($element['id'][0]==$this->id) {
        $this->insertTchatMessage($element->attributes()['type'].' supprimé');
        unset($Elements[$cpt]);
      }
      $cpt++;
    }
  }

  private function insertAction($type='')
  {
    // Zombie, Survivor, Bruit.
    $createId = true;
    switch ($type) {
      case 'Survivor' :
        /////////////////////////////////////////////////////////
        // On ajoute un nouveau Survivor au fichier XML
        $survivor = $this->objXmlDocument->xPath('//survivors')[0]->addChild('survivor');
        $survivorId = $this->post['survivorId'];
        $this->id = $survivorId;
        $survivor->addAttribute('id', $survivorId);
        // On s'appuie sur l'id pour récupérer les infos en base
        $Survivor = $this->SurvivorServices->selectSurvivor(substr($survivorId, 1));
        // Et on peut sauvegarder l'id du portrait
        $usedName = ($Survivor->getAltImgName()!='' ? $Survivor->getAltImgName() : $Survivor->getName());
        $src = 'p'.$Survivor->getNiceName($usedName);
        $survivor->addAttribute('src', $src);
        // On récupère le Token Zone de départ pour y mettre le Survivant.
        $Token = $this->objXmlDocument->xPath('//chip[@type="Starting"]')[0];
        $survivor->addAttribute('coordX', $Token->attributes()['coordX']);
        $survivor->addAttribute('coordY', $Token->attributes()['coordY']);
        // On initialise ensuite les données de base.
        // TODO : Ces infos pourraient ne pas être fixes, selon ... plein de facteurs éventuels.
        $survivor->addAttribute('type', 'Survivor');
        $survivor->addAttribute('status', 'Survivor');
        $survivor->addAttribute('hitPoints', 2);
        $survivor->addAttribute('actionPoints', 3);
        $survivor->addAttribute('experiencePoints', 0);
        $survivor->addAttribute('level', 'Blue');
        /////////////////////////////////////////////////////////

        /////////////////////////////////////////////////////////
        // On va maintenant s'occuper d'ajouter les Skills du Survivor
        $skills = $survivor->addChild('skills');
        $SurvivorSkills = $Survivor->getSurvivorSkills(self::CST_SURVIVORTYPEID_S);
        while (!empty($SurvivorSkills)) {
          $SurvivorSkill = array_shift($SurvivorSkills);
          $skill = $skills->addChild('skill');
          $skill->addAttribute('id', $survivorId.'-'.$SurvivorSkill->getTagLevelId());
          $skill->addAttribute('skillId', $SurvivorSkill->getSkillId());
          $skill->addAttribute('status', ($SurvivorSkill->getTagLevelId()<20 ? 'Active' : 'Unactive'));
          $skill->addAttribute('type', 'Skill');
        }
        /////////////////////////////////////////////////////////

        /////////////////////////////////////////////////////////
        // Enfin, on gère l'équipement de départ pour ceux ayant une compétence spécifique
        // TODO : Enfin... En suspens pour le moment.
        $this->node = $survivor;
        /////////////////////////////////////////////////////////
        $msg = '1 Survivant ajouté.';
      break;
      case 'Noise' :
        /////////////////////////////////////////////////////////
        // On récupère l'id du prochain Token à insérer.
        $chips = $this->objXmlDocument->xPath('//chips')[0];
        $maxId = $chips->attributes()['maxid']+1;
        $chips->attributes()['maxid'] = $maxId;
        /////////////////////////////////////////////////////////
        // On ajoute un nouveau Token au fichier XML
        $this->id = 'c'.$maxId;
        $chip = $this->objXmlDocument->xPath('//chips')[0]->addChild('chip');
        $chip->addAttribute('id', $this->id);
        $chip->addAttribute('type', 'Noise');
        $chip->addAttribute('coordX', $this->post['coordx']);
        $chip->addAttribute('coordY', $this->post['coordy']);
        $chip->addAttribute('quantite', 1);
        $this->node = $chip;
        /////////////////////////////////////////////////////////
        $msg = '1 Bruit ajouté.';
      break;
      default      :
        // Dans le cas des Zombies, c'est un peu plus complexe...
        $patternZombie = '/z(Walker|Runner|Fatty|Abomination)(Standard)/';
        if (preg_match($patternZombie, $type, $matches)) {
          /////////////////////////////////////////////////////////
          // On récupère l'id du prochain Zombie à insérer.
          $zombies = $this->objXmlDocument->xPath('//zombies')[0];
          $maxId = $zombies->attributes()['maxid']+1;
          $zombies->attributes()['maxid'] = $maxId;
          /////////////////////////////////////////////////////////
          // On ajoute un nouveau Zombie au fichier XML
          $this->id = 'z'.$maxId;
          $zombie = $this->objXmlDocument->xPath('//zombies')[0]->addChild('zombie');
          $zombie->addAttribute('id', $this->id);
          $zombie->addAttribute('type', 'Zombie');
          $zombie->addAttribute('src', $type);
          $zombie->addAttribute('coordX', $this->post['coordx']);
          $zombie->addAttribute('coordY', $this->post['coordy']);
          $zombie->addAttribute('quantite', 1);
          $this->node = $zombie;
          /////////////////////////////////////////////////////////
          $msg = '1 '.$matches[1].' '.$matches[2].' ajouté.';

          $oldQte = $this->objXmlDocument->xPath('//pool[@type="'.$type.'"]')[0]->attributes()['current'];
          $this->objXmlDocument->xPath('//pool[@type="'.$type.'"]')[0]->attributes()['current'] = $oldQte + 1;
        } else {
          $createId = false;
          $msg = 'Tentative création Zombie foirée : '.$type.'.';
        }
      break;
    }
    $this->insertTchatMessage($msg);
    return $createId;
  }
  private function addAction($qte='', $type='')
  {
    // Si l'id n'est pas défini, c'est probablement une insertion.
    if ($this->id=='') {
      return $this->insertAction($type);
    }
    // Pour les PA, PV & XP, on a passé un $type. Mais le type du node est Survivor.
    $arrTypes = array('pa'=>'actionPoints', 'pv'=>'hitPoints', 'xp'=>'experiencePoints');
    // Zombie, Noise, Survivor (XP, PV & PA).
    $matchId = true;
    switch ($this->node->attributes()['type']) {
      case 'Noise' :
        $oldQte = $this->node->attributes()['quantite'];
        $this->node->attributes()['quantite'] = $oldQte + $qte;
        $msg = $qte.' Bruit(s) ajouté(s).';
      break;
      case 'Zombie' :
        $oldQte = $this->node->attributes()['quantite'];
        $this->node->attributes()['quantite'] = $oldQte + $qte;
        $msg = $qte.' Zombie(s) ajouté(s).';

        $type = $this->node->attributes()['src'];
        $oldQte = $this->objXmlDocument->xPath('//pool[@type="'.$type.'"]')[0]->attributes()['current'];
        $this->objXmlDocument->xPath('//pool[@type="'.$type.'"]')[0]->attributes()['current'] = $oldQte + $qte;
      break;
      case 'Survivor' :
        $oldQte = $this->node->attributes()[$arrTypes[$type]];
        $this->node->attributes()[$arrTypes[$type]] = $oldQte + $qte;
        $msg = $qte.' '.$type.' ajouté(s).';
      break;
      default       :
        $msg = 'Tentative insertion foirée.';
        $matchId = false;
      break;
    }
    $this->insertTchatMessage($msg);
    return $matchId;
  }

  private function deleteAction($qte, $type)
  {
    // Pour les PA, PV & XP, on a passé un $type. Mais le type du node est Survivor.
    $arrTypes = array('pa'=>'actionPoints', 'pv'=>'hitPoints', 'xp'=>'experiencePoints');
    // Zombie, Noise, Survivor (XP, PV & PA).
    $matchId = true;
    switch ($this->node->attributes()['type']) {
      case 'Noise' :
        $oldQte = $this->node->attributes()['quantite'];
        $this->node->attributes()['quantite'] = $oldQte - $qte;
        $msg = $qte.' Bruit(s) retiré(s).';
      break;
      case 'Zombie' :
        $oldQte = $this->node->attributes()['quantite'];
        $this->node->attributes()['quantite'] = $oldQte - $qte;
        $msg = $qte.' Zombie(s) retiré(s).';
      break;
      case 'Survivor' :
        $oldQte = $this->node->attributes()[$arrTypes[$type]];
        $this->node->attributes()[$arrTypes[$type]] = $oldQte - $qte;
        $msg = $qte.' '.$type.' retiré(s).';
      break;
      default       :
        $msg = 'Tentative suppression foirée.';
        $matchId = false;
      break;
    }
    $this->insertTchatMessage($msg);
    return $matchId;
  }

  private function revealAction()
  {
    // Objectif
    $this->node->attributes()['status'] = 'Unactive';
    $this->insertTchatMessage('Objectif révélé');
    return true;
  }

  private function openCloseMutualAction($newStatus, $label)
  {
    // Door
    $this->node->attributes()['status'] = $newStatus;
    $this->insertTchatMessage('Porte '.$label);
    return true;
  }
  private function openAction()
  { return $this->openCloseMutualAction('Opened', 'ouverte'); }
  private function closeAction()
  { return $this->openCloseMutualAction('Closed', 'fermée'); }

  private function activateUnactivateMutualAction($newStatus, $label)
  {
    // Spawn, Exit ou Skill
    $matchId = true;
    $this->node->attributes()['status'] = $newStatus;
    switch ($this->node->attributes()['type']) {
      case 'Exit'  :
        $msg = 'Zone de Sortie '.$label;
      break;
      case 'Skill' :
        $msg = 'Compétence '.$label;
      break;
      case 'Spawn' :
        $msg = 'Zone de Spawn '.$label;
      break;
      default      :
        $msg = ucfirst($label).' envisagée, mais id ['.$this->id.'] ne trouve pas de cible.';
        $matchId = false;
      break;
    }
    $this->insertTchatMessage($msg);
    return $matchId;
  }
  private function unactivateAction()
  { return $this->activateUnactivateMutualAction('Unactive', 'désactivée'); }
  private function activateAction()
  { return $this->activateUnactivateMutualAction('Active', 'activée'); }

  private function tchatAction()
  {
    // L'idée est d'insérer le message envoyé en paramètre. Ou ne rien faire s'il n'y a pas de message à insérer.
    // Puis de retourner les derniers messages insérés.
    // Pour ça, on a besoin de connaître le timestamp du dernier tchat affiché. Il faut donc qu'il soit stocké dans le tchat...
    $timestamp = $this->post['tsTreshold'];
    $Bean = new WpPageMissionOnlineBean();
    $lstTchats = $Bean->getLstTchats($timestamp);
    $returned = array(
      array('tchat-new', $lstTchats),
    );
    return $this->jsonString($returned, 'lstElements', true);
  }








  private function formatErrorMessage($msgError)
  {
    // TODO
    return "[[$msgError]]";

  }
  private function insertTchatMessage($msg='', $author='Automat')
  {
    $Tchat = $this->objXmlDocument->tchats->addChild('tchat', $msg);
    $Tchat->addAttribute('timestamp', time());
    $Tchat->addAttribute('author', $author);
  }

}

function sort_trees_rank($t1, $t2) {
  return ($t1['rank']*1 > $t2['rank']*1);
}
<file_sep>/core/config/requests.php
[Chat]
select="SELECT id, liveId, sendToId, senderId, timestamp, texte "
from="FROM wp_11_zombicide_chat "
where="WHERE (liveId LIKE '%s' OR sendToId LIKE '%s' OR senderId LIKE '%s') AND timestamp > '%s' "
insert="INSERT INTO wp_11_zombicide_chat (liveId, sendToId, senderId, timestamp, texte) VALUES ('%s', '%s', '%s', '%s', '%s');"
update="UPDATE wp_11_zombicide_chat SET liveId='%s', sendToId='%s', senderId='%s', timestamp='%s', texte='%s' "
[Duration]
select="SELECT id, minDuration, maxDuration "
from="FROM wp_11_zombicide_duration "
where="WHERE minDuration LIKE '%s' AND maxDuration LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_duration (minDuration, maxDuration) VALUES ('%s', '%s');"
update="UPDATE wp_11_zombicide_duration SET minDuration='%s', maxDuration='%s' "
[Equipment]
select="SELECT id, name, textAbility "
from="FROM wp_11_zombicide_equipmentcards "
insert="INSERT INTO wp_11_zombicide_equipmentcards (name, textAbility) VALUES ('%s', '%s');"
update="UPDATE wp_11_zombicide_equipmentcards SET name='%s', textAbility='%s' "
[EquipmentExpansion]
select="SELECT id, equipmentCardId, expansionId, quantity "
from="FROM wp_11_zombicide_equipment_expansion "
where="WHERE equipmentCardId LIKE '%s' AND expansionId LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_equipment_expansion (equipmentCardId, expansionId, quantity) VALUES ('%s', '%s', '%s');"
update="UPDATE wp_11_zombicide_equipment_expansion SET equipmentCardId='%s', expansionId='%s', quantity='%s' "
[EquipmentKeyword]
select="SELECT id, equipmentCardId, keywordId "
from="FROM wp_11_zombicide_equipment_keyword "
where="WHERE equipmentCardId LIKE '%s' AND keywordId LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_equipment_keyword (equipmentCardId, keywordId) VALUES ('%s', '%s');"
update="UPDATE wp_11_zombicide_equipment_keyword SET equipmentCardId='%s', keywordId='%s' "
[EquipmentLiveDeck]
select="SELECT id, liveId, equipmentCardId, rank, status, liveSurvivorId "
from="FROM wp_11_zombicide_equipmentlivedeck "
where="WHERE liveId LIKE '%s' AND equipmentCardId LIKE '%s' AND status LIKE '%s' AND liveSurvivorId LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_equipmentlivedeck (liveId, equipmentCardId, rank, status, liveSurvivorId) VALUES ('%s', '%s', '%s', '%s', '%s');"
update="UPDATE wp_11_zombicide_equipmentlivedeck SET liveId='%s', equipmentCardId='%s', rank='%s', status='%s', liveSurvivorId='%s' "
[EquipementWeaponProfile]
select="SELECT id, equipmentCardId, weaponProfileId, noisy "
from="FROM wp_11_zombicide_equipment_weaponprofile "
where="WHERE equipmentCardId LIKE '%s' AND weaponProfileId LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_equipment_weaponprofile (equipmentCardId, weaponProfileId, noisy) VALUES ('%s', '%s', '%s');"
update="UPDATE wp_11_zombicide_equipment_weaponprofile SET equipmentCardId='%s', weaponProfileId='%s', noisy='%s' "
[Expansion]
select="SELECT id, code, name, displayRank, nbSurvivants, nbMissions, nbDalles, official "
from="FROM wp_11_zombicide_expansion "
where="WHERE code LIKE '%s' AND name LIKE '%s' AND nbMissions >= '%s' AND nbSurvivants >= '%s' "
insert="INSERT INTO wp_11_zombicide_expansion (code, name, displayRank, nbSurvivants, nbMissions, nbDalles, official) VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s');"
update="UPDATE wp_11_zombicide_expansion SET code='%s', name='%s', displayRank='%s', nbSurvivants='%s', nbMissions='%s', nbDalles='%s', official='%s' "
[ExpansionZombies]
select="SELECT id, expansionId, zombieTypeId, zombieCategoryId, quantite "
from="FROM wp_11_zombicide_expansion_zombies "
where="WHERE expansionId LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_expansion_zombies (expansionId, zombieTypeId, zombieCategoryId, quantite) VALUES ('%s', '%s', '%s', '%s');"
update="UPDATE wp_11_zombicide_expansion_zombies SET expansionId='%s', zombieTypeId='%s', zombieCategoryId='%s', quantite='%s' "
[Keyword]
select="SELECT id, name, description "
from="FROM wp_11_zombicide_keyword "
where="WHERE name LIKE '%s' AND description LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_keyword (name, description) VALUES ('%s', '%s');"
update="UPDATE wp_11_zombicide_keyword SET name='%s', description='%s' "
[Level]
select="SELECT id, name "
from="FROM wp_11_zombicide_level "
where="WHERE name LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_level (name) VALUES ('%s');"
update="UPDATE wp_11_zombicide_level SET name='%s' "
[Live]
select="SELECT id, deckKey, dateUpdate "
from="FROM wp_11_zombicide_live "
where="WHERE deckKey LIKE '%s' AND dateUpdate <= '%s' "
insert="INSERT INTO wp_11_zombicide_live (deckKey, dateUpdate) VALUES ('%s', '%s');"
update="UPDATE wp_11_zombicide_live SET deckKey='%s', dateUpdate='%s' "
[LiveDeck]
select="SELECT id, deckKey, dateUpdate "
from="FROM wp_11_zombicide_livedeck "
where="WHERE deckKey LIKE '%s' AND dateUpdate <= '%s' "
insert="INSERT INTO wp_11_zombicide_livedeck (deckKey, dateUpdate) VALUES ('%s', '%s');"
update="UPDATE wp_11_zombicide_livedeck SET deckKey='%s', dateUpdate='%s' "
[LiveMission]
select="SELECT id, liveId, missionId, activeLiveSurvivorId, nbSurvivors, turn "
from="FROM wp_11_zombicide_live_mission "
where="WHERE liveId LIKE '%s' AND missionId LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_live_mission (liveId, missionId, activeLiveSurvivorId, nbSurvivors, turn) VALUES ('%s', '%s', '%s', '%s', '%s');"
update="UPDATE wp_11_zombicide_live_mission SET liveId='%s', missionId='%s', activeLiveSurvivorId='%s', nbSurvivors='%s', turn='%s' "
[LiveMissionToken]
select="SELECT id, liveId, missionTokenId, status "
from="FROM wp_11_zombicide_live_missiontoken "
where="WHERE liveId LIKE '%s' AND tokenId LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_live_missiontoken (liveId, missionTokenId, status) VALUES ('%s', '%s', '%s');"
update="UPDATE wp_11_zombicide_live_missiontoken SET liveId='%s', missionTokenId='%s', status='%s' "
[LiveSurvivor]
select="SELECT id, liveId, survivorId, missionZoneId, survivorTypeId, experiencePoints, hitPoints, playedThisTurn, turnRank, searchedThisTurn "
from="FROM wp_11_zombicide_live_survivor "
where="WHERE liveId LIKE '%s' AND survivorId LIKE '%s' AND missionZoneId LIKE '%s' AND playedThisTurn LIKE '%s' AND turnRank LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_live_survivor (liveId, survivorId, missionZoneId, survivorTypeId, experiencePoints, hitPoints, playedThisTurn, turnRank, searchedThisTurn) VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s');"
update="UPDATE wp_11_zombicide_live_survivor SET liveId='%s', survivorId='%s', missionZoneId='%s', survivorTypeId='%s', experiencePoints='%s', hitPoints='%s', playedThisTurn='%s', turnRank='%s', searchedThisTurn='%s' "
[LiveSurvivorAction]
select="SELECT id, liveSurvivorId, actionId "
from="FROM wp_11_zombicide_livesurvivor_action "
where="WHERE liveSurvivorId LIKE '%s' AND actionId LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_livesurvivor_action (liveSurvivorId, actionId) VALUES ('%s', '%s');"
update="UPDATE wp_11_zombicide_livesurvivor_action SET liveSurvivorId='%s', actionId='%s' "
[LiveSurvivorSkill]
select="SELECT id, liveSurvivorId, skillId, tagLevelId, locked "
from="FROM wp_11_zombicide_live_survivorskill "
where="WHERE liveSurvivorId LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_live_survivorskill (liveSurvivorId, skillId, tagLevelId, locked) VALUES ('%s', '%s', '%s', '%s');"
update="UPDATE wp_11_zombicide_live_survivorskill SET liveSurvivorId='%s', skillId='%s', tagLevelId='%s', locked='%s' "
[LiveZombie]
select="SELECT id, liveId, missionZoneId, zombieTypeId, zombieCategoryId, quantity "
from="FROM wp_11_zombicide_live_zombie "
where="WHERE liveId LIKE '%s' AND missionZoneId LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_live_zombie (liveId, missionZoneId, zombieTypeId, zombieCategoryId, quantity) VALUES ('%s', '%s', '%s', '%s', '%s');"
update="UPDATE wp_11_zombicide_live_zombie SET liveId='%s', missionZoneId='%s', zombieTypeId='%s', zombieCategoryId='%s', quantity='%s' "
[Market]
select="SELECT id, name, description, quantity, price, imgProduct, universId, lang "
from="FROM wp_11_zombicide_market "
insert="INSERT INTO wp_11_zombicide_market (name, description, quantity, price, imgProduct, universId, lang) VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s');"
update="UPDATE wp_11_zombicide_market SET name='%s', description='%s', quantity='%s', price='%s', imgProduct='%s', universId='%s', lang='%s' "
[MessageModel]
select="SELECT code, model_fr "
from="FROM wp_11_zombicide_message_model "
where="WHERE code LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_message_model (code, model_fr) VALUES ('%s', '%s');"
update="UPDATE wp_11_zombicide_message_model SET code='%s', model_fr='%s' "
[Mission]
select="SELECT m.id AS id, title, m.code AS code, levelId, playerId, durationId, origineId, width, height, published, liveAble "
from="FROM wp_11_zombicide_mission AS m "
where="WHERE title LIKE '%s' AND code LIKE '%s' AND levelId LIKE '%s' AND durationId LIKE '%s' AND playerId LIKE '%s' AND origineId LIKE '%s' AND published LIKE '%s' AND liveAble LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_mission (title, code, levelId, playerId, durationId, origineId, width, height, published, liveAble) VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s');"
update="UPDATE wp_11_zombicide_mission SET title='%s', code='%s', levelId='%s', playerId='%s', durationId='%s', origineId='%s', width='%s', height='%s', published='%s', liveAble='%s' "
[MissionExpansion]
select="SELECT id, missionId, expansionId "
from="FROM wp_11_zombicide_mission_expansion "
where="WHERE missionId LIKE '%s' AND expansionId LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_mission_expansion (missionId, expansionId) VALUES ('%s', '%s');"
update="UPDATE wp_11_zombicide_mission_expansion SET missionId='%s', expansionId='%s' "
[MissionObjective]
select="SELECT id, missionId, objectiveId, title "
from="FROM wp_11_zombicide_mission_objective "
where="WHERE missionId LIKE '%s' AND objectiveId LIKE '%s' AND title LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_mission_objective (missionId, objectiveId, title) VALUES ('%s', '%s', '%s');"
update="UPDATE wp_11_zombicide_mission_objective SET missionId='%s', objectiveId='%s', title='%s' "
[MissionRule]
select="SELECT id, missionId, ruleId, title "
from="FROM wp_11_zombicide_mission_rule "
where="WHERE missionId LIKE '%s' AND ruleId LIKE '%s' AND title LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_mission_rule (missionId, ruleId, title) VALUES ('%s', '%s', '%s');"
update="UPDATE wp_11_zombicide_mission_rule SET missionId='%s', ruleId='%s', title='%s' "
[MissionTile]
select="SELECT id, missionId, tileId, orientation, coordX, coordY "
from="FROM wp_11_zombicide_mission_tile "
where="WHERE missionId LIKE '%s' AND coordX LIKE '%s' AND coordY LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_mission_tile (missionId, tileId, orientation, coordX, coordY) VALUES ('%s', '%s', '%s', '%s', '%s');"
update="UPDATE wp_11_zombicide_mission_tile SET missionId='%s', tileId='%s', orientation='%s', coordX='%s', coordY='%s' "
[MissionToken]
select="SELECT id, missionId, tokenId, coordX, coordY, color, status, orientation "
from="FROM wp_11_zombicide_mission_token "
where="WHERE missionId LIKE '%s' AND tokenId LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_mission_token (missionId, tokenId, coordX, coordY, color, status, orientation) VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s');"
update="UPDATE wp_11_zombicide_mission_token SET missionId='%s', tokenId='%s', coordX='%s', coordY='%s', color='%s', status='%s', orientation='%s' "
[MissionZone]
select="SELECT id, missionId, zoneNum, coordsX, coordsY, type, reachZone, startingZone "
from="FROM wp_11_zombicide_mission_zone "
where="WHERE missionId LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_mission_zone (missionId, zoneNum, coordsX, coordsY, type, reachZone, startingZone) VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s');"
update="UPDATE wp_11_zombicide_mission_zone SET missionId='%s', zoneNum='%s', coordsX='%s', coordsY='%s', type='%s', reachZone='%s', startingZone='%s' "
[Objective]
select="SELECT id, code, description "
from="FROM wp_11_zombicide_objective "
where="WHERE code LIKE '%s' AND description LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_objective (code, description) VALUES ('%s', '%s');"
update="UPDATE wp_11_zombicide_objective SET code='%s', description='%s' "
[Origine]
select="SELECT id, name "
from="FROM wp_11_zombicide_origine "
where="WHERE name LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_origine (name) VALUES ('%s');"
update="UPDATE wp_11_zombicide_origine SET name='%s' "
[Player]
select="SELECT id, name "
from="FROM wp_11_zombicide_player "
where="WHERE name LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_player (name) VALUES ('%s');"
update="UPDATE wp_11_zombicide_player SET name='%s' "
[Rule]
select="SELECT id, setting, code, description "
from="FROM wp_11_zombicide_rule "
where="WHERE setting LIKE '%s' AND code LIKE '%s' AND description LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_rule (setting, code, description) VALUES ('%s', '%s', '%s');"
update="UPDATE wp_11_zombicide_rule SET setting='%s', code='%s', description='%s' "
[Skill]
select="SELECT DISTINCT s.id AS id, code, name, description, expansionId "
from="FROM wp_11_zombicide_skill s "
where="WHERE code LIKE '%s' AND (name LIKE '%s' OR description LIKE '%s') AND expansionId LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_skill (code, name, description, expansionId) VALUES ('%s', '%s', '%s', '%s');"
update="UPDATE wp_11_zombicide_skill SET code='%s', name='%s', description='%s', expansionId='%s' "
[Spawn]
select="SELECT id, expansionId, spawnNumber, spawnTypeId, zombieCategoryId, blueZombieTypeId, blueQuantity, yellowZombieTypeId, yellowQuantity, orangeZombieTypeId, orangeQuantity, redZombieTypeId, redQuantity "
from="FROM wp_11_zombicide_spawncards "
where="WHERE expansionId LIKE '%s' AND spawnNumber LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_spawncards (expansionId, spawnNumber, spawnTypeId, zombieCategoryId, blueZombieTypeId, blueQuantity, yellowZombieTypeId, yellowQuantity, orangeZombieTypeId, orangeQuantity, redZombieTypeId, redQuantity) VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s');"
update="UPDATE wp_11_zombicide_spawncards SET expansionId='%s', spawnNumber='%s', spawnTypeId='%s', zombieCategoryId='%s', blueZombieTypeId='%s', blueQuantity='%s', yellowZombieTypeId='%s', yellowQuantity='%s', orangeZombieTypeId='%s', orangeQuantity='%s', redZombieTypeId='%s', redQuantity='%s' "
[SpawnLive]
select="SELECT id, liveId, spawnCardId, rank, status "
from="FROM wp_11_zombicide_spawnlivedeck "
where="WHERE liveId LIKE '%s' AND spawnCardId LIKE '%s' AND status LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_spawnlivedeck (liveId, spawnCardId, rank, status) VALUES ('%s', '%s', '%s', '%s');"
update="UPDATE wp_11_zombicide_spawnlivedeck SET liveId='%s', spawnCardId='%s', rank='%s', status='%s' "
[Survivor]
select="SELECT DISTINCT s.id AS id, name, standard, zombivor, ultimate, ultimatez, expansionId, background, altImgName, liveAble "
from="FROM wp_11_zombicide_survivor s "
where="WHERE name LIKE '%s' AND zombivor LIKE '%s' AND ultimate LIKE '%s' AND expansionId LIKE '%s' and background LIKE '%s' and liveAble LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_survivor (name, standard, zombivor, ultimate, ultimatez, expansionId, background, altImgName, liveAble) VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s');"
update="UPDATE wp_11_zombicide_survivor SET name='%s', standard='%s', zombivor='%s', ultimate='%s', ultimatez='%s', expansionId='%s', background='%s', altImgName='%s', liveAble='%s' "
[SurvivorSkill]
select="SELECT id, survivorId, skillId, survivorTypeId, tagLevelId "
from="FROM wp_11_zombicide_survivor_skill "
where="WHERE survivorId LIKE '%s' AND skillId LIKE '%s' AND survivorTypeId LIKE '%s' AND tagLevelId LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_survivor_skill (survivorId, skillId, survivorTypeId, tagLevelId) VALUES ('%s', '%s', '%s', '%s');"
update="UPDATE wp_11_z_survivor_skill SET survivorId='%s', skillId='%s', survivorTypeId='%s', tagLevelId='%s' "
[Tile]
select="SELECT id, expansionId, code, coordPoly, zoneType, zoneAcces, activeTile, oCode, side_top, side_right, side_bottom, side_left "
from="FROM wp_11_zombicide_tile "
where="WHERE code LIKE '%s' AND expansionId LIKE '%s' AND activeTile LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_tile (expansionId, code, coordPoly, zoneType, zoneAcces, activeTile) VALUES ('%s', '%s', '%s', '%s', '%s', '%s');"
update="UPDATE wp_11_zombicide_tile SET expansionId='%s', code='%s', coordPoly='%s', zoneType='%s', zoneAcces='%s', activeTile='%s', oCode='%s', top='%s', right='%s', bottom='%s', left='%s' "
[Token]
select="SELECT id, code, width, height "
from="FROM wp_11_zombicide_token "
where="WHERE code LIKE '%s' "
insert="INSERT INTO wp_11_zombicide_token (code, width, height) VALUES ('%s', '%s', '%s');"
update="UPDATE wp_11_zombicide_token SET code='%s', width='%s', height='%s' "
[WeaponProfile]
select="SELECT id, minRange, maxRange, nbDice, successRate, damageLevel "
from="FROM wp_11_zombicide_weaponprofile "
insert="INSERT INTO wp_11_zombicide_weaponprofile (minRange, maxRange, nbDice, successRate, damageLevel) VALUES ('%s', '%s', '%s', '%s', '%s');"
update="UPDATE wp_11_zombicide_weaponprofile SET minRange='%s', maxRange='%s', nbDice='%s', successRate='%s', damageLevel='%s' "
[ZombieType]
select="SELECT id, name, pointAction, endurance, pointExperience, zoneDeplacement "
from="FROM wp_11_zombicide_zombieType "
insert="INSERT INTO wp_11_zombicide_zombieType (name, pointAction, endurance, pointExperience, zoneDeplacement) VALUES ('%s', '%s', '%s', '%s', '%s');"
update="UPDATE wp_11_zombicide_zombieType SET name='%s', pointAction='%s', endurance='%s', pointExperience='%s', zoneDeplacement='%s' "
<file_sep>/core/bean/WpPostExpansionBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe WpPostExpansionBean
 * @author Hugues
 * @since 1.07.21
 * @version 1.08.01
 */
class WpPostExpansionBean extends WpPostBean
{
  protected $urlTemplate = 'web/pages/public/wppage-expansion.php';

  /**
   * Class Constructor
   */
  public function __construct($WpPost)
  {
    parent::__construct();
    $this->ExpansionServices = new ExpansionServices();
    $this->WpPost = $WpPost;
    $code = $this->WpPost->getPostMeta(self::FIELD_CODE);
    $Expansions = $this->ExpansionServices->getExpansionsWithFilters(array(self::FIELD_CODE=>$code));
    $this->Expansion = (!empty($Expansions) ? array_shift($Expansions) : new Expansion());

  }
  /**
   * On retourne la page dédiée à la compétence.
   * @return string
   */
  public function getContentPage()
  {
    if ($this->Expansion->isOfficial()) {
      $label = 'Officielle';
      $color = 'success';
    } else {
      $label = 'Custom';
      $color = 'danger';
    }
    //////////////////////////////////////////////////////////////////
    // On enrichi le template puis on le restitue.
    $args = array(
      // Nom de l'Extension - 1
      $this->Expansion->getName(),
      // Description de la Compétence - 2
      $this->WpPost->getPostContent(),
      // Lien de navigation - 3
      '',//$this->getNavLinks(),
      // Infos sur les Survivants / Dalles / Missions... - 4
      $this->Expansion->getBean()->getExpansionDetails(),
      // Mais aussi les cartes Equipements et Invasion... - 5
      $this->Expansion->getBean()->getCardsDetails(),
      // Badge officiel ou Custom - 6
      $this->getBalise(self::TAG_SPAN, $label, array(self::ATTR_CLASS=>'badge badge-'.$color)),
    );
    return $this->getRender($this->urlTemplate, $args);
  }

}
<file_sep>/core/bean/AdminPageExpansionsBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * AdminPageExpansionsBean
 * @author Hugues
 * @since 1.04.30
 * @version 1.05.12
 */
class AdminPageExpansionsBean extends AdminPageBean
{
  protected $tplHomeCheckCard  = 'web/pages/admin/fragments/home-check-card.php';
  protected $urlExpansionListing = 'web/pages/admin/expansion-listing.php';
  protected $urlAdminEdit = 'web/pages/admin/expansion-edit.php';
  /**
   * Class Constructor
   */
  public function __construct($urlParams='')
  {
    $this->urlParams = $urlParams;
    parent::__construct(self::CST_EXPANSION);
    $this->title = 'Extensions';
    $this->ExpansionServices  = new ExpansionServices();
    $this->MissionServices    = new MissionServices();
    $this->SurvivorServices   = new SurvivorServices();
  }
  /**
   * @param array $urlParams
   * @return $Bean
   */
  public function getSpecificContentPage()
  {
    if (isset($this->urlParams[self::FIELD_ID])) {
      $this->Expansion = $this->ExpansionServices->selectExpansion($this->urlParams[self::FIELD_ID]);
    }
    if (isset($_POST)&&!empty($_POST)) {
      $this->dealWithPost();
    }
    switch ($this->urlParams[self::CST_POSTACTION]) {
      case 'confirmEdit'  :
      case self::CST_EDIT :
        return $this->getEditContentPage();
      break;
      default :
        return $this->getListContentPage();
      break;
    }
  }
  private function dealWithPost()
  {
    if ($this->urlParams[self::CST_POSTACTION]=='confirmEdit') {
      // On ne met à jour via cette interface que les données suivantes :
      // Le nombre de Survivants.
      $this->Expansion->setNbSurvivants($_POST[self::FIELD_NBSURVIVANTS]);
      // Le nombre de Missions.
      $this->Expansion->setNbMissions($_POST[self::FIELD_NBMISSIONS]);
      // Une fois fait, on peut sauvegarder les modifications.
      $this->ExpansionServices->updateExpansion($this->Expansion);
    }
  }
  public function getEditContentPage()
  {
    $args = array(
      self::FIELD_EXPANSIONID => $this->Expansion->getId(),
    );
    // Nombre de Survivants attendus
    $ExpectedSurvivors = $this->SurvivorServices->getSurvivorsWithFilters($args);
    $nbExpectedSurvivors = count($ExpectedSurvivors);
    $strExpectedSurvivors = '';
    while (!empty($ExpectedSurvivors)) {
      $Survivor = array_shift($ExpectedSurvivors);
      $strExpectedSurvivors .= $Survivor->getBean()->getCartouche();
    }

    // Nb de Missions attendues
    $ExpectedMissions = $this->MissionServices->getMissionsByExpansionId($this->Expansion->getId());
    $nbExpectedMissions = count($ExpectedMissions);

    //////////////////////////////////////////////////////////////////////////
    // On enrichit le template
    $args = array(
    // L'identifiant de l'extension - 1
    $this->Expansion->getId(),
    // Le code de l'extension - 2
    $this->Expansion->getCode(),
    // Le nom de l'extension - 3
    $this->Expansion->getName(),
    // Le rang d'affichage de l'extension - 4
    $this->Expansion->getDisplayRank(),
    // Le nombre de Survivants de l'extension - 5
    $this->Expansion->getNbSurvivants(),
    // Le nombre théorique de Survivants de l'extension - 6
    $nbExpectedSurvivors,
    // Le nombre de Missions de l'extension - 7
    $this->Expansion->getNbMissions(),
    // Le nombre théorique de Missions - 8
    $nbExpectedMissions,
    // L'extension est-elle officielle ? - 9
    ($this->Expansion->isOfficial() ? self::CST_CHECKED : ''),
    // La liste des Survivants en question - 10
    $strExpectedSurvivors,
    );
    // Puis on le restitue.
    return $this->getRender($this->urlAdminEdit, $args);
  }

  public function getListContentPage()
  {
    $strRows = '';
    $nbPerPage = 15;
    $curPage = $this->initVar(self::WP_CURPAGE, 1);
    $orderby = $this->initVar(self::WP_ORDERBY, self::FIELD_NAME);
    $order = $this->initVar(self::WP_ORDER, self::ORDER_ASC);
    $Expansions = $this->ExpansionServices->getExpansionsWithFilters(array(), $orderby, $order);
    $nbElements = count($Expansions);
    $nbPages = ceil($nbElements/$nbPerPage);
    $curPage = max(1, min($curPage, $nbPages));
    $DisplayedExpansions = array_slice($Expansions, ($curPage-1)*$nbPerPage, $nbPerPage);
    if (!empty($DisplayedExpansions)) {
      foreach ($DisplayedExpansions as $Expansion) {
        $ExpansionBean = new ExpansionBean($Expansion);
        $strRows .= $ExpansionBean->getRowForAdminPage();
      }
    }
    $queryArg = array(
      self::CST_ONGLET => self::CST_EXPANSION,
      self::WP_ORDERBY => $orderby,
      self::WP_ORDER   => $order
    );
    // Pagination
    $strPagination = $this->getPagination($queryArg, $post_status, $curPage, $nbPages, $nbElements);

    $args = array(
      // Liste des extensions affichées - 1
      $strRows,
      // Filtres - 2
      '',
      // Url pour créer une nouvelle Extension - 3
      '/wp-admin/post-new.php',
      // Subs - 4
      '',
      // Pagination - 5
      $strPagination,
    );
    return $this->getRender($this->urlExpansionListing, $args);
  }

  /**
   * @return string
   */
  public function getCheckCard()
  {
    /////////////////////////////////////////////////
    // Gestion des Extensions.
    // On récupère la liste des Extensions qui ont un Article. Puis les données dans la base. On compare et on effectue un diagnostic.
    $Act = new ExpansionActions();
    $strBilan  = $Act->dealWithExpansionVerif();

    $args = array(
      // Le titre de la carte - 1
      $this->title,
      // L'id du container de retour pour afficher les vérifications - 2
      self::CST_EXPANSION,
      // Le contenu du container de vérification - 3
      $strBilan,
   );
    return $this->getRender($this->tplHomeCheckCard, $args);
  }
}
<file_sep>/core/domain/MissionOnline.class.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe MissionOnline
 * @author Hugues.
 * @since 1.11.01
 * @version 1.11.01
 */
class MissionOnline extends WpPostRelais
{
  protected $urlDirLiveMissions = 'web/rsc/missions/live/';

  public function __construct($Mission=null)
  {
    parent::__construct();
    $this->MissionServices = new MissionServices();
    $this->WpPostServices = new WpPostServices();
    $this->EquipmentExpansionServices = new EquipmentExpansionServices();

    if ($Mission==null) {
      $this->openMissionFile();
      $codeMission = $this->objXmlDocument->attributes()['code'];
      $Missions = $this->MissionServices->getMissionsWithFilters(array(self::FIELD_CODE=>$codeMission));
      $Mission = array_shift($Missions);
    }
    $this->Mission = $Mission;
    $this->WpPost  = $Mission->getWpPost();
  }

  private function openMissionFile()
  {
    $this->fileId = $_SESSION['zombieKey'];
    $this->fileName = PLUGIN_PATH.$this->urlDirLiveMissions.$this->fileId.".mission.xml";
    $this->objXmlDocument = simplexml_load_file($this->fileName);
  }
  private function saveMissionFile()
  {
    $this->objXmlDocument->asXML($this->fileName);
  }



  public function setUp()
  {
    // On ouvre le fichier pour pouvoir le modifier.
    $this->openMissionFile();
    // On récupère les Custom Fields liées à la Mission
    $WpPosts = $this->WpPostServices->getWpPostsByCustomField(self::FIELD_MISSIONID, $this->WpPost->getID());
    while (!empty($WpPosts)) {
      // On les parcourt.
      $WpPost = array_shift($WpPosts);

      $hasCategory = false;
      // On en récupère les Catégories
      $WpCategories = $WpPost->getCategories();
      while (!empty($WpCategories)) {
        $WpCategory = array_shift($WpCategories);
        if ($WpCategory->getCatId()==self::WP_CAT_RULE_ID) {
          // On cherche les Catégories de type MissionRule
          $hasCategory = true;
        }
      }

      if ($hasCategory) {
        // Si on en une, on récupère la donnée Méta Code associée
        $metaValue = $WpPost->getPostMeta(self::FIELD_CODE);
        if ($metaValue!='') {
          // Et on vérifie que rien n'est à faire dans le Set Up.
          $this->dealWithSetUpRule($metaValue);
        }
      }
    }
    $this->setUpSpawns();
    $this->setUpItems();
    // On sauvegarde les éventuels changements.
    $this->saveMissionFile();
    // Et on mélange.
    // Besoin de sauvegarder avant car l'action de remélange réouvre le fichier...
    $args = array(
      self::CST_AJAXACTION => 'updateLiveMission',
      'uniqid'             => $_SESSION['zombieKey'],
      'act'                => 'shuffle',
      'type'               => 'Spawn',
    );
    LiveMissionActions::dealWithStatic($args);
    // Besoin de sauvegarder avant car l'action de remélange réouvre le fichier...
    $args['type']          = 'Item';
    LiveMissionActions::dealWithStatic($args);
  }

  public function deleteSpawns()
  {
    // On doit supprimer tous les Spawns.
    $Spawns = $this->objXmlDocument->xpath('//spawns');
    // On récupère l'intervalle actuel.
    $interval = $this->objXmlDocument->xpath('//spawns')[0]->attributes()['interval'];
    // On vire le noeud Spawns
    unset($Spawns);
    // Et on recrée le nouveau, avec le bon intervalle.
    $Spawns = $this->objXmlDocument->addChild('spawns');
    $Spawns->attributes()['interval'] = $interval;
  }
  public function setUpSpawns($interval='')
  {
    // Si l'intervalle n'est pas défini, on va le chercher dans le fichier.
    if ($interval=='') {
      $interval = $this->objXmlDocument->xpath('//spawns')[0]->attributes()['interval'];
    } else {
      // S'il est défini, on va le sauvegarder dans le fichier.
      $this->objXmlDocument->xpath('//spawns')[0]->attributes()['interval'] = $interval;
    }

    $intervals = explode(',', $interval);
    $rank = 1;
    foreach ($intervals as $interval) {
      list($interval, $multi) = explode('x', $interval);
      list($start, $end) = explode('-', $interval);
      if ($multi=='') {
        $multi = 1;
      }
      if ($end=='') {
        $end = $start;
      }
      for ($i=1; $i<=$multi; $i++) {
        for ($j=$start; $j<=$end; $j++) {
          $spawn = $this->objXmlDocument->spawns->addChild('spawn');
          $spawn->addAttribute('id', 'spawn-'.$rank);
          $spawn->addAttribute('src', 'x'.str_pad($j, 3, 0, STR_PAD_LEFT));
          $spawn->addAttribute('rank', $rank);
          $spawn->addAttribute('status', 'deck');
          $rank++;
        }
      }
    }
  }
  public function setUpItems()
  {
    $season = $this->objXmlDocument->xpath('//items')[0]->attributes()['season'];
    $ItemExpansions = $this->EquipmentExpansionServices->getEquipmentExpansionsWithFilters(array(self::FIELD_EXPANSIONID=>$season));
    $rank = 1;
    while (!empty($ItemExpansions)) {
      $ItemExpansion = array_shift($ItemExpansions);
      $qte = $ItemExpansion->getQuantity();
      $Item = $ItemExpansion->getEquipment();

      for ($i=0; $i<$qte; $i++) {
        $item = $this->objXmlDocument->items->addChild('item');
        $item->addAttribute('id', 'item-'.$rank);
        $item->addAttribute('src', str_pad($Item->getId(), 3, 0, STR_PAD_LEFT).str_pad($season, 2, 0, STR_PAD_LEFT));
        $item->addAttribute('rank', $rank);
        if ($Item->isStarter()) {
          $item->addAttribute('status', 'start');
        } elseif ($Item->isPimp()) {
          $item->addAttribute('status', 'pimp');
        } elseif ($Item->hasKeyword('Composite')) {
          $item->addAttribute('status', 'combo');
        } else {
          $item->addAttribute('status', 'deck');
        }
        $rank++;
      }
    }
  }

  private function dealWithSetUpRule($metaValue)
  {
    // Les Règles qui débutent par "AMONG_RED" permettent de mélanger des objectifs de couleur parmi les rouges.
    if (substr($metaValue, 0, 9)=='AMONG_RED') {
      $arrColors = explode('_', $metaValue);
      for ($i=2; $i<count($arrColors); $i++) {
        $lstElements = $this->objXmlDocument->xpath('//map/chips/chip[@type="Objective"][@color="red"]');
        $nbElements = count($lstElements);
        $rnd = random_int(0, $nbElements-1);
        $this->objXmlDocument->xpath('//map/chips/chip[@type="Objective"][@color="red"]')[$rnd]->attributes()['color'] = strtolower($arrColors[$i]);
      }
    }
  }
}
<file_sep>/core/actions/LocalActions.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * LocalActions
 * @author Hugues
 * @since 1.04.00
 * @version 1.04.00
 */
class LocalActions extends GlobalActions implements ConstantsInterface
{
  /**
   * Class Constructor
   */
  public function __construct()
  {
  }

  /**
   * Retourne une chaine json
   * @param string $msg
   * @param string $id
   * @param boolean $directReturn
   * @return string
   */
  protected function jsonString($msg, $id, $directReturn)
  {
    $content = '"'.$id.'":'.json_encode($msg);
    return ($directReturn ? '{'.$content.'}' : $content);
  }
  /**
   * @return bool
   */
  public static function isAdmin()
  { return current_user_can('manage_options'); }
}
<file_sep>/core/domain/Level.class.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe Level
 * @author Hugues.
 * @version 1.0.00
 * @since 1.0.00
 */
class Level extends LocalDomain
{
  /**
   * Id technique de la donnée
   * @var int $id
   */
  protected $id;
  /**
   * nom du niveau de Difficulté
   * @var string $name
   */
  protected $name;
  /**
   * @return int
   */
  public function getId()
  { return $this->id; }
  /**
   * @return string
   */
  public function getName()
  { return $this->name; }
  /**
   * @param int $id
   */
  public function setId($id)
  { $this->id = $id; }
  /**
   * @param string $name
   */
  public function setName($name)
  { $this->name = $name; }
  /**
   * @return array
   */
  public function getClassVars()
  { return get_class_vars('Level'); }
  /**
   * @param array $row
   * @param string $a
   * @param string $b
   * @return Level
   */
  public static function convertElement($row, $a='', $b='')
  { return parent::convertElement(new Level(), self::getClassVars(), $row); }
  /**
   * @return LevelBean
   */
  public function getBean()
  { return new LevelBean($this); }
}
<file_sep>/core/domain/EquipmentWeaponProfile.class.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe EquipmentWeaponProfile
 * @author Hugues.
 * @since 1.0.00
 * @version 1.04.28
 */
class EquipmentWeaponProfile extends LocalDomain
{
  /**
   * Id technique de la jointure
   * @var int $id
   */
  protected $id;
  /**
   * Id technique de la carte Equipement
   * @var int $equipmentCardId
   */
  protected $equipmentCardId;
  /**
   * Id technique du profil de l'arme
   * @var int $weaponProfileId
   */
  protected $weaponProfileId;
  /**
   * L'arme est-elle bruyante
   * @var int $noisy
   */
  protected $noisy;
  /**
   * @return int
   */
  public function getId()
  { return $this->id; }
  /**
   * @ return int
   */
  public function getEquipmentCardId()
  { return $this->equipmentCardId; }
  /**
   * @ return int
   */
  public function getWeaponProfileId()
  { return $this->weaponProfileId; }
  /**
   * @return int
   */
  public function isNoisy()
  { return $this->noisy; }
  /**
   * @param int $id
   */
  public function setId($id)
  { $this->id=$id; }
  /**
   * @param int $equipmentCardId
   */
  public function setEquipmentCardId($equipmentCardId)
  { $this->equipmentCardId = $equipmentCardId; }
  /**
   * @param int $weaponProfileId
   */
  public function setWeaponProfileId($weaponProfileId)
  { $this->weaponProfileId = $weaponProfileId; }
  /**
   * @param int $noisy
   */
  public function setNoisy($noisy)
  { $this->noisy = $noisy; }
  /**
   * @return array
   */
  public function getClassVars()
  { return get_class_vars('EquipmentWeaponProfile'); }
  /**
   * @param array $row
   * @param string $a
   * @param string $b
   * @return EquipmentWeaponProfile
   */
  public static function convertElement($row, $a='', $b='')
  { return parent::convertElement(new EquipmentWeaponProfile(), self::getClassVars(), $row); }
  /**
   * @return WeaponProfile
   */
  public function getWeaponProfile()
  {
    if ($this->WeaponProfile==null) {
      $this->WeaponProfile = $this->WeaponProfileServices->select(__FILE__, __LINE__, $this->weaponProfileId);
    }
    return $this->WeaponProfile;
  }
}
<file_sep>/core/services/ObjectiveServices.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe ObjectiveServices
 * @author Hugues.
 * @since 1.04.08
 * @version 1.04.27
 */
class ObjectiveServices extends LocalServices
{
  /**
   * L'objet Dao pour faire les requêtes
   * @var RuleDaoImpl $Dao
   */
  protected $Dao;
  /**
   * Class Constructor
   */
  public function __construct()
  {
    parent::__construct();
    $this->Dao = new ObjectiveDaoImpl();
  }

  /**
   * @param array $arrFilters
   */
  private function buildFilters($arrFilters)
  {
    $this->arrParams[self::SQL_WHERE] = array();
    array_push($this->arrParams[self::SQL_WHERE], $this->addFilter($arrFilters, self::FIELD_CODE));
    array_push($this->arrParams[self::SQL_WHERE], $this->addFilter($arrFilters, self::FIELD_DESCRIPTION));
  }
  /**
   * @param array $arrFilters
   * @param string $orderby
   * @param string $order
   * @return array
   */
  public function getObjectivesWithFilters($arrFilters=array(), $orderby=self::FIELD_CODE, $order=self::ORDER_ASC)
  {
    $this->arrParams = $this->buildOrderAndLimit($orderby, $order);
    $this->buildFilters($arrFilters);
    return $this->Dao->selectEntriesWithFilters(__FILE__, __LINE__, $this->arrParams);
  }
  /**
   * @param string $value
   * @param string $prefix
   * @param string $classe
   * @param bool $multiple
   * @param string $defaultLabel
   * @return string
   */
  public function getObjectiveSelect($value='', $prefix='id', $classe='form-control', $multiple=false, $defaultLabel='---')
  {
    $Objectives = $this->getObjectivesWithFilters();
    $arrSetLabels = array();
    foreach ($Objectives as $Objective) {
      $arrSetLabels[$Objective->getId()] = $Objective->getCode();
    }
    $this->labelDefault = $defaultLabel;
    $this->classe = $classe;
    $this->multiple = $multiple;
    return $this->getSetSelect($file, $line, $arrSetLabels, $prefix.'objectiveId', $value);
  }
}
<file_sep>/core/bean/UtilitiesBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe UtilitiesBean
 * @author Hugues
 * @since 1.02.00
 * @version 1.05.09
 */
class UtilitiesBean implements ConstantsInterface
{
  /**
   * @param string $balise
   * @param string $label
   * @param array $attributes
   * @return string
   */
  public function getBalise($balise, $label='', $attributes=array())
  { return '<'.$balise.$this->getExtraAttributesString($attributes).'>'.$label.'</'.$balise.'>'; }
  /**
   * @param array $attributes
   * @return array
   */
  private function getExtraAttributesString($attributes)
  {
    $extraAttributes = '';
    if (!empty($attributes)) {
      foreach ($attributes as $key => $value) {
        $extraAttributes .= ' '.$key.'="'.$value.'"';
      }
    }
    return $extraAttributes;
  }
  /**
   * @param array $attributes
   * @return string
   */
  protected function getIcon($attributes=array())
  { return $this->getBalise(self::TAG_I, '', $attributes); }
  /**
   * @return string
   */
  protected function getIconFarCheckSquare()
  { return $this->getIcon(array(self::ATTR_CLASS=>'far fa-check-square')); }
  /**
   * @return string
   */
  protected function getIconFarSquare()
  { return $this->getIcon(array(self::ATTR_CLASS=>'far fa-square')); }
  /**
   * @return string
   */
  protected function getIconFarSquarePointer()
  { return $this->getIcon(array(self::ATTR_CLASS=>'far fa-square pointer')); }
  /**
   * @return string
   */
  protected function getIconFarWindowClose()
  { return $this->getIcon(array(self::ATTR_CLASS=>'far fa-window-close')); }
  /**
   * @param string $urlTemplate
   * @param array $args
   * @return string
   */
  public function getRender($urlTemplate, $args)
  {
    $pattern = "/web\/pages\/(admin|public)\/[fragments\/]?/";
    return (preg_match($pattern, $urlTemplate) ? vsprintf(file_get_contents(PLUGIN_PATH.$urlTemplate), $args) : '');
  }
}
<file_sep>/core/daoimpl/MissionDaoImpl.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe MissionDaoImpl
 * @author Hugues.
 * @since 1.0.00
 * @version 1.04.30
 */
class MissionDaoImpl extends LocalDaoImpl
{
  /**
   * Class constructor
   */
  public function __construct()
  { parent::__construct('Mission'); }
  /**
   * @param array $rows
   * @return array
   */
  protected function convertToArray($rows)
  {
    $Items = array();
    if (!empty($rows)) {
      foreach ($rows as $row) {
        $Items[] = Mission::convertElement($row);
      }
    }
    return $Items;
  }
  /**
   * @param string $file
   * @param int $line
   * @param array $arrParams
   * @return array|Mission
   */
  public function select($file, $line, $arrParams)
  { return parent::localSelect($file, $line, $arrParams, new Mission()); }
  /**
   * @param string $file
   * @param int $line
   * @param array $arrParams
   * @param array $filters
   * @return array
   */
  public function selectEntriesWithFiltersIn($file, $line, $arrParams, $filters)
  {
    // On s'appuie sur la requête de base.
    $requete  = $this->selectRequest.$this->fromRequest;
    // On doit faire une jointure externe pour lier la table mission_expansion si on cherche sur ce critère
    if (isset($filters[self::FIELD_EXPANSIONID])) {
      $requete .= 'INNER JOIN wp_11_zombicide_mission_expansion me ON m.id=me.missionId ';
    }
    // On passe ensuite aux critères de sélection.
    $requete .= 'WHERE 1=1 ';
    // Contrainte sur la difficulté
    if (isset($filters[self::FIELD_LEVELID])) {
      $requete .= 'AND levelId IN ('.$filters[self::FIELD_LEVELID].') ';
    }
    // Contrainte sur la durée
    if (isset($filters[self::FIELD_DURATIONID])) {
      $requete .= 'AND durationId IN ('.$filters[self::FIELD_DURATIONID].') ';
    }
    // Contrainte sur le nombre de joueurs
    if (isset($filters[self::FIELD_PLAYERID])) {
      $requete .= 'AND playerId IN ('.implode(',', $filters[self::FIELD_PLAYERID]).') ';
    }
    // Contrainte sur l'origine
    if (isset($filters[self::FIELD_ORIGINEID])) {
      $requete .= 'AND origineId IN ('.implode(',', $filters[self::FIELD_ORIGINEID]).') ';
    }
    // Contrainte sur l'extension
    if (isset($filters[self::FIELD_EXPANSIONID])) {
      if (strpos($filters[self::FIELD_EXPANSIONID], ',')===false) {
        $requete .= 'AND expansionId = '.$filters[self::FIELD_EXPANSIONID].' ';
      } else {
        $requete .= 'AND expansionId IN ('.$filters[self::FIELD_EXPANSIONID].') ';
      }
    }
    // On peut aussi trier
    $requete .= $this->orderBy;
    // Et retourner le tableau de résultats.
    return $this->convertToArray($this->selectEntriesAndLogQuery($file, $line, $requete, $arrParams));
  }

  public function selectMissionsByExpansionId($expansionId)
  {
    $request  = "SELECT DISTINCT zm.id FROM `wp_11_zombicide_tile` zt ";
    $request .= "INNER JOIN wp_11_zombicide_mission_tile zmt ON zmt.tileId = zt.id ";
    $request .= "INNER JOIN wp_11_zombicide_mission zm ON zmt.missionId = zm.id ";
    $request .= "WHERE zt.expansionId='$expansionId' ";
    $request .= "ORDER BY zm.title ASC;";
    return MySQL::wpdbSelect($request);
  }

}
<file_sep>/core/services/DurationServices.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe DurationServices
 * @author Hugues.
 * @since 1.04.16
 * @version 1.04.27
 */
class DurationServices extends LocalServices
{
  /**
   * L'objet Dao pour faire les requêtes
   * @var DurationDaoImpl $Dao
   */
  protected $Dao;
  /**
   * Class Constructor
   */
  public function __construct()
  {
    parent::__construct();
    $this->Dao = new DurationDaoImpl();
  }

  /**
   * @param array $arrFilters
   */
  private function buildFilters($arrFilters)
  {
    $this->arrParams[self::SQL_WHERE] = array();
    array_push($this->arrParams[self::SQL_WHERE], $this->addFilter($arrFilters, self::FIELD_MINDURATION));
    array_push($this->arrParams[self::SQL_WHERE], $this->addFilter($arrFilters, self::FIELD_MAXDURATION));
  }
  /**
   * @param array $arrFilters
   * @param string $orderby
   * @param string $order
   * @return array
   */
  public function getDurationsWithFilters($arrFilters=array(), $orderby=self::FIELD_MINDURATION, $order=self::ORDER_ASC)
  {
    $this->arrParams = $this->buildOrderAndLimit($orderby, $order);
    $this->buildFilters($arrFilters);
    return $this->Dao->selectEntriesWithFilters(__FILE__, __LINE__, $this->arrParams);
  }
  /**
   * @param int $id
   * @return Duration
   */
  public function selectDuration($id)
  { return $this->select(__FILE__, __LINE__, $id); }

}
<file_sep>/core/services/EquipmentWeaponProfileServices.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe EquipmentWeaponProfileServices
 * @author Hugues.
 * @since 1.0.00
 * @version 1.04.27
 */
class EquipmentWeaponProfileServices extends LocalServices
{
  /**
   * L'objet Dao pour faire les requêtes
   * @var EquipmentWeaponProfileDaoImpl $Dao
   */
  protected $Dao;
  /**
   * Class Constructor
   */
  public function __construct()
  {
    parent::__construct();
    $this->Dao = new EquipmentWeaponProfileDaoImpl();
  }

  /**
   * @param array $arrFilters
   */
  private function buildFilters($arrFilters)
  {
    $this->arrParams[self::SQL_WHERE] = array();
    array_push($this->arrParams[self::SQL_WHERE], $this->addFilter($arrFilters, self::FIELD_EQUIPMENTCARDID));
    array_push($this->arrParams[self::SQL_WHERE], $this->addFilter($arrFilters, self::FIELD_WEAPONPROFILEID));
  }
  /**
   * @param array $arrFilters
   * @param string $orderby
   * @param string $order
   * @return array
   */
  public function getEquipmentWeaponProfilesWithFilters($arrFilters=array(), $orderby='id', $order='asc')
  {
    $this->arrParams = $this->buildOrderAndLimit($orderby, $order);
    $this->buildFilters($arrFilters);
    return $this->Dao->selectEntriesWithFilters(__FILE__, __LINE__, $this->arrParams);
  }
}
<file_sep>/core/services/SpawnTypeServices.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe SpawnTypeServices
 * @author Hugues.
 * @since 1.04.27
 * @version 1.04.27
 */
class SpawnTypeServices extends LocalServices
{
  /**
   * L'objet Dao pour faire les requêtes
   * @var SpawnTypeDaoImpl $Dao
   */
  protected $Dao;
  /**
   * Class Constructor
   */
  public function __construct()
  {
    parent::__construct();
    $this->Dao = new SpawnTypeDaoImpl();
  }

}
<file_sep>/core/domain/Mission.class.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe Mission
 * @author Hugues.
 * @since 1.04.00
 * @version 1.05.14
 */
class Mission extends WpPostRelais
{
  /**
   * Id technique de la donnée
   * @var int $id
   */
  protected $id;
  /**
   * Titre de la Mission
   * @var string $title
   */
  protected $title;
  /**
   * Code de la donnée
   * @var string $code
   */
  protected $code;
  /**
   * Id de la difficulté de la Mission
   * @var int $levelId
   */
  protected $levelId;
  /**
   * Id du nb de joueurs de la Mission
   * @var int $playerId
   */
  protected $playerId;
  /**
   * Id de la durée de la Mission
   * @var int $durationId
   */
  protected $durationId;
  /**
   * Id de l'origine de la Mission
   * @var int $origineId
   */
  protected $origineId;
  /**
   * Nombre de dalles en largeur
   * @var int $width
   */
  protected $width;
  /**
   * Nombre de dalles en hauteur
   * @var int $height
   */
  protected $height;
  /**
   * La mission a-t-elle était publiée ?
   * @var int $published
   */
  protected $published;
  /**
   * La mission peut-elle être jouée en ligne ?
   * @var int $liveAble
   */
  protected $liveAble;

  /**
   * @return int
   */
  public function getId()
  {return $this->id; }
  /**
   * @return string
   */
  public function getTitle()
  { return $this->title; }
  /**
   * @return string
   */
  public function getCode()
  { return $this->code; }
  /**
   * @return int
   */
  public function getLevelId()
  { return $this->levelId; }
  /**
   * @return int
   */
  public function getPlayerId()
  { return $this->playerId; }
  /**
   * @return int
   */
  public function getDurationId()
  { return $this->durationId; }
  /**
   * @return int
   */
  public function getOrigineId()
  { return $this->origineId; }
  /**
   * @return int
   */
  public function getWidth()
  { return $this->width; }
  /**
   * @return int
   */
  public function getHeight()
  { return $this->height; }
  /**
   * @return boolean
   */
  public function isPublished()
  { return ($this->published==1); }
  /**
   * @return boolean
   */
  public function isLiveAble()
  { return ($this->liveAble==1); }
  /**
   * @param int $id
   */
  public function setId($id)
  { $this->id=$id; }
  /**
   * @param string $title
   */
  public function setTitle($title)
  { $this->title=$title; }
  /**
   * @param string $code
   */
  public function setCode($code)
  { $this->code=$code; }
  /**
   * @param int $levelId
   */
  public function setLevelId($levelId)
  { $this->levelId=$levelId; }
  /**
   * @param int $playerId
   */
  public function setPlayerId($playerId)
  { $this->playerId=$playerId; }
  /**
   * @param int $durationId
   */
  public function setDurationId($durationId)
  { $this->durationId=$durationId; }
  /**
   * @param int $origineId
   */
  public function setOrigineId($origineId)
  { $this->origineId=$origineId; }
  /**
   * @param int $width
   */
  public function setWidth($width)
  { $this->width=$width; }
  /**
   * @param int $height
   */
  public function setHeight($height)
  { $this->height=$height; }
  /**
   * @param boolean $published
   */
  public function setPublished($published)
  { $this->published=$published; }
  /**
   * @param boolean $liveAble
   */
  public function setLiveAble($liveAble)
  { $this->liveAble=$liveAble; }

  ///////////////////////////////////////////////////////////////
  /**
   * @return array
   */
  public function getClassVars()
  { return get_class_vars('Mission'); }
  /**
   * @param array $row
   * @param string $a
   * @param string $b
   * @return Mission
   */
  public static function convertElement($row, $a='', $b='')
  { return parent::convertElement(new Mission(), self::getClassVars(), $row); }
  /**
   * @return MissionBean
   */
  public function getBean()
  { return new MissionBean($this); }
  ///////////////////////////////////////////////////////////////

  /**
   * @return string
   */
  public function getWpPost()
  { return $this->getMainWpPost(self::FIELD_CODE, $this->code, self::WP_CAT_MISSION_ID); }







  /**
   * @param string $orderBy
   * @param string $order
   * @return array MissionTile
   */
  public function getMissionTiles($orderBy='id', $order='asc')
  {
    if ($this->MissionTiles == null) {
      $arrFilters = array(self::FIELD_MISSIONID=>$this->id);
      $this->MissionTiles = $this->MissionTileServices->getMissionTilesWithFilters($arrFilters, $orderBy, $order);
    }
    return $this->MissionTiles;
  }
  /**
   * @return array MissionZone
   */
  public function getMissionZones()
  {
    if ($this->MissionZones == null) {
      $arrFilters = array(self::CST_MISSIONID=>$this->id);
      $this->MissionZones = $this->MissionZoneServices->getMissionZonesWithFilters(__FILE__, __LINE__, $arrFilters);
    }
    return $this->MissionZones;
  }
  /**
   * @return array MissionRule
   */
  public function getMissionRules($orderBy='id')
  {
    if ($this->MissionRules == null) {
      $arrFilters = array(self::FIELD_MISSIONID=>$this->id);
      $this->MissionRules = $this->MissionRuleServices->getMissionRulesWithFilters($arrFilters, $orderBy);
    }
    return $this->MissionRules;
  }
  /**
   * @return array MissionObjective
   */
  public function getMissionObjectives($oBy='id')
  {
    if ($this->MissionObjectives == null) {
      $arrF = array(self::FIELD_MISSIONID=>$this->id);
      $this->MissionObjectives = $this->MissionObjectiveServices->getMissionObjectivesWithFilters($arrF, $oBy);
    }
    return $this->MissionObjectives;
  }
  /**
   * @return array MissionExpansion
   */
  public function getMissionExpansions()
  {
    if ($this->MissionExpansions == null) {
      $arrFilters = array(self::FIELD_MISSIONID=>$this->id);
      $this->MissionExpansions = $this->MissionExpansionServices->getMissionExpansionsWithFilters($arrFilters);
    }
    return $this->MissionExpansions;
  }
  /**
   * @return Duration
   */
  public function getDuration()
  {
    if ($this->Duration == null) {
      $this->Duration = $this->DurationServices->selectDuration($this->durationId);
    }
    return $this->Duration;
  }
  /**
   * @return Level
   */
  public function getLevel()
  {
    if ($this->Level == null) {
      $this->Level = $this->LevelServices->selectLevel($this->levelId);
    }
    return $this->Level;
  }
  /**
   * @return Origine
   */
  public function getOrigine()
  {
    if ($this->Origine==null) {
      $this->Origine = $this->OrigineServices->selectOrigine($this->origineId);
    }
    return $this->Origine;
  }
  /**
   * @return Player
   */
  public function getPlayer()
  {
    if ($this->Player == null) {
      $this->Player = $this->PlayerServices->selectPlayer($this->playerId);
    }
    return $this->Player;
  }
  public function getImgUrl()
  { return '/wp-content/plugins/hj-zombicide/web/rsc/img/missions/'.$this->getCode().'-Mission.png'; }
  public function getThumbUrl()
  { return '/wp-content/plugins/hj-zombicide/web/rsc/img/missions/'.$this->getCode().'-Thumb.png'; }
  /**
   * @return string
   */
  public function getStrRules()
  {
    $MissionRules = $this->getMissionRules(self::FIELD_TITLE);
    $strList = '';
    if (!empty($MissionRules)) {
      foreach ($MissionRules as $MissionRule) {
        $strList .= ($strList!='' ? '<br>' : '');
        $strList .= '<span class="objRule">'.$MissionRule->getTitle().' <span class="tooltip">';
        $strList .= '<header>'.$MissionRule->getRuleCode().'</header>';
        $strList .= '<content>'.$MissionRule->getRuleDescription().'</content></span></span> ';
      }
    }
    return $strList;
  }
  /**
   * @return string
   */
  public function getStrObjectives()
  {
    $MissionObjectives = $this->getMissionObjectives(self::FIELD_TITLE);
    $strList = '';
    if (!empty($MissionObjectives)) {
      foreach ($MissionObjectives as $MissionObjective) {
        $strList .= ($strList!='' ? '<br>' : '');
        $strList .= '<span class="objRule">'.$MissionObjective->getTitle().' <span class="tooltip">';
        $strList .= '<header>'.$MissionObjective->getObjectiveCode().'</header>';
        $strList .= '<content>'.$MissionObjective->getObjectiveDescription().'</content></span></span> ';
      }
    }
    return $strList;
  }
  /**
   * @return string
   */
  public function getStrTiles()
  {
    $MissionTiles = $this->getMissionTiles();
    $strName = '';
    while (!empty($MissionTiles)) {
      $MissionTile = array_shift($MissionTiles);
      if ($strName!='') {
        $strName .= ', ';
      }
      $strName .= $MissionTile->getTile()->getCode();
    }
    return $strName;
  }
  /**
   * @return string
   */
  public function getStrExpansions()
  {
    $MissionExpansions = $this->getMissionExpansions();
    $strName = '';
    if (!empty($MissionExpansions)) {
      foreach ($MissionExpansions as $MissionExpansion) {
        if ($strName!='') {
          $strName .= ', ';
        }
        $strName .= $MissionExpansion->getExpansion()->getName();
      }
    }
    return $strName;
  }
  /**
   * @return string
   */
  public function getStrDifPlaDur()
  { return $this->getStrDifficulty().' / '.$this->getStrNbJoueurs().' / '.$this->getStrDuree(); }
  /**
   * @return string
   */
  public function getStrDuree()
  { return $this->getDuration()->getStrDuree(); }
  /**
   * @return string
   */
  public function getStrDifficulty()
  { return $this->getLevel()->getName(); }
  /**
   * @return string
   */
  public function getStrNbJoueurs()
  { return $this->getPlayer()->getNbJoueurs(); }
  /**
   * @return string
   */
  public function getStrOrigine()
  { return $this->getOrigine()->getName(); }
  /**
   * @param bool $isHome
   * @return string
   */
  public function getStrClassFilters($isHome)
  {
    $strClassFilters ='';
    $strClassFilters  = 'player-'.$this->playerId.' ';
    $strClassFilters .= 'duration-'.$this->durationId.' ';
    $strClassFilters .= 'level-'.$this->levelId.' ';
    return $strClassFilters.' col-12 col-sm-6 col-md-4';
  }
  /**
   * @param int $x
   * @param int $y
   * @return MissionTile
   */
  public function getMissionTile($x, $y)
  {
    $MissionTiles = $this->getMissionTiles();
    if (!empty($MissionTiles)) {
      foreach ($MissionTiles as $MissionTile) {
        if ($MissionTile->getCoordX()==$x && $MissionTile->getCoordY()==$y) {
          return $MissionTile;
        }
      }
    }
    return new MissionTile();
  }
  /**
   * @param int $x
   * @param int $y
   * @return int
   */
  public function getTileId($x, $y)
  { return $this->getMissionTile($x, $y)->getTileId(); }
  /**
   * @param int $x
   * @param int $y
   * @return string
   */
  public function getTileCode($x, $y)
  { return $this->getMissionTile($x, $y)->getTileCode(); }
  /**
   * @param int $x
   * @param int $y
   * @return string
   */
  public function getTileCodeAndOrientation($x, $y)
  { return $this->getTileCode($x, $y).'-'.$this->getTileOrientation($x, $y); }
  /**
   * @param int $x
   * @param int $y
   * @return string
   */
  public function getTileOrientation($x, $y)
  { return $this->getMissionTile($x, $y)->getOrientation(); }
  /**
   * @param array $MissionExpansions
   */
  public function setMissionExpansions($MissionExpansions)
  { $this->MissionExpansions = $MissionExpansions; }
  /**
   * @param array $post
   * @return bool
   */
  public function updateWithPost($post)
  {
    $doUpdate = false;
    $arr = array(self::FIELD_TITLE, self::FIELD_CODE, self::CST_LEVELID, self::CST_DURATIONID, self::CST_PLAYERID, self::CST_ORIGINEID);
    while (!empty($arr)) {
      $key = array_shift($arr);
      $value = stripslashes($post[$key]);
      if ($this->{$key} != $value) {
        $doUpdate = true;
        $this->{$key} = $value;
      }
    }
    return $doUpdate;
  }
  /**
   * @param array $post
   * @return bool
   */
  public function initWithPost($post)
  {
    $doInsert = true;
    $arr = array(self::FIELD_TITLE, self::FIELD_CODE, self::CST_LEVELID, self::CST_DURATIONID, self::CST_PLAYERID, self::CST_ORIGINEID);
    while (!empty($arr)) {
      $key = array_shift($arr);
      if ($post[$key] == '') {
        $doInsert = false;
      } else {
        $this->{$key} = stripslashes($post[$key]);
      }
    }
    return $doInsert;
  }
  /**
   * @param int $ruleId
   * @return boolean
   */
  public function hasRule($ruleId)
  {
    $hasRule = false;
    $MissionRules = $this->getMissionRules();
    while (!empty($MissionRules)) {
      $MissionRule = array_shift($MissionRules);
      if ($MissionRule->getRuleId()==$ruleId) {
        $hasRule = true;
      }
    }
    return $hasRule;
  }
  /**
   * @return int
   */
  public function getStartingMissionZoneId()
  {
    return 14;
  }
  /**
   * @param Live $Live
   * @param array $LiveSurvivors
   */
  public function addStandardStartingEquipment($Live, $LiveSurvivors)
  {
    shuffle($LiveSurvivors);
    // On va checker les éventuelles règles qui perturbent cette distribution.
    if ($this->hasRule(2)) {
      // On ne distribue que jusqu'à 3 Poêles aux Survivants. id de la Poêle : 27
      $cpt=0;
      while (!empty($LiveSurvivors) && $cpt<3) {
        $LiveSurvivor = array_shift($LiveSurvivors);
        $args = array(self::CST_LIVESURVIVORID=>$LiveSurvivor->getId());
        $EquipmentLiveDecks = $this->EquipmentLiveDeckServices->getEquipmentLiveDecksWithFilters(__FILE__, __LINE__, $args);
        $rk = count($EquipmentLiveDecks);
        $args = array(
          self::CST_LIVEID=>$Live->getId(),
          self::CST_EQUIPMENTCARDID=>27,
          'rank'=>$rk,
          self::CST_STATUS=>'E',
          self::CST_LIVESURVIVORID=>$LiveSurvivor->getId()
        );
        $EquipmentLiveDeck = new EquipmentLiveDeck($args);
        $this->EquipmentLiveDeckServices->insert(__FILE__, __LINE__, $EquipmentLiveDeck);
        $cpt++;
      }
    } else {
      // On vérifie l'extension rattachée à la Mission et en fonction on donnera du matériel.
    }
  }

}
<file_sep>/core/actions/ToolActions.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * ExpansionActions
 * @author Hugues
 * @since 1.05.09
 * @version 1.08.01
 */
class ToolActions extends LocalActions
{
  /**
   * Constructeur
   */
  public function __construct($post=array())
  {
    parent::__construct();
    $this->post = $post;
    $this->TileServices = new TileServices();
    $this->debug = '';
  }
  /**
   * Point d'entrée des méthodes statiques.
   * @param array $post
   * @return string
   **/
  public static function dealWithStatic($post)
  {
    $returned = '';
    $Act = new ToolActions($post);
    if ($post[self::CST_AJAXACTION]==self::AJAX_GETTHROWDICE) {
      $returned = $Act->dealWithThrowDice();
    } elseif ($post[self::CST_AJAXACTION]==self::AJAX_GETRANDOMMAP) {
      $returned = $Act->dealWithRandomMap();
    } elseif ($post[self::CST_AJAXACTION]=='getBuildingMap') {
      $returned = $Act->dealWithBuildingMap();
    } elseif ($post[self::CST_AJAXACTION]=='getNonUsedTiles') {
      $returned = $Act->dealWithNonUsedTiles();
    } elseif ($post[self::CST_AJAXACTION]=='getEmptyCell') {
      $returned = $Act->getEmptyCell();
    } elseif ($post[self::CST_AJAXACTION]=='getImageMap') {
      $returned = $Act->getImageMap();
    } else {
      $returned  = 'Erreur dans ToolActions > dealWithStatic, '.$_POST[self::CST_AJAXACTION].' inconnu.';
    }
    return $returned;
  }

  public function getImageMap()
  {
    $this->initPost();
    $urlTarget = '/wp-content/plugins/hj-zombicide/web/rsc/img/maps/'.date('Y-m-d_H:i:s').'-map.png';
    $targetPath = getcwd().'/..'.$urlTarget;

    $im = @imagecreatetruecolor(500*$this->width, 500*$this->height);

    for ($row=0; $row<$this->height; $row++) {
      for ($col=0; $col<$this->width; $col++) {
        if (isset($this->lockedMapTiles[$row][$col])) {
          $MapTile = $this->lockedMapTiles[$row][$col];
          $srcFile = getcwd().'/../wp-content/plugins/hj-zombicide/web/rsc/img/tiles/'.$MapTile->getTile()->getCode().'-500px.png';
          $imgTile = @imagecreatefrompng($srcFile);
          switch ($MapTile->getOrientation()) {
            case 'left' :
              $angle = 90;
            break;
            case 'bottom' :
              $angle = 180;
            break;
            case 'right' :
              $angle = 270;
            break;
            default :
              $angle = 0;
            break;
          }
          $rotate = imagerotate($imgTile, $angle, 0);
          imagecopymerge($im, $rotate, 500*$col, 500*$row, 0, 0, 500, 500, 100);
        }
      }
    }

    imagepng($im, $targetPath);
    imagedestroy($im);
    return $urlTarget;
  }
  private function getStrEmptyCell()
  {
    $result  = '<div class="cell hidden cellModel"><img alt="Non définie">';
    $result .= '<nav class="hoverActions nav nav-fill nav-pills"><i class="nav-item far fa-check-square fakeCb" data-cell="cell_0_0"></i>';
    $result .= '<i class="nav-item fas fa-unlock fakeLock" data-lock="cell_0_0"></i><i class="nav-item fas fa-cog"></i>';
    return $result.'<i class="nav-item fas fa-undo"></i><i class="nav-item fas fa-redo"></i></nav></div>';
  }
  public function getEmptyCell()
  { return $this->jsonString($this->getStrEmptyCell(), 'empty-cell', true); }

  private function initPost()
  {
    //////////////////////////////////////////////////////////////////////
    // Initialisation des variables
    $params = $this->post['params'];
    $arrParams = explode('&', $params);
    while (!empty($arrParams)) {
      $param = array_shift($arrParams);
      list($key, $value) = explode('=', $param);
      $this->{$key} = $value;
    }
    $this->expansionIds = str_replace('set-', '', $this->expansionIds);
    $this->cellIds = explode(',', $this->cells);
    // On initialise la MapTile.
    $this->lockedMapTiles = array();
    $this->lockedOCodes   = array();
    $locks = explode(',', $this->locks);
    while (!empty($locks)) {
      $lock = array_shift($locks);
      list($label, $row, $col, $code, $orientation) = explode('_', $lock);
      $Tiles = $this->TileServices->getTilesWithFilters(array(self::FIELD_CODE=>$code));
      $Tile = array_shift($Tiles);
      $MapTile = new MapTile($Tile, $orientation, true);
      $this->lockedMapTiles[$row][$col] = $MapTile;
      $this->debug .= 'On a une Tile : '.$Tile->getCode().'<br>';
      array_push($this->lockedOCodes, $Tile->getCode());
      array_push($this->lockedOCodes, $Tile->getOCode());
    }
  }
  public function dealWithNonUsedTiles()
  {
    $this->initPost();
    // L'actuelle :
    list($label, $row, $col, $code, $orientation) = explode('_', $this->current);
    $Tiles = $this->TileServices->getTilesWithFilters(array(self::FIELD_CODE=>$code));
    $Tile = array_shift($Tiles);
    $code = $Tile->getCode();
    $oCode = $Tile->getOCode();

    $arrTmp = array_keys($this->lockedOCodes, $code);
    unset($this->lockedOCodes[$arrTmp[0]]);
    $arrTmp = array_keys($this->lockedOCodes, $oCode);
    unset($this->lockedOCodes[$arrTmp[0]]);

    $Bean = new LocalBean();
    //////////////////////////////////////////////////////////////////////
    // On récupère les Dalles que l'on veut pouvoir utiliser..
    $Tiles = $this->TileServices->getTilesWithFiltersIn(array(self::FIELD_EXPANSIONID=>$this->expansionIds));
    $lstTiles = '';
    while (!empty($Tiles)) {
      $Tile = array_shift($Tiles);
      if (in_array($Tile->getCode(), $this->lockedOCodes)) {
        continue;
      }
      // On défini les paramètres de base des différentes balises.
      $argsDiv = array(
        self::ATTR_CLASS   => 'cell',
        'style'            => 'display: inline-block;',
      );
      $args = array(
        'data-row'         => $row,
        'data-col'         => $col,
        'data-src'         => $Tile->getImgUrl(),
        'data-code'        => $Tile->getCode(),
        'src'              => $Tile->getImgUrl(),
      );
      // On affiche les 4 positions
      for ($i=0; $i<4; $i++) {
        $this->getNextOrientation();
        $MapTile = new MapTile($Tile, $this->orientation);
        $isCompatible = $MapTile->isCompatibleV2($this->lockedMapTiles, $row, $col);
        if ($isCompatible) {
          $args['data-orientation'] = $this->orientation;
          $args[self::ATTR_CLASS]   = $this->orientation;
          $lstTiles .= $Bean->getBalise(self::TAG_DIV, $Bean->getBalise(self::TAG_IMG, '', $args), $argsDiv);
        }
      }
    }
    $result = '<section class="displayMap proposals" style="width:500px;">'.$lstTiles.'</section>';
    return $this->jsonString($result, self::PAGE_GENERATION_MAP, true);
  }
  public function dealWithBuildingMap()
  {
    $this->initPost();
    unset($this->version);
    $this->post['params'] = str_replace('&version=2', '', $this->post['params']);
    $this->dealWithRandomMap();

    //////////////////////////////////////////////////////////////////////
    $result  = '<div class="overlay"><div class="spinner"></div></div>';
    $result .= $this->getStrEmptyCell();
    $result .= '<section class="displayMap travaux map'.$this->width.'x'.$this->height.' mapWidth'.$this->width.' mb-2">';
    for ($i=0; $i<$this->height; $i++) {
      $result .= '<div class="row">';
      for ($j=0; $j<$this->width; $j++) {
        $result .= '<div class="cell">';
        if (!in_array('cell_'.$i.'_'.$j, $this->cellIds)) {
          if (isset($this->MapTiles[$i][$j])) {
            $MapTile = $this->MapTiles[$i][$j];
            $Tile = $MapTile->getTile();
            $orientation = $MapTile->getOrientation();
            $isLocked = $MapTile->isLocked();
            $result .= '<img class="'.$orientation.'" src="'.$Tile->getImgUrl().'">';
            $dataLock = 'cell_'.$i.'_'.$j.'_'.$Tile->getCode().'_'.$orientation;
          } else {
            $result .= '<img alt="Non définie"/>';
            $dataLock = '';
          }
          $htmlContent  = '<i class="nav-item far fa-check-square fakeCb" data-cell="cell_'.$i.'_'.$j.'"></i>';
          $htmlContent .= '<i class="nav-item fas fa-'.($isLocked ? '' : 'un').'lock fakeLock" data-lock="'.$dataLock.'"></i>';
        } else {
          $result .= '<img alt="Non sélectionnée"/>';
          $htmlContent  = '<i class="nav-item far fa-square fakeCb" data-cell="cell_'.$i.'_'.$j.'"></i>';
          $htmlContent .= '<i class="nav-item fas fa-unlock fakeLock" data-lock=""></i>';
        }
        $htmlContent .= '<i class="nav-item fas fa-cog"></i>';
        $htmlContent .= '<i class="nav-item fas fa-undo"></i>';
        $htmlContent .= '<i class="nav-item fas fa-redo"></i>';
        $result .= '<nav class="hoverActions nav nav-fill nav-pills">'.$htmlContent.'</nav>';
        $result .= '</div>';
      }
      $result .= '</div>';
    }
    $result .= '</section>';

    //////////////////////////////////////////////////////////////////////////////////////////////////////
    $result  = '<section id="page-generation-map" class="row">'.$result.'</section>';
    if (self::isAdmin()) {
      $result .= $this->debug;
    }
    return $this->jsonString($result, self::PAGE_GENERATION_MAP, true);
  }
  //////////////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////////
  // Bloc de gestion de génération aléatoire de map
  public function dealWithRandomMap()
  {
    //////////////////////////////////////////////////////////////////////
    // Initialisation des variables
    $params = $this->post['params'];
    $arrParams = explode('&', $params);
    while (!empty($arrParams)) {
      $param = array_shift($arrParams);
      list($key, $value) = explode('=', $param);
      $this->{$key} = $value;
    }
    if (isset($this->version) && $this->version==2) {
      return $this->dealWithBuildingMap();
    }
    $result = '<section class="displayMap map'.$this->width.'x'.$this->height.' mapWidth'.$this->width.' mb-2">';
    $this->expansionIds = str_replace('set-', '', $this->expansionIds);

    //////////////////////////////////////////////////////////////////////
    // On récupère les Dalles que l'on veut pouvoir utiliser. Et on les mélange.
    $Tiles = $this->TileServices->getTilesWithFiltersIn(array(self::FIELD_EXPANSIONID=>$this->expansionIds));
    // Puis on construit la Map aléatoire.
    $this->nbTestsGlobals = 0;
    $this->maxTestGlobals = ($this->expansionIds=='' ? 1 : 500);
    $content = '';
    do {
      $this->col = 0;
      $this->row = 0;
      $this->MapTiles = isset($this->lockedMapTiles) ? $this->lockedMapTiles : array();
      $this->arrOCode = isset($this->lockedOCodes) ? $this->lockedOCodes : array();
      shuffle($Tiles);
      $this->pendingTiles = array();
      $this->launchGeneration($Tiles);
    } while (count($this->MapTiles)!=$this->width*$this->height && $this->nbTestsGlobals!=$this->maxTestGlobals && $this->row!=$this->height);
    // Et on l'affiche.
    $hasError = false;
    $nbDalles = 0;
    for ($i=0; $i<$this->height; $i++) {
      for ($j=0; $j<$this->width; $j++) {
        if (isset($this->MapTiles[$i][$j])) {
          $MapTile = $this->MapTiles[$i][$j];
          $Tile = $MapTile->getTile();
          $orientation = $MapTile->getOrientation();
          $result .= '<img class="'.$orientation.'" src="'.$Tile->getImgUrl().'">';
          $this->debug .= '['.$Tile->getCode().'/'.$orientation.']';
          $nbDalles++;
        } else {
          $hasError = true;
        }
      }
    }
    if ($hasError) {
      $content  = '<div class="alert alert-danger"><h4 class="alert-heading">Une erreur est survenue durant la génération</h4>';
      $content .= 'Ca peut arriver sur de grandes maps ou lorsque le nombre de Dalles disponibles est très proche de la taille de la Map.<br>';
      $content .= 'Si dans la ligne de debug ci-dessous, le nombre en face de "Tests Global" est <strong>'.$this->nbTestsGlobals.'</strong>, c\'est que vous avez atteint le nombre de tentatives. Réessayez !';
      $content .= '<hr><p>'.date('H:i:s').' - Dimensions : LxH ['.$this->width.';'.$this->height.'] - Nb de Dalles : '.$nbDalles.', Rebut : '.count($this->pendingTiles).', Tests Global : '.$this->nbTestsGlobals.'</p></div>';
    }
    $result .= $content.'</section>';

    $result  = '<section id="page-generation-map" class="row">'.$result;
    $result .= '</section>';

    return $this->jsonString($result, self::PAGE_GENERATION_MAP, true);
  }

  private function launchGeneration($Tiles)
  {
    $this->nbTestsGlobals++;
    $this->debug .= '-=-=-=-=- GLOBAL TEST ['.$this->nbTestsGlobals.'] -=-=-=-=-=-<br>';
    $this->debug .= 'Row '.$this->row.' / Col '.$this->col.'<br>';
    $this->nbTests = 0;
    // Si on n'a plus de Dalles disponibles et si on a fait trop de tentatives, on quitte.
    // Ou qu'on a rempli la Map
    if (empty($Tiles) || $this->nbTestsGlobals==$this->maxTestGlobals || $this->row==$this->height) {
      return;
    }

    // On prend la première Dalle
    $Tile = array_shift($Tiles);
    // Si le recto de cette Dalle a déjà utilisé dans la Map, on ne peut pas la reprendre.
    if (in_array($Tile->getCode(), $this->arrOCode)) {
      $this->debug .= 'Already used '.$Tile->getCode().'<br>';
      // On passe à la Dalle suivante.
      return $this->launchGeneration($Tiles);
    }
    do {
      // On lui affecte une orientation
      ($this->nbTests==0 ? $this->getRandomOrientation() : $this->getNextOrientation());
      // On défini la MapTile
      if (isset($this->MapTiles[$this->row][$this->col])) {
        $this->debug .= 'MapTile '.$this->row.';'.$this->col.' définie<br>';
        $MapTile = $this->MapTiles[$this->row][$this->col];
      } else {
        $this->debug .= 'MapTile '.$this->row.';'.$this->col.' non définie<br>';
        $MapTile = new MapTile($Tile, $this->orientation);
      }
      $isCompatible = true;

      // On teste si elle est viable.
      $this->debug .= 'Testing '.$Tile->getCode().'/'.$this->orientation.' ['.$this->nbTests.']<br>';
      //////////////////////////////////////////////////////////////////////////////////////////////////////////////
      // Début des contrôles pour ajouter la Tile à la Map.
      $isCompatible = $MapTile->isCompatibleV2($this->MapTiles, $this->row, $this->col);
      $this->nbTests++;
      if ($this->nbTests==4) {
        break;
      }
      if ($isCompatible) {
        break;
      }
    } while (true);

    // Soit la DAlle est compatible dans la Map, soit on a essayé les 4 côtés et ça n'a rien donné.
    if (!$isCompatible) {
      // On n'a pas réussi à ajouter cette Dalle, elle ne peut pas être positionnée à cet endroit de la Map.
      $this->debug .= 'Failing to add '.$Tile->getCode().'/'.$this->orientation.'<br>';
      // On met la Dalle de côté.
      array_push($this->pendingTiles, $Tile);
      // On passe à la Dalle suivante.
      return $this->launchGeneration($Tiles);
    } else {
      // On ajoute la Dalle à la liste
      $this->debug .= 'Adding '.$Tile->getCode().'/'.$this->orientation.'<br>';
      // On ajout le code du recto pour l'exclure du champ des possibilités.
      array_push($this->arrOCode, $Tile->getOCode());
      $this->MapTiles[$this->row][$this->col] = $MapTile;
      // On met à jour les coordonnées pour la prochaine Dalle
      $this->upColAndRow();
      // On reprend les Dalles mises de côté et on les met à la fin.
      $Tiles = array_merge($Tiles, $this->pendingTiles);
      // On réinitialise la liste des mises de côté.
      $this->pendingTiles = array();
      // On recherche la Dalle suivante
      return $this->launchGeneration($Tiles);
    }
  }

  private function upColAndRow()
  {
    $this->col++;
    if ($this->col==$this->width) {
      $this->col=0;
      $this->row++;
    }
  }

  private function getNextOrientation()
  {
    switch ($this->orientation) {
      case 'top' :
        $this->orientation = 'right';
      break;
      case 'right' :
        $this->orientation = 'bottom';
      break;
      case 'bottom' :
        $this->orientation = 'left';
      break;
      case 'left' :
        $this->orientation = 'top';
      break;
      default :
        $this->getRandomOrientation();
      break;
    }
  }
  private function getRandomOrientation()
  {
    $arrOrientation = array('top', 'right', 'bottom', 'left');
    shuffle($arrOrientation);
    $this->orientation = array_shift($arrOrientation);
  }

  //////////////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////////


  //////////////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////////
  // Bloc de gestion du lancer de dés

  public function dealWithThrowDice()
  {
    $Bean = new UtilitiesBean();
    $tag = self::TAG_SPAN;
    $this->modif = 0;
    $this->seuil = 4;
    //////////////////////////////////////////////////////////////////////
    // Initialisation des variables
    $params = $this->post['params'];
    $arrParams = explode('&', $params);
    while (!empty($arrParams)) {
      $param = array_shift($arrParams);
      list($key, $value) = explode('=', $param);
      $this->{$key} = $value;
    }

    // TODO : Prendre en compte "surunsix" et "dual".
    // Si on a un nombre dans Barbare / Mode Automatique, on prend le plus gros score entre le nombre de dés de l'arme et le nombre d'acteurs dans al Zone.
    $this->nbDice = max($this->nbDice, $this->barabauto);

    $arrDice = array();
    for ($i=0; $i<$this->nbDice; $i++) {
      $dice = rand(1, 6);
      if ($dice==1) {
        $color = self::COLOR_RED;
        $dice = min(6,max(1,$dice+$this->modif));
      } else {
        $dice = min(6,max(1,$dice+$this->modif));
        if ($dice>=6) {
          $color = self::COLOR_BLUE;
          $this->nbDice += $this->surunsix;
        } elseif ($dice>=$this->seuil) {
          $color = self::COLOR_YELLOW;
        } else {
          $color = self::COLOR_ORANGE;
        }
      }
      $attributes = array(
        self::ATTR_CLASS => 'badge badge-'.$color.'-skill',
      );

      array_push($arrDice, $Bean->getBalise($tag, $dice, $attributes));
    }

    $result = '';
    while (!empty($arrDice)) {
      $num = array_shift($arrDice);
      $result .= '['.$num.']';
    }
    $result = '<section id="page-piste-de-des">Tirage '.date('d-m-Y H:i:s').' : '.$result.'</section>';
    return $this->jsonString($result, self::PAGE_PISTE_DE_DES, true);
  }

  //////////////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////////

}
<file_sep>/core/bean/WpPageSurvivorsBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe WpPageSurvivorsBean
 * @author Hugues
 * @since 1.04.00
 * @version 1.07.19
 */
class WpPageSurvivorsBean extends WpPageBean
{
  protected $urlTemplate = 'web/pages/public/wppage-survivors.php';
  /**
   * Class Constructor
   * @param WpPage $WpPage
   */
  public function __construct($WpPage='')
  {
    parent::__construct($WpPage);
    $this->SkillServices = new SkillServices();
    $this->SurvivorServices = new SurvivorServices();
    $this->ExpansionServices = new ExpansionServices();
  }
  /**
   * On vérifie si on est ici pour traiter la page des Survivants, ou un Survivant en particulier.
   * Pour le cas d'un Survivant, on retourne une WpPostSurvivorBean.
   * @return string
   */
  public function getContentPage()
  {
    $this->setFilters();
    return $this->getListContentPage();
  }
  /**
   * @return string
   */
  public function getListContentPage()
  {
    /////////////////////////////////////////////////////////////////////////////
    // On récupère la liste des Survivants puis les éléments nécessaires à la pagination.
    if (!$this->isSkillSearched()) {
      $Survivors = $this->SurvivorServices->getSurvivorsWithFilters($this->arrFilters, $this->colSort, $this->colOrder);
    } else {
      $Survivors = $this->SurvivorServices->getSurvivorsWithFiltersIn($this->arrFilters, $this->colSort, $this->colOrder);
    }
    $this->nbElements = count($Survivors);
    $this->nbPages = ceil($this->nbElements/$this->nbperpage);
    // On slice la liste pour n'avoir que ceux à afficher
    $displayedSurvivors = array_slice($Survivors, $this->nbperpage*($this->paged-1), $this->nbperpage);
    // On construit le corps du tableau
    $strBody = '';
    if (!empty($displayedSurvivors)) {
      foreach ($displayedSurvivors as $Survivor) {
        $strBody .= $Survivor->getBean()->getRowForPublicPage();
      }
    }
    /////////////////////////////////////////////////////////////////////////////

    /////////////////////////////////////////////////////////////////////////////
    // Affiche-t-on le filtre ?
    $showFilters = isset($this->arrFilters[self::FIELD_NAME])&&$this->arrFilters[self::FIELD_NAME]!=''
      || isset($this->arrFilters[self::FIELD_EXPANSIONID])&&$this->arrFilters[self::FIELD_EXPANSIONID]!=''
      || $this->isSkillSearched();
    /////////////////////////////////////////////////////////////////////////////

    //////////////////////////////////////////////////////////////////
    // On enrichi le template puis on le restitue.
    $args = array(
      // Les lignes du tableau - 1
      $strBody,
      // On affiche le dropdown par pages - 2
      $this->getDropdownNbPerPages(),
      // On affiche la pagination - 3
      $this->getNavPagination(),
      // Affiche ou non le bloc filtre - 4
      $showFilters ? 'block' : 'none',
      // Si le Nom est renseigné - 5
      $this->arrFilters[self::FIELD_NAME],
      // Liste des Extensions - 6
      $this->getExpansionFilters($this->arrFilters[self::FIELD_EXPANSIONID]),
      // Liste des Compétences bleues - 7
      $this->getBeanSkillFilters(self::COLOR_BLUE, $this->arrFilters['blue-skillId']),
      // Liste des Compétences jaunes - 8
      $this->getBeanSkillFilters(self::COLOR_YELLOW, $this->arrFilters['yellow-skillId']),
      // Liste des Compétences oranges - 9
      $this->getBeanSkillFilters(self::COLOR_ORANGE, $this->arrFilters['orange-skillId']),
      // Liste des Compétences rouges - 10
      $this->getBeanSkillFilters(self::COLOR_RED, $this->arrFilters['red-skillId']),
    );
    return $this->getRender($this->urlTemplate, $args);
  }
  private function getExpansionFilters($expansionId='')
  { return parent::getBeanExpansionFilters($expansionId, self::FIELD_EXPANSIONID); }

  /**
   * @return string
   */
  public function getRandomTeam($post)
  {
    // On récupère les paramètres nécessaires
    $nbMax = $post['nbSurvSel'];
    $arrValues = explode(',', $post['value']);
    // On mélange les valeurs possibles
    shuffle($arrValues);
    $nb = 0;
    $strReturned  = '';
    while (!empty($arrValues) && $nb<$nbMax) {
      $value = array_shift($arrValues);
      $Survivor = $this->SurvivorServices->selectSurvivor($value);
      $strReturned .= $Survivor->getBean()->getRowForPublicPage();
      $nb++;
    }
    return '<div id="page-selection-survivants">'.$strReturned.'</div>';
  }
  /**
   * @param array $post
   */
  public function setFilters($post=null)
  { parent::setBeanFilters($post, self::FIELD_NAME); }

  private function isSkillSearched()
  {
    return isset($this->arrFilters[self::COLOR_BLUE.'-'.self::FIELD_SKILLID])&&$this->arrFilters[self::COLOR_BLUE.'-'.self::FIELD_SKILLID]!=''
    || isset($this->arrFilters[self::COLOR_YELLOW.'-'.self::FIELD_SKILLID])&&$this->arrFilters[self::COLOR_YELLOW.'-'.self::FIELD_SKILLID]!=''
    || isset($this->arrFilters[self::COLOR_ORANGE.'-'.self::FIELD_SKILLID])&&$this->arrFilters[self::COLOR_ORANGE.'-'.self::FIELD_SKILLID]!=''
    || isset($this->arrFilters[self::COLOR_RED.'-'.self::FIELD_SKILLID])&&$this->arrFilters[self::COLOR_RED.'-'.self::FIELD_SKILLID]!='';
  }
}
<file_sep>/core/actions/SurvivorActions.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * SurvivorActions
 * @author Hugues
 * @since 1.04.00
 * @version 1.07.19
 */
class SurvivorActions extends LocalActions
{
  /**
   * Constructeur
   */
  public function __construct($post=array())
  {
    parent::__construct();
    $this->post = $post;
    $this->ExpansionServices = new ExpansionServices();
    $this->SurvivorServices  = new SurvivorServices();
    $this->WpPostServices    = new WpPostServices();
  }
  /**
   * Point d'entrée des méthodes statiques.
   * @param array $post
   * @return string
   **/
  public static function dealWithStatic($post)
  {
    $returned = '';
    $Act = new SurvivorActions($post);
    switch ($post[self::CST_AJAXACTION]) {
      case self::AJAX_GETSURVIVORS    :
        $returned = $Act->dealWithGetSurvivors();
      break;
      case self::AJAX_GETRANDOMTEAM :
        $returned = $Act->dealWithGetRandomTeam();
      break;
      case self::AJAX_SURVIVORVERIF  :
        $returned = $Act->dealWithSurvivorVerif(true);
      break;
      default :
        $returned = '';
      break;
    }
    return $returned;
  }

  /**
   * Récupération du contenu de la page via une requête Ajax.
   * @param array $post
   * @return string
   */
  public function dealWithGetSurvivors()
  {
    $Bean = new WpPageSurvivorsBean();
    $Bean->setFilters($this->post);
    return $this->jsonString($Bean->getListContentPage(), self::PAGE_SURVIVOR, true);
  }

  /**
   * @return string;
   */
  public function dealWithGetRandomTeam()
  {
    $Bean = new WpPageSurvivorsBean();
    return $this->jsonString($Bean->getRandomTeam($this->post), self::PAGE_SELECT_SURVIVORS, true);
  }

  //////////////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////////
  // Bloc de gestion de vérifications des Survivants en Home Admin
  /**
   * @param boolean $isVerif
   * @return string
   */
  public function dealWithSurvivorVerif($isVerif=false)
  {
    // On récupère les articles de survivants
    $args = array(
      self::WP_CAT         => self::WP_CAT_SURVIVOR_ID,
      self::WP_TAXQUERY    => array(),
      self::WP_POSTSTATUS  => self::WP_PUBLISH.', future',
    );
    $this->WpPostSurvivors = $this->WpPostServices->getArticles($args);
    $nbWpPostSurvivors = count($this->WpPostSurvivors);
    // Et les Survivants en base
    $this->Survivors = $this->SurvivorServices->getSurvivorsWithFilters();
    $nbSurvivors = count($this->Survivors);
    if ($isVerif) {
      $this->checkSurvivors();
      $strBilan = $this->jsonString($this->strBilan, self::AJAX_SURVIVORVERIF, true);
    } elseif ($nbWpPostSurvivors!=$nbSurvivors) {
      $strBilan  = "Le nombre d'articles ($nbWpPostSurvivors) ne correspond pas au nombre de Survivants en base ($nbSurvivors).";
      $strBilan .= "<br>Une vérification est vivement conseillée.";
    } else {
      $strBilan = "Le nombre d'articles ($nbWpPostSurvivors) correspond au nombre de Survivants en base.";
    }
    return $strBilan;
  }

  private function insertSurvivor()
  {
    // Si on n'en a pas, on doit créer une entrée correspondante.
    $Survivor = new Survivor();
    $name = $this->WpPost->getPostTitle();
    $Survivor->setName($name);
    $description  = $this->WpPost->getPostContent();
    $description  = substr($description, 25, -27);
    $Survivor->setBackground($description);
    $Survivor->setExpansionId($this->getExpansionId());
    $arrProfiles = unserialize($this->WpPost->getPostMeta('profils'));
    foreach ($arrProfiles as $value) {
      switch ($value) {
        case self::LBL_STANDARD :
          $Survivor->setStandard(1);
        break;
        case self::LBL_ZOMBIVANT :
          $Survivor->setZombivor(1);
        break;
        case self::LBL_ULTIMATE :
          $Survivor->setUltimate(1);
        break;
        case self::LBL_ULTIMATEZOMBIVANT :
          $Survivor->setUltimatez(1);
        break;
        default :
        break;
      }
    }
    $this->SurvivorServices->insertSurvivor($Survivor);
    $this->strBilan .= '<br>Survivant créé en base : '.$name.'.';
  }
  private function checkSurvivors()
  {
    // On regarde les articles créés et on vérifie les données en base, si elles existent et si elles sont cohérentes entre elles.
    while (!empty($this->WpPostSurvivors)) {
      // On récupère le WpPost et ses données
      $this->WpPost = array_shift($this->WpPostSurvivors);
      $survivorId = $this->WpPost->getPostMeta(self::FIELD_SURVIVORID);
      // On recherche un Survivant dans la base de données qui correspond.
      $Survivor = $this->SurvivorServices->selectSurvivor($survivorId);
      if ($Survivor->getId()=='') {
        $this->insertSurvivor();
      } else {
        // Si on en a juste une, c'est tranquille.
        $this->checkSurvivor($Survivor);
      }
    }
    // Puis, on regarde les données en base et on vérifie que des articles ont été créés pour elles.
    while (!empty($this->Survivors)) {
      // On récupère l'extension.
      $Survivor = array_shift($this->Survivors);
      $args = array(
        self::WP_METAKEY      => self::FIELD_SURVIVORID,
        self::WP_METAVALUE    => $Survivor->getId(),
        self::WP_TAXQUERY     => array(),
        self::WP_POSTSTATUS   => 'publish, future',
        self::WP_CAT          => self::WP_CAT_SURVIVOR_ID,
      );
      $WpPost = $this->WpPostServices->getArticles($args);
      if (empty($WpPost)) {
        $this->strBilan .= '<br>Article à créer pour un Survivant : '.$Survivor->getName().' ['.$Survivor->toJson().'].';
      }
    }
    if ($this->strBilan=='') {
      $this->strBilan = 'Il semblerait que tout aille à la perfection. Aucune anomalie remontée.';
    }
  }
  private function getExpansionId()
  {
    $postId        = $this->WpPost->getPostMeta(self::FIELD_EXPANSIONID);
    $Wp_post = get_post($postId);
    $WpPost = WpPost::convertElement($Wp_post);
    $codeExpansion = $WpPost->getPostMeta(self::FIELD_CODE);
    $Expansions = $this->ExpansionServices->getExpansionsWithFilters(array(self::FIELD_CODE=>$codeExpansion));
    $Expansion = array_shift($Expansions);
    return $Expansion->getId();
  }
  private function checkSurvivor($Survivor)
  {
    $this->Survivor = $Survivor;
    // On initialise les données
    $this->doUpdate = false;
    $name          = $this->WpPost->getPostTitle();
    $background    = $this->WpPost->getPostContent();
    $expansionId   = $this->getExpansionId();
    $this->arrProfils    = unserialize($this->WpPost->getPostMeta('profils'));
    // On vérifie si la donnée en base correspond à l'article.
    if ($this->Survivor->getName()!=$name) {
      $this->Survivor->setName($name);
      $this->doUpdate = true;
    }
    if ($this->Survivor->getExpansionId()!=$expansionId) {
      $this->Survivor->setExpansionId($expansionId);
      $this->strBilan .= '<br>Survivant mis à jour au niveau de l extension : '.$this->Survivor->getExpansionId().' - '.$expansionId.'.';
      $this->doUpdate = true;
    }
    if ($this->Survivor->getBackground()!=$background && $background!='' ) {
      $this->strBilan .= '<br>Background KO.';
      $this->Survivor->setBackground($background);
      $this->doUpdate = true;
    }
    $this->checkProfiles();
    // Vérifications terminées, on fait la mise à jour si nécessaire.
    if ($this->doUpdate) {
      // Si nécessaire, on update en base.
      $this->SurvivorServices->updateSurvivor($this->Survivor);
      $this->strBilan .= '<br>Survivant mis à jour : '.$name.'.';
    }
  }
  private function checkStandardProfile()
  {
    if (!$this->Survivor->isStandard() && in_array(self::LBL_STANDARD, $this->arrProfils)) {
      $this->Survivor->setStandard(1);
      $this->doUpdate = true;
    } elseif ($this->Survivor->isStandard() && !in_array(self::LBL_STANDARD, $this->arrProfils)) {
      $this->Survivor->setStandard(0);
      $this->doUpdate = true;
    }
  }
  private function checkZombivorProfile()
  {
    if (!$this->Survivor->isZombivor() && in_array(self::LBL_ZOMBIVANT, $this->arrProfils)) {
      $this->Survivor->setZombivor(1);
      $this->doUpdate = true;
    } elseif ($this->Survivor->isZombivor() && !in_array(self::LBL_ZOMBIVANT, $this->arrProfils)) {
      $this->Survivor->setZombivor(0);
      $this->doUpdate = true;
    }
  }
  private function checkProfiles()
  {
    if (isset($this->arrProfils)) {
      // On vérifie le profil Standard
      $this->checkStandardProfile();
      // On vérifie le profil Zombivant
      $this->checkZombivorProfile();
      // On vérifie le profil Ultimate
      if (!$this->Survivor->isUltimate() && in_array(self::LBL_ULTIMATE, $this->arrProfils)) {
        $this->Survivor->setUltimate(1);
        $this->doUpdate = true;
      } elseif ($this->Survivor->isUltimate() && !in_array(self::LBL_ULTIMATE, $this->arrProfils)) {
        $this->Survivor->setUltimate(0);
        $this->doUpdate = true;
      }
      // On vérifie le profil Ultimate Zombivant
      if (!$this->Survivor->isUltimatez() && in_array(self::LBL_ULTIMATEZOMBIVANT, $this->arrProfils)) {
        $this->Survivor->setUltimatez(1);
        $this->doUpdate = true;
      } elseif ($this->Survivor->isUltimatez() && !in_array(self::LBL_ULTIMATEZOMBIVANT, $this->arrProfils)) {
        $this->Survivor->setUltimatez(0);
        $this->doUpdate = true;
      }
    }
  }
  // Fin du bloc relatif à la vérification des extensions sur la Home Admin.
  //////////////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////////

}
<file_sep>/core/services/KeywordServices.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe KeywordServices
 * @author Hugues.
 * @since 1.0.00
 * @version 1.04.28
 */
class KeywordServices extends LocalServices
{
  /**
   * L'objet Dao pour faire les requêtes
   * @var KeywordDaoImpl $Dao
   */
  protected $Dao;
  /**
   * Class Constructor
   */
  public function __construct()
  {
    parent::__construct();
    $this->Dao = new KeywordDaoImpl();
  }
  /**
   * @param int $id
   * @return Keyword
   */
  public function selectKeyword($id)
  { return $this->select(__FILE__, __LINE__, $id); }

}
<file_sep>/core/services/SurvivorSkillServices.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe SurvivorSkillServices
 * @author Hugues.
 * @since 1.04.27
 * @version 1.05.07
 */
class SurvivorSkillServices extends LocalServices
{
  /**
   * L'objet Dao pour faire les requêtes
   * @var SurvivorDaoImpl $Dao
   */
  protected $Dao;
  /**
   * Class Constructor
   */
  public function __construct()
  {
    parent::__construct();
    $this->Dao = new SurvivorSkillDaoImpl();
  }

  /**
   * @param array $arrFilters
   */
  private function buildFilters($arrFilters)
  {
    $this->arrParams[self::SQL_WHERE] = array();
    array_push($this->arrParams[self::SQL_WHERE], $this->addFilter($arrFilters, self::FIELD_SURVIVORID));
    array_push($this->arrParams[self::SQL_WHERE], $this->addFilter($arrFilters, self::FIELD_SKILLID));
    array_push($this->arrParams[self::SQL_WHERE], $this->addFilter($arrFilters, self::FIELD_SURVIVORTYPEID));
    array_push($this->arrParams[self::SQL_WHERE], $this->addFilter($arrFilters, self::FIELD_TAGLEVELID));
  }
  /**
   * @param string $file
   * @param string $line
   * @param array $arrFilters
   * @param string $orderby
   * @param string $order
   * @return array
   */
  public function getSurvivorSkillsWithFilters($arrFilters=array(), $orderby=null, $order=array(self::ORDER_ASC, self::ORDER_ASC))
  {
    if ($orderby==null) {
      $orderby = array(self::FIELD_SURVIVORTYPEID, self::FIELD_TAGLEVELID);
    }
    $this->arrParams = $this->buildOrderAndLimit($orderby, $order);
    $this->buildFilters($arrFilters);
    return $this->Dao->selectEntriesWithFilters(__FILE__, __LINE__, $this->arrParams);
  }

  public function deleteBulkSurvivorSkill($arrFilters)
  {
    $this->buildFilters($arrFilters);
    return $this->Dao->deleteBulkEntriesWithFilters($this->arrParams);
  }

  public function insertSurvivorSkill($SurvivorSkill)
  { $this->insert(__FILE__, __LINE__, $SurvivorSkill); }
}
<file_sep>/core/domain/Keyword.class.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe Keyword
 * @author Hugues.
 * @version 1.0.00
 * @since 1.0.00
 */
class Keyword extends LocalDomain
{
  /**
   * Id technique de la donnée
   * @var int $id
   */
  protected $id;
  /**
   * Nom du keyword
   * @var string $name
   */
  protected $name;
  /**
   * Description du keyword
   * @var string $description
   */
  protected $description;
  /**
   * @return int
   */
  public function getId()
  {return $this->id; }
  /**
   * @return string
   */
  public function getName()
  { return $this->name; }
  /**
   * @return string
   */
  public function getDescription()
  { return $this->description; }
  /**
   * @param int $id
   */
  public function setId($id)
  { $this->id=$id; }
  /**
   * @param string $name
   */
  public function setName($name)
  { $this->name=$name; }
  /**
   * @param string $description
   */
  public function setDescription($description)
  { $this->description=$description; }
  /**
   * @return array
   */
  public function getClassVars()
  { return get_class_vars('Keyword'); }
  /**
   * @param array $row
   * @param string $a
   * @param string $b
   * @return Keyword
   */
  public static function convertElement($row, $a='', $b='')
  { return parent::convertElement(new Keyword(), self::getClassVars(), $row); }
  /**
   * @return KeywordBean
   */
  public function getBean()
  { return new KeywordBean($this); }
}
<file_sep>/core/bean/WpPageToolsBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe WpPageToolsBean
 * @author Hugues
 * @since 1.04.24
 * @version 1.05.09
 */
class WpPageToolsBean extends WpPageBean
{
  protected $urlTemplatePriorityOrder = 'web/pages/public/wppage-ordre-de-priorite.php';
  protected $urlTemplatePisteDes      = 'web/pages/public/wppage-pistededes.php';
  protected $urlTemplateRandomMap     = 'web/pages/public/wppage-generationmap.php';
  protected $urlTemplateSelSurv       = 'web/pages/public/wppage-selectsurvivors.php';
  protected $urlTemplateRandomMapV2   = 'web/pages/public/wppage-generationmap-v2.php';
  /**
   * Class Constructor
   * @param WpPage $WpPage
   */
  public function __construct($WpPage='')
  {
    parent::__construct($WpPage);
    $this->ExpansionServices = new ExpansionServices();
    $this->SurvivorServices = new SurvivorServices();
  }

  /**
   * @return string
   */
  public function getSelectSurvivorsContent()
  {
    // Gestion du menu des Stand Alone et Extensions officielles
    $arrExpansions = array(
      'Saison 1' => array(23, 9, 10),
      'Saison 2' => array(24, 11, 12),
      'Saison 3' => array(25, 16, 17),
      'Extensions' => array(4, 14, 19, 26),
    );
    $str = '';
    $arrOfficiels = array();
    foreach ($arrExpansions as $parent=>$arrChildren) {
      $str .= '<div type="button" class="btn btn-dark btn-expansion-group"><span><i class="fa fa-chevron-circle-down"></i></span> '.$parent.'</div>';
      while (!empty($arrChildren)) {
        $childId = array_shift($arrChildren);
        array_push($arrOfficiels, $childId);
        $Expansion = $this->ExpansionServices->selectExpansion($childId);
        $str .= $Expansion->getBean()->getButton(' btn-secondary hidden');
      }
    }
    // Gestion des extensions fan-made
    $strFanMade = '';
    $Expansions = $this->ExpansionServices->getExpansionsWithFilters();
    while (!empty($Expansions)) {
      $Expansion = array_shift($Expansions);
      $childId = $Expansion->getId();
      if (in_array($childId, $arrOfficiels) || $Expansion->getNbSurvivants()==0) {
        continue;
      }
      $strFanMade .= $Expansion->getBean()->getButton(' btn-secondary hidden');
    }

    // Gestion du menu des Survivants officiels
    $Survivors = $this->SurvivorServices->getSurvivorsWithFilters();
    $strSurvivors = '';
    while (!empty($Survivors)) {
      $Survivor = array_shift($Survivors);
      $strSurvivors .= $Survivor->getBean()->getButton();
    }


    $args = array(
      // Liste des Extensions Officielles et ses Survivants - 1
      $str,
      // Liste des cartouches de tous les Survivants - 2
      $strSurvivors,
      // Liste des extensions Fan-Made - 3
      $strFanMade,
    );
    return $this->getRender($this->urlTemplateSelSurv, $args);
  }

  /**
   * @return string
   */
  public function getThrowSomeDiceContent()
  {
    $args = array(
      '','','',
    );
    return $this->getRender($this->urlTemplatePisteDes, $args);
  }

  public function getPriorityOrderContent()
  {
    $args = array(
      // Le titre de la page - 1
      $this->WpPage->getPostTitle(),
      // Le contenu de la page - 2
      $this->WpPage->getPostContent(),
    );
    return $this->getRender($this->urlTemplatePriorityOrder, $args);
  }

  public function getRandomMapV2()
  {
    /////////////////////////////////////////////////////////////////////////////
    // On récupère la liste de toutes les Extensions
    $Expansions = $this->ExpansionServices->getExpansionsWithFilters(array(), self::FIELD_DISPLAYRANK);
    $strExpansions = '';
    $strIcon = $this->getBalise(self::TAG_SPAN, $this->getBalise(self::TAG_I, '', array(self::ATTR_CLASS=>'far fa-square')));
    while (!empty($Expansions)) {
      $Expansion = array_shift($Expansions);
      // Si l'extension n'a pas de dalles, on passe à l'extension suivante.
      if ($Expansion->getNbDalles()==0) {
        continue;
      }
      // On en profite aussi pour construire le bloc de filtres.
      $attributes = array(
        self::ATTR_TYPE             => 'button',
        self::ATTR_CLASS            => 'btn btn-expansion btn-dark',
        self::ATTR_DATA_EXPANSIONID => $Expansion->getId(),
        'data-nb-dalles'            => $Expansion->getNbDalles(),
      );
      $strExpansions .= $this->getBalise(self::TAG_DIV, $strIcon.' '.$Expansion->getName(), $attributes);
    }

    //////////////////////////////////////////////////////////////////
    // On enrichi le template puis on le restitue.
    $args = array(
      // La liste des extensions - 1
      $strExpansions
    );
    return $this->getRender($this->urlTemplateRandomMapV2, $args);
  }
  public function getRandomMap()
  {
    /////////////////////////////////////////////////////////////////////////////
    // On récupère la liste de toutes les Extensions
    $Expansions = $this->ExpansionServices->getExpansionsWithFilters(array(), self::FIELD_DISPLAYRANK);
    $strFilters = '';
    $strSpawns = '';
    while (!empty($Expansions)) {
      $Expansion = array_shift($Expansions);
      // Si l'extension n'a pas de dalles, on passe à l'extension suivante.
      if ($Expansion->getNbDalles()==0) {
        continue;
      }
      // On en profite aussi pour construire le bloc de filtres.
      $strFilters .= $this->getBalise(self::TAG_OPTION, $Expansion->getName(), array(self::ATTR_VALUE => 'set-'.$Expansion->getId()));
    }

    //////////////////////////////////////////////////////////////////
    // On enrichi le template puis on le restitue.
    $args = array(
      // La liste des extensions - 1
      $strFilters
    );
    return $this->getRender($this->urlTemplateRandomMap, $args);
  }
}
<file_sep>/core/domain/MissionTile.class.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe MissionTile
 * @author Hugues.
 * @since 1.0.00
 * @version 1.04.28
 */
class MissionTile extends LocalDomain
{
  /**
   * Id technique de la jointure
   * @var int $id
   */
  protected $id;
  /**
   * Id technique de la Mission
   * @var int $missionId
   */
  protected $missionId;
  /**
   * Id technique de la Tuile
   * @var int $tileId
   */
  protected $tileId;
  /**
   * Orientation de la Dalle sur la Mission
   * @var string $orientation
   */
  protected $orientation;
  /**
   * Coordonnées en abscisses
   * @var int $coordX
   */
  protected $coordX;
  /**
   * Coordonnée en ordonnées
   * @var int $coordY
   */
  protected $coordY;
  /**
   * @return int
   */
  public function getId()
  {return $this->id; }
  /**
   * @ return int
   */
  public function getMissionId()
  { return $this->MissionId; }
  /**
   * @ return int
   */
  public function getTileId()
  { return $this->tileId; }
  /**
   * @return string
   */
  public function getOrientation()
  { return $this->orientation; }
  /**
   * @return int
   */
  public function getCoordX()
  { return $this->coordX; }
  /**
   * @return int
   */
  public function getCoordY()
  { return $this->coordY; }
  /**
   * @param int $id
   */
  public function setId($id)
  { $this->id=$id; }
  /**
   * @param int $missionId
   */
  public function setMissionId($missionId)
  { $this->missionId = $missionId; }
  /**
   * @param int $tileId
   */
  public function setTileId($tileId)
  { $this->tileId = $tileId; }
  /**
   * @param string $orientation
   */
  public function setOrientation($orientation)
  { $this->orientation=$orientation; }
  /**
   * @param int $coordX
   */
  public function setCoordX($coordX)
  { $this->coordX=$coordX; }
  /**
   * @param int $coordY
   */
  public function setCoordY($coordY)
  { $this->coordY=$coordY; }
  /**
   * @return array
   */
  public function getClassVars()
  { return get_class_vars('MissionTile'); }
  /**
   * @param array $row
   * @param string $a
   * @param string $b
   * @return MissionExpansion
   */
  public static function convertElement($row, $a='', $b='')
  { return parent::convertElement(new MissionTile(), self::getClassVars(), $row); }
  /**
   * @return Tile
   */
  public function getTile()
  {
    if ($this->Tile == null) {
      $this->Tile = $this->TileServices->selectTile($this->tileId);
    }
    return $this->Tile;
  }

}
<file_sep>/core/bean/MainPageBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe MainPageBean
 * @author Hugues
 * @since 1.00.00
 * @version 1.08.01
 */
class MainPageBean extends UtilitiesBean implements ConstantsInterface
{
  /**
   * Template pour afficher le header principal
   * @var $tplMainHeaderContent
   */
  public static $tplMainHeaderContent  = 'web/pages/public/public-main-header.php';
  /**
   * Template pour afficher le footer principal
   * @var $tplMainFooterContent
   */
  public static $tplMainFooterContent  = 'web/pages/public/public-main-footer.php';
  /**
   * Option pour cacher le Header et le footer.
   * @var $showHeaderAndFooter
   */
  public $showHeaderAndFooter  = true;
  /**
   * La classe du shell pour montrer plus ou moins le haut de l'image de fond.
   * @var $shellClass
   */
  protected $shellClass;
  /**
   * Class Constructor
   */
  public function __construct()
  {
    $this->lang = (substr($_SERVER['REQUEST_URI'], 0, 3) == '/en' ? 'en' : 'fr');
    $this->WpPostServices = new WpPostServices();
  }

  /**
   * @return string
   */
  public function displayPublicFooter()
  {
    $args = array(admin_url('admin-ajax.php'));
    return $this->getRender(self::$tplMainFooterContent, $args);
  }
  /////////////////////////// Fin gestion PublicFooter ///////////////////////////

  /**
   * @return string
   */
  public function displayPublicHeader()
  {
    //////////////////////////////////////////////////////////////////////////////////
    // Récupération des pages devant être affichées dans le menu du Header
    $args = array(
      self::WP_POSTTYPE => 'page',
      self::WP_ORDERBY  => 'menu_order',
      self::WP_TAXQUERY => array(),
      'post_parent'     => 0,
    );
    $WpPosts = $this->WpPostServices->getArticles($args, false, 'WpPage');
    // On construit le lien vers l'accueil
    if ($this->lang=='fr') {
      $label = $this->getBalise(self::TAG_SPAN, 'Accueil');
      $strPages = $this->getBalise(self::TAG_A, $label, array(self::ATTR_HREF=>get_site_url()));
    } else {
      $label = $this->getBalise(self::TAG_SPAN, 'Home');
      $strPages = $this->getBalise(self::TAG_A, $label, array(self::ATTR_HREF=>get_site_url().'/en/'));
    }
    // Pour chaque page, si le parent ne vaut pas 0, on passe au suivant...
    while (!empty($WpPosts)) {
      $WpPost = array_shift($WpPosts);
      $strPages .= $this->addWpPageToMenu($WpPost);
    }
    //////////////////////////////////////////////////////////////////////////////////
    // On enrichi le Template et on le restitue.
    $args = array(
      // Plus d'actualité - 1
      '',
      // Plus d'actualité - 2
      '',
      // Contenu du Menu à afficher, mais pas tout le temps (pleine page) - 3
      ($this->showHeaderAndFooter ? $strPages : '')
    );
    return $this->getRender(self::$tplMainHeaderContent, $args);
  }
  /**
   * @param WpPage $WpPost
   * @return string
   */
  private function addWpPageToMenu($WpPost)
  {
    $strMenu = '';
    // On récupère la WpPage qu'on veut afficher.
    $WpPage = new WpPage($WpPost->getID());
    $labelParent = $this->getBalise(self::TAG_SPAN, $WpPage->getPostTitle());
    // On vérifie la présence d'enfants éventuels.
    $Children = $WpPage->hasChildren();
    if (empty($Children)) {
      // S'il n'y en a pas, c'est un siple lien.
      $strMenu = $this->getBalise(self::TAG_A, $labelParent, array(self::ATTR_HREF => $WpPage->getPermalink()));
    } else {
      // Sinon, on doit construire la liste des enfants pour le sous menu
      $strSubMenus = '';
      while (!empty($Children)) {
        $Child = array_shift($Children);
        if ($Child->getMenuOrder()==0 && !self::isAdmin()) {
          // On n'affiche que les enfants ayant un OrderMenu différent de 0
          continue;
        }
        $childLabel   = $this->getBalise(self::TAG_SPAN, $Child->getPostTitle());
        $childLink    = $this->getBalise(self::TAG_A, $childLabel, array(self::ATTR_HREF=>$Child->getPermalink()));
        $strSubMenus .= $this->getBalise(self::TAG_LI, $childLink);
      }
      $parentLink    = $this->getBalise(self::TAG_A, $labelParent, array(self::ATTR_HREF=>'#'));
      $listChildren  = $this->getBalise(self::TAG_UL, $strSubMenus);
      $strMenu      .= $this->getBalise(self::TAG_SPAN, $parentLink.$listChildren, array(self::ATTR_CLASS => 'hasDropDown'));
    }
    return $strMenu;
  }
  /////////////////////////// Fin gestion PublicHeader ///////////////////////////

  /**
   * @return Bean
   */
  public static function getPageBean()
  {
    if (is_front_page()) {
      $returned = new WpPageHomeBean();
    } else {
      $scriptUrl = $_SERVER['REDIRECT_SCRIPT_URL'];
      if (strpos($scriptUrl, '/tag/')!==false) {
        $returned = new WpPageTagBean($scriptUrl);
      } else {
        $post = get_post();
        if (empty($post)) {
          // On a un problème (ou pas). On pourrait être sur une page avec des variables, mais qui n'est pas prise en compte.
          $slug = str_replace('/', '', $_SERVER['REDIRECT_SCRIPT_URL']);
          $args = array(
              'name'=>$slug,
              'post_type'=>'page',
              'numberposts'=>1
          );
          $my_posts = get_posts($args);
          $post = array_shift($my_posts);
        }
        if ($post->post_type == 'page') {
          $returned = new WpPageBean($post);
        } elseif ($post->post_type == 'post') {
          $WpPostBean = new WpPostBean($post);
          $returned = $WpPostBean->getBean();
        } else {
          $returned = new WpPageError404Bean();
        }
      }
    }
    return $returned;
  }
  /**
   * @param array $addArg
   * @param array $remArg
   * @return string
   */
  public function getQueryArg($addArg, $remArg=array())
  {
    $addArg['page'] = 'hj-zombicide/admin_manage.php';
    $remArg[] = 'form';
    $remArg[] = 'id';
    return add_query_arg($addArg, remove_query_arg($remArg, 'http://zombicide.jhugues.fr/wp-admin/admin.php'));
  }
  /**
   * @return bool
   */
  public static function isAdmin()
  { return current_user_can('manage_options'); }
  /**
   * @return string
   */
  public function getShellClass()
  { return $this->shellClass; }
  /**
   * @param string $id
   * @param string $default
   * @return mixed
   */
  public function initVar($id, $default='')
  {
    if (isset($_POST[$id])) {
      return $_POST[$id];
    }
    if (isset($_GET[$id])) {
      return $_GET[$id];
    }
    return $default;
  }

}
<file_sep>/core/bean/WpPageError404Bean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe WpPageError404Bean
 * @author Hugues
 * @since 1.0.00
 * @version 1.07.20
 */
class WpPageError404Bean extends WpPageBean
{
  /**
   * @param WpPage $WpPage
   * @return string
   */
  public static function getStaticPageContent($WpPage='')
  {
    $Bean = new WpPageError404Bean($WpPage);
    return $Bean->getContentPage();
  }
  /**
   * @return string
   */
  public function getContentPage()
  {
    return '<section id="page-live-spawn">Cette page ne peut pas être trouvée. Peut-être devriez vous retourner sur la Home.</section>';
  }
}
<file_sep>/core/bean/AdminPageSurvivorsBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * AdminPageSurvivorsBean
 * @author Hugues
 * @since 1.05.01
 * @version 1.05.10
 */
class AdminPageSurvivorsBean extends AdminPageBean
{
  protected $urlFragmentSurvSkillTabContent = 'web/pages/admin/fragments/fragment-survivor-skills-tabcontent.php';
  protected $tplHomeCheckCard  = 'web/pages/admin/fragments/home-check-card.php';
  protected $urlSurvivorListing = 'web/pages/admin/survivor-listing.php';
  protected $urlSurvivorEdit = 'web/pages/admin/survivor-edit.php';
  /**
   * Class Constructor
   */
  public function __construct($urlParams='')
  {
    $this->urlParams = $urlParams;
    parent::__construct(self::CST_SURVIVOR);
    $this->title = 'Survivants';
    $this->ExpansionServices     = new ExpansionServices();
    $this->SkillServices         = new SkillServices();
    $this->SurvivorServices      = new SurvivorServices();
    $this->SurvivorSkillServices = new SurvivorSkillServices();
  }
  /**
   * @param array $urlParams
   * @return $Bean
   */
  public function getSpecificContentPage()
  {
    if (isset($this->urlParams[self::FIELD_ID])) {
      $this->Survivor = $this->SurvivorServices->selectSurvivor($this->urlParams[self::FIELD_ID]);
    }
    if (isset($_POST)&&!empty($_POST)) {
      $this->dealWithPost();
    }
    switch ($this->urlParams[self::CST_POSTACTION]) {
      case 'confirmEdit'  :
      case self::CST_EDIT :
        return $this->getEditContentPage();
      break;
      default :
        return $this->getListContentPage();
      break;
    }
  }
  private function dealWithPost()
  {
    if ($this->urlParams[self::CST_POSTACTION]=='confirmEdit') {
      // Faudrait supprimer les données existantes.
      $SurvivorSkill = new SurvivorSkill();
      $SurvivorSkill->setSurvivorId($this->Survivor->getId());
      $this->SurvivorSkillServices->deleteBulkSurvivorSkill(array(self::FIELD_SURVIVORID=>$this->Survivor->getId()));
      foreach ($_POST as $key=>$value) {
        list($check, $survivorTypeId, $tagLevelId) = explode('_', $key);
        if ($check!='ss' || $value=='') {
          continue;
        }
        $SurvivorSkill->setSkillId($value);
        $SurvivorSkill->setSurvivorTypeId($survivorTypeId);
        $SurvivorSkill->setTagLevelId($tagLevelId);
        $this->SurvivorSkillServices->insertSurvivorSkill($SurvivorSkill);
      }
    }
  }
  public function getListContentPage()
  {
    //////////////////////////////////////////////////////////////////
    // On récupère les filtres éventuels.
    $argFilters = array();
    $this->expansionId = isset($_POST[self::FIELD_EXPANSIONID]) ? $_POST[self::FIELD_EXPANSIONID] : '';
    $argFilters[self::FIELD_EXPANSIONID] = $this->expansionId;

    //////////////////////////////////////////////////////////////////
    $strRows = '';
    $nbPerPage = 10;
    $orderby = $this->initVar(self::WP_ORDERBY, self::FIELD_NAME);
    $order = $this->initVar(self::WP_ORDER, self::ORDER_ASC);
    $Survivors = $this->SurvivorServices->getSurvivorsWithFilters($argFilters, $orderby, $order);
    $nbElements = count($Survivors);
    $nbPages = ceil($nbElements/$nbPerPage);
    $curPage = $this->initVar(self::WP_CURPAGE, 1);
    $curPage = max(1, min($curPage, $nbPages));
    $DisplayedSurvivors = array_slice($Survivors, ($curPage-1)*$nbPerPage, $nbPerPage);
    while (!empty($DisplayedSurvivors)) {
      $Survivor = array_shift($DisplayedSurvivors);
      $strRows .= $Survivor->getBean()->getRowForAdminPage();
    }
    $queryArg = array(
      self::CST_ONGLET => self::CST_SURVIVOR,
      self::WP_ORDERBY => $orderby,
      self::WP_ORDER   => $order
    );
    //////////////////////////////////////////////////////////////////
    // Construction des filtres utilisés
    $lstFiltres = '';
    $Expansions = $this->ExpansionServices->getExpansionsWithFilters();
    $lstFiltres .= '<select name="expansionId"><option value="">Toutes les extensions</option>';
    while (!empty($Expansions)) {
      $Expansion = array_shift($Expansions);
      if ($Expansion->getNbSurvivants()==0) {
        continue;
      }
      $lstFiltres .= '<option value="'.$Expansion->getId().'" '.($this->expansionId==$Expansion->getId() ? 'selected' : '').'>'.$Expansion->getName().'</option>';
    }
    $lstFiltres .= '</select>';


    //////////////////////////////////////////////////////////////////

    //////////////////////////////////////////////////////////////////
    // Pagination
    $strPagination = $this->getPagination($queryArg, $post_status, $curPage, $nbPages, $nbElements);
    // Tris
    $queryArg[self::WP_ORDERBY] = self::FIELD_NAME;
    $queryArg[self::WP_ORDER] = ($orderby==self::FIELD_NAME && $order==self::ORDER_ASC ? self::ORDER_DESC : self::ORDER_ASC);
    $urlSortTitle = $this->getQueryArg($queryArg);
    //////////////////////////////////////////////////////////////////

    $args = array(
      // Liste des survivants affichés - 1
      $strRows,
      // Liste des Filtres utilisés - 2
      $lstFiltres,
      // Lien pour ajouter un nouveau Survivant - 3
      '/wp-admin/post-new.php',
      // 4
      '',
      // Pagination - 5
      $strPagination,
      // 6 & 7
      '','',
      // class pour le tri sur title - 8
      ($orderby==self::FIELD_NAME ? $order : self::ORDER_DESC),
      // url pour le tri sur title - 9
      $urlSortTitle,
      '','','','','','','','','','','','','','','','','','','','','','',''
    );
    return $this->getRender($this->urlSurvivorListing, $args);
  }

  private function getOption($value, $name, $selection='')
  { return '<option value="'.$value.'"'.($value==$selection ? ' selected' : '').'>'.$name.'</option>'; }

  private function getSkillSelect($name, $id='')
  {
    $strReturned = $this->getOption('', 'Aucune', $id);
    $Skills = $this->Skills;
    while (!empty($Skills)) {
      $Skill = array_shift($Skills);
      $strReturned .= $this->getOption($Skill->getId(), $Skill->getName(), $id);
    }
    return '<select name="'.$name.'">'.$strReturned.'</select>';
  }
  public function getEditContentPage()
  {
    $msgError = "Ce Survivant n'a pas de profil de ce type. Il n'est donc pas possible de sélectionner des compétences.";
    $this->Skills = $this->SkillServices->getSkillsWithFilters();

    $args = array(
    // Le Survivant a-t-il un profil Standard ? - 1
    (!$this->Survivor->isStandard() ? $msgError : $this->getListSelects(self::CST_SURVIVORTYPEID_S)),
    // Le Survivant a-t-il un profil Zombivant ? - 2
    (!$this->Survivor->isZombivor() ? $msgError : $this->getListSelects(self::CST_SURVIVORTYPEID_Z)),
    // Le Survivant a-t-il un profil Ultimate ? - 3
    (!$this->Survivor->isUltimate() ? $msgError : $this->getListSelects(self::CST_SURVIVORTYPEID_U)),
    // Le Survivant a-t-il un profil Ultimate Zombivant ? - 4
    (!$this->Survivor->isUltimatez() ? $msgError : $this->getListSelects(self::CST_SURVIVORTYPEID_UZ)),
    // L'identifiant du Survivant - 5
    $this->Survivor->getId(),
    // Le nom du Survivant - 6
    $this->Survivor->getName(),
    // A-t-il un profil Standard ? - 7
    ($this->Survivor->isStandard() ? ' '.self::CST_CHECKED : ''),
    // A-t-il un profil Standard ? - 8
    ($this->Survivor->isZombivor() ? ' '.self::CST_CHECKED : ''),
    // A-t-il un profil Standard ? - 9
    ($this->Survivor->isUltimate() ? ' '.self::CST_CHECKED : ''),
    // A-t-il un profil Standard ? - 10
    ($this->Survivor->isUltimatez() ? ' '.self::CST_CHECKED : ''),
    // Extension d'origine du Survivant - 11
    $this->Survivor->getExpansion()->getName(),
    // Liste des portraits - 12
    $this->Survivor->getBean()->getAllPortraits(false),
    );

    return $this->getRender($this->urlSurvivorEdit, $args);
  }
  private function getListSelects($survivorTypeId)
  {
    $tagLevelIds = array('10', '11', '20', '30', '31', '40', '41', '42');
    $args = array();
    while (!empty($tagLevelIds)) {
      $levelId = array_shift($tagLevelIds);
      $name = 'ss_'.$survivorTypeId.'_'.$levelId;
      array_push($args, $this->getSkillSelect($name, $this->Survivor->getSkill($survivorTypeId, $levelId)->getId()));
    }
    return  $this->getRender($this->urlFragmentSurvSkillTabContent, $args);
  }

  /**
   * @return string
   */
  public function getCheckCard()
  {
    /////////////////////////////////////////////////
    // Gestion des Survivants.
    // On récupère la liste des Survivants qui ont un Article. Puis les données dans la base. On compare et on effectue un diagnostic.
    $Act = new SurvivorActions();
    $strBilan  = $Act->dealWithSurvivorVerif();

    $args = array(
      // Le titre de la carte - 1
      $this->title,
      // L'id du container de retour pour afficher les vérifications - 2
      self::CST_SURVIVOR,
      // Le contenu du container de vérification - 3
      $strBilan,
   );
    return $this->getRender($this->tplHomeCheckCard, $args);
  }
}
<file_sep>/core/interface/ConstantsInterface.php
<?php
/**
 * @author Hugues
 * @since 1.00.00
 * @version 1.07.25
 */
interface ConstantsInterface
{
  /////////////////////////////////////////////////
  // Icons : https://pngtree.com/free-icon/backpack-management_577946
  // Action Ajax
  const AJAX_ADDMORENEWS    = 'addMoreNews';
  const AJAX_CARDRIVE       = 'carDrive';
  const AJAX_CARIN          = 'carIn';
  const AJAX_CAROUT         = 'carOut';
  const AJAX_CARSWAP        = 'carSwap';
  const AJAX_ENDOFTURN      = 'endOfTurn';
  const AJAX_GETEXPANSIONS  = 'getExpansions';
  const AJAX_EXPANSIONVERIF = 'expansion-verif';
  const AJAX_GETRANDOMMAP   = 'getRandomMap';
  const AJAX_GETMISSIONS    = 'getMissions';
  const AJAX_GETRANDOMTEAM  = 'getRandomTeam';
  const AJAX_GETSKILLS      = 'getSkills';
  const AJAX_GETSURVIVORS   = 'getSurvivants';
  const AJAX_GETTILES       = 'getTiles';
  const AJAX_MAKENOISE      = 'makeNoise';
  const AJAX_MELEEATTACK    = 'meleeAttack';
  const AJAX_MISSIONVERIF   = 'mission-verif';
  const AJAX_MOVE           = 'move';
  const AJAX_OPENDOOR       = 'openDoor';
  const AJAX_ORGANIZE       = 'organize';
  const AJAX_PAGED          = 'paged';
  const AJAX_POSTCHAT       = 'postChat';
  const AJAX_RANGEDATTACK   = 'rangedAttack';
  const AJAX_REFRESHCHAT    = 'refreshChat';
  const AJAX_SEARCH         = 'search';
  const AJAX_SKILLVERIF     = 'skill-verif';
  const AJAX_STARTTURN      = 'startTurn';
  const AJAX_SURVIVORVERIF  = 'survivor-verif';
  const AJAX_TRADE          = 'trade';
  const AJAX_GETTHROWDICE   = 'getThrowDice';

  /////////////////////////////////////////////////
  // Attributs
  const ATTR_ALT               = 'alt';
  const ATTR_CLASS             = 'class';
  const ATTR_HEIGHT            = 'height';
  const ATTR_HREF              = 'href';
  const ATTR_ID                = 'id';
  const ATTR_NAME              = 'name';
  const ATTR_SELECTED          = 'selected';
  const ATTR_SRC               = 'src';
  const ATTR_TITLE             = 'title';
  const ATTR_TYPE              = 'type';
  const ATTR_VALUE             = 'value';
  const ATTR_WIDTH             = 'width';
  // Data
  const ATTR_DATA_AJAXACTION   = 'data-ajaxaction';
  const ATTR_DATA_DISPLAYNAME  = 'data-displayname';
  const ATTR_DATA_EXPANSIONID  = 'data-expansion-id';
  const ATTR_DATA_ID           = 'data-id';
  const ATTR_DATA_KEYDECK      = 'data-keydeck';
  const ATTR_DATA_PAGED        = 'data-paged';
  const ATTR_DATA_SURVIVORID   = 'data-survivor-id';
  const ATTR_DATA_TIMESTAMP    = 'data-timestamp';
  const ATTR_DATA_TYPE         = 'data-type';

  /////////////////////////////////////////////////
  // Chat
  const CHAT_CLEAN           = '/clean';
  const CHAT_EXIT            = '/exit';
  const CHAT_GAMES           = '/games';
  const CHAT_HELP            = '/help';
  const CHAT_INVITE          = '/invite';
  const CHAT_JOIN            = '/join';
  const CHAT_USERS           = '/users';
  const CHAT_ACTIVATEZOMBIES = '/activateZombies';

  /////////////////////////////////////////////////
  // Les niveaux de danger
  const COLOR_BLUE           = 'blue';
  const COLOR_ORANGE         = 'orange';
  const COLOR_RED            = 'red';
  const COLOR_YELLOW         = 'yellow';

  /////////////////////////////////////////////////
  // On conserve malgré tout quelques constantes
  const CST_ACTIVE            = 'active';
  const CST_AJAXACTION        = 'ajaxAction';
  const CST_CHANGEPROFILE     = 'changeProfile';
  const CST_CHECKED           = 'checked';
  const CST_CLONE             = 'clone';
  const CST_COLORDER          = 'colorder';
  const CST_COLSORT           = 'colsort';
  const CST_DISABLED          = 'disabled';
  const CST_EDIT              = 'edit';
  const CST_EXPANSION         = 'expansion';
  const CST_FILTERS           = 'filters';
  const CST_HIDDEN            = 'hidden';
  const CST_NBPERPAGE         = 'nbperpage';
  const CST_MISSION           = 'mission';
  const CST_ONGLET            = 'onglet';
  const CST_PARAMETRE         = 'parametre';
  const CST_POSTACTION        = 'postAction';
  const CST_SELECTED          = 'selected';
  const CST_SKILL             = 'skill';
  const CST_SURVIVOR          = 'survivor';
  const CST_SURVIVORTYPEID_S  = 1;
  const CST_SURVIVORTYPEID_Z  = 2;
  const CST_SURVIVORTYPEID_U  = 3;
  const CST_SURVIVORTYPEID_UZ = 4;
  const CST_TRASH             = 'trash';
  const CST_ULTIMATE          = 'ultimate';
  const CST_ULTIMATEZ         = 'ultimatez';
  const CST_ZOMBIVOR          = 'zombivor';

  /////////////////////////////////////////////////
  // Fields
  const FIELD_ID               = 'id';
  const FIELD_ACTIVETILE       = 'activeTile';
  const FIELD_BACKGROUND       = 'background';
  const FIELD_CODE             = 'code';
  const FIELD_COORDX           = 'coordX';
  const FIELD_COORDY           = 'coordY';
  const FIELD_DATEUPDATE       = 'dateUpdate';
  const FIELD_DECKKEY          = 'deckKey';
  const FIELD_DESCRIPTION      = 'description';
  const FIELD_DISPLAYRANK      = 'displayRank';
  const FIELD_DURATIONID       = 'durationId';
  const FIELD_EQUIPMENTCARDID  = 'equipmentCardId';
  const FIELD_EXPANSIONID      = 'expansionId';
  const FIELD_HEIGHT           = 'height';
  const FIELD_KEYWORDID        = 'keywordId';
  const FIELD_LIVEABLE         = 'liveAble';
  const FIELD_LIVEID           = 'liveId';
  const FIELD_LEVELID          = 'levelId';
  const FIELD_MAXDURATION      = 'maxDuration';
  const FIELD_MINDURATION      = 'minDuration';
  const FIELD_MISSIONID        = 'missionId';
  const FIELD_NAME             = 'name';
  const FIELD_NBMISSIONS       = 'nbMissions';
  const FIELD_NBSURVIVANTS     = 'nbSurvivants';
  const FIELD_OBJECTIVEID      = 'objectiveId';
  const FIELD_OFFICIAL         = 'official';
  const FIELD_ORIGINEID        = 'origineId';
  const FIELD_PLAYERID         = 'playerId';
  const FIELD_PUBLISHED        = 'published';
  const FIELD_RULEID           = 'ruleId';
  const FIELD_SENDERID         = 'senderId';
  const FIELD_SENDTOID         = 'sendToId';
  const FIELD_SETTING          = 'setting';
  const FIELD_SKILLID          = 'skillId';
  const FIELD_SPAWNNUMBER      = 'spawnNumber';
  const FIELD_SURVIVORID       = 'survivorId';
  const FIELD_SURVIVORTYPEID   = 'survivorTypeId';
  const FIELD_TAGLEVELID       = 'tagLevelId';
  const FIELD_TEXTE            = 'texte';
  const FIELD_TILEID           = 'tileId';
  const FIELD_TIMESTAMP        = 'timestamp';
  const FIELD_TITLE            = 'title';
  const FIELD_ULTIMATE         = 'ultimate';
  const FIELD_WEAPONPROFILEID  = 'weaponProfileId';
  const FIELD_WIDTH            = 'width';
  const FIELD_ZOMBIVOR         = 'zombivor';

  /////////////////////////////////////////////////
  // Formats
  const FORMAT_DATE_YmdHis     = 'Y-m-d H:i:s';

  /////////////////////////////////////////////////
  // Identifiant DOM
  const ID_HEADER_UL_CHAT_SAISIE = 'header-ul-chat-saisie';
  const ID_ONLINE_CHAT_CONTENT   = 'online-chat-content';

  /////////////////////////////////////////////////
  // Labels
  const LBL_STANDARD             = 'Standard';
  const LBL_SURVIVANT            = 'Survivant';
  const LBL_ZOMBIVANT            = 'Zombivant';
  const LBL_ULTIMATE             = 'Ultimate';
  const LBL_ULTIMATEZOMBIVANT    = 'Ultimate Zombivant';

  /////////////////////////////////////////////////
  // Niveau de Danger
  const LVL_BLUE                 = 'blue';
  const LVL_YELLOW               = 'yellow';
  const LVL_ORANGE               = 'orange';
  const LVL_RED                  = 'red';

  /////////////////////////////////////////////////
  // Messages
  const MSG_CHAT_EMPTIED        = 'CHAT_EMPTIED';
  const MSG_CHAT_USER_LEFT      = 'CHAT_USER_LEFT';
  const MSG_CHAT_BACK_DEFAULT   = 'CHAT_BACK_DEFAULT';
  const MSG_CHAT_HELP           = 'CHAT_HELP';
  const MSG_CHAT_UNKNOWN_USER   = 'CHAT_UNKNOWN_USER';
  const MSG_CHAT_JOIN_INVITE    = 'CHAT_JOIN_INVITE';
  const MSG_CHAT_INVITE_SENT_TO = 'CHAT_INVITE_SENT_TO';
  const MSG_CHAT_USER_JOINED    = 'CHAT_USER_JOINED';

  /////////////////////////////////////////////////
  // Allowed Pages :
  const PAGE_ONLINE            = 'online';
  const PAGE_EQUIPMENT         = 'page-equipmentcards';
  const PAGE_EXTENSION         = 'page-extensions';
  const PAGE_GENERATION_MAP    = 'page-generation-map';
  const PAGE_MISSION           = 'page-missions';
  const PAGE_ORDRE_PRIORITE    = 'page-ordre-de-priorite';
  const PAGE_PISTE_DE_DES      = 'page-piste-de-des';
  const PAGE_SELECT_SURVIVORS  = 'page-selection-survivants';
  const PAGE_SKILL             = 'page-competences';
  const PAGE_SPAWN             = 'page-spawncards';
  const PAGE_SURVIVOR          = 'page-survivants';
  const PAGE_TILE              = 'page-dalles';

  /////////////////////////////////////////////////
  // Session
  const SESSION_DECKKEY        = 'deckKey';

  const SQL_WHERE              = 'where';

  /////////////////////////////////////////////////
  // Tags
  const TAG_A                  = 'a';
  const TAG_BUTTON             = 'button';
  const TAG_DD                 = 'dd';
  const TAG_DIV                = 'div';
  const TAG_DL                 = 'dl';
  const TAG_DT                 = 'dt';
  const TAG_I                  = 'i';
  const TAG_IMG                = 'img';
  const TAG_LI                 = 'li';
  const TAG_OPTION             = 'option';
  const TAG_SELECT             = 'select';
  const TAG_SPAN               = 'span';
  const TAG_UL                 = 'ul';

  /////////////////////////////////////////////////
  // Constantes XML
  const XML_ATTRIBUTES         = '@attributes';

  /////////////////////////////////////////////////
  // Wordpress
  const WP_CAT                 = 'cat';
  /*
  const WP_CAT_EXPANSION_ID    = 77;
  const WP_CAT_MISSION_ID      = 2;
  const WP_CAT_NEWS_ID         = 54;
  const WP_CAT_OBJECTIVE_ID    = 71;
  const WP_CAT_RULE_ID         = 72;
  const WP_CAT_SKILL_ID        = 75;
  const WP_CAT_SURVIVOR_ID     = 58;
   */
  const WP_CAT_EXPANSION_ID    = 111;
  const WP_CAT_MISSION_ID      = 114;
  const WP_CAT_NEWS_ID         = 117;
  const WP_CAT_OBJECTIVE_ID    = 115;
  const WP_CAT_RULE_ID         = 116;
  const WP_CAT_SKILL_ID        = 110;
  const WP_CAT_SURVIVOR_ID     = 119;
  const WP_CURPAGE             = 'cur_page';
  const WP_FIELD               = 'field';
  const WP_METAKEY             = 'meta_key';
  const WP_METAVALUE           = 'meta_value';
  const WP_NUMBERPOSTS         = 'numberposts';
  const WP_OFFSET              = 'offset';
  const WP_ORDER               = 'order';
  const WP_ORDERBY             = 'orderby';
  const WP_POST                = 'post';
  const WP_POSTSPERPAGE        = 'posts_per_page';
  const WP_POSTSTATUS          = 'post_status';
  const WP_POSTTAG             = 'post_tag';
  const WP_POSTTITLE           = 'post_title';
  const WP_POSTTYPE            = 'post_type';
  const WP_PUBLISH             = 'publish';
  const WP_SLUG                = 'slug';
  const WP_TAXONOMY            = 'taxonomy';
  const WP_TAXQUERY            = 'tax_query';
  const WP_TERMS               = 'terms';

  /////////////////////////////////////////////////
  // Divers
  const IMG_PNG                = '.png';
  const ORDER_ASC              = 'ASC';
  const ORDER_DESC             = 'DESC';
  const ORDER_RAND             = 'rand';



  /////////////////////////////////////////////////
  // Deprecated
  const CST_TIMESTAMP          = 'timestamp';




  /**
   * Chaîne Constante coordY
   */
  const CST_COORDY          = 'coordY';
  /**
   * Chaîne Constante cur_page
   */
  const CST_CURPAGE         = 'cur_page';
  /**
   * Chaîne Constante current
   */
  const CST_CURRENT         = 'current';
  /**
   * Chaîne Constante danger
   */
  const CST_DANGER          = 'danger';
  /**
   * Chaîne Constante description
   */
  const CST_DESCRIPTION     = 'description';
  /**
   * Chaîne Constante durationId
   */
  const CST_DURATIONID      = 'durationId';
  /**
   * Chaîne Constante equipment
   */
  const CST_EQUIPMENT       = 'equipment';
  /**
   * Chaîne Constante equipmentCardId
   */
  const CST_EQUIPMENTCARDID = 'equipmentCardId';
  /**
   * Chaîne Constante Y-m-d H:i:s
   */
  const CST_FORMATDATE      = 'Y-m-d H:i:s';
  /**
   * Chaîne Constante form-control
   */
  const CST_FORMCONTROL     = 'form-control';
  /**
   * Chaîne Constante firstRow
   */
  const CST_FIRSTROW        = 'firstRow';
  /**
   * Chaîne Constante future
   */
  const CST_FUTURE          = 'future';
  /**
   * Chaîne Constante keyAccess
   */
  const CST_KEYACCESS       = 'keyAccess';
  /**
   * Chaîne Constante level
   */
  const CST_LEVEL           = 'level';
  /**
   * Chaîne Constante levelId
   */
  const CST_LEVELID         = 'levelId';
  /**
   * Chaîne Constante liveDeckId
   */
  const CST_LIVEDECKID      = 'liveDeckId';
  /**
   * Chaîne Constante liveId
   */
  const CST_LIVEID          = 'liveId';
  /**
   * Chaîne Constante liveSurvivorId
   */
  const CST_LIVESURVIVORID  = 'liveSurvivorId';
  /**
   * Chaîne Constante minDuration
   */
  const CST_MINDURATION     = 'minDuration';
  /**
   * Chaîne Constante missionId
   */
  const CST_MISSIONID       = 'missionId';
  /**
   * Chaîne Constante name
   */
  const CST_NAME            = 'name';
  /**
   * Chaîne Constante nbMissions
   */
  const CST_NBMISSIONS      = 'nbMissions';
  /**
   * Chaîne Constante objective
   */
  const CST_OBJECTIVE       = 'objective';
  /**
   * Chaîne Constante order
   */
  const CST_ORDER           = 'order';
  /**
   * Chaîne Constante orderby
   */
  /**
   * Chaîne Constante origineId
   */
  const CST_ORIGINEID       = 'origineId';
  /**
   * Chaîne Constante pending
   */
  const CST_PENDING         = 'pending';
  /**
   * Chaîne Constante playerId
   */
  const CST_PLAYERID        = 'playerId';
  /**
   * Chaîne Constante post_status
   */
  const CST_POSTSTATUS      = 'post_status';
  /**
   * Chaîne Constante publish
   */
  const CST_PUBLISH         = 'publish';
  /**
   * Chaîne Constante published
   */
  const CST_PUBLISHED       = 'published';
  /**
   * Chaîne Constante rmvCol
   */
  const CST_RMVCOL          = 'rmvCol';
  /**
   * Chaîne Constante rmvRow
   */
  const CST_RMVROW          = 'rmvRow';
  /**
   * Chaîne Constante selected
   */
  /**
   * Chaîne Constante sendToId
   */
  const CST_SENDTOID        = 'sendToId';
  /**
   * Chaîne Constante setting
   */
  const CST_SETTING         = 'setting';
  /**
   * Chaîne Constante spawn
   */
  const CST_SPAWN           = 'spawn';
  /**
   * Chaîne Constante spawnNumber
   */
  const CST_SPAWNNUMBER     = 'spawnNumber';
  /**
   * Chaîne Constante square pointer
   */
  const CST_SQUAREPOINTER   = 'square pointer';
  /**
   * Chaîne Constante status
   */
  const CST_STATUS          = 'status';
  /**
   * Chaîne Constante success
   */
  const CST_SUCCESS         = 'success';
  /**
   * Chaîne Constante survivorTypeId
   */
  const CST_SURVIVORTYPEID  = 'survivorTypeId';
  /**
   * Chaîne Constante table
   */
  const CST_TABLE           = 'table';
  /**
   * Chaîne Constante tagLevelId
   */
  const CST_TAGLEVELID      = 'tagLevelId';
  /**
   * Chaîne Constante </td><td>
   */
  const CST_TD_SEP          = '</td><td>';
  /**
   * Chaîne Constante texte
   */
  const CST_TEXTE           = 'texte';
  /**
   * Chaîne Constante upload_files
   */
  const CST_UPLOADFILES     = 'upload_files';
  /**
   * Chaîne Constante window-close
   */
  const CST_WINDOWCLOSE     = 'window-close';
}
<file_sep>/core/bean/WpPageExpansionsBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe WpPageExpansionsBean
 * @author Hugues
 * @since 1.07.21
 * @version 1.07.21
 */
class WpPageExpansionsBean extends WpPageBean
{
  protected $urlTemplate = 'web/pages/public/wppage-expansions.php';
  /**
   * Class Constructor
   * @param WpPage $WpPage
   */
  public function __construct($WpPage='')
  {
    parent::__construct($WpPage);
    $this->ExpansionServices = new ExpansionServices();
  }
  /**
   * @return string
   */
  public function getContentPage()
  {
    $this->setFilters();
    return $this->getListContentPage();
  }
  /**
   * @return string
   */
  public function getListContentPage()
  {
    /////////////////////////////////////////////////////////////////////////////
    // On récupère la liste des Extensions puis les éléments nécessaires à la pagination.
    $Expansions = $this->ExpansionServices->getExpansionsWithFilters($this->arrFilters, $this->colSort, $this->colOrder);
    $this->nbElements = count($Expansions);
    $this->nbPages = ceil($this->nbElements/$this->nbperpage);
    // On slice la liste pour n'avoir que ceux à afficher
    $displayedExpansions = array_slice($Expansions, $this->nbperpage*($this->paged-1), $this->nbperpage);
    // On construit le corps du tableau
    $strBody = '';
    if (!empty($displayedExpansions)) {
      foreach ($displayedExpansions as $Expansion) {
        $strBody .= $Expansion->getBean()->getRowForPublicPage();
      }
    }
    /////////////////////////////////////////////////////////////////////////////

    /////////////////////////////////////////////////////////////////////////////
    // Affiche-t-on le filtre ?
    $showFilters = isset($this->arrFilters[self::FIELD_NAME])&&$this->arrFilters[self::FIELD_NAME]!='';
    /////////////////////////////////////////////////////////////////////////////

    //////////////////////////////////////////////////////////////////
    // On enrichi le template puis on le restitue.
    $args = array(
      // Les lignes du tableau - 1
      $strBody,
      // On affiche le dropdown par pages - 2
      $this->getDropdownNbPerPages(),
      // On affiche la pagination - 3
      $this->getNavPagination(),
      // Affiche ou non le bloc filtre - 4
      $showFilters ? 'block' : 'none',
      // Si le Nom est renseigné - 5
      $this->arrFilters[self::FIELD_NAME],
      '','','','','','','','','','','','','','','','','',''
    );
    return $this->getRender($this->urlTemplate, $args);
  }

  /**
   * @param array $post
   */
  public function setFilters($post=null)
  { parent::setBeanFilters($post, self::FIELD_NAME); }

}
<file_sep>/web/rsc/admin_zombicide.js
/*
 * @version 1.04.30
 */
var $hj = jQuery;
$hj(document).ready(function(){
  /***************
   *** #01 - Home
   *** Si on est sur la Homepage
   ***************/
  $hj('#homeAdminBoard .ajaxAction').unbind().click(function(){
    resolveHomeAdminBoardAjaxActions($hj(this));
    return false;
  });
});

function resolveHomeAdminBoardAjaxActions(clicked) {
  var ajaxaction = clicked.data('ajaxaction');
  // On initialise les données de tri et de filtres.
  var data = {'action': 'dealWithAjax', 'ajaxAction': ajaxaction};
  resolveCallAjax(data, ajaxaction);
}

function resolveCallAjax(data, idPage) {
  $hj.post(
    ajaxurl,
    data,
    function(response) {
      try {
        var obj = JSON.parse(response);
        if (obj[idPage] != '' ) {
          $hj('#'+idPage).html(obj[idPage]);
        }
      } catch (e) {
        console.log("error: "+e);
        console.log(response);
      }
    }
  );
}
<file_sep>/core/bean/WpPostSkillBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe WpPostSkillBean
 * @author Hugues
 * @since 1.00.00
 * @version 1.05.20
 */
class WpPostSkillBean extends WpPostBean
{
  protected $urlTemplate = 'web/pages/public/wppage-skill.php';
  protected $arrLvls = array(1=>'S', 2=>'Z', 3=>'U', 4=>'UZ');

  /**
   * Class Constructor
   */
  public function __construct($WpPost)
  {
    parent::__construct();
    $this->SkillServices         = new SkillServices();
    $this->SurvivorServices      = new SurvivorServices();
    $this->SurvivorSkillServices = new SurvivorSkillServices();
    // TODO : Quand toutes les compétences auront leur article, on peut virer ce test et l'exécution secondaire.
    if ($WpPost instanceof WpPost) {
      $this->WpPost = $WpPost;
      $code = $this->WpPost->getPostMeta(self::FIELD_CODE);
      $Skills = $this->SkillServices->getSkillsWithFilters(array(self::FIELD_CODE=>$code));
      $this->Skill = (!empty($Skills) ? array_shift($Skills) : new Skill());
    } else {
      $this->Skill = $this->SkillServices->selectSkill($WpPost);
    }
  }
  /**
   * On retourne la page dédiée à la compétence.
   * @return string
   */
  public function getContentPage()
  {
    //////////////////////////////////////////////////////////////////
    // On enrichi les tableaux de données nécessaires.
    $arrF = array(self::FIELD_SKILLID => $this->Skill->getId());
    $arrTags = array(
      self::COLOR_BLUE   => array(10, 11),
      self::COLOR_YELLOW => array(20),
      self::COLOR_ORANGE => array(30, 31),
      self::COLOR_RED    => array(40, 41, 42),
    );
    //////////////////////////////////////////////////////////////////
    // On construit le tableau nécessaire au listing des Survivants
    foreach ($arrTags as $key => $value) {
      while (!empty($value)) {
        $val = array_shift($value);
        $arrF[self::FIELD_TAGLEVELID] = $val;
        foreach ($this->arrLvls as $k => $v) {
          $arrF[self::FIELD_SURVIVORTYPEID] = $k;
          $SurvivorSkills = $this->SurvivorSkillServices->getSurvivorSkillsWithFilters($arrF);
          foreach ($SurvivorSkills as $SurvivorSkill) {
            $Survivor = $SurvivorSkill->getSurvivor();
            $this->skills[$key][$k][$Survivor->getNiceName()] = $Survivor;
          }
          if (!empty($this->skills[$key][$k])) {
            ksort($this->skills[$key][$k]);
          }
        }
      }
    }

    //////////////////////////////////////////////////////////////////
    // On enrichi le template puis on le restitue.
    $args = array(
      // Nom de la Compétence - 1
      $this->Skill->getName(),
      // Description de la Compétence - 2
      $this->Skill->getDescription(),
      // Liste des Survivants ayant la compétence en Bleu (Zombivant et Ultimate compris) - 3
      $this->buildSkillBadges(1, $arrTags),
      // Liste des Survivants ayant la compétence en Jaune (Zombivant et Ultimate compris) - 4
      $this->buildSkillBadges(2, $arrTags),
      // Liste des Survivants ayant la compétence en Orange (Zombivant et Ultimate compris) - 5
      $this->buildSkillBadges(3, $arrTags),
      // Liste des Survivants ayant la compétence en Rouge (Zombivant et Ultimate compris) - 6
      $this->buildSkillBadges(4, $arrTags),
      // Lien de navigation - 7
      $this->getNavLinks(),
    );
    return $this->getRender($this->urlTemplate, $args);
  }
  private function getNavLinks()
  {
    //////////////////////////////////////////////////////////////////
    // On construit les liens de navigation
    // On récupère toutes les compétences, classées par ordre alphabétique.
    $Skills = $this->SkillServices->getSkillsWithFilters();
    $firstSkill = null;
    while (!empty($Skills)) {
      $Skill = array_shift($Skills);
      // On les parcourt jusqu'à trouver la courante.
      if ($Skill->getId()==$this->Skill->getId()) {
        break;
      }
      if ($firstSkill==null) {
        $firstSkill = $Skill;
      }
      $prevSkill = $Skill;
    }
    $nextSkill = array_shift($Skills);
    if (empty($prevSkill)) {
      $prevSkill = array_pop($Skills);
    }
    if (empty($nextSkill)) {
      $nextSkill = $firstSkill;
    }

    $nav = '';
    // On exploite la précédente et la suivante.
    if (!empty($prevSkill)) {
      $attributes = array(self::ATTR_HREF=>$prevSkill->getWpPost()->getPermalink(), self::ATTR_CLASS=>'adjacent-link col-3');
      $nav .= $this->getBalise(self::TAG_A, '&laquo; '.$prevSkill->getWpPost()->getPostTitle(), $attributes);
    }
    if (!empty($nextSkill)) {
      $attributes = array(self::ATTR_HREF=>$nextSkill->getWpPost()->getPermalink(), self::ATTR_CLASS=>'adjacent-link col-3');
      $nav .= $this->getBalise(self::TAG_A, $nextSkill->getWpPost()->getPostTitle().' &raquo;', $attributes);
    }
    return $nav;
  }
  private function buildSkillBadges($rank, $arrTags)
  {
    $strReturned = '';
    foreach ($arrTags as $key=>$value) {
      $cartoucheAttributes = array(self::ATTR_CLASS=>'cartouche badge badge-'.$key.'-skill');
      $Survivors = $this->skills[$key][$rank];
      if (!empty($Survivors)) {
        ksort($Survivors);
        while (!empty($Survivors)) {
          $Survivor = array_shift($Survivors);
          $strReturned .= $Survivor->getBean()->getCartouche($cartoucheAttributes, true);
        }
        $strReturned .= '<br>';
      }
    }
    return $strReturned;
  }
}
<file_sep>/web/rsc/non-used-yet-anymore.js
  /***************
   *** More News
   ***************

  if ($hj('#page-missions').length!=0 ) {
    $hj('#page-missions .ajaxAction').unbind().click(function(){
      addPageMissionAjaxActions($hj(this));
      return false;
    });
  }


  if ($hj('#filters').length!=0 ) {
    $hj('#filters select').unbind().change(function(){
      var set = $hj(this).val();
      $hj('#card-container .card').each(function(){
        if (set == '' || $hj(this).hasClass(set) ) {
          $hj(this).css('display', 'inline-block');
        } else {
          $hj(this).css('display', 'none');
        }
      })
    });
    $hj('#idReset').unbind().click(function(){
      $hj('#filters select').val('');
      $hj('#card-container .card').each(function(){
        $hj(this).css('display', 'inline-block');
      });
      return false;
    });
  }

  if ($hj('#page-live-spawn').length!=0 ) {
    addPageLiveSpawnActions();
    return false;
  }
  if ($hj('#page-live-equipment').length!=0 ) {
    addPageLiveEquipmentActions();
    return false;
  }

  if ($hj('#page-online').length!=0 ) {
    $hj('#startGame').unbind().click(function(){ $hj('form')[0].submit(); });

    // Sélection de la Mission
    $hj('.btn.btn-mission').unbind().click(function(){
      var missionId = $hj(this).data('mission-id');
      $hj(this).find('input').prop('checked', true);
      $hj(this).siblings().removeClass('active');
      $hj(this).toggleClass('active');
      $hj(this).siblings().find('svg').removeClass('fa-check-square').addClass('fa-square');
      $hj(this).find('svg').removeClass('fa-square').addClass('fa-check-square');
    });

    // Sélection des Survivants
    $hj('.btn.btn-survivor').unbind().click(function(){
      var survivorId = $hj(this).data('survivor-id');
      var isChecked = $hj(this).find('input').prop('checked');
      $hj(this).find('input').prop('checked', !isChecked);
      $hj(this).toggleClass('active');
      $hj(this).find('svg').toggleClass('fa-square fa-check-square');
    });

  }
  if ($hj('#canvas-background').length !=0 ) {
    $hj('canvas').drawImage({
      source: srcImg,
      x: xStart, y: yStart
    });
  }

  $hj('.menuBurger').click(function(){
    $hj(this).toggleClass('open');
  });













  /********
   * Filtres sur l'interface Survivors
   ********
  $hj('.survivorFilters a').unbind().click(function() {
    if (!$hj(this).hasClass('selected') ) {
      var exp = $hj(this).data('exp');
      var nb = $hj('article[data-exp="'+exp+'"]').length;
      if (nb==0 ) { getSurvivorsByExpansionCode(exp); }
    }
    $hj(this).toggleClass('selected');
    filterSurvivors();
  });

  $hj('.survivorFilters select').unbind().change(function() {
    filterSurvivors();
  });

  $hj('.survivorTool .selectSkill a').unbind().click(function() {
    var exp = $hj(this).data('exp');
    var txt = $hj(this).html();
    var strLi  = '<li class=\\"active\\"><article><input type=\\"checkbox\\" checked=\\"checked\\" name=\\"cb-sk-'+exp+'\\" id=\\"cb-sk-'+exp+'\\" class=\\"hidden\\">';
    strLi += '<i class=\\"glyphicon glyphicon-check pull-right\\"></i><span>'+txt+'</span></article></li>';
    var json = '{"selectionSkills":"'+strLi+'"}';
    var obj = JSON.parse(json);
    reloadComponents(obj, 'append');
    $hj('#selectionSkills i').unbind().click(function() {
      $hj(this).parent().parent().remove();
    })
  });

  $hj('.survivorTool .selectSurvivor a').unbind().click(function() {
    var exp = $hj(this).data('exp');
    if (!$hj(this).hasClass('selected') ) {
      var nb = $hj('article[data-exp="'+exp+'"]').length;
      if (nb!=0 ) {
        $hj('article[data-exp="'+exp+'"]').each(function(e) {
          $hj(this).parent().remove();
        });
      }
      getSurvivorsByExpansionCode(exp, 'cartouche');
    } else {
      $hj('article[data-exp="'+exp+'"]').each(function() {
        $hj(this).parent().remove();
        /*
        $hj(this).parent().removeClass('active');
        var inpSib = $hj(this).find('input');
        inpSib.prop("checked", !inpSib.prop("checked"))
        *
      })
    }
    $hj(this).toggleClass('selected');
  });

  $hj('#generateTeam').click(function(e) {
    e.preventDefault();
    var obj;
    var data = {'action': 'dealWithAjax', 'ajaxAction': 'getTeam', 'filters': $hj('#filters').serialize()};
    $hj.post(
      ajaxurl,
      data,
      function(response) {
        try {
          obj = JSON.parse(response);
        } catch (e) {
          console.log("error: "+e);
          console.log(response);
        }
        reloadComponents(obj, 'replace');
      }
    );
    return false;
  });

  $hj('#exportSelectionTeam').click(function(e) {
    var data = '';
    $hj('#selectionSurvivors input').each(function(e) {
      if ($hj(this).is(':checked') ) {
        if (data !='' ) {
        data += ';';
        }
        data += $hj(this).attr('name').substr(8);
      }
    });
    var filename = 'selectionTeam.txt';
    var file = new Blob([data], {type: 'text'});
    if (window.navigator.msSaveOrOpenBlob) // IE10+
        window.navigator.msSaveOrOpenBlob(file, filename);
    else { // Others
        var a = document.createElement("a"),
                url = URL.createObjectURL(file);
        a.href = url;
        a.download = filename;
        document.body.appendChild(a);
        a.click();
        setTimeout(function() {
            document.body.removeChild(a);
            window.URL.revokeObjectURL(url);
        }, 0);
    }
    return false;
  });

  $hj('#importSelectionTeam').change(function(e) {
    var obj;
    var data = {'action': 'dealWithAjax', 'ajaxAction': 'getSurvivorsForImport', 'value': $hj(this).val()};
    $hj.post(
      ajaxurl,
      data,
      function(response) {
        try {
          obj = JSON.parse(response);
        } catch (e) {
          console.log("error: "+e);
          console.log(response);
        }
        reloadComponents(obj, 'replace');
        $hj('#selectionSurvivors i').unbind().click(function() {
          $hj(this).parent().parent().toggleClass('active');
          var inpSib = $hj(this).siblings('input');
          inpSib.prop("checked", !inpSib.prop("checked"))
        });
      }
    );
  });

  /********
   * Filtres sur l'interface Equipements
   ********
  $hj('.equipmentFilters select').unbind().change(function() {
    if ($hj(this).hasClass('zoom') ) {
      $hj('.batchArticles.equipments').removeClass().addClass('batchArticles equipments '+$hj(this).val());
    } else {
      filterCards();
    }
  });
  $hj('.invasionFilters select').unbind().change(function() {
    if ($hj(this).hasClass('zoom') ) {
      $hj('.batchArticles.invasions').removeClass().addClass('batchArticles invasions '+$hj(this).val());
    } else {
      filterCards();
    }
  });

  /********
   * Filtres sur l'interface Skills
   ********
  $hj('input.chosen-search-input-skill').keyup(function() {
    var filter = $hj('input.chosen-search-input-skill').val().toLowerCase();
    $hj('.batchArticles.skills article').each(function() {
      var value = $hj(this).data('title');
      if (value.toLowerCase().indexOf(filter)!=-1 ) {
        $hj(this).show();
      } else {
        $hj(this).hide();
      }
    });
  });

  /********
   * Filtres sur l'interface Missions
   ********
  $hj('.missionFilters a').unbind().click(function() {
    var isSelected = $hj(this).hasClass('selected');
    if (isSelected ) {
      if ($hj(this).parent().parent().find('a.selected').length == 1 ) {
        $hj(this).parent().siblings('li').find('a[data-filter="all"]').addClass('selected');
      }
    } else {
      $hj(this).parent().siblings('li').find('a[data-filter="all"]').removeClass('selected');
    }
    $hj(this).toggleClass('selected');
    filterMissions();
  });

  $hj('div.chosen-container.chosen-container-single a')
    .unbind()
    .click(
      function(){
        fillOptionsList();
        $hj(this).parent().toggleClass('chosen-with-drop chosen-container-active');
        $hj('ul.chosen-results li.result-selected').addClass('highlighted');
        $hj('input.chosen-search-input').val('').focus();
      }
    );
  addActionsOnList();
  $hj('input.chosen-search-input').keyup(function() { fillOptionsList(); });

  /***************
   *** Mission Online
   ***************
  $hj('#genKey').click(function(e){
    e.preventDefault();
    var n = 16;
    var str = 'az<KEY>jkmwxcvbnAZERTYUPQSDFGHJKMWXCVBN23456789';
    var max = str.length;
    var password = '';
    for (var i=1; i<=n; i++ ) {
      var start = Math.floor(Math.random()*max);
      password += str.substring(start, start+1);
    }
    $hj('#deckKey').val(password);
  });

  $hj('.slideinfo').click(function(){
    if ($hj(this).hasClass('show') ) {
      $hj('.slideinfo').removeClass('show');
    } else {
      $hj('.slideinfo').removeClass('show');
      $hj(this).addClass('show');
    }
  });

  $hj('#liveMissionSelection li input').click(function(){
    var id = $hj(this).val();
    $hj('.hideAllMaps > li').hide();
    $hj('.hideAllMaps > li[data-missionId="'+id+'"]').show();
  });

  $hj('#filter-expansion').change(function(){
    var filtre = $hj(this).val();
    $hj('#liveSurvivorsSelection li').each(function(){
      if ($hj(this).hasClass(filtre) || filtre=='' ) {
        $hj(this).show();
      } else {
        $hj(this).hide();
      }
    });
  });

  $hj('#liveSurvivorsSelection input').change(function(){
    $hj('#nbSurvivorsSelected').html($hj('#liveSurvivorsSelection input:checked').length);
  });

  $hj('#randomSelect').unbind().click(function(e){
    e.preventDefault();
    var nb = 6-$hj('#liveSurvivorsSelection input:checked').length;
    var nbEligible = $hj('#liveSurvivorsSelection input:visible:not(:checked)').length;
    if (nbEligible <= nb ) {
      $hj('#liveSurvivorsSelection input:visible:not(:checked)').each(function(){
        $hj(this).prop('checked', true);
      });
    } else {
      for (var i=nb; i>0; i-- ) {
        var rk = Math.floor(Math.random() * nbEligible);
        $hj('#liveSurvivorsSelection input:visible:not(:checked)').eq(rk).prop('checked', true);
        nbEligible--;
      }
    }
    $hj('#nbSurvivorsSelected').html($hj('#liveSurvivorsSelection input:checked').length);
  });

  $hj('.checkall').click(function(){
    $hj(this).parent().siblings().find('input').prop('checked', $hj(this).prop('checked'));
  });

  $hj('#spawnSetupSelection input').click(function(){
    var isChecked = $hj(this).is(':checked');
    var span = $hj(this).data('href');
    var node = $hj('#invasionSpanSelection');
    if (isChecked && node.val().indexOf(span)==-1 ) {
      node.val(node.val()+span);
    } else if (!isChecked && node.val().indexOf(span)!=-1 ) {
      node.val(node.val().replace(span, ''));
    }
  });

  $hj(function() {
    $hj("#sortable1, #sortable2" ).sortable({
      connectWith: ".connectedSortable"
    }).disableSelection();
  });




  /***
  ***
  $hj('.toolEquipments ul li i').unbind().click(function() {
    $hj(this).toggleClass('glyphicon-triangle-bottom').toggleClass('glyphicon-triangle-top');
  });
  $hj('#piocheXCards').unbind().click(function() {
    $hj(this).parent().toggleClass('open');
  });


  * */


/*
function addActionsOnList() {
  $hj('ul.chosen-results li.active-result')
    .unbind()
    .hover(
      function() {
        $hj(this).siblings().removeClass('highlighted');
        $hj(this).addClass('highlighted');
      },
      function() {
        $hj(this).removeClass('highlighted');
      }
    )
    .click(
      function() {
        $hj(this).siblings().removeClass('result-selected');
        $hj(this).addClass('result-selected');
        var rank = $hj(this).data('option-array-index');
        var libelle = $hj('#searchSkill option:nth-child('+rank+')').text();
        var value = $hj('#searchSkill option:nth-child('+rank+')').val();
        getSurvivorsBySkillId(value);
        $hj('a.chosen-single span').html(libelle);
        $hj('div.chosen-container.chosen-container-single a').parent().removeClass('chosen-with-drop chosen-container-active');
      }
    );
}
function fillOptionsList() {
  var filter = $hj('input.chosen-search-input').val().toLowerCase();
  var str = '';
  var cpt = 1;
  var nb = 0;
  $hj('select.chosen-select option').each(function() {
    var value = $hj(this).html();
    if (value.toLowerCase().indexOf(filter)!=-1 ) {
      var libelle = $hj('#searchSkill option:nth-child('+cpt+')').text().toLowerCase();
      if (filter!='' ) {
        libelle = libelle.replace(filter, '<em>'+filter+'</em>');
      }
      str += '<li class="active-result'+(nb==0 ? ' highlighted' : '')+'" style="" data-option-array-index="'+cpt+'">'+libelle+'</li>';
      nb++;
    }
    cpt++;
  }).promise()
    .done(function() {
      $hj('ul.chosen-results').html(str);
      addActionsOnList();
    });
}


/********
 * Filtres sur les articles Missions
 ********
function filterMissions() {
  $hj('.batchArticles article.mission').each(function(){
    var nodeArticle = $hj(this);
    var showArticle = true;
    var cpt = 0;
    $hj('.missionFilters ul').each(function(){
      var tmpShow = false;
      $hj(this).find('a.selected').each(function(){
        var filter = $hj(this).data('filter');
        if (filter == 'all' ){
          tmpShow = true;
          cpt++;
        } else {
          if (nodeArticle.hasClass(filter) ) {
            tmpShow = true;
          }
        }
      });
      showArticle = (showArticle && tmpShow);
    });
    if (!showArticle && cpt==4 ) { showArticle = true; }
    if (showArticle && !nodeArticle.is(':visible')
       || !showArticle && nodeArticle.is(':visible') ) {
       nodeArticle.animate({width: 'toggle'}, 2500);
    }
  });
}

/********
 * Filtres sur les articles Equipements
 ********
function filterCards() {
  $hj('.batchArticles article.equipment').each(function(){
    var nodeArticle = $hj(this);
    var showArticle = true;
    $hj('.equipmentFilters select').each(function(){
      var select = $hj(this).val();
      if (select!='' && !$hj(this).hasClass('zoom') ) {
        if (!nodeArticle.hasClass(select) ) {
          showArticle = false;
        }
      }
    });
    if (showArticle && !nodeArticle.is(':visible')
       || !showArticle && nodeArticle.is(':visible') ) {
       nodeArticle.animate({width: 'toggle'}, 2500);
    }
  });
  $hj('.batchArticles article.invasion').each(function(){
    var nodeArticle = $hj(this);
    var showArticle = true;
    $hj('.invasionFilters select').each(function(){
      var select = $hj(this).val();
      if (select!='' && !$hj(this).hasClass('zoom') ) {
        if (!nodeArticle.hasClass(select) ) {
          showArticle = false;
        }
      }
    });
    if (showArticle && !nodeArticle.is(':visible')
       || !showArticle && nodeArticle.is(':visible') ) {
       nodeArticle.animate({width: 'toggle'}, 2500);
    }
  });
}

/********
 * Filtres sur les articles Survivants
 ********
function filterSurvivors() {
  var typeSelected = $hj('#survivorType').val();
  $hj('section.batchArticles')
    .removeClass()
    .addClass('batchArticles show-'+typeSelected);

  $hj('.batchArticles article.survivor').each(function() {
    var nodeSurvivor = $hj(this);
    var showArticle = true;
    var expValue = $hj(this).data('exp');

    if (!$hj('.survivorFilters a[data-exp="'+expValue+'"]').hasClass('selected') ) {
      showArticle = false;
    }

    var blueSelection = $hj('#searchBlue').val();
    if (blueSelection!=0 && showArticle ) {
      showArticle = false;
      nodeSurvivor.find('li.compBlue').each(function() {
        if ($hj(this).data('idskill') == blueSelection ) {
          showArticle = true;
        }
      });
    }

    var yellowSelection = $hj('#searchYellow').val();
    if (yellowSelection!=0 && showArticle ) {
      showArticle = false;
      nodeSurvivor.find('li.compYellow').each(function() {
        if ($hj(this).data('idskill') == yellowSelection ) {
          showArticle = true;
        }
      });
    }

    var orangeSelection = $hj('#searchOrange').val();
    if (orangeSelection!=0 && showArticle ) {
      showArticle = false;
      nodeSurvivor.find('li.compOrange').each(function() {
        if ($hj(this).data('idskill') == orangeSelection ) {
          showArticle = true;
        }
      });
    }

    var redSelection = $hj('#searchRed').val();
    if (redSelection!=0 && showArticle ) {
      showArticle = false;
      nodeSurvivor.find('li.compRed').each(function() {
        if ($hj(this).data('idskill') == redSelection ) {
          showArticle = true;
        }
      });
    }

    var allSelection = $hj('#searchAll').val();
    if (allSelection!=0 && showArticle ) {
      showArticle = false;
      nodeSurvivor.find('li').each(function() {
        if ($hj(this).data('idskill') == allSelection ) {
          showArticle = true;
        }
      });
    }

    if (typeSelected=='zombivor' && !$hj(this).hasClass('zombivor')
       || (typeSelected=='usurvivor' || typeSelected=='uzombivor') && !$hj(this).hasClass('ultimate') ) {
      showArticle = false;
    }

    if (showArticle && !nodeSurvivor.is(':visible')
       || !showArticle && nodeSurvivor.is(':visible') ) {
       nodeSurvivor.animate({width: 'toggle'}, 2500);
    }
  });
}
function getSurvivorsBySkillId(skillId) {
  var obj;
  var data = {'action': 'dealWithAjax', 'ajaxAction': 'getSurvivorsBySkillId', 'value': skillId};
  $hj.post(
    ajaxurl,
    data,
    function(response) {
      try {
        obj = JSON.parse(response);
      } catch (e) {
        console.log("error: "+e);
        console.log(response);
      }
      reloadComponents(obj, 'replace');
    }
  );
}
function getSurvivorsByExpansionCode(exp, type='') {
  var obj;
  var data = {'action': 'dealWithAjax', 'ajaxAction': 'getSurvivorsByExpansionCode', 'value': exp, 'type': type};
  $hj.post(
    ajaxurl,
    data,
    function(response) {
      try {
        obj = JSON.parse(response);
      } catch (e) {
        console.log("error: "+e);
        console.log(response);
      }
      reloadComponents(obj, 'append');
      if (type=='' ) {
        filterSurvivors();
        sortSurvivors();
      } else if (type=='cartouche' ) {
        $hj('#selectionSurvivors i').unbind().click(function() {
          $hj(this).parent().parent().toggleClass('active');
          var inpSib = $hj(this).siblings('input');
          inpSib.prop("checked", !inpSib.prop("checked"))
        });
      }
    }
  );
}

/********
 * Ajax Utilities
 ********
function reloadComponents(obj, type) {
  for (var anchor in obj ) {
    if ($hj('#'+anchor).length==1 ) {
      switch (anchor ) {
        case 'descSkill'            :
        case 'homeSectionArticles'  :
        case 'moreSurvivors'        :
        case 'selectionSkills'       :
        case 'selectionSurvivors'   :
          switch (type ) {
            case 'append'    : $hj('#'+anchor).append(obj[anchor]); break;
            case 'prepend'  : $hj('#'+anchor).prepend(obj[anchor]); break;
            case 'replace'  : $hj('#'+anchor).html(obj[anchor]); break;
          }
          break;
      }
    }
  }
}
function sortSurvivors() {
  $hj('#moreSurvivors article')
    .sort(sortSurvivorsArticles)
    .appendTo('#moreSurvivors');
}
function sortSurvivorsArticles(a, b){
  return ($hj(b).data('name')) < ($hj(a).data('name')) ? 1 : -1;
}
























/********
 * Ajax Actions - Missions Page
 ********
var displayValue = $hj('#displayedRows').val();
var colsort = $hj('section th[data-colorder="_asc"]').data('colsort');
if (colsort=='' ) { colsort = $hj('section th[data-colorder="_asc"]').data('colsort'); }
var colorder = 'asc';
var paged = $hj('.pagination.justify-content-end .page-item.disabled a').data('paged');
function addPageMissionAjaxActions(clicked) {
  var ajaxaction = clicked.data('ajaxaction');
  var callAjax = true;
  switch (ajaxaction ) {
    // On change le critère de tri
    case 'sort' :
      colsort = clicked.data('colsort');
      if (!clicked.hasClass('sorting') ) {
        var actualorder = clicked.data('colorder');
        if (actualorder=='_asc' ) { colorder = 'desc'; }
        else { colorder = 'asc'; }
      }
    break;
    // On change le nombre d'éléments affichés
    case 'display' :
      displayValue = clicked.val();
      paged = 1;
    break;
    // On change la page affichée
    case 'paged' :
      paged = clicked.data('paged');
    break;
    case 'filter' :
      if ($hj('#rowFilterMission').hasClass('hidden') ) {
        callAjax = false;
        $hj('#rowFilterMission').removeClass('hidden');
      }
    break;
    default : callAjax = false; break;
  }
  if (callAjax ) {
    var obj;
    var filters = $hj('select.filters').serialize();
    var data = {'action': 'dealWithAjax', 'ajaxAction': 'getMissions', 'colsort': colsort, 'colorder': colorder, 'nbperpage': displayValue, 'paged': paged, 'filters': filters};
    $hj.post(
      ajaxurl,
      data,
      function(response) {
        try {
          obj = JSON.parse(response);
          if (obj['page-missions'] != '' ) {
            $hj('#page-missions').replaceWith(obj['page-missions']);
            $hj('#page-missions .ajaxAction').unbind().click(function(){
              addPageMissionAjaxActions($hj(this));
              return false;
            });
          }
        } catch (e) {
          console.log("error: "+e);
          console.log(response);
        }
      }
    );
  }
}
function addPageSurvivantAjaxActions(clicked) {
    var ajaxaction = clicked.data('ajaxaction');
    var callAjax = true;
    switch (ajaxaction ) {
      // On change le critère de tri
      case 'sort' :
        colsort = clicked.data('colsort');
        if (!clicked.hasClass('sorting') ) {
          var actualorder = clicked.data('colorder');
          if (actualorder=='_asc' ) { colorder = 'desc'; }
          else { colorder = 'asc'; }
        }
      break;
      // On change le nombre d'éléments affichés
      case 'display' :
        displayValue = clicked.val();
        paged = 1;
      break;
      // On change la page affichée
      case 'paged' :
        paged = clicked.data('paged');
      break;
      case 'filter' :
        var filter = clicked.data('filter');
        var top = clicked.offset().top;
        var left = clicked.offset().left;
        var widthCol = clicked.width();
        var widthPopup = $hj('#popover'+filter.charAt(0).toUpperCase()+filter.slice(1)).width();
        $hj('#popover'+filter.charAt(0).toUpperCase()+filter.slice(1))
          .toggleClass('show')
          .css('transform', 'translate3d('+(left+widthCol-widthPopup+34)+'px, '+(top-182)+'px, 0px)')
          .find('.arrow').css('left', (widthPopup-34)+'px');
        callAjax = !$hj('#popover'+filter.charAt(0).toUpperCase()+filter.slice(1)).hasClass('show');
      break;
      default : callAjax = false; break;
    }
    if (callAjax ) {
      var obj;
      var filters = $hj('input.filters').serialize();
      var data = {'action': 'dealWithAjax', 'ajaxAction': 'getSurvivants', 'colsort': colsort, 'colorder': colorder, 'nbperpage': displayValue, 'paged': paged, 'filters': filters};
      $hj.post(
        ajaxurl,
        data,
        function(response) {
          try {
            obj = JSON.parse(response);
            if (obj['page-survivants'] != '' ) {
              $hj('#page-survivants').replaceWith(obj['page-survivants']);
              $hj('#page-survivants .ajaxAction').unbind().click(function(){
                addPageSurvivantAjaxActions($hj(this));
              });
              $hj('#page-survivants .changeProfile').unbind().click(function(){
                addPageSurvivantLocalActions($hj(this));
                return false;
              });
            }
          } catch (e) {
            console.log("error: "+e);
            console.log(response);
          }
        }
      );
    }
  }

function doEquipmentDeckActions(data, type) {
  var obj;
  $hj.post(
    ajaxurl,
    data,
    function(response) {
      try {
        obj = JSON.parse(response);
        if (type == 'reload' ) {
          location.href = 'http://zombicide.jhugues.fr/page-live-pioche-equipment/';
        }
        for (var anchor in obj ) {
          if (type == 'insert' ) {
            $hj('#'+anchor).html(obj[anchor]);
          }
        }
        $hj('.discardEquipButton').unbind().click(function(){
          var data = {'action': 'dealWithAjax', 'ajaxAction': 'discardEquippedCard', 'keyAccess': $hj(this).data('keyaccess'), 'id': $hj(this).data('id')};
          doEquipmentDeckActions(data, 'insert');
        });
      } catch (e) {
        console.log("error: "+e);
        console.log(response);
      }
    }
  );
}
function doSpawnDeckActions(data, type) {
  var obj;
  $hj.post(
    ajaxurl,
    data,
    function(response) {
      try {
        obj = JSON.parse(response);
        if (type == 'reload' ) {
          location.href = 'http://zombicide.jhugues.fr/page-live-pioche-invasion/';
        }
        for (var anchor in obj ) {
          if (type == 'insert' ) {
            $hj('#'+anchor).html(obj[anchor]);
          }
        }
      } catch (e) {
        console.log("error: "+e);
        console.log(response);
      }
    }
  );
}
function addGenKeyActions() {
  $hj('#genKey').click(function(e){
    e.preventDefault();
    var n = 16;
    var str = 'azertyupqsdfghjkmwxcvbnAZERTYUPQSDFGHJKMWXCVBN23456789';
    var max = str.length;
    var password = '';
    for (var i=1; i<=n; i++ ) {
      var start = Math.floor(Math.random()*max);
      password += str.substring(start, start+1);
    }
    $hj('#keyAccess').val(password);
  });
}
function addPageLiveEquipmentActions() {
  addGenKeyActions();
  $hj('#equipmentSetupSelection .btn-expansion').click(function(){
    $hj(this).toggleClass('active');
    $hj(this).find('svg').toggleClass('fa-square fa-check-square');
    var expansionIds = '';
    $hj('.btn-expansion.active').each(function(){
      if (expansionIds!='') {
        expansionIds+=',';
      }
      expansionIds += $hj(this).data('expansion-id');
    });
    var data = {'action': 'dealWithAjax', 'ajaxAction': 'pregenEquipmentCard', 'expansionIds': expansionIds};
    doEquipmentDeckActions(data, 'insert');
  });
  if ($hj('#btnDrawEquipmentCard').length!=0 ) {
    $hj('.withEquipmentAction').unbind().click(function(){
      var action = $hj(this).data('action');
      var keyAccess = $hj(this).data('keyaccess');
      var data = {'action': 'dealWithAjax', 'ajaxAction': 'EquipmentDeck' , 'ajaxChildAction': action, 'keyAccess': keyAccess};
      doEquipmentDeckActions(data, $hj(this).data('type'));
    });
  }
}
function addPageLiveSpawnActions() {
  addGenKeyActions();
  $hj('#spawnSetupSelection .btn-expansion span').click(function(){
    $hj(this).find('svg').toggleClass('fa-square fa-check-square');
    var isChecked = $hj(this).find('svg').hasClass('fa-check-square');
    var span = $hj(this).data('spawnspan');
    var node = $hj('#invasionSpanSelection');
    if (isChecked && node.val().indexOf(span)==-1 ) {
      node.val(node.val()+span);
    } else if (!isChecked && node.val().indexOf(span)!=-1 ) {
      node.val(node.val().replace(span, ''));
    }
  });

  if ($hj('#btnDrawSpawnCard').length!=0 ) {
    $hj('.withSpawnAction').unbind().click(function(){
      var action = $hj(this).data('action');
      var keyAccess = $hj(this).data('keyaccess');
      var data = {'action': 'dealWithAjax', 'ajaxAction': 'SpawnDeck' , 'ajaxChildAction': action, 'keyAccess': keyAccess};
      doSpawnDeckActions(data, $hj(this).data('type'));
    });
  }
}
function joinGame() {
    var data = {'action': 'dealWithAjax', 'ajaxAction': 'joinGame', 'keyAccess': $hj('#keyAccess').val()};
  $hj.post(
    ajaxurl,
    data,
    function(response) {
      try {
        var obj = JSON.parse(response);
        console.log(obj);
      } catch (e) {
        console.log("error: "+e);
        console.log(response);
      }
    }
  );
}
*/

<file_sep>/core/actions/TileActions.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * TileActions
 * @author Hugues
 * @since 1.08.30
 */
class TileActions extends LocalActions
{
  /**
   * Constructeur
   */
  public function __construct($post=array())
  {
    parent::__construct();
    $this->post = $post;
    $this->TileServices  = new TileServices();
  }
  /**
   * Point d'entrée des méthodes statiques.
   * @param array $post
   * @return string
   **/
  public static function dealWithStatic($post)
  {
    $returned = '';
    $Act = new TileActions($post);
    if ($post[self::CST_AJAXACTION]==self::AJAX_GETTILES) {
      $returned = $Act->dealWithGetTiles(true);
    } else {
      $returned  = 'Erreur dans TileActions > dealWithStatic, '.$_POST[self::CST_AJAXACTION].' inconnu.';
    }
    return $returned;
  }

  //////////////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////////
  // Bloc de gestion de la récupération des dalles selon une extension

  public function dealWithGetTiles()
  {
    //////////////////////////////////////////////////////////////////////
    // Initialisation des variables
    $expansionId = $this->post['idSet'];
    $Tiles = $this->TileServices->getTilesWithFilters(array(self::FIELD_EXPANSIONID=>$expansionId), self::FIELD_CODE, self::ORDER_ASC);

    //////////////////////////////////////////////////////////////////////
    // On parcourt la liste des Tiles pour les afficher.
    $result = '';
    while (!empty($Tiles)) {
      $Tile = array_shift($Tiles);
      $result .= '<div class="card"><img class="card-img-top" src="'.$Tile->getImgUrl().'"/></div>';
    }

    $result = '<div id="tile-container"><div class="card-columns">'.$result.'</div></div>';
    return $this->jsonString($result, 'tile-container', true);
  }

  //////////////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////////

}

<file_sep>/core/actions/AjaxActions.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * AjaxActions
 * @author Hugues
 * @since 1.04.00
 * @version 1.04.27
 */
class AjaxActions extends LocalActions
{
  /**
   * Constructeur
   */
  public function __construct()
  {}

  /**
   * Gère les actions Ajax
   * @version 1.04.30
   * @version 1.07.21
   */
  public static function dealWithAjax()
  {
    switch ($_POST[self::CST_AJAXACTION]) {
      case self::AJAX_GETEXPANSIONS  :
      case self::AJAX_EXPANSIONVERIF :
        $returned = ExpansionActions::dealWithStatic($_POST);
      break;
      case self::AJAX_ADDMORENEWS    :
        $returned = HomePageActions::dealWithStatic($_POST);
      break;
      case self::AJAX_GETMISSIONS    :
      case self::AJAX_MISSIONVERIF   :
        $returned = MissionActions::dealWithStatic($_POST);
      break;
      case self::AJAX_GETSKILLS      :
      case self::AJAX_SKILLVERIF     :
        $returned = SkillActions::dealWithStatic($_POST);
      break;
      case self::AJAX_GETRANDOMTEAM  :
      case self::AJAX_GETSURVIVORS   :
      case self::AJAX_SURVIVORVERIF  :
        $returned = SurvivorActions::dealWithStatic($_POST);
      break;
      case self::AJAX_GETRANDOMMAP   :
      case self::AJAX_GETTHROWDICE   :
      case 'getBuildingMap'          :
      case 'getNonUsedTiles'         :
      case 'getEmptyCell'            :
      case 'getImageMap'             :
        $returned = ToolActions::dealWithStatic($_POST);
      break;
      case self::AJAX_GETTILES       :
        $returned = TileActions::dealWithStatic($_POST);
      break;
      case 'updateLiveMission'       :
        $returned = LiveMissionActions::dealWithStatic($_POST);
      break;
      default :
        $returned  = 'Erreur dans le $_POST['.self::CST_AJAXACTION.'] : '.$_POST[self::CST_AJAXACTION].'<br>';
      break;
    }
    return $returned;
  }
}
<file_sep>/core/domain/WeaponProfile.class.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe WeaponProfile
 * @author Hugues.
 * @version 1.0.00
 * @since 1.0.00
 */
class WeaponProfile extends LocalDomain
{
  /**
   * Id technique de la donnée
   * @var int $id
   */
  protected $id;
  /**
   * Quelle portée minimale
   * @var int $minRange
   */
  protected $minRange;
  /**
   * Quelle portée maximale
   * @var int $maxRange
   */
  protected $maxRange;
  /**
   * Combien de dés
   * @var int $nbDice
   */
  protected $nbDice;
  /**
   * Quel seuil de réussite
   * @var int $successRate
   */
  protected $successRate;
  /**
   * Combien de dégâts
   * @var int $damageLevel
   */
  protected $damageLevel;
  /**
   * @return int
   */
  public function getId()
  { return $this->id; }
  /**
   * @return int
   */
  public function getMinRange()
  { return $this->minRange; }
  /**
   * @return int
   */
  public function getMaxRange()
  { return $this->maxRange; }
  /**
   * @return int
   */
  public function getNbDice()
  { return $this->nbDice; }
  /**
   * @return int
   */
  public function getSuccessRate()
  { return $this->successRate; }
  /**
   * @return int
   */
  public function getDamageLevel()
  { return $this->damageLevel; }
  /**
   * @param int $id
   */
  public function setId($id)
  { $this->id = $id; }
  /**
   * @param int $minRange
   */
  public function setMinRange($minRange)
  { $this->minRange = $minRange; }
  /**
   * @param int $maxRange
   */
  public function setMaxRange($maxRange)
  { $this->maxRange = $maxRange; }
  /**
   * @param int $nbDice
   */
  public function setNbDice($nbDice)
  { $this->nbDice = $nbDice; }
  /**
   * @param int $successRate
   */
  public function setSuccessRate($successRate)
  { $this->successRate = $successRate; }
  /**
   * @param int $damageLevel
   */
  public function setDamageLevel($damageLevel)
  { $this->damageLevel = $damageLevel; }
  /**
   * @return array
   */
  public function getClassVars()
  { return get_class_vars('WeaponProfile'); }
  /**
   * @param array $row
   * @param string $a
   * @param string $b
   * @return WeaponProfile
   */
  public static function convertElement($row, $a='', $b='')
  { return parent::convertElement(new WeaponProfile(), self::getClassVars(), $row); }
  /**
   * @return WeaponProfileBean
   */
  public function getBean()
  { return new WeaponProfileBean($this); }
}
<file_sep>/core/bean/SpawnBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe SpawnBean
 * @author Hugues
 * @since 1.02.00
 * @version 1.04.26
 */
class SpawnBean extends LocalBean
{
  /**
   * Class Constructor
   * @param Spawn $Spawn
   */
  public function __construct($Spawn=null)
  {
    parent::__construct();
    $this->Spawn = ($Spawn==null ? new Spawn() : $Spawn);
  }

  public function displayCard()
  {
    $attrImg = array(
      self::ATTR_WIDTH  => 320,
      self::ATTR_HEIGHT => 440,
      self::ATTR_SRC    => $this->Spawn->getImgUrl(),
      self::ATTR_ALT    => '#'.$this->Spawn->getSpawnNumber(),
    );
    $strImg = $this->getBalise(self::TAG_IMG, '', $attrImg);
    return $this->getBalise(self::TAG_DIV, $strImg, array(self::ATTR_CLASS => 'card spawn set-'.$this->Spawn->getExpansionId()));
  }
}
<file_sep>/admin_manage.php
<?php
/**
 * @author Hugues
 * @since 1.00.00
 * @version 1.07.25
 */
  define('ZOMB_SITE_URL', 'http://zombicide.jhugues.fr/');
  define('PLUGINS_MYCOMMON', ZOMB_SITE_URL.'wp-content/plugins/mycommon');
  define('PLUGINS_ZOMBICIDE', ZOMB_SITE_URL.'wp-content/plugins/hj-zombicide');
?>
<link rel="stylesheet" href="<?php echo PLUGINS_MYCOMMON; ?>/web/rsc/css/jquery-ui.min.css" type="text/css" media="all" />
<link rel="stylesheet" href="<?php echo PLUGINS_ZOMBICIDE; ?>/web/rsc/admin_zombicide.css" type="text/css" media="all" />
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
global $Zombisite;
if (empty($Zombisite)) {
  $Zombisite = new Zombisite();
}
$AdminPageBean = new AdminPageBean();
echo $AdminPageBean->getContentPage();
?>
<script type='text/javascript' src='<?php echo PLUGINS_MYCOMMON; ?>/web/rsc/js/jquery-ui-min.js'></script>
<script type='text/javascript' src='<?php echo PLUGINS_ZOMBICIDE; ?>/web/rsc/admin_zombicide.js'></script>
<file_sep>/core/bean/AdminPageMissionsBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * AdminPageMissionsBean
 * @author Hugues
 * @since 1.05.10
 * @version 1.07.25
 */
class AdminPageMissionsBean extends AdminPageBean
{
  protected $tplHomeCheckCard  = 'web/pages/admin/fragments/home-check-card.php';
  protected $urlMissionListing = 'web/pages/admin/mission-listing.php';
  protected $urlAdminEdit      = 'web/pages/admin/mission-edit.php';
  /**
   * Class Constructor
   */
  public function __construct($urlParams='')
  {
    $this->urlParams = $urlParams;
    parent::__construct(self::CST_MISSION);
    $this->title = 'Missions';
    $this->MissionServices    = new MissionServices();
  }
  /**
   * @param array $urlParams
   * @return $Bean
   */
  public function getSpecificContentPage()
  {
    if (isset($this->urlParams[self::FIELD_ID])) {
      $this->Mission = $this->MissionServices->selectMission($this->urlParams[self::FIELD_ID]);
    }
    if (isset($_POST)&&!empty($_POST)) {
      $this->dealWithPost();
    }
    switch ($this->urlParams[self::CST_POSTACTION]) {
      case 'confirmEdit'  :
      case self::CST_EDIT :
        return $this->getEditContentPage();
      break;
      default :
        return $this->getListContentPage();
      break;
    }
  }
  private function dealWithPost()
  {
    if ($this->urlParams[self::CST_POSTACTION]=='confirmEdit') {
      $this->Mission->setWidth($this->urlParams[self::FIELD_WIDTH]);
      $this->Mission->setHeight($this->urlParams[self::FIELD_HEIGHT]);
      $this->MissionServices->updateMission($this->Mission);
    }
  }
  public function getListContentPage()
  {
    $strRows = '';
    $nbPerPage = 15;
    $curPage = $this->initVar(self::WP_CURPAGE, 1);
    $orderby = $this->initVar(self::WP_ORDERBY, self::FIELD_TITLE);
    $order = $this->initVar(self::WP_ORDER, self::ORDER_ASC);
    $filters = array();
    if (isset($this->urlParams[self::FIELD_ORIGINEID])) {
      $filters[self::FIELD_ORIGINEID] = $this->urlParams[self::FIELD_ORIGINEID];
    }
    $Missions = $this->MissionServices->getMissionsWithFilters($filters, $orderby, $order);
    $nbElements = count($Missions);
    $nbPages = ceil($nbElements/$nbPerPage);
    $curPage = max(1, min($curPage, $nbPages));
    $DisplayedMissions = array_slice($Missions, ($curPage-1)*$nbPerPage, $nbPerPage);
    if (!empty($DisplayedMissions)) {
      foreach ($DisplayedMissions as $Mission) {
        $MissionBean = new MissionBean($Mission);
        $strRows .= $MissionBean->getRowForAdminPage();
      }
    }
    $queryArg = array(
      self::CST_ONGLET => self::CST_MISSION,
      self::WP_ORDERBY => $orderby,
      self::WP_ORDER   => $order
    );
    // Pagination
    $strPagination = $this->getPagination($queryArg, $post_status, $curPage, $nbPages, $nbElements);

    $args = array(
      // Liste des missions affichées - 1
      $strRows,
      // Filtres - 2
      '',
      // Url pour créer une nouvelle Mission - 3
      '/wp-admin/post-new.php',
      // Subs - 4
      '',
      // Pagination - 5
      $strPagination,
      // Filtre Extensions - 6
      OrigineBean::getStaticSelect(self::FIELD_ORIGINEID, $this->urlParams[self::FIELD_ORIGINEID]),
    );
    return $this->getRender($this->urlMissionListing, $args);
  }
  public function getEditContentPage()
  {
    //////////////////////////////////////////////////////////////////////////
    // On enrichit le template
    $args = array(
    // L'identifiant de la mission - 1
    $this->Mission->getId(),
    // Le code de la Mission - 2
    $this->Mission->getCode(),
    // Le titre de la Mission - 3
    $this->Mission->getTitle(),
    // Le synopsis de la Mission - 4
    $this->Mission->getWpPost()->getPostContent(),
    // La difficulté de la Mission - 5
    $this->Mission->getStrDifficulty(),
    // Le nombre de Survivants de la Mission - 6
    $this->Mission->getStrNbJoueurs(),
    // La durée de la Mission - 7
    $this->Mission->getStrDuree(),
    // L'origine de la Mission - 8
    $this->Mission->getStrOrigine(),
    // La liste des extensions utilisées - 9
    implode(', ', unserialize($this->Mission->getWpPost()->getPostMeta('expansionIds'))),
    // La liste des dalles utilisées - 10
    $this->Mission->getWpPost()->getPostMeta('tileIds'),
    // Url de l'image de la map - 11
    $this->Mission->getImgUrl(),
    // Largeur de la Map - 12
    $this->Mission->getWidth(),
    // Hauteur de la Map - 13
    $this->Mission->getHeight(),
    // Liste des Objectifs - 14
    '',
    // Liste des Règles Spéciales - 15
    '',
    '', '', '', '', '', '', '', '', '', '', '',
    );
    // Puis on le restitue.
    return $this->getRender($this->urlAdminEdit, $args);
  }

  /**
   * @return string
   */
  public function getCheckCard()
  {
    /////////////////////////////////////////////////
    // Gestion des Missions.
    // On récupère la liste des Missions qui ont un Article. Puis les données dans la base. On compare et on effectue un diagnostic.
    $Act = new MissionActions();
    $strBilan  = $Act->dealWithMissionVerif();

    $args = array(
      // Le titre de la carte - 1
      $this->title,
      // L'id du container de retour pour afficher les vérifications - 2
      self::CST_MISSION,
      // Le contenu du container de vérification - 3
      $strBilan,
   );
    return $this->getRender($this->tplHomeCheckCard, $args);
  }
}
<file_sep>/core/services/SkillServices.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe SkillServices
 * @author Hugues.
 * @since 1.00.00
 * @version 1.05.06
 */
class SkillServices extends LocalServices
{
  /**
   * L'objet Dao pour faire les requêtes
   * @var SkillDaoImpl $Dao
   */
  protected $Dao;
  /**
   * Class Constructor
   */
  public function __construct()
  {
    parent::__construct();
    $this->Dao = new SkillDaoImpl();
  }

  /**
   * @param array $arrFilters
   */
  private function buildFilters($arrFilters)
  {
    $this->arrParams[self::SQL_WHERE] = array();
    array_push($this->arrParams[self::SQL_WHERE], $this->addFilter($arrFilters, self::FIELD_CODE));
    array_push($this->arrParams[self::SQL_WHERE], $this->addNonArrayWideFilter($arrFilters, self::FIELD_NAME));
    array_push($this->arrParams[self::SQL_WHERE], $this->addNonArrayWideFilter($arrFilters, self::FIELD_DESCRIPTION));
    array_push($this->arrParams[self::SQL_WHERE], $this->addFilter($arrFilters, self::FIELD_EXPANSIONID));
  }
  /**
   * @param array $arrFilters
   * @param string $orderby
   * @param string $order
   * @return array
   */
  public function getSkillsWithFilters($arrFilters=array(), $orderby=self::FIELD_NAME, $order=self::ORDER_ASC)
  {
    $this->arrParams = $this->buildOrderAndLimit($orderby, $order);
    $this->buildFilters($arrFilters);
    return $this->Dao->selectEntriesWithFilters(__FILE__, __LINE__, $this->arrParams);
  }
  /**
   * @param array $arrFilters
   * @param string $orderby
   * @param string $order
   * @return array
   */
  public function getSkillsWithFiltersIn($arrFilters=array(), $orderby=self::FIELD_NAME, $order=self::ORDER_ASC)
  {
    $this->arrParams = $this->buildOrderAndLimit($orderby, $order);
    $this->buildFilters($arrFilters);
    return $this->Dao->selectEntriesWithFiltersIn($this->arrParams, $arrFilters);
  }

  public function insertSkill($Skill)
  { return $this->insert(__FILE__, __LINE__, $Skill); }
  public function selectSkill($id)
  { return $this->select(__FILE__, __LINE__, $id); }
  public function updateSkill($Skill)
  { return $this->update(__FILE__, __LINE__, $Skill); }
}
<file_sep>/core/domain/Rule.class.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe Rule
 * @author Hugues.
 * @since 1.04.08
 * @version 1.04.08
 */
class Rule extends LocalDomain
{
  /**
   * Id technique de la donnée
   * @var int $id
   */
  protected $id;
  /**
   * Eventuel Settings
   * @var bool $setting
   */
  protected $setting;
  /**
   * Code de la Règle
   * @var string $code
   */
  protected $code;
  /**
   * Description de la Règle
   * @var string $description
   */
  protected $description;
  /**
   * @return int
   */
  public function getId()
  { return $this->id; }
  /**
   * @return int
   */
  public function getSetting()
  { return $this->setting; }
  /**
   * @return string
   */
  public function getCode()
  { return $this->code; }
  /**
   * @return string
   */
  public function getDescription()
  { return $this->description; }
  /**
   * @param int $id
   */
  public function setId($id)
  { $this->id=$id; }
  /**
   * @param int $setting
   */
  public function setSetting($setting)
  { $this->setting=$setting; }
  /**
   * @param string $code
   */
  public function setCode($code)
  { $this->code=$code; }
  /**
   * @param string $description
   */
  public function setDescription($description)
  { $this->description=$description; }
  /**
   * @return array
   */
  public function getClassVars()
  { return get_class_vars('Rule'); }
  /**
   * @param array $row
   * @param string $a
   * @param string $b
   * @return Rule
   */
  public static function convertElement($row, $a='', $b='')
  { return parent::convertElement(new Rule(), self::getClassVars(), $row); }
  /**
   * @return RuleBean
   */
  public function getBean()
  { return new RuleBean($this); }

}
<file_sep>/core/services/MissionServices.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe MissionServices
 * @author Hugues.
 * @since 1.00.00
 * @version 1.05.10
 */
class MissionServices extends LocalServices
{
  /**
   * L'objet Dao pour faire les requêtes
   * @var MissionDaoImpl $Dao
   */
  protected $Dao;
  /**
   * Class Constructor
   */
  public function __construct()
  {
    parent::__construct();
    $this->Dao = new MissionDaoImpl();
    $this->MissionExpansionServices = new MissionExpansionServices();
  }

  /**
   * @param array $arrFilters
   */
  private function buildFilters($arrFilters)
  {
    $this->arrParams[self::SQL_WHERE] = array();
    array_push($this->arrParams[self::SQL_WHERE], $this->addNonArrayWideFilter($arrFilters, self::FIELD_TITLE));
    array_push($this->arrParams[self::SQL_WHERE], $this->addFilter($arrFilters, self::FIELD_CODE));
    array_push($this->arrParams[self::SQL_WHERE], $this->addNonArrayFilter($arrFilters, self::FIELD_LEVELID));
    array_push($this->arrParams[self::SQL_WHERE], $this->addNonArrayFilter($arrFilters, self::FIELD_DURATIONID));
    array_push($this->arrParams[self::SQL_WHERE], $this->addNonArrayFilter($arrFilters, self::FIELD_PLAYERID));
    array_push($this->arrParams[self::SQL_WHERE], $this->addNonArrayFilter($arrFilters, self::FIELD_ORIGINEID));
    array_push($this->arrParams[self::SQL_WHERE], $this->addNonArrayFilter($arrFilters, self::FIELD_PUBLISHED));
    array_push($this->arrParams[self::SQL_WHERE], $this->addNonArrayFilter($arrFilters, self::FIELD_LIVEABLE));
    array_push($this->arrParams[self::SQL_WHERE], $this->addFilter($arrFilters, self::FIELD_EXPANSIONID));
  }
  /**
   * @param array $arrFilters
   * @param string $orderby
   * @param string $order
   * @return array
   */
  public function getMissionsWithFilters($arrFilters=array(), $orderby=self::FIELD_TITLE, $order=self::ORDER_ASC)
  {
    $this->arrParams = $this->buildOrderAndLimit($orderby, $order);
    $this->buildFilters($arrFilters);
    if ($arrFilters[self::FIELD_EXPANSIONID]) {
      return $this->Dao->selectEntriesWithFiltersIn(__FILE__, __LINE__, $this->arrParams, $arrFilters);
    } else {
      return $this->Dao->selectEntriesWithFilters(__FILE__, __LINE__, $this->arrParams);
    }
  }
  /**
   * @param array $arrFilters
   * @param string $orderby
   * @param string $order
   * @return array
   */
  public function getMissionsWithFiltersIn($arrFilters=array(), $orderby=self::FIELD_TITLE, $order=self::ORDER_ASC)
  {
    $this->arrParams = $this->buildOrderAndLimit($orderby, $order);
    return $this->Dao->selectEntriesWithFiltersIn(__FILE__, __LINE__, $this->arrParams, $arrFilters);
  }

  public function getMissionsByExpansionId($expansionId)
  {
    $Missions = array();
    $MissionExpansions = $this->MissionExpansionServices->getMissionExpansionsWithFilters(array(self::FIELD_EXPANSIONID=>$expansionId));
    while (!empty($MissionExpansions)) {
      $MissionExpansion = array_shift($MissionExpansions);
      array_push($Missions, $this->selectMission($MissionExpansion->getMissionId()));
    }
    return $Missions;
  }

  /**
   * @param string $file
   * @param string $line
   * @param string $value
   * @param string $prefix
   * @return string
   */
  public function getDifficultySelect($file, $line, $value='', $prefix='')
  {
    $arrDifficulties = array('TUTO'=>'Tutoriel', 'EASY'=>'Facile', 'MED'=>'Moyenne', 'HARD'=>'Difficile',
      'VHARD'=>'Très Difficile', 'PVP'=>'Compétitive', 'BLUE'=>'Bleue', 'YELLOW'=>'Jaune', 'ORANGE'=>'Orange', 'RED'=>'Rouge');
    $arrSetValues = $this->getSetValues($file, $line, 'difficulty', false);
    $arrSetLabels = array();
    foreach ($arrSetValues as $setValue) {
      $arrSetLabels[$setValue] = $arrDifficulties[$setValue];
    }
    $this->labelDefault = 'Difficultés';
    return $this->getSetSelect($file, $line, $arrSetLabels, $prefix.'difficulty', $value);
  }
  /**
   * @param string $file
   * @param string $line
   * @param string $field
   * @param string $isSet
   * @return array
   */
  public function getSetValues($file, $line, $field, $isSet=true)
  { return $this->Dao->getSetValues($file, $line, $field, $isSet); }
  /**
   * @param string $file
   * @param string $line
   * @param string $value
   * @param string $prefix
   * @return string
   */
  public function getNbPlayersSelect($file, $line, $value='', $prefix='')
  {
    $arrSetValues = $this->getSetValues($file, $line, 'nbPlayers', false);
    $arrSetLabels = array();
    foreach ($arrSetValues as $setValue) {
      if (strpos($setValue, '+')!==false) {
        $arrSetLabels[$setValue] = $setValue[0].' Survivants et +';
      } else {
        list($min, $max) = explode('-', $setValue);
        $arrSetLabels[$setValue] = $min.' à '.$max.' Survivants';
      }
    }
    $this->labelDefault = 'Survivants';
    return $this->getSetSelect($file, $line, $arrSetLabels, $prefix.'nbPlayers', $value);
  }
  /**
   * @param string $file
   * @param string $line
   * @param string $field
   * @return array
   */
  public function getDistinctValues($file, $line, $field)
  { return $this->Dao->getDistinctValues($file, $line, $field); }
  /**
   * @param string $file
   * @param string $line
   * @param string $value
   * @param string $prefix
   * @return string
   */
  public function getDimensionsSelect($file, $line, $value='', $prefix='')
  {
    $arrParams = $this->buildOrderAndLimit(array('width', 'height'), array('ASC', 'ASC'));
    $arrSetValues = $this->Dao->selectDistinctDimensions($file, $line, $arrParams);
    $arrSetLabels = array();
    foreach ($arrSetValues as $setValue) {
      $arrSetLabels[$setValue->label] = $setValue->label;
    }
    $this->labelDefault = 'Dimensions';
    return $this->getSetSelect($file, $line, $arrSetLabels, $prefix.'dimension', $value);
  }
  /**
   * @param string $file
   * @param string $line
   * @param string $value
   * @param string $prefix
   * @return string
   */
  public function getDurationSelect($file, $line, $value='', $prefix='')
  {
    $arrSetValues = $this->getDistinctValues($file, $line, 'duration');
    $arrSetLabels = array();
    foreach ($arrSetValues as $setValue) {
      $arrSetLabels[$setValue] = $setValue.' minutes';
    }
    $this->labelDefault = 'Durées';
    return $this->getSetSelect($file, $line, $arrSetLabels, $prefix.'duration', $value);
  }
  /**
   * @param int $width
   * @return string
   */
  public function getWidthSelect($width)
  {
    $widthSelect  = '<select name="width">';
    $widthSelect .= '<option value="0">0</option>';
    for ($i=1; $i<=6; $i++) {
      $widthSelect .= '<option value="'.$i.'"'.($width==$i?' selected="selected"':'').'>'.$i.'</option>';
    }
    return $widthSelect.'</select>';
  }
  /**
   * @param int $height
   */
  public function getHeightSelect($height)
  {
    $heightSelect  = '<select name="height">';
    $heightSelect .= '<option value="0">0</option>';
    for ($i=1; $i<=6; $i++) {
      $heightSelect .= '<option value="'.$i.'"'.($height==$i?' selected="selected"':'').'>'.$i.'</option>';
    }
    return $heightSelect.'</select>';
  }
  private function addLiveZombie(&$LiveZombies, $Live, $missionZoneId, $zombieTypeId, $zombieCategoryId, $quantity)
  {
    $args = array(
      'liveId'=>$Live->getId(),
      'missionZoneId'=>$missionZoneId,
      'zombieTypeId'=>$zombieTypeId,
      'zombieCategoryId'=>$zombieCategoryId,
      'quantity'=>$quantity,
    );
    array_push($LiveZombies, new LiveZombie($args));
  }
  /**
   * @param Live $Live
   * @param Mission $Mission
   * @return array
   */
  public function getStartingZombies($Live, $Mission)
  {
    $LiveZombies = array();
    if ($Mission->hasRule(11)) {
      switch ($Mission->getId()) {
        case 1 :
          $this->addLiveZombie($LiveZombies, $Live, 4, 1, 1, 1);
          $this->addLiveZombie($LiveZombies, $Live, 12, 1, 1, 1);
        break;
        case 8 :
          $arrIds = array(1, 2, 3, 4, 6, 7, 8, 16, 17, 18, 19, 21, 22, 23, 24);
          while (!empty($arrIds)) {
            $id = array_shift($arrIds);
            $this->addLiveZombie($LiveZombies, $Live, $id, 1, 1, 1);
          }
        break;
        default :
          // Une Mission a des Zombies à mettre en place...
        break;
      }
    }
    return $LiveZombies;
  }
  /**
   * @param Mission $Mission
   * @return array
   */
  public function getStartingEquipmentDeck($Mission)
  {
    $arrEE = array();
    // On récupère les Extensions rattachées à la Mission.
    $MissionExpansions = $Mission->getMissionExpansions();
    while (!empty($MissionExpansions)) {
      $MissionExpansion = array_shift($MissionExpansions);
      // On récupère les Equipements rattachés aux Extensions
      $EquipmentExpansions = $MissionExpansion->getEquipmentExpansions();
      while (!empty($EquipmentExpansions)) {
        $EquipmentExpansion = array_shift($EquipmentExpansions);
        $EquipmentCard = $EquipmentExpansion->getEquipment();
        // On ne doit pas prendre les cartes suivantes :
        // Starter / Pimp / TODO : gérer les cartes comme le Molotov, la Batte Cloutée...
        if ($EquipmentCard->isStarter() || $EquipmentCard->isPimp()) {
          continue;
        }
        // On ajoute autant de fois la carte que requis.
        for ($i=0; $i<$EquipmentExpansion->getQuantity(); $i++) {
          array_push($arrEE, $EquipmentExpansion->getId());
        }
      }
    }
    shuffle($arrEE);
    // Certaines règles peuvent demander un traitement spécifique pour certaines cartes.
    if ($Mission->hasRule(2)) {
      // On rajoute le Pistolet, le Pied-de-biche et la Hache Starters en début de pioche.
      $arrAdd = array(13, 23, 25);
      shuffle($arrAdd);
      $arrEE = array_merge($arrAdd, $arrEE);
    }
    return $arrEE;
  }
  /**
   * @param Mission $Mission
   * @return array
   */
  public function getSpawnDeck($Mission)
  {
    // Certaines règles peuvent demander un traitement spécifique pour certaines cartes.
    if ($Mission->hasRule(1)) {
      // On ne joue qu'avec les cartes #1, #2, #3, #4 et #41.
      $arrNumbers = array(1, 2, 3, 4, 41);
      shuffle($arrNumbers);
    }
    return $arrNumbers;
  }


  public function selectMission($missionId)
  { return $this->select(__FILE__, __LINE__, $missionId); }
  public function updateMission($Mission)
  { return $this->update(__FILE__, __LINE__, $Mission); }
  public function insertMission($Mission)
  { return $this->insert(__FILE__, __LINE__, $Mission); }
}
<file_sep>/core/bean/WpPageHomeBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe WpPageHomeBean
 * @author Hugues
 * @since 1.04.00
 * @version 1.05.09
 */
class WpPageHomeBean extends WpPageBean
{
  /**
   * Class Constructor
   */
  public function __construct()
  {
    parent::__construct();
    $this->MissionServices = new MissionServices();
  }

  /**
   * {@inheritDoc}
   * @see MainPageBean::getContentPage()
   */
  public function getContentPage()
  {
    $strContent  = '<section id="homeSectionArticles" class="batchArticles missions survivors show-survivor">';
    $strContent .= $this->addMoreNews();
    $strContent .= '</section>';
    $strContent .= '<section class="col-xs-4 col-xs-offset-4">';
    $strContent .= '<div class="text-center"><div id="more_news" class="special_buttons">';
    $strContent .= ($this->lang=='en' ? 'More news' : 'Plus de news');
    $strContent .= '</div></div>';
    $strContent .= '</section>';
    return $strContent.'<div class="clearfix"></div>';
  }
  /**
   * @param number $offset
   * @return string
   */
  public static function staticAddMoreNews($offset=0)
  {
    $Bean = new WpPageHomeBean();
    return $Bean->addMoreNews($offset, true, false);
  }
  /**
   * @param number $offset
   * @param string $isAjax
   * @return string
   *     return $Bean->addMoreNews($offset, true, false);
  }
  /**
   * @param number $offset
   * @param string $isAjax
   * @return string
   *
  public function addMoreNews($offset=0, $isAjax=false, $getSticky=true)
  {
    // TODO :
    // Si getSticky vaut true, on cherche le sticky post publié le plus récent.
    // Si on a bien un article, on met à jour posts_per_page à 5.
    // Si offset ne vaut pas 0, on le réduit de 1

   */
  public function addMoreNews($offset=0, $isAjax=false, $getSticky=true)
  {
    $nbPostPerPage = 6;
    $args = array(
      self::WP_ORDERBY    => 'post_date',
      self::WP_ORDER      => self::ORDER_DESC,
      self::WP_OFFSET     =>0,
    );
    if ($getSticky) {
      // Si on veut le Sticky, on en veut un
      $args[self::WP_POSTSPERPAGE] = 1;
      // Et donc on voudra un article de moins ensuite
      $nbPostPerPage--;
      // On veut le sticky évidemment
      $args['post__in'] = get_option( 'sticky_posts' );
      // On initialise TaxQuery pour prendre n'importe quel article
      $args[self::WP_TAXQUERY] = array();
      // Et on veut qu'il soit publié.
      $args[self::WP_POSTSTATUS] = self::WP_PUBLISH;
      // Et go !
      $WpStickyPosts = $this->WpPostServices->getArticles($args);
    } else {
      $WpStickyPosts = array();
      // Si on veut pas de sticky, c'est qu'on est en train de paginer. On fait -1 pour compenser le sticky sur la première page.
      $offset--;
    }
    // On récupère maintenant les Articles à afficher.
    $postStatus = ($this->isAdmin() ? ', private, future' : '');
    $args[self::WP_OFFSET] = $offset;
    $args[self::WP_POSTSPERPAGE] = $nbPostPerPage;
    $args[self::WP_POSTSTATUS] = self::WP_PUBLISH.$postStatus;
    $args[self::WP_TAXQUERY] = array(array(
      self::WP_TAXONOMY=>self::WP_POSTTAG,
      self::WP_FIELD=>self::WP_SLUG,
      self::WP_TERMS=>array('mission', 'survivant')
    ));
    $args['post__in'] = '';

    $WpPosts = $this->WpPostServices->getArticles($args);
    // On merge avec les Sticky
    $WpPosts = array_merge($WpStickyPosts, $WpPosts);
    $strContent = '';
    while (!empty($WpPosts)) {
      $WpPost = array_shift($WpPosts);
      $WpBean = $WpPost->getBean();
      $strContent .= $WpBean->displayWpPost(true);
    }
    $strContent .= '<div class="clearfix"></div>';
    return ($isAjax ?  '{"homeSectionArticles":'.json_encode($strContent).'}' : $strContent);
  }
}
<file_sep>/core/bean/WpPostBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * WpPostBean
 * @author Hugues
 * @since 1.04.00
 * @version 1.05.01
 */
class WpPostBean extends MainPageBean
{
  /**
   * WpPost affiché
   * @var WpPost $WpPost
   */
  protected $WpPost;
  /**
   * @param string $post
   * @param array $services
   */
  public function __construct($post='', $services=array())
  {
    if ($post=='') {
      $post = get_post();
    }
    if (get_class($post) == 'WpPost') {
      $this->WpPost = $post;
    } else {
      $this->WpPost = WpPost::convertElement($post);
    }
    parent::__construct($services);
  }
  /**
   * @return Bean
   */
  public function getBean()
  {
    $this->WpCategs = $this->WpPost->getCategories();
      if (self::isAdmin()) {
        print_r($this->WpCategs);
      }
    if (!empty($this->WpCategs)) {
      $this->WpCateg = array_shift($this->WpCategs);
      switch ($this->WpCateg->getCatID()) {
        case self::WP_CAT_EXPANSION_ID  :
          $Bean = new WpPostExpansionBean($this->WpPost);
        break;
        case self::WP_CAT_MISSION_ID    :
          $Bean = new WpPostMissionBean($this->WpPost);
        break;
        case self::WP_CAT_NEWS_ID       :
          $Bean = new WpPostNewsBean($this->WpPost);
        break;
        case self::WP_CAT_SKILL_ID      :
          $Bean = new WpPostSkillBean($this->WpPost);
        break;
        case self::WP_CAT_SURVIVOR_ID   :
          $Bean = new WpPostSurvivorBean($this->WpPost);
        break;
        default :
          $Bean = new WpPageError404Bean();
        break;
      }
    } else {
      $Bean = new WpPageError404Bean();
    }
    return $Bean;
  }

  /**
   * @return string
   */
  public function getShellClass()
  { return ''; }
}
<file_sep>/core/domain/MissionObjective.class.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe MissionObjective
 * @author Hugues.
 * @since 1.04.08
 * @version 1.04.28
 */
class MissionObjective extends LocalDomain
{
  /**
   * Id technique de la donnée
   * @var int $id
   */
  protected $id;
  /**
   * Id technique de la Mission
   * @var int $missionId
   */
  protected $missionId;
  /**
   * Id technique de l'Objectif
   * @var int $objectiveId
   */
  protected $objectiveId;
  /**
   * titre de la règle
   * @var string $title
   */
  protected $title;
  /**
   * @return int
   */
  public function getId()
  { return $this->id; }
  /**
   * @return int
   */
  public function getMissionId()
  { return $this->missionId; }
  /**
   * @return int
   */
  public function getObjectiveId()
  { return $this->objectiveId; }
  /**
   * @return string
   */
  public function getTitle()
  { return $this->title; }
  /**
   * @param int $id
   */
  public function setId($id)
  { $this->id = $id; }
  /**
   * @param int $missionId
   */
  public function setMissionId($missionId)
  { $this->missionId = $missionId; }
  /**
   * @param int $objectiveId
   */
  public function setObjectiveId($objectiveId)
  { $this->objectiveId = $objectiveId; }
  /**
   * @param string $title
   */
  public function setTitle($title)
  { $this->title = $title; }
  /**
   * @return array
   */
  public function getClassVars()
  { return get_class_vars('MissionObjective'); }
  /**
   * @param array $row
   * @param string $a
   * @param string $b
   * @return MissionRule
   */
  public static function convertElement($row, $a='', $b='')
  { return parent::convertElement(new MissionObjective(), self::getClassVars(), $row); }

}
<file_sep>/core/bean/ExpansionBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe ExpansionBean
 * @author Hugues
 * @since 1.04.24
 * @version 1.08.01
 */
class ExpansionBean extends LocalBean
{
  protected $urlRowAdmin  = 'web/pages/admin/fragments/expansion-row.php';
  protected $urlRowPublic = 'web/pages/public/fragments/expansion-row.php';
  /**
   * @param Expansion $Expansion
   */
  public function __construct($Expansion=null)
  {
    parent::__construct();
    $this->Expansion = ($Expansion==null ? new Expansion() : $Expansion);
    $this->ExpansionServices = new ExpansionServices();
    $this->EquipmentExpansionServices = new EquipmentExpansionServices();
    $this->SpawnServices = new SpawnServices();
  }

  //////////////////////////////////////////////////////////////////////////
  // Différentes modes de présentation
  /**
   * @return string
   */
  public function getRowForAdminPage()
  {
    ///////////////////////////////////////////////////////////////////////////
    // On enrichit le template
    $args = array(
      // L'identifiant de l'extension - 1
      $this->Expansion->getId(),
      // Le code de l'extension - 2
      $this->Expansion->getCode(),
      // L'url d'édition du WpPost - 3
      $this->Expansion->getWpPostEditUrl(),
      // L'url publique de l'extension - 4
      $this->Expansion->getWpPostUrl(),
      // Son nom - 5
      $this->Expansion->getName(),
      // Son rang d'affichage - 6
      $this->Expansion->getDisplayRank(),
      // Le nombre de Survivants - 7
      $this->Expansion->getNbSurvivants(),
      // Le nombre de Missions - 8
      $this->Expansion->getNbMissions(),
      // Est une Mission officielle - 9
      ($this->Expansion->isOfficial() ? 'Oui' : 'Non'),
      // Lien de détail de l'extension - 10
      $this->Expansion->getEditUrl(self::CST_EXPANSION),
    );
    ///////////////////////////////////////////////////////////////
    // Puis on le retourne
    return $this->getRender($this->urlRowAdmin, $args);
  }

  /**
   * @return string
   */
  public function getRowForPublicPage()
  {
    ///////////////////////////////////////////////////////////////
    // On enrichi le template et on le retourne.
    $args = array(
      // Front Url de la Compétence - 1
      $this->Expansion->getWpPostUrl(),
      // Nom de la Compétence - 2
      $this->Expansion->getName(),
      // Identifiant de la Compétence - 3
      $this->Expansion->getId(),
      // Nb de Survivants / Dalles / Missions - 4
      $this->getExpansionDetails(),
      // Détails des Zombies - 5
      $this->getZombiesDetails(),
      // Cartes Equipement et Spawn - 6
      $this->getCardsDetails(),
      // Officiel ou non - 7
      ($this->Expansion->isOfficial() ? 'Officielle' : 'Custom'),
    );
    return $this->getRender($this->urlRowPublic, $args);
  }

  public function getCardsDetails()
  {
    $arr = array();
    $EquipmentCards = $this->EquipmentExpansionServices->getEquipmentExpansionsWithFilters(array(self::FIELD_EXPANSIONID=>$this->Expansion->getId()));
    if (!empty($EquipmentCards)) {
      $sum = 0;
      while (!empty($EquipmentCards)) {
        $EquipmentCard = array_shift($EquipmentCards);
        $sum += $EquipmentCard->getQuantity();
      }
      array_push($arr, $sum.' Cartes Équipement');
    }
    $SpawnCards = $this->SpawnServices->getSpawnsWithFilters(array(self::FIELD_EXPANSIONID=>$this->Expansion->getId()), self::FIELD_SPAWNNUMBER);
    if (!empty($SpawnCards)) {
      $First = array_shift($SpawnCards);
      $Last = array_pop($SpawnCards);
      array_push($arr, 'Cartes Spawns : #'.str_pad($First->getSpawnNumber(), 3, '0', STR_PAD_LEFT).' à #'.str_pad($Last->getSpawnNumber(), 3, '0', STR_PAD_LEFT));
    }
    return implode('<br>', $arr);
  }

  private function getZombiesDetails()
  { return 'WIP'; }

  public function getExpansionDetails()
  {
    $arr = array();
    /////////////////////////////////////////////////////////////////////////////
    // On affiche le nombre de Survivants si nécessaire
    if ($this->Expansion->getNbSurvivants()!=0) {
      array_push($arr, $this->Expansion->getNbSurvivants().' Survivants');
    }
    /////////////////////////////////////////////////////////////////////////////
    // On affiche le nombre de Dalles si nécessaire
    if (self::isAdmin()) {
      $Tiles = $this->Expansion->getTiles();
      if (count($Tiles)!=$this->Expansion->getNbDalles()) {
        $this->Expansion->setNbDalles(count($Tiles));
        $this->ExpansionServices->updateExpansion($this->Expansion);
      }
    }
    if ($this->Expansion->getNbDalles()!=0) {
      array_push($arr, $this->Expansion->getNbDalles().' Dalles');
    }
    /////////////////////////////////////////////////////////////////////////////
    // On met à jour le nombre de Missions si nécessaire puis on le restitue
    $Missions = $this->Expansion->getMissions();
    if (count($Missions)!=$this->Expansion->getNbMissions() && !empty($Missions)) {
      $this->Expansion->setNbMissions(count($Missions));
      $this->ExpansionServices->updateExpansion($this->Expansion);
    }
    if ($this->Expansion->getNbMissions()!=0) {
      array_push($arr, $this->Expansion->getNbMissions().' Missions');
    }
    return implode('<br>', $arr);
  }















  /**
   * @return string
   */
  public function getButton($extraClass='btn-dark')
  {
    $str  = '<div type="button" class="btn btn-expansion'.$extraClass.'" data-expansion-id="'.$this->Expansion->getId();
    $str .= '" data-nb-survivants="'.$this->Expansion->getNbSurvivants();
    return $str. '"><span><i class="far fa-square"></i></span> '.$this->Expansion->getName().'</div>';
  }




















  ////////////////////////////////////////////////////////////////////////////



  /**
   * @param int $id
   * @return string
   *
  public function getMenuButtonLive($id)
  {
    $Expansion = $this->Expansion;
    $str  = '<div type="button" class="btn btn-dark btn-expansion" data-expansion-id="'.$id.'"><span class="';
    return $str.'"><i class="far fa-square"></i></span> '.$Expansion->getName().'</div>';
  }
  /**
   * @param string $id
   * @param string $spawnSpan
   * @return string
   *
  public function getSpawnMenuButtonLive($id, $spawnSpan)
  {
    $Expansion = $this->Expansion;
    $str  = '<div type="button" class="btn btn-dark btn-expansion" data-expansion-id="'.$id.'"><span data-spawnspan="'.$spawnSpan;
    return $str.'"><i class="far fa-square"></i></span> '.$Expansion->getName().$spawnSpan.'</div>';
  }
  * */
}
<file_sep>/core/actions/HomePageActions.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * HomePageActions
 * @author Hugues
 * @since 1.04.07
 * @version 1.05.09
 */
class HomePageActions extends LocalActions
{
  /**
   * Constructeur
   */
  public function __construct($post)
  {
    parent::__construct();
    $this->post = $post;
  }
  /**
   * Point d'entrée des méthodes statiques.
   * @param array $post
   * @return string
   **/
  public static function dealWithStatic($post)
  {
    $returned = '';
    $Act = new HomePageActions($post);
    if ($post[self::CST_AJAXACTION]==self::AJAX_ADDMORENEWS) {
      $returned = $Act->dealWithGetMoreNews();
    } else {
      $returned = '';
    }
    return $returned;
  }

  /**
   * Récupération du contenu de la page via une requête Ajax.
   * @param array $post
   * @return string
   */
  public function dealWithGetMoreNews()
  {
    $Bean = new WpPageHomeBean();
    return $Bean->addMoreNews($this->post[self::ATTR_VALUE], true, false);
  }
}
<file_sep>/core/bean/MissionBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe MissionBean
 * @author Hugues
 * @since 1.00.00
 * @version 1.07.26
 */
class MissionBean extends LocalBean
{
  protected $urlRowAdmin        = 'web/pages/admin/fragments/mission-row.php';
  protected $urlRowPublic       = 'web/pages/public/fragments/mission-row.php';
  protected $urlArticle         = 'web/pages/public/fragments/mission-article.php';
  /**
   * @param Mission $Mission
   */
  public function __construct($Mission=null)
  {
    parent::__construct();
    $this->Mission = ($Mission==null ? new Mission() : $Mission);
    $this->ExpansionServices = new ExpansionServices();
    $this->MissionServices   = new MissionServices();
    $this->WpPostServices    = new WpPostServices();
  }

  //////////////////////////////////////////////////////////////////////////
  // Différentes modes de présentation
  /**
   * @return string
   */
  public function getRowForAdminPage()
  {
    ///////////////////////////////////////////////////////////////
    // Les infos WpPost regroupées dans une cellule.
    $infosWpPost  = $this->Mission->getTitle().' - '.$this->Mission->getCode().'<br>';
    $infosWpPost .= $this->Mission->getStrDifPlaDur();

    /////////////////////////////////////////////////////////////////
    // On enrichit le template
    $args = array(
      // Identifiant de la Mission - 1
      $this->Mission->getId(),
      // Les infos du WpPost associé - 2
      $infosWpPost,
      // Url d'édition du WpPost - 3
      $this->Mission->getWpPostEditUrl(),
      // Url d'édition de la BDD - 4
      $this->Mission->getEditUrl(self::CST_MISSION),
      // Url pulique de l'article en ligne - 5
      $this->Mission->getWpPostUrl(),
      // Dimensions de la map - 6
      $this->Mission->getHeight().'x'.$this->Mission->getWidth(),
      // Url de la Map - 7
      $this->Mission->getThumbUrl(),
      // Nb d'Objectifs et Règles Spéciales - 8
      $this->getObjRules()
    );
    ///////////////////////////////////////////////////////////////
    // Puis on le retourne
    return $this->getRender($this->urlRowAdmin, $args);
  }
  /**
   * @return string
   */
  public function getRowForPublicPage()
  {
    ///////////////////////////////////////////////////////////////
    // On enrichi le template et on le retourne.
    $urlWpPost = $this->Mission->getWpPostUrl();
    $args = array(
      // L'identifiant de la Mission - 1
      $this->Mission->getId(),
      // L'url pour accéder au détail de la Mission - 2
      $urlWpPost,
      // Le Titre de la Mission - 3
      '['.$this->Mission->getCode().'] - '.$this->Mission->getTitle(),
      // La Difficulté, le nombre de Survivants et la Durée de la Mission - 4
      $this->Mission->getStrDifPlaDur(),
      // La liste des Extensions nécessaires à la Mission - 5
      $this->Mission->getStrExpansions(),
      // L'origine de la publication originelle - 6
      $this->getStrOrigine(),
    );
    ///////////////////////////////////////////////////////////////
    // Puis on le retourne
    return $this->getRender($this->urlRowPublic, $args);
  }
  /**
   * @return string
   */
  public function getContentForHome()
  {
    ///////////////////////////////////////////////////////////////
    // On enrichit le template et on le retourne.
    $args = array(
      // Titre de la Mission - 1
      $this->Mission->getWpPost()->getPostTitle(),
      // Synopsis - 2
      $this->Mission->getWpPost()->getPostContent(),
      // Extensions nécessaires - 3
      $this->getStrExpansions(),
      // Dalles nécessaires - 4
      $this->getStrTiles(),
      // Classes additionnellets - 5
      ' col-12 col-md-6 col-xl-4',
      // Url de l'Article de la Mission - 6
      $this->Mission->getWpPostUrl(),
      // Url de l'img source de la map - 7
      $this->Mission->getThumbUrl(),
      // Url vers la page Missions - 7
      '/'.self::PAGE_MISSION,
      // Difficulté - 9
      $this->getLinkedDifficulty(),
      // Nb de Survivants - 10
      $this->getStrNbJoueurs(),
      // Durée - 11
      $this->getLinkedDuration(),
      '','','','','','','','',
    );
    ///////////////////////////////////////////////////////////////
    // Puis on le retourne
    return $this->getRender($this->urlArticle, $args);
  }

  public function getStrExpansions()
  {
    $expansionIds = unserialize($this->Mission->getWpPost()->getPostMeta('expansionIds'));
    if ($expansionIds=='') {
      if (self::isAdmin()) {
        $strReturned = 'Wip Extensions';
      } else {
        $strReturned = '';
      }
    } else {
      $strReturned = implode(', ', $expansionIds);
    }
    return $strReturned;
  }
  public function getStrTiles()
  {
    $strTileIds = $this->Mission->getWpPost()->getPostMeta('tileIds');
    if ($strTileIds=='') {
      $strTileIds = (self::isAdmin() ? 'Wip Tiles' : '');
    }
    return $strTileIds;
  }
  public function getLinkedDifficulty()
  { return '<a href="/tag/'.strtolower($this->getStrDifficulty()).'">'.$this->getStrDifficulty().'</a>'; }
  private function getStrDifficulty()
  {
    $strLevel = $this->Mission->getWpPost()->getPostMeta(self::FIELD_LEVELID);
    if ($strLevel=='') {
      $strLevel = $this->Mission->getLevel()->getName();
    }
    return $strLevel;
  }
  public function getStrNbJoueurs()
  {
    $strPlayers = $this->Mission->getWpPost()->getPostMeta(self::FIELD_PLAYERID);
    if ($strPlayers=='') {
      $strPlayers = (self::isAdmin() ? 'Wip Nb' : '');
    }
    return $strPlayers.' Survivants';
  }
  public function getLinkedDuration()
  { return '<a href="/tag/'.strtolower(str_replace(' ', '-', $this->getStrDuree())).'">'.$this->getStrDuree().'</a>'; }
  private function getStrDuree()
  {
    $strDuree = $this->Mission->getWpPost()->getPostMeta(self::FIELD_DURATIONID);
    return ($strDuree=='' ? $this->getMission()->getDuration()->getStrDuree() : $strDuree.' minutes');
  }
  private function getStrOrigine()
  {
    $str = $this->Mission->getStrOrigine();
    if (empty($str)) {
      $str = 'TODO';
    }
    return $str;
  }
  // Fin des extras pour l'affichage d'un article de la Home
  ///////////////////////////////////////////////////////////////

  public function getObjRules()
  {
    $WpPosts = $this->WpPostServices->getWpPostsByCustomField(self::FIELD_MISSIONID, $this->Mission->getWpPost()->getID());
    $nbObjs = 0;
    $nbRules = 0;
    while (!empty($WpPosts)) {
      $WpPost = array_shift($WpPosts);
      $WpCategories = $WpPost->getCategories();
      while (!empty($WpCategories)) {
        $WpCategory = array_shift($WpCategories);
        if ($WpCategory->getTermTaxonomyId()==self::WP_CAT_OBJECTIVE_ID) {
          $nbObjs++;
        } elseif ($WpCategory->getTermTaxonomyId()==self::WP_CAT_RULE_ID) {
          $nbRules++;
        }
      }
    }
    return $nbObjs.' Objectifs<br>'.$nbRules.' Règles Spéciales';
  }








  protected $urlTemplateExtract = 'web/pages/public/fragments/mission-article.php';
  protected $urlTemplateHome    = 'web/pages/public/fragments/mission-article-home.php';
  protected $strModelObjRules   = '<li class="objRule hasTooltip"><span class="tooltip"><header>%1$s</header><div>%2$s</div></span></li>';
  protected $h5Ul               = '<h5>%1$s</h5><ul>%2$s</ul>';


  /**
   * Class par défaut du Select
   * @var $classe
   */
  public $classe = 'custom-select custom-select-sm filters';

  private function getMissionContentObjRules($categId, $label)
  {
    $WpPosts = $this->WpPostServices->getWpPostsByCustomField(self::FIELD_MISSIONID, $this->Mission->getWpPost()->getID());
    $strObj = array();
    while (!empty($WpPosts)) {
      $WpPost = array_shift($WpPosts);
      $WpCategories = $WpPost->getCategories();
      $isObj = false;
      while (!empty($WpCategories)) {
        $WpCategory = array_shift($WpCategories);
        if ($WpCategory->getCatId()==$categId) {
          $isObj = true;
        }
      }
      if ($isObj) {
        $rank = $WpPost->getPostMeta('rang');
        $strObj[$rank] = vsprintf($this->strModelObjRules, array($WpPost->getPostTitle(), $WpPost->getPostContent()));
      }
    }
    if (!empty($strObj)!=0) {
      ksort($strObj);
      return vsprintf($this->h5Ul, array($label, implode('', $strObj)));
    } else {
      return vsprintf($this->h5Ul, array($label, 'Non saisis pour le moment.'));
    }
  }
  public function getMissionContentObjectives()
  { return $this->getMissionContentObjRules(self::WP_CAT_OBJECTIVE_ID, 'Objectifs'); }
  public function getMissionContentRules()
  { return $this->getMissionContentObjRules(self::WP_CAT_RULE_ID, 'Regles speciales'); }
  /**
   * @return Mission
   */
  public function getMission()
  { return $this->Mission; }







}
<file_sep>/core/bean/WpPostSurvivorBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe WpPostSurvivorBean
 * @author Hugues
 * @since 1.04.00
 * @version 1.07.19
 */
class WpPostSurvivorBean extends WpPostBean
{
  protected $urlTemplate = 'web/pages/public/wppage-survivor.php';
  protected $arrLvls = array(1=>'S', 2=>'Z', 3=>'U', 4=>'UZ');
  protected $tplPortraitAndSkills = '<div class="col-2" style="margin-bottom:5px;">%1$s</div><div class="col-10" style="margin-bottom:5px;">%2$s</div>';

  /**
   * Class Constructor
   */
  public function __construct($WpPost)
  {
    parent::__construct();
    $this->SurvivorServices      = new SurvivorServices();
    $this->WpPost = $WpPost;
    $postMetas = $this->WpPost->getPostMetas();
    $survivorId = $postMetas[self::FIELD_SURVIVORID][0];
    $this->Survivor = $this->SurvivorServices->selectSurvivor($survivorId);
  }
  /**
   * @return string
   */
  public function displayWpPost()
  { return $this->Survivor->getBean()->getContentForHome(); }
  /**
   * On retourne la page dédiée au Survivant.
   * @return string
   */
  public function getContentPage()
  {
    // On va construire les lignes portrait + compétences
    //
    $strPortraitsSkills = '';
    if ($this->Survivor->isStandard()) {
      $arg = array(
        // Le portrait - 1
        $this->Survivor->getBean()->getPortrait(),
        // Les Compétences - 2
        $this->Survivor->getBean()->getSkills(),
      );
      $strPortraitsSkills .= vsprintf($this->tplPortraitAndSkills, $arg);
    }
    if ($this->Survivor->isZombivor()) {
      $arg = array(
        // Le portrait - 1
        $this->Survivor->getBean()->getPortrait('z'),
        // Les Compétences - 2
        $this->Survivor->getBean()->getSkills('z'),
      );
      $strPortraitsSkills .= vsprintf($this->tplPortraitAndSkills, $arg);
    }
    if ($this->Survivor->isUltimate()) {
      $arg = array(
        // Le portrait - 1
        $this->Survivor->getBean()->getPortrait('u'),
        // Les Compétences - 2
        $this->Survivor->getBean()->getSkills('u'),
      );
      $strPortraitsSkills .= vsprintf($this->tplPortraitAndSkills, $arg);
    }
    if ($this->Survivor->isUltimatez()) {
      $arg = array(
        // Le portrait - 1
        $this->Survivor->getBean()->getPortrait('uz'),
        // Les Compétences - 2
        $this->Survivor->getBean()->getSkills('uz'),
      );
      $strPortraitsSkills .= vsprintf($this->tplPortraitAndSkills, $arg);
    }

    //////////////////////////////////////////////////////////////////
    // On enrichit le template puis on le restitue.
    $args = array(
      // Nom du Survivant - 1
      $this->Survivor->getName(),
      // Background du Survivant - 2
      $this->Survivor->getBackground(),
      // Les lignes portrait + compétences - 3
      $strPortraitsSkills,
      // Lien de navigation - 4
      $this->getNavLinks(),
    );
    return $this->getRender($this->urlTemplate, $args);
  }
  private function getNavLinks()
  {
    //////////////////////////////////////////////////////////////////
    // On construit les liens de navigation
    // On récupère tous les Survivants, classées par ordre alphabétique.
    $Survivors = $this->SurvivorServices->getSurvivorsWithFilters();
    $firstSurvivor = null;
    while (!empty($Survivors)) {
      $Survivor = array_shift($Survivors);
      // On les parcourt jusqu'à trouver la courante.
      if ($Survivor->getId()==$this->Survivor->getId()) {
        break;
      }
      if ($firstSurvivor==null) {
        $firstSurvivor = $Survivor;
      }
      $prevSurvivor = $Survivor;
    }
    $nextSurvivor = array_shift($Survivors);
    if (empty($prevSurvivor)) {
      $prevSurvivor = array_pop($Survivors);
    }
    if (empty($nextSurvivor)) {
      $nextSurvivor = $firstSurvivor;
    }

    $nav = '';
    // On exploite la précédente et la suivante.
    if (!empty($prevSurvivor)) {
      $attributes = array(self::ATTR_HREF=>$prevSurvivor->getWpPost()->getPermalink(), self::ATTR_CLASS=>'adjacent-link col-3');
      $nav .= $this->getBalise(self::TAG_A, '&laquo; '.$prevSurvivor->getWpPost()->getPostTitle(), $attributes);
    }
    if (!empty($nextSurvivor)) {
      $attributes = array(self::ATTR_HREF=>$nextSurvivor->getWpPost()->getPermalink(), self::ATTR_CLASS=>'adjacent-link col-3');
      $nav .= $this->getBalise(self::TAG_A, $nextSurvivor->getWpPost()->getPostTitle().' &raquo;', $attributes);
    }
    return $nav;
  }






  /**
   * @return Survivor
   */
  public function getSurvivor()
  { return $this->Survivor; }
}
<file_sep>/core/services/MissionTileServices.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe MissionTileServices
 * @author Hugues.
 * @since 1.04.07
 * @version 1.07.25
 */
class MissionTileServices extends LocalServices
{
  /**
   * L'objet Dao pour faire les requêtes
   * @var MissionTileDaoImpl $Dao
   */
  protected $Dao;
  /**
   * Class Constructor
   */
  public function __construct()
  {
    parent::__construct();
    $this->Dao = new MissionTileDaoImpl();
  }

  /**
   * @param array $arrFilters
   */
  private function buildFilters($arrFilters)
  {
    $this->arrParams[self::SQL_WHERE] = array();
    array_push($this->arrParams[self::SQL_WHERE], $this->addFilter($arrFilters, self::FIELD_MISSIONID));
    array_push($this->arrParams[self::SQL_WHERE], $this->addFilter($arrFilters, self::FIELD_COORDX));
    array_push($this->arrParams[self::SQL_WHERE], $this->addFilter($arrFilters, self::FIELD_COORDY));
  }
  /**
   * @param array $arrFilters
   * @param string $orderby
   * @param string $order
   * @return array
   */
  public function getMissionTilesWithFilters($arrFilters=array(), $orderby=self::FIELD_ID, $order=self::ORDER_ASC)
  {
    $this->arrParams = $this->buildOrderAndLimit($orderby, $order);
    $this->buildFilters($arrFilters);
    return $this->Dao->selectEntriesWithFilters(__FILE__, __LINE__, $this->arrParams);
  }

  public function insertMissionTile($MissionTile)
  { return $this->insert(__FILE__, __LINE__, $MissionTile); }
}
<file_sep>/core/domain/EquipmentKeyword.class.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe EquipmentKeyword
 * @author Hugues.
 * @version 1.0.00
 * @since 1.0.00
 */
class EquipmentKeyword extends LocalDomain
{
  /**
   * Id technique de la jointure
   * @var int $id
   */
  protected $id;
  /**
   * Id technique de la carte Equipement
   * @var int $equipmentCardId
   */
  protected $equipmentCardId;
  /**
   * Id technique du profil de l'arme
   * @var int $keywordId
   */
  protected $keywordId;
  /**
   * @return int
   */
  public function getId()
  { return $this->id; }
  /**
   * @ return int
   */
  public function getEquipmentCardId()
  { return $this->equipmentCardId; }
  /**
   * @ return int
   */
  public function getKeywordId()
  { return $this->keywordId; }
  /**
   * @param int $id
   */
  public function setId($id)
  { $this->id=$id; }
  /**
   * @param int $equipmentCardId
   */
  public function setEquipmentCardId($equipmentCardId)
  { $this->equipmentCardId = $equipmentCardId; }
  /**
   * @param int $keywordId
   */
  public function setKeywordId($keywordId)
  { $this->keywordId = $keywordId; }
  /**
   * @return array
   */
  public function getClassVars()
  { return get_class_vars('EquipmentKeyword'); }
  /**
   * @param array $row
   * @param string $a
   * @param string $b
   * @return EquipmentKeyword
   */
  public static function convertElement($row, $a='', $b='')
  { return parent::convertElement(new EquipmentKeyword(), self::getClassVars(), $row); }
  /**
   * @return Keyword
   */
  public function getKeyword()
  {
    if ($this->Keyword == null) {
      $this->Keyword = $this->KeywordServices->selectKeyword($this->keywordId);
    }
    return $this->Keyword;
  }
}
<file_sep>/core/services/EquipmentServices.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe EquipmentServices
 * @author Hugues.
 * @since 1.04.15
 * @version 1.04.27
 */
class EquipmentServices extends LocalServices
{
  /**
   * L'objet Dao pour faire les requêtes
   * @var EquipmentDaoImpl $Dao
   */
  protected $Dao;
  /**
   * Class Constructor
   */
  public function __construct()
  {
    parent::__construct();
    $this->Dao = new EquipmentDaoImpl();
  }
  /**
   * @param int $equipmentId
   * @return Equipment
   */
  public function selectEquipment($equipmentId)
  { return $this->select(__FILE__, __LINE__, $equipmentId); }
}
<file_sep>/core/bean/AdminPageSkillsBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * AdminPageSkillsBean
 * @author Hugues
 * @since 1.00.00
 * @version 1.05.07
 */
class AdminPageSkillsBean extends AdminPageBean
{
  protected $tplHomeCheckCard  = 'web/pages/admin/fragments/home-check-card.php';
  protected $urlSkillListing = 'web/pages/admin/skill-listing.php';
  /**
   * Class Constructor
   */
  public function __construct($urlParams='')
  {
    $this->urlParams = $urlParams;
    parent::__construct(self::CST_SKILL);
    $this->title = 'Compétences';
    $this->SkillServices  = new SkillServices();
  }
  /**
   * @param array $urlParams
   * @return $Bean
   */
  public function getSpecificContentPage()
  {
    $strRows = '';
    $nbPerPage = 15;
    $curPage = $this->initVar(self::WP_CURPAGE, 1);
    $orderby = $this->initVar(self::WP_ORDERBY, self::FIELD_NAME);
    $order = $this->initVar(self::WP_ORDER, self::ORDER_ASC);
    $Skills = $this->SkillServices->getSkillsWithFilters(array(), $orderby, $order);
    $nbElements = count($Skills);
    $nbPages = ceil($nbElements/$nbPerPage);
    $curPage = max(1, min($curPage, $nbPages));
    $DisplayedSkills = array_slice($Skills, ($curPage-1)*$nbPerPage, $nbPerPage);
    if (!empty($DisplayedSkills)) {
      foreach ($DisplayedSkills as $Skill) {
        $SkillBean = new SkillBean($Skill);
        $strRows .= $SkillBean->getRowForAdminPage();
      }
    }
    $queryArg = array(
      self::CST_ONGLET => self::CST_SKILL,
      self::WP_ORDERBY => $orderby,
      self::WP_ORDER   => $order
    );
    // Pagination
    $strPagination = $this->getPagination($queryArg, $post_status, $curPage, $nbPages, $nbElements);
    // Sorts
    $queryArg[self::WP_ORDERBY] = self::FIELD_CODE;
    $queryArg[self::WP_ORDER] = ($orderby==self::FIELD_CODE && $order==self::ORDER_ASC ? self::ORDER_DESC : self::ORDER_ASC);
    $urlSortCode = $this->getQueryArg($queryArg);
    $queryArg[self::WP_ORDERBY] = self::FIELD_NAME;
    $queryArg[self::WP_ORDER] = ($orderby==self::FIELD_NAME && $order==self::ORDER_ASC ? self::ORDER_DESC : self::ORDER_ASC);
    $urlSortTitle = $this->getQueryArg($queryArg);
    $args = array(
      // Liste des compétences affichées - 1
      $strRows,
      // Filtres - 2
      '',
      // Url pour créer une nouvelle Compétence - 3
      '/wp-admin/post-new.php',
      // Subs - 4
      '',
      // Pagination - 5
      $strPagination,
      // class pour le tri sur code - 6
      ($orderby==self::FIELD_CODE ? $order : self::ORDER_DESC),
      // url pour le tri sur code - 7
      $urlSortCode,
      // class pour le tri sur title - 8
      ($orderby==self::FIELD_NAME ? $order : self::ORDER_DESC),
      // url pour le tri sur title - 9
      $urlSortTitle,
      '','','','','','','','','','','','',''
    );
    return $this->getRender($this->urlSkillListing, $args);
  }

  /**
   * @return string
   */
  public function getCheckCard()
  {
    /////////////////////////////////////////////////
    // Gestion des Compétences.
    // On récupère la liste des Compétences qui ont un Article. Puis les données dans la base. On compare et on effectue un diagnostic.
    $Act = new SkillActions();
    $strBilan  = $Act->dealWithSkillVerif();

    $args = array(
      // Le titre de la carte - 1
      $this->title,
      // L'id du container de retour pour afficher les vérifications - 2
      self::CST_SKILL,
      // Le contenu du container de vérification - 3
      $strBilan,
   );
    return $this->getRender($this->tplHomeCheckCard, $args);
  }
}
<file_sep>/core/services/OrigineServices.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe OrigineServices
 * @author Hugues.
 * @since 1.04.00
 * @version 1.04.27
 */
class OrigineServices extends LocalServices
{
  /**
   * L'objet Dao pour faire les requêtes
   * @var OrigineDaoImpl $Dao
   */
  protected $Dao;
  /**
   * Class Constructor
   */
  public function __construct()
  {
    parent::__construct();
    $this->Dao = new OrigineDaoImpl();
  }

  /**
   * @param array $arrFilters
   */
  private function buildFilters($arrFilters)
  {
    $this->arrParams[self::SQL_WHERE] = array();
    array_push($this->arrParams[self::SQL_WHERE], $this->addFilter($arrFilters, self::FIELD_NAME));
  }
  /**
   * @param array $arrFilters
   * @param string $orderby
   * @param string $order
   * @return array
   */
  public function getOriginesWithFilters($arrFilters=array(), $orderby=self::FIELD_NAME, $order=self::ORDER_ASC)
  {
    $this->arrParams = $this->buildOrderAndLimit($orderby, $order);
    $this->buildFilters($arrFilters);
    return $this->Dao->selectEntriesWithFilters(__FILE__, __LINE__, $this->arrParams);
  }
  /**
   * @param int $id
   * @return Origine
   */
  public function selectOrigine($id)
  { return $this->select(__FILE__, __LINE__, $id); }
}
<file_sep>/core/bean/SpawnTypeBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe SpawnTypeBean
 * @author Hugues
 * @since 1.0.00
 * @version 1.0.00
 */
class SpawnTypeBean extends LocalBean
{
  /**
   * Class Constructor
   * @param SpawnType $SpawnType
   */
  public function __construct($SpawnType='')
  {
    parent::__construct();
    $this->SpawnType = ($SpawnType=='' ? new SpawnType() : $SpawnType);
  }
  /**
   * @param string $tBodyButtons Template des Boutons de fin de ligne
   * @return string
   */
  public function getRowForAdminPage($tBodyButtons)
  {
    $SpawnType = $this->SpawnType;
    $queryArg = array(
      self::CST_ONGLET=>'parametre',
      self::CST_POSTACTION=>'edit',
      'table'=>'spawntype',
      'id'=>$SpawnType->getId()
    );
    $urlEdit = $this->getQueryArg($queryArg);
    $queryArg[self::CST_POSTACTION] = 'trash';
    $urlTrash = $this->getQueryArg($queryArg);
    $args = array(
      '&nbsp;',
      $urlEdit,
      $urlTrash
    );
    $tBody  = '<tr><td>'.$SpawnType->getId().'</td><td>'.$SpawnType->getName();
    return $tBody.vsprintf($tBodyButtons, $args).'</tr>';
  }
}
<file_sep>/core/bean/WpPageBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * WpPageBean
 * @author Hugues
 * @since 1.04.00
 * @version 1.07.21
 */
class WpPageBean extends MainPageBean
{
  protected $urlTemplateDropdown = 'web/pages/public/fragments/dropdown-nbperpages.php';
  protected $urlTemplateNavPagination = 'web/pages/public/fragments/nav-pagination.php';
  /**
   * WpPost affiché
   * @var WpPost $WpPage
   */
  protected $WpPage;
  /**
   * @param string $post
   */
  public function __construct($post='')
  {
    if ($post=='') {
      $post = get_post();
    }
    if ($post!='') {
      if (get_class($post)=='WpPost') {
        $this->WpPage = $post;
      } else {
        $this->WpPage = WpPost::convertElement($post);
      }
    }
    parent::__construct();
  }
  /**
   * @return string|Error404PageBean
   */
  public function getContentPage()
  {
    switch ($this->WpPage->getPostName()) {
      case self::PAGE_EQUIPMENT         :
        $Bean = new WpPageEquipmentsBean($this->WpPage);
        $strReturned = $Bean->getContentPage();
      break;
      case self::PAGE_EXTENSION         :
        $Bean = new WpPageExpansionsBean($this->WpPage);
        $strReturned = $Bean->getContentPage();
      break;
      case 'page-travaux'    :
        $Bean = new WpPageMissionOnlineBean($this->WpPage);
        $strReturned = $Bean->getContentPage();
      break;
      case self::PAGE_GENERATION_MAP    :
        $Bean = new WpPageToolsBean($this->WpPage);
        $strReturned = $Bean->getRandomMapV2();
      break;
      case self::PAGE_MISSION           :
        $Bean = new WpPageMissionsBean($this->WpPage);
        $strReturned = $Bean->getContentPage();
      break;
      case self::PAGE_ORDRE_PRIORITE    :
        $Bean = new WpPageToolsBean($this->WpPage);
        $strReturned = $Bean->getPriorityOrderContent();
      break;
      case self::PAGE_PISTE_DE_DES      :
        $Bean = new WpPageToolsBean($this->WpPage);
        $strReturned = $Bean->getThrowSomeDiceContent();
      break;
      case self::PAGE_SELECT_SURVIVORS  :
        $Bean = new WpPageToolsBean($this->WpPage);
        $strReturned = $Bean->getSelectSurvivorsContent();
      break;
      case self::PAGE_SKILL             :
      case 'page-skills'                :
        $Bean = new WpPageSkillsBean($this->WpPage);
        $strReturned = $Bean->getContentPage();
      break;
      case self::PAGE_SPAWN             :
        $Bean = new WpPageSpawnsBean($this->WpPage);
        $strReturned = $Bean->getContentPage();
      break;
      case self::PAGE_SURVIVOR          :
        $Bean = new WpPageSurvivorsBean($this->WpPage);
        $strReturned = $Bean->getContentPage();
      break;
      case self::PAGE_TILE              :
        $Bean = new WpPageTilesBean($this->WpPage);
        $strReturned = $Bean->getContentPage();
      break;
      default                          :
        if ($this->isAdmin()) {
          echo "[[".$this->WpPage->getPostName()."]]";
        }
        $Bean = new WpPageError404Bean();
        $strReturned = $Bean->getContentPage();
      break;
    }
    return $strReturned;
  }
  /**
   * {@inheritDoc}
   * @see MainPageBean::getShellClass()
   */
  public function getShellClass()
  { return ''; }


  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  protected function getDropdownNbPerPages()
  {
    //////////////////////////////////////////////////////////////////
    // On enrichi le template puis on le restitue.
    $args = array(
      ($this->nbperpage==10 ? self::CST_SELECTED : ''),
      ($this->nbperpage==25 ? self::CST_SELECTED : ''),
      ($this->nbperpage==50 ? self::CST_SELECTED : ''),
    );
    return $this->getRender($this->urlTemplateDropdown, $args);
  }
  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  // Méthodes relatives à la pagination
  /**
   * Retourne le bloc de pagination complet
   * @return string
   */
  protected function getNavPagination()
  {
    /////////////////////////////////////////////////////////////////////////////
    // On construit les liens de la pagination.
    $strPagination = $this->getPaginateLis($this->paged, $this->nbPages);
    /////////////////////////////////////////////////////////////////////////////

    //////////////////////////////////////////////////////////////////
    // On enrichi le template puis on le restitue.
    $args = array(
      // Nb Total - 1
      $this->nbElements,
      // Si page 1, on peut pas revenir à la première - 2
      ($this->paged==1 ? ' '.self::CST_DISABLED : ''),
      // Liste des éléments de la Pagination - 3
      $strPagination,
      // Si page $nbPages, on peut pas aller à la dernière - 4
      ($this->paged==$this->nbperpage ? ' '.self::CST_DISABLED : ''),
      // Nombre de pages - 5
      $this->nbperpage,
      // S'il n'y a qu'une page, la pagination ne sert à rien - 6
      ($this->nbPages<=1 ? ' '.self::CST_HIDDEN : ''),
    );
    return $this->getRender($this->urlTemplateNavPagination, $args);
  }
  /**
   * Retourne la liste des liens numérotés d'une pagination
   * @param int $curPage Page courante
   * @param int $nbPages Nombre de pages
   * @return string
   */
  private function getPaginateLis($curPage, $nbPages)
  {
    $strPagination = '';
    //////////////////////////////////////////////////////////////////////////
    // On renseigne la page 1
    $strPagination .= $this->buildPaginationElement(1, $curPage);
    //////////////////////////////////////////////////////////////////////////

    $hasPrevIgnore = false;
    $hasNextIgnore = false;

    for ($i=2; $i<$nbPages; $i++) {
      if ($i<$curPage-1) {
        if (!$hasPrevIgnore) {
          $strPagination .= $this->buildPaginationElement('...', '...');
        }
        $hasPrevIgnore = true;
      } elseif ($i>$curPage+1) {
        if (!$hasNextIgnore) {
          $strPagination .= $this->buildPaginationElement('...', '...');
        }
        $hasNextIgnore = true;
      } else {
        $strPagination .= $this->buildPaginationElement($i, $curPage);
      }
    }

    //////////////////////////////////////////////////////////////////////////
    // On renseigne la page 12
    return $strPagination.$this->buildPaginationElement($nbPages, $curPage);
  }
  private function buildPaginationElement($i, $curPage)
  {
    $attributes = array(
      self::ATTR_CLASS => 'page-link '.self::CST_AJAXACTION,
      self::ATTR_HREF  => '#',
      self::ATTR_DATA_PAGED => $i,
      self::ATTR_DATA_AJAXACTION => self::AJAX_PAGED,
    );
    $label = $this->getBalise(self::TAG_A, $i, $attributes);
    if ($i=='...') {
      $attrClass = ' '.self::CST_DISABLED;
    } elseif ($i==$curPage) {
      $attrClass = ' '.self::CST_ACTIVE;
    } else {
      $attrClass = '';
    }
    $argsBalise = array(self::ATTR_CLASS => 'page-item'.$attrClass);
    return $this->getBalise(self::TAG_LI, $label, $argsBalise);
  }
  // Fin des méthodes relatives à la pagination
  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


  /**
   * @param array $post
   * @param string $fieldNameTitle
   */
  public function setBeanFilters($post=null, $fieldNameTitle='')
  {
    $this->arrFilters = array();
    if (isset($post[self::CST_FILTERS])) {
      $arrParams = explode('&', $post[self::CST_FILTERS]);
      while (!empty($arrParams)) {
        $arrParam = array_shift($arrParams);
        list($key, $value) = explode('=', $arrParam);
        if ($value!='') {
          $this->arrFilters[$key]= $value;
        }
      }
    }
    $this->paged     = (isset($post[self::AJAX_PAGED]) ? $post[self::AJAX_PAGED] : 1);
    $this->colSort   = (isset($post[self::CST_COLSORT]) ? $post[self::CST_COLSORT] : $fieldNameTitle);
    $this->colOrder  = (isset($post[self::CST_COLORDER]) ? $post[self::CST_COLORDER] : self::ORDER_ASC);
    $this->nbperpage = (isset($post[self::CST_NBPERPAGE]) ? $post[self::CST_NBPERPAGE] : 10);
  }
  /**
   * @return string
   */
  public function getBeanExpansionFilters($expansionId='', $fieldToCheck=0)
  {
    $selExpansionsId = explode(',', $expansionId);
    $Expansions = $this->ExpansionServices->getExpansionsWithFilters();
    $strReturned = '';
    while (!empty($Expansions)) {
      $Expansion = array_shift($Expansions);
      if ($Expansion->getField($fieldToCheck)==0)
      { continue; }
      $strReturned .= $this->getOption($Expansion->getId(), $Expansion->getName(), $selExpansionsId);
    }
    return $strReturned;
  }
  /**
   * @return string
   */
  public function getBeanSkillFilters($color='', $skillId='')
  {
    switch ($color) {
      case self::COLOR_BLUE :
        $label = 'Bleues';
        $tagLevelIds = '10,11';
      break;
      case self::COLOR_YELLOW :
        $label = 'Jaunes';
        $tagLevelIds = '20';
      break;
      case self::COLOR_ORANGE :
        $label = 'Oranges';
        $tagLevelIds = '30,31';
      break;
      case self::COLOR_RED :
        $label = 'Rouges';
        $tagLevelIds = '40,41,42';
      break;
      default :
        $label = 'Toutes';
        $tagLevelIds = '';
      break;
    }
    $strReturned = $this->getOption('', $label, $skillId);
    if ( $tagLevelIds!='') {
      $filters = array(self::FIELD_TAGLEVELID=>$tagLevelIds);
      $Skills = $this->SkillServices->getSkillsWithFiltersIn($filters);
    } else {
      $Skills = $this->SkillServices->getSkillsWithFilters();
    }
    while (!empty($Skills)) {
      $Skill = array_shift($Skills);
      $strReturned .= $this->getOption($Skill->getId(), $Skill->getName(), $skillId);
    }
    return $strReturned;
  }

  protected function getOption($value, $name, $selection=array())
  {
    $strOption = '<option value="'.$value.'"';
    if (!is_array($selection)) {
      $selection = array($selection);
    }
    if (in_array($value, $selection)) {
      $strOption .= ' selected';
    }
    return $strOption.'>'.$name.'</option>';
  }
}
<file_sep>/core/domain/WpPostRelais.class.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe WpPostRelais
 * @author Hugues.
 * @since 1.05.12
 * @version 1.05.12
 */
class WpPostRelais extends LocalDomain
{

  public function __construct($attributes=array())
  { parent::__construct($attributes); }

  ////////////////////////////////////////////////////////////////////////////
  // Méthodes relatives à l'article WpPost
  /**
   * @return WpPost
   */
  public function getMainWpPost($metakey, $value, $categId)
  {
    if ($this->WpPost==null) {
      $args = array(
        self::WP_METAKEY   => $metakey,
        self::WP_METAVALUE => $value,
        self::WP_TAXQUERY  => array(),
        self::WP_CAT       => $categId,
      );
      if (MainPageBean::isAdmin()) {
        $args[self::WP_POSTSTATUS] = self::WP_PUBLISH.', future';
      }
      $WpPosts = $this->WpPostServices->getArticles($args);
      $this->WpPost = (!empty($WpPosts) ? array_shift($WpPosts) : new WpPost());
    }
    return $this->WpPost;
  }
  /**
   * @return string
   */
  public function getWpPostUrl()
  { return $this->getWpPost()->getPermalink(); }
  /**
   * @return string
   */
  public function getWpPostEditUrl()
  { return ($this->getWpPost()->getID()!='' ? '/wp-admin/post.php?post='.$this->getWpPost()->getID().'&action=edit' : '/wp-admin/post-new.php'); }
  ////////////////////////////////////////////////////////////////////////////

  /**
   * @return string
   */
  public function getEditUrl($onglet)
  {
    $queryArgs = array(
      self::CST_ONGLET     => $onglet,
      self::CST_POSTACTION => self::CST_EDIT,
      self::FIELD_ID       => $this->getId()
    );
    return $this->getQueryArg($queryArgs);
  }

}
<file_sep>/core/services/SurvivorServices.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe SurvivorServices
 * @author Hugues.
 * @since 1.04.27
 * @version 1.05.07
 */
class SurvivorServices extends LocalServices
{
  /**
   * L'objet Dao pour faire les requêtes
   * @var SurvivorDaoImpl $Dao
   */
  protected $Dao;
  /**
   * Class Constructor
   */
  public function __construct()
  {
    parent::__construct();
    $this->Dao = new SurvivorDaoImpl();
  }
  /**
   * @param array $arrFilters
   */
  private function buildFilters($arrFilters)
  {
    $this->arrParams[self::SQL_WHERE] = array();
    array_push($this->arrParams[self::SQL_WHERE], $this->addNonArrayWideFilter($arrFilters, self::FIELD_NAME));
    array_push($this->arrParams[self::SQL_WHERE], $this->addNonArrayFilter($arrFilters, self::FIELD_ZOMBIVOR));
    array_push($this->arrParams[self::SQL_WHERE], $this->addNonArrayWideFilter($arrFilters, self::FIELD_ULTIMATE));
    array_push($this->arrParams[self::SQL_WHERE], $this->addNonArrayFilter($arrFilters, self::FIELD_EXPANSIONID));
    array_push($this->arrParams[self::SQL_WHERE], $this->addNonArrayFilter($arrFilters, self::FIELD_BACKGROUND));
    array_push($this->arrParams[self::SQL_WHERE], $this->addNonArrayFilter($arrFilters, self::FIELD_LIVEABLE));
  }
  /**
   * @param array $arrFilters
   * @param string $orderby
   * @param string $order
   * @return array
   */
  public function getSurvivorsWithFilters($arrFilters=array(), $orderby=self::FIELD_NAME, $order=self::ORDER_ASC)
  {
    $this->arrParams = $this->buildOrderAndLimit($orderby, $order);
    $this->buildFilters($arrFilters);
    if (isset($arrFilters[self::FIELD_EXPANSIONID]) && strpos($arrFilters[self::FIELD_EXPANSIONID], ',')!==false) {
      return $this->Dao->selectEntriesInExpansions($this->arrParams);
    } else {
      return $this->Dao->selectEntriesWithFilters(__FILE__, __LINE__, $this->arrParams);
    }
  }
  /**
   * @param array $arrFilters
   * @param string $orderby
   * @param string $order
   * @return array
   */
  public function getSurvivorsWithFiltersIn($arrFilters=array(), $orderby=self::FIELD_NAME, $order=self::ORDER_ASC)
  {
    $this->arrParams = $this->buildOrderAndLimit($orderby, $order);
    $this->buildFilters($arrFilters);
    return $this->Dao->selectEntriesWithFiltersIn($this->arrParams, $arrFilters);
  }

  public function selectSurvivor($id)
  { return $this->select(__FILE__, __LINE__, $id); }
  public function updateSurvivor($Survivor)
  { return $this->update(__FILE__, __LINE__, $Survivor); }
  public function insertSurvivor($Survivor)
  { return $this->insert(__FILE__, __LINE__, $Survivor); }
}
<file_sep>/core/daoimpl/ExpansionDaoImpl.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe ExpansionDaoImpl
 * @author Hugues.
 * @version 1.0.00
 * @since 1.0.00
 */
class ExpansionDaoImpl extends LocalDaoImpl
{
  /**
   * Class constructor
   */
  public function __construct()
  { parent::__construct('Expansion'); }
  /**
   * @param array $rows
   * @return array
   */
  protected function convertToArray($rows)
  {
    $Items = array();
    if (!empty($rows)) {
      foreach ($rows as $row) {
        $Items[] = Expansion::convertElement($row);
      }
    }
    return $Items;
  }
  /**
   * @param string $file
   * @param int $line
   * @param array $arrParams
   * @return array|Expansion
   */
  public function select($file, $line, $arrParams)
  { return parent::localSelect($file, $line, $arrParams, new Expansion()); }
  /**
   * @param string $file
   * @param string $line
   */
  protected function updateNbMissions($file, $line)
  {
    $subRequest = 'SELECT COUNT(*) FROM wp_11_zombicide_mission_expansion me WHERE me.expansionId=e.id';
    $requete = 'UPDATE wp_11_zombicide_expansion e SET nbMissions = ('.$subRequest.');';
    $this->createEditDeleteEntry($file, $line, $requete);
  }
}
<file_sep>/core/services/MissionRuleServices.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe MissionRuleServices
 * @author Hugues.
 * @since 1.04.08
 * @version 1.04.27
 */
class MissionRuleServices extends LocalServices
{
  /**
   * L'objet Dao pour faire les requêtes
   * @var MissionRuleDaoImpl $Dao
   */
  protected $Dao;
  /**
   * Class Constructor
   */
  public function __construct()
  {
    parent::__construct();
    $this->Dao = new MissionRuleDaoImpl();
  }

  /**
   * @param array $arrFilters
   */
  private function buildFilters($arrFilters)
  {
    $this->arrParams[self::SQL_WHERE] = array();
    array_push($this->arrParams[self::SQL_WHERE], $this->addFilter($arrFilters, self::FIELD_MISSIONID));
    array_push($this->arrParams[self::SQL_WHERE], $this->addFilter($arrFilters, self::FIELD_RULEID));
    array_push($this->arrParams[self::SQL_WHERE], $this->addFilter($arrFilters, self::FIELD_TITLE));
  }
  /**
   * @param array $arrFilters
   * @param string $orderby
   * @param string $order
   * @return array
   */
  public function getMissionRulesWithFilters($arrFilters=array(), $orderby=self::FIELD_ID, $order=self::ORDER_ASC )
  {
    $this->arrParams = $this->buildOrderAndLimit($orderby, $order);
    $this->buildFilters($arrFilters);
    return $this->Dao->selectEntriesWithFilters(__FILE__, __LINE__, $this->arrParams);
  }
}
<file_sep>/core/bean/WpPageMissionsBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe WpPageMissionsBean
 * @author Hugues
 * @since 1.04.01
 * @version 1.05.01
 */
class WpPageMissionsBean extends WpPageBean
{
  protected $urlTemplate = 'web/pages/public/wppage-missions.php';
  /**
   * Class Constructor
   * @param WpPage $WpPage
   */
  public function __construct($WpPage='')
  {
    parent::__construct($WpPage);
    $this->DurationServices  = new DurationServices();
    $this->ExpansionServices = new ExpansionServices();
    $this->LevelServices     = new LevelServices();
    $this->MissionServices   = new MissionServices();
    $this->OrigineServices   = new OrigineServices();
    $this->PlayerServices    = new PlayerServices();
  }
  /**
   * On vérifie si on est ici pour traiter la page des Missions, ou une Mission en particulier.
   * Pour le cas d'une Mission, on retourne une WpPostMissionBean.
   * @return string
   */
  public function getContentPage()
  {
    $this->setFilters();
    return $this->getListContentPage();
  }
  /**
   * @return string
   */
  public function getListContentPage()
  {
    /////////////////////////////////////////////////////////////////////////////
    // On récupère la liste des Missions puis les éléments nécessaires à la pagination.
    $Missions = $this->MissionServices->getMissionsWithFilters($this->arrFilters, $this->colSort, $this->colOrder);
    $this->nbElements = count($Missions);
    $this->nbPages = ceil($this->nbElements/$this->nbperpage);
    // On slice la liste pour n'avoir que ceux à afficher
    $displayedMissions = array_slice($Missions, $this->nbperpage*($this->paged-1), $this->nbperpage);
    // On construit le corps du tableau
    $strBody = '';
    if (!empty($displayedMissions)) {
      foreach ($displayedMissions as $Mission) {
        $strBody .= $Mission->getBean()->getRowForPublicPage();
      }
    }
    /////////////////////////////////////////////////////////////////////////////

    /////////////////////////////////////////////////////////////////////////////
    // Affiche-t-on le filtre ?
    $showFilters = !empty($this->arrFilters);
    /////////////////////////////////////////////////////////////////////////////

    //////////////////////////////////////////////////////////////////
    // On enrichi le template puis on le restitue.
    $args = array(
      // On affiche les lignes du tableau - 1
      $strBody,
      // On affiche le dropdown par pages - 2
      $this->getDropdownNbPerPages(),
      // On affiche la pagination - 3
      $this->getNavPagination(),
      // Affiche ou non le bloc filtre - 4
      ($showFilters ? 'block' : 'none'),
      // Si le Titre est renseigné - 5
      $this->arrFilters[self::FIELD_TITLE],
      // La liste des Difficultés - 6
      $this->getLevelFilters($this->arrFilters[self::FIELD_LEVELID]),
      // La liste des Survivants - 7
      $this->getPlayerFilters($this->arrFilters[self::FIELD_PLAYERID]),
      // La liste des Durée - 8
      $this->getDurationFilters($this->arrFilters[self::FIELD_DURATIONID]),
      // La liste des Origines - 9
      $this->getOrigineFilters($this->arrFilters[self::FIELD_ORIGINEID]),
      // Liste des Extensions - 10
      $this->getExpansionFilters($this->arrFilters[self::FIELD_EXPANSIONID]),
    );
    return $this->getRender($this->urlTemplate, $args);
  }
  private function getExpansionFilters($expansionId='')
  { return parent::getBeanExpansionFilters($expansionId, self::FIELD_NBMISSIONS); }
  private function getLevelFilters($levelId='')
  {
    $Levels = $this->LevelServices->getLevelsWithFilters();
    $strReturned = '<option value="">Difficultés</option>';
    while (!empty($Levels)) {
      $Level = array_shift($Levels);
      $strReturned .= $this->getOption($Level->getId(), $Level->getName(), $levelId);
    }
    return $strReturned;
  }
  private function getPlayerFilters($playerId='')
  {
    $Players = $this->PlayerServices->getPlayersWithFilters();
    $strReturned = '<option value="">Survivants</option>';
    while (!empty($Players)) {
      $Player = array_shift($Players);
      $strReturned .= $this->getOption($Player->getId(), $Player->getNbJoueurs(), $playerId);
    }
    return $strReturned;
  }
  private function getDurationFilters($durationId='')
  {
    $Durations = $this->DurationServices->getDurationsWithFilters();
    $strReturned = '<option value="">Durées</option>';
    while (!empty($Durations)) {
      $Duration = array_shift($Durations);
      $strReturned .= $this->getOption($Duration->getId(), $Duration->getStrDuree(), $durationId);
    }
    return $strReturned;
  }
  private function getOrigineFilters($origineId='')
  {
    $Origines = $this->OrigineServices->getOriginesWithFilters();
    $strReturned = '<option value="">Origine</option>';
    while (!empty($Origines)) {
      $Origine = array_shift($Origines);
      $strReturned .= $this->getOption($Origine->getId(), $Origine->getName(), $origineId);
    }
    return $strReturned;
  }
  /**
   * @param array $post
   */
  public function setFilters($post=null)
  { parent::setBeanFilters($post, self::FIELD_TITLE); }
}
<file_sep>/core/domain/EquipmentExpansion.class.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe EquipmentExpansion
 * @author Hugues.
 * @since 1.0.00
 * @version 1.04.28
 */
class EquipmentExpansion extends LocalDomain
{
  /**
   * Id technique de la jointure
   * @var int $id
   */
  protected $id;
  /**
   * Id technique de la carte Equipement
   * @var int $equipmentCardId
   */
  protected $equipmentCardId;
  /**
   * Id technique de l'Expansion
   * @var int $expansionId
   */
  protected $expansionId;
  /**
   * Nombre de cartes dans l'extension
   * @var int $quantity
   */
  protected $quantity;
  /**
   * @param array $attributes
   */
  public function __construct($attributes=array())
  {
    parent::__construct($attributes);
    $this->EquipmentServices          = new EquipmentServices();
  }
  /**
   * @return int
   */
  public function getId()
  { return $this->id; }
  /**
   * @ return int
   */
  public function getEquipmentCardId()
  { return $this->equipmentCardId; }
  /**
   * @ return int
   */
  public function getExpansionId()
  { return $this->expansionId; }
  /**
   * @ return int
   */
  public function getQuantity()
  { return $this->quantity; }
  /**
   * @param int $id
   */
  public function setId($id)
  { $this->id=$id; }
  /**
   * @param int $equipmentCardId
   */
  public function setEquipmentCardId($equipmentCardId)
  { $this->equipmentCardId = $equipmentCardId; }
  /**
   * @param int $expansionId
   */
  public function setExpansionId($expansionId)
  { $this->expansionId = $expansionId; }
  /**
   * @param int $quantity
   */
  public function setQuantity($quantity)
  { $this->quantity = $quantity; }
  /**
   * @return array
   */
  public function getClassVars()
  { return get_class_vars('EquipmentExpansion'); }
  /**
   * @param array $row
   * @param string $a
   * @param string $b
   * @return EquipmentExpansion
   */
  public static function convertElement($row, $a='', $b='')
  { return parent::convertElement(new EquipmentExpansion(), self::getClassVars(), $row); }
  /**
   * @return Equipment
   */
  public function getEquipment()
  {
    if ($this->Equipment == null) {
      $this->Equipment = $this->EquipmentServices->selectEquipment($this->equipmentCardId);
    }
    return $this->Equipment;
  }
  /**
   * @param Equipment $Equipment
   */
  public function setEquipment($Equipment)
  { $this->Equipment = $Equipment; }
}
<file_sep>/core/bean/SurvivorSkillBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe SurvivorSkillBean
 * @author Hugues.
 * @since 1.05.02
 * @version 1.07.19
 */
class SurvivorSkillBean extends LocalBean
{

  /**
   * @param SurvivorSkill $SurvivorSkill
   */
  public function __construct($SurvivorSkill=null)
  {
    parent::__construct();
    $this->SurvivorSkill = ($SurvivorSkill==null ? new SurvivorSkill() : $SurvivorSkill);
  }

  public function getBadge($linked=false)
  {
    if ($linked) {
      $tag = self::TAG_A;
      $href = '/page-competences/?skillId='.$this->SurvivorSkill->getSkill()->getId();
      $WpPost = $this->SurvivorSkill->getSkill()->getWpPost();
      if ($WpPost->getID()!='') {
        $href = $WpPost->getPermaLink();
      }
      $attributes = array(
        self::ATTR_CLASS => 'badge badge-'.$this->getColor().'-skill',
        self::ATTR_HREF  => $href,
      );
    } else {
      $tag = self:: TAG_SPAN;
      $attributes = array(
        self::ATTR_CLASS => 'badge badge-'.$this->getColor().'-skill',
      );
    }
    return $this->getBalise($tag, $this->SurvivorSkill->getSkill()->getName(), $attributes);
  }

  public function getColor()
  {
    switch ($this->SurvivorSkill->getTagLevelId()) {
      case 20 :
        $color = self::COLOR_YELLOW;
      break;
      case 30 :
      case 31 :
        $color = self::COLOR_ORANGE;
      break;
      case 40 :
      case 41 :
      case 42 :
        $color = self::COLOR_RED;
      break;
      default :
        $color = self::COLOR_BLUE;
      break;
    }
    return $color;
  }
}


<file_sep>/core/bean/WpPageSpawnsBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe WpPageSpawnsBean
 * @author Hugues
 * @since 1.0.00
 * @version 1.0.00
 */
class WpPageSpawnsBean extends WpPageBean
{
  protected $urlTemplate = 'web/pages/public/wppage-spawncards.php';
  /**
   * Class Constructor
   * @param WpPage $WpPage
   */
  public function __construct($WpPage='')
  {
    parent::__construct($WpPage);
    $this->ExpansionServices = new ExpansionServices();
    $this->SpawnServices     = new SpawnServices();
  }
  /**
   * @return string
   */
  public function getContentPage()
  { return $this->getListContentPage(); }
  /**
   * Retourne la liste des cartes Invasions
   * @return string
   */
  public function getListContentPage()
  {
    /////////////////////////////////////////////////////////////////////////////
    // On récupère la liste de toutes les Extensions
    $Expansions = $this->ExpansionServices->getExpansionsWithFilters(array(), self::FIELD_DISPLAYRANK);
    $strFilters = '';
    $strSpawns = '';
    while (!empty($Expansions)) {
      $Expansion = array_shift($Expansions);
      // On récupère les cartes Invasions relatives à l'extension. S'il n'y en a pas, on passe à l'extension suivante.
      $SpawnCards = $this->SpawnServices->getSpawnsWithFilters(array(self::FIELD_EXPANSIONID=>$Expansion->getId()));
      if (empty($SpawnCards)) {
        continue;
      }
      // On en profite aussi pour construire le bloc de filtres.
      $strFilters .= $this->getBalise(self::TAG_OPTION, $Expansion->getName(), array(self::ATTR_VALUE => 'set-'.$Expansion->getId()));
      // On ajoute chaque carte Invasion à la liste à afficher.
      foreach ($SpawnCards as $SpawnCard) {
        $strSpawns .= $SpawnCard->getBean()->displayCard();
      }
    }

    //////////////////////////////////////////////////////////////////
    // On enrichi le template puis on le restitue.
    $args = array(
      // La liste des cartes - 1
      $strSpawns,
      // Les filtres disponibles - 2
      $strFilters,
    );
    return $this->getRender($this->urlTemplate, $args);
  }
}
<file_sep>/core/bean/WpPostMissionBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe WpPostMissionBean
 * @author Hugues
 * @since 1.00.00
 * @version 1.07.25
 */
class WpPostMissionBean extends WpPostBean
{
  protected $urlTemplate        = 'web/pages/public/wppage-mission.php';
  protected $urlImgBase         = '/wp-content/plugins/hj-zombicide/web/rsc/img/missions/';
  protected $urlTemplateArticle = 'web/pages/public/fragments/mission-article.php';




  /**
   * Constructeur
   */
  public function __construct($missionId='')
  {
    parent::__construct();
    $this->MissionServices = new MissionServices();
    if ($missionId instanceof WpPost) {
      $this->WpPost = $missionId;
      $code = $this->WpPost->getPostMeta(self::FIELD_CODE);
      $Missions = $this->MissionServices->getMissionsWithFilters(array(self::FIELD_CODE=>$code));
      $this->Mission = array_shift($Missions);
    } else {
      $this->Mission = $this->MissionServices->selectMission($missionId);
    }
  }
  /**
   * @return string
   */
  public function displayWpPost()
  { return $this->Mission->getBean()->getContentForHome(); }
  /**
   * @return string
   */
  public function getContentPage()
  {
    //////////////////////////////////////////////////////////////////
    // On enrichit le template puis on le restitue.
    $args = array(
      // On affiche la Mission demandée - 1
      $this->getArticlePage(true),
      // Liens de navigation - 2
      $this->getNavLinks(),
      // Contenu additionnel en bas de page - 3
      '',
    );
    return $this->getRender($this->urlTemplate, $args);
  }

  public function getArticlePage($isWhole=false)
  {
    if ($isWhole) {
      $classExtra = 'wholeArticle';
      $imgExt = '-Missions.png';
    } else {
      $classExtra = '';
      $imgExt = '-Thumb.png';
    }

    ///////////////////////////////////////////////////////////////
    // On enrichi le template et on le retourne.
    $args = array(
      // Titre de la Mission - 1
      '['.$this->getMission()->getCode().'] - '.$this->WpPost->getPostTitle(),
      // Synopsis - 2
      $this->WpPost->getPostContent(),
      // Extensions nécessaires - 3
      $this->getMission()->getBean()->getStrExpansions(),
      // Dalles nécessaires - 4
      $this->getMission()->getBean()->getStrTiles(),
      // Classe supplémentaire - 5
      $classExtra,
      // Url de la Mission... - 6
      $this->WpPost->getPermalink(),
      // Image de la Map éventuelle - 7
      $this->urlImgBase.$this->getMission()->getCode().$imgExt,
      // Lien vers la page des Missions - 8
      '/'.self::PAGE_MISSION,
      // Difficulté - 9
      $this->getMission()->getBean()->getLinkedDifficulty(),
      // Nb Survivants - 10
      $this->getMission()->getBean()->getStrNbJoueurs(),
      // Durée - 11
      $this->getMission()->getBean()->getLinkedDuration(),
      // Liste des Objectifs - 12
      $this->getMission()->getBean()->getMissionContentObjectives(),
      // Liste des Règles - 13
      $this->getMission()->getBean()->getMissionContentRules(),
    );
    return $this->getRender($this->urlTemplateArticle, $args);
  }

  private function getNavLinks()
  {
    //////////////////////////////////////////////////////////////////
    // On construit les liens de navigation
    // On récupère toutes les missions, classées par ordre alphabétique.
    $Missions = $this->MissionServices->getMissionsWithFilters(array(), self::FIELD_CODE);
    $firstMission = null;
    while (!empty($Missions)) {
      $Mission = array_shift($Missions);
      // On les parcourt jusqu'à trouver la courante.
      if ($Mission->getId()==$this->Mission->getId()) {
        break;
      }
      if ($firstMission==null) {
        $firstMission = $Mission;
      }
      $prevMission = $Mission;
    }
    $nextMission = array_shift($Missions);
    if (empty($prevMission)) {
      $prevMission = array_pop($Missions);
    }
    if (empty($nextMission)) {
      $nextMission = $firstMission;
    }

    $nav = '';
    // On exploite la précédente et la suivante.
    if (!empty($prevMission)) {
      $attributes = array(self::ATTR_HREF=>$prevMission->getWpPost()->getPermalink(), self::ATTR_CLASS=>'adjacent-link col-3');
      $nav .= $this->getBalise(self::TAG_A, '&laquo; '.$prevMission->getWpPost()->getPostTitle(), $attributes);
    }
    if (!empty($nextMission)) {
      $attributes = array(self::ATTR_HREF=>$nextMission->getWpPost()->getPermalink(), self::ATTR_CLASS=>'adjacent-link col-3');
      $nav .= $this->getBalise(self::TAG_A, $nextMission->getWpPost()->getPostTitle().' &raquo;', $attributes);
    }
    return $nav;
  }

  public function getMission()
  { return $this->Mission; }


}
<file_sep>/core/domain/Equipment.class.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe Equipment
 * @author Hugues.
 * @since 1.0.00
 * @version 1.04.28
 */
class Equipment extends LocalDomain
{
  /**
   * Id technique de la donnée
   * @var int $id
   */
  protected $id;
  /**
   * Nom de la carte
   * @var string $name
   */
  protected $name;
  /**
   * Abilité spéciale de l'équipement
   * @var string $textAbility
   */
  protected $textAbility;
  /**
   * @return $id
   */
  public function getId()
  {return $this->id; }
  /**
   * @return $name
   */
  public function getName()
  { return $this->name; }
  /**
   * @return $textAbility
   */
  public function getTextAbility()
  { return $this->textAblity; }
  /**
   * @param int $id
   */
  public function setId($id)
  { $this->id=$id; }
  /**
   * @param int $name
   */
  public function setName($name)
  { $this->name=$name; }
  /**
   * @param int $textAbility
   */
  public function setTextAbility($textAbility)
  { $this->textAbility=$textAbility; }
  /**
   * @return array
   */
  public function getClassVars()
  { return get_class_vars('Equipment'); }
  /**
   * @param array $row
   * @param string $a
   * @param string $b
   * @return Spawn
   */
  public static function convertElement($row, $a='', $b='')
  { return parent::convertElement(new Equipment(), self::getClassVars(), $row); }
  /**
   * @return string
   */
  public function getNiceName()
  {
    $cleanDigits = array(' ', '#', '-', '!', 'à', 'é', "'", '(', ')', 'ê', 'ç', '&', '.', 'è');
    return str_replace($cleanDigits, '', strtolower($this->getName()));
  }
  /**
   * @param $expansionId
   * @return string
   */
  public function getImgUrl($expansionId='00')
  {
    $uniqueId = (str_pad($this->id, 3, '0', STR_PAD_LEFT)).(str_pad($expansionId, 2, '0', STR_PAD_LEFT));
    $urlThumb = '/wp-content/plugins/zombicide/web/rsc/images/equipments/'.$uniqueId.'-thumb.jpg';
    // Si l'image Thumb n'existe pas, on va la créer à partir de l'original. Puis on supprime l'original.
    if (!is_file(getcwd().$urlThumb)) {
      $urlOriginal = '/wp-content/plugins/zombicide/web/rsc/images/equipments/'.$uniqueId.'.png';
      $adminUrl = getcwd().$urlOriginal;
      $src = imagecreatefrompng($adminUrl);
      $dst = imagecreatetruecolor(320, 440);
      imagecopyresized($dst, $src, 0, 0, 0, 0, 320, 440, 597, 822);
      imagejpeg($dst, getcwd().$urlThumb);
      unlink($adminUrl);
    }
    return $urlThumb;
  }






  /**
   * @return array EquipmentWeaponProfile
   */
  public function getEquipmentWeaponProfiles()
  {
    if ($this->EquipmentWeaponProfiles == null) {
      $arrF = array(self::FIELD_EQUIPMENTCARDID=>$this->id);
      $this->EquipmentWeaponProfiles = $this->EWProfileServices->getEquipmentWeaponProfilesWithFilters($arrF);
    }
    return $this->EquipmentWeaponProfiles;
  }
  /**
   * @return array EquipmenKeyword
   */
  public function getEquipmentKeywords()
  {
    if ($this->EquipmentKeywords == null) {
      $arrFilters = array(self::FIELD_EQUIPMENTCARDID=>$this->id);
      $this->EquipmentKeywords = $this->EquipmentKeywordServices->getEquipmentKeywordsWithFilters($arrFilters);
    }
    return $this->EquipmentKeywords;
  }
  /**
   * @return Keyword
   */
  public function getKeyword()
  {
    if ($this->Keyword == null) {
      $this->Keyword = $this->KeywordServices->selectKeyword($this->keywordId);
    }
    return $this->Keyword;
  }
  /**
   * @return int
   */
  public function getExpansionId()
  { return $this->expansionId; }
  /**
   * @return boolean
   */
  public function isRanged()
  {
    if ($this->ranged == null) {
      if ($this->EquipmentWeaponProfiles == null) {
        $this->EquipmentWeaponProfiles = $this->getEquipmentWeaponProfiles();
      }
      if (empty($this->EquipmentWeaponProfiles)) {
        $this->ranged = false;
      } else {
        $isRanged = false;
        foreach ($this->EquipmentWeaponProfiles as $EquipmentWeaponProfile) {
          $WeaponProfile = $EquipmentWeaponProfile->getWeaponProfile(__FILE__, __LINE__);
          if ($WeaponProfile->getMaxRange()>0) {
            $isRanged = true;
          } else {
            $this->melee = true;
          }
        }
        $this->ranged = $isRanged;
      }
    }
    return $this->ranged;
  }
  /**
   * @return boolean
   */
  public function isMelee()
  {
    if ($this->melee==null) {
      if ($this->EquipmentWeaponProfiles == null) {
        $this->EquipmentWeaponProfiles = $this->getEquipmentWeaponProfiles();
      }
      if (empty($this->EquipmentWeaponProfiles)) {
        $this->melee = false;
      } else {
        $isMelee = false;
        foreach ($this->EquipmentWeaponProfiles as $EquipmentWeaponProfile) {
          $WeaponProfile = $EquipmentWeaponProfile->getWeaponProfile(__FILE__, __LINE__);
          if ($WeaponProfile->getMaxRange()==0) {
            $isMelee = true;
          } else {
            $this->ranged = true;
          }
        }
        $this->melee = $isMelee;
      }
    }
    return $this->melee;
  }
  /**
   * @return boolean
   */
  public function isPimp()
  { return $this->hasKeyword('Pimp'); }
  /**
   * @return boolean
   */
  public function isStarter()
  { return $this->hasKeyword('Starter'); }
  /**
   * @return boolean
   */
  public function isDual()
  { return $this->hasKeyword('Dual'); }
  /**
   * @param string $keyword
   * @return boolean
   */
  public function hasKeyword($keyword)
  {
    $hasKeyword = false;
    if ($this->Keywords == null) {
      $this->initKeywords();
    }
    if (!empty($this->Keywords)) {
      foreach ($this->Keywords as $Keyword) {
        if ($Keyword->getName()==$keyword) {
          $hasKeyword = true;
        }
      }
    }
    return $hasKeyword;
  }
  private function initKeywords()
  {
    if ($this->Keywords==null) {
      $EquipmentKeywords = $this->getEquipmentKeywords();
      $ownKeyWords = array();
      if (!empty($EquipmentKeywords)) {
        foreach ($EquipmentKeywords as $EquipmentKeyword) {
          array_push($ownKeyWords, $EquipmentKeyword->getKeyword(__FILE__, __LINE__));
        }
      }
      $this->Keywords = empty($ownKeyWords) ? array(new Equipment()) : $ownKeyWords;
    }
  }
  /**
   * @param int $expansionId
   */
  public function setExpansionId($expansionId)
  { $this->expansionId = $expansionId; }
}
<file_sep>/core/services/ExpansionServices.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe ExpansionServices
 * @author Hugues.
 * @since 1.04.00
 * @version 1.07.21
 */
class ExpansionServices extends LocalServices
{
  /**
   * L'objet Dao pour faire les requêtes
   * @var ExpansionDaoImpl $Dao
   */
  protected $Dao;
  /**
   * Class constructor
   */
  public function __construct()
  {
    parent::__construct();
    $this->Dao = new ExpansionDaoImpl();
  }

  /**
   * @param array $arrFilters
   */
  private function buildFilters($arrFilters)
  {
    $this->arrParams[self::SQL_WHERE] = array();
    array_push($this->arrParams[self::SQL_WHERE], $this->addNonArrayFilter($arrFilters, self::FIELD_CODE));
    array_push($this->arrParams[self::SQL_WHERE], $this->addNonArrayWideFilter($arrFilters, self::FIELD_NAME));
    array_push($this->arrParams[self::SQL_WHERE], $this->addNonArrayFilter($arrFilters, self::FIELD_NBMISSIONS, 0));
    array_push($this->arrParams[self::SQL_WHERE], $this->addNonArrayFilter($arrFilters, self::FIELD_NBSURVIVANTS, 0));
  }
  /**
   * @param array $arrFilters
   * @param string $orderby
   * @param string $order
   * @return array
   */
  public function getExpansionsWithFilters($arrFilters=array(), $orderby=self::FIELD_NAME, $order=self::ORDER_ASC)
  {
    $this->arrParams = $this->buildOrderAndLimit($orderby, $order);
    $this->buildFilters($arrFilters);
    return $this->Dao->selectEntriesWithFilters(__FILE__, __LINE__, $this->arrParams);
  }
  /**
   * @param int $id
   * @return Expansion
   */
  public function selectExpansion($id)
  { return $this->select(__FILE__, __LINE__, $id); }
  public function updateExpansion($Expansion)
  { $this->update(__FILE__, __LINE__, $Expansion); }
  public function insertExpansion($Expansion)
  { return $this->insert(__FILE__, __LINE__, $Expansion); }

}
<file_sep>/core/actions/MissionActions.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * MissionActions
 * @author Hugues
 * @since 1.02.00
 * @version 1.07.25
 */
class MissionActions extends LocalActions
{
  /**
   * Constructeur
   */
  public function __construct($post=array())
  {
    parent::__construct();
    $this->post = $post;
    $this->WpPostServices   = new WpPostServices();
    $this->DurationServices = new DurationServices();
    $this->ExpansionServices = new ExpansionServices();
    $this->LevelServices    = new LevelServices();
    $this->MissionServices  = new MissionServices();
    $this->MissionExpansionServices = new MissionExpansionServices();
    $this->MissionTileServices = new MissionTileServices();
    $this->OrigineServices  = new OrigineServices();
    $this->PlayerServices   = new PlayerServices();
    $this->TileServices     = new TileServices();
  }
  /**
   * Point d'entrée des méthodes statiques.
   * @param array $post
   * @return string
   **/
  public static function dealWithStatic($post)
  {
    $returned = '';
    $Act = new MissionActions($post);
    if ($post[self::CST_AJAXACTION]==self::AJAX_GETMISSIONS) {
      $returned = $Act->dealWithGetMissions();
    } elseif ($post[self::CST_AJAXACTION]==self::AJAX_MISSIONVERIF) {
      $returned = $Act->dealWithMissionVerif(true);
    } else {
      $returned = '';
    }
    return $returned;
  }

  /**
   * Récupération du contenu de la page via une requête Ajax.
   * @param array $post
   * @return string
   */
  public function dealWithGetMissions()
  {
    $Bean = new WpPageMissionsBean();
    $Bean->setFilters($this->post);
    return $this->jsonString($Bean->getListContentPage(), self::PAGE_MISSION, true);
  }

  //////////////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////////
  // Bloc de gestion de vérifications des Missions en Home Admin
  /**
   * @param boolean $isVerif
   * @return string
   */
  public function dealWithMissionVerif($isVerif=false)
  {
    // On récupère les articles de missions
    $args = array(
      self::WP_CAT         => self::WP_CAT_MISSION_ID,
      self::WP_TAXQUERY    => array(),
      self::WP_POSTSTATUS  => self::WP_PUBLISH.', future, pending',
    );
    $this->WpPostMissions = $this->WpPostServices->getArticles($args);
    $nbWpPostMissions = count($this->WpPostMissions);
    // Et les Missions en base
    $this->Missions = $this->MissionServices->getMissionsWithFilters();
    $nbMissions = count($this->Missions);

    if ($isVerif) {
      $this->checkMissions();
      $strBilan = $this->jsonString($this->strBilan, self::AJAX_MISSIONVERIF, true);
    } elseif ($nbWpPostMissions!=$nbMissions) {
      $strBilan  = "Le nombre d'articles ($nbWpPostMissions) ne correspond pas au nombre de Missions en base ($nbMissions).";
      $strBilan .= "<br>Une vérification est vivement conseillée.";
    } else {
      $strBilan = "Le nombre d'articles ($nbWpPostMissions) correspond au nombre de Missions en base.";
    }
    return $strBilan;
  }

  private function checkMissions()
  {
    $hasErrors = false;
    $strErrors = '';
    $this->strBilan  = "Début de l'analyse des données relatives aux Missions.<br>";
    $this->strBilan .= "Il y a ".count($this->WpPostMissions)." articles de Missions.<br>";
    $this->strBilan .= "Il y a ".count($this->Missions)." entrées en base.<br>";
    /////////////////////////////////////////////////////////////////////
    // On va réorganiser les Missions pour les retrouver facilement
    $arrMissions = array();
    while (!empty($this->Missions)) {
      $Mission = array_shift($this->Missions);
      if (isset($arrMissions[$Mission->getCode()])) {
        $strErrors .= "Le code <em>".$Mission->getCode()."</em> semble être utilisé deux fois dans la base de données.<br>";
        $hasErrors = true;
      }
      $arrMissions[$Mission->getCode()] = $Mission;
    }
    /////////////////////////////////////////////////////////////////////

    /////////////////////////////////////////////////////////////////////
    while (!empty($this->WpPostMissions)) {
      // On regarde les articles créés et on vérifie les données en base, si elles existent et si elles sont cohérentes entre elles.
      // On récupère le WpPost et ses données
      $this->WpPost = array_shift($this->WpPostMissions);
      $code = $this->WpPost->getPostMeta(self::FIELD_CODE);
      if (!isset($arrMissions[$code])) {
        // A priori l'article n'a pas de code associé en base. Il faut donc en créé un qui corresponde
        $Mission = new Mission();
        $name = $this->WpPost->getPostTitle();
        $Mission->setTitle($name);
        $Mission->setCode($code);
        // Set la Difficulté
        $Mission->setLevelId($this->getWpPostLevelId());
        // Set le Nb de Joueurs
        $Mission->setPlayerId($this->getWpPostPlayerId());
        // Set la Durée
        $Mission->setDurationId($this->getWpPostDurationId());
        // Set l'Origine
        $Mission->setOrigineId($this->getWpPostOrigineId());
        // Set Width & Height
        list($width, $height) = explode(',', $this->getWpPostDimensions());
        $Mission->setWidth($width);
        $Mission->setHeight($height);
        // On peut insérer
        $this->MissionServices->insertMission($Mission);
        $this->strBilan .= "L'article <em>".$name."</em> a été créé en base.<br>";
        continue;
      }
      $Mission = $arrMissions[$code];
      unset($arrMissions[$code]);
      $this->checkMission($Mission);
    }
    /////////////////////////////////////////////////////////////////////
    // On vérifie que la totalité des Missions en base ont été utilisées. Si ce n'est pas le cas, il faut créer des articles correspondants.
    if (!empty($arrMissions)) {
      $this->strBilan .= "On a des données en base qui n'ont pas d'article correspondant.<br>";
      while (!empty($arrMissions)) {
        $Mission = array_shift($arrMissions);
        $this->strBilan .= '<br>Article à créer pour une Extension  : '.$Mission->getName().' ['.$Mission->toJson().'].<br>';
      }
    }

    /////////////////////////////////////////////////////////////////////
    $this->strBilan .= "Fin de l'analyse des données relatives aux Missions.<br>";
    if ($hasErrors) {
      $this->strBilan .= "Anomalies constatées :<br>".$strErrors;
    } else {
      $this->strBilan .= "Aucune anomalie constatée.";
    }
  }
  private function checkMission($Mission)
  {
    $doUpdate = false;
    // On initialise les données de l'article
    $name         = $this->WpPost->getPostTitle();
    // On vérifie si la donnée en base correspond à l'article.
    $strError = '';
    if ($Mission->getTitle()!=$name) {
      $Mission->setTitle($name);
      $strError .= "Le Titre a été mis à jour.<br>";
      $doUpdate = true;
    }
    $levelId = $this->getWpPostLevelId();
    if ($Mission->getLevelId()!=$levelId) {
      $Mission->setLevelId($levelId);
      $strError .= "La Difficulté a été mise à jour.<br>";
      $doUpdate = true;
    }
    $playerId = $this->getWpPostPlayerId();
    if ($Mission->getPlayerId()!=$playerId) {
      $Mission->setPlayer($playerId);
      $strError .= "Le Nb de joueurs a été mis à jour.<br>";
      $doUpdate = true;
    }
    $durationId = $this->getWpPostDurationId();
    if ($Mission->getDurationId()!=$durationId) {
      $Mission->setDurationId($durationId);
      $strError .= "La Durée a été mise à jour.<br>";
      $doUpdate = true;
    }
    $origineId = $this->getWpPostOrigineId();
    if ($Mission->getOrigineId()!=$origineId) {
      $Mission->setOrigineId($origineId);
      $strError .= "L'Origine a été mise à jour.<br>";
      $doUpdate = true;
    }
    list($width, $height) = explode(',', $this->getWpPostDimensions());
    if ($Mission->getWidth()!=$width || $Mission->getHeight()!=$height) {
      if ($width==0) {
        $this->strBilan .= "Il ne semble pas y avoir de Dalles saisies dans le champ tileIds de l'Article <em>".$name."</em> <strong>".$Mission->getCode()."</strong>.<br>";
      } elseif ($width==2 && $height==2 && $Mission->getWidth()!=$Mission->getHeight()) {
        $this->strBilan .= "Les Dimensions (".$Mission->getWidth().", ".$Mission->getHeight().") => ($width, $height) ne peuvent pas être renseignées automatiquement pour l'Article <em>".$name."</em>.<br>";
      } else {
        $strError .= "Les Dimensions (".$Mission->getWidth().", ".$Mission->getHeight().") => ($width, $height) ont été mises à jour.<br>";
        $Mission->setWidth($width);
        $Mission->setHeight($height);
        $doUpdate = true;
      }
    }

    // On récupère les Extensions rattachées à l'article.
    $arrExpansions = $this->getWpPostExpansions();
    // On récupère les MissionExpansions rattachés à la Mission.
    $MissionExpansions = $this->MissionExpansionServices->getMissionExpansionsWithFilters(array(self::FIELD_MISSIONID=>$Mission->getId()));
    if (count($arrExpansions)==count($MissionExpansions)) {
      //$this->strBilan .= "Le nombre entre les deux univers correspond. Il faudrait vérifier que ce sont bien les mêmes...<br>";
    } else {
      // On a une différence. On s'embête pas, on supprime les MissionExpansions existantes, on insère les nouvelles.
      while (!empty($MissionExpansions)) {
        $MissionExpansion = array_shift($MissionExpansions);
        $this->MissionExpansionServices->deleteMissionExpansion($MissionExpansion);
      }
      $MissionExpansion = new MissionExpansion();
      $MissionExpansion->setMissionId($Mission->getId());
      while (!empty($arrExpansions)) {
        $Expansion = array_shift($arrExpansions);
        $MissionExpansion->setExpansionId($Expansion->getId());
        $this->MissionExpansionServices->insertMissionExpansion($MissionExpansion);
      }
    }

    if ($doUpdate) {
      // Si nécessaire, on update en base.
      $this->MissionServices->updateMission($Mission);
      $this->strBilan .= "Les données de la Mission <em>".$name."</em> ont été mises à jour.<br>".$strError;
    }
  }

  private function getWpPostExpansions()
  {
    $Expansions = array();
    $expansionNames = unserialize($this->WpPost->getPostMeta('expansionIds'));
    while (!empty($expansionNames)) {
      $expansionName = array_shift($expansionNames);
      $SearchedExpansions = $this->ExpansionServices->getExpansionsWithFilters(array(self::FIELD_NAME=>$expansionName));
      if (empty($SearchedExpansions)) {
        echo "[[ERROR : $expansionName ne correspond pas à une Extension.]]\r\n";
      } else {
        $Expansion = array_shift($SearchedExpansions);
        array_push($Expansions, $Expansion);
      }
    }
    return $Expansions;
  }

  private function getWpPostLevelId()
  {
    $levelId = $this->WpPost->getPostMeta(self::FIELD_LEVELID);
    $Levels = $this->LevelServices->getLevelsWithFilters(array(self::FIELD_NAME=>$levelId));
    $Level = array_shift($Levels);
    return $Level->getId();
  }

  private function getWpPostPlayerId()
  {
    $playerId = $this->WpPost->getPostMeta(self::FIELD_PLAYERID);
    $Players = $this->PlayerServices->getPlayersWithFilters(array(self::FIELD_NAME=>$playerId));
    $Player = array_shift($Players);
    return $Player->getId();
  }

  private function getWpPostDurationId()
  {
    $durationId = $this->WpPost->getPostMeta(self::FIELD_DURATIONID);
    list($min, $max) = explode('-', $durationId);
    $Durations = $this->DurationServices->getDurationsWithFilters(array(self::FIELD_MINDURATION=>$min, self::FIELD_MAXDURATION=>$max));
    $Duration = array_shift($Durations);
    return $Duration->getId();
  }

  private function getWpPostOrigineId()
  {
    $origineId = $this->WpPost->getPostMeta(self::FIELD_ORIGINEID);
    $Origines = $this->OrigineServices->getOriginesWithFilters(array(self::FIELD_NAME=>$origineId));
    $Origine = array_shift($Origines);
    return $Origine->getId();
  }

  private function getWpPostDimensions()
  {
    $tileCodes = $this->WpPost->getPostMeta('tileIds');
    $arrTileIds = explode(', ', str_replace(' &', ',', $tileCodes));
    switch (count($arrTileIds)) {
      case 2 :
      case 3 :
        $width = count($arrTileIds);
        $height = 1;
      break;
      case 4 :
      case 6 :
        $width = count($arrTileIds)/2;
        $height = 2;
      break;
      case 5 :
        $width = 1;
        $height = count($arrTileIds);
      break;
      case 8 :
      case 10 :
        $width = 2;
        $height = count($arrTileIds)/2;
      break;
      case 9 :
        $width = 3;
        $height = 3;
      break;
      case 12 :
        $width = 4;
        $height = 3;
      break;
      default :
        $width = 0;
        $height = 0;
      break;
    }
    return $width.','.$height;
  }

  // Fin du bloc relatif à la vérification des Missions sur la Home Admin.
  //////////////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////////
}
<file_sep>/core/bean/AdminPageBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe AdminPageBean
 * @author Hugues
 * @since 1.00.00
 * @version 1.05.12
 */
class AdminPageBean extends MainPageBean
{
  protected $urlFragmentPagination = 'web/pages/admin/fragments/fragment-pagination.php';
  protected $tplHomeAdminBoard = 'web/pages/admin/home-admin-board.php';

  /**
   * Backup Cron Table
   */
  const WP_DB_BACKUP_CRON = 'wp_db_backup_cron';
  /**
   * @param string $tag
   */
  public function __construct($tag='')
  {
    parent::__construct();
    $this->urlParams = array();
    $this->analyzeUri();
    $this->tableName = 'wp_11_zombicide_'.$tag;
    $this->tplAdminerUrl  = 'http://zombicide.jhugues.fr/wp-content/plugins/adminer/inc/adminer/loader.php';
    $this->tplAdminerUrl .= '?username=dbo507551204&db=db507551204&table='.$this->tableName;
  }

  /**
   * @return string
   */
  public function analyzeUri()
  {
    $uri = $_SERVER['REQUEST_URI'];
    $pos = strpos($uri, '?');
    if ($pos!==false) {
      $arrParams = explode('&', substr($uri, $pos+1, strlen($uri)));
      if (!empty($arrParams)) {
        foreach ($arrParams as $param) {
          list($key, $value) = explode('=', $param);
          $this->urlParams[$key] = $value;
        }
      }
      $uri = substr($uri, 0, $pos-1);
    }
    $pos = strpos($uri, '#');
    if ($pos!==false) {
      $this->anchor = substr($uri, $pos+1, strlen($uri));
    }
    if (isset($_POST)) {
      foreach ($_POST as $key => $value) {
        $this->urlParams[$key] = $value;
      }
    }
    return $uri;
  }
  /**
   * @return string
   */
  public function getContentPage()
  {
    $strReturned = 'Need to be an admin';
    if (self::isAdmin()) {
      if (!isset($this->urlParams[self::CST_ONGLET])) {
        $strReturned = $this->getHomeContentPage();
      } elseif ($this->urlParams[self::CST_ONGLET]==self::CST_SKILL) {
        $Bean = new AdminPageSkillsBean($this->urlParams);
        $strReturned = $Bean->getSpecificContentPage();
      } elseif ($this->urlParams[self::CST_ONGLET]==self::CST_SURVIVOR) {
        $Bean = new AdminPageSurvivorsBean($this->urlParams);
        $strReturned = $Bean->getSpecificContentPage();
      } elseif ($this->urlParams[self::CST_ONGLET]==self::CST_EXPANSION) {
        $Bean = new AdminPageExpansionsBean($this->urlParams);
        $strReturned = $Bean->getSpecificContentPage();
      } elseif ($this->urlParams[self::CST_ONGLET]==self::CST_MISSION) {
        $Bean = new AdminPageMissionsBean($this->urlParams);
        $strReturned = $Bean->getSpecificContentPage();
      } else {
        $strReturned = "Need to add <b>".$this->urlParams[self::CST_ONGLET]."</b> to AdminPageBean > getContentPage().";
      }
    }
    return $strReturned;
  }
  /**
   * @return string
   */
  public function getHomeContentPage()
  {
    /////////////////////////////////////////////////
    // Gestion des Cartes.
    // On récupère les cartes qu'on souhaite afficher sur la Home
    // La carte relatives aux compétences
    $Bean = new AdminPageSkillsBean();
    $lstCards  = $Bean->getCheckCard();
    // La carte relatives aux extensions
    $Bean = new AdminPageExpansionsBean();
    $lstCards .= $Bean->getCheckCard();
    // La carte relatives aux Survivants
    $Bean = new AdminPageSurvivorsBean();
    $lstCards .= $Bean->getCheckCard();
    // La carte relatives aux Missions
    $Bean = new AdminPageMissionsBean();
    $lstCards .= $Bean->getCheckCard();

    $args = array(
      // La liste des Cartes affichées sur le panneau d'accueil de la Home - 1
      $lstCards,
   );
    return $this->getRender($this->tplHomeAdminBoard, $args);
  }


  /**
   * @param unknown $queryArg
   * @param unknown $post_status
   * @param unknown $curPage
   * @param unknown $nbPages
   * @param unknown $nbElements
   * @return string
   */
  protected function getPagination($queryArg, $post_status, $curPage, $nbPages, $nbElements)
  {
    ////////////////////////////////////////////////////////////////////////////
    // Lien vers la première page. Seulement si on n'est ni sur la première, ni sur la deuxième page.
    if ($curPage>=3) {
      $queryArg[self::CST_CURPAGE] = 1;
      $strToFirst = '<a class="first-page button" href="'.$this->getQueryArg($queryArg).'"><span aria-hidden="true">&laquo;</span></a>';
    } else {
      $strToFirst = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&laquo;</span>';
    }
    ////////////////////////////////////////////////////////////////////////////
    // Lien vers la page précédente. Seulement si on n'est pas sur la première.
    if ($curPage>=2) {
      $queryArg[self::CST_CURPAGE] = $curPage-1;
      $strToPrevious = '<a class="prev-page button" href="'.$this->getQueryArg($queryArg).'"><span aria-hidden="true">&lsaquo;</span></a>';
    } else {
      $strToPrevious = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&lsaquo;</span>';
    }
    ////////////////////////////////////////////////////////////////////////////
    // Lien vers la page suivante. Seulement si on n'est pas sur la dernière.
    if ($curPage<$nbPages) {
      $queryArg[self::CST_CURPAGE] = $curPage+1;
      $strToNext = '<a class="next-page button" href="'.$this->getQueryArg($queryArg).'"><span aria-hidden="true">&rsaquo;</span></a>';
    } else {
      $strToNext = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&rsaquo;</span>';
    }
    ////////////////////////////////////////////////////////////////////////////
    // Lien vers la dernière page. Seulement si on n'est pas sur la dernière, ni l'avant-dernière.
    if ($curPage<$nbPages-1) {
      $queryArg[self::CST_CURPAGE] = $nbPages;
      $strToLast = '<a class="next-page button" href="'.$this->getQueryArg($queryArg).'"><span aria-hidden="true">&raquo;</span></a>';
    } else {
      $strToLast = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">&raquo;</span>';
    }

    $args = array(
      // Nombre d'éléments - 1
      $nbElements,
      // Lien vers la première page - 2
      $strToFirst,
      // Lien vers la page précédente - 3
      $strToPrevious,
      // Page courante - 4
      $curPage,
      // Nombre total de pages - 5
      $nbPages,
      // Lien vers la page suivante - 6
      $strToNext,
      // Lien vers la dernière page - 7
      $strToLast,
    );
    return $this->getRender($this->urlFragmentPagination, $args);
  }


}
<file_sep>/core/bean/SurvivorBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe SurvivorBean
 * @author Hugues
 * @since 1.00.00
 * @version 1.07.26
 */
class SurvivorBean extends LocalBean
{
  protected $urlRowAdmin  = 'web/pages/admin/fragments/survivor-row.php';
  protected $urlRowPublic = 'web/pages/public/fragments/survivor-row.php';
  protected $urlArticle   = 'web/pages/public/fragments/survivor-article.php';
  /**
   * @param Survivor $Survivor
   */
  public function __construct($Survivor=null)
  {
    parent::__construct();
    $this->Survivor = ($Survivor==null ? new Survivor() : $Survivor);
    $this->ExpansionServices = new ExpansionServices();
    $this->SurvivorServices  = new SurvivorServices();
  }

  //////////////////////////////////////////////////////////////////////////
  // Différentes modes de présentation des Survivants
  /**
   * @return string
   */
  public function getRowForAdminPage()
  {
    ///////////////////////////////////////////////////////////////////////////
    // On enrichit le template
    $args = array(
      // Identifiant du Survivant - 1
      $this->Survivor->getId(),
      // Portraits - 2
      $this->getAllPortraits(),
      // Url d'édition - 3
      $this->Survivor->getEditUrl(self::CST_SURVIVOR),
      // Nom du Survivant - 4
      $this->Survivor->getName(),
      // Url d'édition du WpPost - 5
      $this->Survivor->getWpPostEditUrl(),
      // Article publié ? - 6
      $this->Survivor->getWpPostUrl(),
      // Liste des profils existants - 7
      $this->getListeProfils(),
      // Extension de provenance - 8
      $this->Survivor->getExpansion()->getName(),
      // Background du Survivant - 9
      ($this->Survivor->getBackground()!='' ? substr($this->Survivor->getBackground(), 0, 50).'...' : 'Non renseigné'),
      // Nom de l'image alternative, si défini. - 10
      $this->Survivor->getAltImgName(),
      // Type du Survivant - 11
      (!$this->Survivor->isStandard() ? ' ultimate' : ''),
    );
    ///////////////////////////////////////////////////////////////
    // Puis on le retourne
    return $this->getRender($this->urlRowAdmin, $args);
  }
  /**
   * @return string
   */
  public function getRowForPublicPage()
  {
    ///////////////////////////////////////////////////////////////
    // On enrichit le template et on le retourne.
    $args = array(
      // Id du Survivant - 1
      $this->Survivor->getId(),
      // Les portraits du Survivants - 2
      $this->getAllPortraits(),
      // Url du WpPost associé, s'il existe - 3
      $this->Survivor->getWpPostUrl(),
      // Nom du Survivant - 4
      $this->Survivor->getName(),
      // Si on a un profil de Zombivant, on donne la possibilité de l'afficher - 5
      ($this->Survivor->isZombivor()?self::CST_CHANGEPROFILE:''),
      // Si on a un profil de Zombivant, on veut une case à cocher - 6
      ($this->Survivor->isZombivor()?self::CST_SQUAREPOINTER:self::CST_WINDOWCLOSE),
      // Si on a un profil d'Ultimate, on donne la possibilité de l'afficher - 7
      ($this->Survivor->isUltimate()?self::CST_CHANGEPROFILE:''),
      // Si on a un profil d'Ultimate, on veut une case à cocher - 8
      ($this->Survivor->isUltimate()?self::CST_SQUAREPOINTER:self::CST_WINDOWCLOSE),
      // Extension à laquelle est rattaché le Survivant - 9
      $this->Survivor->getExpansion()->getName(),
      // Liste des Compétences du Survivant - 10
      $this->getAllSkills(),
      // Background du Survivant - 11
      $this->Survivor->getBackground(),
      // Classe additionnelle, pour la ligne - 12
      (!$this->Survivor->isStandard() ? ' ultimate' : ''),
    );
    return $this->getRender($this->urlRowPublic, $args);
  }
  /**
   * @return string
   */
  public function getContentForHome()
  {
    //////////////////////////////////////////////////////////////////
    // Liste des profils du Survivants.
    $strProfiles  = '';
    $strSkills = '';
    if ($this->Survivor->isStandard()) {
      $strProfiles .= $this->getBalise(self::TAG_LI, $this->getFormRadioBouton('survivant', self::LBL_SURVIVANT), array(self::ATTR_CLASS=>self::CST_ACTIVE));
      $strSkills   .= $this->getBalise(self::TAG_UL, $this->Survivor->getUlSkills('', false, true), array(self::ATTR_CLASS=>'colSkills skills-survivant'));
    }
    if ($this->Survivor->isZombivor()) {
      $strProfiles .= $this->getBalise(self::TAG_LI, $this->getFormRadioBouton('zombivant', self::LBL_ZOMBIVANT));
      $strSkills   .= $this->getBalise(self::TAG_UL, $this->Survivor->getUlSkills('z', false, true), array(self::ATTR_CLASS=>'colSkills skills-zombivant'));
    }
    if ($this->Survivor->isUltimate()) {
      $strProfiles .= $this->getBalise(self::TAG_LI, $this->getFormRadioBouton('ultimate', self::LBL_ULTIMATE));
      $strSkills   .= $this->getBalise(self::TAG_UL, $this->Survivor->getUlSkills('u', false, true), array(self::ATTR_CLASS=>'colSkills skills-ultimate'));
    }
    if ($this->Survivor->isUltimatez()) {
      $strProfiles .= $this->getBalise(self::TAG_LI, $this->getFormRadioBouton('ultimatez', self::LBL_ULTIMATEZOMBIVANT));
      $strSkills   .= $this->getBalise(self::TAG_UL, $this->Survivor->getUlSkills('uz', false, true), array(self::ATTR_CLASS=>'colSkills skills-ultimatez'));
    }
    //////////////////////////////////////////////////////////////////

    //////////////////////////////////////////////////////////////////
    // On enrichit le template
    $args = array(
      // Url de l'article - 1
      $this->Survivor->getWpPostUrl(),
      // Url du portrait du Survivant - 2
      $this->Survivor->getPortraitUrl(),
      // Url vers la page Survivants - 3
      '/'.self::PAGE_SURVIVOR,
      // Nom du Survivant - 4
      $this->Survivor->getName(),
      // Les Compétences du Survivant - 5
      $strSkills,
      // Liste des profils du Survivant - 6
      '<ul>'.$strProfiles.'</ul>',
      // Plus utilisé - 7
      '',
      // Background du Survivant - 8
      $this->Survivor->getBackground(),
      // Classe additionnelle de l'article - 9
      $this->Survivor->getStrClassFilters().' '.$this->Survivor->getExpansion()->getCode(),
      // Le Nom de l'extension - 10
      $this->Survivor->getExpansion()->getName(),
    );
    ///////////////////////////////////////////////////////////////
    // Puis on le retourne
    return $this->getRender($this->urlArticle, $args);
  }















  protected $urlFormRadioBouton = 'web/pages/public/fragments/form-radio-bouton.php';
  protected $urlCardVisit = 'web/pages/public/fragments/survivor-cardvisit.php';
  protected $tplSkillBadge = '<a class="badge badge-%1$s-skill" href="%2$s">%3$s</a>';
  protected $tplDisabledSkillBadge = '<span class="badge badge-%1$s-skill">%3$s</span>';
  private $strPortraitSurvivant = 'portrait-survivant';
  private $strPortraitZombivant = 'portrait-zombivant';
  private $strPortraitUltimate  = ' portrait-ultimate';


  //////////////////////////////////////////////////////////////////////////
  private function getFormRadioBouton($value, $libelle)
  {
    return '<div class="form-check badge badge-outline changeProfile" data-type="'.$value.'">'.$libelle.'</div>';
  }



  /**
   * @return string
   */
  public function getButton()
  {
    $label = $this->getIconFarSquare().' '.$this->Survivor->getName();
    $attributes = array(
      self::ATTR_TYPE             => self::TAG_BUTTON,
      self::ATTR_CLASS            => 'btn btn-light btn-survivor hidden',
      self::ATTR_DATA_EXPANSIONID => $this->Survivor->getExpansionId(),
      self::ATTR_DATA_SURVIVORID  => $this->Survivor->getId(),
    );
    return $this->getBalise(self::TAG_BUTTON, $label, $attributes);
  }







  /**
   * @param string $color
   * @return string
   */
  public function getSkillBadge($color)
  {
    ////////////////////////////////////////////////////////////////////
    // On enrichi les paramètres du template et on le retourne
    $args = array(
      $color,
      $this->Survivor->getWpPostUrl(),
      $this->Survivor->getName()
    );
    return vsprintf(($this->Survivor->getWpPostUrl()=='#' ? $this->tplDisabledSkillBadge : $this->tplSkillBadge), $args);
  }
  public function getPortrait($type='')
  { return $this->getStrImgPortrait($this->Survivor->getPortraitUrl($type), 'Portrait', ''); }
  /**
   * @return string
   */
  public function getAllPortraits($displayedFiltered=true)
  {
    $Survivor = $this->Survivor;
    $name = $Survivor->getName();
    $str = '';
    if ($Survivor->isStandard()) {
      $str .= $this->getStrImgPortrait($Survivor->getPortraitUrl(), 'Portrait Survivant - '.$name, ($displayedFiltered?$this->strPortraitSurvivant:''));
    }
    if ($Survivor->isZombivor()) {
      $str .= $this->getStrImgPortrait($Survivor->getPortraitUrl('z'), 'Portrait Zombivant - '.$name, ($displayedFiltered?$this->strPortraitZombivant:''));
    }
    if ($Survivor->isUltimate()) {
      $label = $this->strPortraitSurvivant.$this->strPortraitUltimate;
      $str .= $this->getStrImgPortrait($Survivor->getPortraitUrl('u'), 'Portrait Ultimate - '.$name, ($displayedFiltered?$label:''));
    }
    if ($Survivor->isUltimatez()) {
      $label = $this->strPortraitZombivant.$this->strPortraitUltimate;
      $str .= $this->getStrImgPortrait($Survivor->getPortraitUrl('uz'), 'Portrait ZUltimate - '.$name, ($displayedFiltered?$label:''));
    }
    return $str;
  }
  private function getProfileLi($type, $survivorTypeId, $label)
  {
    $strProfils = '<li data-id="'.$this->Survivor->getId().'" data-type="'.$type.'" class="hasTooltip pointer"> ';
    if ($this->Survivor->areDataSkillsOkay($survivorTypeId)) {
      $strProfils .= $this->getIconFarCheckSquare().' '.$label.' <div class="tooltip">';
      $strProfils .= $this->Survivor->getAdminUlSkills($survivorTypeId).'</div>';
    } else {
      $strProfils .=  $this->getIconFarWindowClose().' '.$label;
    }
    return $strProfils.'</li>';
  }
  public function getListeProfils()
  {
    $strProfils  = '<ul>';
    // A-t-il un profil Standard ?
    if ($this->Survivor->isStandard()) {
      $strProfils .= $this->getProfileLi('survivant', self::CST_SURVIVORTYPEID_S, 'Standard');
    }
    // A-t-il un profil Zombivant ?
    if ($this->Survivor->isZombivor()) {
      $strProfils .= $this->getProfileLi('zombivant', self::CST_SURVIVORTYPEID_Z, 'Zombivant');
    }
    // A-t-il un profil Ultimate ?
    if ($this->Survivor->isUltimate()) {
      $strProfils .= $this->getProfileLi('ultimate survivant', self::CST_SURVIVORTYPEID_U, 'Ultimate');
    }
    // A-t-il un profil UltimateZ ?
    if ($this->Survivor->isUltimatez()) {
      $strProfils .= $this->getProfileLi('ultimate zombivant', self::CST_SURVIVORTYPEID_UZ, 'Ultimate Zombivant');
    }
    return $strProfils.'</ul>';
  }
  /**
   * @param string $src
   * @param string $alt
   * @param string $addClass
   * @return string
   */
  private function getStrImgPortrait($src, $alt, $addClass)
  {
    $attributes = array(
      self::ATTR_SRC => $src,
      self::ATTR_ALT => $alt,
      self::ATTR_CLASS => 'thumb '.$addClass,
    );
    return $this->getBalise(self::TAG_IMG, '', $attributes);
  }
  public function getSkills($type='')
  { return $this->getSkillsBySurvivorType('row', $this->Survivor->getUlSkills($type, true)); }
  /**
   * @return string
   */
  public function getAllSkills()
  {
    $Survivor = $this->Survivor;
    $str = $this->getSkillsBySurvivorType('skills-survivant row', $Survivor->getUlSkills('', true));
    if ($Survivor->isZombivor()) {
      $str .= $this->getSkillsBySurvivorType('skills-zombivant row', $Survivor->getUlSkills('z', true));
    }
    if ($Survivor->isUltimate()) {
      $str .= $this->getSkillsBySurvivorType('skills-ultimate skills-survivant row', $Survivor->getUlSkills('u', true));
      $str .= $this->getSkillsBySurvivorType('skills-ultimate skills-zombivant row', $Survivor->getUlSkills('uz', true));
    }
    return $this->getBalise(self::TAG_UL, $str);
  }
  /**
   * @param string $addClass
   * @param string $content
   * @return string
   */
  private function getSkillsBySurvivorType($addClass, $content)
  { return $this->getBalise(self::TAG_LI, $content, array(self::ATTR_CLASS=>$addClass)); }
  /**
   * @return string
   */
  public function getCheckBoxType()
  {
    $strType  = '';
    if ($this->Survivor->isZombivor()) {
      $attributes = array(
        self::ATTR_DATA_ID => $this->Survivor->getId(),
        self::ATTR_DATA_TYPE => 'zombivant',
        self::ATTR_CLASS => 'changeProfile',
      );
      $strType .= $this->getBalise(self::TAG_DIV, $this->getIconFarSquarePointer().' Zombivant', $attributes);
      if ($this->Survivor->isUltimate()) {
        $attributes[self::ATTR_DATA_TYPE] = self::FIELD_ULTIMATE;
        $strType .= '&nbsp;'.$this->getBalise(self::TAG_DIV, $this->getIconFarSquarePointer().' Ultimate', $attributes);
      }
    }
    return $strType;
  }

  public function getCartouche($extraAttributes=array(), $linked=false)
  {
    $content = $this->getStrImgPortrait($this->Survivor->getPortraitUrl(), '', '').' '.$this->Survivor->getName();
    $attributes = array(
      self::ATTR_CLASS => 'cartouche',
    );
    if (!empty($extraAttributes)) {
      $attributes = array_merge($attributes, $extraAttributes);
    }
    if ($linked) {
      $tag = self::TAG_A;
      $attributes[self::ATTR_HREF] = $this->Survivor->getWpPost()->getPermalink();
    } else {
      $tag = self::TAG_SPAN;
    }
    return $this->getBalise($tag, $content, $attributes);
  }


}
<file_sep>/core/bean/WpPageMissionOnlineBean.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe WpPageMissionOnlineBean
 * @author Hugues
 * @since 1.10.04
 * @version 1.10.04
 */
class WpPageMissionOnlineBean extends WpPageBean
{
  protected $urlDirMissions     = '/web/rsc/missions/';
  protected $urlDirLiveMissions = '/web/rsc/missions/live/';
  protected $urlLoginTemplate   = 'web/pages/public/wppage-mission-online-login.php';
  protected $urlTemplate        = 'web/pages/public/wppage-mission-online.php';
  protected $urlTchatMsgTpl     = 'web/pages/public/fragments/tchat-message.php';
  protected $urlSectionSetup    = 'web/pages/public/fragments/online-section-setup.php';
  protected $xmlSuffixe         = '.mission.xml';
  /**
   * Class Constructor
   * @param WpPage $WpPage
   */
  public function __construct($WpPage='')
  {
    parent::__construct($WpPage);
    $this->MissionServices = new MissionServices();
    $this->SurvivorServices = new SurvivorServices();
  }
  /**
   * @return string
   */
  public function getContentPage()
  {
    $this->msgError = '';
    if ($_POST['radioChoice']=='new') {
      $Missions = $this->MissionServices->getMissionsWithFilters(array(self::FIELD_CODE=>$_POST['selectMission']));
      if (empty($Missions)) {
        $this->msgError = '<em>Attention</em>, le code sélectionné n\'existe pas.';
      } elseif (is_file(PLUGIN_PATH.$this->urlDirMissions.$_POST['selectMission'].$this->xmlSuffixe)) {
        // ON doit générer une clef qui va bien et la stocker dans zombieKey.
        // Puis on génère un fichier live à partir du fichier référence de la Mission.
        //AnJwMKqNkXba2suQ
        $str = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
        $strCode = substr(str_shuffle($str), 0, 16);
        $Mission = array_shift($Missions);
        copy(PLUGIN_PATH.$this->urlDirMissions.$Mission->getCode().$this->xmlSuffixe, PLUGIN_PATH.$this->urlDirLiveMissions.$strCode.$this->xmlSuffixe);
        $_SESSION['zombieKey'] = $strCode;
        $MissionOnline = new MissionOnline($Mission);
        $MissionOnline->setUp();
        $this->msgError = '<em>Attention</em>, la Mission sélectionnée existe.';
      } else {
        $this->msgError = '<em>Attention</em>, la Mission sélectionnée n\'existe pas.';
      }
    } elseif ($_POST['radioChoice']=='old') {
      if (is_file(PLUGIN_PATH.$this->urlDirLiveMissions.$_POST['saveCode'].$this->xmlSuffixe)) {
        // On récupère la clef fournie et on la stocke dans zombieKey
        $_SESSION['zombieKey'] = $_POST['saveCode'];
        $this->msgError = '<em>Attention</em>, le code saisi correspond à une partie sauvegardée.';
      } else {
        // Prévoir une gestion d'erreur pour fichier inexistant.
        $this->msgError = '<em>Attention</em>, le code saisi ne correspond pas à une partie sauvegardée.';
      }
    } elseif (isset($_GET['logout'])) {
      unset($_SESSION['zombieKey']);
    }


    if (isset($_SESSION['zombieKey']) && is_file(PLUGIN_PATH.$this->urlDirLiveMissions.$_SESSION['zombieKey'].$this->xmlSuffixe)) {
      return $this->getBoard();
    } else {
      return $this->getLogin();
    }
  }
  public function getLogin()
  {
    if ($this->msgError!='') {
      $strMsgError = $this->getBalise(self::TAG_DIV, $this->msgError, array(self::ATTR_CLASS=>'alert alert-danger'));
    } else {
      $strMsgError = '';
    }
    // Gérer les cas éventuels d'erreur.
    $args = array(
      // Le message d'erreur éventuel - 1
      $strMsgError,
      // Si old est checked - 2
      (isset($_POST['radioChoice']) && $_POST['radioChoice']=='old') ? 'checked' : '',
      // Si old n'est pas checked - 3
      (!isset($_POST['radioChoice']) || $_POST['radioChoice']=='new') ? 'checked' : '',
      // Si old est checked - 2
      (isset($_POST['radioChoice']) && $_POST['radioChoice']=='old') ? 'fa-dot-circle-o' : 'fa-circle-o',
      // Si old n'est pas checked - 3
      (!isset($_POST['radioChoice']) || $_POST['radioChoice']=='new') ? 'fa-dot-circle-o' : 'fa-circle-o',

    );
    return $this->getRender($this->urlLoginTemplate, $args);
  }
  public function getBoard()
  {
    //////////////////////////////////////////////////////////////////
    // On va afficher une Mission, à partir de son XML... Donc, déjà, il faut l'ouvrir !
    $this->openFile();
    // On initialise quelques variables :
    $this->arrLstPortraits = array();
    $this->arrLstSurvivorDetail = array();

    $Missions = $this->MissionServices->getMissionsWithFilters(array(self::FIELD_CODE=>'AJ01'));
    $Mission = array_shift($Missions);
    $MissionBean = $Mission->getBean();


    //////////////////////////////////////////////////////////////////
    // On enrichi le template puis on le restitue.
    $args = array(
      // class pour afficher correctement la Map - 1
      $this->getDimensions(),
      // La liste des Dalles - 2
      $this->displayTiles(),
      // La liste des Zones - 3
      '',
      // La liste des Tokens - 4
      $this->displayTokens().$this->displaySurvivors().$this->displayZombies(),
      // Identifiant de la Partie - 5
      // TODO : en dur pour le moment.
      $_SESSION['zombieKey'],
      // Liste des Objectifs - 6
      $MissionBean->getMissionContentObjectives(),
      // Liste des Règles Spéciales - 7
      $MissionBean->getMissionContentRules(),
      // Portraits des Survivants dans la Sidebar - 8
      $this->getLstPortraits(),
      // Fiche d'identité des Survivants dans la Sidebar - 9
      $this->getLstDetails(),
      // Le Tchat - 10
      $this->getLstTchats(),
      // Le contenu de l'onglet SetUp - 11
      $this->getMissionSetup(),
    );
    return $this->getRender($this->urlTemplate, $args);
  }

  private function getMissionSetup()
  {
    $Spawns = $this->objXmlDocument->xPath('//spawns')[0];
    $Pools  = $this->objXmlDocument->xPath('//pools/pool');

    $lstPools = '';
    foreach ($Pools as $Pool) {
      $innerDiv = $this->getBalise(self::TAG_DIV, $Pool->attributes()['current'].' / '.$Pool->attributes()['max'], array(self::ATTR_CLASS=>'badge'));
      $img = $this->getBalise(self::TAG_IMG, '', array(self::ATTR_SRC=>'/wp-content/plugins/hj-zombicide/web/rsc/img/zombies/'.$Pool->attributes()['type'].'.png'));
      $outerDiv = $this->getBalise(self::TAG_DIV, $img.$innerDiv, array(self::ATTR_CLASS=>'chip token zombie Standard non-draggable'));
      $lstPools .= $this->getBalise(self::TAG_LI, $outerDiv);
    }

    $args = array(
      // Spawn actuel - 1
      $Spawns->attributes()['interval'],
      // Etat de la réserve de Zombies - 2
      $lstPools,
    );
    return $this->getRender($this->urlSectionSetup, $args);
  }

  public function getLstTchats($tsTreshold='')
  {
    if ($tsTreshold=='') {
      $Tchats = $this->objXmlDocument->xPath('//tchat');
    } else {
      $this->openFile();
      $Tchats = $this->objXmlDocument->xPath('//tchat[@timestamp>"'.$tsTreshold.'"]');
    }
    usort($Tchats, 'sort_trees');
    $lstMsgs = '';
    $prevTs = '';
    while (!empty($Tchats)) {
      $Tchat = array_shift($Tchats);
      $author = $Tchat->attributes()['author'];
      $ts = $Tchat->attributes()['timestamp']*1;
      // On insère un Tag pour séparer les messages des différentes journées.
      if ($prevTs!='' && date('d', $ts)!=date('d', $prevTs)) {
        $liClass = 'clearfix';
        $msgDataClass = ' message changeDate';
        $msgDataContent  = date('d m Y', $ts);
        $msgClass = ' hidden';
        $args = array(
          $liClass,
          $msgDataClass,
          $msgDataContent,
          $msgClass,
          '',
          $ts,
        );
        $lstMsgs .= $this->getRender($this->urlTchatMsgTpl, $args);
      }
      // Selon que l'auteur est Automat, le user courant ou un autre, le visuel change
      if ($author=='Automat') {
        $liClass = 'clearfix';
        $msgDataClass = '';
        $msgDataContent  = date('H:i', $ts);
        $msgClass = ' tech-message';
      } elseif ($author=='me') {
        $liClass = 'clearfix';
        $msgDataClass = ' align-right';
        $msgDataContent  = $this->getBalise(self::TAG_SPAN, date('H:i', $ts), array(self::ATTR_CLASS=>'message-data-time')).'&nbsp;&nbsp;';
        $msgDataContent .= $this->getBalise(self::TAG_SPAN, $author, array(self::ATTR_CLASS=>'message-data-name'));
        $msgClass = ' other-message float-right';
      } else {
        $liClass = '';
        $msgDataClass = '';
        $msgDataContent  = $this->getBalise(self::TAG_SPAN, $author, array(self::ATTR_CLASS=>'message-data-name')).'&nbsp;&nbsp;';
        $msgDataContent .= $this->getBalise(self::TAG_SPAN, date('H:i', $ts), array(self::ATTR_CLASS=>'message-data-time'));
        $msgClass = ' my-message';
      }
      $args = array(
        $liClass,
        $msgDataClass,
        $msgDataContent,
        $msgClass,
        $Tchat[0],
        $ts,
      );
      $lstMsgs .= $this->getRender($this->urlTchatMsgTpl, $args);
      $prevTs = $ts;
    }
    return $lstMsgs;
  }

  private function getLstDetails()
  {
    $lstDetails       = array();
    $survivors        = $this->objXmlDocument->xPath('//survivor');
    while (!empty($survivors)) {
      $survivor       = array_shift($survivors);
      $TokenBean      = new TokenBean($survivor);
      $lstDetails[]   = $TokenBean->getTokenDetail();
    }
    return implode('', $lstDetails);
  }
  private function getLstPortraits()
  {
    $lstPortraits     = array();
    $survivors        = $this->objXmlDocument->xPath('//survivor');
    while (!empty($survivors)) {
      $survivor       = array_shift($survivors);
      $TokenBean      = new TokenBean($survivor);
      $lstPortraits[] = $TokenBean->getTokenPortrait();
    }
    // On rajoute un Unkonwn, pour pouvoir ajouter un Survivant.
    $args = array(
      self::ATTR_ID    => 'portrait-new',
      self::ATTR_CLASS => 'unknown',
      self::ATTR_SRC   => '/wp-content/plugins/hj-zombicide/web/rsc/img/portraits/p.jpg',
      self::ATTR_TITLE => 'Add a Survivor',
    );
    $lstPortraits[] = $this->getBalise(self::TAG_IMG, '', $args);
    return implode('', $lstPortraits);
  }

  private function displayZombies()
  {
    $lstZombies    = '';
    // On récupère les Zombies pour les afficher
    $zombies       = $this->objXmlDocument->xPath('//zombie');
    while (!empty($zombies)) {
      $zombie      = array_shift($zombies);
      $TokenBean   = new TokenBean($zombie);
      $lstZombies .= $TokenBean->getTokenBalise();
      $lstZombies .= $TokenBean->getTokenMenu();
    }
    return $lstZombies;
  }
  private function displaySurvivors()
  {
    $lstSurvivors    = '';
    // On récupère les Survivants pour les afficher
    $survivors       = $this->objXmlDocument->xPath('//survivor');
    while (!empty($survivors)) {
      $survivor      = array_shift($survivors);
      $TokenBean     = new TokenBean($survivor);
      $lstSurvivors .= $TokenBean->getTokenBalise();
      $lstSurvivors .= $TokenBean->getTokenMenu();
    }
    return $lstSurvivors;
  }
  private function displayTokens()
  {
    $lstChips    = '';
    // On récupère les Tokens pour les afficher
    $chips       = $this->objXmlDocument->xPath('//chip');
    while (!empty($chips)) {
      $chip      = array_shift($chips);
      $TokenBean = new TokenBean($chip);
      $lstChips .= $TokenBean->getTokenBalise();
      $lstChips .= $TokenBean->getTokenMenu();
    }
    return $lstChips;
  }
  private function displayTiles()
  {
    $lstTiles      = '';
    // On récupère les Dalles pour les afficher
    $tiles         = $this->objXmlDocument->xPath('//tile');
    while (!empty($tiles)) {
      $tile        = array_shift($tiles);
      $code        = $tile->attributes()[self::FIELD_CODE];
      $orientation = $tile->attributes()['orientation'];
      $args = array(
        self::ATTR_CLASS => 'mapTile '.$orientation,
        'style'          => "background:url('/wp-content/plugins/hj-zombicide/web/rsc/img/tiles/".$code."-500px.png');",
      );
      $lstTiles .= $this->getBalise(self::TAG_DIV, '', $args);
    }
    return $lstTiles;
  }
  private function getDimensions()
  {
    $maps = $this->objXmlDocument->xPath('//map');
    $map  = array_shift($maps);
    // On détermine les dimensions de la map pour pouvoir appliquer les styles css
    $this->width  = $map->attributes()['width'];
    $this->height = $map->attributes()['height'];
    return 'map'.$this->height.'x'.$this->width;
  }
  private function openFile()
  {
    $fileName = PLUGIN_PATH.$this->urlDirLiveMissions.$_SESSION['zombieKey'].".mission.xml";
    $this->objXmlDocument = simplexml_load_file($fileName);
    $objXmlDocument = simplexml_load_file($fileName);
    $objJsonDocument = json_encode($objXmlDocument);
    $arrOutput = json_decode($objJsonDocument, TRUE);
    $this->map = $arrOutput['map'];
  }
}

function sort_trees($t1, $t2) {
  return ($t1['timestamp']*1 > $t2['timestamp']*1);
}
<file_sep>/core/daoimpl/SkillDaoImpl.php
<?php
if (!defined('ABSPATH')) {
  die('Forbidden');
}
/**
 * Classe SkillDaoImpl
 * @author Hugues.
 * @since 1.00.00
 * @version 1.05.06
 */
class SkillDaoImpl extends LocalDaoImpl
{
  /**
   * Class constructor
   */
  public function __construct()
  { parent::__construct('Skill'); }
  /**
   * @param array $rows
   * @return array
   */
  protected function convertToArray($rows)
  {
    $Items = array();
    if (!empty($rows)) {
      foreach ($rows as $row) {
        $Items[] = Skill::convertElement($row);
      }
    }
    return $Items;
  }

  /**
   * @param array $arrParams
   * @param array $filters
   * @return array
   */
  public function selectEntriesWithFiltersIn($arrParams, $filters)
  {
    // On s'appuie sur la requête de base.
    $requete  = $this->selectRequest.$this->fromRequest;
    // On doit faire une jointure externe pour lier la table mission_expansion si on cherche sur ce critère
    if (isset($filters[self::FIELD_TAGLEVELID])) {
      $requete .= 'INNER JOIN wp_11_zombicide_survivor_skill ss ON s.id=ss.skillId ';
      // On passe ensuite aux critères de sélection.
      $requete .= 'WHERE 1=1 ';
      // Contrainte sur le niveau
      $requete .= 'AND tagLevelId IN ('.$filters[self::FIELD_TAGLEVELID].') ';
    }
    // On peut aussi trier
    $requete .= $this->orderBy;
    // Et retourner le tableau de résultats.
    return $this->convertToArray($this->selectEntriesAndLogQuery(__FILE__, __LINE__, $requete, $arrParams));
  }

  /**
   * @param string $file
   * @param int $line
   * @param array $arrParams
   * @return array|Skill
   */
  public function select($file, $line, $arrParams)
  { return parent::localSelect($file, $line, $arrParams, new Skill()); }
}
