Skip to content

Welcome

Welcome to my blog about the software tools / techniques I have used.

The first posts, were originally hosted in a variety of places,  &  I thought it would be nice to bring them all together.   Its mainly a journal of  some of the things I have worked on, that might be useful to me in the future.  If it helps anyone else, then great.

I have been developing Web Applications with Domino / Lotus Notes since the 90s, mainly in areas where workflow & security are important. Before that I worked on financial systems with Foxpro, Oracle & PL/1.

In parallel with Domino, recently I have used Java , PHP, C# .NET, Sharepoint, JQuery , HTML5 and a bit of  Ruby On Rails.

Security is a bit of a hobby horse of mine – so its bound to crop up once or twice.  If you’ve found this site, please do check out OWASP  The Open Web Application Security Project – it has some great FREE stuff that helps build more secure applications.

Richard in Warwick.

Use Angular to dynamically filter an external json list and format it with an Angular filter or css

The code below combines two ‘try it now’ exercises  from the W3Schools.com course on Angular js to do the following :

1) Read a json list of customers & their countries from an external url:

http://www.w3schools.com//website/Customers_JSON.php  ( The data returned from this is shown at  the end of this text )

2) Filter the list in real time to show only rows containing the text entered in the Input_Text_Filter field.

3) Format the list in two contradictory ways ( for demonstration purposes), to either :

 3.1) Make the first letter of the first word uppercase, & then everything else is lower case – using the Angular app, custom filter called ‘ucf‘.

or

3.2) Make the first letter of EVERY word upper case : using a css style definition, with a class called ‘capitalize’ assigned to it.  The style definition for li.capitalize  formats the text so that every word in that element starts with a capital.

Both functions are run, to allow you to delete the unnecessary, The two approaches conflict, but capitalize wins out in this case.  In the real world use ONE approach,  not both:


<!DOCTYPE html>

<html>
<head>

<style>
li.capitalize {
text-transform: capitalize;
}
</style>

</head>

<body>

<div ng-app=”App” ng-controller=”customersController“>
<p>Filtering input:</p>
<p><input type=”text” ng-model=”Input_Text_Filter “></p>
<ul>
  <li ng-repeat=”x in names | filter:Input_Text_Filter | orderBy:’country‘” class=”capitalize”>
    {{ (x.Name |ucf) + ‘, ‘ + x.Country }}
  </li>
</ul>
</div>
<script>
var app = angular.module(‘App’,[]);
function customersController($scope,$http) {
  .success(function(response) {$scope.names = response;});
}
app.filter(‘ucf’, function()
{
    return function(word)
    {
        return word.substring(0,1).toUpperCase() + word.slice(1).toLowerCase();
    }
});
</script>
</body>

</html>


Json Data returned at time of writing from http://www.w3schools.com//website/Customers_JSON.php

[ { "Name" : "Alfreds Futterkiste", "City" : "Berlin", "Country" : "Germany" }, { "Name" : "Berglunds snabbköp", "City" : "Luleå", "Country" : "Sweden" }, { "Name" : "Centro comercial Moctezuma", "City" : "México D.F.", "Country" : "Mexico" }, { "Name" : "Ernst Handel", "City" : "Graz", "Country" : "Austria" }, { "Name" : "FISSA Fabrica Inter. Salchichas S.A.", "City" : "Madrid", "Country" : "Spain" }, { "Name" : "Galería del gastrónomo", "City" : "Barcelona", "Country" : "Spain" }, { "Name" : "Island Trading", "City" : "Cowes", "Country" : "UK" }, { "Name" : "Königlich Essen", "City" : "Brandenburg", "Country" : "Germany" }, { "Name" : "Laughing Bacchus Wine Cellars", "City" : "Vancouver", "Country" : "Canada" }, { "Name" : "Magazzini Alimentari Riuniti", "City" : "Bergamo", "Country" : "Italy" }, { "Name" : "North/South", "City" : "London", "Country" : "UK" }, { "Name" : "Paris spécialités", "City" : "Paris", "Country" : "France" }, { "Name" : "Rattlesnake Canyon Grocery", "City" : "Albuquerque", "Country" : "USA" }, { "Name" : "Simons bistro", "City" : "København", "Country" : "Denmark" }, { "Name" : "The Big Cheese", "City" : "Portland", "Country" : "USA" }, { "Name" : "Vaffeljernet", "City" : "Århus", "Country" : "Denmark" }, { "Name" : "Wolski Zajazd", "City" : "Warszawa", "Country" : "Poland" } ]

NUnit validation of a web service at “http://localhost:1234/api/values”;

NUnit validation of a web service at “http://localhost:1234/api/values&#8221;;

UnitTest.cs :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MVCWebApi.Controllers;
using NUnit.Framework;
using System.Web.Http;
using System.Net.Http;
using MVCWebApi.Models;
using System.Configuration;
using System.Net;
using System.Net.Http.Headers;
using Newtonsoft.Json;
namespace MVCWebApi.Controllers.Tests
{
[TestFixture()]
public class UnitTest1
{

private string UrlBase = “http://localhost:49493/&#8221;;
private string servicePath = “api/values”;
private HttpClient client;
private HttpResponseMessage response;
private string jsonData;
private List<Person> list;

[SetUp]
public async void SetUP()
{

client = new HttpClient();
client.BaseAddress = new Uri(UrlBase);

// get service header data into a queryable form

response = client.GetAsync(servicePath).Result;
// assuming service works load json into a string
jsonData = client.GetStringAsync(servicePath).Result;

// Parse json into a custom c# class – in this case, Person
list = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Person>>(jsonData);
}
// Test for OK status code

[Test]
public void GetResponseIsSuccess()
{
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
// Test for returned data is JSON

[Test]
public void GetResponseIsJson()
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(“application/json”));

Assert.AreEqual(“application/json”, response.Content.Headers.ContentType.MediaType);
}

// Test for authentication

[Test]
public void GetAuthenticationStatus()
{
Assert.AreNotEqual(HttpStatusCode.Unauthorized, response.StatusCode);

}
// Test parsed ( people) object contains data from the json

[Test]
public void jsonIsvalid()
{
Assert.GreaterOrEqual(list.Count, 0);

}

// Test parsed ( people) object contains a value for specific name

[Test]
public void jsonIsvalid2()
{
bool found = false;
foreach (Person people in list)
{
if (people.Name == “sankar parida”)
{ found = true; }
}

Assert.IsTrue(found);
}

// Test parsed ( people) object contains another specific name

[Test]
public void jsonIsvalid3()
{
bool found = false;
foreach (Person people in list)
{
if (people.Address == “cuttack”)
{ found = true; }
}

Assert.IsTrue(found);
}
}
}

Where MVCWebApi looks like

API / ValuesController

using MVCWebApi.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;

namespace MVCWebApi.Controllers
{
public class ValuesController : ApiController
{
PersonEntities db = new PersonEntities();
// GET api/values
public IEnumerable<Person> Get()
{
return db.Persons.ToList();
//return new string[] { “value1″, “value2″ };
}
// GET api/values/5
public Person Get(int id)
{
return db.Persons.Find(id);
//return “value”;
}
// POST api/values
public void Post(List<string> val)//(Person obj )
{
try
{
Person obj = new Person();
obj.Name = val[0];
obj.Address = val[1];
obj.DOB = Convert.ToDateTime(val[2]);
db.Persons.Add(obj);
db.SaveChanges();
}
catch (Exception) { }
}
// PUT api/values/5
public void Put(int id, Person obj)//(int id, [FromBody]string value)
{
try
{
db.Entry(obj).State = System.Data.EntityState.Modified;
db.SaveChanges();
}
catch (Exception) { }
}
//This method for update through Json
public void Put(List<string> val)
{
try
{
int id = Convert.ToInt32(val[3]);
Person obj = db.Persons.Find(id);
obj.Name = val[0];
obj.Address = val[1];
obj.DOB = Convert.ToDateTime(val[2]);
db.Persons.Add(obj);
db.Entry(obj).State = System.Data.EntityState.Modified;
db.SaveChanges();
}
catch (Exception) { }
}
// DELETE api/values/5
public void Delete(int id)
{
Person obj = db.Persons.Find(id);
db.Persons.Remove(obj);
db.SaveChanges();
}
}
}

Controllers / PersonController is

using MVCWebApi.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MVCWebApi.Controllers
{
public class PersonController : Controller
{
ValuesController db = new ValuesController();
public ActionResult Index()
{
var list = db.Get(); //Getting List of records from web API
return View(list);
}
public ActionResult Index2()
{
return View();
}
public ActionResult Details(int id)
{
Person person = db.Get(id);
return View(person);
}

public ActionResult Create()
{
return View();
}
[HttpPost]
public ActionResult Create(Person obj)
{
try
{
List<string> person = new List<string>();
person.Add(obj.Name);
person.Add(obj.Address);
person.Add(obj.DOB.ToString());
db.Post(person);
return RedirectToAction(“Index”);
}
catch
{
return View();
}
}

public ActionResult Edit(int id)
{
Person person = db.Get(id);
return View(person);
}
[HttpPost]// POST: /Person/Edit/5
public ActionResult Edit(int id, Person obj)
{
try
{
db.Put(id, obj);
return RedirectToAction(“Index”);
}
catch
{
return View();
}
}

// GET: /Person/Delete/5
public ActionResult Delete(int id)
{
Person person = db.Get(id);
return View(person);
}
[HttpPost]// POST: /Person/Delete/5
public ActionResult Delete(int id, FormCollection collection)
{
try
{
db.Delete(id);
return RedirectToAction(“Index”);
}
catch
{
return View();
}
}
}
}

Person.cs

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;

namespace MVCWebApi.Models
{
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public DateTime DOB { get; set; }
}
public class PersonEntities : DbContext
{
public DbSet<Person> Persons { get; set; }
}
}

TSQL Syntax to get useful data from Sharepoint usage logs

SELECT DocumentPath
,Replace(Replace(Reverse(LEFT(REVERSE([DocumentPath]),Charindex(‘/’,REVERSE([DocumentPath])))),’/’,”),’%20′,’ ‘) As DocName
, logtime
, REPLACE(UserLogin,’myDomain\’,”) as UserId
, Convert(date,LogTime) as DateLogged
, CONVERT(time(0),logtime) as TimeLogged
, ReferrerUrl
, SubString(DocumentPath+’/’, (CHARINDEX(‘/’, DocumentPath+’/’, 0) + 1)
, (CharIndex(‘/’, RIGHT(DocumentPath+’/’, (LEN(DocumentPath+’/’) – (CharIndex(‘/’, DocumentPath+’/’, 0)))), 0) – 1)) As TeamSite
,Replace(item,’%20′,’ ‘) as DocumentSet
FROM [SharepointDomain].[dbo].[RequestUsage]
CROSS APPLY dbo.DelimitedSplit8K(DocumentPath,’/’) split
 WHERE DocumentPath like ‘/ascs/%’
  And DocumentPath not like ‘%dll’
  And DocumentPath not like ‘%asmx’
  And DocumentPath not like ‘%_vti_bin%’
  And DocumentPath not like ‘%_layouts%’
This uses the following features to get levels out of the URL
SubString(DocumentPath+’/’, (CHARINDEX(‘/’, DocumentPath+’/’, 0) + 1)
, (CharIndex(‘/’, RIGHT(DocumentPath+’/’, (LEN(DocumentPath+’/’) – (CharIndex(‘/’, DocumentPath+’/’, 0)))), 0) – 1)) As TeamSite
Jeff Modens DelimitedSplit8k to parse out the second level of the url
Replace(Replace(Reverse(LEFT(REVERSE([DocumentPath]),Charindex(‘/’,REVERSE([DocumentPath])))),’/’,”),’%20′,’ ‘) As DocName

DROP FUNCTION dbo.DelimitedSplit8K
GO
 CREATE FUNCTION dbo.Split8K
/***************************************************************************************************
 Purpose:
 Split a given string at a given delimiter and return a list of the split elements (items).

 Returns:
 iTVF containing the following:
 ItemNumber = Element position of Item as a BIGINT (not converted to INT to eliminate a CAST)
 Item       = Element value as a VARCHAR(8000)

 CROSS APPLY Usage Example:
---------------------------------------------------------------------------------------------------
--===== Conditionally drop the test tables to make reruns easier for testing.
     -- (this is NOT a part of the solution)
     IF OBJECT_ID('tempdb..#JBMTest') IS NOT NULL 
        DROP TABLE #JBMTest
;
--===== Create and populate a test table on the fly (this is NOT a part of the solution).
 SELECT *
   INTO #JBMTest
   FROM (
         SELECT 1,'1,10,100,1000,10000,100000,1000000' UNION ALL
         SELECT 2,'2000000,200000,20000,2000,200,20,2' UNION ALL
         SELECT 3, 'This,is,a,test'                    UNION ALL
         SELECT 4, 'and so is this'                    UNION ALL
         SELECT 5, 'This, too (no pun intended)'
        ) d (SomeID,SomeValue)
;
GO
--===== Split the CSV column for the whole table using CROSS APPLY (this is the solution)
 SELECT test.SomeID, split.ItemNumber, split.Item
   FROM #JBMTest test
  CROSS APPLY
        (
         SELECT ItemNumber, Item
           FROM dbo.DelimitedSplit8k(test.SomeValue,',')
        ) split
;
---------------------------------------------------------------------------------------------------
 Notes:
 1. Optimized for VARCHAR(7999) or less.  No testing or error reporting for truncation at 7999
    characters is done.
 2. Optimized for single character delimiter.  Multi-character delimiters should be resolved
    externally from this function.
 3. Optimized for use with CROSS APPLY.
 4. Does not "trim" elements just in case leading or trailing blanks are intended.
 5. If you don't know how a Tally table can be used to replace loops, please see the following...
    http://www.sqlservercentral.com/articles/T-SQL/62867/
 6. Changing this function to use VARCHAR(MAX) will cause it to run twice as slow.  It's just the 
    nature of VARCHAR(MAX) whether it fits in-row or not.  Some recovery of speed can be realized
    by doing datatype matching for CHARINDEX which changes it's datatype for VARCHAR(MAX).
 7. Multi-machine testing for the method of using UNPIVOT instead of 10 SELECT/UNION ALLs shows
    that the UNPIVOT method is quite machine dependent and can slow things down quite a bit.
 8. Performance testing shows using "TOP" for the limiting criteria of "N" is actually 
    slower and slightly more CPU intensive than the traditional WHERE N < LEN(@pString) + 2. 
 9. Performance testing shows using ORDER BY (SELECT x) where "x" is anything is actually
    slower and slightly more CPU intensive than the traditional ORDER BY (SELECT N).
***************************************************************************************************/
--===== Define I/O parameters
        (
        @pString    VARCHAR(7999),
        @pDelimiter CHAR(1)
        )
RETURNS TABLE
   WITH SCHEMABINDING
AS
RETURN
--===== "Inline" CTE Driven "Tally Table” produces values up to
     -- 10,000... enough to cover VARCHAR(8000)
WITH
      E1(N) AS ( --=== Create Ten 1's
                 SELECT 1 UNION ALL SELECT 1 UNION ALL
                 SELECT 1 UNION ALL SELECT 1 UNION ALL
                 SELECT 1 UNION ALL SELECT 1 UNION ALL
                 SELECT 1 UNION ALL SELECT 1 UNION ALL
                 SELECT 1 UNION ALL SELECT 1 --10
               ),
      E2(N) AS (SELECT 1 FROM E1 a, E1 b),   --100
      E4(N) AS (SELECT 1 FROM E2 a, E2 b),   --10,000
cteTally(N) AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT N)) FROM E4)  
--===== Do the split
 SELECT ROW_NUMBER() OVER (ORDER BY N) AS ItemNumber,
        SUBSTRING(@pString, N, CHARINDEX(@pDelimiter, @pString + @pDelimiter, N) - N) AS Item
   FROM cteTally
  WHERE N <= DATALENGTH(@pString)+1 --DATATLENGTH allows for trailing space delimiters
    AND SUBSTRING(@pDelimiter + @pString, N, 1) = @pDelimiter
;
GO

C# function for file upload to convert HttpPostedFileBase to byte array.

The commented code is here for comparison.   This had the drawback that it generated binary nulls after the end of file marker.  Some PDF files were corrupted by that code.

private byte[] readFileContents(HttpPostedFileBase file)
{
Stream fileStream = file.InputStream;
var mStreamer = new MemoryStream();
mStreamer.SetLength(fileStream.Length);
fileStream.Read(mStreamer.GetBuffer(), 0, (int)fileStream.Length);
mStreamer.Seek(0, SeekOrigin.Begin);
byte[] fileBytes = mStreamer.GetBuffer();

//////using (MemoryStream ms = new MemoryStream())
//////{
////// file.InputStream.CopyTo(ms);
////// fileBytes = ms.GetBuffer();
//////}

return fileBytes;
}

 

Typical use may be within MVC …..   EG

[HttpPost]
public ActionResult Create(myMetaDataClass collection, HttpPostedFileBase uploadfile)
{

// Other code

byte[] fileContents = null;
fileContents = readFileContents(uploadfile);

///other code to put the filecontents into form that can be posted to a web service defined by serviceurl

StringBuilder stringBuilder = new StringBuilder();

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(serviceurl);

StringBuilder stringBuilder = new StringBuilder();
// code to parse out myMetaDataClass collection into parameters for webservice

stringBuilder.Append(postParameterKeys.ElementAt(1) + “=” +
HttpUtility.UrlEncode(Convert.ToBase64String(fileContents)) + “&”);

//More string builder code
//

byte[] postData = Encoding.UTF8.GetBytes(stringBuilder.ToString());

request.ContentLength = postData.Length;
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(postData, 0, postData.Length);
}

using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
//etc…..

Dynamic JQGrid definition with Razor… & time sensitive toastr..

Build JavaScript Functions to format a url based on that row : This one has a bootstrap button in it, for a “Delete” function

 function fmatDelLnk(cellvalue, options, rowObject) {

var file_name = rowObject[6].toString();

var catref = rowObject[7].toString();

var ServiceURL = $(‘#ServiceURL’).val();

var link=”/Document/Del?identifier=” + $_GET["identifier"] + “&cat=” + catref + “&empid=” + $_GET["empid"] + “&filename=” +  file_name ;

var url = “<a href=’” + link + “‘ class=’btn btn-mini ‘ style=’color:white;font-weight:bold’>Delete</a>”;

return url

}

( The inline css is to fix a conflict between bootstrap & other css for hrefs on the page)

//Similar function but with text as a link.  The blank on the end of label is to make the entire cell clickable

   function fmatDLnk(cellvalue, options, rowObject) {

var  file_name  = rowObject[6].toString();

var label = rowObject[1].toString()+”                                  “;

var catref = rowObject[7].toString();

var WebServiceURL = $(‘#WebServiceURL’).val();

var urlLink = “<a href=/Document/Launch?identifier=” + $_GET["identifier"] + “&cat=” + catref + “&empid=” + $_GET["empid"] + “&filename=” +  file_name  + “>” + label+”</a>”;

return urlLink

}

//Similar function but for use in another column. Its label for the href would be different

function fmatELnk(cellvalue, options, rowObject) {

etc…….

}

JQuery to run when the form opens

//Controls whether or not to display a toastr alert from an update function.  This page is launched directly & is a redirect page after updates.  #LastDbUpdate is a form field populated from a session variable indicating when an update was successful

$(document).ready(function () {

var dstr = $(‘#LastDbUpdate’).val();

// max diff is the cutoff period in milliseconds  after which we ignore the update parm 

       var maxdiff = 10000;

var updateVal = $(‘#update’).val();

var diff = maxdiff + 90;

var currTime = new Date();

var LstUpdDte = new Date();

LstUpdDte.setDate(LstUpdDte.getDate() – 1);

// #LastDbUpdatestored on server as the last datetime this user updated the database. All components are seprated by “:” 

       if (dstr != “”)

{   var d  = dstr.split(“:”);

for (var i in d )

{

d[i] = parseInt(d [i], 10);

}

LstUpdDte = new Date(d[0], d[1]-1,d[2], d[3], d[4], d[5],0);

diff = currTime.getTime() – LstUpdDte.getTime();

var d_dte = LstUpdDte.toTimeString().substr(0, 5);

// if an update happened in the last 10000 milliseconds, then display the toastr message 

if ((updateVal == ‘true’) && ( diff<maxdiff))

{

toastr.options = {

//                  … assorted toastr display parms

}

toastr.success(“Database updated  ” + d_dte )

}

// Build dynamic array for jqgrid determining whether or not to add columns, & making the content reusable between different tables ( EG in a Tabbed interface)

$(function () {

var colMdl = [];

// Add each column definition one at a time to the array, the formatters are doclinks set by the above js functions. Not all the formatters have been included above

colMdl.push({ name: ‘id’, index: ‘id’, hidden: true });

colMdl.push({ name: ‘title’, index: ‘title’, width: 330, formatter: fmatDLnk });

colMdl.push({ name: ‘effectivedate’, index: ‘effectivedate’, width: 105, formatter: fmatELnk });

colMdl.push({ name: ‘assignmentid’, index: ‘assignmentid’, hidden: true });

colMdl.push({ name: ‘createdby’, index: ‘createdby’, width: 105, formatter: fmatCbyLink });

colMdl.push({ name: ‘createddate’, index: ‘createddate’, width: 105, formatter: fmatCDteLnk });

colMdl.push({ name: ‘linkfilename’, index: ‘linkfilename’, hidden: true });

colMdl.push({ name: ‘categoryreferencenumber’, index: ‘categoryreferencenumber’, hidden: true });

//Razor if test to determine whether or not add the “Delete” Button as a cell in the row 

       @if (User.IsInRole(“myCustomRole”)) {

<text>

colMdl.push({ name: ‘Delete’, width: 150, formatter: fmatDelLnk });

</text>

}

var colHdr = ['Id', 'Document Title', 'Effective Date', '', 'Created By', 'Created Date', 'Link Filename', 'Category Reference'];

//Razor if test to determine whether or not add the “header” column for a delete button

@if (User.IsInRole(“myCustomRole”)) {

<text>

colHdr.push(‘Actions’);

</text>

}

//Sample table to position code on page  

jQuery(“#cat1″).jqGrid({

url: ‘/DocsJSON/read?ident=’+$_GET["ident"] + ‘&empid=’ + $_GET["empid"] + ‘&cat=1′,

datatype: “json”,

colNames: colHdr,

colModel: colMdl,

rowNum: 10,pager: ‘#pagerrecruitment’,

sortname: ‘effectivedate’,viewrecords: true,

sortorder: “desc”,height: “350px”,border: “none”

});

jQuery(“#cat1″).jqGrid(‘navGrid’, ‘#pagerrecruitment’, { edit: false, add: false, del: false, search: false, refresh: false });

jQuery(“#cat2″).jqGrid({

url: ‘/DocsJSON/read?ident=’+$_GET["ident"] + ‘&empid=’ + $_GET["empid"] + ‘&cat=2′,

datatype: “json”,

colNames: colHdr,

colModel: colMdl,

rowNum: 10,pager: ‘#pagerclearances’,

sortname: ‘effectivedate’, viewrecords: true,

sortorder: “desc”,height: “350px”, border: “none”

});

jQuery(“#cat2″).jqGrid(‘navGrid’, ‘#pagerclearances’, { edit: false, add: false, del: false, search: false, refresh: false });

etc…

Jquery, twitter, bootstrap & MVC form layout

Bootstrap is framework of css classes that simplify web form design. These are some of the conventions I found useful & how I got round the gotchas…
Required js libraries: 

<script src=”@Url.Content(“~/Scripts/jquery-1.5.1.min.js”)” type=”text/javascript”></script>
<script src=”@Url.Content(“~/Scripts/jquery.validate.js”)” type=”text/javascript”></script>
<script src=”@Url.Content(“~/Scripts/jquery.validate.unobtrusive.js”)” type=”text/javascript”></script>
<script src=”@Url.Content(“~/Scripts/modernizr-1.7.min.js”)” type=”text/javascript”></script>
<link rel=”stylesheet” href=”http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css”&gt;
<script src=”../../Scripts/bootstrap.file-input.js” type=”text/javascript”></script>
<script src=”../../Scripts/modernizr-2.5.3.js” type=”text/javascript”></script>

& associated css

Textfields

<div>
       <label>
           @Html.LabelFor(model => model.sourceURL)</label>
       <div>
           : @Html.TextBoxFor(model => model.sourceURL, new { @class = “form-control”,placeholder =”First field name” })
           @Html.ValidationMessageFor(model => model.sourceURL)
       </div>
   </div>
Built using .net razor, relying on the MVC model for validation, labeling etc,this makes it easier to cascade a description or validation through the application. .
The fieldlabel sits in a class “3” wide The field sits in a class 8 wide.     ( There is a sidebar to of width 1 to make up the 12 units bootstrapper is based on. The formcontrol class, left aligns the field. ‘placeholder’ places text in the field until it is clicked on ( Not IE 8 or )
Integer fields
       <div>
           : @Html.TextBoxFor(model => model.anInteger, new { @class = “text-right”, type=”number”,placeholder =”0″})
           @Html.ValidationMessageFor(model => model.anInteger)
       </div>
also set in col-xs-8 class @class = “text-right”, aligns the field to the right to display like a number . type=”number” places a spinner

Date fields

The HTML or razor code is set up like text .  ( even though the model is defined as a datetime) A dynamic calendar picker is created with jqueryui:
<script>
$(function () {
//Bootstrap can interfere with this, but noConflict()clears this
$.noConflict();
// When a field( eg #fromDate) is clicked in, show calendar
// When a value is picked, set a limit on a related date         
$(“#fromDate”).datepicker({dateFormat: “dd/mm/yy”,
onClose: function (sel_Dte)
{     $(“#toDate”).datepicker(“option”, “minDate”, sel_Dte);     }
});
       
$(“#toDate”).datepicker({dateFormat: “dd/mm/yy”,
onClose: function (sel_Dte)
{     $(“#fromDate”).datepicker(“option”, “maxDate”, sel_Dte);     }
     });
   });</script>

File picker

By default File pickers display differently in each browser.  This is a pain.  This website suggested hiding the file picker, & then showing a button to invoke the hidden filepickers click event & then showing its results in a field.    There were a few cross browser issues, but these were addressed as follows :
1) Place hidden file input at end of form , with an onchange event calling a javascript function myFunction
<input type=”file” name=”file” style=”visibility: hidden” id=”pdffile” onchange=”myFunction(this,’#subfile’);” />
2) Place the visible elements in a control group . These consist of a button , whose purpose is to invoke the hidden file input’s click event, & the field “subfile” to show the results:
<div>  
       <label>
           Choose A File
       </label>
       <div>
           : <input type=”text” id=”subfile”>
           <a onclick=”$(‘#pdffile’).click();”>Browse</a>
       </div>
   </div>
3) Add the javascript for myFunction()
   function myFunction(source, target) {
\\ take in a field object & the name of a target
var x = $(source).val();
\\get the value of the field from source, convert it to an array.
\\ get out the last element
       var z = x.split(“\\”).pop();
\\ set the target value
       $(target).val(z);
   }
The sequence runs as follows:
  1. User clicks visible button .
  2. Visible button invokes pdffiles.click() event
  3. This displays a file picker, whose results are returned to ‘pdffile’
  4. pdffile’s onchange event is triggered, passing itself & the name of a target id, to the function myFunction()
  5. This gets the filename element from this input field ( it could include, a filename only , the  true path or a false path)  & updates the field accordingly
@model BootStrapApp.Models.Document
@{
   ViewBag.Title = “Create”;
   Layout = “~/Views/Shared/_Layout.cshtml”;
}
<h2>Create</h2>
<script>
   function myFunction(source, target) {
       var x = $(source).val();
       var z = x.split(“\\”).pop();
       $(target).val(z);
   }
   
   $(function () {
       $.noConflict();
       $(“#fromDate”).datepicker({
           dateFormat: “dd/mm/yy”,
           onClose: function (selDte) {
               $(“#toDate”).datepicker(“option”, “minDate”, selDte);
           }
       });
       
       $(“#toDate”).datepicker({
           dateFormat: “dd/mm/yy”,
           onClose: function (selDte) {
               $(“#fromDate”).datepicker(“option”, “maxDate”, selDte);
           }
       });
   });
</script>
<form>
<fieldset>
   <legend>Document</legend>
   <div>
       <label>
           @Html.LabelFor(model => model.sourceURL)</label>
       <div>
           : @Html.TextBoxFor(model => model.sourceURL, new { @class = “form-control”,placeholder =”First field name” })
           @Html.ValidationMessageFor(model => model.sourceURL)
       </div>
   </div>
   <div>
       <label>
           @Html.LabelFor(model => model.anInteger)</label>
       <div>
           : @Html.TextBoxFor(model => model.anInteger, new { @class = “text-right”, type=”number”,placeholder =”0″})
           @Html.ValidationMessageFor(model => model.anInteger)
       </div>
   </div>
   <div>
       <label>
           @Html.LabelFor(model => model.toDate)</label>
       <div>
           : @Html.TextBoxFor(model => model.toDate, new { @class = “form-control” })
           @Html.ValidationMessageFor(model => model.toDate)
       </div>
   </div>
   <div>
       <label>
           @Html.LabelFor(model => model.fromDate)</label>
       <div>
           : @Html.TextBoxFor(model => model.fromDate)
           @Html.ValidationMessageFor(model => model.fromDate)
       </div>
   </div>
   
   <div>
 
       <label>
           Choose A File
       </label>
       <div>
           : <input type=”text” id=”subfile”>
           <a onclick=”$(‘#pdffile’).click();”>Browse</a>
       </div>
   </div>
</fieldset>
<div>
   <label for=”inputEmail”>
       Email:</label>
   <div>
       : <input type=”email” id=”inputEmail” placeholder=”Email”>
   </div>
</div>
<div>
   <label for=”inputPassword”>
       Password:</label>
   <div>
       : <input type=”password” id=”inputPassword” placeholder=”Password”>
   </div>
</div></div>
<div>
   <label for=”confirmPassword”>
       Confirm Password:</label>
   <div>
      : <input type=”password” id=”confirmPassword” placeholder=”Confirm Password”>
   </div>
</div>
<div>
   <label for=”firstName”>
       First Name:</label>
   <div>
       : <input type=”text” id=”firstName” placeholder=”First Name”>
   </div>
</div>
<div>
   <label for=”lastName”>
       Last Name:</label>
   <div>
       : <input type=”text” id=”lastName” placeholder=”Last Name”>
   </div>
</div>
<div>
   <label for=”phoneNumber”>
       Phone:</label>
   <div>
       <input type=”tel” id=”phoneNumber” placeholder=”Phone Number”>
   </div>
</div>
<div>
   <label>
       Date of Birth:</label>
   <div>
       <select>
           <option>Date</option>
       </select>
   </div>
   <label>
   </label>
   <div>
       <select>
           <option>Month</option>
       </select>
   </div>
   <label>
   </label>
   <div>
       <select>
           <option>Year</option>
       </select>
   </div>
<div>
   <label>
       Gender:</label>
   <div>
       <label>
           <input type=”radio” name=”genderRadios” value=”male”>
           Male
       </label>
   </div>
   <div>
       <label>
           <input type=”radio” name=”genderRadios” value=”female”>
           Female
       </label>
   </div>
</div>
<div>
   <div>
       <label>
           <input type=”checkbox” value=”news”>
           Send me latest news and updates.
       </label>
   </div>
</div>
<div>
   <div>
       <label>
           <input type=”checkbox” value=”agree”>
           I agree to the <a href=”#”>Terms and Conditions</a>.
       </label>
   </div>
</div>
<br>
<div>
   <div>
       <input type=”submit” value=”Submit”>
       <input type=”reset” value=”Reset”>
   </div>
</div>
     <input type=”file” name=”file” style=”visibility: hidden” id=”pdffile” onchange=”myFunction(this,’#subfile’);” />
</form>

C# MVC reports / navigation screen populated from webconfig with JQuery calendar validation & radio button.

Set the RepNams & urls as values in Web.Config. Use tilda ~ to separate the values.  Its a nice generic separator, because it is not commonly used in English words & phrases.

<appSettings>

<add key=”RepNams” value=”Rep1~Rep2~Rep3~Rep4″ />

<add key=”RepLnk” value=”http://www.google.co.uk~http://WWW.BBC.co.UK~http://www.netvibes.com~http://www.icloud.com&#8221; />

<add key=”RepNamsR” value=”Records up for disposal~Employee records opened~Employee records moved~Employee records deleted” />

<add key=”RepLnkR” value=”http://www.LindaScannell.com~http://WWW.BBC.co.UK~http://www.netvibes.com~http://WWW.BBC.co.UK&#8221; />

<add key=”RepActions” value=”All~Create~Read~Update~Delete” />

</appSettings>

Build a simple Model, to help pass data from the query string to the View, & to allow for easier expansion of validation:

public class Rep

{

[DisplayName("User ID")]

public string UserId { get; set; }

[DisplayName("Employee Id")]

public string EmployeeId { get; set; }

[DataType(DataType.Date)]

[DisplayName("Date from ")]

public DateTime FromDate { get; set; }

[DataType(DataType.Date)]

[DisplayName("Date until")]

public DateTime ToDate { get; set; }

[DisplayName("Actions to Rep on")]

public string[] RepActions { get; set; }

}

Build a controller, with an index capable of taking input parameters

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Web.Mvc;

using System.Net;

using System.Configuration;

using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.Unity;

using Microsoft.Practices.EnterpriseLibrary.Security;

using Microsoft.Practices.Unity;

using myMVC_app.Common;

using myMVC_app.Models;

namespace myMVC_app.Controllers

{

[Authorise("RepsAccess")]

public class RepsController : Controller

{

//

// Index takes multiple parms

public ActionResult Index(string userid=””,string empno=””, string from=””,string to =””)

{

// Load data from web.config & parse into arrays

String RepNamStr = ConfigurationManager.AppSettings["RepNams"];

String RepLnkStr = ConfigurationManager.AppSettings["RepLnk"];

String RepActionsStr = ConfigurationManager.AppSettings["RepActions"];

// Build links for left hand array

string[] RepNamArr = RepNamString.Split(‘~’);

string[] RepLnkArr = RepLnkString.Split(‘~’);

string[] RepActionsArr = RepActionsStr.Split(‘~’);

int arrlen = RepLnkArr.Length;

ViewBag.RepNamArr= RepNamArr;

ViewBag.RepLnkArr = RepLnkArr;

ViewBag.RepActionsArr = RepActionsArr;

ViewBag.Warning = “”;

// Build links for right hand array

String RepNamStringR = ConfigurationManager.AppSettings["RepNamsR"];

String RepLnkStringR = ConfigurationManager.AppSettings["RepLnkR"];

string[] RepNamArrR = RepNamStringR.Split(‘~’);

string[] RepLnkArrR = RepLnkStringR.Split(‘~’);

int arrlenR = RepLnkArr.Length;

ViewBag.RepNamArrR = RepNamArrR;

ViewBag.RepLnkArrR = RepLnkArrR;

Rep myRep = new Rep();

myRep.RepActions = RepActionsArr;

DateTime tempdate;

if (userid != “”)

{

myRep.UserId = userid;

}

if (empno != “”)

{

myRep.EmployeeId = empno;

}

// validate & set from date

if (from == “”)

{

myRep.FromDate = DateTime.Today.AddYears(-1).Date;

}

else

{

if (DateTime.TryParse(from, out tempdate))

{

myRep.FromDate = tempdate;

}

else

{

myRep.FromDate = DateTime.Today.AddYears(-1).Date;

ViewBag.Warning = “A date has been reset to the default”;

}

}

// validate & set to date

if (to == “”)

{

myRep.ToDate = DateTime.Today.Date;

}

else

{

if (DateTime.TryParse(to, out tempdate))

{

myRep.ToDate = tempdate;

}

else

{

myRep.ToDate = DateTime.Today.Date;

ViewBag.Warning = “A date has been reset to the default”;

}

}

return View(myRep);

}

}

}

Create the view  JQuery data validation displays calendar boxes, whose maximum & minimum values are dynamically set according to what has been chosen already:

The ReportAction is a radio button populated from the ReportActions array with a foreach loop

@foreach(var option in Model.ReportActions)

{

@Html.RadioButton(“ReportAction”, option)

@Html.Label(option)

}

( Prefer radio buttons over dropdowns, as it’s a 1 click operation

There is a left list & a right list of report URLS,  The left list is built with an unorderlist ( UL) whose Li elements are populated with an @for loop.   It shows a report name & passes a report URL to the LaunchReport function…

div id=”ReportsLeft”>

<ul>

@for (int i = 0; i < @ViewBag.ReportNameArray.Length; i++)

{

<li><a  onclick=”LaunchReport(‘@ViewBag.ReportLinksArray[i]‘)”>@ViewBag.ReportNameArray[i]</a></li>

}

</ul>

</div>

LaunchReport uses Jquery to get the field values, including the RadioButton

var userID = $(‘#UserId’).val();

.

.

.

var RadioButton = $(“input[name='RadioButtonName']:checked”).val();

@model myMVC_app.Models.Report

@{Html.EnableClientValidation();}

@{

ViewBag.Title = “Reports Menu”;

Layout = “~/Views/Shared/_Layout.cshtml”;

}

<script>

$(function () {

//  Use Jquery ui date picker to provide date ranges that dynamically adjust the max & //minimum available

$(“#FromDate”).datepicker({

dateFormat: “dd/mm/yy”,

onClose: function (selectedDate) {

$(“#ToDate”).datepicker(“option”, “minDate”, selectedDate);

}

});

$(“#ToDate”).datepicker({

dateFormat: “dd/mm/yy”,

onClose: function (selectedDate) {

$(“#FromDate”).datepicker(“option”, “maxDate”, selectedDate);

}

});

});

</script>

<script type=”text/javascript”>

//  Take the url, & add the available parameters to it..

function LaunchReport(reporturl) {

var userID = $(‘#UserId’).val();

var emp = $(‘#EmployeeId’).val();

var from = $(‘#FromDate’).val();

var to = $(‘#ToDate’).val();

var reportaction = $(“input[name='ReportAction']:checked”).val();

var url = reporturl;

if (reportaction != “”) {

url += “&reportaction=” + reportaction.toString();

}

if (userID != “”) {

url += “&userid=” + userID.toString();

}

if (emp != “”) {

url += “&empno=” + emp.toString();

}

if (from != “”) {

url += “&from=” + from.toString();

}

if (to != “”) {

url += “&to=” + to.toString();

}

window.open(url);

}

</script>

<div id=”headpanel”>

<h3>

Reports Menu</h3>

</div>

<div id=”reportdisplaypanel”>

<div id=”reportdisplaycontent”>

<div id=”SearchParm”>

<div id=”ParmList”>

<h3>

Please enter report parameters to use

</h3>

<div>

<div>

@Html.LabelFor(model => model.UserId)

</div>

<div>

@Html.TextBoxFor(model => model.UserId)

@Html.ValidationMessageFor(model => model.UserId)

</div>

</div>

<div>

<div>

@Html.LabelFor(model => model.EmployeeId)

</div>

<div>

@Html.TextBoxFor(model => model.EmployeeId)

@Html.ValidationMessageFor(model => model.EmployeeId)

</div>

</div>

<div>

<div>

@Html.LabelFor(model => model.FromDate)

</div>

<div>

@Html.EditorFor(model => model.FromDate)

@Html.ValidationMessageFor(model => model.FromDate)

</div>

</div>

<div>

<div>

@Html.LabelFor(model => model.ToDate)

</div>

<div>

@Html.EditorFor(model => model.ToDate)

@Html.ValidationMessageFor(model => model.ToDate)

</div>

</div>

<div>

<div>

@Html.LabelFor(model => model.ReportActions)

</div>

<div>

@foreach(var option in Model.ReportActions) {

@Html.RadioButton(“ReportAction”, option)

@Html.Label(option)

}

@Html.ValidationMessageFor(model => model.ReportActions)

</div>

</div>

<div>

@Html.ValidationSummary(true)

@ViewBag.Warning

</div>

</div>

</div>

<h3>

Then click a report below</h3>

<p>

</p>

<div id=”ReportsLeft”>

<ul>

@for (int i = 0; i < @ViewBag.ReportNameArray.Length; i++)

{

<li><a  onclick=”LaunchReport(‘@ViewBag.ReportLinksArray[i]‘)”>@ViewBag.ReportNameArray[i]</a></li>

}

</ul>

</div>

<div id=”ReportsRight”>

<ul>

@for (int i = 0; i < @ViewBag.ReportNameArrayR.Length; i++)

{

<li><a onclick=”LaunchReport(‘@ViewBag.ReportLinksArrayR[i]‘)”>@ViewBag.ReportNameArrayR[i]</a></li>

}

</ul>

</div>

</div>

</div>

Follow

Get every new post delivered to your Inbox.