Thursday, June 7, 2018

Using CSS to customize the look of a SharePoint Search Box Web Part

I recently received a request from one of our users about dropping a Search Box web part on a page of their site that would provide users with the ability to search for local content on this site.  This typically isn't a problem; however, in this situation, we already had a Search Box web part installed on the site via the master page with a rather heavily customized look.  Unfortunately, that look didn't go well at all with this new web part so, to fix the problem, I dropped an Embed Code/Script Editor web part at the bottom of their web page and added the following CSS that brought the Search Box back to a more "normal", but slightly customized look:

Here's the look they wanted (nothing really fancy, as you can see):


Here's the CSS:

<style>
   #WebPartWPQ2 #SearchBox {
      width: 650px;
   }

   #WebPartWPQ2 .ms-srch-sbLarge > .ms-srch-sb-searchLink {
      background-color: #fff;
      float: right;
      color: #fff;
      text-decoration:none;
      height: 24px;
      width: 33px;
   }


   #WebPartWPQ2 .ms-srch-sbLarge>input {
      border: 1px solid black;
      border-radius: 5px;
      font-size:16px;
      height:30px;
      width: 600px;
      margin: 0px 0px 0px 0px;
      padding: 5px 1%;
   }

   #WebPartWPQ2 .ms-srch-sbLarge-searchImg {
      position: relative;
   }
</style>
NOTE: I'm specifically targeting their web part's ID, WebPartWPQ2; therefore, you'll want to change that to reflect the ID of you web part or remove it if you want all Search Boxes to inherit that particular look.

Friday, June 1, 2018

CSOM CamlQuery Example - Returning list items that fall within a specified date range

Since CamlQuery isn't very intuitive or easy to work with, I thought I'd put together a serious of posts that display some examples that you can use in order to make it work better for you.

In this particular example, I'm connecting to a SharePoint list and performing the following activities:

  1. Filtering the list items that were created within a specified date range while, also, ignoring the time values.  NOTE:  If you need to include the time values, simply change the IncludeTimeValue attribute from FALSE to TRUE.
  2. Ordering the list so that it returns the most recently created items first and older items last
So that you can focus on the actual CamlQuery structure, I've greatly simplified the code as follows:

var selectedMinDate = '2018-1-1';
var selectedMaxDate = '2018-1-31';
var camlQueryText = "<View><Query><Where><And><Geq><FieldRef Name=\'Created\' /><Value Type=\'DateTime\' IncludeTimeValue=\'FALSE\' >"
+ selectedMinDate + "</Value></Geq><Lt><FieldRef Name=\'Created\' /><Value Type=\'DateTime\' IncludeTimeValue=\'FALSE\' >"
+ selectedMaxDate + "</Value></Lt></And></Where><OrderBy><FieldRef Name=\'Created\' Ascending=\'False\' /></OrderBy></Query></View>";
var ctx = new SP.ClientContext.get_current();
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml(camlQueryText);
this.items = ctx.get_web().get_lists().getByTitle('NameOfList').getItems(camlQuery);
ctx.load(items);
ctx.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));

function onQuerySucceeded(sender, args) {
     while (listItemEnumerator.moveNext()) {
          var oListItem = listItemEnumerator.get_current();
          var itemInfo = oListItem.get_item('Title') + " - " + oListItem.get_item('Created');
          alert(itemInfo);
     }
}
function onQueryFailed(sender, args) {
     alert('request failed ' + args.get_message() + '\n'+ args.get_stackTrace());
}

Wednesday, August 9, 2017

Customizing how a ToolTip is displayed using CSS

I was messing around with various ways to customize the look of a tooltip and happened to stumble across an incredibly simple solution utilizing CSS.  In this particular instance, the "tooltip" will appear to the immediate right of the anchor tag's text in a nice, friendly-looking blue box.  Nothing fancy, but something you could easily build on if you like.

Here's the code:


<style>
        .toolTip:hover {
            text-decoration: none;
        }

        .toolTip:hover:after {
            content: attr(data-tooltip);
            text-decoration:none;
            position: absolute;
            background: #40ABF0;
            padding: 3px 7px;
            margin-left: 7px;
            margin-top: -3px;
            color: #FFF;
            border-radius: 3px;
            -moz-border-radius: 3px;
            -webkit-border-radius: 3px;
        }
</style>

...
<a href='urlOfSite' target='_blank' class='toolTip' data-tooltip='Text to show in the custom tooltip!'>

Wednesday, May 3, 2017

Using CSS to cause an image to "expand" when hovered over

I just stumbled across some interesting code written by one of my co-workers that utilizes CSS to cause an image to expand slightly when the user of the web page hovers over it.  As you might guess, this would be perfect for highlighting clickable components such as buttons and the like.  This idea was so clever that I thought I'd share:


<style>
        .imgButton {
          display: inline-block;
          vertical-align: middle;
          -webkit-transform: perspective(1px) translateZ(0);
          transform: perspective(1px) translateZ(0);
          box-shadow: 0 0 1px transparent;
          -webkit-transition-duration: 0.3s;
          transition-duration: 0.3s;
          -webkit-transition-property: transform;
          transition-property: transform;
        }
        .imgButton:hover, .imgButton:active, .imgButton:focus {
          -webkit-transform: scale(1.1);
          transform: scale(1.1);
        }

</style>

...

<img src="pathToImageFile" class="imgButton" />

Tuesday, January 17, 2017

Making sure the Time Component is accounted for in a DateTime CAML Query

The key to making certain that the time component is leveraged when you're performing a datetime comparison in a CAML Query is to include the IncludeTimeValue attribute in your query with a value set to "TRUE".

In the following example, I leveraged a javascript CSOM CAML query to only return items in my image library called "Banners" in which the current datetime falls within the boundaries specified in my custom StartDate and EndDate datetime fields of the image library:

<View>
 <Query>
  <Where>
   <And>
    <Leq>
     <FieldRef Name=\'StartDate\'/><Value IncludeTimeValue=\'TRUE\' Type=\'DateTime\'><Today /></Value>
    </Leq>
    <Geq>
     <FieldRef Name=\'EndDate\'/><Value IncludeTimeValue=\'TRUE\' Type=\'DateTime\'><Today /></Value>
    </Geq>
   </And>
  </Where>
 </Query>
</View>

Here is a more complete version of the code that I used:

function execRequest_Banner() {
     var ctx_Banner = SP.ClientContext.get_current();
     var camlQuery_Banner = new SP.CamlQuery();
     // Filter the image to display based on the following criteria:
     // 1) Only display images in which today's date falls within the Start and End Date range
     // specified

     camlQuery_Banner.set_viewXml('<View><Query><Where><And><Leq><FieldRef Name=\'StartDate\'/>
<Value Type=\'DateTime\'><Today /></Value></Leq><Geq><FieldRef Name=\'EndDate\'/>
<Value IncludeTimeValue=\'TRUE\' Type=\'DateTime\'><Today /></Value></Geq></And>
</Where></Query></View>');
     this.items_Banner = 
     ctx_Banner.get_web().get_lists().getByTitle('Banners').getItems(camlQuery_Banner);
     ctx_Banner.load(items_Banner);
     ctx_Banner.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceeded_Banner),
     Function.createDelegate(this, this.onQueryFailed_Banner));
}

Friday, December 30, 2016

Hide components located on the SharePoint Online master page, oslo.master

In order to hide various components located on the oslo.master master page of SharePoint Online site, a brutally simple solution is to insert a Script Editor web part on the web page and add one or more of the following CSS elements to it:

<style>

     .ms-srch-sb {display:none;}

     .ms-core-pageTitle {display:none;}

     #pageContentTitle { display:none;}

     #titlerow { display:none;}

</style>

Just a quick warning, placing all of these CSS elements on the web page is going to turn the web page into a blank page (that is, if you don't have any content on it) so feel free to remove any of those CSS elements listed above if you want that component to be displayed.

Here is a key that explain which element removes which control:

.ms-srch-sb {display:none;} - removes just the Search control
.ms-core-pageTitle {display:none;} - removes just the Page Title
#pageContentTitle { display:none;} - removes just the Site Title
#titlerow { display:none;} - removes the Site Logo, Site Title, and Search Control

Friday, November 4, 2016

RequiredExpressionValidator expression to limit the number of characters entered in a textbox

For the ASP.NET project that I'm currently working on, I wanted to limit the number of characters that could be entered into a specific textbox control to a maximum of 25 characters (NOTE: since this was a required field so I also wanted them to at least enter 1 character); however, in this particular instance, I wanted to allow the user to be able to enter any kind of special character.  To provide this capability, I used the following value for my ValidationExpression:

^.{1,25}$

Now, if I did want to limit the type of characters entered to be only alpha-numeric and empty spaces, I used the following:

^[a-zA-Z0-9 ]{1,25}$


Tuesday, June 28, 2016

Obtain a list of SharePoint subsites using Javascript CSOM

Here is some code that you can utilize for obtaining a list of SharePoint subsites that the given user has access to:

<div id="selectsubwebs"></div>

<script type="text/javascript">
   var subsites = null;
   var web = null;
   var context = null;
   var subweb = null;
   var renderedHTML = "";
   var scriptbase = "https://rootSiteUrl/_layouts/15/";

   $.getScript(scriptbase + "SP.Runtime.js", function() {
      $.getScript(scriptbase + "SP.js", function() {
         $.getScript(scriptbase + "SP.RequestExecutor.js", getSubWebs);
      });
   });

   function getSubWebs(){
      context = SP.ClientContext.get_current();
      web = context.get_web();
      context.load(web);
      context.executeQueryAsync(onGetWebSuccess, onGetWebFail);
   }

   function onGetWebSuccess(sender, args) {
      //subsites = web.get_webs();  //Complete list of subsites
      subsites = web.getSubwebsForCurrentUser(null);
      context.load(webCollection);
      context.executeQueryAsync(onGetSubwebsSuccess, onGetSubwebsFail);
   }

   function onGetSubwebsSuccess(sender, args) {
      renderedHTML = renderedHTML + "<table id='table_id' class='display'><thead><tr><th>Team Site Title</th></tr></thead><tbody>";
      var webEnumerator = subsites.getEnumerator();
      while (webEnumerator.moveNext()){
         subweb = webEnumerator.get_current();
         renderedHTML = renderedHTML + "<tr><td><a href='" + subweb.get_url() + "' target='_blank'>" + subweb.get_title() + "</a></td></tr>";
      }
      renderedHTML = renderedHTML + "</tbody></table>";

      document.getElementById("selectsubwebs").innerHTML = renderedHTML;

   }
   function onGetSubwebsFail(sender, args){
      alert("Request to retrieve subwebs failed. Error: " + args.get_message())
   }

   function onGetWebFail(sender, args){
      alert("Request to retrieve subwebs failed. Error: " + args.get_message())
   }
</script>

Friday, August 14, 2015

Remove dashed line that appears around elements of an HTML image map

If you discover that you're being plagued by a dashed line that appears around the <area>  elements of your HTML image map whenever a user clicks on a given element of the image map, here is some jQuery code that you can use to remove those dashed lines...

$(document).ready(function() {

     $("area").focus(function() { $(this).blur(); });

}

To help explain matters, the dashed line is simply an indicator that the user placed the focus on the given element when he/she clicked on it.  The above code works by using the blur method to remove the focus from the selected item after it's been has been clicked.

Monday, June 15, 2015

Reading a value from the Web.config file of an ASP .NET Web Application using Javascript

To read a value from the web.config file of an ASP .NET Web Application using javascript, you can do the following:

  1. In your web.config file, add a new <appSettings> with the key and value that you wish for the javascript code to obtain.  In this example, here is the new <appSettings> node, I'm going to reference:

    <appSettings>
         <add key="Environment" value="TEST" />
    </appSettings>


  2. In the HTML code of my default.aspx page, I'm going to add the following javascript function that's going to reference the new <appSettings> node that I just added:

    function getEnvironment() { 
         var environment = '<%= ConfigurationManager.AppSettings["Environment"].ToString() %>';
         alert(environment);
    }
All you have to do is call this function at an appropriate location in your code and it will obtain the value of the respective <appSettings> node.  Naturally, you'll want to swap out the alert call with some valid code, but the idea of this example is to get you rolling.

Friday, June 12, 2015

Reseeding the Primary Key/ID column of a SQL Table

If you ever need to remove all the records from a SQL database table and then reseed/reset the value of the primary key/ID column, here are the Transact-SQL queries that you can use:

USE [DatabaseName]
TRUNCATE TABLE [TableName]
DBCC CHECKIDENT ("TableName", RESEED, 1);

Friday, May 15, 2015

Debugging a Web Application that was recently converted from Windows to Forms Authentication

If you recently converted a web application you set up to run under Windows Authentication to Forms Authentication (this would have taken place during the setup of your solution), you're likely to encounter an issue in which the debug operation is still running under Windows Authentication despite all of your changes to the web.config file to support Forms Authentication.  Anyway, the reason for this is that there are some setting in your project file that are overriding the web.config settings and they're tied to the IIS Express instance that hosts the site when you place your web application in debug mode.

To fix this problem, you can do the following:

  1. Open your web application's solution in Visual Studio 2013
  2. In Solution Explorer, click on your web application's project so that it's highlighted
  3. Hit the F4 key to open the Project Properties menu
  4. Modify your settings appropriately similar to what you did in IIS Manager when you deployed your web application to your Production environment (i.e. Windows Authentication -> Disabled and, perhaps, Anonymous Authentication -> Enabled)
  5. Save your changes and hit the F5 key to place the application in debug mode again
At this point, running your web application in debug mode will now execute in similar fashion to what you configured on your production web server for your web application.

Tuesday, April 21, 2015

Bootstrap box-sizing: border-box Issue

While applying the Twitter Bootstrap to one of my existing web applications, I ran into a slight problem with a univeral CSS setting being applied by the bootstrap.min.css file that was resulting in a few controls appearing, for lack of a better word, "squashed".

The universal setting in the boostrap.min.css file that was causing the issue is presented as follows:

*, *:before, *:after {
     box-sizing: border box;
}

Two resolve the issue, I simply updated the CSS element associated with the controls that were being "squashed" as follows:

#mcc-search-bar * {
  box-sizing: content-box;
}

Once in place, the controls that were leveraging the mcc-search-bar class now appeared as expected.

Thursday, February 12, 2015

How to obtain a list of documents from a SharePoint library that were created X days ago using CSOM

In case you ever need to generate a list of documents contained in a SharePoint library that were created, say, 30 days ago or earlier, here is one way that you might go about doing that using CSOM:

using (ClientContext context = new ClientContext(@"https://webApplicationName/siteName"))
{
    context.Credentials = System.Net.CredentialCache.DefaultCredentials;
                    
    List documentsLibrary = context.Web.Lists.GetByTitle("Documents");
                    
    CamlQuery query = new CamlQuery();
    query.ViewXml = "<View><Query><Where><Leq><FieldRef Name='Created'/><Value Type='DateTime'><Today OffsetDays='-30' /></Value></Leq></Where></Query></View>";
         
    //CamlQuery query = CamlQuery.CreateAllItemsQuery();
    ListItemCollection expiringDocs = approvedDocumentsLibrary.GetItems(query);
                   
    context.Load(expiringDocs);
    context.ExecuteQuery();

    foreach (ListItem document in expiringDocs)
    {
        Console.WriteLine(string.Format("{0}", document["Title"]));
    }
}

You'll notice that the crux of the filtering operation is dependent on the XML contents contained in my CamlQuery object as follows:

<View><Query><Where><Leq><FieldRef Name='Created'/><Value Type='DateTime'><Today OffsetDays='-30' /></Value></Leq></Where></Query></View>

with the <Today> element being the key to this in order to take the current date and offsetting it by 30 days in the past.

CAML Queries always take a bit of trial and error in order to get them right so I hope this helps save you some time!

Tuesday, February 3, 2015

Possible solution for resolving a 409 Conflict Error when uploading a file to a SharePoint 2013 site using CSOM

I was just attempting to upload a file to one of my SharePoint 2013 sites using CSOM when I ran into a 409 Conflict error.  A simplified version of the code I was using is as follows:

using Microsoft.SharePoint.Client;
using Microsoft.SharePoint.Client.Publishing;
using Microsoft.SharePoint.Client.Publishing.Navigation;
using Microsoft.SharePoint;
using System.IO;
...
using (ClientContext context = new ClientContext(@"https://webapplicationname/sitename"))
{
   context.Credentials = System.Net.CredentialCache.DefaultCredentials;
   using (FileStream fs = new FileStream(@"c:\docs\sample.doc", FileMode.Open))
   {
      Microsoft.SharePoint.Client.File.SaveBinaryDirect(context, @"/Documents/sample.doc", fs, true);
   }
}

One major thing I'd like to highlight in the code above is that I was trying to set the context to reference a subsite that is located under the root site collection.  After a bit of experimentation, I actually stumbled onto the solution which is as follows:

using (ClientContext context = new ClientContext(@"https://webapplicationname"))
{
   context.Credentials = System.Net.CredentialCache.DefaultCredentials;
   using (FileStream fs = new FileStream(@"c:\docs\sample.doc", FileMode.Open))
   {
      Microsoft.SharePoint.Client.File.SaveBinaryDirect(context, @"sitename/Documents/sample.doc", fs, true);
   }
}

In the code that worked, notice that I set the context to reference the root site collection and then used the second parameter in the SaveBinaryDirect method call to specify the relative path to the document library on the site in question.  At that point, the code worked perfectly and I was able to upload the document to the site's document library without any issues.

Hopefully, this may help solve your problem as well.

Thursday, January 15, 2015

Using jQuery Datatables on an ASP .NET GridView control

Lately, I've been using jQuery DataTables within several custom SharePoint apps that I've created; however, I've recently resurrected an ASP .NET Web Forms application that I was working on many moons ago and wanted to leverage jQuery DataTables on a GridView control that I'm using in this application.

What I discovered is that the crux of the matter when attempting to layer jQuery DataTables over a GridView control is that you need to format the HTML of the GridView so that it's recognizable by the DataTables code.  Essentially, your GridView needs to be formatted like so in order for it to be recognized:

<table>      <thead>           <tr>                <th></th>           </tr>      </thead>      <tbody>           <tr>                <td></td>           </tr>      </tbody> </table>

Here is what you can do at the very minimum to make this happen:

ASPX Page:

<script  type="text/javascript" language="javascript" src="Scripts/jquery-1.10.2.js"></script> <script type="text/javascript" language="javascript" src="Scripts/jquery.datatables.min.js"></script> <link type="text/css" href="Content/jquery.dataTables.min.css" rel="stylesheet" /> <div class="row">      <div class="col-md-4">               <asp:GridView ID="GridView1" runat="server" CssClass="gvdatatable" AutoGenerateColumns="true" OnPreRender="GridView1_PreRender">           </asp:GridView>      </div> </div> <script type="text/javascript">      $(document).ready(function () {            $('.gvdatatable').dataTable({});      }); </script>


ASPX Page Code-Behind:

protected void Page_Load(object sender, EventArgs e)
{
}

protected void GridView1_PreRender(object sender, EventArgs e)
{
     GridView1.DataSource = GetData();  //GetData is your method that you will use to obtain the data you're populating the GridView with

     GridView1.DataBind();

     if (GridView1.Rows.Count > 0)
     {
           //Replace the <td> with <th> and adds the scope attribute
           GridView1.UseAccessibleHeader = true;

          //Adds the <thead> and <tbody> elements required for DataTables to work
          GridView1.HeaderRow.TableSection = TableRowSection.TableHeader;

           //Adds the <tfoot> element required for DataTables to work
           GridView1.FooterRow.TableSection = TableRowSection.TableFooter;
      }
}

This code has been simplified from my original work, but I wanted to provide you with the bare bones minimum code that's needed so that you can get an idea of how to bolt this onto your existing project.  Hope this helps!

Tuesday, January 13, 2015

How to filter multiple metadata fields when using the SharePoint REST API

To place a filter on multiple metadata fields when you're leveraging the SharePoint REST API to extract information from a SharePoint list or document library, you can utilize the $filter operator; however, you'll need to be careful with how you structure the query or you'll get an error when submitting the request.

To combine these filters, you will need to utilize parentheses to seperate each unique filter operator.  To demonstrate this technique, I'm going to place two filters in my query that will be used to filter on two different metadata fields:

http://SharePointWebApp/SharePointSite/_api/web/lists/getbytitle('Documents')/items?$select=Name,LinkFilename&$filter=((Field1%20eq%20'FilterValueOnField1')%20and%20(Field2%20eq%20'FilterValueOnField2'))&$orderby=Name%20asc

Essentially, the basics of the REST call is that it will obtain the the Name and LinkFilename fields from documents contained in the default Documents library and order the results alphabetically. These results will be filtered so that only documents that have a value in Field1 equal to the specified value of FilterValueOnField1 and a value in Field2 equal to the specified value of FilterValueOnField2.

Thursday, January 8, 2015

Use jQuery to programmatically parse the text from an HTML element

If you ever run into a situation in which you need to programmatically remove the HTML tags from an HTML element in order to get the text contained within it, jQuery has a very nice option for this using the jQuery.text() method.  Here is a brutally simple example of how it works:

var htmlText = "<a href='#' target='_blank'>I need this text</a>";
var textINeed = $(htmlText).text();
alert(textINeed);

Naturally, real-life situations are going to be far more complex than this, but I thought this might get you pointed in the right direction.

Wednesday, August 20, 2014

Parsing a CSV file in C#

Every so often, I seem to run into a situation in which I need to parse a CSV file in order to read data into a system that I'm working with.  Rather than re-invent the wheel by writing my own parser, I tend to leverage the existing Microsoft.VisualBasic.TextFieldParser to do the work (yes, even though I'm using C#).  Here is some sample C# code that demonstrates how this can be used:


using Microsoft.VisualBasic;
using Microsoft.VisualBasic.FileIO;

...

TextFieldParser parser = new TextFieldParser(@"pathToCSVFile");
parser.TextFieldType = Microsoft.VisualBasic.FileIO.FieldType.Delimited;
parser.SetDelimiters(",");
while (!parser.EndOfData)
{
     string[] cols = parser.ReadFields();
     foreach (string col in cols)
     {
          Do something with the info in each field...
     }
}

To make a long story short, this existing Microsoft object will make your life a heck of a lot easier since it tends to handle most scenarios that would require special coding (i.e. properly handling quotes).  

Wednesday, June 11, 2014

How to obtain the PerformancePoint Dashboard Designer for SharePoint 2013

There is a lot of documentation floating around the web concerning the use of SharePoint 2013's PerformancePoint Dashboard Designer, but I had to do quite a lot of searching before I was able to determine exactly how you obtain it in the first place.  Here are the steps:

If you don't have a Business Intelligence Center site collection located on your farm, you will first need to create one via the following steps:
  1. Open Central Administration on your SharePoint 2013 environment
  2. Select Application Management
  3. Under Site Collections, select Create site collections
  4. Choose the appropriate web application where it should be installed
  5. Enter an appropriate Title and URL for your new site collection
  6. For Select experience version, select "2013"
  7. For Select a template, click on the Enterprise tab and select Business Intelligence Center
  8. Select an appropriate Primary and Secondary Site Collection Administrator
  9. Click OK
Once your Business Intelligence Center site collection is in place, use the following steps to obtain the PerformancePoint Dashboard Designer:

  1. Log on to the Business Intelligence Center site collection via your browser
  2. In the left navigation panel, select PerformancePoint Content

  3. Click on the tab that reads "PERFORMANCEPOINT"

  4. Click on the Dashboard Designer icon

  5. Click the Run button when prompted

Once the installation is complete, the Dashboard Designer will open and you can now begin working on all those other tutorials you found.