diff --git a/ccm-core/src/com/arsdigita/ui/UIResources.properties b/ccm-core/src/com/arsdigita/ui/UIResources.properties
index ff63cc0cf..2e4db4681 100755
--- a/ccm-core/src/com/arsdigita/ui/UIResources.properties
+++ b/ccm-core/src/com/arsdigita/ui/UIResources.properties
@@ -19,3 +19,12 @@ ui.debug.transform.on=Display transformation
ui.debug.transform.off=Hide transformation
ui.debug.xml=View document XML
ui.debug.xsl=Download XSL files
+com.arsdigita.ui.admin.applications.tree.heading=Applications
+com.arsdigita.ui.admin.applications.url.validation.not_blank=The URL of an application instance can is mandatory.
+com.arsdigita.ui.admin.applications.url.valiation.minmaxlength=The length of an URL of an application instance must be between 1 and 100 characters.
+com.arsdigita.ui.admin.applications.title.validation.not_blank=Title is mandatory for an application instance.
+com.arsdigita.ui.admin.applications.title.valiation.minmaxlength=The minimum length of the title of an applicatio instance is one character, the maximum length are 200 characters
+com.arsdigita.ui.admin.applications.desc.valiation.minmaxlength=The maximum length of a descrption of an application instance are 4000 characters.
+com.arsdigita.ui.admin.applications.url.label=URL
+com.arsdigita.ui.admin.applications.title.label=Title
+com.arsdigita.ui.admin.applications.desc.label=Description
diff --git a/ccm-core/src/com/arsdigita/ui/UIResources_de.properties b/ccm-core/src/com/arsdigita/ui/UIResources_de.properties
index dc2d53275..6e925603b 100644
--- a/ccm-core/src/com/arsdigita/ui/UIResources_de.properties
+++ b/ccm-core/src/com/arsdigita/ui/UIResources_de.properties
@@ -5,17 +5,26 @@ ui.admin.signout=Abmelden
ui.admin.portal=Meine Startseite
ui.admin.greeting=Willkommen
ui.sitemap.site_node=Site Node:
-ui.sitemap.mounted_instance=Verf\u00FCgbare Instanz\:
+ui.sitemap.mounted_instance=Verf\u00fcgbare Instanz\:
ui.sitemap.enter_new_node_name_in_text_field=Tragen Sie einen neuen Namen im Textfeld ein.
ui.sitemap.are_you_sure_you_want_to_remove_this_node=Sind Sie sicher, diesen Knoten zu entfernen?
-ui.sitemap.are_you_sure_you_want_to_umount_this_instance=Sind Sie sicher, diese Instanz auszuh\u00E4ngen?
+ui.sitemap.are_you_sure_you_want_to_umount_this_instance=Sind Sie sicher, diese Instanz auszuh\u00e4ngen?
ui.sitemap.enter_name_for_new_package_instance_in_text_field_then_select_a_package_to_mount_from_list=Enter name for new package instance in text field, then select a package to mount from list.
ui.sitemap.h4sitemap_treeh4=
SiteMap Tree
ui.sitemap.configure_sitemap_admin_page=Configure SiteMap Admin Page
-ui.sitemap.h4emselect_sitenode_to_view_detailsemh4=Knoten f\u00FCr die Detailansicht w\u00E4hlen
+ui.sitemap.h4emselect_sitenode_to_view_detailsemh4=Knoten f\u00fcr die Detailansicht w\u00e4hlen
ui.sitemap.configuration_menu_placeholder=Configuration Menu Placeholder
ui.sitemap.access_denied_to_sitemap=Zugang zur SiteMap abgelehnt.
ui.debug.transform.on=Transformation anzeigen
ui.debug.transform.off=Transformation verbergen
ui.debug.xml=XML Dokument ansehen
ui.debug.xsl=Download XSL Dateien
+com.arsdigita.ui.admin.applications.tree.heading=Applikationen
+com.arsdigita.ui.admin.applications.url.validation.not_blank=Die Angabe einer URL ist erforderlich
+com.arsdigita.ui.admin.applications.url.valiation.minmaxlength=Die URL einer Applikations-Instanz muss zwischen einem und 100 Zeichen lang sein
+com.arsdigita.ui.admin.applications.title.validation.not_blank=Der Titel ist f\u00fcr das Anlegen einer Applikations-Instanz erforderlich
+com.arsdigita.ui.admin.applications.title.valiation.minmaxlength=Der Titel einer Applikations-Instanz muss zwischen einem und 200 Zeichen lang sein.
+com.arsdigita.ui.admin.applications.desc.valiation.minmaxlength=Die Beschreibung einer Applikations-Instanz darf max. 4000 Zeichen lang sein
+com.arsdigita.ui.admin.applications.url.label=URL
+com.arsdigita.ui.admin.applications.title.label=Bezeichnung
+com.arsdigita.ui.admin.applications.desc.label=Beschreibung
diff --git a/ccm-core/src/com/arsdigita/ui/UIResources_en.properties b/ccm-core/src/com/arsdigita/ui/UIResources_en.properties
index ff63cc0cf..464d988f3 100755
--- a/ccm-core/src/com/arsdigita/ui/UIResources_en.properties
+++ b/ccm-core/src/com/arsdigita/ui/UIResources_en.properties
@@ -19,3 +19,12 @@ ui.debug.transform.on=Display transformation
ui.debug.transform.off=Hide transformation
ui.debug.xml=View document XML
ui.debug.xsl=Download XSL files
+com.arsdigita.ui.admin.applications.tree.heading=Applications
+com.arsdigita.ui.admin.applications.url.validation.not_blank=
+com.arsdigita.ui.admin.applications.url.valiation.minmaxlength=
+com.arsdigita.ui.admin.applications.title.validation.not_blank=
+com.arsdigita.ui.admin.applications.title.valiation.minmaxlength=
+com.arsdigita.ui.admin.applications.desc.valiation.minmaxlength=
+com.arsdigita.ui.admin.applications.url.label=
+com.arsdigita.ui.admin.applications.title.label=
+com.arsdigita.ui.admin.applications.desc.label=
diff --git a/ccm-core/src/com/arsdigita/ui/UIResources_fr.properties b/ccm-core/src/com/arsdigita/ui/UIResources_fr.properties
index 692322fae..4f5b28d07 100755
--- a/ccm-core/src/com/arsdigita/ui/UIResources_fr.properties
+++ b/ccm-core/src/com/arsdigita/ui/UIResources_fr.properties
@@ -1,4 +1,4 @@
-ui.admin.access_denied=Accès refusé
+ui.admin.access_denied=Acc\u00e8s refus\u00e9
ui.admin.help=Aide
ui.admin.signout=se deconnecter
ui.admin.portal=Ma Porte
@@ -14,4 +14,13 @@ ui.sitemap.h4sitemap_treeh4=Plan du site
ui.sitemap.configure_sitemap_admin_page=TRANSLATE THIS: Configure SiteMap Admin Page (ui.sitemap.configure_sitemap_admin_page)
ui.sitemap.h4emselect_sitenode_to_view_detailsemh4=TRANSLATE THIS: Select SiteNode to View Details
(ui.sitemap.h4emselect_sitenode_to_view_detailsemh4)
ui.sitemap.configuration_menu_placeholder=TRANSLATE THIS: Configuration Menu Placeholder (ui.sitemap.configuration_menu_placeholder)
-ui.sitemap.access_denied_to_sitemap=Vous n'avez pas accès au plan du site
+ui.sitemap.access_denied_to_sitemap=Vous n'avez pas acc\u00e8s au plan du site
+com.arsdigita.ui.admin.applications.tree.heading=
+com.arsdigita.ui.admin.applications.url.validation.not_blank=
+com.arsdigita.ui.admin.applications.url.valiation.minmaxlength=
+com.arsdigita.ui.admin.applications.title.validation.not_blank=
+com.arsdigita.ui.admin.applications.title.valiation.minmaxlength=
+com.arsdigita.ui.admin.applications.desc.valiation.minmaxlength=
+com.arsdigita.ui.admin.applications.url.label=
+com.arsdigita.ui.admin.applications.title.label=
+com.arsdigita.ui.admin.applications.desc.label=
diff --git a/ccm-core/src/com/arsdigita/ui/admin/AdminResources.properties b/ccm-core/src/com/arsdigita/ui/admin/AdminResources.properties
index 8ef1a8e31..7c8d5dbb6 100644
--- a/ccm-core/src/com/arsdigita/ui/admin/AdminResources.properties
+++ b/ccm-core/src/com/arsdigita/ui/admin/AdminResources.properties
@@ -90,3 +90,40 @@ ui.admin.user.userpasswordform.confirmpasswordlabel=Confirm password:
ui.admin.user.userpasswordform.passwordlabel=Password:
ui.admin.user.userpasswordform.question=Question:
ui.admin.user.userpasswordform.submit=Change
+ui.admin.tab.applications.title=Applications
+ui.admin.applications.tree.heading=Applications
+ui.admin.applications.url.validation.not_blank=The URL of an application instance can is mandatory.
+ui.admin.applications.url.valiation.minmaxlength=The length of an URL of an application instance must be between 1 and 100 characters.
+ui.admin.applications.title.validation.not_blank=Title is mandatory for an application instance.
+ui.admin.applications.title.valiation.minmaxlength=The minimum length of the title of an applicatio instance is one character, the maximum length are 200 characters
+ui.admin.applications.desc.valiation.minmaxlength=The maximum length of a descrption of an application instance are 4000 characters.
+ui.admin.applications.url.label=URL
+ui.admin.applications.title.label=Title
+ui.admin.applications.desc.label=Description
+ui.admin.applications.url.validation.url_already_in_use=The provided URL is already in use
+ui.admin.applications.url.validation.no_slash_allowed=The URL fragement may not contain slashes
+ui.admin.applications.ApplicationInstancePane.title.label=Title of the instance
+ui.admin.applications.ApplicationInstancePane.parent_app.label=Parent application
+ui.admin.applications.ApplicationInstancePane.path.label=Path of the application
+ui.admin.applications.ApplicationInstancePane.desc.label=Description
+ui.admin.applications.ApplicationInstancePane.info.heading=Instance data
+ui.admin.MultiInstanceApplicationPane.manage.heading=Edit instance specific settings
+ui.admin.MultiInstancePane.manage.no_instance_admin_pane_found=No instance admin pane for instances of the application type '{0]' found. Maybe it is not implemented yet.
+ui.admin.applications.ApplicationInfoSection.title.label=Title
+ui.admin.applications.ApplicationInfoSection.app_class.label=Application Class
+ui.admin.applications.ApplicationInfoSection.singleton.label=Singleton
+ui.admin.applications.ApplicationInfoSection.singleton.yes=Yes
+ui.admin.applications.ApplicationInfoSection.singleton.no=No
+ui.admin.applications.ApplicationInfoSection.singleton_instance.path.label=Path of the singleton instance
+ui.admin.applications.ApplicationInfoSection.singleton_instance.no_instance_found=No instance found
+ui.admin.applications.ApplicationInfoSection.heading=Application information
+ui.admin.applicationsMultiInstanceApplicationPane.instances.table.col_title.header=Title
+ui.admin.applicationsMultiInstanceApplicationPane.instances.table.col_url.header=Path
+ui.admin.applicationsMultiInstanceApplicationPane.instances.table.col_desc.header=Description
+ui.admin.MultiInstanceApplicationPane.instances=Instances
+ui.admin.MultiInstanceApplicationPane.manage_instances.heading=Manage instances
+ui.admin.MultiInstancePane.manage.no_create_form_found=No form for manageing instances of type '{0}' found.
+ui.admin.MultiInstanceApplicationPane.create_instance=Create new instance
+ui.admin.SingletonApplicationPane.manage.heading=Edit settings
+ui.admin.SingletonApplicationPane.manage.no_admin_pane_found=No admin pane for applications of type '{0}' found.
+ui.admin.applications.ApplicationInfoSection.desc.label=Description
diff --git a/ccm-core/src/com/arsdigita/ui/admin/AdminResources_de.properties b/ccm-core/src/com/arsdigita/ui/admin/AdminResources_de.properties
index ffce9dcce..6fb96295a 100644
--- a/ccm-core/src/com/arsdigita/ui/admin/AdminResources_de.properties
+++ b/ccm-core/src/com/arsdigita/ui/admin/AdminResources_de.properties
@@ -4,14 +4,14 @@ ui.admin.dispatcher.groupsLabel=Gruppen
ui.admin.dispatcher.title=Administration
ui.admin.dispatcher.usersLabel=Benutzer
ui.admin.groups.actioncontinue=Fortsetzen
-ui.admin.groups.groupdeletefailed=Gruppe nicht zu l\u00F6schbar
+ui.admin.groups.groupdeletefailed=Gruppe nicht zu l\u00f6schbar
ui.admin.groups.add=Neue Untergruppe erstellen
ui.admin.groups.addeditform.namelabel=Name:
-ui.admin.groups.addeditform.primaryemaillabel=Prim\u00E4re E-Mail\:
+ui.admin.groups.addeditform.primaryemaillabel=Prim\u00e4re E-Mail\:
ui.admin.groups.addeditform.submit=Sichern
-ui.admin.groups.addgrouplabel=Neue Gruppe hinzuf\u00FCgen
-ui.admin.groups.addsubmemberlabel=Mitglied hinzuf\u00FCgen
-ui.admin.groups.delete=Gruppe l\u00F6schen
+ui.admin.groups.addgrouplabel=Neue Gruppe hinzuf\u00fcgen
+ui.admin.groups.addsubmemberlabel=Mitglied hinzuf\u00fcgen
+ui.admin.groups.delete=Gruppe l\u00f6schen
ui.admin.groups.deletefailed=Fehlermeldung
ui.admin.groups.edit=Edit
ui.admin.groups.groupedit=Edit Group
@@ -27,22 +27,22 @@ ui.admin.groups.removeExisting=Als Subgruppe entfernen
ui.admin.groups.search=Search for groups to add as subgroups
ui.admin.groups.button.search=Suche
ui.admin.groups.searchForm.noResults=Your search matches no valid groups (groups that cause circular references are excluded). Search again
-ui.admin.groups.found.title=Gruppen ausw\u00E4hlen
-ui.admin.groups.select.explanation=Gruppen ausw\u00E4hlen\:
+ui.admin.groups.found.title=Gruppen ausw\u00e4hlen
+ui.admin.groups.select.explanation=Gruppen ausw\u00e4hlen\:
ui.admin.save=Sichern
ui.admin.nav.logout=Abmelden
ui.admin.nav.workspace=Workspace
ui.admin.searchAndList.submit=Suchen
ui.admin.searchAndList.submitAgain=Erneut suchen
ui.admin.tab.group.title=Gruppen
-ui.admin.tab.user.browse=Bl\u00E4ttern
+ui.admin.tab.user.browse=Bl\u00e4ttern
ui.admin.tab.user.createuser=Neuen Benutzer erstellen
ui.admin.tab.user.navbartitle=Benutzerverwaltung
ui.admin.tab.user.search=Suche
ui.admin.tab.user.summary=Zusammenfassung
ui.admin.tab.user.title=Benutzer
ui.admin.user.action.continue=Fortfahren
-ui.admin.user.action.delete.failed.header=Benutzer kann nicht gel\u00F6scht werden
+ui.admin.user.action.delete.failed.header=Benutzer kann nicht gel\u00f6scht werden
ui.admin.user.action.header=Aktionen
ui.admin.user.addeditform.additionalemail=Weitere E-Mail Adresse\:
ui.admin.user.addeditform.additionalemaillist=Weitere E-Mail
@@ -51,7 +51,7 @@ ui.admin.user.addeditform.confirmation=Password Confirmation:
ui.admin.user.addeditform.deleteemail=Entfernen
ui.admin.user.addeditform.error.answer.null=This parameter is required.
ui.admin.user.addeditform.error.answer.whitespace=Answer must be empty or contain non-whitespace characters
-ui.admin.user.addeditform.error.password.notmatch=Passworte stimmen nicht \u00FCberein
+ui.admin.user.addeditform.error.password.notmatch=Passworte stimmen nicht \u00fcberein
ui.admin.user.addeditform.error.primaryemail.notunique=Primary email is not unique
ui.admin.user.addeditform.error.screenname.notunique=Benutzername ist nicht eindeutig
ui.admin.user.addeditform.firstname=Vorname\:
@@ -74,8 +74,8 @@ ui.admin.user.ban.confirm=Are you sure you want to ban this user?
ui.admin.user.unban.confirm=Are you sure you want to unban this user?
ui.admin.user.ban.label=Ban user
ui.admin.user.unban.label=Unban user
-ui.admin.user.delete.failed.label=L\u00F6schen des Benutzers fehlgeschlagen
-ui.admin.user.delete.label=Benutzer l\u00F6schen
+ui.admin.user.delete.failed.label=L\u00f6schen des Benutzers fehlgeschlagen
+ui.admin.user.delete.label=Benutzer l\u00f6schen
ui.admin.user.editlink=Editieren
ui.admin.user.groupmembership.header=Group Membership
ui.admin.user.password.header=Update Security Information
@@ -86,7 +86,44 @@ ui.admin.user.summarypanel.totalusers=Total users:
ui.admin.user.useredit.header=Edit User Information
ui.admin.user.userinfo.header=Benutzerinformation
ui.admin.user.userpasswordform.answer=Antwort\:
(Leerlassen um aktuelle Antwort beizubehalten)
-ui.admin.user.userpasswordform.confirmpasswordlabel=Passwort best\u00E4tigen\:
+ui.admin.user.userpasswordform.confirmpasswordlabel=Passwort best\u00e4tigen\:
ui.admin.user.userpasswordform.passwordlabel=Passwort\:
ui.admin.user.userpasswordform.question=Frage\:
-ui.admin.user.userpasswordform.submit=\u00C4ndern
+ui.admin.user.userpasswordform.submit=\u00c4ndern
+ui.admin.tab.applications.title=Applikationen
+ui.admin.applications.tree.heading=Applikationen
+ui.admin.applications.url.validation.not_blank=Die Angabe einer URL ist erforderlich
+ui.admin.applications.url.valiation.minmaxlength=Die URL einer Applikations-Instanz muss zwischen einem und 100 Zeichen lang sein
+ui.admin.applications.title.validation.not_blank=Der Titel ist f\u00fcr das Anlegen einer Applikations-Instanz erforderlich
+ui.admin.applications.title.valiation.minmaxlength=Der Titel einer Applikations-Instanz muss zwischen einem und 200 Zeichen lang sein.
+ui.admin.applications.desc.valiation.minmaxlength=Die Beschreibung einer Applikations-Instanz darf max. 4000 Zeichen lang sein
+ui.admin.applications.url.label=URL
+ui.admin.applications.title.label=Bezeichnung
+ui.admin.applications.desc.label=Beschreibung
+ui.admin.applications.url.validation.url_already_in_use=Der angegebene URL wird bereits verwendet
+ui.admin.applications.url.validation.no_slash_allowed=Das URL-Fragment darf keine Schr\u00e4gstriche enthalten
+ui.admin.applications.ApplicationInstancePane.title.label=Titel der Instanz
+ui.admin.applications.ApplicationInstancePane.parent_app.label=\u00dcbergeordnete Applikation
+ui.admin.applications.ApplicationInstancePane.path.label=Pfad der Applikation
+ui.admin.applications.ApplicationInstancePane.desc.label=Beschreibung
+ui.admin.applications.ApplicationInstancePane.info.heading=Daten der Instanz
+ui.admin.MultiInstanceApplicationPane.manage.heading=Einstellungen der Instanz bearbeiten
+ui.admin.MultiInstancePane.manage.no_instance_admin_pane_found=Kein Administrationsformular f\u00fcr Instanzen des Applikationstyps '{0}' gefunden. M\u00f6glicherweise noch nicht implementiert.
+ui.admin.applications.ApplicationInfoSection.title.label=Titel
+ui.admin.applications.ApplicationInfoSection.app_class.label=Applikationsklasse
+ui.admin.applications.ApplicationInfoSection.singleton.label=Singleton
+ui.admin.applications.ApplicationInfoSection.singleton.yes=Ja
+ui.admin.applications.ApplicationInfoSection.singleton.no=Nein
+ui.admin.applications.ApplicationInfoSection.singleton_instance.path.label=Pfad der Singleton Instanz
+ui.admin.applications.ApplicationInfoSection.singleton_instance.no_instance_found=Keine Instanz gefunden
+ui.admin.applications.ApplicationInfoSection.heading=Applikationsinformationen
+ui.admin.applicationsMultiInstanceApplicationPane.instances.table.col_title.header=Titel
+ui.admin.applicationsMultiInstanceApplicationPane.instances.table.col_url.header=Pfad
+ui.admin.applicationsMultiInstanceApplicationPane.instances.table.col_desc.header=Beschreibung
+ui.admin.MultiInstanceApplicationPane.instances=Instanzen
+ui.admin.MultiInstanceApplicationPane.manage_instances.heading=Instanzen verwalten
+ui.admin.MultiInstancePane.manage.no_create_form_found=Keine Formular zum verwalten von Applikationen des Types '{0}' gefunden.
+ui.admin.MultiInstanceApplicationPane.create_instance=Neue instanz anlegen
+ui.admin.SingletonApplicationPane.manage.heading=Eigenschaften bearbeiten
+ui.admin.SingletonApplicationPane.manage.no_admin_pane_found=Keine Admin-Formular f\u00fcr Applikationen des Types '{0}' gefunden
+ui.admin.applications.ApplicationInfoSection.desc.label=Beschreibung
diff --git a/ccm-core/src/com/arsdigita/ui/admin/AdminResources_en.properties b/ccm-core/src/com/arsdigita/ui/admin/AdminResources_en.properties
index 8ef1a8e31..1276d793d 100755
--- a/ccm-core/src/com/arsdigita/ui/admin/AdminResources_en.properties
+++ b/ccm-core/src/com/arsdigita/ui/admin/AdminResources_en.properties
@@ -90,3 +90,40 @@ ui.admin.user.userpasswordform.confirmpasswordlabel=Confirm password:
ui.admin.user.userpasswordform.passwordlabel=Password:
ui.admin.user.userpasswordform.question=Question:
ui.admin.user.userpasswordform.submit=Change
+ui.admin.tab.applications.title=Applications
+ui.admin.applications.tree.heading=Applications
+ui.admin.applications.url.validation.not_blank=
+ui.admin.applications.url.valiation.minmaxlength=
+ui.admin.applications.title.validation.not_blank=
+ui.admin.applications.title.valiation.minmaxlength=
+ui.admin.applications.desc.valiation.minmaxlength=
+ui.admin.applications.url.label=
+ui.admin.applications.title.label=
+ui.admin.applications.desc.label=
+ui.admin.applications.url.validation.url_already_in_use=
+ui.admin.applications.url.validation.no_slash_allowed=
+ui.admin.applications.ApplicationInstancePane.title.label=
+ui.admin.applications.ApplicationInstancePane.parent_app.label=
+ui.admin.applications.ApplicationInstancePane.path.label=
+ui.admin.applications.ApplicationInstancePane.desc.label=
+ui.admin.applications.ApplicationInstancePane.info.heading=
+ui.admin.MultiInstanceApplicationPane.manage.heading=
+ui.admin.MultiInstancePane.manage.no_instance_admin_pane_found=
+ui.admin.applications.ApplicationInfoSection.title.label=
+ui.admin.applications.ApplicationInfoSection.app_class.label=
+ui.admin.applications.ApplicationInfoSection.singleton.label=
+ui.admin.applications.ApplicationInfoSection.singleton.yes=
+ui.admin.applications.ApplicationInfoSection.singleton.no=
+ui.admin.applications.ApplicationInfoSection.singleton_instance.path.label=
+ui.admin.applications.ApplicationInfoSection.singleton_instance.no_instance_found=
+ui.admin.applications.ApplicationInfoSection.heading=
+ui.admin.applicationsMultiInstanceApplicationPane.instances.table.col_title.header=
+ui.admin.applicationsMultiInstanceApplicationPane.instances.table.col_url.header=
+ui.admin.applicationsMultiInstanceApplicationPane.instances.table.col_desc.header=
+ui.admin.MultiInstanceApplicationPane.instances=
+ui.admin.MultiInstanceApplicationPane.manage_instances.heading=
+ui.admin.MultiInstancePane.manage.no_create_form_found=
+ui.admin.MultiInstanceApplicationPane.create_instance=
+ui.admin.SingletonApplicationPane.manage.heading=
+ui.admin.SingletonApplicationPane.manage.no_admin_pane_found=
+ui.admin.applications.ApplicationInfoSection.desc.label=
diff --git a/ccm-core/src/com/arsdigita/ui/admin/AdminResources_fr.properties b/ccm-core/src/com/arsdigita/ui/admin/AdminResources_fr.properties
index 0a8782e27..10da70198 100755
--- a/ccm-core/src/com/arsdigita/ui/admin/AdminResources_fr.properties
+++ b/ccm-core/src/com/arsdigita/ui/admin/AdminResources_fr.properties
@@ -1,4 +1,4 @@
-ui.admin.dispatcher.accessDenied=Accès refusé
+ui.admin.dispatcher.accessDenied=Acc\u00e8s refus\u00e9
ui.admin.dispatcher.contextTitle=Index
ui.admin.dispatcher.groupsLabel=Groupes
ui.admin.dispatcher.title=Administration
@@ -21,31 +21,31 @@ ui.admin.groups.removesubmemberlabel=Retirer
ui.admin.groups.subgroupcountlabel=Sous-groupes:
ui.admin.groups.subgroups=Information sur le sous-groupe
ui.admin.groups.submembers=Information sur le membre
-ui.admin.nav.logout=Déconnexion
+ui.admin.nav.logout=D\u00e9connexion
ui.admin.nav.workspace=Espace de travail
ui.admin.searchAndList.submit=Rechercher
ui.admin.searchAndList.submitAgain=Rechercher suivant
ui.admin.tab.group.title=Groupe
ui.admin.tab.user.browse=Parcourir
-ui.admin.tab.user.createuser=Créer un nouvel utilisateur
+ui.admin.tab.user.createuser=Cr\u00e9er un nouvel utilisateur
ui.admin.tab.user.navbartitle=Gestion de l'utilisateur
ui.admin.tab.user.search=Rechercher
-ui.admin.tab.user.summary=Table des matières
+ui.admin.tab.user.summary=Table des mati\u00e8res
ui.admin.tab.user.title=Utilisateurs
ui.admin.user.action.continue=Continuer
ui.admin.user.action.delete.failed.header=Impossible de supprimer l'utiisateur
ui.admin.user.action.header=Actions
ui.admin.user.addeditform.additionalemail=Adresse e-mail secondaire:
ui.admin.user.addeditform.additionalemaillist=Autre e-mail
-ui.admin.user.addeditform.answer=Réponse:
+ui.admin.user.addeditform.answer=R\u00e9ponse:
ui.admin.user.addeditform.confirmation=Confirmation du mot de passe:
ui.admin.user.addeditform.deleteemail=Supprimer
-ui.admin.user.addeditform.error.answer.null=Ce paramètre est obligatoire.
-ui.admin.user.addeditform.error.answer.whitespace=La réponse doit être vide ou ne pas contenir d'espace
-ui.admin.user.addeditform.error.password.notmatch=Les mots de passe sont différents
+ui.admin.user.addeditform.error.answer.null=Ce param\u00e8tre est obligatoire.
+ui.admin.user.addeditform.error.answer.whitespace=La r\u00e9ponse doit \u00eatre vide ou ne pas contenir d'espace
+ui.admin.user.addeditform.error.password.notmatch=Les mots de passe sont diff\u00e9rents
ui.admin.user.addeditform.error.primaryemail.notunique=L'e-mail principal n'est pas unique
ui.admin.user.addeditform.error.screenname.notunique=Screen name n'est pas unique
-ui.admin.user.addeditform.firstname=Prénom:
+ui.admin.user.addeditform.firstname=Pr\u00e9nom:
ui.admin.user.addeditform.lastname=Nom:
ui.admin.user.addeditform.password=Mot de passe:
ui.admin.user.addeditform.primaryemail=e-mail principal:
@@ -56,23 +56,60 @@ ui.admin.user.addeditform.url=URL:
ui.admin.user.browselink=Montrer tout
ui.admin.user.browsepanel.becomeUser=Devenir cet utilisateur
ui.admin.user.browsepanel.extremeaction=Action dangereuse
-ui.admin.user.browsepanel.header=Tous les utilisateurs enregistrés
-ui.admin.user.browsepanel.updatePassword=Mettre à jour le mot de passe
-ui.admin.user.createpanel.header=Créer un nouvel utilisateur
-ui.admin.user.delete.confirm=Etes vous sûr de vouloir supprimer cet utilisateur?
-ui.admin.user.delete.failed.label=La suppression de l'utilisateur a échoué
+ui.admin.user.browsepanel.header=Tous les utilisateurs enregistr\u00e9s
+ui.admin.user.browsepanel.updatePassword=Mettre \u00e0 jour le mot de passe
+ui.admin.user.createpanel.header=Cr\u00e9er un nouvel utilisateur
+ui.admin.user.delete.confirm=Etes vous s\u00fbr de vouloir supprimer cet utilisateur?
+ui.admin.user.delete.failed.label=La suppression de l'utilisateur a \u00e9chou\u00e9
ui.admin.user.delete.label=Supprimer cet utilisateur
ui.admin.user.editlink=Modifier
ui.admin.user.groupmembership.header=Groupe(s) d'appartenance
-ui.admin.user.password.header=Mise à jour des informations de sécurité
+ui.admin.user.password.header=Mise \u00e0 jour des informations de s\u00e9curit\u00e9
ui.admin.user.search.header=Rechercher
-ui.admin.user.summarypanel.createUser=Créer un nouvel utilisateur
-ui.admin.user.summarypanel.header=Table des matières
+ui.admin.user.summarypanel.createUser=Cr\u00e9er un nouvel utilisateur
+ui.admin.user.summarypanel.header=Table des mati\u00e8res
ui.admin.user.summarypanel.totalusers=Total des utilisateurs:
ui.admin.user.useredit.header=Modifier les informations de l'utilisateur
ui.admin.user.userinfo.header=Informations de l'utilisateur
-ui.admin.user.userpasswordform.answer=Réponse:
(Ne pas remplir pour conserver la réponse actuelle)
+ui.admin.user.userpasswordform.answer=R\u00e9ponse:
(Ne pas remplir pour conserver la r\u00e9ponse actuelle)
ui.admin.user.userpasswordform.confirmpasswordlabel=Confirmer le mot de passe:
ui.admin.user.userpasswordform.passwordlabel=Mot de passe:
ui.admin.user.userpasswordform.question=Question:
ui.admin.user.userpasswordform.submit=Changer
+ui.admin.tab.applications.title=
+ui.admin.applications.tree.heading=
+ui.admin.applications.url.validation.not_blank=
+ui.admin.applications.url.valiation.minmaxlength=
+ui.admin.applications.title.validation.not_blank=
+ui.admin.applications.title.valiation.minmaxlength=
+ui.admin.applications.desc.valiation.minmaxlength=
+ui.admin.applications.url.label=
+ui.admin.applications.title.label=
+ui.admin.applications.desc.label=
+ui.admin.applications.url.validation.url_already_in_use=
+ui.admin.applications.url.validation.no_slash_allowed=
+ui.admin.applications.ApplicationInstancePane.title.label=
+ui.admin.applications.ApplicationInstancePane.parent_app.label=
+ui.admin.applications.ApplicationInstancePane.path.label=
+ui.admin.applications.ApplicationInstancePane.desc.label=
+ui.admin.applications.ApplicationInstancePane.info.heading=
+ui.admin.MultiInstanceApplicationPane.manage.heading=
+ui.admin.MultiInstancePane.manage.no_instance_admin_pane_found=
+ui.admin.applications.ApplicationInfoSection.title.label=
+ui.admin.applications.ApplicationInfoSection.app_class.label=
+ui.admin.applications.ApplicationInfoSection.singleton.label=
+ui.admin.applications.ApplicationInfoSection.singleton.yes=
+ui.admin.applications.ApplicationInfoSection.singleton.no=
+ui.admin.applications.ApplicationInfoSection.singleton_instance.path.label=
+ui.admin.applications.ApplicationInfoSection.singleton_instance.no_instance_found=
+ui.admin.applications.ApplicationInfoSection.heading=
+ui.admin.applicationsMultiInstanceApplicationPane.instances.table.col_title.header=
+ui.admin.applicationsMultiInstanceApplicationPane.instances.table.col_url.header=
+ui.admin.applicationsMultiInstanceApplicationPane.instances.table.col_desc.header=
+ui.admin.MultiInstanceApplicationPane.instances=
+ui.admin.MultiInstanceApplicationPane.manage_instances.heading=
+ui.admin.MultiInstancePane.manage.no_create_form_found=
+ui.admin.MultiInstanceApplicationPane.create_instance=
+ui.admin.SingletonApplicationPane.manage.heading=
+ui.admin.SingletonApplicationPane.manage.no_admin_pane_found=
+ui.admin.applications.ApplicationInfoSection.desc.label=
diff --git a/ccm-core/src/com/arsdigita/ui/admin/AdminServlet.java b/ccm-core/src/com/arsdigita/ui/admin/AdminServlet.java
index d38092604..cb99f84bf 100644
--- a/ccm-core/src/com/arsdigita/ui/admin/AdminServlet.java
+++ b/ccm-core/src/com/arsdigita/ui/admin/AdminServlet.java
@@ -18,6 +18,7 @@
*/
package com.arsdigita.ui.admin;
+import com.arsdigita.ui.admin.ApplicationsAdministrationTab;
import com.arsdigita.bebop.Page;
import com.arsdigita.bebop.PageFactory;
import com.arsdigita.bebop.TabbedPane;
@@ -194,11 +195,11 @@ public class AdminServlet extends BaseApplicationServlet
/*
* Create application administration panel
*/
- ApplicationsAdministrationTab appsAdministrationTab =
- new ApplicationsAdministrationTab();
-
+ ApplicationsAdministrationTab appsAdministrationTab =
+ new ApplicationsAdministrationTab();
+
SettingsTab settingsTab = new SettingsTab();
-
+
// Create the Admin's page tab bar, currently 2 elements: user & groups
TabbedPane tb = new TabbedPane();
@@ -218,4 +219,5 @@ public class AdminServlet extends BaseApplicationServlet
return p;
}
+
}
diff --git a/ccm-core/src/com/arsdigita/ui/admin/ApplicationsAdministrationTab.java b/ccm-core/src/com/arsdigita/ui/admin/ApplicationsAdministrationTab.java
index 107ed8394..c2ac68d70 100644
--- a/ccm-core/src/com/arsdigita/ui/admin/ApplicationsAdministrationTab.java
+++ b/ccm-core/src/com/arsdigita/ui/admin/ApplicationsAdministrationTab.java
@@ -1,6 +1,20 @@
/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
+ * Copyright (c) 2013 Jens Pelzetter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
*/
package com.arsdigita.ui.admin;
@@ -8,9 +22,11 @@ import com.arsdigita.bebop.BoxPanel;
import com.arsdigita.bebop.Component;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.Link;
+import com.arsdigita.bebop.Page;
import com.arsdigita.bebop.PageState;
-import com.arsdigita.bebop.SplitPanel;
+import com.arsdigita.bebop.SimpleContainer;
import com.arsdigita.bebop.Table;
+import com.arsdigita.bebop.Tree;
import com.arsdigita.bebop.event.ChangeEvent;
import com.arsdigita.bebop.event.ChangeListener;
import com.arsdigita.bebop.event.TableActionEvent;
@@ -20,53 +36,251 @@ import com.arsdigita.bebop.table.TableColumn;
import com.arsdigita.bebop.table.TableColumnModel;
import com.arsdigita.bebop.table.TableModel;
import com.arsdigita.bebop.table.TableModelBuilder;
-import com.arsdigita.globalization.GlobalizedMessage;
+import com.arsdigita.toolbox.ui.LayoutPanel;
+import com.arsdigita.toolbox.ui.Section;
+import com.arsdigita.ui.admin.applications.ApplicationCreateForm;
+import com.arsdigita.ui.admin.applications.ApplicationInstancePane;
+import com.arsdigita.ui.admin.applications.ApplicationManager;
+import com.arsdigita.ui.admin.applications.BaseApplicationPane;
+import com.arsdigita.ui.admin.applications.MultiInstanceApplicationPane;
+import com.arsdigita.ui.admin.applications.SingletonApplicationPane;
+import com.arsdigita.ui.admin.applications.tree.ApplicationTreeModelBuilder;
+import com.arsdigita.ui.admin.GlobalizationUtil;
import com.arsdigita.util.LockableImpl;
import com.arsdigita.web.Application;
import com.arsdigita.web.ApplicationCollection;
+import com.arsdigita.web.ApplicationType;
+import com.arsdigita.web.ApplicationTypeCollection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.ServiceLoader;
/**
- *
+ * A tab for managing Application and application instances.
+ *
* @author pb
* @author Jens Pelzetter
*/
-public class ApplicationsAdministrationTab extends BoxPanel
- implements AdminConstants, ChangeListener {
+public class ApplicationsAdministrationTab extends BoxPanel implements AdminConstants, ChangeListener {
- private GlobalizedMessage m_title;
+ //private GlobalizedMessage m_title;
+ private final Tree applicationTree;
+ private final Map singletonAppPanes =
+ new HashMap();
+ private final Map> multiInstAppPanes =
+ new HashMap>();
+ private final Map appPanes = new HashMap();
+ private final Map instancePanes = new HashMap();
+ private SimpleContainer visiblePane;
+ //private final Label label1;
+ //private final Label label2;
/**
* Constructor
*/
public ApplicationsAdministrationTab() {
+ super();
+
// m_title = "TEST für ein neues Pannel";
setClassAttr("sidebarNavPanel");
setAttribute("navbar-title", "Sitemap");
// m_componentList = new ArrayList();
// m_keys = new ArrayList();
+ applicationTree = new Tree(new ApplicationTreeModelBuilder());
+ applicationTree.addChangeListener(this);
- final BoxPanel box = new BoxPanel();
- box.setClassAttr("main");
+ final Section treeSection = new Section();
+ treeSection.setHeading(GlobalizationUtil.globalize("ui.admin.applications.tree.heading"));
+ treeSection.setBody(applicationTree);
- final SplitPanel panel = new SplitPanel();
- panel.setClassAttr("sidebarNavPanel");
- panel.setRightComponent(box);
+ //final BoxPanel panel1 = new BoxPanel();
+// label1 = new Label("login");
+// label2 = new Label("ppp");
+// panel1.add(label1);
+// panel1.add(label2);
- box.add(new ApplicationsTable());
+
+ final LayoutPanel panel = new LayoutPanel();
+ //panel.setLeft(applicationTree);
+ panel.setLeft(treeSection);
+ //panel.setRight(new Label("<<>>"));
+ //panel.setRight(panel1);
+
+ final ApplicationTypeCollection applicationTypes = ApplicationType.retrieveAllApplicationTypes();
+
+ final Map> createForms = retrieveAppCreateForms();
+ final Map> managementForms = retrieveAppManagers();
+
+ while (applicationTypes.next()) {
+ if (applicationTypes.getApplicationType().isSingleton()) {
+ createSingletonAppPane(applicationTypes.getApplicationType(), managementForms);
+ } else {
+ createAppPane(applicationTypes.getApplicationType(), createForms, managementForms);
+ }
+ }
+
+ final BoxPanel appPanel = new BoxPanel();
+ for (Map.Entry entry : appPanes.entrySet()) {
+ appPanel.add(entry.getValue());
+ }
+
+ for (Map.Entry entry : instancePanes.entrySet()) {
+ appPanel.add(entry.getValue());
+ }
+ panel.setRight(appPanel);
+
+
+
+
+
+// final BoxPanel box = new BoxPanel();
+// box.setClassAttr("main");
+//
+// final SplitPanel panel = new SplitPanel();
+// panel.setClassAttr("sidebarNavPanel");
+// panel.setLeftComponent(applicationTree);
+// panel.setRightComponent(box);
+//
+// //box.add(new ApplicationsTable());
+// box.add(new Label("<<>>"));
+//
add(panel);
}
+ @SuppressWarnings("rawtypes")
+ private Map> retrieveAppCreateForms() {
+ final Map> appCreateForms = new HashMap>();
+
+ final ServiceLoader loader = ServiceLoader.load(ApplicationCreateForm.class);
+ for (ApplicationCreateForm> appCreateForm : loader) {
+ appCreateForms.put(appCreateForm.getAppClassName(), appCreateForm);
+ }
+
+ return appCreateForms;
+ }
+
+ @SuppressWarnings("rawtypes")
+ private Map> retrieveAppManagers() {
+ final Map> appManagers = new HashMap>();
+
+ final ServiceLoader loader = ServiceLoader.load(ApplicationManager.class);
+ for (ApplicationManager> appManager : loader) {
+ appManagers.put(appManager.getApplication().getName(), appManager);
+ }
+
+ return appManagers;
+ }
+
+ private void createSingletonAppPane(final ApplicationType applicationType,
+ final Map> managementForms) {
+ final String appObjectType = applicationType.getApplicationObjectType();
+
+ final ApplicationManager> manager = managementForms.get(appObjectType);
+ final SingletonApplicationPane pane;
+ if (manager == null) {
+ pane = new SingletonApplicationPane(applicationType, null);
+ } else {
+ pane = new SingletonApplicationPane(
+ applicationType, managementForms.get(appObjectType).getApplicationAdminForm());
+ }
+ //singletonAppPanes.put(appObjectType, pane);
+ appPanes.put(appObjectType, pane);
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ private void createAppPane(final ApplicationType applicationType,
+ final Map> createForms,
+ final Map> managementForms) {
+ final MultiInstanceApplicationPane> appPane = new MultiInstanceApplicationPane(
+ applicationType,
+ createForms.get(applicationType.getApplicationObjectType()));
+ //multiInstAppPanes.put(applicationType.getApplicationObjectType(), appPane);
+ appPanes.put(applicationType.getApplicationObjectType(), appPane);
+
+ final ApplicationCollection instances = Application.retrieveAllApplications(
+ applicationType.getApplicationObjectType());
+
+ while (instances.next()) {
+ createInstancePane(instances.getApplication(), managementForms);
+ }
+ }
+
+ private void createInstancePane(final Application application,
+ final Map> managementForms) {
+ final ApplicationManager> manager = managementForms.get(application.getClass().getName());
+
+ final ApplicationInstancePane instPane;
+ if (manager == null) {
+ instPane = new ApplicationInstancePane(application, null);
+ } else {
+ instPane = new ApplicationInstancePane(
+ application,
+ managementForms.get(application.getClass().getName()).getApplicationAdminForm());
+ }
+ instancePanes.put(application.getClass().getName(), instPane);
+ }
+
+ @Override
+ public void register(final Page page) {
+ super.register(page);
+
+ for (Map.Entry entry : appPanes.entrySet()) {
+ page.setVisibleDefault(entry.getValue(), false);
+ }
+ for (Map.Entry entry : instancePanes.entrySet()) {
+ page.setVisibleDefault(entry.getValue(), false);
+ }
+ //page.setVisibleDefault(label1, false);
+ //page.setVisibleDefault(label2, false);
+ }
+
/**
*
- * @param e
+ * @param event
*/
@Override
- public void stateChanged(ChangeEvent e) {
+ public void stateChanged(final ChangeEvent event) {
+
+ final PageState state = event.getPageState();
+
+ System.out.println("State changed.");
+
+ final String selectedKey = (String) applicationTree.getSelectedKey(state);
+ if (selectedKey != null) {
+ if (selectedKey.contains(".")) {
+ // Selected key is a classname and therefore the key of an ApplicationPane
+ final BaseApplicationPane pane = appPanes.get(selectedKey);
+ if (pane != null) {
+ setPaneVisible(pane, state);
+ }
+ } else {
+ // Selected key is the name of a instance pane
+ final ApplicationInstancePane pane = instancePanes.get(selectedKey);
+ if (pane != null) {
+ setPaneVisible(pane, state);
+ }
+ }
+ }
+
+ //ToDo Find out if the key is the key of a application type (contains dots) or the key of an instance (may
+ //contains Slashes
+
+
+// if ("ui.login.Login".equals(applicationTree.getSelectedKey(state))) {
+// label1.setVisible(state, true);
+// label2.setVisible(state, false);
+// } else if ("com.arsdigita.cms.publicpersonalprofile.PublicPersonalProfile".equals(applicationTree.
+// getSelectedKey(state))) {
+// label1.setVisible(state, false);
+// label2.setVisible(state, true);
+// } else {
+// label1.setVisible(state, false);
+// label2.setVisible(state, false);
+// }
- PageState ps = e.getPageState();
// String key = (String) m_tree.getSelectedKey(ps);
// added cg - reset existing group add panel to the search screen
// when a new group is selected from the tree
@@ -75,6 +289,15 @@ public class ApplicationsAdministrationTab extends BoxPanel
// setTab(selectedIndex, ps);
}
+ private void setPaneVisible(final SimpleContainer pane, final PageState state) {
+ if (visiblePane != null) {
+ visiblePane.setVisible(state, false);
+ }
+
+ pane.setVisible(state, true);
+ visiblePane = pane;
+ }
+
private class ApplicationsTable extends Table implements TableActionListener {
private static final String COL_APP_CLASS = "col_app_class";
@@ -100,7 +323,7 @@ public class ApplicationsAdministrationTab extends BoxPanel
colModel.add(new TableColumn(
2,
"App View URL",
- COL_APP_VIEW_URL));
+ COL_APP_VIEW_URL));
colModel.add(new TableColumn(
3,
"Is Singleton?",
@@ -145,7 +368,7 @@ public class ApplicationsAdministrationTab extends BoxPanel
final Object key,
final int row,
final int column) {
- return new Link(value.toString(), value.toString());
+ return new Link(value.toString(), value.toString());
}
});
@@ -219,7 +442,7 @@ public class ApplicationsAdministrationTab extends BoxPanel
case 1:
return app.getApplicationType().getTitle();
case 2:
- return app.getPath();
+ return app.getPath();
case 3:
return Boolean.toString(app.getApplicationType().isSingleton());
default:
@@ -239,7 +462,7 @@ public class ApplicationsAdministrationTab extends BoxPanel
return String.format("%s/%s", constructAppPath(app.getParentApplication()), app.getPath());
}
}
-
+
}
@Override
diff --git a/ccm-core/src/com/arsdigita/ui/admin/GlobalizationUtil.java b/ccm-core/src/com/arsdigita/ui/admin/GlobalizationUtil.java
new file mode 100644
index 000000000..0641f5c4a
--- /dev/null
+++ b/ccm-core/src/com/arsdigita/ui/admin/GlobalizationUtil.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2013 Jens Pelzetter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.ui.admin;
+
+import com.arsdigita.globalization.GlobalizedMessage;
+
+/**
+ * Globalization Util for admin classes
+ *
+ * @author Jens Pelzetter
+ * @version $Id$
+ */
+public class GlobalizationUtil {
+
+ private static final String BUNDLE_NAME = "com.arsdigita.ui.admin.AdminResources";
+
+ public static GlobalizedMessage globalize(final String key) {
+ return new GlobalizedMessage(key, BUNDLE_NAME);
+ }
+
+ public static GlobalizedMessage globalize(final String key, final Object[] args) {
+ return new GlobalizedMessage(key, BUNDLE_NAME, args);
+ }
+
+}
diff --git a/ccm-core/src/com/arsdigita/ui/admin/GroupAdministrationTab.java b/ccm-core/src/com/arsdigita/ui/admin/GroupAdministrationTab.java
index 632a998da..e1fdcebec 100755
--- a/ccm-core/src/com/arsdigita/ui/admin/GroupAdministrationTab.java
+++ b/ccm-core/src/com/arsdigita/ui/admin/GroupAdministrationTab.java
@@ -156,7 +156,8 @@ class GroupAdministrationTab extends BoxPanel
BoxPanel c = new BoxPanel();
c.setClassAttr("navbar");
- m_tree = new Tree(new GroupTreeModel());
+ //m_tree = new Tree(new GroupTreeModel());
+ m_tree = new Tree(new GroupTreeModelBuilder());
m_tree.addChangeListener(this);
c.add(m_tree);
diff --git a/ccm-core/src/com/arsdigita/ui/admin/GroupTreeModel.java b/ccm-core/src/com/arsdigita/ui/admin/GroupTreeModel.java
index 9c08ca604..062b96117 100755
--- a/ccm-core/src/com/arsdigita/ui/admin/GroupTreeModel.java
+++ b/ccm-core/src/com/arsdigita/ui/admin/GroupTreeModel.java
@@ -66,6 +66,7 @@ public class GroupTreeModel implements TreeModel {
public void remove() {
throw new UnsupportedOperationException();
}
+
}
/**
@@ -113,22 +114,22 @@ public class GroupTreeModel implements TreeModel {
DataCollection coll = ssn.retrieve("com.arsdigita.kernel.Group");
coll.addInSubqueryFilter("id", "com.arsdigita.ui.admin.AllNoParentGroups");
-
- coll.addOrder("lower("+ Group.DISPLAY_NAME + ") asc");
-
+
+ coll.addOrder("lower(" + Group.DISPLAY_NAME + ") asc");
+
return new GroupIterator(new ACSObjectCollection(coll));
} else {
Group group = null;
try {
- group = new Group(new BigDecimal((String)n.getKey()));
+ group = new Group(new BigDecimal((String) n.getKey()));
} catch (DataObjectNotFoundException ed) {
// Group is not found just return null.
return null;
}
GroupCollection coll = group.getSubgroups();
- coll.addOrder("lower("+ Group.DISPLAY_NAME + ") asc");
+ coll.addOrder("lower(" + Group.DISPLAY_NAME + ") asc");
return new GroupIterator(coll);
@@ -147,6 +148,7 @@ class RootTreeNode implements TreeNode {
public Object getElement() {
return "/";
}
+
}
class GroupTreeNode implements TreeNode {
@@ -166,4 +168,5 @@ class GroupTreeNode implements TreeNode {
public Object getElement() {
return m_name;
}
+
}
diff --git a/ccm-core/src/com/arsdigita/ui/admin/GroupTreeModelBuilder.java b/ccm-core/src/com/arsdigita/ui/admin/GroupTreeModelBuilder.java
new file mode 100644
index 000000000..f35773434
--- /dev/null
+++ b/ccm-core/src/com/arsdigita/ui/admin/GroupTreeModelBuilder.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2013 Jens Pelzetter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.ui.admin;
+
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.Tree;
+import com.arsdigita.bebop.tree.TreeModel;
+import com.arsdigita.bebop.tree.TreeModelBuilder;
+import com.arsdigita.util.LockableImpl;
+
+/**
+ *
+ * @author Jens Pelzetter
+ * @version $Id$
+ */
+public class GroupTreeModelBuilder extends LockableImpl implements TreeModelBuilder {
+
+ public TreeModel makeModel(final Tree tree, final PageState state) {
+ tree.expand("-1", state);
+ return new GroupTreeModel();
+ }
+
+}
diff --git a/ccm-core/src/com/arsdigita/ui/admin/SettingsTab.java b/ccm-core/src/com/arsdigita/ui/admin/SettingsTab.java
deleted file mode 100644
index 9b4eb530b..000000000
--- a/ccm-core/src/com/arsdigita/ui/admin/SettingsTab.java
+++ /dev/null
@@ -1,130 +0,0 @@
-package com.arsdigita.ui.admin;
-
-import com.arsdigita.bebop.BoxPanel;
-import com.arsdigita.bebop.Label;
-import com.arsdigita.bebop.PageState;
-import com.arsdigita.bebop.Table;
-import com.arsdigita.bebop.event.TableActionEvent;
-import com.arsdigita.bebop.event.TableActionListener;
-import com.arsdigita.bebop.table.TableModel;
-import com.arsdigita.bebop.table.TableModelBuilder;
-import com.arsdigita.globalization.GlobalizedMessage;
-import com.arsdigita.packaging.Config;
-import com.arsdigita.runtime.ConfigRegistry;
-import com.arsdigita.util.LockableImpl;
-import com.arsdigita.util.parameter.ErrorList;
-import com.arsdigita.util.parameter.Parameter;
-import java.io.ByteArrayOutputStream;
-import java.io.PrintStream;
-import java.util.List;
-
-/**
- * Tab for viewing and changing the settings in the registry.
- *
- * @author Jens Pelzetter
- */
-public class SettingsTab extends BoxPanel implements AdminConstants {
-
- private GlobalizedMessage title;
-
- public SettingsTab() {
-
- setClassAttr("sidebarNavPanel");
- setAttribute("navbar-title", "Settings");
-
- final BoxPanel box = new BoxPanel(BoxPanel.VERTICAL);
- box.setClassAttr("main");
-
- final ConfigRegistry registry = new ConfigRegistry();
- final Config config = new Config(registry);
- final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
- final PrintStream printStream = new PrintStream(outputStream);
- config.load(printStream);
- final String errors = outputStream.toString();
-
- box.add(new Label(errors));
-
- final List contexts = registry.getContexts();
- for (Object context : contexts) {
- final String storage = registry.getStorage((Class) context);
- add(new Label(String.format("%s", ((Class) context).getName()), false));
- }
-
- final List parameters = config.getParameters();
- for(int i = 0; i < parameters.size(); i++) {
- final Parameter parameter = (Parameter) parameters.get(i);
- add(new Label(parameter.getName()));
- }
-
- add(box);
-
- }
-
- private class SettingsTable extends Table implements TableActionListener {
-
- public SettingsTable() {
- super();
-
- setEmptyView(new Label("No settings found"));
-
- //Add columns here
-
- setModelBuilder(new SettingsTableModelBuilder());
- }
-
- public void cellSelected(TableActionEvent e) {
- throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
- }
-
- public void headSelected(TableActionEvent e) {
- throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
- }
- }
-
- private class SettingsTableModelBuilder extends LockableImpl implements TableModelBuilder {
-
- @Override
- public TableModel makeModel(final Table table, final PageState state) {
- return new SettingsTableModel(table);
- }
- }
-
- private class SettingsTableModel implements TableModel {
-
- private final Table table;
- private final String errors;
- private final ConfigRegistry registry;
- private final Config config;
-
- public SettingsTableModel(final Table table) {
- this.table = table;
-
- registry = new ConfigRegistry();
- config = new Config(registry);
- final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
- final PrintStream printStream = new PrintStream(outputStream);
- config.load(printStream);
- errors = outputStream.toString();
-
-
-
- }
-
- @Override
- public int getColumnCount() {
- return table.getColumnModel().size();
- }
-
- public boolean nextRow() {
- throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
- }
-
- public Object getElementAt(int columnIndex) {
- throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
- }
-
- public Object getKeyAt(int columnIndex) {
- throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
- }
- }
-}
diff --git a/ccm-core/src/com/arsdigita/ui/admin/UserBrowsePane.java b/ccm-core/src/com/arsdigita/ui/admin/UserBrowsePane.java
index e3bc80d51..2fe054ef0 100755
--- a/ccm-core/src/com/arsdigita/ui/admin/UserBrowsePane.java
+++ b/ccm-core/src/com/arsdigita/ui/admin/UserBrowsePane.java
@@ -20,6 +20,7 @@ package com.arsdigita.ui.admin;
+import com.arsdigita.ui.admin.ApplicationsAdministrationTab;
import com.arsdigita.bebop.ActionLink;
import com.arsdigita.bebop.BoxPanel;
import com.arsdigita.bebop.Component;
diff --git a/ccm-core/src/com/arsdigita/ui/admin/applications/AbstractApplicationManager.java b/ccm-core/src/com/arsdigita/ui/admin/applications/AbstractApplicationManager.java
new file mode 100644
index 000000000..403cf89b2
--- /dev/null
+++ b/ccm-core/src/com/arsdigita/ui/admin/applications/AbstractApplicationManager.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2013 Jens Pelzetter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.ui.admin.applications;
+
+import com.arsdigita.bebop.Form;
+import com.arsdigita.web.Application;
+
+/**
+ * Provides a basic implementation of {@link ApplicationManager} for multi-instance applications.
+ *
+ * @param Type of the application for which this ApplicationManager provides the administration forms.
+ *
+ * @author Jens Pelzetter
+ * @version $Id$
+ */
+public abstract class AbstractApplicationManager implements ApplicationManager{
+
+ /**
+ * Provides a standard form for creating new instances of an application.
+ * Suitable for most application types.
+ *
+ * @param appClass
+ * @return The standard form for creating new instances of an application.
+ */
+ public Form getApplicationCreateForm(final Class appClass) {
+ return new ApplicationCreateForm(appClass);
+ }
+
+}
diff --git a/ccm-core/src/com/arsdigita/ui/admin/applications/AbstractSingletonApplicationManager.java b/ccm-core/src/com/arsdigita/ui/admin/applications/AbstractSingletonApplicationManager.java
new file mode 100644
index 000000000..9ab9c6c8b
--- /dev/null
+++ b/ccm-core/src/com/arsdigita/ui/admin/applications/AbstractSingletonApplicationManager.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2013 Jens Pelzetter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.ui.admin.applications;
+
+import com.arsdigita.bebop.Form;
+import com.arsdigita.web.Application;
+
+/**
+ * An abstract class providing a default implementation of {@link ApplicationManager#getApplicationCreateForm()}.
+ *
+ * @param Type of the application for which this ApplicationManager provides the administration forms.
+ *
+ * @author Jens Pelzetter
+ * @version $Id$
+ */
+public abstract class AbstractSingletonApplicationManager implements ApplicationManager{
+
+ /**
+ * Implementation of {@link ApplicationManager#getApplicationCreateForm()}
+ * for singleton applications.
+ *
+ * @return {@code} null because it is not possible to create instances
+ * of singleton applications.
+ */
+ @SuppressWarnings("PMD.EmptyMethodInAbstractClassShouldBeAbstract")
+ public final Form getApplicationCreateForm() {
+ return null;
+ }
+}
diff --git a/ccm-core/src/com/arsdigita/ui/admin/applications/ApplicationCreateForm.java b/ccm-core/src/com/arsdigita/ui/admin/applications/ApplicationCreateForm.java
new file mode 100644
index 000000000..44074d270
--- /dev/null
+++ b/ccm-core/src/com/arsdigita/ui/admin/applications/ApplicationCreateForm.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2013 Jens Pelzetter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.ui.admin.applications;
+
+import com.arsdigita.bebop.Form;
+import com.arsdigita.bebop.FormProcessException;
+import com.arsdigita.bebop.Label;
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.event.FormProcessListener;
+import com.arsdigita.bebop.event.FormSectionEvent;
+import com.arsdigita.bebop.event.FormValidationListener;
+import com.arsdigita.bebop.form.TextArea;
+import com.arsdigita.bebop.form.TextField;
+import com.arsdigita.bebop.parameters.NotNullValidationListener;
+import com.arsdigita.bebop.parameters.StringInRangeValidationListener;
+import com.arsdigita.bebop.parameters.StringParameter;
+import com.arsdigita.domain.DomainObjectFactory;
+import com.arsdigita.persistence.DataCollection;
+import com.arsdigita.persistence.Session;
+import com.arsdigita.persistence.SessionManager;
+import com.arsdigita.ui.admin.GlobalizationUtil;
+import com.arsdigita.web.Application;
+import com.arsdigita.web.ApplicationType;
+
+/**
+ * Basic form for creating new Application instances. Should be suitable for
+ * most applications types. If you have special needs... $todo
+ *
+ * This form does not support parent/child application structures. If
+ * your app needs this, add a widget for selecting the parent application
+ * and extend the process method.
+ *
+ * @param Type of application
+ *
+ * @author Jens Pelzetter
+ * @version $Id$
+ */
+public class ApplicationCreateForm
+ extends Form
+ implements FormProcessListener, FormValidationListener {
+
+ public static final String FORM_NAME = "ApplicationCreateForm";
+ private static final String APPLICATION_URL = "applicationUrl";
+ private static final String APPLICATION_TITLE = "applicationTitle";
+ private static final String APPLICATION_DESC = "applicationDesc";
+ private final String appClassName;
+ private final ApplicationType applicationType;
+ private final TextField applicationUrl;
+ private final TextField applicationTitle;
+ private final TextArea applicationDesc;
+
+ public ApplicationCreateForm(final Class appClass) {
+
+ super(FORM_NAME);
+
+ appClassName = appClass.getName();
+
+ final Session session = SessionManager.getSession();
+ final DataCollection appTypes = session.retrieve(ApplicationType.BASE_DATA_OBJECT_TYPE);
+ appTypes.addEqualsFilter("objectType", appClass.getName());
+
+ if (appTypes.isEmpty()) {
+ throw new IllegalArgumentException(String.format("Not application found for object type '%s'.",
+ appClass.getName()));
+ }
+
+ appTypes.next();
+ applicationType = (ApplicationType) DomainObjectFactory.newInstance(appTypes.getDataObject());
+
+ applicationUrl = new TextField(new StringParameter(APPLICATION_URL));
+ applicationUrl.setSize(42);
+ applicationUrl.addValidationListener(new NotNullValidationListener(
+ GlobalizationUtil.globalize("ui.admin.applications.url.validation.not_blank")));
+ applicationUrl.addValidationListener(new StringInRangeValidationListener(1, 100, GlobalizationUtil.globalize(
+ "ui.admin.applications.url.valiation.minmaxlength")));
+
+ applicationTitle = new TextField(new StringParameter(APPLICATION_TITLE));
+ applicationTitle.setSize(42);
+ applicationTitle.addValidationListener(new NotNullValidationListener(
+ GlobalizationUtil.globalize("ui.admin.applications.title.validation.not_blank")));
+ applicationTitle.addValidationListener(new StringInRangeValidationListener(1, 200, GlobalizationUtil.globalize(
+ "ui.admin.applications.title.valiation.minmaxlength")));
+
+ applicationDesc = new TextArea(new StringParameter(APPLICATION_DESC));
+ applicationDesc.setRows(5);
+ applicationDesc.setCols(42);
+ applicationDesc.addValidationListener(new StringInRangeValidationListener(0, 4000, GlobalizationUtil.globalize(
+ "ui.admin.applications.desc.valiation.minmaxlength")));
+
+ add(new Label(GlobalizationUtil.globalize("ui.admin.applications.url.label")));
+ add(applicationUrl);
+ add(new Label(GlobalizationUtil.globalize("ui.admin.applications.title.label")));
+ add(applicationTitle);
+ add(new Label(GlobalizationUtil.globalize("ui.admin.applications.desc.label")));
+ add(applicationDesc);
+
+
+ }
+
+ /**
+ * Creates a new application instance using the provided data.
+ *
+ * @param event
+ * @throws FormProcessException
+ */
+ public void process(final FormSectionEvent event) throws FormProcessException {
+ final PageState state = event.getPageState();
+
+ final Application application = Application.createApplication(applicationType,
+ (String)applicationUrl.getValue(state),
+ (String)applicationTitle.getValue(state),
+ null,
+ false);
+ application.setDescription((String) applicationDesc.getValue(state));
+ }
+
+ public void validate(final FormSectionEvent event) throws FormProcessException {
+ final PageState state = event.getPageState();
+
+ final String url = (String) applicationUrl.getValue(state);
+
+ if (url.contains("/")) {
+ throw new FormProcessException((String) GlobalizationUtil.globalize(
+ "ui.admin.applications.url.validation.no_slash_allowed").localize());
+ }
+
+ if (Application.isInstalled(Application.BASE_DATA_OBJECT_TYPE, url)) {
+ throw new FormProcessException((String) GlobalizationUtil.globalize(
+ "ui.admin.applications.url.validation.url_already_in_use").localize());
+ }
+ }
+
+ public String getAppClassName() {
+ return appClassName;
+ }
+
+}
diff --git a/ccm-core/src/com/arsdigita/ui/admin/applications/ApplicationInstancePane.java b/ccm-core/src/com/arsdigita/ui/admin/applications/ApplicationInstancePane.java
new file mode 100644
index 000000000..ef7e69ca4
--- /dev/null
+++ b/ccm-core/src/com/arsdigita/ui/admin/applications/ApplicationInstancePane.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2013 Jens Pelzetter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.ui.admin.applications;
+
+import com.arsdigita.bebop.Label;
+import com.arsdigita.bebop.SegmentedPanel;
+import com.arsdigita.bebop.SimpleContainer;
+import com.arsdigita.ui.admin.GlobalizationUtil;
+
+import com.arsdigita.web.Application;
+
+/**
+ * This pane shows informations about a specific instance of a multi instance application, like title, parent
+ * application (if any) and the path. Also it contains a form for editing settings specific to the instance.
+ *
+ * @author Jens Pelzetter
+ * @version $Id$
+ */
+public class ApplicationInstancePane extends SegmentedPanel {
+
+ public ApplicationInstancePane(final Application appInstance, final SimpleContainer appAdminPane) {
+
+ super();
+
+ final InfoPanel appInstInfoPanel = new InfoPanel();
+ appInstInfoPanel.addLine("ui.admin.applications.ApplicationInstancePane.title.label",
+ appInstance.getTitle());
+ if (appInstance.getParentApplication() != null) {
+ appInstInfoPanel.addLine("ui.admin.applications.ApplicationInstancePane.parent_app.label",
+ appInstance.getParentApplication().getPath());
+ }
+ appInstInfoPanel.addLine("ui.admin.applications.ApplicationInstancePane.path.label",
+ appInstance.getPath());
+ appInstInfoPanel.addLine("ui.admin.applications.ApplicationInstancePane.desc.label",
+ appInstance.getDescription());
+
+ addSegment(new Label(GlobalizationUtil.globalize(
+ "ui.admin.applications.ApplicationInstancePane.info.heading")),
+ appInstInfoPanel);
+
+ if (appAdminPane == null) {
+ addSegment(new Label(com.arsdigita.ui.util.GlobalizationUtil.globalize(
+ "ui.admin.MultiInstanceApplicationPane.manage.heading")),
+ new Label(com.arsdigita.ui.util.GlobalizationUtil.globalize(
+ "ui.admin.MultiInstancePane.manage.no_instance_admin_pane_found",
+ new String[]{appInstance.getApplicationType().getApplicationObjectType()})));
+ } else {
+ addSegment(new Label(GlobalizationUtil.globalize(
+ "ui.admin.applications.ApplicationInstancePane.manage.heading")),
+ appAdminPane);
+ }
+ }
+
+}
diff --git a/ccm-core/src/com/arsdigita/ui/admin/applications/ApplicationManager.java b/ccm-core/src/com/arsdigita/ui/admin/applications/ApplicationManager.java
new file mode 100644
index 000000000..6133b5a38
--- /dev/null
+++ b/ccm-core/src/com/arsdigita/ui/admin/applications/ApplicationManager.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2013 Jens Pelzetter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.ui.admin.applications;
+
+import com.arsdigita.bebop.Form;
+import com.arsdigita.bebop.SimpleContainer;
+import com.arsdigita.web.Application;
+import java.util.ServiceLoader;
+
+/**
+ * Implementations of this class are used by the new
+ * {@link ApplicationsAdministrationTab} to get the forms for editing the
+ * configuration of an application and for creating new instances of an
+ * application.
+ *
+ * The {@link ApplicationsAdministrationTab} uses the {@link ServiceLoader}
+ * from the Java Standard Library to find all implementations of this interface.
+ * To make implementations of this interface known add an file named
+ * {@code com.arsdigita.ui.admin.applications.ApplicationManager} to the
+ * {@code META-INF/services} directory of the module which provides the
+ * application. In this file add a line with the full qualified class name
+ * of each implementations of this interface provided by the module.
+ *
+ * There a two abstract classes to help you with implementing this class.
+ * {@link AbstractSingletonApplicationManager} is suitable for singleton
+ * applications. {@link AbstractApplicationManager} is for multi-instance
+ * applications.
+ *
+ * @param Type of the application for which this ApplicationManager
+ * provides the administration forms.
+ *
+ * @author Jens Pelzetter
+ * @version $Id$
+ */
+public interface ApplicationManager {
+
+ /**
+ * Used to determine the Applications class for which this
+ * manager provides the administration forms.
+ *
+ * @return The class of the application for which this
+ * manager provides the administration forms.
+ */
+ Class getApplication();
+
+ /**
+ * Provides a pane with administration forms for the application or for an
+ * instance of the application if the application is not a singleton.
+ *
+ * @return A container containing one or more forms for managing instances
+ * of an application.
+ */
+ SimpleContainer getApplicationAdminForm();
+
+ /**
+ * Provides a form for creating new instances of applications. For
+ * singleton applications an implementation of this method will return
+ * {@code null}.
+ *
+ * @return A form for creating new instances of applications or
+ * {@code null} if the is a singleton.
+ */
+ Form getApplicationCreateForm();
+
+}
diff --git a/ccm-core/src/com/arsdigita/ui/admin/applications/BaseApplicationPane.java b/ccm-core/src/com/arsdigita/ui/admin/applications/BaseApplicationPane.java
new file mode 100644
index 000000000..670fae476
--- /dev/null
+++ b/ccm-core/src/com/arsdigita/ui/admin/applications/BaseApplicationPane.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2013 Jens Pelzetter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.ui.admin.applications;
+
+import com.arsdigita.bebop.ColumnPanel;
+import com.arsdigita.bebop.Label;
+import com.arsdigita.bebop.SegmentedPanel;
+import com.arsdigita.ui.admin.GlobalizationUtil;
+import com.arsdigita.web.Application;
+import com.arsdigita.web.ApplicationCollection;
+import com.arsdigita.web.ApplicationType;
+
+/**
+ * Basic application pane containing the parts common for singleton and multi instance applications types. Shows
+ * informations about a specific application type.
+ *
+ * @author Jens Pelzetter
+ * @version $Id$
+ */
+public class BaseApplicationPane extends SegmentedPanel {
+
+ //private final ApplicationType applicationType;
+ public BaseApplicationPane(final ApplicationType applicationType) {
+ super();
+
+ //this.applicationType = applicationType;
+
+ final InfoPanel appInfoPanel = new InfoPanel();
+ appInfoPanel.addLine("ui.admin.applications.ApplicationInfoSection.title.label",
+ applicationType.getTitle());
+ appInfoPanel.addLine("ui.admin.applications.ApplicationInfoSection.app_class.label",
+ applicationType.getApplicationObjectType());
+ if (applicationType.isSingleton()) {
+ appInfoPanel.addLine("ui.admin.applications.ApplicationInfoSection.singleton.label",
+ "ui.admin.applications.ApplicationInfoSection.singleton.yes",
+ true);
+ } else {
+ appInfoPanel.addLine("ui.admin.applications.ApplicationInfoSection.singleton.label",
+ "ui.admin.applications.ApplicationInfoSection.singleton.no",
+ true);
+ }
+ appInfoPanel.addLine("ui.admin.applications.ApplicationInfoSection.desc.label",
+ applicationType.getDescription());
+ if (applicationType.isSingleton()) {
+ final ApplicationCollection applications = Application.retrieveAllApplications(applicationType.
+ getApplicationObjectType());
+ if (applications.next()) {
+ appInfoPanel.addLine(
+ "ui.admin.applications.ApplicationInfoSection.singleton_instance.path.label",
+ applications.getApplication().getPath());
+ } else {
+ appInfoPanel.addLine(
+ "ui.admin.applications.ApplicationInfoSection.singleton_instance.path.label",
+ "ui.admin.applications.ApplicationInfoSection.singleton_instance.no_instance_found");
+ }
+ applications.close();
+ }
+
+ addSegment(new Label(GlobalizationUtil.globalize(
+ "ui.admin.applications.ApplicationInfoSection.heading")),
+ appInfoPanel);
+ }
+
+}
diff --git a/ccm-core/src/com/arsdigita/ui/admin/applications/InfoPanel.java b/ccm-core/src/com/arsdigita/ui/admin/applications/InfoPanel.java
new file mode 100644
index 000000000..e32e4f983
--- /dev/null
+++ b/ccm-core/src/com/arsdigita/ui/admin/applications/InfoPanel.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2013 Jens Pelzetter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.ui.admin.applications;
+
+import com.arsdigita.bebop.ColumnPanel;
+import com.arsdigita.bebop.Label;
+import com.arsdigita.ui.admin.GlobalizationUtil;
+
+/**
+ * A helper class for creating a column panel with two labels in each row.
+ *
+ * @author Jens Pelzetter
+ * @version $Id$
+ */
+public class InfoPanel extends ColumnPanel {
+
+ public InfoPanel() {
+ super(2);
+ }
+
+ public void addLine(final String labelKey, final String data) {
+ addLine(labelKey, data, false);
+ }
+
+ public void addLine(final String labelKey, final String data, final boolean globalizeData) {
+ add(new Label(GlobalizationUtil.globalize(labelKey)));
+ if (globalizeData) {
+ add(new Label(GlobalizationUtil.globalize(data)));
+ } else {
+ add(new Label(data));
+ }
+ }
+
+}
diff --git a/ccm-core/src/com/arsdigita/ui/admin/applications/MultiInstanceApplicationPane.java b/ccm-core/src/com/arsdigita/ui/admin/applications/MultiInstanceApplicationPane.java
new file mode 100644
index 000000000..d5d490869
--- /dev/null
+++ b/ccm-core/src/com/arsdigita/ui/admin/applications/MultiInstanceApplicationPane.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2013 Jens Pelzetter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.ui.admin.applications;
+
+import com.arsdigita.bebop.Label;
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.Table;
+import com.arsdigita.bebop.table.TableColumn;
+import com.arsdigita.bebop.table.TableModel;
+import com.arsdigita.bebop.table.TableModelBuilder;
+import com.arsdigita.ui.admin.GlobalizationUtil;
+import com.arsdigita.util.LockableImpl;
+import com.arsdigita.web.Application;
+import com.arsdigita.web.ApplicationCollection;
+import com.arsdigita.web.ApplicationType;
+
+/**
+ * Pane for multi instance applications. Additional to the data provided by {@link BaseApplicationPane} it shows a
+ * table of all instances of the application type and a form for creating new instances of the application type.
+ *
+ * @param
+ * @author Jens Pelzetter
+ * @version $Id$
+ */
+public class MultiInstanceApplicationPane extends BaseApplicationPane {
+
+ private final static int COL_TITLE = 0;
+ private final static int COL_URL = 1;
+ private final static int COL_DESC = 2;
+
+ public MultiInstanceApplicationPane(final ApplicationType applicationType,
+ final ApplicationCreateForm createForm) {
+ super(applicationType);
+
+ final ApplicationCollection applications = Application.retrieveAllApplications(applicationType.
+ getApplicationObjectType());
+ final Table table = new Table();
+ table.getColumnModel().add(new TableColumn(COL_TITLE, GlobalizationUtil.globalize(
+ "ui.admin.applicationsMultiInstanceApplicationPane.instances.table.col_title.header")));
+ table.getColumnModel().add(new TableColumn(COL_URL, GlobalizationUtil.globalize(
+ "ui.admin.applicationsMultiInstanceApplicationPane.instances.table.col_url.header")));
+ table.getColumnModel().add(new TableColumn(COL_DESC, GlobalizationUtil.globalize(
+ "ui.admin.applicationsMultiInstanceApplicationPane.instances.table.col_desc.header")));
+
+ table.setModelBuilder(new ApplicationInstancesTableModelBuilder(applications));
+
+ addSegment(new Label(GlobalizationUtil.globalize(
+ "ui.admin.MultiInstanceApplicationPane.instances")),
+ table);
+
+ if (createForm == null) {
+ addSegment(new Label(com.arsdigita.ui.util.GlobalizationUtil.globalize(
+ "ui.admin.MultiInstanceApplicationPane.manage_instances.heading")),
+ new Label(com.arsdigita.ui.util.GlobalizationUtil.globalize(
+ "ui.admin.MultiInstancePane.manage.no_create_form_found",
+ new String[]{applicationType.getApplicationObjectType()})));
+ } else {
+ addSegment(new Label(GlobalizationUtil.globalize(
+ "ui.admin.MultiInstanceApplicationPane.create_instance")),
+ createForm);
+
+ }
+ }
+
+ private class ApplicationInstancesTableModelBuilder extends LockableImpl implements TableModelBuilder {
+
+ private final ApplicationCollection applications;
+
+ public ApplicationInstancesTableModelBuilder(final ApplicationCollection applications) {
+ super();
+
+ this.applications = applications;
+ }
+
+ public TableModel makeModel(final Table table, final PageState state) {
+ return new ApplicationInstancesTableModel(table, applications);
+ }
+
+ }
+
+ private class ApplicationInstancesTableModel implements TableModel {
+
+ private final Table table;
+ private final ApplicationCollection applications;
+
+ public ApplicationInstancesTableModel(final Table table, final ApplicationCollection applications) {
+ this.table = table;
+ this.applications = applications;
+ }
+
+ public int getColumnCount() {
+ return table.getColumnModel().size();
+ }
+
+ public boolean nextRow() {
+ return applications.next();
+ }
+
+ public Object getElementAt(final int columnIndex) {
+ switch (columnIndex) {
+ case COL_TITLE:
+ return applications.getApplication().getTitle();
+ case COL_DESC:
+ return applications.getApplication().getDescription();
+ case COL_URL:
+ return applications.getApplication().getPath();
+ default:
+ return null;
+ }
+ }
+
+ public Object getKeyAt(final int columnIndex) {
+ return applications.getApplication().getPath();
+ }
+
+ }
+}
diff --git a/ccm-core/src/com/arsdigita/ui/admin/applications/SingletonApplicationPane.java b/ccm-core/src/com/arsdigita/ui/admin/applications/SingletonApplicationPane.java
new file mode 100644
index 000000000..be207cec1
--- /dev/null
+++ b/ccm-core/src/com/arsdigita/ui/admin/applications/SingletonApplicationPane.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2013 Jens Pelzetter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.ui.admin.applications;
+
+import com.arsdigita.bebop.Label;
+import com.arsdigita.bebop.SimpleContainer;
+import com.arsdigita.ui.admin.GlobalizationUtil;
+import com.arsdigita.web.ApplicationType;
+
+/**
+ * Pane for managing singleton applications. Shows a form to edit application specific settings.
+ *
+ * @author Jens Pelzetter
+ * @version $Id$
+ */
+public class SingletonApplicationPane extends BaseApplicationPane {
+
+ public SingletonApplicationPane(final ApplicationType applicationType, final SimpleContainer appAdminPane) {
+ super(applicationType);
+
+ if (appAdminPane == null) {
+ addSegment(new Label(GlobalizationUtil.globalize(
+ "ui.admin.SingletonApplicationPane.manage.heading")),
+ new Label(GlobalizationUtil.globalize(
+ "ui.admin.SingletonApplicationPane.manage.no_admin_pane_found",
+ new String[]{applicationType.getApplicationObjectType()})));
+ } else {
+ addSegment(new Label(GlobalizationUtil.globalize(
+ "ui.admin.SingletonApplicationPane.manage.heading")),
+ appAdminPane);
+ }
+ }
+
+}
diff --git a/ccm-core/src/com/arsdigita/ui/admin/applications/tree/ApplicationInstanceTreeNode.java b/ccm-core/src/com/arsdigita/ui/admin/applications/tree/ApplicationInstanceTreeNode.java
new file mode 100644
index 000000000..cde45114a
--- /dev/null
+++ b/ccm-core/src/com/arsdigita/ui/admin/applications/tree/ApplicationInstanceTreeNode.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2013 Jens Pelzetter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.ui.admin.applications.tree;
+
+import com.arsdigita.bebop.tree.TreeNode;
+import com.arsdigita.ui.admin.ApplicationsAdministrationTab;
+import com.arsdigita.web.Application;
+
+/**
+ * Tree Node for the application tree representing an instance of a application.
+ *
+ * @author Jens Pelzetter
+ * @version $Id$
+ *
+ * @see ApplicationTreeModel
+ * @see ApplicationTreeModelBuilder
+ * @see ApplicationTypeTreeNode
+ * @see ApplicationsAdministrationTab
+ * @see TreeNode
+ */
+public class ApplicationInstanceTreeNode implements TreeNode {
+
+ /**
+ * The application instance represented by this {@code TreeNode}
+ */
+ private final Application application;
+
+ /**
+ * Constructor
+ *
+ * @param application The application instance to represent by this {@code TreeNode}
+ */
+ public ApplicationInstanceTreeNode(final Application application) {
+ this.application = application;
+ }
+
+ /**
+ * Returns the key for this {@link TreeNode}.
+ *
+ * @return The path of the application instance.
+ * @see TreeNode#getKey()
+ */
+ public Object getKey() {
+ return application.getPath();
+ }
+
+ /**
+ * Data to show in the tree for this node.
+ *
+ * @return The title of the application instance
+ * @see TreeNode#getElement()
+ */
+ public Object getElement() {
+ return application.getTitle();
+ }
+
+}
diff --git a/ccm-core/src/com/arsdigita/ui/admin/applications/tree/ApplicationTreeModel.java b/ccm-core/src/com/arsdigita/ui/admin/applications/tree/ApplicationTreeModel.java
new file mode 100644
index 000000000..7ad9e7035
--- /dev/null
+++ b/ccm-core/src/com/arsdigita/ui/admin/applications/tree/ApplicationTreeModel.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2013 Jens Pelzetter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.ui.admin.applications.tree;
+
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.tree.TreeModel;
+import com.arsdigita.bebop.tree.TreeNode;
+import com.arsdigita.ui.admin.ApplicationsAdministrationTab;
+import com.arsdigita.web.Application;
+import com.arsdigita.web.ApplicationCollection;
+import com.arsdigita.web.ApplicationType;
+import com.arsdigita.web.ApplicationTypeCollection;
+import java.util.Iterator;
+
+/**
+ * A {@link TreeModel} for the tree of applications in {@link ApplicationsAdministrationTab}. The tree consists of two
+ * different types of nodes: Nodes for {@link ApplicationTypes} and nodes for {@link Application} instances.
+ *
+ * @author Jens Pelzetter
+ * @version $Id$
+ */
+public class ApplicationTreeModel implements TreeModel {
+
+ public ApplicationTreeModel() {
+ //Nothing
+ }
+
+ public TreeNode getRoot(final PageState state) {
+ return new RootTreeNode();
+ }
+
+ public boolean hasChildren(final TreeNode node, final PageState state) {
+ if (node instanceof RootTreeNode) {
+ return true;
+ } else if (node instanceof ApplicationTypeTreeNode) {
+ final ApplicationTypeTreeNode typeTreeNode = (ApplicationTypeTreeNode) node;
+
+ if (typeTreeNode.getApplicationType().isSingleton()) {
+ return false;
+ } else {
+ return !retrieveApplicationInstances(typeTreeNode.getApplicationType()).isEmpty();
+ }
+ } else if (node instanceof ApplicationInstanceTreeNode) {
+ return false;
+ } else {
+ throw new IllegalArgumentException(
+ "The ApplicationTreeModel can only work with ApplicationTypeTreeNodes and"
+ + "ApplicationInstanceTreeNodes.");
+ }
+ }
+
+ public Iterator getChildren(final TreeNode node, final PageState state) {
+ if (node instanceof RootTreeNode) {
+ final ApplicationTypeCollection appTypes = ApplicationType.retrieveAllApplicationTypes();
+ appTypes.addOrder("title");
+
+ return new AppTypesIterator(appTypes);
+ } else if (node instanceof ApplicationTypeTreeNode) {
+ final ApplicationTypeTreeNode typeTreeNode = (ApplicationTypeTreeNode) node;
+ final ApplicationType appType = typeTreeNode.getApplicationType();
+
+ final ApplicationCollection applications = Application.retrieveAllApplications(
+ appType.getApplicationObjectType());
+ applications.addOrder("title");
+
+ return new AppIterator(applications);
+ } else if (node instanceof ApplicationInstanceTreeNode) {
+ return null;
+ } else {
+ throw new IllegalArgumentException(
+ "The ApplicationTreeModel can only work with ApplicationTypeTreeNodes and"
+ + "ApplicationInstanceTreeNodes.");
+ }
+ }
+
+ private ApplicationCollection retrieveApplicationInstances(final ApplicationType applicationType) {
+ final ApplicationCollection applications = Application.retrieveAllApplications();
+ applications.addEqualsFilter("objectType", applicationType.getApplicationObjectType());
+
+ return applications;
+ }
+
+ private class RootTreeNode implements TreeNode {
+
+ public RootTreeNode() {
+ //Nothing
+ }
+
+ public Object getKey() {
+ return "-1";
+ }
+
+ public Object getElement() {
+ return "/";
+ }
+
+ }
+
+ private class AppTypesIterator implements Iterator {
+
+ private final ApplicationTypeCollection appTypes;
+
+ public AppTypesIterator(final ApplicationTypeCollection appTypes) {
+ this.appTypes = appTypes;
+ }
+
+ public boolean hasNext() {
+ return appTypes.next();
+ }
+
+ public ApplicationTypeTreeNode next() {
+ return new ApplicationTypeTreeNode(appTypes.getApplicationType());
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException("Not supported.");
+ }
+ }
+
+ private class AppIterator implements Iterator {
+
+ private final ApplicationCollection applications;
+
+ public AppIterator(final ApplicationCollection applications) {
+ this.applications = applications;
+ }
+
+ public boolean hasNext() {
+ return applications.next();
+ }
+
+ public ApplicationInstanceTreeNode next() {
+ return new ApplicationInstanceTreeNode(applications.getApplication());
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException("Not supported.");
+ }
+ }
+}
diff --git a/ccm-core/src/com/arsdigita/ui/admin/applications/tree/ApplicationTreeModelBuilder.java b/ccm-core/src/com/arsdigita/ui/admin/applications/tree/ApplicationTreeModelBuilder.java
new file mode 100644
index 000000000..e26570ae6
--- /dev/null
+++ b/ccm-core/src/com/arsdigita/ui/admin/applications/tree/ApplicationTreeModelBuilder.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2013 Jens Pelzetter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.ui.admin.applications.tree;
+
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.Tree;
+import com.arsdigita.bebop.tree.TreeModel;
+import com.arsdigita.bebop.tree.TreeModelBuilder;
+import com.arsdigita.ui.admin.ApplicationsAdministrationTab;
+import com.arsdigita.util.LockableImpl;
+
+/**
+ * The {@link TreeModelBuilder} creating the {@link TreeModel} for the applications tree used in
+ * {@link ApplicationsAdministrationTab}.
+ *
+ * @author Jens Pelzetter
+ * @version $Id$
+ */
+public class ApplicationTreeModelBuilder extends LockableImpl implements TreeModelBuilder {
+
+ public TreeModel makeModel(final Tree tree, final PageState state) {
+ tree.expand("-1", state);
+ return new ApplicationTreeModel();
+ }
+}
diff --git a/ccm-core/src/com/arsdigita/ui/admin/applications/tree/ApplicationTypeTreeNode.java b/ccm-core/src/com/arsdigita/ui/admin/applications/tree/ApplicationTypeTreeNode.java
new file mode 100644
index 000000000..1e2a323f2
--- /dev/null
+++ b/ccm-core/src/com/arsdigita/ui/admin/applications/tree/ApplicationTypeTreeNode.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2013 Jens Pelzetter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.ui.admin.applications.tree;
+
+import com.arsdigita.bebop.tree.TreeNode;
+import com.arsdigita.web.ApplicationType;
+
+/**
+ * Tree Node implementation for the Application Tree in the Application
+ * admin tab.
+ *
+ * @author Jens Pelzetter
+ * @version $Id$
+ */
+public class ApplicationTypeTreeNode implements TreeNode {
+
+ private final ApplicationType applicationType;
+
+ public ApplicationTypeTreeNode(final ApplicationType applicationType) {
+ this.applicationType = applicationType;
+ }
+
+ public ApplicationType getApplicationType() {
+ return applicationType;
+ }
+
+ public Object getKey() {
+ return applicationType.getApplicationObjectType();
+ }
+
+ public Object getElement() {
+ return applicationType.getTitle();
+ }
+
+}