X
dnn.blog.
Back

DnnImagehandler - Hot or not ?

All started with a facebook conversation about a little tweak I found to help me massively with a problem I had in a website that I built for my wife.

I had to explain her the structure of her cattery website (varelvens.de) full of custom built modules mixed with Html-module parts.

I had the idea to hack dnn a little bit and with some research I found a possibility to blend in the module title in the drag bar. Only two additional lines of code in a dnn javascript file and three additional properties in a css class were enough too see a perfect structure in design mode and perfect kitten baby presentations in view mode - Yeah, mission completed!

Module titlebar

Those are the tweaks:

in \Resources\Shared\scripts\dnn.dragDrop.js: (ca. line 104)

for (var moduleNo = 0; moduleNo < $modules.length; moduleNo++) {
    $module = $($modules[moduleNo]);
    mid = getModuleId($module);
    moduleName = $module.attr("class").split(' ')[1].replace("DnnModule-", "");

    //Add a drag handle
    if ($module.find(".dnnDragHint").length === 0) {
        $module.prepend("<div class=\"dnnDragHint\">&nbsp;" + moduleName + " (ID:" + mid + ")</div>");
    }

    //Add a drag hint
    $module.find(".dnnDragHint").dnnHelperTip({
        helpContent: settings.dragHintText,
        holderId: "ModuleDragToolTip-" + mid
    });
}

in \Resources\Shared\stylesheet\dnn.dragDrop.css:

.dnnDragHint {
    background-color: black;
    color: white;
    cursor: move;
    font-family: Arial;
    font-size: 12px;
    height: 22px !important;
    outline: 1px dashed #ccc;
}

I loved this nice tweak, so I started a facebook post :

Facebook Post

A lot of people commented on this thread and came up with many ideas what should also be shown....  but not in the dragbar (very small modules etc. ...).

So this came in my mind:

Info Menu

Yep, nice design. Must be a popup when hovering over the i-Button.

My first idea was to write a web service method which is called by client site code. This should sample the infos from dnn and create some html, styles etc. and show this html in the popup. But from the design-point  this is a nightmare. Different styles have to be considered and in every theme it looks different - a bad choice for people who use dnn as admins and have to work with modules on a daily base.

This rotated a few days in my mind and one morning I had the idea! Showing this as an image when hovering over "info" is really easy to do. All we need is an imagehandler like the "profilepic" from dotnetnuke (creates the user profile photos ). Adding  the moduleid and the tabid to the image url should be enough to retrieve all the needed information.

My bbimagehandler on codeplex is working much in the same technology. It bases on an 7 years old codeplex project from Microsoft. It was more a good architectural shell for an image handler than a final product:

Generated Image Project

see here: https://aspnet.codeplex.com/releases/view/16449 in detail.

BBImageHandler

Over the years (I started my http://bbimagehandler.codeplex.com project in June 2011) ,I restructured the project and added a lot of gimmics here and there. The original code generates bad rendering results and I added another, a better Quantizer (OctreeQuantizer) to the ImageQuantization. Thanks to the guy who posted this class somewhere in the internet ! (This is not my specialization ;-) )

Inside the imagehandler we have three main parts:

  • StartTransform: Is generating the main image (profile pic, image from disk, ...)
  • Filtertransform: These transforms change the main image (greyscale, resize etc.) 
    • ImageQuantization: Mathematical operations for filters
  • And the "engine" : ImageHandler main and base classes: 
    • Reading the parameters, 
    • Initiating the pipeline
    • Adding dependend on "mode" parameter the selected start transformation to the pipeline
    • Adding dependend on other parameters the additional filters with their corresponding parameter values to the pipeline
  • Running the pipeline and return the resulting image

Transform chain

The project is well structured in the same manner so you can see easy whats going on:

Transform classes

Recently I made a short introduction in the last MVP online meeting and the first questions that came up were:

What about caching ? 

As said above,the imagehandler is based on an old but robust framework for image generation in asp.net by the Microsoft guys himself.  I think they wrote it an the beginning days of ASP.NET to demonstrate the power of the product. It have had also full Visual Studio Designer mode support by a ton of additional [attributes] in the source code. (I threw them out here because we don't need this in our image engine room).

The imagehandler has full support for server site AND client site caching. Server site caching is done by building a unique hash key over the parameters and save this generated image to a subdirectory in _App_Data. Client site caching is done by sending the appropiate 304 Headers - this works all well out of the box.

Did you implement security ?

What about DDOS scenarios generating tons of images with different sizes that let the cache explode (Shaun himself asked this question, remembering some issues they had with the original profile pic handler in DNN) ? Thats a point I had not thought about, but finally I found a solution; its all in the _IPCount.cs class. On every hit to the imagehandler IPCount retrieves the IP address of the requester and looks for a file in _App_data/_IPCount folder with the same name as this IP address. If found, it opens the file and reads the number in it and compares it to the maxAllowed value. If this border is hit, the imagehandler returns a 403 http status code (IP address rejected), if not the number is incremented by 1 and saved back to the file. If no writing appears into the file for a specified time (LastWriteTime) the file is automatically deleted.

What about other websites that "steale" my images ?

I created a config option to allow or disallow invoking the imagehandler as a "standalone" image (as typing the image url directly into the browser) and additionally to define which domains are allowed to incude images into their website. 

What is implemented "Out of the box" ?

Enabling the image handler functionality is done by an additional statement in the system.webServer/handlers section of web.config:

<add name="DnnImageHandler" path="DnnImageHandler.ashx" verb="*" type="DotNetNuke.Services.GeneratedImage.DnnImageHandler, DotNetNuke" preCondition="integratedMode" />

The image handler could be used if you enter the following image url:

http://mydomain.com/dnnimagehandler.ashx?mode=xyz&param1=value1&param2=value2...

There are a few modes (StartTransform) defined:

  • profilepic – shows the profile picture to a given userid (if allowed) or a generic “no avatar” image
  • file – shows an image by given filename with path or an url or filepath and index (alphabetical order)
  • securefile – shows the image by a given fileid (if allowed). If no image format shows filetype icon instead
  • placeholder – shows a rectangle with image dimensions or text on it
  • modinfo – shows some information about a specific module on a page like title, skin etc.

These “modes” are generating an image. The generated image could be modified by additional parameters. Available modifications (FilterTransform) are:

  • Resize – defining the dimension of the resulting image with different resize modes, Adding borders and colored background
  • Format – define the resulting format of the generated image (jpg, png…)
  • Gamma – adjust the gamma value
  • Brightness – adjust the brightness value
  • Contrast – adjust the contrast value
  • Greyscale – convert to greyscale image
  • Invert – convert to negative image
  • Rotate + Flip – rotate and / or flip the image

Mode: Profilepic

The profilepic mode shows the profile picture to a given userid (if allowed) or a generic “no avatar” image. The functionality is the same as the old profilepic.ashx handler.

Parameters:

Name Meaning
userid ID of user whose profile image should be shown. If omitted, the profile pic of the current user is generated. If no user is logged in or the profile pic is not allowed to show, a generic avatar image is generated
<img src="/dnnimagehandler.ashx?mode=profilepic&amp;w=120" />  
<img src="/dnnimagehandler.ashx?mode=profilepic&amp;userid=2&amp;w=120" />

clip_image0016

Mode: File

The file mode shows an image by given filename with path or url or filepath and index (alphabetical order)

Parameters:

Name Meaning
file Relative path to the file incl. filename, eg: /Portals/0/images/myimage.gif

or:

Name Meaning
url Url to image (url-encoded), eg: http%3A%2F%2Fwww.indisoftware.de%2FPortals%2F0%2Findilogo.png

or:

Name Meaning
path Relative path to a folder containing images
Index Index of the file in the folder (starting with 1, alphabetical order)
<img src="/dnnimagehandler.ashx?mode=file&amp;file=Portals/0/Images/icon-qa.png&amp;size=xs" />
<img src="/dnnimagehandler.ashx?mode=file&amp;url=http%3A%2F%2Fwww.indisoftware.de%2FPortals%2F0%2Findilogo.png" />
<img src="/dnnimagehandler.ashx?mode=file&amp;path=Portals/0/Images&amp;index=5" />

Mode: File

Mode: Securefile

The securefile mode shows the image by a given fileid (if allowed). If no image format shows filetype icon instead

Parameters:

Name Meaning
fileid Shows the image with the given fileid if the logged user has the view permission for this file. If the file is no image file, an icon is generated instead (from file icons/Sigma/ext%EXTENSION%\_32x32\_standard.png)
<img src="/dnnimagehandler.ashx?mode=securefile&amp;fileid=100" /> (skin.css)
<img src="/dnnimagehandler.ashx?mode=securefile&amp;fileid=101" /> (1-icn.png)

Mode: Securefile

Mode: Placeholder

The placeholder mode shows a rectangle with image dimensions or text on it

Parameters:

Name Meaning
w Width in pixel
h Height in pixel
color Foreground color
backcolor Background color
text Text to display on image. If omitted, dimensions are shown
<img src="/dnnimagehandler.ashx?mode=placeholder&amp;w=150&amp;h=100&amp;text=plugin-modul" />
<img src="/dnnimagehandler.ashx?mode=placeholder&amp;w=150&amp;h=100&amp;color=yellow&amp;backcolor=green" />

Mode: Placeholder

Mode: Modinfo

The modinfo mode shows some information about a specific module on a page like title, skin etc.

Parameters:

Name Meaning
moduleid Module id of the module
Tabid Tab Id of the page where the module is located
<img alt="" src="/dnnimagehandler.ashx?mode=modinfo&amp;moduleid=415&amp;tabid=55" />

Mode: Modinfo

Filter:Resize

Defining the dimension of the resulting image with different resize modes, Adding borders and colored background.

Parameters:

Name Meaning
w Width of Image
h Height of Image
size Predefined size of image: xxs: 16x16, xs:32x32, s:50x50, l:64x64, xl:128x128, xxl:256x256
maxwidth Instead of Width this can be used to leave the picture as it is until the width reaches the max value.
maxheight Instead of Height this can be used to leave the picture as it is until the height reaches the max value.
resizemode
  • fit mode maintains the aspect ratio of the original image while ensuring that the dimensions of the result do not exceed the maximum values for the resize transformation. (Needs width or height parameter)
  • fitsquare resizes the image with the given width as its longest side (depending on image direction) and maintains the aspect ratio. The image will be centered in a square area of the chosen background color (Needs width parameter, backcolor optional)
  • crop resizes the image and removes parts of it to ensure that the dimensions of the result are exactly as specified by the transformation.(Needs width and height parameter)
  • fill resizes the image with the given width or height without maintaing the aspect ratio.
backcolor color of background or/and border when resizemode is fitsquare or fit
border border width in pixels around the image (added to width / height) when resizemode is fitsquare or fit.
<img src="/dnnimagehandler.ashx?mode=profilepic&amp;userid=2&amp;size=xl&amp;backcolor=%23F58719&amp;border=10" />
<img src="/dnnimagehandler.ashx?mode=file&amp;file=Portals/0/Images/icon-qa.png&amp;h=128&amp;w=40&amp;resizemode=fill" />

Filter: Resize

Filter:Gamma

Adjusting the gamma value

Parameters:

Name Meaning
gamma Value for gamma adjustment between 0.2 and 5
<img src="/dnnimagehandler.ashx?mode=profilepic&amp;userid=2&amp;size=xl&amp;gamma=4"/>

Filter: Gamma

Filter:Brightness

Adjusting the brightness value

Parameters:

Name Meaning
brightness Value for brightness adjustment between -255 and +255
<img src="/dnnimagehandler.ashx?mode=profilepic&amp;userid=2&amp;size=xl&amp;brightness=128" />

Filter: Brightness

Filter: Contrast

Adjusting the contrast value

Parameters:

Name Meaning
contrast Value for contrast adjustment between -100 and +100
<img src="/dnnimagehandler.ashx?mode=profilepic&amp;userid=2&amp;size=xl&amp;contrast=75" />

Filter: Contrast

Filter: Greyscale

Convert image to greyscale

Parameters:

Name Meaning
greyscale If present, image is converted to greyscale (needs dummy value, e.g “1”)
<img src="/dnnimagehandler.ashx?mode=profilepic&amp;userid=2&amp;size=xl&amp;greyscale=1" />

Filter: greyscale

Filter:Invert

Converts an image to its negative representation

Parameters:

Name Meaning
invert If present, image is inverted (needs dummy value, e.g “1”)
<img src="/dnnimagehandler.ashx?mode=profilepic&amp;userid=2&amp;size=xl&amp;invert=1" />

Filter: Invert

Filter: Rotateflip

Rotates and / or flips the image

Parameters:

Name Meaning
rotateflip
  • RotateNoneFlipNone: Specifies no clockwise rotation and no flipping.
  • Rotate90FlipNone: Specifies a 90-degree clockwise rotation without flipping.
  • Rotate180FlipNone: Specifies a 180-degree clockwise rotation without flipping.
  • Rotate270FlipNone: Specifies a 270-degree clockwise rotation without flipping.
  • RotateNoneFlipX: Specifies no clockwise rotation followed by a horizontal flip.
  • Rotate90FlipX: Specifies a 90-degree clockwise rotation followed by a horizontal flip.
  • Rotate180FlipX: Specifies a 180-degree clockwise rotation followed by a horizontal flip.
  • Rotate270FlipX: Specifies a 270-degree clockwise rotation followed by a horizontal flip.
  • RotateNoneFlipY: Specifies no clockwise rotation followed by a vertical flip.
  • Rotate90FlipY: Specifies a 90-degree clockwise rotation followed by a vertical flip.
  • Rotate180FlipY: Specifies a 180-degree clockwise rotation followed by a vertical flip.
  • Rotate270FlipY: Specifies a 270-degree clockwise rotation followed by a vertical flip.
  • RotateNoneFlipXY: Specifies no clockwise rotation followed by a horizontal and vertical flip.
  • Rotate90FlipXY: Specifies a 90-degree clockwise rotation followed by a horizontal and vertical flip.
  • Rotate180FlipXY: Specifies a 180-degree clockwise rotation followed by a horizontal and vertical flip.
  • Rotate270FlipXY: Specifies a 270-degree clockwise rotation followed by a horizontal and vertical flip.
<img src="/dnnimagehandler.ashx?mode=profilepic&amp;userid=2&amp;size=xl&amp;rotateflip=Rotate90FlipY" />
<img src="/dnnimagehandler.ashx?mode=profilepic&amp;userid=2&amp;size=xl&amp;rotateflip=RotateNoneFlipY" />
<img src="/dnnimagehandler.ashx?mode=profilepic&amp;userid=2&amp;size=xl&amp;rotateflip=RotateNoneFlipX" />

Filter: RotateFlip

Configuration

There are a few options for the imagehandler that could be set up via configuration in web.config. You could set your options in appSettings-section:

<add key="DnnImageHandler" value="EnableIpCount=false;AllowStandalone=false;LogSecurity=true;AllowedDomains=mydomain.com,otherdomain.net;EnableServerCache=true;EnableClientCache=true;" /></font>

The following options are available :

Name Meaning
EnableIpCount Default: true. If true, every generation of an image will be counted. If IPCountMax is hit no more images are generated until IPCountPurgeInterval is up. Physically there is a file in _App_Data/_ipcount for every IP containing the number of hits. When IPCountPurgeInterval is over, this file is deleted.
IpCountMax Default: 200. Max number of hits from an IP allowed in defined time span IPCountPurgeInterval
IPCountPurgeInterval Default: 300. Seconds for measuring the hits from one IP.
EnableServerCache Default: true. Enables the server side caching of images in folder _App_Data/_images
ServerCacheExpiration Default: 180 Seconds until the image is deleted from image cache folder and generated again.
EnableClientCache Default: true. Enables the client side caching of images (answering with 304 http response until ClientCacheExpiration is reached)
ClientCacheExpiration Default: 60 Seconds caching the image on client site.
AllowStandAlone Default: false. If true, Images could be shown standalone via image url. If false, only images integrated in a web page (referrer is not null) are shown, others return 403 (forbidden) http status code.
LogSecurity Default: false. If true, hitting IPCountMax or AllowStandalone bounderies produce a eventlog entry
AllowedDomains Default: empty string. By default, all websites are allowed as referrer. Include here a comma-separated list of domains to restrict the embedding of your images to pages from these domains (e.g. mydomain.com,myotherdomain.net )
ImageCompression Default: 95. Set the image generation quality. Range is from 0 to 100.

But: Thats not all! The DnnImageHandler is extensible by third party developers!

Additional Mode Extensions

If a developer creates a dll containing a class (for example: PercentTransform) based on ImageTransform class with two public property named “percentage” and "color":

using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Drawing.Text;

namespace Dotnetnuke.Web.GeneratedImage.Transform
{
        public class ImagePercentTransform : ImageTransform
       {
               /// <summary>
               /// Sets the percentage value for the radial indicator
               /// </summary>
               public int Percentage { get; set; }

               /// <summary>
               /// Sets the Color of the indicator element
               /// </summary>
               public Color Color { get; set; }

               public override string UniqueString
              {
                      get { return base.UniqueString + this.Percentage.ToString() + "-" + this .Color.ToString(); }
              }

               public ImagePercentageTransform()
              {
                     InterpolationMode = InterpolationMode.HighQualityBicubic;
                     SmoothingMode = SmoothingMode.HighQuality;
                     PixelOffsetMode = PixelOffsetMode.Default;
                     CompositingQuality = CompositingQuality.HighSpeed;
              }

               public override Image ProcessImage(Image image)
              {
                     Bitmap bitmap = new Bitmap( 100, 100);
                      using (Graphics objGraphics = Graphics.FromImage(bitmap))
                     {
                            // Initialize graphics
                           objGraphics.Clear(Color.White);
                           objGraphics.SmoothingMode = SmoothingMode.AntiAlias;
                           objGraphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;

                            // Fill pie
                            // Degrees are taken clockwise, 0 is parallel with x
                            // For sweep angle we must convert percent to degrees (90/25 = 18/5)
                            float startAngle = - 90.0F;
                            float sweepAngle = ( 18.0F/ 5)*Percentage;

                           Rectangle rectangle = new Rectangle( 5, 5, 90, 90);
                           Brush colorBrush = new SolidBrush(Color);
                           objGraphics.FillPie(colorBrush, rectangle, startAngle, sweepAngle);

                            // Fill inner circle with white
                           rectangle = new Rectangle( 20, 20, 60, 60);
                           objGraphics.FillEllipse(Brushes.White, rectangle);

                            // Draw circles
                           rectangle = new Rectangle( 5, 5, 90, 90);
                           objGraphics.DrawEllipse(Pens.LightGray, rectangle);
                           rectangle = new Rectangle( 20, 20, 60, 60);
                           objGraphics.DrawEllipse(Pens.LightGray, rectangle);

                            // Draw text on image
                            // Use rectangle for text and align text to center of rectangle
                            var font = new Font( "Arial" , 13 , FontStyle.Bold);
                           StringFormat stringFormat = new StringFormat();
                           stringFormat.Alignment = StringAlignment.Center;
                           stringFormat.LineAlignment = StringAlignment.Center;

                           rectangle = new Rectangle( 20, 40, 62, 20);
                           objGraphics.DrawString(Percentage + "%" , font, Brushes.DarkGray, rectangle, stringFormat);

                            // Save indicator to file
                           objGraphics.Flush();
                     }
                      return (Image) bitmap;
              }
       }
}

and copy this dll to the DNN bin folder and also adds an entry to the AppSettings section of web.config like this:

<add key="DnnImageHandler.Percent" value="MyNamespace.PercentTransform, NameOfMyDll" />

he is able to invoke the imagehandler with an URL syntax like this:

http://mydomain.com/DnnImagehandler.ashx?mode=percent&percentage=30&color=green

Percentage

So this is a really cool extension point where developers can gain leverage from the imagehandler foundation. This is for example one extension I built for a renting module

Booking schedule

The possibilities are endless ! Think of watermarking images in an ecommerce application or adding fancy borders to images.

So, what do you think ? Should this go into the core ? Pull request is pending! And regarding the initial idea: I promise to work on it later, even if the imagehandler will be denied. But I really believe that this one could be a really powerfull image engine for DNN and a unique feature point in competition with other CMS Systems.

Back

about.me.

Torsten WeggenMy name is Torsten Weggen and I am CEO of indisoftware GmbH in Hanover, Germany. I'm into DNN since 2008. Before this, I did a lot of desktop stuff mainly coded with Visual Foxpro (see http://www.auktionsbuddy.de). 

I'm programmer, husband, father + born in 1965.

Please feel free to contact me if you have questions.

Latest Posts

DNN module development with Angular 2+ (Part 7)
6/10/2018 1:43 PM | Torsten Weggen
DNN module development with AngularJS (Part 6)
12/16/2016 7:00 AM | Torsten Weggen
DNN module development with AngularJS (Part 5)
12/16/2016 6:00 AM | Torsten Weggen
DNN module development with AngularJS (Part 4)
12/16/2016 5:00 AM | Torsten Weggen
DNN module development with AngularJS (Part 3)
12/16/2016 4:00 AM | Torsten Weggen
DNN module development with AngularJS (Part 2)
12/16/2016 3:00 AM | Torsten Weggen
DNN module development with AngularJS (Part 1)
12/15/2016 7:19 AM | Torsten Weggen
Blogging in DNN with Markdown Monster by Rick Strahl
11/27/2016 1:14 PM | Torsten Weggen
Creating a global token engine
11/18/2016 10:25 AM | Torsten Weggen
DnnImagehandler - Hot or not ?
2/21/2015 11:52 PM | Torsten Weggen

My Twitter

Keine News gefunden!