Ajouter une barre d’outils à un module existant de FengOffice

Filtre ajouté au module de temps de FengOfficeCet article explique comment créer une barre d’outils et l’intégrer à un module existant de FengOffice.

La petite histoire.
Nous utilisons le module de temps de FengOffice pour saisir nos temps de travail sur différents projets. Cette façons de procéder permet à aux chefs de projets de suivre les temps passé sur chaque projet où action de projet (comme la hotline) et d’en déduire différentes données (coûts d’une action, coût supplémentaire d’un projet).
L’outil étant utilisé par un grand nombre de personnes, la liste des temps est très vite devenus difficile à lire, et chaque personnes a du mal à retrouver ses petits. C’est dans ce cadre que nous avons développé une barre d’outils qui s’intègre au module temps.

Vous trouverez ici les fichiers modifiés dans leur arborescence d’origine.

Cette barre d’outils va contenir une comboBox listant l’intégralité des utilisateurs. Et dans un soucie de respect de la trame de FengOffice, nous ajouterons deux champs, « moi » et « Touts le monde ».

De plus nous modifions le comportement par défaut du module temps. Lorsque le module temps sera demandé, seul les temps enregistrés de l’utilisateur seront affiché.

Je me suis basé sur le code de la barre d’outils du module tâches. Le code que je vous proposes est j’espère suffisamment commenté pour que sa compréhensions soit la plus aisé possible.

Bien, commençons par créer le fichier JavaScript qui sera un objet de type « barre d’outils » et intégrant des composants de formulaires.
Dans le dossier « fengOffice/public/assets/javascript/og/time/ » créer un fichier « TimeBottomToolbar.js »

Ce code va donc permettre la construction de la barre d’outils et la comboBox. Avant de construire l’objet comboBox, on va récupérer la liste des utilisateurs et extraire l’utilisateur en cours (celui qui est connecté) et l’utilisateur à filtrer (s’il en existe un). Suite à cela, on crée un objet comboBox grâce au framwork JavaScript ExtJs. On définis un listner sur l’action de sélection, qui aura pour travail de recharger la page en passant dans l’url l’identifiant de l’utilisateur sélectionné. Et en dernier lieu on définie à l’objet comboBox sur quel utilisateur faire le focus.

og.TimeBottomToolbar = function(config) {
    // User sort function
    function ogTimeOrderUsers(usersList)
    {
        for (var i = 0; i < usersList.length - 1; i++)
        {
            for (var j = i+1; j < usersList.length; j++)
            {
                if (usersList[i][1].toUpperCase() > usersList[j][1].toUpperCase())
                {
                    var aux = usersList[i];
                    usersList[i] = usersList[j];
                    usersList[j] = aux;
                }
            }
        }
        return usersList;
    }

    Ext.applyIf(config,
        {
            id:"timePanelBottomToolbarObject", // value for the id attribute of the toolbar
            renderTo: "timePanelBottomToolbar", // target for the toolbar
            height: 28,
            style:"border:0px none"
        });

    // referencing object in the "og" object
    og.TimeBottomToolbar.superclass.constructor.call(this, config);

    var currentUser = null; // Cid and Id of the current user
    var selectedUserFilter = null; // Cid and Id of the user selected in the filter liste
    var usersArrayForFilter = new Array();
    // Retrieve the list of users in a hidden form field
    var usersArray = Ext.util.JSON.decode(document.getElementById(config.usersHfId).value);
    var x = 0;
    for (i in usersArray) {

        if (usersArray[i].isCurrent) { // if user's object is the current
            currentUser = usersArray[i].cid + ':' + usersArray[i].id;
        }

        if (config.filterUserIdAndCid == (usersArray[i].cid + ':' + usersArray[i].id)) // if user's object is the user selected in the liste
        {
            selectedUserFilter = usersArray[i].cid + ':' + usersArray[i].id;
        }

        if(usersArray[i].cid && usersArray[i].id && usersArray[i].name)
        {
            usersArrayForFilter[x] = [usersArray[i].cid + ':' + usersArray[i].id, usersArray[i].name];
            x++;
        }
    }
    if(config.filterUserIdAndCid == '0:0')
    {
        selectedUserFilter = '0:0';
    }

    // Object array that contains the users liste and two more row ('me' and 'everyone')
    var ucsData = [[currentUser, lang('me')],['0:0',lang('everyone')],['0:0','--']];

    // Concat the users liste to the two first row
    ucsData = ucsData.concat(ogTimeOrderUsers(usersArrayForFilter));

    // Creation of the comboBox that contains the list of the user
    this.filterNamesCombo = new Ext.form.ComboBox({
        id: 'ogTimeFilterNameCombo',
        store: new Ext.data.SimpleStore({
            fields: ['value', 'text'],
            data : ucsData
        }),
        displayField:'text',
        typeAhead: true,
        mode: 'local',
        triggerAction: 'all',
        selectOnFocus:true,
        width:200,
        valueField: 'value',
        listeners: {
            'select' : function(combo, record) { // this function is fire when a user is selected in the liste
                var toolbar = Ext.getCmp('timePanelBottomToolbarObject');
                toolbar.load();
            }
        }
    });

    // puts the focus on the user selected
    this.filterNamesCombo.setValue(((selectedUserFilter!=null)?selectedUserFilter:currentUser));

    // add some text before the comboBox into the 'timePanelBottomToolbarObject'
    this.add(lang('filter') + ':');
    // add the comboBox into the 'timePanelBottomToolbarObject'
    this.add(this.filterNamesCombo);

};

/**
 * Etend les fonctions de TimeBottomToolbar qui seront appelé par les objets "visuel" de formulaire
 * Extend functionalities of  TimeBottomToolbar that will be call by the comboBox
 */
Ext.extend(og.TimeBottomToolbar, Ext.Toolbar, {
    load: function(params) {
        // Reload the page with new parameters
        og.openLink(og.getUrl('time','index', {filter_user_id:this.filterNamesCombo.getValue()}));
    },
    getDisplayCriteria : function(){

    },
    getFilters : function(){
        return {
            fval:this.filterNamesCombo.getValue()
        }
    }
});

// referencing the object in the instance of ExtJS
Ext.reg("TimeBottomToolbar", og.TimeBottomToolbar);

Maintenant nous devons faire quelques modification dans la vue du module de temps (« fengOffice/application/views/time/index.php ») : l’appel au fichier JavaScript en début de fichier,

require_javascript('og/time/TimeBottomToolbar.js'); // JavaScript for the ToolBar

le code HTML dans lequel sera logé la barre d’outil (id = timePanelToolBar),

[ligne 38 ...]
<div id="timePanel" class="ogContentPanel" style="background-color:#FFF;height:100%;">

<!-- HTML which will be included in toolbar  -->

<div id="timePanelToolBar" style="background-color:white;background-color:#F0F0F0;height:30px;width:100%;margin-bottom:7px;">

	<div id="timePanelBottomToolbar" class="x-panel-tbar" style="width:100%;height:30px;display:block;background-color:#F0F0F0;border-bottom:1px solid #CCC;">

	</div>

</div>

<!-- /HTML which will be included in toolbar -->

<div id="timePanelContent" style="padding:0px;width:100%;min-width:929px;overflow:auto">
[...]

le code Javascript en fin de fichier qui va permettre d’instancier la barre d’outils,

[ligne 263 …]
< script >
// object instantiation TimeBottomToolbar
var ogTimeBT = new og.TimeBottomToolbar({
	renderTo:'timePanelBottomToolbar', // target for the toolbar
	filterUserIdAndCid: '<?php echo $filterUserIdAndCid; ?>', // User Cid and Id
	usersHfId:'<?php echo $genid ?>hfUsers', // id of a hidden form field containing a Json all users
	companiesHfId:'<?php echo $genid ?>hfCompanies' // id of a hidden form field a Json all companies
	});

 function resizeTimePanel(e, id) {
		var tpc = document.getElementById('timePanelContent');
		if (tpc) {
			tpc.style.height = (document.getElementById('timePanel').clientHeight - 38) + 'px';
		} else {
			og.removeDomEventHandler(window, 'resize', id);
		}
	}
	if (Ext.isIE) {
		og.addDomEventHandler(document.getElementById('timePanelContent'), 'resize', resizeTimePanel);
	} else {
		og.addDomEventHandler(window, 'resize', resizeTimePanel);
	}

	resizeTimePanel();
	[...]

ajouter une variable aux liens de paginations ('filter_user_id' => $filterUserIdAndCid).

[ligne 214 …]
<td colspan="2" class="coViewBody">

	<?php if ($total > 0) {

		$page = intval($start / $limit);

		$totalPages = ceil($total / $limit);

		if ($totalPages > 1) {

		$a_nav = array(

			'<span class="x-tbar-page-first" style="padding-left:16px"/>',

			'<span class="x-tbar-page-prev" style="padding-left:16px"/>',

			'<span class="x-tbar-page-next" style="padding-left:16px"/>',

			'<span class="x-tbar-page-last" style="padding-left:16px"/>'

		);

		$nav = '';

		if ($page != 0) { ?>

			<a class="internalLink" href="<?php echo get_url('time', 'index', array('start' => '0', 'limit' => $limit, 'filter_user_id' => $filterUserIdAndCid)) ?>"><span class="x-tbar-page-first db-ico" style="padding-left:16px"> </span></a>

			<a class="internalLink" href="<?php  echo get_url('time', 'index', array('start' => $start - $limit, 'limit' => $limit, 'filter_user_id' => $filterUserIdAndCid)) ?>"><span class="x-tbar-page-prev db-ico" style="padding-left:16px"> </span></a>

		<?php } else { ?>

			<span class="og-disabled x-tbar-page-first db-ico" style="padding-left:16px"> </span>

			<span class="og-disabled x-tbar-page-prev db-ico" style="padding-left:16px"> </span>

		<?php }

		for ($i = 1; $i < $totalPages + 1; $i++) {

			$off = $limit * ($i - 1);

			if(($i != $page + 1) && abs($i - 1 - $page) <= 2 ) { ?>

				<a class="internalLink" href="<?php echo get_url('time', 'index', array('start' => $off, 'limit' => $limit, 'filter_user_id' => $filterUserIdAndCid)) ?>"><?php echo $i ?></a>

			<?php } else if($i == $page + 1) { ?>

				<b><?php echo $i ?></b>

			<?php }

		}

		if ($page < $totalPages - 1) {

			$off = $start + $limit; ?>

			<a class="internalLink" href="<?php echo get_url('time', 'index', array('start' => $off, 'limit' => $limit, 'filter_user_id' => $filterUserIdAndCid)) ?>"><span class="x-tbar-page-next db-ico" style="padding-left:16px"> </span></a>

			<?php $off = $limit * ($totalPages - 1); ?>

			<a class="internalLink" href="<?php echo get_url('time', 'index', array('start' => $off, 'limit' => $limit, 'filter_user_id' => $filterUserIdAndCid)) ?>"><span class="x-tbar-page-last db-ico" style="padding-left:16px"> </span></a>

		<?php } else { ?>

			<span class="og-disabled x-tbar-page-next db-ico" style="padding-left:16px"> </span>

			<span class="og-disabled x-tbar-page-last db-ico" style="padding-left:16px"> </span>

		<?php } ?>

		<br/><span class='desc'> <?php echo lang('total') . ": " . $totalPages . " " . lang('pages') ?></span>

		<?php }

	} ?>

	</td>
[...]

Nous devons modifier le contrôleur de cette page pour prendre en compte les nouveaux paramètres. Éditer le fichier « fengOffice/application/controllers/TimeController.class.php » On ajoute au début de la fonction index la gestion de la variable de filtre.

[ligne 29 …]
/**
 * $filterUserIdAndCid User Cid and Id. exemple "0:17"
 * $filterUserId User id. exemple "17"
 */
$filterUserIdAndCid = $filterUserId = array_var($_GET,'filter_user_id',null);

if($filterUserId != null)
{
	if($filterUserId == "0:0")// if "everyone" is selected in the filter
	{
		$filterUserId = null;
	}
	else
	{
		$filterUserId = explode(":", $filterUserId);
		$filterUserId = $filterUserId[1];
	}
}
else // By default it displays the timeslot of the current user
{
	$filterUserId = logged_user()->getId();
}
[...]

Puis il va falloir modifier les fonctions Timeslots::getProjectTimeslots() et Timeslots::countProjectTimeslots(), afin de leurs ajouter la variable contenant l’identifiant de l’utilisateur ciblé.

[ligne 95 …]
$timeslots = Timeslots::getProjectTimeslots(logged_user()->getWorkspacesQuery(), $timeslotsUser, active_project(), $start, $limit, $filterUserId);
				$total = Timeslots::countProjectTimeslots(logged_user()->getWorkspacesQuery(), $timeslotsUser, active_project(), $filterUserId);
				break;
[...]

Donc pour finir, il faut retoucher les deux fonctions du modèle « fengOffice/application/models/timeslot/Timeslots.class.php ». Nous ajoutons juste un filtre de plus aux requêtes.

[ligne 95 …]
static function getProjectTimeslots($allowedWorkspaceIdsCSV = null, $user = null, $project = null, $offset = 0, $limit = 20, $specific_user = null) {

		$project_sql = "";
		if ($allowedWorkspaceIdsCSV != null) {
			$project_sql .= " AND `object_id` IN ($allowedWorkspaceIdsCSV)";
		}
		if ($project instanceof Project) {
			$pids = $project->getAllSubWorkspacesQuery();
			$project_sql .= " AND `object_id` IN ($pids)";
		}

		$user_sql = "";
		if ($user instanceof User) {
			$user_sql = " AND user_id = " . $user->getId();
		}
		// to filter by user
		$specific_user_sql = "";
		if ($specific_user != null) {
			$specific_user_sql .= " AND (user_id  = " . $specific_user . ")";
		}

		return Timeslots::findAll(array(
			'conditions' => "object_manager = 'Projects'" . $project_sql . $user_sql . $specific_user_sql,
			'order' => 'start_time DESC, id DESC',
			'offset' => $offset,
			'limit' => $limit));
	}

	static function countProjectTimeslots($allowedWorkspaceIdsCSV = null, $user = null, $project = null, $specific_user = null) {
		$project_sql = "";
		if ($allowedWorkspaceIdsCSV != null) {
			$project_sql .= " AND `object_id` IN ($allowedWorkspaceIdsCSV)";
		}
		if ($project instanceof Project) {
			$pids = $project->getAllSubWorkspacesQuery();
			$project_sql .= " AND `object_id` IN ($pids)";
		}

		$user_sql = "";
		if ($user instanceof User) {
			$user_sql = " AND user_id = " . $user->getId();
		}
		// to filter by user
		$specific_user_sql = "";
		if ($specific_user != null) {
			$specific_user_sql .= " AND (user_id  = " . $specific_user . ")";
		}

		return Timeslots::count("object_manager = 'Projects'" . $project_sql . $user_sql . $specific_user_sql);
	}
[...]

Pour finir un petit avant/après ^^

FengOffice module de Temps

FengOffice module de Temps avec filtre

Tagués avec : ,
Publié dans PHP
Un commentaire pour “Ajouter une barre d’outils à un module existant de FengOffice

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

*