Thursday, March 23, 2006

WSS : Multiple Templates DocLib v2.0

I have fix the security issue when running as non-admin user in MultiTemplate DocLib and re-relase it as v2.0


Wednesday, March 22, 2006

Security Issue with MultiTemplate DocLib

The reason for the pull back of v1.1 is that as I tested the doc lib with a non-admin user, an authentication problem arise.

My initial idea is to store all the local templates in the Forms folder. However, I found out that non-admin user will not be able to access files in the Forms folder. I found a workaround for this. I just create another folder(named documentTemplate) in the doc lib and store those templates in this folder. My code will then read all those templates from this folder.

As i continue testing, I found another security issue. When a non-admin user create a new document from a template stored in global template (under _layouts/documentTemplate), the user get a authentication dialog. Word, Excel and Powerpoint handle this differently. In Word and Excel, if you click Cancel in the dialog, the template that you select will still open. In Powerpoint, however, the template will not be open. This issue also happen in v1.0 which I have not tested with non-admin user.

I am still in the middle of finding out why there is an authentication dialog. Will update once I found the solution.


Tuesday, March 21, 2006

WSS : Multiple Templates DocLib v1.1

Note : MultiTemplate DocLib v1.1 has been pulled back due to some issues run running as non-admin.

I have posted a new release of my Multiple Templates DocLib on GotDotNet workspace.

What is new is v1.1?
Added support to retrive document templates from the document library Forms folder.


Thursday, March 09, 2006

PS2003 : Creating new ProjectServer site with WSS integration.

If you are creating the second ProjectServer site using the EditSite tool and want to integrate with WSS, you need to change the Server Intranet Address in ProjectServer after creating the site in order for it to link correctly.

Take the following scenario.

I installed ProjectServer and WSS.

My ProjectServer URL is http://servername/ProjectServer and the WSS URL is http://servername:800.

Now I want to create another ProjectServer site and WSS site and integrate the two together.

The easist way for me to start is to backup the existing ProjectServer database and restore it as another database(EG: ProjectServer2).

Next, I create and extend another WSS site to run on port 850. so the WSS URL will be http://servername:850.

Now I use the EditSite tool to create another ProjectServer site. The URL for this PS site will be http://servername/projectServer2. When I create the site, I specify I want to setup WSS and ProjectServer integration.

If your didn't make any mistake, everything will work just fine as of this stage.

Now, I publish a new project to ProjectServer2 and automatically a new WSS project site will be created. Say for example, the URL for the project site is http://servername:850/sites/ProjectServer2_201.

Then I go to the project site and create a new issue. In the new issue page, if I click on the 'Select project tasks that are impacted by this issue' link or any of those related link at the bottom, you will notice Task Dialog does not show the correct task for the project. It either show the task for another project or it will be disabled.

When you save the issue, you will also notice there is no issue icon for that project in the Web Access Project Center. This show that the issue is not link back to the project.

After some tracing and hacking, I found out why it show the wrong project task. If your project ID is 201, the task dialog will actually retrieve tasks for project which has ID 201 from ProjectServer(the default one) and not ProjectServer2. If it cannot find a project with ID 201 in ProjectServer, it will disable the dialog.

Here come the question, why is it so?

When ProjectServer create a new WSS site for a project, it will insert the ProjectServer intranet URL into the site's SPWeb property. The intranet URL is used by the Task dialog to retrieve the project task. The intranet URL is stored in the database. So when I restore the ProjectServer database to ProjectServer2, it will
carry the orginial value which is 'http://servername/projectserver' in ProjectServer2 database.

To correct this, after you create the new ProjectServer site, you need to logon to the Web Access (EG: http://servername/projectserver2), go to Admin -> Server Configuration. Then change the Server Intranet Address to 'http://servername/projectserver2' (or whatever your project server url).


ASPNET 2.0 : Finding control in WebForm that use MasterPage

When you try to lookup a control by passing a control ID to the FindControl method,
it work a little bit differenly depend on whether your WebForm page use MasterPage or not.

If your are not using MasterPage, the following code will simple return a reference to the WebControl with ID "TextBox1".

Control c = FindControl("TextBox1");

However, if your WebForm use MasterPage, the above code won't work. You have to get a reference to the ContentPlaceHolder which contain your control and then call FindControl() on the ContentPlaceHolder object.

The following code show hot to do it. Create the following MasterPage and call it MasterPage.master

<%@ Master Language="C#"
Inherits="MasterPage" %>

"-//W3C//DTD XHTML 1.0 Transitional//EN"

<html xmlns="" >
<head runat="server">
<title>Untitled Page</title>
<form id="form1" runat="server">
<asp:contentplaceholder id="ContentPlaceHolder1"

Now, create a WebForm that use MasterPage.master as master page and insert a TextBox into the WebForm. Give the TextBox an ID "txtFindControl".

<%@ Page Language="C#"
MasterPageFile="~/MasterPage.master" %>

<asp:Content ID="Content1"
<asp:TextBox ID="txtFindControl" runat="server">

Next, go to the code behind file of the web form and insert the following code:

public partial class MasterPageJustFindControl : System.Web.UI.Page
protected void Page_Load(object sender, EventArgs e)

private void JustFindControl()
Control c = FindControl("txtFindControl");

if (c is WebControl)
Response.Write("<BR>JustFindControl : WebControl found<BR>");
Response.Write("<BR>JustFindControl : NOT found<BR>");


private void MasterPageFindControl()
ContentPlaceHolder cpholder = (ContentPlaceHolder)Master.

Control c = cpholder.FindControl("txtFindControl");

if (c is WebControl)
Response.Write("<BR>MasterPageFindControl : WebControl found<BR>");
Response.Write("<BR>MasterPageFindControl : NOT found<BR>");



When you run the above code, the output should be

JustFindControl : NOT found

MasterPageFindControl : WebControl found

That approach make some sense though. Because a MasterPage can have multiple ContentPlaceHolder(s) which host different WebForm. Each of those WebForm could have controls with the same ID. So, I think this is a way to isolate the controls from being having ID conflict.


Thursday, March 02, 2006

WSS : Security Exception when Upload File using WSS SDK

I keep hitting an exception when I use the WSS SDK to upload a document to a document library in my WSS app. The error message read
The security validation for this page is invalid.
Click Back in your Web browser, refresh the page,
and try your operation again.

After some googling, the solution is you have to set the AllowUnsafeUpdates property of SPSite and SPWeb object to true before you upload the file. The code look like this:

using System.IO;

private void btnUpload_Click(object sender, System.EventArgs e)
string targetSite = "http://wssserver/sites/myteamsite";
string docLib = "my doc lib";
byte[] contents = null;

string saveAsFile = Path.GetFileName(FileUpload.PostedFile.FileName);
Stream stream = FileUpload.PostedFile.InputStream;

contents = new byte[(int)stream.Length];
stream.Read(contents, 0, (int)stream.Length);

SPSite site = new SPSite(targetSite);
SPWeb web = site.OpenWeb();

site.AllowUnsafeUpdates = true;
web.AllowUnsafeUpdates = true;

SPFolder folder = web.Folders[docLib];

folder.Files.Add(saveAsFile, contents, true);

site.AllowUnsafeUpdates = false;
web.AllowUnsafeUpdates = false;