Tuesday, 24 February 2009

EPiServer -- Deleted Pages


I was asked recently a few times how to detect deleted pages. Deleted from editors perspective, which means -- moved to the Recycle Bin. Answer to this question is really simple, PageData class has a property called IsDeleted, here is an example:

   1:  public static bool IsPageDeleted(PageReference pageRef)
   2:  {
   3:      PageData page = DataFactory.Instance.GetPage(pageRef);
   4:      return page.IsDeleted;
   5:  }

In fact, that is all what is needed to check if page was deleted. Below I would like to list additionally a few related tips which might be useful:
  • You can get reference to the Recycle Bin thanks to this static property:

       PageReference.WasteBasket
  • Another way to check if instance of PageReference class points to the Recycle Bin is to call this method:

       DataFactory.Instance.IsWastebasket(new PageReference(12))
  • If you would like to move a page to the Recycle Bin programmatically then you don't really want to delete the page, you should use this method instead:

       DataFactory.Instance.MoveToWastebasket(new PageReference(121));
  • EPiServer has by default scheduled job called "Automatic Emptying of Recycle Bin" which is responsible for:
    With Automatic Emptying of Recycle Bin, you can set how often your Recycle Bin should be emptied. The aim of this function is to stop old information from being left in the Recycle Bin for a long period of time. With automatic emptying, all information that is older than 30 days will be deleted from the Recycle Bin.

    So what can be wrong when pages from your Recycle Bin don't get removed? The most likely option is that this scheduled job is not activated, make sure that checkbox Active is checked ;)
If you need further details or some relevant information is missing then feel free to leave a comment.

Other interesting posts:

Wednesday, 18 February 2009

Google Trends

Have you ever wanted to check how popular are certain key words in search engines? Or maybe how busy are popular websites? Now it's all possible with Google Trends.

For instance you can check number of daily unique visitors for popular websites like Twitter and Digg and additionally, you can compare them on a single chart:

Clearly you can see that Twitter is growing whereas Digg has some problems with keeping the levels.

If you check Search Volume Index then you will get the same conclusion:

On this graph you don't see actual traffic numbers ...
The numbers you see on the y-axis of the Search Volume Index aren't absolute search traffic numbers. Instead, Trends scales the first term you've entered so that its average search traffic in the chosen time period is 1.0; subsequent terms are then scaled relative to the first term. Note that all numbers are relative to total traffic.
It's not my intention here to show you that Twitter is trendy, I want to give you an idea how interesting information you can find with Google Trends. You can now easily check how popular different ideas/technologies/products/politicians are. Try to see for instance how rapidly is growing number of searches for ASP.NET MVC - it's growing really fast. (Actually, thanks to an article Interest in ASP.NET MVC is raising I have learned about Google Trends)

Remember though, Google Trends will give you only estimated values, those are not accurate data:
It's important to keep in mind that all results from Trends for Websites are estimated. Moreover, the data is updated periodically, so recent changes in traffic data may not be reflected. Finally, keep in mind that Trends for Websites is a Google Labs product, so it's still in its early stages of development and may therefore contain some inaccuracies.
In opinion ... even though the data are estimated and not all websites are included ... it's still an awesome tool!

Tuesday, 17 February 2009

FluentConfiguration -- New API to configure NHibernate

Fluent NHibernate from the very beginning provides really clean API to configure NHibernate. I didn't expect to see any changes in this area ... but yet new "fluent" way to configure NHibernate has been introduced.

This is the way I was using so far (it still works well):

   1:  private static Configuration GetNHibernateConfig()
   2:  {
   3:      return MsSqlConfiguration.MsSql2005
   4:          .ConnectionString(c => c.Is(@"Data Source=db_server;Database=db_name;...."))
   5:          .UseReflectionOptimizer()
   6:          .ShowSql()
   7:          .ConfigureProperties(new Configuration());
   8:  }
   9:  
  10:  public static ISessionFactory GetSessionFactory()
  11:  {
  12:      // configure nhibernate
  13:      Configuration config = GetNHibernateConfig();
  14:  
  15:      var models = new PersistenceModel();
  16:      
  17:      // alter default conventions if necessary
  18:      SetUpConvention(models.Conventions);
  19:  
  20:      models.addMappingsFromAssembly(typeof (Product).Assembly);
  21:      models.Configure(config);
  22:  
  23:      // save xml files with mappings to some random location
  24:      models.WriteMappingsTo(@"c:\dev\mappings");
  25:  
  26:      // build factory
  27:      return config.BuildSessionFactory();
  28:  }
  29:  

Honestly I didn't expect that readability can be improved much ... but check this:

   1:  public static ISessionFactory GetFluentlyConfiguredSessionFactory()
   2:  {
   3:      return Fluently.Configure()
   4:          .Database(MsSqlConfiguration
   5:                        .MsSql2005
   6:                        .ConnectionString(c => c.Is(@"Data Source=db_server;Database=db_name;....")))
   7:  
   8:          .Mappings(m =>
   9:                    m.FluentMappings.AddFromAssemblyOf<Product>()
  10:                        .ConventionDiscovery.Add(new AdventureWorksConvention())
  11:                        .ExportTo(@"c:\dev\mappings"))
  12:  
  13:          .BuildSessionFactory();
  14:  }

What I really like about Fluent NHibernate is that it doesn't force user to take all or nothing. If you want you can easily use combine different types of mappings. For instance you can add fluent-nh to your existing project, reuse old mappings (XML files) and add new mappings configured with fluent API. Here is an example:

   1:  public static ISessionFactory GetFluentlyConfiguredSessionFactoryWithHbmFiles()
   2:  {
   3:      return Fluently.Configure()
   4:          .Database(MsSqlConfiguration
   5:                        .MsSql2005
   6:                        .ConnectionString(c =>
   7:                                          c.Is(@"Data Source=db_server;Database=db_name;....")))
   8:  
   9:          .Mappings(m =>
  10:                        {
  11:                            m.FluentMappings.AddFromAssemblyOf<Product>()
  12:                                .ConventionDiscovery.Add(new AdventureWorksConvention())
  13:                                .ExportTo(@"c:\dev\mappings");
  14:                            m.HbmMappings.AddFromAssemblyOf<Product>();
  15:                        })
  16:  
  17:          .BuildSessionFactory();
  18:  }
In a similar way it is possible to combine standard fluent-nh mappings with XML files and auto mapping.

I didn't include in this post anything about conventions to keep things short and concise but you can find details in this post.

Related posts:

Monday, 16 February 2009

EPiServer - Outgoing Links

In this post I will show how to get list of all referenced pages (and files) for any EPiServer page. Although it sounds like a trivial task, in fact, it's not so obvious. First of all it's necessary to realize that there are two major groups of "linking" properties:
  • Properties that derive from PropertyPageReference, internally they store link as a page id. Out of the box there in only one property type in EPiServer which uses this class -- PageReference.
  • And the bunch of properties which use permanent links internally like:
    • PropertyImageUrl - Url to image
    • PropertyDocumentUrl - Url to document
    • PropertyUrl - URL to page/external address
    • PropertyXhtmlString - Xhtml Long String
    • PropertyLinkCollection - Link Collection
It's fairly simple to get referenced page from PropertyPageReference:

   1:  var pageReference = CurrentPage.Property["propert_name"] as PropertyPageReference;
   2:  var page = DataFactory.Instance.GetPage(pageReference.PageLink);

What about other property types? There is a one common thing for them -- they all implement IReferenceMap interface:


We can use following code to get outgoing links:

   1:  var referenceMap = property as IReferenceMap;
   2:  if (referenceMap != null)
   3:  {
   4:      IList<Guid> linkIds = referenceMap.ReferencedPermanentLinkIds;
   5:      foreach (Guid guid in linkIds)
   6:      {
   7:          PermanentLinkMap map = PermanentLinkMapStore.Find(guid);
   8:          
   9:          // mappedUrl example: /Templates/Public/Pages/NewsItem.aspx?id=30
  10:          string mappedUrl = map.MappedUrl.ToString();
  11:  
  12:          // and get friendly URL version using UrlRewriteProvider
  13:          var url = new UrlBuilder(mappedUrl);
  14:          EPiServer.Global.UrlRewriteProvider.ConvertToExternal(url, null, System.Text.Encoding.UTF8);
  15:          string friendlyUrl = UriSupport.AbsoluteUrlBySettings(url.ToString());
  16:      }
  17:  }

What are permanent links?

Internal URL's in EPiServer are stored in the database using a format called Permanent Links. Property types are responsible to transform a URL from a permanent link to a standard template link upon access from user code, and of course the other way around before content is stored to the database.
It's a very useful feature of EPiServer because it enables you to manipulate files and pages without risk that some links will get broken.
You can rename files and templates without affecting the links; you can even move an EPiServer site from a virtual directory to a root site without breaking a single link.

Permanent Links and EPiserver's API

IReferenceMap Interface exposes ReferencedPermanentLinkIds property thanks to which we have access to all links stored internally. That is very convenient especially for properties like PropertyXhtmlString which usually also store lots of other data. It is worth noticing that PropertyLongString doesn't implement this interface, hence it doesn't use permanent links. That is a reason why PropertyXhtmlString is recommended over PropertyLongString.

PermanentLinkMapStore class is a part of EPiServer's API for permanent links. I used this class to get mapped URL based on link's Guid. In next step mapped URL can be converted to friendly URL (code based on Ted Nyberg's post). Permanent link can be broken (referenced page was deleted) in which case PermanentLinkStore.Find() method will return null.

Based on above code I have created an edit mode plugin which lists all outgoing links, it looks like this:

Source code can be downloaded from here.

Other interesting posts:

Saturday, 14 February 2009

Basic Software Estimation Concepts

In one of my recent posts I was writing that single point estimates are meaningless. In this post I would like to carry on with this topic and talk about a few other fundamental concepts for software estimation based on Steve McConnell's "Software Estimation: Demystifying the Black Art".

One of the most important things is to know the difference between estimates, targets and commitments.
While a target is a description of a desirable business objective, a commitment is a promise to deliver defined functionality at a specific level of quality by a certain date. A commitment can be the same as the estimate, or it can be more aggressive or more conservative than the estimate. In other words, do not assume that the commitment has to be the same as the estimate; it doesn't.
It's quite typical situation when developers are asked to estimate new project or piece of functionality for which deadline is already set. You have to know if you are really asked to provide estimates or to figure our how to meet a deadline. Those are two totally different things. Estimation should be unbiased therefore deadline doesn't matter. If deadline matters then you are, in fact, asked to provide a plan in which goal is to deliver before the deadline.

Single point estimates are meaningless
, estimations should always be represented as a range -- the best and the worst scenario. Don't estimate only at the beginning of a project. At every stage estimates can be useful and they can show that project goal is in danger.
Once we make an estimate and, on the basis of that estimate, make a commitment to deliver functionality and quality by a particular date, then we control the project to meet the target. Typical project control activities include removing non-critical requirements, redefining requirements, replacing less-experienced staff with more-experienced staff, and so on
Controlling the project include dealing with changing requirements. But with new requirements estimations also change and after a few iterations your target is to deliver something radically different then it was estimated at the very beginning. How can you say then if initial estimates were accurate?

In practice, if we deliver a project with about the level of functionality intended, using about the level of resources planned, in about the time frame targeted, then we typically say that the project "met its estimates," despite all the analytical impurities implicit in that statement.

If it's well known that assumptions will change, functionality will change then what is the real purpose of estimates?

The primary purpose of software estimation is not to predict a project's outcome; it is to determine whether a project's targets are realistic enough to allow the project to be controlled to meet them.

Important implication is that gap between estimates and actual times has to be small enough to be manageable. According to book, 20% is the limit which can be controlled.

Estimates don't need to be perfectly accurate as much as they need to be useful. When we have the combination of accurate estimates, good target setting, and good planning and control, we can end up with project results that are close to the "estimates."

That takes us to a definition of "good estimate"
A good estimate is an estimate that provides a clear enough view of the project reality to allow the project leadership to make good decisions about how to control the project to hit its targets.
All of that and much more can be found in the book, it's worth reading.

Thursday, 12 February 2009

EPiServer 5 R2 and Link Collection property

With EPiServer 5 R2 new property type was released -- Link Collection. It looks like a EPiServer's version of very popular Mulitipage property. In this post I would like to show you exactly how it can be used and also what are the pros and cons.

After adding a property of this type to a page you will see in edit mode this:


And with a few links added property looks like this:


This is first significant change comparing to old Multipage property (MP) -- list of all links is visible on the page. With old MP it was necessary to click on the button to get a popup with a list of links. That is a good change!

What is missing here for me is a ability to test links. Text which you can see for the first item on the list is not necessary a page name (it might be a clickable text) so it's impossible to figure out from this view what page is referenced.

Funny thing is that title for this link has a following form:

It is very useful isn't it? ;) I think the simplest solution would be to make link text clickable.

After clicking on 'Add Link' or 'Edit' button you will get old popup:


There are no surprises here, it's an old well-know dialog.

Lets check now how to deal with Link collection in a code. It's quite common to use Repeater to display links:

   1:  <asp:Repeater ID="rptRelatedLinks" runat="server">
   2:      <HeaderTemplate><dl></HeaderTemplate>
   3:      <ItemTemplate><dt><asp:HyperLink runat="server" ID="hplMainLink" /></dt></ItemTemplate>
   4:      <FooterTemplate></dl></FooterTemplate>
   5:  </asp:Repeater>

And here is a code to get links from the CurrentPage:

   1:  PropertyLinkCollection links = (PropertyLinkCollection) CurrentPage.Property["RelatedLinks"];
   2:  rptRelatedLinks.DataSource = links;
   3:  rptRelatedLinks.DataBind();

And a method populating the Repeater:

   1:  void rptRelatedLinks_ItemDataBound(object sender, RepeaterItemEventArgs e)
   2:  {
   3:      if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
   4:      {
   5:          LinkItem linkItem = (LinkItem) e.Item.DataItem;
   6:          HyperLink link = (HyperLink) e.Item.FindControl("hplMainLink");
   7:  
   8:          // mapped link has a form like:
   9:          // <a href="/Templates/Public/Pages/Page.aspx?id=16&epslanguage=en" target="_blank" 
  10:          //       title="this is link title">Information about the meeting</a>
  11:          string mappedLink = linkItem.ToMappedLink();
  12:          
  13:          // permanent link form:
  14:          // <a href="~/link/bb6aa3227f8f467bbe1a42154cb56ba5.aspx" target="_blank" 
  15:          //           title="this is link title">Information about the meeting</a>
  16:          string permanentLink = linkItem.ToPermanentLink();
  17:  
  18:          // because Href property will return permanent link like 
  19:          // ~/link/bb6aa3227f8f467bbe1a42154cb56ba5.aspx
  20:          //
  21:          // it's necessary to use PermanentLinkMapStore.ToMapped(url) to covert it to normal form
  22:          // result is required to determine if conversion was successful
  23:          // it will fail for mails (mailto:test@test.com), documents and external links
  24:          UrlBuilder url = new UrlBuilder(linkItem.Href);
  25:          bool result = PermanentLinkMapStore.ToMapped(url);
  26:  
  27:          link.NavigateUrl = result ? url.ToString() : linkItem.Href;
  28:          link.Text = linkItem.Text;
  29:          link.ToolTip = linkItem.Title;
  30:          link.Target = linkItem.Target;
  31:      }
  32:  }

The basic problem is that Href property returns permanent link, therefore it's necessary to use PermanentLinkMapStore class to convert the links. ToMappedLink() method returns a full "a" tag, which might be convenient in some cases. Take a look on all properties again:

My overall impression is positive, new property Link collection is easy to use but for sure there are things which could be improved like ability to test a link or ability to define page root to look for pages to include. It's a hassle to always start from the very top!

I wonder now if there is still a reason to use old Multipage property, what do you think?

Related posts:

Sunday, 8 February 2009

That's what I call a comfortable office

You guys know Joel Spolsky right? At the moment he is a CEO at Fog Creek Software, small company in Manhattan. I'm really impressed with their new office, take a look on a slideshow.

Joel was writing a few times that his main goal is to provide the best possible environment for software developers and this way gain the highest productivity:

Building great office space for software developers serves two purposes: increased productivity, and increased recruiting pull. Private offices with doors that close prevent programmers from interruptions allowing them to concentrate on code without being forced to stop and listen to every interesting conversation in the room. And the nice offices wow our job candidates, making it easier for us to attract, hire, and retain the great developers we need to make software profitably. It’s worth it, especially in a world where so many software jobs provide only the most rudimentary and depressing cubicle farms.
I know exactly how important that is as I'm lucky to work for company having the same goal. But still, there are things I feel jealous about after checking the slideshow. Beside the awesome design I especially like 30“ monitors (I had a chance to work a bit on 22“ monitors and I'm 100% convinced that it makes a difference) or long desks (huge monitors require huge desks ;) ). I'm sure that one day I will talk my boss into buying such a nice monitors for us ;)


Here you can find Joel's post about new office, and an article about the office in The New York Times.