Feeds:
Posts
Comments

With my earlier post users(Thanks to Das) reported issue while parsing a date format that it wouldn’t properly validate / compare the  dates for eg. if you use the script posted in my previous blog, and use Date e.g. start date – 10-Jan-2014 and finish date as 6-Feb-2014, it would alert that finish date should not be less than start date, however on investigating the issue, it seemed to be working fine in my environment, with trial and error trying it out in different environments it was narrowed down to the IE locale issue, in scenario where date format is different eg works where date format is dd/mm/yyyy but fails where date format is mm/dd/yyyy and the reason for it is we are extracting the date in an array and comparing them hence the problem, try swapping the array index for date & month as below and it should work, verify it by adding an alert statement that you are comparing the correct values

var smallDt = smallDateArr[1]; var smallMt = smallDateArr[0]; var smallYr = smallDateArr[2];

alert(“StDt-”+smallDt+” StMt-”+smallMt+” StYr-”+smallYr);

var largeDt = largeDateArr[1];var largeMt = largeDateArr[0]; var largeYr = largeDateArr[2];

alert(“FiDt-”+largeDt+” FiMt-”+largeMt+” FiYr-”+largeYr);


With reference to our earlier post  Date Validation on PDP and similar kind of example in the post secure PDP pages, quite a few people have been trying to use the logic for disabling the controls on PDP pages now, while you can disable all the fields on the PDP, however it doesn’t do it for multi line text fields, the reason for it is while it initially disables it, but being a context sensistive control while you click in the box it loads the rich text ribbon and enables the text box for editing, however this can be overcome by disabling the entire div tag instead of just the text box, however also be aware the OOB description text box behaves slightly different than other textarea controls, to disable the text area control use the procedure as below

var arrtxtArea = document.getElementsByTagName(‘textarea’);
for (var k = 0; k < arrtxtArea.length; k++)
{
if(arrtxtArea[k].title == ‘Description’)
{
arrtxtArea[k].disabled=’disabled’;
arrtxtArea[k].readonly=’readonly’;
}
else
{
var divID=arrtxtArea[k].id+’_div’;
 var txtAreaDiv =document.getElementById(divID);
if(txtAreaDiv!=null)
{
txtAreaDiv.disabled = true;
}
}

As you notice in this case i am disabling the entire div tag for the text area and that works

RichTextBoxDisabled


As we all know project server security is not very granular when it comes to PDP pages or enterprise custom fields, So project server security is more or less at project level for eg. If a user has rights to edit the project, hen can edit anything in the project, but at times we have seen this is not the case wherein multiple departments are working on project and you wouldn’t want a share PDP to be edited by a contractor, wherein he just needs to edit other few pages but not the one containing the secured details, have been thinking over it and finally came up with a solution which restricts PDP page based on project server group, if a person is present in a group then he should be able to edit the page else he shouldn’t be

We have seen a lots of methods wherein adding a javascript to CEW disables but with that we have a lack of control on how dynamically we can use it so

As usual to make this happen i had to perform a bit of customization, which i did through having a custom webpart on the page which loads a javascript on runtime comparing the users presence in the project server group, challenge was not just injecting a javascript on the page rather managing the AJAX postbacks which overrides the script as well ;)

so here it goes, have a look at the snapshots before we proceed towards the code
You would notice that even though i have edit rights, project is checked out to me but all the fields on the specific PDP pages are disabled, also notice the save button is no more available on the ribbon(i will explain why we needed this)
SecuredPDPInAction2

SecuredPDPInAction1

Now lets take a look at the configuration >> Added the secure custom webpart on the page >> edit webpart properties
note: currently i am in Administrator group, but the security group is set to Executive hence i am not restricted from editing the page

SecuredWebpartConfig

Now lets go over the logic on how i did it, this custom web part gets the current logged in user, grabs the resourceUID property and run it against the security group all PSI calls, if current logged in user is found in the security group it doesn’t injects the javascript, however if not found injects the javascript to disable all fields on PDP and trims the Save button on the ribbon and the code behind looks like this

//find the current resource name
string resourceName = string.Empty;
var currentUserUId = psUtil.resourceClient.GetCurrentUserUid();
SPSecurity.RunWithElevatedPrivileges(delegate
{
ResourceDataSet resDs = psUtil.resourceClient.ReadResource(currentUserUId);

if (resDs != null && resDs.Resources.Count > 0)
{
resourceName = resDs.Resources[0].RES_NAME;
}
});

// Check for User in security Group
if (this.SecurityGroupUid.HasValue) // Security group taken from web part properties
{
ResourceAuthorizationDataSet authDS = null;

SPSecurity.RunWithElevatedPrivileges(delegate
{
authDS = psUtil.resourceClient.ReadResourceAuthorization(currentUserUId);
});

if (authDS != null && authDS.GroupMemberships.Any(p => p.WSEC_GRP_UID == this.SecurityGroupUid.Value))
{
displayinReadonlyMode = false;
}
}

if (displayinReadonlyMode)
{
MakeControlsReadonly(); // this is the actual javascript which is injected to disable the PDP
}
else
{
RegisterCloseOutReadonlyScript();
CloseOutStageReadonly();
}

///

/// Makes all children Webcontrols and HTMLControls readonly
///

///
private void MakeControlsReadonly()
{

//ScriptManager.RegisterStartupScript(this, GetType(), “DisableControl”, “MakeReadOnly();”, true);
SPRibbon ribbon = SPRibbon.GetCurrent(this.Page);
if (ribbon != null)
{
ribbon.TrimById(“Ribbon.Tabs.PDP.Home.Project.Save”);
}
ScriptManager.RegisterStartupScript(this, GetType(), “DisableControl”, “_spBodyOnLoadFunctionNames.push(‘MakeReadOnly()’);”, true);
}

///////////////////////////Script////////////////

/*This script makes all input, button, textarea, and select elements readonly*/
function MakeReadOnly()
{
try {
var arrInput = document.getElementsByTagName(‘input’);
for (var i = 0; i < arrInput.length; i++)
{
__MakeReadOnly(arrInput[i]);
}
var arrbtn = document.getElementsByTagName('button');
for (var j = 0; j < arrbtn.length; j++) {
__MakeReadOnly(arrbtn[j]);
}
var arrtxtArea = document.getElementsByTagName('textarea');
for (var k = 0; k < arrtxtArea.length; k++)
{
if(arrtxtArea[k].title == 'Description')
{
arrtxtArea[k].disabled='disabled';
arrtxtArea[k].readonly='readonly';
}
else
{
var divID=arrtxtArea[k].id+'_div';
var txtAreaDiv =document.getElementById(divID);
if(txtAreaDiv!=null)
{
txtAreaDiv.disabled = true;
}
}
}
var arrFlag = document.getElementsByTagName('select');
for (var l = 0; l > with regular javascript you would notice the description / multiline text boxed wouldn’t get disabled even though you have looped through all of the controls on the page, hence there are specific things added to the script

>> also note you will need to make these fields workflow controlled so that they are not editable in MSP

Now i did it for the entire page to be disabled, however this same logic can be extended and applied in order to secure specific fields for different project server security group on the same page :)

Autogenerate Unique ID


This is with reference to an earlier post wherein we explained on how you can have a custom webpart which would in turn generate unique ID each time a project is created and saved, This should be considered as an enhancement to my previous post wherein i have enabled a few options within the webpart properties to make it easier to configure as a requirement you would need to perform the following steps as outlined below, try this webpart, even though the codebase remains more or less the same, however there are few enhancements, which i will be outlining in my future posts

1. use the script to create a table in database, i have used reporting DB to house my custom table, change DB names as required

USE
GO

/****** Object: Table [dbo].[tbl_ProjUniqueID] Script Date: 01/20/2014 08:49:33 ******/
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N’[dbo].[tbl_ProjUniqueID]‘) AND type in (N’U’))
DROP TABLE [dbo].[tbl_ProjUniqueID]
GO

USE
GO

/****** Object: Table [dbo].[tbl_ProjUniqueID] Script Date: 01/20/2014 08:49:33 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[tbl_ProjUniqueID](
[ProjectID] [bigint] IDENTITY(100000,1) NOT NULL,
[ProjectUID] [uniqueidentifier] NULL,
[ProjectCustomUniqueID] [nvarchar](100) NULL,
CONSTRAINT [PK_tbl_ProjUniqueID] PRIMARY KEY CLUSTERED
(
[ProjectID] 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. Make sure your webapp pool account has read write permissions to the database where you created the table in above steps

3. Deploy the WSP File by using powershell command line or Central Admin
Add-SPSolution -LiteralPath
Install-SPSolution -Identity ProjectUniqueID.wsp -WebApplication http://Servername -GACDeployment -Force

Once installed verify the feature in site collection and activate it

ProjectUniqueIDFeatureActive1

Add the webpart to desired PDP page as shown in the image below, note it will display error message, unless configured
ProjectUniqueIDAddWebPart

Once added configure the webpart as described in the image below
ProjectUniqueIDConfigureWebPart

And finally you would see webpart in action
ProjectUniqueIDWebPartInAction

Finally the link – WSP can be downloaded from here


I have had quite a few requests from users, wherein they wanted to bulk add Risks/Issues onto project site from other systems, while working on it there are several ways to do it, i chose one to use the powershell as it seemed easiest to implement and use, even within powershell you can either use web service to do the same or you can use the SharePoint object model to add/update, i have chosen object model as that suits my need but for reference i am also posting the web service method in case you want to use it

Generally have seen users having problem with updating DateTime type and person type columns, hence emphasizing on that in notes below

As usual before we make it to the actual powershell, few snapshots

Note::

Make Sure your URL is properly formatted i.e. without any special characters

ExcelPowershellUpdateURLFormat

Make sure Date Format is like yyyy-mm-dd hh:mm:ss

I used a text type column and formatted the date time type as above

ExcelPowershellUpdateDateFormat

ExcelPowershellUpdateSharepointUpdate

If your excel is properly formatted, launch SharePoint powershell prompt, or if its native powershell add sharepoint snapin

#Adding list item using Object Model

$xl = New-Object -COM “Excel.Application”
#$xl.Visible = $true
$wb = $xl.Workbooks.Open(“C:\Users\Administrator\Desktop\IssuesandRisks.xls”)
$ws = $wb.Sheets.Item(1)
for ($i = 2; $i -le 5; $i++) #Change row no highlighted here
{
$ProjectHref = $ws.Cells.Item($i, 2).value2 #Adjust Column no here
$SPWeb = Get-SPWeb $ProjectHref
$List = $SPWeb.Lists["Risks"]
$Item = $List.Items.add()
$Item["Title"] = $ws.Cells.Item($i, 4).value2
$Item["Status"] = $ws.Cells.Item($i, 7).value2
$Item["Category"] = $ws.Cells.Item($i, 8).value2
$Item["Description"] = $ws.Cells.Item($i, 10).value2
$Item["Due Date"] = $ws.Cells.Item($i, 9).value2
[Microsoft.SharePoint.SPUser]$spuser = $SPWeb.EnsureUser($ws.Cells.Item($i, 5).value2)
$Item["Assigned To"] =$spuser
[Microsoft.SharePoint.SPUser]$spuser = $SPWeb.EnsureUser($ws.Cells.Item($i, 6).value2)
$Item["Owner"] =$spuser
$item.Update()
Write-Host “Updated row-” $i
$SPWeb.Dispose()
}
$wb.Close()
$xl.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($xl)
Write-Host “Operation Complete”

#Adding list item using Web Service

$xl = New-Object -COM “Excel.Application”
#$xl.Visible = $true
$wb = $xl.Workbooks.Open(“C:\Users\Administrator\Desktop\IssuesandRisks.xls”)
$ws = $wb.Sheets.Item(1)
for ($i = 2; $i -le 5; $i++) #Change row no highlighted here
{
$ProjectHref = $ws.Cells.Item($i, 2).value2 #Adjust Column no here
$uri = “$ProjectHref/_vti_bin/lists.asmx?wsdl”
$listname = “Risks”
$SPService = New-WebServiceProxy -uri $uri -NameSpace SpWs -UseDefaultCredential
$ListInfo = $SPservice.GetListandView($listname,””)
$ListID  = $ListInfo.List.Name
$ViewID  = $ListInfo.View.Name
# build the XML ‘batch’ of entries that make up the Item
$doc = new-object “System.Xml.XmlDocument”
$batch = $doc.CreateElement(“Batch”)
$batch.SetAttribute(“OnError”, “Continue”);
$batch.SetAttribute(“ListVersion”, “1″);
$batch.SetAttribute(“ViewName”, $ViewID);

$batch.InnerXml = “<Method ID=’1′ Cmd=’New’>” +
“”+$ws.Cells.Item($i, 4).value2+”” +
“”+$ws.Cells.Item($i, 7).value2+”” +
“”+$ws.Cells.Item($i, 8).value2+”” +
“”+$ws.Cells.Item($i, 10).value2+”” + “”
$response = $SPservice.UpdateListItems($ListID, $batch)

}
$wb.Close()
$xl.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($xl)


There are instances when we see that even though resources are assigned on the tasks, yet in Resource Usage or Task Usage Views you see 0 hrs. assigned to those resource. Refer to the snapshot below, even though resources are assigned yet in Resource Usage their Work Hours is shown as 0.

Pic1

Workaround : Turn on the Calculation Mode after each edits by -> File -> Options -> Schedule -> Calculate Project after each edit – turn it ON. This will fix the above problem.

Pic2


Well, all products of MS have this wonderful feature of do-undo, yet in 2013 they have added new icons for this, which doesn’t only do-undo but intelligently also displays what all you have added and from list of items which things you specifically want to undo-redo.

I opened a dummy plan and did following :
1) changed duration of a task from 1d to 10d (i.e. I added 10 to duration).
2) Similarly I added 2d and 20d to some other tasks.
3) I added a link to a task and made task id 4 as its predecessor.
5) I added a resource “trainer” to one of the task.
6) Renamed a task to “prod test”

Now when I want to undo something, the feature very intelligently give me option to select which action exactly I want to undo :

Undo

I undid the first item, till now you would see that re-do button just near undo arrow is inactive, but the moment I undo, the action gets stored in re-do list. See below :

redo

I simply loved this feature. I will be posting more on new features…keep watching.

Follow

Get every new post delivered to your Inbox.