Feeds:
Posts
Comments

Archive for the ‘Programmability’ Category


Often we have seen that out of box securities are not enough to handle the security requirements within an organization, so we start looking for options for managing security, often we end up creating multiple groups & categories, though there is another security model that can be worked out using RBS but as per my experience RBS has its own constraints which in itself is another topic of discussion, hence leaving it to be explained as part of next post, so as said we often end up creating multiple categories in order to segregate projects, it remains good as long as there are few but often down the line it starts getting complex and hence adding manual overhead to maintain it, now there are several things options we could pursue

I won’t be talking about managing security categories & groups manually as we all know that’s the option typically used other methods but would definitely recommend plan early for all your security needs as if you are planning customized automated security implementation you may need to start discussions early in the phase of implementation you will know why i am saying so as as you read down

Using Project Permission :: Let the project owner decide & manage the security, this is also applicable in cases where there are some people who are not on project team or project task neither overseeing a project but need permission to see the project, for eg an Audit group which is not related directly to project but as an individual department needs to audit few projects, so this could be achieved using Project Permission which an owner can manage by himself, refer to the link for more details on how to use it, Advantage of using this method is you don’t need to create additional categories or groups to manage permission every time such a request comes, disadvantage is there are predefined permissions that you can but still doesn’t makes an overhead for your administrators, but be cautioned as this method gives flexibility it also adds another level of risk and needs monitoring on a regular basis as to when the permission needs to be removed once the work is done

Using Automated methods which essentially involves customization :: So when you think security management would be an overhead for administrators and you don’t want to call upon an administrator every time a project is created to get the project added to a specific category, you may want to get some customization done where in using code you get your projects automatically added to a specific category as soon as the project is created, for eg based on an enterprise project type and some other project level metadata you would want to add the project automatically to the category, so for this you would need to get some PSI code working and hence in this post below you will find the code base for achieving the same

Now remember above i said you should start planning / discussion early on your security automation and the reason was now so you have the code base available which can automatically add the projects to categories but to implement the code there are several possible solutions one of them being the custom workflow, so if your custom workflow is in development you may want to embed the code within your workflow and so you need it early, there isn’t any hard or fast rule why you should be doing it the first time but from my experience would recommend to get it done earlier as no of times you redeploy your workflow you lose workflow history as you need to reattach all your projects to the workflow and need to move them into appropriate stages

Hence there are 2 possible ways you could get your code deployed as below

1. Embed the code base within your custom workflow

2. Add the code base as server-side event handler (Recommended)

In my opinion i would recommend using the second approach as it removes the dependency of workflow and if in worst case anything appears to be causing trouble with the code it can be easily redeployed and would not cause any trouble to workflow, but adding to workflow has its own advantages 🙂

Now having talked too much would just post my code below, remember it’s based on WCF & i am using compiled ProjectServerServices.DLL & Project Server Library DLL as reference hence please remember to add & other referenced them before you start using it

 

private void CreateCategory_Click(object sender, EventArgs e)

{

pwaUrl = @”http://ServerName/pwa/”;

pwaUri = new Uri(pwaUrl);

const int MAXSIZE = 500000000;

const string svcRouter = “_vti_bin/PSI/ProjectServer.svc”;

pwaUrl = pwaUri.Scheme + Uri.SchemeDelimiter + pwaUri.Host + “:” + pwaUri.Port + pwaUri.AbsolutePath;

// Create a binding for HTTP.

BasicHttpBinding binding = null;

if (pwaUri.Scheme.Equals(Uri.UriSchemeHttps))

{

// Create binding for HTTPS.

binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);

}

else

{

// Create binding for HTTP.

binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly);

}

binding.Name = “basicHttpConf”;

binding.SendTimeout = TimeSpan.MaxValue;

Console.WriteLine(“SendTimeout value:\n\t{0} days,\n\t{1} hours,\n\t{2} minutes,\n\t{3} seconds”,

binding.SendTimeout.Days.ToString(), binding.SendTimeout.Hours.ToString(),

binding.SendTimeout.Minutes.ToString(), binding.SendTimeout.Seconds.ToString());

binding.MaxReceivedMessageSize = MAXSIZE;

binding.ReaderQuotas.MaxNameTableCharCount = MAXSIZE;

binding.MessageEncoding = WSMessageEncoding.Text;

binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm;

// The endpoint address is the ProjectServer.svc router for all public PSI calls.

EndpointAddress address = new EndpointAddress(pwaUrl + svcRouter);

#region UID

ICredentials credentials = new NetworkCredential(“UserName”, “Password”, “Domain”);

#endregion

SvcSecurity.SecurityClient SecClient = new SvcSecurity.SecurityClient(binding,address);

SecClient.ClientCredentials.Windows.ClientCredential = (NetworkCredential)credentials;

SecClient.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;

SecClient.ChannelFactory.Credentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;

SecClient.ChannelFactory.Credentials.Windows.AllowNtlm = true;

SecClient.Endpoint.Address = new EndpointAddress(pwaUrl + “_vti_bin/PSI/ProjectServer.svc”);

using (OperationContextScope scope = new OperationContextScope(SecClient.InnerChannel))

{

// Disable Forms/ADFS authentication, to enable Windows authentication.

//WebOperationContext.Current.OutgoingRequest.Headers.Remove(“X-FORMS_BASED_AUTH_ACCEPTED”);

//WebOperationContext.Current.OutgoingRequest.Headers.Add(“X-FORMS_BASED_AUTH_ACCEPTED”, “f”);

try

{

Guid NewCustomCatID = Guid.NewGuid();

SvcSecurity.SecurityCategoriesDataSet CatDS = new SvcSecurity.SecurityCategoriesDataSet();

SvcSecurity.SecurityCategoriesDataSet.SecurityCategoriesRow NewCatRow = CatDS.SecurityCategories.NewSecurityCategoriesRow();

NewCatRow.WSEC_CAT_UID = NewCustomCatID;

NewCatRow.WSEC_CAT_NAME = “Test Category from Code “;

NewCatRow.WSEC_CAT_DESC = “This is test category 1.”;

CatDS.SecurityCategories.AddSecurityCategoriesRow(NewCatRow);

#region Optional operations which can be performed

////////////////////////Examples OF Other Functions that can be perofrmed (Optional)/////////////////////////////////////

// Add a user to New category.

SvcSecurity.SecurityCategoriesDataSet.UserRelationsRow userRelationsRow = CatDS.UserRelations.NewUserRelationsRow();

userRelationsRow.WSEC_CAT_UID = NewCustomCatID;

// Pass a resource GUID.

Guid existingResUid = new Guid(“xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx”);

userRelationsRow.RES_UID = existingResUid;

CatDS.UserRelations.AddUserRelationsRow(userRelationsRow);

// Specify the permissions for the user on new Category.

SvcSecurity.SecurityCategoriesDataSet.UserPermissionsRow userPermRow = CatDS.UserPermissions.NewUserPermissionsRow();

userPermRow.WSEC_CAT_UID = NewCustomCatID;

userPermRow.RES_UID = existingResUid;

userPermRow.WSEC_ALLOW = true;

// Add an object (project or resource) to new category

SvcSecurity.SecurityCategoriesDataSet.SecurityCategoryObjectsRow category2ObjectRow = CatDS.SecurityCategoryObjects.NewSecurityCategoryObjectsRow();

category2ObjectRow.WSEC_CAT_UID = NewCustomCatID;

category2ObjectRow.WSEC_OBJ_TYPE_UID = PSLibrary.PSSecurityObjectType.Project;

category2ObjectRow.WSEC_OBJ_UID = new Guid(“xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx”); // Pass Project UID to be added to the category

CatDS.SecurityCategoryObjects.AddSecurityCategoryObjectsRow(category2ObjectRow);

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#endregion

SecClient.CreateCategories(CatDS);

}

catch (System.ServiceModel.FaultException fault)

{

string errAttributeName;

string errAttribute;

string errOut;

string errMess = “”.PadRight(30, ‘=’) + “\r\n”

+ “Error details: ” + “\r\n”;

PSLibrary.PSClientError error = GetPSClientError(fault, out errOut);

errMess += errOut;

PSLibrary.PSErrorInfo[] errors = error.GetAllErrors();

PSLibrary.PSErrorInfo thisError;

for (int i = 0; i < errors.Length; i++)

{

thisError = errors[i];

errMess += “\r\n”.PadRight(30, ‘=’) + “\r\nPSClientError output:\r\n”;

errMess += thisError.ErrId.ToString() + “\n”;

for (int j = 0; j < thisError.ErrorAttributes.Length; j++)

{

errAttributeName = thisError.ErrorAttributeNames()[j];

errAttribute = thisError.ErrorAttributes[j];

errMess += “\r\n\t” + errAttributeName

+ “: ” + errAttribute;

}

}

MessageBox.Show(errMess);

}

}

}

Read Full Post »


Recently Received a query from user, at times users open plan from server without noticing that plan is already checked out, hence ending up opening plan in read only mode (though the open dialog box itself displays the project check in state and whom it is checked out to, but doesn’t displays warning ), they make on all the changes and don’t realize untill they try to save the plan 😦

So here is a quick n easy solution by which you can explicitly notify users, in case they open plan in read only mode,
place the macro given below in global file, which would fire on Project Open event and would notify users just in case plan has been opened up in read only mode, thereby saving hours of planning effort 🙂

Sub TestProjectReadOnlyState()
    If ActiveProject.ReadOnly = True Then
        MsgBox (“Project has opened in Readonly mode”)
    End If
End Sub

Read Full Post »


Yet another topic 🙂

Okay so there are many ways to do it even within powershell, for example use the project server commandlets & the same task i have done below in no of steps can be done in very short, however the idea to do this post is to showcase detailed way on how powershell can be leveraged to consume web services & relevant methods, so here it goes, have fun

This article can be also found at : http://gallery.technet.microsoft.com/office/Autopublish-All-Projects-3dc55f13


<#

Autopublish All Project Server Projects 🙂

#Author : : Sunil Kumar Singh

#>

<strong>CLS</strong>

# Set your variables

$PWAUrl = "http://ServerName/PWA"

# initiate the PSI Web Services

#Project Web Service

$ProjSvcURL = $PWAUrl + "/_vti_bin/PSI/Project.asmx?wsdl"

$ProjSvcProxy = <strong>New-WebServiceProxy</strong> <em>-uri</em> $ProjSvcURL <em>-useDefaultCredential</em> #-credential $Credential

#Queue web service

$QSvcUrl = $pwaUrl + "/_vti_bin/PSI/QueueSystem.asmx?wsdl"

$QSvcProxy = <strong>New-WebServiceProxy</strong> <em>-uri</em> $QSvcUrl <em>-useDefaultCredential</em> #-credential $Credential

#Create Dataset & Fill with Project List

$projDataSet = <strong>New-Object</strong> Microsoft.PowerShell.Commands.NewWebserviceProxy.AutogeneratedTypes.WebServiceProxy1_vti_bin_PSI_Project_asmx_wsdl.ProjectDataSet

$projDataSet = $ProjSvcProxy.ReadProjectList()

$PrjDS = $projDataSet.Project

#$projDataSet.Tables[0].Rows.Count

#CheckOut Project & Publish the Project#

foreach ($p in $PrjDS)

{

<strong>Write-host</strong> <em>-ForegroundColor</em> green  $p.PROJ_NAME ":: is being Checked Out for publishing" <strong>`</strong>

#Not required, just for illustration in case you would want to use it for update etc..

#$ProjSvcProxy.CheckOutProject($p.PROJ_UID, [System.Guid]::NewGuid(), "Checkout For AutoPublish via Powershell");

sleep 2

<strong>Write-host</strong> <em>-ForegroundColor</em> red  $p.PROJ_NAME ":: Will be published now, you may check the queue" <strong>`</strong>

$ProjSvcProxy.QueuePublish([System.Guid]::NewGuid(),$p.PROJ_UID, $true,"");

sleep 2

#$ProjSvcProxy.QueueCheckInProject([System.Guid]::NewGuid(),$p.PROJ_UID,$true,[System.Guid]::NewGuid(),"Checkin Post Autopublish");

<strong>Write-host</strong> <em>-ForegroundColor</em> yellow  $p.PROJ_NAME ":: is being checked in now" <strong>`</strong>

}

Read Full Post »


Often we are faced with situation where in we need to update certain list values to reflect new values 🙂 this time we had a requirement wherein we needed to replace item values in a list with an updated value, though there are many ways to do it for instance using sharepoint API or object model & use code to get it done 🙂 However interestingly we this time i thought of using powershell command to perform the same & i had to write a very small script to get the job done & here it goes

$sites = Get-SPSite ‘http://ServerName/PWA&#8217; | Get-SPWeb -Limit All
Foreach($site in $sites)
{
$site.title

$list = $site.Lists.TryGetList(“Risks”)

#Get all items in this list and save them to a variable
$items = $list.items

Foreach($item in $items)
{
#If the “Column” column value equals “Specified Value” Update with new Value
if($item[“Column Name”] -eq “Specified Value”)
{
#Change the value of the “Column Name” column
$item[“Column Name”] = “New Value”
#Update the item
$item.Update()

#Use this Optional Code to add new column to current list

#$myField = $list.Fields.Add(“Test5”, “Text”, 0)

#$list.Fields[$myField].Update()

#Write-host -ForegroundColor green “Column has been added”

}

}

}

Read Full Post »


As we all know Project server workflow are sequential workflow rather than state machine hence we cannot push workflow a stage back  when required, a typical scenario is project is in execution stage but for business reasons it needs to go on hold, now it could be handled multiple ways, the way i handle it by moving it to a new stage, now in project life cycle it needs to be again resumed in execution stage, we don’t know how many times a project can go on hold and then resume back, and since we cannot put all these stages in (as no of times is not definite) hence we use an alternate solution of restarting the workflow using PSI (WCF method instead of  ASMX) which includes using of skip to stage feature as well 🙂 so here goes the code which will help restarting the workflow and setting a stage

Note: on restarting the workflow you loose workflow history data from audit perspective, hence you need to have measures in place to retain those 🙂  

Add all the required references especially the ProjectServerServices.dll as described in technet documentation :: http://msdn.microsoft.com/en-us/library/ee767691.aspx


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Security.Principal; // Required for the TokenImpersonationLevel enumeration.
using System.ServiceModel;
using System.ServiceModel.Channels;
//using Microsoft.IdentityModel.Protocols.WSTrust;
using System.Xml;
using System.ServiceModel.Security;
//using Microsoft.IdentityModel.SecurityTokenService;
using System.IdentityModel.Tokens;
using System.ServiceModel.Web;
using PSLibrary = Microsoft.Office.Project.Server.Library;
using System.Web.Services.Protocols;
using System.Xml.Serialization;

namespace WebServiceWCFTest

{

public partial class Form1 : Form

{

public Form1()

{

InitializeComponent();

}

static private Uri pwaUri;          // URI of Project Web App.

static private string pwaUrl;       // URL of Project Web App.

private void ChangeStage_Click(object sender, EventArgs e)

{

pwaUrl = @"https://ServerName/pwa/";

pwaUri = new Uri(pwaUrl);

//SetClientEndpoints(pwaUri);

//CreateWSSWorkspace(pwaUri);

const int MAXSIZE = 500000000;

const string svcRouter = "_vti_bin/PSI/ProjectServer.svc";

pwaUrl = pwaUri.Scheme + Uri.SchemeDelimiter + pwaUri.Host + ":" + pwaUri.Port + pwaUri.AbsolutePath;

// Create a binding for HTTP.

BasicHttpBinding binding = null;

if (pwaUri.Scheme.Equals(Uri.UriSchemeHttps))

{

// Create binding for HTTPS.

binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);

}

else

{

// Create binding for HTTP.

binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly);

}

binding.Name = "basicHttpConf";

binding.SendTimeout = TimeSpan.MaxValue;

Console.WriteLine("SendTimeout value:\n\t{0} days,\n\t{1} hours,\n\t{2} minutes,\n\t{3} seconds",

binding.SendTimeout.Days.ToString(), binding.SendTimeout.Hours.ToString(),

binding.SendTimeout.Minutes.ToString(), binding.SendTimeout.Seconds.ToString());

binding.MaxReceivedMessageSize = MAXSIZE;

binding.ReaderQuotas.MaxNameTableCharCount = MAXSIZE;

binding.MessageEncoding = WSMessageEncoding.Text;

binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm;

// The endpoint address is the ProjectServer.svc router for all public PSI calls.

EndpointAddress address = new EndpointAddress(pwaUrl + svcRouter);

SvcWorkflow.WorkflowClient WrkflowClient = new SvcWorkflow.WorkflowClient(binding,address);

#region UID

ICredentials credentials = new NetworkCredential("Username", "Password", "Domain");

#endregion

WrkflowClient.ClientCredentials.Windows.ClientCredential = (NetworkCredential)credentials;

WrkflowClient.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;

WrkflowClient.ChannelFactory.Credentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;

WrkflowClient.ChannelFactory.Credentials.Windows.AllowNtlm = true;

WrkflowClient.Endpoint.Address = new EndpointAddress(pwaUrl + "_vti_bin/PSI/ProjectServer.svc");

using (OperationContextScope scope = new OperationContextScope(WrkflowClient.InnerChannel))

{

// Disable Forms authentication, to enable Windows authentication.

WebOperationContext.Current.OutgoingRequest.Headers.Remove("X-FORMS_BASED_AUTH_ACCEPTED");

WebOperationContext.Current.OutgoingRequest.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");

try

{

SvcWorkflow.WorkflowDataSet WfDS = new SvcWorkflow.WorkflowDataSet();

Guid PrjUID = new Guid("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX ");

Guid JobID = Guid.NewGuid();

Guid EptUID = new Guid("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX ");

Guid NewStageUID = new Guid("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"); //New Stage ID

//  WfDS = WrkflowClient.ReadWorkflowStatus(PrjUID,true);

WfDS.UpdateProjectWorkflows.AddUpdateProjectWorkflowsRow(JobID, PrjUID, EptUID, false, NewStageUID);

WrkflowClient.QueueUpdateProjectWorkflows(WfDS);

WrkflowClient.Close();

WrkflowClient = null;

}

catch (System.ServiceModel.FaultException fault)

{

string errAttributeName;

string errAttribute;

string errOut;

string errMess = "".PadRight(30, '=') + "\r\n"

+ "Error details: " + "\r\n";

PSLibrary.PSClientError error = GetPSClientError(fault, out errOut);

errMess += errOut;

PSLibrary.PSErrorInfo[] errors = error.GetAllErrors();

PSLibrary.PSErrorInfo thisError;

for (int i = 0; i < errors.Length; i++)

{

thisError = errors[i];

errMess += "\r\n".PadRight(30, '=') + "\r\nPSClientError output:\r\n";

errMess += thisError.ErrId.ToString() + "\n";

for (int j = 0; j < thisError.ErrorAttributes.Length; j++)

{

errAttributeName = thisError.ErrorAttributeNames()[j];

errAttribute = thisError.ErrorAttributes[j];

errMess += "\r\n\t" + errAttributeName

+ ": " + errAttribute;

}

}

MessageBox.Show(errMess);

}

}

}

}

}

Read Full Post »


At times we need the ability to hide / show ribbon buttons as per the requirement, now any sharepoint developer can do this very easily, just illustrating this for people who have little development experience but know visual studio a bit 🙂

So as usual brief context on why this was done, we didn’t wanted our users to navigate to project center and use the new button to create project from there since we wanted users to first narrow down on what type of EPT they should choose while creating a new project, as in our environment we have over 30 EPT’s to select from, and as part of the process the select several options and based on that the custom webpart automatically launches an EPT 🙂 and hence we wanted to hide the New project button form Project center

so here goes snapshot followed by short one liner code, obviously embedded in webpart but can be also used in Javascript CEW with bit of research 🙂

Create a visual web part project, of course you can choose other type of projects to proceed with 🙂

reference the required assemblies and use this code in code behind cs file of user control

<div>using System;</div>
<div>using System.Web.UI;</div>
<div>using System.Web.UI.WebControls;</div>
<div>using System.Web.UI.WebControls.WebParts;</div>
<div>using Microsoft.SharePoint.WebControls;</div>
<div>using Microsoft.Web.CommandUI;</div>
<div></div>
<div>namespace CustomRibbonElements.HideNewBtn</div>
<div>{</div>
<div>    public partial class HideNewBtnUserControl : UserControl</div>
<div>    {</div>
<div>        protected void Page_Load(object sender, EventArgs e)</div>
<div>        {</div>
<div>            SPRibbon ribbon = SPRibbon.GetCurrent(this.Page);</div>
<div>            ribbon.TrimById("Ribbon.ContextualTabs.ProjectCenter.Home.Editing.NewProject");</div>
<div>        }</div>
<div>    }</div>
<div>}</div>
<div>
=======================Alternative way by using Content editor web part=======================
</div>
<div>

<script language="javascript" type="text/javascript">

function RibbonFunction()
{

// Gets a reference to a CUI.Ribbon object (CUI.js)

var ribbon = SP.Ribbon.PageManager.get_instance().get_ribbon();
'Ribbon.Read' if the Browse tab is selected.

alert(ribbon.get_selectedTabId());

//ribbon.TrimById("ribbon.ContextualTabs.ProjectCenter.Home.Editing.NewProject");
}

SP.SOD.executeOrDelayUntilScriptLoaded(function()
{

var getInst = SP.Ribbon.PageManager.get_instance();
getInst.add_ribbonInited(function() {
RibbonFunction();
});

var ribbon = null;

try

{

ribbon = getInst.get_ribbon();

}

catch (e) { }

if (!ribbon)
{
if (typeof(_ribbonStartInit) == "function")

_ribbonStartInit(_ribbon.initialTabId, false, null);

}

else {
RibbonFunction(); }

},
"sp.ribbon.js");
</script>

Read Full Post »


Have been hearing a lot from users they wanted to have a custom unique ID for project, though project name itself  is unique and the Project GUID, but for most cases Project GUID doesn’t makes sense & name sometimes isn’t that effective, in my case it all goes back to old 2003 project server system where in there were 2 disparate systems being maintained one for creation & selection of project (custom sharepoint based solution) and once the project was selected for execution it was pushed to project server for execution & monitoring, now to have a correlation between both the systems we used an auto generated numeric identifier, which has been base for couple of reports too and these numeric codes were carried over in 2007 system when we migrated, with 2010 system we consolidated the two systems and now everything is being managed into one system but however end users were  reluctant to give away the unique numbering system 🙂 hence we had to devise a way to auto generate these numbers, i know which can be easily done by customizing and having a web part, but our problem was a little more complex, end users wanted this number to be generated on the very first initial (New Project ) page and should be saved and to be made available in project center as soon as a project is saved and created, ok so that was the background now coming to the solution 🙂

Couple of things to note we made a decision to have these unique ID entries in a custom SQL table, reason being we are using it for other purposes, you might want to tweak it as per your requirement

1. Create custom webpart, place it on the new project PDP page, Custom webpart on load searches through the custom table and allocates an unused unique ID to the project which is shown on the PDP page, the webpart itself renders the ID, it also checks if there is already a unique ID assigned, if yes just display and donot set, if not available set the value, this way this web part can be used in multiple places on different pages throughout the workflow

2. Have a custom field created for Unique ID, add it to the PDP page in hidden mode, as soon as the custom webpart is loaded it writes the value in custom field using javascript 🙂

Note:: Always place custom web part below the unique ID custom field web part

Now once the Unique id has been set in the UniqueID custom field, you can insert corresponding Project UID anywhere in the workflow in the custom table against the unique ID generated, to correlate with other LOB system, or you can just leave it if not required, since you already have this value in the project level custom field J

now getting to the actual code, should be simple enough 🙂 first couple of snapshots, before i proceed to the code 🙂

1. Create a table


/****** Object:  Table [dbo].[tbl_ProjectUniqueID]    *****/

SET ANSI_NULLS ON

GO

SET QUOTED_IDENTIFIER ON

GO

CREATE TABLE [dbo].[tbl_ProjectUniqueID](

[UniqueID] [bigint] IDENTITY(2000,1) NOT NULL,

[ProjectUID] [uniqueidentifier] NULL,

CONSTRAINT [PK_tbl_ProjectUniqueID] PRIMARY KEY CLUSTERED

(

[UniqueID] ASC

)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

) ON [PRIMARY]

GO

2. Create Visual web part project in visual studio and use the code 🙂

Java script function to copy unique ID generated in Project Custom field, place this in control.ascx file 🙂


<script type="text/javascript">

function SetUniqueID(ProjectUniqueCFGUID, UniqueID) {

try {

var arr = document.getElementsByTagName('input');

for (var i = 0; i < arr.length; i++) {

if (arr[i].type == 'text' && arr[i].GUID == ProjectUniqueCFGUID) {

arr[i].value = UniqueID;

break;

}

}

WPDPParts[0].IsDirty = true;

}

catch (ex) { }

}

</script>

/////////////////////CODE BEHIND FOR VISUAL WEBPART/////////////////////


using System;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Web.UI.WebControls.WebParts;

using Microsoft.Practices.EnterpriseLibrary.Data.Sql;

using System.Data.Common;

using Microsoft.SharePoint;

namespace CreateUniqueID

{

public partial class CreateUniqueIDUserControl : UserControl

{

public CreateUniqueID CreateUniqueIDWebPart { get; set; }

protected override void OnPreRender(EventArgs e)

{

base.OnPreRender(e);

if (this.CreateUniqueIDWebPart != null)

{

if (this.CreateUniqueIDWebPart.isMandatory == true)

this.lblFieldName.Text = this.CreateUniqueIDWebPart.FieldName + "<span class="ms-alerttext">* </span>";

else

this.lblFieldName.Text = this.CreateUniqueIDWebPart.FieldName;

this.lblFieldDesc.Text = this.CreateUniqueIDWebPart.FieldDesc;

}

}

protected void Page_Load(object sender, EventArgs e)

{

if (Request.QueryString["ProjUid"] == null)

{

if (!Page.IsPostBack)

{

int UniqueID = GenerateUniqueID();

if (UniqueID > 0)

{

string UniqueIDGuid = “xxx-xxxxx-xxxxxxx-xxxx”;

lblUniqueID.Text = onboadID.ToString();

ScriptManager.RegisterStartupScript(this, GetType(), "initializeVar", " SetUniqueID('" + UniqueIDGuid + "' , '" + UniqueID.ToString() + "');", true);

}

}

}

}

private int GenerateUniqueID()

{

try

{

String Connect = “Enter connection string here”;

SqlDatabase dbepm = new SqlDatabase(Connect);

DbCommand cmdSubmit;

cmdSubmit = dbepm.GetStoredProcCommand("usp_CreateProjectUniqueId"); // You can use sql command / text here to execute

int Id = Convert.ToInt32(dbepm.ExecuteScalar(cmdSubmit));

return Id;

}

catch (Exception ex)

{

return 0;

}

}

}

}

Read Full Post »

« Newer Posts - Older Posts »