Link del día: Mockups iPhone, funcionales!

Acordándome de cuando hablé de Create Free iPhone Apps, quería charlar sobre esta aplicación web llamada Mokk.me, una aplicación que nos permite visualmente construir elementos de un diseño gráfico para iPhone y personalizar esos elementos a gusto.

Cabe aclarar que todo este sistema funciona en nuestra máquina, quizá la única funcionalidad del servidor es guardar URLs de diseños en particular para luego poder modificarlos. El UI es bastante completo y fácil de utilizar, prácticamente todo se puede hacer con un click, así que es una experiencia muy intuitiva.

Mejor aún, del resultado que tengamos, sólo tenemos que visitar un link para verificar en nuestro iPhone cómo se ve, y luego de eso ya podemos guardar ese HTML para utilizarlo en nuestro producto. Más simple, quizá sólo sea la otra aplicación, que hace todo por nosotros.

Soy un zorrinito mobile.

(Read more →)

Link del día: Linux en tu explorador

De los creadores de Compiladores en línea y de Más compiladores online y compitiendo con Máquinas virtuales en la web nos llega un linux que podemos correr en nuestro navegador. Emulado. En JavaScript.

Así como suena, este logro interesante llamado JavaScript PC Emulator parece correr totalmente del lado del cliente, con unos 150 Ks de memoria (aunque no lo he verificado).

Parece que este es otro de los proyectos de Fabrice Bellard, aunque no el único. Si no les suena el nombre de este muchacho, él es quién comenzó qemu, ffmpeg, lzexe… palabras grandes.

En fin, la versión de Linux en el navegador parece ser totalmente funcional, aunque no tiene mucho en él, y no me he puesto a investigar de qué forma podrían extenderse sus capabilidades, tampoco he visto que detecte dispositivos de comunicación más allá del clipboard (que tenemos disponible como un textbox en la página), como para poder instalar algo. Aún así… interesante, no?

Si leemos las notas técnicas al respecto veremos otros detalles y el hecho de que esto comenzó como pura diversión, pero que fácilmente podrían encontrársele algunas aplicaciones útiles. Cómo que no! Un sistema operativo corriendo en el navegador!

Soy un zorrinito asombrado.

(Read more →)

Link del día: Herramientas para CSS3

Desde hace rato que tengo ganas de hacer algo en CSS3, y no dudo que muchos de ustedes también. La gran pregunta que todos debemos tener es “Cómo empezar?”. El qué hacer es un tema aparte, pero el cómo hacerlo es algo que seguramente podemos solucionar con algunas herramientas.

Algunas de estas herramientas están enumeradas en el artículo de Cats Who Code llamada 10 Useful tools for CSS3 development. En este pequeño artículo nos cuentan de distintas herramientas (todas muy visuales y atractivas) que nos permiten usar características de CSS3 en Internet Explorer, crear estilos desde cero, o trabajar con características en particular, como sombreados, refactorización, border-radius.

Hay dos en particular que más que herramientas son un algo especial. Una de ellas es Modernizr, del cual me sorprende no haber comentado antes: sin ningún tipo de JavaScript hace uso de selectores para utilizar condicionalmente características que pueden estar soportadas o no. (Aunque yo prefiero Selectivizr que emula esos comportamientos.),

El otro de ellos es simplemente una página de referencia, que es el HTML5 & CSS3 browser support. Su nombre ya es bastante indicativo de qué contenido tiene.

Y como extra, y como no podía ser de otra manera, un CSS3 Cheat sheet.

Soy un zorrinito CSS3.

(Read more →)

Link del día: Img to CSS

Del cajón de las utilidades curiosas y técnicas que algún día puede que utilicemos, llega la aplicación web llamada Img to CSS, que, como su nombre lo indica, transforma imágenes en CSS.

En el fondo no es taaaaaaaan verdad, porque termina generando tablas con celdas de un píxel (o más, si se trata del mismo color) que con CSS inline genera los colores para la imagen. Esto puede ser interesante para enviar en mails, y que los destinatarios puedan ver imágenes sin necesidad de aceptar la bajada de imágenes. Sin embargo, no se puede utilizar en todas las formas que usamos las imágenes comunes.

Soy un zorrinito curioso.

(Read more →)

Link del día: Performance HTML5, CSS3 y DOM, Parte 2: Performance HTML5

Hay varias formas de aproximarse a la performance de una aplicación que está construida bajo los nuevos estándares e implementaciones de HTML5, CSS3, y, por supuesto, JavaScript. Como ya lo habíamos mencionado en la parte 1 de este artículo, el tutorial de HTML5 Rocks llamado Improving the performance of your HTML5 App trata varios puntos que son importantes para lograr una buena performance y una aplicación sólida.

Repasémoslos rápidamente:

  • Delegar animaciones al browser siempre que sea posible
    • Transformaciones y trancisiones con instrucciones CSS
    • Renderización “incentivada” a ser a través del GPU con -webkit-transform: translateZ(0);
    • Separación de threads de animación con window.requestAnimationFrame
  • Profiling de JavaScript
    • Utilizar el DOM lo menos posible
    • Nombrar funciones anónimas para identificar dónde están los problemas más fácilmente
    • Refactorizar el código
    • Crear funciones definidas y autollamadas si hacer métodos más pequeños no se puede (de esa forma “nombramos” parte del código)
  • Utilización del DOM
    • Lo menos posible (nuevamente)
    • Cachear elementos cuando tenga sentido
    • Hacer lecturas, luego modificaciones, luego escrituras para evitar reflows
    • No usar el DOM dentro de loops
  • Inicialización tardía
    • Delegación de eventos en lugar de asociación de handlers

Y eso es, a muy grandes rasgos, este artículo. Realmente recomiendo que le den una leída a fondo si ustedes trabajan del lado de la web. Realmente no verán una página de la misma manera.

Soy un zorrinito performante.

(Read more →)

Link del día: John Resig, más personal

Hace un tiempo Josh Resig, famoso por ser el creador de JQuery, anunció en su cuenta de Twitter que estaría brindando una especie de “conferencia de prensa”, una rueda de preguntas y respuestas en Reddit, específicamente en este thread.

Ahí podemos conocer un poco más de lo que él piensa sobre distintos temas, desde que le encantan los memes del Rage Guy hasta las propuestas que estaba pensando en plantear para la reestructuración del estándar del DOM. Podemos saber qué máquina usa y qué software hasta qué cosas estudió en la universidad y cómo hace para concentrarse en su trabajo.

Es el tipo de oportunidad de tener a un caso de éxito bien cerca para preguntar y escuchar respuestas. Desafortunadamente me enteré de esto cuando ya había ocurrido, pero habría estado interesante poder hacer un par de preguntas, no?

Soy un zorrinito preguntón.

(Read more →)

Link del día: Humane.js

Vamos a seguir con otra librería JavaScript, en este caso, Humane.js. Esta en particular se trata de un sistema de envío de mensajes al usuario a través de ventanas que recuerdan mucho al lightbox, sin ser tan intrusivas, ni difíciles de utilizar en el código. Estos mensajitos automáticamente se van luego de un tiempo o podemos esperar a que el usuario realice alguna acción (para asegurarnos de que haya efectivamente leído nuestro mensaje).

Para ser sincero, no lo veo como algo revolucionario o ni siquiera nuevo, pero desde la simplicidad de su utilización hasta lo simple de su implementación (la cual podemos ver en GitHub), hace que sea ideal para implementar en dispositivos en donde el espacio es importante, y la simplicidad del mensaje también. Yo creo que uno de estos se vería muy bien en aplicaciones móviles.

Soy un zorrinito simple.

(Read more →)

Crear sitios de SharePoint programáticamente

El objetivo

Estuve peleando estos días con la posibilidad de crear sitios de SharePoint programáticamente desde mi código. Sin embargo, aún cuando esto fue hecho para un proyecto interno, tomé tanto de la internet que realmente siento que debo devolver algo a la comunidad. Además, no voy a divulgar nada relacionado al proyecto, así que aquí vamos.

Primero, buscando en internet una forma de crear estos sitios programáticamente, me topé con un artículo del 2007 de WinSmarts.com, llamado Programatically create a Sharepoint site based on a site definition. Aquí está el código de ese post:

public static bool CreateSite(string parentSiteURL, string siteURLRequested,
    string siteTitle, string siteTemplateName)
{
    bool returnCondition = false; // Assume failure.
    const Int32 LOCALE_ID_ENGLISH = 1033;
    using (SPSite siteCollection = new SPSite(parentSiteURL))
    {
        SPWeb parentWeb = siteCollection.OpenWeb();
        SPWebTemplateCollection Templates = siteCollection.GetWebTemplates(Convert.ToUInt32(LOCALE_ID_ENGLISH));
        SPWebTemplate siteTemplate = Templates[siteTemplateName];
        if (parentWeb.Webs[siteURLRequested].Exists)
        {
            parentWeb.Webs.Delete(siteURLRequested);
        }
        parentWeb.Webs.Add(siteURLRequested, siteTitle, "", Convert.ToUInt32(LOCALE_ID_ENGLISH), siteTemplate, false, false);

        // All is good?
        returnCondition = true;
    }
    return returnCondition;
}

Lo que yo quería cambiar de esta solución era:

  • Los Locales estaban hardcodeados
  • Los Site templates estaban hardcodeados
  • La descripción del sitio estaba hardcodeado (esa es la cadena vacía en la llamada a webs.add)

Removiendo literales…

Entonces fui a revisar la lista de locales: SharePoint Locale ID (LCID) Table, y una lista de los identificadores de site templates, en un artículo llamado Create a site programmatically in moss.

Bueno… aquí vamos (cuidado, vienen enumeraciones largas):

public enum SPLocales: uint
{
	Afrikaans /*af*/ = 1078,
	Albanian /*sq*/ = 1052,
	Arabic_UnitedArabEmirates /*ar-ae*/ = 14337,
	Arabic_Bahrain /*ar-bh*/ = 15361,
	Arabic_Algeria /*ar-dz*/ = 5121,
	Arabic_Egypt /*ar-eg*/ = 3073,
	Arabic_Iraq /*ar-iq*/ = 2049,
	Arabic_Jordan /*ar-jo*/ = 11265,
	Arabic_Kuwait /*ar-kw*/ = 13313,
	Arabic_Lebanon /*ar-lb*/ = 12289,
	Arabic_Libya /*ar-ly*/ = 4097,
	Arabic_Morocco /*ar-ma*/ = 6145,
	Arabic_Oman /*ar-om*/ = 8193,
	Arabic_Qatar /*ar-qa*/ = 16385,
	Arabic_SaudiArabia /*ar-sa*/ = 1025,
	Arabic_Syria /*ar-sy*/ = 10241,
	Arabic_Tunisia /*ar-tn*/ = 7169,
	Arabic_Yemen /*ar-ye*/ = 9217,
	Armenian /*hy*/ = 1067,
	Azeri_Latin /*az-az*/ = 1068,
	Azeri_Cyrillic /*az-az*/ = 2092,
	Basque /*eu*/ = 1069,
	Belarusian /*be*/ = 1059,
	Bulgarian /*bg*/ = 1026,
	Catalan /*ca*/ = 1027,
	Chinese_China /*zh-cn*/ = 2052,
	Chinese_HongKongSAR /*zh-hk*/ = 3076,
	Chinese_MacauSAR /*zh-mo*/ = 5124,
	Chinese_Singapore /*zh-sg*/ = 4100,
	Chinese_Taiwan /*zh-tw*/ = 1028,
	Croatian /*hr*/ = 1050,
	Czech /*cs*/ = 1029,
	Danish /*da*/ = 1030,
	Dutch_TheNetherlands /*nl-nl*/ = 1043,
	Dutch_Belgium /*nl-be*/ = 2067,
	English_Australia /*en-au*/ = 3081,
	English_Belize /*en-bz*/ = 10249,
	English_Canada /*en-ca*/ = 4105,
	English_Caribbean /*en-cb*/ = 9225,
	English_Ireland /*en-ie*/ = 6153,
	English_Jamaica /*en-jm*/ = 8201,
	English_NewZealand /*en-nz*/ = 5129,
	English_Phillippines /*en-ph*/ = 13321,
	English_SouthAfrica /*en-za*/ = 7177,
	English_Trinidad /*en-tt*/ = 11273,
	English_UnitedKingdom /*en-gb*/ = 2057,
	English_UnitedStates /*en-us*/ = 1033,
	Estonian /*et*/ = 1061,
	Farsi /*fa*/ = 1065,
	Finnish /*fi*/ = 1035,
	Faroese /*fo*/ = 1080,
	French_France /*fr-fr*/ = 1036,
	French_Belgium /*fr-be*/ = 2060,
	French_Canada /*fr-ca*/ = 3084,
	French_Luxembourg /*fr-lu*/ = 5132,
	French_Switzerland /*fr-ch*/ = 4108,
	Gaelic_Ireland /*gd-ie*/ = 2108,
	Gaelic_Scotland /*gd*/ = 1084,
	German_Germany /*de-de*/ = 1031,
	German_Austria /*de-at*/ = 3079,
	German_Liechtenstein /*de-li*/ = 5127,
	German_Luxembourg /*de-lu*/ = 4103,
	German_Switzerland /*de-ch*/ = 2055,
	Greek /*el*/ = 1032,
	Hebrew /*he*/ = 1037,
	Hindi /*hi*/ = 1081,
	Hungarian /*hu*/ = 1038,
	Icelandic /*is*/ = 1039,
	Indonesian /*id*/ = 1057,
	Italian_Italy /*it-it*/ = 1040,
	Italian_Switzerland /*it-ch*/ = 2064,
	Japanese /*ja*/ = 1041,
	Korean /*ko*/ = 1042,
	Latvian /*lv*/ = 1062,
	Lithuanian /*lt*/ = 1063,
	FYRO_Macedonian /*mk*/ = 1071,
	Malay_Malaysia /*ms-my*/ = 1086,
	Malay_Brunei /*ms-bn*/ = 2110,
	Maltese /*mt*/ = 1082,
	Marathi /*mr*/ = 1102,
	Norwegian_Bokmal /*no-no*/ = 1044,
	Norwegian_Nynorsk /*no-no*/ = 2068,
	Polish /*pl*/ = 1045,
	Portuguese_Portugal /*pt-pt*/ = 2070,
	Portuguese_Brazil /*pt-br*/ = 1046,
	Raeto_Romance /*rm*/ = 1047,
	Romanian_Romania /*ro*/ = 1048,
	Romanian_RepublicOfMoldova /*ro-mo*/ = 2072,
	Russian /*ru*/ = 1049,
	Russian_RepublicOfMoldova /*ru-mo*/ = 2073,
	Sanskrit /*sa*/ = 1103,
	Serbian_Cyrillic /*sr-sp*/ = 3098,
	Serbian_Latin /*sr-sp*/ = 2074,
	Setsuana /*tn*/ = 1074,
	Slovenian /*sl*/ = 1060,
	Slovak /*sk*/ = 1051,
	Sorbian /*sb*/ = 1070,
	Spanish_Spain /*es-es*/ = 1034,
	Spanish_Argentina /*es-ar*/ = 11274,
	Spanish_Bolivia /*es-bo*/ = 16394,
	Spanish_Chile /*es-cl*/ = 13322,
	Spanish_Colombia /*es-co*/ = 9226,
	Spanish_CostaRica /*es-cr*/ = 5130,
	Spanish_DominicanRepublic /*es-do*/ = 7178,
	Spanish_Ecuador /*es-ec*/ = 12298,
	Spanish_Guatemala /*es-gt*/ = 4106,
	Spanish_Honduras /*es-hn*/ = 18442,
	Spanish_Mexico /*es-mx*/ = 2058,
	Spanish_Nicaragua /*es-ni*/ = 19466,
	Spanish_Panama /*es-pa*/ = 6154,
	Spanish_Peru /*es-pe*/ = 10250,
	Spanish_PuertoRico /*es-pr*/ = 20490,
	Spanish_Paraguay /*es-py*/ = 15370,
	Spanish_ElSalvador /*es-sv*/ = 17418,
	Spanish_Uruguay /*es-uy*/ = 14346,
	Spanish_Venezuela /*es-ve*/ = 8202,
	Sutu /*sx*/ = 1072,
	Swahili /*sw*/ = 1089,
	Swedish_Sweden /*sv-se*/ = 1053,
	Swedish_Finland /*sv-fi*/ = 2077,
	Tamil /*ta*/ = 1097,
	Tatar /*tt*/ = 1092,
	Thai /*th*/ = 1054,
	Turkish /*tr*/ = 1055,
	Tsonga /*ts*/ = 1073,
	Ukrainian /*uk*/ = 1058,
	Urdu /*ur*/ = 1056,
	Uzbek_Cyrillic /*uz-uz*/ = 2115,
	Uzbek_Latin /*uz-uz*/ = 1091,
	Vietnamese /*vi*/ = 1066,
	Xhosa /*xh*/ = 1076,
	Yiddish /*yi*/ = 1085,
	Zulu /*zu*/ = 1077
}

public enum SPSiteTemplates
{
	TeamSite,
	BlankSite,
	DocumentWorkspace,
	BasicMeetingWorkspace,
	BlankMeetingWorkspace,
	DecisionMeetingWorkspace,
	SocialMeetingWorkspace,
	MultiPageMeetingWorkspace,
	Wiki,
	Blog,
	///
<summary>
	/// A central document management location for an enterprise
	/// </summary>

	DocumentCenter,
	///
<summary>
	/// A central location in which records managers can define routes for incoming files
	/// </summary>

	RecordsCenter1,
	RecordsCenter2,
	PublishingSite,
	///
<summary>
	/// A site for publishing web pages on a schedule with workflow features enabled
	/// </summary>

	PublishingSite2,
	PressReleasesSite,
	///
<summary>
	/// A publishing site for web pages using approval workflows
	/// </summary>

	PublishingSiteWithWorkflow,
	///
<summary>
	/// A site for publishing news and articles
	/// </summary>

	NewsSite,
	///
<summary>
	/// A site for creating, managing, and delivering web pages, dashboards, and Key Performance Indicators (KPIs)
	/// </summary>

	ReportCenter,
	///
<summary>
	/// A starter hierarchy for an intranet divisional portal
	/// </summary>

	SPSPortal,
	///
<summary>
	/// A profile site that includes page layouts with zones
	/// </summary>

	ProfileSite,
	///
<summary>
	/// A site collection preconfigured for revision-controlled, secure content creation and publication
	/// </summary>

	PublishingPortal,
	///
<summary>
	/// Keep in mind that only one of these can be provisioned per Shared Services Provider
	/// </summary>

	MySiteHost,
	///
<summary>
	/// A site designed to deliver the search query and results experience
	/// </summary>

	SearchCenter,
	///
<summary>
	/// A superset of the previous; does not appear in navigation bars
	/// </summary>

	SearchCenter2
}

Un poco de magia enum

Lo que nos queda es:

  • Convertir enums SPLocales a su representación correspondiente en uint
  • Convertir enums SPSiteTemplates a su representación en cadena correspondientes
  • Evitar intentar crear sitios duplicados

Para convertir enums SPLocales a su representación uint, sólo tenemos que hacer el casting correspondiente. Esta es la razón por la que hicimos al enum heredar de unit y setear el valor apropiado en cada elemento.

Para poder convertir enums SPSiteTemplates a cadenas, el método común es utilizar atributos y reflexión, pero personalmente no me gusta esa forma, así que hice una con diccionarios y métodos de extensión. La idea es proveer al enum la posibilidad de convertirse en una cadena, y estas cadenas de ser parseadas en el enum nuevamente. Para tener este tipo de relación en ambos sentidos, usé la implementación de **BiDictionaryOneToOne ** del usuario de StackOverflow’s Joel in Go, en una pregunta de sobre cómo hacer un diccionario buscable key-value y buscable value-key: Bidirectional 1 to 1 Dictionary in C#.

public static class SPSiteTemplateMethodExtension
{
	private const string SP_SITE_TEMPLATE_TEAM_SITE = "STS#0";
	private const string SP_SITE_TEMPLATE_BLANK_SITE = "STS#1";
	private const string SP_SITE_TEMPLATE_DOCUMENT_WORKSPACE = "STS#2";
	private const string SP_SITE_TEMPLATE_BASIC_MEETING_WORKSPACE = "MPS#0";
	private const string SP_SITE_TEMPLATE_BLANK_MEETING_WORKSPACE = "MPS#1";
	private const string SP_SITE_TEMPLATE_DECISION_MEETING_WORKSPACE = "MPS#2";
	private const string SP_SITE_TEMPLATE_SOCIAL_MEETING_WORKSPACE = "MPS#3";
	private const string SP_SITE_TEMPLATE_MULTI_PAGE_MEETING_WORKSPACE = "MPS#4";
	private const string SP_SITE_TEMPLATE_WIKI = "WIKI#0";
	private const string SP_SITE_TEMPLATE_BLOG = "BLOG#0";
	private const string SP_SITE_TEMPLATE_DOCUMENT_CENTER = "BDR#0";
	private const string SP_SITE_TEMPLATE_RECORDS_CENTER_1 = "OFFILE#0";
	private const string SP_SITE_TEMPLATE_RECORDS_CENTER_2 = "OFFILE#1";
	private const string SP_SITE_TEMPLATE_PUBLISHING_SITE = "CMSPUBLISHING#0";
	private const string SP_SITE_TEMPLATE_PUBLISHING_SITE_2 = "BLANKINTERNET#0";
	private const string SP_SITE_TEMPLATE_PRESS_RELEASES_SITE = "BLANKINTERNET#1";
	private const string SP_SITE_TEMPLATE_PUBLISHING_SITE_WITH_WORKFLOW = "BLANKINTERNET#2";
	private const string SP_SITE_TEMPLATE_NEWS_SITE = "SPSNHOME#0";
	private const string SP_SITE_TEMPLATE_REPORT_CENTER = "SPSREPORTCENTER#0";
	private const string SP_SITE_TEMPLATE_SPS_PORTAL = "SPSPORTAL#0";
	private const string SP_SITE_TEMPLATE_PROFILE_SITE = "PROFILES#0";
	private const string SP_SITE_TEMPLATE_PUBLISHING_PORTAL = "BLANKINTERNETCONTAINER#0";
	private const string SP_SITE_TEMPLATE_MY_SITE_HOST = "SPSMYSITEHOST#0";
	private const string SP_SITE_TEMPLATE_SEARCH_CENTER = "SRCHCENTERLITE#0";
	private const string SP_SITE_TEMPLATE_SEARCH_CENTER_2 = "SRCHCENTERLITE#1";

	private static BiDictionaryOneToOne<SPSiteTemplates, string> _templates;
	private static BiDictionaryOneToOne<SPSiteTemplates, string> Templates {
		get {
			if (_templates == null) {
				_templates = new BiDictionaryOneToOne<SPSiteTemplates, string>();
				_templates.Add(SPSiteTemplates.BasicMeetingWorkspace, SP_SITE_TEMPLATE_BASIC_MEETING_WORKSPACE);
				_templates.Add(SPSiteTemplates.BlankMeetingWorkspace, SP_SITE_TEMPLATE_BLANK_MEETING_WORKSPACE);
				_templates.Add(SPSiteTemplates.BlankSite , SP_SITE_TEMPLATE_BLANK_SITE);
				_templates.Add(SPSiteTemplates.Blog , SP_SITE_TEMPLATE_BLOG);
				_templates.Add(SPSiteTemplates.DecisionMeetingWorkspace , SP_SITE_TEMPLATE_DECISION_MEETING_WORKSPACE);
				_templates.Add(SPSiteTemplates.DocumentCenter , SP_SITE_TEMPLATE_DOCUMENT_CENTER);
				_templates.Add(SPSiteTemplates.DocumentWorkspace , SP_SITE_TEMPLATE_DOCUMENT_WORKSPACE);
				_templates.Add(SPSiteTemplates.MultiPageMeetingWorkspace , SP_SITE_TEMPLATE_MULTI_PAGE_MEETING_WORKSPACE);
				_templates.Add(SPSiteTemplates.MySiteHost , SP_SITE_TEMPLATE_MY_SITE_HOST);
				_templates.Add(SPSiteTemplates.NewsSite , SP_SITE_TEMPLATE_NEWS_SITE);
				_templates.Add(SPSiteTemplates.PressReleasesSite , SP_SITE_TEMPLATE_PRESS_RELEASES_SITE);
				_templates.Add(SPSiteTemplates.ProfileSite , SP_SITE_TEMPLATE_PROFILE_SITE);
				_templates.Add(SPSiteTemplates.PublishingPortal , SP_SITE_TEMPLATE_PUBLISHING_PORTAL);
				_templates.Add(SPSiteTemplates.PublishingSite , SP_SITE_TEMPLATE_PUBLISHING_SITE);
				_templates.Add(SPSiteTemplates.PublishingSite2 , SP_SITE_TEMPLATE_PUBLISHING_SITE_2);
				_templates.Add(SPSiteTemplates.PublishingSiteWithWorkflow , SP_SITE_TEMPLATE_PUBLISHING_SITE_WITH_WORKFLOW);
				_templates.Add(SPSiteTemplates.RecordsCenter1 , SP_SITE_TEMPLATE_RECORDS_CENTER_1);
				_templates.Add(SPSiteTemplates.RecordsCenter2 , SP_SITE_TEMPLATE_RECORDS_CENTER_2);
				_templates.Add(SPSiteTemplates.ReportCenter , SP_SITE_TEMPLATE_REPORT_CENTER);
				_templates.Add(SPSiteTemplates.SearchCenter , SP_SITE_TEMPLATE_SEARCH_CENTER);
				_templates.Add(SPSiteTemplates.SearchCenter2 , SP_SITE_TEMPLATE_SEARCH_CENTER_2);
				_templates.Add(SPSiteTemplates.SocialMeetingWorkspace , SP_SITE_TEMPLATE_SOCIAL_MEETING_WORKSPACE);
				_templates.Add(SPSiteTemplates.SPSPortal , SP_SITE_TEMPLATE_SPS_PORTAL);
				_templates.Add(SPSiteTemplates.TeamSite , SP_SITE_TEMPLATE_TEAM_SITE);
				_templates.Add(SPSiteTemplates.Wiki , SP_SITE_TEMPLATE_WIKI);
			}
			return _templates;
		}
	}

	public static string GetSharePointStringKey(this SPSiteTemplates siteTemplate)
	{
		return Templates.GetByFirst(siteTemplate);
	}
}

Sí, eso de ahí es un singleton y podría tener problemas potenciales de concurrencia, pero no estamos esperando agregar/remover/editar items. Además, yo habría ido con un constructor estático, pero los métodos de extensión tienen que implementarse en clases estáticas, y las clases marcadas como estáticas no tienen permitido tener constructores estáticos.

Toques extras

Con esto ya casi había terminado, pero resulta que si queremos verificar que un sitio existe, necesitamos usar su URL completa, pero si usamos el símbolo de los dos puntos ( : ) entonces vamos a tener una linda SPException diciéndonos que la URL es inválida. Lo que tenemos que hacer es usar URLs relativas a la URL del site collection, así que incluí un par de líneas para parsearlas.

Así que, aquí está la última versión del método CreateSite:

public SPWeb CreateSite(string parentSiteURL, string siteURLRequested, string siteTitle, SPSiteTemplates siteTemplate, SPLocales locale, string description)
{
	SPWeb resultWeb = null;

	PerformActionOnSite(parentSiteURL, (siteCollection, parentWeb) =>
	{
		SPWebTemplateCollection Templates = siteCollection.GetWebTemplates(Convert.ToUInt32(locale));
		SPWebTemplate spTemplate = Templates[siteTemplate.GetSharePointStringKey()];

		var rootUrl = siteCollection.Url;
		if (siteURLRequested.ToUpper().StartsWith(rootUrl.ToUpper()))
		{
			siteURLRequested = siteURLRequested.Remove(0, rootUrl.Length);
			if (siteURLRequested.StartsWith("/"))
				siteURLRequested = siteURLRequested.Remove(0, 1);

			if (siteURLRequested.Length == 0) //they were equal, don't create the root site
			{
				resultWeb = parentWeb;
			}
			else if (parentWeb.Webs[siteURLRequested].Exists) //it's not the root site, but it exist anyway
			{
				resultWeb = parentWeb.Webs[siteURLRequested];
			}
			else
			{
				resultWeb = parentWeb.Webs.Add(siteURLRequested, siteTitle, description, Convert.ToUInt32(locale), spTemplate, false, false);
			}
		}
	});

	return resultWeb;
}

Si se sienten confundidos por el método PerformActionOnSite, es un wrapper que creé para automáticamente usar y hacer dispose en los objetos **SPSite **y **SPWeb **.

protected void PerformActionOnSite(string siteUrl, Action<SPSite, SPWeb> action)
{
	using (SPSite site = new SPSite(siteUrl))
	{
		using (SPWeb web = site.OpenWeb())
		{
			action(site, web);
			web.Close();
		}
		site.Close();
	}
}

Ok, entonces ya todo está en su lugar, deberían poder usarlo así::

CreateSite("http://siteCollectionUrl", "http://siteCollectionUrl/newSite", SPSiteTemplates.PublishingSite, SPLocales.English_UnitedStates, "site description");

Referencias:

(Read more →)

Link del día: Cómo atacar un problema de performance

Hablando de la web y performance y todo eso, creo que nunca hemos visto un caso práctico de cómo debería atacarse un problema de performance. Es decir, si sospechamos que hay un problema de estos en un determinado lugar, ¿cuáles son los pasos a ejecutar? Creo que la experiencia juega mucho a favor, porque cada caso es distinto, pero siempre es mejor observar este tipo de situaciones en webs o aplicaciones que tienen una carga pesada diaria, con lo cual un problema de performance es mucho más pequeño y difícil de detectar, y las mejoras que se necesitan son mucho más finas y difíciles de lograr.

En el blog de Sam Saffron, nos cuenta cómo se trata uno de estos problemas en la famosa web Stack Overflow, llamada A day in the life of a slow page at Stack Overflow.

Como podemos ver, las aproximaciones al problema son varias, desde la tecnología utilizada, hasta el tipo de queries que se hacen a base de datos, optimizaciones en las mismas, y hay miles de cosas más que podrían estar involucradas (imágenes, textos, procesamiento de distintos elementos… en fin, es todo un mundo.

Soy un zorrinito performante.

(Read more →)

Link del día: Performance HTML5, CSS3 y DOM, Parte 1: Intro

Acabo de ver dos videos de Paul Irish, del grupo de Google Chrome. Uno de ellos era Google Chrome Developer Tools: 12 Tricks to Develop Quicker. Aquí básicamente nos da una explicación de los Chrome Developer Tools con algunos trucos que no son tan sabidos. Interesante y útil.

Sin embargo, el video que más interesante me resultó es uno de media hora llamado HTML5, CSS3 and DOM Performance.

Este video es terriblemente informativo sobre varias temáticas. Quiero en el futuro poder dedicarle un poco más de tiempo a cada una, pero mientras tanto, dejenmé hacer un resumen de las cosas que se tratan en el video:

  • Reflows: primero se habla de este concepto, relacionado a las operaciones de repintado que un navegador debe realizar. Nos cuenta el caso de Chrome y de Safari (WebKit) y de ciertas operaciones y trucos para evitar. Nos muestra cómo efectivamente podemos monitorear y mejorar esa performance.
    Herramientas relacionadas: Comandos ocultos de Chrome.
  • Animaciones CSS con aceleración por hardware: Mucho del trabajo de repintado puede hacerse de forma tal que el navegador termine delegándolo al GPU de mando en la máquina cliente. Esto trae experiencias muy distintas, y puede forzarse con pocos truquitos, que en este momento son básicamente hacks. Incluso podemos ver el efecto en ciertos dispositivos móviles.
    Herramientas relacionadas: Improving the performance of your HTML5 App (artículo), Elements Complete (demostración)
  • Animación y web workers: Mucho del trabajo que nuestro javascript hace no tiene por qué estar en el thread principal de pintado de la página, esto lleva a las páginas frizadas y funcionando lento. Para eso podemos delegar nuestra ejecución a web workers, que tienen una buena performance y es amigable con la batería.
    Herramientas relacionadas: WebGL Field (demo)
  • Benchmarking: relacionado a todo lo anterior, necesitamos de una forma objetiva y poderosa de poder probar qué elementos son mejores. El ejemplo que Paul menciona es: realmente es más performante usar el === en lugar del == en JavaScript? Ahora tenemos la forma de probarlo fácilmente.
    Herramientas relacionadas: jsPerf (herramienta de test cases para benchmarking), BrowserScope (versión colectiva de pruebas en browsers), Benchmark.js (librería JavaScript para benchmarking y testing).
  • Build scripts: la posibilidad de automatizar el generado de los sitios puede ahorrar muchos problemas en la performance. Así como alguna vez hablamos de mod_pagespeed, Paul menciona otras alternativas.
    Herramientas relacionadas: HTML5 BoilerPlate (librería de startup para generar aplicaciones bajo buenas prácticas).

Espero poder ahondar en cada uno de estos en el futuro. Estén atentos!

Soy un zorrinito performante.

(Read more →)