Alias: two paths for the same content

Topics: Troubleshooting
Jun 6, 2012 at 8:58 AM

I have one page that I want to be accessible via two urls.

(Background: I'm using a url prefix to change the layout, so when a certain page must be displayable with this certain layout, I add an url via Alias UI).

Suppose there is a main url (set automatically when page is created) "my-page".

Now, I add an alias manually via alias UI, to the same content "Contents/Item/Display/14" via "hl/my-page".

Now the page is accessible both via "/my-page" and "/hl/my-page".

But if I edit the page (note the permalink displayed is still "my-page"), the additional alias is removed and only the default permalink remains.

Is it an expected behavior ? Is there another way to make a page available via two or more urls and does not lost these aliases when editing the page ?

Ps: I'm using source code enlistment

Jun 6, 2012 at 10:14 AM

I have read the source code and now I understand this behavior : when saving the autoroute part, the AutorouteService.PublishAlias method has no clue about the previous value, so it call DefaultAliasService.Replace(), which remove any alias with a different path (removing my custom one) before creating the new one.

I'm trying to find if there is a way to know if an alias was created "by hand" or by the AutoroutePartHandler.

Jun 6, 2012 at 11:15 AM

I've found a first solution, that I post here for review and comments.

Because page allow versioning (and so is autoroutepart) I can get the displayalias from the previous version.

It allows IAliasService.Replace() to only delete the previous alias if the parameter is supplied.

I've changed IAliasService to add a new method signature :

void Replace(string aliasPath, RouteValueDictionary reuteValues, string aliasSource, string previousAliasPath);

And DefaultAliasService implements it this way :

public void Replace(string aliasPath, RouteValueDictionary routeValues, string aliasSource) {
    Replace(aliasPath, routeValues, aliasSource, null);
}

public void Replace(string aliasPath, RouteValueDictionary routeValues, string aliasSource, string previousAliasPath) {
    foreach (var lookup in Lookup(routeValues).Where(path => path != aliasPath && (previousAliasPath == null || previousAliasPath == path))) {
        Delete(lookup);
    }
    Set(aliasPath, routeValues, aliasSource);
}

See the func<> which now only removes previousAliasPath when specified (when null it reverts to current behavior).

So, now, AutorouteService.PublishAlias() tries to resolve previousAliasPath :

public void PublishAlias(AutoroutePart part) {
    var displayRouteValues = _contentManager.GetItemMetadata(part).DisplayRouteValues;

    string previousDisplayAlias = null;
    if(part.ContentItem.Version > 1) {
        // Item is versioned and has previous revisions, we try to load the DisplayAlias value from the previous revision
        var previousPart = _contentManager.Get<AutoroutePart>(part.Id, VersionOptions.Number(part.ContentItem.Version - 1));
        if (previousPart != null) {
            previousDisplayAlias = previousPart.DisplayAlias;
        }
    }

    var aliasSource = "Autoroute:View:" + part.Id;

    _aliasService.Replace(part.DisplayAlias, displayRouteValues, aliasSource, previousDisplayAlias);

    _routeEvents.Routed(part, part.DisplayAlias);
}

I've tested with some content samples with success.

I'm not confident enough with the orchard codebase, and this code seems to be a "hack" more than a "feature", considering that this behavior will only work when content is versioned.

Perhaps there is a better way to retrieve previous value for the alias ? The current db model is available somewhere ?

Coordinator
Jun 6, 2012 at 5:52 PM

I would rather go with the url rewrite module.

Coordinator
Jun 6, 2012 at 5:53 PM

But the alias should also handle that, might be worth opening a bug.