Bouton d'action sur une ressource côté client

Bonjour,

Je souhaite, sur un agenda fullcalendar en liste, ajouter un bouton d’action (CRB_AGM_MAIL_RELANCE) sur les objets (CrbAgmSollicitation) de cette liste.

La liste des objets étant développée dans un script de type “Ressource” (côté front), et la requête d’appel à mon action (https://xxxxxx.bzh/agmanif/ui/json/obj?action=CRB_AGM_MAIL_RELANCE&object=CrbAgmSollicitation&inst=the_ajax_CrbAgmSollicitation&_md=true) étant en POST,
comment procéder ?

  • Dois-je pour chacune de mes lignes d’agenda ajouter une balise href pointant vers un objet externe en précisant l’id de mon objet ? La gestion des droits se ferait ainsi au niveau de l’objet externe.

  • Dois-je faire une balise <a href=javascript(...)> avec une fonction côté back qui fait une requête directe vers l’url en POST ?

  • Dois-je utiliser les classes fournies côté front (https://docs.simplicite.io/4.0/jsdoc/Simplicite.UI.Engine.html) ? Dans ce cas, auriez-vous un exemple de code ?

Merci de votre réponse,

Guillaume

Dans la mesure où votre formulaire est du code front spécifique je ne vois pas bien quoi vous répondre sans le code complet, il sera plus simple d’ajouter des boutons après chargement de l’agenda dans chaque cellule identifiées par un classe spécifique.

Quelque chose du genre :

$(".my-agd-cell").each(function() {
  var x = $(this);
  $('<button class="btn"/>')
     .css({ float:"right" })
     .data("x",x)
     .click(click)
     .appendTo(x);
});
function click(e) {
  e.stopPropagation();
  var x = $(this).data("x");
  var id = x.attr("data-rowid"); // à vous de retrouver le row_id dans la cellule
  var obj = app.getBusinessObject("CrbAgmSollicitation");
  obj.get(function(item) {
    obj.action(function() { /* done */ }, "CRB_AGM_MAIL_RELANCE");
  }, id);
}

A priori avec un agenda fullCalendar cela donne :

	$j("#sollicitationcalendar").fullCalendar({

		events: function(start, end, tz, callback) {
...
			sollicitation.search(function() {
...
     
				for (var i = 0; i < sollicitation.list.length; i++) {
					var item = sollicitation.list[i];
					var s = moment(item.CrbAgmSolliManifId__CrbAgmManifDtDebut);
					var e = moment(item.CrbAgmSolliManifId__CrbAgmManifDtFin);
					evts.push({
						id: item.row_id,
						data: item,
						title: (item.CrbAgmSolliManifId__CrbAgmManifReponsePresident ? "(" + item.CrbAgmSolliManifId__CrbAgmManifReponsePresident + ")" : '') + ' '
						+ item.CrbAgmSolliManifId__CrbAgmManifObjet + ' | '
						+ item.CrbAgmSolliCrbUserId__usr_last_name + ' '
						+ item.CrbAgmSolliCrbUserId__usr_first_name + ' - '
						+ (item.CrbAgmSolliReponse=='SRE' ? 'Non répondu' : item.CrbAgmSolliReponse=='ABS' ? 'Absent' : 'Présent') + ' '
						+ "<button type=\"button\" class=\"btn btn-action action-not-xs\" onClick=\""
						+ " var obj = new Simplicite.Ajax().getBusinessObject(\'CrbAgmSollicitation\'); "
						+ "	obj.get(function(item) { "
						+ "		obj.action(function() { /* done */ }, \'CRB_AGM_MAIL_RELANCE\'); "
						+ "	}, " + item.row_id + "); "						
						+ " \">Relancer</button>",
						
						color: item.CrbAgmSolliReponse=='SRE' ? 'orange' : item.CrbAgmSolliReponse=='ABS' ? 'lightgrey' : 'green', 
						start: s,
...
					});
					
					if (debug) console.debug(" resa : " + item.gdrResaDemandeur + " ressource : " + item.gdrResaRessource_fk +"-"+item.gdrResaRessource_fk__gdrRessourceLibelle);
				} 
				if (debug) console.debug("fin boucle :"+new Date());
				if (debug) console.debug(evts.length + " resa displayed between " + dmin + " and " + dmax);
				callback(evts);
			}, {}, {});
		},
		eventRender: function( event, element, view ) {
...
		},
		eventClick: function(s) {
...
		},
	});

Je ne sais pas dire si votre code fonctionne,
mais sur la UI on instancie un objet comme ça :

var app = $ui.getAjax(); // ou Simplicite.Application
var obj = app.getBusinessObject('CrbAgmSollicitation');

Merci,

Je souhaite par ailleurs masquer ce bouton suivant la responsabilité de l’utilisateur. J’ai tenté d’implémenter un masquage du champ selon votre documentation, dans la partie “Main page startup”.

image

Or, dans mon cas, ui est undefined.

Avez- vous une solution pour masquer, côté front, un élément selon la reponsabilité de l’utilisateur ?

Merci d’avance,

Cordialement,

Guillaume

Bonjour

Je suis désolé j’ai du mal à cerner ce dont vous parlez.
De quel “ce bouton” parlez vous ?
Quel rapport avec un “masquage de champ” ?
Etc.
Merci de mettre des copies d’écran si possible.

Quoi qu’il en soit, si quelque chose dépend des droits de l’utilisateur connecté une approche purement front n’est sans doute pas la bonne approche…

Bonjour,
Je ne comprends pas le besoin, le code correspond à quelle entité ?

  • new Simplicite.Ajax() : surtout pas, il faut utiliser la session UI s’il y en a déjà une $ui.getAjax() ou Simplicite.Application qui pointent sur la session ouverte / faire un new c’est uniquement si vous êtes sur un accès API externe par exemple en dehors de la UI native
  • ui.loaded et ui.ready : ces événements sont appelés une seule fois à l’ouverture du site, donc pas d’un objet externe ou autre = donc ne sert que dans dans le SCRIPT de la disposition

Pour vous aider, il faut nous dire ce que vous voulez faire, sur quel objet, en front ou en back…

  • Bouton d’action = action native, action spécifique ?
  • ressource côté client = de quoi parle-t-on ?
    • pour customiser un objet il faut lui déclarer une ressource SCRIPT contenant ses hooks front
    • pour faire un objet externe il faut nous fournir votre code display back et ses ressources front

Bonjour,

Je parlais d’un masquage du bouton et pas d’un champ, c’est une erreur, désolé.

Le bouton est le bouton “Relancer” figurant sur chacune des lignes représentant une manifestation.

Il appelle l’action CRB_AGM_MAIL_RELANCE_MANIF dans le fichier de type Javascript de l’objet externe CrbAgmManifestationAgenda :

CrbAgmManifestationAgenda = (function() {

var $s=true;
var debug = false;

function display(url) {
	$s = new Simplicite.Ajax();
	var manifestation = $s.getBusinessObject("CrbAgmManifestation", "agenda_CrbAgmManifestation");
	
	$j("#manifestationcalendar").fullCalendar({
		defaultView: (localStorage.getItem("fcDefaultView") !== '' ? localStorage.getItem("fcDefaultView") : "list"),
		defaultDate : (localStorage.getItem("fcDefaultDate") !== '' ? localStorage.getItem("fcDefaultDate") : moment()),
		header: {
			left: "prev,next",
			center: "title",
			right: "list, month,listMonth"
		},
		visibleRange: {
			start: (localStorage.getItem("fcDefaultDate") !== '' ? localStorage.getItem("fcDefaultDate") : moment()),
			end: moment().add('days', 7)
		},
    	views: {
        	day: {
            	titleFormat: "dddd DD MMMM YYYY",
            	columnFormat: "DD/mm",           
        	},
        	listWeek: { 
        		buttonText: 'Manifestations de la semaine',
        	},
        	listMonth: { 
        		buttonText: 'Manifestations du mois',
        	}
		}, 
		timezone: "local",
		editable: true,
		firstDay: 1,
		maxTime: "21:30:00",
		businessHours: {
			dow: [ 1, 2, 3, 4, 5 ],
			start: '09:00',
			end: '17:30'
		},
		events: function(start, end, tz, callback) {
			var f = "YYYY-MM-DD HH:mm:ss Z";
			var debut=moment(start);
			var fin=moment(end);
			var dmin=debut.format(f);
			var dmax = fin.format(f);
			console.debug("Calendar view range = " + debut + " to " + fin);
			manifestation.search(function() {
				console.debug(manifestation.list.length + " sollicitations trouvées entre " + dmin + " et " + dmax);
				var evts = [];
				for (var i = 0; i < manifestation.list.length; i++) {
					var item = manifestation.list[i];
					var s = moment(item.CrbAgmManifDtDebut);
					var e = moment(item.CrbAgmManifDtFin);
					evts.push({
						id: item.row_id,
						data: item,
						title: (item.CrbAgmManifReponsePresident ? "(" + item.CrbAgmManifReponsePresident + ")" : '') + ' '
						+ item.CrbAgmManifObjet + ' '
						+ "<button type=\"button\" class=\"btn btn-action action-not-xs\" onClick=\""
						+ " var obj = $ui.getAjax().getBusinessObject(\'CrbAgmManifestation\'); "
						+ "	obj.get(function(item) { "
						+ "		obj.action(function() { /* done */ }, \'CRB_AGM_MAIL_RELANCE_MANIF\'); "
						+ "	}, " + item.row_id + "); "					
						+ " \">Relancer</button>"
						+ item.CrbAgmManifListeRep,
						start: s,
						end: e
					});
				} 
				if (debug) console.debug("fin boucle :"+new Date());
				if (debug) console.debug(evts.length + " resa displayed between " + dmin + " and " + dmax);
				callback(evts);
			}, {}, {});
		},

		eventClick: function(s) {
			if (debug) console.log("Manifestation " + s.id + " clicked");
			if (typeof parent.$ui !== "undefined") {
				parent.$ui.displayForm(null, "CrbAgmManifestation", s.id, { nav: "add" });
			}
			else
				document.location.replace(url.replace(/ROWID/, e.id));
		},
		viewRender: function (view) {
			localStorage.setItem("fcDefaultView", view.name);
			var viewStart = moment(view.start);
			var viewEnd = moment(view.end);
			$j('#datedebutselector').val(moment(view.start).format("YYYY-MM-DD"));
			$j('#datefin').val(moment(view.end).format("YYYY-MM-DD"));
			localStorage.setItem("fcDefaultDate", viewStart.format("YYYY-MM-DD"));
		},
		eventRender: function( event, element, view ) {
			if (view.name.startsWith('list')) {
		    	var title = element.find('.fc-list-item-title');
		    	var title_html = title.text();
		    	var array_html = [];
		    	var array_sollicitation = title_html.split('●');
		    	array_sollicitation.forEach(function(ligne){
		    		ligne = ligne.replace(/.* - (INF)/, function(ligneSoll){return "<span style='color:grey'>"+ligneSoll.replace(' - INF','')+"</span>"});
		    	    ligne = ligne.replace(/.* - ((SRE)|(ABS)|(PRE))/, function(ligneSoll){return "<span style='color:#0080ff'>"+ligneSoll+"</span>"});
		        	ligne = ligne.replace(/ \- ABS/, "<b><span style='color:red'> Non</span></b>");
		            ligne = ligne.replace(/ \- SRE/, "<b><span style='color:red'> ?</span></b>");
		            ligne = ligne.replace(/ \- PRE/, "<b><span style='color:red'> Oui</span></b>");
		            array_html.push(ligne);
		    	});
		    	title.html(array_html.join('<br/>'));
			}
			
			$j('button[aria-label=next]').click(function() {
				$('#manifestationcalendar').fullCalendar('option', 'visibleRange', {
    				start: moment(view.start).add(7, 'days'),
    				end: moment(view.end).add(7, 'days')
    			});
			});
			$j('button[aria-label=prev]').click(function() {
				$('#manifestationcalendar').fullCalendar('option', 'visibleRange', {
    				start: moment(view.start).subtract(7, 'days'),
    				end: moment(view.end).subtract(7, 'days')
    			});
			});
		},
	});

	$j('#datedebutselector').change(function(){
		$('#manifestationcalendar').fullCalendar('option', 'visibleRange', {
    		start: moment($j('#datedebutselector').val()),
    		end: moment($j('#datedebutselector').val()).add(7, 'days')
    	});
    });
}

return { display: display };

})(jQuery);

Je veux que se bouton soit masqué lorsque l’utilisateur ne dispose pas de la responsabilité ‘APPLI_SIMPLICITE_AGMANIF_GESTIONNAIRE’

J’ai vu qu’il existe la fonction $ui.grant.hasResponsability()

image

Mais je ne sais pas comment l’utiliser dans mon script pour masquer le bouton.

Pour info, les utilisateurs pour qui le bouton est masqué n’ont pas le droit d’exécuter l’action “côté serveur”.

Merci de vos retours,

Cordialement,

Guillaume

Je ne comprends pas ce que vous décrivez…

Si une action n’est pas accessible aux membres d’un groupe, il suffit de ne pas l’habiliter à ce groupe.

Si la règle de gestion est plus complexe/subtile elle doit être implémentée coté serveur via le hook isActionEnable

Mais de toute façon il ne faut jamais implémenter de règles de gestion métier (surtout des règles liées aux habilitations) coté client (UI).

on est donc dans du code spécifique.
dans votre code le bouton est positionné en dur ici :

evts.push({
						id: item.row_id,
						data: item,
						title: (item.CrbAgmManifReponsePresident ? "(" + item.CrbAgmManifReponsePresident + ")" : '') + ' '
						+ item.CrbAgmManifObjet + ' '
						+ "<button type=\"button\" class=\"btn btn-action action-not-xs\" onClick=\""
						+ " var obj = $ui.getAjax().getBusinessObject(\'CrbAgmManifestation\'); "
						+ "	obj.get(function(item) { "
						+ "		obj.action(function() { /* done */ }, \'CRB_AGM_MAIL_RELANCE_MANIF\'); "
						+ "	}, " + item.row_id + "); "					
						+ " \">Relancer</button>"
						+ item.CrbAgmManifListeRep,
						start: s,
						end: e
					});

il suffit donc d’y mette un if grant.hasResponsibility quelque part :

var btn = "<button...>Relancer</button>";
evts.push({
... 
+ ($ui.grant.hasResponsibility("xxx")  ? btn : "") 
+ ...

Bonjour,

J’ai dû passer par du code spécifique car le besoin d’un bouton d’action directement intégré à l’agenda était important pour nos utilisateurs.
Cela fonctionne, en revanche j’ai dû utiliser parent.$ui au lieu de $ui.

Merci,

Guillaume

Le fait que vous soyez obliger d’utiliser parent.$ui signifie que votre objet externe s’affiche en mode compatibilité 3.x donc dans une iframe. C’est un pattern en fin de vie qu’il convient de refactorer.

Dans la demo à jour vous avez un exemple d’objet externe (utilisant FullCalendar) dans la logique de la UI 4.0+ one page.

D’autres exemples simples d’objets externes à la mode 4.0+ dans cette doc: https://docs.simplicite.io/documentation/01-core/externalobject-code-examples.md#responsive

Exact Simplicité est une page unique, donc si vous faites un composant qui demande d’afficher une page/ressource via son URL complète (commençant par http) elle s’affichera dans une iframe, obligeant à recharger toutes les ressources web, pas responsive…
Pouvez nous préciser par quel mécanisme/verbe/api vous affichez votre calendrier ?

Si votre composant est affichable au sein de la UI principale, il faut alors que votre objet externe retourne uniquement un simple container HTML <div id="my-container"></div> avec un SCRIPT+STYLES pour le remplir.