12 tháng 4, 2012

Amazon Web Service seminar

I presented Amazon Web Service seminar in my company office to general IT community on Sunday, Apr 8th, 2012. I found myself very lucky to have chance to get to the Amazon cloud service. In general, their cloud solution is great and it's worth for any IT professional to invest time for their need.







Some pictures from the seminar




18 tháng 2, 2012

Troy - New challenge

My team decided to build a new application that employs a lot of new cutting-edge technologies and techniques that most of us haven't done before. Because I can't give any more detail info about the problem due to company privacy but some of technical stuffs that we definitely want the app to have are : running on Amazon cloud platform (EC2 and other services), use node.js to handle a persistent network connection with clients, apply NoSQL on database (mongoDB), apply a lot of graphics techniques in HTML5, etc. I will indicate this product as code name Troy on this blog but obviously our real code name for it in company is different.

Our team is not big (only 5 developers) and I take project manager + developer too. This chance is what I'm waiting for a while because I want to try myself on the management track as well as being a developer too. I promised to myself to post all my researching and cool things about this new product to share with you since I think what we're going to do could be pretty hot and new in developer world at this time.

See you again on next post : A introduction to Express - a web framework on node.js

02 tháng 8, 2011

A present at Barcamp Sai Gon 2011

On 24 July, I gave a presentation at Barcamp Sai Gon about Google App Engine. Since the present at Barcamp should be around 30 minutes, I just spoke on some interesting services of App Engine that I thought people may find it different from other cloud computing verdors like Windows Azure.

Here is the slide




Checkout the sample code at: http://code.google.com/p/google-app-engine-samples/

And the live demo at: http://barcamp2011.appspot.com

Some pictures (taken by a friend in KMS :)


















09 tháng 7, 2010

SharePoint workflow API

In this post I'd like to write down several important notes on working with workflow API in SharePoint.

Snippet code to assign an workflow template to a List or Content Type


Because the SharePoint feature schema doesn't support in assigning a workflow template to a specific List or Content Type, we must do it with code. A convenient place to do it is in Feature Activated event. Before looking at the code, you could check out Workflow Object Model Overview in SharePoint Foundation for some basic related-workflow classes. The snippet code below is what you could use to assign (associate) workflow template to List or Content Type


using System.Xml.Linq; // Include this namespace to use LINQ to XML

// ....

// When creating a workflow for list or content type, it needs two other list to operate: a list for tasks and a list for history
spWeb.Lists.Add("Workflow Tasks", string.Empty, SPListTemplateType.Tasks);
spWeb.Lists.Add("Workflow History Tasks",string.Empty, SPListTemplateType.WorkflowHistory);

// Retrieve Approval workflow template with it Guid defined in ReviewWorkflows feature
var approvalWFTemplate = spWeb.WorkflowTemplates[new Guid("C6964BFF-BF8D-41ac-AD5E-B61EC111731C")]; /* This guid is Approval Workflow , you can find it in ReviewWorkflows folder in Features */
var wfAssociation = SPWorkflowAssociation.CreateListAssociation(approvalWFTemplate, "Sample Approval Workflow", "Workflow Tasks","Workflow History Tasks");
// If you want to assign workflow to content type in list, call CreateListContentTypeAssociation()
// var wfAssociation = SPWorkflowAssociation.CreateListContentTypeAssociation(approvalWFTemplate, "Sample Approval Workflow", "Workflow Tasks","Workflow History Tasks");
wfAssociation.AllowManual = false;

XNamespace MyNamespace = @"http://schemas.microsoft.com/office/infopath/2003/myXSD";
XNamespace XsiNamespace = @"http://www.w3.org/2001/XMLSchema-instance";

// The AssociationData property represents for the settings that user see in 2nd screen when creating workflow in SharePoint UI
var associationDataXml = XElement.Parse(wfAssociation.AssociationData);
associationDataXml.SetElementValue(MyNamespace+"CreateTasksInSerial", false); // Run workflow in parallel
associationDataXml.SetElementValue(MyNamespace + "Description", "Please review this task");
// Continue to modify appropriate xml elements for desired settings. See following paragraph to know elements and they are mapped to which settings on UI

// The element is little tricky
// It is by default
// If we want to set its value to true, remove the attribute first
var elementNameWithNS = MyNamespace + "StopOnAnyReject";
associationDataXml.Element(elementNameWithNS).Attribute(XsiNamespace + "nil").Remove();
associationDataXml.SetElementValue(elementNameWithNS, true);

// Reviewers is Approvers in UI
associationDataXml.Element(MyNamespace + "Reviewers").Add(
new XElement(MyNamespace + "Person",
new XElement(MyNamespace + "DisplayName", "Managers"),
new XElement(MyNamespace + "AccountId", "Managers"),
new XElement(MyNamespace + "AccountType", "SharePointGroup")
)
);

// CC in UI
associationDataXml.Element(MyNamespace + "CC").Add(
new XElement(MyNamespace + "Person",
new XElement(MyNamespace + "DisplayName","Managers"),
new XElement(MyNamespace + "AccountId", "Managers"),
new XElement(MyNamespace + "AccountType", "SharePointGroup")
)
);

// Update modifed xml segment back to WorkflowAssociation object
wfAssociation.AssociationData = associationDataXml.ToString();
var list = spWeb.Lists["Unapproved Document Library"];
list.AddWorkflowAssociation(wfAssociation);
// If you want to assign workflow template to content type in list, run below code
// list.ContentTypes["Custom ContentType"].AddWorkflowAssociation(wfAssociation);


The main idea here is that I modified the XML segment in AssociationData property which represents for settings in workflow association form. So how the XML segment in AssociationData looks like ? Here you're :

<my:myFields xml:lang="en-us" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD">

<my:Reviewers>
<my:Person>
<my:DisplayName>Managers</my:DisplayName>
<my:AccountId>Managers</my:AccountId>
<my:AccountType>SharePointGroup</my:AccountType>
</my:Person>
</my:Reviewers>

<my:CC></my:CC>
<my:DueDate xsi:nil="true"></my:DueDate>
<my:Description xsi:nil="true"></my:Description>
<my:Title></my:Title>
<my:DefaultTaskType>1</my:DefaultTaskType>
<my:CreateTasksInSerial>true</my:CreateTasksInSerial>
<my:AllowDelegation>false</my:AllowDelegation>
<my:AllowChangeRequests>true</my:AllowChangeRequests>
<my:StopOnAnyReject xsi:nil="true"></my:StopOnAnyReject>
<my:WantedTasks xsi:nil="true"></my:WantedTasks>
<my:SetMetadataOnSuccess>false</my:SetMetadataOnSuccess>
<my:MetadataSuccessField></my:MetadataSuccessField>
<my:MetadataSuccessValue></my:MetadataSuccessValue>
<my:ApproveWhenComplete>false</my:ApproveWhenComplete>
<my:TimePerTaskVal xsi:nil="true"></my:TimePerTaskVal>
<my:TimePerTaskType xsi:nil="true"></my:TimePerTaskType>
<my:Voting>false</my:Voting>
<my:MetadataTriggerField></my:MetadataTriggerField>
<my:MetadataTriggerValue></my:MetadataTriggerValue>
<my:InitLock>true</my:InitLock>
<my:MetadataStop>false</my:MetadataStop>
<my:ItemChangeStop>false</my:ItemChangeStop>
<my:GroupTasks>true</my:GroupTasks>
</my:myFields>


Looking that XML, we have no idea which XML element goes with which setting. I figured out some of them. In below pictures, I point out settings and which XML is mapped to











Programming starting a workflow


Starting a workflow is not hard, just call StartWorkflow() method with appropriate parameters. But the third parameter is pretty tricky because the MSDN doesn't give any document for it. After soul searching, I found out this actually is the XML in AssociationData property. Here the sample code

// Get workflow association of the list
SPWorkflowAssociation approvalAssociation = null;
var requestDetailList = spWeb.Lists["Unapproved Document Library"];
foreach (SPWorkflowAssociation wfAssociation in requestDetailList.WorkflowAssociations)
{
if (wfAssociation.Name == "Sample Approval Workflow")
{
approvalAssociation = wfAssociation;
break;
}
}

if (approvalAssociation != null)
{
spSite.WorkflowManager.StartWorkflow(spWeb.Lists["Unapproved Document Library"].Items[0], approvalAssociation, approvalAssociation.AssociationData /* We can change AssociationData as our needs before giving to StartWorkflow() */ );
}


Just a few notes, hope it help

03 tháng 6, 2010

Customize SharePoint standard forms with Data View web part

The Data View webpart is encapsulated control for presenting data and uses a SharePoint data source server control to load data items from List or Document Library or multiple List. This web part also allows user to add / edit / remove data entries in data source. SharePoint Designer is used to add (and modify) Data View web part to an existing .aspx page. In this post, I'd like to point out notably things we need to care when working with Data View web part.

Here is the common scenario we may see client's need that the Data View web part is likely a solution: they want to use SharePoint to store invoice data. They have two List :
  • Invoice : store data for an invoice. The possible columns for this list are : Client (could be lookup field but here I use textbox for simple) , Created Date , Payment Status (Completed / Pending / Invalid)
  • Invoice Detail : store items that going with the an invoice. The columns for this list are : Invoice ID (hidden and managed by our code) , Item Name (text for simple) , Price
And they want something that supports them in entering invoice easily. I thought it's not easy to implement a solution that user could be able to enter invoice details when they're creating a new Invoice in the NewForm.aspx of Invoice . So I went for an alternative way : I will customize the DispForm.aspx of Invoice so that Invoice Detail grid shows up below the Invoice item and user can enter invoice details within in DispForm.aspx. Below is some sample pictures I captured from the demo project (download link is in the end of this post)













Before I start, if you have no idea what's Data View web part, kindly read this blog first to get the insight on how to play with Data View webpart: http://www.endusersharepoint.com/2009/05/12/data-view-web-part-the-basics-insert-a-dvwp-on-your-page/

Now I will create a SharePoint-template project for demonstration and use it through this post. From above analysis, List Definition project will be used







After create the project, I organized the folder structure as 12-hive





From the structure, you see that the feature has 2 list definition (and its instances) for Invoice and InvoiceDetail. To start with, I add some fields on two lists and deploy the feature to web. Below is Invoice and Invoice Detail list after installed.









Ok, Now I'll use SharePoint Designer (SPD) to create Data View WebPart in DispForm.aspx of Invoice. First, I open the DispForm.aspx page of Invoice list





Second, I find the default List Form webpart and put the cursor right after that where the new Data View webpart will be added. You can see that two webparts are within the default WebPartZone control in page.





Then I start adding Data View webpart and apply some settings
  • Add InvoiceID column
  • Allow sorting and filtering on header
  • Allow insert/edit/remove




Select Invoice Detail list as source




Select three columns that represent for invoice detail and insert them to list as Multiple Item View





Now we need to do some customization. Open Common Data View Task dialog, click on Edit Columns link to add InvoiceID column which is used to associate invoice detail to a invoice






Click on Data View properties link




Allow sorting / filtering and inserting ...







At this point, the webpart show all invoice detail but we only want it to show invoice detail going with current viewing invoice. To do that, add filtering value on data source. Let create a parameter to represent for ID of Invoice








Name the new parameter: InvoiceID and make it get data from query string




Configure filtering




Only retrieve Invoice Detail that its InvoiceID field is same value as ID parameter in query string








Because the Data View webpart needs to have list id of Invoice Detail in order to retrieve query data as well as perform update to. By default the SPD creates a parameter that contains the current list id Invoice Detail but we should add some code so that the parameter can work on other environment. Let modify the ListID parameter




We will tell the data view webpart to get the list id from our server control (created soon)




Enter the server control ID, this control isn't available in the .aspx page but we'll create soon




Open the DispForm.aspx in Invoice folder in Visual Studio, on the beginning of PlaceHolderMain, add an ASP.NET label control with ID lblInvoiceDetailId

<asp:Content ContentPlaceHolderId="PlaceHolderLeftNavBar" runat="server"/>
<asp:Content ContentPlaceHolderId="PlaceHolderMain" runat="server">

<asp:Label ID="lblInvoiceDetailId" runat="server" style="display:none;"></asp:Label>

<table cellpadding=0 cellspacing=0 id="onetIDListForm">



We also need to make this page inherit from our class instead of default SharePoint. Go to top of page, modify the Inherits attribute to our code behind (created soon)

<%@ Page language="C#" MasterPageFile="~masterurl/default.master"   
Inherits="DataViewWebPartDemo.Invoice.DispForm,DataViewWebPartDemo,Version=1.0.0.0,Culture=neutral,PublicKeyToken=9f4da00116c38ec5"
meta:webpartpageexpansion="full" meta:progid="SharePoint.WebPartPage.Document" %>



Ok, now we'll create code behind for DispForm.aspx. Add new class in Invoice folder and name it DispForm.aspx.cs








Fill up the file with below code, remember to add Microsoft.SharePoint and System.Web assembly

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;

namespace DataViewWebPartDemo.Invoice
{
public class DispForm : Microsoft.SharePoint.WebPartPages.WebPartPage
{
protected global::System.Web.UI.WebControls.Label lblInvoiceDetailId;

public void Page_Init(object sender, EventArgs e)
{
lblInvoiceDetailId.Text = SPContext.Current.Web.Lists["Invoice Detail"].ID.ToString();
}
}
}



In the code, we assign the current Id of list named Invoice Detail to control so that the data view webpart then use it to retrieve data

You need to modify the web.config (adding SafeControl) after deploying the feature and assembly in order the DispForm.aspx can be run in SharePoint


Save all changes before going on





SPD will generate XSL definition based on what we've done on the UI, so we need to pack those definition to the feature. This can be done by using webpart exporting option on SharePoint UI. Let just create a test item on Invoice list and view it in display view





Now we're going to export what we have done so far on data view webpart






Temporarily save Invoice_Detail.webpart to Desktop




Open schema.xml file of Invoice list in feature





Modify the Form (Type="DisplayForm") element as below
<Forms>
<Form Type="DisplayForm" Url="DispForm.aspx" WebPartZoneID="Main">
<WebParts>
<AllUsersWebPart WebPartZoneID="Main" WebPartOrder="2">
<![CDATA[

<!-- Exported webpart definition goes here -->

]]>
</AllUsersWebPart>
</WebParts>
</Form>
<Form Type="EditForm" Url="EditForm.aspx" WebPartZoneID="Main"/>
<Form Type="NewForm" Url="NewForm.aspx" WebPartZoneID="Main"/>
</Forms>



Then open the downloaded Invoice_Detail.webpart, copy whole content and put inside CDATA. It looks like below

<Forms>
<Form Type="DisplayForm" Url="DispForm.aspx" WebPartZoneID="Main">
<WebParts>
<AllUsersWebPart WebPartZoneID="Main" WebPartOrder="2">
<![CDATA[

<!-- Exported webpart definition goes here -->
<webparts>
<webpart xmlns="http://schemas.microsoft.com/WebPart/v3">
<metadata>
<type name="Microsoft.SharePoint.WebPartPages.DataFormWebPart, Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c">
<importerrormessage>Cannot import this Web Part.</importerrormessage>
</type></metadata>
<data>
<properties>
<property name="MissingAssembly" type="string">Cannot import this Web Part.</property>
<property name="FireInitialRow" type="bool">True</property>
<property name="TitleIconImageUrl" type="string">
............
]]>
</AllUsersWebPart>
</WebParts>
</Form>
<Form Type="EditForm" Url="EditForm.aspx" WebPartZoneID="Main"/>
<Form Type="NewForm" Url="NewForm.aspx" WebPartZoneID="Main"/>
</Forms>



We have finished 80% work so far. Next we have one important thing and it's a little bit problematic: Automate filling the ID of current Invoice item to the Invoice ID field of detail when user enters new detail


This is where SharePoint Designer team comes to help :-). They wrote a blog dealing with problem and it's very helpful :-). Please go and check this blog before going on:
http://blogs.msdn.com/b/sharepoint/archive/2007/06/21/using-javascript-to-manipulate-a-list-form-field.aspx


Now we will employ a little bit JavaScript code to automate filling the value. We will put the javascript code to DispForm.aspx page in Invoice folder in Visual Studio.

<script type="text/javascript">

/////////////////////////////////////////////////////////////
// How they do that: http://blogs.msdn.com/b/sharepoint/archive/2007/06/21/using-javascript-to-manipulate-a-list-form-field.aspx
/////////////////////////////////////////////////////////////

_spBodyOnLoadFunctionNames.push("fillDefaultValues"); // Use JS SharePoint API to register onload event

function fillDefaultValues() {
// Parse the key/value from query string
var qs = location.search.substring(1, location.search.length);
var args = qs.split("&");
var vals = new Object();
for (var i = 0; i < args.length; i++) {
var nameVal = args[i].split("=");
var temp = unescape(nameVal[1]).split('+');
nameVal[1] = temp.join(' ');
vals[nameVal[0]] = nameVal[1];
}

// Assign ID query string to InvoiceID field
setTextFromFieldName("InvoiceID", vals["ID"]);
}

// @fieldName: the field display name
function setTextFromFieldName(fieldName, value) {
// If the webpart isn't in Insert/Edit Mode, the control doesn't exist so using try/catch to prevent error js icon on browser
try {
var theInput = getTagFromIdentifierAndTitle("input", "TextField", fieldName);
if (theInput != null) {
theInput.value = value;
}
}
catch (e) { }
}

function getTagFromIdentifierAndTitle(tagName, identifier, title) {
var len = identifier.length;
var tags = document.getElementsByTagName(tagName);
for (var i = 0; i < tags.length; i++) {
var tempString = tags[i].id;
if (tags[i].title == title && (identifier == "" || tempString.indexOf(identifier) == tempString.length - len)) {
return tags[i];
}
}
return null;
}</script>



From the JavaScript code, You see that we set the ID in query string to the field called "InvoiceID" (of course we can set the value on lookup field too (as original code showed).


Here is the whole picture of what we've done: when the DispForm.aspx run, the DataView web part look for server control with Id lblInvoiceDetailId and use the GUID to know where the Invoice Detail list. Then when user enters new record for detail, its Invoice ID field is filled automatically by javascript. That's interesting :-).

This is end of the post. Here is download link for the source of sample I presented

23 tháng 5, 2010

How to win friends and influence people

How to Win Friends and Influence People is well-known book for everyone who wants to improve their social skills (Vietnamese version). I read it but sometime it's not easy to remember and follow its ideas. Today I found a mind map picture that summarizes all aspects in the book and I think it would be helpful for other




From Internet