Allow group descriptions to supply email and URL
Some backends have external management interfaces that are not embedded into Gerrit Code Review. Allow those backends to supply a URL to the web management interface for a group, so a user can manage their membership, view current members, or do whatever other features the group system might support. Some backends also have an email address associated with every group. Sending email to that address will distribute the message to the group's members. Permit backends to supply an optional email address, and use this in the project level notification system if a group is selected as the target for a message. Change-Id: Ifaebc01571c2db84872b2c08ff99c05389372f61
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
package com.google.gerrit.common;
|
||||
|
||||
import com.google.gerrit.common.data.ChangeInfo;
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.reviewdb.client.Change.Status;
|
||||
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||
@@ -101,6 +102,10 @@ public class PageLinks {
|
||||
return status(status) + " " + op("project", proj.get());
|
||||
}
|
||||
|
||||
public static String toGroup(AccountGroup.UUID uuid) {
|
||||
return ADMIN_GROUPS + "uuid-" + uuid;
|
||||
}
|
||||
|
||||
private static String status(Status status) {
|
||||
switch (status) {
|
||||
case ABANDONED:
|
||||
|
@@ -16,6 +16,8 @@ package com.google.gerrit.common.data;
|
||||
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Group methods exposed by the GroupBackend.
|
||||
*/
|
||||
@@ -32,6 +34,22 @@ public class GroupDescription {
|
||||
|
||||
/** @return whether the group is visible to all accounts. */
|
||||
boolean isVisibleToAll();
|
||||
|
||||
/**
|
||||
* @return optional email address to send to the group's members. If
|
||||
* provided, Gerrit will use this email address to send
|
||||
* change notifications to the group.
|
||||
*/
|
||||
@Nullable
|
||||
String getEmailAddress();
|
||||
|
||||
/**
|
||||
* @return optional URL to information about the group. Typically a URL to a
|
||||
* web page that permits users to apply to join the group, or manage
|
||||
* their membership.
|
||||
*/
|
||||
@Nullable
|
||||
String getUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -14,6 +14,7 @@
|
||||
|
||||
package com.google.gerrit.common.data;
|
||||
|
||||
import com.google.gerrit.common.PageLinks;
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@@ -52,6 +53,18 @@ public class GroupDescriptions {
|
||||
public AccountGroup getAccountGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getEmailAddress() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getUrl() {
|
||||
return "#" + PageLinks.toGroup(getGroupUUID());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -21,6 +21,7 @@ public class GroupInfo {
|
||||
protected AccountGroup.UUID uuid;
|
||||
protected String name;
|
||||
protected String description;
|
||||
protected String url;
|
||||
|
||||
protected GroupInfo() {
|
||||
}
|
||||
@@ -44,6 +45,7 @@ public class GroupInfo {
|
||||
public GroupInfo(GroupDescription.Basic a) {
|
||||
uuid = a.getGroupUUID();
|
||||
name = a.getName();
|
||||
url = a.getUrl();
|
||||
|
||||
if (a instanceof GroupDescription.Internal) {
|
||||
AccountGroup group = ((GroupDescription.Internal) a).getAccountGroup();
|
||||
@@ -65,4 +67,8 @@ public class GroupInfo {
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
@@ -145,12 +145,12 @@ public class Dispatcher {
|
||||
return "/admin/groups/" + id.toString() + "," + panel;
|
||||
}
|
||||
|
||||
public static String toGroup(final AccountGroup.UUID uuid) {
|
||||
return "/admin/groups/uuid-" + uuid.toString();
|
||||
public static String toGroup(AccountGroup.UUID uuid) {
|
||||
return PageLinks.toGroup(uuid);
|
||||
}
|
||||
|
||||
public static String toGroup(AccountGroup.UUID uuid, String panel) {
|
||||
return "/admin/groups/uuid-" + uuid.toString() + "," + panel;
|
||||
return toGroup(uuid) + "," + panel;
|
||||
}
|
||||
|
||||
public static String toProject(Project.NameKey n) {
|
||||
|
@@ -24,7 +24,7 @@ public class MyGroupsScreen extends SettingsScreen {
|
||||
@Override
|
||||
protected void onInitUI() {
|
||||
super.onInitUI();
|
||||
groups = new GroupTable(true /* hyperlink to admin */);
|
||||
groups = new GroupTable();
|
||||
add(groups);
|
||||
}
|
||||
|
||||
|
@@ -35,6 +35,7 @@ import com.google.gerrit.reviewdb.client.AccountGroupIncludeByUuid;
|
||||
import com.google.gerrit.reviewdb.client.AccountGroupMember;
|
||||
import com.google.gwt.event.dom.client.ClickEvent;
|
||||
import com.google.gwt.event.dom.client.ClickHandler;
|
||||
import com.google.gwt.user.client.ui.Anchor;
|
||||
import com.google.gwt.user.client.ui.Button;
|
||||
import com.google.gwt.user.client.ui.CheckBox;
|
||||
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
|
||||
@@ -369,6 +370,14 @@ public class AccountGroupMembersScreen extends AccountGroupScreen {
|
||||
new Hyperlink(info.getName(), Dispatcher.toGroup(uuid)));
|
||||
fmt.getElement(row, 2).setTitle(null);
|
||||
table.setText(row, 3, info.getDescription());
|
||||
} else if (info.getUrl() != null) {
|
||||
Anchor a = new Anchor();
|
||||
a.setText(info.getName());
|
||||
a.setHref(info.getUrl());
|
||||
a.setTitle("UUID " + uuid.get());
|
||||
table.setWidget(row, 2, a);
|
||||
fmt.getElement(row, 2).setTitle(null);
|
||||
table.clearCell(row, 3);
|
||||
} else {
|
||||
table.setText(row, 2, info.getName());
|
||||
fmt.getElement(row, 2).setTitle("UUID " + uuid.get());
|
||||
|
@@ -82,7 +82,7 @@ public class GroupListScreen extends AccountScreen implements FilteredUserInterf
|
||||
setPageTitle(Util.C.groupListTitle());
|
||||
initPageHeader();
|
||||
|
||||
groups = new GroupTable(true /* hyperlink to admin */, PageLinks.ADMIN_GROUPS);
|
||||
groups = new GroupTable(PageLinks.ADMIN_GROUPS);
|
||||
add(groups);
|
||||
}
|
||||
|
||||
|
@@ -14,6 +14,7 @@
|
||||
|
||||
package com.google.gerrit.client.admin;
|
||||
|
||||
import static com.google.gerrit.client.admin.Util.C;
|
||||
import com.google.gerrit.client.Dispatcher;
|
||||
import com.google.gerrit.client.Gerrit;
|
||||
import com.google.gerrit.client.groups.GroupInfo;
|
||||
@@ -21,9 +22,12 @@ import com.google.gerrit.client.groups.GroupList;
|
||||
import com.google.gerrit.client.groups.GroupMap;
|
||||
import com.google.gerrit.client.ui.HighlightingInlineHyperlink;
|
||||
import com.google.gerrit.client.ui.NavigationTable;
|
||||
import com.google.gerrit.client.ui.Util;
|
||||
import com.google.gerrit.common.PageLinks;
|
||||
import com.google.gwt.event.dom.client.ClickEvent;
|
||||
import com.google.gwt.event.dom.client.ClickHandler;
|
||||
import com.google.gwt.user.client.History;
|
||||
import com.google.gwt.user.client.ui.Anchor;
|
||||
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
|
||||
import com.google.gwt.user.client.ui.HTMLTable.Cell;
|
||||
import com.google.gwt.user.client.ui.Image;
|
||||
@@ -36,20 +40,17 @@ import java.util.List;
|
||||
public class GroupTable extends NavigationTable<GroupInfo> {
|
||||
private static final int NUM_COLS = 3;
|
||||
|
||||
private final boolean enableLink;
|
||||
|
||||
public GroupTable(final boolean enableLink) {
|
||||
this(enableLink, null);
|
||||
public GroupTable() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public GroupTable(final boolean enableLink, final String pointerId) {
|
||||
super(Util.C.groupItemHelp());
|
||||
this.enableLink = enableLink;
|
||||
public GroupTable(final String pointerId) {
|
||||
super(C.groupItemHelp());
|
||||
setSavePointerId(pointerId);
|
||||
|
||||
table.setText(0, 1, Util.C.columnGroupName());
|
||||
table.setText(0, 2, Util.C.columnGroupDescription());
|
||||
table.setText(0, 3, Util.C.columnGroupVisibleToAll());
|
||||
table.setText(0, 1, C.columnGroupName());
|
||||
table.setText(0, 2, C.columnGroupDescription());
|
||||
table.setText(0, 3, C.columnGroupVisibleToAll());
|
||||
table.addClickHandler(new ClickHandler() {
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
@@ -104,11 +105,18 @@ public class GroupTable extends NavigationTable<GroupInfo> {
|
||||
}
|
||||
|
||||
void populate(final int row, final GroupInfo k, final String toHighlight) {
|
||||
if (enableLink) {
|
||||
table.setWidget(row, 1, new HighlightingInlineHyperlink(k.name(),
|
||||
Dispatcher.toGroup(k.getGroupId()), toHighlight));
|
||||
if (k.url() != null) {
|
||||
if (k.url().startsWith("#" + PageLinks.ADMIN_GROUPS)) {
|
||||
table.setWidget(row, 1, new HighlightingInlineHyperlink(k.name(),
|
||||
Dispatcher.toGroup(k.getGroupId()), toHighlight));
|
||||
} else {
|
||||
Anchor link = new Anchor();
|
||||
link.setHTML(Util.highlight(k.name(), toHighlight));
|
||||
link.setHref(k.url());
|
||||
table.setWidget(row, 1, link);
|
||||
}
|
||||
} else {
|
||||
table.setText(row, 1, k.name());
|
||||
table.setHTML(row, 1, Util.highlight(k.name(), toHighlight));
|
||||
}
|
||||
table.setText(row, 2, k.description());
|
||||
if (k.isVisibleToAll()) {
|
||||
|
@@ -31,6 +31,7 @@ public class GroupInfo extends JavaScriptObject {
|
||||
public final native String name() /*-{ return this.name; }-*/;
|
||||
public final native boolean isVisibleToAll() /*-{ return this['visible_to_all'] ? true : false; }-*/;
|
||||
public final native String description() /*-{ return this.description; }-*/;
|
||||
public final native String url() /*-{ return this.url; }-*/;
|
||||
|
||||
private final native int group_id() /*-{ return this.group_id; }-*/;
|
||||
private final native String owner_uuid() /*-{ return this.owner_uuid; }-*/;
|
||||
|
@@ -47,6 +47,7 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.naming.InvalidNameException;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.directory.DirContext;
|
||||
@@ -152,6 +153,18 @@ public class LdapGroupBackend implements GroupBackend {
|
||||
public boolean isVisibleToAll() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getEmailAddress() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getUrl() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -21,8 +21,9 @@ import com.google.gerrit.server.util.Url;
|
||||
|
||||
public class GroupInfo {
|
||||
final String kind = "gerritcodereview#group";
|
||||
public String id;
|
||||
public String name;
|
||||
String id;
|
||||
String name;
|
||||
String url;
|
||||
Boolean visibleToAll;
|
||||
|
||||
// These fields are only supplied for internal groups.
|
||||
@@ -33,6 +34,7 @@ public class GroupInfo {
|
||||
public GroupInfo(GroupDescription.Basic group) {
|
||||
id = Url.encode(group.getGroupUUID().get());
|
||||
name = Strings.emptyToNull(group.getName());
|
||||
url = Strings.emptyToNull(group.getUrl());
|
||||
visibleToAll = group.isVisibleToAll() ? true : null;
|
||||
|
||||
if (group instanceof GroupDescription.Internal) {
|
||||
|
@@ -14,8 +14,10 @@
|
||||
|
||||
package com.google.gerrit.server.mail;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gerrit.common.data.GroupDescription;
|
||||
import com.google.gerrit.common.data.GroupDescriptions;
|
||||
import com.google.gerrit.common.data.GroupReference;
|
||||
import com.google.gerrit.common.errors.EmailException;
|
||||
@@ -418,22 +420,7 @@ public abstract class ChangeEmail extends NotificationEmail {
|
||||
private void add(Watchers matching, NotifyConfig nc, Project.NameKey project)
|
||||
throws OrmException, QueryParseException {
|
||||
for (GroupReference ref : nc.getGroups()) {
|
||||
AccountGroup group =
|
||||
GroupDescriptions.toAccountGroup(args.groupBackend.get(ref.getUUID()));
|
||||
if (group == null) {
|
||||
log.warn(String.format(
|
||||
"Project %s has invalid group %s in notify section %s",
|
||||
project.get(), ref.getName(), nc.getName()));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (group.getType() != AccountGroup.Type.INTERNAL) {
|
||||
log.warn(String.format(
|
||||
"Project %s cannot use group %s of type %s in notify section %s",
|
||||
project.get(), ref.getName(), group.getType(), nc.getName()));
|
||||
continue;
|
||||
}
|
||||
|
||||
GroupDescription.Basic group = args.groupBackend.get(ref.getUUID());
|
||||
ChangeQueryBuilder qb = args.queryBuilder.create(new SingleGroupUser(
|
||||
args.capabilityControlFactory,
|
||||
ref.getUUID()));
|
||||
@@ -443,9 +430,29 @@ public abstract class ChangeEmail extends NotificationEmail {
|
||||
p = Predicate.and(qb.parse(nc.getFilter()), p);
|
||||
p = args.queryRewriter.get().rewrite(p);
|
||||
}
|
||||
if (p.match(changeData)) {
|
||||
recursivelyAddAllAccounts(matching.list(nc.getHeader()), group);
|
||||
if (!p.match(changeData)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Strings.isNullOrEmpty(group.getEmailAddress())) {
|
||||
matching.list(nc.getHeader()).emails.add(new Address(group.getEmailAddress()));
|
||||
continue;
|
||||
}
|
||||
|
||||
AccountGroup ig = GroupDescriptions.toAccountGroup(group);
|
||||
if (ig == null) {
|
||||
log.warn(String.format(
|
||||
"Project %s has invalid group %s in notify section %s",
|
||||
project.get(), ref.getName(), nc.getName()));
|
||||
continue;
|
||||
}
|
||||
if (ig.getType() != AccountGroup.Type.INTERNAL) {
|
||||
log.warn(String.format(
|
||||
"Project %s cannot use group %s of type %s in notify section %s",
|
||||
project.get(), ref.getName(), ig.getType(), nc.getName()));
|
||||
continue;
|
||||
}
|
||||
recursivelyAddAllAccounts(matching.list(nc.getHeader()), ig);
|
||||
}
|
||||
|
||||
if (!nc.getAddresses().isEmpty()) {
|
||||
|
Reference in New Issue
Block a user