Urler

En sida i en Dynamixlösning har alltid en huvud-url, vad vi kallar den "kanoniska" urlen. Man kan eventuellt nå sidan via andra urler, t ex om man har satt upp genvägar till sidan, men webbläsaren kommer då skickas vidare till den kanoniska urlen. Detta är viktigt bl a av seo-skäl.

Navigeringslägen

Sidor har (i första hand) fyra lägen för vilken sidans kanoniska url skall vara, detta kan man ändra genom att redigera egenskaperna för sidan, t ex genom att högerklicka på sidan i lösningsträdet.

  • Sidans namn – innebär att sidan får sin url efter namnet på sidan och dess position i sajtträdet

  • Angivet namn – innebär att man kan ange ett annat namn som ska användas i urlen, i övrigt identisk med ”Sidans namn”

  • Sidans id – innebär att sidan får en url med sidans id-nummer inbakat, detta kan vara användbart om det finns flera sidor som annars skulle få samma url.

  • Sajtens inställning – innebär att standardinställningen för sajten används, denna kan vara antingen ”Sidans id” eller ”Sidans namn”

Man kan surfa till en sida via andra urler än den kanoniska, man kan t ex alltid nå en sida via dess id, men då kommer alltid webbläsaren skickas vidare till den kanoniska urlen.

Genvägar

Man kan dessutom ha ett antal genvägar som skickar vidare webbläsaren till sidans kanoniska url, detta är användbart både om man vill ha en url som är lätt att komma ihåg (t ex minsajt.se/blogg skickar vidare till minsajt.se/Svenska/Innehall/Startsida_blogg) och om man har flyttat sidor eller migrerat från en annan lösning (tex minsajt.se/blog.php?view=categorylist skickar vidare till minsajt.se/Svenska/Innehall/Blog/Kategorier)

För en specifik sida redigeras genvägarna enklast via egenskaperna, samma flik som url-inställningarna.

Om man vill få en överblick över alla genvägar i lösningen kan man använda genvägsverktyget, där kan man även lägga upp genvägar till andra objekt som implementerar IUrl.

Routing

Om man vill att en sida ska ha en annan kanonisk url än de som är möjliga genom att ändra navigeringsläge och/eller skicka med parametrar till sidan kan man göra en egen router-metod som registreras hos Dynamix huvud-router (förslagsvis i Global.asax). Denna routermetod kommer sedan att anropas när Dynamix försöker matcha en url med rätt sida och då kan man kolla om den efterfrågade urlen ska skickas vidare till en annan sida och sätta eventuella parametrar.

  1. protected void Application_Start(Object sender, EventArgs e)
  2. {
  3. ...
  4. Dynamix.Routing.Routes.Router += Blog_Router;
  5. ...
  6. }

Routing-metoden får ett objekt av typen Dynamix.Routing.RoutingEventArgs som parameter, detta objekt har ett antal egenskaper som du kan skriva till i din metod för att precisera hur en url ska hanteras.

  • Site - denna parameter kan du läsa av för att se vilken sajt besökaren har surfat till i en lösning som innehåller flera webbplatser

  • OriginalHRef – den efterfrågade urlen, kontrollera om detta är en url som du vill hantera

  • IsMatch – sätt denna till true om du har hanterat urlen

  • NewUrl – den nya url som ska användas istället för den efterfrågade

  • Action – ange om det ska göras en intern övergång (InternalTransfer) eller om den nya urlen ska skickas tillbaka till webbläsaren (Redirect)

  • Parameters – här kan du lägga in parametrar som kan läsas av innehåll på sidan, detta motsvarar url-parametrar

  • IsDone - denna ska sättas till true om NewUrl pekar på en "fysisk" fil (en egen aspx eller liknande), om NewUrl går till en Dynamix-sida krävs ett ytterligare routing-steg och IsDone ska då vara false.

Ett exempel är om du har blogginlägg på sajten som i lösningsträdet ligger direkt under "blog" men där du vill att enskilda inlägg ska ha urler av formen "minsajt.se/blog/kategori/rubrik". I din router-metod behöver du då kontrollera om OriginalHRef matchar ett blogginlägg och sätta NewUrl till urlen för blogginläggets sida.

  1. void Blog_Router( Dynamix.Routing.RoutingEventArgs e )
  2. {
  3. // Try to find a matching blog page
  4. Dynamix.NavigationItem blogPage = GetBlogPage( e.OriginalHref );
  5. // If we found a blog page and it is active we will redirect to that page
  6. if( blogPage != null && blogPage.IsActive )
  7. {
  8. e.IsMatch = true;
  9. e.NewUrl = blogPage.Url;
  10. e.Action = Dynamix.Routing.RoutingAction.InternalTransfer;
  11. e.IsDone = false;
  12. }
  13. }

Om du har en sida som kan visa blogginlägg som hör till en viss kategori kan du även se till man kan komma till en lista med inlägg som hör till en viss kategori via urler av formen minsajt.se/blog/kategori genom att routa sådana urler till sidan som visar kategorilistan och skicka med den efterfrågade kategorin i en parameter.

  1. void Blog_Router( Dynamix.Routing.RoutingEventArgs e )
  2. {
  3. ...
  4. // Try to find a matching blog category
  5. BlogCategory category = GetBlogCategory( e.OriginalHRef );
  6. // If we found a category, redirect to the category page and set the parameter
  7. if( category != null )
  8. {
  9. e.IsMatch = true;
  10. e.NewUrl = GetBlogCategoryPage().Url;
  11. e.Action = Dynamix.Routing.RoutingAction.InternalTransfer;
  12. e.AddParameter( "category", category );
  13. e.IsDone = false;
  14. }
  15. }

I innehållskontrollen eller mallen kan man sedan läsa parametern och visa blogginlägg från den kategorin.

  1. void RenderBlogPosts( HtmlTextWriter writer )
  2. {
  3. //Get category from parameters
  4. var category = RequestContext.Current.GetParameterObject<BlogPostCategory>( "category" );
  5. //Render blogposts in category
  6. ...
  7. }

Ändra kanonisk url

En nackdel med att bara registrera en router-metod är att även om en sidas url externt (från en besökares perspektiv) verkar vara en annan än dess kanoniska url så är det fortfarande den kanoniska urlen som returneras av NavigationUtil.GetObjectUrl, dvs interna länkar till sidan kommer att använda minsajt.se/blog/sidnamn även om du har gjort en router som ser till att du kan surfa till sidan via minsajt.se/blog/kategori/rubrik.

För att ändra sidans kanoniska url måste du registrera en UrlProvider för typen NavigationItem.

  1. [assembly: UrlProvider( ProviderType = typeof( BlogUrlProvider ), TargetType = typeof( NavigationItem ) )]

I metoden GetUrl kan du sedan kontrollera om objektet som skickas in är en sida som du vill ta hand om, och returnera den url som du tycker att den ska ha. I exemplet nedan kollar vi om sidan är en bloggsida och returnerar blogginläggets url.

  1. public class BlogUrlProvider : IUrlProvider
  2. {
  3. public Url GetUrl( object x )
  4. {
  5. var item = x as NavigationItem;
  6. if( item != null )
  7. {
  8. // Try to find a blog post with this page
  9. var post = BlogPost.GetBlogPost( item );
  10. if ( post != null )
  11. return new Url( post.GetUrl() );
  12. }
  13. // item is not a NavigationItem null or it does not represent a blog post
  14. return null;
  15. }
  16. }

Navigationsstruktur

Ibland vill man att den navigationsstruktur som besökare upplever skall vara en annan än den som syns i Dynamix-gränssnittet (t ex i panelen "Webbplatser och sidor"). En vanlig sitiuation är när du vill lägga till nya "virtuella" sidor som pekar på en existerande sida med vissa parameter satta, t ex om du har en sida som visar information om en produkt kan du på detta sätt få det att se ut som om varje produkt har en egen, unik sida. Ett annat exempel är om du har artiklar som taggas med kategorier, då kanske du vill ha en virtuell sida för varje kategori och visa de artiklar som tillhör en kategori som undersidor till kategorisidan.

Som vi såg tidigare kan man registrera en routermetod som hanterar urler av formen minsajt.se/blog/kategorinamn och ser till att de routas till en sida som visar en lista med blogginlägg och skickar med "kategorinamn" som en parameter så att bara blogginlägg som hör till den kategorin visas. Ett problem med denna lösning är att om din sajt använder standardkontroller för navigation och/eller sajtkarta/sidindex, eller en modul (t ex Initivas Seo-modul) som genererar en xml-sajtkarta för sökmotorer, vill du nog att dina "virtuella" kategorisidor kommer med även där, men standardkontrollerna kan ju inte veta att det ska finnas fler sidor och var de ska ligga i strukturen. Detta problem kan lösas genom att registrera en NavigationProvider.

I de flesta fall är navigationsträdet uppbyggd av Site- och NavigationItem-objekt men man kan även lägga in andra typer av objekt. Både Site och NavigationItem implementerar interfacet INavigation och kan returnera ett NavigationInformation-objekt som innehåller information om dess plats i navigationsträdet men genom att registrera en NavigationProvider har man möjlighet att returnera annan information än standard.

Här följer ett exempel på hur man kan ha virtuella sidor för produkter. Notera att klassen Product antingen måste implementera IUrl eller ha en registrerad UrlProvider som returnerar vilken url enskilda produkter ska ha, förmodligen något i stil med: http://www.minsajt.se/produkter/produktid, samt att det måste finnas en router-metod som hanterar urler på den formen, lagrar produktid i en parameter och skickar vidare till http://www.minsajt.se/produkter eller motsvarande.

  1. [assembly: NavigationProvider( ProviderType = typeof( ProductNavigationProvider ), TargetType = typeof( NavigationItem ) )]
  2. [assembly: NavigationProvider( ProviderType = typeof( ProductNavigationProvider ), TargetType = typeof( Product ) )]
  3. public class BlogNavigationProvider : INavigationProvider
  4. {
  5. public NavigationInformation GetNavigationInformation( object x, User user )
  6. {
  7. var item = x as NavigationItem;
  8. if( item != null )
  9. {
  10. if( Equals( x, Settings.Instance.ProductsRoot ) )
  11. {
  12. return new ProductsRootNavigationInformation( Dynamix.Util.CurrentUser );
  13. }
  14. }
  15. var product = x as Product;
  16. if( product != null )
  17. {
  18. return new ProductPageNavigationInformation( Dynamix.Util.CurrentUser, Product product );
  19. }
  20. }
  21. }
  22. public class ProductsRootNavigationInformation: NavigationItemNavigationInformation
  23. {
  24. public ProductsRootNavigationInformation( User user )
  25. : base( Settings.Instance.ProductsRoot , user )
  26. {
  27. }
  28. public override IEnumerable<object> NavigationChildren
  29. {
  30. get
  31. {
  32. return Dynamix.Object.GetAll<Product>().Cast<object>().ToList();
  33. }
  34. }
  35. }
  36. public class ProductPageNavigationInformation: NavigationInformation
  37. {
  38. private Product _product;
  39. public ProductPageNavigationInformation( User user, Product product )
  40. {
  41. _product = product;
  42. }
  43. public override IEnumerable<object> NavigationChildren
  44. {
  45. get
  46. {
  47. yield break;
  48. }
  49. }
  50. public override object NavigationParent
  51. {
  52. get
  53. {
  54. return Settings.Instance.ProductsRoot;
  55. }
  56. }
  57. public override bool IsNavigationVisible
  58. {
  59. get
  60. {
  61. return false;
  62. }
  63. }
  64. public override bool IsContentVisible
  65. {
  66. get
  67. {
  68. return true;
  69. }
  70. }
  71. public override bool IsIndexed
  72. {
  73. get
  74. {
  75. return true;
  76. }
  77. }
  78. }

När man gör en kontroll eller liknande som ska använda sig av navigationsstukturen, t ex en meny-navigationskontroll, bör man inte använda INavigation-interfacet direkt utan hämta NavigationInformation-objektet via NavigationUtil.GetNavigationInformation för att eventuella NavigationProviders ska få möjlighet att bygga om navigationsträdet.

Här följer ett enkelt exempel på hur en kontroll som renderar ut en sajtkarta kan se ut:

  1. public class MySiteMapControl : System.Web.UI.WebControls.CompositeControl
  2. {
  3. protected override void Render( HtmlTextWriter writer )
  4. {
  5. writer.WriteFullBeginTag("ul");
  6. foreach( var site in Dynamix.GetAll<Site>() )
  7. {
  8. RenderObject(site);
  9. }
  10. writer.WriteEndTag("ul");
  11. }
  12. private void RenderObject( HtmlTextWriter writer, object obj )
  13. {
  14. var navigationInformation = NavigationUtil.GetNavigationInformation( obj, Util.CurrentUser );
  15. var url = NavigationUtil.GetObjectUrl( obj );
  16. writer.WriteFullBeginTag("li");
  17. writer.WriteBeginTag( "a" );
  18. writer.WriteAttribute( "href", url.HRef );
  19. writer.Write(">");
  20. writer.Write( NamesUtil.GetObjectName( obj ) );
  21. writer.WriteEndTag( "a" );
  22. writer.WriteFullBeginTag("ul");
  23. foreach( var child in navigationInformation.NavigationChildren )
  24. {
  25. RenderObject( child );
  26. }
  27. writer.WriteEndTag("ul");
  28. writer.WriteEndTag("li");
  29. }
  30. }