1  //!-- UTF8
     2  /*
     3   Oregano Multiuser Server - Version 1.2.0 - January 4th, 2005
     4   
     5  	Web:  www.oregano-server.org
     6  	Mail: info@oregano-server.org
     7   
     8  	Copyright 2003 - 2004 - 2004 Jens Halm / Cologne, Germany
     9   
    10   This library is free software; you can redistribute it and/or
    11   modify it under the terms of the GNU Lesser General Public
    12   License as published by the Free Software Foundation; either
    13   version 2.1 of the License, or (at your option) any later version.
    14   
    15   This library is distributed in the hope that it will be useful,
    16   but WITHOUT ANY WARRANTY; without even the implied warranty of
    17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    18   Lesser General Public License for more details.
    19   
    20   You should have received a copy of the GNU Lesser General Public
    21   License along with this library; if not, write to the Free Software
    22   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    23  */
    24  
    25  /*
    26   -------------------------------------------
    27  	Classe Mailbox
    28   
    29  	@description :
    30  	Aggège des objets Mail, conteneurs de messages pesistants. 
    31   
    32   
    33  	@author Jens Halm copyright http://www.spicefactory.org/
    34  	@author erixtekila copyleft http://www.v-i-a.net  
    35   -------------------------------------------
    36  	version history :
    37  	1.2.0 : 03/02/05
    38  			- Portage en actionscript 2 pour le
    39  			compile time type checking
    40  			- fonction sort modifiée en utilisant l'activation object
    41  			pour l'accès aux variables locales d'une fonction interne.
    42   -------------------------------------------
    43   */
    44  
    45  import org.omus.util.EventDispatcher;
    46  import org.omus.util.iObservable;
    47  import org.omus.util._Error;
    48  import org.omus.util._Class;
    49  
    50  import org.omus.msg.MessageHandler;
    51  import org.omus.msg.iMessageHandler;
    52  import org.omus.msg.Mail;
    53  import org.omus.msg.Message;
    54  import org.omus.msg.MessageRouter;
    55  import org.omus.msg.Envelope;
    56  import org.omus.msg.EnvelopeFactory;
    57  
    58  import org.omus.data.Table;
    59  import org.omus.data.TableDefinition;
    60  
    61  import org.omus.core.User;
    62  
    63  /**
    64   *	Cette classe gère des objets Mail.
    65   *	Chaque client a deux mailBox : outBox and inBox.
    66   *	La référence à celles-ci s'obtient via User.
    67   *	<p>
    68   *	Evénements auxquels s'abonner :
    69   *	<ul>
    70   *	<li>onLoad(box:Mailbox)				Généré lorsque tous les mails sont chargés.
    71   *												_param box		Référence à la boîte.
    72   *	</li><li>onNewMail(box:Mailbox)			Généré lorsqu'un nouveau Mail est reçu par le serveur. Il faut invoquer load pour le charger.
    73   *												_param box		Référence à la boîte.
    74   *	</li><li>onRemoveAll(box:Mailbox)			Généré lorsque tous les Mails ont été supprimés.
    75   *												_param box		Référence à la boîte.
    76   *	</li><li>onError(error:_Error)				Erreur survenue au chargement ou suppression de tous les mails.
    77   *												_param box		Référence à la boîte.
    78   *	</li></ul></p>
    79   *	Cette classe est aggrémentée par composition des méthodes des sources d'événements (EventDispatcher).
    80   *	Elle est aggrémentée par composition de la classe MessageHandler.
    81   *
    82   *	@see org.omus.util.EventDispatcher
    83   *	@see org.omus.util.iObservable
    84   *	@see org.omus.msg.MessageHandler
    85   *	@see org.omus.msg.iMessageHandler
    86   *
    87   *	@see org.omus.core.User#getInBox
    88   *	@see org.omus.core.User#getOutBox
    89   *
    90   *	@author Jens Halm copyright http://www.spicefactory.org/
    91   *	@author erixtekila copyleft http://www.v-i-a.net 
    92   *	@version 1.2.0
    93   */
    94  class org.omus.msg.Mailbox implements iObservable, iMessageHandler
    95  {
    96  	//--------------------
    97  	// PROPRIETES
    98  	//--------------------
    99  	/**
   100  	 *	Inbox ou Outbox
   101  	 */
   102  	public var type:String;
   103  	
   104  	/**
   105  	 *	Classement des messages.
   106  	 */
   107  	private var order:Array;
   108  	
   109  	/**
   110  	 *	Nombre de mail total.
   111  	 */
   112  	private var total:Number;
   113  	
   114  	/**
   115  	 *	Nombre de mail non lus.
   116  	 */
   117  	public var unread:Number;
   118  	
   119  	/**
   120  	 *	TODO
   121  	 */
   122  	private var maxMsgID:Number;
   123  	
   124  	/**
   125  	 *	Nombre de mail déjà chargés.
   126  	 */
   127  	private var loaded:Number;
   128  	
   129  	/**
   130  	 *	Dernier identifiant de Mail de la liste.
   131  	 */
   132  	private var msgTable:Table;
   133  	
   134  	/**
   135  	 *	Indexation des identifiants de mail.
   136  	 */
   137  	private var idMap:Object;
   138  	
   139  	/**
   140  	 *	Référence au système de génération d'événements.
   141  	 */
   142  	private var _eventSource:EventDispatcher;
   143  	
   144  	/**
   145  	 *	Référence à la classe MessageHandler;
   146  	 */
   147  	private var _messageHandler:MessageHandler;
   148  	
   149  	//--------------------
   150  	// CONSTRUCTEUR
   151  	//--------------------
   152  	/**
   153  	 *	L'objet Mailbox dispose des méthodes d'EventDispatcher
   154  	 *	et de celles de MessageHandler par composition.
   155  	 *
   156  	 *	@param type		Type de boîte : "in" / "out".
   157  	 *	@see org.omus.util.EventDispatcher
   158  	 *	@see org.omus.util.iObservable
   159  	 *	@see org.omus.msg.MessageHandler
   160  	 *	@see org.omus.msg.iMessageHandler
   161  	 */
   162  	public function Mailbox (type:String)
   163  	{
   164  		// Composition avec EventDispatcher
   165  		_eventSource = new EventDispatcher();
   166  		// Composition avec MessageHandler
   167  		_messageHandler = new MessageHandler();
   168  		
   169  		// Initialisation des événements
   170  		_messageHandler.initMessageHandler(this);
   171  		
   172  		// Propriétés
   173  		this.type = type;
   174  		// Classement par défaut
   175  		order = ["dt", false];
   176  
   177  		// Callbacks des envois de messages.
   178  		addHandler("loadHeaders","handleLoadHeaders");
   179  		addHandler("removeAll","handleRemoveAll");
   180  		addHandler("newMail","handleNewMail"); // inBox only
   181  		addHandler("markMail","handleMarkMail"); // outBox only
   182  		
   183  		// trace(this+ " installé.");
   184  	}
   185  	
   186  	
   187  	//--------------------
   188  	// METHODES PUBLIQUES
   189  	//--------------------
   190  	/**
   191  	 *	Utilisé dans un contexte littéral
   192  	 *	@return	Une chaine définissant l'objet
   193  	 */
   194  	public function toString():String
   195  	{
   196  		var s = "[Objet Mailbox]";
   197  		s += "\norg.omus.Mailbox:\n=============\ntype = " + type;
   198  		s += "\nmails total: " + total;
   199  		s += "\nmails unread: " + unread;
   200  		s += "\nmails loaded: " + loaded;
   201  		return s;
   202  	}
   203  	
   204  	/**
   205  	 *	Initialisation d'une boîte aux lettres.
   206  	 *
   207  	 *	@param totalMail			Nombre de mail maximal.	
   208  	 *	@param unreadMail			Nombre de mail non lus.
   209  	 */
   210  	public function init (totalMail:Number, unreadMail:Number):Void
   211  	{
   212  		total = totalMail;
   213  		unread = unreadMail;
   214  		maxMsgID = 0;
   215  		loaded = 0;
   216  		
   217  		if (total > 0) 
   218  		{
   219  			msgTable = null;
   220  		} else
   221  		{
   222  			var def = new TableDefinition();
   223  			def.addColumn("msgID", "int");
   224  			def.addColumn("username", "string");
   225  			def.addColumn("subject", "string");
   226  			def.addColumn("dt", "date");
   227  			def.addColumn("readFlag", "boolean");
   228  			msgTable = new Table(def);
   229  		}
   230  		idMap = new Object();
   231  	}
   232  	
   233  	/**
   234  	 *	Renvoie le type de la boîte.
   235  	 *
   236  	 *	@return		Le type de boîte : "outBox" ou "inBox"
   237  	 */
   238  	public function getType ():String
   239  	{
   240  		return type + "Box";
   241  	}
   242  	
   243  	/**
   244  	 *	Renvoie le nombre total de mails de la boîte.
   245  	 *
   246  	 *	@return		Le nombre toal de mail présents.
   247  	 */
   248  	public function getTotal ():Number
   249  	{
   250  		return total;
   251  	}
   252  	
   253  	/**
   254  	 *	Renvoie le nombre de mails non lus.
   255  	 *
   256  	 *	@return		Nombre de messages non lus.
   257  	 */
   258  	public function getUnread ():Number
   259  	{
   260  		return unread;
   261  	}
   262  	
   263  	/**
   264  	 *	Renvoie un nombre de Mail chargés.
   265  	 *
   266  	 *	@return		Une quantité de mails chargés.
   267  	 */
   268  	public function getLoaded ():Number
   269  	{
   270  		return loaded;
   271  	}
   272  	
   273  	/**
   274  	 *	Renvoie le mail selon son index.
   275  	 *	Par défaut, ils sont classés par date. o correspond au plus ancien Mail.
   276  	 *
   277  	 *	@param index		Identifiant du message.
   278  	 *	@return				Une révérence à Mail.
   279  	 *	@see org.omus.msg.Mail
   280  	 */
   281  	public function getMail (index:Number):Mail
   282  	{
   283  		var row = msgTable.getRow(index);
   284  		if (row == null) return null;
   285  		return idMap["m" + row.msgID];
   286  	}
   287  	
   288  	/**
   289  	 *	Charge tous les Mails valables.
   290  	 *	Génère une événement onLoad aux observateurs.
   291  	 */
   292  	public function load ():Void
   293  	{
   294  		if (loaded >= total)
   295  		{
   296  			fireEvent("info", "onLoad", this);
   297  			return;
   298  		}
   299  		// Chargement lancé.
   300  		var msg = new Message("loadHeaders");
   301  		var attach = msg.getAttachment();
   302  		attach.box = type;
   303  		attach.msgID = maxMsgID;
   304  		// TODO : Accès Singleton
   305  		var envFactory = EnvelopeFactory.getInstance();
   306  		var env = envFactory.getOutgoing(msg, "mailbox");
   307  		//  TODO : Accès Singleton
   308  		var msgRouter = MessageRouter.getInstance();
   309  		msgRouter.handleOutgoing(env, this, "load", arguments, null);
   310  	}
   311  	
   312  	/**
   313  	 *	Supprime tous les messages de la boîte courante.
   314  	 */
   315  	public function removeAll ():Void
   316  	{
   317  		var msg = new Message("removeAll");
   318  		var attach = msg.getAttachment();
   319  		attach.box = type;
   320  		// TODO : Accès Singleton
   321  		var envFactory = EnvelopeFactory.getInstance();
   322  		var env = envFactory.getOutgoing(msg, "mailbox");
   323  		//  TODO : Accès Singleton
   324  		var msgRouter = MessageRouter.getInstance();
   325  		msgRouter.handleOutgoing(env, this, "removeAll", arguments, null);
   326  	}
   327  	
   328  	/**
   329  	 *	Classe les mails par date.
   330  	 *
   331  	 *	@return ascending		true si classement par ordre croissant.
   332  	 */
   333  	public function sortByDate (ascending:Boolean):Void
   334  	{
   335  		// TODO : Accès Singleton
   336  		var clazz = _Class.getInstance();
   337  		if (! clazz.checkArguments("org.omus.Mailbox.sortByDate", [[ascending, "boolean", true]])) return;
   338  		sort("dt", ascending);
   339  	}
   340  	
   341  	/**
   342  	 *	Classe les malis par envoyeur.
   343  	 *
   344  	 *	@param ascending		true si classmeent par ordre croissant.
   345  	 */
   346  	public function sortBySender (ascending:Boolean):Void
   347  	{
   348  		// TODO : Accès Singleton
   349  		var clazz = _Class.getInstance();
   350  		if (! clazz.checkArguments("org.omus.Mailbox.sortBySender", [[ascending, "boolean", true]])) return;
   351  		sort("username",ascending);
   352  	}
   353  
   354  	/**
   355  	 *	Classe les mails par sujet.
   356  	 *
   357  	 * @param ascending		true si classement dans l'ordre croissant.
   358  	 */
   359  	public function sortBySubject (ascending:Boolean):Void
   360  	{
   361  		// TODO : Accès Singleton
   362  		var clazz = _Class.getInstance();
   363  		if (! clazz.checkArguments("org.omus.Mailbox.sortBySubject", [[ascending, "boolean", true]])) return;
   364  		sort("subject", ascending);
   365  	}
   366  	
   367  	/**
   368  	 *	Supprime une mail de la boîte courante.
   369  	 *
   370  	 *	@param mail			Référence à l'objet Mail à supprimer.
   371  	 *	@param msgID		Identifiant du Mail.
   372  	 */
   373  	public function _removeMail (mail:Mail, msgID:Number):Void
   374  	{
   375  		delete idMap["m" + msgID];
   376  		
   377  		var tb = msgTable;
   378  		var cnt = tb.size();
   379  		for (var i = 0; i < cnt; i++)
   380  		{
   381  			if (tb.getRow(i).msgID == msgID)
   382  			{
   383  				tb.removeRow(i);
   384  				break;
   385  			}
   386  		}
   387  		
   388  		loaded--;
   389  		total--;
   390  		if (mail.isUnread()) unread--;
   391  	}
   392  	
   393  	/**
   394  	 *	Rajoute un Mail à la boîte courante.
   395  	 *
   396  	 *	@param mail			Référence à l'objet Mail à supprimer.
   397  	 *	@param msgID		Identifiant du Mail.
   398  	 */
   399  	public function addMail (mail:Mail, msgID:Number):Void
   400  	{
   401  		// TODO : Pourquoi pas après if(loaded < total) ?
   402  		total++;
   403  		unread++;
   404  		if (loaded < total) return;
   405  		
   406  		idMap["m" + msgID] = mail;
   407  		msgTable.addRow(mail.row);
   408  		
   409  		// Reclasse tous les messages.
   410  		sort(order[0], order[1]);
   411  		loaded++;
   412  	}
   413  		
   414  	//*** Implémentation de iObservable ***\
   415  	/**
   416  	 *	Notifie les observateurs d'un événement.
   417  	 *
   418  	 *	@param logLevel Une chaine caractérisant le niveau de logging de l'information.
   419  	 *	@param eventName Nom de l'événement.
   420  	 *	@param arg1		[option] Autant de paramètres de voulu.
   421  	 */
   422  	private function fireEvent (logLevel:String, eventName:String):Void
   423  	{
   424  		_eventSource.fireEvent.apply (_eventSource, arguments);
   425  	}
   426  	
   427  	/**
   428  	 *	Ajoute un nouvel observateur.
   429  	 * 
   430  	 *	@param		listener Référence de l'observateur.
   431  	 *	@return Un booléen indiquant la réussite de l'opération.
   432  	 */
   433  	public function addListener (listener:Object):Boolean
   434  	{
   435  		return _eventSource.addListener(listener);
   436  	}
   437  	
   438  	/**
   439  	 *	Supprime un observateur.
   440  	 *
   441  	 *	@param		listener Référence de l'observateur.
   442  	 *	@return Un booléen indiquant la réussite de l'opération.
   443  	 */
   444  	public function removeListener (listener:Object):Boolean
   445  	{
   446  		return _eventSource.removeListener(listener);
   447  	}
   448  	
   449  	/**
   450  	 *	Supprime tous les abonnés.
   451  	 */
   452  	public function removeAllListeners ():Void
   453  	{
   454  		_eventSource.removeAllListeners();
   455  	}
   456  	
   457  	/**
   458  	 *	Retourne le nombre d'observateurs.
   459  	 *
   460  	 *	@return Le nombre d'observateurs enregistrés.
   461  	 */
   462  	public function countListeners ():Number
   463  	{
   464  		return _eventSource.countListeners();
   465  	}
   466  	
   467  	
   468  	//*** Implémentation de iMessageHandler ***\\
   469  	/**
   470  	 *	Active la gestion d'un type de message par accusé de réception
   471  	 *	en fonction du contenu de son enveloppe.
   472  	 *
   473  	 * @param env		Une référence à l'enveloppe.
   474  	 */
   475  	public function handleMessage (env:Envelope):Void
   476  	{
   477  		_messageHandler.handleMessage(env);
   478  	}
   479  	
   480  	/**
   481  	 *	Rajoute un gestionnaire chargé d'intercepter 
   482  	 *	la réponse du serveur suite à un message soumis.
   483  	 *	Forme d'accusé de réception (callback).
   484  	 *
   485  	 *	@param		subject Le type de message.
   486  	 *	@param		methodName Le nom de l'événement gérant un type de message. 
   487  	 */
   488  	public function addHandler (subject:String, methodName:String):Void
   489  	{
   490  		_messageHandler.addHandler (subject, methodName);
   491  	}
   492  
   493  		
   494  	// Callbacks des messges Message avec accusés de réception
   495  	/**
   496  	 *	Gestionnaire de l'accusé de réception du message de chargement des entêtes de Mail.
   497  	 *	Génère un événement onLoad ou onError aux observateurs.
   498  	 *
   499  	 *	@param env		Enveloppe du message retourné.
   500  	 */
   501  	private function handleLoadHeaders (env:Envelope):Void
   502  	{
   503  		var attach = env.getMessage().getAttachment();
   504  		var errCode = attach.error;
   505  		if (errCode == "ok")
   506  		{
   507  			var table = attach.tables.result;
   508  			if (msgTable == null)
   509  			{
   510  				msgTable = table;
   511  			} else 
   512  			{
   513  				msgTable.addAllRows(table);
   514  			}
   515  			sort(order[0], order[1]);
   516  			
   517  			var cnt = table.size();
   518  			var map = idMap;
   519  			var row:Object;
   520  			var mail:Mail;
   521  			var id:Number;
   522  			for (var i = 0; i < cnt; i++)
   523  			{
   524  				row = table.getRow(i);
   525  				id = row.msgID;
   526  				if (id > maxMsgID) maxMsgID = id;
   527  				mail = new Mail("");
   528  				mail.init(this, row);
   529  				map["m" + row.msgID] = mail;
   530  			}
   531  			
   532  			loaded = msgTable.size();
   533  			if (total < loaded)
   534  			{
   535  				// some newMail messages have been lost
   536  				unread += (loaded - total);
   537  				total = loaded;
   538  				// Logs internes
   539  				_global.oregano.iLog.error("clj-069");
   540  			}
   541  			fireEvent("info", "onLoad", this);
   542  		} else
   543  		{
   544  			var err = new _Error(errCode, "load", new Array());
   545  			fireEvent("error", "onError", this, err);
   546  		}
   547  	}
   548  	
   549  	/**
   550  	 *	Gestionnaire de l'accusé de réception du message de suppression des Mails.
   551  	 *	Génère un événement onRemoveAll ou onError aux observateurs.
   552  	 *
   553  	 *	@param env		Enveloppe du message retourné.
   554  	 */
   555  	private function handleRemoveAll (env:Envelope):Void
   556  	{
   557  		var errCode = env.getMessage().getAttachment().error;
   558  		if (errCode == "ok")
   559  		{
   560  			init(0,0);
   561  			fireEvent("info", "onRemoveAll", this);
   562  		} else
   563  		{
   564  			var err = new _Error(errCode, "removeAll", new Array());
   565  			fireEvent("error", "onError", this, err);
   566  		}
   567  	}
   568  	
   569  	/**
   570  	 *	Gestionnaire de l'accusé de réception du message d'ajout d'un nouveau Mail.
   571  	 *	inBox only
   572  	 *	Génère un événement onNewMail aux observateurs.
   573  	 *
   574  	 *	@param env		Enveloppe du message retourné.
   575  	 */
   576  	private function handleNewMail (env:Envelope):Void
   577  	{
   578  		total++;
   579  		unread++;
   580  		fireEvent("info", "onNewMail", this);
   581  	}
   582  	
   583  	/**
   584  	 *	Gestionnaire de l'accusé de réception du message d'ajout d'un nouveau Mail.
   585  	 *	outBox only
   586  	 *	Génère un événement onNewMail aux observateurs.
   587  	 *
   588  	 *	@param env		Enveloppe du message retourné.
   589  	 */
   590  	private function handleMarkMail (env:Envelope):Void
   591  	{
   592  		var msgID = env.getMessage().getAttachment().msgID;
   593  		var mail = idMap["m" + msgID];
   594  		if (mail != undefined) mail.handleMessage(env);
   595  	}
   596  	
   597  	// Fin des callbacks des messges Message avec accusés de réception
   598  
   599  	
   600  	//--------------------
   601  	// METHODES PRIVEES
   602  	//--------------------
   603  	/**
   604  	 *	Fonction général de classement des mails.
   605  	 *
   606  	 *	@param propName		Nom de la catégorie.
   607  	 *	@param asc			true si classement par ordre croissant.
   608  	 */
   609  	private function sort (propName:String, asc:Boolean):Void
   610  	{
   611  		// TODO : Suppression des membres statiques au profit de l'utilisation de l'activation object.
   612  		// org.omus.Mailbox.propName = propName;
   613  		// org.omus.Mailbox.asc = asc;
   614  		order = [propName, asc];
   615  		if (msgTable == null) return;
   616  		
   617  		// fonction de tri
   618  		var func = function (row1,row2) 
   619  		{
   620  			var p1 = row1[propName];
   621  			var p2 = row2[propName];
   622  			if (p1 == p2) return 0;
   623  			var bool = (p1 < p2);
   624  			if ((bool && asc) || (! bool && ! asc)) return -1;
   625  			return 1;
   626  		};
   627  		
   628  		msgTable.rowList.sort(func);
   629  		msgTable.rowIDMap = null;
   630  	}
   631  	
   632  	//--------------------
   633  	// METHODES STATIQUES
   634  	//--------------------
   635  	/**
   636  	 *	Utilisé dans un contexte littéral
   637  	 *
   638  	 *	@return Une chaine définissant l'objet.
   639  	 */
   640  	public static function toLog():String
   641  	{
   642  		return "[Objet Mailbox]";
   643  	}
   644  	
   645  	/**
   646  	 *	Gestionnaire d'accusé de réception émanant directement de MessageRouter
   647  	 *	et chargé de redistribuer les messages entre les propriétés d'un groupe ou d'un utilisateur.
   648  	 *	TODO : Compileur MMC 2004 n'accepte pas une méthode de classe et d'instance ayant le même nom.
   649  	 *
   650  	 *	@param env		Enveloppe du message.
   651  	 *	@see org.omus.msg.MessageRouter#handleIncoming
   652  	 */
   653  	public static function handleIncomingMessage (env:Envelope):Void
   654  	{
   655  		var subj = env.getMessage().getSubject();
   656  		// TODO : Accès Singleton
   657  		var user = User.getInstance();
   658  		if (subj == "newMail")
   659  		{
   660  			user.getInBox().handleMessage(env);
   661  		} else if (subj == "markMail")
   662  		{
   663  			user.getOutBox().handleMessage(env);
   664  		} else 
   665  		{
   666  			_global.org.omus.msgRouter.unknownMsgType(env);
   667  		}
   668  	}
   669  	
   670  }
   671