Tag Archives: HTML5

Powershell srcset image generator

If you have a website and SEO matters for you, then you probably had to optimize images. To this aim, you may want to have responsive images. As explained here,

a responsive image is an image which is displayed in its best form on a web page, depending on the device your website is being viewed from.

One of the modern way to serve quickly responsive images is to benefit from the srcset html attribute. Shortly, depending on parameters and your viewport (i.e. browser window) the srcset attribute will tell the browser to download the best appropriate image for the current display.

For example, if you put the following HTML element

<img src="images/fcnantes-champions-95.jpg"
srcset="images/fcnantes-champions-95.jpg 200w, images/fcnantes-champions-95-400.jpg 400w,
images/fcnantes-champions-95-600.jpg 600w,
images/fcnantes-champions-95-800.jpg 800w">

Your server logic can serve up to four different images representing the same pictures.

You may guess that creating all this different resized pictures can be painful manually. In this blog post we propose the following Powershell script to help you for the automation of this task.

Param ( [Parameter(Mandatory=$True)] [ValidateNotNull()] $imageSource, [Parameter(Mandatory=$true)][ValidateNotNull()] $quality )

if (!(Test-Path $imageSource)){throw( "Cannot find the source image")}
if ($quality -lt 0 -or $quality -gt 100){throw( "quality must be between 0 and 100.")}

[void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
$resolvedPath = Join-Path $PWD -ChildPath $imageSource
$bmp = [System.Drawing.Image]::FromFile($resolvedPath)

#hardcoded canvas size...
$canvasWidths = @(200, 400, 600, 800)

foreach($canvasWidth in $canvasWidths){
    #Encoder parameter for image quality
    $myEncoder = [System.Drawing.Imaging.Encoder]::Quality
    $encoderParams = New-Object System.Drawing.Imaging.EncoderParameters(1)
    $encoderParams.Param[0] = New-Object System.Drawing.Imaging.EncoderParameter($myEncoder, $quality)
    # get codec
    $myImageCodecInfo = [System.Drawing.Imaging.ImageCodecInfo]::GetImageEncoders()|where {$_.MimeType -eq 'image/jpeg'}

    #compute the final ratio to use
    $ratioX = $canvasWidth / $bmp.Width;
    $ratioY = $canvasWidth / $bmp.Height;
    $ratio = $ratioY
    if($ratioX -le $ratioY){
        $ratio = $ratioX
    }

    #create resized bitmap
    $newWidth = [int] ($bmp.Width*$ratio)
    $newHeight = [int] ($bmp.Height*$ratio)
    $bmpResized = New-Object System.Drawing.Bitmap($newWidth, $newHeight)
    $graph = [System.Drawing.Graphics]::FromImage($bmpResized)

    $graph.Clear([System.Drawing.Color]::White)
    $graph.DrawImage($bmp,0,0 , $newWidth, $newHeight)

    $targetFileName = [System.IO.Path]::GetFileNameWithoutExtension($imageSource) + "-" + $canvasWidth + ".jpg"
    $dir = [System.IO.Path]::GetDirectoryName($resolvedPath)
    $targetFilePath = Join-Path $dir -ChildPath $targetFileName
    Write-Host "Saving file" $targetFilePath
    #save to file
    $bmpResized.Save($targetFilePath,$myImageCodecInfo, $($encoderParams))
    $graph.Dispose()
    $bmpResized.Dispose()
}
$bmp.Dispose()

Now you can simply invoke the script like this: .\SrcsetBuilder.ps1 "..\images\MyImage.jpg" 85. Then all generated images: MyImage-200.jpg, MyImage-400.jpg, MyImage-600.jpg, MyImage-800.jpg are located next to MyImage.jpg.
You can modify the generated images widths by changing the values in the array $canvasWidths (line 11).

Implementing the OAUTH 2.0 flow in app for office sandboxed environment

EDIT: this approach is outdated. You can have a look at the sample we released and especially in the README section we compare the different approaches
https://github.com/Keluro/Office365-AddinWeb-SignInSample

In this post I will explain how I implemented the OAUTH 2.0 flow in the app for office sandboxed environment. The objective of this post is to discuss with others if the proposed implementation is satisfactory. Do not hesitate to criticize and propose alternatives.

Following the msdn documentation, the app for office model is the new model for

engaging new consumer and enterprise experiences for Office client applications. Using the power of the web and standard web technologies like HTML5, XML, CSS3, JavaScript, and REST APIs, you can create apps that interact with Office documents, email messages, meeting requests, and appointments.

Personally, I find this extremely promising, I even release my first app last year: Keluro web analytics that is distributed here on the store. I even wrote a small article dealing with app for office on the Keluro web blog.

Let us get straight to the point for this tech article. Apps for office aim at building connected service. For example, Keluro web analytics fetchs data from Google Analytics within MS Excel. I am working on a new project that must communicate with other Office 365 APIs. In all cases, you must use the OAUTH 2.0 flow to grant your app the right to communicate with those APIs. Sometimes there are some client libraries to leverage technicalities of the OAUTH flow. For my fist app, I used the Google Javascript API and for the second one I used the Azure Active Directory adal.js library.

However, there is problem. The apps for office run in a sandboxed environment that prevent cross domain navigation. This sandboxed environment is an iframe with sandbox attributes for the case of the office web apps. I asked the question for more information in this msdn thread. In all cases, the consequence is that the usual OAUTH 2.0 flow that redirects you to https://login.microsoft.com/etc. or https://apis.google.com to prompt the user for allowing access will not work. In addition, the pages served by those domains cannot be ‘iframed’ whether they are served to with X-Frame-Options deny or some js ‘frame busting’ is embedded.

I will get straight to the only working solution that I found. Actually, its the same recipe that I used for Google APIs and ADAL.

It looks like that the apps for office run with the following sandboxed attributes sandbox=”allow-scripts allow-forms allow-same-origin ms-allow-popups allow-popups”. Therefore, we are allowed to use popups and this popup is not restricted by CrossOrigin. What we will do is to open a popup and we will do the OAUTH flow in this popup, when its done we will get back the necessary information in the main app iframe. Here is a summary with a small graphic.

An overview of the solution found for the OAUTH2.0 in sandboxed env

An overview of the solution found for the OAUTH2.0 in sandboxed env

Then, you got it we will use a special callback.html file (that is basically blank and contains only javascript) as returned url. Here it its code.

<!--usual html5 doctype and meta-->
<body>
    <div id="oauthurl"></div>
     <script>
        var closeWindowCheck = function () {
        <!--transmit the hash of the return url to the parent iframe-->
            window.oauthHash =  location.hash;
            <!-- wait that the parent window give its approbation to close-->
            if (window.canclosewindow === true) { 
                window.close();
            }
        }
        var interval = setInterval(closeWindowCheck, 200);
    </script>
</body>
</html>

Then in the main iframe when it’s time to start the OAUTH flow, you have a piece of javascript that looks like the following

//this javascript lives in the sandboxed environment
//urlNavigate is the oauth2.0 url of the apis you are targeting with the parameters
//use callback.html as redirect uri.
var childWindow =window.open(urlNavigate,'Auth window', 'height=500,width=500');
var interval;
var pollChildWindow = function () {
    if (childWindow.closed === false) {
        var oauthHash;
        try {
           //this throws exception until the popup returns to
           //callback.html served in our domain.
            oauthHash = childWindow.oauthHash; 
        }catch(ex){}

        if (typeof oauthHash === &quot;string&quot;) {
            //childWindow.close(); // does not work with Chrome
            // ->; that is why we have the setInterval in callback.html javascript
            childWindow.canclosewindow = true; // allow the child window to close.
            var startWith = oauthHash.indexOf('#id_token');
            var currentRef = window.location.href.split('#')[0];
            //you get the id_token and other information from OAUTH2.0 
            //do whatever you have to do with it in your app.
            //YOUR LOGIC HERE
        }
    } else {
        clearInterval(interval);
    }
};                                                                                                                                          
interval = setInterval(pollChildWindow, 200);

I personally find this implementation not satisfactory because of the use of the popup (that may be blocked). What do you think of it ? Do you think of an alternative. I tried many things using postMessage API but it does not work with IE in sandboxed environment.

This is the implementation that is discussed in this ADAL.js issue. You may also find a setup of a sandboxed environment and some implementation attempt (without apps for office for simplicity) on my remote branch of my Adal.js fork.