HTML

JIRA DEV

Az Atlassian termékek felhasználási területei, paraméterezésük és alkalmazásfejlesztési lehetőségeik. Esettanulmány és érdekességek a JIRA-ról, Confluence-ről, GreenHopper-ről és a hozzájuk kapcsolódó plugin fejlesztésről, egy kevés Scrum módszertannal fűszerezve.

Kérdéseket és észrevételeidet a lacimol kukatc gmail pont com címre tudod elküldeni.

Üdv:
lacimol ()

Legutóbbi bejegyzések

Nincs megjeleníthető elem

Címkék

JiraDevTutor

Nincs megjeleníthető elem

Friss topikok

03.
június

Confluence ékezetek

Magyar szöveges ékezetek kezelése az URL-ben |  lacimol

Hivatkozás megjelenítése

A Confluence-ben az oldalak és blogok létrehozása után a megadott címből generálódik az elérési út, pl: az "Ekezet nelkuli szoveg"-hez a  "/display/COM/Ekezet+nelkuli+szoveg" olvasható elérés. Ez egészen addig jól működik, amíg rövid szöveget és angol karaktereket (pontosabban ASCII-t) használunk. A magyar ékezeteknél az elérési út a fenti könnyen olvasható megoldástól eltérően nem a cím, hanem csak egy pageId alapján tölti be a hivatkozott oldalt, pl: az "Ékezetes szöveg"-nél "/pages/viewpage.action?pageId=123456". Ennek az oldal használata közben nincs különösebb jelentősége, de ha linkelnénk egy külső oldalra vagy e-mailben küldjük, akkor szebb és olvashatóbb, ha látszik a cím (és a space: COM) is. Publikus szervereknél a keresési találatokhoz szükséges indexelés is jobb lehet egy olvasható linkkel (pl: Google találatok).

confluence_pages.png

Az ékezetek normalizálása

A Page és a BlogPost osztály is az AbstractPage-ből származik, aminek a "getUrlPath()" metódusa adja azt a hivatkozást, amit a Confluence oldalakon láthatunk. Magát az URL generálást az innen meghívott GeneralUtil.getPageUrl(AbstractPage page) metódusa végzi két egymástól eltérő módon:

  • toDisplayUrl: ha a cím (title) megfelelő és az adott oldal a legkésőbbi, akkor a "szép" URL-t adja
  • getIdBasedPageUrl: ha ez nem teljesük, akkor a pageId szerintit

A feltételeket az "isSafeTitleForUrl()"-ben találjuk, melyek szerint:

  • nem lehet a cím üres és 150 karakternél hosszabb
  • nem végződhet írásjellel
  • nem tartalmazhat illegális karaktereket: '+', '?', '%', '&', '"', '/', '\\', ';'
  • nem lehet benne nem ASCII karakter

A magyar ékezetek pageId szerinti kezelését ez utóbbi okozza. A problémát a cím normalizálásával oldhatjuk meg:

private static String decompose(String input) {
	return Normalizer.normalize(input, Normalizer.Form.NFD).replaceAll("\\p{InCombiningDiacriticalMarks}+", "");
}

Ékezetes oldal megjelenítése

A linkek beszédesé alakítása csak a feladat egyik része, mert ha így kattintunk rájuk, akkor nem tölti be a kívánt oldalt (PageNotFoundAction). Ennek oka, hogy a request-ek feldolgozásánál (PageAwareInterceptor) is két módon történik az adott oldal keresése: pageId vagy cím (+ space) alapján. A gondot az okozza, hogy az ékezetek nélküli linkből kivett cím (title) alapján nem lesz egyezés az adatbázisban az ékezetes címre. A legegyszerűbb megoldás, ha az ilyen normalizált URL-ek mellé betesszük a pageId-t is, így beszédes link mellett az oldal is egyértelműen azonosítható. Ennél szebb lenne, ha magát a DAO-t írnánk át, úgy hogy megtalálja az ékezetes rekordokat is (az ékezet nélküli címmel), de ezt majd a Confluence fejlesztők megoldják :)

GeneralUtil.java részlet

public static String getPageUrl(AbstractPage page) {
    if (page == null || (page.getOriginalVersion() == null && page.getSpace() == null)) {
        return "";
    }

    String title = page.getTitle();

    // only use simple/nice page url if the page does not contain:
    // - non-ASCII characters
    // - '+' or '-' characters because these can be picked up by the insert and stripe through filters
    // - double quotes (") because orion doesn't play nicely with them (CONF-1287)
    // - ends in punctuation (CONFDEV-3995)
    if (isSafeTitleForUrl(title) && page.isLatestVersion()) {
        return toDisplayUrl(page);
    } else if (isSafeTitleForNormalize(title) && page.isLatestVersion()) {
        return toDisplayNormalizedUrl(page);
    } else {
    	return getIdBasedPageUrl(page);
    }
}

/**
 * Get page URL that is id based (i.e. in the format /pages/viewpage.action?pageId=<pageId>)
 * @param page the page to generate a url for
 * @return page URL that is id based
 */
public static String getIdBasedPageUrl(AbstractPage page) {
    if (page == null) {
        return "";
    }

    return "/pages/viewpage.action?pageId=" + page.getId();
}

/**
 * Is "title" something we can safely put in a /foo/bar/title URL? Or should we reference this thing by ID
 * just to be safe?
 * @param title The title to check
 * @return True of the title can be put in a URL, false otherwise
 */
public static boolean isSafeTitleForUrl(String title) {
    if (!isSafeTitleForNormalize(title)) {
        return false;
    }

    for (int i = 0; i < title.length(); ++i) {
        char c = title.charAt(i);

        if (!isAscii(c))
            return false;
    }

    return true;
}

private static boolean isSafeTitleForNormalize(String title) {
    if (StringUtils.isEmpty(title) || title.length() >= 150) {
        return false;
    }

    if (ENDS_WITH_PUNCTUATION.matcher(title).find()) {
        return false;
    }

    if (".".equals(title)) {
        return false;
    }

    for (int i = 0; i < title.length(); ++i) {
        char c = title.charAt(i);

        if (ILLEGAL_URL_TITLE_CHARS.contains(c)) {
            return false;
        }
    }

    return true;
}

/**
 * This method is only ever called when we know that the page title consists of
 * ASCII characters, so we only need to single-encode it, not double-encode.
 * @param page The page to get the URL of
 * @return The URL for the given page object
 */
private static String toDisplayUrl(AbstractPage page) {
	 return toDisplayUrl(page, false);
}

private static String toDisplayNormalizedUrl(AbstractPage page) {
	 return toDisplayUrl(page, true) + "?pageId=" + page.getId();
}

private static String toDisplayUrl(AbstractPage page, boolean normalize) {
    StringBuilder displayUrl = new StringBuilder("/display/");
    displayUrl.append(GeneralUtil.urlEncode(page.getSpace().getKey()));
    displayUrl.append("/");
    if ("blogpost".equals(page.getType())) {
    	//Why shouldn't a blog have any creation date? It happens in my case... Maybe a bug or so!
        if (page.getCreationDate() != null) {
            displayUrl.append(new SimpleDateFormat("yyyy/MM/dd").format(page.getCreationDate()));
            displayUrl.append("/");
        }
    }
    
    String title = normalize ? decompose(page.getTitle()) : page.getTitle();
    displayUrl.append(urlEncode(title));
    return displayUrl.toString();
}

private static String decompose(String input) {
	return Normalizer.normalize(input, Normalizer.Form.NFD).replaceAll("\\p{InCombiningDiacriticalMarks}+", "");
}

Tesztelés

TestGeneralUtil.java részlet

public void testNonAsciiPageUrl() {

		final String spaceKey = "HUN";
		final String pageTitle= "Ékezetes szöveg";

		Space space = new Space();
		space.setKey(spaceKey);

		Page page = new Page();
		page.setSpace(space);
		page.setTitle(pageTitle);

		assertEquals("/display/" + spaceKey + "/Ekezetes+szoveg?pageId=0", GeneralUtil.getPageUrl(page));

}

A végeredmény

confluence_pages_ékezet_m.png

A meglévő 2 megoldás mellé létrehoztunk egy harmadik típusú link generálást.  Az így kialakított kompromisszumos megoldás (beszédes hivatkozás pageId-val kiegészítve):
/display/COM/Uj+szoveg+ekezettel?pageId=1081346

Címkék: confluence

komment

süti beállítások módosítása