Wednesday, 7 December 2016

Azure API: Why does my API in the API Management Developer Portal not have a subscription key?

I encountered this problem recently when trying to add an API to my API Management. I followed all the steps but my API execution complained that the subscription key was missing.

The reason for the error is that my API was not associated to a Product. To fix this,

1. Open the Publisher Portal from 'Overview' option in the API Management blade



2. Navigate to the 'Products' tab and assign the API to a product



3. Now navigate to the Developer Portal and subscription key value is now set in the request header.



Tuesday, 6 December 2016

How can I document my API with Swagger?

Swagger has provided a very simple way to document your APIs.

I created a simple OOTB Web Application with the Web API template. To create the swagger output:

1. Add the SwashBuckle nuget package



2. Publish the API and navigate to <API URL>/swagger/docs/v1. This will create the swagger json


3. Save the file in a swagger.json file in the solution.
4. Open the file with the http://editor.swagger.io/#/ to retrieve the documentation.




Thursday, 1 December 2016

C#: How do I create a comma separated list from an object?

        public static string CreateCSVFromObject(object obj)
        {
            if (obj == null)
            {
                throw new ArgumentNullException("obj", "Value can not be null or Nothing!");
            }

            StringBuilder sb = new StringBuilder();
            Type t = obj.GetType();
            PropertyInfo[] pi = t.GetProperties();

            for (int index = 0; index < pi.Length; index++)
            {
                sb.Append(pi[index].GetValue(obj, null));

                if (index < pi.Length - 1)
                {
                    sb.Append(",");
                }
            }

            return sb.ToString();
        }

Tuesday, 29 November 2016

How can I create a generic data repository for my Entity Framework solution?

I recently created a quick console application that required CRUD activities on a SQL database. I had a few tables to update and did not want the headache of creating multiple items in my DAL.

Enter a generic data repository.

The code is pretty simple:

1. Create a base interface for the CRUD operations

public interface IGenericDataRepository<T> where T : class
    {
        List<T> GetAll(params Expression<Func<T, object>>[] navigationProperties);
        List<T> GetList(Func<T, bool> where, params Expression<Func<T, object>>[] navigationProperties);
        T GetSingle(Func<T, bool> where, params Expression<Func<T, object>>[] navigationProperties);
        void Add(params T[] items);
        void Update(params T[] items);
        void Remove(params T[] items);
        void RemoveAll(string tableName);
        void ExecuteSQLCommand(string sqlCommand, SqlParameter[] parameters);
    }

2. Create your Entity Framework data model. I called my ImportEntities.

3. Create a repository class that uses the interface

 public class GenericDataRepository<T> : IGenericDataRepository<T> where T : class
    {
        public virtual List<T> GetAll(params Expression<Func<T, object>>[] navigationProperties)
        {
            List<T> list;
            using (var context = new ImportEntities())
            {
                IQueryable<T> dbQuery = context.Set<T>();

                //Apply eager loading
                foreach (Expression<Func<T, object>> navigationProperty in navigationProperties)
                    dbQuery = dbQuery.Include<T, object>(navigationProperty);

                list = dbQuery
                    .AsNoTracking()
                    .ToList<T>();
            }
            return list;
        }

        public virtual List<T> GetList(Func<T, bool> where,
             params Expression<Func<T, object>>[] navigationProperties)
        {
            List<T> list;
            using (var context = new ImportEntities())
            {
                IQueryable<T> dbQuery = context.Set<T>();

                //Apply eager loading
                foreach (Expression<Func<T, object>> navigationProperty in navigationProperties)
                    dbQuery = dbQuery.Include<T, object>(navigationProperty);

                list = dbQuery
                    .AsNoTracking()
                    .Where(where)
                    .ToList<T>();
            }
            return list;
        }

        public virtual T GetSingle(Func<T, bool> where,
             params Expression<Func<T, object>>[] navigationProperties)
        {
            T item = null;
            using (var context = new ImportEntities())
            {
                IQueryable<T> dbQuery = context.Set<T>();

                //Apply eager loading
                foreach (Expression<Func<T, object>> navigationProperty in navigationProperties)
                    dbQuery = dbQuery.Include<T, object>(navigationProperty);

                item = dbQuery
                    .AsNoTracking() //Don't track any changes for the selected item
                    .FirstOrDefault(where); //Apply where clause
            }
            return item;
        }

        public virtual void Add(params T[] items)
        {
            try
            {
                using (var context = new ImportEntities())
                {
                    foreach (T item in items)
                    {
                        context.Entry(item).State = EntityState.Added;
                    }
                    context.SaveChanges();
                }
            }
            catch (DbEntityValidationException dbEx)
            {
                foreach (var validationErrors in dbEx.EntityValidationErrors)
                {
                    foreach (var validationError in validationErrors.ValidationErrors)
                    {
                        Console.WriteLine("Property: {0} Error: {1}",
                                                validationError.PropertyName,
                                                validationError.ErrorMessage);
                    }
                    Console.ReadLine();
                }
            }

        }

        public virtual void Update(params T[] items)
        {
            using (var context = new ImportEntities())
            {
                foreach (T item in items)
                {
                    context.Entry(item).State = EntityState.Modified;
                }
                context.SaveChanges();
            }
        }

        public virtual void Remove(params T[] items)
        {
            using (var context = new ImportEntities())
            {
                foreach (T item in items)
                {
                    context.Entry(item).State = EntityState.Deleted;
                }
                context.SaveChanges();
            }
        }

        public virtual void RemoveAll(string tableName)
        {
            using (var context = new ImportEntities())
            {
                context.Database.ExecuteSqlCommand(string.Format("TRUNCATE TABLE [{0}]", tableName));
            }
        }

        public virtual void ExecuteSQLCommand(string sqlCommand, SqlParameter[] parameters)
        {
            using (var context = new ImportEntities())
            {
                context.Database.ExecuteSqlCommand(sqlCommand, parameters);
            }
        }
    }

4. Now, we can access the repository in code: For example, I have a table called 'TempTable' configured with my Entity Framework data model, I can then instantiate the repository like so:

var repository = new GenericDataRepository<TempTable>();

and use it as follows:
- clear the list: 
repository.RemoveAll("TempTable");
- add an item:
repository,add(new TempTable() { Id = 1, Name = "test" });
- execute a stored procedure:
var parameters = new SqlParameter[]
                 {
                    new SqlParameter("ExecutionInstance",Guid.NewGuid()),
                    new SqlParameter("Sequence", 1)
                 };
            repository.ExecuteSQLCommand("dbo.MyProc @ExecutionInstance, @Sequence", parameters);

Monday, 28 November 2016

Azure: How do I create an MVC site to load files to BLOB storage?

I recently created a simple POC site to load files to blob storage containers in Azure. Here is how I achieved it.

My code is based on the sample provided here

After creating a simple MVC web project, I added the following nuget packages:

DropZone
WindowsAzure.Storage
Microsoft.WindowsAzure.ConfigurationManager

The code is pretty simple:

Here is Index.cshtml:

@model List<string>

<script src="~/Scripts/dropzone/dropzone.min.js"></script>
<script src="~/Scripts/bootstrap.min.js"></script>
<link href="~/Scripts/dropzone/dropzone.min.css" rel="stylesheet" />

<div class="jumbotron">
    <h2>FILE UPLOAD</h2>
    <form action="~/Home/Upload"
          class="dropzone"
          id="dropzoneJsForm"
          style="background-color:#00BFFF"></form>
   
    <button id="refresh" onclick="window.location.reload();">
        Refresh
    </button>

    <table>
        @foreach (var i in Model)
        {
            <tr>
                <td>@i</td>
            </tr>
        }
    </table>
</div>

<script type="text/javascript">

    Dropzone.options.dropzoneForm =
    {
        init: function () {
            this.on("complete", function (data) {
                var res = JSON.parse(data.xhr.responseText);
            });
        }
    };
</script>

My HomeController is pretty simple as well:

 public class HomeController : Controller
    {
        public ActionResult Index()
        {
            var container = BLOBHelper.GetContainer("mycontainer", true);
            var items = BLOBHelper.GetBlobItems(container);
            return View(items);
        }

        public ActionResult Upload()
        {

            foreach (string f in Request.Files)
            {
                HttpPostedFileBase file = Request.Files[f];
                //fName = file.FileName;
                if (file != null && file.ContentLength > 0)
                {
                    var fileName = Path.GetFileName(file.FileName);
                    var container = BLOBHelper.GetContainer("mycontainer", true);
// optionally set access permissions                  
// container.SetPermissions(new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Blob });
                    BLOBHelper.UploadFile(container, file,  fileName);
                }
            }
            return RedirectToAction("Index");
        }

But the core of the application is the BLOBHelper class:

  public class BLOBHelper
    {
       // lists all the items in the container at the root level
        public static List<string> GetBlobItems(CloudBlobContainer container)
        {
            List<string> items = new List<string>();

            foreach (IListBlobItem item in container.ListBlobs(null, false))
            {
                if (item.GetType() == typeof(CloudBlockBlob))
                {
                    CloudBlockBlob blob = (CloudBlockBlob)item;
                    items.Add(blob.Uri.ToString());
                }
                else if (item.GetType() == typeof(CloudPageBlob))
                {
                    CloudPageBlob pageBlob = (CloudPageBlob)item;
                    items.Add(pageBlob.Uri.ToString());
                }
                else if (item.GetType() == typeof(CloudBlobDirectory))
                {
                    CloudBlobDirectory directory = (CloudBlobDirectory)item;
                    items.Add(directory.Uri.ToString());
                }
            }

            return items;
        }

        // uploads a file to a container
        public static bool UploadFile(CloudBlobContainer container, HttpPostedFileBase file,  string fileName)
        {
            CloudBlockBlob blockBlob = container.GetBlockBlobReference(fileName);

            using (var reader = new BinaryReader(file.InputStream))
            {
                byte[] imgData = reader.ReadBytes(file.ContentLength);
                using (var fileStream = new MemoryStream(imgData))
                {
                    blockBlob.UploadFromStream(fileStream);
                }
            }

            return true;
        }

        // get / create a container
        public static CloudBlobContainer GetContainer(string name, bool createIfNotExists = false)
        {
            CloudStorageAccount storageAccount = CloudStorageAccount.Parse(
  Microsoft.Azure.CloudConfigurationManager.GetSetting("StorageConnectionString"));
            CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer container = blobClient.GetContainerReference(name);
            if (createIfNotExists) {
                container.CreateIfNotExists();
            }
            return container;
        }

Dont forget to add the required web.config settings:

<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
  </startup>
 <appSettings>
      <add key="StorageConnectionString" value="DefaultEndpointsProtocol=https;AccountName=CHANGEME;AccountKey=CHANGEME" />
  </appSettings>
</configuration>

Wednesday, 26 October 2016

CSS: How do I create a spinning 'loading' icon using an image

The icon can be created using the following simple classes

.loading{background:url(../img/loading-purple.gif) no-repeat center}
.loading > div{display:none}

Here is a sample image that could be used.



Too easy. However, this is old school. You can create a simpler one using CSS and HTML5.

Here is the CSS:
.loading-container{display:block;width:100%;min-height:45%;margin-top:60px;text-align:center}
.loader {margin:0 auto;text-align:center;width:200px;min-height:100%;display:block;vertical-align:middle}
.loader:hover{opacity:1}
.loading-spinning-bubbles{position:relative;margin:auto;min-height:1px}
.loading-spinning-bubbles .bubble-container {position:absolute;top:calc(50% - 10px/2);left:calc(50% - 10px/2);transform-origin:-150% 50%;-webkit-transform-origin:-150% 50%;-moz-transform-origin:-150% 50%}
.loading-spinning-bubbles .bubble-container .bubble {background:#00ac00;width:10px;height:10px;border-radius:50%;animation:bubble 1s infinite;-webkit-animation:bubble 1s infinite;-moz-animation:bubble 1s infinite;animation-delay:inherit;-webkit-animation-delay:inherit;-moz-animation-delay:inherit}
.loading-spinning-bubbles .bubble-container:nth-of-type(0n+1) {transform:translateX(200%) rotate(-90deg);-moz-transform:translateX(200%) rotate(-90deg); -webkit-transform:translateX(200%) rotate(-90deg);animation-delay:-1.5s;-webkit-animation-delay:-1.5s}
.loading-spinning-bubbles .bubble-container:nth-of-type(0n+2) {transform:translateX(200%) rotate(-45deg);-moz-transform:translateX(200%) rotate(-45deg); -webkit-transform:translateX(200%) rotate(-45deg) ;animation-delay:-1.375s;-webkit-animation-delay:-1.375s}
.loading-spinning-bubbles .bubble-container:nth-of-type(0n+3) {transform:translateX(200%);-moz-transform:translateX(200%); -webkit-transform:translateX(200%);animation-delay:-1.25s;-webkit-animation-delay:-1.25s}
.loading-spinning-bubbles .bubble-container:nth-of-type(0n+4) {transform:translateX(200%) rotate(45deg);-moz-transform:translateX(200%) rotate(45deg); -webkit-transform:translateX(200%) rotate(45deg);animation-delay:-1.125s;-webkit-animation-delay:-1.125s}
.loading-spinning-bubbles .bubble-container:nth-of-type(0n+5) {transform:translateX(200%) rotate(90deg);-moz-transform:translateX(200%) rotate(90deg); -webkit-transform:translateX(200%) rotate(90deg);animation-delay: -1s;-webkit-animation-delay:-1s}
.loading-spinning-bubbles .bubble-container:nth-of-type(0n+6) {transform:translateX(200%) rotate(135deg);-moz-transform:translateX(200%) rotate(135deg); -webkit-transform:translateX(200%) rotate(135deg);animation-delay:-0.875s;-webkit-animation-delay:-0.875s}
.loading-spinning-bubbles .bubble-container:nth-of-type(0n+7){transform:translateX(200%) rotate(180deg);-moz-transform:translateX(200%) rotate(180deg); -webkit-transform:translateX(200%) rotate(180deg);animation-delay:-0.75s;-webkit-animation-delay:-0.75s}
.loading-spinning-bubbles .bubble-container:nth-of-type(0n+8){transform:translateX(200%) rotate(225deg);-moz-transform:translateX(200%) rotate(225deg); -webkit-transform:translateX(200%) rotate(225deg);animation-delay:-0.625s;-webkit-animation-delay:-0.625s}

And an HTML code snippet (using angular)

<div class="loading-container" ng-show="isProcessing">
    <div class="loader">
        <div class="loading-spinning-bubbles">
            <div class="bubble-container">
                <div class="bubble"></div>
            </div>
            <div class="bubble-container">
                <div class="bubble"></div>
            </div>
            <div class="bubble-container">
                <div class="bubble"></div>
            </div>
            <div class="bubble-container">
                <div class="bubble"></div>
            </div>
            <div class="bubble-container">
                <div class="bubble"></div>
            </div>
            <div class="bubble-container">
                <div class="bubble"></div>
            </div>
            <div class="bubble-container">
                <div class="bubble"></div>
            </div>
            <div class="bubble-container">
                <div class="bubble"></div>
            </div>
        </div>
    </div>
    <p class="loading-text">Loading...</p>
</div>

Wednesday, 19 October 2016

Web API: How do I clean up the data in my HttpResponse?

There are some very simple web.config settings that can be applied to reduce the amount on unnecessary data returned in an HttpReponse and to minimise ways that hackers can hijack system information.

Here is a quick snippet of web.config settings that may be useful.

<Config>
<system.web>
 <httpRuntime enableVersionHeader="false" />
</system.web>
<system.webServer>
<httpProtocol>
 <customHeaders>
<add name="Strict-Transport-Security" value="778000; includeSubdomains" />
<add name="X-Content-Type-Options" value="nosniff" />
<add name="X-Permitted-Cross-Domain-Policies" value="none" />
<add name="X-XSS-Protection" value="1; mode=block" />
<add name="Cache-Control" value="no-store" />
<add name="Pragma" value="no-cache" />
<remove name="X-Powered-By" />
 </customHeaders>
</httpProtocol>
</system.webServer>
</Config>


Sunday, 25 September 2016

C#: What is the difference between IQueryable vs IEnumerable?

The main difference between the two is the location of the query execution.

The following example demonstrates the difference:

IQueryable<Model> models = ...
models.Where(x => x.ID ...)

IQueryable is used from LINQ-to-Anything (such as SQL). In this case, the query filter will be executed on the SQL server. This is great for reducing the amount of data returned from the data store.

However, if the code looks as follows:
IEnumerable<Model> models = ...
models.Where(x=> x.ID ...)

The results might be the same but using IEnumerable will cause the query results to be loaded in the memory and the predicate will be executed on the entire result set.

Wednesday, 21 September 2016

C#: How can I get my Selemium test to log into Azure?

I have been writing some entry level Selenium tests for my Azure website that authenticates using Azure AD. The biggest issue is trying to get the WebDriver to authenticate into the solution.

After a lot of learning (read: failed attempts) I have the following solution (read: hack) that seems to work.

1. In my [TestInitialize] method, I load JQuery to the created page

public static class Common
    {
        public static string GetEval(IWebDriver driver, string script)
        {
            IJavaScriptExecutor js = driver as IJavaScriptExecutor;
            return (string)js.ExecuteScript(script);
        }

        public static string GetFile(string path)
        {
            using (var reader = new StreamReader(path))
            {
                return reader.ReadToEnd();
            }
        }
}

Common.GetEval(driver, Common.GetFile("Lib\\jquery.js"));

NOTE: I created a folder called 'lib' and added the jquery.js file. Most importantly, I set the property 'Copy to Output Directory' to 'Copy Always'

2. When I create my connection to the target site, I use JQuery to manipulate the DOM and insert the login credentials. The problem occurs when trying to press 'Enter'. The code simulates pressing 'Enter', waits 5 seconds and then does it again.

 Driver.Navigate().GoToUrl(ConfigurationManager.AppSettings["SiteUrl"]);

            // If we are in the login page, then login
            if (driver.Url.Contains(@"https://login.microsoftonline.com"))
            {
               // set the credentails
                Common.GetEval(driver, "$('#cred_userid_inputtext').val('me@mytenant.onmicrosoft.com')");
                Common.GetEval(driver, "$('#cred_password_inputtext').val('MyPassword123')");

                // press enter
                Common.GetEval(driver, "var e = jQuery.Event('keypress'); e.which = 13; e.keyCode = 13; $('#cred_password_inputtext').trigger(e);");

                System.Threading.Thread.Sleep(5000);

                // press enter again
                Common.GetEval(driver, "var e = jQuery.Event('keypress'); e.which = 13; e.keyCode = 13; $('#cred_password_inputtext').trigger(e);");
            }

3. In the test method, wait 1 second and the DOM should be available.

System.Threading.Thread.Sleep(1000);

var elements = driver.FindElements(By.CssSelector(".myclass"));


Thursday, 8 September 2016

CSS: How do I create a single 'back to top' button?

A little CSS and JQuery can produce a lot of magic in an application. Here is the code.

1. Start with a simple html page. The important item here is to have an 'ID' to navigate to. In my case, I called it 'top'.

<html>
<head>
    <base href="/" />
    <title>Test Page</title>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <script src=”//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js”></script>

    <link href="css/myStuff.css" rel="stylesheet" />
</head>
<body>
    <div>
        <!-- Start Body Content -->
        <div ng-view role="main" id="top"></div>
        <!-- End Body Content -->
    </div>
</body>
</html>

2. Create the element you want to perform the navigation

<a id="backtotop" href="" class="back-to-top hidden" onclick="$('html, body').animate({'scrollTop': $('#top').offset().top}, 500);"><span class="icon-arrow-up"></span>Back to Top</a>

3. Add some CSS

.back-to-top{background-color:blue;bottom:64px;box-sizing:border-box;color:#fff;display:block;height:80px;line-height:1.4em;padding:10px 19px;position:fixed;right:40px;text-align:center;width:77px}
.back-to-top:hover{background-color:green}
.back-to-top [class^="icon-"]{display:block;position:relative;top:-1px}
.back-to-top .icon-arrow-up:before{font-weight:bold;font-size:1.5em}

4. Finally, add come code to make it appear when required. This should go in the 'head' of the html.

    <script>
        var isVisible = false;
        $(window).scroll(function () {
            var shouldBeVisible = $(window).scrollTop() > 300;
            if (shouldBeVisible && !isVisible) {
                isVisible = true;
                $('#backtotop').show();
            } else if (isVisible && !shouldBeVisible) {
                isVisible = false;
                $('#backtotop').hide();
            }
        });
    </script>

You are now good to go.The element can be placed in a any page/module. It will appear when the user has scrolled down the page and will return them to the top.

Thursday, 1 September 2016

SharePoint: Cannot file "Style%2520Library/Js/myfile.js"

I recent SharePoint deployment raised the interesting error about a missing file. When I checked the expected folder, the file was there so the error seemed a little strange.

The solution is in the message:
The missing file is Style%2520Library.

The cause was an incorrect link reference in the masterpage. The link was set to
<SharePoint:ScriptLink Name="~SiteCollection/Style%20Library/JS/myfile.js" runat="server" ID="ScriptLink2" />

SharePoint is url encoding the value. The encoded value of '%' is '%25', which results in the strange url.

I removed the %20 from the link address and the issue was resolved.

Wednesday, 31 August 2016

HTML5: How do I store values in the session (and why is $cookie complaining about space).

It appears that there is a size limitation for angular-cookies - 4KB. Bummer.

An alternative is to use the HTML 5 objects window.localStorage or window.sessionStorage. See here for details

I used sessionStorage to hold a single object and an object array from a REST endpoint.

The code to get and set the values is as follows:

First, set the values:
sessionStorage.Object1 = JSON.stringify(singleObject);
sessionStorage.Object2 = JSON.stringify(multipleObjects)

Now populate objects with the cached values:
var single = JSON.parse($.parseJSON(sessionStorage.Object1))
var array = $.parseJSON(sessionStorage.Object2)

C#: Pass multiple values to a REST endpoint without having multiple parameters

Instead of passing the parameters to /REST.svc?param1=a&param2=b

var items = new NameValueCollection();
items.Add("param1", "a");
items.Add("param2", "b");

var content = HttpPost("http://MyRestEndpoint", items);

public static string HttpPost(string uri, NameValueCollection pairs)
        {
            byte[] response = null;
            using (WebClient client = new WebClient())
            {
                response = client.UploadValues(uri, pairs);
            }
            return System.Text.Encoding.UTF8.GetString(response);
        }

NOTE: You can quite easily inject a proxy using the following code:
client.Proxy = new System.Net.WebProxy("myProxy", 1001);

Monday, 22 August 2016

AngularJS: Creating headers in an ng-repeat

I came across this great directive here to create headers based on sections in an ng-repeat. The cides works like a charm.

Here is a copy.

// html
<div ng-repeat="item in data.items | orderBy:... | filter:... as filteredItems" header-list objects="filteredItems" header="getHeader">
    <div ng-show="item.$header">{{item.$header}}</div>
    <div>Your normal content</div>
 </div>

// controller function
$scope.getHeader = function(item) {
  return item.name.slice(0, 1);
}

// directive
app.module("...").directive('headerList', function () {
  return {
    restrict: 'A',
    scope: {
      objects: '=',
      header: '=' // This must be a function or instance method
    },
    link: function(scope) {
      scope.$watch('objects', function() {

        var lastHeader, currentHeader;

        scope.objects.forEach(function (obj) {
          // We pass obj twice, so it can be used is instance method or regular function that takes the object as argument.
          currentHeader = scope.header.call(obj, obj);

          // in order to display a header per type, we mark the first event of each kind as header
          if (currentHeader !== lastHeader) {
            obj.$header = currentHeader;
            lastHeader = currentHeader;
          } else {
            obj.$header = null;
          }
        });

      });
    }
  }
});

c#: How can I read / parse a JSON file?

I was tasked with iterating through the JSON results generated by a REST API. I looked at several options such as

dynamic array = JsonConvert.DeserializeObject(jsondataasstring)

but I finally ended on using NewtonSoft.JSON to parse the data. The code is as follows:

// get the json data. in my case, I am reading it from a sample file from my MVC app
using (StreamReader reader = new StreamReader(HttpContext.Current.Server.MapPath("~/App_Data/sample.json")))
{
        json = reader.ReadToEnd();
}

JObject jObj = JObject.Parse(json);

// navigate to the area in the object tree that you require

var messages = jObj["response"]["data"]["messages"];

// I now have a messages array that I can iterate through
foreach(var item in messages)
{
  // Write out the property 'title'
  Console.WriteLine(string.Format("The title is {0}", (string)item["title"]));
}

Tuesday, 9 August 2016

Javascript: Why are my FileReader and JSON objects not defined in IE11?

I recently encountered this problem when referencing FileSaver.js in my project. The code worked in all browsers except Internet Explorer 11.

This post in StackOverflow helped me resolve the problem.

The problem was caused by a meta tag forcing the browser to behave like IE 8.

<meta http-equiv="X-UA-Compatible" content="IE=8"/>

This is an issue as my HTML5 reference is now broken.

A simple change resolved the problem.

<meta http-equiv="X-UA-Compatible" content="IE=edge"/>

Wednesday, 3 August 2016

How do I add additional logic to a input (buttons) click event?

Sometimes you need to add additional logic to a existing buttons 'click' event. The code to add extra logic to the item (without overwriting the existing code) is as follows:

1. Store the elements click event in a variable

var clickEvent = $("input[id='MyButtonId']").click

2. Overwrite the existing 'click' function, but execute the saved clicked function.

$("input[id='MyButtonId']").on('click', function() {  eval(clickEvent); MyNewSaveMethod();} );

Tuesday, 2 August 2016

API: How do I pass parameters to a REST API without including them in the QueryString? (Overcome penetration testing issues before they occur)

A recent penetration test of some API code showed that user information (such as an email address) was being displayed in the querystring.

The API was:

[HttpPost]
[Route("api/MyAPIEndPoint")]
public void DoSomething(string login, string email) { // stuff }

A simple view of the endpoint with a browser would display the users details.
The network tab in Chrome showed "api/MyAPIEndPoint?login=X12345&email=test@test.com".

A new approach was required.
The solution is to include the data in the body of the Request.

The factory event is updated as follows:

function DoSomething(login, email, success, failure) {
var url = "/api/MyAPIEndPoint";

var parameter =
{
"Login": login,
                "Email": email
};

return executePost(url, parameter, success, failure);
}

function executePost(url, parameters, success, failure) {
var req = {
method: 'POST',
url: url,
data: parameters
};

$http(req).then(function (data) {
return success(data);
},
function (err) {
return failure(err);
}
);
}

And I added the following code to the endpoint to capture the parameters:

1. Declare a class to hold the data
private class Parameters()
{
public string Login {get;set;}
public string Email {get;set;}
}

2. Deserialise the JSON object from the request

HttpContent requestContent = Request.Content;
string jsonContent = requestContent.ReadAsStringAsync().Result;
Parameters userParams = JsonConvert.DeserializeObject<Parameters>(jsonContent);

I now have the parameters in the userParams class. 



Monday, 1 August 2016

SharePoint Online: How do I run a Javascript update with a lookup on a NewForm.aspx or EditForm.aspx?

The requirement I faced with my SharePoint online project was to update fields on the new and edit forms once an item had been selected. The main problem was that the update required a lookup to existing data for each item in a multi select box.

The obvious solution was to use PreSaveAction in a Script Editor webpart,
The script looked something like this:

<script type="text/javascript" src="//code.jquery.com/jquery-1.11.2.min.js"></script>         
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery.SPServices/2014.02/jquery.SPServices.min.js"></script>
<script type="text/javascript">
var $j = jQuery.noConflict(); 
function PreSaveAction() { 
function SetValue(onComplete)
{
// clear the target item
$("textarea[id*='MyTargetField']").val('');
var listTitle = 'MyLookupList';
var ids = [];
                // get the items from the selection result
$("select[id*='_SelectResult']").children().each(function() {ids.push($(this).val())});

var ctx = SP.ClientContext.get_current();
var web = ctx.get_web();
var list = web.get_lists().getByTitle(listTitle);
var result = []; //for storing items 
ids.forEach(function(id){
var item = list.getItemById(id);
ctx.load(item, ['Title', 'Id', 'MyLookupColumn']);

result.push(item);
});

ctx.executeQueryAsync(
function() { 
                        // update the text area with the required field (comma seperated)
result.forEach(function(x){
$("textarea[id*='MyTargetField']").val($("textarea[id*='MyTargetField']").val() + x.get_item('MyLookupColumn') + ','); 
})
                        // call a method so that we can return True and save the data (THIS DID NOT WORK)
onComplete();
 },
 function(sender,args){console.log(args.get_message());} 
); 
}

SetValue(
function()
{
                        // remove the trailing comma  
$("textarea[id*='MyTargetField']").val($("textarea[id*='MyTargetField']").val().substring(0, $("textarea[id*='MyTargetField']").val().length - 1));
return true;
}
)
}

</script>      

The corresponding field was updated (yay!), but since the method did not return 'True', no data was persisted (bummer!).

A new approach was required. My solution is not optimal, but functional.
I created a listener to the DOMNodeInserted and DOMNodeRemoved events and run my update script.

$('#s4-workspace').bind('DOMNodeInserted DOMNodeRemoved', function() {
UpdateCodes();
});

My final code looked as follows:

<script type="text/javascript" src="//code.jquery.com/jquery-1.11.2.min.js"></script>         
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery.SPServices/2014.02/jquery.SPServices.min.js"></script>

<script type="text/javascript">
function UpdateCodes()
{
var listTitle = 'MyLookupList';
var ids = [];
$("select[id*='_SelectResult']").children().each(function() {ids.push($(this).val())});
var ctx = SP.ClientContext.get_current();
var web = ctx.get_web();
var list = web.get_lists().getByTitle(listTitle);
var result = []; //for storing items 
ids.forEach(function(id){
var item = list.getItemById(id);
ctx.load(item, ['Title', 'Id', 'MyLookupColumn']);
result.push(item);
});

ctx.executeQueryAsync(
function() { 
$("textarea[id*='MyTargetField']").val('');

result.forEach(function(x){
$("textarea[id*='MyTargetField']").val($("textarea[id*='MyTargetField']").val() + x.get_item('MyLookupColumn') + ','); 
})
$("textarea[id*='MyTargetField']").val($("textarea[id*='MyTargetField']").val().substring(0, $("textarea[id*='MyTargetField']").val().length - 1));
},
function(sender,args){console.log(args.get_message());} 
); 
}

$('#s4-workspace').bind('DOMNodeInserted DOMNodeRemoved', function() {
UpdateCodes();
});

</script>      


Tuesday, 12 July 2016

Angular: Redirecting to another location with query strings

There are a couple of ways to redirect to a new location:

window.location.href / $window.location.href

However, when using routing ($routeProvider), $location is the way to go.

To navigate to another route:

$location.path("/Search");
$location.replace();

To navigate to another route using query strings (such as ?k=test)

$location.path("/Search").search('k', 'test');

To extract the query values, use 

$location.search().k;

Tuesday, 28 June 2016

SharePoint: Urls in the virtual directories

While looking for the virtual location of the list of SharePoint masterpages (which is /_catalogs/masterpage/Forms/AllItems.aspx), I found this blog on MSDN outlining other useful folders. There post is replicated here.

Site collection level recycle bin:
/_layouts/15/AdminRecycleBin.aspx

Site level recycle bin:
/_layouts/RecycleBin.aspx

Recreate default site sp groups:
_layouts/15/permsetup.aspx

Load document tab initial:
?InitialTabId=Ribbon.Document

Delete user from Site collection (on-premises):
/_layouts/15/people.aspx?MembershipGroupId=0

Display list in grid view. ‘True’ is case sensitive:
?ShowInGrid=True

Quick Launch settings page:
/_layouts/quiklnch.aspx

Navigation Settings page:
/_layouts/15/AreaNavigationSettings.aspx

Sandboxed Solution Gallery:
/_catalogs/solutions/Forms/AllItems.aspx

Workflow history hidden list:
/lists/Workflow History

Filter toolbar for Lists and libraries:
?Filter=1

Site usage page:
/_layouts/usage.aspx

Site content and structure  page:
/_layouts/sitemanager.aspx

Site settings page:
/_layouts/settings.aspx

View all site content page:
/_layouts/viewlsts.aspx

Manage site collection features 
/_layouts/ManageFeatures.aspx?Scope=Site

Manage site features:
/_layouts/ManageFeatures.aspx

 Get the version of the SharePoint server (Patch level):
 /_vti_pvt/Service.cnf

Web Part Maintenance Page:
?Contents=1

Show Page in Dialog View:
?isdlg=1

Application page for registering SharePoint apps:
/_layouts/15/appregnew.aspx

Save Site as a template:
/_layouts/savetmpl.aspx

Sign in as a different user:
/_layouts/closeConnection.aspx?loginasanotheruser=true

Enable SharePoint designer:
/_layouts/SharePointDesignerSettings.aspx

Welcome Page (Default page settings):
/_layouts/AreaWelcomePage.aspx

Change Site Master Page:
/_layouts/ChangeSiteMasterPage.aspx

Page Layouts and Site Templates:
/_Layouts/AreaTemplateSettings.aspx

Master Pages library:
/_catalogs/masterpage/Forms/AllItems.aspx

Quick Deploy List:
Quick%20Deploy%20Items/AllItems.aspx

Open Page in Edit Mode:
?ToolPaneView=2

Taxonomy Hidden List (MMS):
Lists/TaxonomyHiddenList/AllItems.aspx

User Information List:
 _catalogs/users
_catalogs/users/simple.aspx

Force displaying the user profile in the site collection:
/_layouts/userdisp.aspx?id={UserID}&Force=True

Site hierarchy page (lists of sub sites)
/_layouts/vsubwebs.aspx
/_layouts/1033/vsubwebs.aspx

Monday, 30 May 2016

AngularJS: How do I create cascading lists?

I was looking for a cascading list solution and I found one here that almost gave me what I needed. I added a few tweaks to get my desired result.

UPDATE: Be very careful with the items that are used in the filter. The filter is a string based comparison, so performing a filter on 'ID=1' will also return items for ID=10,ID=11 etc.

My solution was to create two new properties: filterId and filterParentId. These are set to '<placeholder>ID<placeholder>'. For example, ###1###. You can then filter

So, instead of searching for 1, you could search for ##1##, which would remove the unwanted results.

My code is as follows:

html:
            <select class="form-control"
                    ng-model="selectedParentItem"
                    ng-change="parentchanged()"
                    ng-options="p.displayName for p in parentItems"></select>

            <select class="form-control"
                    ng-model="selectedChildItem"
                    ng-disabled="!selectedParentItem"
                    ng-options="c.displayName for c in childItems | filter:{filterParentId: selectedParentItem.filterId}"></select>

            <select class="form-control"
                    ng-model="selectedGrandChildItem"
                    ng-disabled="!selectedChildItem"
                    ng-options="g.displayName for g in grandChildItems | filter:{filterParentId: selectedChildItem.filterParentId}"></select>

js:
    $scope.parentchanged = function ()
    {
        $scope.selectedGrandChildItem = undefined;
    }

    $scope.parentItems = [
        {
            "id": 0,
            "displayName": "parent 00",
            "filterId": ##0##
        },
        {
            "id": 1,
            "displayName": "parent 01",
            "filterId": ##1##
        },
        {
            "id": 2,
            "displayName": "parent 02"
            "filterId": ##2##
        }
    ];

    $scope.childItems = [
        {
            "id": 0,
            "displayName": "child0 of 00",
            "parentId": 0,
            "filterParentId": ##0##
        },
        {
            "id": 1,
            "displayName": "child1 of 00",
            "parentId": 0,
            "filterParentId": ##0##
        },
        {
            "id": 2,
            "displayName": "child2 of 00",
            "parentId": 0,
            "filterParentId": ##0##
        },
        {
            "id": 3,
            "displayName": "child0 of 01",
            "parentId": 1,
            "filterParentId": ##1##
        },
        {
            "id": 4,
            "displayName": "child1 of 01",
            "parentId": 1,
            "filterParentId": ##1##
        },
        {
            "id": 5,
            "displayName": "child0 of 02",
            "parentId": 2,
            "filterParentId": ##2##
        }
    ];

    $scope.grandChildItems = [
    {
        "id": 0,
        "displayName": "grandChild0 of 00",
        "parentId": 0,
         "filterParentId": ##0##
    },
    {
        "id": 1,
        "displayName": "grandChild1 of 00",
        "parentId": 0,
         "filterParentId": ##0##
    },
    {
        "id": 2,
        "displayName": "grandChild2 of 00",
        "parentId": 0,
         "filterParentId": ##0##
    },
    {
        "id": 3,
        "displayName": "grandChild0 of 01",
        "parentId": 1,
         "filterParentId": ##1##
    },
    {
        "id": 4,
        "displayName": "grandChild1 of 01",
        "parentId": 1,
         "filterParentId": ##1##
    },
    {
        "id": 5,
        "displayName": "grandChild0 of 02",
        "parentId": 2,
         "filterParentId": ##2##
    }
    ];


SharePoint Search: How do I limit a search query to a specific site collection?

The url needs to be as follows (as per this entry):

http://localhost/_api/search/query?querytext='test+path:"http://localhost/subsite/"'

The C# code the create the querystring is as follows:

string searchRestUrl = "/_api/search/query?querytext='" + text + "+%2b+path:\"" + HttpUtility.UrlEncode(SiteUrl) + "\"'&rowlimit=5";

where SiteUrl is the full url for my site.

Wednesday, 25 May 2016

Web API: Why am a getting a '500' error when accessing my REST endpoints for javascript?

For me, the solution was found by doing the following:

1. Add a 'Route' annotation above the endpoints

   [Authorize]
    public class MyAPIController : ApiController
    {
        [HttpPost]
        [Route("api/MyAPI/MyPostEndPoint")]
        public void MyPostEndPoint()
       {
       }

       [HttpGet]
       [Route("api/MyAPI/MyGetEndPoint")]
       public void MyGetEndPoint()
       {
       }
    }

2. When calling the endpoints from the js factory, my POST call we different to my GET command:

function executeGet(url, success, failure) {
            $http.get(url, {
                headers: { 'Authorization': 'Blah' }
            }).then(
            function (data) {
                return success(data);
            }, function (err) {
                return failure(err);
            });
        }

        function executePost(url, success, failure) {
            var req = {
                method: 'POST',
                url: url,
                headers: {
                    'Authorization': 'Blah']
                },
                data: { test: 'test' }
            };

            $http(req).then(function(data){
                return success(data);
                }, 
                function (err) {
                    return failure(err);
                }
            );
        }