Download pages as PDF

Every now and then a customer requests that the pages in their website be downloadable as PDF and although it feels a bit “web 1.0” there are situations in which it makes sense. I guess there is still a mojority of people that like to hold a product sheet in their hands instead of viewing it on a screen. Nevertheless, I created a simple handler that will serve the page as pdf. To get the actual Pdf rendering done I used ABCPDF, which I found ultra simple to implement.

The ABCPDF wrapper class

First step is to create a wrapper class with a method to render any url as PDF and output the result to a file. By serving files we will able to add some caching to the solution and not have the overhead of processing a PDF for each page.

public static class AbcPdf
    public static void CreatePdf(string url, string fileName)
        //Create a document and set the options
        var theDoc = new WebSupergoo.ABCpdf8.Doc();
        theDoc.HtmlOptions.Media = WebSupergoo.ABCpdf8.MediaType.Print;
        theDoc.HtmlOptions.RequestMethod = WebSupergoo.ABCpdf8.UrlRequestMethodType.Get;
        theDoc.HtmlOptions.Paged = true;
        theDoc.HtmlOptions.Engine = WebSupergoo.ABCpdf8.EngineType.Gecko;
        theDoc.HtmlOptions.UseScript = true;
        theDoc.Rect.Inset(50, 50);

        //Insert the url html (example of AbcPdf)
        int theID;
        theID = theDoc.AddImageUrl(url);
        while (true)
            if (!theDoc.Chainable(theID))
            theDoc.Page = theDoc.AddPage();
            theID = theDoc.AddImageToChain(theID);
        for (int i = 1; i <= theDoc.PageCount; i++)
            theDoc.PageNumber = i;

        //Create a directory if needed and save the pdf

The link between the CMS and ABCPDF

Next we create a class that will be the link between our CMS and the ABCPDF wrapper. It will simply get the page url based on a CMS page id using the CMS api; in this case EPiServer. It then compares the last publication date of the page in the CMS with the filetime of the last rendered pdf version of the page. If the file does not exist or is too old we create the PDF and return the file location.

public static class Pdfs
    private static string _pdfCachePath = ConfigurationManager.AppSettings["pdfcachePath"];

    public static string GetPdf(int pageId, out string pageName)

        //Get the date and time of the last cached pdf if available
        var fileName = Path.Combine(_pdfCachePath, pageId.ToString()) +  ".pdf";
        var exists = File.Exists(fileName);
        DateTime? fileTime = null;
        if (exists)
            var fi = new FileInfo(fileName);
            fileTime = fi.LastWriteTime;

        //Get the pagedata and retrieve the name
        PageData page = DataFactory.Instance.GetPage(new PageReference(pageId));
        pageName = page.PageName;

        //If we have a filetime we get the page and see if the page is newer. 
                  //If so we will make a new pdf
        //If we have no filetime we always create the pdf
        var createPdf = false;
        if (fileTime == null)
            createPdf = true;
            //Get the page modfieid date and time
            var pageTime = page.Saved;
            if (pageTime > fileTime)
                createPdf = true;

        //Create as requested using the url of the page;
        if (createPdf)
            var url = page.GetExternalUrl();
            AbcPdf.CreatePdf(url, fileName);

        //Return the filename
        return fileName;

And ofcourse the handler

Finally we create the handler that will use the previous two classes to create the pdf file if needed and then serve it to the client. It receives the id to the CMS page and returns the PDF result file as bytes and with a content-disposition so the client can choose to open or save the PDF.

public class ServePdf : IHttpHandler
    public void ProcessRequest(HttpContext context)
        //Get the page id which needs to be returned as pdf
        var pid = int.Parse(context.Request["pid"] ?? "0");

        //If correct: get the pdf file location and return the file-data in the response
            string name;
            var file = Pdfs.GetPdf(pid, out name);
            name = Utils.Strings.ToSlug(name) + ".pdf";
            context.Response.AddHeader("Content-disposition""attachment; filename=" + name);
            context.Response.Status = "404 Not Found";
        context.Response.ContentType = "application/pdf";

    public bool IsReusable
            return false;


Clearly no rocket science, but if you do run into this request it might help get you up to speed. Of course there are other solution available besides ABCPDF and this is in no way a promotional post for that product. However, having tried both open source and payed products I think that the price of ABCPDF is by any means preferable above the hassle I encoutered using e.g. iTextSharp.

Posted in ASP.Net, EPiServer | Tagged , , , , , | Leave a comment

Keeping track of images in dynamic content plugins

Using images in dynamic content plugins is easy and something I use a lot to keep control over images that editors insert to the their content. Although the following code shows how easy this is done the EPiServer dynamic content plugin framework does not seem to take into account that images and files might be moved to another folder in the file manager. This post will explain how to solve this little caveat.

[EPiServer.DynamicContent.DynamicContentPlugIn(DisplayName = "Image")]
public partial class Image : DynamicContentBase
   // Property for holding the image state.
   private PropertyImageUrl _image;

   public Image()
      //Prepare the property
      _image = new PropertyImageUrl();
      _image.Name = "Image";

   public string File
         //If available: return the image file path
         if (_image != null && !_image.IsNull)
            return _image.Value.ToString();
         return string.Empty;

   public override Control GetControl(PageBase hostPage)
      //Rendering will be done using the Render method
      return null;

   public override string Render(PageBase hostPage)
      //Return an image tag with the selected image
      return string.Format("<img src=\"{0}\" />", this.Image);

   public override bool RendersWithControl
      //Tell the EPiServer to use the render method
      get { return false; }

Although this is a very easy way to create a Dynamic Content Plugin and uses the build-in EPiServer PropertyImage is seems to come with one major problem: EPiServers filemanager does not keep track of the stored image data when used in a dynamic content plugins. When you add an image to a pagetype this does happen and when you move an image to another folder you even get the pages where the image is used. If you then proceed with the relocation all references to the image on pages are automatically updated.

Recently I ran into this problem when an editor of a website decided to reorganize their files and images. As a result all pages with moved files showed images with red crosses (IE). Clearly I had to find another way of storing the image since I couldn’t depend on EPiServer here. The solution lay in what was actually stored in the state bag op de plugin.

The solution

The quick way to build the plugin is letting EPiServer handle the read and writes of the plugin data but apparently EPiServer stores the virtual path of the jmage, including the folder in which it resides. Since the virtual path changes when the file is moved this will break the image when that happens.

To overcome this we need to tell EPiServer to store a more persistant reference to the file and after some searching found the PermanentFileLink property on the UnifiedFile object. This property contains a url to the file that stays the same regardless of its location in the filemanager. Ideal for this situation and easily implemented since the UnifiedFile constructor takes both virtual paths and permanent links so switching from one to the other is easy.

All I had to do now was to override the state read and write procedure of the plugin and tell EpiServer to store the permanent link and to convert it to a virtual link before parsing it into the image property. This shows how:

public override string State
      //If there is an image file path: build the state datat
      if (!string.IsNullOrWhiteSpace(this.File))
         //Decode the file path 
         var img = HttpUtility.UrlDecode(this.File);

         //Get the permanent link to the image because the 
         //permanent link does not change when the image is moved
         //to another folder in the filemanager.
         var imgLink = string.Empty;
         if (!string.IsNullOrEmpty(img))
            //Get the unified file object from EPiServer using the
            //virtual filepath of the selected image
            var ufImg = (UnifiedFile)System.Web.Hosting.HostingEnvironment.VirtualPathProvider.GetFile(img);
            //Retrieve the permanent link to the image
            imgLink = ufImg.PermanentLinkVirtualPath.ToString();
         //The state we return is the permanent link
         return imgLink;
      //If no image is selected, return an empty state
      return string.Empty;
      //Process the state if there is a value 
      if (!string.IsNullOrEmpty(value))
         //Create an EPiServer UnifiedFile object from the state
         //data which should contain the permanent link to the 
         var ufImg = (UnifiedFile)System.Web.Hosting.HostingEnvironment.VirtualPathProvider.GetFile(imgLink);
         //Use the virtual filepath of the object to parse into
         //the PropertyImage object

Obviously this will work for documents as well. If the plugin consists of more then one property then remind to add those to the state read and write procedures. Since EPiServer won’t take care of this anymore you have to implement your own method. A quick and easy way is to concatenate the values of each property with e.g. a pipe (|) when the state get is called and split it up again when it is set.

Posted in ASP.Net, EPiServer | Tagged , , | Leave a comment

ASP.Net: Create a guestbook using a twitter account

The following describes how to add a guestbook or mini forum to your website using only twitter:

1. Create a new twitteraccount that will serve all your guestbook messages

2. Register a twitter application through It will provide you with all the needed keys to communicate with your new twitter account.

3. Create or open a Asp.Net website or web project

4. Download the twitterizer library from and reference it in your project.

5. Create a tweet object that we will use to generate our own list of tweets

public class Tweet
    //Public properties
    public bool isShoutbox { get; set; }
    public string Text { get; set; }
    public DateTime Time { get; set; }
    public string Author { get; set; }

    /// <summary>
    /// Formats the tweet to give a nice html representation of the 
    /// message replacing url's @ and # tags.
    /// </summary>
    public string Html
            //Replace urls' with hyperlinks using regex
            var html = this.Text;
            string regex = 
                9\/_:@=.+?,##%&~-]*[^.|\'|\# |!|\(|?|,| |>|<|;|\)])";
            Regex r = new Regex(regex, RegexOptions.IgnoreCase);
                r.Replace(html, "<a href=\"$1\" title=\"Click to open\" 
                        .Replace("href=\"www", "href=\"http://www");

            //Replace @ tags with hyperlinks using regex
            regex = @"(^|\s)@(\w+)";
            r = new Regex(regex, RegexOptions.IgnoreCase);
            html = 
                r.Replace(html, "<a target=\"_blank\" class=\"external\" 

            //Replace # tags with hyperlinks using regex
            regex = @"(^|\s)#(\w+)";
            r = new Regex(regex, RegexOptions.IgnoreCase);
            html = r.Replace(html, "<a target=\"_blank\" class=\"external\"  

            return html;

6. Create a helper class called twitter.cs which contains a few methods that we will use to post our guestbook messages with and to generate an overview of existing messages. Make sure you replace the OAuth imformation with the information you got when registering your application.

public static class Twitter
    //Declare global variables for the class
    private static OAuthTokens _tokens;

    static Twitter()
        //Init the twitterizer library token

    private static void InitTwitterizer()
        //Create a new tokens class and set you personal values
        _tokens = new Twitterizer.OAuthTokens();
        _tokens.AccessToken = "YOUR TOKEN";
        _tokens.AccessTokenSecret = "YOUR SECRET";
        _tokens.ConsumerKey = "YOUR CONSUMER KEY";
        _tokens.ConsumerSecret = "YOUR CONSUMER SECRET";

    /// <summary>
    /// Creates and adds a new tweet/message to the initialized twitter 
    /// </summary>
    /// <param name="tweet">the message</param>
    /// <param name="name">the name of the anonymous person who is sending 
        the tweet</param>
    public static void PostTweet(string tweet,string name)
        //We add the text shoutbox to indicate that the message on our twitter 
        //account is added outside our control
        //We also add the name in brackets so we can show it in our guestbook
        tweet = "SHOUTBOX(" + name + ") " + tweet;

        //We check the lenght of the message and cut it off at 140 (twitter's max)
        if (tweet.Length > 140) { tweet = tweet.Substring(0, 140); }
        //We send the full new tweet to our twitter feed.
        Twitterizer.TwitterStatus.Update(_tokens, tweet);

    /// <summary>
    /// Creates a list of tweet objects using our own twitter feed as well 
    /// as tweets mentioning us
    /// </summary>
    /// <returns>A list of tweet objects</returns>
    public static List<Tweet> GetTweets()
        //Create options: for now we only get the first page. 
        //We can always implement multi pages if needed
        var options = new TimelineOptions();
        options.Page = 1;
        //Create a new empty list
        var tweets = new List<Tweet>();

        //Retrieve all tweets from our own timeline and add them to the list.
        foreach (var status in Twitterizer.TwitterTimeline.HomeTimeline
            (_tokens, options).ResponseObject.ToList())
            AddTweetToList(tweets, status);

        //Retrieve all tweets mentioning us and add them to the list
        foreach (var status in Twitterizer.TwitterTimeline.Mentions(_tokens, options)
            AddTweetToList(tweets, status);

        //Return the returning list after sorting it descending by date/time
        return tweets.OrderByDescending(t => t.Time).ToList();

    private static void AddTweetToList(List<Tweet> tweets, TwitterStatus status)
        //Create new tweet object and set the basic properties
        var tweet = new Tweet();
        tweet.isShoutbox = false;
        var text = status.Text;
        var author = status.User.Name;
        //If the tweet starts with our own @ tag we might as wel 
        //remove it for clearancy
        if (status.Text.ToLower().StartsWith("@yourtwitteraccount"))
            text = text.Substring(6);
        //If the tweet starts with shoutbox we remove it from the tweet 
        //and set the IsShoutbox prop to true
        //We also get the name of the anonymous author from the brackets behind it.
        if (status.Text.StartsWith("SHOUTBOX("))
            author = status.Text.Substring(9, status.Text.IndexOf(")") - 9);
            text = text.Substring(status.Text.IndexOf(")") + 1);
            tweet.isShoutbox = true;

        //Set the remaining properties.
        tweet.Text = text;
        tweet.Author = author;
        tweet.Time = status.CreatedDate;

7.Create a new usercontrol that will host the existing messages and call it TwitterFeed.ascx:

<div id="tweets">
    <asp:Repeater runat="server" ID="rptTweets">
        <%#DataBinder.Eval(Container.DataItem,"Author") %>                
        <asp:PlaceHolder runat="server" ID="phSB" Visible="false">
            <img src="/images/sb.png" alt="shoutbox icon" />
            <%# (Container.DataItem as Twitter.Tweet)
                .Time.ToString("d MMM H:mm")%>
            <%#DataBinder.Eval(Container.DataItem,"Html") %>

8. And add the following in the code-behind file:

protected void Page_PreRender(object sender, EventArgs e)
    //Set-up a handler so we can so some stuff when a tweet is 
    //processed by the repeater
    rptTweets.ItemCreated += new RepeaterItemEventHandler(rptTweets_ItemCreated);

    //Bind the repeater to the list of tweets using the helper
    rptTweets.DataSource = Twitter.GetTweets();

void rptTweets_ItemCreated(object sender, RepeaterItemEventArgs e)
    //Only process if we are dealing with an actual item 
    //(and not the header or footer etc...)
    if (e.Item.ItemType == ListItemType.Item 
        || e.Item.ItemType == ListItemType.AlternatingItem)
        //Get the binding tweet object
        var item = e.Item.DataItem as Twitter.Tweet;

        //In case it is a shoutbox message 
        //(meening it was posted using the guestbook and not by our selves)
        //Find the placeholder for the Shoutbox icon and make it visible.
        if (item.isShoutbox)
            var ph = e.Item.FindControl("phSB") as PlaceHolder;
            ph.Visible = true;

9. Create another usercontrol called TwitterForm.ascx and add the following to the ascx file:

<h2>NEW MESSAGE</h2>
<div id="twitterForm">
        <asp:Label Text="Naam" runat="server" AssociatedControlID="txtName" />
        <asp:TextBox runat="server" ID="txtName" CssClass="textField" />
        <asp:Label ID="Label1" Text="Bericht" runat="server" 
            AssociatedControlID="txtTweet" />
        <asp:TextBox CssClass="textField" TextMode="MultiLine" Rows="5" 
            Columns="30" MaxLength="140" ID="txtTweet" runat="server" />
        <asp:Button ID="btnAddMessage" runat="server" Text="Verstuur" 
            OnClick="btnAddMessage_Click" CssClass="button" />

10. Add the following to the conde-behind file

protected void btnAddMessage_Click(object sender, EventArgs e)
    //Post the anonymous tweet usintg the filled-in message and the filled-in name
    Twitter.PostTweet(txtTweet.Text, txtName.Text);
    //Reset the text
    txtTweet.Text = "";

11. Add the user controls to a webpage depending on your specific needs

12. Style the controls using the classes in the html or add classes as needed

13. Build and run the project

Depending on your styling you could get something like this:

Final thoughts

I found this an amazingly easy way yo create a guestbook for my website without having to create a storage location and writing code to select and insert data. Furthermore you get the automatic advantage that visitors of your guestbook can follow the twitter account and be instantly updated on what is said. And even better, they can send messages to the guestbook instantly from their twitter application. The cool thing is that people that have no affinity with twitter are not confronted with it and can still add their oppinions. This is why it is wise to add the prominent SHOUTBOX text to the message when it comes from the website guestbook, since you want to make absolutely clear that the message did not originate from the owner of the twitter account.

Posted in ASP.Net | Tagged , , , | 5 Comments

Javascript: Case insensitive highlighting of a searchterm

Lets say you have a page with searchresults base on the search term ‘foo’ and you want to highlight all appearances of that word in the results list html. Well, a simple replace function won’t work, because it will not take into account that some appearances of the word might be in uppercase of partially in uppercase. Also, using a /term/gi will not work because you would replace the uppercased word with the original term, which might be is in lowercase. So, this is the solution:

var html = '<ul><li>These are the foo fighters</li>';
html+='<li>Foo fighters are the best</li></ul>';
var term = 'foo';
var re = new RegExp('(' + term + ')', 'gi');
html = html.replace(re, '<span class="highlighted">$1</span>');

Obviously you can set any formatting on the highlighted class to make it appear the way you want in the browser.

Posted in Javascript | Tagged , , , | Leave a comment

EPiServer: Extending the DataFactory for Pagetypebuilder

I just love using Pagetypebuilder for EPiServer. Not only because it makes it all a whole lot faster to setup, but whenever I can use typed objects instead of string based references I am in. But since the api of EPiServer is not (yet) build on the same principal each page is returned as the PageData object and can only by distinguished by type through its type name or id. Having embraced the strongly typed world given to me by Pagetypebuilder I was not going to let it stop here.

Digging in I quickly concluded that the simplest way to strongly verify the page type I could do a type check using MyObject.GetType == typeof(PagetypebuilderObject) or one of the other typical ways to compare object types. Strangely though I was soon to discover that these comparisons did not always return true where I would expect them to be. I am still not completely sure why but it seems to be caused by the dynamic proxy classes of the Castle library that Pagetypebuilder uses. To my surprise however I found that the IsAssignableFrom method of the Type object was much better in determining this equality I was looking for.

So, having tackled this hurdle in didn’t take much time to create some nice extension methods for the datafactory, pagebase and pagedata classes of EpiServer. I thought I’d share them:

DataFactory Extensions

Get parents

This method gets all the ancestors starting at the given fromRoot reference that are of the given pagetype (or derived from that type) and returns them as a list of that type.

public static List<T> GetParents<T>(this DataFactory df, PageReference fromRoot)
    var refs = df.GetParents(fromRoot);
    var data = new PageDataCollection();
    foreach (var reference in refs)
        var page = df.GetPage(reference);
        if (typeof(T).IsAssignableFrom(page.GetType()))
    return data.Cast<T>().ToList();

Get descendants

This method gets all the descendents  that are not deleted starting at the given fromRoot reference that are of the given pagetype (or derived from that type) and returns them as a list of that type. Default it filters for visitors unless specified not to.

public static List<T> GetDescendents<T>(this DataFactory df, PageReference fromRoot, bool doNotFilterForVisitor=false)
    var refs = df.GetDescendents(fromRoot);
    var data = new PageDataCollection();
    foreach (var reference in refs)
        var page = df.GetPage(reference);
        if (typeof(T).IsAssignableFrom(page.GetType()) && !page.IsDeleted)
    if (!doNotFilterForVisitor)
    return data.Cast<T>().ToList();

Get all

Uses GetDescendants but starts and node 1

public static List<T> GetAll<T>(this DataFactory df)
    return df.GetDescendents<T>(new PageReference(1));

Get children

This method gets the children directlyl under the parent reference that are of the given pagetype (or derived from that type) and returns them as a list of that type. It filters for visitors and skips deleted pages.

public static List<T> GetChildren<T>(this DataFactory df, PageReference parent)
    var data = df.GetChildren(parent);
    return data.Where(c => typeof(T).IsAssignableFrom(c.GetType()) && !c.IsDeleted).Cast<T>().ToList();

PageBase Extensions

Get children

This one simply uses the previous extensions but it saves typing DataFactory.Instance I guess.

public static List<T> GetChildren<T>(this PageBase pb, PageReference parent)
    return DataFactory.Instance.GetChildren<T>(parent);

PageData Extensions

Get children and get parents

Another two methods that use the earliers Datafactory extension methods but come in handy when you’re already working with a pagedata object. And since all Pagetypebuilder objects eventually derive from Pagedata they will autmoaticly have them too.

public static List<T> GetChildren<T>(this PageData pb)
    return DataFactory.Instance.GetChildren<T>(pb.PageLink);

public static List<T> GetParents<T>(this PageData pb)
    return DataFactory.Instance.GetParents<T>(pb.PageLink);

Get the external url

This one returns the full url of a page including the hostheader etc.

public static string GetExternalUrl(this PageData page)
    string result = string.Empty;
    if (page.PageLink != null && page.PageLink.ID > 0)
        var url = new UrlBuilder(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority) +
        HttpContext.Current.Request.ApplicationPath.TrimEnd('/') + page.LinkURL);
        if (UrlRewriteProvider.IsFurlEnabled)
            Global.UrlRewriteProvider.ConvertToExternal(url, null, Encoding.UTF8);
            result = url.ToString();
            result = url.ToString();
    return result;
Posted in EPiServer | Tagged , , , , | Leave a comment

Seneca presents concept roadmap iXperion

With the turbulent year at Seneca it was good to get everybody together for an update on Smartsite. And so we were invited a month ago in a little meeting room in Nieuwegein to hear about its future, .Net migration paths and get the opportunity to give Seneca our opinion. I myself was happy to renew my expectations on Smartsite and getting some positive feeling after all the postponing of the 1.2 release lately.

Arriving a little late I entered the presentation during the introduction of Smartsite codenames Eagle and Condor. Apparently these catchy names are the future to be iXperion 2 and iXperion 3, where the first will be a 67% .Net migration of the CMS engine and the latter will finally give us a 100% .Net build platform. With this being in the first quarter of 2011 it took Seneca a good 10 years to migrate Smartsite from VB6. Let’s hope it’s worth the wait. As for me, 2011 seems a little too far in the future to get all jolly about. And although Seneca says they want to put more effort in keeping their projected planning, my past experiences are still a bit too fresh in my memory. Maybe I’ll get a bit calmer once the roadmap is final and the first goals of it are met.

So, what else does the roadmap have to offer. Basically it can be split up in 3 phases: extending the modules within the current iXperion1 (Falcon), then building towards iXperion2 (Eagle) and finally the release of iXperion3 (Condor). Oh, and then there’s about 2 quarters going into building extensions for their own customers. I guess they need a little attention too🙂

Phase 1, iXperion 1 (Falcon)

The first phase is expected to last until Q2 of 2010, focuses on the current version and is expected to consist of:

  • Smartsite Client Framework (Smartlets).
  • Oracle support.
  • Database Creation Script.
  • Smartlet Editor
  • Forms Integration improvement
  • Outscaling improvements (dashboard)
  • LDAP, Active Directory improvements
  • Set Site Security Utility
  • Smartsite Personalization Framework
  • Modules: DigiD, Ogone, Personalization

As said, I think the release of iXperion2 is too far away, but some of these features would make me very happy. Especially the forms integration, outscaling improvements and site security drew my attention. Although I have to warn that the forms improvement will only consist of minor changes since a complete overhaul of the forms generator is part of the Eagle release. Leaves me with the hope on an ouscaling console that actually communicates what it is doing and a site security interface that will finally let me control the access level of one item without having to decipher the hierarchy for an hour. Perhaps a lot of you out there are also looking forward to the Smartlet improvements. I for one, have not yet been blown away by their power, but then again, I have not dug into them very extensively either. You can expect however that there’ll be more and more smartlets for grabs concerning web2.0 features. I believe some Flickr-based smartlets are already available.

Phase 2, iXperion 2 (Eagle)

This phase will focus mostly on the migration and improvements of the CMS for web-editors and providing for a .Net based editorial manager. As for the technical side things will be addressed in the next phase, so don’t expect too much improvement if you’re a developer. Btw, we’re already talking Q1, 2011 now, so don’t hold your breath. In a nutshell, the following will be addressed between Q3-2010 and Q1-2011:

  • Forms Engine (webeditor/ manager). usability in Smartlets, building forms and as a basis for the new manager.
  • Workflow Engine (webeditor / manager). Front End (E-business) and back End (CMS processes)
  • CMS Engine (webeditor / manager)
  • Connecting layer(user interface, browser-independency
  • Role based UI
  • Modules: SMI2.0 and Smartsite Indexed Search

Even though I am a developer myself, I am still looking forward to these changes. Having a proper forms-manager and finally being able to tell my customers that they can use their beloved browser can’t come too soon. As for the Role based UI, I’m not quite clear what to expect, but I believe it will shield all unnecessary parts from the manager the web-editor does not want to be bothered with. Next, some front-end editing capabilities were mentioned. Oh, and of course the introduction of a real search engine will spice things up nicely.

Phase 3, iXperion 3 (Condor)

Well, we’re way into the next decennium by then and all deadlines have been met (I hope). Still, if everything goes well, we’ll reach Q3 2011 with a smile on our face as the first full .Net based Smartsite is released. What else to expect in this release? Here it is:

  • Configure outscaling
  • Configure Contenttypes
  • Configure Security
  • Data Management

What this all means exactly will still remain a mystery for a while I guess. According to the people at Seneca it will all consist of major improvements in comparison to the current workings. Good! because I can think of an improvement or two in each category. Besides this Seneca expects to work on some improvements in the maintenance, migration and deployment areas.


I was and am happy to see Seneca being transparent about their plans. This gives me and others an idea of what to expect and how to prepare for the future when it comes to implementing Smartsite. Still,  it’s only a concept roadmap, which is also why I didn’t give a fully detailed view of it, and I am in anticipation of the final version. Rumors have it that Seneca is still trying to find ways to bring the release of Eagle and Condor further forward. Something I’d be very happy to see. Anyway, roadmaps give a sense of control and are in that way very valuable. Nevertheless, with the danger of  them being unrealistic and resulting in deadlines that cannot be met, they have the potential to build up hopes that can’t be met. I’ll give it the benefit of the doubt.

Posted in Press | Tagged , , | Leave a comment

Who am I

Ok ok, for years I was that person at parties that said that the internet was not meant to be a trashbin for all sorts of non needed information about peoples private little issues. And, I still believe this to be the case. On the other hand though, there are many people who do put content on the internet that make my life easier and more fun. Not a day goes past without me looking up technical solutions or read reviews about holiday locations, travel agencies and so forth. All this information is provided to me without questions asked, without having to pay. All I have to do is to consume it and make my life more confortable. This is why I came to think that where one takes it’s only fair enough to add my own pieces of knowledge to this ever expanding pile of information.

So, who am I? My name is Sandor Voordes and I work for a full service web company in the Netherlands, called Tam Tam. In my role of developer / architect I deal with areas like Visual Studio, .Net, Smartsite, Silverlight, jQuery and other’s. Expect this blog to be focussing mostly on this subjects and with an amount of technoligcal content that  requiers the reader to have have a general knowledge of them.

So, in fear of already writing more then needed, I’ll stop my introduction here and hope to be adding pages to this blog on a regular basis. I’ve agreed with myself to give it 6 months. If nothing or to little has been posted by then I’ll accept my loss and stick to my current contribution to the content of the internet.


Posted in Uncategorized | Tagged , , , , , | Leave a comment