Tuesday, December 11, 2007

New entries will be published by the better blog.

12/11/2007 2:37:20 PM (Mitteleuropäische Zeit , UTC+01:00)  #    Disclaimer  |  Comments [1]  | 
 Wednesday, May 30, 2007

Do you want to save about 500kb traffic for every anonymous request to your MOSS 2007 Publishing Site?

There is no default way in MOSS 2007 where you can say: "I'm online visible for anonymous users, I don't want to load core.js, portal.js, init.js, ... every time a user is accessing a page". I was searching for this switch a long time, but it doesn't seems to exist.

 

Microsoft's proposal for this is to force a delayed loading of the core.js, which can be read here: http://support.microsoft.com/kb/933823/en-us or here: http://blogs.msdn.com/ecm/archive/2007/02/21/building-a-new-page-layout-which-does-not-reference-core-js-but-downloads-it-while-the-page-is-being-viewed-thereby-optimizing-response-time.aspx. This doesn't solve the problem, but is one way to speed up your pages.

 

A solution for this problem is a HTTP-Filter. This filter can be deployed in the /bin directory of your web application and has to be referenced in your web.config.

 

The solution below is not perfect, but it works and saves a lot of traffic, server load, time and money. And it enables you to publish lightweight pages with MOSS 2007.

 

Improvements for the code below would be a separation of the "tags to cleanup" in a configuration file and the usage of regular expressions. But with output cache enabled, this shouldn't be a performance issue.

 

Make sure to adapt this code for your own needs, as I'm removing e.g. ViewState and so on, which could possibly crash your MOSS applications.

MOSSCleanupModule.cs:

using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using Microsoft.SharePoint;
namespace HttpModules
{
   
public class MOSSCleanupModule : IHttpModule
   
{
      
public void Init(HttpApplication app)
      {
         app.ReleaseRequestState +=
new EventHandler(InstallResponseFilter);
      }

      private void InstallResponseFilter(object sender, EventArgs e)
      {
         HttpResponse response = HttpContext.Current.Response;
         HttpRequest request = HttpContext.Current.Request;
         if (response.ContentType == "text/html" && request.Url.AbsolutePath.EndsWith(".aspx") && !request.Url.AbsolutePath.Contains
            ("_layouts/"))
         {
            SPUser user = SPContext.Current.Web.CurrentUser;
            if (user == null || string.IsNullOrEmpty(user.LoginName))
            {
               response.Filter =
new MOSSCleanupFilter(response.Filter);
            } 
         }
      }

      public void Dispose()
      {
      }
   }
}



MOSSCleanupFilter.cs:

using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.IO;
namespace HttpModules
{
public class MOSSCleanupFilter : Stream
{
   private static string[] completeTagsToCleanup = new string[] 
   {
      "<meta name=\"GENERATOR\" content=\"Microsoft SharePoint\" />",
      "<input type=\"hidden\" name=\"__SPSCEditMenu\" id=\"__SPSCEditMenu\" value=\"true\" />"+System.Environment.NewLine,
      "<input type=\"hidden\" name=\"MSOWebPartPage_PostbackSource\" id=\"MSOWebPartPage_PostbackSource\" value=\"\" />"+System.Environment.NewLine,
      "<input type=\"hidden\" name=\"MSOTlPn_SelectedWpId\" id=\"MSOTlPn_SelectedWpId\" value=\"\" />"+System.Environment.NewLine,
      "<input type=\"hidden\" name=\"MSOTlPn_View\" id=\"MSOTlPn_View\" value=\"0\" />"+System.Environment.NewLine,
      "<input type=\"hidden\" name=\"MSOTlPn_ShowSettings\" id=\"MSOTlPn_ShowSettings\" value=\"False\" />"+System.Environment.NewLine,
      "<input type=\"hidden\" name=\"MSOGallery_SelectedLibrary\" id=\"MSOGallery_SelectedLibrary\" value=\"\" />"+System.Environment.NewLine,
      "<input type=\"hidden\" name=\"MSOGallery_FilterString\" id=\"MSOGallery_FilterString\" value=\"\" />"+System.Environment.NewLine,
      "<input type=\"hidden\" name=\"MSOTlPn_Button\" id=\"MSOTlPn_Button\" value=\"none\" />"+System.Environment.NewLine,
      "<input type=\"hidden\" name=\"MSOAuthoringConsole_FormContext\" id=\"MSOAuthoringConsole_FormContext\" value=\"\" />"+System.Environment.NewLine,
      "<input type=\"hidden\" name=\"MSOAC_EditDuringWorkflow\" id=\"MSOAC_EditDuringWorkflow\" value=\"\" />"+System.Environment.NewLine,
      "<input type=\"hidden\" name=\"MSOSPWebPartManager_DisplayModeName\" id=\"MSOSPWebPartManager_DisplayModeName\" value=\"Browse\" />"+System.Environment.NewLine,
      "<input type=\"hidden\" name=\"MSOWebPartPage_Shared\" id=\"MSOWebPartPage_Shared\" value=\"\" />"+System.Environment.NewLine,
      "<input type=\"hidden\" name=\"MSOLayout_LayoutChanges\" id=\"MSOLayout_LayoutChanges\" value=\"\" />"+System.Environment.NewLine,
      "<input type=\"hidden\" name=\"MSOLayout_InDesignMode\" id=\"MSOLayout_InDesignMode\" value=\"\" />"+System.Environment.NewLine,
      "<input type=\"hidden\" name=\"MSOSPWebPartManager_OldDisplayModeName\" id=\"MSOSPWebPartManager_OldDisplayModeName\" value=\"Browse\" />"+System.Environment.NewLine,
      "<input type=\"hidden\" name=\"MSOSPWebPartManager_StartWebPartEditingName\" id=\"MSOSPWebPartManager_StartWebPartEditingName\" value=\"false\" />"+System.Environment.NewLine,
      "_spBodyOnLoadWrapper();",
   };

   private static string[] tagsToCleanup = new string[] 
   {
      "<link rel=\"stylesheet\" type=\"text/css\" href=\"/_layouts/1033/styles/core.css?rev=",
      "<link rel=\"stylesheet\" type=\"text/css\" href=\"/Style%20Library/en-US/Core%20Styles/Band.css",
      "<link rel=\"stylesheet\" type=\"text/css\" href=\"/Style%20Library/en-US/Core%20Styles/Controls.css",
      "<link rel=\"stylesheet\" type=\"text/css\" href=\"/_layouts/1033/styles/HtmlEditorCustomStyles.css?rev=",
      "<link rel=\"stylesheet\" type=\"text/css\" href=\"/_layouts/1033/styles/HtmlEditorTableFormats.css?rev=",
      "<input type=\"hidden\" name=\"__REQUESTDIGEST\" id=\"__REQUESTDIGEST\"",
      "<input type=\"hidden\" name=\"__VIEWSTATE\" id=\"__VIEWSTATE\" value=",
   };

   private static string[] scriptsToCleanup = new string[] 
   {
      "<script type=\"text/javascript\" language=\"javascript\" src=\"/_layouts/1033/core.js?rev=",
      "<script src=\"/WebResource.axd?",
      "<script> var MSOWebPartPageFormName = 'aspnetForm'",
      "<script type=\"text/javascript\" language=\"javascript\" src=\"/_layouts/1033/init.js?rev=",
      "<script type=\"text/javascript\" language=\"javascript\" src=\"/_layouts/1033/init.js?rev=",
      "<script type=\"text/javascript\" language=\"javascript\" src=\"/_layouts/portal.js?rev=",
      "<script type=\"text/javascript\" language=\"javascript\" src=\"/_layouts/1033/ie55up.js?rev=",
      "<script type=\"text/javascript\" language=\"javascript\" src=\"/_layouts/1033/non_ie.js?rev=",
      "<script type=\"text/javascript\">"+System.Environment.NewLine+"<!--"+System.Environment.NewLine+"var __wpmExportWarning",
      "<script type=\"text/JavaScript\" language=\"JavaScript\">"+System.Environment.NewLine+"<!--"+System.Environment.NewLine+"var L_Menu_BaseUrl",
   };

   private Stream responseStream;
   
private long position;

   public MOSSCleanupFilter(Stream inputStream)
   {
      this.responseStream = inputStream;
   }

   public override void Write(byte[] buffer, int offset, int count)
   {
      StringBuilder html = new StringBuilder(System.Text.UTF8Encoding.UTF8.GetString(buffer, offset, count));
      foreach(string completeTagToClean in completeTagsToCleanup) 
      {
         this.CleanUp(html, completeTagToClean);
      }

      foreach (string tagToClean in tagsToCleanup)
      {
         this.CleanUp(html, tagToClean, ">");
      }

      foreach (string scriptToClean in scriptsToCleanup)
      {
         this.CleanUp(html, scriptToClean, "</script>");
      }

      byte
[] data = System.Text.UTF8Encoding.UTF8.GetBytes(html.ToString());
      responseStream.Write(data, 0, data.Length);
   }

   private void CleanUp(StringBuilder html, string search, string endTag)
   {
      int startPos = html.ToString().IndexOf(search);
      if (startPos != -1)
      {
         if (!string.IsNullOrEmpty(endTag))
         {
            int endPos = html.ToString().IndexOf(endTag, startPos);
            if (endPos != -1)
            {
               html.Remove(startPos, endPos - startPos + endTag.Length);
            }
         }
         else
         {
            html.Remove(startPos, search.Length);
         }
      }
   }

   private
void CleanUp(StringBuilder html, string search)
   {
      
this.CleanUp(html, search, null);
   }

   #region Filter overrides
   
   
public override bool CanRead
   {
      get
      {
         return true;
      }
   }

   public
override bool CanSeek
   {
      get
      {
         return true;
      }
    }

   public override bool CanWrite
   {
      get
      {
         return true;
      }
   }

   public override void Close()
   {
      responseStream.Close();
   }

   public override void Flush()
   {
      responseStream.Flush();
   }

   public override long Length
   {
      get
      {
         return 0;
      }
   }

   public override long Position
   {
      get
      {
         return position;
      }
      set
      {
         position =
value;
      }
   }

   public override long Seek(long offset, SeekOrigin origin)
   {
      return responseStream.Seek(offset, origin);
   }

   public override void SetLength(long length)
   {
      responseStream.SetLength(length);
   }

   public override int Read(byte[] buffer, int offset, int count)
   {
      return responseStream.Read(buffer, offset, count);
   }
   #endregion
   }
}



web.config:

<httpModules>
...

   <
add name="MOSSCleanupModule" type="HttpModules.MOSSCleanupModule, HttpModules, Version=1.0.0.1, Culture=neutral, PublicKeyToken=..."/>
...
</httpModules>

5/30/2007 5:12:02 PM (Mitteleuropäische Sommerzeit , UTC+02:00)  #    Disclaimer  |  Comments [11]  | 
 Tuesday, April 24, 2007

WebRequest myReq = WebRequest.Create(url);

string
username="username";
string password="password";
string usernamePassword = username + ":" + password;
CredentialCache mycache = new CredentialCache();
mycache.Add(
new Uri(url), "Basic", new NetworkCredential(username, password));
myReq.Credentials = mycache;
myReq.Headers.Add(
"Authorization", "Basic " + Convert.ToBase64String(new ASCIIEncoding().GetBytes(usernamePassword)));

WebResponse wr = myReq.GetResponse();
Stream receiveStream = wr.GetResponseStream();
StreamReader reader =
new StreamReader(receiveStream, Encoding.UTF8);
string 
content = reader.ReadToEnd();

4/24/2007 2:46:20 PM (Mitteleuropäische Sommerzeit , UTC+02:00)  #    Disclaimer  |  Comments [4]  | 
 Monday, April 23, 2007

using Microsoft.SharePoint.Publishing;

SPWeb site = SPContext.Current.Site.OpenWeb(yourSitePath);

if
(PublishingWeb.IsPublishingWeb(site))
{
   PublishingWeb web = PublishingWeb.GetPublishingWeb(site);
   SPFile defaultPage = web.DefaultPage;
}

4/23/2007 4:51:43 PM (Mitteleuropäische Sommerzeit , UTC+02:00)  #    Disclaimer  |  Comments [0]  | 
 Monday, March 26, 2007

You can use the following code snippet to iterate through your navigation tree recursive:

using Microsoft.SharePoint;
using Microsoft.SharePoint.Publishing.Navigation;

public class SiteMapTest : System.Web.UI.WebControls.WebParts.WebPart
{

   protected
override void RenderContents(System.Web.UI.HtmlTextWriter writer)
   {
      PortalSiteMapProvider map = new PortalSiteMapProvider();
      writer.Write("<table>");
      this.PrintTree(map.RootNode, writer, 0);
      writer.Write("</table>");
   }

   public
void PrintTree(SiteMapNode node, System.Web.UI.HtmlTextWriter writer, int level)
   {
      string space = string.Empty;
      for (int i = 0; i < level; i++)
      {
         space += "&nbsp;&nbsp;&nbsp"&nbsp;&nbsp;&nbsp";
      }
      writer.Write("<tr><td>" + space + "<a href=\"" + node.Url + "\">" + node.Title + "</a></tr></td>");
      foreach (SiteMapNode childNode in node.ChildNodes)
      {
         PrintTree(childNode, writer, level + 1);
      }
   }

}

If you use a SPSiteMapProvider instead of PortalSiteMapProvider, you will only see your sites, no pages.
SPContentMapProvider delivers nothing for me, there are no ChildNodes defined. I didn't check out SPNavigationProvider, but I had some bad expierence with Microsoft.SharePoint.Navigation.SPNavigation.
For some more informations see: http://msdn2.microsoft.com/en-us/library/aa830815.aspx#Office2007SSBrandingWCMPart2_CustomizingNavigation

3/26/2007 2:26:31 PM (Mitteleuropäische Sommerzeit , UTC+02:00)  #    Disclaimer  |  Comments [1]  | 
 Thursday, March 15, 2007

Visual Studio Integration

It would be very helpful if it would be possible to edit master pages and page layouts in VS2005.
It would be groundbreaking if you could run a little instance of MOSS 2007 on a local machine (e.g. in Cassini) to debug and develop your WebParts. This "little instance" should connect to your MOSS 2007 content database. I bet this will come with one of the next MOSS releases or service packs?!? (If I would be jobless I would implement this or a similar thing and make a lot of money)

Variations

Let's say you have three different page layouts and only one content type (all page layouts use the same content type). This makes sense because you are not sure which layout to use or you want to give your authors the possibility to switch between page layouts (what only works between page layouts with the same content type).
If you now create a variation of a page using one of these page layouts (or let MOSS created this variation automatically), SharePoint 2007 allocates the first page layout you created with the content type to your new page. This is wrong in 66% of all cases (for three page layouts), so you probably have to assign the right page layout by hand. Not funny in ECMS environments with more than one language variation. But we heard about this as a feature.

Separation Of Content And Structure

Separating content like images, text, etc from structure (navigation tree, site structure, master pages, page layouts, css, ...) would allow you to implement redesigns easier and to enable parallel work between developers and authors.
If you developed a large scale enterprise website with daily new content, and, on the other side, plan a redesign with seriously impact on the structure, you have no other possibility as implementing your redesign on one system (let's say staging environment) and to maintain your content on the other system (let's say authoring environment).
What you have to do now is to maintain your content on both systems (authoring and staging). This can be very hard. Another possibility would be to automatically deploy your content from authoring to staging, but this is no out of the box functionality and I'm sure it's not easy to implement.
Partially the separation seems to work if you are not provisioning publishing features and if you don't use SharePoint Designer. At least master pages are stored in the filesystem for that case.

Deployment with the integrated deployment path...

... should work (without re-setting master pages or breaking sites). Hope there will be a hotfix soon.

SharePoint Designer...

... should not freeze the whole system, double html headers or reformat your well formatted html code.

 

... to be continued.

3/15/2007 8:53:14 PM (Mitteleuropäische Zeit , UTC+01:00)  #    Disclaimer  |  Comments [0]  | 
 Wednesday, March 14, 2007

If you add inline code to your e.g. PageLayout in MOSS 2007 you will get the following error: "Code blocks are not allowed in this file".

To avoid this problem, add the following code block to your web.config:

<PageParserPaths>

  <PageParserPath VirtualPath="/" CompilationMode="Always" AllowServerSideScript="true" IncludeSubFolders="true" />

</PageParserPaths>

This will allow inline code in your entire site collection. Make sure to be aware with security issues and inline code. To enable inline code only for a single file use:

<PageParserPaths>

  <PageParserPath VirtualPath="/Pages/MyPage.aspx" CompilationMode="Always" AllowServerSideScript="true" />

</PageParserPaths>

3/14/2007 11:53:21 AM (Mitteleuropäische Zeit , UTC+01:00)  #    Disclaimer  |  Comments [0]  | 
 Tuesday, February 20, 2007

Example:
DataSet data = new DataSet();
SPWeb site = SPContext.Current.Site.OpenWeb("/SiteName");
SPList list = site.Lists["Listname"];
data.Tables.Add(list.Items.GetDataTable());

 

2/20/2007 1:19:34 PM (Mitteleuropäische Zeit , UTC+01:00)  #    Disclaimer  |  Comments [7]  |