]> source.dussan.org Git - gitblit.git/commitdiff
Support mailing lists for Teams
authorJames Moger <james.moger@gitblit.com>
Wed, 21 Dec 2011 22:13:00 +0000 (17:13 -0500)
committerJames Moger <james.moger@gitblit.com>
Wed, 21 Dec 2011 22:13:00 +0000 (17:13 -0500)
15 files changed:
docs/01_features.mkd
docs/01_setup.mkd
docs/04_releases.mkd
groovy/sendmail.groovy
src/com/gitblit/ConfigUserService.java
src/com/gitblit/FileUserService.java
src/com/gitblit/GitBlit.java
src/com/gitblit/client/EditTeamDialog.java
src/com/gitblit/models/TeamModel.java
src/com/gitblit/utils/StringUtils.java
src/com/gitblit/wicket/GitBlitWebApp.properties
src/com/gitblit/wicket/pages/EditRepositoryPage.html
src/com/gitblit/wicket/pages/EditTeamPage.html
src/com/gitblit/wicket/pages/EditTeamPage.java
src/com/gitblit/wicket/pages/EditUserPage.html

index 39fbfaaccbef09ee5389532b123e64fbaf27c1ca..7d33911b6d4a065282ddce90bb460bcd427b4cd2 100644 (file)
@@ -12,6 +12,7 @@
 - Java/Swing Gitblit Manager tool \r
 - Gitweb inspired web UI\r
 - Groovy pre- and post- push hook scripts, per-repository or globally for all repositories\r
+- Email push notifications *(via sendmail.groovy push script)*\r
 - Administrators may create, edit, rename, or delete repositories through the web UI or RPC interface\r
 - Administrators may create, edit, rename, or delete users through the web UI or RPC interface\r
 - Administrators may create, edit, rename, or delete teams through the web UI or RPC interface\r
index 7ea575655d96258fd0ff6e510ee725c7bbda0855..e400a0b19f10f6258073cc57831d64cb3ed105f9 100644 (file)
@@ -195,6 +195,7 @@ The `users.conf` file uses a Git-style configuration format:
                user = murdock\r
                user = babaracus\r
                repository = topsecret.git\r
+               mailingList = list@ateam.org\r
 \r
 The `users.conf` file allows flexibility for adding new fields to a UserModel object that the original `users.properties` file does not afford without imposing the complexity of relying on an embedded SQL database. \r
 \r
@@ -204,7 +205,7 @@ All users are stored in the `users.properties` file or in the file you specified
 The format of `users.properties` loosely follows Jetty's convention for HashRealms:\r
 \r
     username=password,role1,role2,role3...\r
-    @teamname=!username1,!username2,!username3,repository1,repository2,repository3...\r
+    @teamname=&mailinglist,!username1,!username2,!username3,repository1,repository2,repository3...\r
 \r
 #### Usernames\r
 Usernames must be unique and are case-insensitive.  \r
@@ -218,7 +219,7 @@ There are two actual *roles* in Gitblit: *#admin*, which grants administrative p
 \r
 ## Authentication and Authorization Customization\r
 \r
-### Choice 1: Customize Authentication Only\r
+### Customize Authentication Only\r
 This is the simplest choice where you implement custom authentication and delegate all other standard user and team operations to one of Gitblit's user service implementations.  This choice insulates your customization from changes in User and Team model classes and additional API that may be added to IUserService.\r
 \r
 Please subclass [com.gitblit.GitblitUserService](https://github.com/gitblit/gitblit/blob/master/src/com/gitblit/GitblitUserService.java) and override the *setup()* and *authenticate()* methods.  \r
@@ -228,7 +229,7 @@ You may use your subclass by specifying its fully qualified classname in the *re
 \r
 Your subclass must be on Gitblit's classpath and must have a public default constructor.  \r
 \r
-### Choice 2: Customize Everything\r
+### Customize Everything\r
 Instead of maintaining a `users.conf` or `users.properties` file, you may want to integrate Gitblit into an existing environment.\r
 \r
 You may use your own custom *com.gitblit.IUserService* implementation by specifying its fully qualified classname in the *realm.userService* setting.\r
@@ -247,15 +248,16 @@ The Groovy hook mechanism allows for dynamic extension of Gitblit to execute cus
 ### Rules, Requirements, & Behaviors\r
 1. Your Groovy scripts must be stored in the *groovy.scriptsFolder* as specified in `gitblit.properties` or `web.xml`.\r
 2. All script files must have the *.groovy* extension. Because of this you may omit the extension when specifying the script.\r
-3. Scripts must be explicitly specified to be executed, no scripts are *automatically* executed by name or extension.\r
-4. A script can be specified to run on *all repositories* by adding the script file name to *groovy.preReceiveScripts* or *groovy.postReceiveScripts* in `gitblit.properties` or `web.xml`.  Be mindful of access retrictions and global properties like *mail.mailingLists* if specifying *sendmail* to run on all repositories.\r
-5. Scripts may also be specified per-repository in the repository's settings.\r
-6. Globally specified scripts are excluded from the list of available scripts in a repository's settings \r
-7. Globally specified scripts are executed first, in their listed order, followed by per-repository scripts, in their listed order.\r
-8. A script may only be defined once in a pre-receive list and once in a post-receive list.  \r
+3. Script filenames must not have spaces!\r
+4. Scripts must be explicitly specified to be executed, no scripts are *automatically* executed by name or extension.\r
+5. A script can be specified to run on *all repositories* by adding the script file name to *groovy.preReceiveScripts* or *groovy.postReceiveScripts* in `gitblit.properties` or `web.xml`.\r
+6. Scripts may also be specified per-repository in the repository's settings.\r
+7. Globally specified scripts are excluded from the list of available scripts in a repository's settings \r
+8. Globally specified scripts are executed first, in their listed order, followed by per-repository scripts, in their listed order.\r
+9. A script may only be defined once in a pre-receive chain and once in a post-receive chain.  \r
 You may execute the same script on pre-receive and post-receive, just not multiple times within a pre-receive or post-receive event.\r
-9. Gitblit does not differentiate between what can be a pre-receive script and what can be a post-receive script.\r
-10. If a script *returns false* then the pre-receive or post-receive hook chain is aborted and none of the subsequent scripts will execute.\r
+10. Gitblit does not differentiate between what can be a pre-receive script and what can be a post-receive script.\r
+11. If a script *returns false* then the hook chain is aborted and none of the subsequent scripts will execute.\r
 \r
 Some sample scripts are included in the GO and WAR distributions to show you how you can tap into Gitblit with the provided bound variables.  Additional implementation details may be specified in the header comment of these examples.\r
 \r
@@ -273,6 +275,33 @@ Post-Receive scripts execute after all refs have been updated.
 \r
 This is the appropriate point to trigger continuous integration builds or send email notifications, etc.\r
 \r
+## Push Email Notifications\r
+\r
+Gitblit implements email notifications in *sendmail.groovy* which uses the Groovy Hook Script mechanism.  This allows for dynamic customization of the notification process at the installation site and serves as an example push script.\r
+\r
+### Enabling Push Notifications\r
+\r
+In order to send email notifications on a push to Gitblit, this script must be specified somewhere in the *post-receive* script chain.  \r
+You may specify *sendmail* in one of two places:\r
+\r
+1. *groovy.postReceiveScripts* in `gitblit.properties` or `web.xml`, globally applied to all repositories\r
+2. post-receive scripts of a Repository definition\r
+\r
+### Destination Addresses\r
+\r
+Gitblit does not currently support individual subscriptions to repositories; i.e. a *user* can not subscribe or unsubscribe from push notifications.\r
+\r
+However, Repository Managers and Administrators can specify subscribed email addresses in one of three places:\r
+\r
+1. *mail.mailingLists* in `gitblit.properties` or `web.xml`, globally applied to all push-notified repositories\r
+2. mailing lists in a Team definition, applied to all repositories that are part of the team definition\r
+3. mailing lists in a Repository definition\r
+\r
+All three sources are checked and merged into a unique list of destination addresses for push notifications.\r
+\r
+**NOTE:**  \r
+Care should be taken when devising your notification scheme as it relates to any VIEW restricted repositories you might have.  Setting a global mailing list and activating push notifications for a VIEW restricted repository may send unwanted emails.\r
+\r
 ## Client Setup and Configuration\r
 ### Https with Self-Signed Certificates\r
 You must tell Git/JGit not to verify the self-signed certificate in order to perform any remote Git operations.\r
index 292ff9877dc6553b533985ac6876db998dc30b74..3e49c1883f912d0d70d41d4b7856f58b442b1585 100644 (file)
@@ -5,11 +5,12 @@
 \r
 - updated: Gitblit GO is now monolithic like the WAR build. (issue 30)  \r
 This change helps adoption of GO in environments without an internet connection or with a restricted connection.\r
-- added: Groovy 1.8.4 and sample pre- and post- push Groovy hook scripts.  Hook scripts can be set per-repository or globally for all repositories.  \r
+- added: Groovy 1.8.4 and a push hook script mechanism.  Hook scripts can be set per-repository or globally for all repositories.  \r
 Unfortunately this adds another 6 MB to the 8MB Gitblit package, but it allows for a *very* powerful, flexible, platform-independent hook script mechanism.  \r
     **New:** *groovy.scriptsFolder = groovy*  \r
     **New:** *groovy.preReceiveScripts =*  \r
     **New:** *groovy.postReceiveScripts =*\r
+- added: *sendmail.groovy* for optional email notifications on push\r
 - added: New key for mailing lists.  This is used in conjunction with the *sendmail.groovy* hook script.  \r
     **New:** *mail.mailingLists =*\r
 - added: GitblitUserService.  This is a wrapper object for the built-in user service implementations.  For those wanting to only implement *custom authentication* it is recommended to subclass GitblitUserService and override the appropriate methods.  Going forward, this will insulate customized behavior from new IUserService API and changes in model classes.\r
index b43f9dea03f06bb4547e2a4f38c3c199c9190c52..1020241f6c20200ce077b3fcdc88c44c2fbcc347 100644 (file)
@@ -16,6 +16,7 @@
 import com.gitblit.GitBlit\r
 import com.gitblit.Keys\r
 import com.gitblit.models.RepositoryModel\r
+import com.gitblit.models.TeamModel\r
 import com.gitblit.models.UserModel\r
 import com.gitblit.utils.JGitUtils\r
 import org.eclipse.jgit.lib.Repository\r
@@ -87,6 +88,15 @@ if (mailinglist != null) {
 // add all mailing lists defined in gitblit.properties or web.xml\r
 toAddresses.addAll(gitblit.getStrings(Keys.mail.mailingLists))\r
 \r
+// add all team mailing lists\r
+def teams = gitblit.getRepositoryTeams(repository)\r
+for (team in teams) {\r
+       TeamModel model = gitblit.getTeamModel(team)\r
+       if (model.mailingLists) {\r
+               toAddresses.addAll(model.mailingLists)\r
+       }\r
+}\r
+\r
 // add all mailing lists for the repository\r
 toAddresses.addAll(repository.mailingLists)\r
 \r
index a0a38e6a5bd5db2654e6b3d5e6f37880a9f71095..b899d9241d9d1cfeb7d9b74073e65593d5435f59 100644 (file)
@@ -62,6 +62,8 @@ public class ConfigUserService implements IUserService {
        private static final String REPOSITORY = "repository";\r
 \r
        private static final String ROLE = "role";\r
+       \r
+       private static final String MAILINGLIST = "mailingList";\r
 \r
        private final File realmFile;\r
 \r
@@ -303,7 +305,7 @@ public class ConfigUserService implements IUserService {
                List<String> list = new ArrayList<String>(teams.keySet());\r
                return list;\r
        }\r
-       \r
+\r
        /**\r
         * Returns the list of all users who are allowed to bypass the access\r
         * restriction placed on the specified repository.\r
@@ -648,6 +650,14 @@ public class ConfigUserService implements IUserService {
                        if (model.users != null) {\r
                                config.setStringList(TEAM, model.name, USER, new ArrayList<String>(model.users));\r
                        }\r
+\r
+                       // null check on "final" mailing lists because JSON-sourced\r
+                       // TeamModel\r
+                       // can have a null users object\r
+                       if (model.mailingLists != null) {\r
+                               config.setStringList(TEAM, model.name, MAILINGLIST, new ArrayList<String>(\r
+                                               model.mailingLists));\r
+                       }\r
                }\r
 \r
                config.save();\r
@@ -714,6 +724,7 @@ public class ConfigUserService implements IUserService {
                                        team.addRepositories(Arrays.asList(config.getStringList(TEAM, teamname,\r
                                                        REPOSITORY)));\r
                                        team.addUsers(Arrays.asList(config.getStringList(TEAM, teamname, USER)));\r
+                                       team.addMailingLists(Arrays.asList(config.getStringList(TEAM, teamname, MAILINGLIST)));\r
 \r
                                        teams.put(team.name.toLowerCase(), team);\r
 \r
index 880ca7b8ab9c61f92ee777d1ee102cc33fcf7988..27892f71c099d4aa0c02a1ca56ad920a4363fe96 100644 (file)
@@ -618,15 +618,19 @@ public class FileUserService extends FileSettings implements IUserService {
                                        TeamModel team = new TeamModel(username.substring(1));\r
                                        List<String> repositories = new ArrayList<String>();\r
                                        List<String> users = new ArrayList<String>();\r
+                                       List<String> mailingLists = new ArrayList<String>();\r
                                        for (String role : roles) {\r
                                                if (role.charAt(0) == '!') {\r
                                                        users.add(role.substring(1));\r
+                                               } else if (role.charAt(0) == '&') {\r
+                                                               mailingLists.add(role.substring(1));\r
                                                } else {\r
                                                        repositories.add(role);\r
                                                }\r
                                        }\r
                                        team.addRepositories(repositories);\r
                                        team.addUsers(users);\r
+                                       team.addMailingLists(mailingLists);\r
                                        teams.put(team.name.toLowerCase(), team);\r
                                } else {\r
                                        // user definition\r
@@ -832,6 +836,11 @@ public class FileUserService extends FileSettings implements IUserService {
                        sb.append(user);\r
                        sb.append(',');\r
                }\r
+               for (String address : model.mailingLists) {\r
+                       sb.append('&');\r
+                       sb.append(address);\r
+                       sb.append(',');\r
+               }\r
                // trim trailing comma\r
                sb.setLength(sb.length() - 1);\r
                allUsers.remove("@" + teamname);\r
index 835aa132f9f9569e451d038145747835922d26f4..c818dd5092cc7d9e6701b6e903abd037a06ad25b 100644 (file)
@@ -25,6 +25,7 @@ import java.lang.reflect.Field;
 import java.text.MessageFormat;\r
 import java.util.ArrayList;\r
 import java.util.Arrays;\r
+import java.util.Collection;\r
 import java.util.Collections;\r
 import java.util.HashMap;\r
 import java.util.HashSet;\r
@@ -1505,7 +1506,7 @@ public class GitBlit implements ServletContextListener {
         * @param message\r
         * @param toAddresses\r
         */\r
-       public void sendMail(String subject, String message, ArrayList<String> toAddresses) {\r
+       public void sendMail(String subject, String message, Collection<String> toAddresses) {\r
                this.sendMail(subject, message, toAddresses.toArray(new String[0]));\r
        }\r
 \r
index 4297599ca34559e246974c1189d9df08eb790cf2..a04ed2cfaa7221e2fcf9ab2a99c3d5c112c20484 100644 (file)
@@ -65,6 +65,8 @@ public class EditTeamDialog extends JDialog {
 \r
        private JTextField teamnameField;\r
 \r
+       private JTextField mailingListsField;\r
+\r
        private JPalette<String> repositoryPalette;\r
 \r
        private JPalette<String> userPalette;\r
@@ -105,16 +107,20 @@ public class EditTeamDialog extends JDialog {
        private void initialize(int protocolVersion, TeamModel aTeam) {\r
                teamnameField = new JTextField(aTeam.name == null ? "" : aTeam.name, 25);\r
 \r
+               mailingListsField = new JTextField(aTeam.mailingLists == null ? ""\r
+                               : StringUtils.flattenStrings(aTeam.mailingLists, " "), 50);\r
+\r
                JPanel fieldsPanel = new JPanel(new GridLayout(0, 1));\r
                fieldsPanel.add(newFieldPanel(Translation.get("gb.teamName"), teamnameField));\r
+               fieldsPanel.add(newFieldPanel(Translation.get("gb.mailingLists"), mailingListsField));\r
 \r
                final Insets _insets = new Insets(5, 5, 5, 5);\r
                repositoryPalette = new JPalette<String>();\r
                userPalette = new JPalette<String>();\r
-               \r
+\r
                JPanel fieldsPanelTop = new JPanel(new BorderLayout());\r
                fieldsPanelTop.add(fieldsPanel, BorderLayout.NORTH);\r
-               \r
+\r
                JPanel repositoriesPanel = new JPanel(new BorderLayout()) {\r
 \r
                        private static final long serialVersionUID = 1L;\r
@@ -140,7 +146,6 @@ public class EditTeamDialog extends JDialog {
                panel.addTab(Translation.get("gb.teamMembers"), usersPanel);\r
                panel.addTab(Translation.get("gb.restrictedRepositories"), repositoriesPanel);\r
 \r
-\r
                JButton createButton = new JButton(Translation.get("gb.save"));\r
                createButton.addActionListener(new ActionListener() {\r
                        public void actionPerformed(ActionEvent event) {\r
@@ -162,7 +167,7 @@ public class EditTeamDialog extends JDialog {
                JPanel controls = new JPanel();\r
                controls.add(cancelButton);\r
                controls.add(createButton);\r
-               \r
+\r
                JPanel centerPanel = new JPanel(new BorderLayout(5, 5)) {\r
 \r
                        private static final long serialVersionUID = 1L;\r
@@ -218,9 +223,22 @@ public class EditTeamDialog extends JDialog {
                }\r
                team.name = tname;\r
 \r
+               String ml = mailingListsField.getText();\r
+               if (!StringUtils.isEmpty(ml)) {\r
+                       Set<String> list = new HashSet<String>();\r
+                       for (String address : ml.split("(,|\\s)")) {\r
+                               if (StringUtils.isEmpty(address)) {\r
+                                       continue;\r
+                               }\r
+                               list.add(address.toLowerCase());\r
+                       }\r
+                       team.mailingLists.clear();\r
+                       team.mailingLists.addAll(list);\r
+               }\r
+\r
                team.repositories.clear();\r
                team.repositories.addAll(repositoryPalette.getSelections());\r
-               \r
+\r
                team.users.clear();\r
                team.users.addAll(userPalette.getSelections());\r
                return true;\r
@@ -251,7 +269,7 @@ public class EditTeamDialog extends JDialog {
                }\r
                repositoryPalette.setObjects(restricted, selected);\r
        }\r
-       \r
+\r
        public void setUsers(List<String> users, List<String> selected) {\r
                Collections.sort(users);\r
                if (selected != null) {\r
index 195b9d5c79d5f4d89420c86bd6ccbd75cb0c7fcc..3258ef6c0bfb2fbd0c9366ef8e0f2a4e417ff0a6 100644 (file)
@@ -18,6 +18,7 @@ package com.gitblit.models;
 import java.io.Serializable;\r
 import java.util.Collection;\r
 import java.util.HashSet;\r
+import java.util.List;\r
 import java.util.Set;\r
 \r
 /**\r
@@ -35,6 +36,7 @@ public class TeamModel implements Serializable, Comparable<TeamModel> {
        public String name;\r
        public final Set<String> users = new HashSet<String>();\r
        public final Set<String> repositories = new HashSet<String>();\r
+       public final Set<String> mailingLists = new HashSet<String>();\r
 \r
        public TeamModel(String name) {\r
                this.name = name;\r
@@ -76,6 +78,12 @@ public class TeamModel implements Serializable, Comparable<TeamModel> {
                users.remove(name.toLowerCase());\r
        }\r
 \r
+       public void addMailingLists(Collection<String> addresses) {\r
+               for (String address:addresses) {\r
+                       mailingLists.add(address.toLowerCase());\r
+               }\r
+       }\r
+\r
        @Override\r
        public String toString() {\r
                return name;\r
index a51c157f9e46109749477fc1ad8296527da36499..1e47899947181c7e942ad3d5f6d76c75a17c383b 100644 (file)
@@ -19,6 +19,7 @@ import java.io.UnsupportedEncodingException;
 import java.security.MessageDigest;\r
 import java.security.NoSuchAlgorithmException;\r
 import java.util.ArrayList;\r
+import java.util.Collection;\r
 import java.util.Collections;\r
 import java.util.Comparator;\r
 import java.util.List;\r
@@ -127,7 +128,7 @@ public class StringUtils {
         * @param values\r
         * @return flattened list\r
         */\r
-       public static String flattenStrings(List<String> values) {\r
+       public static String flattenStrings(Collection<String> values) {\r
                return flattenStrings(values, " ");\r
        }\r
 \r
@@ -139,7 +140,7 @@ public class StringUtils {
         * @param separator\r
         * @return flattened list\r
         */\r
-       public static String flattenStrings(List<String> values, String separator) {\r
+       public static String flattenStrings(Collection<String> values, String separator) {\r
                StringBuilder sb = new StringBuilder();\r
                for (String value : values) {\r
                        sb.append(value).append(separator);\r
index c0b0010d56a9b1cd4b5cdc293f8794a6b6e521d7..aead2c41d0f93779dd805d82e37ed9039b93bfcb 100644 (file)
@@ -200,4 +200,5 @@ gb.mailingLists = mailing lists
 gb.mailingListsDescription = used by the sendmail hook\r
 gb.preReceiveScripts = pre-receive scripts\r
 gb.postReceiveScripts = post-receive scripts\r
-gb.hookScripts = hook scripts
\ No newline at end of file
+gb.hookScripts = hook scripts\r
+gb.accessPermissions = access permissions
\ No newline at end of file
index 310f59e5bba3db18fc77c4748f61d56f37b24a6c..9381344b7511afa9cd006a5bd23723474af268c2 100644 (file)
@@ -23,7 +23,7 @@
                                <tr><th><wicket:message key="gb.skipSummaryMetrics"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="skipSummaryMetrics" tabindex="10" /> &nbsp;<span class="help-inline"><wicket:message key="gb.skipSummaryMetricsDescription"></wicket:message></span></td></tr>\r
                                <tr><th><wicket:message key="gb.isFrozen"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="isFrozen" tabindex="11" /> &nbsp;<span class="help-inline"><wicket:message key="gb.isFrozenDescription"></wicket:message></span></td></tr>\r
                                <tr><th><wicket:message key="gb.mailingLists"></wicket:message></th><td class="edit"><input class="span12" type="text" wicket:id="mailingLists" size="40" tabindex="12" /> &nbsp;<span class="help-inline"><wicket:message key="gb.mailingListsDescription"></wicket:message></span></td></tr>\r
-                               <tr><td colspan="2"><h3><wicket:message key="gb.accessRestriction"></wicket:message></h3></td></tr>     \r
+                               <tr><td colspan="2"><h3><wicket:message key="gb.accessPermissions"></wicket:message></h3></td></tr>     \r
                                <tr><th><wicket:message key="gb.accessRestriction"></wicket:message></th><td class="edit"><select class="span6" wicket:id="accessRestriction" tabindex="13" /></td></tr>                                \r
                                <tr><th style="vertical-align: top;"><wicket:message key="gb.permittedUsers"></wicket:message></th><td style="padding:2px;"><span wicket:id="users"></span></td></tr>\r
                                <tr><th style="vertical-align: top;"><wicket:message key="gb.permittedTeams"></wicket:message></th><td style="padding:2px;"><span wicket:id="teams"></span></td></tr>\r
index 614f0aa1e32ae6a708d51732eba8af6271e4c2ae..537d78d25a1cf40bbb28edef4ee4d088abb61c29 100644 (file)
        <form style="padding-top:5px;" wicket:id="editForm">\r
                <table class="plain">\r
                        <tbody class="settings">\r
+                               <tr><td colspan="2"><h3><wicket:message key="gb.general"></wicket:message></h3></td></tr>                       \r
                                <tr><th><wicket:message key="gb.teamName"></wicket:message></th><td class="edit"><input type="text" wicket:id="name" id="name" size="30" tabindex="1" /></td></tr>\r
-                               <tr><td colspan="2"><hr></hr></td></tr>\r
+                               <tr><th><wicket:message key="gb.mailingLists"></wicket:message></th><td class="edit"><input class="span12" type="text" wicket:id="mailingLists" size="40" tabindex="2" /> &nbsp;<span class="help-inline"><wicket:message key="gb.mailingListsDescription"></wicket:message></span></td></tr>\r
+                               <tr><td colspan="2" style="padding-top:10px;"><h3><wicket:message key="gb.accessPermissions"></wicket:message></h3></td></tr>                           \r
                                <tr><th style="vertical-align: top;"><wicket:message key="gb.teamMembers"></wicket:message></th><td style="padding:2px;"><span wicket:id="users"></span></td></tr>\r
                                <tr><td colspan="2"><hr></hr></td></tr>\r
                                <tr><th style="vertical-align: top;"><wicket:message key="gb.restrictedRepositories"></wicket:message></th><td style="padding:2px;"><span wicket:id="repositories"></span></td></tr>\r
index 47f3568fd0e1be30fca8a4db0b58febfd95192f7..c5c240fd845d8732d0af6f9a37036cb5beb4aab8 100644 (file)
@@ -18,8 +18,10 @@ package com.gitblit.wicket.pages;
 import java.text.MessageFormat;\r
 import java.util.ArrayList;\r
 import java.util.Collections;\r
+import java.util.HashSet;\r
 import java.util.Iterator;\r
 import java.util.List;\r
+import java.util.Set;\r
 \r
 import org.apache.wicket.PageParameters;\r
 import org.apache.wicket.extensions.markup.html.form.palette.Palette;\r
@@ -28,6 +30,8 @@ import org.apache.wicket.markup.html.form.ChoiceRenderer;
 import org.apache.wicket.markup.html.form.Form;\r
 import org.apache.wicket.markup.html.form.TextField;\r
 import org.apache.wicket.model.CompoundPropertyModel;\r
+import org.apache.wicket.model.IModel;\r
+import org.apache.wicket.model.Model;\r
 import org.apache.wicket.model.util.CollectionModel;\r
 import org.apache.wicket.model.util.ListModel;\r
 \r
@@ -45,6 +49,8 @@ public class EditTeamPage extends RootSubPage {
 \r
        private final boolean isCreate;\r
 \r
+       private IModel<String> mailingLists;\r
+\r
        public EditTeamPage() {\r
                // create constructor\r
                super();\r
@@ -128,6 +134,20 @@ public class EditTeamPage extends RootSubPage {
                                teamModel.users.clear();\r
                                teamModel.users.addAll(members);\r
 \r
+                               // set mailing lists\r
+                               String ml = mailingLists.getObject();\r
+                               if (!StringUtils.isEmpty(ml)) {\r
+                                       Set<String> list = new HashSet<String>();\r
+                                       for (String address : ml.split("(,|\\s)")) {\r
+                                               if (StringUtils.isEmpty(address)) {\r
+                                                       continue;\r
+                                               }\r
+                                               list.add(address.toLowerCase());\r
+                                       }\r
+                                       teamModel.mailingLists.clear();\r
+                                       teamModel.mailingLists.addAll(list);\r
+                               }\r
+                               \r
                                try {\r
                                        GitBlit.self().updateTeamModel(oldName, teamModel, isCreate);\r
                                } catch (GitBlitException e) {\r
@@ -149,8 +169,12 @@ public class EditTeamPage extends RootSubPage {
 \r
                // field names reflective match TeamModel fields\r
                form.add(new TextField<String>("name"));\r
-               form.add(repositories);\r
                form.add(users);\r
+               mailingLists = new Model<String>(teamModel.mailingLists == null ? ""\r
+                               : StringUtils.flattenStrings(teamModel.mailingLists, " "));\r
+               form.add(new TextField<String>("mailingLists", mailingLists));\r
+               \r
+               form.add(repositories);\r
 \r
                form.add(new Button("save"));\r
                Button cancel = new Button("cancel") {\r
index 319a2b65ffb1ede149904a1afee10309021204b1..87263c24b322fd369f7fe39bde374bcd49372dd6 100644 (file)
        <form style="padding-top:5px;" wicket:id="editForm">\r
                <table class="plain">\r
                        <tbody class="settings">\r
+                               <tr><td colspan="2"><h3><wicket:message key="gb.general"></wicket:message></h3></td></tr>                       \r
                                <tr><th><wicket:message key="gb.username"></wicket:message></th><td class="edit"><input type="text" wicket:id="username" id="username" size="30" tabindex="1" /></td></tr>\r
                                <tr><th><wicket:message key="gb.password"></wicket:message></th><td class="edit"><input type="password" wicket:id="password" size="30" tabindex="2" /></td></tr>\r
                                <tr><th><wicket:message key="gb.confirmPassword"></wicket:message></th><td class="edit"><input type="password" wicket:id="confirmPassword" size="30" tabindex="3" /></td></tr>\r
                                <tr><th><wicket:message key="gb.canAdmin"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="canAdmin" tabindex="6" /> &nbsp;<span class="help-inline"><wicket:message key="gb.canAdminDescription"></wicket:message></span></td></tr>                            \r
                                <tr><th><wicket:message key="gb.excludeFromFederation"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="excludeFromFederation" tabindex="7" /> &nbsp;<span class="help-inline"><wicket:message key="gb.excludeFromFederationDescription"></wicket:message></span></td></tr>                             \r
-                               <tr><td colspan="2"><hr></hr></td></tr>\r
+                               <tr><td colspan="2" style="padding-top:10px;"><h3><wicket:message key="gb.accessPermissions"></wicket:message></h3></td></tr>                           \r
                                <tr><th style="vertical-align: top;"><wicket:message key="gb.teamMemberships"></wicket:message></th><td style="padding:2px;"><span wicket:id="teams"></span></td></tr>\r
                                <tr><td colspan="2"><hr></hr></td></tr>\r
                                <tr><th style="vertical-align: top;"><wicket:message key="gb.restrictedRepositories"></wicket:message></th><td style="padding:2px;"><span wicket:id="repositories"></span></td></tr>\r