Bootstrap Responsive Navigation in SharePoint

Introduction

Many of my most recent branding projects have all been responsive. Clients are building sites that they want to work on desktops, tablets, & phones. If you’ve ever tried to deal with this request and SharePoint is your platform then you know what a handful this can be. For all my responsive branding projects I always go to Bootstrap. If you don’t know what that is then this article is not for you. We will not going into the details of implementing Bootstrap or the issues / fixes to do that.  If you’re looking for cookie cutter bootstrap in SharePoint then I recommend that you check out Responsive SharePoint.

This article we will simply focus on the navigation. We will be replicating the standard out of the box (OOTB) SharePoint navigation and formatting it in a way that works with Bootstrap 3.0. I’ll provide you with the code snippet and an explanation of how to implement, and what is happening. It should leave you with the functionality of the Bootstrap’s collapsible navigation.

Our main problems with the SharePoint navigation are:

    • SharePoint out of the box navigation is NOT responsive
    • SharePoint out of the box navigation doesn’t work well with touch enabled devices
      • It has hover actions on the navigation, and hover doesn’t work on touch devices

b

Prerequisites

The following need to be reference in your site.

  • jQuery
  • Bootstrap 3.0
  • On-Prem SharePoint environment w/ access to the Masterpage
    • You can see the references these in my master page

SORRY SharePoint Onliner’s and O365’s – this won’t work b/c you can’t use code blocks in the MasterPage

ref

The Navigation Code

You can view the html markup that Bootstrap requires in order to work properly – http://getbootstrap.com/components/#navbar . The OOTB SharePoint navigation does its own thing so it would never work with Bootstrap. In this approach we’ll use the SharePoint top navigation provider but we’ll create our own navigation markup using asp repeaters. The following code will only work for 2 levels deep, it could be modified to work on X number of levels. You would just have to keep nesting repeaters. I am using ASP repeaters in the master page with no code behind in this approach. If you wanted to you could write a user control and simplify what goes into the master page but for me no C# code means less things that could break. There is one minor issue with this approach – see the Known Issues section.

Download Snippet Here

1 <nav class="navbar navbar-default s4-notdlg noindex s4-noindex"> 2 <div class="container"> 3 <!-- Brand and toggle get grouped for better mobile display --> 4 <div class="navbar-header"> 5 <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> 6 <span class="sr-only">Toggle navigation</span> 7 <span class="icon-bar"></span> 8 <span class="icon-bar"></span> 9 <span class="icon-bar"></span> 10 </button> 11 <SharePoint:SPLinkButton runat="server" NavigateUrl="~sitecollection/" ID="onetidProjectPropertyTitleGraphic" CssClass="navbar-brand"> 12 <SharePoint:SiteLogoImage name="onetidHeadbnnr0" ID="onetidHeadbnnr2" LogoImageUrl="images/logo.png" runat="server"></SharePoint:SiteLogoImage> 13 </SharePoint:SPLinkButton> 14 </div> 15 16 <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> 17 18 <asp:Repeater runat="server" ID="TopNavMenu" DataSourceID="topSiteMap"> 19 <HeaderTemplate> 20 <ul class="nav navbar-nav navbar-right"> 21 </HeaderTemplate> 22 <ItemTemplate> 23 <li runat="server" class="root-node"> 24 <a href="<%# Eval("Url") %>"> 25 <%# Eval("Title") %> 26 </a> 27 </li> 28 <asp:Repeater runat="server" ID="FirstLevelNodes" DataSource="<%# ((SiteMapNode)Container.DataItem).ChildNodes %>"> 29 <ItemTemplate> 30 <!-- if has children --> 31 <li runat="server" visible="<%# ((SiteMapNode) Container.DataItem).ChildNodes.Count > 0 %>" data-node-count="<%# ((SiteMapNode)((RepeaterItem)Container.Parent.Parent).DataItem).ChildNodes.Count %>" data-node-index="<%# Container.ItemIndex %>" class='<%# Container.ItemIndex == ((SiteMapNode)((RepeaterItem)Container.Parent.Parent).DataItem).ChildNodes.Count-1 ? "dropdown last-node nav-node" : (Container.ItemIndex == 0 ? "dropdown first-node nav-node" : "dropdown nav-node") %>'> 32 <a href="<%# Eval("Url") %>" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"> 33 <%# Eval("Title") %> 34 <span class="caret"></span> 35 </a> 36 <asp:Repeater runat="server" ID="ChildMenuRepeater" DataSource="<%# ((SiteMapNode)Container.DataItem).ChildNodes %>"> 37 <HeaderTemplate> 38 <ul class="dropdown-menu" role="menu"> 39 </HeaderTemplate> 40 <ItemTemplate> 41 <li data-node-index="<%# Container.ItemIndex %>" class='nav-node'> 42 <a href="<%# Eval("Url") %>"> 43 <%# Eval("Title") %> 44 </a> 45 </li> 46 </ItemTemplate> 47 <FooterTemplate></ul></FooterTemplate> 48 </asp:Repeater> 49 </li> 50 <!-- if has zero children --> 51 <li runat="server" visible="<%# ((SiteMapNode) Container.DataItem).ChildNodes.Count <= 0 %>" data-node-count="<%# ((SiteMapNode)((RepeaterItem)Container.Parent.Parent).DataItem).ChildNodes.Count %>" data-node-index="<%# Container.ItemIndex %>" class='<%# Container.ItemIndex == ((SiteMapNode)((RepeaterItem)Container.Parent.Parent).DataItem).ChildNodes.Count-1 ? "last-node nav-node" : (Container.ItemIndex == 0 ? "first-node nav-node" : "nav-node") %>' > 52 <a href="<%# Eval("Url") %>"> 53 <%# Eval("Title") %> 54 </a> 55 </li> 56 </ItemTemplate> 57 </asp:Repeater> 58 </ItemTemplate> 59 <FooterTemplate> 60 </ul> 61 </FooterTemplate> 62 </asp:Repeater> 63 64 </div> 65 <!-- /.navbar-collapse --> 66 67 </div> 68 <!-- end container --> 69 </nav> 70 <!-- end nav-bar -->

Take a look at how this renders, much better.

render

And when the screen hits the break point. It collapses and you have the mobile friendly menu. The break point is set in the bootstrap CSS (I believe it is 768px). The menu below is also

resp1

And the drop down

resp2

The Code Explained

You can skip this part if you don’t care what the code snippet actually does. I’ll break it down into parts to help you understand so you can make changes to it.

1) It’s up to you to determine where you are going to put the navigation inside your masterpage

In my particular case below I’ve created my own custom header where I put the navbar. I then turn the visibility off on the PlaceHolderTopNavBar (line 543)

ex

2) Looking at just the very high level bootstrap nav html

boot1

3) The Root Level Repeater

The first level I consider the root level. It contains the root navigation node typically “Home”. Below is the best representation of how the data comes back from the topSiteMap navigation provider. Each color is a level. The children of “Home” are the main level. This is similar to the OOTB, you might have seen the first node that isn’t in the navigation provider but just shows up anyway.

nav123 nav1234

Now for the root level. In this screen shot you can see I render the root node then have a second repeater for its children nodes. The html is fairly simple for the first node and gets more involved for the next level.

zerolevel

4) The First Level Repeater

This level gets a bit more interesting because now we have two different classifications of nav nodes, with children and without children. We have to handle each a slight bit different and I’ll explain. If you have children we need to specify a different class to have a drop down. Next it needs a nested repeater to handle the next level (2nd level). If there are no children we can simply just render the navigation node just as we did with the root level. I use the ChildNodes.Count to determine which to hide or show. You might notice that the

  • ’s, line 487 & 507, have some other attributes like data-node-count and data-node-index and are REALLY long.  I’ll explain that in the next section.

level_one

4a) First Level

  • ’s

Let’s examine the first

  • the one with children. They are both the same except for the visible attr. We are going to focus on the following attributes, data-node-count, data-node-index, & class.

data-node-count – is not needed, but this will tell you how many sibling nodes are detected. I used this to help generate the if statements for class.

data-node-index – is not needed, but this will tell you the index of the node in amongst it’s current siblings. I used this to help generate the if statements for class.

a

class – this is basically a fancy edition I added which provides a first-node and last-node class. Again you’ll notice this one has “dropdown” in the class, the other

  • won’t because it doesn’t have children and doesn’t need a drop down.
    • The first node of a set will have the class=“dropdown first-node nav-node”
    • The middle nodes would have the class=”dropdown nav-node”
    • The last node would have the class=”dropdown last-node nav-node”
1 class='<%# Container.ItemIndex == ((SiteMapNode)((RepeaterItem)Container.Parent.Parent).DataItem).ChildNodes.Count-1 ? "dropdown last-node nav-node" : (Container.ItemIndex == 0 ? "dropdown first-node nav-node" : "dropdown nav-node") %>'>

5) The Second Level Repeater

The second level repeater code gets a little simpler. This is our last level and we don’t consider if the node has children or not. We just render the value of the node and leave it at that. If you wanted to go 3, 4 or 5 levels essentially it’s just copy what we’ve already done but I wouldn’t go more than 2 levels. Bootstrap 3.0 removed  support for multilevel dropdowns, citing usability issues as the cause.  You would be on your own if you decided to go for more levels. There are 3rd party plugins available to assist Bootstrap 3.0 with multi levels.

second_level

Known Issues

You expected everything to work beautifully, I know! But there is always a gotcha! when it comes to SharePoint.

The only issue with this method is that you loose the selected state of the navigation. This is easily over come with supplemental jQuery. You’re only other alternative is to convert this all to a user control (like I mentioned way earlier) and then in code what page you are on and add a class. I don’t have the reference to this method – but I’m looking. To be posted later.

Here is my jQuery work around – for highlighting the navigation for a selected node.

Download Script Here

1 $(document).ready(function() { 2 var nav = $(".nav"); 3 if (nav == null || typeof (nav) == 'undefined') return; 4 5 /* Match navigation on current page */ 6 var url = window.location.href.toLowerCase(); 7 var decUrl = decodeURI(url); 8 nav.find("li > a").each(function () { 9 10 var href = $(this).attr("href").toLowerCase(); 11 var decHref = decodeURI(href); 12 13 if (decUrl.indexOf(decHref) != -1) { 14 $(this).addClass("active"); 15 return false; 16 } 17 }); 18 });

Conclusion

That explains how I was able to utilize the SharePoint out of the box top navigation provider and build a new menu with ASP repeaters to behave responsively and work with Bootstrap 3.0. This is a simple enhancement that I’ve been re-using on many on of my On-Prem responsive projects. Unfortunately this doesn’t not work for SharePoint Online or O365 as the Masterpage does not allow code blocks. I’m curious if that’s with only editing pages by SharePoint designer, but what about deployed through wsp. http://stackoverflow.com/questions/22566589/sharepoint-master-page-asprepeater-tag . I hope to uncover an answer either way. Thanks for reading!

Office 365 Icons

Introduction

I recently came across an interesting blog – http://www.n8d.at/blog/office-365-icon-font-documentation/ which mentions O365 icons. This was back in February so when I attempted to locate these fonts so I could use them in some of my work. I could not located the file on the CDN that was mentioned. I kind of shrugged it off for a while but recently needed the icons again. So I went digging inside O365 and I found them. They are there if you want to use them. I’ll show you what classes to use. I also pulled the files which I’ll provide a link to at the end of the blog so you can download them and use them and not have to worry about CDN’s.

I found all the O365 icon inside my O365 Portal.  I downloaded the web fonts and a portion of the css file that has the code for the icons.  Using this approach you would be locked into the current icon. Microsoft is likely to continually update the icon & css, so this set would eventually become stale. If there ever is an official CDN I’d switch to that. If you’re on office 365 you don’t need to link to any fonts or css. You are able to just take advantages of the icons immediately you just need to look at my chart to see the classes or determine the code for the css content. If you don’t understand anything about using web fonts or what office 365 icons are, read the blog I mentioned above. That should bring you right up to speed.

Installation

    • copy the css & font folder to your on prem site
    • link to the css file in your masterpage or page (adjust path as needed)
      
      
  • you can implement one of the icons using something like this below
    
    
  • i have additional styles for color, font size, and spacing. view the source to check that out

Explanation of the Grid

  • the orange tiles are icons that are available by using the css:after { content: ‘code'; } or if you copy the supplemental css above
  • the blue tiles are icons that have classes in O365. you can use those classes in directly on your o365 site without attaching any styles or fonts
  • the black tiles are dead, meaning they don’t show or are not in the font package but may be in the official css and have not been purged.

The concept of using fonts for icons is nothing new and if you are not exactly familiar with that check out Font Awesome or Glyphicons. The concept is that fonts will scale nice, the are light weight and fully loaded once when you hit the site. You can easily resize them and re-color them. No need for messing around with images.

Where I found the files

Fonts

https://outlook.office365.com/owa/prem/16.0.772.13/resources/styles/fonts/office365icons.svg

https://outlook.office365.com/owa/prem/16.0.772.13/resources/styles/fonts/office365icons.eot

https://outlook.office365.com/owa/prem/16.0.772.13/resources/styles/fonts/office365icons.ttf

https://outlook.office365.com/owa/prem/16.0.772.13/resources/styles/fonts/office365icons.woff

CSS

https://prod.msocdn.com/16.00.0791.004/en-US/css/shellg2corecss_f727a58f.css

Full Preview Site

Download Files Here

Screenshot of all the possible icons –> see full preview site above for a live example

1

2

3

4

5

6

7

8

Full Preview Site

Download Files Here

Thanks for checking this out

My Favorite JavaScript Libraries and jQuery plugins

These are a few of my favorite and go to JavaScript libraries. These tend to work well in SharePoint with no problem. This is my personal list that I use

Grid

Console Log

Glyphs

Carousel

Cookies

Click Outside

Mutate / Resize

Select Box (supports Multi)

Time / Date

Truncating Text

Layouts (masonry type)

Notify / Alerts

Modal

Scroll Bars

SharePoint Specific

Working with SASS style sheets in Office 365 / SharePoint 2013

My new favorite thing is creating all my style sheets with SASS. SASS makes writing my style sheets faster, easier, cleaner, and more reusable. Don’t ask me why I chose SASS, but I liked it just a little better than LESS even though they are very similar.  You can go do research for yourself and decide which you’d like to use.  SASS – http://sass-lang.com/ LESS – http://lesscss.org/.

SASS is nothing new however it is very difficult to work with when using with SharePoint 2010/2013 .. or Office 365. Why is it difficult? Because SASS needs to be compiled to CSS and none of your typical SharePoint tools do that.

 

Current Scenarios:

  1. If you are working on O365 you might be working with SharePoint Designer which won’t do anything for you.
  2. Maybe you have Visual Studio you can use Web Essentials or Mindscape Web Workbench. Both of these will compile the SASS to CSS
  3. You can use a stand alone compiler like Koala, Propos, & Scout (for Windows) … but configuring them to work can be tricky.
  4. You can use an online compiler – SassMeister | The Sass Playground! , but then you will be doing a lot of copy / pasting / uploading.

 

This article is going to show you how to use Sublime Text 2 on your desktop, to write and compile SASS, which will automatically be saved to O365. No copy / paste or uploading needed!

 

General Approach

  • Map network drive to O365 site
  • Use a desktop editor to create SASS
  • Use a desktop compiler to create the CSS
  • Use Scout to monitor CSS changes locally and copy file to network drive

 

Pre-Requisites

1. Java v7 – http://www.java.com/en/download [Scout has issue with any other version, https://github.com/mhs/scout-app/issues/173]

2. O365 Site

 

Step 1 – Install Sublime Text 2 & SASS Builder

1. go to http://www.sublimetext.com/2 and download and install

2. go to https://github.com/jaumefontal/SASS-Build-SublimeText2 (Follow Instructions there skip to step 3)

Installing Ruby

Install Ruby library (for windows) – http://rubyinstaller.org/downloads/

  • check box to add Ruby executables to your PATH

Start Command Prompt with Ruby

 image

In the console type the following

  • gem install sass
  • IF you get this error

image

Take a deep breath – you need to download the cert and add it to your RubyGem’s certificate directory.

image

  • go to that folder in windows in windows explorer
  • open up a subfolder rubygems\ssl_certs
  • copy the AddTrustExternalCARoot-2048.pem to the rubygems\ssl_certs
  • Retry “gem install sass” –SUCCESS!

image 

Installing SASS Build System

The easiest way to install this package is through Package Control.

1. Download and install the Package Control Plugin. Follow the instructions on the website

2. Open the command panel: Control+Shift+P (Linux/Windows) or Command+Shift+P (OS X) and select ‘Package Control: Install Package‘.

image

3. When the packages list appears type ‘SASS‘ and you’ll find the SASS Build System. Select to install it.

image 

4. Set the Build System to SASS,  In Tools –> Build System –> SASS

image

5. Now you can compile your SASS files! Launch your build with Control+B (Linux/Windows) or Command+B (OS X).

**NOTE** if you are not getting you are not getting colors on your SASS – click the bottom right corner and set the Language as SASS

image

 

Step 2 – Map Network Folder to O365

The key to this part is to first log into your O365 site w/ Internet Explorer. I then go to any folder and click the button to Open with Explorer. Then I can usually successfully map the network drive.

PRE-REQUISITES – Your site must be in the trusted sites. I actually add a few *.microsoft[sites] http://blogs.technet.com/b/sharepoint_made_easy/archive/2013/03/20/map-network-drive-webdav-with-sharepoint-online-o365.aspx

IE > Tools > Internet Options > Security > Trusted Sites [click sites button]

image 

1. Log into your site with Internet Explorer – be sure to check ‘Keep me signed in’

image

2. Open up any document library [might look different. this screenshot from small resolution virtual machine]

image

3. Next, do the standard map network drive per your Operating System. I’m running Windows 7.

basically right click on Computer and click Map Network Drive

image

4. Enter in the site url w/ any subfolder you want [root is fine]. I also check connect using different credentials.

4.1 Enter in your O365 credentials

image

5. SUCCCESS !

image

Step 3 – Install & Setup Scout

For this you will need to create a local working folder for all CSS. for example I will create a folder on the desktop called ‘MySassExample’

1. Download and Install Scout @ http://mhs.github.io/scout-app/

2. Once Scout installs, Open it up and click the + in the bottom left to add a new project

image

3. Navigate to your local working folder

image

4. Under Configure, Stylesheet Directories –> set the Input Folder to your local working folder & the Output folder to your network mapped drive (o365)

I set it to output to the Style Library of my site.

image

5. Click the Play Button

image

 

Step 4 – The Final Step, Verifying it all works!

1. With Scout running, Go to your local working folder.

2. Create a new .SCSS file or open an existing one in Sublime Text 2

3. Create some SASS

image

4. Save, then CTRL+B to build it. You should get the write ~file output.
 image

5. In Scout, you should see …. overwrite .css

Sometimes you need to give it a few seconds to save the file to o365, the network drive tends to take longer to save files

image

** NOTE if you are using an existing file make sure you check it out via SharePoint. Otherwise it won’t save and you’ll see errors in Scout when it tries **

 

Wrap Up

The concludes how to setup Scout & Sublime Text 2 to create and update SASS styles sheets which compile to .css and automatically push to O365. The hardest thing I found is mapping the drive successfully. There are many helpful articles on the different errors you might get trying to do so.  It may seen like a lot of steps to get going, but if you have used SASS or LESS before you know how powerful it is and how much time you might potentially save. This blog was meant to help get this setup and show you how you can get started. There are other programs like Scout, but Scout was the only one that I could get to work the way I liked. There are also other editors that you could use if you don’t like Sublime Text 2. Best of luck!

 

Resources

More on Scout – http://www.impressivewebs.com/sass-on-windows-with-scout-app/

Errors Mapping Network Drive – http://blogs.technet.com/b/sharepoint_made_easy/archive/2013/03/20/map-network-drive-webdav-with-sharepoint-online-o365.aspx

SASS – http://sass-lang.com/

Delete a Workflow on the Host Web from the App Web

below is some sample code to delete a workflow on the host web from your app web. This would be useful in the event you want your App to remove a workflow that you have deployed to the host web.

NOTE: you must include a reference to “/_layouts/15/SP.WorkflowServices.js”

this file can be downloaded here

   1: var deleteWorkflow = function (workflowName) {

   2:  

   3:     //Using the App Web as the client context

   4:     clientContext = new SP.ClientContext.get_current();

   5:  

   6:     //Get the host web URL from the query string params

   7:     //I have a function getHostWebUrl() - which is not included.

   8:     //http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript

   9:     var hostWebUrl = getHostWebUrl();

  10:     

  11:     //Using the hostWebContext as an AppContextSite

  12:     hostWebContext = new SP.AppContextSite(clientContext, hostWebUrl);

  13:  

  14:     //Get the Workflow Services Manager for the Host web, NOTE: you initialize it with the clientContext & the hostWebContext Web)

  15:     var hostWebWorkflowServicesManager = new SP.WorkflowServices.WorkflowServicesManager.newObject(clientContext, hostWebContext.get_web());

  16:     var hostWebWorkflowDeploymentService = hostWebWorkflowServicesManager.getWorkflowDeploymentService();

  17:     var hostWebDefinitionsCollection = hostWebWorkflowDeploymentService.enumerateDefinitions(false);

  18:  

  19:     //load all the workflow definitions from the host web 

  20:     clientContext.load(hostWebDefinitionsCollection);

  21:     clientContext.executeQueryAsync(function () {

  22:  

  23:         //get the enumerator for all the workflow definitions

  24:         var workflowDefinitions = hostWebDefinitionsCollection.getEnumerator();

  25:         while (workflowDefinitions.moveNext()) {

  26:             

  27:             //set the current definition to a variable

  28:             var workflowDefinition = workflowDefinitions.get_current();

  29:             

  30:             //uncomment to see all the workflows that it finds

  31:             //console.log("Found Workflow : " + workflowDefinition.get_displayName());

  32:             

  33:             //looking for a match on the name of the workflow

  34:             if (workflowDefinition.get_displayName() === workflowName) {

  35:                 //using the host web workflow deployment service to delete the workflow definition

  36:                 

  37:                 hostWebWorkflowDeploymentService.deleteDefinition(workflowDefinition.get_id());

  38:                 //this comment will just display what workflow WILL BE deleted 

  39:                 console.log("Deleted workflow : " + workflowName + ", " + workflowDefinition.get_id().toString());

  40:                 

  41:                 //if more than one workflow with the same name is found it will delete all of them

  42:                 //the delete will get queued up and executed by the .executeQueryAsync

  43:                 //if you don't want that then you have to break out of the while loop

  44:             }

  45:         }

  46:         //this query will execute the delete action

  47:         clientContext.executeQueryAsync(function () {

  48:             //Successfully deleted   the workflow

  49:         }, function (sender, args) {

  50:             console.log("Unable to delete workflow : " + workflowName);

  51:             console.log("<span style='color:red'>Reason : " + args.get_message() + "</span>");

  52:         });

  53:     }, function (sender, args) {

  54:         console.log("Failed to load workflows");

  55:         console.log("<span style='color:red'>Reason : " + args.get_message() + "</span>");

  56:     });

  57: };

Provision a Workflow to the Host Web from the App Web

below is some sample code to provision a workflow on the host web from your app web. This sample code is in one block for demo purposes. It makes 4 asynchronous calls to SharePoint to complete successfully.

In the event you’d want to define in your App a workflow that would run on a list in the host web this is the code you would use to deploy (move it from App to Host) and attach it to a list in the host web.

NOTE: you must include a reference to “/_layouts/15/SP.WorkflowServices.js”

this file can be downloaded here

   1: var attachWorkflow = function(workflowName, workflowListName, workflowHistoryListName, workflowTasksListName) {

   2:  

   3:      //Using the App Web as the client context

   4:      clientContext = new SP.ClientContext.get_current();

   5:  

   6:      //Get the host web URL from the query string params

   7:      //I have a function getHostWebUrl() - which is not included.

   8:      //http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript

   9:      var hostWebUrl = getHostWebUrl();

  10:      

  11:      //Using the hostWebContext as an AppContextSite

  12:      hostWebContext = new SP.AppContextSite(clientContext, hostWebUrl);

  13:      

  14:      //Get the Workflow Services Manager for the Host web, NOTE: you initialize it with the clientContext & the hostWebContext Web)

  15:      var hostWebWorkflowServicesManager = new SP.WorkflowServices.WorkflowServicesManager.newObject(clientContext, hostWebContext.get_web());

  16:      var hostWebWorkflowDeploymentService = hostWebWorkflowServicesManager.getWorkflowDeploymentService();

  17:      var hostWebSubscriptionService = hostWebWorkflowServicesManager.getWorkflowSubscriptionService();

  18:  

  19:      //Get the Workflow Services Manager for the App web 

  20:      var workflowServicesManager = new SP.WorkflowServices.WorkflowServicesManager(clientContext, clientContext.get_web());

  21:      var workflowDeploymentService = workflowServicesManager.getWorkflowDeploymentService();

  22:      var workflowDeploymentServiceDefinitions = workflowDeploymentService.enumerateDefinitions(false);

  23:  

  24:      //Load all the Workflow Definitions from the App web

  25:      clientContext.load(workflowDeploymentServiceDefinitions);

  26:      clientContext.executeQueryAsync(function () {

  27:  

  28:          //Get the enumerator for all the workflow definitions in the App web

  29:          var workflowDefinitions = workflowDeploymentServiceDefinitions.getEnumerator();

  30:          while (workflowDefinitions.moveNext()) {

  31:  

  32:              //set the current definition to a variable

  33:              var workflowDefinition = workflowDefinitions.get_current();

  34:              //test, trying to locate your workflow in the App web

  35:              if (workflowDefinition.get_displayName() === workflowName) {

  36:  

  37:                  //now grab the xaml definition of the selected workflow

  38:                  var workflowXaml = workflowDefinition.get_xaml();

  39:  

  40:                  //define a new workflow and assign it xaml & name

  41:                  var newWorkflow = new SP.WorkflowServices.WorkflowDefinition.newObject(clientContext, hostWebContext.get_web());

  42:                  newWorkflow.set_xaml(workflowXaml);

  43:                  newWorkflow.set_displayName(workflowName);

  44:                  

  45:                  //using the host webs Workflow Deployment Service, save the workflow.

  46:                  hostWebWorkflowDeploymentService.saveDefinition(newWorkflow);

  47:                  

  48:                  //load the new workflow and initialize the Id

  49:                  clientContext.load(newWorkflow, "Id");

  50:                  //this query will save your workflow to the host web

  51:                  clientContext.executeQueryAsync(function() {

  52:  

  53:                      //publish the workflow on the host web

  54:                      hostWebWorkflowDeploymentService.publishDefinition(newWorkflow.get_id());

  55:                      

  56:                      //getting all the lists that the workflow will attach to

  57:                      //these lists already created on the host web

  58:                      var targetList = hostWebContext.get_web().get_lists().getByTitle(workflowListName);

  59:                      var historyList = hostWebContext.get_web().get_lists().getByTitle(workflowHistoryListName);

  60:                      var tasksList = hostWebContext.get_web().get_lists().getByTitle(workflowTasksListName);

  61:  

  62:                      //loading the lists & initialize the Id

  63:                      clientContext.load(targetList, "Id");

  64:                      clientContext.load(historyList, "Id");

  65:                      clientContext.load(tasksList, "Id");

  66:  

  67:                      //this query will publish the workflow and get the required list id's for the next step

  68:                      clientContext.executeQueryAsync(function() {

  69:  

  70:                              //creating a new workflow subscription

  71:                              var subscription = new SP.WorkflowServices.WorkflowSubscription(clientContext, hostWebContext.get_web());

  72:  

  73:                              //setting some properties of the subscription

  74:                              subscription.set_name(workflowName + "-ItemAdded");

  75:                              subscription.set_enabled(true);

  76:                              subscription.set_definitionId(newWorkflow.get_id().toString());

  77:                              subscription.set_eventSourceId(targetList.get_id());

  78:                              subscription.set_eventTypes(["ItemAdded"]);

  79:  

  80:                              subscription.setProperty("TaskListId", tasksList.get_id().toString());

  81:                              subscription.setProperty("HistoryListId", historyList.get_id().toString());

  82:                              subscription.setProperty("FormData", "");

  83:  

  84:                              //attaching the subscription to the target list 

  85:                              hostWebSubscriptionService.publishSubscriptionForList(subscription, targetList.get_id());

  86:  

  87:                              //this query will execute the creation of the new subscription and adding attaching the subscription to the target list on the host web

  88:                              clientContext.executeQueryAsync(function() {

  89:                                  console.log("Workflow : \"" + workflowName + "\" successfully added to list : \"" + workflowListName + "\"");

  90:                              }, function(sender, args) {

  91:                                  console.log("Workflow : " + workflowName + " not successfully added to list : " + workflowListName);

  92:                                  console.log("<span style='color:red'>Reason : " + args.get_message() + "</span>");

  93:                              });

  94:                          },

  95:                          function(sender, args) {

  96:                              console.log("Failed to get workflow list IDs");

  97:                              console.log("<span style='color:red'>Reason : " + args.get_message() + "</span>");

  98:                          });

  99:  

 100:                  }, function(sender, args) {

 101:                      console.log("Failed to create the workflow definition");

 102:                      console.log("<span style='color:red'>Reason : " + args.get_message() + "</span>");

 103:                  });

 104:              }

 105:          }

 106:      }, function (sender, args) {

 107:          console.log("Could not find workflow : " + workflowName);

 108:          console.log("<span style='color:red'>Reason : " + args.get_message() + "</span>");

 109:      });

 110:  };

Deleting a List on the Host Web from the App Web with JavaScript Object Model (JSOM)

Below is some sample code of how to delete a list on the host web from the app web via SharePoint JavaScript object model
 
   1: function DeleteList(listName) {

   2:  

   3:     //Using the App Web as the client context

   4:     clientContext = new SP.ClientContext.get_current();

   5:  

   6:     //Get the host web URL from the query string params

   7:     //I have a function getHostWebUrl() - which is not included.

   8:     //http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript

   9:     var hostWebUrl = getHostWebUrl();

  10:     //Using the hostWebContext as an AppContextSite

  11:     hostWebContext = new SP.AppContextSite(clientContext, hostWebUrl);

  12:  

  13:     //get the list using the host web context

  14:     var list = hostWebContext.get_web().get_lists().getByTitle(listName);

  15:     list.deleteObject();

  16:     

  17:     //Always use the context of the app web to do the work or load and executing

  18:     clientContext.executeQueryAsync(function() {

  19:         console.log("Deleted List : \"" + listName + "\"");

  20:     }, function(sender, args) {

  21:         console.log("Failed to delete list : " + listName);

  22:         console.log("<span style='color:red'>Reason : " + args.get_message() + "</span>");

  23:     });

  24:  

  25: }

  26:  

  27: // parameters : List Name

  28: DeleteList("My Test List");