Friday, 21 December 2012

Workflow email task Link

So you've created a work flow in SharePoint Designer and now you want to let someone know they have a task, but you don't want to just tell them that they have a task and go find it you want to send them a link to the task. Let's face it odds are they're a middle tier manager who's responsibility extends to approving business card request and paid time off; a B.Com degree paying off.

So let's pretend that you have a workflow already set up and all you want to do is create this assignment email: 
In SharePoint Destroyer Designer navigate to your workflows

once you've selected your work flow, edit it.


now that you're ready to edit your work flow, add a step and inside that step create a "start custom task process" action. In the picture below the task is called "Business Card request Task" this is a name I gave it, originally it should start as "Task".


once you've opened the task, under Customization click "Change the behaviour of a single task"


this will load 5 default steps
  1. Before a task is assigned
  2. when a task is pending
  3. when a task expires
  4. when a task is deleted
  5. when a task is completed
You are concerned with the second one "Pending". Add the action email user.


Once you select the user add the appropriate fields


To Create the link address click the link button circled in green above.


Click the fx button to build the URL for your link

Select the current task, and pick the Form_URN, this will give you the task item url

Keep hitting ok followed by a publish and give it a try.

Wednesday, 19 December 2012

Find Text on Publishing Page

Sometimes you need to find a specific text string on your site, maybe it's a common typo, or it could be a name change lets say mr Chooch turned into Dr Chooch and you want to know all the pages that Mr Chooch appears on.


param($web_app_url=$(read-host "Please provide web app url of the list to be delete (EG:'http://SSRAP1'):"))

$TextToFind = $(read-host "Please Provide the text to find")
 
if($ListToDelete -eq $null -or $ListToDelete -eq '')
{
    throw "You must specify a text string to search for"
}

function LoadSharePointPowerShellEnviroment
{
write-host "Setting up Powershell enviroment for Sharepoint" -foregroundcolor Blue
Add-PSSnapin "Microsoft.Sharepoint.PowerShell" -ErrorAction SilentlyContinue
Write-host "Sharepoint PowerShell Snapin loaded." -foregroundcolor Green
}

function ScrubPages($nodeWeb2)
{
    $pubWeb = [Microsoft.SharePoint.Publishing.PublishingWeb]::GetPublishingWeb($nodeWeb2)
   
    try
    {
        $pubPages = $pubWeb.GetPublishingPages($pubWeb)
        foreach($pp in $pubPages)
        {
            $content = $pp.ListItem["Page Content"]
       
            if($content -ne $null)
            {
                if ($content.Contains($TextToFind))
                {
                    $data = @{"url" = $pubWeb.url.ToString() + "/pages/" + $pp.name.ToString() }
                    New-Object PSObject -Property $data         
                }
            }
        }
    }
    catch [Exception]
    {
         #It wasn't a publishing page
    } 

}

function RecurseNav([Microsoft.SharePoint.SPWeb]$nodeWeb)
{
    $subWebs = $nodeWeb.GetSubwebsForCurrentUser()
    ScrubPages($nodeWeb)

    foreach ($nextweb in $subWebs)
    {
        RecurseNav($nextWeb)
    }
}


try
{
    if($web_app_url -eq $null -or $web_app_url -eq '')
    {
        throw "You must specify your server name. Value provided was null/empty."
    }
  
    $web = Get-SPWeb $web_app_url

    RecurseNav($web) | Out-GridView
}
catch [Exception]
{
    Write-Host -ForegroundColor Red "Error: $_"


Saturday, 1 December 2012

Validate & Verify a Date

When I say Verify, I mean make sure that it's in a correct format; this can very easily be accomplished using regex; if you don't know what that is, it's basically an expression to check formatting of a string against. I suggest reading the following site:

Zytrax this is an excellent source for regex knowledge, I use it regularly.

The following JavaScript function takes in a string and checks it against the following pattern ##/##/####. you may notice it doesn't make sure that the month is between 1-12 or the day is less then 31, it just makes sure that you entered 3 numbers and that they're separated by forward-slashes.

function validdateDate(dateString)
{
    var datePattern = /^\d{2}[/]\d{2}[/]\d{4}$/;
    return datePattern.test(dateString);
}

Now Validate, well that's a bit more tricky. First lets talk about what I mean by validate, if you run the date 02/29/2011 against a simple date Regex it will come back as valid, but it's clearly not since that date never occurred.

At first I thought I could just load the individual month day year into a constructor for a JavaScript Date object and just get an invalid date exception, but no such luck, lovely JavaScript  will just bump the date up to 03/01/2011. Good bad it doesn't really matter, what matters is that I still haven't validated my Date. It's high school programming time.

To resolve this issue first we need a function to check if we're dealing with a leap year, simple enough you can grab the algorithm from numerous sites; I got this one from Wikipedia or if your super lazy it's below.

function isLeapYear(year)
{
    if(year%400==0)
        return true;
    else if(year%100==0)
        return false;
    else if(year%4==0)
        return true;
    return false;
}

Now that we can tell if we're dealing with a leap year lets check to make sure that our date is an actual date. that has occurred.

function verifyDate(dateString)
{
    var daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];

    if(dateString.length != 10)
        return false;

    var da = dateString.split("/");
  
    if(da[2] <= new Date().getFullYear())
    {
        if(da[0] > 0 && da[0] < 13)
        {
            if(da[0] == 2 && isLeapYear(da[2]))
                return (da[1] > 0 && da[1] < 30);
            else
                return (da[1] > 0 && da[1] < daysInMonth[da[0]-1]);
        }
    }
    return false;
}

Well there you have it, are there other much more robust ways to check date, sure there are, could I have written this function to take in some sort of pattern to check against rather then just month/day/year, absolutely, but i'm not writing an API, I'm just making a one off function to filter out shitty data before it hits my server.

Monday, 19 November 2012

CSS selector

When it comes to selecting html tags by their ID or Class specifically in SharePoint you have id's that look like this"WebPartctl00_m_g_e7bf4283_529c_413e_884c_10c2adb8690a", now wtf is the jargon after webpart? I don't want to create a css selector that looks like

div#WebPartctl00_m_g_e7bf4283_529c_413e_884c_10c2adb8690a

so instead i'd use an attribute selector

div[id^="WebPart"] {
  font-size:1.5em
}

or i could use

div[id*="Part"] {
  font-size:1.5em
}

or if i hate myself

div[$="ctl00_m_g_e7bf4283_529c_413e_884c_10c2adb8690a"] {
  font-size=1.5em
}

so to sum it up

^= "selects text at the beginning of the attribute"
*= "selects text that's contained in our attribute"
$= "selects text at the end of the attribute"

Friday, 16 November 2012

Simple jQuery & JavaScript Bubble Sort

jQuery is pretty powerful, and you should leverage it as much as possible. Someone might say but what if the client has javascript disabled? My response is simple, they don't even deserve to sort. anyway here's a quick example.

Here's the HTML

<html>
  <head>
    <title>Date Example</title>
    <script src = "jquery-1.6.1.js"></script>
    <script src = "script.js"></script>
  </head>
  <body>
    <input type = "button" value = "sort" onclick="sort();" />
    <div>Nov 12, 2012 1:00 PM EST</div>
    <div>Oct 09, 2012 5:00 PM EDT</div>
    <div>Nov 29, 2012 2:00 PM EST</div>
    <div>Oct 09, 2012 5:00 PM EDT</div>
    <div>Nov 09, 2012 2:00 PM EST</div>
    <div>Nov 19, 2012 2:00 PM EST</div>
    <div>Nov 09, 2012 2:00 PM EST</div>
    <div>Nov 29, 2012 2:00 PM EST</div>
    <div>Nov 05, 2012 12:00 AM EST</div>
    <div>Nov 08, 2012 9:10 AM EST</div>
  </body>
</html>

and followed by the JQuery

function sort()
{
  var swap = false
  var prev = null;
 
  do {
    prev = null;
    swap = false;
    $('div').each(
    function (indexelement)
    {
      if(prev != null && shouldSwap(prev,element))
      {
        $(this).after($(prev));
        swap = true;
      } 
  
      prev = element;
    });
  }while(swap)
}

function shouldSwap(d1d2)
{
  var dateOne = new Date($(d1).text());
  var dateTwo = new Date($(d2).text());
 
  return dateOne > dateTwo;
}

or if you prefer vanilla JavaScript (which I now do, since I haven't written JQuery in years)

function sort() {
  let swap = false
  let prev = null;
  let predicate = (d1d2=> new Date(d1) > new Date(d2);
  
  do { 
    prev = null;
    swap = false;
    const divs = [...document.getElementsByTagName("div")];
    
    divs.forEach((elementindex=> {
        if(prev != null && predicate(prev.innerText, element.innerText)) {
          element.after(prev);
          swap = true;
        } 
        prev = element;
   });
 } while(swap)
}

now you may notice this is not the most efficient bubble sort, it's a simple example to prove a point not efficiently sort 1'000'000 records.

Tuesday, 13 November 2012

Populate List with Attachment

once in a while you'll need to create a list that contains an attachment; why not use a document library or a documents set, well i'll tell you why. When I came up with this i was a nOOb and had no idea what I was doing. So this example is by no means a best practice, but hey maybe one day I'll come across a scenario where it'll come in handy, so hey toss it into the old toolbox for later.



$RootURL = "http://ssrdevinter.corp.windsor/"

function LoadSharePointPowerShellEnviroment
{
write-host "Setting up Powershell enviroment for Sharepoint" -foregroundcolor Blue
Add-PSSnapin "Microsoft.Sharepoint.PowerShell" -ErrorAction SilentlyContinue
Write-host "Sharepoint PowerShell Snapin loaded." -foregroundcolor Green
}

function AddAttachment($item, $filePath)
{
    $bytes = [System.IO.File]::ReadAllBytes($filePath)
    $item.Attachments.Add([System.IO.Path]::GetFileName($filePath), $bytes)
    $item.Update()
}
     
function CreateJobPosting($newJobPost, $Title, $PostingID, $StartDate, $EndTime, $DepartmentIndex, $DescriptionPath)  
{
    $newJobPost["Title"] = $Title
    $newJobPost["PostingID"] = $PostingID
    $newJobPost["StartDate"] =  $StartDate
    $newJobPost["EndDate"] = $EndTime
    $newJobPost["Department"] = 1
    AddAttachment $newJobPost $DescriptionPath
    $newJobPost.update()
   
    Write-Host -ForegroundColor Green Job Posting: $Title Completed
}
 
try
{
    LoadSharePointPowerShellEnviroment
    $RootSite = new-object Microsoft.SharePoint.SPSite($RootURL)
    $jobWeb = $RootSite.rootweb.webs["cityhall"].webs["work-for-windsor"]
    $JobPostingList = $jobWeb.lists["JobPostingsListInstance"]
 
    CreateJobPosting $JobPostingList.items.Add() "It Weather Widget Updater" "IT100" ([DateTime]::Now) ([DateTime]::Now.addDays(15)) 1 "c:\test.txt"
    CreateJobPosting $JobPostingList.items.Add() "Barista" "IT001" ([DateTime]::Now) ([DateTime]::Now.addDays(5)) 1 "c:\test.txt"
    CreateJobPosting $JobPostingList.items.Add() "Bard" "IT002" ([DateTime]::Now) ([DateTime]::Now.addDays(5)) 1 "c:\test.txt"
    CreateJobPosting $JobPostingList.items.Add() "Easter Bunny" "IT003" ([DateTime]::Now) ([DateTime]::Now.addDays(5)) 1 "c:\test.txt"

}
catch [Exception]
{
    Write-Host -ForegroundColor Red "Error in  Create job Postings$_"
}
finally
{
    $jobWeb.Dispose()
}  

Thursday, 8 November 2012

List non published pages

This is a PowerShell snippet that lists all non published pages.


param($web_app_url=$(read-host "Please provide web app url of the list to be delete (EG:'http://SSRAP1'):"))
 
function LoadSharePointPowerShellEnviroment
{
write-host "Setting up Powershell enviroment for Sharepoint" -foregroundcolor Blue
Add-PSSnapin "Microsoft.Sharepoint.PowerShell" -ErrorAction SilentlyContinue
Write-host "Sharepoint PowerShell Snapin loaded." -foregroundcolor Green
}

function NonPublishedPagesReport($nodeWeb2)
{
    $pubWeb = [Microsoft.SharePoint.Publishing.PublishingWeb]::GetPublishingWeb($nodeWeb2)
    $pubPages = $PubWeb.GetPublishingPages()

    foreach($pp in $pubPages)
    {
        if ($pp.ListItem.File.level -ne "Published")
        {
            $data = @{"url" = $pubWeb.url.ToString() + "/pages/"
                      "name" = $pp.name.ToString()
                      "Status" = $pp.ListItem.File.level.ToString()
                      "user" = $pp.ListItem.File.CheckedOutByUser.LoginName
                      }
             New-Object PSObject -Property $data        
        }
    }
}

function RecurseNav([Microsoft.SharePoint.SPWeb]$nodeWeb)
{
    $subWebs = $nodeWeb.GetSubwebsForCurrentUser()
    NonPublishedPagesReport($nodeWeb)

    foreach ($nextweb in $subWebs)
    {
        RecurseNav($nextWeb)
    }
}


try
{
    if($web_app_url -eq $null -or $web_app_url -eq '')
    {
        throw "You must specify your server name. Value provided was null/empty."
    }
 
    $web = Get-SPWeb $web_app_url

    RecurseNav($web) | Out-GridView
}
catch [Exception]
{
    Write-Host -ForegroundColor Red "Error: $_"
}