Ein kleiner – aber wie ich finde vertretbarer – Wermutstropfen ist, dass es nicht ohne Anpassung des Confluence-Cores geht. Die abgeänderten Dateien, die nachfolgend erläutert werden, packt man am besten in eine jar und legt diese in das WEB-INF/lib Verzeichnis.
Die Aktivierung/Deaktivierung der Funktion wird über einen Schalter in der GUI realisiert. Damit der Zustand ins Backend übertragen werden kann, wird eine ThreadLocale verwendet. Diese ist wie folgt aufgebaut:
public class ShowI18NKeysThreadLocal {
public static final ThreadLocal showI18nKeysLocal = new ThreadLocal () {
protected synchronized Boolean initialValue() {
return false;
}
};
}
Die DefaultI18nBean besitzt u.a. eine Methode namens isShowKeyMode. Mit dieser Methode wird abgefragt, ob die Schlüssel-Wert Paare zurückgeliefert werden sollen. Nun vermutet man, dass die Methode aus einer Property, etc… den Zustand (aktiv, inaktiv) ausliest. Dies ist leider nicht der Fall, da sie immer den Wert false (hard-codiert) zurückliefert, sodass ohne dem Überschreiben der Methode ein Switch gar nicht möglich ist. Deshalb muss die Klasse wie folgt erweitert werden.
public ExtendedDefaultI18NBean(Locale locale, PluginAccessor pluginManager) {
super(locale, pluginManager);
}
@Override
public boolean isShowKeyMode() {
return ShowI18NKeysThreadLocal.showI18nKeysLocal.get();
}
Die Klasse DefaultI18NBean wird nicht wie die meisten Klassen per Spring Injection erzeugt, sondern über eine Factory-Klasse. Aus diesem Grund muss auch die DefaultI18NBeanFactory wie folgt überschrieben werden.
public class ExtendedDefaultI18NBeanFactory implements I18NBeanFactory {
private PluginAccessor pluginAccessor;
public I18NBean getI18NBean(Locale locale) {
return new ExtendedDefaultI18NBean(locale, pluginAccessor);
}
public I18NBean getI18NBean() {
return getI18NBean(LocaleManager.DEFAULT_LOCALE);
}
public void setPluginAccessor(PluginAccessor pluginAccessor) {
this.pluginAccessor = pluginAccessor;
}
}
Damit nun die von uns angepasste Factory-Klasse verwendet werden kann, muss in der i18nContext.xml der zugehörige Eintrag wie folgt angepasst werden:
<bean id="defaultI18NBeanFactory" class="de.communardo.customizations.i18n.ExtendedDefaultI18NBeanFactory" autowire="byName"/>
Letztlich ist es noch notwendig, dass die web.xml auf die abgeänderte i18nContext.xml verweist. Dazu muss die entsprechende Zeile wie folgt angepasst werden.
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
...,
classpath:/customizedI18nContext.xml,
...,
Wie zuvor bereits erwähnt, zielt dieses Vorgehen auf eine Laufzeitlösung ab, sodass im Frontend ein entsprechender “Schalter” implementiert werden muss. In diesem Beispiel werden die Frontend-Anpassungen über ein Plugin durchgeführt.
Der neue Filter hat einzig und allein die Aufgabe zu überprüfen, ob und auf welchen Wert der Request-Parameter showI18nKeys gesetzt ist. Sollte der Wert true sein, dann werden beim Reload der aktuellen Seiten nicht mehr nur die Texte, sondern auch die zugehörigen Schlüssel angezeigt. Außerdem wird der aktuelle Zustand in der Session vermerkt, sodass beim Seitenwechsel der gewünschte Zustand immernoch existiert. Natürlich wirkt sich die Zustandsänderung nur auf den aktuellen Nutzer aus und beeinflusst nicht die Sicht der anderer Nutzer!
public class ShowI18NKeyFilter implements Filter {
private static final String I18N_KEYS = "showI18nKeys";
private static final String CONFLUENCE_I18N_SHOWKEY = "confluence.i18n.showkey";
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException {
if (request instanceof PluginHttpRequestWrapper) {
PluginHttpRequestWrapper httpRequest = (PluginHttpRequestWrapper) request;
Boolean showI18nKeysSession = (Boolean) httpRequest.getSession().getAttribute(CONFLUENCE_I18N_SHOWKEY);
if (httpRequest.getParameter(I18N_KEYS) != null) {
Boolean showI18nKeysRequest = Boolean.valueOf(httpRequest.getParameter(I18N_KEYS));
if (showI18nKeysRequest != showI18nKeysSession) {
if (showI18nKeysRequest) {
httpRequest.getSession().setAttribute(CONFLUENCE_I18N_SHOWKEY, true);
ShowI18NKeysThreadLocal.showI18nKeysLocal.set(true);
} else {
httpRequest.getSession().removeAttribute(CONFLUENCE_I18N_SHOWKEY);
ShowI18NKeysThreadLocal.showI18nKeysLocal.set(false);
}
}
} else {
ShowI18NKeysThreadLocal.showI18nKeysLocal.set(showI18nKeysSession != null ? showI18nKeysSession : false);
}
}
chain.doFilter(request, response);
}
public void init(FilterConfig arg0) throws ServletException {
}
}
Letztlich fehlt nur noch ein Link (am besten im Header, da diese Stelle auf fast allen Seiten sichtbar ist), welcher den GET-Parameter showI18nKeys konträr zum aktuellen Zustand den Wert auf true bzw. auf false übermitteln soll.