Showing posts with label Programming. Show all posts
Showing posts with label Programming. Show all posts

Saturday, November 15, 2008

Get and Manipulate Temporary Internet Explorer Files using C# .NET Code

 
Recently, I want to access my temporary Internet Explorer files based on some filter (for example: get all jpg files which are downloaded from http://www.google.com or get all pdf files which are downloaded from  http://www.yahoo.com)  and get their absolute path in order to copy them to another folder. I hardly found articles that can help me to do it. Usually they don't solve all my problem. Most of all articles gives code about deleting temporary Internet Explorer files , not getting and manipulating it. After googling a few hours, I found some articles that (although not completely) solve my problems.

First , article from this blog http://blog.datalisto.com/2008/08/accessing-contents-of-ie-cache.html gives a solution about how to convert url like http://www.google.com/images/nav_logo3.png to its actual path C:\Documents and Settings\agoesz\Local Settings\Temporary Internet Files\Content.IE5\FMGZR189\nav_logo3[1].png using GetUrlCacheEntryInfo method from Wininet.dll assembly. This article also help you to understand how Internet Explorer store its temporary files.

Second, articles from this blog http://omnicode.blogspot.com/2008/04/extract-urls-from-internet-explorerie.html gives a solution (this is the code : http://omniware.googlepages.com/getIEhistory.html ) about how to enumerate / list all the history in our Temporary Internet Explorer folder.

I combine this two solution to solve my problem.

Here are the code. This code is done using C# on Console Application.

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;

using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
namespace ConsoleApplication1
{
class Program
{

#region Get Files URL From Cache
//Declare the WIN32 API calls to get the entries from IE's history cache
[DllImport("wininet.dll", SetLastError = true)]
public static extern IntPtr FindFirstUrlCacheEntry(string lpszUrlSearchPattern, IntPtr lpFirstCacheEntryInfo, out UInt32 lpdwFirstCacheEntryInfoBufferSize);

[DllImport("wininet.dll", SetLastError = true)]
public static extern long FindNextUrlCacheEntry(IntPtr hEnumHandle, IntPtr lpNextCacheEntryInfo, out UInt32 lpdwNextCacheEntryInfoBufferSize);

[DllImport("wininet.dll", SetLastError = true)]
public static extern long FindCloseUrlCache(IntPtr hEnumHandle);

[DllImport("Wininet.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern Boolean GetUrlCacheEntryInfo(String lpxaUrlName, IntPtr lpCacheEntryInfo, ref int lpdwCacheEntryInfoBufferSize);
[StructLayout(LayoutKind.Sequential)]
public struct INTERNET_CACHE_ENTRY_INFO
{
public UInt32 dwStructSize;
public string lpszSourceUrlName;
public string lpszLocalFileName;
public UInt32 CacheEntryType;
public UInt32 dwUseCount;
public UInt32 dwHitRate;
public UInt32 dwSizeLow;
public UInt32 dwSizeHigh;
public FILETIME LastModifiedTime;
public FILETIME ExpireTime;
public FILETIME LastAccessTime;
public FILETIME LastSyncTime;
public IntPtr lpHeaderInfo;
public UInt32 dwHeaderInfoSize;
public string lpszFileExtension;
public UInt32 dwExemptDelta;
};

public static class Hresults
{
public const int ERROR_SUCCESS = 0;
public const int ERROR_FILE_NOT_FOUND = 2;
public const int ERROR_ACCESS_DENIED = 5;
public const int ERROR_INSUFFICIENT_BUFFER = 122;
public const int ERROR_NO_MORE_ITEMS = 259;
};
//private static void getUrlEntriesInHistory(TextWriter writer)
private static List<string> getUrlEntriesInHistory(string sourceUrlFilter, string fileExtensionFilter)
{
List<string> filesList = new List<string>();
IntPtr buffer = IntPtr.Zero;
UInt32 structSize;
const string urlPattern = "Visited:";

//This call will fail but returns the size required in structSize
//to allocate necessary buffer
IntPtr hEnum = FindFirstUrlCacheEntry(null, buffer, out structSize);
try
{
if (hEnum == IntPtr.Zero)
{
int lastError = Marshal.GetLastWin32Error();
if (lastError == Hresults.ERROR_INSUFFICIENT_BUFFER)
{
//Allocate buffer
buffer = Marshal.AllocHGlobal((int)structSize);
//Call again, this time it should succeed
//hEnum = FindFirstUrlCacheEntry(urlPattern, buffer, out structSize);
hEnum = FindFirstUrlCacheEntry(null, buffer, out structSize);
}
else if (lastError == Hresults.ERROR_NO_MORE_ITEMS)
{
Console.Error.WriteLine("No entries in IE's history cache");
//return;
return filesList;
}
else if (lastError != Hresults.ERROR_SUCCESS)
{
Console.Error.WriteLine("Unable to fetch entries from IE's history cache");
//return;
return filesList;
}
}


INTERNET_CACHE_ENTRY_INFO result = (INTERNET_CACHE_ENTRY_INFO)Marshal.PtrToStructure(buffer, typeof(INTERNET_CACHE_ENTRY_INFO));
//writer.WriteLine(result.lpszSourceUrlName);
string fileUrl = result.lpszSourceUrlName.Substring(result.lpszSourceUrlName.LastIndexOf('@') + 1);
if (fileUrl.Contains(sourceUrlFilter) && fileUrl.EndsWith(fileExtensionFilter))
{
//Console.WriteLine(fileUrl);
filesList.Add(fileUrl);
}


// Free the buffer
if (buffer != IntPtr.Zero)
{
try { Marshal.FreeHGlobal(buffer); }
catch { }
buffer = IntPtr.Zero;
structSize = 0;
}

//Loop through all entries, attempt to find matches
while (true)
{
long nextResult = FindNextUrlCacheEntry(hEnum, buffer, out structSize);
if (nextResult != 1) //TRUE
{
int lastError = Marshal.GetLastWin32Error();
if (lastError == Hresults.ERROR_INSUFFICIENT_BUFFER)
{
buffer = Marshal.AllocHGlobal((int)structSize);
nextResult = FindNextUrlCacheEntry(hEnum, buffer, out structSize);
}
else if (lastError == Hresults.ERROR_NO_MORE_ITEMS)
{
break;
}
}

result = (INTERNET_CACHE_ENTRY_INFO)Marshal.PtrToStructure(buffer, typeof(INTERNET_CACHE_ENTRY_INFO));
//writer.WriteLine(result.lpszSourceUrlName);
fileUrl = result.lpszSourceUrlName.Substring(result.lpszSourceUrlName.LastIndexOf('@') + 1);
if (fileUrl.Contains(sourceUrlFilter) && fileUrl.EndsWith(fileExtensionFilter))
{
//Console.WriteLine(fileUrl);
filesList.Add(fileUrl);
}


if (buffer != IntPtr.Zero)
{
try { Marshal.FreeHGlobal(buffer); }
catch { }
buffer = IntPtr.Zero;
structSize = 0;
}
}
}
finally
{
if (hEnum != IntPtr.Zero)
{
FindCloseUrlCache(hEnum);
}
if (buffer != IntPtr.Zero)
{
try { Marshal.FreeHGlobal(buffer); }
catch { }
}
}
return filesList;
}

#endregion

const int ERROR_FILE_NOT_FOUND = 2;
#region Convert Files URL to Actual Directory URL
struct LPINTERNET_CACHE_ENTRY_INFO
{
public int dwStructSize;
IntPtr lpszSourceUrlName;
public IntPtr lpszLocalFileName;
int CacheEntryType;
int dwUseCount;
int dwHitRate;
int dwSizeLow;
int dwSizeHigh;
FILETIME LastModifiedTime;
FILETIME Expiretime;
FILETIME LastAccessTime;
FILETIME LastSyncTime;
IntPtr lpHeaderInfo;
int dwheaderInfoSize;
IntPtr lpszFileExtension;
int dwEemptDelta;
}
private static string GetPathForCachedFile(string fileUrl)
{
int cacheEntryInfoBufferSize = 0;
IntPtr cacheEntryInfoBuffer = IntPtr.Zero;
int lastError; Boolean result;
try
{
// call to see how big the buffer needs to be
result = GetUrlCacheEntryInfo(fileUrl, IntPtr.Zero, ref cacheEntryInfoBufferSize);
lastError = Marshal.GetLastWin32Error();
if (result == false)
{
if (lastError == ERROR_FILE_NOT_FOUND) return null;
}
// allocate the necessary amount of memory
cacheEntryInfoBuffer = Marshal.AllocHGlobal(cacheEntryInfoBufferSize);

// make call again with properly sized buffer
result = GetUrlCacheEntryInfo(fileUrl, cacheEntryInfoBuffer, ref cacheEntryInfoBufferSize);
lastError = Marshal.GetLastWin32Error();
if (result == true)
{
Object strObj = Marshal.PtrToStructure(cacheEntryInfoBuffer, typeof(LPINTERNET_CACHE_ENTRY_INFO));
LPINTERNET_CACHE_ENTRY_INFO internetCacheEntry = (LPINTERNET_CACHE_ENTRY_INFO)strObj;
//INTERNET_CACHE_ENTRY_INFO internetCacheEntry = (INTERNET_CACHE_ENTRY_INFO)strObj;
String localFileName = Marshal.PtrToStringAuto(internetCacheEntry.lpszLocalFileName); return localFileName;
}
else return null;// file not found
}
finally
{
if (!cacheEntryInfoBuffer.Equals(IntPtr.Zero)) Marshal.FreeHGlobal(cacheEntryInfoBuffer);
}
}
#endregion

[STAThread]
static void Main(string[] args)
{
string urlFilter = "http://www.google.com";
string fileExtensionFilter = "png";

List<string> myFiles = getUrlEntriesInHistory(urlFilter, fileExtensionFilter);
foreach (string actualPathName in myFiles)
{
string realPath = GetPathForCachedFile(actualPathName);
Console.WriteLine("actualPathName : " + actualPathName);
Console.WriteLine("realPath : " + realPath);
}

}


}
}


 


Here is the screenshot of the result in my computer :



screenshot



after you get the absolute path, you can manipulate it as you wish, such as moving it to another folder etc



Note:

I know this code is not well-tested and may be not efficient, there are two struct defined with almost same content,just different data type. Use it at your own risk. It works on my machine. But it may be not working in yours. I just combine the codes from above sites and it worked. It's enough for me :) If you have another solution better than this. Please let me know. Hope it helps.

Monday, July 21, 2008

SharePoint Document Library TreeView Browser WebPart

SharePoint have a nice way to organize your files in document library by using folders, just like Windows Explorer does. But it lacks one important feature, the treeview browser. That's what I need in my last project requested by my client. I found great SharePoint Document Library TreeView Browser WebPart, called SharePoint Stramit Document Library browser , made by Renaud Comte, Microsoft MVP. And the best part, it's free :) You could see the source code given by Renaud Comte too in the CodePlex.

There are 3 kinds of treeview here (the description quoted from CodePlex):
1. Static TreeView ,a simple TreeView WP load in one shot the document library hierachy
2. Dynamic TreeView, for large library, the loading append when you open a folder
3. Multiple TreeView, This WebPart load automatically all the document library on your local WSS site. It's a derived version of the dynamic treeview, so not big problem if there some huge lists.

To use it, just use stsadm.exe utility (read Sean Chase tutorial here, see point 22). Here are some screenshots from the CodePlex. All the credits go to Renaud Comte. Cool and great job!

Tree1

Tree4

MultipleTV

SharePoint Polling WebPart

I just found a great Polling WebPart from Phil Wicklund. You can find it in CodePlex or in his personal site to check for working examples, screenshots etc. It works great in my last project. There are some restrictions (in current release), like the poll submitter can submit twice/ more and max 5 poll questions / answers. But it can be customized according to our requirements. Phil has given the source code too. Just download it from CodePlex. In order to use it, just use the stsadm.exe command tool (read this tutorial by liedong). All the credit goes to Phil Wicklund. Great Job!

Here are some screenshots taken from Phil's website.

Tuesday, June 17, 2008

Printing a Web Page

I found a useful article to print a web page. The fastest and simplest solution is using javascript window.print()

<input type="button" value="Print" onclick="window.print();">

If you click this button , it will show this print dialog

printdialog

This example will print the entire web page,including the print button and anything else. To get better result, we can make two versions of web page, one for showing on screen and the other one for printing

On Screen 1
===========
<input type="button" value="Print" onclick="window.open("Print.aspx");">

On Screen 2 (Print.aspx)
======================
<script language="javascript"> window.print(); </script>

For screen version it is best to use pixels to define size of fonts, tables, images etc., as you probably already do. But, for print version use points instead of pixels. Example:

body {   font: 12pt; }

Actually, we don't have to make separate page to prepare printing version. There is a way use only 1 page, but we can differentiate show-on-screen version and printing version by using CSS like this example

<LINK rel="stylesheet" href="style-for-screen.css" type="text/css" media="screen">
<LINK rel="stylesheet" href="style-for-print.css" type="text/css" media="print">

As you may notice, there is media attribute inside the LINK tag. Web browsers will use style-for-screen.css to show page on screen and style-for-print.css to print the page. For example, let say we want to hide <div id="menu"> in printing version, so in style-for-print.css we can add this code:

#menu {   display: none; }

Or if we don't want to have two separate CSS files, we can do it all in one file, like this :

@media print  {
    /*****   Styles for print   ******/
      h1, h2, h3 {color:black;}
      #navigation {display:none}
}
 
@media screen {
      /*****   Styles for screen  ******/
}

You can see the details on this Printing in ASP.NET article. All credits should go to the original poster of this article.

Friday, June 13, 2008

Using Sharepoint Web Controls (PeopleEditor and InputFormTextBox) in Microsoft Office Sharepoint Designer 2007

Recently, in my last project, I used SPD a lot. I want to share how to use sharepoint web controls ,especially PeopleEditor (used to select user/multiple users) and InputFormTextbox (used for Enhanced Rich Text).

To use Sharepoint  InputFormTextBox , just use this code in your SPD.

<SharePoint:InputFormTextBox ID="txtBxDetails" RichText="true"  RichTextMode="Compatible" runat="server" TextMode="MultiLine" Rows="10" Columns="10" ></SharePoint:InputFormTextBox>

The result will be like this

Sharepoint InputFormTextBox

To use Sharepoint  PeopleEditor , just use this code in your SPD.

<SharePoint:PeopleEditor id="peopleEditor" runat="server" IsValid="true" AllowEmpty="false" Height="20px" Width="200px" BackColor="Cornsilk" AllowTypeIn="true" MultiSelect="false" />

To enable multiple selection , just set MultiSelect property to true, false otherwise.
The result will be like this


Sharepoint PeopleEditor

Here are the sources if you want to see the details : InputFormTextBox and PeopleEditor

Tuesday, April 29, 2008

Accessing Infopath 2007 XML Data Source using Infopath Deserialization

Usually, if we want to access Infopath data source , we can use XPathNavigator class. Take a look of this example. Suppose we have infopath form with this data source.


This infopath form template have 2 data source :
- name , data type : text(string) , default value : Carlos
- age, data type : whole number (integer), default value : 25

You can use this code to get the value of name / age :
public void FormEvents_Loading(object sender, LoadingEventArgs e)
{
// Write your code here.
XPathNavigator root = MainDataSource.CreateNavigator();
XPathNavigator nameNode = root.SelectSingleNode("/my:myFields/my:Name", NamespaceManager);
EventLog.WriteEntry("InfopathTest", "The name is " + nameNode.Value);
}

The result :


I found a nice way to navigate the infopath data source using deserialization. You can read the details in the Patrick Tisseghem's article here

I modify the code a bit to be usable in this example. The first thing you have to do is save the infopath form as source files (Click File - Save as Source Files in the Infopath menubar) or just use WinZip / WinRAR / other software archiver to extract the xsn files.

Save it to your harddisk (ex : C:\temp\Infopath\sourcefiles). There are some files that will be extracted by Infopath like this picture below.

The only file you have to use is the myschema.xsd. Close your Infopath form (if you don't do this, the program cannot generate the class). Open Visual Studio command prompt and navigate to the path you selected before (in my example : C:\temp\Infopath\sourcefiles ) and run this code
XSD myschema.xsd /c


This command will generate .NET classes from the schema we provided (in C# by default). In this example it will generate a file named myschema.cs

Next step, add this class to your infopath code


Then use this code to retrieve the value of name / age.

public void FormEvents_Loading(object sender, LoadingEventArgs e)
{
// Write your code here.
XPathNavigator root = MainDataSource.CreateNavigator();
XPathNavigator node = root.SelectSingleNode("/my:myFields", NamespaceManager);
myFields mf = new myFields();
XmlSerializer xs = new XmlSerializer(typeof(myFields));
UTF8Encoding encoding = new UTF8Encoding();
byte[] fileBytes = encoding.GetBytes(node.OuterXml);
MemoryStream ms = new MemoryStream(fileBytes);
mf = (myFields)xs.Deserialize(ms);
EventLog.WriteEntry("InfopathTest", mf.Name + " is " + mf.Age + " years old");
}

don't forget to add these codes to the using section

using System.Xml.Serialization;
using System.IO;
using System.Text;

The result :


In my opinion, this is a nice and cool way to access Infopath data source. We don't have to care about any XPath query again. Feel free to give your comment here.

Wednesday, March 19, 2008

Write Microsoft Excel File in ASP .NET

As usual , I want to share my experience in my last project. The topic is how to write excel file in ASP .NET

1. Use Response object from System.Web
First, you have to make the content in HTML table code. Here is the example:

string filename = "myexcel.xls";
Response.Clear();
Response.Buffer = true;
Response.ContentType = "application/vnd.ms-excel";
Response.AddHeader("Content-Disposition", "inline;filename=" + filename);
Response.Charset = "";
this.EnableViewState = false;
Response.Write("<table border='1'><tr><th>test title</th><th>test title2</th></tr><tr><td>value1</td><td>value2</td></tr></table>");
Response.Flush();
Response.Close();

Here is the result if you execute this code


2. Another way to create excel file is using this class below( I forgot where I took this code. I will post the source URL as soon as I find it). Don't forget to add Microsoft Office 12.0 object library reference. Add this line of code to the using section

using Excel = Microsoft.Office.Interop.Excel;

Here is the class code :

class ExcelHelper
{
private Excel.Application _excelApplication;

public ExcelHelper()
{
_excelApplication = new Excel.Application();
}

public Excel.Workbook Create(string caption, string heading1)
{
try
{
_excelApplication.Caption = caption;
_excelApplication.ScreenUpdating = false;
_excelApplication.Visible = false;

Excel.Workbook book = _excelApplication.Workbooks.Add(Excel.XlSheetType.xlWorksheet);
Excel.Worksheet sheet = (Excel.Worksheet)book.ActiveSheet;

Excel.Range r = (Excel.Range)sheet.Cells[1, "A"];
r.Value2 = heading1;
r.EntireRow.Font.Bold = true;

return book;

}
catch (Exception ex)
{
throw (ex);
}
}

public void Close()
{
_excelApplication.ScreenUpdating = true;
_excelApplication.Visible = true;
_excelApplication.DisplayAlerts = true;

if (_excelApplication != null)
{
_excelApplication.Quit();
_excelApplication = null;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
}

I will give two examples how to use this ExcelHelper class:
- First, this example will write cell A4 and B4 with "Parent Directory" and "Testing" and also make all the cell font bold.

ExcelHelper _eh = new ExcelHelper();
object _missing = System.Reflection.Missing.Value;
Excel.Workbook _book;
Excel.Worksheet _sheet;
Excel.Range _rng;
int _row = 4;
string caption = "Test Caption";
string heading1 = "Test Heading1";
_book = _eh.Create(caption, heading1);
_sheet = ((Excel.Worksheet)_book.ActiveSheet);

_rng = ((Excel.Worksheet)_book.ActiveSheet).get_Range("A4", "H3");
_rng.Font.Bold = true;
_rng.EntireRow.Font.Bold = true;

_sheet.Columns.ColumnWidth = 30;

_rng = (Excel.Range)_sheet.Cells[_row, "A"];
_rng.Value2 = "Parent Directory";
_rng = (Excel.Range)_sheet.Cells[_row, "B"];
_rng.Value2 = "Testing";

_eh.Close();

- Second, use this ExcelFileReport class (I took this code from someone's blog, I will post the source URL as soon as I find it)

public class ExcelFileReport
{
private object _missing;
private Excel.Workbook _book;
Excel.Worksheet _sheet;
Excel.Range _rng;
int _row;
private DirectoryInfo _di;
ExcelHelper _eh = new ExcelHelper();

public ExcelFileReport(DirectoryInfo di)
{
_di = di;
_missing = System.Reflection.Missing.Value;
_row = 4;
}

public void DocumentDirectory(DirectoryInfo di)
{
foreach (DirectoryInfo d in di.GetDirectories())
{
DocumentDirectory(d);
}

foreach (FileInfo f in di.GetFiles())
{
_row++;
_rng = (Excel.Range)_sheet.Cells[_row, "A"];
_rng.Value2 = di.Name;
_rng = (Excel.Range)_sheet.Cells[_row, "B"];
_rng.Value2 = f.FullName;
_rng = (Excel.Range)_sheet.Cells[_row, "C"];
_rng.Value2 = f.Name;
_rng = (Excel.Range)_sheet.Cells[_row, "D"];
_rng.Value2 = f.Length;
_rng = (Excel.Range)_sheet.Cells[_row, "E"];
_rng.Value2 = f.Extension;
_rng = (Excel.Range)_sheet.Cells[_row, "F"];
_rng.Value2 = f.LastWriteTime.ToLongDateString();
}
}

public void Generate()
{
string caption = "File Analysis Results";
string heading1 = "File Analysis Report for Folder " + _di.FullName;
_book = _eh.Create(caption, heading1);
_sheet = ((Excel.Worksheet)_book.ActiveSheet);
WriteTableHeader();
DocumentDirectory(_di);
SetAutoFilter();
_eh.Close();
}

private void SetAutoFilter()
{
string lastrow = "F" + _row.ToString();
_rng = ((Excel.Worksheet)_book.ActiveSheet).get_Range("A4", lastrow);
_rng.AutoFilter(1, _missing, Excel.XlAutoFilterOperator.xlAnd, _missing, true);
_rng.Borders.LineStyle = Excel.XlLineStyle.xlContinuous;
}

public void WriteTableHeader()
{
_rng = ((Excel.Worksheet)_book.ActiveSheet).get_Range("A4", "H3");
_rng.Font.Bold = true;
_rng.EntireRow.Font.Bold = true;

_rng = (Excel.Range)_sheet.Cells[_row, "A"];
_rng.Value2 = "Parent Directory";
_rng = (Excel.Range)_sheet.Cells[_row, "B"];
_rng.Value2 = "Full Path";
_rng = (Excel.Range)_sheet.Cells[_row, "C"];
_rng.Value2 = "File Name";
_rng = (Excel.Range)_sheet.Cells[_row, "D"];
_rng.Value2 = "Size";
_rng = (Excel.Range)_sheet.Cells[_row, "E"];
_rng.Value2 = "Type";
_rng = (Excel.Range)_sheet.Cells[_row, "F"];
_rng.Value2 = "Last Modified";

_sheet.Columns.ColumnWidth = 30;
}
}
And use this code to invoke the Generate method :

DirectoryInfo di = new DirectoryInfo(@"C:\Program Files\Microsoft Office\OFFICE12\1033");
ExcelFileReport efr = new ExcelFileReport(di);
efr.Generate();

Please remind that these two examples will run Microsoft Excel and "Do you want to save" dialog will pop up. Therefore you must have Microsoft Excel installed in the PC you want to execute this code. Here is the result if you execute the last code

If you have any suggestion or have better and optimized code, please put your comment. Any suggestion will be appreciated.

Monday, January 14, 2008

Microsoft Infopath 2007 Tips And Trick January 14, 2008

In my last project, I use Microsoft Infopath 2007 intensively. In the beginning, I have difficulties in navigating Microsoft Infopath 2007 data source because all data is stored in XML. Here are the codes I often use when dealing with Infopath.

Most of my codes use XPathNavigator class, so don't forget to add this reference in the using section.I assume you place this code in Infopath code( example : in Form Load event)

using System.Xml;
using System.Xml.XPath;

Common problem #1 : Get Microsoft Infopath 2007 Data Source XML value
Suppose you have a data source like this :To get the value of name, you can use this code.

XPathNavigator root = MainDataSource.CreateNavigator();
XPathNavigator nameNode = root.SelectSingleNode("/my:myFields/my:Name",NamespaceManager);

string name = nameNode.Value;

note : the parameter in SelectSingleNode method is a XPath query. You can get this by right-clicking the data source you want, click "Copy XPath"

Common problem #2 : Set A Microsoft Infopath 2007 Data Source XML Value
Suppose you want to set the name to a value "newvalue". You can use this code.

XPathNavigator root = MainDataSource.CreateNavigator();
XPathNavigator nameNode = root.SelectSingleNode("/my:myFields/my:Name",NamespaceManager);

nameNode.SetValue("newvalue");
Common problem #3 : Loop through a repeating table in Microsoft Infopath 2007
Suppose you have a data source like thisThen , to get the value of field1, field2, and field3 iteratively, use this code.

XPathNavigator domNav = MainDataSource.CreateNavigator();
XPathNodeIterator rows = domNav.Select("/my:myFields/my:table/my:row", NamespaceManager);

while (rows.MoveNext())
{
string field1 = rows.Current.SelectSingleNode("my:field1", NamespaceManager).Value;
string field2 = rows.Current.SelectSingleNode("my:field2", NamespaceManager).Value;
string field3 = rows.Current.SelectSingleNode("my:field3", NamespaceManager).Value;
}
Common problem #4 : Add a row to Microsoft Infopath 2007 repeating table

If you have a repeating table like thiswith this data sourceYou can add a row to this repeating table by using this code.

string myNamespace = NamespaceManager.LookupNamespace("my");
using (XmlWriter writer = MainDataSource.CreateNavigator().SelectSingleNode(
"/my:myFields/my:group1", NamespaceManager).AppendChild())
{
writer.WriteStartElement("group2", myNamespace);
writer.WriteElementString("field1", myNamespace, "Cell 1");
writer.WriteElementString("field2", myNamespace, "Cell 2");
writer.WriteElementString("field3", myNamespace, "Cell 3");
writer.WriteEndElement();
writer.Close();
}
Article #3 was taken from this great site . Article #4 was taken from the another great site.

Note that NamespaceManager is a property which exists only in Infopath code. To use these code in another place, such as K2 Blackpearl, you have to define the NamespaceManager by yourself. You can use this method.

public XmlNamespaceManager InitNamespaceManager(XmlDocument xmlDOMDoc)
{
XmlNamespaceManager xnmMan;
xnmMan = new XmlNamespaceManager(xmlDOMDoc.NameTable);
foreach (XmlAttribute nsAttr in xmlDOMDoc.DocumentElement.Attributes)
{
if (nsAttr.Prefix=="xmlns")
xnmMan.AddNamespace(nsAttr.LocalName,nsAttr.Value);
}
return xnmMan;
}
and use this code to create your NamespaceManager ( I assume you use K2 Blackpearl and Microsoft Infopath 2007)

XmlDocument doc = new XmlDocument();
string xmlString = K2.ProcessInstance.XmlFields["MyInfopathForm"].Value;
doc.LoadXml(xmlString);
XmlNamespaceManager NamespaceManager = InitNamespaceManager(doc);

I took this code from this great blog.
All credits should go to the original poster.

Hope this article helps:)

Friday, January 11, 2008

K2 Tips And Trick January 2008

In last few months, I encountered some common problem in K2 blackpearl development. I want to share what I've got:) I assume you are using Virtual PC given by K2 (DENALLIX)

Common problem #1: Get Active Directory's Full Name from Login Name
case : Suppose you want to get Codi's full name from active directory (login name : denallix\codi). You can use this code.

public string GetFullName(string userID)
{
DirectoryEntry entry = new DirectoryEntry("LDAP://DENALLIX.COM");
DirectorySearcher searcher = new DirectorySearcher(entry);
searcher.Filter = "(SAMAccountName=" + userID + ")";
searcher.PropertiesToLoad.Add("displayname");

SearchResult result = searcher.FindOne();

return result.Properties["displayname"][0].ToString();
}

Sample code : string myFullName = GetFullName("codi"); //returns Codi

Common problem #2: Get Active Directory's Email from Login Name
case : Suppose you want to get Codi's email from active directory (login name : denallix\codi).

public string GetEmail(string userID)
{
DirectoryEntry entry = new DirectoryEntry("LDAP://DENALLIX.COM");
DirectorySearcher searcher = new DirectorySearcher(entry);
searcher.Filter = "(SAMAccountName=" + userID + ")";
searcher.PropertiesToLoad.Add("mail");

SearchResult result = searcher.FindOne();

return result.Properties["mail"][0].ToString();
}
Sample code : string myFullName = GetEmail("codi"); //returns codi@denallix.com

Common problem #3: Insert a List Item to a Sharepoint List
case : Suppose you want to insert a list item to a Sharepoint List named "MyList". You can use this code.

public void InsertMyList(string Column1Value, string Column2Value, string Column3Value)
{
SPSite site = new SPSite("http://moss.denallix.com");
SPWeb web = site.OpenWeb();
web.AllowUnsafeUpdates = true;
SPList myList = web.Lists["MyList"];
SPListItem myNewItem = myList.Items.Add();
myNewItem["Column1Title"] = Column1Value;
myNewItem["Column2Title"] = Column2Value;
myNewItem["Column3Title"] = Column3Value;
myNewItem.Update();
}
Sample code : InsertMyList("Column1Value","Column2Value","Column3Value");

Common problem #4: Get a List Item From a Sharepoint List with a condition (using CAML query)
case : Suppose you want to get Column2 Value from a list item where the Column1 Value is "Column1Value" . You can use this code.

public string GetColumn2ValueFromMyList(string Column1Value)
{
SPSite site = new SPSite("http://moss.denallix.com");
SPWeb web = site.OpenWeb();
SPList list = web.Lists["MyList"];
SPQuery query = new SPQuery();
query.Query = "<Where><Eq><FieldRef Name='Column1Title' /><Value Type='Text'>" + Column1Value + "</Value></Eq></Where>";
SPListItemCollection listItem = list.GetItems(query);
if (listItem.Count > 0)
{
return listItem[0]["Column2Title"].ToString();
}
else
{
return string.Empty;
}
}
sample code : string myColumn2Value = GetColumn2ValueFromMyList("Column1Value");
Some bonus from this week's tips and trick.
My friend had found a way to validate email format in Microsoft Infopath 2007 browser-enabled form. You can add a data validation matching this pattern:
.+@\p{L}+\.\p{L}+\.*.*

This pattern can handle agus123@yahoo.com , agus.santoso@yahoo.co.id , or agus_santoso@yahoo.co.id. I haven't tested it for all email format. Just inform me if you have better pattern. I will add it to this post. Thank you :)

Monday, December 17, 2007

How to get role items in a K2 blackpearl role

In my last project , I got so many obstacles in development phase. One of most annoying thing I encountered is how to get role item in a K2 blackpearl role. After days of training with K2 guys from Singapore, finally I got the code. Here they are.

First, add this reference to the project you want to place the code.

using SourceCode.Security.UserRoleManager.Management;

Second, declare an instance of UserRoleManager like this :

private SourceCode.Security.UserRoleManager.Management.UserRoleManager _roleManager = new SourceCode.Security.UserRoleManager.Management.UserRoleManager();

Third, add a property like this (note that the connection string may vary according to your computer configuration)


public SourceCode.Security.UserRoleManager.Management.UserRoleManager RoleManager
{
get
{
string connString = string.Empty;

if (_roleManager == null)
_roleManager = new UserRoleManager();

_roleManager.CreateConnection();

if (!_roleManager.Connection.IsConnected)
{
connString = "Integrated=True;IsPrimaryLogin=True;Authenticate=True;EncryptedPassword=False;Host=BLACKPEARL;Port=5555";//Connection string to HostServer
}

_roleManager.Connection.Open(connString);

return _roleManager;
}
}

Then , add a method to read the role items in a K2 blackpearl role (this method returns the role items in semicolon separated format, example: denallix\codi;denallix\anthony)

public string GetK2RoleUsers(string K2Role)
{
Role role = RoleManager.GetRole(K2Role);
string sRoleName = "";

foreach (UserItem UIDel in role.Include)
{
if (sRoleName.Equals(""))
sRoleName += UIDel.Name.Split(":".ToCharArray())[1];
else
sRoleName += ";" + UIDel.Name.Split(":".ToCharArray())[1];
}

return sRoleName.ToLower();
}


To make it clearer , I give a complete code how to use this in a server event code. In this example, I want to get all the role items in a K2 role named "IA" and place the result in K2 DataField named "DestinationApproval"


using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Drawing;
using SourceCode.KO;
using SourceCode.Workflow.Common.Extenders;
using hostContext = Project_5b686e4bb3d24c9b9dfa1d2ef7e5ffb5.EventItemContext_cd0b00f67cf744188529af119ccd3f6b;
using System.Xml;
using System.Xml.XPath;
using SourceCode.Security.UserRoleManager.Management;

namespace ExtenderProject_5b686e4bb3d24c9b9dfa1d2ef7e5ffb5
{
public partial class EventItem_cd0b00f67cf744188529af119ccd3f6b : ICodeExtender<hostContext>
{
//Open Connection to RoleManager
private SourceCode.Security.UserRoleManager.Management.UserRoleManager _roleManager = new SourceCode.Security.UserRoleManager.Management.UserRoleManager();
public void Main(Project_5b686e4bb3d24c9b9dfa1d2ef7e5ffb5.EventItemContext_cd0b00f67cf744188529af119ccd3f6b K2)
{
string userIA = GetK2RoleUsers("IA");
K2.ProcessInstance.DataFields["DestinationApproval"].Value = userIA;
//you can ignore this code below, it's specific to business process of my module :)
XmlDocument doc = new XmlDocument();
string infopathFormName = K2.ProcessInstance.DataFields["InfopathFormName"].Value.ToString();
string xmlString = K2.ProcessInstance.XmlFields[infopathFormName].Value;
string xpathTempUser = K2.ProcessInstance.DataFields["XPathTempUser"].Value.ToString();
doc.LoadXml(xmlString);
XmlNamespaceManager NamespaceManager = InitNamespaceManager(doc);
XPathNavigator root = doc.CreateNavigator();
XPathNavigator node = root.SelectSingleNode(xpathTempUser, NamespaceManager);
node.SetValue(userIA);
K2.ProcessInstance.XmlFields[infopathFormName].Value = doc.InnerXml;

}
public string GetK2RoleUsers(string K2Role)
{
Role role = RoleManager.GetRole(K2Role);
string sRoleName = "";

foreach (UserItem UIDel in role.Include)
{
if (sRoleName.Equals(""))
sRoleName += UIDel.Name.Split(":".ToCharArray())[1];
else
sRoleName += ";" + UIDel.Name.Split(":".ToCharArray())[1];
}

return sRoleName.ToLower();
}
public SourceCode.Security.UserRoleManager.Management.UserRoleManager RoleManager
{
get
{
string connString = string.Empty;

if (_roleManager == null)
_roleManager = new UserRoleManager();

_roleManager.CreateConnection();

if (!_roleManager.Connection.IsConnected)
{
connString = "Integrated=True;IsPrimaryLogin=True;Authenticate=True;EncryptedPassword=False;Host=BLACKPEARL;Port=5555";//Connection string to HostServer
}

_roleManager.Connection.Open(connString);

return _roleManager;
}
}
public XmlNamespaceManager InitNamespaceManager(XmlDocument xmlDOMDoc)
{
XmlNamespaceManager xnmMan;

xnmMan = new XmlNamespaceManager(xmlDOMDoc.NameTable);

foreach (XmlAttribute nsAttr in xmlDOMDoc.DocumentElement.Attributes)
{

if (nsAttr.Prefix == "xmlns")

xnmMan.AddNamespace(nsAttr.LocalName, nsAttr.Value);

}

return xnmMan;

}
}
}

Monday, December 10, 2007

How to make and use Microsoft Sharepoint Web Part

If you develop applications with MOSS 2007, you likely often make and implement Web Parts in your MOSS 2007 page. Here are some walkthrough / tutorial in making web part. Hope it helps :)

1.- Open Visual Studio 2005.
- Click File - New - Project

2.- On the Project types box, choose Other Project Types - Visual Studio Solutions.
- click Blank Solution form the templates provided.
- Give name for your solution (in my case , the name is MyWebpart and the location is C:\projects\MyWebpart)

3. - On the solution explorer , right click your solution.
- Click Add - new Website.

4. - change Language to Visual C# , then click OK

5. klik File - Add - New Project

6. - Click Visual C# on the project types
- Click Class Library on the Templates , then click OK

7. - On Solution Explorer, right click web site project
- click Add New Item

8. - Click Web User Control on templates
- Give names to your Web User Control (ex: MyGrid.ascx)
- ensure the language is C# , click OK

9. - modify the web user control as you wish (if you want to create your own custom control , skip to step 22)
- in my case , I want to create hierarchical grid using UltraWebGrid control from Infragistics NetAdvantage 2007
- I assume you already have it :D
- drag UltraWebGrid to the design view of MyGrid.ascx
- Infragistics Quick Design dialog show up
- Change the properties as follows:
-- ID : UG1
-- DisplayLayout
---AutoGenerateColumns : False
---ViewType : Hierarchical


10. - next , we have to modify the structure of the grid
- before that, let's look to the sample database diagram
(1 folioname can have 1 or more processname )


11. - next, click Data Structure - Edit Data Structure- Bands and Columns


12. click Column 0 , set the properties as follows :
- BaseColumnName : HeaderID
- Key : HeaderID
- Hidden : True


13. Add new Column


14. repeat step 12 with following properties , then click apply
- BaseColumnName : FolioName
- Key : FolioName
- Hidden : False
- Header
--Caption : Folio Name

15. Add new Band


16. Click the new band, and clik add - column
17. click the new column, change the properties (same with step 12)
- BaseColumnName : HeaderID
- Key : HeaderID
- Hidden : True
18. Click Add - new column (same with step 16)
19. Click the new column, change the properties (same with step 12)
- BaseColumnName : ProcessName
- Key : ProcessName
- Hidden : False
- Header
--Caption : Process Name
then click OK

20. On the code behind of MyGrid.ascx , place this code on Page_Load


SqlConnection con;
SqlDataAdapter daHeader;
SqlDataAdapter daDetail;
DataSet ds;
protected void Page_Load(object sender, EventArgs e)
{
con = new SqlConnection("Data Source=.;Initial Catalog=BSI;Integrated Security=True");
con.Open();
daHeader = new SqlDataAdapter("SELECT * FROM Header", con);
daDetail = new SqlDataAdapter("SELECT * FROM Detail", con);
ds = new DataSet();
daHeader.Fill(ds, "Header");
daDetail.Fill(ds, "Detail");
try
{
ds.Relations.Add("HeaderID", ds.Tables["Header"].Columns["HeaderID"],
ds.Tables["Detail"].Columns["HeaderID"]);
}
catch (Exception ex)
{

Response.Write(ex.Message);
}

UG1.DataSource = ds.Tables["Header"];
UG1.DataBind();
}


21. Don't forget to add using System.Data.SqlClient; on top of your code behind page or by using resolve. Save your work.

22. Open Visual Studio 2005 command prompt
23. type this command
cd \projects\MyWebpart\WebSite1 , press enter
sn -k MyWebpart.snk , press enter


24. go to solution explorer, right click your website project, click Publish Web Site
25. - check the Use Fixed Naming ... checkbox
- check the Enable strong naming ... checkbox
- browse the MyWebpart.snk file made on step 23
- click OK


26. copy DLL file on the PrecompiledWeb folder

to the _app_bin folder of your MOSS
(ex : C:\Inetpub\wwwroot\wss\VirtualDirectories\moss.denallix.com80\_app_bin )


27. - go to solution explorer
- right click the class library projects, click properties
- click Signing tab
- check the Sign the assembly checkbox
- click new


28. - type key file name as you wish (Ex : WebpartLib)
- uncheck Protect my key ... checkbox
- click OK
- click Save


29. - make a new folder in your MOSS folder (ex:C:\Inetpub\wwwroot\wss\VirtualDirectories\moss.denallix.com80) , give name to the folder as you wish , ex: MyUserControl
- place your web user control ( *.ascx and *.ascx.cs) this new folder

30. - go to solution explorer
- right click the class library projects, click add new item
- click Class , give name to it (ex : MyGridWebpart.cs)
- click Add
31. - on the class library projects, right click References folder, click Add reference
- on .NET tab, click System.Web, click OK
- add this code to using section of MyGridWebpart.cs

using System.Web.UI;
using System.Web.UI.WebControls.WebParts;

- don't forget to add public keyword to the class
- inherit the class from Webpart class
- belows are the complete code for MyGridWebpart.cs


using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;
namespace ClassLibrary1
{
public class MyGridWebpart : WebPart
{
private Control child = null;

protected override void CreateChildControls()
{
child = this.Page.LoadControl("~/MyUserControl/MyGrid.ascx");
this.Controls.Add(child);
}

public override void RenderControl(HtmlTextWriter writer)
{
this.EnsureChildControls();
child.RenderControl(writer);
}
}
}


- notice that the path in LoadControl method is the path where you placed your *.ascx and
*.ascx.cs file (in step 29)

32. - on the solution explorer, open AssemblyInfo.cs under the Properties folder in Class library project
- add using System.Security; on using section
- add this code to the last line of the AssemblyInfo.cs
[assembly: AllowPartiallyTrustedCallers()]


33. - right click the class library project, click build
- copy the class library project's DLL (ex : C:\projects\MyWebpart\ClassLibrary1\bin\Debug)

to _app_bin of your MOSS folder (ex :C:\Inetpub\wwwroot\wss\VirtualDirectories\moss.denallix.com80\_app_bin)

34.- go to your MOSS folder (ex : C:\Inetpub\wwwroot\wss\VirtualDirectories\moss.denallix.com80)
- open web.config
- add this code between SafeControls element


<SafeControl Assembly="ClassLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0fb73cb8dfe13eb7"
Namespace="ClassLibrary1" TypeName="*" Safe="True" />


Assembly and Namespace attribute can be retrieved from Project properties (right click Class Library projects, click Application tab)
Version, culture, and PublicKeyToken can be retrived by dragging the project DLL to C:\windows assembly

After you get the PublicKeyToken , uninstall from the assembly

35. - Done, you already made your webpart :)
- you have to do one more thing , add the assembly you used to MOSS web config
if you use native .NET controls, you can skip this. But if you use 3rd party control like
Infragistics etc. You have to do this. Add this code to assemblies element
<add assembly="Infragistics2.WebUI.UltraWebGrid.v7.2, Version=7.2.20072.61, Culture=neutral, PublicKeyToken=7DD5C3163F2CD0CB"/>
<add assembly="Infragistics2.WebUI.Shared.v7.2, Version=7.2.20072.61, Culture=neutral, PublicKeyToken=7DD5C3163F2CD0CB"/>
- Next step is to populate it on Web Parts Gallery
- Open Your MOSS
- Click Site Actions - Site Settings - Modify All Site Settings
- Click Web Parts on Gallery group
- Click new
- check your Webpart checkbox , click Populate Gallery
- your Webpart now show up in the Web part list

36. - You can create a new list to test your web part, or use an existing list
- in my case , I create TestWebpart list
- click TestWebpart list
- click Site Actions - Edit Page
- click Add new webpart , Add Web Parts dialog show up
- click MyGridWebPart checkbox on the Miscellaneous
- click Add
- Your web part has been up :D