changeset 100:cc91817a4206

Merge
author jorge.rowies
date Sat, 04 Jun 2011 22:46:06 -0300
parents 3027c64344bd (current diff) 2434c2323f3d (diff)
children 9bfdd5bf3ad2
files Agendas/trunk/src/Agendas.Blog/Impl/AgendarReunionPostWriter.cs Agendas/trunk/src/Agendas.Blog/Impl/PostWriter.cs Agendas/trunk/src/Agendas.Blog/Impl/PublicarReunionPostWriter.cs Agendas/trunk/src/Agendas.Domain/Ponente.cs Agendas/trunk/src/Agendas.Tests/Agendas.Tests.csproj Agendas/trunk/src/Agendas.Tests/Blog/PublicadorTests.cs
diffstat 54 files changed, 1287 insertions(+), 394 deletions(-) [+]
line wrap: on
line diff
--- a/Agendas/trunk/src/Agendas.Blog/Impl/PostWriter.cs	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Blog/Impl/PostWriter.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -25,7 +25,7 @@
 
     protected string GetNombreUsuario(Evento evento)
     {
-      return evento.Tracks.Single(t => t.Accion == Accion.Publicar).Usuario;
+      return evento.Tracks.Single(t => t.Accion == Accion.Publicar).Usuario.Nombre;
     }
 
     protected void ExecuteService(string title, string body, string author)
--- a/Agendas/trunk/src/Agendas.Domain/Agenda.cs	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Domain/Agenda.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -8,14 +8,14 @@
 	public class Agenda
 	{
 		private readonly IEventoRepository _eventosRepository;
-		private readonly IPonenteRepository _ponenteRepository;
+		private readonly IPersonaRepository _personaRepository;
 		private readonly IPublicador _publicador;
 
 		public Agenda(IPublicador publicador, IEventoRepository eventosRepository,
-		              IPonenteRepository ponenteRepository)
+		              IPersonaRepository personaRepository)
 		{
 			_publicador = publicador;
-			_ponenteRepository = ponenteRepository;
+			_personaRepository = personaRepository;
 			_eventosRepository = eventosRepository;
 		}
 
@@ -41,17 +41,17 @@
 		{
 			Evento evento = GetEvento(eventoId);
 
-			Ponente ponente = null;
+			Persona persona = null;
 			if (!string.IsNullOrWhiteSpace(ponenteNombre))
-				ponente = GetPonente(ponenteNombre);
+				persona = GetPonente(ponenteNombre);
 
 			if (evento.Titulo != titulo)
 				evento.CambiarTitulo(titulo);
 
 			if (evento.Fecha == null && fecha != null)
-				evento.Agendar(ponente, fecha, urlInvitacion);
-			else if (evento.Fecha != fecha || evento.Ponente != ponente || evento.UrlInvitacion != urlInvitacion)
-				evento.Actualizar(ponente, fecha, urlInvitacion);
+				evento.Agendar(persona, fecha, urlInvitacion);
+			else if (evento.Fecha != fecha || evento.Ponente != persona || evento.UrlInvitacion != urlInvitacion)
+				evento.Actualizar(persona, fecha, urlInvitacion);
 
 			Notify(evento);
 
@@ -65,8 +65,8 @@
 
 		public Resultado Proponer(string titulo, string ponenteNombre, string urlInvitacion)
 		{
-			Ponente ponente = GetPonente(ponenteNombre);
-			var evento = Evento.Proponer(titulo, ponente, urlInvitacion);
+			Persona persona = GetPonente(ponenteNombre);
+			var evento = Evento.Proponer(titulo, persona, urlInvitacion);
 
 			if (string.IsNullOrWhiteSpace(evento.Titulo))
 				return new Resultado(false);
@@ -74,7 +74,7 @@
 			Notify(evento);
 
 			_eventosRepository.Save(evento);
-			_ponenteRepository.Save(ponente);
+			_personaRepository.Save(persona);
 
 			return new Resultado(true);
 		}
@@ -86,18 +86,18 @@
 			if (string.IsNullOrWhiteSpace(ponenteNombre))
 				return new Resultado(false);
 
-			Ponente ponente = GetPonente(ponenteNombre);
+			Persona persona = GetPonente(ponenteNombre);
 
 			Evento evento = _eventosRepository.GetPropuestaByTitulo(titulo);
 			if (evento == null)
-				evento = Evento.Agendar(titulo, ponente, fecha.Value, urlInvitacion);
+				evento = Evento.Agendar(titulo, persona, fecha.Value, urlInvitacion);
 			else
-				evento.Agendar(ponente, fecha, urlInvitacion);
+				evento.Agendar(persona, fecha, urlInvitacion);
 
 			Notify(evento);
 
 			_eventosRepository.Save(evento);
-			_ponenteRepository.Save(ponente);
+			_personaRepository.Save(persona);
 
 			return new Resultado(true);
 		}
@@ -137,15 +137,15 @@
 
 		public void RegistrarPonente(string nombre, string mail, string twitter, string blog)
 		{
-			var ponente = new Ponente(nombre, mail, twitter, blog);
-			_ponenteRepository.Save(ponente);
+			var ponente = new Persona(nombre, mail, twitter, blog);
+			_personaRepository.Save(ponente);
 		}
 
-		private Ponente GetPonente(string nombre)
+		private Persona GetPonente(string nombre)
 		{
-			Ponente ponente = _ponenteRepository.GetByNombre(nombre) ?? new Ponente(nombre);
-			_ponenteRepository.Save(ponente);
-			return ponente;
+			Persona persona = _personaRepository.GetByNombre(nombre) ?? new Persona(nombre);
+			_personaRepository.Save(persona);
+			return persona;
 		}
 	}
 }
\ No newline at end of file
--- a/Agendas/trunk/src/Agendas.Domain/Agendas.Domain.csproj	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Domain/Agendas.Domain.csproj	Sat Jun 04 22:46:06 2011 -0300
@@ -42,18 +42,22 @@
   <ItemGroup>
     <Compile Include="Agenda.cs" />
     <Compile Include="CompositePublicador.cs" />
+    <Compile Include="Cuenta.cs" />
     <Compile Include="Evento.cs" />
     <Compile Include="Exceptions\EventoNotFoundException.cs" />
     <Compile Include="Exceptions\IdentityContextNotConfiguredException.cs" />
     <Compile Include="Exceptions\UsuarioNoAutenticadoException.cs" />
     <Compile Include="Identificable.cs" />
+    <Compile Include="Identification.cs" />
     <Compile Include="IdentityContext.cs" />
     <Compile Include="ISeguridad.cs" />
-    <Compile Include="Ponente.cs" />
+    <Compile Include="Persona.cs" />
     <Compile Include="Repositories\IEventoRepository.cs" />
     <Compile Include="IPublicador.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Repositories\IPersonaRepository.cs" />
     <Compile Include="Resultado.cs" />
+    <Compile Include="Services\PersonaService.cs" />
     <Compile Include="Track.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Agendas/trunk/src/Agendas.Domain/Cuenta.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -0,0 +1,19 @@
+namespace AltNetHispano.Agendas.Domain
+{
+	public class Cuenta : Identificable
+	{
+		public virtual Persona Persona { get; set; }
+		public virtual string LogonName { get; set; }
+		public virtual IdentityProviderEnum IdentityProvider { get; set; }
+
+		protected Cuenta()
+		{
+		}
+
+		public Cuenta(IdentityProviderEnum identityProvider, string logonName)
+		{
+			LogonName = logonName;
+			IdentityProvider = identityProvider;
+		}
+	}
+}
\ No newline at end of file
--- a/Agendas/trunk/src/Agendas.Domain/Evento.cs	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Domain/Evento.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -13,7 +13,6 @@
 		#region Propiedades del Evento
 
 		private readonly IList<Track> _tracks;
-		private Ponente _ponente;
 		private readonly ICollection<Track> _newTracks = new List<Track>();
 
 		/// <summary>
@@ -44,18 +43,7 @@
 		/// <summary>
 		/// Ponente o responsable del evento
 		/// </summary>
-		public virtual Ponente Ponente
-		{
-			get { return _ponente; }
-			private set
-			{
-				if (value == null && _ponente != null || value != _ponente && _ponente != null)
-					_ponente.RemoveEvento(this);
-				if (value != null)
-					value.AddEvento(this);
-				_ponente = value;
-			}
-		}
+		public virtual Persona Ponente { get; private set; }
 
 		/// <summary>
 		/// Track de cambios que ha sufrido el evento
@@ -73,11 +61,11 @@
 		/// Propone un evento
 		/// </summary>
 		/// <param name="titulo">Título del evento propuesto</param>
-		/// <param name="ponente">Ponente para evento propuesto</param>
+		/// <param name="persona">Ponente para evento propuesto</param>
 		/// <returns></returns>
-		public static Evento Proponer(string titulo, Ponente ponente, string urlInvitacion)
+		public static Evento Proponer(string titulo, Persona persona, string urlInvitacion)
 		{
-			var evento = new Evento {Titulo = titulo, Ponente = ponente, UrlInvitacion = urlInvitacion};
+			var evento = new Evento {Titulo = titulo, Ponente = persona, UrlInvitacion = urlInvitacion};
 			evento.AddTrack(new Track(evento, Accion.Proponer));
 
 			return evento;
@@ -87,14 +75,14 @@
 		/// Agenda un evento que no estaba propuesto
 		/// </summary>
 		/// <param name="titulo">Título del evento a agendar</param>
-		/// <param name="ponente">Ponente para el evento</param>
+		/// <param name="persona">Ponente para el evento</param>
 		/// <param name="fecha">Fecha de realización del evento</param>
 		/// <param name="urlInvitacion">Url con la invitación realizada por el ponente</param>
 		/// <returns></returns>
-		public static Evento Agendar(string titulo, Ponente ponente, DateTime fecha, string urlInvitacion)
+		public static Evento Agendar(string titulo, Persona persona, DateTime fecha, string urlInvitacion)
 		{
 			var evento = new Evento { Titulo = titulo };
-			evento.Agendar(ponente, fecha, urlInvitacion);
+			evento.Agendar(persona, fecha, urlInvitacion);
 
 			return evento;
 		}
@@ -102,20 +90,20 @@
 		/// <summary>
 		/// Agenda el evento actual
 		/// </summary>
-		/// <param name="ponente">Ponente para el evento</param>
+		/// <param name="persona">Ponente para el evento</param>
 		/// <param name="fecha">Fecha de realización del evento</param>
 		/// <param name="urlInvitacion">Url con la invitación realizada por el ponente</param>
-		public virtual void Agendar(Ponente ponente, DateTime? fecha, string urlInvitacion)
+		public virtual void Agendar(Persona persona, DateTime? fecha, string urlInvitacion)
 		{
-			Ponente = ponente;
+			Ponente = persona;
 			Fecha = fecha;
 			UrlInvitacion = urlInvitacion;
 			AddTrack(new Track(this, Accion.Agendar));
 		}
 
-		public virtual void Actualizar(Ponente ponente, DateTime? fecha, string urlInvitacion)
+		public virtual void Actualizar(Persona persona, DateTime? fecha, string urlInvitacion)
 		{
-			Ponente = ponente;
+			Ponente = persona;
 			Fecha = fecha;
 			UrlInvitacion = urlInvitacion;
 
--- a/Agendas/trunk/src/Agendas.Domain/Identificable.cs	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Domain/Identificable.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -2,7 +2,7 @@
 
 namespace AltNetHispano.Agendas.Domain
 {
-	public class Identificable
+	public abstract class Identificable
 	{
 		public virtual Guid Id { get; private set; }
 	}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Agendas/trunk/src/Agendas.Domain/Identification.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -0,0 +1,17 @@
+namespace AltNetHispano.Agendas.Domain
+{
+	public class Identification
+	{
+		public const string Map = " !@";
+
+		public Identification(string name)
+		{
+			char c = name[0];
+			IdentityProvider = (IdentityProviderEnum)Map.IndexOf(c);
+			LogonName = name.Substring(1);
+		}
+
+		public string LogonName { get; set; }
+		public IdentityProviderEnum IdentityProvider { get; set; }
+	}
+}
\ No newline at end of file
--- a/Agendas/trunk/src/Agendas.Domain/IdentityContext.cs	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Domain/IdentityContext.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -1,20 +1,28 @@
-using AltNetHispano.Agendas.Domain.Exceptions;
+using System;
+using AltNetHispano.Agendas.Domain.Exceptions;
+using AltNetHispano.Agendas.Domain.Repositories;
 
 namespace AltNetHispano.Agendas.Domain
 {
 	public class IdentityContext
 	{
 		private static ISeguridad _current;
+		private static IPersonaRepository _personaRepository;
 
-		public static ISeguridad Current
+		public static void Init(ISeguridad seguridad, IPersonaRepository personaRepository)
 		{
-			private get
+			_current = seguridad;
+			_personaRepository = personaRepository;
+		}
+
+		private static ISeguridad Current
+		{
+			get
 			{
 				if (_current == null)
 					throw new IdentityContextNotConfiguredException();
 				return _current;
 			}
-			set { _current = value; }
 		}
 
 		public static string GetUserName()
@@ -24,5 +32,11 @@
 				throw new UsuarioNoAutenticadoException();
 			return username;
 		}
+
+		public static Persona GetUsuario()
+		{
+			var cuenta = _personaRepository.GetCuenta(IdentityProviderEnum.Twitter, GetUserName());
+			return cuenta != null ? cuenta.Persona : null;
+		}
 	}
 }
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Agendas/trunk/src/Agendas.Domain/Persona.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -0,0 +1,61 @@
+using System.Collections.Generic;
+
+namespace AltNetHispano.Agendas.Domain
+{
+	public class Persona : Identificable
+	{
+		private readonly IList<Cuenta> _cuentas;
+
+		protected Persona()
+		{
+			//ctor para NHibernate
+		}
+
+		public Persona(string nombre, string mail, string twitter, string blog)
+			: this(nombre)
+		{
+			Mail = mail;
+			Twitter = twitter;
+			Blog = blog;
+		}
+
+		public Persona(string nombre)
+		{
+			Nombre = nombre;
+			_cuentas = new List<Cuenta>();
+		}
+
+		public virtual string Nombre { get; private set; }
+
+		public virtual string Mail { get; private set; }
+
+		public virtual string Twitter { get; set; }
+
+		public virtual string Blog { get; private set; }
+
+		public virtual IEnumerable<Cuenta> Cuentas
+		{
+			get { return _cuentas; }
+		}
+
+		public virtual void AddCuenta(Cuenta cuenta)
+		{
+			if (!_cuentas.Contains(cuenta))
+				_cuentas.Add(cuenta);
+			cuenta.Persona = this;
+		}
+
+		public virtual void RemoveCuenta(Cuenta cuenta)
+		{
+			if (_cuentas.Contains(cuenta))
+				_cuentas.Remove(cuenta);
+			cuenta.Persona = null;
+		}
+	}
+
+	public enum IdentityProviderEnum
+	{
+		BuiltIn = 1,
+		Twitter = 2
+	}
+}
\ No newline at end of file
--- a/Agendas/trunk/src/Agendas.Domain/Ponente.cs	Sat Jun 04 22:33:05 2011 -0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace AltNetHispano.Agendas.Domain
-{
-	public class Ponente : Identificable
-	{
-		private readonly IList<Evento> _eventos;
-
-		protected Ponente()
-		{
-			//ctor para NHibernate
-		}
-
-		public Ponente(string nombre, string mail, string twitter, string blog)
-			: this(nombre)
-		{
-			Mail = mail;
-			Twitter = twitter;
-			Blog = blog;
-		}
-
-		public Ponente(string nombre)
-		{
-			Nombre = nombre;
-			_eventos = new List<Evento>();
-		}
-
-		public virtual string Nombre { get; private set; }
-
-		public virtual string Mail { get; private set; }
-
-		public virtual string Twitter { get; private set; }
-
-		public virtual string Blog { get; private set; }
-
-		public virtual IEnumerable<Evento> Eventos
-		{
-			get { return _eventos; }
-		}
-
-		public virtual void AddEvento(Evento evento)
-		{
-			if (!_eventos.Contains(evento))
-				_eventos.Add(evento);
-		}
-
-		public virtual void RemoveEvento(Evento evento)
-		{
-			if (_eventos.Contains(evento))
-				_eventos.Remove(evento);
-		}
-	}
-}
\ No newline at end of file
--- a/Agendas/trunk/src/Agendas.Domain/Repositories/IEventoRepository.cs	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Domain/Repositories/IEventoRepository.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -13,11 +13,4 @@
 		void Save(Evento evento);
 		void Update(Evento evento);
 	}
-
-	public interface IPonenteRepository
-	{
-		IList<Ponente> GetAll();
-		Ponente GetByNombre(string ponenteNombre);
-		void Save(Ponente ponente);
-	}
 }
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Agendas/trunk/src/Agendas.Domain/Repositories/IPersonaRepository.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -0,0 +1,12 @@
+using System.Collections.Generic;
+
+namespace AltNetHispano.Agendas.Domain.Repositories
+{
+	public interface IPersonaRepository
+	{
+		IList<Persona> GetAll();
+		Persona GetByNombre(string ponenteNombre);
+		void Save(Persona persona);
+		Cuenta GetCuenta(IdentityProviderEnum identityProvider, string username);
+	}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Agendas/trunk/src/Agendas.Domain/Services/PersonaService.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -0,0 +1,36 @@
+using AltNetHispano.Agendas.Domain.Repositories;
+
+namespace AltNetHispano.Agendas.Domain.Services
+{
+	public class PersonaService
+	{
+		private readonly IPersonaRepository _personaRepository;
+
+		public PersonaService(IPersonaRepository personaRepository)
+		{
+			_personaRepository = personaRepository;
+		}
+
+		public bool Validate(IdentityProviderEnum identityProvider, string username)
+		{
+			var cuenta = _personaRepository.GetCuenta(identityProvider, username);
+			return cuenta != null;
+		}
+
+		public bool Validate(IdentityProviderEnum identityProvider, string username, string nombre)
+		{
+			var cuenta = _personaRepository.GetCuenta(identityProvider, username);
+			if (cuenta==null)
+			{
+				var persona=new Persona(nombre);
+				persona.AddCuenta(new Cuenta(identityProvider, username));
+
+				if (identityProvider == IdentityProviderEnum.Twitter)
+					persona.Twitter = username;
+
+				_personaRepository.Save(persona);
+			}
+			return true;
+		}
+	}
+}
\ No newline at end of file
--- a/Agendas/trunk/src/Agendas.Domain/Track.cs	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Domain/Track.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -9,14 +9,14 @@
 
 		public Track(Evento evento, Accion accion)
 		{
-			Usuario = IdentityContext.GetUserName();
+			Usuario = IdentityContext.GetUsuario();
 			Evento = evento;
 			Accion = accion;
 		}
 
 		public virtual Evento Evento { get; private set; }
 		public virtual Accion Accion { get; private set; }
-		public virtual string Usuario { get; private set; }
+		public virtual Persona Usuario { get; private set; }
 	}
 
 	public enum Accion
--- a/Agendas/trunk/src/Agendas.Factories/AgendaFactory.cs	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Factories/AgendaFactory.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -1,5 +1,8 @@
-using Agendas.NHibernate;
+using System;
+using Agendas.NHibernate;
 using AltNetHispano.Agendas.Domain;
+using AltNetHispano.Agendas.Domain.Repositories;
+using AltNetHispano.Agendas.Domain.Services;
 using AltNetHispano.Agendas.Repositories.NHibernate;
 using NHibernate;
 
@@ -13,7 +16,18 @@
 		public static Agenda GetAgenda()
 		{
 			ISessionFactory sessionFactory = NhHelper.GetSessionFactory();
-			return _agenda ?? (_agenda = new Agenda(null, new EventoRepository(sessionFactory), new PonenteRepository(sessionFactory)));
+			return _agenda ?? (_agenda = new Agenda(null, new EventoRepository(sessionFactory), new PersonaRepository(sessionFactory)));
+		}
+
+		public static IPersonaRepository GetPersonaRepository()
+		{
+			ISessionFactory sessionFactory = NhHelper.GetSessionFactory();
+			return new PersonaRepository(sessionFactory);
+		}
+
+		public static PersonaService GetPersonaService()
+		{
+			return new PersonaService(GetPersonaRepository());
 		}
 	}
 }
\ No newline at end of file
--- a/Agendas/trunk/src/Agendas.NHibernate/NhHelper.cs	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.NHibernate/NhHelper.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -18,12 +18,15 @@
 			if (_cfg == null)
 			{
 				var orm = new ObjectRelationalMapper();
+				orm.TablePerClass<Persona>();
 				orm.TablePerClass<Evento>();
 				orm.TablePerClass<Track>();
-				orm.TablePerClass<Ponente>();
+				orm.TablePerClass<Cuenta>();
 
-				orm.Cascade<Evento, Ponente>(Cascade.None);
-				orm.Cascade<Ponente, Evento>(Cascade.None);
+				orm.Cascade<Evento, Persona>(Cascade.None);
+				orm.Cascade<Persona, Evento>(Cascade.None);
+				
+				orm.Cascade<Persona, Cuenta>(Cascade.All);
 
 				var mapper = new Mapper(orm);
 
--- a/Agendas/trunk/src/Agendas.Repositories.Memory/EventoRepository.cs	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Repositories.Memory/EventoRepository.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -15,8 +15,6 @@
 
 		public void Delete(Evento evento)
 		{
-			if (evento.Ponente != null)
-				evento.Ponente.RemoveEvento(evento);
 			Objects.Remove(evento.Id);
 		}
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Agendas/trunk/src/Agendas.Repositories.Memory/PersonaRepository.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -0,0 +1,15 @@
+using System.Linq;
+using AltNetHispano.Agendas.Domain;
+using AltNetHispano.Agendas.Domain.Repositories;
+
+namespace AltNetHispano.Agendas.Repositories.Memory
+{
+	public class PersonaRepository : RepositoryBase<Persona>, IPersonaRepository
+	{
+		public Persona GetByLogonName(string logonName)
+		{
+			return Objects.Values.SingleOrDefault(p => p.Cuentas.Any(c=>c.LogonName==logonName));
+
+		}
+	}
+}
\ No newline at end of file
--- a/Agendas/trunk/src/Agendas.Repositories.Memory/PonenteRepository.cs	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Repositories.Memory/PonenteRepository.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -1,18 +1,27 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
 using System.Linq;
 using AltNetHispano.Agendas.Domain;
 using AltNetHispano.Agendas.Domain.Repositories;
 
 namespace AltNetHispano.Agendas.Repositories.Memory
 {
-	public class PonenteRepository : RepositoryBase<Ponente>, IPonenteRepository
+	public class PersonaRepository : RepositoryBase<Persona>, IPersonaRepository
 	{
-		public Ponente GetByNombre(string nombre)
+		public Persona GetByNombre(string nombre)
 		{
 			return Objects.Values.SingleOrDefault(p => p.Nombre == nombre);
 		}
 
-		public IList<Ponente> GetAll()
+		public Cuenta GetCuenta(IdentityProviderEnum identityProvider, string username)
+		{
+			var cuentas = from p in Objects.Values
+			             select p.Cuentas.SingleOrDefault(c => c.IdentityProvider == identityProvider && c.LogonName == username);
+
+			return cuentas.Where(c => c != null).SingleOrDefault();
+		}
+
+		public IList<Persona> GetAll()
 		{
 			return Objects.Values.ToList();
 		}
--- a/Agendas/trunk/src/Agendas.Repositories.NHibernate/PonenteRepository.cs	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Repositories.NHibernate/PonenteRepository.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -1,24 +1,32 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
 using AltNetHispano.Agendas.Domain;
 using AltNetHispano.Agendas.Domain.Repositories;
 using NHibernate;
 
 namespace AltNetHispano.Agendas.Repositories.NHibernate
 {
-	public class PonenteRepository : RepositoryBase<Ponente>, IPonenteRepository
+	public class PersonaRepository : RepositoryBase<Persona>, IPersonaRepository
 	{
-		public PonenteRepository(ISessionFactory sessionFactory) : base(sessionFactory)
+		public PersonaRepository(ISessionFactory sessionFactory) : base(sessionFactory)
 		{
 		}
 
-		public IList<Ponente> GetAll()
+		public IList<Persona> GetAll()
 		{
-			return Session.QueryOver<Ponente>().List();
+			return Session.QueryOver<Persona>().List();
 		}
 
-		public Ponente GetByNombre(string ponenteNombre)
+		public Persona GetByNombre(string ponenteNombre)
 		{
-			return Session.QueryOver<Ponente>().Where(p => p.Nombre == ponenteNombre).SingleOrDefault();
+			return Session.QueryOver<Persona>().Where(p => p.Nombre == ponenteNombre).SingleOrDefault();
+		}
+
+		public Cuenta GetCuenta(IdentityProviderEnum identityProvider, string username)
+		{
+			return
+				Session.QueryOver<Cuenta>().Where(c => c.IdentityProvider == identityProvider && c.LogonName == username).
+					SingleOrDefault();
 		}
 	}
 }
\ No newline at end of file
--- a/Agendas/trunk/src/Agendas.Tests/AgendarTests.cs	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Tests/AgendarTests.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -4,6 +4,7 @@
 using AltNetHispano.Agendas.Domain;
 using AltNetHispano.Agendas.Domain.Exceptions;
 using AltNetHispano.Agendas.Domain.Repositories;
+using AltNetHispano.Agendas.Repositories.Memory;
 using Moq;
 using NUnit.Framework;
 
@@ -28,7 +29,7 @@
 
 			publicador.Setup(p => p.Publicar(It.IsAny<IEnumerable<Track>>())).Throws(new Exception("Error intencional"));
 
-			var agenda = new Agenda(publicador.Object, repository.Object, DefaultPonenteRepository);
+			var agenda = new Agenda(publicador.Object, repository.Object, DefaultPersonaRepository);
 
 			Assert.Throws<Exception>(() => agenda.Agendar("Van para publicar", "jjmontes", DateTime.Now, urlInvitacion));
 			Assert.AreEqual(0, agenda.GetEventosAgendados().Count);
@@ -40,12 +41,12 @@
 		[Test]
 		public void Intentar_agendar_evento_sin_servicio_de_seguridad()
 		{
-			IdentityContext.Current = null;
+			IdentityContext.Init(null, new PersonaRepository());
 
 			var publicador = new Mock<IPublicador>();
 			var repository = new Mock<IEventoRepository>();
 
-			var agenda = new Agenda(publicador.Object, repository.Object, DefaultPonenteRepository);
+			var agenda = new Agenda(publicador.Object, repository.Object, DefaultPersonaRepository);
 
 			Assert.Throws<IdentityContextNotConfiguredException>(
 				() => agenda.Agendar("Van para publicar", "jjmontes", DateTime.Now,
@@ -56,12 +57,12 @@
 		[Test]
 		public void Intentar_agendar_evento_sin_usuario_autenticado()
 		{
-			SetCurrentUser(null);
+			SetCurrentUser(null, null);
 
 			var publicador = new Mock<IPublicador>();
 			var repository = new Mock<IEventoRepository>();
 
-			var agenda = new Agenda(publicador.Object, repository.Object, DefaultPonenteRepository);
+			var agenda = new Agenda(publicador.Object, repository.Object, DefaultPersonaRepository);
 
 			Assert.Throws<UsuarioNoAutenticadoException>(() => agenda.Agendar("Van para publicar", "jjmontes", DateTime.Now,
 				urlInvitacion));
@@ -73,7 +74,7 @@
 		[Test]
 		public void Intentar_agendar_evento_sin_ponente()
 		{
-			var agenda = new Agenda(null, DefaultEventoRepository, DefaultPonenteRepository);
+			var agenda = new Agenda(null, DefaultEventoRepository, DefaultPersonaRepository);
 
 			var r = agenda.Agendar("Van para publicar", string.Empty, DateTime.Now,
 				urlInvitacion);
@@ -90,7 +91,7 @@
 			var repository = new Mock<IEventoRepository>();
 
 			var agenda = new Agenda(new CompositePublicador(new[] {publicador1.Object, publicador2.Object}),
-															repository.Object, DefaultPonenteRepository);
+															repository.Object, DefaultPersonaRepository);
 
 			agenda.Agendar("Van para publicar", "jjmontes", DateTime.Now,
 				urlInvitacion);
@@ -105,7 +106,7 @@
 		{
 			var publicador = new Mock<IPublicador>();
 
-			var agenda = new Agenda(publicador.Object, DefaultEventoRepository, DefaultPonenteRepository);
+			var agenda = new Agenda(publicador.Object, DefaultEventoRepository, DefaultPersonaRepository);
 
 			agenda.Agendar("Van para publicar", "jjmontes", DateTime.Now,
 				urlInvitacion);
@@ -120,10 +121,6 @@
 			var evento = eventos[0];
 			
 			Assert.IsNotNull(evento.Ponente);
-			Assert.IsNotNull(evento.Ponente.Eventos);
-			Assert.AreEqual(1, evento.Ponente.Eventos.Count());
-			Assert.AreEqual(evento, evento.Ponente.Eventos.First());
-
 		}
 
 		[Test]
@@ -132,7 +129,7 @@
 			var repository = DefaultEventoRepository;
 			var publicador = new Mock<IPublicador>();
 
-			var agenda = new Agenda(publicador.Object, repository, DefaultPonenteRepository);
+			var agenda = new Agenda(publicador.Object, repository, DefaultPersonaRepository);
 
 			agenda.Agendar("Van para publicar", "jjmontes", DateTime.Now,
 				urlInvitacion);
@@ -165,7 +162,7 @@
 			var publicador2 = new Mock<IPublicador>();
 
 			var agenda = new Agenda(new CompositePublicador(new[] {publicador1.Object, publicador2.Object}),
-															DefaultEventoRepository, DefaultPonenteRepository);
+															DefaultEventoRepository, DefaultPersonaRepository);
 
 			agenda.Agendar("Van para publicar", "jjmontes", DateTime.Now,
 				urlInvitacion);
@@ -185,7 +182,7 @@
 			var publicador2 = new Mock<IPublicador>();
 
 			var agenda = new Agenda(new CompositePublicador(new[] { publicador1.Object, publicador2.Object }),
-															DefaultEventoRepository, DefaultPonenteRepository);
+															DefaultEventoRepository, DefaultPersonaRepository);
 
 			agenda.Proponer("Html 5", "jjmontes", urlInvitacion);
 			Assert.AreEqual(1, agenda.GetEventosPropuestos().Count);
--- a/Agendas/trunk/src/Agendas.Tests/Agendas.Tests.csproj	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Tests/Agendas.Tests.csproj	Sat Jun 04 22:46:06 2011 -0300
@@ -77,6 +77,7 @@
     <Compile Include="Cruds\EventoCrud.cs" />
     <Compile Include="Cruds\EventoCrudNhTests.cs" />
     <Compile Include="DateTimeFormattingTests.cs" />
+    <Compile Include="PersonaServiceTests.cs" />
     <Compile Include="PonentesTests.cs" />
     <Compile Include="PropuestasTests.cs" />
     <Compile Include="Cruds\EventoTests.cs" />
--- a/Agendas/trunk/src/Agendas.Tests/Blog/PublicadorTests.cs	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Tests/Blog/PublicadorTests.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -5,6 +5,7 @@
 using Agendas.Blog;
 using Agendas.Blog.Impl;
 using AltNetHispano.Agendas.Domain;
+using AltNetHispano.Agendas.Repositories.Memory;
 using Moq;
 using NUnit.Framework;
 
@@ -16,10 +17,10 @@
 		[SetUp]
 		public void SetearUsuario()
 		{
-			var seguridad = new Mock<ISeguridad>();
-			seguridad.Setup(s => s.GetUserName()).Returns("neluz");
-			IdentityContext.Current = seguridad.Object;
-		}
+      var seguridad = new Mock<ISeguridad>();
+      seguridad.Setup(s => s.GetUserName()).Returns("neluz");
+      IdentityContext.Init(seguridad.Object, new PersonaRepository());
+    }
 
     [Test]
     public void El_publicador_debe_disparar_los_writers_segun_la_accion_de_cada_track()
--- a/Agendas/trunk/src/Agendas.Tests/Cruds/EventoCrud.cs	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Tests/Cruds/EventoCrud.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -10,12 +10,12 @@
 	{
 		private readonly IEventoRepository _eventoRepository;
 		private readonly Func<IDisposable> _requestEmulator;
-		private readonly IPonenteRepository _ponenteRepository;
+		private readonly IPersonaRepository _personaRepository;
 
-		public EventoCrud(Func<IEventoRepository> createEventoRepository, Func<IPonenteRepository> createPonenteRepository, Func<IDisposable> requestEmulator)
+		public EventoCrud(Func<IEventoRepository> createEventoRepository, Func<IPersonaRepository> createPonenteRepository, Func<IDisposable> requestEmulator)
 		{
 			_eventoRepository = createEventoRepository.Invoke();
-			_ponenteRepository = createPonenteRepository.Invoke();
+			_personaRepository = createPonenteRepository.Invoke();
 
 			_requestEmulator = requestEmulator;
 		}
@@ -24,8 +24,8 @@
 		{
 			using (_requestEmulator.Invoke())
 			{
-				var ponente = new Ponente("Carlos Blé");
-				_ponenteRepository.Save(ponente);
+				var ponente = new Persona("Carlos Blé");
+				_personaRepository.Save(ponente);
 
 				var evento = Evento.Proponer("TDD - Diseño Basado en Ejemplos", ponente, string.Empty);
 				_eventoRepository.Save(evento);
@@ -45,8 +45,6 @@
 				Assert.IsNotNull(van);
 				Assert.IsNotNull(van.Ponente);
 				Assert.AreEqual("Carlos Blé", van.Ponente.Nombre);
-				Assert.AreEqual(1, van.Ponente.Eventos.Count());
-				Assert.AreEqual(van, van.Ponente.Eventos.First());
 
 				var track1 = van.Tracks.Where(t => t.Accion == Accion.Proponer).FirstOrDefault();
 				Assert.IsNotNull(track1);
@@ -68,8 +66,8 @@
 
 				van.CambiarTitulo("TDD & Ejemplos");
 
-				var otroPonente = new Ponente("José");
-				_ponenteRepository.Save(otroPonente);
+				var otroPonente = new Persona("José");
+				_personaRepository.Save(otroPonente);
 
 				van.Actualizar(otroPonente, new DateTime(2010, 04, 17), null);
 
@@ -86,13 +84,11 @@
 				Assert.AreEqual("TDD & Ejemplos", van.Titulo);
 				Assert.AreEqual(new DateTime(2010, 04, 17), van.Fecha);
 
-				var ponente1 = _ponenteRepository.GetByNombre("Carlos Blé");
+				var ponente1 = _personaRepository.GetByNombre("Carlos Blé");
 				Assert.IsNotNull(ponente1);
-				Assert.AreEqual(0, ponente1.Eventos.Count());
 
-				var ponente2 = _ponenteRepository.GetByNombre("José");
+				var ponente2 = _personaRepository.GetByNombre("José");
 				Assert.IsNotNull(ponente2);
-				Assert.AreEqual(1, ponente2.Eventos.Count());
 
 				Assert.AreEqual(1, van.Tracks.Where(t => t.Accion == Accion.Proponer).Count());
 				Assert.AreEqual(1, van.Tracks.Where(t => t.Accion == Accion.Modificar).Count());
@@ -117,10 +113,9 @@
 
 				Assert.IsNull(van);
 
-				var ponente = _ponenteRepository.GetByNombre("Carlos Blé");
+				var ponente = _personaRepository.GetByNombre("Carlos Blé");
 				
 				Assert.IsNotNull(ponente);
-				Assert.AreEqual(0, ponente.Eventos.Count());
 			}
 		}
 	}
--- a/Agendas/trunk/src/Agendas.Tests/Cruds/EventoCrudNhTests.cs	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Tests/Cruds/EventoCrudNhTests.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -31,14 +31,14 @@
 		{
 			var seguridad = new Mock<ISeguridad>();
 			seguridad.Setup(s => s.GetUserName()).Returns("neluz");
-			IdentityContext.Current = seguridad.Object;
+			IdentityContext.Init(seguridad.Object, new PersonaRepository(NhHelper.GetSessionFactory()));
 		}
 
 		[SetUp]
 		public void CreateCrud()
 		{
 			ISessionFactory sessionFactory=NhHelper.GetSessionFactory();
-			_eventoCrud = new EventoCrud(() => new EventoRepository(sessionFactory), () => new PonenteRepository(sessionFactory),
+			_eventoCrud = new EventoCrud(() => new EventoRepository(sessionFactory), () => new PersonaRepository(sessionFactory),
 			                             () => new RequestEmulator(sessionFactory));
 		}
 
--- a/Agendas/trunk/src/Agendas.Tests/Cruds/EventoTests.cs	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Tests/Cruds/EventoTests.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -17,7 +17,7 @@
 		public void BorrarRepositorios()
 		{
 			EventoRepository.Clear();
-			PonenteRepository.Clear();
+			PersonaRepository.Clear();
 		}
 
 		[SetUp]
@@ -25,13 +25,13 @@
 		{
 			var seguridad = new Mock<ISeguridad>();
 			seguridad.Setup(s => s.GetUserName()).Returns("neluz");
-			IdentityContext.Current = seguridad.Object;
+			IdentityContext.Init(seguridad.Object, new PersonaRepository());
 		}
 
 		[SetUp]
 		public void CreateCrud()
 		{
-			_eventoCrud = new EventoCrud(() => new EventoRepository(), ()=>new PonenteRepository(), () => new Mock<IDisposable>().Object);
+			_eventoCrud = new EventoCrud(() => new EventoRepository(), ()=>new PersonaRepository(), () => new Mock<IDisposable>().Object);
 		}
 
 		#endregion
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Agendas/trunk/src/Agendas.Tests/PersonaServiceTests.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -0,0 +1,55 @@
+using System.Linq;
+using AltNetHispano.Agendas.Domain;
+using AltNetHispano.Agendas.Domain.Repositories;
+using AltNetHispano.Agendas.Domain.Services;
+using Moq;
+using NUnit.Framework;
+
+namespace AltNetHispano.Agendas.Tests
+{
+	[TestFixture]
+	public class PersonaServiceTests : TestBase
+	{
+		[Test]
+		public void Crear_nueva_persona()
+		{
+			var personaRepository = new Mock<IPersonaRepository>();
+			Persona persona=null;
+			personaRepository.Setup(r => r.Save(It.IsAny<Persona>())).Callback<Persona>(p => persona = p);
+
+			var personaService = new PersonaService(personaRepository.Object);
+			
+			Assert.IsTrue(personaService.Validate(IdentityProviderEnum.Twitter, "nelopauselli", "Nelo Pauselli"));
+			personaRepository.Verify(r=>r.Save(It.IsAny<Persona>()), Times.Once());
+			Assert.IsNotNull(persona);
+			Assert.AreEqual(1, persona.Cuentas.Count());
+			Assert.AreEqual("nelopauselli", persona.Twitter);
+
+			Cuenta cuenta = persona.Cuentas.First();
+			Assert.AreEqual(IdentityProviderEnum.Twitter, cuenta.IdentityProvider);
+			Assert.AreEqual("nelopauselli", cuenta.LogonName);
+
+			personaRepository.Setup(r => r.GetCuenta(IdentityProviderEnum.Twitter, "nelopauselli")).Returns(cuenta);
+
+			Assert.AreEqual(1, persona.Cuentas.Count());
+			Assert.IsTrue(personaService.Validate(IdentityProviderEnum.Twitter, "nelopauselli", "Nelo Pauselli"));
+			Assert.AreEqual(1, persona.Cuentas.Count());
+		}
+
+		[Test]
+		public void IdentificacionBuiltIn()
+		{
+			var builtIn = new Identification(Identification.Map[(int) IdentityProviderEnum.BuiltIn] + "nelo");
+			Assert.AreEqual(IdentityProviderEnum.BuiltIn, builtIn.IdentityProvider);
+			Assert.AreEqual("nelo", builtIn.LogonName);
+		}
+
+		[Test]
+		public void IdentificacionTwitter()
+		{
+			var builtIn = new Identification(Identification.Map[(int)IdentityProviderEnum.Twitter] + "nelo");
+			Assert.AreEqual(IdentityProviderEnum.Twitter, builtIn.IdentityProvider);
+			Assert.AreEqual("nelo", builtIn.LogonName);
+		}
+	}
+}
\ No newline at end of file
--- a/Agendas/trunk/src/Agendas.Tests/PonentesTests.cs	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Tests/PonentesTests.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -16,8 +16,9 @@
 		public void Publicar_dos_eventos_con_el_mismo_ponente_no_registrado()
 		{
 			var publicador = new Mock<IPublicador>();
+			Assert.AreEqual(1, DefaultPersonaRepository.GetAll().Count);
 
-			var agenda = new Agenda(publicador.Object, DefaultEventoRepository, DefaultPonenteRepository);
+			var agenda = new Agenda(publicador.Object, DefaultEventoRepository, DefaultPersonaRepository);
 
 			agenda.Agendar("Audit (parallel model) con NHibernate 3", "Fabio Maulo", new DateTime(2011, 2, 26), 
 				urlInvitacion);
@@ -29,15 +30,11 @@
 
 			publicador.Verify(p => p.Publicar(It.IsAny<IEnumerable<Track>>()), Times.Exactly(2));
 
-			Assert.AreEqual(1, DefaultPonenteRepository.GetAll().Count);
+			Assert.AreEqual(2, DefaultPersonaRepository.GetAll().Count);
 
 			var evento = eventos[0];
 
 			Assert.IsNotNull(evento.Ponente);
-			var ponente = evento.Ponente;
-			Assert.IsNotNull(evento.Ponente.Eventos);
-			Assert.Contains(eventos[0], new List<Evento>(ponente.Eventos));
-			Assert.Contains(eventos[1], new List<Evento>(ponente.Eventos));
 		}
 
 		[Test]
@@ -48,7 +45,7 @@
 			publicador.Setup(p => p.Publicar(It.IsAny<IEnumerable<Track>>())).Callback<IEnumerable<Track>>(
 				t => evento = t.First().Evento);
 
-			var agenda = new Agenda(publicador.Object, DefaultEventoRepository, DefaultPonenteRepository);
+			var agenda = new Agenda(publicador.Object, DefaultEventoRepository, DefaultPersonaRepository);
 
 			agenda.RegistrarPonente("Fabio Maulo", "fabiomaulo@gmail.com", "@fabiomaulo", "http://fabiomaulo.blogspot.com");
 
@@ -73,7 +70,7 @@
 			publicador.Setup(p => p.Publicar(It.IsAny<IEnumerable<Track>>())).Callback<IEnumerable<Track>>(
 				t => evento = t.First().Evento);
 
-			var agenda = new Agenda(publicador.Object, DefaultEventoRepository, DefaultPonenteRepository);
+			var agenda = new Agenda(publicador.Object, DefaultEventoRepository, DefaultPersonaRepository);
 
 			agenda.Agendar("Audit (parallel model) con NHibernate 3", "Fabio Maulo", new DateTime(2011, 2, 26),
 				urlInvitacion);
@@ -90,13 +87,13 @@
 		[Test]
 		public void Quitar_ponente_de_un_evento()
 		{
-			var agenda = new Agenda(null, DefaultEventoRepository, DefaultPonenteRepository);
+			var agenda = new Agenda(null, DefaultEventoRepository, DefaultPersonaRepository);
 
 			const string titulo = "Audit (parallel model) con NHibernate 3";
 			Guid eventoId;
 
 			agenda.Proponer(titulo, "Fabio", urlInvitacion);
-			Ponente ponente;
+			Persona persona;
 			{
 				var eventos = agenda.GetEventosPropuestos();
 				Assert.AreEqual(1, eventos.Count);
@@ -105,7 +102,7 @@
 				Assert.AreEqual("Fabio", evento.Ponente.Nombre);
 
 				eventoId = evento.Id;
-				ponente = evento.Ponente;
+				persona = evento.Ponente;
 			}
 
 			agenda.ModificarPropuesta(eventoId, titulo, null, urlInvitacion);
@@ -116,20 +113,17 @@
 				Evento evento = eventos[0];
 				Assert.IsNull(evento.Ponente);
 			}
-
-			Assert.AreEqual(0, ponente.Eventos.Count());
 		}
 
 		[Test]
 		public void Cambiar_ponente_de_un_evento()
 		{
-			var agenda = new Agenda(null, DefaultEventoRepository, DefaultPonenteRepository);
+			var agenda = new Agenda(null, DefaultEventoRepository, DefaultPersonaRepository);
 
 			const string titulo = "Audit (parallel model) con NHibernate 3";
 			Guid eventoId;
 
 			agenda.Proponer(titulo, "Fabio", urlInvitacion);
-			Ponente ponente1;
 			{
 				var eventos = agenda.GetEventosPropuestos();
 				Assert.AreEqual(1, eventos.Count);
@@ -138,22 +132,16 @@
 				Assert.AreEqual("Fabio", evento.Ponente.Nombre);
 
 				eventoId = evento.Id;
-				ponente1 = evento.Ponente;
 			}
 
 			agenda.ModificarPropuesta(eventoId, titulo, "José", urlInvitacion);
-			Ponente ponente2;
 			{
 				var eventos = agenda.GetEventosPropuestos();
 				Assert.AreEqual(1, eventos.Count);
 
 				Evento evento = eventos[0];
 				Assert.IsNotNull(evento.Ponente);
-				ponente2 = evento.Ponente;
 			}
-
-			Assert.AreEqual(0, ponente1.Eventos.Count());
-			Assert.AreEqual(1, ponente2.Eventos.Count());
 		}
 	}
 }
\ No newline at end of file
--- a/Agendas/trunk/src/Agendas.Tests/PropuestasTests.cs	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Tests/PropuestasTests.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -17,7 +17,7 @@
 		[Test]
 		public void Crear_modificar_y_publicar_evento_propuesto()
 		{
-			var agenda = new Agenda(null, DefaultEventoRepository, DefaultPonenteRepository);
+			var agenda = new Agenda(null, DefaultEventoRepository, DefaultPersonaRepository);
 
 			agenda.Proponer("Van", null, urlInvitacion);
 			{
@@ -52,7 +52,7 @@
 		[Test]
 		public void Intentar_agendar_evento_propuesto_sin_indicar_fecha()
 		{
-			var agenda = new Agenda(null, DefaultEventoRepository, DefaultPonenteRepository);
+			var agenda = new Agenda(null, DefaultEventoRepository, DefaultPersonaRepository);
 
 			{
 				agenda.Proponer("Van propuesta", null, urlInvitacion);
@@ -70,7 +70,7 @@
 		[Test]
 		public void Intentar_agendar_evento_propuesto_sin_indicar_ponente()
 		{
-			var agenda = new Agenda(null, DefaultEventoRepository, DefaultPonenteRepository);
+			var agenda = new Agenda(null, DefaultEventoRepository, DefaultPersonaRepository);
 
 			{
 				agenda.Proponer("Van propuesta", null, urlInvitacion);
@@ -92,7 +92,7 @@
 		{
 			var idEventoNoExistente = new Guid("99999999999999999999999999999999");
 
-			var agenda = new Agenda(null, DefaultEventoRepository, DefaultPonenteRepository);
+			var agenda = new Agenda(null, DefaultEventoRepository, DefaultPersonaRepository);
 
 			Assert.Throws<EventoNotFoundException>(() => agenda.GetEvento(idEventoNoExistente));
 		}
@@ -102,7 +102,7 @@
 		{
 			var repository = new Mock<IEventoRepository>();
 
-			var agenda = new Agenda(null, repository.Object, DefaultPonenteRepository);
+			var agenda = new Agenda(null, repository.Object, DefaultPersonaRepository);
 
 			var r = agenda.Proponer(string.Empty, null, urlInvitacion);
 			Assert.IsFalse(r.Succeful);
@@ -113,9 +113,9 @@
 		[Test]
 		public void Intentar_proponer_evento_sin_usuario_autenticado()
 		{
-			var agenda = new Agenda(null, DefaultEventoRepository, DefaultPonenteRepository);
+			var agenda = new Agenda(null, DefaultEventoRepository, DefaultPersonaRepository);
 
-			SetCurrentUser(null);
+			SetCurrentUser(null, null);
 
 			Assert.Throws<UsuarioNoAutenticadoException>(() => agenda.Proponer("Inmortalidad de la meduza.", null, urlInvitacion));
 		}
@@ -123,7 +123,7 @@
 		[Test]
 		public void Proponer_evento_correctamente()
 		{
-			var agenda = new Agenda(null, DefaultEventoRepository, DefaultPonenteRepository);
+			var agenda = new Agenda(null, DefaultEventoRepository, DefaultPersonaRepository);
 			agenda.Proponer("Van propuesta", null, urlInvitacion);
 
 			IList<Evento> eventosPropuestos = agenda.GetEventosPropuestos();
@@ -135,7 +135,7 @@
 		[Test]
 		public void Verificar_propuesta_separada_de_publicacion()
 		{
-			var agenda = new Agenda(null, DefaultEventoRepository, DefaultPonenteRepository);
+			var agenda = new Agenda(null, DefaultEventoRepository, DefaultPersonaRepository);
 
 			agenda.Proponer("Van propuesta", null, urlInvitacion);
 			var r = agenda.Agendar("Van publicada", "jjmontes", DateTime.Now,
--- a/Agendas/trunk/src/Agendas.Tests/TestBase.cs	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Tests/TestBase.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -1,5 +1,6 @@
 using AltNetHispano.Agendas.Domain;
 using AltNetHispano.Agendas.Domain.Repositories;
+using AltNetHispano.Agendas.Domain.Services;
 using AltNetHispano.Agendas.Repositories.Memory;
 using Moq;
 using NUnit.Framework;
@@ -13,9 +14,9 @@
 			get { return new EventoRepository(); }
 		}
 
-		protected static IPonenteRepository DefaultPonenteRepository
+		protected static IPersonaRepository DefaultPersonaRepository
 		{
-			get { return new PonenteRepository(); }
+			get { return new PersonaRepository(); }
 		}
 
 		[SetUp]
@@ -27,20 +28,23 @@
 		[SetUp]
 		public void LimpiarPonentes()
 		{
-			PonenteRepository.Clear();
+			PersonaRepository.Clear();
+			SetCurrentUser("nelopauselli", "Nelo Pauselli");
 		}
 
-		[SetUp]
-		public void SetearUsuario()
+		protected static void SetCurrentUser(string username, string nombre)
 		{
-			SetCurrentUser("neluz");
-		}
+			const IdentityProviderEnum identityProvider = IdentityProviderEnum.Twitter;
 
-		protected static void SetCurrentUser(string username)
-		{
 			var seguridad = new Mock<ISeguridad>();
 			seguridad.Setup(s => s.GetUserName()).Returns(username);
-			IdentityContext.Current = seguridad.Object;
+			
+			IPersonaRepository personaRepository = new PersonaRepository();
+			var service = new PersonaService(personaRepository);
+			service.Validate(identityProvider, username, nombre);
+
+			IdentityContext.Init(seguridad.Object, personaRepository);
+
 		}
 	}
 }
\ No newline at end of file
--- a/Agendas/trunk/src/Agendas.Tests/TrackTests.cs	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Tests/TrackTests.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -15,7 +15,7 @@
 		{
 			var repository = DefaultEventoRepository;
 
-			var agenda = new Agenda(null, repository, DefaultPonenteRepository);
+			var agenda = new Agenda(null, repository, DefaultPersonaRepository);
 
 			var fecha = DateTime.Now.AddDays(5);
 			agenda.Agendar("Html 5", "jjmontes", fecha, urlInvitacion);
@@ -25,18 +25,24 @@
 			var ultimo = evento.Tracks.Last();
 			Assert.AreEqual(evento, ultimo.Evento);
 			Assert.AreEqual(Accion.Agendar, ultimo.Accion);
+			Assert.IsNotNull(ultimo.Usuario);
+			Assert.AreEqual("Nelo Pauselli", ultimo.Usuario.Nombre);
 
 			agenda.ModificarEvento(evento.Id, "Html 5", "otro ponente", fecha, urlInvitacion);
 			Assert.AreEqual(2, evento.Tracks.Count());
 			ultimo = evento.Tracks.Last();
 			Assert.AreEqual(evento, ultimo.Evento);
 			Assert.AreEqual(Accion.Modificar, ultimo.Accion);
+			Assert.IsNotNull(ultimo.Usuario);
+			Assert.AreEqual("Nelo Pauselli", ultimo.Usuario.Nombre);
 
 			agenda.ModificarEvento(evento.Id, "Html 5 y Css 3", "otro ponente", fecha, urlInvitacion);
 			Assert.AreEqual(3, evento.Tracks.Count());
 			ultimo = evento.Tracks.Last();
 			Assert.AreEqual(evento, ultimo.Evento);
 			Assert.AreEqual(Accion.CambiarTitulo, ultimo.Accion);
+			Assert.IsNotNull(ultimo.Usuario);
+			Assert.AreEqual("Nelo Pauselli", ultimo.Usuario.Nombre);
 		}
 
 		[Test]
@@ -44,7 +50,7 @@
 		{
 			var repository = DefaultEventoRepository;
 
-			var agenda = new Agenda(null, repository, DefaultPonenteRepository);
+			var agenda = new Agenda(null, repository, DefaultPersonaRepository);
 
 			agenda.Agendar("Html 5", "jjmontes", DateTime.Now,
 				urlInvitacion);
--- a/Agendas/trunk/src/Agendas.Tests/Workflows/Workflow.cs	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Tests/Workflows/Workflow.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -9,9 +9,9 @@
 	{
 		private readonly Agenda _agenda;
 
-		public Workflow(IPublicador publicador, IEventoRepository eventoRepository, IPonenteRepository ponenteRepository)
+		public Workflow(IPublicador publicador, IEventoRepository eventoRepository, IPersonaRepository personaRepository)
 		{
-			_agenda = new Agenda(publicador, eventoRepository, ponenteRepository);
+			_agenda = new Agenda(publicador, eventoRepository, personaRepository);
 		}
 
 		public void Proponer()
--- a/Agendas/trunk/src/Agendas.Tests/Workflows/WorkflowTests.cs	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Tests/Workflows/WorkflowTests.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -16,7 +16,7 @@
 		public void InitPublicador()
 		{
 			_publicador = new Mock<IPublicador>();
-			_workflow = new Workflow(_publicador.Object, DefaultEventoRepository, DefaultPonenteRepository);
+			_workflow = new Workflow(_publicador.Object, DefaultEventoRepository, DefaultPersonaRepository);
 		}
 
 		[Test]
--- a/Agendas/trunk/src/Agendas.Twitter/Agendas.Twitter.csproj	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Twitter/Agendas.Twitter.csproj	Sat Jun 04 22:46:06 2011 -0300
@@ -32,7 +32,9 @@
   </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />
+    <Reference Include="System.Configuration" />
     <Reference Include="System.Core" />
+    <Reference Include="System.Web" />
     <Reference Include="System.Xml.Linq" />
     <Reference Include="System.Data.DataSetExtensions" />
     <Reference Include="Microsoft.CSharp" />
@@ -40,6 +42,8 @@
     <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="oAuth.cs" />
+    <Compile Include="oAuthTwitter.cs" />
     <Compile Include="TwitterPublicador.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
   </ItemGroup>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Agendas/trunk/src/Agendas.Twitter/oAuth.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -0,0 +1,373 @@
+using System;
+using System.Security.Cryptography;
+using System.Collections.Generic;
+using System.Text;
+using System.Web;
+
+namespace AltNetHispano.Agendas.Twitter
+{
+    public class OAuthBase
+    {
+
+        /// <summary>
+        /// Provides a predefined set of algorithms that are supported officially by the protocol
+        /// </summary>
+        public enum SignatureTypes
+        {
+            HMACSHA1,
+            PLAINTEXT,
+            RSASHA1
+        }
+
+        /// <summary>
+        /// Provides an internal structure to sort the query parameter
+        /// </summary>
+        protected class QueryParameter
+        {
+            private string name = null;
+            private string value = null;
+
+            public QueryParameter(string name, string value)
+            {
+                this.name = name;
+                this.value = value;
+            }
+
+            public string Name
+            {
+                get { return name; }
+            }
+
+            public string Value
+            {
+                get { return value; }
+            }
+        }
+
+        /// <summary>
+        /// Comparer class used to perform the sorting of the query parameters
+        /// </summary>
+        protected class QueryParameterComparer : IComparer<QueryParameter>
+        {
+
+            #region IComparer<QueryParameter> Members
+
+            public int Compare(QueryParameter x, QueryParameter y)
+            {
+                if (x.Name == y.Name)
+                {
+                    return string.Compare(x.Value, y.Value);
+                }
+                else
+                {
+                    return string.Compare(x.Name, y.Name);
+                }
+            }
+
+            #endregion
+        }
+
+        protected const string OAuthVersion = "1.0";
+        protected const string OAuthParameterPrefix = "oauth_";
+
+        //
+        // List of know and used oauth parameters' names
+        //        
+        protected const string OAuthConsumerKeyKey = "oauth_consumer_key";
+        protected const string OAuthCallbackKey = "oauth_callback";
+        protected const string OAuthVersionKey = "oauth_version";
+        protected const string OAuthSignatureMethodKey = "oauth_signature_method";
+        protected const string OAuthSignatureKey = "oauth_signature";
+        protected const string OAuthTimestampKey = "oauth_timestamp";
+        protected const string OAuthNonceKey = "oauth_nonce";
+        protected const string OAuthTokenKey = "oauth_token";
+        protected const string OAuthTokenSecretKey = "oauth_token_secret";
+        protected const string OAuthVerifierKey = "oauth_verifier";
+
+        protected const string HMACSHA1SignatureType = "HMAC-SHA1";
+        protected const string PlainTextSignatureType = "PLAINTEXT";
+        protected const string RSASHA1SignatureType = "RSA-SHA1";
+
+        protected Random random = new Random();
+
+        protected string unreservedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";
+
+        /// <summary>
+        /// Helper function to compute a hash value
+        /// </summary>
+        /// <param name="hashAlgorithm">The hashing algoirhtm used. If that algorithm needs some initialization, like HMAC and its derivatives, they should be initialized prior to passing it to this function</param>
+        /// <param name="data">The data to hash</param>
+        /// <returns>a Base64 string of the hash value</returns>
+        private string ComputeHash(HashAlgorithm hashAlgorithm, string data)
+        {
+            if (hashAlgorithm == null)
+            {
+                throw new ArgumentNullException("hashAlgorithm");
+            }
+
+            if (string.IsNullOrEmpty(data))
+            {
+                throw new ArgumentNullException("data");
+            }
+
+            byte[] dataBuffer = System.Text.Encoding.ASCII.GetBytes(data);
+            byte[] hashBytes = hashAlgorithm.ComputeHash(dataBuffer);
+
+            return Convert.ToBase64String(hashBytes);
+        }
+
+        /// <summary>
+        /// Internal function to cut out all non oauth query string parameters (all parameters not begining with "oauth_")
+        /// </summary>
+        /// <param name="parameters">The query string part of the Url</param>
+        /// <returns>A list of QueryParameter each containing the parameter name and value</returns>
+        private List<QueryParameter> GetQueryParameters(string parameters)
+        {
+            if (parameters.StartsWith("?"))
+            {
+                parameters = parameters.Remove(0, 1);
+            }
+
+            List<QueryParameter> result = new List<QueryParameter>();
+
+            if (!string.IsNullOrEmpty(parameters))
+            {
+                string[] p = parameters.Split('&');
+                foreach (string s in p)
+                {
+                    if (!string.IsNullOrEmpty(s) && !s.StartsWith(OAuthParameterPrefix))
+                    {
+                        if (s.IndexOf('=') > -1)
+                        {
+                            string[] temp = s.Split('=');
+                            result.Add(new QueryParameter(temp[0], temp[1]));
+                        }
+                        else
+                        {
+                            result.Add(new QueryParameter(s, string.Empty));
+                        }
+                    }
+                }
+            }
+
+            return result;
+        }
+
+        /// <summary>
+        /// This is a different Url Encode implementation since the default .NET one outputs the percent encoding in lower case.
+        /// While this is not a problem with the percent encoding spec, it is used in upper case throughout OAuth
+        /// </summary>
+        /// <param name="value">The value to Url encode</param>
+        /// <returns>Returns a Url encoded string</returns>
+        public string UrlEncode(string value)
+        {
+            StringBuilder result = new StringBuilder();
+
+            foreach (char symbol in value)
+            {
+                if (unreservedChars.IndexOf(symbol) != -1)
+                {
+                    result.Append(symbol);
+                }
+                else
+                {
+                    result.Append('%' + String.Format("{0:X2}", (int)symbol));
+                }
+            }
+
+            return result.ToString();
+        }
+
+        /// <summary>
+        /// Normalizes the request parameters according to the spec
+        /// </summary>
+        /// <param name="parameters">The list of parameters already sorted</param>
+        /// <returns>a string representing the normalized parameters</returns>
+        protected string NormalizeRequestParameters(IList<QueryParameter> parameters)
+        {
+            StringBuilder sb = new StringBuilder();
+            QueryParameter p = null;
+            for (int i = 0; i < parameters.Count; i++)
+            {
+                p = parameters[i];
+                sb.AppendFormat("{0}={1}", p.Name, p.Value);
+
+                if (i < parameters.Count - 1)
+                {
+                    sb.Append("&");
+                }
+            }
+
+            return sb.ToString();
+        }
+
+        /// <summary>
+        /// Generate the signature base that is used to produce the signature
+        /// </summary>
+        /// <param name="url">The full url that needs to be signed including its non OAuth url parameters</param>
+        /// <param name="consumerKey">The consumer key</param>        
+        /// <param name="token">The token, if available. If not available pass null or an empty string</param>
+        /// <param name="tokenSecret">The token secret, if available. If not available pass null or an empty string</param>
+        /// <param name="callBackUrl">The callback URL (for OAuth 1.0a).If your client cannot accept callbacks, the value MUST be 'oob' </param>
+        /// <param name="oauthVerifier">This value MUST be included when exchanging Request Tokens for Access Tokens. Otherwise pass a null or an empty string</param>
+        /// <param name="httpMethod">The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc)</param>
+        /// <param name="signatureType">The signature type. To use the default values use <see cref="OAuthBase.SignatureTypes">OAuthBase.SignatureTypes</see>.</param>
+        /// <returns>The signature base</returns>
+        public string GenerateSignatureBase(Uri url, string consumerKey, string token, string tokenSecret, string callBackUrl, string oauthVerifier, string httpMethod, string timeStamp, string nonce, string signatureType, out string normalizedUrl, out string normalizedRequestParameters)
+        {
+            if (token == null)
+            {
+                token = string.Empty;
+            }
+
+            if (tokenSecret == null)
+            {
+                tokenSecret = string.Empty;
+            }
+
+            if (string.IsNullOrEmpty(consumerKey))
+            {
+                throw new ArgumentNullException("consumerKey");
+            }
+
+            if (string.IsNullOrEmpty(httpMethod))
+            {
+                throw new ArgumentNullException("httpMethod");
+            }
+
+            if (string.IsNullOrEmpty(signatureType))
+            {
+                throw new ArgumentNullException("signatureType");
+            }
+
+            normalizedUrl = null;
+            normalizedRequestParameters = null;
+
+            List<QueryParameter> parameters = GetQueryParameters(url.Query);
+            parameters.Add(new QueryParameter(OAuthVersionKey, OAuthVersion));
+            parameters.Add(new QueryParameter(OAuthNonceKey, nonce));
+            parameters.Add(new QueryParameter(OAuthTimestampKey, timeStamp));
+            parameters.Add(new QueryParameter(OAuthSignatureMethodKey, signatureType));
+            parameters.Add(new QueryParameter(OAuthConsumerKeyKey, consumerKey));
+
+            if (!string.IsNullOrEmpty(callBackUrl))
+            {
+                parameters.Add(new QueryParameter(OAuthCallbackKey, UrlEncode(callBackUrl)));
+            }
+
+
+            if (!string.IsNullOrEmpty(oauthVerifier))
+            {
+                parameters.Add(new QueryParameter(OAuthVerifierKey, oauthVerifier));
+            }
+
+            if (!string.IsNullOrEmpty(token))
+            {
+                parameters.Add(new QueryParameter(OAuthTokenKey, token));
+            }
+
+            parameters.Sort(new QueryParameterComparer());
+
+            normalizedUrl = string.Format("{0}://{1}", url.Scheme, url.Host);
+            if (!((url.Scheme == "http" && url.Port == 80) || (url.Scheme == "https" && url.Port == 443)))
+            {
+                normalizedUrl += ":" + url.Port;
+            }
+            normalizedUrl += url.AbsolutePath;
+            normalizedRequestParameters = NormalizeRequestParameters(parameters);
+
+            StringBuilder signatureBase = new StringBuilder();
+            signatureBase.AppendFormat("{0}&", httpMethod.ToUpper());
+            signatureBase.AppendFormat("{0}&", UrlEncode(normalizedUrl));
+            signatureBase.AppendFormat("{0}", UrlEncode(normalizedRequestParameters));
+
+            return signatureBase.ToString();
+        }
+
+        /// <summary>
+        /// Generate the signature value based on the given signature base and hash algorithm
+        /// </summary>
+        /// <param name="signatureBase">The signature based as produced by the GenerateSignatureBase method or by any other means</param>
+        /// <param name="hash">The hash algorithm used to perform the hashing. If the hashing algorithm requires initialization or a key it should be set prior to calling this method</param>
+        /// <returns>A base64 string of the hash value</returns>
+        public string GenerateSignatureUsingHash(string signatureBase, HashAlgorithm hash)
+        {
+            return ComputeHash(hash, signatureBase);
+        }
+
+        /// <summary>
+        /// Generates a signature using the HMAC-SHA1 algorithm
+        /// </summary>		
+        /// <param name="url">The full url that needs to be signed including its non OAuth url parameters</param>
+        /// <param name="consumerKey">The consumer key</param>
+        /// <param name="consumerSecret">The consumer seceret</param>
+        /// <param name="token">The token, if available. If not available pass null or an empty string</param>
+        /// <param name="tokenSecret">The token secret, if available. If not available pass null or an empty string</param>
+        /// <param name="callBackUrl">The callback URL (for OAuth 1.0a).If your client cannot accept callbacks, the value MUST be 'oob' </param>
+        /// <param name="oauthVerifier">This value MUST be included when exchanging Request Tokens for Access Tokens. Otherwise pass a null or an empty string</param>
+        /// <param name="httpMethod">The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc)</param>
+        /// <returns>A base64 string of the hash value</returns>
+        public string GenerateSignature(Uri url, string consumerKey, string consumerSecret, string token, string tokenSecret, string callBackUrl, string oauthVerifier, string httpMethod, string timeStamp, string nonce, out string normalizedUrl, out string normalizedRequestParameters)
+        {
+            return GenerateSignature(url, consumerKey, consumerSecret, token, tokenSecret, callBackUrl, oauthVerifier, httpMethod, timeStamp, nonce, SignatureTypes.HMACSHA1, out normalizedUrl, out normalizedRequestParameters);
+        }
+
+        /// <summary>
+        /// Generates a signature using the specified signatureType 
+        /// </summary>		
+        /// <param name="url">The full url that needs to be signed including its non OAuth url parameters</param>
+        /// <param name="consumerKey">The consumer key</param>
+        /// <param name="consumerSecret">The consumer seceret</param>
+        /// <param name="token">The token, if available. If not available pass null or an empty string</param>
+        /// <param name="tokenSecret">The token secret, if available. If not available pass null or an empty string</param>
+        /// <param name="callBackUrl">The callback URL (for OAuth 1.0a).If your client cannot accept callbacks, the value MUST be 'oob' </param>
+        /// <param name="oauthVerifier">This value MUST be included when exchanging Request Tokens for Access Tokens. Otherwise pass a null or an empty string</param>
+        /// <param name="httpMethod">The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc)</param>
+        /// <param name="signatureType">The type of signature to use</param>
+        /// <returns>A base64 string of the hash value</returns>
+        public string GenerateSignature(Uri url, string consumerKey, string consumerSecret, string token, string tokenSecret, string callBackUrl, string oauthVerifier, string httpMethod, string timeStamp, string nonce, SignatureTypes signatureType, out string normalizedUrl, out string normalizedRequestParameters)
+        {
+            normalizedUrl = null;
+            normalizedRequestParameters = null;
+
+            switch (signatureType)
+            {
+                case SignatureTypes.PLAINTEXT:
+                    return HttpUtility.UrlEncode(string.Format("{0}&{1}", consumerSecret, tokenSecret));
+                case SignatureTypes.HMACSHA1:
+                    string signatureBase = GenerateSignatureBase(url, consumerKey, token, tokenSecret, callBackUrl, oauthVerifier, httpMethod, timeStamp, nonce, HMACSHA1SignatureType, out normalizedUrl, out normalizedRequestParameters);
+
+                    HMACSHA1 hmacsha1 = new HMACSHA1();
+                    hmacsha1.Key = Encoding.ASCII.GetBytes(string.Format("{0}&{1}", UrlEncode(consumerSecret), string.IsNullOrEmpty(tokenSecret) ? "" : UrlEncode(tokenSecret)));
+
+                    return GenerateSignatureUsingHash(signatureBase, hmacsha1);
+                case SignatureTypes.RSASHA1:
+                    throw new NotImplementedException();
+                default:
+                    throw new ArgumentException("Unknown signature type", "signatureType");
+            }
+        }
+
+        /// <summary>
+        /// Generate the timestamp for the signature        
+        /// </summary>
+        /// <returns></returns>
+        public virtual string GenerateTimeStamp()
+        {
+            // Default implementation of UNIX time of the current UTC time
+            TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
+            return Convert.ToInt64(ts.TotalSeconds).ToString();
+        }
+
+        /// <summary>
+        /// Generate a nonce
+        /// </summary>
+        /// <returns></returns>
+        public virtual string GenerateNonce()
+        {
+            // Just a simple implementation of a random number between 123400 and 9999999
+            return random.Next(123400, 9999999).ToString();
+        }
+
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Agendas/trunk/src/Agendas.Twitter/oAuthTwitter.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -0,0 +1,257 @@
+using System;
+using System.Configuration;
+using System.Web;
+using System.Net;
+using System.IO;
+using System.Collections.Specialized;
+
+namespace AltNetHispano.Agendas.Twitter
+{
+    public class OAuthTwitter : OAuthBase
+    {
+        public enum Method { GET, POST, DELETE };
+        public const string REQUEST_TOKEN = "http://twitter.com/oauth/request_token";
+        public const string AUTHORIZE = "http://twitter.com/oauth/authorize";
+        public const string ACCESS_TOKEN = "http://twitter.com/oauth/access_token";
+
+        private string _consumerKey = "";
+        private string _consumerSecret = "";
+        private string _token = "";
+        private string _tokenSecret = "";
+        private string _callBackUrl = "oob";
+        private string _oauthVerifier = "";
+
+    	private string ConsumerKey 
+        {
+            get
+            {
+                if (_consumerKey.Length == 0)
+                {
+                    _consumerKey = ConfigurationManager.AppSettings["consumerKey"];
+                }
+                return _consumerKey; 
+            }
+        }
+
+    	private string ConsumerSecret { 
+            get {
+                if (_consumerSecret.Length == 0)
+                {
+                    _consumerSecret = ConfigurationManager.AppSettings["consumerSecret"];
+                }
+                return _consumerSecret; 
+            } 
+        }
+
+        public string Token { get { return _token; } set { _token = value; } }
+        public string TokenSecret { get { return _tokenSecret; } set { _tokenSecret = value; } }
+        public string CallBackUrl { get { return _callBackUrl; } set { _callBackUrl = value; } }
+        public string OAuthVerifier { get { return _oauthVerifier; } set { _oauthVerifier = value; } }
+
+        /// <summary>
+        /// Get the link to Twitter's authorization page for this application.
+        /// </summary>
+        /// <returns>The url with a valid request token, or a null string.</returns>
+        public string AuthorizationLinkGet()
+        {
+            string ret = null;
+
+            string response = OAuthWebRequest(Method.GET, REQUEST_TOKEN, String.Empty);
+            if (response.Length > 0)
+            {
+                //response contains token and token secret.  We only need the token.
+                NameValueCollection qs = HttpUtility.ParseQueryString(response);
+
+                if (qs["oauth_callback_confirmed"] != null)
+                {
+                    if (qs["oauth_callback_confirmed"] != "true")
+                    {
+                        throw new Exception("OAuth callback not confirmed.");
+                    }
+                }
+
+                if (qs[OAuthTokenKey] != null)
+                {
+					ret = AUTHORIZE + "?" + OAuthTokenKey + "=" + qs[OAuthTokenKey];
+                }
+            }
+            return ret;
+        }
+
+        /// <summary>
+        /// Exchange the request token for an access token.
+        /// </summary>
+        /// <param name="authToken">The oauth_token is supplied by Twitter's authorization page following the callback.</param>
+        /// <param name="oauthVerifier">An oauth_verifier parameter is provided to the client either in the pre-configured callback URL</param>
+        public void AccessTokenGet(string authToken, string oauthVerifier)
+        {
+            Token = authToken;
+            OAuthVerifier = oauthVerifier;
+
+            string response = OAuthWebRequest(Method.GET, ACCESS_TOKEN, String.Empty);
+
+            if (response.Length > 0)
+            {
+                //Store the Token and Token Secret
+                NameValueCollection qs = HttpUtility.ParseQueryString(response);
+                if (qs[OAuthTokenKey] != null)
+                {
+					Token = qs[OAuthTokenKey];
+                }
+				if (qs[OAuthTokenSecretKey] != null)
+                {
+					TokenSecret = qs[OAuthTokenSecretKey];
+                }
+            }
+        }
+        
+        /// <summary>
+        /// Submit a web request using oAuth.
+        /// </summary>
+        /// <param name="method">GET or POST</param>
+        /// <param name="url">The full url, including the querystring.</param>
+        /// <param name="postData">Data to post (querystring format)</param>
+        /// <returns>The web server response.</returns>
+        public string OAuthWebRequest(Method method, string url, string postData)
+        {
+            string outUrl;
+            string querystring;
+            string ret;
+
+
+            //Setup postData for signing.
+            //Add the postData to the querystring.
+            if (method == Method.POST || method == Method.DELETE)
+            {
+                if (postData.Length > 0)
+                {
+                    //Decode the parameters and re-encode using the oAuth UrlEncode method.
+                    NameValueCollection qs = HttpUtility.ParseQueryString(postData);
+                    postData = "";
+                    foreach (string key in qs.AllKeys)
+                    {
+                        if (postData.Length > 0)
+                        {
+                            postData += "&";
+                        }
+                        qs[key] = HttpUtility.UrlDecode(qs[key]);
+                        qs[key] = UrlEncode(qs[key]);
+                        postData += key + "=" + qs[key];
+
+                    }
+                    if (url.IndexOf("?") > 0)
+                    {
+                        url += "&";
+                    }
+                    else
+                    {
+                        url += "?";
+                    }
+                    url += postData;
+                }
+            }
+
+            var uri = new Uri(url);
+            
+            string nonce = GenerateNonce();
+            string timeStamp = GenerateTimeStamp();
+
+            //Generate Signature
+            string sig = GenerateSignature(uri,
+                ConsumerKey,
+                ConsumerSecret,
+                Token,
+                TokenSecret,
+                CallBackUrl,
+                OAuthVerifier,
+                method.ToString(),
+                timeStamp,
+                nonce,
+                out outUrl,
+                out querystring);
+
+        	querystring += "&" + OAuthSignatureKey + "=" + UrlEncode(sig);
+
+            //Convert the querystring to postData
+            if (method == Method.POST || method == Method.DELETE)
+            {
+                postData = querystring;
+                querystring = "";
+            }
+
+            if (querystring.Length > 0)
+            {
+                outUrl += "?";
+            }
+
+            ret = WebRequest(method, outUrl +  querystring, postData);
+
+            return ret;
+        }
+
+        /// <summary>
+        /// Web Request Wrapper
+        /// </summary>
+        /// <param name="method">Http Method</param>
+        /// <param name="url">Full url to the web resource</param>
+        /// <param name="postData">Data to post in querystring format</param>
+        /// <returns>The web server response.</returns>
+        public string WebRequest(Method method, string url, string postData)
+        {
+            HttpWebRequest webRequest;
+            StreamWriter requestWriter;
+            string responseData;
+
+            webRequest = System.Net.WebRequest.Create(url) as HttpWebRequest;
+            webRequest.Method = method.ToString();
+            webRequest.ServicePoint.Expect100Continue = false;
+            //webRequest.UserAgent  = "Identify your application please.";
+            //webRequest.Timeout = 20000;
+
+            if (method == Method.POST || method == Method.DELETE)
+            {
+                webRequest.ContentType = "application/x-www-form-urlencoded";
+
+                //POST the data.
+                requestWriter = new StreamWriter(webRequest.GetRequestStream());
+                try
+                {
+                    requestWriter.Write(postData);
+                }
+                finally
+                {
+                    requestWriter.Close();
+                }
+            }
+
+            responseData = WebResponseGet(webRequest);
+
+        	return responseData;
+
+        }
+
+        /// <summary>
+        /// Process the web response.
+        /// </summary>
+        /// <param name="webRequest">The request object.</param>
+        /// <returns>The response data.</returns>
+        public string WebResponseGet(HttpWebRequest webRequest)
+        {
+            StreamReader responseReader = null;
+            string responseData;
+
+            try
+            {
+                responseReader = new StreamReader(webRequest.GetResponse().GetResponseStream());
+                responseData = responseReader.ReadToEnd();
+            }
+            finally
+            {
+                webRequest.GetResponse().GetResponseStream().Close();
+                responseReader.Close();
+            }
+
+            return responseData;
+        }
+    }
+}
--- a/Agendas/trunk/src/Agendas.Web/Agendas.Web.csproj	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Web/Agendas.Web.csproj	Sat Jun 04 22:46:06 2011 -0300
@@ -72,14 +72,23 @@
     <Compile Include="CustomModelMetadataProvider\DataAnnotationAndConventionModelMetadataProvider.cs" />
     <Compile Include="CustomModelMetadataProvider\IResolverByConvention.cs" />
     <Compile Include="CustomModelMetadataProvider\ResolverThroughResource.cs" />
+    <Compile Include="IdentityHelper.cs" />
     <Compile Include="Models\AccountModels.cs" />
     <Compile Include="Models\EventoModel.cs" />
     <Compile Include="Models\PropuestaModel.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Services\AccountMembershipService.cs" />
+    <Compile Include="Services\FormsAuthenticationService.cs" />
+    <Compile Include="Services\IFormsAuthenticationService.cs" />
+    <Compile Include="Services\IMembershipService.cs" />
   </ItemGroup>
   <ItemGroup>
     <Content Include="Content\altnetlogo.png" />
     <Content Include="Content\question.jpg" />
+    <Content Include="Content\twitter\sign-in-with-twitter-d-sm.png" />
+    <Content Include="Content\twitter\sign-in-with-twitter-d.png" />
+    <Content Include="Content\twitter\sign-in-with-twitter-l-sm.png" />
+    <Content Include="Content\twitter\sign-in-with-twitter-l.png" />
     <Content Include="Global.asax" />
     <Content Include="hibernate.cfg.xml">
       <CopyToOutputDirectory>Always</CopyToOutputDirectory>
@@ -164,6 +173,10 @@
       <Project>{444D25FB-FC1C-48DC-9EAD-D4C78F2A10CA}</Project>
       <Name>Agendas.Resources</Name>
     </ProjectReference>
+    <ProjectReference Include="..\Agendas.Twitter\Agendas.Twitter.csproj">
+      <Project>{D8DA7ED2-A274-4DF6-9DD2-A9DC65F4C2F5}</Project>
+      <Name>Agendas.Twitter</Name>
+    </ProjectReference>
   </ItemGroup>
   <ItemGroup>
     <Content Include="Views\Shared\_Menu.cshtml" />
@@ -193,7 +206,7 @@
       <FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
         <WebProjectProperties>
           <UseIIS>False</UseIIS>
-          <AutoAssignPort>True</AutoAssignPort>
+          <AutoAssignPort>False</AutoAssignPort>
           <DevelopmentServerPort>1424</DevelopmentServerPort>
           <DevelopmentServerVPath>/</DevelopmentServerVPath>
           <IISUrl>
Binary file Agendas/trunk/src/Agendas.Web/Content/twitter/sign-in-with-twitter-d-sm.png has changed
Binary file Agendas/trunk/src/Agendas.Web/Content/twitter/sign-in-with-twitter-d.png has changed
Binary file Agendas/trunk/src/Agendas.Web/Content/twitter/sign-in-with-twitter-l-sm.png has changed
Binary file Agendas/trunk/src/Agendas.Web/Content/twitter/sign-in-with-twitter-l.png has changed
--- a/Agendas/trunk/src/Agendas.Web/Controllers/AccountController.cs	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Web/Controllers/AccountController.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -1,7 +1,12 @@
-using System.Web.Mvc;
+using System;
+using System.Web.Mvc;
 using System.Web.Routing;
 using System.Web.Security;
+using AltNetHispano.Agendas.Domain;
+using AltNetHispano.Agendas.Factories;
+using AltNetHispano.Agendas.Twitter;
 using AltNetHispano.Agendas.Web.Models;
+using AltNetHispano.Agendas.Web.Services;
 
 namespace AltNetHispano.Agendas.Web.Controllers
 {
@@ -33,29 +38,89 @@
         {
             if (ModelState.IsValid)
             {
-                if (MembershipService.ValidateUser(model.UserName, model.Password))
-                {
-                    FormsService.SignIn(model.UserName, model.RememberMe);
-                    if (Url.IsLocalUrl(returnUrl))
-                    {
-                        return Redirect(returnUrl);
-                    }
-                    else
-                    {
-                        return RedirectToAction("Index", "Home");
-                    }
-                }
-                else
-                {
-                    ModelState.AddModelError("", "The user name or password provided is incorrect.");
-                }
+				if (MembershipService.ValidateUser(model.UserName, model.Password))
+				{
+					var personaService = AgendaFactory.GetPersonaService();
+					if (personaService.Validate(IdentityProviderEnum.BuiltIn, model.UserName))
+					{
+						FormsService.SignIn(Identification.Map[(int)IdentityProviderEnum.BuiltIn] + model.UserName, model.RememberMe);
+						if (Url.IsLocalUrl(returnUrl))
+							return Redirect(returnUrl);
+						return RedirectToAction("Index", "Home");
+					}
+				}
+            	ModelState.AddModelError("", "The user name or password provided is incorrect.");
             }
 
             // If we got this far, something failed, redisplay form
             return View(model);
         }
 
-        // **************************************
+		public ActionResult TwitterLogOn()
+		{
+			var oAuth = new OAuthTwitter();
+
+			if (Request["oauth_token"] == null)
+			{
+				//Redirect the user to Twitter for authorization.
+				//Using oauth_callback for local testing.
+				var action = Url.Action("TwitterLogOn");
+				var url = Request.Url.Scheme + "://" + Request.Url.Host +
+				          (Request.Url.Port != 80 ? ":" + Request.Url.Port : string.Empty) + action;
+
+				oAuth.CallBackUrl = url;
+				Response.Redirect(oAuth.AuthorizationLinkGet());
+			}
+			else
+			{
+				//Get the access token and secret.
+				oAuth.AccessTokenGet(Request["oauth_token"], Request["oauth_verifier"]);
+				if (oAuth.TokenSecret.Length > 0)
+				{
+					//We now have the credentials, so make a call to the Twitter API.
+					const string url = "http://twitter.com/account/verify_credentials.xml";
+					string xml = oAuth.OAuthWebRequest(OAuthTwitter.Method.GET, url, String.Empty);
+
+					var username = GetXmlContent(xml, "screen_name");
+					var nombre = GetXmlContent(xml, "name");
+
+					var personaService = AgendaFactory.GetPersonaService();
+					if (personaService.Validate(IdentityProviderEnum.Twitter, username, nombre))
+					{
+						FormsService.SignIn(Identification.Map[(int)IdentityProviderEnum.Twitter]+username, false);
+						return RedirectToAction("Index", "Home");
+					}
+					ModelState.AddModelError("", "The user name or password provided is incorrect.");
+
+
+					//POST Test
+					//url = "http://twitter.com/statuses/update.xml";
+					//xml = oAuth.oAuthWebRequest(oAuthTwitter.Method.POST, url, "status=" + oAuth.UrlEncode("Hello @swhitley - Testing the .NET oAuth API"));
+					//apiResponse.InnerHtml = Server.HtmlEncode(xml);
+				}
+			}
+
+			return RedirectToAction("LogOn");
+		}
+
+    	private static string GetXmlContent(string xml, string tagName)
+    	{
+    		var tagStart = "<" + tagName + ">";
+    		var tagEnd = "</" + tagName + ">";
+
+    		var start = xml.IndexOf(tagStart)+tagStart.Length;
+			if (start>tagStart.Length)
+			{
+				var end = xml.IndexOf(tagEnd, start);
+				if (end>0)
+				{
+					return xml.Substring(start, end - start);
+				}
+			}
+    		return string.Empty;
+    	}
+
+    	// **************************************
         // URL: /Account/LogOff
         // **************************************
 
@@ -86,13 +151,14 @@
 
                 if (createStatus == MembershipCreateStatus.Success)
                 {
-                    FormsService.SignIn(model.UserName, false /* createPersistentCookie */);
-                    return RedirectToAction("Index", "Home");
+					var personaService = AgendaFactory.GetPersonaService();
+					if (personaService.Validate(IdentityProviderEnum.BuiltIn, model.UserName, model.Nombre))
+					{
+						FormsService.SignIn(Identification.Map[(int)IdentityProviderEnum.BuiltIn] + model.UserName, false);
+						return RedirectToAction("Index", "Home");
+					}
                 }
-                else
-                {
-                    ModelState.AddModelError("", AccountValidation.ErrorCodeToString(createStatus));
-                }
+                ModelState.AddModelError("", AccountValidation.ErrorCodeToString(createStatus));
             }
 
             // If we got this far, something failed, redisplay form
--- a/Agendas/trunk/src/Agendas.Web/Global.asax.cs	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Web/Global.asax.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -41,7 +41,7 @@
             RegisterGlobalFilters(GlobalFilters.Filters);
             RegisterRoutes(RouteTable.Routes);
 
-            IdentityContext.Current = new HttpContextIdentityProvider();
+            IdentityContext.Init(new HttpContextIdentityProvider(), AgendaFactory.GetPersonaRepository());
         }
     }
 }
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Agendas/trunk/src/Agendas.Web/IdentityHelper.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -0,0 +1,19 @@
+using System.Web;
+using System.Web.Mvc;
+using AltNetHispano.Agendas.Domain;
+
+namespace AltNetHispano.Agendas.Web
+{
+	public static class IdentityHelper
+	{
+		public static string DisplayName(this HtmlHelper<dynamic> helper)
+		{
+			var personaRepository = Factories.AgendaFactory.GetPersonaRepository();
+			
+			var identification = new Identification(HttpContext.Current.User.Identity.Name);
+
+			var c = personaRepository.GetCuenta(identification.IdentityProvider, identification.LogonName);
+			return c == null ? identification.LogonName : c.Persona != null ? c.Persona.Nombre : c.LogonName;
+		}
+	}
+}
\ No newline at end of file
--- a/Agendas/trunk/src/Agendas.Web/Models/AccountModels.cs	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Web/Models/AccountModels.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -47,7 +47,10 @@
 
     public class RegisterModel
     {
-        [Required]
+		[Required]
+		public string Nombre { get; set; }
+		
+		[Required]
         [Display(Name = "User name")]
         public string UserName { get; set; }
 
@@ -69,109 +72,7 @@
     }
     #endregion
 
-    #region Services
-    // The FormsAuthentication type is sealed and contains static members, so it is difficult to
-    // unit test code that calls its members. The interface and helper class below demonstrate
-    // how to create an abstract wrapper around such a type in order to make the AccountController
-    // code unit testable.
-
-    public interface IMembershipService
-    {
-        int MinPasswordLength { get; }
-
-        bool ValidateUser(string userName, string password);
-        MembershipCreateStatus CreateUser(string userName, string password, string email);
-        bool ChangePassword(string userName, string oldPassword, string newPassword);
-    }
-
-    public class AccountMembershipService : IMembershipService
-    {
-        private readonly MembershipProvider _provider;
-
-        public AccountMembershipService()
-            : this(null)
-        {
-        }
-
-        public AccountMembershipService(MembershipProvider provider)
-        {
-            _provider = provider ?? Membership.Provider;
-        }
-
-        public int MinPasswordLength
-        {
-            get
-            {
-                return _provider.MinRequiredPasswordLength;
-            }
-        }
-
-        public bool ValidateUser(string userName, string password)
-        {
-            if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");
-            if (String.IsNullOrEmpty(password)) throw new ArgumentException("Value cannot be null or empty.", "password");
-
-            return _provider.ValidateUser(userName, password);
-        }
-
-        public MembershipCreateStatus CreateUser(string userName, string password, string email)
-        {
-            if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");
-            if (String.IsNullOrEmpty(password)) throw new ArgumentException("Value cannot be null or empty.", "password");
-            if (String.IsNullOrEmpty(email)) throw new ArgumentException("Value cannot be null or empty.", "email");
-
-            MembershipCreateStatus status;
-            _provider.CreateUser(userName, password, email, null, null, true, null, out status);
-            return status;
-        }
-
-        public bool ChangePassword(string userName, string oldPassword, string newPassword)
-        {
-            if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");
-            if (String.IsNullOrEmpty(oldPassword)) throw new ArgumentException("Value cannot be null or empty.", "oldPassword");
-            if (String.IsNullOrEmpty(newPassword)) throw new ArgumentException("Value cannot be null or empty.", "newPassword");
-
-            // The underlying ChangePassword() will throw an exception rather
-            // than return false in certain failure scenarios.
-            try
-            {
-                MembershipUser currentUser = _provider.GetUser(userName, true /* userIsOnline */);
-                return currentUser.ChangePassword(oldPassword, newPassword);
-            }
-            catch (ArgumentException)
-            {
-                return false;
-            }
-            catch (MembershipPasswordException)
-            {
-                return false;
-            }
-        }
-    }
-
-    public interface IFormsAuthenticationService
-    {
-        void SignIn(string userName, bool createPersistentCookie);
-        void SignOut();
-    }
-
-    public class FormsAuthenticationService : IFormsAuthenticationService
-    {
-        public void SignIn(string userName, bool createPersistentCookie)
-        {
-            if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");
-
-            FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
-        }
-
-        public void SignOut()
-        {
-            FormsAuthentication.SignOut();
-        }
-    }
-    #endregion
-
-    #region Validation
+	#region Validation
     public static class AccountValidation
     {
         public static string ErrorCodeToString(MembershipCreateStatus createStatus)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Agendas/trunk/src/Agendas.Web/Services/AccountMembershipService.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -0,0 +1,70 @@
+using System;
+using System.Web.Security;
+
+namespace AltNetHispano.Agendas.Web.Services
+{
+	public class AccountMembershipService : IMembershipService
+	{
+		private readonly MembershipProvider _provider;
+
+		public AccountMembershipService()
+			: this(null)
+		{
+		}
+
+		public AccountMembershipService(MembershipProvider provider)
+		{
+			_provider = provider ?? Membership.Provider;
+		}
+
+		public int MinPasswordLength
+		{
+			get
+			{
+				return _provider.MinRequiredPasswordLength;
+			}
+		}
+
+		public bool ValidateUser(string userName, string password)
+		{
+			if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");
+			if (String.IsNullOrEmpty(password)) throw new ArgumentException("Value cannot be null or empty.", "password");
+
+			return _provider.ValidateUser(userName, password);
+		}
+
+		public MembershipCreateStatus CreateUser(string userName, string password, string email)
+		{
+			if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");
+			if (String.IsNullOrEmpty(password)) throw new ArgumentException("Value cannot be null or empty.", "password");
+			if (String.IsNullOrEmpty(email)) throw new ArgumentException("Value cannot be null or empty.", "email");
+
+			MembershipCreateStatus status;
+			_provider.CreateUser(userName, password, email, null, null, true, null, out status);
+			return status;
+		}
+
+		public bool ChangePassword(string userName, string oldPassword, string newPassword)
+		{
+			if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");
+			if (String.IsNullOrEmpty(oldPassword)) throw new ArgumentException("Value cannot be null or empty.", "oldPassword");
+			if (String.IsNullOrEmpty(newPassword)) throw new ArgumentException("Value cannot be null or empty.", "newPassword");
+
+			// The underlying ChangePassword() will throw an exception rather
+			// than return false in certain failure scenarios.
+			try
+			{
+				MembershipUser currentUser = _provider.GetUser(userName, true /* userIsOnline */);
+				return currentUser.ChangePassword(oldPassword, newPassword);
+			}
+			catch (ArgumentException)
+			{
+				return false;
+			}
+			catch (MembershipPasswordException)
+			{
+				return false;
+			}
+		}
+	}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Agendas/trunk/src/Agendas.Web/Services/FormsAuthenticationService.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -0,0 +1,20 @@
+using System;
+using System.Web.Security;
+
+namespace AltNetHispano.Agendas.Web.Services
+{
+	public class FormsAuthenticationService : IFormsAuthenticationService
+	{
+		public void SignIn(string userName, bool createPersistentCookie)
+		{
+			if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");
+
+			FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
+		}
+
+		public void SignOut()
+		{
+			FormsAuthentication.SignOut();
+		}
+	}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Agendas/trunk/src/Agendas.Web/Services/IFormsAuthenticationService.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -0,0 +1,8 @@
+namespace AltNetHispano.Agendas.Web.Services
+{
+	public interface IFormsAuthenticationService
+	{
+		void SignIn(string userName, bool createPersistentCookie);
+		void SignOut();
+	}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Agendas/trunk/src/Agendas.Web/Services/IMembershipService.cs	Sat Jun 04 22:46:06 2011 -0300
@@ -0,0 +1,13 @@
+using System.Web.Security;
+
+namespace AltNetHispano.Agendas.Web.Services
+{
+	public interface IMembershipService
+	{
+		int MinPasswordLength { get; }
+
+		bool ValidateUser(string userName, string password);
+		MembershipCreateStatus CreateUser(string userName, string password, string email);
+		bool ChangePassword(string userName, string oldPassword, string newPassword);
+	}
+}
\ No newline at end of file
--- a/Agendas/trunk/src/Agendas.Web/Views/Account/LogOn.cshtml	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Web/Views/Account/LogOn.cshtml	Sat Jun 04 22:46:06 2011 -0300
@@ -41,3 +41,4 @@
         </fieldset>
     </div>
 }
+<a href="@Url.Action("TwitterLogOn")"><img src="@Url.Content("~/Content/twitter/sign-in-with-twitter-d.png")" alt="Sign in with twitter" /></a>
--- a/Agendas/trunk/src/Agendas.Web/Views/Account/Register.cshtml	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Web/Views/Account/Register.cshtml	Sat Jun 04 22:46:06 2011 -0300
@@ -12,45 +12,8 @@
 
 @using (Html.BeginForm()) {
     @Html.ValidationSummary(true, "Account creation was unsuccessful. Please correct the errors and try again.")
-    <div>
-        <fieldset>
-            <legend>Account Information</legend>
-
-            <div class="editor-label">
-                @Html.LabelFor(m => m.UserName)
-            </div>
-            <div class="editor-field">
-                @Html.TextBoxFor(m => m.UserName)
-                @Html.ValidationMessageFor(m => m.UserName)
-            </div>
-
-            <div class="editor-label">
-                @Html.LabelFor(m => m.Email)
-            </div>
-            <div class="editor-field">
-                @Html.TextBoxFor(m => m.Email)
-                @Html.ValidationMessageFor(m => m.Email)
-            </div>
-
-            <div class="editor-label">
-                @Html.LabelFor(m => m.Password)
-            </div>
-            <div class="editor-field">
-                @Html.PasswordFor(m => m.Password)
-                @Html.ValidationMessageFor(m => m.Password)
-            </div>
-
-            <div class="editor-label">
-                @Html.LabelFor(m => m.ConfirmPassword)
-            </div>
-            <div class="editor-field">
-                @Html.PasswordFor(m => m.ConfirmPassword)
-                @Html.ValidationMessageFor(m => m.ConfirmPassword)
-            </div>
-
-            <p>
-                <input type="submit" value="Register" />
-            </p>
-        </fieldset>
-    </div>
+    @Html.EditorForModel()
+    <p>
+        <input type="submit" value="Register" />
+    </p>
 }
--- a/Agendas/trunk/src/Agendas.Web/Views/Shared/_LogOnPartial.cshtml	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Web/Views/Shared/_LogOnPartial.cshtml	Sat Jun 04 22:46:06 2011 -0300
@@ -1,5 +1,5 @@
 @if(Request.IsAuthenticated) {
-    <text>Welcome <b>@Context.User.Identity.Name</b>!
+    <text>Welcome <b>@Html.DisplayName()</b>!
     [ @Html.ActionLink("Log Off", "LogOff", "Account") ]</text>
 }
 else {
--- a/Agendas/trunk/src/Agendas.Web/Views/Web.config	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Web/Views/Web.config	Sat Jun 04 22:46:06 2011 -0300
@@ -16,6 +16,7 @@
 				<add namespace="System.Web.Mvc.Ajax" />
 				<add namespace="System.Web.Mvc.Html" />
 				<add namespace="System.Web.Routing" />
+				<add namespace="AltNetHispano.Agendas.Web"/>
 			</namespaces>
 		</pages>
 	</system.web.webPages.razor>
--- a/Agendas/trunk/src/Agendas.Web/Web.config	Sat Jun 04 22:33:05 2011 -0300
+++ b/Agendas/trunk/src/Agendas.Web/Web.config	Sat Jun 04 22:46:06 2011 -0300
@@ -6,20 +6,20 @@
 
 <configuration>
 	<connectionStrings>
-		<add name="ApplicationServices"
-			 connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true"
-			 providerName="System.Data.SqlClient" />
+		<add name="ApplicationServices" connectionString="Data Source=.\sqlexpress;Initial Catalog=AltNetHispano;Integrated Security=SSPI" providerName="System.Data.SqlClient" />
 		<add name="AltNetHispano" connectionString="Data Source=.\sqlexpress;Initial Catalog=AltNetHispano;Integrated Security=SSPI"/>
-
 	</connectionStrings>
 
 	<appSettings>
+		<add key="consumerKey" value="uUSzC5EZ2LCNoBmaQXcUFA"/>
+		<add key="consumerSecret" value="CNN2uUwza4nGNoZf3FlAqC0xoUEKXKFWxZn3Frow"/>
+		
 		<add key="ClientValidationEnabled" value="true"/>
 		<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
 	</appSettings>
 
 	<system.web>
-		<customErrors mode="On"/>
+		<customErrors mode="Off"/>
 		<compilation debug="true" targetFramework="4.0">
 			<assemblies>
 				<add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />