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>