Thứ Sáu, 18 tháng 3, 2011

Stored Procedures Paging Solution in ASP.NET MVC 2

The article Server-Side Paging with the Entity Framework and ASP.NET MVC 3 describes how to paging using Entity Framework and ASP.NET MVC 3. Using Skip() and Take() extension methods is main idea of the article. But you can’t do it if your project using Oracle database. Because EF is not supported from Microsoft for Oracle database. So, I’ll describe how to implement paging from stored procedures in ASP.NET MVC 2.0. This approach bellow using Northwind database but it’s also applied for Oracle database.

1. Define the GetProductsByFilter stored procedures

This stored procedures will contain total rows of the table and selection of records by page size

CREATE PROCEDURE GetProductsByFilter 
-- Add the parameters for the stored procedure here
@i_Page_Index int = 0,
@i_Page_Count int = 10,
@o_total_rows int output
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
--Select
SELECT TOP (@i_Page_Count) a.* FROM
(
SELECT p.*,
ROW_NUMBER() OVER (ORDER BY p.ProductID) AS num
FROM dbo.Products p
) AS a
WHERE num > @i_Page_Index * @i_Page_Count;
-- Get Total Rows
SET @o_total_rows = (SELECT COUNT(1)
FROM Products);
END
GO

2. Define PagerParams class


This class used to store paging information. It’s include the current page index, the page size and the total records.

[Serializable]
public class PagerParams
{
/// <summary>
/// Page size
/// </summary>
private int _pageSize;
/// <summary>
/// Total record count
/// </summary>
private int _totalRecords;
/// <summary>
/// Page index
/// </summary>
private int _pageIndex;

/// <summary>
/// Gets the size of the page.
/// </summary>
/// <value>The size of the page.</value>
public int PageSize
{
get { return _pageSize; }
}

/// <summary>
/// Gets or sets the index of the page.
/// </summary>
/// <value>The index of the page.</value>
public int PageIndex
{
get { return _pageIndex; }
set
{
if (_pageSize == 0 && value > 0)
{
throw new ArgumentOutOfRangeException("error");
}
if (_totalRecords > 0)
{
if (value * _pageSize >= _totalRecords)
{
throw new ArgumentOutOfRangeException("error");
}
}
else if (_totalRecords == 0)
{
if (value > 0)
{
throw new ArgumentOutOfRangeException("error");
}
}
_pageIndex = value;
}
}

/// <summary>
/// Gets or sets the total records.
/// </summary>
/// <value>The total records.</value>
public int TotalRecords
{
get { return _totalRecords; }
set
{
if (_totalRecords != value)
{
_totalRecords = value;
if (_totalRecords > 0)
{
if (_pageIndex * _pageSize >= _totalRecords)
{
PageIndex = (_totalRecords - 1) / _pageSize;
}
}
else if (_totalRecords == 0)
{
PageIndex = 0;
}
}
}
}

/// <summary>
///
/// </summary>
/// <param name="pageSize"></param>
/// <param name="pageIndex"></param>
/// <param name="totalRecords"></param>
public PagerParams(int pageSize, int pageIndex, int totalRecords)
{
_pageSize = pageSize;
_totalRecords = totalRecords;
PageIndex = pageIndex;
}

/// <summary>
/// Gets the default pager parameters.
/// </summary>
/// <value>The default pager parameters.</value>
public static PagerParams Default
{
get
{
return new PagerParams(20, 0, -1);
}
}

/// <summary>
/// Gets the no paging parameters.
/// </summary>
/// <value>The no paging parameters.</value>
public static PagerParams NoPaging
{
get
{
return new PagerParams(int.MaxValue, 0, -1);
}
}
}

3. Implement the IPagedList


After that We defines IPagedList from the idea of Rob Conery's blog.


IPagedList interface

public interface IPagedList
{
int PageCount { get; }
int TotalItemCount { get; }
int PageIndex { get; }
int PageNumber { get; }
int PageSize { get; }
bool HasPreviousPage { get; }
bool HasNextPage { get; }
bool IsFirstPage { get; }
bool IsLastPage { get; }
}

PagedList class

public partial class PagedList : IPagedList
{
public PagedList(PagerParams pagerParams)
{
Initialize(pagerParams);
}

#region IPagedList Members

public int PageCount { get; private set; }
public int TotalItemCount { get; private set; }
public int PageIndex { get; private set; }
public int PageNumber { get { return PageIndex + 1; } }
public int PageSize { get; private set; }
public bool HasPreviousPage { get; private set; }
public bool HasNextPage { get; private set; }
public bool IsFirstPage { get; private set; }
public bool IsLastPage { get; private set; }

#endregion

protected void Initialize(PagerParams pagerParams)
{
//### argument checking
if (pagerParams.PageIndex < 0)
{
throw new ArgumentOutOfRangeException("PageIndex cannot be below 0.");
}
if (pagerParams.PageSize < 1)
{
throw new ArgumentOutOfRangeException("PageSize cannot be less than 1.");
}


//### set properties
TotalItemCount = pagerParams.TotalRecords;
PageSize = pagerParams.PageSize;
PageIndex = pagerParams.PageIndex;
if (TotalItemCount > 0)
{
PageCount = (int)Math.Ceiling(TotalItemCount / (double)PageSize);
}
else
{
PageCount = 0;
}
HasPreviousPage = (PageIndex > 0);
HasNextPage = (PageIndex < (PageCount - 1));
IsFirstPage = (PageIndex <= 0);
IsLastPage = (PageIndex >= (PageCount - 1));
}
}

3. Create HtmlHelpers methods


Now We create a Pager class to render pagination.

public class Pager
{
private ViewContext viewContext;
private readonly int pageSize;
private readonly int currentPage;
private readonly int totalItemCount;
private readonly RouteValueDictionary linkWithoutPageValuesDictionary;

public Pager(ViewContext viewContext, int pageSize, int currentPage, int totalItemCount, RouteValueDictionary valuesDictionary)
{
this.viewContext = viewContext;
this.pageSize = pageSize;
this.currentPage = currentPage;
this.totalItemCount = totalItemCount;
this.linkWithoutPageValuesDictionary = valuesDictionary;
}

public string RenderHtml()
{
int pageCount = (int)Math.Ceiling(this.totalItemCount / (double)this.pageSize);
int nrOfPagesToDisplay = 10;

var sb = new StringBuilder();

// Previous
if (this.currentPage > 1)
{
sb.Append(GeneratePageLink("&lt;", this.currentPage - 1));
}
else
{
sb.Append("<span class=\"disabled\">&lt;</span>");
}

int start = 1;
int end = pageCount;

if (pageCount > nrOfPagesToDisplay)
{
int middle = (int)Math.Ceiling(nrOfPagesToDisplay / 2d) - 1;
int below = (this.currentPage - middle);
int above = (this.currentPage + middle);

if (below < 4)
{
above = nrOfPagesToDisplay;
below = 1;
}
else if (above > (pageCount - 4))
{
above = pageCount;
below = (pageCount - nrOfPagesToDisplay);
}

start = below;
end = above;
}

if (start > 3)
{
sb.Append(GeneratePageLink("1", 1));
sb.Append(GeneratePageLink("2", 2));
sb.Append("...");
}
for (int i = start; i <= end; i++)
{
if (i == this.currentPage)
{
sb.AppendFormat("<span class=\"current\">{0}</span>", i);
}
else
{
sb.Append(GeneratePageLink(i.ToString(), i));
}
}
if (end < (pageCount - 3))
{
sb.Append("...");
sb.Append(GeneratePageLink((pageCount - 1).ToString(), pageCount - 1));
sb.Append(GeneratePageLink(pageCount.ToString(), pageCount));
}

// Next
if (this.currentPage < pageCount)
{
sb.Append(GeneratePageLink("&gt;", (this.currentPage + 1)));
}
else
{
sb.Append("<span class=\"disabled\">&gt;</span>");
}
return sb.ToString();
}

private string GeneratePageLink(string linkText, int pageNumber)
{
var pageLinkValueDictionary = new RouteValueDictionary(this.linkWithoutPageValuesDictionary);
pageLinkValueDictionary.Add("page", pageNumber);
//var virtualPathData = this.viewContext.RouteData.Route.GetVirtualPath(this.viewContext, pageLinkValueDictionary);
var virtualPathData = RouteTable.Routes.GetVirtualPath(this.viewContext.RequestContext, pageLinkValueDictionary);

if (virtualPathData != null)
{
string linkFormat = "<a href=\"{0}\">{1}</a>";
return String.Format(linkFormat, virtualPathData.VirtualPath, linkText);
}
else
{
return null;
}
}
}

and a PagingExtentions class

public static class PagingExtensions
{
#region HtmlHelper extensions

public static string Pager(this HtmlHelper htmlHelper, int pageSize, int currentPage, int totalItemCount)
{
return Pager(htmlHelper, pageSize, currentPage, totalItemCount, null, null);
}

public static string Pager(this HtmlHelper htmlHelper, int pageSize, int currentPage, int totalItemCount, string actionName)
{
return Pager(htmlHelper, pageSize, currentPage, totalItemCount, actionName, null);
}

public static string Pager(this HtmlHelper htmlHelper, int pageSize, int currentPage, int totalItemCount, object values)
{
return Pager(htmlHelper, pageSize, currentPage, totalItemCount, null, new RouteValueDictionary(values));
}

public static string Pager(this HtmlHelper htmlHelper, int pageSize, int currentPage, int totalItemCount, string actionName, object values)
{
return Pager(htmlHelper, pageSize, currentPage, totalItemCount, actionName, new RouteValueDictionary(values));
}

public static string Pager(this HtmlHelper htmlHelper, int pageSize, int currentPage, int totalItemCount, RouteValueDictionary valuesDictionary)
{
return Pager(htmlHelper, pageSize, currentPage, totalItemCount, null, valuesDictionary);
}

public static string Pager(this HtmlHelper htmlHelper, int pageSize, int currentPage, int totalItemCount, string actionName, RouteValueDictionary valuesDictionary)
{
if (valuesDictionary == null)
{
valuesDictionary = new RouteValueDictionary();
}
if (actionName != null)
{
if (valuesDictionary.ContainsKey("action"))
{
throw new ArgumentException("The valuesDictionary already contains an action.", "actionName");
}
valuesDictionary.Add("action", actionName);
}
var pager = new Pager(htmlHelper.ViewContext, pageSize, currentPage, totalItemCount, valuesDictionary);
return pager.RenderHtml();
}

#endregion


#region
public static IPagedList ToPagedList(this PagerParams pagerParams)
{
return new PagedList(pagerParams);
}
#endregion
}

4. Create ProductViewModel ViewModel


This model will have IPagedList properties and collection of Product Model

public class ProductViewModel
{
public IPagedList PagedList { get; set; }
public List<ProductModel> Products { get; set; }
}

5. Create ProductServices to accessing to database using the GetProductsByFilter stored procedures.


In this procedure, we have to update TotalRecords for PagerParams

cmd.CommandText = "GetProductsByFilter"; //store procedure name                    
cmd.CommandType = CommandType.StoredProcedure;

SqlParameter i_Page_Index = cmd.Parameters.Add("@i_Page_Index", SqlDbType.Int);
i_Page_Index.Direction = ParameterDirection.Input;
i_Page_Index.Value = param.PageIndex;

SqlParameter i_Page_Count = cmd.Parameters.Add("@i_Page_Count", SqlDbType.Int);
i_Page_Count.Direction = ParameterDirection.Input;
i_Page_Count.Value = param.PageSize;

cmd.Parameters.Add("@o_total_rows", SqlDbType.Int).Direction = ParameterDirection.Output;
//cmd.ExecuteNonQuery();
//int total = Convert.ToInt32(cmd.Parameters["@o_total_rows"].Value);
using (SqlDataReader reader = cmd.ExecuteReader())
{
//total = Convert.ToInt32(cmd.Parameters["@o_total_rows"].Value);
while (reader.Read())
{
int id = Int32.Parse(reader["ProductID"].ToString());
string name = reader["ProductName"].ToString();
ProductModel model = new ProductModel
{
Id = id,
ProductName = name
};
list.Add(model);
}
reader.NextResult();
param.TotalRecords = Convert.ToInt32(cmd.Parameters["@o_total_rows"].Value);
}

6. The controller will look like this


The pageIndex is start from 0

public ActionResult Index(int? page)
{
int currentPageIndex = page.HasValue ? page.Value - 1 : 0;
PagerParams param = new PagerParams(5, currentPageIndex, -1);
ProductViewModel model = new ProductViewModel
{
Products = ProductServices.ListProducts(param)
};
model.PagedList = param.ToPagedList();
return View(model);
}

7. Create View and implement paging using HtmlHelpers. The view like this

<table class="gridview" width="100%">
<tr>
<th>
ID
</th>
<th>
NAME
</th>
</tr>

<% foreach (var item in Model.Products) { %>

<tr>
<td style="width:50px">
<%= Html.Encode(item.Id) %>
</td>
<td>
<%= Html.Encode(item.ProductName) %>
</td>
</tr>

<% } %>

</table>
<div class="pager">
<%= Html.Pager(ViewData.Model.PagedList.PageSize, ViewData.Model.PagedList.PageNumber, ViewData.Model.PagedList.TotalItemCount) %>
</div>

8. Now is the results


image

Thứ Sáu, 14 tháng 1, 2011

ASP.NET MVC: naming convention when using custom ViewModel

If you are going to use custom ViewModel (complex Model) in ASP.NET MVC 2, you may always get a null Returns in Form when call Html.EditorFor(p => p.PageContent) If you review ASP.NET MVC 2 BindModel() Source, you see:

public virtual object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
if (bindingContext == null) {
throw new ArgumentNullException("bindingContext");
}

bool performedFallback = false;

if (!String.IsNullOrEmpty(bindingContext.ModelName) && !DictionaryHelpers.DoesAnyKeyHavePrefix(bindingContext.ValueProvider, bindingContext.ModelName)) {
// We couldn't find any entry that began with the prefix. If this is the top-level element, fall back
// to the empty prefix.
if (bindingContext.FallbackToEmptyPrefix) {
bindingContext = new ModelBindingContext() {
Model = bindingContext.Model,
ModelState = bindingContext.ModelState,
ModelType = bindingContext.ModelType,
PropertyFilter = bindingContext.PropertyFilter,
ValueProvider = bindingContext.ValueProvider
};
performedFallback = true;
}
else {
return null;
}
}

// Simple model = int, string, etc.; determined by calling TypeConverter.CanConvertFrom(typeof(string))
// or by seeing if a value in the request exactly matches the name of the model we're binding.
// Complex type = everything else.
if (!performedFallback) {
ValueProviderResult vpResult;
bindingContext.ValueProvider.TryGetValue(bindingContext.ModelName, out vpResult);
if (vpResult != null) {
return BindSimpleModel(controllerContext, bindingContext, vpResult);
}
}
if (TypeDescriptor.GetConverter(bindingContext.ModelType).CanConvertFrom(typeof(string))) {
return null;
}

return BindComplexModel(controllerContext, bindingContext);
}

The Controller action I defined to hand Create action

public ActionResult Create(EnterpriseSearch seach)

Thus causing the DefaultModelBinder find the Search model then it is not match with EnterpriseSearch model => search property always null.


So, your have to change your Create action like this (Model name and parameter name is the same characters: EnterpriseSearch enterpriseSearch)

public ActionResult Create(EnterpriseSearch enterpriseSearch)

Thứ Ba, 28 tháng 12, 2010

Error: {"OCI-22303: type \"{SCHEMA}\".\"{UDTTYPE}\" not found"}

To resolve the error You have to:

- Use UPPERCASE for your type: for example "VIBRIP.PRODUCTNESTEDTABLE" where VIBRIP is schema name and PRODUCTNESTEDTABLE is your type name

- Grant execute on your type for the user you are connected to Oracle

GRANT EXECUTE ON PRODUCTNESTEDTABLE TO user;

where user is the user that should access the type.

ORA-06550, PLS-00306: wrong number or types of arguments in call to…

Hi There,
I think that I can help you somewhere because I got this problem. I'm working with oracle 10g and VB.NET 2008.
I was stucked since 3 weeks searching the resolution for this kind of error.
Fortunately, I've resolved it today.
It's very dummy I think but it worked lool.
You have to pass all your OUT parameters in right order fiirst before that those of IN ones.
cmd.Parameters.Add("p_task_id", OracleDbType.Int32).Direction = ParameterDirection.Output;
and then
cmd.Parameters.Add("p_task_status", OracleDbType.Varchar2).Direction = ParameterDirection.Input;
cmd.Parameters["p_task_status"].Value = TaskStatus.COMPLETED;
That's it I think.
Hope this will help you.
Viele Glück.
Walid

Reference

Thứ Hai, 15 tháng 11, 2010

Develop a File IO ActiveX control in Microsoft .NET

Development

1. Use this guide to create your own ActiveX control

Writing an ActiveX Control in .NET

2. Design your ActiveX as picture bellow:

image

3. Use following code behind

namespace FileIOActiveXControl
{
public partial class WriteToClientControl : UserControl
{
public WriteToClientControl()
{
InitializeComponent();
}

private void btnWrite_Click(object sender, EventArgs e)
{
WriteLog(@"c:\temp\log.txt", txtUserInput.Text);
}

public void WriteLog(string fileName, string content)
{
try
{
StreamWriter log = new StreamWriter(fileName, true);
log.WriteLine(content);
log.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
}

4. Create TestActiveX.htm

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<body>
<hr>
Please note that you MUST load this HTML document from your local IIS, or from a TRUSTED SITE or the control will not load properly on the page due to security policy.
<br>
To fix this:<br>
<li>copy the HTM file and the DLL into your c:\inetpub\wwwroot folder (or applicable IIS root directory)
<li>load the document in your browser with the url: http://localhost/TestActiveX.htm
<hr>
<font face=arial size=1>
<OBJECT id="fileIOActiveX" name="fileIOActiveX" classid="http:FileIOActiveXControl.dll#FileIOActiveXControl.WriteToClientControl" width="450" height="100" VIEWASTEXT>
</OBJECT>
</font>
<hr>
</body>
</script>
</html>

Deployment


1. Copy FileIOActiveXControl.dll and TestActiveX.htm to the same folder


2. Go to IIS to create a new web site and point to that folder


3. Use another computer to test your control.


If you control cannot load to page like picture bellow.


image


a. Try to add the site to Trusted Zone in IE


b. Make sure .dll and .htm in the same folder


If your control is loaded correctly, you will see:


image


4. Enter some text to the textbox then click Write To File button. You may see:


“Request for the permission of type 'System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.”


image


To fix this. You have to configure Code Access Security for Microsoft .NET on client. Run following command to fix this issue.


“C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\caspol.exe -quiet -machine -chggroup Trusted_Zone FullTrust”


image


And  (MANDATORY if your client is running OS 64 bit)


“C:\WINDOWS\Microsoft.NET\Framework64\v2.0.50727\caspol.exe -quiet -machine -chggroup Trusted_Zone FullTrust”


image


5. Stop IE –> reload the test page –> input some text –> click “Write To File” button again now. There will not have any error message appear. Check the log file from C:\temp\log.txt


Success.

Thứ Sáu, 5 tháng 11, 2010

Add Digital Signature to a PDF programmatically using Adobe SDK and Microsoft .NET

Requirement:

- Adobe Professional Installed

- Adobe SDK downloaded

- Microsoft Visual Studio

- Valid Certificate file (.pfx). It means the .pfx file not yet expired.

Setup environment:

- Copy sdkAddSignature.js from SDK to Acrobat\Javascripts folder

- Optional: Create Your Own Test Certificate

Code C#

            Type AcrobatCAcroAppType;
AcrobatCAcroAppType = Type.GetTypeFromProgID("AcroExch.app");
Acrobat.CAcroApp gapp = (Acrobat.CAcroApp)Activator.CreateInstance(AcrobatCAcroAppType);

Type AcrobatPDDocType;
AcrobatPDDocType = Type.GetTypeFromProgID("AcroExch.PDDoc");
Acrobat.CAcroPDDoc gpddoc = (Acrobat.CAcroPDDoc)Activator.CreateInstance(AcrobatPDDocType);

object jso;
if (gpddoc.Open("d:\\temp\\s.pdf"))
{
jso = gpddoc.GetJSObject();
object[] param = new object[1];

param[0] = "c:\\CATest.pfx";
object con = jso.GetType().InvokeMember("SetUserDigitalIDPath",
BindingFlags.InvokeMethod, null, jso, param);

param[0] = "testpassword";
con = jso.GetType().InvokeMember("SetUserPassword",
BindingFlags.InvokeMethod, null, jso, param);

param[0] = jso;
con = jso.GetType().InvokeMember("AddSignature",
BindingFlags.InvokeMethod, null, jso, param);
}

Code VB:

        Dim gapp As Acrobat.CAcroApp
Dim gpddoc As Acrobat.CAcroPDDoc
Dim jso As Object
gapp = CreateObject("acroexch.app")
gpddoc = CreateObject("acroexch.pddoc")

If gpddoc.Open("d:\temp\s.pdf") Then
jso = gpddoc.GetJSObject()
jso.SetUserPassword("testpassword")
jso.SetUserDigitalIDPath("/C/CATest.pfx") //remember this path
jso.AddSignature(jso)
gapp.Show()
End If

basquang@hotmail.com

Thứ Hai, 25 tháng 10, 2010

SharePoint PermissionMask

I found the values in C:\Program Files (x86)\Microsoft Office\Office14\CAML2XSL.XSL

<!-- keep in sync with global.cs!!!
EmptyMask =0x0000000000000000,

// list/document perms =0x000000000000XXXX,
ViewListItems =0x0000000000000001,
AddListItems =0x0000000000000002,
EditListItems =0x0000000000000004,
DeleteListItems =0x0000000000000008,
ApproveItems =0x0000000000000010,
OpenItems =0x0000000000000020,
ViewVersions =0x0000000000000040,
DeleteVersions =0x0000000000000080,
CancelCheckout =0x0000000000000100,
ManagePersonalViews =0x0000000000000200,
// UnusedList10 =0x0000000000000400, /* obsolete ManageListPermission */
ManageLists =0x0000000000000800,
ViewFormPages =0x0000000000001000,
// UnusedList20 =0x0000000000002000,
// UnusedList40 =0x0000000000004000,
// UnusedList80 =0x0000000000008000,

// web level perms =0x0000XXXXXXXX0000,
Open =0x0000000000010000,
ViewPages =0x0000000000020000,
AddAndCustomizePages =0x0000000000040000,
ApplyThemeAndBorder =0x0000000000080000,
ApplyStyleSheets =0x0000000000100000,
ViewUsageData =0x0000000000200000,
CreateSSCSite =0x0000000000400000,
ManageSubwebs =0x0000000000800000,
CreateGroups =0x0000000001000000,
ManagePermissions =0x0000000002000000,
BrowseDirectories =0x0000000004000000,
BrowseUserInfo =0x0000000008000000,
AddDelPrivateWebParts =0x0000000010000000,
UpdatePersonalWebParts =0x0000000020000000,
ManageWeb =0x0000000040000000,
UseRemoteAPIs =0x0000002000000000,
ManageAlerts =0x0000004000000000,
CreateAlerts =0x0000008000000000,
EditMyUserInfo =0x0000010000000000,

// special perms =0xXXXX000000000000,
EnumeratePermissions =0x4000000000000000,

// end of list
FullMask =0x7FFFFFFFFFFFFFFF,
-->

GetPermissionMask

<xsl:template name="GetPermissionMask">

<xsl:variable name="Permission" select="name(@*[1])"/>

<xsl:choose>
<xsl:when test="$Permission='PermEmptyMask'">0</xsl:when>
<xsl:when test="$Permission='PermViewListItems'">1</xsl:when>
<xsl:when test="$Permission='PermAddListItems'">2</xsl:when>
<xsl:when test="$Permission='PermEditListItems'">4</xsl:when>
<xsl:when test="$Permission='PermDeleteListItems'">8</xsl:when>
<xsl:when test="$Permission='PermApproveItems'">16</xsl:when>
<xsl:when test="$Permission='PermOpenItems'">32</xsl:when>
<xsl:when test="$Permission='PermViewVersions'">64</xsl:when>
<xsl:when test="$Permission='PermDeleteVersions'">128</xsl:when>
<xsl:when test="$Permission='PermCancelCheckout'">256</xsl:when>
<xsl:when test="$Permission='PermManagePersonalViews'">512</xsl:when>
<xsl:when test="$Permission='PermManageLists'">2048</xsl:when>
<xsl:when test="$Permission='PermViewFormPages'">4096</xsl:when>
<xsl:when test="$Permission='PermOpen'">65536</xsl:when>
<xsl:when test="$Permission='PermViewPages'">131072</xsl:when>
<xsl:when test="$Permission='PermAddAndCustomizePages'">262144</xsl:when>
<xsl:when test="$Permission='PermApplyThemeAndBorder'">524288</xsl:when>
<xsl:when test="$Permission='PermApplyStyleSheets'">1048576</xsl:when>
<xsl:when test="$Permission='PermViewUsageData'">2097152</xsl:when>
<xsl:when test="$Permission='PermCreateSSCSite'">4194314</xsl:when>
<xsl:when test="$Permission='PermManageSubwebs'">8388608</xsl:when>
<xsl:when test="$Permission='PermCreateGroups'">16777216</xsl:when>
<xsl:when test="$Permission='PermManagePermissions'">33554432</xsl:when>
<xsl:when test="$Permission='PermBrowseDirectories'">67108864</xsl:when>
<xsl:when test="$Permission='PermBrowseUserInfo'">134217728</xsl:when>
<xsl:when test="$Permission='PermAddDelPrivateWebParts'">268435456</xsl:when>
<xsl:when test="$Permission='PermUpdatePersonalWebParts'">536870912</xsl:when>
<xsl:when test="$Permission='PermManageWeb'">1073741824</xsl:when>
<xsl:when test="$Permission='PermUseRemoteAPIs'">137438953472</xsl:when>
<xsl:when test="$Permission='PermManageAlerts'">274877906944</xsl:when>
<xsl:when test="$Permission='PermCreateAlerts'">549755813888</xsl:when>
<xsl:when test="$Permission='PermEditMyUserInfo'">1099511627776</xsl:when>
<xsl:when test="$Permission='PermEnumeratePermissions'">4611686018427387904</xsl:when>
<xsl:when test="$Permission='PermFullMask'">9223372036854775807</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:template>

Thứ Sáu, 15 tháng 10, 2010

Building batch approval ribbon in SharePoint 2010

In SharePoint 2010, We can delete multiple items but cannot with approve/reject items. So, how can user approve/reject multiple items in one time? For example TimeSheet application in SharePoint. We can create custom Ribbon then using Application page as dialog and SharePoint Server-Side object model to solve that problem. To complete your function, you have to resolve following things:

1. How we pass the selected items and list to dialog page? We use ECMAScript as following script in CommanAction funtion of the ribbon button

// Shows custom dialog using SharePoint Client JS OM
function RibbonButtonHandler() {
var ctx = SP.ClientContext.get_current();
var items = SP.ListOperation.Selection.getSelectedItems(ctx);
var myItems = '';
var k;

for (k in items) {
myItems += '|' + items[k].id;
}
var options = {
url: "/_layouts/BatchApprovalRibbon/UI/ApproveRejectPage.aspx?items=" + myItems + "&list=" + SP.ListOperation.Selection.getSelectedList(),
width: 600,
height: 400,
dialogReturnValueCallback: demoCallback
};

SP.UI.ModalDialog.showModalDialog(options);
}

By passing selected items and list as text in url parameter, we can easy to get those in application page by Request.QueryString


2. In Application Page dialog, how can We approve/reject item programmatically? We cannot use item[“Approval Status”] = “approved”, we must use following scripts instead.

SPListItem item = currentList.GetItemById(Int32.Parse(id));               SPModerationInformation approvalStatus = item.ModerationInformation; approvalStatus.Status = status;                        
approvalStatus.Comment = comments;
item.Update();

with status is SPModerationStatusType.


3. After approve/reject Items, how can we close dialog page and refresh parent page?


First we must close  the dialog page in server-side code:

protected void CloseDialogPage()
{
Context.Response.Write("<script type='text/javascript'>window.frameElement.commitPopup();</script>");
Context.Response.Flush();
Context.Response.End();
}

Then we call SP.UI.ModalDialog.RefreshPage(SP.UI.DialogResult.OK) in dialogReturnValueCallback function as following

function demoCallback(dialogResult, returnValue) {
SP.UI.Notify.addNotification('Operation Successful!');
SP.UI.ModalDialog.RefreshPage(SP.UI.DialogResult.OK);
}

Now You can use your ribbon to process batch approve/reject items.


clip_image002


4. But I think we should consider how to enable/disable your ribbon by condition or context. To Enable/Disable ribbon button, we use EnabledScript attribute in CommandUIHandler element in you ribbon custom action

<CommandUIHandler
Command="Ribbon.Approval.Batch Approval.Button_CMD"
EnabledScript="javascript:EnableRibbonButton();"
CommandAction="javascript:RibbonButtonHandler();" />

And in javascript funtion, in my scenario, we want to disable the button when there isn’t any items has selected and will enable for special list only. We use following script

function EnableRibbonButton() {
var items = SP.ListOperation.Selection.getSelectedItems();
var ci = CountDictionary(items);
var listId = SP.ListOperation.Selection.getSelectedList();
if (ci > 0 && listId == '{A32BC151-5396-4377-AFB3-B769668C3A83}') {
return true;
}
return false;
}

Finally: NOTE


Keep in mind that we pass url: "/_layouts/BatchApprovalRibbon/UI/ApproveRejectPage.aspx…. It mean dialog page will called under top level sie context. So SPContext.Current always return top level site. So if You use this ribbon in subsite, You should get error message “the list does not exist”. To resolve it, We can


- change the url to: url: "/[your subsite]/_layouts/…


- In Code behind, does not use SPContext.Current.Web directly, we must find the exactly the web of the list instead. We was use

SPSite site = SPContext.Current.Site;           
foreach (SPWeb web in site.AllWebs)
{
currentList = SearchListById(web, listGuid);
.................
}
SPList SearchListById(SPWeb web, Guid listId)
{
foreach (SPList list in web.Lists)
{
if (list.ID.Equals(listGuid))
{
return list;
}
}
return null;
}

Hope this help!

Thứ Năm, 14 tháng 10, 2010

ECMAScript: SP.ClientContext is undefined or null

Sometime We cannot use SP.ClientContext in Site pages, Web part pages or Application pages. Although We was following this guide from msdn

Setting Up an Application Page for ECMAScript

If so, please replace the below lines

<script type="text/ecmascript" src="/_layouts/SP.Core.js" />
<script type="text/ecmascript" src="/_layouts/SP.Debug.js" />
<script type="text/ecmascript" src="/_layouts/SP.Runtime.Debug.js" />

With

<SharePoint:ScriptLink ID="ScriptLink1" Name="sp.debug.js" LoadAfterUI="true" Localizable="false" runat="server" />

Reference MSDN

SharePoint 2010 Woes: Avoid comments in your CustomAction-XML for Ribbon customizations

For a customer project I had to add a custom tab to the Ribbon of SharePoint 2010 for a specific library. In this tab I wanted to have several groups of controls. In one group I needed to have a button that triggers the standard upload functionality of SharePoint (Command="UploadDocument"). So far this is a pretty straight forward process.

If you've ever created a CustomAction to add a tab to the Ribbon, you'll agree that you've to write a looot of XML. I personally believe it's a best practice to comment the code one creates. If you have a XML-document with hundreds of lines of mark-up, usually it makes sense to add a comment here and there. In my case my CustomAction-XML was commented like this:


<Groups
Id="Test.Ribbon.Tab.Groups">
  <!--
    Group "New" with Button "Upload"
-->
  <Group
Id="Test.Ribbon.Tab.NewGroup"

>
    <Controls
Id="Test.Ribbon.Tab.NewGroup.Controls">
  <Button
Id="Test.Ribbon.Tab.NewGroup.Controls.Upload"
Command="UploadDocument"

/>
    </Controls>
  </Group>

</Groups>

As you can see I've added a comment above the group "Test.Ribbon.Tab.NewGroup" and with this I've entered a world of pain. The Ribbon and the tab including all groups and controls were rendered without any issues. I also was able to click on the button "Test.Ribbon.Tab.NewGroup.Controls.Upload" and the "Upload Document"-dialog was displayed as expected, but after clicking on the "OK"-button in the dialog, I ran into this well-known error:

clip_image001[7]

In the SharePoint log you can find the following entry related to this error:

System.NullReferenceException: Object reference not set to an instance of an object. at Microsoft.Web.CommandUI.Ribbon.CreateRenderContext(CUIDataSource uiproc) at Microsoft.Web.CommandUI.Ribbon.Render(HtmlTextWriter writer) at Microsoft.SharePoint.WebControls.SPRibbon.Render(HtmlTextWriter writer) at System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children) at System.Web.UI.HtmlControls.HtmlForm.RenderChildren(HtmlTextWriter writer) at System.Web.UI.HtmlControls.HtmlForm.Render(HtmlTextWriter output) at System.Web.UI.HtmlControls.HtmlForm.RenderControl(HtmlTextWriter writer) at System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children) at System.Web.UI.HtmlControls.HtmlContainerControl.Render(HtmlTextWriter writer) at System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children) at System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children) at System.Web.UI.Page.Render(HtmlTextWriter writer) at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

The method "Microsoft.Web.CommandUI.Ribbon.CreateRenderContext" throws a "System.NullReferenceException". Whoa! To make a long story short, after digging around for several hours I found the reason for this issue: the comment above the group in the CustomAction-XML. Here is what Reflector tells you about the implementation of "Microsoft.Web.CommandUI.Ribbon.CreateRenderContext":

DataNode node4 = uiproc.GetResultDocument().SelectSingleNode("/spui:CommandUI/spui:Ribbon/spui:Tabs/spui:Tab[@Id='" + this.InitialTabId + "']/spui:Groups");

if (node4 == null)

{

node4 = uiproc.GetResultDocument().SelectSingleNode("/spui:CommandUI/spui:Ribbon/spui:ContextualTabs/spui:ContextualGroup/spui:Tab[@Id='" + this.InitialTabId + "']/spui:Groups");

}

Hashtable hashtable = new Hashtable();

if (node4 != null)

{

foreach (DataNode node5 in node4.ChildNodes)

{

XmlAttribute attribute2 = node5.Attributes["Id"];

if (attribute2 != null)

{

hashtable[attribute2.Value] = node5;

} } }

I will not comment on the quality of this implementation, but if you look at XmlAttribute attribute2 = node5.Attributes["Id"]; it becomes clear: the Attributes-property is the reason why I get the NullReferenceException, because for a System.Xml.XmlNode-Object that is a System.Xml.XmlComment this property is null (http://msdn.microsoft.com/en-us/library/system.xml.xmlnode.attributes.aspx). So if you put a comment above your group in your CustomAction-XML you're doomed. Do yourself a favor and don't do it.

I hope this blog-post will save you save some time and headaches. It took me quite some time to figure out the reason why my button in my custom Ribbon-tab wasn't working.

Reference

Created by Sven Häfker

Thứ Bảy, 9 tháng 10, 2010

Update current user info using SPServices and ECMAScript

Here is the source code:

1. Get user info then auto fill to controls

var displayName = "";
function GetUser()
{
displayName = $().SPServices.SPGetCurrentUser({
fieldName: "Title"
});
var ctx = new SP.ClientContext.get_current();
this.currentUser = ctx.get_web().get_currentUser();
ctx.load(this.currentUser);
ctx.executeQueryAsync(Function.createDelegate(this, this.onSucceededGetUser),
Function.createDelegate(this, this.onQueryFailed));
}

function onSucceededGetUser(sender, args)
{
//alert("The users " + this.currentUser.get_loginName());
$("input[Title='Title']").val(displayName);
$("input[Title='Account']").val(this.currentUser.get_loginName());
$("input[Title='Email']").val(this.currentUser.get_email());
}
function onQueryFailed(sender, args)
{
alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}

2. Update user email info

function UpdateUserEmail() 
{
var ctx = new SP.ClientContext.get_current();
this.currentUser = ctx.get_web().get_currentUser();
var email = $("input[Title='Email']").val();
this.currentUser.set_email(email);
this.currentUser.update();
ctx.executeQueryAsync(Function.createDelegate(this, this.onSucceededUpdateUserEmail),
Function.createDelegate(this, this.onQueryFailed));
}
function onSucceededUpdateUserEmail(sender, args)
{
alert("Updated! " + this.currentUser.get_email());
}

I’d like SPServices and ECMAScripts.

Thứ Năm, 7 tháng 10, 2010

Complete Install SharePoint 2010 on Windows 7

Following these guide:

Setting Up the Development Environment for SharePoint 2010 on Windows Vista, Windows 7, and Windows Server 2008

Single Server Complete Install of SharePoint 2010 using local accounts

Notice:

- In SharePoint 2010 Management Shell: don’t use localhost, (local) or (.) for DatabaseServer parameter. Use machine name or IP address instead. Otherwise you may get “localhost is an invalid or loopback address.  Specify a valid server address” error message.

- In login page: use full user name like “machinename\account name” instead only account name. For example: “basquang\Nguyen Ba Quang” is correct.

- imageimage

Hope this help!

Thứ Năm, 30 tháng 9, 2010

jQuery for SharePoint Web Services

The “core” of the jQuery Library for SharePoint Web Services is the $().SPServices function, which allows you to simply call each Web Service operation.

The table below shows the Web Services currently available in the library and where they work, along with links to the MSDN documentation:

Web Service WSS 3.0 MOSS MSDN Documentation
Alerts Alerts Web Service
Authentication Authentication Web Service
Forms Forms Web Service
Lists Lists Web Service
Permissions Permissions Web Service
Users and Groups Users and Groups Web Service
Versions Versions Web Service
Views Views Web Service
WebPartPages Web Part Pages Web Service
Webs Webs Web Service
PublishedLinksService   PublishedLinksService Web Service
Search   Search Web Service
UserProfileService   User Profile Web Service
Workflow   Workflow Web Service
 
Calling the Library

To set things up to use the jQuery Library for SharePoint Web Services, you’ll need to add references to it and the core jQuery library in the right context for what you are trying to accomplish.

<script src="/TimeSheet/jQuery%20Libraries/jquery-1.4.2.min.js" type="text/javascript"></script>
<script src="/TimeSheet/jQuery%20Libraries/jquery.SPServices-0.5.6.min.js" type="text/javascript"></script>


What all this adds up to is that you can use the jQuery Library for SharePoint Web Services to call any of the Web Service operations it wraps as simply as this:



   1: $().SPServices({
   2:    operation:  "GetList",
   3:    listName:  "Project",
   4:    completefunc: function  (xData, Status) {
   5:      ...do something...
   6:    }
   7: });

The Results

When you make one of these calls, you get back XML, just as you’ve passed XML into it. As I was building the library, I found that I very often wanted to see what was in that XML in an easy to read format, so I built the $().SPServices.SPDebugXMLHttpResult function. If you’re comfortable with XML and debugging tools, you may never need this function, but I find it useful. If you wanted to use it in the above example, replace the completefunc with this:



   1: completefunc: function (xData, Status) {
   2:    var out =  $().SPServices.SPDebugXMLHttpResult({
   3:      node:  xData.responseXML
   4:    });
   5:    $(divId).html("").append("<b>This  is the output from the GetList operation:</b>" + out);
   6: }

where divId is the ID for a DIV on the calling page where you’d like to place the results. The output from the $().SPServices.SPDebugXMLHttpResult function for GetList will look something like this excerpt:


 

A21319952F68E79E_946_0[1]A21319952F68E79E_946_1[1]

On the other hand, if you’d like to work with the output, you’d probably do something like this:



   1: $().SPServices({
   2:    operation:  "GetList",
   3:    async: false,
   4:    webURL: opt.webURL,
   5:    listName:  opt.listName,
   6:    completefunc:  function(xData, Status) {
   7:      $(xData.responseXML).find("Field").each(function()  {
   8:        if($(this).attr("StaticName")  == opt.columnStaticName) displayName = $(this).attr("DisplayName");
   9:      });
  10:    }
  11: });

This example comes from the $().SPServices.SPGetDisplayFromStatic function. In the completefunc, I find all of the Field elements, iterate through them until I find the one I’m looking for, then grab the DisplayName attribute to return.


Source Reference

Thứ Tư, 22 tháng 9, 2010

Load control template file /_controltemplates/TaxonomyPicker.ascx failed: SharePoint 2010

Problem:
Numerous errors in the event log of a SharePoint 2010 server with the following error event descriptions:

Load control template file /_controltemplates/TaxonomyPicker.ascx failed: Could not load type 'Microsoft.SharePoint.Portal.WebControls.TaxonomyPicker' from assembly 'Microsoft.SharePoint.Portal, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c'.

and

Load control template file /_controltemplates/ScenarioNavigation.ascx failed: The 'wssuc:ButtonSection' tag has already been registered.

Fix 1:
Locate the file “TaxonomyPicker.ascx” in the Controltemplates directory of the 14 Hive, search for the following line

<%@ Control className="TaxonomyPickerControl" Language="C#" Inherits="Microsoft.SharePoint.Portal.WebControls.TaxonomyPicker&#44;Microsoft.SharePoint.Portal, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>


and replace ‘&#44;’ with ‘,’.

The correct line should look like


<%@ Control className="TaxonomyPickerControl" Language="C#" Inherits="Microsoft.SharePoint.Portal.WebControls.TaxonomyPicker,Microsoft.SharePoint.Portal, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>


Fix 2:
Locate the file “ScenarioNavigation.ascx” in the Controltemplates directory of the 14 Hive, search for a duplicate line of


<%@ Register TagPrefix="wssuc" TagName="ButtonSection" Src="/_controltemplates/ButtonSection.ascx" %>


and delete the second one.

Update:

The second problem seems to be fixed in the RTM version. The “TaxonomyPicker.ascx”-Problem still persists.

Update2:

Fix 1 (“TaxonomyPicker.ascx”) doesn’t apply to the RTM Version anymore as the “TaxonomyPicker” Class seems to be obsolete.
If you want to fix this error simply rename the “TaxonomyPicker.ascx” to something like “TaxonomyPicker.ascxBACKUP” to prevent recompilation by the CLI and the error is gone.

Reference





Comments




Quang Nguyen Ba - 9/22/2010 11:15:55 AM
Infact TaxonomyPicker is no longer in use: http://todd-carter.com/post/2010/05/03/Help-Wanted-Taxonomy-Picker.aspx