måndag 30 juli 2012

Gruppera sidtyper i adminläget på EPiServer CMS 5

Hej igen, åter från semester och tänkte slänga upp lite fulkod som grupperar sidtyperna i adminläget på EPiServer CMS 5.

Jag har nämligen upplevt att när man bygger en stor EPiServer-site med många sidtyper, så blir det ofta rörigt att bläddra bland sidtyper i adminläget. Eftersom så gott som samtliga projekt jag varit med i följt namnsättningsstandarden som EPiServer själva följer i sitt templates-projekt, med prefixet [Public] osv., så kändes gruppering på prefix som den logiska vägen att gå.

Obs. på att följande lösning enbart är testad i EPiServer CMS 5 R2 SP2 (5.2.375.236)

Hittar ni några fel eller andra dômmheter så hojta gärna till!

Steg 1, hitta & öppna aspx-sida som sköter EPiServers adminläge-meny.

Den återfinns under EPiServers installationsmapp, t.ex. "C:\Program Files\EPiServer\CMS\5.2.375.236\Application\UI\Admin\" och heter menu.aspx. Börja med att skapa en backup av filen.

Steg 2, lägga in javascript för gruppering

Öppna sedan filen i din favoriteditor, leta upp raden:


EPi.AddEventListener(window, "load", EPiCheckSortOrder);

och släng in följande javascript efteråt:


/* Change grouping behavior to selected one */
function changeGrouping(grouping) {
    var pageTypeTreeGrouping = document.getElementById('PageTypeTreeGrouping');

    if (!window.top.epiMenuGrouping)
        window.top.epiMenuGrouping = null;

    // Already toggled, untoggle
    if (window.top.epiMenuGrouping == grouping) {
        pageTypeTreeGrouping.className = 'EPEdit-CommandTool';
        window.top.epiMenuGrouping = null;
        UnGroupPageTypesByPrefix();
    } else {
        switch (grouping) {
            case 'prefix':
                pageTypeTreeGrouping.className = 'EPEdit-CommandToolClicked';
                GroupPageTypesByPrefix();
                break;
        } // Add more grouping alternatives when implemented...
        window.top.epiMenuGrouping = grouping;
    }
}

/* For browser compability (IE) */
var getElementsByClassName = function(oNode, class_name) {
    var docList = oNode.all || oNode.getElementsByTagName('*'),
            matchArray = new Array(),
            re = new RegExp("(?:^|\\s)" + class_name + "(?:\\s|$)"),
            i = 0,
            docListLength = docList.length;

    for (i = 0; i < docListLength; i = i + 1) {
        if (re.test(docList[i].className)) {
            matchArray[matchArray.length] = docList[i];
        }
    }
    return matchArray;
};

/* Toggles selected group visibility */
function toggleGroup() {
    var oParent = this.parentElement,
            oToggle = getElementsByClassName(oParent, 'toggle'),
            oExpandIcon = getElementsByClassName(oParent, 'expandIcon');
    if (!!oToggle && oToggle.length > 0 && !!oExpandIcon && oExpandIcon.length > 0) {
        oToggle = oToggle[0];
        var toggleVisible = oToggle.style.display == 'none';
        oToggle.style.display = (toggleVisible ? '' : 'none');
        oExpandIcon = oExpandIcon[0];
        oExpandIcon.style.backgroundImage = (toggleVisible ? 'url(../../App_Themes/Default/Images/ExplorerTree/minus.gif)' : 'url(../../App_Themes/Default/Images/ExplorerTree/plus.gif)');
    }
    return false;
};

/* Ungroups the page types and restores them into default behavior */
function UnGroupPageTypesByPrefix() {
    var oListDiv = document.getElementById('PageTypeListDiv'),
            oMenuItems = getElementsByClassName(oListDiv, 'EPAdmin-menuItem'),
            clonedMenuItems = [],
            i = 0;

    for (i; i < oMenuItems.length; i = i + 1) {
        clonedMenuItems.push(oMenuItems[i].cloneNode(true));
    }
    oListDiv.innerHTML = '';
    for (i = 0; i < clonedMenuItems.length; i = i + 1) {
        oListDiv.appendChild(clonedMenuItems[i]);
    }
};

/* Groups the page types depending on their prefix (eg. [Public] ... */
function GroupPageTypesByPrefix() {
    var oListDiv = document.getElementById('PageTypeListDiv'),
            oPageTypeLinks = getElementsByClassName(oListDiv, 'pageTypeLink'),
            linkGroups = [],
            i = 0;

    /* Get prefixes */
    for (i; i < oPageTypeLinks.length; i = i + 1) {
        var pageTypeName = oPageTypeLinks[i].innerText.replace(new RegExp(" ", "g"), '').replace(/(\r\n|\n|\r)/gm, ""),
                pageTypePrefixes = pageTypeName.match("\\[[^\\]]*]"),
                pageTypePrefix = (!!pageTypePrefixes && pageTypePrefixes.length > 0) ? pageTypePrefixes[0] : "";

        if (!!!pageTypePrefix) pageTypePrefix = '[ ]';

        if (!!!linkGroups[pageTypePrefix]) {
            linkGroups.push(pageTypePrefix);
            linkGroups[pageTypePrefix] = [oPageTypeLinks[i].cloneNode(true)];
        } else {
            linkGroups[pageTypePrefix].push(oPageTypeLinks[i].cloneNode(true));
        }
    }

    oListDiv.innerHTML = '';

    /* Create prefix group holders */
    var oDiv,
            oInnerDiv,
            j = 0,
            expandIcon,
            groupTitle = '',
            oClearDiv;

    for (i = 0; i < linkGroups.length; i = i + 1) {
        oDiv = document.createElement('div');
        oDiv.className = 'group';
        oDiv.style.marginTop = '5px';
        oListDiv.appendChild(oDiv);

        expandIcon = document.createElement('span');
        expandIcon.className = 'expandIcon';
        expandIcon.style.backgroundImage = 'url(../../App_Themes/Default/Images/ExplorerTree/plus.gif)';
        expandIcon.style.width = '15px';
        expandIcon.style.height = '18px';
        expandIcon.style.position = 'absolute';
        expandIcon.style.cursor = 'pointer';
        expandIcon.style.backgroundPosition = 'left center';
        expandIcon.style.backgroundRepeat = 'no-repeat';
        expandIcon.style.backgroundColor = '#EEEEEE';
        expandIcon.onclick = groupTitle.onclick = toggleGroup;
        expandIcon.innerHtml = '&nbsp;';
        oDiv.appendChild(expandIcon);

        groupTitle = document.createElement('span');
        groupTitle.innerText = linkGroups[i];
        groupTitle.className = 'groupTitle';
        groupTitle.style.marginLeft = "20px";
        groupTitle.style.fontWeight = "bold";
        groupTitle.style.cursor = 'pointer';
        groupTitle.onclick = toggleGroup;
        oDiv.appendChild(groupTitle);

        oInnerDiv = document.createElement('div');
        oInnerDiv.style.display = 'none';
        oInnerDiv.style.marginLeft = '25px';
        oInnerDiv.className = 'toggle';
        oDiv.appendChild(oInnerDiv);

        for (j = 0; j < linkGroups[linkGroups[i]].length; j = j + 1) {
            oInnerInnerDiv = document.createElement('div');
            oInnerInnerDiv.className = 'EPAdmin-menuItem';
            oInnerDiv.appendChild(oInnerInnerDiv);
            oInnerInnerDiv.appendChild(linkGroups[linkGroups[i]][j]);
        }

        oClearDiv = document.createElement('div');
        oClearDiv.style.clear = 'both';
        oDiv.appendChild(oClearDiv);
    }
}


Steg 3, modifiera sortering så att grupperingen inte bryts vid sortering. 

Ersätt funktionen "changeSortOrder" med följande:


/* Note: modified */
function changeSortOrder(order) {
    var container = document.getElementById('PageTypeListDiv'),
            groups = getElementsByClassName(container, 'group'),
                                       links,
                                       linkArray = [[]],
                                       sortFunction,
            i = 0,
            j = 0,
            oPageTypeIndexSorting = document.getElementById('PageTypeIndexSorting'),
            oPageTypeAlphabeticalSorting = document.getElementById('PageTypeAlphabeticalSorting');

    // IE has innerText property which other browsers don't
    // why we have to get and set the text in another way.
    function GetInnerText(node) {
        if (node.innerText != null)
            return node.innerText;
        else
            if (node.childNodes.length > 0 && node.childNodes[0].nodeType == 3) // nodeType 3 is textNode.
            return node.childNodes[0].nodeValue;
    }

    function SetInnerText(node, text) {
        if (node.innerText != null)
            node.innerText = text;
        else
            if (node.childNodes.length > 0 && node.childNodes[0].nodeType == 3) // nodeType 3 is textNode.
            return node.childNodes[0].nodeValue = text;
    }

    function CreateLinkObject(oLink) {
        var link = new Object();
        link.innerText = GetInnerText(oLink);
        link.id = oLink.id;
        link.href = oLink.href;
        return link;
    }

    if (!!groups && groups.length > 0) {
        for (i = 0; i < groups.length; i = i + 1) {
            links = getElementsByClassName(groups[i], 'pageTypeLink');
            for (j = 0; j < links.length; j = j + 1) {
                if (!!!linkArray[i]) {
                    linkArray[i] = [];
                }
                linkArray[i].push(CreateLinkObject(links[j]))
            }
        }
    } else {
        links = document.getElementsByName('pLink');
        linkArray[0] = [];
        for (i = 0; i < links.length; i = i + 1) {
            linkArray[0].push(CreateLinkObject(links[i]));
        }
    }

    switch (order) {
        case 'index':
            sortFunction = SortByIndex;
            oPageTypeIndexSorting.className = 'EPEdit-CommandToolClicked';
            oPageTypeIndexSorting.style.cursor = 'default';
            oPageTypeAlphabeticalSorting.className = 'EPEdit-CommandTool';
            oPageTypeAlphabeticalSorting.style.cursor = 'pointer';
            break;
        case 'alphabetical':
            sortFunction = SortByAlphabet;
            oPageTypeAlphabeticalSorting.className = 'EPEdit-CommandToolClicked';
            oPageTypeAlphabeticalSorting.style.cursor = 'default';
            oPageTypeIndexSorting.className = 'EPEdit-CommandTool';
            oPageTypeIndexSorting.style.cursor = 'pointer';
            break;
    }

    for (i = 0; i < linkArray.length; i = i + 1) {
        linkArray[i].sort(sortFunction);
    }

    window.top.epiMenuSortOrder = order;

    if (!!groups && groups.length > 0) {
        for (i = 0; i < groups.length; i = i + 1) {
            links = getElementsByClassName(groups[i], 'pageTypeLink');
            for (j = 0; j < links.length; j = j + 1) {
                var link = linkArray[i][j];
                links[j].href = link.href;
                SetInnerText(links[j], link.innerText);
                links[j].id = link.id;
            }
        }
    } else {
        for (i = 0; i < links.length; i = i + 1) {
            var link = linkArray[0][i];
            links[i].href = link.href;
            SetInnerText(links[i], link.innerText);
            links[i].id = link.id;
        }
    }
}



Steg 4, lägg till ikon.

Kopiera in en ikon i mappen Images/AdminMenu (under App_Themes), i mitt fall är sökvägen: "C:\Program Files\EPiServer\CMS\5.2.375.236\Application\App_Themes\Default\Images\AdminMenu".

Jag valde följande ikon:



(som jag kallade för PrefixGroup.jpg).

Steg 5, lägg till ikon-knapp i markup.

Återvänd till filen menu.aspx, och leta upp följande rad:


<img id="PageTypeAlphabeticalSorting" class="EPEdit-CommandTool" style="vertical-align: bottom; cursor: pointer;" src="<%=GetImageThemeUrl("AdminMenu/AlphabeticalSort.gif")%>" alt="<%=EPiServer.Core.LanguageManager.Instance.Translate("/admin/menuheadings/pagetypesorting/sortalphabetically")%>" title="<%=EPiServer.Core.LanguageManager.Instance.Translate("/admin/menuheadings/pagetypesorting/sortalphabetically")%>" onclick="changeSortOrder('alphabetical')" />

Lägg till följande efter denna rad:



<img id="PageTypeTreeGrouping" class="EPEdit-CommandTool" style="vertical-align: bottom; cursor: pointer;" src="<%=GetImageThemeUrl("AdminMenu/PrefixGroup.jpg")%>" alt="<%=EPiServer.Core.LanguageManager.Instance.Translate("/admin/menuheadings/pagetypegrouping/prefixgroup")%>" title="<%=EPiServer.Core.LanguageManager.Instance.Translate("/admin/menuheadings/pagetypegrouping/prefixgroup")%>" onclick="changeGrouping('prefix')" />


Steg 6, lägg till i språkfiler:

Välj önskade språkfiler i mappen för edit/admin-språkfiler, i ditt projekt. Dvs. webbapplikationens språk-mapp (som standard: /lang/language<språkändelse>.xml)

Leta upp elementet <pagetypesorting>...</...> och lägg till följande direkt efteråt (ex. engelska):

<pagetypegrouping>
  <prefixgroup>Group by prefix</prefixgroup>
</pagetypegrouping>



Sen är det klart!
Obs. på att EPiServer-uppdateringar som installeras antagligen förstör implementationen ovan.

Några skärmdumpar:

Utseende utan gruppering aktiverad.




















Grupperad och sorterad alfabetiskt.