Tuesday, May 10, 2011

Using Java Scripts in Contend Editor Web Parts

 

Recently I had to face this requirement of hiding the New from Document Library. I generally disapprove changing the basic functionality/behaviour of SharePoint. At times you have to scratch your head while this kind of changes make SharePoint go crazy and starts behaving erratically.

All these said and done.. I had to implement the requirement.. and the document library was only left with upload option.

Sometimes we can hide the New option by restricting the users with only Read/View permission, but that will also take away the Upload option...

Like a good SharePoint-boy I immediately thought my solution will be creating a new feature that uses the tag for hiding different menu items in SharePoint.

To my surprise I learned that the “new” menu item in the list view web part tool bar is not listed as a in any feature, so it cannot be removed as so.

Finding no other choices, I added a Content Editor Web Part in the Document, created this JS code and added in the Source Editor.

First edit the page by clicking Site Action and the Edit Page

clip_image002

Then add a Content Editor Web Part at the same web part zone of the list view.

clip_image004

Now modify the web part and get to the source editor and the following JavaScript code.

<div id="Tt" onload="HideNewMenuItem();">

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

_spBodyOnLoadFunctionNames.push("HideNewMenuItem");

function HideNewMenuItem() {

//alert('test script');

try {

if (ctx) {

if (ctx.listBaseType == 1) {

var tables = document.getElementsByTagName("table");

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

if (tables[i].id.indexOf("NewMenu") > 0) {

var element = tables[i];

element.parentElement.parentElement.style.display = "none";

element.parentElement.parentElement.nextSibling.style.display = "none";

}

}

}

}

}

catch (e) { }

}

</script>

</div>

The _spBodyOnLoadFunctionNames.push("HideNewMenuItem");add this custom JavaScript method the on load function collection. The js will not fire if this is not added.

This checks if the current page has a document library view and if so – locate and hide its “new” menu item…
clip_image006

Save it and see the New Menu is gone... Viola....

clip_image008

If you have a more elegant solution – please post a comment… I’ll be happy to hear.

Share this......

HaPpY CoDiNg... (Aurum)

Adding JavaScript to a Content Editor Web Part to directly go to Add List Item

 

What a tricks to accomplish a custom development of web part or a page with out of the box Content Editor Web Part (CEWP), just needed to use JavaScript in a Content Editor Web Part. I have the code noted below entered in a web part to accomplish the functionality to reach the list item in edit more and add new item in the list directly.

The Content Editor Web Part (CEWP) allows you to add text, html, scripts or styles to a SharePoint page. Its versatility makes it a favourite among developers.

The CEWP has two edit options:
- Rich Text Editor: as it says, allows you to add formatted text.
- Source Editor: this is a more powerful option, allowing you to add html, scripts or styles.

Note that the CEWP is not the only Web Part to allow you to add html to your pages. In some cases you may also consider the Form Web Part or the Page Viewer Web Part.

Having said all these it's time for the actual work.......

First edit the page by clicking Site Action and the Edit Page

clip_image002

Then add a Content Editor Web Part at the same web part zone of the list view.

clip_image004

Now modify the web part and get to the source editor and the following JavaScript code.

 

<span><div><strong><font color="#ff8040" size="5"><font size="4">_______________________________________________________</font></font></strong></div></span><br /><div onkeydown="javascript:if (event.keyCode == 13)GoToPageRelative('/Lists/TestCEWP/NewForm.aspx');"><font color="#ff8040" size="4"><strong>Add&nbsp;New Item&nbsp;</strong></font>&nbsp;&nbsp;<input id="btnAddNew" onclick="javascript:GoToPageRelative('Lists/TestCEWP/NewForm.aspx');"value=" Add New" type="button"></div><span><div><strong><font color="#ff8040" size="5"><font size="4">_______________________________________________________</font></font></strong></div></span>

clip_image006

Save it and see the page...... Viola.... behaves like a custom built web part.

clip_image008

Press the button and it take you directly to the

clip_image010

If you break your Web page…

When playing with the CEWP, you run the risk of adding bad code that will break your page. SharePoint will then throw out an error message, without offering any way to undo your changes.
If this happens to you, here is a useful trick: append the “?contents=1” in query string to your URL. It will give you access to the maintenance page, where you’ll be able to get rid of the faulty Web part.

For example, if you inadvertently break this page:

http://ThisServer.com/sites/ThisSite/ThisLibrary/allitems.aspx

Then Enter

http://ThisServer.com/sites/ThisSite/ThisLibrary/allitems.aspx?contents=1

A trick to edit Web Part pages

On some pages, the edit option is not available or is greyed out. This is for example the case for the edit form of a list. The workaround here is to append the “?ToolPaneView=2” in query string to your URL, which will switch your page to edit mode. Note that it seems to be unsupported by Microsoft, though I haven’t read an official confirmation.

For example, if you want to edit:
http://ThisServer.com/sites/ThisSite/Lists/ThisList/editform.aspx
Enter:
http://ThisServer.com/sites/ThisSite/Lists/ThisList/editform.aspx?ToolPaneView=2

If you have a more elegant solution – please post a comment… I’ll be happy to hear.

Share this............

HaPpY CoDiNg... (Aurum)

Friday, May 6, 2011

Creating Custom SharePoint Timer Job

When I started looking for some materials on SharePoint Timer Job, I was overwhelmed to see the contents available, however I felt to share my part of SharePoint Timer Job experience. This being my first effort to create the timer job, I was bit confused with the windows scheduler. Though, the SharePoint timer job is very simple and straight forward.

Let me give the topic a totality by outlining the points I would wish to cover

1.    When do we need to create a custom SharePoint timer job?

2.    How to create it?

3.    How to deploy it in Development Environment?

4.    How to debug it?

5.    How to deploy it in Test/Production Environment?

6.    Errors you might face

 

1.    When do we need to create a custom SharePoint timer job?

When we want SharePoint to act on a scheduled time without any user intervention, such as sending mails, fetch external data, synchronizing lists etc. on a periodical basis.

2.    How to create it?

The timer job would need only two classes (i) The timer job and (ii) Feature Receiver class

a)    Need to create a class that will inherit from the SPJobdefintion class, i.e. from "Microsoft.SharePoint.Administration.SPJobdefinition".  Add the reference of the namespace using Microsoft.SharePoint.Administration; the class would look like public class MySPTimerJobActivation : SPJobDefinition

b)    Constructor is very important in SP Timer Jobs. In this code the constructor would be called from the feature receiver class while instantiating the MySPTimerJobActivation. There are two overloaded constructors apart from the default contractor available, in my case the one I have used is sufficient.

#region Overloaded Contructors

public MySPTimerJobActivation(): base()

{

}

public MySPTimerJobActivation(string jobName, SPWebApplication webApp, SPServer server, SPJobLockType joblock)

            : base(jobName, webApp, server, joblock)

{

   this.Title = jobName;

}

#endregion

 

c)     Implement the desired code in Execute() method. Whenever the timer job would be activated, this Execute() method would be called. Another important thing worth mentioning is from within the Timer Jobs class we will not be able to get the reference to the site, since the context is different. Thus we need to put the site url in the properties bag of the timer job class while activating the feature in feature receiver class and use it here

public override void Execute(Guid targetInstanceId)

{

string strUrl = this.Properties["SiteURL"].ToString();

SPSite oSiteColl = new SPSite(strUrl)

////Desired Code to execute here

 

}

The Timer Job Class would look something like this.

public class MySPTimerJobActivation : SPJobDefinition

{

  #region Overloaded Contructors

  public MySPTimerJobActivation()

  : base()

  {

  }

 

  public MySPTimerJobActivation(string jobName, SPWebApplication webApp, SPServer server, SPJobLockType joblock)

  : base(jobName, webApp, server, joblock)

  {

    this.Title = jobName;

  }

  #endregion

 

  #region Execute Method

  public override void Execute(Guid targetInstanceId)

  {

    string strUrl = this.Properties["SiteURL"].ToString();

    string strListName = "TimerList";

    try

    {

      using (SPSite oSiteColl = new SPSite(strUrl))

      {

        using (SPWeb oWeb = oSiteColl.OpenWeb())

          {

            ////Desired Code to execute here

            ////Get the list item

       SPListItemCollection oItemColl = oWeb.Lists[strListName].Items;

            ////Add new item

            SPListItem oListItem = oItemColl.Add();

            ////Logging the data

            oListItem["Title"] = "From MySP Timer Job";

            oListItem["DateTimeFired"] = DateTime.Now;

            oListItem.Update();

          }

        }

      }

      catch (Exception ex)

      {

        PortalLog.LogString("Exception occured in Notification Update Module – {0} – {1}", ex.Message, ex.StackTrace);

      }

    }

   #endregion

}

The timer job class is complete now. The feature receiver class would be the next stop to look into

d)    The Feature Receiver Class would be similar to any other receiver class inheriting from SPFeatureReceiver. There are few very important and interesting things to remember at this point.

i)              Pass the parameter in Constructor – “SPServer” as null (the constructor would expect this parameter)

ii)             SPJobLockType.Job – Options are ContentDatabase,Job,None

Consider the different available Job Locks:

·         SPJobLockType.ContentDatabase - Locks the content database. A timer job runs one time for each content database that is associated with the Web Application; therefore your job will run as many times for each content database associated with the Web Application that exists

·         SPJobLockType.Job - Locks the timer job. This prevents multiple instances of the job from running on a single server (Recommended).

·         SPJobLockType.None - No locks

 

iii)            oTimerJob.Properties["SiteURL"]=SiteCol.Url; as mentioned earlier we would need this site url in the Execute Method of Timer Job class

iv)            Good practice to delete any previous instance of the job

////Deleting the Job on deactivation of the feature

    foreach (SPJobDefinition objMyJob in jobCol)

    {

      if (objMyJob.Name == My_Timer_Job)

      jobToDelete = objMyJob;

    }

    if (jobToDelete != null)

    jobToDelete.Delete();

 

Do not delete the job within the enumeration rather get the job and delete it outside the enumeration.

v)             Timer Job Running Intervals



If you want to run it at 11:05 PM every day, use SPDailySchedule class to configure:

SPDailySchedule tempSchedule = new SPDailySchedule();

tempSchedule.BeginHour = 23;

tempSchedule.BeginMinute=5;

tempSchedule.BeginSecond = 0;

tempSchedule.EndSecond = 15;

tempSchedule.EndMinute = 5;

tempSchedule.EndHour = 23;

oTimerJob.Schedule = tempSchedule;                oTimerJob.Update();

If you want to run it on the 1st of every month between 1:15am to 1:30am, use SPMonthlySchedule

SPMonthlySchedule schedule = new SPMonthlySchedule();  


schedule.BeginDay = 1;  


schedule.EndDay = 1;  


schedule.BeginHour = 1;  


schedule.EndHour = 1;  


schedule.BeginMinute = 15;  


schedule.EndMinute = 30; 


tempJob.Schedule = schedule;


tempJob.Update();


 


If you want to run on Monday every week between 2:01:00 am to 2:01:05 am, use SPWeeklySchedule 


 


SPWeeklySchedule schedule = new SPWeeklySchedule();


schedule.BeginDayOfWeek = DayOfWeek.Monday;


schedule.BeginHour = 2;


schedule.BeginMinute = 1;


schedule.BeginSecond = 0;


schedule.EndSecond = 5;


schedule.EndMinute = 1;


schedule.EndHour = 2;


schedule.EndDayOfWeek = DayOfWeek.Monday;


tempJob.Schedule = schedule;


tempJob.Update();


If every year on Jan 23 at 9:05AM, use SPYearlySchedule



SPYearlySchedule JobSchedule = new SPYearlySchedule();


JobSchedule.BeginMonth = 1;


JobSchedule.EndMonth = 1;


JobSchedule.BeginDay = 23;


JobSchedule.EndDay = 23;


JobSchedule.BeginHour = 9;


JobSchedule.EndHour = 9;


JobSchedule.BeginMinute = 5;


JobSchedule.EndMinute = 5;


JobSchedule.BeginSecond = 0;


JobSchedule.EndSecond = 5;


tempJob.Schedule = schedule;


tempJob.Update();



 



At the end the class would look like below. As said earlier this class will inherit from the Microsoft.SharePoint.SPFeatureReceiver class and implement the FeatureActivated & FeatureDeactivated event handlers:








public class MySPTimerJobActivationFeatureReceiver : SPFeatureReceiver



{



public const string My_Timer_Job = "Notification Timer Job";



#region Feature Activated



public override void FeatureActivated(SPFeatureReceiverProperties properties)



{



 try



  {



    ////Get the Site collection



    SPSite SiteCol = (SPSite)properties.Feature.Parent;



    SPWebApplication webApp = SiteCol.WebApplication;



    SPJobDefinitionCollection jobCol = webApp.JobDefinitions;



    ////Deleting older vrsion of Job if exists               



    SPJobDefinition jobToDelete = null;



    ////Deleting the Job on deactivation of the feature



    foreach (SPJobDefinition objMyJob in jobCol)



    {



      if (objMyJob.Name == My_Timer_Job)



      jobToDelete = objMyJob;



    }



    if (jobToDelete != null)



    jobToDelete.Delete();



  ////Install the Job Definition



  MySPTimerJobActivation oTimerJob = new      MySPTimerJobActivation(My_Timer_Job, webApp, null, SPJobLockType..Job);



 ////Set the Site Url.. will be used later from the Timer Job Class



  oTimerJob.Properties["SiteURL"] = SiteCol.Url;



  ////Run the Scheduler every 2 minutes



  SPMinuteSchedule minSchedule = new SPMinuteSchedule();



  minSchedule.BeginSecond = 0;



  minSchedule.EndSecond = 30;



  minSchedule.Interval = 2;



  oTimerJob.Schedule = minSchedule;



  oTimerJob.Update();



  }



  catch (Exception ex)



  {



  Microsoft.Office.Server.Diagnostics.PortalLog.LogString("Exception  occured in fetch query module– {0} – {1}", ex.Message, ex.StackTrace);



  }



}



#endregion



 



#region Feature Deactivating



public override void FeatureDeactivating(SPFeatureReceiverProperties properties)



{



            try



            {



                SPSite SiteCol = (SPSite)properties.Feature.Parent;



                SPWebApplication webApp = SiteCol.WebApplication;



                SPJobDefinitionCollection jobCol = webApp.JobDefinitions;



                ////Deleting older vrsion of Job if exists               



                SPJobDefinition jobToDelete = null;



                ////Deleting the Job on deactivation of the feature



                foreach (SPJobDefinition objMyJob in jobCol)



                {



                    if (objMyJob.Name == My_Timer_Job)



                        jobToDelete = objMyJob;



                }



                if (jobToDelete != null)



                    jobToDelete.Delete();



            }



            catch (Exception ex)



            {



                Microsoft.Office.Server.Diagnostics.PortalLog.LogString("Exception occured in fetch query module– {0} – {1}", ex.Message, ex.StackTrace);



            }



}



#endregion




 



3.    How to deploy in Dev Environment?



Now... to get it working, all you need to do is:



     I.        Deploy the strongly named assembly to the GAC.



    II.        Reset IIS (required for SharePoint to "see" the new timer job in the GAC) or AppPool



   III.        Create a feature specifying the receiver class and assembly that contains the event receivers.



   IV.        Install the feature.



    V.        Activate the feature.



If the WSPBuilder is installed, these all task could be performed from Visual Studio à WSPBuilder deploy option will do everything except the Activate feature.



Now the Timer Job is ready and we have to install it on the farm and deploy to our web application. The "recommended way" for doing this would be to create a Feature Receiver and implement the FeatureActivated event. In this event, can instantiate the job, set the job schedule and update the Job. Below is the code snippet of the Feature.xml



 








<?xml version="1.0" encoding="utf-8"?>



<Feature  Id="E4922BE5-5F1E-448b-9BD9-4CA4B1C652CD"



          Title="MySPTimerJob"



          Description="This feature will create a MySp Timer Job"



          Version="1.0.0.0"



          Hidden="FALSE"



          Scope="Site"



          ImageUrl ="DECISION.GIF"



          DefaultResourceFile="core"



          ReceiverAssembly="MySPTimerJob, Version=1.0.0.0, Culture=neutral, PublicKeyToken=86244771e654f5a1"



         ReceiverClass="MySPTimerJob.MySPTimerJobActivationFeatureReceiver"



          xmlns="http://schemas.microsoft.com/sharepoint/">



</Feature>




 



The Feature Receiver Class has already been discussed earlier with few examples on schedule timer to run hourly, daily, yearly, monthly, weekly or minute basis.



Depending on the requirement, sometimes the feature could be deployed at WebApplication level keeping scope as webapplication i.e, Scope="WebApplication" then we can activate the feature from Central Admin as well.



Activating this feature will deploy this Timer Job on the web application. Once the solution is deployed, can either activate the feature using stsadm -o activatefeature command or go to Central Administration -> Application Management -> Manage Web Application Features -> select web application -> Activate your Feature as shown in the snapshot below







 



Once the feature is activated, it should show up on the Timer Job Definitions page in Central Administration / Operations. It won't appear in the Timer Job Status page until it's executed at least one time.



OK L, I know it is giving a typical WSS_Config database error once trying to activate the feature or it is not showing in the Timer Job Definitions. There is nothing wrong with the code rather it is about the permission. The feature needs firm admin credential to activate it. See below section on Error you might face - for solution or visit by blog on “The EXECUTE permission was denied on the object 'proc_putObject', database 'SharePoint_Config'”. Testing the timer job in dev environment, we can temporarily change the application pool account to the application pool account being used for Central Administration. Once that is done, try activating.



4.    How to debug it?



Debugging a timer job application is not simple as compared to some other custom developed components of SharePoint. The SharePoint Timer jobs runs with the SharePoint Firm Admin credentials since, the information get into the SharePoint Config Database. Thus the application pool will not have the access. I guess, is that the timer service is supposed to run under NT AUTHORITY\NetworkService windows account which has SHAREPOINT\System SharePoint privileges, and thus there's no need to elevate privileges for a timer job.



SharePoint Jobs do not have a current context, they are executed by another windows process called “Windows SharePoint Services Timer”. The executable name of this process is “OWSTIMER.EXE”. This is the process that should be attached to the code within Execute() method to be able to debug the job



 



Set Breakpoints in your code especially the Execute() method. Click on "Attach to Process..." from the menu bar as shown below:





 



Check the box at the bottom "Show Processes from All Users".

Select OWSTIMER.EXE



 





Click the Attach button and a breakpoint should display.



 



 



5.    How to deploy it in Test/Production Environment?



Create the solution package of the timer job and deploy in production environment by automated stsadm.exe scripts.



ScriptDeployTimerJob.bat



@echo Off



echo **************************** Deployment Starts ******************************



@Set STSADM="C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\stsadm.exe"



echo -----------------------------------------------------------------------------



%STSADM% -o addsolution -filename MySPTimerJobr.wsp



echo -----------------------------------------------------------------------------



%STSADM% -o deploysolution -name MySPTimerJobr.wsp -allowgacdeployment -immediate



Rem %STSADM% -o execadmsvcjobs



echo -----------------------------------------------------------------------------



echo **************************** Deployment ends ******************************



 



6.    Errors you might face












1



The EXECUTE permission was denied on the object 'proc_putObject', database 'SharePoint_Config', schema 'dbo'.



The SharePoint Timer jobs runs with the SharePoint Firm Admin credentials since, the information get into the SharePoint Config Database. Thus the application pool will not have the access.



1. Can run the stsadm with firm admin credential and activate the feature.



2. You can create a hidden feature and activate it, making sure you are logged in as a farm administrator




 



Please post a comment if you have other ways of doing this same thing… I’ll be happy to hear.







If this helps...... then please share......



HaPpY CoDiNg... (Aurum)