Bei der Entwicklung von mehreren Plugins kann es vorkommen, dass Texte mehrfach übersetzt werden müssen, wodurch somit Redundanzen entstehen. Der Ursprung liegt dann bei Atlassian Confluence, welches das Sharen von i18n-keys über die verschiedenen Plugins hinweg nicht ermöglicht.
Angenommen man hat ein Plugin X entwickelt und in ihm den i18n-key manager.user.email definiert. Zusätzlich existiert ein Plugin Y, dass Plugin X um weitere Funktionalität ergänzt und eine neue vm anlegt, die eben auch den i18n-key manager.user.email verwenden soll. Nun gibt es das Plugin-Konzept von Confluence nicht her auf die in den properties definierten Texte von Plugin X zuzugreifen. Dies bedeutet, dass der Key auch im Plugin Y hinterlegt werden muss. Folglich ergibt sich ein erhöhter Pflegeaufwand.
Damit Redundanzen beseitigt werden können, müssen die i18n-keys zentral abgelegt werden. Dazu kann man einen bestimmten Plugintyp von Confluence – das Sprachplugin – nutzen.
Diese Erkenntnis ist wichtig, da für die Lösung des oben genannten Problems, es notwendig ist, die Standard Sprachplugins von Confluence zu deinstallieren und die darin enthaltenen property Dateien in das neue SprachPlugin zu überführen.
Außerdem muss die atlassian-plugin.xml des Sprachplugins, wie folgt angepasst werden.
<language name="German" key="de_DE" language="de" country="DE">
<resource name="de_DE.png" type="download"
location="templates/languages/de_DE/de_DE.png">
<property key="content-type" value="image/png"/>
</resource>
</language>
<language name="Spain" key="es_ES" language="es" country="ES">
<resource name="es_ES.png" type="download"
location="templates/languages/es_ES/es_ES.png">
<property key="content-type" value="image/png"/>
</resource>
</language>
<resource name="i18n" name="i18n_my_plugin" type="i18n" location="/my_plugin"/>
<!-- Important entry, because you have to add the default
i18n files to this new plugin -->
<resource name="i18n" name="i18n_default" type="i18n"
location="/ConfluenceActionSupport"/>
Diese Lösungsansatz beseitigt nicht nur Redundanzen, sondern bietet auch in Kombination mit Maven, eine Möglichkeit mehrere Varianten einer Pluginlösung textuell abzugrenzen.
Für diesen Fall kann man mittels Maven tricksen. Wichtig hierbei ist eine eindeutige Namenskonvention für die Unterteilung der properties vorzunehmen. Beispielsweise kann man die properties wie folgt namentlich abgrenzen:
Ist das geschehen, definiert man in der atlassian-plugin.xml des LanguagePool-Plugins folgende Zeilen:
<resource name="i18n" name="i18n_my_plugin_general" type="i18n"
location="/my_plugin"/>
<resource name="i18n" name="i18n_my_plugin_specific" type="i18n"
location="/my_plugin_${TYPE}"/>
Letztlich müssen im Eclipse zwei neue Maven Run-Configurations erstellt werden, die das entsprechende Goal (Bsp.: package, install oder atlassian-pdk:install) verwendet und im Environment-Tab die Variable TYPE auf typex oder auf typey setzt.
Das Berechtigungskonzept von Confluence ist in der Confluence Doku verteilt auf mehrere Seiten. Da wir in letzter Zeit immer wieder Antworten auf Berechtigungsfragen liefern mussten, habe ich eine Zusammenfassung geschrieben, die die wichtigsten Eckpunkte aufnimmt und Confluence Eigenheiten aufdeckt.
Confluence Berechtigungskonzept besteht aus drei verschiedenen Stufe. Diese sind Globale Berechtigung, Bereichsberechtigungen und Seitenberechtigungen.
Liste der globalen Berechtigungen:
| globales Recht | Bedeutung |
|---|---|
| Can Use | Recht, die Anwendung zu benutzen (Abhängigkeit mit Lizenz, sofern Nutzerbegrenzung) |
| Attach File to User Profile | Recht, Dateien in das Profil hochzuladen (überflüssig, seit Einführung der persönlichen Bereiche |
| Personal Space | Recht, einen persönlichen Bereich anzulegen |
| Create Space | Recht, einen Bereich innerhalb von Confluence anzulegen (Beim Erstellen des Bereichs wird der Ersteller automatisch zum Bereichsadministrator.) |
| Confluence Administrator | siehe Confluence Administrator |
| System Administrator | siehe System Administrator |
Die Rolle des System Administrators ist die mächtigste, durch sie hat der Nutzer uneingeschränkte Befugnisse bei der Verwaltung von Confluence. (u.a. Zugriff auf die Administrationskonsole)
Nutzer, die diese Rolle besitzen, haben die meisten Befugnisse, die auch der System Administrator hat, inne. Die wenigen, die er nicht hat werden in der folgenden Liste zusammengetragen.
| Administrationsmenü | Ausnahmen im Vergleich zum System-Admin |
|---|---|
| Allgemeine Konfiguration |
|
| Administration der täglichen Sicherung | Komplett deaktiviert |
| Plugins | Komplett deaktiviert |
| Plugin Repository | Komplett deaktiviert |
| Mailserver | Komplett deaktiviert |
| Benutzermakros | Komplett deaktiviert |
| Dateianhang-Speicherung | Komplett deaktiviert |
| Layouts | Komplett deaktiviert |
| Kundenspezifische HTML | Komplett deaktiviert |
| Sichern und Wiederherstellen | Komplett deaktiviert |
| SnipSnap Import | Komplett deaktiviert |
| Protokoll- und Profilerstellung | Komplett deaktiviert |
| Cluster Konfiguration (Confluence 3.0+) | Komplett deaktiviert |
Das Setzen des Confluence-Administrator oder des System-Administrator Rechts für einen Nutzer gibt den Nutzern nicht automatisch Zugriff auf alle Seiten und Bereiche, sondern erlaubt Zugriff auf die Administrator Konsole.
Bereichsberechtigungen können nur durch einen Bereichsadministrator vergeben werden. Ein Nutzer ist ein Bereichsadministrator, wenn er für den Bereich das Recht “Admin” innehat.
Jeder Bereich besitzt seinen eigenen unabhängigen Rechtesatz. Dieser besteht aus den folgenden Rechten:
| Recht | Bedeutung für Nutzer eines Bereiches |
|---|---|
| Anzeigen | Anzeigen von Bereichsinhalten (Bereichsdetails, -seiten, News) |
| Seiten – Erstellen | Erstellen und Bearbeiten von Seiten |
| Seiten – Exportieren | Exportieren von Seiten |
| Seiten – Beschränken | Beschränken von Seiten |
| Seiten – Entfernen | Entfernen von Seiten |
| News – Ersellen | Erstellen von News |
| News – Entfernen | Entfernen von News |
| Kommentare – Erstellen | Erstellen von Kommentaren |
| Kommentare – Entfernen | Enfernen von Kommentaren |
| Anhänge – Erstellen | Hinzufügen von Anhängen |
| Anhänge – Entfernen | Entfernen von Anhängen |
| Mail – Entfernen | Entfernen individueller Mails |
| Bereich – Exportieren | Exportieren vom gesamten Bereich |
| Bereich – Admin | Administrieren des Bereiches (Bereichsadministrator) |
Um Seiten beschränken zu können, muss dem Nutzer das Recht “Seiten beschränken” über die Bereichsadministration zugeordnet sein.
Confluence besitzt von grundauf 2 Gruppen:
confluence-administrators
confluence-users
Zusätzlich unterscheidet Confluence noch nicht-eingeloggte Nutzer als Anonymous, im Rechtemanagement ist es möglich für diese spezielle Gruppe von Nutzern Rechte zu definieren.
User dieser Gruppe haben Zugriff auf die Administrator Console und können systemweit administrieren.
Mitglieder dieser Gruppe können auch alle Seiten und Bereiche der betroffenen Confluence Instanz einsehen. Allerdings keine Seiten, die durch Seitenzugriffsbeschränken die Ansicht für diese Gruppe verbieten. Mitglieder der confluence-administrators-Gruppe können allerdings über den Space-Adminbereich die Einschränkungen wieder entfernen.
Gehört ein Nutzer zu mehreren Gruppen oder/und hat zusätzlich Nutzerspezifische Rechte so addieren sich diese.
Sie sind quasi OR (additiv) verknüpft.
Ist ein Nutzer also Mitglied von 2 Gruppen (gruppe1 und gruppe2):
| Gruppe/Nutzer | Rechte |
|---|---|
| gruppe1 |
|
| gruppe2 |
|
| Nutzer |
|
| Nutzer in der gruppe1 und gruppe2 |
|
So besitzt der Nutzer alle 3 Rechte.
Mittels der Seitenberechtigung kann man die eingestellten Bereichsberechtigungen nochmals auf Seitenebene eingrenzen.
Beispiel: (zur Vereinfachung nur mit Gruppe. Gilt allerdings ebenso mit Nutzern)
Bereichsberechtigung für Bereich X:
| Gruppe | Recht |
|---|---|
| gruppe1 |
|
| gruppe2 |
|
Seitenberechtigung für Seite Y im Bereich X:
| Gruppe | Recht |
|---|---|
| gruppe2 |
|
Jeder Nutzer der gruppe2 kann auf die Seite Y im Bereich X zugreifen. Die gruppe1 hat keine Möglichkeit den Inhalt der Seite angezeigt zu bekommen.
Der YUI Compressor ist ein Projekt aus dem Hause Yahoo. Ziel des Projekt ist die Minimierung der Größe von Javascript – sowie CSS Dateien. Dazu werden komplexe Algorithmen verwendet, die dafür Sorge tragen, dass die Funktionalität des Quellcodes erhalten bleibt und trotzdem die Bytegröße der Dateien erheblich verkleinert wird. Die trivialsten Beispiele dafür wären das Entfernen von Kommentarzeilen sowie von Zeilenumbrüchen.
Die Minimierung der Dateigrößen resultiert in einer spürbaren Verbesserung der Performance für den Benutzer bei der Nutzung einer Webanwendung, da das vom Server zum Client übertragende Datenvolumen erheblich kleiner ist. Zu erwähnen ist noch, dass durch diverse Cache-Strategien des Browsers dieser Effekt nach dem einmaligen Download der Ressourcen drastisch reduziert wird. Die Entwickler von Yahoo haben allerdings statische Werte für die Nutzung ihrer eigenen Yahoo Homepage erhoben, die aufzeigen, dass jeder fünfte Nutzer mit einem geleerten Cache ihre Seite besucht. (Referenz: http://yuiblog.com/blog/2007/01/04/performance-research-part-2/)
Die Dokumentation, Release Notes sowie die Software steht auf der Yahoo Developer Homepage zur Verfügung.
Das Projekt YUI Compressor Maven Mojo ermöglicht es den YUI Compressor auf relativ einfache Weise in ein Maven Projekt zu integrieren. Des Weiteren erlaubt es Dateien zu Aggregationen zusammenzufassen, damit nicht nur die Größe von CSS – und Javascript Dateien gesenkt werden kann, sondern auch die Anzahl der Requests, die nötig sind um eine Webseite zu laden. Zusätzlich stellt es ein Goal zur Verfügung, mit dem Javascript Dateien per jslint auf deren Qualität hin anhand vordefinierter Merkmale ausgewertet werden können.
Damit das Maven Projekt, um diese neue Funktionalität erweitert werden kann, muss ein zusätzliches Plugin Tag in die pom.xml eingetragen werden.
<plugins> ... <plugin> <groupId>net.sf.alchim</groupId> <artifactId>yuicompressor-maven-plugin</artifactId> <executions> <execution> <goals> <goal>compress</goal> <!--<goal>jslint</goal>--> </goals> </execution> </executions> </plugin> ... </plugins>
Des Weiteren kann zusätzlich ein configuation Tag hinzugefügt werden, indem unterschiedlichste Konfigurationen vorgenommen werden können.
Beispielsweise ist es möglich genau festzulegen, welche Javascript – oder CSS Dateien minified (includes) bzw. nicht minified (excludes) werden sollen. Wildcards wie Sternchen bzw. Doppelsternchen können dabei problemlos verwendet werden. Außerdem können Aggregationen (aggregations) definiert werden, die mehrere Dateien zu einer einzigen zusammenführen. Quasi on the top wird die gzip Funktionalität, mit der Dateien gepackt werden können, zu Verfügung gestellt.
Die genaue Definitionen der Tags sowie die Vielfalt der Funktionen kann über die YUI Compressor Maven Mojo Homepage eingesehen werden.
Es gibt eine Vielzahl an Situationen, bei den man spezifische Properties verwendet. Generelle und fast überall aufzufindende Beispiele wären die Konfiguration des Logs und der Datenbank. Für diese speziellen Ressourcen existieren zumeist spezielle Dateien, die beim Start des Servers durch individuelle Klassen/Mechanismen ausgelesen werden.
Die Frage, die zu diesem Artikel geführt hat, befasst sich mit dem Thema:
Wie können anwendungsspezfische Properties für Confluence relativ einfach und leicht wartbar hinterlegt werden?
Gemeint sind hierbei Properties, die als Schalter zwischen Modi innerhalb einer Software dienen oder die zusätzliche Konfigurationen ermöglichen.
Dieser Weg ist für Java Anwendungen allgemeingültig und somit für die meisten Programmierer nicht neu. Als JVM – Parameter (JAVA_OPTS) wird mit dem Präfix -D und dem anschließenden Property-Wertepaar (<property_name>=<property_value>) die Property definiert. In der Anwendung selbst kann dann mit System.getProperty(“<propety_name>”) darauf zugegriffen werden. Als Rückgabewert liefert die Methode immer ein String, den man ggf. explizit casten muss.
Atlassian Confluence nutzt eine zentrale Datei (confluence.cfg.xml) in der Properties definiert werden können. Diese Datei befindet sich im data Verzeichnis.
Wie die Extension vermuten lässt, handelt es sich hierbei um eine XML Datei. Die Definition der einzelnen Properties erfolgt wie folgt:
<confluence-configuration>
<setupStep>complete</setupStep>
<setupType>custom</setupType>
<buildNumber>1517</buildNumber>
<properties>
<property name="attachments.dir">${confluenceHome}\attachments</property>
...
<property name="this.is.an.example">true</property>
</properties>
</confluence-configuration>
In diesesm Ausschnitt habe ich eine neue Property mit Namen “this.is.an.example” mit dem Wert true angelegt. (rot gekenntzeichnet) Um nun in Quellcode diesen Wert auslesen zu können muss die Serviceklasse BootstrapManagers verwendet werden.
Eine Variante dafür wäre die getProperty() Methode zu nutzen.
BootstrapManager bootstrapManager = BootstrapUtils.getBootstrapManager();
Object objectVal = bootstrapManager.getProperty("this.is.an.example");
// here comes the explicit cast
boolean booleanVal = ((Boolean) objectVal).booleanValue();
Möglich wäre auch die getString() Methode zu wählen.
BootstrapManager bootstrapManager = BootstrapUtils.getBootstrapManager();
String stringVal = bootstrapManager.getString("this.is.an.example");
boolean booleanVal = Boolean.valueOf(stringVal);
Die kürzeste Variante allerdings ist die Methode isPropertyTrue() zu verwenden. Die kann folgendermaßen aussehen:
BootstrapManager bootstrapManager = BootstrapUtils.getBootstrapManager();
boolean booleanVal = bootstrapManager.isPropertyTrue("this.is.an.example");
Meiner Meinung nach steigt durch die Nutzung der confluence.cfg.xml die Übersicht. Da zum einen die Daten lesbar (XML) abgelegt und zudem zentral an einer Stelle konfiguriert werden. Ein weiterer Vorteil gegenüber den JVM Parametern ist, dass der Betrieb, der die Wartung der Skripte und der Anwendung auf dem Server vornimmt, keine Konfigurationen vergessen bzw. überschreiben kann. Letztlich ermöglichen solche gebündelten Konfigurationen in einer einzigen Datei, dass sie relativ unproblematisch für bestimmte Anwendungsfälle/Instanzen ausgetauscht werden können.
In diesem Beitrag wird eine der Möglichkeiten aufgezeigt, wie der TinyMCE Editor in Sachen Performance optimiert werden kann. Speziell wird auf die Java Variante JSP eingegangen. TinyMCE Compressor bietet allerdings vielfältige Anbindungsmöglichkeiten. (Bsp.: php)
Der TinyMCE Kompressor packt alle TinyMCE Javascript Dateien in eine GZIP – Datei. Dadurch verringert sich das Datenvolumen, welches heruntergeladen werden muss, um 75%. Ein weiterer wichtiger Effekt ist, dass die Anzahl der einzelnen Request, die beim Laden des Editors verursacht werden, reduziert wird. Letztlich sorgt die Kompression für eine Verringerung der Zeit, die der TinyMCE – Editor zur Initialisierung benötigt, erheblich.
Der Initialisierungsprozess wird mit dem Aufruf der Javascript Funktion tinyMCE_GZ.init() aufgerufen. Dieser Methode können zudem Parameter übergeben werden, um den Prozess an das gewünschte Verhalten anzupassen.
| Parameter | Standard Einstellung | Bedeutung |
|---|---|---|
| plugins | leerer String | TinyMCE Plugins, die eingebunden werden sollen |
| themes | leerer String | Themes, die eingebunden werden sollen (Bsp.: simple, advanded) |
| languages | leerer String | Sprachen, die eingebunden werden sollen (Bsp.: de, en) |
| disk_cache | true | Cache Aktivierung |
| page_name | tiny_mce_gzip.jsp | Name der zugehörigen JSP |
| debug | false | Debug Modus für Kompressor |
| suffix | leerer String | Suffix für editor_template<suffix>.js |
<script type="text/javascript">
<!--
tinyMCE_GZ.init({
plugins: 'inlinepopups,table,advhr,emotions',
themes: 'advanced',
languages: 'en,de',
disk_cache: true,
debug: false
});
//-->
</script>
Die tiny_mce_gzip.js und tiny_mce_gzip.jsp Dateien können unter folgender URL heruntergeladen werden. http://tinymce.moxiecode.com/download.php
Im Abschnitt Anpassung von Core-Klassen des Atlassian Confluence Frameworks wurde bereits aufgezeigt, in wieweit Confluence 2.10 Klassen im nachhinein angepasst werden können. Dieser Abschnitt soll sich nun aufbauend darauf mit der Lucene-Suche beschäftigen.
Anmerkung:
Die nachfolgenden Erweiterungen/Anpassungen werden nicht – sofern nicht explizit darauf hingewiesen wurde – in den Plugin-Projekten vorgenommen. Es ist unbedingt erforderlich, dass ein Projekt erstellt werden muss, welches kompiliert als jar direkt im WEB-INF/lib Ordner abgelegt wird, so dass Confluence die Klassen beim Start unmittelbar zur Verfügung stehen.
Vorgehen:
1. Die Sprache muss in den Lucene-Index gespeichert werden.
Damit Lucene beim Indizieren auch das zusätzliche Attribut aufnehmen kann, muss eine neue Klasse angelegt werden, die von der Klasse Extractor erbt. Die Methode addFields() muss dabei so umgeschrieben werden, dass das neue Attribut ausgelesen und als Feld unter einem festgelegten Attributschlüssel indiziert werden kann. Damit Confluence diesen Extractor auch ausführt, muss die Datei core-extractors.xml* angepasst werden. Dafür ist es erforderlich die folgenden Zeilen einzufügen.
<extractor name="..." key="..." priority="..."> <description>...</description> </extractor>
2. Nach der Sprache muss gesucht werden können.
Damit nach einem festgelegten Attributschlüssel gesucht werden, muss die SearchQuery Klasse erweitert werden. Die getKey() Methode, die den festgelegten Attributschlüssel für die Suche zurückgibt und die getParameters() Methode, die die Suchemenge zurückliefert, müssen dabei überschrieben werden.
Des Weiteren ist eine Klasse notwendig, die vom LuceneQueryMapper erbt. Die convertToLuceneQuery() Methode ist dabei die einzige Methode, die überschrieben werden muss. Sie enthält die Suchlogik für ein bestimmtes Kriterium. Welche Möglichkeiten Lucene an dieser Stelle bietet, lesen Sie bitte in der einschlägigen Literatur oder im Internet unter Lucence nach. Damit Confluence diesen Mapper verwendet, muss die lucene-search-mappers.xml* mit dem folgenden Eintrag ergänzt werden.
<lucene-query-mapper name="..." key="..." class="..." handles="..."> <description>...</description> </lucene-query-mapper>
Die Confluence Action-Klassen, wie die SearchSiteAction, verwenden die SearchQueryParameters Klasse, um die Suchkriterien zu transportieren. Es empfiehlt sich diese Klasse um das zusätzliche Attribut zu ergänzen. Im Falle der Sprachattributerweiterung wären das eine Instanzvariabel und die zugehörigen Setter und Getter.
Auf die Anpassung innerhalb der Plugin Actions wird hier allerdings nicht näher eingegangen werden, da sie recht manigfaltig ausfallen kann, trotzdem nicht allzu kompliziert sind.
Die SearchQueryParameters werden der Methode siteExtendedSearch der Klasse DefaultPredefinedSearchBuilder übergeben. Damit diese Methode weiterhin verwendet werden kann, muss die DefaultPredefinedSearchBuilder Klasse entsprechend erweitert werden. Im Body dieser Methode werden die einzelnen SearchQuerys einem Set hinzugefügt. Notwendig wäre zum Beispiel für das Sprachattribut der folgende Eintrag.
if ((searchQueryParams.getLanguages() != null) && !searchQueryParams.getLanguages().isEmpty()) {
scopedQuery.add(new LanguagesQuery(searchQueryParams.getLanguages()));
}
Nach diesen Änderung hat man bereits den Titel dieses Beitrags überstanden.
* Wie die core-extractors.xml und lucene-search-mappers.xml entsprechend verändert werden müssen, damit Confluence diese auch berücksichtigt, entnehmen Sie bitte von diesem Beitrag Anpassung von Core-Klassen des Atlassian Confluence Frameworks. Zu beachten wäre, dass dafür auch die pluginServiceContext.xml verändert werden muss.
In meisten Fällen beim Entwickeln von Confluence Plugins ist es nicht notwendig die von Confluence zur Verfügung gestellten Core-Klassen (z.B. diverse Manager: ContentEntityManager, SmartListManager oder Daos: LabelDao, PageDao) zu überschreiben. Zwei Paradebeispiele, bei denen es allerdings unumgänglich wäre, sind die Änderung des Datenbankschemas oder die Erweiterung der Managerfunktionalität.
Atlassian Confluence selbst bietet neben dem Erweitern von Actions mittels atlassian-plugin.xml (siehe Erweitern von Actions in Confluence) keinerlei Möglichkeiten direkt die Core-Klassen zu überschreiben. Aus diesem Grund ist es notwendig in die Spring Context-Dateien, die für die Registrierung der einzelnen Klassen verwendet werden, einzugreifen. Dies kann auf zwei Wegen bewerkstelligt werden:
Die erste Variante erweist sich als deutlich unpraktikabel, da dadurch offensichtlich das Testen, die Wartbarkeit sowie die Updatefähigkeit leiden würden.
Bei der zweiten Möglichkeit hingegen verhält es sich etwas anders, da das JAR-Archiv zu keiner Zeit direkt manipuliert wird. Wie das funktioniert, wird in den nachfolgenden zwei Schritten demonstriert.
In der confluence/WEB-INF/web.xml befindet sich folgender Abschnitt (hier verkürzt dargestellt):
<context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:/databaseSubsystemContext.xml, ... classpath:/applicationContext.xml, ... classpath:/jmxContext.xml </param-value> </context-param>
Hevorzuheben wären die Dateien databaseSubsystemContext.xml, die für die Entity-Mappings zuständig ist, sowie die applicationContext.xml, die die Registrierung sämtlicher Manager- und Dao-Klassen vornimmt.
Nun muss lediglich der Pfad bzw. der Name der zu bearbeitenden Datei geändert werden. Prinzipiell ist es im übrigen auch möglich eine neue Context-Datei einzutragen, in der zusätzliche Beans registriert werden können.
Als Ausgangspunkt beim Erstellen einer neuen Context-Datei bietet sich an den Inhalt der anzupassenden Datei zu kopieren, um darin die gewünschten Änderungen vorzunehmen. Wie die einzelnen Anpassungen bzw. Erweiterungen einer Spring-Bean vorzunehmen sind, ist aus der sehr guten, einschlägigen Dokumentation von Spring zu entnehmen. (siehe Spring Dokumentation)
Abschließend verbleibt noch das Erzeugen eines JAR-Archivs und das Kopieren jenes in das confluence/WEB-INF/lib Verzeichnis des Apache Tomcats.
Releaseparty at Atlassian? Confluence 3.2 BETA and 3.1.2 with soms bugfixes were released yesterday. [...]
Tino Schmidt's Vortrag zu Enterprise Mashups auf der webciety, 4.3 Remix the Web http://bit.ly/d26rtA [...]
neuer Blogpost: February Cumulative Update (2010) http://bit.ly/cwxZGE [...]
Webinar am 16.03.: „Communote Enterprise Microblogging - Funktionen und Einsatzbereiche im Unternehmen“ http://bit.ly/96eexF [...]