Problems with 4K UHD and HyperV VM

As part of my new ‘kachirho’ company setup, I’ve purchased a new laptop – DELL XPS15 9550…    It’s a beast with new SkyLake i7 6700 processor – and 16GB RAM – and 512 SSD – see here for a YouTube review.   And it looks super-sexy with carbon finish…

It’s got the new (almost 4K) UHD resolution – 3840×2160 – and touch screen.   The visuals on this resolution are amazing – BUT – the clincher is that you need to ‘up’ the font size scaling.

By default, it was set to 200% – if setting to 100% – it’s hard to read !

The next dilemma – and discussion with a colleague – was the scenario for a HYPER-V VM.    I have one for some local development and working with K2.

Yes, I could use an Azure VM – but I have a local VM – so there…

While I can run a VM at decent speed, and with 10GB RAM, it’s not something that I can run on ‘multiple monitors’.  

The next consideration, was to drop my screen resolution to 1920×1080 – to match the second external monitor.    This WORKED – to a degree.

The VM would now pick up the base screen resolution (3840×1920) – within the VM – regardless of the desktop resolution I’d just changed to.

It turns out that HyperV must grab the base screen resolution – at BOOT time.

So – the answer was :

  • Change down from scale at 200% to 100% (I use 175% a lot of the time)
  • Drop the laptop screen resolution to 1920×1080
  • Reboot
  • Start up the VM
  • Connect – and tick the box for ‘span multiple monitors’

I can now run my VM across both screens – and both at 1920×1080.

It’s a fiddly way to do it – but luckily it’s so quick to re-boot, it doesn’t matter !

Yes – as Scott Hanselman mentioned, living a High-DPI desktop lifestyle can be painful….

#FirstWorldProblems

But – it’s a problem I’m happy to live with.    Viva la 4K !!

Smile

JavaScript Date Format

One of my main bugbears with JavaScript is the way it handles DATE variables.   Or, more specifically, date FORMAT.

There are some great jQuery plug-ins – like MOMENT.JS – but if you have the need for a quick formatter – I just use a simple function, like this :

function formatDate(dateObject) {
    var d = new Date(dateObject);
    var day = d.getDate();
    var month = d.getMonth() + 1;
    var year = d.getFullYear();
    if (day < 10) {
        day = “0” + day;
    }
    if (month < 10) {
        month = “0” + month;
    }
    var date = day + “/” + month + “/” + year;
    return date;
}

This does a format of “dd/mm/yyyy” – which is the only one you need, right ??

To use the above function, you just need to do this :

var nowDateTime = new Date();
var formattedDateTime = formatDate(nowDateTime);

Feel free to edit/use as you need – can change to dd-mm-yyyy, for example.

But never mm/dd/yyyy – that’s just crazy talk !   

😛

Remove all webparts

For a recent SP2010 to SP2013 upgrade, we have a SharePoint Publishing page that has some SandBoxed Solution webparts.   We need to REMOVE these webparts – as we’re going with a different approach – some JavaScript/CSOM – to do the same function.

Anyway, I’ve defined a script that will get a SP page, and remove ALL webparts.

This is via PowerShell + CSOM, so the same approach would would work for SP2013, or O365 (just need a different ClientContext – and login/connection).

# values from constants
$urlSite = "https://intranet/projectX"

$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($urlSite)
$ctxWeb = $ctx.Web
$ctx.Load($ctxWeb)
$ctx.Load($ctxWeb.Lists)
$ctx.ExecuteQuery();
	
[Microsoft.SharePoint.Client.Publishing.PublishingWeb] $pubWeb = [Microsoft.SharePoint.Client.Publishing.PublishingWeb]::GetPublishingWeb($ctx, $ctxWeb);
$ctx.Load($pubWeb)
$ctx.ExecuteQuery();

function RemoveAllWebParts($pageName)
{
    $messageText = "Checking page " + $pageName
	$messageText 

	# remove webparts 
	# wipe them out - ALL of them      (order 66)

	# get the pages library		
	$pagesLib = $ctxWeb.Lists.GetByTitle("Pages");
    $ctx.Load($pagesLib)
	$ctx.ExecuteQuery();
		
	# find the page we wanna ditch the webparts from
    [Microsoft.SharePoint.Client.CamlQuery] $camlQuery = New-Object Microsoft.SharePoint.Client.CamlQuery
	$camlQuery.ViewXml = "<View Scope='RecursiveAll'><Query><Where><Eq><FieldRef Name='FileLeafRef' /><Value Type='Text'>" + $pageName + "</Value></Eq></Where></Query><RowLimit>1</RowLimit></Query></View>"
	$pageFound = $false
	$pageItems = $pagesLib.GetItems($camlQuery)
	$ctx.Load($pageItems)
	$ctx.ExecuteQuery();

	if($pageItems.Count -gt 0)
	{	$pageFound = $true
		$pubPageItem = $pageItems[0]
		$pubFile = $pubPageItem.File
	}
		
	if($pubFile.CheckOutType -eq [Microsoft.SharePoint.Client.CheckOutType]::None -or $pubFile.CheckOutType -eq $null)
	{
		try
		{
			$pubFile.CheckOut()
			$ctx.Load($pubFile)
			$ctx.ExecuteQuery(); 
		}
		catch
		{
		}
	}

	$wpm = $pubFile.GetLimitedWebPartManager([Microsoft.SharePoint.Client.WebParts.PersonalizationScope]::Shared);
    $webparts = @();

	$wpcoll = $wpm.WebParts;
	$ctx.Load($wpcoll);
	$ctx.ExecuteQuery();

    $messageText = "Delete webparts for the page";
    $messageText;

    foreach($spwebpart in $wpcoll)
    {
        $webparts = $webparts + $spwebpart.ID;
        $spwebpart.deleteWebPart();
        $ctx.ExecuteQuery();
    }

	if($pubFile.CheckOutType -ne [Microsoft.SharePoint.Client.CheckOutType]::None)
	{
		$pubFile.CheckIn("", [Microsoft.SharePoint.Client.CheckinType]::MajorCheckIn);
		$pubFile.Publish("");
		$pubFile.Approve("");
	}
}

RemoveAllWebParts 'Page1.aspx';
RemoveAllWebParts 'Page2.aspx';
RemoveAllWebParts 'Page3.aspx';
RemoveAllWebParts 'Page4.aspx';

O365 Saturday Melbourne

Last weekend, I presented a session at the Office 365 Saturday held in Melbourne.   I didn’t get there until lunch was being served, as I was coaching my son’s basketball team – but there was a great buzz & lots of chatter when I got there !

My session was entitled “VS, PS, CS, JS and no BS”.

If you don’t know the acronyms, it was a session about :

  • VS = Visual Studio 2015
  • PS = PowerShell
  • CS = C#
  • JS = JavaScript
  • BS = well, there was none, right !?    

🙂

Using Visual Studio 2015 as my IDE, I covered three main areas, with corresponding demos.

Provision a Site Collection – using PowerShell

  • Connect to SPO
  • Create a new Site Collection
  • Regional Settings
  • Activate features
  • Upload files
  • Create lists & pages
  • Set Alt Css Url
  • Welcome page
  • Add JavaScript reference (JS injection)

This last point was a topic that I’ll cover in detail in another post – a way to get some good functionality into your SharePoint O365 site, without needing to change the Master Page – otherwise known by some work colleagues as ‘dentistry via the belly button’ – LOL !

Provider hosted app in Azure – using C#

Using CSOM, users can provision a sub-site (SPWeb), and have it configured with a variety of features and layout.   My code even provisions a Yammer group if required.

  • Activate features
  • Add lists
  • Create webparts
  • Set security
  • Create Yammer group
  • Add Yammer webpart to page
  • Add blog site
  • Add audience targetted content editor webparts

Additional functionality – using JavaScript

Lastly, I showed some of the functions we developed using CSOM and the JSOM :

  • Add footer – without Master Page update (JavaScript injection)
  • Waffle
  • Show dialog
  • Delete sub-sites
  • Get AppTiles

Much of the demo’s were fairly fast paced, and “you had to be there to see it” – but I’ve loaded the slides for your viewing – and will blog some of the items above in depth.

Let me know if you would like to see/hear more – and thanks for attending my session – if you were there on the day.    (no BS, was there ??   right !!?!?)

Convert IP Address to numeric (JS)

I’ve been using some fancy functions in SQL to convert to/from an IP Address and Mask – but have needed to do the same within JavaScript.

This is a requirement based on some MySQL functions – eg. INET_NTOA and INET_ATON.

This first one will take an input value – eg. 255.240.0.0 – and return a number (BIGINT / INT64 for calculations – or storing in a database – as we’re needing to do.

    function convertIpToNumeric(ipAddress) {
        var arrIp = ipAddress.split(&quot;.&quot;);

        var segment1 = parseInt(arrIp[0]);
        var segment2 = parseInt(arrIp[1]);
        var segment3 = parseInt(arrIp[2]);
        var segment4 = parseInt(arrIp[3]);

        //reverse order calc 
        //eg. 255.255.240.0     &lt;-- start at the end
        // 0 + (240*256) + (255*65536) + (255*16777216)
        var calc = segment4 + (segment3 * 256) + (segment2 * 65536) + (segment1 * 16777216);

        return calc;

    }

And – if needing to do the opposite – you can use the following function.

This will take an input value – eg. 167772161 – and return the resultant IP Address :

function convertNumericToIp(bigNumber) {

    var Octet1 = Math.floor(bigNumber / 16777216)
    var RestOfIP = bigNumber - (Octet1 * 16777216)

    var Octet2 = Math.floor(RestOfIP / 65536)
    var RestOfIP = RestOfIP - (Octet2 * 65536)

    var Octet3 = Math.floor(RestOfIP / 256)
        
    var Octet4 = RestOfIP - (Octet3 * 256)

    var returnValue = Octet1 + &quot;.&quot; + Octet2 + &quot;.&quot; + Octet3 + &quot;.&quot; + Octet4; 

    return returnValue;

}

Turn OFF flag for HIDE PHYSICAL URL’s

We have a set of publishing pages that are not correctly showing if/when the following flag is set to ON :

image

This is to do with the SEO and vanity URL’s stuff – but we want this “OFF”.

Looking at the field via the REST URL, you can see the default value.    But, the field/column is actually a SEALED column, and so you can’t update it via the UI.

https://tenant.sharepoint.com/sites/MyFancySiteColl/_api/Web/AvailableFields(guid’50631c24-1371-4ecf-a5ae-ed41b03f4499′)

image 

So – it’s some fancy PowerShell to force this value (after connecting to O365) :

#get the fields at the root site level
$rootWeb = $clientContext.Web
$fields = $rootWeb.Fields
$clientContext.Load($fields)
$clientContext.ExecuteQuery()

#grab the field 
$fld = $fields.GetByInternalNameOrTitle(&quot;PublishingIsFurlPage&quot;)
$clientContext.Load($fld)
$clientContext.ExecuteQuery()

$fld

$fld.DefaultValue = $false
$fld.Update()
$clientContext.ExecuteQuery()

$rootWeb.Update()
$clientContext.ExecuteQuery()

After running this PowerShell, the field is SET when viewing the column via REST :

image

And now, when users create a page – regardless of PAGE LAYOUT – this value will be ticked to OFF by default – and standard URL’s will be used – too easy.

The main issue we had was with a Content Search WebPart that was NOT showing the pages that had the checkbox ON.   We needed users to make sure to set it off – making it DEFAULT to off was the best option.

🙂

SQL Server INET_ATON IP Address + CIDR

Most IT folk would understand that an IP Address is a 4-octset set – eg. 192.168.53.123

There’s another piece that is the subnet mask – eg.  255.255.252.0

These are usually managed as numerical values – BIGINT values (Int64).

Within MySQL, you can determine the IP Address to/from a number using the functions INET_ATON and INET_NTOA.

For an equivalent in SQL Server – you can use the following UDF’s :

Integer to IP Address :

CREATE FUNCTION [dbo].[IntegerToIPAddress] (@IP AS bigint)
RETURNS varchar(15)
AS
BEGIN
 DECLARE @Octet1 bigint
 DECLARE @Octet2 tinyint
 DECLARE @Octet3 tinyint
 DECLARE @Octet4 tinyint
 DECLARE @RestOfIP bigint
 
 SET @Octet1 = @IP / 16777216
 SET @RestOfIP = @IP - (@Octet1 * 16777216)
 SET @Octet2 = @RestOfIP / 65536
 SET @RestOfIP = @RestOfIP - (@Octet2 * 65536)
 SET @Octet3 = @RestOfIP / 256
 SET @Octet4 = @RestOfIP - (@Octet3 * 256)
 
 RETURN(CONVERT(varchar, @Octet1) + '.' +
        CONVERT(varchar, @Octet2) + '.' +
        CONVERT(varchar, @Octet3) + '.' +
        CONVERT(varchar, @Octet4))
END

IP Address to Integer

CREATE FUNCTION [dbo].[IPAddressToInteger] (@IP AS varchar(15))
RETURNS bigint
AS
BEGIN
 RETURN (CONVERT(bigint, PARSENAME(@IP,1)) +
         CONVERT(bigint, PARSENAME(@IP,2)) * 256 +
         CONVERT(bigint, PARSENAME(@IP,3)) * 65536 +
         CONVERT(bigint, PARSENAME(@IP,4)) * 16777216)
 
END

Yet another piece of the puzzle is related to a CIDR – which looks like this :  192.30.250.00/18

It’s an acronym for “Classless Inter-Domain Routing” – I had to look it up too…!

I’ve created another function that determines the “/18” for a given numeric sequence (in T-SQL).

CREATE FUNCTION [dbo].[CIDRFromMask] (@mask AS BIGINT)
RETURNS VARCHAR(5)
AS
BEGIN

	 DECLARE @maskCalc BIGINT
	 SELECT @maskCalc = dbo.IPAddressToInteger('255.255.255.255') - @mask + 1

	 DECLARE @logCalc int
	 SELECT @logCalc = (32 - LOG(@maskCalc, 2))

	 RETURN '/' + CAST(@logCalc AS VARCHAR(5))

END

This helped me with the retrieval of an IP Address – and CIDR – fairly complex, but easy to use in the end – eg.  255.255.252.0/22

select dbo.IntegerToIPAddress('4294966272') + dbo.CIDRFromMask('4294966272')

image