![]() | ![]() | ![]() |
Parametros:
Recursos:
Blog a cerca de mis experiencias con Silverlight,
WSS 3.0, MOSS y algunas otras tecnologías
![]() | ![]() | ![]() |
![]() |
![]() |

using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.WebControls;
using System.Drawing;
using System.Web.UI.SilverlightControls;
namespace Blog.Webparts.WebPartCode
{
public class ColorEditor : EditorPart
{
HiddenField hiddenField;
public override bool ApplyChanges()
{
this.EnsureChildControls();
SampleWebpart s = (SampleWebpart)this.WebPartToEdit;
//obtenemos el valor del hidden control
string strcolor = hiddenField.Value;
Color c = Color.Transparent.FromHex(strcolor);
s.BackColor = c;
return true;
}
public override void SyncChanges()
{
this.EnsureChildControls();
SampleWebpart s = (SampleWebpart)this.WebPartToEdit;
Color c = s.BackColor;
hiddenField.Value = c.ToHex();
}
protected override void CreateChildControls()
{
base.CreateChildControls();
try
{
hiddenField = new HiddenField();
hiddenField.ID = "hfcolorPicker";
base.Controls.Add(hiddenField);
Silverlight slControl = new Silverlight();
slControl.ID = "slColorPicker";
slControl.InitParameters = "controlid=" + hiddenField.ClientID;
slControl.Source = "/ClientBin/ColorPickerEditor.xap";
slControl.Width = 300;
slControl.Height = 250;
slControl.MinimumVersion = "2.0.31005.0";
slControl.Windowless = true;
base.Controls.Add(slControl);
}
catch(Exception ex)
{
}
}
}
}
public override EditorPartCollection CreateEditorParts()
{
List<Editorpart> editor = new List<Editorpart>();
EditorPart ep = new Blog.Webparts.WebPartCode.ColorEditor();
ep.ID = this.ID + "_ColorEditor";
editor.Add(ep);
EditorPartCollection baseEditors = base.CreateEditorParts();
return new EditorPartCollection(baseEditors, editor );
}using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.WebControls;
using System.Drawing;
using System.ComponentModel;
using System.Drawing.Design;
using Blog.Webparts.WebPartCode;
namespace Blog.Webparts
{
[Guid("057b92f2-193e-4ac4-ba28-bf06e6180aa2")]
public class SampleWebpart : Microsoft.SharePoint.WebPartPages.WebPart, IWebEditable
{
private bool _error = false;
Label lbl1;
[Personalizable(PersonalizationScope.Shared)]
[WebBrowsable(false)]
[WebDisplayName("Color de fondo")]
[WebDescription("Color de fondo")]
[DefaultValue(typeof(Color), "Red")]
public override Color BackColor
{
get
{
return base.BackColor;
}
set
{
base.BackColor = value;
}
}
public SampleWebpart()
{
this.ExportMode = WebPartExportMode.All;
}
///
/// Create all your controls here for rendering.
/// Try to avoid using the RenderWebPart() method.
///
protected override void CreateChildControls()
{
if (!_error)
{
try
{
base.CreateChildControls();
lbl1 = new Label();
lbl1.ID = "lblInitialDate";
lbl1.Width = 300;
lbl1.Text = "Label con texto";
lbl1.Font.Size = new FontUnit(24);
this.Controls.Add(lbl1);
this.Controls.Add(new LiteralControl("
"));
this.Controls.Add(new LiteralControl("
"));
this.Height = "200";
}
catch (Exception ex)
{
HandleException(ex);
}
}
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
lbl1.Text = this.BackColor.ToHex();
}
public override EditorPartCollection CreateEditorParts()
{
List<Editorpart> editor = new List<Editorpart>();
EditorPart ep = new Blog.Webparts.WebPartCode.ColorEditor();
ep.ID = this.ID + "_ColorEditor";
editor.Add(ep);
EditorPartCollection baseEditors = base.CreateEditorParts();
return new EditorPartCollection(baseEditors, editor );
}
///
/// Ensures that the CreateChildControls() is called before events.
/// Use CreateChildControls() to create your controls.
///
protected override void OnLoad(EventArgs e)
{
if (!_error)
{
try
{
base.OnLoad(e);
this.EnsureChildControls();
ScriptManager sm = ScriptManager.GetCurrent(this.Page);
if (sm == null)
{
sm = new ScriptManager();
this.Controls.AddAt(0,sm);
}
}
catch (Exception ex)
{
HandleException(ex);
}
}
}
///
/// Clear all child controls and add an error message for display.
///
private void HandleException(Exception ex)
{
this._error = true;
this.Controls.Clear();
this.Controls.Add(new LiteralControl(ex.Message));
}
}
}public Image GetImageFromUIElement(UIElement uiElement)
{
WriteableBitmap bm = new WriteableBitmap((int)uiElement.RenderSize.Width, (int)uiElement.RenderSize.Height);
bm.Render(container, new MatrixTransform());
bm.Invalidate();
Image im = new Image();
im.Source = bm;
return im;
}La clase WriteableBitmap, puede ser usada como fuente de datos para un objeto Image y asi mostrarlo en pantalla o guardarlo en un archivo.Se me ocurren algunas interesantes aplicaciones como: Sin lugar a dudas un excelente lugar para encontrar proyectos muy buenos y útiles es el Visual Studio Gallery, en el cual podemos encontrar una gran cantidad de addins para VS, algunos totalmente free y otros que tienen un costo.
Por ejemplo el SharePoint Services Site Top Navigation DSL, el cual nos permite modelar la barra de navegación de sitios, recordemos que por default WSS solo despliega un nivel del menú.
Si necesitamos lograr algo como esto:
Primero debemos modificar la Master Page para permitir que se despliegue más de un nivel del menú, como lo explica este artículo.
Acto seguido, utilizamos esta herramienta para modelar el menú en Visual Studio. Al final del modelado, esta herramienta creará un script de PowerShell, que deberemos ejecutar en el servidor de WSS, con permisos de administrador obviamente.
Espero que se den una vuelta por el Visual Studio Gallery.
Happy Coding!
Un problema que ocurre con mucha frecuencia al personalizar SharePoint, sobre todo los menús. Es, que si tenemos objetos Silverlight dentro de la pagina, los menus aparecen por debajo de los objetos Silverlight, lo cual es un error fácil de solucionar.
Solo se debe establecer el parámetro Windowless a true, y automagicamente el problema estará resuelto. El parámetro se puede establecer de diferentes maneras:
<object id="xamlHost0" width="900" height="412" type="application/x-silverlight">
<param value="transparent" name="background"/>
<param value="true" name="windowless"/>
<!-- a bunch of other params go here -->
</object>
Silverlight.createHostedObjectEx({
source: 'Silverlight/Player/Player.xaml?v=1',
parentElement: $get(parentId || "Player_SilverlightContainer"),
id: this._hostname,
properties: { width: '900', height: '412',
framerate: '24',
version: '1.0',
background: 'transparent', isWindowless: 'true'
}
<asp:Silverlight runat="server" ID="Silverlight1"
Height="340"
Width="320"
Windowless="true"
Source="../Xaml1.xaml"
OnPluginError="onXamlError"/>
<!—Inclusive, mediante javascript-->
silverlightObject.settings.Windowless = true;
Para el mismo problema pero con objetos Flash, he aquí un post del buen Haarón González, en el que explica la solución
Habrá muchas ocasiones en las que requeriremos ejecutar alguna función de javascript en el evento onload de nuestra página, cuando esto sucede, la primera alternativa en la que pensamos podría ser el método ClientScriptManager.RegisterStartupScript, sin embargo en el modelo de programación de SharePoint esto se logra de manera un poco diferente, debido a que si somos programadores de componentes que se ejecutaran en una página de SharePoint, la mayoría de las veces no tendremos acceso al tag <body>, que se encuentra comúnmente en la masterpage, para hacer algo así:
<body onload=”MyFunction()”>
.
…..
</body>
Y claro, podríamos pensar en hacer algo como esto, que es sintácticamente correcto:
window.onload = function() {
//do Something
};
Pero podríamos generar comportamiento inesperado, ya que SharePoint también utiliza este evento para ejecutar funcionas propias
<body class="body" onload="javascript:_spBodyOnLoadWrapper();">
Lo recomendable es utilizar la función _spBodyOnLoadFunctionNames.push(), para registrar todas aquellas funciones de javascript que queramos que se ejecuten en el evento onload de la página, de la siguiente forma:
<script type="text/javascript">
_spBodyOnLoadFunctionNames.push('MyFunction');
function MyFunction() {
alert('Esta función se ejecutara en el evento onload de la página');
}
</script>
alert(“Happy coding!”);
| El próximo 23 de abril del presente año se llevará acabo el Segundo Simposio Latinoamericano de SharePoint donde habrá platicas acerca del poderoso SharePoint, lo interesante es que se abordaran tanto temas para desarrolladores como IT Pro’s. La lista de expositores es la siguiente: Luis Du Solier Misael Monterroca Rodrigo Diaz Mauricio Angulo Hector Insua Ruben Colomo Tomas Hernández Javier De Labra Francisco Hernández Joel Olson. |
| Hace unos días, se presento la siguiente situación con dos desarrolladores (Juan Topo y Max Power)de Sharepoint, ambos estaban utilizando un componente creado por ellos, para un proyecto anterior, llameMOSSle (no pude evitarlo) Assembly1. Ahora ellos lo utilizaban para proyectos diferentes, a Juan Topo le funcionaba a las mil maravillas y a Max Power le lanzaba SecurityExceptions hasta en el web.config. Después de los comentarios llenos de odio y reclamos al altísimo de Max Power, llegamos a la parte en la que debuggeamos visualmente© el código que usa su componente, la forma en la que ambos estaban utilizando el componente era muy parecida y nada fuera de lo normal. Ahora llegamos a la parte de analizar de ejecución e inspeccionar la información proporcionada por la excepción lanzada. Efectivamente, el código lanza una excepción al momento de intentar acceder a un objeto SPWeb, pero no ofrece mucha información. Primer pensamiento, si encierro esta operación dentro de un RunWithElevatedPrivileges, funcionará sin problemas….. Pues no. |
¿Entonces cual es la diferencia en sus dos implementaciones?
Después de un rato recopilando información, me doy cuenta que Juan Topo agrego su Assembly1 al GAC y Max Power al directorio BIN de su aplicación. He ahí la respuesta, cualquier assembly agregado al GAC se ejecuta con Full Trust, por otro lado, cualquier assembly agregado al directorio bin de una web application de SharePoint se ejecuta dentro del contexto de seguridad establecido por el CAS policy local.
Entonces la solución es que Max debe agregar su assembly a la GAC?
La respuesta depende de las necesidades, en la mayoría de los casos, no es conveniente agregar el Assembly1 a la GAC, ya que este se salta el CAS policy y por lo tanto adquiere acceso total a los recursos de la maquina, lo que puede ser un hueco de seguridad. Sin embargo existen algunas consideraciones que podemos tomar en cuenta al tomar esta decisión:
1. Si se trata de un Assembly de Webparts, lo recomendable es que sea despegado en el directorio BIN de tu web application, para asegurate que el código de las webparts solo accedan a recursos sobre los que se les ha dado permisos explícitos.
2. Si tienes una o más webparts que requieren un conjunto de permisos superior al de otras, es recomendable que compiles estas webparts en un assembly separado y asignes permisos específicos a este ensamblado, en lugar poner todas tus webparts en un assembly y asignar permisos elevados a este.
3. Si tienes un ensamblado que requiere un nivel de confianza (level trust) mayor, no intentes subir subas el nivel de seguridad local, porque esto subirá automáticamente el nivel confianza de todos los ensamblados que se ejecutan con ese nivel, en lugar de esto, asigna permisos especifico al assembly.
4. Pero qué pasa con los elementos de SharePoint que requieren ser desplegados en la GAC? Por ejemplo, los event receivers y las features?.Si tienes mezclados este tipo de componentes con webparts dentro de tu solución de SharePoint, debes compila tus webparts en un assembly aparte para que puedan ser desplegados en el folder BIN.
5. Los administradores de SharePoint son como perros bull terry, y no permitirán que despliegues assemblies en la GAC, solo porque les digas que está firmado, debes habilitarlos para desplegar dentro del folder BIN de sus aplicaciones, si tienes alguna webpart que requiera de mas permisos, debes otorgárselos explícitamente a través de WSS_Minimal trust.
Es importante tener en cuenta esto para construir código seguro por defecto.
Happy Coding!
Sin lugar a dudas uno de los huecos mas grandes de Silvelight 2.0 es la integración con servicios web, restricciones tales como basicHttpBinding, hacían la vida de un desarrollador menos divertida. Como sabemos el basicHttpBinding codifca los mensajes de respuesta como texto, lo cual es grandioso si uno de los alcances de nuestro proyecto es la interoperabilidad con SOAP 1.1 pero nefasto por la sobrecarga de información que se envía en la respuesta. Cuantos desarrolladores no dedicamos tiempo creando alguna forma para que la respuesta de un servicio web se redujera de tamaño?
Y he aquí una de las características, por la que rezaba todas las noches, que mas esperaba de la versión Beta 1 que fue anunciada en el pasado mix.
Binary Message Encoding
BinaryHttpBinding es la respuesta que Silverlight 3 trae a los desarrolladores, para reducir el overhead que se tenía en la codificación a texto. Este nuevo tipo de binding es, por alguna razón, implementado como un custom binding.
<bindings>
<customBinding>
<binding name="binaryHttpBinding">
<binaryMessageEncoding />
<httpTransport />
</binding>
</customBinding>
</bindings>
<endpoint address="" binding="customBinding" bindingConfiguration="binaryHttpBinding" contract="ServiceContract" />
El gran incremento en performance que trae este nuevo tipo de binding, seguramente repercutirá en su utilización como default binding, y relegaremos el basicHttpBinding en los escenarios en los que sea obligatorio la interoperabilidad.
Un modo de seguridad adicional
Silverlight 3 introduce un nuevo modo de seguridad, que ya conocíamos al utilizar otros clientes con WCF, TransportWithMessageCredential es casi la respuesta a nuestras plegarias, ya que nos permite enviar credenciales para autentificarnos con el servicio web que estamos consumiendo. La desventaja (porque siempre hay un “pero”?), es que las credenciales que enviemos para la autentificación, serán enviadas en cada mensaje, y el servicio validara las credenciales en una capa SOAP. Por lo anterior las credenciales viajaran en texto plano, por lo que será necesario utilizar HTTPS
<security mode="TransportWithMessageCredential">
SLsvcutil.exe
Por ultimo, aunque no menos importante, quiero hacer mención de la herramienta SLsvcutil.exe, que podremos utilizar desde la línea de comandos, la cual nos permitirá crear las clases proxy, para realizar peticiones a los servicios web. Recordemos que en la versión anterior de Silverlight solo podíamos hacer esto a través de Visual Studio | Add Service Reference.
Aquí pueden encontrar la documentación para utilizar esta herramienta.
Después del maratón Guadalupe-Reyes, algunos hemos querido retomar nuestros primeros pasos en el CTP de Visual Studio 2010, encontrándonos con que esta versión ya expiró.
Sin embargo, es muy fácil darle la vuelta a este pequeño obstáculo. Solo tenemos que hacer lo siguiente:
Abrir el archivo de configuración de la maquina virtual (Virtual Machine Settings File) en Notepad
Y agregar la siguiente sección (que esta en rojo), exactamente debajo de la sección de configuración del mouse.
<integration>
<microsoft>
<mouse>
<allow type="boolean">true</allow>
</mouse>
<components>
<host_time_sync>
<enabled type="boolean">false</enabled>
</host_time_sync>
</components>
Bueno, espero que esto ayude a que sigan jugando con la VM de Visual Studio 2010 CTP, que por cierto pueden descargar de aquí.