Dailycode.info

Short solution for short problems

Save a HTML page to File

When I print a report, I create a html page that the user can print. Now, there was a need to backup these reports. So I had to find out how to save the html code to a file. The solution was very simple:

System.Net.WebClient client = null;
try
{
    client = new System.Net.WebClient();
    client.Encoding = System.Text.Encoding.UTF8;
    client.Headers = new System.Net.WebHeaderCollection();
    client.Headers.Add("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2;)");
    string url = Request.Url.ToString().Replace("administration","reports");
    url = url.Replace("reports.aspx", "reportprint.aspx?reportGuid=" + guid);
    //string html = client.DownloadString(url);
    string path = ConfigurationManager.AppSettings["ArchivePath"] + guid +".html";
    client.DownloadFile(url, path);
 
    
}
catch (Exception r) 
{
    HandleError(r);
}
finally
{
    client.Dispose();
}
 

The url string had to be the full url, no relative url's. So I took the url of the current page and replaced 2 parts of the url to make the correct url. The path where the report are saved is stored in the config file. 

All this worked fine until I deployed it to the server. Then I got this lovely error:

 The remote server returned an error: (401) Unauthorized.System.Net.WebException: The remote server returned an error: (401) Unauthorized. at System.Net.WebClient.DownloadFile(Uri address, String fileName) at System.Net.WebClient.DownloadFile(String address, String fileName) … 

The solution is simple, you need to pass credentials to the proxy. If the default credentials are ok, just set the usedefaultcredentials flag to true. The proxy will now detect the default credentials and use these. When your website is set to Use integrated windows authentication, then this the default credentials should pass your proxy and the problem should be solved.

 
System.Net.WebClient client = null;
try
{
    client = new System.Net.WebClient();
    client.Encoding = System.Text.Encoding.UTF8;
    client.Headers = new System.Net.WebHeaderCollection();
    client.Headers.Add("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2;)");
    client.UseDefaultCredentials = true;
    string url = Request.Url.ToString().Replace("administration","reports");
    url = url.Replace("reports.aspx", "reportprint.aspx?reportGuid=" + guid);
    //string html = client.DownloadString(url);
    string path = ConfigurationManager.AppSettings["ArchivePath"] + guid +".html";
    client.DownloadFile(url, path);
 
 
}
catch (Exception r) 
{
    HandleError(r);
}
finally
{
    client.Dispose();
}


How to create an image map?

Very simple, all the code you'll need, you can find below:

<MAP NAME="map1">

<AREA

   HREF="contacts.html" ALT="Contacts" TITLE="Contacts"

   SHAPE=RECT COORDS="6,116,97,184">

<AREA

   HREF="products.html" ALT="Products" TITLE="Products"

   SHAPE=CIRCLE COORDS="251,143,47">

<AREA

   HREF="new.html" ALT="New!" TITLE="New!"

   SHAPE=POLY COORDS="150,217, 190,257, 150,297,110,257">

</MAP>

<IMG SRC="testmap.gif"

   ALT="map of GH site" BORDER=0 WIDTH=300 HEIGHT=300

   USEMAP="#map1"><BR>

You can find this example here
If you are having problems finding the correct coördinates for the image mappings, open the image in paint or any image editor. Put the mouse on the top left corner of the first piece where you want your map. Write down the coördinates. Then put the cursor in the bottom right corner and write down the coördinates. You can do this for all the mappings you need on the image. Eg.

 
<area href="modules.aspx" shape="rect" coords="42,12,143,80" 
alt="Modules" title="Manage modules"> 
<area href="processes.aspx" shape="rect" coords="183,12,285,80" 
alt="Processes" title="Manage processes"> 

42,12 are the coördinates of the upper left corner, 143,80 are the coördinates of the lower right corner.

Why use them? It's a very nice extra navigation option. Make a site overlay picture and add links to each action. When navigation is complex, it could provide an easy overview.

If you want to use images or other object on the picture, you should work with DIV and absolute positions.


Custom approval workflow

I've created a workflow for document approval in a large firm. There are 4 states in the workflow that a document can have: Draft, Reviewed, Approved, Published.

At first the document is uploaded and recieves the status Draft. The workflow kicks in here! A task is created for all users who are in the sharepoint group 'Reviewers'. They recieve a mail that referres to the task that is created for them. One of the reviewers needs to complete the task in order to activate the workflow to the next fase.

The status of the document is set to Reviewed. When a task is created the user will be informed of it by email to. The email holds the link to the item, in this case the document and it has a link to the task. Here is the content of an email that was send after creating the review task when a author uploaded a document.

So the user has 2 options, read the document via the link, and set the task to complete when he approves with the content. Whe he disapproves, he has to start a manual workflow to inform the reviewer about the corrections that need to be done.

When the last approvers give green light, the document will be published, first the 2nd approvers are asked to give the users who need to be informed about the new document and then it will be copied to an document library that has read permissions for these users. The email that informs the uses holds a link to a webpart page. This page has a webpart that contains a link to the document and a button for read confirmation. The complete workflow can be visualized like this:

This is how it looks like in Sharepoint Designer:

There are still some things that can be improved, but we decided to implement the workflow now because the time it will save. At this moment they manage using outlook only.


Send email to user group in Sharepoint Workflow

Should be easy, yes, but can be difficult. When you notice no email is sent, but no error message is delivered. Maybe this can help the ones who are having simular problems:

There are 2 settings you have to make in your Sharepoint user group, the first one is to give everyone view acces to this group. Like this:

Then the user group should have read permissions on the site.

Now the workflow can send email's to sharepoint groups.


How to rename workflow in sharepoint designer?

Although the function: Rename is available by rightclicking the workflow name, it will not correctly rename the workflow. The problem is that it only changes the title but in the config file, still the old name exists. We will need to perform some manual tasks after you renamed all the files!

Open the OLDWFName.xoml.wfconfig.xml file.

Change the path name to the new name. I will give an example below.

OLD name was New SOP WF, new name is: SOP WF

Old config file was:

<WorkflowConfig>
    <Template        BaseID="{FD02CD7B-DE27-486F-BF2B-65E807ACD1E4}"
        DocLibID="{F1E7A575-B9D8-415B-9BF7-7BD5CC293DD0}"
        XomlHref="Workflows/New SOP WF/New SOP WF.xoml"
        XomlVersion="V75.0"
        RulesHref="Workflows/New SOP WF/New SOP WF.xoml.rules"
        RulesVersion="V75.0"    >
    </Template>
    <Association        ListID="{DB955486-0511-44DD-ACE4-B1649ACF4859}"
        TaskListID="{40FC9FF0-2B2F-4825-A184-49E772277C1F}"
        StartManually="true"        StartOnCreate="true"    >
    </Association>    <ContentTypes>        ... 
    </ContentType>    </ContentTypes>
    <Initiation URL="Workflows/New SOP WF/New SOP WF.aspx">
        <Fields/>
        <Parameters></Parameters>
    </Initiation></WorkflowConfig>

 

The new config file looks like this (I placed the part I changed in bold):

<WorkflowConfig>
    <Template        BaseID="{FD02CD7B-DE27-486F-BF2B-65E807ACD1E4}"
        DocLibID="{F1E7A575-B9D8-415B-9BF7-7BD5CC293DD0}"
        XomlHref="Workflows/SOP WF/New SOP WF.xoml"
        XomlVersion="V75.0"
        RulesHref="Workflows/SOP WF/New SOP WF.xoml.rules"
        RulesVersion="V75.0"    >
    </Template>
    <Association        ListID="{DB955486-0511-44DD-ACE4-B1649ACF4859}"
        TaskListID="{40FC9FF0-2B2F-4825-A184-49E772277C1F}"
        StartManually="true"
        StartOnCreate="true"    >
    </Association>
    <ContentTypes>        ... 
    </ContentType>
    </ContentTypes>
    <Initiation URL="Workflows/SOP WF/New SOP WF.aspx">
        <Fields/>
        <Parameters></Parameters>
    </Initiation></WorkflowConfig> 

How to export a WSS 2003 List to WSS 2007?

There where several optoins that crossed my mind.

  1. Saving the list as template and importing it into WSS 2007.
  2. Exporting the WSS 2003 list to a spreadsheet and creating a new list in WSS 3.0 (or 2007) based on the spreadsheet.
  3. Worst case, copy and paste the data in DataSheet view.

 

But guess what, none of the above worked. When I tried the 2nd solution, I got presented with this beautifull error: Method 'Post' of object 'IOWSPostData' failed .

It was hard labour trying to find a solution. Finally I found it on this site: http://www.tek-tips.com/viewthread.cfm?qid=1330234&page=1

The solution is (as described in the above link) the following:

Open the Excel Add-In EXPTOOWS.XLA located in C:\Program Files\Microsoft Office\Office12\1033 by default.Press Alt+F11 to display the Visual Basic code editor and search (Ctrl+F) for the line lVer = Application.SharePointVersion(URL). Comment out that line with a single quote and add the line lVer=2 so your Intialize() method should now look like this:

 
Sub Initialize(List, Title, URL, QuickLaunch)
strQuickLaunch = QuickLaunch
aTarget(iPublishURL) = URL
aTarget(iPublishListName) = List
aTarget(iPublishListDesc) = Title
'lVer = Application.SharePointVersion(URL)
lVer = 2
End Sub
 

You only have to change this once, because you change the script of the Add-In. All future export spreadsheets will work!.Hope this helps...

* I had some more problems, but I guess they are related to my hosting company. When I try to import from the spreadsheet, I get this nice error: Cannot connect to the server at this time. Your list cannot be published. So to work around this error, I first imported it into a local WSS site, saved it as a template and uploaded this template to my online WSS hosting. Then I created the list. A lot of work maybe, maybe 10 minutes per List, but not as much as typing the whole thins again or copy paste each field. Here are some more workarounds for this problem: http://support.microsoft.com/kb/838703


Playing a beep sound on windows CE devices

Beeping.... 

It was not such a pleasant search, looking for code that works on all CE devices. But finally I combined some stuff and this works fine!

public enum PlaySoundFlags : int        
{            
 SND_SYNC = 0x0,     // play synchronously (default)            
 SND_ASYNC = 0x1,    // play asynchronously            
 SND_NODEFAULT = 0x2,    // silence (!default) if sound not found            
 SND_MEMORY = 0x4,       // pszSound points to a memory file            
 SND_LOOP = 0x8,     // loop the sound until next sndPlaySound            
 SND_NOSTOP = 0x10,      // don't stop any currently playing sound            
 SND_NOWAIT = 0x2000,    // don't wait if the driver is busy            
 SND_ALIAS = 0x10000,    // name is a registry alias            
 SND_ALIAS_ID = 0x110000,// alias is a predefined ID            
 SND_FILENAME = 0x20000, // name is file name            
 SND_RESOURCE = 0x40004 // name is resource name or atom        
}         
[DllImport("coredll.dll")]        
public static extern int PlaySound(string szSound,IntPtr hModule,int flags);
      
public static void Beep()        
{  
 Play(@"\Windows\Voicbeep");        
} 
public static void Play(string fileName)        
{            
 try            
 { 
  PlaySound(fileName, IntPtr.Zero, (int)
  (PlaySoundFlags.SND_FILENAME | PlaySoundFlags.SND_SYNC)); 
 } 
 catch {} 
} 

 

Just call the Beep() method to create a beep sound!

You'll need to include these namespaces:

System.Runtime.InteropServices; // for PInvoke
Microsoft.Win32;


On bubble event

When you are creating ASP.net pages and content is build up from a DB, you'll probably work with usercontrols, placeholders ...

Recently I encountered another challenge. I was adding usercontrols to a placeholder to a page dynamically. The user controls have a save button which saved the data to the database. But then the parent page needed to be refreshed. I could do this by using a redirect to the page and it would be refreshed. But when data grows, a page refresh is less and less attractive. So I googled and came up with this solution: The OnBubbleEvent. If you want to read on what the event actually does, please google some more, I can show you a practical example.

Put this code on the page that calls holds the user control.   

    protected override bool OnBubbleEvent(object source, EventArgs args)    
    {
        CommandEventArgs e = (CommandEventArgs)args;        
        if (e.CommandName == "Rebind")        
       {            ClearThePlaceHolders();            
            RefreshTheData();       
        }
        return true;
    } 

   

When you finished the event (After you saved the data) on the user control, you can activate the bubble event!

    CommandEventArgs args = new CommandEventArgs("Rebind", String.Empty);
    RaiseBubbleEvent(null, args);
 

So now, only the part we choose is refreshed. This way the asp stays light weight.


Short if Structure

A quick and to many probably stupid post, but I keep forgetting the short if structure, so to save the googling everytime I need it, here it is: 

 rep.IsActive = (row["is_active"].ToString()=="1"?true:false);
 

object result = (comparisonA==comparisonB?returnObjectA(true):returnObjectB(false));

 bool bMe = (Myname=="Mark"?true:false);
 
If Myname == "Mark", then nMe is true, else it's false!