Thursday, November 21, 2013

Open SharePoint 2013 List Items Directly into Edit View

I had written another post on this blog about opening list items directly into Edit View, which was based on SharePoint 2010.  Since then I have received several requests on how to do this in SharePoint 2013.  So here we go.

The original post is here: Open SharePoint 2010 List Items Directly into Edit View

In the original post I had given two options to perform a direct edit on list items:
  • The Brute Force - Using jQuery to modify the HTML on the page
  • Elegant (Custom List View) - This used a custom list view where we edited the link for the item
Now, the issue with SharePoint 2013 and SharePoint Designer 2013 is that the Design View is now gone when working with lists!  So, based on my earlier post, the Elegant method no longer applies.  As you can see in the screen shot below, there is no Design option anymore.


Luckily the first option, Brute Force, will still work!  And even better, there is the new Script Editor Web Part in SharePoint 2013, which makes this a more elegant solution that in SharePoint 2010.

First here is the script:

<script type="text/javascript" src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script type="text/javascript">
$(function() {
  // Change all display form links to edit form links
  $
('.ms-vb a[href*="listform.aspx"]').each(function(){
     
var link = $(this).attr('href');
      link
= link.replace("PageType=4", "PageType=6");
      $
(this).attr('href', link);
  });
});
</script>

This script is pretty straight forward, it looks for all links within a class of .ms-vb that have listform.aspx in their value.  If you inspect the links on your form you'll see they are wrapped in a DIV with this class applied.  After it finds all of the links it pulls the href value out and performs a replace changing the PageType to 6 (which is the type for Edit).  It then sets the link back to this new updated value.

To apply this to your list, simply navigate to your list and add a Script Editor Web Part.  In this example I have a simple list called List 1:

If we inspect the link of the individual list items, we'll see the link goes to .../listform.aspx&PageType=4....


Next, place the page into Edit mode and click the "Add a Web Part" link.  From the menu, choose the "Media and Content" category, and under "Parts" choose "Script Editor":


Click the "Edit Snippet" link on the Script Editor Web Part:



Paste the script from above into the editor and click the "Insert" button.  One thing to note here, if you already have jQuery on your page like in the MasterPage, then don't include the first line.  This simply loads jQuery from a CDN, so if you already have it, you don't need that line.


Once the snippet is inserted, click "Stop Editing" on the page to save the changes.  Now if you inspect the links of the page, you should see that the "PageType" query string parameter has been updated to '6' in the listform.aspx link:


Now clicking the list item will open the form in Edit mode:

If you have your list setup to launch forms in dialogs, it still works:


And there you go!  Again, not as elegant as being able to work directly with the list as you could in SharePoint 2010.  However, having a dedicated web part for scripts makes it an easy solution to implement.

Enjoy!

Tuesday, October 22, 2013

Stop Building Custom Web Parts in SharePoint, Use the XML Viewer Web Part Instead!

With the rise of SharePoint 2013 and Apps for SharePoint, client side object model (CSOM) and JavaScript object model (JSOM) are becoming the norm for development on SharePoint.  In SharePoint 2013 the CSOM and JSOM object models have been greatly expanded and optimized making client side development a joy.  Even in SharePoint 2010, these object models allowed for a lot of development opportunities.  If you needed something more robust, you could very easily deploy a WCF web service and make AJAX calls to the server.

With this thinking in mind, we can stop building custom web parts which are deployed to the server and instead build functionality using JSOM and the out of the box XML View web part.  Let's take a look at how to accomplish this.

The complete code for this post can be downloaded from the TechNet Gallery here.

There are only a few components to this:
  • XML Viewer Web Part - This is the OOTB XML View web part that comes with SharePoint.
  • XML File - This is a standard XML file that contains HTML markup that will be rendered in the XML Viewer web part.
  • JavaScript File - This is a standard JavaScript file with which to make JSOM and/or jQuery calls from.
  • CSS File - A standard CSS file to applying styling to your HTML.
To start off, we'll create a new document library to hold our XML and later CSS and JavaScript files.  For this demo it will be a generic document library called "XmlWebParts".


Inside this library I've created a folder to hold the first demo files.  Inside the folder is an XML file titled "HelloWorld.xml".


This is a simple XML file that only contains HTML markup.  The markup is very simple and only contains two DIVs and a Hello World statement, some formatting, and another line of text.
<div>Hello <strong>World!</strong> </div> <div> This is another DIV. </div>

Now that we have that, we can create a new blank site page, and add an XML Viewer web part to it.  Opening the properties pane of the web part, we only need to set the "XML Link" property to point to our new XML file.


In this example the file lives at "/sites/blogexamples/XmlWebParts/HelloWorld/HelloWorld.xml", so this is the path placed into the "XML Link" property.  Saving this and checking in the page results in the HTML content from the XML file being displayed in the web part.

Now obviously this demo is pretty simple and not "real world" by any means.  So, let's translate this into something real world.  Let's say your manager has asked you to develop a web part (SharePoint 2013) for the SharePoint homepage which has the following requirements:
  • Web part must display information for the current user logged in from their user profile and social feeds.
  • Web part must display a welcome message to the user above their user profile picture.
  • Under the welcome message and profile picture, the web part must display any items the user is following, along with a link to that item for easy access.
We can easily produce this web part using HTML, jQuery, JSOM, and an XML Viewer web part.  First let's start with building out the files needed for this web part.  We'll create another folder in our document library called "UserWelcome".

Inside that folder we'll place an XML file and a JavaScript file.


Now we have two files, an XML file for our HTML markup and a JavaScript file.  Let's take a look at the HTML markup in the XML file.

Image below to account for horrible formatting of code!
<script type="text/javascript" src="http://code.jquery.com/jquery-1.10.1.min.js"></script> <script type="text/javascript" src="/_layouts/15/sp.runtime.js"></script> <script type="text/javascript" src="/_layouts/15/sp.js"></script> <script type="text/javascript" src="/_layouts/15/sp.userprofiles.js"></script> <script type="text/javascript" src="/sites/blogexamples/XmlWebParts/UserWelcome/UserWelcome.js"></script> <div id="WelcomeMessage"> Welcome back <span id="WelcomeMessageUserName"></span> </div> <div id="UserProfilePicture"> <img id="UserProfileImage" /> </div> <div id="UserFollows"> </div>


In this markup, the first thing we do is reference jQuery from a CDN, then we load in some specific SharePoint JavaScript files needed to pull user profile data.  Finally we reference the UserWelcome.js file we uploaded.  As you can see, the HTML is very minimal and only includes a few DIVs and an image placeholder.  All the content will be loaded from the JavaScript file.  Let's take a look at that.

Image below to account for horrible formatting of code!
//<![CDATA[ $(function () { // Don't fire function until sp.userprofiles.js is loaded ExecuteOrDelayUntilScriptLoaded(getUserProfileProperties, "sp.userprofiles.js"); }); // Get the user properties for the current user function getUserProfileProperties() { // Create a client context var clientContext = new SP.ClientContext.get_current(); // Get the People Manager and the current user properties var peopleManager = new SP.UserProfiles.PeopleManager(clientContext); userProperties = peopleManager.getMyProperties(); clientContext.load(userProperties); // Get the following Manager and the items the user is following var followingManager = new SP.Social.SocialFollowingManager(clientContext); following = followingManager.getFollowed(15); // Execute the query clientContext.executeQueryAsync(onSuccess, onFailure); } // Process the results and populate a DIV function onSuccess() { $("#WelcomeMessageUserName").text(userProperties.get_displayName()); $("#UserProfileImage").attr("src",userProperties.get_pictureUrl()); var followedItems = "Items you are following:<br />"; $.each(following, function( index, value ) { followedItems += "<a href='" + value.get_uri() + "'>" + value.get_name() + "</a><br />"; }); $("#UserFollows").append(followedItems); } function onFailure() { alert("Error occurred retrieving user profile information."); } //]]>


NOTE: The scripts are wrapped in a CDATA container in order to make them render and function properly.  In my experience sometimes you do not need this, but it never hurts to have it there.

This is where all the work takes place.  There is one major function here called "getUserProfileProperties()" which is called using the ExecuteOrDelayUntilScriptLoaded function built into SharePoint.  Basically we will be accessing user profile information, so we need to make sure the sp.userprofiles.js file is loaded first.

This function simply creates a PeopleManager object and calls the getMyProperties() method to retrieve all the user profile properties for the current user.  Then it creates a SocialFollowingManger object and calls the getFollowed() method to pull all the items the user is following.  If you look at the code the method call includes an INT which indicates which type of SocialActors we want to retrieve using the SocialActorTypes enumeration.  For instance, Users, Documents, Sites, etc.  15 indicates we want all SocialActors to be returned.  For more info on the SocialActorTypes click here.

If all goes well the onSuccess() function is fired and the user profile properties and following information will be populated in the DIVs in the HTML, as shown below.


Bam!  Not too bad, but its basically just some data without any formatting.  Let's add some CSS to pretty it up.  We'll add a CSS file to the UserWelcome library and call it UserWelcome.css.


Since we gave all of our HTML elements IDs, targeting them with some CSS is quite easy.  The following code is added to our CSS file.

#WelcomeMessage {
    float: left;
    padding-right: 6px;
}
#WelcomeMessageUserName {
    font-weight: bold;
}
#UserProfilePicture {
}
#UserProfileImage {
    border: solid 1px #C0C0C0;
}
#UserFollows {
    border:dashed 1px #C0C0C0;
    width:240px;
    padding: 6px;
}

Finally, we'll update the UserWelcome.xml file to include a reference to the CSS file with the following line:

<link rel="stylesheet" type="text/css" href="/sites/blogexamples/XmlWebParts/UserWelcome/UserWelcome.css"/>


With the CSS added, our web part has some simple styling in place to make it a little more presentable (and I mean just a little more)!


And there you go!  A simple demonstration using HTML, JavaScript, JSOM, and the XML Viewer web part to create a "custom" web part for your users.

Using this approach has several advantages:
  • It allows for non-C#/Visual Studio development to take place
  • It allows for users with only HTML and JavaScript experience to create rich web parts without the need for server side code
  • It allows for easy maintenance and upgrades since all the code is stored in a standard document library
  • It utilizes JSOM for building web parts, which is a best practice in 2013 (also CSOM)
  • You can utilize versioning in the library to ensure new updates that may break functionality can be easily rolled back
This of course is not a one size fits all solution, as there will certainly be times you need server side code.  However with the expansion of the CSOM/JSOM functionality in SharePoint 2013, many development opportunities can be explored in this fashion.  It also makes a great development model for Office 365 as well.

The complete code for this post can be downloaded from the TechNet Gallery here.

Enjoy!

Tuesday, August 20, 2013

Discontinued features and modified functionality in Microsoft SharePoint 2013

While searching for something completely different I came across this article today on the Office site:

Discontinued features and modified functionality in Microsoft SharePoint 2013


I have seen several posts on the SharePoint forums about the Chart Web Part and "where has it gone".  This article clears that up.

Enjoy!

Friday, August 2, 2013

SharePoint 2013 REST API - Retrieving Subsites and Getting Forbidden Error, Fixed with CSOM

Recently a colleague was working on some AJAX calls using SharePoint 2013.  He needed to show all the sub-sites a user had access to in a site collection.  Up until now he was using the REST API which much success.  Getting the list of sub-sites is quite easy with REST using the following URL:

http://WEBAPPURL/sites/SITENAME/_api/web/webs

In this example I have the following site structure:
  • Root Site - All Users have access
  • Sub Site 1 - Only Administrator has access
  • Sub Site 2 - All Users have access
  • Sub Site 3 - All Users have access
So, hitting the above REST URL with an admin account produces the following:


We get back three (3) sub-sites just as expected.  Now hitting the same URL while logged in as a non-admin we would expect to get back two (2) sub-sites since we only have access to two sites.  However, we get this instead:


Bummer.  We expected that the results would just be security trimmed, but since this user does not have access to all sites, it simply returns an unauthorized message.  This message in fact:

"{"error":{"code":"-2147024891, System.UnauthorizedAccessException","message":{"lang":"en-US","value":"Access denied. You do not have permission to perform this action or access this resource."}}}"

So what to do?  Client Side Object Model to the rescue.  While using the REST API provides a much more succinct way to retrieve data, there are apparently areas where you'll need to use the more robust CSOM.

Now, the first pass at this we tried to simply load all the sub-sites using the SPWeb Webs property.  This ended up giving us the same message!  Luckily the MSDN documentation for the CSOM on the JavaScript side is coming along nicely and we found this page SP.Web.getSubwebsForCurrentUser.


This method provides a way to retrieve the sub-sites for which the user has access to and thus bypassing these errors.  Putting the example code in place we got the following while logged in as an admin:


And this while logged in as a non-admin user:


(Note:  Using IE for the admin user and FireFox for the non-admin user.  Much easier to test different permission levels this way ;) )

As you can see in the alerts, the admin sees all three sites, and the non-admin user only sees two sites.  And of course, no errors!

This method was offered up as a solution to the REST API errors:

http://WEBAPPURL/sites/SITENAME/_api/web/webs/?$select=title&$filter=effectivebasepermissions/high%20gt%200

As you can see, this URL uses the filter EffectiveBasePermissions to limit the query.  After testing in my environment, I could not get this to work.  However, one of my colleagues did have success with this filter.  So, I'm not sure where we stand on that point.  I think the REST API is great, but may not be fully baked just yet.  The good news is, if you find you have troubles with REST, you can always fall back on the CSOM.

Enjoy!

Tuesday, July 23, 2013

Find Lists or Libraries Using a Content Type in SharePoint with PowerShell

The following PowerShell script will loop through all the lists in a given site and look for a particular content type.  This is useful if you ever need to report on how many lists are using a content type, or you're just curious.

You can download the complete script from the TechNet Gallery here: http://gallery.technet.microsoft.com/sharepoint/Find-Lists-or-Libraries-fd247709

For this script I wanted to use arguments when the script is run, these are very simple and include the following:
  • Web URL - This is a fully qualified URL to the site.
  • Content Type Name - This is the content type name.
So using the script would look like the following:
  • Find-Content-Types.ps1 "http://SPURL/SITE" "CONTENT TYPE NAME"

So first, we'll make sure the SharePoint snap-in is loaded:
 
if ((Get-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue) -eq $null) { 
    Add-PSSnapin "Microsoft.SharePoint.PowerShell" 
}

Next, we'll populate some variables from the arguments:
$webUrl = $args[0]
$ctName = $args[1]

# Varibale to hold document count
$count = 0

We also include a "count" variable that will hold a count of objects that have been updated.  Now, in a try/catch block we'll execute the main code.

# Get site instance
$site = get-spsite $webUrl

# Formatting
Write-Host ""
Write-Host "Starting search...."
Write-Host ""

# Loop through each web in site, then each list
foreach ($web in $site.AllWebs)
 {
   foreach ($lst in $web.lists)
   {
     foreach ($ctype in $lst.ContentTypes)
     {
        if ($ctype.Name -eq $ctName)
        {
  Write-Host $lst.DefaultViewUrl -ForegroundColor Yellow
  $count++
 }
     }
   }
   $web.Dispose()
 }

Finally we write out how many lists we found.
Write-Host "Found $count lists using the content type '$ctName'." -ForegroundColor Green

Here is an overview of what's going on:
  • First we object the site object
  • Next, we loop through all the sub-webs via the AllWebs property
  • Inside this loop is another loop that will go through all the lists in the web
  • Once we have the list, we loop through all the content types associated with the list
  • We check to see if the content type name matches what was supplied as an argument
Its a fairly simple script, but can save you a lot of time if you ever need it.

Thanks to Tom Benjamin for showing me the way ;)

You can download the complete script from the TechNet Gallery here: http://gallery.technet.microsoft.com/sharepoint/Find-Lists-or-Libraries-fd247709

Enjoy!

Thursday, July 11, 2013

PowerShell to Reset Unique Permissions (Inherit Permissions) on a List or Library and it's Items in SharePoint

From time to time I will come across a list or library that has had its inheritance broken resulting in unique permissions.  Sometimes this also includes items that have unique permissions.  When the item count gets very large it can be a real pain to reset all the permissions in the list/library from the UI.  Luckily PowerShell can come to the rescue.

Using PowerShell we can grab all the items in a list/library that have unique permissions and reset them.  Let's take a look at the script to accomplish this.

You can download the complete script from the TechNet Gallery here: http://gallery.technet.microsoft.com/sharepoint/PowerShell-to-Reset-Unique-d885a93f

For this script I wanted to use arguments when the script is run, these are very simple and include the following:
  • Web URL - This is a fully qualified URL to the site where the list/library is located.
  • List Name - This is the list/library name.
  • Should List Inherit - This is a Boolean and indicates if the list/library should have its permissions reset as well.
So using the script would look like the following:
  • Restore-Inheritance.ps1 "http://SPURL/SITE" "LIST NAME" true   
So first, we'll make sure the SharePoint snap-in is loaded:
if ((Get-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue) -eq $null) {
    Add-PSSnapin "Microsoft.SharePoint.PowerShell"
}
Next, we'll populate some variables from the arguments:
$webUrl = $args[0]
$listName = $args[1]
$listInherits = $args[2] 
# Varibale to hold document count
$count = 0
We also include a "count" variable that will hold a count of objects that have been updated.  Now, in a try/catch block we'll execute the main code.
try {
 # Open the web and list objects
 $web = Get-SPWeb $webUrl
 $list = $web.Lists[$listName] 
 # If the list should inherit, reset the role inheritance
 if ($listInherits -eq $true) {
  $list.ResetRoleInheritance()
  Write-Host "Updated permissions on list." -foregroundcolor Green
 } 
 # Get all items with unique permissions
 $itemsWithUniquePermissions = $list.GetItemsWithUniquePermissions()
 Write-Host $itemsWithUniquePermissions.Count "number of items with unique permissions found." 
 # Only update items if some exist
 if ($itemsWithUniquePermissions.Count -gt 0) {
  foreach ($itemInfo in $itemsWithUniquePermissions) {
   $item = $list.GetItemById($itemInfo.Id)
       $item.ResetRoleInheritance()
   $count++
  } 
  # Display number of items updated
  Write-Host "Updated permissions on $count items." -foregroundcolor Green
 }
 else {
  Write-Host "No items with unique permissions exist, nothing to update."
 } 
 # Dispose of web object
 $web.Dispose()
}
catch [Exception] {
 Write-Host "Exception encountered.  Please ensure all arguments are valid." -foregroundcolor Red
 Write-Host $_.Exception.Message -foregroundcolor Red
}
Here is an overview of what's going on:
  • First we open the web and list objects.
  • Next, if the user has specified that the list should have its permissions reset, we do that with a ResetRoleInheritance() method call.
  • Next, we get a list of items that have unique permissions using the GetItemsWithUniquePermissions() method call.  This is key since you may have thousands of items, but only a few with unique permissions.  Using this method, we only get back the few and don't have to take actions on the rest.
  • Now we iterate through the items that we found.  The GetItemsWithUniquePermissions() returns a collection of SPListItemInfo objects.  These objects don't hold the list item, but they do have the ID of the items.  We can take the ID and use the GetItemById() method call on the list to get the actual item back.
  • Finally we spit out the results using a Write-Host and dispose of the web object.
Its a pretty simple script, but can save you a lot of time if you need to do this on many items all at once.

You can download the complete script from the TechNet Gallery here: http://gallery.technet.microsoft.com/sharepoint/PowerShell-to-Reset-Unique-d885a93f

Enjoy!

Wednesday, July 10, 2013

Setup Multiple Search Pages & Result Sources (Search Scopes) for a Site Search in SharePoint 2013

When using an Enterprise Search Center in SharePoint you get all the additional search pages (scopes) out of the box, like Everything, People, Conversations, etc.  This is a very impressive search site with a lot of functionality created for you.  However, what if you don't want to use an Enterprise Search Center and all you need is the basic site search?  Luckily you can get the same experience with just a little configuration using search pages and result sources.

First, let's take a look at the site collection I've created for this demo.  Its just a simple team site with a couple of lists and a document library, nothing fancy.  I've added a few list items and a couple of documents so that the search will return some results.


Performing a search on "Item 1" returns 11 results in this case with a mix of list items, documents, and web pages.  Now, let's say we want to include a search page that only returns list items.  We can easily do this using a custom result source and a new search page. 

Let's start by creating the result source.  Navigate to the Site Settings page, and under the "Search" heading click "Result Sources".


Note:
Since we are creating this new result source at the site level, it will only be available for this site.  If we needed something for the entire site collection, you would choose "Search Result Sources" under the "Site Collection Administration" heading.


On the "Manage Result Sources" page you will see all the pre-defined result sources that come out of the box as well as any user defined ones.  Click the "New Result Source" link to create a new one.


On the "Add Result Source" page we'll need to supply some information.  First give it a name and a description, in this case "List Items" and "Only returns list items".  We also need to specify a protocol, in this case "Local SharePoint" as we want to use the SharePoint search results.


Next, we specify the type which is "SharePoint Search Results".  Jumping down to the end we have to specify the "Credentials Information" which in this case will just be "Default Authentication".

In the middle is the "Query Transform", and this is where the magic happens.  When you first come to this page you'll see this textbox only has "{searchTerms}" in it.  This is just a place holder and will be replaced with the actual search terms the user wants to search for.  We'll add two additional pieces of text to get our desired results:
  • contentClass:STS_ListItem - This property restrictor will only return items that are custom list items.  In this example since I have built a couple of custom lists and I only want to return items from these lists.
  • IsDocument:0 - This property restrictor will limit results to only items that are not documents.  If we placed a 1 instead of a 0 we would only get documents.
Combining these two property restrictors will return only custom list items that are not documents.  The final string for the textbox is:
  • {searchTerms} contentClass:STS_ListItem IsDocument:0
You can view more content class restrictors here: http://brandonatkinson.blogspot.com/2013/06/sharepoint-2013-keyword-query-kql.html

Click "Save" and you'll now see the new result source in the previous list.


Now that we have our new custom result source, we need to apply it to a search page.  Looking back at the site search page you'll notice a couple of things.  First, you can not edit the page!

 
Looking at the address bar in the browser, you can see this page is coming from the Layouts folder.


The search page is the "osssearchresults.aspx" page that may look familiar to anyone using SharePoint.  So we need to add some other search pages that we can edit.  Luckily there are some search pages already in the environment we can use.  Navigate to the "Site Settings" page and under the "Web Designer Galleries" heading, click the "Master pages" link.

 
In the "Master Page Gallery" you'll see a lot of pages here and also some folders.  For this example we're looking for the "SearchResults.aspx" page located near the bottom.


This page is a standard search results page that looks and acts just like the "osssearchresults.aspx" page, but with one major difference.  We can edit it!  However, we don't want to edit this particular one.  We'll make a copy of it in SharePoint Designer to use the custom result source with.  Open SharePoint Designer and navigate to the "Master Pages" area in the left hand navigation.


Highlight the page titled "SearchResults.aspx", then from the ribbon choose "Copy".


Once the copy has been created, highlight it and choose "Rename" from the ribbon.  In this example we'll call it "SearchResultsListItems.aspx".  Now we have two search results pages in the Master Page Gallery.


Note:
Its very important to note that this is the only way to copy these files without getting errors.  If you were to download a copy, then upload it you would get errors about code blocks on the page.  Using Designer to copy it ensures you can bypass these errors.

Now that we have our copied search results page, we're ready to add it to our site search and configure it to use the result source.  Navigate to the "Site Settings" page again, and under the "Search" heading click "Search Settings".


On the "Search Settings" page we'll be working in the "Configure Search Navigation" section.  In this area we can add links that will show up under the search box on the search results page.  These links will act just like search scopes did in SharePoint 2010 once we have everything setup.

To start we have to add a link to a default search page.  The first link you add will automatically become the default search result page.  This is important to note, since if you plan on using pages with custom result sources that limit search results, you may not want these to be the default search.


Click the "Add Link" button to add in the first page.  Since we already know we have a default search results page in the master page gallery, we'll use that as the default search page.  The URL format will be "/sites/blogexamples/_catalogs/masterpage/SearchResults.aspx".

Click "OK" to add the link.

 
You can see the link has been added, and you can highlight it to see the details.  Click "Add Link" again, to add the second page.  Use the same URL path, but with the new page name: "/sites/blogexamples/_catalogs/masterpage/SearchResultsListItems.aspx".  Click "OK" to see the second link has been added.


Click "OK" on the search settings page to save the work.  Now go perform a search.

 
You can now see we have two pages available for searching!  Now you're basic site search is starting to act like an Enterprise Search Center with scopes!  Pretty cool!  However, as it stands right now both pages return the same results.  So let's edit the "List Items" page to use the custom result source.

Click the "List Items" link to navigate to that page.  Then from the page menu, you'll see we have an "Edit Page" option.


Click "Edit Page" to place the page into edit mode.  On the "Search Results" web part, choose "Edit Web Part" from the context menu.


From the web part properties window, click the "Change Query" button.


In the "Build Your Query" modal, the first section is called "Select a Query".  There is a drop down list that shows all the result sources you can choose from.


The custom result source we created earlier is listed here, called "List Items".  Select it from the drop down and click "OK" to save the settings.  Click "OK" in the web part properties and choose "Stop Editing" from the ribbon.  The search results web part is now configured to use the custom result source.  Performing another search will show the filtered results on the new page.

In my environment, searching the term "Item 1" on the main search page returns about 11 items:


However, clicking the "List Items" page, which is using the custom result source to limit results, I only get 3 items since the result source only returns list items that are not documents:


And that's it!  Now we have a basic site search that acts more like an Enterprise Search Center using custom search pages, custom result sources (search scopes), and links to each page at the top.

You can easily expand this example with more pages, more custom result sources, and even incorporate some Display Templates and Query Rules to get even more fancy!

Enjoy!