Tuesday, December 25, 2007
Lesswoodstock 2008: The Black Box Revelation
Monday, December 24, 2007
Free ASP.NET Hosting
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:
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
Thursday, August 23, 2007
Sharepoint
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. :-)
Monday, June 4, 2007
Wednesday, May 30, 2007
Thursday, May 17, 2007
Encrypt URL ASP.NET
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()
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()
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
Wednesday, March 28, 2007
Getting rid of the DTC using TableAdapters
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:
providerName="System.Data.SqlClient" />
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
Thanks to Mark Devos (calidos.be)