//TODO: add caching
    public ActionResult MenuWidget(string workspace, string collection, int? year,
      int? month, int? day, string path)
    {
      MenuModel model = new MenuModel();

      //add home link
      model.MenuItems.Add(new MenuItem()
      {
        Href = Url.Content("~/"),
        Text = "Home",
        Selected = Request.Url.AbsoluteUri.ToLower()
        .StartsWith(Request.Url.GetLeftPart(UriPartial.Authority) + Request.ApplicationPath)
      });

      //add link for each visible collection in the workspace
      if (Workspace != null)
      {
        foreach (AppCollection coll in Workspace.Collections.Where(c => c.Visible))
        {
          if (!coll.Visible) continue;
          string view = "AtomPubCollectionIndex";
          if (!string.IsNullOrEmpty(coll.DefaultView)) view = coll.DefaultView;
          model.MenuItems.Add(new MenuItem()
          {
            Href = Url.RouteIdUrl(view, coll.Id),
            Title = coll.Title.ToString(),
            Text = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(coll.Id.Collection),
            Selected = Request.Url.AbsoluteUri.ToLower()
            .StartsWith(Url.RouteIdUrl(view, coll.Id, AbsoluteMode.Force).Split('.').First().ToLower())
          });
        }
      }
      EnforceMaxSelection(model.MenuItems);
      return PartialView("MenuWidget", model);
    }
    //TODO: add caching?
    public ActionResult CustomWidget(Include include)
    {
      MenuModel model = new MenuModel();
      var menuItems = new MenuCustomInclude(include).MenuItems.Select(mi => new MenuItem()
      {
        Href = mi.Href,
        Title = mi.Title,
        Text = mi.Text,
        ExactSelect = mi.ExactSelect
      }).ToList();

      if (menuItems.Count() < 1) throw new ArgumentException("Include is missing custom menu items.");

      // mark selected, first pass do exact
      bool exact = false;
      foreach (MenuItem item in menuItems.Where(mi => mi.ExactSelect))
      {
        bool selected = item.Href.Substring(item.Href.IndexOf('/')) == Request.Url.PathAndQuery;
        if (selected)
        {
          item.Selected = true;
          exact = true;
          break;
        }
      }

      // no exact match, do fuzzy match
      if (!exact)
      {
        foreach (MenuItem item in menuItems.Where(mi => !mi.ExactSelect))
        {
          int lastSlash = item.Href.LastIndexOf('/');
          if (lastSlash < 1) lastSlash = item.Href.Length - 1;
          string areaPath = item.Href.Substring(0, lastSlash).Split('.').First().ToLower();
          item.Selected = Request.Url.PathAndQuery.ToLower().StartsWith(areaPath);
        }
      }

      foreach (MenuItem item in menuItems) model.MenuItems.Add(item);
      EnforceMaxSelection(model.MenuItems);
      return PartialView("MenuWidget", model);
    }