resolve conflic

This commit is contained in:
Arnaud Fornerot 2020-12-15 16:37:58 +01:00
commit 0dba8c4dc1
44 changed files with 1829 additions and 37 deletions

View File

@ -14,6 +14,10 @@ websocket:
resource: "@CadolesWebsocketBundle/Resources/config/routing.yml"
prefix: /
edispatcher:
resource: "@CadolesEdispatcherBundle/Resources/config/routing.yml"
prefix: /
lightsaml_sp:
resource: "@LightSamlSpBundle/Resources/config/routing.yml"
prefix: saml

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -0,0 +1,71 @@
$(function() {
var _reduit_timeout=null
var _resize_timeout=null
var METICE= {
logoutSelector : ".navbar-top-links li:last-child a",
logoutSessionName : "_metice_logout"
}
window.addEventListener("message", function(msg){
var header = window.top.document.getElementsByClassName("header")
// logout url envoyé par message, pour changer le lien de logout
if (msg.data.message=="setLogoutUrl" && header && header.length!=0) {
// récupère la valeur de l'attribut href du lien de login/logout
var elt=header[0].querySelector(METICE.logoutSelector);
var logoutUrl=elt.getAttribute("href")
// Ce n'est pas du logout => on ne fait rien
if (logoutUrl.indexOf("logout")==-1) {
return;
}
// url de logout déja renseignée, on va l'utiliser
var logoutSession = sessionStorage.getItem(METICE.logoutSessionName)
if (logoutSession) {
if (header && header.length!=0) { header[0].querySelector(METICE.logoutSelector).setAttribute("href",logoutSession) }
return;
}
var elt=header[0].querySelector(METICE.logoutSelector);
logoutUrl = window.location.origin+logoutUrl
elt.setAttribute("href",msg.data.url+"?orign="+logoutUrl)
// Sauvegarde de l'url de logout, pour restauration lors de l'actualisation page
sessionStorage.setItem(METICE.logoutSessionName,msg.data.url+"?orign="+logoutUrl)
return;
}
// message on scroll, pour réduire le header
if (msg.data.message=="onScroll" && header && header.length!=0){
if (msg.data.position > 100 ) {
if(_reduit_timeout) {clearTimeout(_reduit_timeout);_reduit_timeout=null}
header[0].classList.add("reduit");
if (_resize_timeout==null) {
_resize_timeout=setTimeout(function() {console.log("resize");window.top.resizeFrame();_resize_timeout=null},100);
}
}
if (msg.data.position == 0 ) {
header[0].classList.remove("reduit");
_reduit_timeout = setTimeout(function(){ console.log("resize");window.top.resizeFrame();},100)
}
return;
}
});
// Remise en place de l'url de logout si c'est une url de logout qui est présente dans
var header = window.top.document.getElementsByClassName("header")
if (header && header.length!=0) {
var logout = sessionStorage.getItem(METICE.logoutSessionName)
var elt = header[0].querySelector(METICE.logoutSelector);
var logoutUrl = elt.getAttribute("href")
if (logout && logoutUrl.indexOf("logout")!=-1) {
header[0].querySelector(METICE.logoutSelector).setAttribute("href",logout)
}
}
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 556 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -1,53 +1,186 @@
div.header {
background-image: linear-gradient(90deg,rgba(255,255,255,1) 0%, rgba(255,255,255,1) 15%, rgba(255,255,255,0.1) 50%, rgba(255,255,255,0) 100%),var(--header) !important;
/* feuille de style Ninegate
* METICE 2020 - Académie de la Réunion
* --
* DSI4
*/
/* Teintes en fonction du profil utilisateur */
/* eleves #VERT #169B62
* dark : #3b8211
* light : #dffdcf
*/
/* ====== ESSAI D'UN MODE REDUIT ======= */
.header.reduit {
height: 38px !important;
overflow-y: hidden;
}
.header.reduit .nav.navbar-top-links.navbar-right > li a {font-size: 1.5em; }
.header .title img {transition:margin-left 0.1s ease; }
.header.reduit .title img { margin-left:-300px !important;}
.header.reduit small {
top: 18px !important;
font-size: 0.5em;
}
.header.reduit > a.title > span {
line-height: 25px !important;
transition:line-height 0.5s ease;
font-size: 0.9em;
}
.header.reduit .avatar {height: 25px; }
/* ===================== FIN MODE REDUIT ==========================*/
/* fix defaut d'affichage sur toogle switch */
body, .slick .slicksubtitle, .slick .slicklink { height: 30px;}
body {
background: linear-gradient(180deg, var(--main), white,50%);
}
body.eleves, body.grp-National_1 {
--colorbody: #ffffff;
--main: #169b62;
--mainR: 22;
--mainG: 155;
--mainB: 98;
--dark: #006930;
--darkR: 0;
--darkG: 105;
--darkB: 48;
--light: #48cd94;
--lightR: 72;
--lightG: 205;
--lightB: 148;
--heightheader: 150;
}
/* responsables #MAUVE #7D4E5B
*/
body.responsables, body.grp-National_2 {
--colorbody: #ffffff;
--main: #7d4e5b;
--mainR: 125;
--mainG: 78;
--mainB: 91;
--dark: #4b1c29;
--darkR: 75;
--darkG: 28;
--darkB: 41;
--light: #af7f8d;
--lightR: 175;
--lightG: 128;
--lightB: 141;
--heightheader: 150;
}
/* professeurs, enseignants1d #BLEU1 #5770BE */
body.professeurs, body.enseignants1d, body.grp-National_3 {
--colorbody: #ffffff;
--main: #5770be;
--mainR: 87;
--mainG: 112;
--mainB: 190;
--dark: #253e8c;
--darkR: 37;
--darkG: 62;
--darkB: 140;
--light: #89a2f0;
--lightR: 137;
--lightG: 162;
--lightB: 240;
--heightheader: 150;
}
/* administratifs, admin #ORANGE #FF6F4C */
body.administratifs, body.admin, body.grp-National_4 , body.grp-National_5, body.grp-National_6 {
--colorbody: #ffffff;
--main: #ff6f4c;
--mainR: 255;
--mainG: 111;
--mainB: 76;
--dark: #cd3d1a;
--darkR: 205;
--darkG: 61;
--darkB: 26;
--light: #ffa17e;
--lightR: 255;
--lightG: 161;
--lightB: 126;
--heightheader: 150;
}
/* PersAcad #BLEU2 #484d7a */
body.PersAcad, body.grp-National_7, body.grp-PersAcad {
--colorbody: #ffffff;
--main: #484d7a;
--mainR: 72;
--mainG: 77;
--mainB: 122;
--dark: #161b48;
--darkR: 22;
--darkG: 27;
--darkB: 72;
--light: #7a7fac;
--lightR: 122;
--lightG: 127;
--lightB: 172;
--heightheader: 150;
}
/* Cartouche header Métice - région académique La Réunion */
div.header {
background: white !important;
/*background-image: linear-gradient(45deg, white 200px, var(--light));
background-position-x: 300px;
background-repeat: no-repeat;*/
}
.avatar { background-color: var(--main) !important; }
#logo {
height: 79px !important;
height: 130px !important;
float: left !important;
top: -8px !important;
left: -10px !important;
top: -10px !important;
left: 0px !important;
position: relative !important;
}
div.header > a > span {
.container-fluid #logo { display: none; }
.header .avatar {height: 32px; }
.header .title {
font-size: 30px !important;
}
div.header > a.title > span {
color: black !important;
line-height: 114px !important;
padding-left: 20px !important;
}
nav#appmenu > ul > li.active > a.active, .nav>li>a:hover {
background-color: white !important;
color: var(--main);
font-weight: bold;
.header small {
/* font-size: 16px !important; */
font-size: initial;
top: 24px !important;
margin-left: 3px !important;
font-family: var(--fontfacebody) !important;
}
.navbar-default, .grid-item, .widgetheader {
background-color: var(--main) !important;
}
.grid-item-content {
background-color: var(--dark) !important;
}
.widget-mini > .widgetheader {
background-color: var(--light) !important;
}
/* reprise des couleurs sur les icones d'action */
.navbar-default { border-color: var(--light) !important }
.nav > li > a:hover {
background:none !important;
.nav.navbar-top-links.navbar-right > li a {
color: var(--main, #337ab7) !important;
font-size: 2.0em;
padding-top: 9px;
}
.nav.navbar-top-links.navbar-left>li>a:hover {
text-shadow: var(--light) 0px 0px 20px;
color: var(--main) !important;
background-color: var(--light) !important;
.nav.navbar-top-links.navbar-right > li a:hover {
color: var(--fontcolorhover, white) !important;
background: var(--fontcolorhoverdark, black) !important;
}
.nav.navbar-top-links.navbar-right>li>a:hover {
text-shadow: white 0px 0px 20px;
color: var(--main) !important;
background: none !important;
}
nav#appmenu { zoom: 0.68; font-weight: bold; }

View File

@ -0,0 +1,3 @@
*~
*.swp
*.swo

View File

@ -0,0 +1,9 @@
<?php
namespace Cadoles\EdispatcherBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class CadolesEdispatcherBundle extends Bundle
{
}

View File

@ -0,0 +1,903 @@
<?php
namespace Cadoles\EdispatcherBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpFoundation\File\File;
use GuzzleHttp\Exception\RequestException;
use Symfony\Component\Form\FormError;
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
use Symfony\Component\Serializer\Serializer;
use Cadoles\CoreBundle\Entity\User;
use Cadoles\CoreBundle\Entity\Group;
use Cadoles\CoreBundle\Entity\UserGroup;
use Cadoles\CoreBundle\Entity\UserModo;
use Cadoles\CoreBundle\Entity\Niveau01;
use Cadoles\CoreBundle\Entity\Niveau02;
use Cadoles\WebsocketBundle\Entity\Message;
use Cadoles\CoreBundle\Form\UserType;
use Cadoles\CoreBundle\Form\MailingType;
use Cadoles\PortalBundle\Entity\Pagewidget;
use Cadoles\PortalBundle\Form\PagewidgetType;
use Cadoles\PortalBundle\Entity\Bookmark;
use Cadoles\CASBundle\Controller\SecurityController;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Cadoles\EdispatcherBundle\Entity\Datasource;
class ApiController extends Controller
{
public function testAction() {
$em = $this->getDoctrine()->getManager();
$datasource = $em->getRepository('CadolesEdispatcherBundle:Datasource')->findAll();
$request = $this->container->get('request_stack')->getCurrentRequest();
$id = $request->query->get('id',false);
if ($id) {
$user=$em->getRepository('CadolesCoreBundle:User')->findOneBy(["username"=>$id]);
if (!$user) {
$output["error"]="id=".$id." : user not found";
return new Response(json_encode($output), 400);
}
$groups = array();
foreach($user->getGroups() as $usergroup) {
$tmp = $usergroup->getGroup()->getLabel();
array_push($groups,$tmp);
}
$groups = implode("|",$groups);
} else $groups = "syntaxe: /edispatcher/test?id={username} - pour afficher une liste des groupes d'un username arbitraire (pas celui en session)";
// Init Client CAS
\phpCAS::setDebug(false);
\phpCAS::client(CAS_VERSION_2_0, $this->getParameter('cas_host'), $this->getParameter('cas_port'), is_null($this->getParameter('cas_path')) ? '' : $this->getParameter('cas_path'), false);
\phpCAS::setNoCasServerValidation();
// Authentification
\phpCAS::forceAuthentication();
// Récupération UID
$username = \phpCAS::getUser();
$attributes = \phpCAS::getAttributes();
return $this->render('CadolesEdispatcherBundle:Test:test.html.twig',[
'useheader' => true,
'usemenu' => false,
'usesidebar' => false,
'attributes' => $attributes,
'groups' => $groups,
'username' => $username,
'datasource' => $datasource,
]);
}
public function desktopitemAction() {
$request = $this->container->get('request_stack')->getCurrentRequest();
$id = $request->query->get('id','null');
$em = $this->getDoctrine()->getManager();
// Récupération Attribut
// acces base pour recuperer profil et groupe pour le user
$datasource = $em->getRepository('CadolesEdispatcherBundle:Datasource')->findOneBy(["iddatasource"=>$id]);
if (!$datasource) {
$output["error"]="no datasource session found for id = $id";
return new Response(json_encode($output), 400);
}
$login = $datasource->getUsername();
$profil = $datasource->getProfil();
// Récupérer l'utilisateur
$user=$em->getRepository('CadolesCoreBundle:User')->findOneBy(["username"=>$login]);
if(!$user) {
$output["error"]="user not exist";
return new Response(json_encode($output), 400);
}
$groups = array();
foreach($user->getGroups() as $usergroup) {
$tmp = $usergroup->getGroup()->getLabel();
array_push($groups,$tmp);
}
$groupes=implode("|",$groups);
$listessoitems = $datasource->getSsoitems();
$ssoitems=explode("|",$listessoitems);
// Format de sortie
$output["bookmarks"] = [];
$output["items"] = [];
$output["itemcategorys"] = [];
$bookmarks=null;
$items=null;
$itemcategorys=null;
$weburl="https://".$this->getParameter("weburl")."/".$this->getParameter("alias")."/";
$em->getRepository("CadolesPortalBundle:Item")->getUserItems($user,$bookmarks,$items,$itemcategorys,null,$ssoitems,3);
$itemsperso = false;
if($bookmarks) {
foreach($bookmarks as $bookmark) {
$tmp=[];
$tmp["id"] = $bookmark->getId();
$tmp["title"] = $bookmark->getTitle();
$tmp["subtitle"] = $bookmark->getSubtitle();
$tmp["url"] = $bookmark->getUrl();
$tmp["target"] = $bookmark->getTarget();
$tmp["order"] = 0;
$tmp["icon"] = $weburl.($bookmark->getIcon()?$bookmark->getIcon()->getLabel():"uploads/icon/icon_pin.png");
$tmp["color"] = ($bookmark->getColor()?$bookmark->getColor():$this->get('session')->get('color')["main"]);
$tmp["essential"] = false;
if ($bookmark->getItem())
$tmp["category"] = $bookmark->getItem()->getItemcategory()->getId();
else {
$tmp["category"] = -999;
$itemsperso = true;
}
$tmp["badge_url"] = "";
$tmp["badge_type"] = "";
$tmp["badge_message"] = "";
$tmp["mode"] = false;
if ($tmp["category"] != -999) array_push($output["bookmarks"],$tmp);
array_push($output["items"],$tmp);
}
}
$arrayitems = array();
if($items) {
foreach($items as $item) {
$tmp=[];
$tmp["id"] = $item->getId();
$tmp["title"] = $item->getTitle();
$tmp["subtitle"] = $item->getSubtitle();
$tmp["url"] = $item->getUrl();
$tmp["target"] = $item->getTarget();
$tmp["order"] = $item->getRoworder();
$tmp["color"] = ($item->getColor()?$item->getColor():$this->get('session')->get('color')["main"]);
$tmp["icon"] = $weburl.($item->getIcon()?$item->getIcon()->getLabel():"uploads/icon/icon_pin.png");
$tmp["essential"] = $item->getEssential();
$tmp["category"] = $item->getItemcategory()->getId();
$tmp["badge_url"] = "";
$tmp["badge_type"] = "";
$tmp["badge_message"] = "";
$tmp["mode"] = false;
array_push($arrayitems,$tmp);
}
}
sort($arrayitems);
foreach ($arrayitems as $elem) {
if (!in_array($elem,$output["items"])) array_push($output["items"],$elem);
}
$arrayitemcats = array();
if($itemcategorys) {
foreach($itemcategorys as $itemcategory) {
$tmp=[];
$tmp["id"] = $itemcategory->getId();
$tmp["title"] = $itemcategory->getLabel();
$tmp["order"] = $itemcategory->getRoworder();
$tmp["color"] = ($itemcategory->getColor()?$itemcategory->getColor():$this->get('session')->get('color')["main"]);
array_push($arrayitemcats,$tmp);
}
}
sort($arrayitemcats);
foreach ($arrayitemcats as $elem) {
if (!in_array($elem,$output["itemcategorys"])) array_push($output["itemcategorys"],$elem);
}
//
// xdesktop2ninegate 2020
//
// Gestion des infos du profil ==============
$rne = $user->getSiren();
$arr=array();
$arr["profil"]=$profil;
$arr["rne"]=$rne;
$arr["groupes"]=$groupes;
$user_infos=json_encode($arr);
// L'ensemble des favoris qui correspond à l'utilisateur
$tbfav=array();
foreach($output["bookmarks"] as $bookmark) {
$tmp=array('url'=>$bookmark['url'],'libelle'=>$bookmark['title'],'icon'=>$bookmark['icon']);
array_push($tbfav,$tmp);
}
$favoris=json_encode($tbfav);
// Nom des categories
$tbcat = $output["itemcategorys"];
$cat=array();
foreach($tbcat as $item)
{
$cat[$item['id']] = $item['title'];
}
$tbitem = $output["items"];
// Construction de la chaine xml
$carriage_return = array("\r\n", "\n", "\r");
$ret_str="<main>";
$ret_str=$ret_str."<buttondatas>";
foreach($tbitem as $item) {
$ret_str=$ret_str."<buttondata>";
$ret_str=$ret_str."<nom>".str_replace("&"," ",($item['title']))."</nom>";
$ret_str=$ret_str."<icon>".htmlentities(($item['icon']))."</icon>";
$ret_str=$ret_str."<libelle>".str_replace("&"," ",($item['subtitle']))."</libelle>";
$ret_str=$ret_str."<url>".htmlentities(($item['url']))."</url>";
if ( $item['category'] != -999 ) {
$ret_str=$ret_str."<categoriename>".($cat[$item['category']])."</categoriename>";
} else $ret_str=$ret_str."<categoriename>Mes Ressources</categoriename>";
$ret_str=$ret_str."<categorieid>".$item['category']."</categorieid>";
$ret_str=$ret_str."<infos_url>".htmlentities($item['badge_url'])."</infos_url>";
$ret_str=$ret_str."<infos_type>".$item['badge_type']."</infos_type>";
# ref: #5629 et #4261
if ($item['mode']) {
$ret_str=$ret_str."<external>true</external>";
}
$ret_str=$ret_str."<infos_message>".str_replace($carriage_return,"<br/>",htmlentities($item['badge_message']))."</infos_message>";
$ret_str=$ret_str."</buttondata>";
}
$ret_str=$ret_str."</buttondatas>";
// Description
$ret_str=$ret_str."<description>";
$ret_str=$ret_str."<services_url>mxServices</services_url>";
$ret_str=$ret_str."</description>";
// Categories
$ret_str=$ret_str."<categories>";
foreach ($tbcat as $categorie) {
$ret_str=$ret_str."<categorie>";
$ret_str=$ret_str."<nom>".$categorie['title']."</nom>";
if (isset($categorie['color'])) $ret_str=$ret_str."<couleur>".$categorie['color']."</couleur>";
$ret_str=$ret_str."<indice>".$categorie['order']."</indice>";
$ret_str=$ret_str."<icone></icone>";
$ret_str=$ret_str."</categorie>";
}
if ($itemsperso) {
$ret_str=$ret_str."<categorie>";
$ret_str=$ret_str."<nom>Mes Ressources</nom>";
$ret_str=$ret_str."<indice>-1</indice>";
$ret_str=$ret_str."<icone>fa-user-circle</icone>";
$ret_str=$ret_str."</categorie>";
}
$ret_str=$ret_str."</categories>";
$ret_str=$ret_str."</main>";
if (isset($_GET["callback"])) {postData($ret_str,$message,$favoris,$user_infos);die();}
$message="";
// Si js
if (isset($_GET["js"])) {
$id=$_GET["id"];
header("Content-type: text/javascript");
$data=json_encode(array("xml"=>(htmlentities($ret_str,ENT_QUOTES,"UTF-8")),
"favoris"=>(htmlentities($favoris,ENT_QUOTES,"UTF-8")),
"userinfos"=>(htmlentities(str_replace("'",'"',$user_infos),ENT_QUOTES,"UTF-8")),
"id"=>$id/*,
"referer"=>(isset($_SERVER['HTTPS']) ? "https" : "http") . "://{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}"*/
));
ob_start();
echo <<<EOL
/* DATA_FOR_MXDESKTOP: Ne pas supprimer cette ligne */
var username='$login';
var source=MX.getSource('$id');
if (!source) {
console.log("Source $id introuvable");
} else {
source.setData('$data');
}
EOL
;
$sortie= ob_get_clean();
$reponse = new Response();
$reponse->setContent($sortie);
$reponse->headers->set('Content-Type', 'text/javascript');
return $reponse;
}
ob_start();
echo $ret_str;
echo "\n";
echo "MSG=$message";
echo "\n";
echo "FAV=$favoris";
echo "\n";
echo "UI=$user_infos";
$sortie= ob_get_clean();
$reponse = new Response();
$reponse->setContent($sortie);
$reponse->headers->set('Content-Type', 'text/html');
return $reponse;
}
public function messageAction($idalert = null,$action = null,$access="config") {
$em = $this->getDoctrine()->getManager();
$request = $this->container->get('request_stack')->getCurrentRequest();
$id = $request->query->get('id',null);
$token = $request->request->get('token',null);
$data = $request->request->get('data',null);
if($data) $token=$data['token'];
$api = $request->query->get('api','');
// Tester présence des parametres
if ((is_null($id)) and (is_null($token))) {
$output["error"]="missing parameter : id / token";
return new Response(json_encode($output), 400);
}
// Récupération Attribut
// acces base pour recuperer profil et groupe pour le user
if($id) $datasource = $em->getRepository('CadolesEdispatcherBundle:Datasource')->findOneBy(["iddatasource"=>$id]);
else if($token) $datasource = $em->getRepository('CadolesEdispatcherBundle:Datasource')->findOneBy(["token"=>$token]);
if (!$datasource) {
$output["error"]="no datasource session found for id = $id / token = $token";
return new Response(json_encode($output), 400);
}
$username = $datasource->getUsername();
// Gestion des infos du profil ==============
$output = [];
// Récupérer l'utilisateur
$user=$em->getRepository('CadolesCoreBundle:User')->findOneBy(["username"=>$username]);
if(!$user) {
$output["error"]="user not exist";
return new Response(json_encode($output), 400);
}
$weburl="https://".$this->getParameter("weburl")."/".$this->getParameter("alias")."/";
$serializer = new Serializer(array(new DateTimeNormalizer()));
if ($action == null) {
// /!\ ->getUserAlerts from eoledev repository paquet dev1.0+3-100 (ssoitems)
$listessoitems = $datasource->getSsoitems();
$ssoitems=explode("|",$listessoitems);
$alerts=$em->getRepository("CadolesPortalBundle:Alert")->getUserAlerts($user,null,null,$ssoitems);
$messages = [];
foreach($alerts as $alert) {
$tmp=[];
$tmp["id"] = $alert->getId();
$bgcolor = ($alert->getAlertcategory()->getColor()?$alert->getAlertcategory()->getColor():$this->get('session')->get('color')["main"]);
$iconeurl = $weburl.($alert->getAlertcategory()->getIcon()?$alert->getAlertcategory()->getIcon()->getLabel():"uploads/icon/icon_pin.png");
$compteur=0;
$urls=[];
foreach($alert->getItems() as $item) {
$urls[] = $item->getUrl();
$compteur++;
}
$tmp["url"] = implode("|",$urls);
switch ($alert->getAlertcategory()->getLabel()) {
case "sans categorie": $flag="none"; break;
case "Rappel": $flag="rappel"; break;
case "Attention/Important": $flag="attention"; break;
case "Message": $flag="mail"; break;
default: $flag=""; break;
}
$categorielabel="";
if ($compteur==0) {
if ($alert->getAlertcategory()->getLabel()!="sans categorie")
if ($flag=="") {
$categorielabel="<div style='float:left;width: 102.5%;padding: 4px;padding-left: 4px;color:white;background-color: #$bgcolor;position:relative;top: -16px;left: -11px;'><img src='$iconeurl' width='24em'>&nbsp;".$alert->getAlertcategory()->getLabel()."</div></br>";
$tmp["message"] = "$categorielabel<h4>".$alert->getTitle()."</h4>".$alert->getContent();
} else $tmp["message"] = "<h4>".$alert->getTitle()."</h4>".$alert->getContent();
}
else
{
if ($alert->getAlertcategory()->getLabel()!="sans categorie")
if ($flag=="") {
$categorielabel="<img src='$iconeurl' width='16em' ><span style='font-weight: bold'> ".$alert->getAlertcategory()->getLabel()."</span> : ";
$tmp["message"] = $categorielabel.$alert->getTitle()."".$alert->getContent();
} else $tmp["message"] = $alert->getTitle()."</br>".$alert->getContent();
}
$tmp["filtre"] = "aucun";
$tmp["valeur"] = "[]";
$tmp["flag"] = $flag;
$tmp["closeable"] = ($alert->getFghideable()?"true":"false");
$tmp["requestable"] = "false";
$tmp["display_mode"]= "postit";
$tmp["responses"] = "[]";
$tmp["start"] = $serializer->normalize($alert->getPublishedat(),null,array(DateTimeNormalizer::FORMAT_KEY => 'Y-m-d H:i:s'));
$tmp["stop"] = $serializer->normalize($alert->getUnpublishedat(),null,array(DateTimeNormalizer::FORMAT_KEY => 'Y-m-d H:i:s'));
$tmp["updated"] = $serializer->normalize($alert->getPublishedat(),null,array(DateTimeNormalizer::FORMAT_KEY => 'Y-m-d H:i:s'));
$tmp["user_responses"] = "[]";
array_push($messages,$tmp);
}
$data = json_encode([
'messages' => $messages,
'admin' => false,
]);
$datajs="var data=$data;
$api('$id',data);";
$reponse = new Response();
$reponse->setContent($datajs);
$reponse->headers->set('Content-Type', 'text/javascript');
}
else if ($action=="never") {
/*
* ACTION : BASCULER LA VISIBILITE DU MESSAGE : SI VISIBLE, LE CACHER ET INVERSEMENT
*/
// Tester présence des parametres
if(is_null($idalert)||is_null($action)) {
$output["error"]="missing parameter : idalert - action";
return new Response(json_encode($output), 400);
}
// Tester l'existence de l'alert
$alert=$em->getRepository('CadolesPortalBundle:Alert')->find($idalert);
if(!$alert) {
$output["error"]="alert not exist";
return new Response(json_encode($output), 400);
}
// construction de la réponse
$tmp=[];
$tmp["id"] = $alert->getId();
$bgcolor = ($alert->getAlertcategory()->getColor()?$alert->getAlertcategory()->getColor():$this->get('session')->get('color')["main"]);
$iconeurl = $weburl.($alert->getAlertcategory()->getIcon()?$alert->getAlertcategory()->getIcon()->getLabel():"uploads/icon/icon_pin.png");
$tmp["message"] = "<div width=100% style='background-color:#$bgcolor;color:white;padding:12px'><h4><img src='$iconeurl' width='30em' ><span style='font-weight: bold'> ".$alert->getAlertcategory()->getLabel()."</span> : <small style='color:white'>".$alert->getTitle()."</small></h4>".$alert->getContent()."</div>";
$tmp["filtre"] = "aucun";
$tmp["valeur"] = "[]";
$tmp["flag"] = "";
$tmp["closeable"] = ($alert->getFghideable()?"true":"false");
$tmp["requestable"] = "false";
$tmp["display_mode"]= "postit";
$tmp["responses"] = "[]";
$tmp["start"] = $serializer->normalize($alert->getPublishedat(),null,array(DateTimeNormalizer::FORMAT_KEY => 'Y-m-d H:i:s'));
$tmp["stop"] = $serializer->normalize($alert->getUnpublishedat(),null,array(DateTimeNormalizer::FORMAT_KEY => 'Y-m-d H:i:s'));
$tmp["updated"] = $serializer->normalize($alert->getPublishedat(),null,array(DateTimeNormalizer::FORMAT_KEY => 'Y-m-d H:i:s'));
$tmp["user_responses"] = "[]";
// Tester que l'alert est masquable
if(!$alert->getFghideable()) {
$output["error"]="alert not hideable";
return new Response(json_encode($output), 400);
}
// Basculer l'état de visibilité de l'alert
if($alert->getReaders()->contains($user)) $alert->removeReader($user);
else $alert->addReader($user);
$em->persist($alert);
$em->flush();
$reponse=new Response(json_encode($tmp), 200);
$reponse->headers->set('Access-Control-Allow-Origin', '*');
return $reponse;
}
else if ($action=="reset") {
/*
* ACTION : RESTAURER LE MESSAGE
*/
// Tester présence des parametres
if(is_null($idalert)||is_null($action)) {
$output["error"]="missing parameter : idalert - action";
return new Response(json_encode($output), 400);
}
// Tester l'existence de l'alert
$alert=$em->getRepository('CadolesPortalBundle:Alert')->find($idalert);
if(!$alert) {
$output["error"]="alert not exist";
return new Response(json_encode($output), 400);
}
// construction de la réponse
$tmp=[];
$tmp["id"] = $alert->getId();
$bgcolor = ($alert->getAlertcategory()->getColor()?$alert->getAlertcategory()->getColor():$this->get('session')->get('color')["main"]);
$iconeurl = $weburl.($alert->getAlertcategory()->getIcon()?$alert->getAlertcategory()->getIcon()->getLabel():"uploads/icon/icon_pin.png");
$tmp["message"] = "<div width=100% style='background-color:#$bgcolor;color:white;padding:12px'><h4><img src='$iconeurl' width='30em' ><span style='font-weight: bold'> ".$alert->getAlertcategory()->getLabel()."</span> : <small style='color:white'>".$alert->getTitle()."</small></h4>".$alert->getContent()."</div>";
$tmp["filtre"] = "aucun";
$tmp["valeur"] = "[]";
$tmp["flag"] = "";
$tmp["closeable"] = ($alert->getFghideable()?"true":"false");
$tmp["requestable"] = "false";
$tmp["display_mode"]= "postit";
$tmp["responses"] = "[]";
$tmp["start"] = $serializer->normalize($alert->getPublishedat(),null,array(DateTimeNormalizer::FORMAT_KEY => 'Y-m-d H:i:s'));
$tmp["stop"] = $serializer->normalize($alert->getUnpublishedat(),null,array(DateTimeNormalizer::FORMAT_KEY => 'Y-m-d H:i:s'));
$tmp["updated"] = $serializer->normalize($alert->getPublishedat(),null,array(DateTimeNormalizer::FORMAT_KEY => 'Y-m-d H:i:s'));
$tmp["user_responses"] = "[]";
// Tester que l'alert est masquable
if(!$alert->getFghideable()) {
$output["error"]="alert not hideable";
return new Response(json_encode($output), 400);
}
// Restaurer l'alert
if($alert->getReaders()->contains($user)) {
$alert->removeReader($user);
$em->persist($alert);
$em->flush();
}
$reponse=new Response(json_encode($tmp), 200);
$reponse->headers->set('Access-Control-Allow-Origin', '*');
return $reponse;
} else {
dump("action = $action sur idmsg = $idmsg");
die;
}
return $reponse;
}
public function helloAction() {
$reponse = new Response('ninegate', Response::HTTP_OK);
return $reponse;
}
public function meAction($access="config") {
$em = $this->getDoctrine()->getManager();
$request = $this->container->get('request_stack')->getCurrentRequest();
$token = $request->query->get('token',false);
// Récupération Attribut
// acces base pour recuperer profil et groupe pour le user
if ($token) {
$datasource = $em->getRepository('CadolesEdispatcherBundle:Datasource')->findOneBy(["token"=>$token]);
if (!$datasource) {
$output["error"]="no datasource session found for token = $token";
return new Response(json_encode($output), 400);
}
$username = $datasource->getUsername();
} else {
$output["error"]="no token provided - Abort";
return new Response(json_encode($output), 400);
}
$output = [];
// Récupérer l'utilisateur
$user=$em->getRepository('CadolesCoreBundle:User')->findOneBy(["username"=>$username]);
if(!$user) {
$output["error"]="user not exist";
return new Response(json_encode($output), 400);
}
$roles=($user?$user->getRoles():["ROLE_ANONYME"]);
if (in_array("ROLE_MODO",$roles)) {
$permmod=$em->getRepository("CadolesCoreBundle:PermModo")->findOneBy(["route"=>"cadoles_portal_config_alert","permmodoprofil"=>$user->getPermmodoprofil()]);
$adminpostit=$permmod?$permmod->getVisible():false;
} else $adminpostit=in_array("ROLE_ADMIN",$roles);
// Profilage
$username = $user->getFirstName()." ".$user->getLastName();
$reponse = new Response();
$reponse->setContent(json_encode([
'display_name' => $username,
'admin_postit' => $adminpostit,
]));
$reponse->headers->set('Content-Type', 'text/html');
$reponse->headers->set('Access-Control-Allow-Origin', '*');
return $reponse;
}
public function sessionAction($id="",$access="config") {
// Masteridentity
$masteridentity=$this->getParameter("masteridentity");
if($masteridentity!="SSO") {
$output["error"]="Le bundle Edispatcher ne fonctionne qu'en mode MasterIdentity=SSO";
return new Response(json_encode($output), 400);
}
// Init Client CAS
\phpCAS::setDebug(false);
\phpCAS::client(CAS_VERSION_2_0, $this->getParameter('cas_host'), $this->getParameter('cas_port'), is_null($this->getParameter('cas_path')) ? '' : $this->getParameter('cas_path'), false);
\phpCAS::setNoCasServerValidation();
// Authentification
\phpCAS::forceAuthentication();
// Récupération UID
$username = \phpCAS::getUser();
$attributes = \phpCAS::getAttributes();
$profil = $attributes["ENTPersonProfils"];
// -- autocreate
// Rechercher l'utilisateur
$em = $this->getDoctrine()->getManager();
if(isset($attributes[$this->getParameter('user_attr_cas_username')]))
$username = $attributes[$this->getParameter('user_attr_cas_username')];
if(isset($attributes[$this->getParameter('user_attr_cas_mail')]))
$email = $attributes[$this->getParameter('user_attr_cas_mail')];
if(isset($attributes[$this->getParameter('user_attr_cas_lastname')]))
$lastname = $attributes[$this->getParameter('user_attr_cas_lastname')];
if(isset($attributes[$this->getParameter('user_attr_cas_firstname')]))
$firstname = $attributes[$this->getParameter('user_attr_cas_firstname')];
$user = $em->getRepository('CadolesCoreBundle:User')->findOneBy(array("username"=>$username));
if (!$user) {
$user = new User();
// On calcule le niveau01 de l'utilisateur
$niveau01=$em->getRepository('CadolesCoreBundle:Niveau01')->calculateNiveau01($attributes);
if(!$niveau01)
throw $this->createNotFoundException('Permission denied. No Organisation Niveau 01 match');
$user->setUsername($username);
$user->setEmail($email);
$user->setLastname($lastname);
$user->setFirstname($firstname);
$user->setPassword("CASPWD-".$username);
$user->setSalt("CASPWD-".$username);
$user->setNiveau01($niveau01);
$user->setSiren($niveau01->getSiren());
$user->setSiret("");
$user->setAvatar("noavatar.png");
$user->setVisible(true);
$user->setAuthlevel("simple");
$user->setRole("ROLE_USER");
if(in_array($username,$this->getParameter("ldap_usersadmin")))
$user->setRole("ROLE_ADMIN");
$em->persist($user);
$em->flush();
// Génération auto des groupes
$this->submitGroup($attributes);
// On calcule les groupes de l'utilisateur
$user=$em->getRepository('CadolesCoreBundle:Group')->calculateGroup($user,$attributes);
}
//-- fin de l'auto create --
// groupes depuis ninegate
$roles=($user?$user->getRoles():["ROLE_ANONYME"]);
$adminpostit=(in_array("ROLE_MODO",$roles));
$groups = array();
foreach($user->getGroups() as $usergroup) {
$tmp = $usergroup->getGroup()->getLabel();
array_push($groups,$tmp);
}
$groupes=implode("|",$groups);
if($this->getParameter('ssosynchroitem')) {
$ssoitems=implode("|",$attributes[$this->GetParameter("user_attr_cas_item")]);
} else $ssoitems = "";
$request = $this->container->get('request_stack')->getCurrentRequest();
$api = $request->query->get('api','null');
$id = $request->query->get('id','xxxxx-xxxxx-xxxx');
// profil et groupes fournis par SSO => on les update dans l'entité Datasource
// en attendant que les infos soient dispos dans le modele User de ninegate
$datasource = $em->getRepository('CadolesEdispatcherBundle:Datasource')->findOneBy(["username"=>$username]);
if(!$datasource) {
$datasource = new Datasource();
$datasource->setUsername($username);
$token = substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyz'),1,16);
$datasource->setToken($token);
$datasource->setHorodatage(date('YmdHi'));
} else $token = $datasource->getToken();
$datasource->setProfil($profil);
$datasource->setGroupes($groupes);
$datasource->setIddatasource($id);
$datasource->setSsoitems($ssoitems);
// persistance du token pour 24h (1j)
$horodatage=$datasource->getHorodatage();
$maintenant=date('YmdHi');
$delta=($maintenant)-($horodatage);
if ($delta > 10000) {
$token = substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyz'),1,16);
$datasource->setToken($token);
$datasource->setHorodatage(date('YmdHi'));
}
$em->persist($datasource);
$em->flush();
$data = json_encode([
'token' => $token,
'adminPostit' => $adminpostit,
]);
// Profilage
$reponse = new Response();
if ($api!="null") {
$datajs="var data=$data;
$api('$id',data);";
$reponse->setContent($datajs);
$reponse->headers->set('Content-Type', 'text/javascript');
}
else {
$reponse->setContent($data);
$reponse->headers->set('Content-Type', 'text/html');
}
//$reponse->headers->set('Access-Control-Allow-Origin', '*');
return $reponse;
}
// /edispatcher/api/mxServices
//
// Ajout d'un item ninegate existant comme bookmark d'un utilisateur
// POST
// token = datasource user token, permettant d'établir le lien avec l'uid de l'utilisateur
// GET
// id = id interne de l'item sur lequel on souhaite ajouter/supprimer un bookmark
// libelle = libelle de la ressource
// icon = icone de la ressource
// favurl = url icone escapée
// nom = nom ou id
// categorie = categorie de la ressource
// callback = url du call back à retourner
// action = BOOKMARK_ADD | BOOKMARK_DEL
// callbackid = listeDesApplications/idinterne
public function mxServicesAction(Request $request) {
$token = $request->query->get('token',null);
$id = $request->query->get('id',null);
$libelle =$request->query->get('libelle',null);
$icon =$request->query->get('icon',null);
$favurl = rtrim($request->query->get('favurl',null),"?");
// urldecode(stripslashes(
$nom =$request->query->get('nom',null);
$categorie =$request->query->get('categorie',null);
$callback =$request->query->get('callback',null);
$action =$request->query->get('action',null);
$callbackid =$request->query->get('callbackid',null);
ob_start();
//var_dump($request->request);
var_dump($request->query);
$debug = ob_get_clean();
//$debug=json_encode($request->query);
if(is_null($token)||is_null($libelle)||is_null($icon)||is_null($favurl)||is_null($nom)||is_null($categorie)||is_null($callback)||is_null($action)||is_null($callbackid)){
$output["error"]="missing parameter : token - id - libelle - icon - favurl - nom - categorie - callback - action - callbackid";
return new Response(json_encode($output), 400);
}
$em = $this->getDoctrine()->getManager();
// Récupération Attribut
$datasource = $em->getRepository('CadolesEdispatcherBundle:Datasource')->findOneBy(["token"=>$token]);
if (!$datasource) {
$output["error"]="no datasource session found for token = $token";
return new Response(json_encode($output), 400);
}
$login = $datasource->getUsername();
// Récupérer l'utilisateur
$user=$em->getRepository('CadolesCoreBundle:User')->findOneBy(["username"=>$login]);
if(!$user) {
$output["error"]="user not exist";
return new Response(json_encode($output), 400);
}
// chercher l'item
$item=$em->getRepository('CadolesPortalBundle:Item')->findOneBy(["url"=>$favurl]);
if(!$item) {
$altitem=$em->getRepository('CadolesPortalBundle:Item')->findOneBy(["url"=>$favurl."?gestion_edispatcher=locale"]);
if(!$altitem) {
$output["error"]="item with url=$favurl(?gestion_edispatcher=locale) not exist";
return new Response(json_encode($output), 400);
} else {
$item = $altitem;
}
}
$bookmark = $em->getRepository('CadolesPortalBundle:Bookmark')->findOneBy(["user"=>$user,"item"=>$item]);
$title="";
$subtitle="";
$output="";
if(!$bookmark) {
if ($action=="BOOKMARK_ADD") {
$bookmark = new Bookmark();
$bookmark->setTitle($item->getTitle());
$title=$item->getTitle();
$bookmark->setSubtitle($item->getSubtitle());
$subtitle=$item->getSubtitle();
$bookmark->setUrl($item->getUrl());
$bookmark->setIcon($item->getIcon());
$bookmark->setColor($item->getColor());
$bookmark->setTarget($item->getTarget());
$bookmark->setItem($item);
$bookmark->setUser($user);
$em->persist($bookmark);
$em->flush();
$success=1;
$output="<html><body>
<form id='form2submit' name='form2submit' method='POST' action='$callback'>
<input name='action' id='action' type='hidden' value='$action' />
<input name='message' id='message' type='hidden' value='$title : $subtitle a &eacute;t&eacute; ajout&eacute; &agrave; vos favoris' />
<input name='success' id='success' type='hidden' value='1' />
<input name='id' id='id' type='hidden' value='$id' />
<input name='callbackid' id='callbackid' type='hidden' value='$callbackid' />
</form>
<script>
document.form2submit.submit();
</script>
</body></html>
";
} else $success=0; // item pas en favori, mais ce n'est pas une demande d'ajout
} else {
if ($action=="BOOKMARK_DEL") {
$em->remove($bookmark);
$em->flush();
$success=1;
$output="<html><body>
<form id='form2submit' name='form2submit' method='POST' action='$callback'>
<input name='action' id='action' type='hidden' value='$action' />
<input name='message' id='message' type='hidden' value='item supprimé de vos favoris' />
<input name='success' id='success' type='hidden' value='1' />
<input name='id' id='id' type='hidden' value='$id' />
<input name='callbackid' id='callbackid' type='hidden' value='$callbackid' />
</form>
<script>
document.form2submit.submit();
</script>
</body></html>
";
} else $success=0; // item deja en favori mais demande d'ajout
}
$reponse = new Response();
$reponse->setContent($output);
$reponse->headers->set('Content-Type', 'text/html');
$reponse->headers->set('Access-Control-Allow-Origin', '*');
return $reponse;
}
private function submitGroup($attributes) {
$em = $this->getDoctrine()->getManager();
if(!$this->getParameter('ssosynchrogroup'))
return null;
$user_attr_cas_group=$this->getParameter('user_attr_cas_group');
// Si l'utilisateur possège l'attribut groupe dans ses attributs
if(array_key_exists($user_attr_cas_group,$attributes)) {
if(!is_array($attributes[$user_attr_cas_group])) {
$attributes[$user_attr_cas_group]=[$attributes[$user_attr_cas_group]];
}
foreach($attributes[$user_attr_cas_group] as $ssogroup) {
// Recherche du groupe
$group=$em->getRepository("CadolesCoreBundle:Group")->findOneBy(["label"=>$ssogroup]);
if(!$group) {
$group=new Group();
$group->setLabel($ssogroup);
$group->setFgcancreatepage(false);
$group->setFgcancreateblog(false);
$group->setFgcancreatecalendar(false);
$group->setFgcancreateproject(false);
$group->setFgcanshare(false);
$group->setFgopen(false);
$group->setFgall(false);
}
$group->setAttributes('{"'.$user_attr_cas_group.'":"'.$ssogroup.'"}');
$group->setFgtemplate(false);
$em->persist($group);
$em->flush();
}
}
}
}

View File

@ -0,0 +1,245 @@
<?php
namespace Cadoles\EdispatcherBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;
/**
* @ORM\Entity
* @ORM\Table(name="edispatcher")
* @ORM\HasLifecycleCallbacks()
*
* @UniqueEntity(fields="username", message="Un utilisateur existe déjà avec ce login.")
*/
class Datasource
{
/**
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\Column(type="string", length=50, unique=true)
*/
private $username;
/**
* @ORM\Column(type="string", length=256, nullable=true)
*/
private $profil;
/**
* @ORM\Column(type="string", length=512, nullable=true)
*/
private $groupes;
/**
* @ORM\Column(type="text", nullable=true)
*/
private $ssoitems;
/**
* @ORM\Column(type="string", length=256, nullable=true)
*/
private $token;
/**
* @ORM\Column(type="string", length=256, nullable=true)
*/
private $iddatasource;
/**
* @ORM\Column(type="string", length=12, nullable=true)
*/
private $horodatage;
/**
* Get UserName
*
* @return string
*/
public function getUserName()
{
return $this->username;
}
/**
* Get Profil
*
* @return string
*/
public function getProfil()
{
return $this->profil;
}
/**
* Get Groupes
*
* @return string
*/
public function getGroupes()
{
return $this->groupes;
}
/**
* Get Ssoitems
*
* @return string
*/
public function getSsoitems()
{
return $this->ssoitems;
}
/**
* Get Token
*
* @return string
*/
public function getToken()
{
return $this->token;
}
/**
* Get Iddatasource
*
* @return string
*/
public function getIddatasource()
{
return $this->iddatasource;
}
/**
* Get Horodatage
*
* @return string
*/
public function getHorodatage()
{
return $this->horodatage;
}
/**
* Constructor
*/
public function __construct()
{
}
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set username
*
* @param string $username
*
* @return Datasource
*/
public function setUsername($username)
{
$this->username = $username;
return $this;
}
/**
* Set profil
*
* @param string $profil
*
* @return Datasource
*/
public function setProfil($profil)
{
$this->profil = $profil;
return $this;
}
/**
* Set groupes
*
* @param string $groupes
*
* @return Datasource
*/
public function setGroupes($groupes)
{
$this->groupes = $groupes;
return $this;
}
/**
* Set ssoitems
*
* @param string $ssoitems
*
* @return Datasource
*/
public function setSsoitems($ssoitems)
{
$this->ssoitems = $ssoitems;
return $this;
}
/**
* Set token
*
* @param string $token
*
* @return Datasource
*/
public function setToken($token)
{
$this->token = $token;
return $this;
}
/**
* Set iddatasource
*
* @param string $iddatasource
*
* @return Datasource
*/
public function setIddatasource($iddatasource)
{
$this->iddatasource = $iddatasource;
return $this;
}
/**
* Set horodatage
*
* @param string $horodatage
*
* @return Datasource
*/
public function setHorodatage($horodatage)
{
$this->horodatage = $horodatage;
return $this;
}
}

View File

@ -0,0 +1,147 @@
![Logo NineGateEdispatcherBundle](https://gitlab.forge.education.gouv.fr/uploads/-/system/project/avatar/2613/ninegate.jpg?width=64)
# eole Ninegate : eDispatcherBundle
eDispatcherBundle est un bundle symfony 3 recréant les apis *xDesktop* permettant de récupérer les *items de bureau* et les *messages* d'annonce déclarés par l'administrateur du portail `NineGate` et d'exploiter ces données lors de la construction du Bureau fédéré de l'ENT académique via `eDispatcher`.
# CHANGELOG
Nouveautés :
- prise en compte du *flag* des catégories d'annonces héritées : `mail, rappel, attention`
- token de mise en correspondance user ninegate / edispatcher persisté pour une durée de 24h plutôt que réinit à chaque appel de /edispatcher/api/session
- mise en conformité des annonces : mémo et messages applicatifs, mise en forme, catégories par défaut
- prise en compte d'un attribut SSO permettant le profilage d'items (attribut contenant une liste de mots clés, mot clé indiqué lors de la création d'un item)
- autocreate du user lors de l'appel en session SSO de la route /edispatcher/session
# PREAMBULE : calcul d'attribut SSO => obtenir les groupes
Sur le serveur eole-sso, éditer le fichier `/usr/share/sso/app_filters/ninegate.ini` pour préciser le calcul des attributs `user_groups`, `displayName` et `ENTPersonProfils` dans la rubrique `[attributes]` ainsi qu'un attribut permettant d'associer des items automatiquement, décrit plus bas :
```
(...)
[attributes]
(...)
user_groups=user_groups
displayName=displayName
ENTPersonProfils=ENTPersonProfils
ssoitems=applications
```
/!\ l'attribut attendu dans le code du bundle pour fournir la valeur du profil national ENT est *ENTPersonProfils* (ex. National_1)
/!\ prise en compte d'un attribut facultatif *Associer automatiquement les items en fonction d'un attribut SSO*
L'attribut *ssoitems* indiqué dans l'exemple est un libellé libre à indiquer dans le gen_config si l'on choisit d'activer le profilage d'items via un attribut SSO :
La valeur de cet attribut SSO est calculé par un script python (ici applications.py qui se charge de parser un fichier YAML applications.yml)
Cet attribut est facultatif, il est utilisé sur un ninegate dit "académique" permettant le profilage de ressources communes : les items sont créés dans ninegate sans réglages de permissions, mais en indiquant un "key name" dans le champ obligatoire *Visible si attribut SSO "NOMattributSSO" égale à*.
# INSTALLATION
cloner le projet dans un dossier, ex `/ninegate/src/Acreunion/EdispatcherBundle/`
# Routes
Editer le fichier `/ninegate/app/config/routing.yml` et rajouter le bloc :
```
edispatcher:
resource: "@AcreunionEdispatcherBundle/Resources/config/routing.yml"
prefix: /
```
# prise en compte du Bundle
Editer le fichier `/ninegate/app/AppKernel.php` et dans l'énumération de l`array $bundles` rajouter la ligne :
```
new Acreunion\EdispatcherBundle\AcreunionEdispatcherBundle(),
```
Editer le fichier `/ninegate/app/config/template.yml` et dans la section `doctrine: > orm: > entity_managers: > default: > mappings:`
```
AcreunionEdispatcherBundle: ~
```
=> un `bin/console doctrine:schema:update --force` sera donc nécessaire, déclenche la création d'une table "edispatcher" qui correspond à l'entité Datasource du bundle.
# Catégories d'annonces
Pour reproduire un modèle d'annonces isofonctionnelles avec ce qui était connu sur xdesktop, il convient de créer les catégories suivantes :
- "sans categorie" (sans accent), icone chercher "stop", couleur sans effet : n'affichera pas de catégorie sur le bureau edispatcher
- "Message", icone chercher "mail" & choisir l'enveloppe, couleur #00a996
- "RAPPEL", icone chercher "clock", couleur #004666
- "Attention/Important", icone chercher "caution", couleur #ff9375
ou bien importer le sql suivant :
```
INSERT INTO `alertcategory`
(`id`, `icon_id`, `label`, `color`)
VALUES
(-100, 205, 'sans categorie', NULL),
(1, 51, 'Message', '00a996'),
(2, 154, 'RAPPEL', '004666'),
(3, 217, 'Attention/Important', 'ff9375');
```
# Détail des routes implémentées par le bundle, en lien avec la cinématique des appels effectués par Edispatcher :WARNING: _Work in progress_
## API debug
`/edispatcher/test`
- affiche le contenu de la table edispatcher, entité "datasource" utilisée pour établir un lien entre un user ninegate, un token et un id fourni par *edispatcher*
- affiche les attributs SSO du user en cours de session
`/edispatcher/test?id={{ username }}`
- complète la réponse précédente en affichant les groupes de l'utilisateur désigné - non lié à la session
## API Hello
`/edispatcher/public/hello`
[ETAPE 1 CINEMATIQUE DES APPELS EDISPATCHER]
`/edispatcher/api/apps/all` pour compatibilité appels xdesktop
- route non authentifiée (anonyme) interrogée par *edispatcher* pour vérifier que le serveur réponds, en l'occurence la réponse contient le mot clé "ninegate" prise en compte par *edispatcher* pour adapter les apis qui seront interrogées pour construire le bureau à partir des ressources de ce serveur.
## API Session
[ETAPE 2 CINEMATIQUE DES APPELS EDISPATCHER]
`/ninegate/edispatcher/api/session` route interrogée par fédération, la réponse fournit une valeur de token (et l'enregistre dans l'entité datasource pour l'associer au user en cours de session *token persistant pour une durée de 24h*), et retourne un attribut `adminPostit` à `true|false`
[ETAPE 4 CINEMATIQUE DES APPELS EDISPATCHER]
`/ninegate/edispatcher/api/session?id=xxx-yyy-zzz&api=_CALLBACK.setToken` l'`id` définit par *edispatcher* en étape 3 est communiqué en `GET` sur cette route, la réponse est un snippet javascript executé dans *edispatcher* en retour, lequel permet d'associer le `token` éphémère à la source de donnée identifiée par *edispatcher* `id`
## API Me
[ETAPE 3 CINEMATIQUE DES APPELS EDISPATCHER]
`/edispatcher/api/me` appelée en passant un token en `POST` (celui obtenu à l'étape 2)
- la réponse JSON contient des informations sur l'utilisateur (`display_name`, `admin_postit`), *edispatcher* associe a ces informations un `id` de source de données
## API DesktopItems
[ETAPE 5 CINEMATIQUE DES APPELS EDISPATCHER]
`/edispatcher/api/items?js=1&id={{ id }}`
`/edispatcher/api/controllers/desktopitems.php?js=1&id={{ id }}` pour compatibilité appels xdesktop
- Route appelée avec le `token` en `POST`, la réponse au format javascript (js=1) contient un chaine xml qui décrit les items de bureau, ainsi qu'une liste json précisant les url des items favoris.
## API Messages
[ETAPE 6 CINEMATIQUE DES APPELS EDISPATCHER]
`/edispatcher/api/messages` Route appelée avec le `token` en `POST` ou l'`id` en `GET`, la réponse au format javascript est exécutée à réception par *edispatcher* et alimente le panneau des mémos avec les éventuels messages visibles par le user indiqué par la datasource désignée par le `token` ou l'`id`.
`/edispatcher/api/message/{idalert}/{action}` Route utilisée par *edispatcher* pour basculer l'état de visibilité des annonces "masquables".
## API Favoris
`/edispatcher/api/mxServices` Route utilisée par *edispatcher* pour basculer la mise en favori d'un item

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

@ -0,0 +1,28 @@
{% set logo = "" %}
{% set fgheader = "" %}
{% set header = "" %}
{% set heightheader = "" %}
{% set colormain = "004d9a" %}
{% set fontcolorhover = "" %}
{% set colorbody = "" %}
{% set fontfacetitle = "" %}
{% set fontfacebody = "" %}
{{
render(url("cadoles_core_theme_setconfig",
{
'logo':logo,
'fgheader':fgheader,
'header': header,
'heightheader': heightheader,
'colormain': colormain,
'fontcolorhover': fontcolorhover,
'colorbody': colorbody,
'fontfacetitle': fontfacetitle,
'fontfacebody': fontfacebody
}
))
}}

View File

@ -0,0 +1,3 @@
name: Métice
author: Pierre Cadéot
version: 1.0

Binary file not shown.

After

Width:  |  Height:  |  Size: 556 KiB

View File

@ -0,0 +1,8 @@
INSERT INTO `niveau01` (`id`, `label`, `siren`, `showsubappname`, `logo`, `header`, `colormain`, `fontcolorhover`, `colorbody`, `fontfacetitle`, `fontfacebody`, `ldapfilter`, `attributes`) VALUES
(-100, 'Eleve', 'Eleves', 1, NULL, 'uploads/niveau01/Eleve.jpg', '3b8211', 'ffffff', 'ecf8dd', NULL, NULL, '', '{\"ENTPersonProfils\":\"National_1\"}'),
(3, 'Responsable', 'Responsab', 1, NULL, 'uploads/niveau01/Responsable.jpg', '57248c', 'ffffff', 'faf3fc', NULL, NULL, '(&(uid=*)(objectclass=inetOrgPerson)(!(description=Computer)))', '{\"ENTPersonProfils\":\"National_2\"}'),
(4, 'Enseignant', 'Enseignan', 1, NULL, 'uploads/niveau01/Enseignant.jpg', '0e6586', 'ffffff', 'b1e3f6', NULL, NULL, '(&(uid=*)(objectclass=inetOrgPerson)(!(description=Computer)))', '{\"ENTPersonProfils\":\"National_3\"}'),
(5, 'Perdir', 'Perdir', 1, NULL, 'uploads/niveau01/Perdir.jpg', '0e6586', 'ffffff', 'edecf3', NULL, NULL, '(&(uid=*)(objectclass=inetOrgPerson)(!(description=Computer)))\r\n', '{\"ENTPersonProfils\":\"National_4\"}'),
(6, 'Viescolaire', 'VieScolai', 1, NULL, 'uploads/niveau01/Viescolaire.jpg', 'a77e25', 'ffffff', 'f4e5d2', NULL, NULL, '(&(uid=*)(objectclass=inetOrgPerson)(!(description=Computer)))\r\n', '{\"ENTPersonProfils\":\"National_5\"}'),
(7, 'Rectorat', 'rectorat', 1, NULL, 'uploads/niveau01/Rectorat.jpg', '004d9a', 'ffffff', 'e5eeff', NULL, NULL, '(&(uid=*)(objectclass=inetOrgPerson)(!(description=Computer)))\r\n', '{\"ENTPersonProfils\":\"National_7\"}'),
(8, 'Administratif', 'Administr', 1, NULL, 'uploads/niveau01/Administratif.jpg', 'a77e25', NULL, 'f4e5d2', NULL, NULL, NULL, '{\"ENTPersonProfils\":\"National_6\"}');

View File

@ -0,0 +1,53 @@
div.header {
background-image: linear-gradient(90deg,rgba(255,255,255,1) 0%, rgba(255,255,255,1) 15%, rgba(255,255,255,0.1) 50%, rgba(255,255,255,0) 100%),var(--header, url()) !important;
}
.avatar { background-color: var(--main, #004d9a) !important; }
#logo {
height: 79px !important;
float: left !important;
top: -8px !important;
left: -10px !important;
position: relative !important;
}
div.header > a > span {
color: black !important;
}
nav#appmenu > ul > li.active > a.active, .nav>li>a:hover {
background-color: white !important;
color: var(--main, #004d9a);
font-weight: bold;
}
.navbar-default, .grid-item, .widgetheader {
background-color: var(--main, #004d9a) !important;
}
.grid-item-content {
background-color: var(--dark, #004d9a) !important;
}
.widget-mini > .widgetheader {
background-color: var(--light, white) !important;
}
.navbar-default { border-color: var(--light, white) !important }
.nav > li > a:hover {
background:none !important;
}
.nav.navbar-top-links.navbar-left>li>a:hover {
text-shadow: var(--light, white) 0px 0px 20px;
color: var(--main, #004d9a) !important;
background-color: var(--light, white) !important;
}
.nav.navbar-top-links.navbar-right>li>a:hover {
text-shadow: white 0px 0px 20px;
color: var(--main, #004d9a) !important;
background: none !important;
}
nav#appmenu { zoom: 0.68; font-weight: bold; }

View File

@ -0,0 +1,42 @@
#== API EDISPATCHER ============================================================================================================
cadoles_edispatcher_test:
path: /edispatcher/test
defaults: { _controller: CadolesEdispatcherBundle:Api:test }
cadoles_edispatcher_api_js_item:
path: /edispatcher/api/items
defaults: { _controller: CadolesEdispatcherBundle:Api:desktopitem }
cadoles_edispatcher_api_controllers_desktopitems:
path: /edispatcher/api/controllers/desktopitems.php
defaults: { _controller: CadolesEdispatcherBundle:Api:desktopitem }
cadoles_edispatcher_api_js_alert:
path: /edispatcher/api/messages
defaults: { _controller: CadolesEdispatcherBundle:Api:message }
cadoles_edispatcher_api_js_alert_action:
path: /edispatcher/api/message/{idalert}/{action}
defaults: { _controller: CadolesEdispatcherBundle:Api:message }
cadoles_edispatcher_api_public_hello:
path: /edispatcher/api/public/hello
defaults: { _controller: CadolesEdispatcherBundle:Api:hello }
cadoles_edispatcher_api_apps_all:
path: /edispatcher/api/apps/all
defaults: { _controller: CadolesEdispatcherBundle:Api:hello }
cadoles_edispatcher_api_me:
path: /edispatcher/api/me
defaults: { _controller: CadolesEdispatcherBundle:Api:me }
cadoles_edispatcher_api_session:
path: /edispatcher/api/session
defaults: { _controller: CadolesEdispatcherBundle:Api:session, access: user }
cadoles_edispatcher_fav_manage:
path: /edispatcher/api/mxServices
defaults: { _controller: CadolesEdispatcherBundle:Api:mxServices }

View File

@ -0,0 +1,33 @@
{% extends '@CadolesCore/base.html.twig' %}
{% block pagewrapper %}
<h1>TEST EDISPATCHER</h1>
<h2>Datasources</h2>
{% for source in datasource %}
<h3><strong>id</strong> = {{ source.id }}</h3>
<strong>username</strong> = {{ source.username }}<br>
<strong>profil</strong> = {{ source.profil }}<br>
<strong>groupes</strong> = {{ source.groupes }}<br>
<strong>ssoitems</strong> = {{ source.ssoitems }}<br>
<strong>token</strong> = {{ source.token }}<br>
<strong>horodatage</strong> = {{ source.horodatage }}<br>
<strong>iddatasource</strong> = {{ source.iddatasource }}<br>
{% endfor %}
<h2>Appartient aux Groupes</h2>
<strong>Groupes :</strong> = {{ groups }}<br>
<h2>Atttribut SSO</h2>
{% for key, attribute in attributes %}
{% if attribute is iterable %}
{% for value in attribute %}
<strong>{{ key }}</strong> = {{ value }}<br>
{% endfor %}
{% else %}
<strong>{{ key }}</strong> = {{ attribute }}<br>
{% endif %}
{% endfor %}
<br><br><br><br><br><br><br>
{% endblock %}

View File

@ -0,0 +1,52 @@
/* DATA_FOR_MXDESKTOP: Ne pas supprimer cette ligne */
var username='{{ username }}';
var source=MX.getSource('{{ id }}');
if (!source) {
console.log("Source {{ id }} introuvable");
} else {
source.setData('{"xml":"&lt;main&gt;&lt;buttondatas&gt;{% set mycategs = [] %}
{% for itemcategory in itemcategorys %}
{% set haveitem=false %}
{% for item in items if item.itemcategory==itemcategory %}
{% if loop.index ==1 %}
{% set mycategs = mycategs|merge({ (loop.index) : itemcategory}) %}
{% endif %}
{% endfor %}
{% endfor %}
{% set firstcat=true %}
{% for itemcategory in mycategs %}
{% set haveitem=false %}
{% for item in items if item.itemcategory==itemcategory %}
{% if loop.index ==1 %}
{% set haveitem=true %}
{% if (bookmarks is not empty) %}
{% set toview=true %}
{% if menu and not menuall%}
{% set toview=false %}
{% endif %}
{% else %}
{% if menu and not menuall%}
{% if firstcat %}
{% set toview=true %}
{% else %}
{% set toview=false %}
{% endif %}
{% else %}
{% set toview=true %}
{% endif %}
{% endif %}
{% set firstcat=false %}
{% endif %}&lt;buttondata&gt&lt;nom&gt;{{ item.title|lower }}&lt;\/nom&gt;&lt;icon&gt;{% if item.icon %}{{ app.request.getSchemeAndHttpHost() }}/{{ alias }}/{{ item.icon.label }}{% else %}{{ app.request.getSchemeAndHttpHost() }}/{{ alias }}/uploads/icon/icon_pin.png{% endif %}&lt;\/icon&gt;&lt;libelle&gt;{{ item.subtitle | e('js') }}&lt;\/libelle&gt;&lt;url&gt;{{ item.url }}&lt;\/url&gt;&lt;categoriename&gt;{{ item.itemcategory.label|lower }}&lt;\/categoriename&gt;&lt;categorieid&gt;{{ item.itemcategory.id }}&lt;\/categorieid&gt;&lt;infos_url&gt;&lt;\/infos_url&gt;&lt;infos_type&gt;&lt;\/infos_type&gt;&lt;infos_message&gt;&lt;\/infos_message&gt;&lt;\/buttondata&gt;{% endfor %}{% endfor %}{% if (bookmarks is not empty) %}
{% for bookmark in bookmarks %}
{% if bookmark.item %}&lt;buttondata&gt&lt;nom&gt;{{ bookmark.item.title|lower }}&lt;\/nom&gt;&lt;icon&gt;{% if bookmark.item.icon %}{{ app.request.getSchemeAndHttpHost() }}/{{ alias }}/{{ bookmark.item.icon.label }}{% else %}{{ app.request.getSchemeAndHttpHost() }}/{{ alias }}/uploads/icon/icon_pin.png{% endif %}&lt;libelle&gt;{{ bookmark.item.subtitle|lower|e('js') }}&lt;\/libelle&gt;;&lt;url&gt;{{ bookmark.item.url }}&lt;\/url&gt;&lt;categoriename&gt;{{ bookmark.item.itemcategory.label|lower }}&lt;\/categoriename&gt;&lt;categorieid&gt;{{ bookmark.item.itemcategory.id }}&lt;\/categorieid&gt;&lt;infos_url&gt;&lt;\/infos_url&gt;&lt;infos_type&gt;&lt;\/infos_type&gt;&lt;infos_message&gt;&lt;\/infos_message&gt;&lt;\/buttondata&gt;{% endif %}
{% endfor %}
{% endif %}
&lt;\/buttondatas&gt;&lt;description&gt;&lt;services_url&gt;undefined&lt;\/services_url&gt;&lt;\/description&gt;&lt;categories&gt;{% for itemcategory in mycategs %}&lt;categorie&gt;&lt;nom&gt;{{ itemcategory.label }}&lt;\/nom&gt;&lt;couleur&gt;{% if itemcategory.color is null %}#A0A0A0{% else %}{{ itemcategory.color }}{% endif %}&lt;\/couleur&gt;&lt;indice&gt;{{ itemcategory.rowOrder }}&lt;\/indice&gt;&lt;icone&gt;{% if itemcategory.icon is null %}fa-square{% else %}{{ itemcategory.icon }}{% endif %}&lt;\/icone&gt;&lt;\/categorie&gt;{% endfor %}&lt;\/categories&gt;&lt;\/main&gt;","favoris":"{% if (bookmarks is not empty) %}[
{% for bookmark in bookmarks %}
{% if bookmark.item %}
{&quot;url&quot;:&quot;{{ bookmark.item.url }}&quot;,&quot;libelle&quot;:&quot;{{ bookmark.item.title|lower }}&quot;,&quot;icon&quot;:&quot;{% if bookmark.item.icon %}{{ app.request.getSchemeAndHttpHost() }}/{{ alias }}/{{ bookmark.item.icon.label }}{% else %}{{ app.request.getSchemeAndHttpHost() }}/{{ alias }}/uploads/icon/icon_pin.png{% endif %}&quot;},
{% endif %}
{% endfor %}
{% endif %}
"userinfos":{&quot;profil&quot;:&quot;{{ profil }}&quot;,&quot;rne&quot;:&quot;{{ rne }}&quot;,&quot;groupes&quot;:&quot;{{ groupe }}&quot;}","id":{{ id }}}');
}

View File

@ -0,0 +1,45 @@
{% import "@CadolesPortal/Pagewidget/constants.twig" as constants %}
{% set colormain = constants.mycolormain() %}
{#
{% set stylewidget = constants.mystylewidget(entity) %}
{% set stylewidgetmenu = constants.mystylewidgetmenu(entity) %}
{% set stylewidgetheader = constants.mystylewidgetheader(entity) %}
{% set stylewidgetbody = constants.mystylewidgetbody(entity) %}
<p>TEST ALERT</p>
#}
{
"messages": [
{% for alert in alerts %}
{
"id": "{{ alert.id }}",
"message": "{{ alert.content | e("js") }}",
"filtre": "aucun",
"valeur": "[]",
"flag": "",
"closeable": "{{ alert.fghideable }}",
"requestable": "false",
"display_mode": "postit",
"responses": "[]",
"url": "",
"start": "2020-09-02 00:00:00",
"stop": "2020-10-01 23:59:59",
"updated": "2020-09-03 10:45:21",
"user_responses": []
},
{% endfor %}
],
"admin": true
},
{#
id = {{ alert.id }}
background = {{ alert.alertcategory.color ? '#'~alert.alertcategory.color : '#'~colormain }};
{% if alert.alertcategory.icon %}
icon ="/{{ alias }}/{{ alert.alertcategory.icon.label }}"
{% else %}
icon = "/{{ alias }}/uploads/icon/icon_megaphone.png"
{% endif %}
title = {{ alert.title }}
hideable = {{ alert.fghideable }}
contenu = {{ alert.content }}
#}

View File

@ -0,0 +1,12 @@
{
"name" : "acreunion/edispatcherbundle",
"description" : "",
"type" : "symfony-bundle",
"minimum-stability" : "dev",
"autoload" : {
"psr-0" : {
"Acreunion\\EdispatcherBundle" : ""
}
},
"target-dir" : "Acreunion/EdispatcherBundle"
}

View File

@ -542,6 +542,7 @@ doctrine:
CadolesCronBundle: ~
CadolesPortalBundle: ~
CadolesWebsocketBundle: ~
CadolesEdispatcherBundle: ~