Friday, November 6, 2015

Callout Extension for SharePoint 2013 Pages

In SharePoint 2013, Microsoft has Introduced a new Framework to Create CallOuts that is somewhat similar to the Dialog Framework Introduced in SharePoint 2010.

The SharePoint callout control provides a flexible way to engage user. When you do searches in a SharePoint 2013 site, you’ll see examples of the callout control in action, as it pops up whenever you hover over a search result.

Please find more details from MSDN

As you know Callouts are displayed OOB for following Lists & Libraries
  • Document Library
  • Assert Library
  • Images Library
  • Pages Library
  • Task Lists
So, I extended this to my custom page with a javascript.

Script:
<script type="text/javascript">
//This functions makes the request to the RESTful ListData service
function getListItems(sende,callback, OrderNumber, Element) {

    var Url = "http://servername/_vti_bin/ListData.svc/ElementsHelp()?$filter=OrderNumber eq "+OrderNumber+" and ElementValue eq '"+Element+"'";

    //Create a WebRequest object
    var request = new Sys.Net.WebRequest();

    //Specify the verb
    request.set_httpVerb("GET");

    //Use the URL we already formulated
    request.set_url(Url);

    //Set the Accept header to ensure we get a JSON response
    request.get_headers()["Accept"] = "application/json";

    //Add a callback function that will execute when the request is completed
    request.add_completed(callback);

    //Run the web requests
    request.invoke();
}

function onCompletedCallback(response, eventArgs) {
    //Parse the JSON reponse into a set of objects by using the JavaScript eval() function
    var shelptext = eval("(" + response.get_responseData() + ")");

    //Display some properties
    _elementhelpTitle = shelptext.d.results[0].Title;
    _elementhelpID = shelptext.d.results[0].OrderNumber;
    _elementhelpBody = shelptext.d.results[0].HelpText;

    var launchPoint = document.getElementById("Element"+_elementhelpID);
    var listCallout = CalloutManager.createNew({
        launchPoint: launchPoint,
        beakOrientation: "topBottom",
        contentWidth: 610,
        ID: _elementhelpID,
        title: _elementhelpTitle,
        content: "<div class=\"ms-soften\" style=\"margin-top:4px;\">" 
                        + "<hr/></div>" 
                        + "<div class=\"callout-section\" style=\"margin-top:10px;\">" + _elementhelpBody + "
",             }); 
 listCallout.open();
}

SP.SOD.executeFunc("callout.js", "Callout", function () {
});
</script>

Elements Help List:

Usage on page:

< a href="Javascript:getListItems(this, onCompletedCallback, '1', 'Element 1');" id="1">Systems are in place for reporting, investigating, analyzing, communicating and documenting all incidents (including significant near-misses). Analysis is conducted using statistically valid methods </a>

End Results:



Monday, December 22, 2014

Automate Deployments with PowerShell

I created and enhanced some PowerShell script code snippets for the purpose of fully automating any given SharePoint .WSP artifact deployment

Usual steps for a clean SharePoint deployment as follows:

DisableSPFeature(Disable-SPFeature cmdlet)

Disables a SharePoint Feature at the given scope. If the scope of the Feature is the farm, the URL is not needed. Otherwise, provide the URL at which this Feature is to be deactivated.

clip_image001

Usage: DisableSPFeature($CRMWebPartsFeatureId, $False)

UninstallSPFeature(Uninstall-SPFeature cmdlet)

Removes the specified feature definition from the collection of feature definitions in the farm.

clip_image002

Usage: UninstallSPFeature($CRMWebPartsFeatureId)

UninstallSPSolution($CRMWebPartsSolutionName, $False)

Retracts a deployed SharePoint solution.

clip_image003

Usage: UninstallSPSolution($CRMWebPartsSolutionName, $False)

RemoveSPSolution(Remove-SPSolution cmdlet)

Removes a SharePoint solution from a farm.

clip_image004

Usage: RemoveSPSolution($CRMWebPartsSolutionName)

AddSPSolution(Add-SPSolution cmdlet)

Adds a SharePoint solution package to the farm.

clip_image005

Usage: AddSPSolution($CRMWebPartsSolutionPath)

InstallSPSolution(Install-SPSolution cmdlet)

Deploys an installed SharePoint solution in the farm.

clip_image006

Usage: InstallSPSolution($CRMWebPartsSolutionName, $False)

EnableSPFeature(Enable-SPFeature cmdlet)

Enables an installed SharePoint Feature at the given scope.

clip_image007

Usage: EnableSPFeature($CRMWebPartsFeatureId, $False)

WaitForJobTimerToFinish (The Get-SPTimerJob & Start-Sleep cmdlet)

Reads a specified timer job, timer jobs of a specified type, or timer jobs defined for a specified scope. If no parameters are specified, this returns all timer job definitions for the farm. We will validate the state of the job in a while loop until null (finished) returned. Otherwise suspends the activity in a script or session for the specified period of time in our case 2 seconds.

clip_image008

Usage: WaitForJobToFinish

Please find the fully deployment script. Modify as need to fit your deployment.

Thursday, February 27, 2014

SharePoint 2013 One More Hidden Gem

If you are working with SharePoint 2013, you might always wonder how nice it would be if you could use

OOB "Get stared with your site" tiles for your web parts, application pages, apps, lists etc. if you are wondering how to do this, you are at right spot.

OOB Get Started With Your Site Tile:clip_image001

Let's get started then

  • Go to Settings >> Site Contents
  • Click on add an app, then search for "Promoted Links" app.
    • Once found click on it to add/create your own promoted links list.
    • Give it a name.
  • Navigate into the list by clicking it (obvious!)
    • You will see "The list is empty. Add tiles from the All Promoted Links view."
    • Click on "All Promoted Links" hyperlink.
    • Start adding your links here.
  • Here is a my sample link

clip_image002

  • Order: The tiles in the sequence you want them displayed.
  • All fields are self-explanatory.
  • Note: adding/editing the list items I found "All Promoted Links" view makes life easy.
  • Once you are done adding the links, go back to the page where you want to display them.
    • Click edit page
    • Navigate to Insert section
    • Click App part/webpart then select your list, save page and browse!

The Final Results:clip_image003

Thursday, January 30, 2014

SharePoint Design Workflow: Issue with workflow content type

While I was working with a client, we stumbled across this issue.
They have this request to add/modify existing SharePoint designer workflow task form with new fields. Simple change!
But came across these 2 issues and how I solved it.

Issue: Field Update
Update the form fields (As required)
 
No reflection in InfoPath form

Even after force updated from CT, still no success.

To Fix Field updates:
Remove/rename/delete away the form from workflow

Re-update the CT from Workflow which will generate the new form & fix the issue.

Issue: CT Unable to show up in the task form
While updating the task form for the user collection activity, you will see blank & CT updated with no custom fields

To Fix CT not showing in the form:
Proceed with adding back fields, (here you need to know previous fields, you can track back from previous task forms)

Delete away the existing InfoPath form for that CT

Publish the workflow. Will fix the CT & form.

Tuesday, September 17, 2013

Navigation Link Modal Popup – Add new item with JavaScript

It's cumbersome for user to add new item in a list, which grown huge, he/she need to scroll all the way to get the "+ Add new item" Option and not only that, the user need to navigate to that specific list in order to perform this task.

And I know you all going to say, hey, how about add that as a navigation link. Well, adding the navigation link just give you a quicker way to launch the list but not the "add new item" option. And as you know adding any JavaScript in navigation will fail with below error!

clip_image001

Fix:

Navigate to the quick launch http://<server/site>/_layouts/quiklnch.aspx

Add new like with web address as

"javascript:OpenPopUpPage('<listname>/NewForm.aspx?RootFolder=&IsDlg=1');"

clip_image002

Thursday, March 7, 2013

Missing "Sign in as Different User" (SP2013)

As we all noticed a key feature(Sign in as Different User) was depreciated from SharePoint 2013.And I guess all the developer love to have this option back!.
And here is how you can get this option back.

Option 1: Update the webcome.ascx page from “C:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\TEMPLATE\CONTROLTEMPLATES” By adding below code in <CustomTemplate> section (watch out for Service Pack updates!, sometimes they over write your changes).
 
<SharePoint:MenuItemTemplate runat="server" ID="ID_LoginAsDifferentUser" Text=" <%$Resources:wss,personalactions_loginasdifferentuser%>" Description=" <%$Resources:wss,personalactions_loginasdifferentuserdescription%>" MenuGroupId="100" Sequence="100" UseShortId="true" /%> 

ð Result

 


Option 2: Bring up login screen (Authentication). I guess, this is much safer option.
http://<Web>or<Site>/_layouts/closeConnection.aspx?loginasanotheruser=true

ð Result
 
     


Thursday, October 11, 2012

2nd Popup Issue In SharePoint 2010 Visual Web Part (With Ajax Update Panel)

I need to popup an alert for the user 2 times based on his/her option picked.
 
Business scenario:
If user choose to deleting a master record, 1st popup and alert the user his/her action.
And if (s)he choose to delete, now I have to find if any child records effecting, if so, not to delete the main record but alert the user again for further action.

This is how I solved it: With the help of JavaScript and Server events.
Added two delete buttons on webpart designer
  1. Main Delete:
                   <asp:Button ID="btnDelete" runat="server" Text="Delete" Width="100px" OnClientClick="if(confirm('Are you sure you want to delete this saved record?')==true)return true;else return false;" CausesValidation="false" onclick="btnDelete_Click" class="ms-ButtonHeightWidth" ChildrenAsTriggers="true"/>            
 2. Hidden Delete: (using style to hide)
                        <asp:Button ID="btnDeleteConfirm" runat="server" Text="DeleteConf" Width="100px" CausesValidation="false" onclick="btnDeleteConfirm_Click" class="ms-ButtonHeightWidth" style="display:none" />
 
OnClientClick event of the Main delete button prompt for 1st initial popup. Once he/she choose to say YES, onclick="btnDelete_Click" will fire as coded in javascript function.
Now in the btnDelete_Click event if I find any child records (I am using entity framwork here) I will register and call ConfirmDelete()

protected void btnDelete_Click(object sender, EventArgs e) {
.
.
if(entityList.Count > 0)
{
   RegisterPopupScript();
ScriptManager.RegisterStartupScript(this.Page, this.Page.GetType(), "PopupDelete", "ConfirmDelete();", true);
}else
{MasterSalesRepService.Delete(Convert.ToInt32(lblSalesRepId.Text));
RedirectPage();
}
}

private void RegisterPopupScript()
        {
            Type type =
this.GetType();
            if (Page.ClientScript.IsStartupScriptRegistered(type, "
PopupScript"))           
               
return;
                       
            System.Text.
StringBuilder script = new System.Text.StringBuilder();
script.Append("<script language=\"javascript\" type=\"text/javascript\">\n");
script.Append("function ConfirmDelete(){if(confirm('Do you want to delete child record(s) as well?')==true) return document.getElementById('" + btnDeleteConfirm.ClientID + "').click(); else  return false; }");
script.Append("
</script >");                      
           
ScriptManager.RegisterStartupScript(this.Page, this.Page.GetType(), "PopupScript", script.ToString(), false);          
        } 

protected void btnDeleteConfirm_Click(object sender, EventArgs e)
        {
            DeleteXrefSalesRep();
            DeleteMasterSalesRep();
            RedirectPage();
        }

In the script I will fire "btnDeleteConfirm_Click" only if user proceed other wise I won't
Note: I won't delete main record as well as child record if user cancels the action as you see from my btnDelete_Click code.

Tuesday, October 9, 2012

Attaching & Debugging to a specific w3wp.exe

Very common thing we developers do or run into is attaching all the w3wp.exe process while debugging.

Simply because we don't know which service a/c or IIS process to be attached while debugging.
Now in my case, as shown below I have to confirm (7 times) attachment of process, right! That's another fold to the debugging process.

 














Save the grief by use this little script to know your exact web application and attach to it irrespective of service A/C.
 
@ECHO OFF
cls
c:\Windows\System32\inetsrv\appcmd list wp

Save the above as wp.bat and store in C:\windows\system32 folder, I am choosing this because the path already added to the Environmental variable's path, which means now I can call this file from any command prompt path/folder location.

I will be invoking the script from H:\> by typing "wp" and enter.
Results shown as below & In my case I will be only attaching to 9044.

Happy debugging!

Tuesday, November 8, 2011

Terminating/Canceling the Workflows

As we know some time it's bit trick to terminate/cancel a workflow.. Which made me create this web part, so that I can delegate my job to the power user so that they can manager it better! And also gives me some extra time to concentrate on my deadlines.

Challenges:
Very first that you need to have proper admin access to see "Terminate this workflow now" link
When you click on the workflow hyper link on the library item 


Here is the web part UI:





Note: ID is the document list item id; you can make more fancy by providing the file name or even loading the active workflow items once selected the document library name

Code for Process Button:
private void CancelWF(string ListName, string ItemId)
{
  SPSecurity.RunWithElevatedPrivileges(delegate()
  {
   
using (SPSite siteColl = new SPSite(SPContext.Current.Site.Url))
    {
     
using (SPWeb site = siteColl.OpenWeb())
      {
      
if (site != null)
       {
       
try
        {
        
SPList workflowTasks = site.Lists["Tasks"];
         SPList doc = site.Lists[ListName];
         SPListItem docItem = doc.GetItemById(Convert.ToInt32(ItemId));

         foreach (SPListItem listItem in docItem.ListItems)
         {
          site.AllowUnsafeUpdates =
true;
          foreach (SPWorkflow itemWorkflow in listItem.Workflows)
          {
          
SPWorkflowManager.CancelWorkflow(itemWorkflow);
           _lblMessage.Text = string.Format(("Administratively Canceled Workflow for the ItemId: {0} / File Name : {1}"), ItemId, listItem.File.Name);
           _lblMessage.ForeColor = Color.Blue;
          }
          site.AllowUnsafeUpdates =
false;
        }
      }
     
catch (Exception ex)
      {
        HandleException(ex);
      }
     }
    }
   }
  });
}


End results:

Thursday, July 28, 2011

Updating Timer Job Schedule

Some time we need to build and deploy a timer job with specific schedule say hourly or daily.
But for testing with the business or on your own , you don't want to wait for a day to see the change

Here is a simple console app code which you can use to change timer schedule for testing for any other reason

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

namespace UpdateTimerJob
{
  class Program
  {       
   
static void Main(string[] args)
    {
      updateTimer();
    }

static void updateTimer()

{
  using (SPSite site = new SPSite("http://sp2010/sites/timer/"))
  {    foreach (SPJobDefinition job in site.WebApplication.JobDefinitions)
    {      if (job.Title == "WorkflowEscalation")
      {
        SPMinuteSchedule schedule =
new SPMinuteSchedule();
        schedule.BeginSecond = 0;
        schedule.EndSecond = 59;
        schedule.Interval = 5;
        job.Schedule = schedule;
        job.Update();


        Console.WriteLine("START UPDATING... ");
       }
     
Console.WriteLine("JOB: " + job.Title);
    }
  }
}
} }


Note: this need to run on the server; you can further modify this app to read site, title & schedule, then update accordingly

Wednesday, July 27, 2011

Timer Job With Trick

Timer Job our good old friend, for further details or quick start take a look at this http://technet.microsoft.com/en-us/library/cc678870(office.12).aspx 

A little know trick I have implement in my timer job so that it install only on Web front end or Single server (ex. DEV box, usually all in one box).

const string JOB_DEFINITION_NAME = "EscalationTimerJob";

public override void FeatureActivated(SPFeatureReceiverProperties properties)
 {
  SPSecurity.RunWithElevatedPrivileges(delegate()
  {                     
   #region ONLY WebFrontEnd/SingleServer
   SPSite site = properties.Feature.Parent as SPSite;
   SPWebApplication webApp = site.WebApplication;

   //make sure job isn't already registered
   if (webApp == null)
   {
    throw new SPException("Error obtaining reference to Web application.");
   }
   foreach (SPJobDefinition job in webApp.JobDefinitions)
   {    if (job.Name == JOB_DEFINITION_NAME)
       job.Delete();
   }
   foreach (SPServer server in SPFarm.Local.Servers)
   {    if (server.Role == SPServerRole.SingleServer || server.Role == SPServerRole.WebFrontEnd)
 {
  //Install the Job                        
  WorkflowEscalation taskJob = new WorkflowEscalation(JOB_DEFINITION_NAME, webApp, server, SPJobLockType.Job);
  SPHourlySchedule schedule = new SPHourlySchedule(); //Every Hour
  schedule.BeginMinute = 0;
  schedule.EndMinute = 59;
  taskJob.Schedule = schedule;
  taskJob.Update();
  }
}
#endregion
});
}


Note:
1. Added RunWithElevatedPrivileges, so that job definition will be create when activated even without full access. Feature activation runs under the application pool account. So it needs to be a farm administrator which will be mitigate by implementing RunWithElevatedPrivileges

2. Also make sure you also implemented below in your main execute() file
public WorkflowEscalation(string jobName, SPWebApplication webApp, SPServer server, SPJobLockType targetType): base(jobName, webApp, server, targetType)
 {
  this.Title = jobName;
 }

Usual code for installing in on all the servers:
const string JOB_DEFINITION_NAME = "EscalationTimerJob";
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
SPSecurity.RunWithElevatedPrivileges(
delegate()
{
  #region For Multiple Servers
  SPSite site = properties.Feature.Parent as SPSite;
  //make sure job isn't already registered
  if (site.WebApplication == null)
  {
   
throw new SPException("Error obtaining reference to Web application.");
  }

  foreach (SPJobDefinition job in site.WebApplication.JobDefinitions)

  {
   
if (job.Name == JOB_DEFINITION_NAME)
      job.Delete();
  }

 
//Install the Job                    
  WorkflowEscalation taskJob = new WorkflowEscalation(JOB_DEFINITION_NAME, site.WebApplication);
  SPHourlySchedule schedule = new SPHourlySchedule(); //Every Hour
  schedule.BeginMinute = 0;
  schedule.EndMinute = 59;
  taskJob.Schedule = schedule;
  taskJob.Update();

  #endregion                                     
  });
}