Monday, August 15, 2016

SharePoint Online Tasks list Custom Action

I was trying to add a custom action to my Custom list using Visual Studio 2013 and was pretty straight forward. But when I tried to do the same with Tasks list my custom ribbon action button never showed up. For custom tasks list it is little different compared to custom list template. Here is how you can add a Custom button for Tasks list:

1. Create Custom Tasks list using Tasks list template in SharePoint online. My list name here is "My Tasks"
2. Open VS 2013
3. Create a SharePoint hosted app and add a custom Ribbon action.
4 Below are the changes made in Elements.xml to make it to work

Replace CommandUIDefinition Location value with "Ribbon.Tasks.New.Controls._children"
and for Button Id value with "Ribbon.Tasks.New.RibbonCustomAction4Button"

In the example below I have custom ribbon action added to my "My Tasks" list.

This is how my Emelemts.xml looks like-

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <CustomAction Id="a3957d8c-dee7-4a6c-9d32-6e1531da84f5.GetReport"
                RegistrationType="List"
                RegistrationId="{$ListId:Lists/My Tasks;}"
                Location="CommandUI.Ribbon"
                Sequence="10001"
                Title="Invoke &apos;GetReport&apos; action" >
    <CommandUIExtension>
      <!--
      Update the UI definitions below with the controls and the command actions
      that you want to enable for the custom action.
      -->
      <CommandUIDefinitions>
        <CommandUIDefinition Location="Ribbon.Tasks.New.Controls._children">
          <Button Id="Ribbon.Tasks.New.RibbonCustomAction4Button"
                  Alt="Request GetReport"
                  Sequence="100"
                  Command="Invoke_GetReportButtonRequest"
                  LabelText="GetReport"
                  TemplateAlias="o1"
                  Image32by32="_layouts/15/images/placeholder32x32.png"
                  Image16by16="_layouts/15/images/placeholder16x16.png" />
        </CommandUIDefinition>
      </CommandUIDefinitions>
      <CommandUIHandlers>
        <CommandUIHandler Command="Invoke_GetReportButtonRequest"
                          CommandAction="~appWebUrl/Pages/Default.aspx?{StandardTokens}&amp;SPListItemId={SelectedItemId}&amp;SPListId={SelectedListId}"/>
      </CommandUIHandlers>
    </CommandUIExtension >
  </CustomAction>
</Elements>

Deploy your app and see if the custom button "GetReport" shows up in the ribbon for "My Tasks" list.

Saturday, August 6, 2016

SharePoint Online Announcements or News App Part

One of my requirements was to display all Announcements from my hosted SharePoint site in a Client Web Part.

To keep it simple for this post I have an Announcements list in my SharePoint Online site. This list contains Title, Body, Expiration Date columns. I have to display all announcements from the list in my Client Web Part. Also. I have a custom property where the list name is configured and use that as my parameter to read all list items.

For now I will go with simple CSS and JS. You can customize the CSS as needed as per your branding requirements.

I am using Visual studio 2013 for my project here. Start from creating a SharePoint Hosted App and type some name like AnnouncementsAppPart. Add a new Client Web Part (Hosted Web) and lets give same name AnnouncementsAppPart.

When we added a client web part it adds AnnoucenmentAppPArt with Elements.xml file and AnnoucenmentAppPArt.aspx  page under Pages.

This is how my project file structure looks like:



Before getting into App Part code or rather I should before I forget, I prefer to set the permissions of my app part. So that I know this app has access to Read SharePoint Announcements list.

If you notice you will have a AppManifest.xml, which is where I will configure permissions of my hosted app. Since I want to Read and display Announcements items from my hosted App, at a minimum I have to give Read access.


This will give my app permissions to read list items from a list.

Now going back to the AnnouncementsAppPart, if you notice this added a Elements.xml file just below the AnnouncementApp Part. This is file where we can configure Announcements Client Web Part custom Web part Properties, Title, Description, default height and Width.

Instead of hard coding Announcements list name in my App, I am adding a custom property to get the list name and display the list items. This is how we can add a custom property-

 <Properties>
           <Property Name="ListName" Type="string" WebBrowsable="true" WebDisplayName="Announcement ListName" WebDescription="Announcement ListName" WebCategory="App Settings" DefaultValue="Announcements" RequiresDesignerPermission="true" />
    </Properties>

If needed I can also add default list name "Announcements", since I already know the list name.

Now the question is how can I get my custom property "ListName" value in my code to read the list items. So here is how we can-


In the Content tag above I give my AnnoucenmentAppPArt  Page name, which was created under Pages folder and if you notice after {StandardTokens} I am passing my custom parameter name like -

&amp;ListName=_ListName_

Basically with this code line above I am trying to pass my ListName as a query string parameter after & in the host web url to grab it in my code later and display the list items.

Now starts the fun part. First I thought I can just create a client context "context " to read SharePoint list and display items. But I was wrong, I was not able to access my Announcements list with this object.

After googling and trying to figure out what was I missing, I came through Corey Roth's article, and that saved my day. After creating a new context from SP.AppContextSite I was able get my list object and then retrieve all the list items.

I created a custom Announcements.js file and here is the code looks like.

'use strict';

var context = SP.ClientContext.get_current();
var parentContext;
var web;
var collListItem;
// This code runs when the DOM is ready and creates a context object which is needed to use the SharePoint object model
$(document).ready(function () {
    getAnnouncements();
});

// This function prepares, loads, and then executes a SharePoint query to get the current users information
function getAnnouncements() {


    //display announcements
    $('#lblResultLists').text("Corporate Announcements:");

  var  hostwebUrl = decodeURIComponent(getQueryStringParameter("SPHostUrl"));
  var  appwebUrl = decodeURIComponent(getQueryStringParameter("SPAppWebUrl"));

    parentContext = new SP.AppContextSite(context, hostwebUrl);

    var listName = decodeURIComponent(getQueryStringParameter("ListName"));
    if (listName == "undefined")
        listName = 'Announcements';

    web = parentContext.get_web();

   // alert(context.get_web().get_url());
    var oList = web.get_lists().getByTitle(listName);

    //get items
    var camlQuery = new SP.CamlQuery();
    camlQuery.set_viewXml('<View><Query><FieldRef Name=\'Title\'/>' +
        '<FieldRef Name=\'Body\'/></Query></View>');
     collListItem = oList.getItems(camlQuery);

    context.load(collListItem);
    context.executeQueryAsync(onGetAnnouncementSuccess, onGetAnnouncementFail);
}

// This function is executed if the above call is successful
// It replaces the contents of the 'message' element with the user name
function onGetAnnouncementSuccess() {

    var listItemInfo = '';

    var listItemEnumerator = collListItem.getEnumerator();

    while (listItemEnumerator.moveNext()) {
        var oListItem = listItemEnumerator.get_current();
        
        listItemInfo += "<table cellpadding='0' cellspacing='0' width='100%'><tr><td class='S2StyleTitle' colspan='2'> <a id='OpenDialog' class='announcementPopUp' href='#' >";
        listItemInfo += oListItem.get_item('Title');
        listItemInfo += "</a></td></tr><tr><td class='announcementdescription' valign='top' >";
        listItemInfo += oListItem.get_item('Body');
        listItemInfo += "</td></tr></table>";
    }

    $("#grid").html(listItemInfo);

}

// This function is executed if the above call fails
function onGetAnnouncementFail(sender, args) {
    alert('Failed to display Announcements. Error:' + args.get_message());
}
function getQueryStringParameter(paramToRetrieve) {
    var params = document.URL.split("?")[1].split("&");
    var strParams = "";
    for (var i = 0; i < params.length; i = i + 1) {
        var singleParam = params[i].split("=");
        if (singleParam[0] == paramToRetrieve)
            return singleParam[1];
    }

}

Now the next step is to point all my custom css and js files form AnnouncementsAppPart.aspx page



Now the last step is to select AnnouncementsAppPart.aspx in AppManifest.xml file and save it.



That's it, hope this helps. Save it and deploy to your developer site. When you are ready with your custom CSS changes add this client web part as App Part to App catalog and you can add this as App Part in any other SharePoint Online sites.

Attached is the source code.
sourcecode


Friday, March 22, 2013

Send embedded images via email to Outlook from SharePoint web part or Workflow

Recently I was working on a custom application and we had a requirement to send an email through SharePoint code with embedded images. Usually it is good practice to use SPUtility.SendEmail(...) for sending emails dynamically through code since we do not have to worry about the extracting SMTP Server information. SPUtility figures it out automatically for that server where code is running and sends these emails. But I did not find a way to embed images while sending email using SPUtility.

After spending some time I thought to use back the old fashion of .Net namespaces for sending these emails with embedded images.

1. How to embed one image in an Email and send an HTML formatted email to Outlook

Code:

 private void SendEmail( string SubjectLine, string fromaddress, string  toaddress)
        {
           
                 string strMailContent = "Send email with an image;
                string strSmtpServer = SPAdministrationWebApplication.Local.OutboundMailServiceInstance.Server.Address;

                string fromAddress = fromaddress;
                string toAddress = toaddress;
           

                string templatePath = SPUtility.GetGenericSetupPath("TEMPLATE");
                string path = templatePath + "\\LAYOUTS\\featurefoldername\\image1.gif";
         

                MailMessage mailMessage = new MailMessage(fromAddress, toAddress);
                mailMessage.Subject = SubjectLine;

                     LinkedResource logo = new LinkedResource(path);
                      logo.ContentId = "logo";
     // done HTML formatting in the next line to display my logo
            AlternateView av1 = AlternateView.CreateAlternateViewFromString("<html><body><img src=cid:logo/><br></body></html>" + strMailContent, null, MediaTypeNames.Text.Html);
            av1.LinkedResources.Add(logo);
           

                mailMessage.AlternateViews.Add(av1);
                mailMessage.IsBodyHtml = true;
                SmtpClient mailSender = new SmtpClient(strSmtpServer);
                mailSender.Send(mailMessage);
            }
        }


2. For embedding multiple images

 private void SendEmail(string SubjectLine, string fromaddress, string  toaddress)
        {
           
                 string strMailContent = "Send email with an image;
                string strSmtpServer = SPAdministrationWebApplication.Local.OutboundMailServiceInstance.Server.Address;

                string fromAddress = fromaddress;
                string toAddress = toaddress;
           

                string templatePath = SPUtility.GetGenericSetupPath("TEMPLATE");
                string logopath1 = templatePath + "\\LAYOUTS\\Featurefoldername\\logo1.gif";
                string logopath2 = templatePath + "\\LAYOUTS\\Featurefoldername\\logo2.gif";
                string logopath3 = templatePath +"\\LAYOUTS\\Featurefoldername\\logo3.gif";

                MailMessage mailMessage = new MailMessage(fromAddress, toAddress);
                mailMessage.Subject = SubjectLine;

                List<LinkedResource> resources = new List<LinkedResource>();
                LinkedResource logo1= new LinkedResource(logopath1);
                logo1.ContentId = "logo1";
                resources.Add(logo1);

                LinkedResource logo2= new LinkedResource(logopath2 );
                logo2.ContentId = "logo2";
                resources.Add(logo2);

                LinkedResource logo3= new LinkedResource(logopath3);
                logo3.ContentId = "logo3";
                resources.Add(logo3);

             

                // done HTML formatting in the next line to display my logo
                AlternateView av1 = AlternateView.CreateAlternateViewFromString("<html><body><img src=cid:logo1/><br><img src=cid:logo2/><br><img src=cid:logo3/><br></body></html>" + strMailContent, null, MediaTypeNames.Text.Html);

           
                resources.ForEach(x => av1.LinkedResources.Add(x));
                mailMessage.AlternateViews.Add(av1);
                mailMessage.IsBodyHtml = true;
                SmtpClient mailSender = new SmtpClient(strSmtpServer);
                mailSender.Send(mailMessage);

            }

        }

Wednesday, February 23, 2011

Points to note while doing Field and List level Validation

Formulas
  1. You can only compare column values to one another in a list level validation.
  2. A validation formula at the column level cannot include any other columns besides itself.  For example, [Column1]>[Column2] is an invalid formula and SharePoint will not allow it to be used at the column level.  In this case, you want to use list-level validation.
  3. There is only one formula available at the list level. 
  4. The formula syntax is similar to that used in Calculated Columns
There are a few dozen functions available - I have not tried every one.  One interesting thing to note is that SharePoint 2010 would not allow me to use the TODAY function, even when validating a Date field.  [DateColumn]>TODAY is not a valid validation formula.

Conflicts
  1. What if you have both column level validation and list level validation?
    1. The column level formulas will be evaluated first, then the list formulas
  1. What if the column and list level validations are in conflict?
    1. Example - at the list level, you require that [Text1] = [Text2], but each column has it's own validation; [Text1]="AAA", and [Text2]="BBB".  In this case, it will be impossible to actually submit a list item.  The column validations are evaluated first, but if the values validate here, they will of course fail the list validation.
  1. What if the list level validation includes columns not included in a particular content type?
    1. If a column used in the list formula isn't available in the current content type, validation will always fail.  This means that if you have multiple content types in your list,  you should not validate at the list level for a column that is not included in all content types on the list.  These columns can only be validated at the column level.

Site Columns
  1. You can set validation on custom columns of the field types listed above
  2. You can override the validation of a site column at the list level

Friday, February 4, 2011

InfoPath forms getting information from Active Directory

I had a request where I had to develop an InfoPath form for a project and user wanted a people picker in that form. In SharePoint lists/libraries we can use a "Person and Group" column which gets info from AD and will display all names but to have the same functionality in InfoPath we have to follow few manual steps BUT the good news is this do not need any programming.

2007 ships with a control called Contact Selector Control that will resolve this issue!

Wednesday, February 2, 2011

Where are all other site templates in SharePoint 2010

I was creating a new publishing site collection SP 2010 and noticed that when I click on "Publishing" tab, there were only two available options:

Custom Site Template with Publishing feature enable in SharePoint 2007

I had a request to create a custom site template for reusability. As we always enable "Publishing Feature" for all team sites, I tried to save an existing site as template. But you know what, site with Publishing feature, does not provide an option to "save site as template".

Then I tried a trick to open save site as template page:

So just to make it clear.  If your subsite is
http://sites/toplevelsite/teamsite/default.aspx
You would go to :
http://sites/toplevelsite/teamsite/default.aspx/_layouts/savetmpl.aspx

Here you should be able to save site as template.BUT the problem was not solved. Doing this, when you try to create a new site from this template, it is not going to work. It will throw an error and your site is not created.

So to explain in one line, you have to save site templates without enabling "Publishing Feature" and then activate it manually after creating a site from this template.

Friday, January 28, 2011

Display list items from one site collection to other site collection

Recently I had a request to display Calendar events from one site collection to other site collection. This way users do not need to readd the same events in both site collections.
I accomplish this I was thinking of web part or a workflow which will solve the process. But there is also another way to share with you.
This can be done by using IFrames, Javascript.Thanks to Christophe I got this code help from
http://blog.pathtosharepoint.com/2009/01/22/a-simple-method-to-display-a-list-in-another-site/

Check this code

Thursday, January 27, 2011

Playing with Notifications in Tasks lists

One of my user had a requirement in one of their sites. The requirement was, they have a Tasks list where tasks are assigned to members of that site.
 
First question was: How can a person get notified when a task is assigned to him/her ?
 
Answer : By default the notification is disabled. To enable it, follow these steps.
Step 1: Open Tasks list in that site.
step 2: Go to Settings (Next to Actions), List Settings.
Step 3: Click on "Advance Settings" link under "General Settings" section.
Step 4: In "E-Mail Notification " section, select "Yes" for "Send e-mail when ownership is assigned?". Leave all other default settings. Click Ok at the bottom of the page.
 
You are done. Whenever a new task is created and assigned to a person, that person will get notified.
 
Second Question: When a assigned person changes Task status from "In Progress" to "Completed", how can my boss gets notified ?
 
Answer:
Step 1: Open Tasks list in that site.
Step 2: Select Actions, Alert me.
Step 3: Type some Title in "Alert Title" section.
Step 4: In "Send Alerts To" section, type your name. To do this, click on small book icon to the right. This will display a dialog box, type your boss name and click OK. Select name and click Add. you will see your name with an underline.
Step 5: Leave default settings in "Change Type" section.
Step 6: In "Send Alerts for These Changes " section select "A task becomes complete".
Step 7: In "When to Send Alerts " section select "Send e-mail immediately" radio button. And Click Ok.
 
That's it. You are done. you will get notified when someone changes task status from "In Progress" to "Completed".
 
You can use the same logic or tweak it a little bit to fit your scenario.
Let me know if you have any questions.

Tuesday, January 18, 2011

Save a custom view and reuse it in another document

Want to reuse existing view from one list/library and copy iy to other list/library.
Follow these steps:
Step 1: Create a new document library called Documents in site 1. Create new view called SourceView with all the columns in it.
Step 2: Create a new document library called Documents in site 2.
Step 3: Open Site 1 in SharePoint designer and navigate to Documents and open Forms folder. Here you will notice SourceView.aspx. Double click and open the page.
Step 4: Open Site 2 in another instance of SharePoint designer and navigate to Documents/Forms folder. Right click and create new aspx page. Name it MyView.aspx.
Step 5: Copy all the content from "SourceView.aspx" opened in step 3 paste it in "MyView.aspx" page.
Step 6: Now replace GUID of source Documents library with destination Documents library GUID.
To get GUID of Site 2 Document library, open Documents library and click Setting, Document library settings.
In the address bar, the URL will have the guid as the last part of the address.
Example:
http://Site2/_layouts/listedit.aspx?List=%7B35A23714%2D25E3%2D42A5%2DB4BA%2D868F31906FA2%7D
Guid would be the last part of the URL:
%7B35A23714%2D25E3%2D42A5%2DB4BA%2D868F31906FA2%7D
Modify the guid to be in the following format:
{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
You are alomost done.

You’ll see a popup informing you that “The URL `Documents/Forms/MyView.aspx’ is invalid.
A workaround for this error is to Save MyView.aspx again with different name in same Forms folder. Say we call it "NewView.aspx".

Step 7: Now open Site2 and navigate to Docuements library and click on views drop down and change the view to "NewView" Now you will see all the columns are automatically created.

I know this is a time consuming but this is one way to do it.

SharePoint LINQ Support

With SharePoint 2007, we had to use CAML queries to write queries against the server, using the SPQuery or SPSiteDataQuery objects. You would write your CAML as a string and pass it to those objects, so there were no strongly typed objects or syntax checking as part of the API.  Instead, you would either have to cross your fingers that you got the query right or use a third-party tool to try to generate your CAML queries.  To make this easier, SharePoint 2010 introduces SharePoint LINQ (SPLINQ).  By having a LINQ Provider, 2010 allows you to use LINQ to write your queries against SharePoint in a  strongly typed way with Intellisense and compile-time checking.   Under the covers, the SharePoint LINQ provider translates your LINQ query into a CAML query and executes it against the server.  As you will, you can retrieve the CAML query that the LINQ provider generated to understand what is being passed back to the server.

Sample code in old  days:

SpSite site = new SpSite(http://samlehome.com/);
SpWeb web = site.openweb();
List<SPList> hiddenLists = new List<SPlist>(); // Do not use an SPListCollection here
foreach (SPList list in web.Lists){
if (list.Hidden)
hiddenLists.Add(list);

}
Notice number of lines of code with LINQ:
SPSite site = new SPSite("http://samlehome.com");SPWeb web = site.AllWebs[0];var hiddenLists = from list in web.Lists where list.Hidden select list;

Friday, December 31, 2010

How to add a WebPart to SharePoint 2010 site web page.

In this post I will walk you through how to add a web part to your SharePoint site.

In my previous post, i explained how you can create and deploy a custom web part using Visual Studio 2010 to SharePoint 2010 site. In this post I will add my custom web part to Sharepoint site.

To add a web part:
Step 1: Open you SharePoint site in a browser.

Step 2: Select Site Actions, Edit page (similar to SharePoint 2007)  or you can directly use a edit button provided in the Ribbon. The page displays in edit mode.

Thursday, December 30, 2010

Create Custom Actions for SharePoint 2010 lists using SharePoint designer 2010

In SharePoint 2010, it is easier to add custom actions that are scoped to a specific list using SharePoint Designer 2010. To start with adding Custom Action to a list, you have to create your own custom list from Custom List Template.

Step 1: Create a custom list called Phone Book.
Step 2: Open the site in SharePoint Designer 2010 by clicking on Edit Site in SharePoint Designer action off of the Site Actions menu.

How to create a Custom Master Page in SharePoint 2010

Creating a Custom Master Page in SharePoint 2010 is same as creating Custom Master page in SharePoint 2007.
Step 1: Open your root level site in SharePoint Designer 2010 by selecting Site Actions, Edit in Sharepoint Designer.

Wednesday, December 29, 2010

How to increase width of SharePoint List Column

One of my user asked me how we can increase width of SharePoint List Column. SharePoint does not provide a direct way to increase the width of list column. Now again this is a customization.
But one way I could solve this problem is by using javascript.
Copy paste this code in between opening and closing script tags (
) a Content Editor web part and then change the name of the columns like Title, Department etc. Then you are done.

Monday, November 29, 2010

Meeting Workspace error: ‘g_InstanceID’ is undefined

After changing the master page on a Meeting Workspace site, the recurring meeting workspace hyperlinks under “Select a date from the list below” do not work and throw a JavaScript error.

If you create a custom master page and use it for your Meeting Workspaces, a JavaScript error is thrown.

Error:
‘g_InstanceID’ is undefined

Cause:
The g_instanceId global JavaScript variable is declared in a script registered runtime by the