Tales from the Evil Empire

Bertrand Le Roy's blog

News


Bertrand Le Roy

BoudinFatal's Gamercard

Tales from the Evil Empire - Blogged

Blogs I read

My other stuff

Archives

March 2012 - Posts

Overriding the Pager rendering in Orchard

The Pager shape that is used in Orchard to render pagination is one of those shapes that are built in code rather than in a Razor template. This can make it a little more confusing to override, but nothing is impossible.

If we look at the Pager method in CoreShapes, here is what we see:

[Shape]
public IHtmlString Pager(dynamic Shape, dynamic Display) {
    Shape.Metadata.Alternates.Clear(); 
    Shape.Metadata.Type = "Pager_Links";
    return Display(Shape);
}

The Shape attribute signals a shape method. All it does is remove all alternates that may exist and replace the type of the shape with “Pager_Links”. In turn, this shape method is rather large and complicated, but it renders as a set of smaller shapes: a List with a “pager” class, and under that Pager_First, Pager_Previous, Pager_Gap, for each page a Pager_Link or a Pager_Current, then Pager_Gap, Pager_Next and Pager_Last.

Each of these shapes can be displayed or not depending on the properties of the pager. Each can also be overridden with a Razor template. This can be done by dropping a file into the Views folder of your theme. For example, if you want the current page to appear between square braces, you could drop this Pager-CurrentPage.cshtml into your views folder:

<span>[@Model.Value]</span>

This overrides the original shape method, which was this:

[Shape]
public IHtmlString Pager_CurrentPage(HtmlHelper Html, dynamic Display,
    object Value) {
    var tagBuilder = new TagBuilder("span");
    tagBuilder.InnerHtml = Html.Encode(Value is string ?
        (string)Value : Display(Value));
            
    return MvcHtmlString.Create(tagBuilder.ToString());
}

And here is what it would look like:An overridden current page template

Now what if we want to completely hide the pager if there is only one page? Well, the easiest way to do that is to override the Pager shape by dropping the following into the Views folder of your theme:

@{
    if (Model.TotalItemCount > Model.PageSize) {
        Model.Metadata.Alternates.Clear();
        Model.Metadata.Type = "Pager_Links";
        @Display(Model)
    }
}

And that’s it. The code in this template just adds a check for the number of items to display (in a template, Model is the shape) and only displays the Pager_Links shape if it knows that there’s going to be more than one page.

ZenGallery: a minimalist image gallery for Orchard

The gallery, added to a commerce siteThere are quite a few image gallery modules for Orchard but they were not invented here I wanted something a lot less sophisticated that would be as barebones and minimalist as possible out of the box, to make customization extremely easy. So I made this, in less than two days (during which I got distracted a lot).

Nwazet.ZenGallery uses existing Orchard features as much as it can:

  • Galleries are just a content part that can be added to any type
  • The set of photos in a gallery is simply defined by a folder in Media
  • Managing the images in a gallery is done using the standard media management from Orchard
  • Ordering of photos is simply alphabetical order of the filenames (use 1_, 2_, etc. prefixes if you have to)
  • The path to the gallery folder is mapped from the content item using a token-based pattern
  • The pattern can be set per content typeSetting the pattern
  • You can edit the generated gallery path for each itemEditing the path
  • The default template is just a list of links over images, that get open in a new tab
  • No lightbox script comes with the module, just customize the template to use your favorite script.Using a lightbox script
  • Light, light, light.The gallery displays thumbnails in admin summary view

Rather than explaining in more details this very simple module, here is a video that shows how I used the module to add photo galleries to a product catalog:

Adding a gallery to a product catalog

You can find the module on the Orchard Gallery:
https://gallery.orchardproject.net/List/Modules/Orchard.Module.Nwazet.ZenGallery/

The source code is available from BitBucket:
https://bitbucket.org/bleroy/nwazet.zengallery

Drawing transparent glyphs on the HTML canvas

The HTML canvas has a set of methods, createImageData and putImageData, that look like they will enable you to draw transparent shapes pixel by pixel. The data structures that you manipulate with these methods are pseudo-arrays of pixels, with four bytes per pixel. One byte for red, one for green, one for blue and one for alpha. This alpha byte makes one believe that you are going to be able to manage transparency, but that’s a lie.

Here is a little script that attempts to overlay a simple generated pattern on top of a uniform background:

var wrong = document.getElementById("wrong").getContext("2d");
wrong.fillStyle = "#ffd42a";
wrong.fillRect(0, 0, 64, 64);
var overlay = wrong.createImageData(32, 32),
    data = overlay.data;
fill(data);
wrong.putImageData(overlay, 16, 16);

where the fill method is setting the pixels in the lower-left half of the overlay to opaque red, and the rest to transparent black.

And here’s how it renders:putImageData replaces existing pixels

As you can see, the transparency byte was completely ignored. Or was it? in fact, what happens is more subtle. What happens is that the pixels from the image data, including their alpha byte, replaced the existing pixels of the canvas. So the alpha byte is not lost, it’s just that it wasn’t used by putImageData to combine the new pixels with the existing ones.

This is in fact a clue to how to write a putImageData that works: we can first dump that image data into an intermediary canvas, and then compose that temporary canvas onto our main canvas. The method that we can use for this composition is drawImage, which works not only with image objects, but also with canvas objects.

var right = document.getElementById("right").getContext("2d");
right.fillStyle = "#ffd42a";
right.fillRect(0, 0, 64, 64);
var overlay = wrong.createImageData(32, 32),
    data = overlay.data;
fill(data);
var overlayCanvas = document.createElement("canvas");
overlayCanvas.width = overlayCanvas.height = 32;
overlayCanvas.getContext("2d").putImageData(overlay, 0, 0);
right.drawImage(overlayCanvas, 16, 16);

And there is is, a version of putImageData that works like it should always have:Using an intermediary canvas gets the transparency right

My Orchard comment notification rule

The Rules module in Orchard enable you to set-up “if this then that” types of rules. The system is fully extensible in terms of what “this” and “that” are of course, but Orchard comes with everything you need out of the box to set-up comment notifications. Let’s create this rule:Comment notification rule

Before you begin, you need to make sure you have the rules features installed and enabled.

For comment notifications, “this” is: “when content with type comment is published”, and “that is: “send an e-mail”.

In order to reproduce this, create a new rule named “Content Notification”. Add an event, choose “Content Published” and select “Comment” as the published type:Choose the Comment type

Now we will add a “Send e-mail action”. As the recipient of the e-mail, I usually set “Site Admin”, but many people will want the mail to be sent to the owner of the blog. To get that result, you can select “Other” with the following pattern:

{Content.CommentedOn.Container.Author}

As the subject of the e-mail, I use this pattern:

nwazet.com: A new comment has been posted by {Content.CommentAuthor}

Finally, as the body, I use the following:

<p>New comment by {Content.CommentAuthor} on {Content.CommentedOn.DisplayText}</p>

<p>{Content.CommentMessage}</p>

<p><a href="http://nwazet.com/admin/comments">Moderate</a></p>

Once you’ve saved all that, don’t forget to enable the new rule, and you should now get an e-mail every time someone posts a comment. Oh, one last thig: you need e-mail settings to be correctly set-up for this to work, naturally.

More Posts