SharePoint – Yet Another CAML Query tool (2)

Continuing with the YACAMLQT, I have finished the first version. Now the «toy» tool supports, «group by» and «order by», and some expression errors are now fixed. In order to not increase the complexity of the lexer part, the clause «group by» must be write like «groupby» and the «order by» clause as «orderby».

To support this new clauses the first expression must begin with a «where» clause. The syntax is:

[WHERE boolean_expression] [GROUPBY field_list] [ORDERBY field_list]

A CAML complex query written in sql mode can look as:

WHERE ((Column1 = «Value1») AND (Column2 = «Value2»)) OR ((Column3 = 10)
AND (Column3 <> NULL)) GROUPBY Column1 ORDERBY Column1, Column2 ASC, Column3 DESC

Also, you can use ==  for equal and != for not equal  and && for and, or, || for or (it’s a c# goodie)

The CAML result code

<Query>
  <Where>
    <Or>
      <And>
        <Eq>
          <FieldRef Name="Column1" />
          <Value Type="Text">Value1</Value>
        </Eq>
        <Eq>
          <FieldRef Name="Column2" />
          <Value Type="Text">Value2</Value>
        </Eq>
      </And>
      <And>
        <Eq>
          <FieldRef Name="Column3" />
          <Value Type="Integer">10</Value>
        </Eq>
        <IsNotNull>
          <FieldRef Name="Column3" />
        </IsNotNull>
      </And>
    </Or>
  </Where>
  <GroupBy>
    <FieldRef Name="Column1" />
  </GroupBy>
  <OrderBy>
    <FieldRef Name="Column1" />
    <FieldRef Name="Column2" Ascending="True" />
    <FieldRef Name="Column3" Ascending="False" />
  </OrderBy>
</Query>

 

Also you can add the assembly (IdeSeg.SharePoint.Caml.QueryParse) in your projects

using IdeSeg.SharePoint.Caml.ParseQuery;
 
string query = "where Campo1=10 && campo2="Test" and campo3=#01/01/2007# order Campo3";
try
{
    string xmlQuery = CAMLParser.Query(query);        
}
catch (ParserException ex)
{
    statusError = ex.Message;
}       

Internally the code use a Parser to generate an AST and a Lexer to scan the query, they do the hard work you can see the UML class diagrams below: 

Finally in the winforms application there is also a c# generator to do copy and paste. Enjoy!!!!!

 YACAMLQT.zip

(File Updated December 9, 2007)

Frikadas – Yo tengo un IBM AT ¿y vos?

Bueno, algunos de los que me conocen ya saben que soy un retro-friki, que de vez en cuando me da la venada y compro alguna estación de trabajo u ordenador viejo que me dedico a restaurar. La diversión consiste en comprar algo barato en ebay, para después juguetear con el una temporada.


La última adquisición ha sido este IBM AT del año 84. Por aquellos años no podía pagar lo que valía (creo que unos $5000), y me conformaba con mi Commodore 64 que sin lugar a dudas era un gran salto desde el ZX81, más tarde cuando pude tuve un Amstrad 1512, pero aquella estética del IBM, mazacona, ha permanecido en mi retina desde aquellos años, todavía estoy viendo el anuncio en las revistas …


Koldo, el chico que me ha vendido el IBM, resulta que es tan friki como yo, y aunque le ha dolido desprenderse de este «cacharro» que ha guardado 20 años nada más y nada menos. Cuando ya me iba, me ha enseñado su ultima adquisición un Compaq con cuatro procesadores XEON, que ha comprado en Alemania, y ya claro, nos hemos puesto a rajar de estos temas.



En la foto, con las PCTOOLS y la otra con las Utilidades Norton 🙂



Ale todo el  mundo a contar sus frikadas …

SharePoint – Yet Another CAML Query tool (ala SQL)

Some time ago, I thought write a small tool in order to do CAML queries in plain text, as in SQL «where» clause. I have the idea forgotten, I missed myself in the daily work. Yesterday doing a little modification in a program I remembered this, of course while I was writting a complex CAML query.

I named it «Yet Another CAML Query Tool» aka (YACAML-QT).

By moment it’s a little toy, you can write your queries as in a «where» clause, using the same syntax that you’d write in a SQL Sentence. It’s is more easy!!!

Updated
Use «=null» as CAML IsNull operator and «<>null» as IsNotNull also you can use dates enclosed into sharp characters #01/01/1990# the sql Like operator is replaced by CAML Contains and the at «@» operator for the CAML BeginsWith. Also support parentheses for operator precedence.

TODO: a pretty interface 🙂

 YACAMLQT.zip

(File Updated December 9, 2007)

SharePoint – Destripando el Wiki (4)

En está última parte de Destripando el Wiki, veremos como crear un campo personalizado para cambiar el comportamiento por defecto del SPFieldMultiLineText el usado por WikiField, de modo que podremos cambiar el comportamiento a la hora de visualizar los enlaces a entradas que no están definidas para que estas sean redirigidas a nuestra página CustomCreatePage.aspx.


Como comentaba en anteriores posts, hemos visto que el wiki es una biblioteca de documentos que almacena páginas basadas en un tipo de contenido llamado Página Wiki que hereda de documento. Cuando se crea una nueva página la plantilla llamada wkpstd.aspx se copia en la biblioteca de documentos. Vimos que podemos crear nuestra propia página CreateCustomPage.aspx, para recrear el mismo comportamiento, utilizando nuestras propias plantillas y que esto funcionaba correctamente. Pero el problema llega cuando el campo WikiField se renderiza, ya que mantiene un enlace a CreateWebPage.aspx.


Antes de continuar, creo que es evidente que los errores de diseño en la solución Wiki de Sharepoint son más que notables. Para empezar, han limitado la plantilla incrustando el nombre de la misma en el código de CreateWebPage.aspx, limitando los usos del Wiki. Por otro lado, el campo WikiField, es el campo SPFieldMultiLine, al cual han añadido el método ProcessWikiLinks para realizar la conversión de los enlaces, cuando este debía ser una clase heredada del mismo. Por último, el control a través del cual se renderiza WikiField, es RichTextField, el cual hereda de NoteField, y que debía haber sido también una clase heredada con el fin de que cada una se dedicara a lo suyo y no mezclar distintas funcionalidades aunque sean parecidas en la misma clase, lo cual nos permitiría a los desarrolladores una comprensión mejor del funcionamiento sin tener que buscar donde han ido haciendo los trucos, y nos facilitaría la tarea a la hora de extender SharePoint. Como si no fuera lo suficientemente grande el SDK, como para añadir unas cuantas clases más.


En primer lugar tendremos que crear nuestro propio campo personalizado, yo lo he llamado Wiki que a su vez hereda de SPFieldMultiLineText,  y he sobreescrito GetFieldValueAsHtml, para reemplazar la página por defecto por la nuestra.


 



   1:  public class Wiki : SPFieldMultiLineText
   2:      {
   3:          #region SPField Constructors
   4:   
   5:          public Wiki(SPFieldCollection fields, string fieldName)
   6:              : base(fields, fieldName)
   7:          {           
   8:          }
   9:   
  10:          public Wiki(SPFieldCollection fields, string typeName, string displayName)
  11:              : base(fields, typeName, displayName)
  12:          {        
  13:          }
  14:   
  15:          #endregion
  16:   
  17:          public new string GetFieldValueAsHtml(object value, SPListItem item)
  18:          {            
  19:              string html = base.GetFieldValueAsHtml(value, item);
  20:              return html.Replace(«CreateWebPage», «CreateCustomPage»);
  21:          }
  22:        
  23:          public override BaseFieldControl FieldRenderingControl
  24:          {
  25:              get
  26:              {
  27:                  BaseFieldControl fieldControl = new WikiField();
  28:                  fieldControl.FieldName = InternalName;
  29:                  return fieldControl;
  30:              }
  31:          }
  32:      }

 


Para subsanar el segundo problema, necesitamos crear también un control que llame a nuestro GetFieldValueAsHtml, para lo cual he heredado de NoteField, y sobrescrito el método de RenderFieldForDisplay


 



   1:   public class WikiField : NoteField
   2:      {
   3:   
   4:          protected override void RenderFieldForDisplay(HtmlTextWriter output)
   5:          {
   6:              Wiki field = (Wiki) Field;
   7:              if (field != null)
   8:              {
   9:                  if (field.WikiLinking)
  10:                  {
  11:                      output.Write(«<div class=»ms-wikicontent»>»);
  12:                      output.Write(field.GetFieldValueAsHtml(ItemFieldValue, ListItem));
  13:                      output.Write(«<p></p></div>»);
  14:                      return;
  15:                  }
  16:              }
  17:              else
  18:              {
  19:                  return;
  20:              }
  21:              base.RenderFieldForDisplay(output);
  22:          }
  23:   
  24:      }

 


Y la definición del nuevo campo Wiki


 



   1:   <FieldType>
   2:          <Field Name=»TypeName»>Wiki</Field>
   3:          <Field Name=»ParentType»>Note</Field>        
   4:          <Field Name=»TypeDisplayName»>
   5:              WikiField 
   6:          </Field>
   7:          <Field Name=»TypeShortDescription»>
   8:              WikiField supports custom page
   9:          </Field>
  10:          <Field Name=»UserCreatable»>FALSE</Field>
  11:          <Field Name=»ShowInListCreate»>FALSE</Field>
  12:          <Field Name=»ShowInSurveyCreate»>FALSE</Field>
  13:          <Field Name=»ShowInDocumentLibraryCreate»>FALSE</Field>
  14:          <Field Name=»ShowInColumnTemplateCreate»>FALSE</Field>
  15:          <Field Name=»Sortable»>FALSE</Field>
  16:          <Field Name=»Filterable»>FALSE</Field>
  17:          <Field Name=»FieldTypeClass»>IdeSeg.SharePoint.CustomFields.Wiki.Wiki,
IdeSeg.SharePoint.CustomFields,Version=1.0.0.0,
Culture=neutral, PublicKeyToken=…</Field>
  18:  </FieldType>    


 


Bien, por último solo nos quedaría crear nuestro propio tipo de contenido para poder adjuntarlo a la biblioteca de documentos, como es lógico podríamos crear una definición de lista con el tipo de contenido, pero por abreviar, yo solo he creado el tipo de contenido que después podemos añadir a una biblioteca de documentos.


 


feature.xml



   1:  <Feature Id=»1d9a8ea3-0b7d-42cf-8c64-78df773b7e41″ 
   2:      Title=»IdeSeg Wiki» 
   3:      Description=»Wiki with custom templates» 
   4:      Version=»1.0.0.0″ 
   5:      Scope=»Site» 
   6:      xmlns=»http://schemas.microsoft.com/sharepoint/»>
   7:    <ElementManifests>
   8:      <ElementManifest Location=»fields.xml» />
   9:      <ElementManifest Location=»ctypes.xml» />    
  10:    </ElementManifests>
  11:  </Feature>


 


fields.xml



   1:  <?xml version=»1.0″ encoding=»utf-8″?>
   2:  <!–
   3:  –>
   4:  <Elements xmlns=»http://schemas.microsoft.com/sharepoint/»>
   5:    <Field ID=»{f1d10620-1f85-460b-8132-7c57cd6eABCD}» 
   6:       Name=»WikiFld»
   7:       DisplayName=»Contenido»
   8:       StaticName=»WikiField»
   9:       Group=»_IdeSeg»
  10:       Hidden=»TRUE»
  11:       Type=»Wiki»
  12:       RichText=»TRUE»
  13:       RichTextMode=»FullHtml»
  14:       IsolateStyles=»TRUE»
  15:       RestrictedMode=»FALSE»
  16:       NumLines=»30″
  17:       DisplaySize=»110″
  18:       UnlimitedLengthInDocumentLibrary=»TRUE»
  19:       WikiLinking=»TRUE»
  20:       Sortable=»FALSE»
  21:       Sealed=»TRUE»
  22:       AllowDeletion=»TRUE»>
  23:      </Field>
  24:  </Elements>


 


ctypes.xml



   1:  <?xml version=»1.0″ encoding=»utf-8″?>
   2:  <Elements xmlns=»http://schemas.microsoft.com/sharepoint/»>
   3:    <ContentType ID=»0x0101005ADDEAA7312EE967BFC3076B9699C3D4″
   4:      Name=»IdeSeg Wiki» 
   5:      Group=»_IdeSeg» 
   6:      Description=»IdeSeg Wiki with custom templates»     
   7:      Version=»1″>
   8:      <FieldRefs>
   9:        <RemoveFieldRef ID=»{fa564e0f-0c70-4ab9-b863-0177e6ddd247}» Name=»Title» />
  10:        <FieldRef ID=»{f1d10620-1f85-460b-8132-7c57cd6eABCD}» Name=»WikiField» />
  11:      </FieldRefs>
  12:      <XmlDocuments>
  13:              <XmlDocument NamespaceURI=»http://schemas.microsoft.com/sharepoint/v3/contenttype/forms»>
  14:                  <FormTemplates xmlns=»http://schemas.microsoft.com/sharepoint/v3/contenttype/forms»>
  15:                      <Display>DocumentLibraryForm</Display>
  16:                      <Edit>WikiEditForm</Edit>
  17:                      <New>WikiEditForm</New>
  18:                  </FormTemplates>
  19:              </XmlDocument>
  20:          </XmlDocuments>
  21:          <DocumentTemplate TargetName=»/_layouts/CreateCustomPage.aspx» />
  22:    </ContentType>
  23:  </Elements>
 


He recibido algunos comentarios acerca de el nombre del post «Destripando«, puede que se deba al carácter latino, pero no. En un principio pensé en llamarle Anatomía del Wiki, que sonaba más científico, al igual que Disección del Wiki; Funcionamiento interno del Wiki, sonaba muy clásico y Autopsia del Wiki es para los cadáveres  y moss todavía esta calentito. Destripando el wiki, tiene un sabor más de carnicero, por que lo que estamos haciendo con el wiki, ya que su diseño es de casquería,  Destripando el Wiki, era el título correcto.


 

SharePoint – Destripando el Wiki (3)

En esta parte veremos cómo trabaja el campo especial WikiField, antes de nada recordaros un webpart, que escribí el año pasado y pasó sin pena ni gloria, pero resolvía el problema de los Wikis en WSS2.


csegMiniWiki



Bueno, resulta que el WikiField realiza (CASI) las mismas tareas que hacía yo en el webpart que es traducir las etiquetas marcadas entre corchetes por los vínculos correspondientes.


El problema de las búsquedas


Cuando escribí el webpart csegMiniWiki, el único problema que tenía eran las búsquedas. Esto es debido a que al albergar el contenido del Wiki en elementos de la lista, cuando se buscaba cierto texto, el enlace de los resultados era al elemento correspondiente de la lista, en vez de la página en donde aparecía el texto renderizado y con la traducción de vínculos correspondiente; con lo que veíamos la fuente de la página wiki.


Este problema, lo han resuelto en el Wiki de MOSS dando a cada entrada una página, la plantilla que vimos anteriormente y que es copiada para cada entrada. De este modo MOSS indexa la página con el contenido correspondiente. Y la lista que mantiene los datos a su vez no es indexada.


Sin embargo, (y no es, porque yo lo hiciera) me encontraba más cómodo con mi Wiki.


WikiField


Ya lo hemos visto con anterioridad, y está definido en fieldswss.xml


   1:  <!– Wiki Fields –>
   2:      <Field ID=»{C33527B4-D920-4587-B791-45024D00068A}»
   3:          Name=»WikiField»
   4:          DisplayName=»$Resources:core,WikiField;»
   5:          StaticName=»WikiField»
   6:          SourceID=»http://schemas.microsoft.com/sharepoint/v3″
   7:          Group=»_Hidden»
   8:          Type=»Note»
   9:          RichText=»TRUE»
  10:          RichTextMode=»FullHtml»
  11:          IsolateStyles=»TRUE»
  12:          RestrictedMode=»FALSE»
  13:          NumLines=»30″
  14:          DisplaySize=»110″
  15:          UnlimitedLengthInDocumentLibrary=»TRUE»
  16:          WikiLinking=»TRUE»
  17:          Sortable=»FALSE»
  18:          Sealed=»TRUE» 
  19:          AllowDeletion=»TRUE» />
  20:      <!– End Wiki Fields –>

 


WikiField, es un campo de tipo «Note», control NoteField, e internamente usa SPFieldMultiLineText, como sabéis los campos tienen un tipo interno que alberga la lógica del campo, en este caso SPFieldMultiLineText y un control que sirve de fachada para manipularlo, NoteField en este caso.


 



De modo que el trabajo se realiza en la clase SPFieldMultiLineText, que hereda de SPField



 


Todos los campos de sharepoint heredan de SPField, dentro de SPField, hay un conjunto de métodos virtuales que son sobrescritos para cada implementación, los que nos interesan ahora son :


    // Devuelve el valor del campo
    public virtual object GetFieldValue(string value)
    {
        return value;
    }
    // Devuelve el valor del campo en formato Html
    public virtual string GetFieldValueAsHtml(object value)
    {
        return SPHttpUtility.HtmlEncode(this.GetFieldValueAsText(value));
    }
    // Devuelve el valor del campo como texto
    public virtual string GetFieldValueAsText(object value)
    {
        if (value != null)
        {
            return value.ToString();
        }
        return string.Empty;
    }
    // Devuelve el valor del campo en formato para usarse en los formularios nuevo y de edición
    public virtual string GetFieldValueForEdit(object value)
    {
        return this.GetFieldValueAsText(value);
    }

 


Si vemos el diagrama de la clase SPFieldMultiLineText, veremos como estos son sobrescritos para el campo mistilínea, en concreto GetFieldValueAsHtml, es el encargado de procesar los enlaces para los campos WikiField (aquellos que establecen la propiedad WikiLinking en True, ver el XML de la definición del campo), este tratamiento se realiza en el método ProcessWikiLinks, que gracias a dios es público de modo que podremos sobrescribirlo :-).


 


Y digo esto, por que cuando se procesan los enlaces del Wiki, aquellos que no se encuentran definidos automáticamente son enlazados a _layouts/CreateWebPage.aspx  🙁

SharePoint – Destripando el Wiki (2)

Continuando con el Wiki, hemos visto como esta construido básicamente y donde oculta el secreto de las páginas que se generan, ahora bien


¿Podemos cambiar el diseño?


Una vez que hemos visto como esta construido, podemos de tratar de realizar una solución que implemente un comportamiento similar. Para ello deberemos hacer algunas cosas, como crear nuestra propia página de de entrada, y configurar una lista (biblioteca de documentos) para que realice el mismo comportamiento que el Wiki, eso es que llame a nuestra página personalizada para crear nuevas páginas.


Para crear la página de entrada podemos partir de la que trae SharePoint, CreateWebPage.aspx e implementar nuestra propia solución para que en vez de usar la plantilla del wiki por defecto Wkpstd.aspx, podamos utilizar la nuestra. A modo de ejemplo yo he creado una solución un poco más genérica que la que viene por defecto. En este caso, he llamado a la solución CustomTemplate, y lo único que hace es añadir al layouts una nueva página llamada CreateCustomPage.aspx que será la encargada de crear nuevas páginas para nuestro Wiki.


A diferencia de la CreateWebPage.aspx que copia la plantilla Wkpstd.apsx para cada una de las entradas, esta nueva página CreateCustomPage.aspx buscará una página llamada template.aspx que deberá existir en la biblioteca de documentos, para usarla como plantilla. Esto nos va a permitir tener en cada uno de nuestros wikis, una plantilla personalizada, como es lógico la solución podría extenderse incluso para tener más de una plantilla e incluso, decidir que plantilla es la que se quiere aplicar a las páginas que creemos.


Como he comentado anteriormente, podemos partir del código suministrado por CreateWebPage.aspx  y lo único que debemos hacer es modificar el Submit, para que copie nuestra plantilla



   1:  protected void SubmitBtn_Click(object sender, EventArgs e)
   2:  {
   3:      string fileName;
   4:      bool flag;
   5:   
   6:      Page.Validate();
   7:   
   8:      if (Page.IsValid)
   9:      {
  10:          fileName = Name.Text.Trim();
  11:        
  12:          if (fileName == null || fileName.Length == 0)
  13:          {
  14:              Error.Text = SPHttpUtility.HtmlEncode(SPResource.GetString(«CreateWebPageInvalidTitle», new object[0]));
  15:          }
  16:          else
  17:          {
  18:              fileName = fileName + «.aspx»;
  19:              string listGuid = Request.QueryString.Get(«List»);
  20:              try
  21:              {
  22:                  Guid guid;
  23:   
  24:                  guid = new Guid(listGuid);
  25:   
  26:                  SPList list = spWeb.Lists[guid];
  27:   
  28:                  if (list != null)
  29:                  {
  30:                      // Check if already exist the file                                               
  31:                      string newFileUrl = list.RootFolder.Url + «/» + fileName;
  32:                      if (spWeb.GetFile(newFileUrl).Exists)
  33:                      {
  34:                          Error.Text = SPHttpUtility.HtmlEncode(SPResource.GetString(«CreateWebPageDuplicateTitle», new object[0]));
  35:                      }
  36:                      else
  37:                      {                        
  38:                          
  39:                          // Get the template file
  40:                          SPFile srcFile = list.RootFolder.Files[«template.aspx»];
  41:                          byte[] binFile = srcFile.OpenBinary();
  42:   
  43:                          // Create the new file based in the template
  44:                          SPListItem item = list.RootFolder.Files.Add(fileName, binFile, false).Item;
  45:   
  46:                          // Save field values
  47:                          foreach (BaseFieldControl baseField in SPContext.Current.FormContext.FieldControlCollection)
  48:                          {
  49:                              item[baseField.Field.InternalName] = baseField.Value;
  50:                          }
  51:                          item.UpdateOverwriteVersion();
  52:                          SPUtility.Redirect(item.Url, SPRedirectFlags.UseSource | SPRedirectFlags.Static, Context);
  53:                      }
  54:                  }
  55:                  else
  56:                  {
  57:                      Error.Text = SPHttpUtility.HtmlEncode(SPResource.GetString(«CreateWebPageInvalidList», new object[0]));
  58:                  }
  59:              }
  60:              catch (Exception ex)
  61:              {
  62:                  // There is a template file ?
  63:                  Error.Text = SPHttpUtility.HtmlEncode(ex.ToString());
  64:              }
  65:          }
  66:      }
  67:  }


Una vez que tenemos nuestra solución para crear páginas, lo que debemos hacer es crear nuestra propia biblioteca de documentos para almacenar las páginas de nuestro wiki. Para que esta biblioteca de documentos pueda usar nuestra página personalizada para crear contenido CreateCustomPage.aspx, tendremos que crear un tipo de contenido y asignarle nuestra página.



Podemos aprovechar este tipo de contenido para albergar más columnas que luego formen parte de la plantilla, imágenes, y otros cosas. Por último el campo WikiField, El campo WikiField, es básicamente el contenido de nuestra página, y es un campo especial de SharePoint,  que debemos respetar ya que tiene un comportamiento particular, tanto para la entrada de datos como a la hora de renderizarse, ya que trata realiza un tratamiento especial para los los vínculos. Aquí tenemos otro problema ya que tanto la definición del tipo de contenido para los Wikis así como la definición del campo WikiField esta oculta.


De modo que no podemos implementarla directamente y deberemos crear nuestro propio tipo de contenido, para ello, podemos copiar la definición que vimos anteriormente e implementarlo como una característica de modo que podamos asignarlo a cualquier biblioteca de documentos.


feature.xml 

   1:  <?xml version=»1.0″ encoding=»utf-8″ ?>
   2:  <Feature  Id=»6D145000-D35F-43af-83A2-797A263A3ABC»
   3:            Title=»Custom Wiki Content Type»
   4:            Description=»Wiki content type to use templates»
   5:            Version=»12.0.0.0″
   6:            Scope=»Site»
   7:            xmlns=»http://schemas.microsoft.com/sharepoint/»>
   8:      <ElementManifests>
   9:          <ElementManifest Location=»elements.xml» />
  10:      </ElementManifests>
  11:  </Feature>
 


elements.xml

   1:  <?xml version=»1.0″ encoding=»utf-8″ ?>
   2:  <Elements xmlns=»http://schemas.microsoft.com/sharepoint/»>
   3:  <ContentType ID=»0x010100f7c73f52e54d0bbdf1e83273d6db6800″         
   4:          Name=»Template based Wiki»      
   5:          Description=»Template based wiki»
   6:      Group=»IdeSeg»
   7:          Version=»0″>
   8:          <FieldRefs>         
   9:              <RemoveFieldRef ID=»{fa564e0f-0c70-4ab9-b863-0177e6ddd247}» Name=»Title» />
  10:              <FieldRef ID=»{c33527b4-d920-4587-b791-45024d00068a}» Name=»WikiField» />
  11:          </FieldRefs>
  12:      <XmlDocuments>
  13:              <XmlDocument NamespaceURI=»http://schemas.microsoft.com/sharepoint/v3/contenttype/forms»>
  14:                  <FormTemplates xmlns=»http://schemas.microsoft.com/sharepoint/v3/contenttype/forms»>
  15:                      <Display>DocumentLibraryForm</Display>
  16:                      <Edit>WikiEditForm</Edit>
  17:                      <New>WikiEditForm</New>
  18:                  </FormTemplates>
  19:              </XmlDocument>
  20:          </XmlDocuments>
  21:          <DocumentTemplate TargetName/_layouts/CreateCustomPage.aspx» />
  22:  </ContentType>
  23:  </Elements>


Finalmente solo nos queda hacer las modificaciones pertinentes en la plantilla de las páginas Wiki e introducirlas en nuestra biblioteca de documentos como template.aspx. En el ejemplo inferior, he añadido el creador de la entrada del Wiki.



   1:  <asp:Content ContentPlaceHolderId=»PlaceHolderMain» runat=»server»>
   2:      <SharePoint:FormField FieldName=»WikiField» ControlMode=»Display» runat=»server»/>
   3:      <TABLE class=»ms-formtable» border=0 cellpadding=0 id=»formTbl» cellspacing=0 width=100%>
   4:              <SharePoint:ListFieldIterator
   5:                          ControlMode=»Display»
   6:                          TemplateName=»WideFieldListIterator»
   7:                          ExcludeFields=»FileLeafRef;#WikiField»
   8:                          runat=»server»/>
   9:      </TABLE>
  10:      <WebPartPages:WebPartZone runat=»server» FrameType=»None» ID=»Bottom» Title=»loc:Bottom»>
  11:      <ZoneTemplate></ZoneTemplate>
  12:      </WebPartPages:WebPartZone>
  13:      <!– BEGIN CUSTOM –>
  14:      <table border=0 cellpadding=2 cellspacing=0 width=100%>
  15:          <tr>
  16:              <td class=»ms-descriptiontext» ID=onetidinfoblock2>
  17:                  <SharePoint:FormattedString FormatText=»<%$Resources:wss,form_createdby%>» runat=»server»>
  18:                      <SharePoint:FormField ControlMode=»Display» FieldName=»Created» runat=»server»/>
  19:                      <SharePoint:FormField ControlMode=»Display» FieldName=»Author» runat=»server»/>
  20:                  </SharePoint:FormattedString>
  21:              </td>
  22:          </tr>
  23:      </table>
  24:      <!– END CUSTOM –>
  25:      <table border=0 cellpadding=2 cellspacing=0 width=100%>
  26:          <tr>
  27:              <td class=»ms-descriptiontext» ID=onetidinfoblock2>
  28:                  <SharePoint:FormattedString FormatText=»<%$Resources:wss,form_modifiedby%>» runat=»server»>
  29:                      <SharePoint:FormField ControlMode=»Display» FieldName=»Modified» runat=»server»/>
  30:                      <SharePoint:FormField ControlMode=»Display» FieldName=»Editor» runat=»server»/>
  31:                  </SharePoint:FormattedString>
  32:              </td>
  33:          </tr>
  34:      </table>
  35:  </asp:Content>