Tuesday, December 25, 2007

Monday, December 24, 2007

Free ASP.NET Hosting

Finally, I found some good Free ASP.NET Webhosting: Quantasoft (Spain).

The free hosting plan includes 50MB Disk space, ASP.NET 2.0 and Sql Express.

Haven't done heavy programming on it, but the simple ASP.NET stuff (in my case, uploading documents and write/read a sql express database) for the simple sites work, so I'm happy ;-)

Thursday, December 20, 2007

Sharepoint: Force Expiry Policy Timer job to run

To test your custom expiry policy, you have to force the Expiry Policy Timer job to run. I found this very useful posting:

http://blogs.msdn.com/mattlind/archive/2007/06/05/force-execution-of-expiration-policies-in-moss.aspx

Sunday, December 16, 2007

SharePoint: Create and add a Policy to a content type programmatically

I had to add a custom action to the Sharepoint expiry policy of a content type.

How to make a custom action, and add it to a policy in the Sharepoint configuration screen of the content type is not so hard - see http://jack.whyyoung.com/blog/www-sharepointblogs-com-MainFeed-aspx-GroupID-3/19732-aspx.htm for this.

But to add this policy to a content type automatically - in code int the feature event receiver of this content type - I had to search a little bit harder..

Use the Policy (in namespace Microsoft.Office.RecordsManagement.InformationPolicy) object (do not use the SPPolicy class, that's for other purposes :) ) to add the expiration policy programmatically to the content type. Here is the code:

using (SPSite site = (SPSite)properties.Feature.Parent)
            {
                string policyFeatureId = "Microsoft.Office.RecordsManagement.PolicyFeatures.Expiration";
                SPContentType administrativeDocumentContentType = site.RootWeb.ContentTypes[new SPContentTypeId("0x01010091C4CA4E2BA24AE9BD4F276750C0A5A9")];

                //Add the custom action "AdministrativeDocumentExpirationSendEmail" to the policy resource collection
                string actionmanifest = @"<?xml version=""1.0"" encoding=""utf-8"" ?><p:PolicyResource id=""Dolmen.SharePoint.Customer.ExpirationActions.AdministrativeDocumentExpirationSendEmail"" featureId=""Microsoft.Office.RecordsManagement.PolicyFeatures.Expiration"" type=""Action"" xmlns:p=""urn:schemas-microsoft-com:office:server:policy""><p:LocalizationResources>dlccore</p:LocalizationResources><p:Name>AdministrativeDocumentExpirationSendEmail</p:Name><p:Description>Sends a mail on the Expiration date</p:Description><p:Publisher>Dolmen Computer Applications</p:Publisher><p:AssemblyName>Dolmen.SharePoint.Customer.ExpirationActions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=79b87b017c9b0654</p:AssemblyName><p:ClassName>Dolmen.SharePoint.Customer.ExpirationActions.AdministrativeDocumentExpirationSendEmail</p:ClassName></p:PolicyResource>";
                //string actionmanifest = System.IO.File.ReadAllText("actionmanifest.xml");
                PolicyResourceCollection.Add(actionmanifest);

                if (Policy.GetPolicy(administrativeDocumentContentType) == null)
                {
                    //if the content type hasn't got a Policy yet, create a new Policy
                    Policy.CreatePolicy(administrativeDocumentContentType, null);
                }

                Policy policyOfContentType = Policy.GetPolicy(administrativeDocumentContentType);
                policyOfContentType.Name = "Administrative Document - Expiration Policy";
                //Add expiration policy to the content type
                if (policyOfContentType.Items[policyFeatureId] == null)
                {
                    string customData = @"<data><formula id=""Microsoft.Office.RecordsManagement.PolicyFeatures.Expiration.Formula.BuiltIn""><number>0</number><property>Expiration</property><period>days</period></formula><action type=""action"" id=""Dolmen.SharePoint.Customer.ExpirationActions.AdministrativeDocumentExpirationSendEmail"" /></data>";

                    policyOfContentType.Items.Add(policyFeatureId, customData);
                }
            }

Tip: to get the customData and the policyFeatureId you can create a policy with the standard Sharepoint configuration screen and read it out in a console application using following code:

SPContentType administrativeDocumentContentType = site.RootWeb.ContentTypes[new SPContentTypeId("0x01010091C4CA4E2BA24AE9BD4F276750C0A5A9")];

                Policy policy = Policy.GetPolicy(administrativeDocumentContentType);

                foreach (PolicyItem item in policy.Items) {
                    Console.WriteLine("Custom data: " + item.CustomData + "\n policy feature id: " + item.Id);
                }

Wednesday, November 21, 2007

Set custom properties of a custom field in List Definition

Problem:

I made a custom field, named "CrossSiteLookupFilterFieldControl". This field has some properties:

...

<PropertySchema>

<Fields>

<Field Name="SelectedSite" DisplayName="Site" Type="Text" Hidden="TRUE" />
<Field Name="SelectedList" DisplayName="List" Type="Text" Hidden="TRUE" />
<Field Name="SelectedColumn" DisplayName="Column" Type="Text" Hidden="TRUE" />
<Field Name="FilterValue" DisplayName="Filter value" Type="Text" Hidden="TRUE" />
<Field Name="FilterQuery" DisplayName="Filter query" Type="Text" Hidden="TRUE" />

</Fields>

</PropertySchema>
...

I want to use this field in a Custom List, so I add the following to schema.xml of my List:

<Field ID="{222EF3E9-3470-4619-89EC-7FFCDA18A3FC}" Name="Segment"  SourceID="{F9C3BB43-1EAA-4ac1-8878-EDBD7AFF3B4C}" StaticName="Segment"  Type="CrossSiteLookupFilterFieldControl" DisplayName="Segment" />

This works. But if I want to add a value for the FilterQuery property of the field, something like this doesn't work automatically:

<Field ID="{222EF3E9-3470-4619-89EC-7FFCDA18A3FC}" FilterQuery="Test" Name="Segment"  SourceID="{F9C3BB43-1EAA-4ac1-8878-EDBD7AFF3B4C}" StaticName="Segment"  Type="CrossSiteLookupFilterFieldControl" DisplayName="Segment" />

Solution

In the constructor of your custom field, you can read out the SchemaXml, and find the desired attributes. This way, you can set the values of your custom field with the values in the attributes:

XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(this.SchemaXml);
XmlAttributeCollection fieldAttributes = xmlDoc.FirstChild.Attributes;

if (fieldAttributes["FilterQuery"] != null)
                this.FilterQuery = fieldAttributes["FilterQuery"].Value;

 

PS: More about custom field definitions later..

Tuesday, November 13, 2007

Upload an InfoPath attachment to a document library

To copy a file attachment in an InfoPath form to a document library, I used this code. The "workfiche" object is an instance of a class generated by the XML Class Generator. For more information about this tool, go to http://www.canerten.com/xml-c-class-generator-for-c-using-xsd-for-deserialization/

byte[] attachFile = workfiche.Vlerick_TP_GeneralInformation.Brochure.AttachDraftText;

//get filename
int namebufferlen = attachFile[20] * 2;
byte[] filenameBuffer = new byte[namebufferlen];
for (int i = 0; i < filenameBuffer.Length; i++)
{
          filenameBuffer[i] = attachFile[24 + i];
}
char[] asciiChars = UnicodeEncoding.Unicode.GetChars(filenameBuffer);
string filename = new string(asciiChars);

filename = filename.Substring(0, filename.Length - 1);

//upload to Input Documents doc library

SPFolder inputDocumentsList = web.GetFolder("InputDocuments");

inputDocumentsList.Files.Add(filename, attachFile);

Sharepoint: Programmatically set an alert on a task list for a User

This code adds an alert for all users in a site, with a filter: The task has to be assigned to the user, the status has to be Completed and the Start date has to be less than "today". In other words: the active tasks :-)

foreach (SPUser user in newWeb.SiteUsers)
{
SPAlert alert = newWeb.Alerts.Add();

alert.Filter = "<Query><And><And><Eq><FieldRef Name=\"AssignedTo\"/><Value type=\"string\">" + user.LoginName + "</Value></Eq><Neq><FieldRef Name=\"Status\"/><Value type=\"string\">completed</Value></Neq></And><Leq><FieldRef Name=\"StartDate\"/><Value type=\"datetime\"><Today/></Value></Leq></And></Query>";

alert.User = user;
alert.AlertType = SPAlertType.List;
alert.List = newWeb.Lists["Tasks"];
alert.EventType = SPEventType.Modify;
alert.AlertFrequency = SPAlertFrequency.Immediate;
alert.Update();

}

Friday, November 2, 2007

Modify Members Web Part in code

To set a Members Webpart to "Show people in group" and fill in the group it has to show, use following code:

SPWeb web = GetWeb();

SPWebPartCollection webPartCollection = web.GetWebPartCollection(web.Navigation.Home.Url, Microsoft.SharePoint.WebPartPages.Storage.Shared);
                    webPartCollection.Web.AllowUnsafeUpdates = true;
                    foreach (WebPart webPart in webPartCollection) {
                        if (webPart is MembersWebPart) {
                            MembersWebPart membersWebPart = (MembersWebPart)webPart;
                            membersWebPart.DisplayType = MembersWebPartDisplayType.GroupMembership;
                            membersWebPart.MembershipGroupId = newWeb.Groups["groupName"].ID;
                            webPartCollection.SaveChanges(membersWebPart.StorageKey);
                        }
                    }
                    webPartCollection.Web.AllowUnsafeUpdates = false;

Wednesday, September 26, 2007

TextBox TextChanged client side

The TextChanged event of a TextBox fires when doing a postback in ASP.NET. So when you type something in the textbox, it won't fire before you change the focus or do something else that causes a postback to the server.

To solve this, I found a piece of code that adds a Javascript to the HTML control's OnKeyUp event. This Javascript causes a PostBack, so that your event fires each time you type something in your textbox.

Code:

string js = "javascript:" + ClientScript.GetPostBackEventReference(TextBox1, "@@@@@buttonPostBack") + ";" ;
TextBox1.Attributes.Add("onkeyup", js);

Tuesday, September 11, 2007

Windows Live Writer

Even Windows Live Writer uittesten om op mijn blog berichten te zetten... 't ziet er allemaal wel chique uit :-)

 

EDIT: howla, het werkt nog ook, netjes ;-)

Monday, August 27, 2007

Tafiti

Tafiti: Live Search in Silverlight! Chique :)
Had wel een ander font gekozen voor het zoekveld..

Thursday, August 23, 2007

Sharepoint

Sharepoint. I'm not an expert (yet), but my employer asked me to put my focus on the Sharepoint technology from now on. I got a book, "Real World Sharepoint 2007", so I know what to do in my weekends ;-)

I'm really excited about learning Sharepoint, I always hear a lot about it but I never got the chance learn the details.

Yeah :-)

Monday, August 20, 2007

Microsoft Certified Technology Specialist


Today I passed the Exam 070–528: Microsoft .NET Framework 2.0 - Web-Based Client Development, so now I'm a Microsoft Certified Technology Specialist. Sounds hot :p

I'm proud, yes. :-)

Thursday, May 17, 2007

Encrypt URL ASP.NET

We had to encrypt our url with sensitive querystring information, so we took a look at the world wide web and we found this:

http://www.webpronews.com/expertarticles/2007/01/25/aspnet-httpmodule-for-query-string-encryption

Great, just add a httpModule and it's fixed! Without changing any of your code!

Wednesday, May 9, 2007

Postback in ASP.NET page opend with showModalDialog()

Problem today: we created a ASP.NET page, opened it in a Model Dialog using javascript (showModalDialog()), and when the page did a postback, the stuff crashed.

Found the solution on this site: http://www.geekpedia.com/Question23_Using-showModalDialog()-with-an-ASP.NET-page-that-does-PostBack-opens-another-window.html

Postback in ASP.NET page opend with showModalDialog()

Problem today: we created a ASP.NET page, opened it in a Model Dialog using javascript (showModalDialog()), and when the page did a postback, the stuff crashed.

Found the solution on this site: http://www.geekpedia.com/Question23_Using-showModalDialog()-with-an-ASP.NET-page-that-does-PostBack-opens-another-window.html

place in the tags..

Wednesday, March 28, 2007

Getting rid of the DTC using TableAdapters

DTC is not good for your performance, and it's a pain in the ass to get rid of..

An interesting link is http://weblogs.asp.net/ryanw/archive/2006/03/30/441529.aspx, this solution uses Reflection to set the SqlTransaction object to the commands of your TableAdapter.

But, we wanted to use the (new in .NET 2.0) TransactionScope object instead of the SqlTransaction object to control our transactions.. If I just use the TableAdapters of my typed DataSets as such, i got the DTC running, which isn't good for your performance. So I tried some stuff and I concluded that I had to control the connections of the TableAdapters.

Instead of letting the TableAdapters create their own SqlConnection objects, I had to give them the reference to one and the same SqlConnection object for each TableAdapter.

If you look at my blogpost "Use assemblies containing typed DataSets: How to handle the connection strings?" you can see how I use a Factory class to control my TableAdapters. When invoking the methods to ask the Factory for your desired TableAdapter, you'll have to pass the ConnectionString.
Well, now I made it so that I have to pass a SqlConnection object.

Conclusion: To get rid of the DTC, you'll have to use 1 and the same SqlConnection object for all your TableAdapters AND you'll have to Open() the connection before you use a TransactionScope and you'll have to Close() the connection after the use of a TransactionScope object.

Maybe I'll post an example program, but I haven't got much time.. If you want one, please mail me, I'll do my best..

Monday, March 19, 2007

Use assemblies containing typed DataSets: How to handle the connection strings?

Remark 1: Found syntax- or language errors? Let me know!

Remark 2: I’d rather not get in discussion with people about the use of typed datasets, I’m not a fan of it, but I had to use it at a customer.

The problem
If the user of the application you’re making has to set the connection string in a config file of the application, the user had to define a connection string setting for each assembly that uses typed datasets.
Suppose I have two assemblies named Knrs.ConnectionStringsExample.Db.dll and Knrs.ConnectionStringsExample.Db2.dll, you’ll get something like this:


connectionString="Data Source=.;Initial Catalog=Northwind;Integrated Security=True"
providerName="System.Data.SqlClient" />

connectionString="Data Source=qsf;Initial Catalog=Northwind;Integrated Security=True"
providerName="System.Data.SqlClient" />


Download the zip file containing a Visual Studio 2005 sln with the projects (+ setup project) STEP 1: Problem

What do we want?
We wanted one single connectionstring setting in our config file for all the assemblies!

The solution
Last week, we found a solution for this annoying problem. We studied the handling of connection string in the code that the typed dataset designer creates.

We had a look at the property Connection that the designer generated. It’s a internal property, meaning that the property can be get/set by a file in the same assembly. Because the Windows form project that the assemblies uses, is not the same assembly, we can not set/get the Connection property directly.

So we found out the concept: TableAdapterManager. We created a static class in each assembly that uses typed datasets:
public static class DbTableAdapterManager
{
///


/// Get a SuppliersTableAdapter object with a custom connection string
///

/// The connectionstring you want to assign to the table adapter
public static SuppliersTableAdapter GetSuppliersTableAdapter(string connectionString) {
SuppliersTableAdapter suppliersTableAdapter = new SuppliersTableAdapter();

//Make a SqlConnection with the given connection string
SqlConnection sqlConnection = new SqlConnection(connectionString);

//Assign the connection to the table adapter
suppliersTableAdapter.Connection = sqlConnection;

return suppliersTableAdapter;
}
}

(for full code, download the sln file!)

As you can see, it has a method called Get[tableadapternamehere](string connectionString) and it’s pretty obvious what it does: it just replaces the connection of a new instance of the table adapter class by a new connection using a connection string that you can pass in the method.

If you make such a class for each assembly that uses typed datasets, your problem is solved! You can just place 1 connectionstring in the config file of the application and pass this connectionstring in the method to get the desired table adapter!

The only little contra for this method is that you can not just make an instance of the table adapter class, but that you have to ask the TableAdapterManager class for an instance..

But it’s easier and cleaner for the users of your application to just define 1 connectionstring, instead of defining 2, 3, …, x connectionstrings.

Download the zip file containing a Visual Studio 2005 sln with the projects (+ setup project) STEP 2: Solution

Questions? koen [dot] roos [at] telenet [dot] be

Thanks to Mark Devos (calidos.be)