Add group editing support to the UI
Admins and members who have the "owner" flag set to true may edit the contents of a group by changing its description or editing its membership list. Changes are mostly done live, as soon as the user presses the action keys. Account lookup is completed through the suggest service, helping the user to complete an account identity by name and/or the user's preferred email address. Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
@@ -22,6 +22,8 @@
|
||||
class='com.google.gerrit.server.AccountServiceSrv'/>
|
||||
<servlet path='/rpc/AccountSecurity'
|
||||
class='com.google.gerrit.server.AccountSecuritySrv'/>
|
||||
<servlet path='/rpc/AdminService'
|
||||
class='com.google.gerrit.server.AdminServiceSrv'/>
|
||||
<servlet path='/rpc/ChangeDetailService'
|
||||
class='com.google.gerrit.server.ChangeDetailServiceSrv'/>
|
||||
<servlet path='/rpc/ChangeListService'
|
||||
|
@@ -15,6 +15,7 @@
|
||||
package com.google.gerrit.client;
|
||||
|
||||
import com.google.gerrit.client.account.AccountSettings;
|
||||
import com.google.gerrit.client.admin.AccountGroupScreen;
|
||||
import com.google.gerrit.client.changes.AccountDashboardScreen;
|
||||
import com.google.gerrit.client.changes.ChangeScreen;
|
||||
import com.google.gerrit.client.changes.MineStarredScreen;
|
||||
@@ -23,6 +24,7 @@ import com.google.gerrit.client.data.ChangeInfo;
|
||||
import com.google.gerrit.client.patches.PatchSideBySideScreen;
|
||||
import com.google.gerrit.client.patches.PatchUnifiedScreen;
|
||||
import com.google.gerrit.client.reviewdb.Account;
|
||||
import com.google.gerrit.client.reviewdb.AccountGroup;
|
||||
import com.google.gerrit.client.reviewdb.Change;
|
||||
import com.google.gerrit.client.reviewdb.Patch;
|
||||
import com.google.gerrit.client.rpc.RpcUtil;
|
||||
@@ -124,6 +126,10 @@ public class Link implements HistoryListener {
|
||||
if (token.startsWith(p))
|
||||
return new AccountDashboardScreen(Account.Id.parse(skip(p, token)));
|
||||
|
||||
p = "admin,group,";
|
||||
if (token.startsWith(p))
|
||||
return new AccountGroupScreen(AccountGroup.Id.parse(skip(p, token)));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,81 @@
|
||||
// Copyright 2008 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.client.admin;
|
||||
|
||||
import com.google.gerrit.client.data.AccountInfo;
|
||||
import com.google.gerrit.client.data.AccountInfoCache;
|
||||
import com.google.gerrit.client.data.AccountInfoCacheFactory;
|
||||
import com.google.gerrit.client.reviewdb.Account;
|
||||
import com.google.gerrit.client.reviewdb.AccountGroup;
|
||||
import com.google.gerrit.client.reviewdb.AccountGroupMember;
|
||||
import com.google.gerrit.client.reviewdb.ReviewDb;
|
||||
import com.google.gwtorm.client.OrmException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class AccountGroupDetail {
|
||||
protected AccountInfoCache accounts;
|
||||
protected AccountGroup group;
|
||||
protected List<AccountGroupMember> members;
|
||||
|
||||
public AccountGroupDetail() {
|
||||
}
|
||||
|
||||
public void load(final ReviewDb db, final AccountInfoCacheFactory acc,
|
||||
final AccountGroup g) throws OrmException {
|
||||
group = g;
|
||||
members = db.accountGroupMembers().byGroup(group.getId()).toList();
|
||||
for (final AccountGroupMember m : members) {
|
||||
acc.want(m.getAccountId());
|
||||
}
|
||||
accounts = acc.create();
|
||||
|
||||
Collections.sort(members, new Comparator<AccountGroupMember>() {
|
||||
public int compare(final AccountGroupMember o1,
|
||||
final AccountGroupMember o2) {
|
||||
final AccountInfo a = accounts.get(o1.getAccountId());
|
||||
final AccountInfo b = accounts.get(o2.getAccountId());
|
||||
return n(a).compareTo(n(b));
|
||||
}
|
||||
|
||||
private String n(final AccountInfo a) {
|
||||
String n = a.getFullName();
|
||||
if (n != null && n.length() > 0) {
|
||||
return n;
|
||||
}
|
||||
|
||||
n = a.getPreferredEmail();
|
||||
if (n != null && n.length() > 0) {
|
||||
return n;
|
||||
}
|
||||
|
||||
return a.getId().toString();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void loadOneMember(final ReviewDb db, final Account a,
|
||||
final AccountGroupMember m) throws OrmException {
|
||||
final AccountInfoCacheFactory acc = new AccountInfoCacheFactory(db);
|
||||
acc.put(a);
|
||||
acc.want(m.getAccountId());
|
||||
members = new ArrayList<AccountGroupMember>(1);
|
||||
members.add(m);
|
||||
accounts = acc.create();
|
||||
}
|
||||
}
|
@@ -0,0 +1,345 @@
|
||||
// Copyright 2008 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.client.admin;
|
||||
|
||||
import com.google.gerrit.client.data.AccountInfoCache;
|
||||
import com.google.gerrit.client.reviewdb.Account;
|
||||
import com.google.gerrit.client.reviewdb.AccountGroup;
|
||||
import com.google.gerrit.client.reviewdb.AccountGroupMember;
|
||||
import com.google.gerrit.client.rpc.GerritCallback;
|
||||
import com.google.gerrit.client.ui.AccountDashboardLink;
|
||||
import com.google.gerrit.client.ui.AccountSuggestOracle;
|
||||
import com.google.gerrit.client.ui.FancyFlexTable;
|
||||
import com.google.gerrit.client.ui.Screen;
|
||||
import com.google.gerrit.client.ui.TextSaveButtonListener;
|
||||
import com.google.gwt.user.client.ui.Button;
|
||||
import com.google.gwt.user.client.ui.CheckBox;
|
||||
import com.google.gwt.user.client.ui.ClickListener;
|
||||
import com.google.gwt.user.client.ui.FlowPanel;
|
||||
import com.google.gwt.user.client.ui.FocusListenerAdapter;
|
||||
import com.google.gwt.user.client.ui.Label;
|
||||
import com.google.gwt.user.client.ui.SourcesTableEvents;
|
||||
import com.google.gwt.user.client.ui.SuggestBox;
|
||||
import com.google.gwt.user.client.ui.TableListener;
|
||||
import com.google.gwt.user.client.ui.TextArea;
|
||||
import com.google.gwt.user.client.ui.TextBox;
|
||||
import com.google.gwt.user.client.ui.VerticalPanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
|
||||
import com.google.gwtjsonrpc.client.VoidResult;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
public class AccountGroupScreen extends Screen {
|
||||
private AccountGroup.Id groupId;
|
||||
private AccountInfoCache accounts = AccountInfoCache.empty();
|
||||
private MemberTable members;
|
||||
|
||||
private TextArea descTxt;
|
||||
private Button saveDesc;
|
||||
private Button addMember;
|
||||
private TextBox nameTxtBox;
|
||||
private SuggestBox nameTxt;
|
||||
private Button delMember;
|
||||
|
||||
public AccountGroupScreen(final AccountGroup.Id toShow) {
|
||||
groupId = toShow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
if (members == null) {
|
||||
initUI();
|
||||
}
|
||||
|
||||
enableForm(false);
|
||||
saveDesc.setEnabled(false);
|
||||
super.onLoad();
|
||||
|
||||
Util.ADMIN_SVC.groupDetail(groupId,
|
||||
new GerritCallback<AccountGroupDetail>() {
|
||||
public void onSuccess(final AccountGroupDetail result) {
|
||||
enableForm(true);
|
||||
saveDesc.setEnabled(false);
|
||||
display(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void enableForm(final boolean on) {
|
||||
descTxt.setEnabled(on);
|
||||
addMember.setEnabled(on);
|
||||
nameTxtBox.setEnabled(on);
|
||||
delMember.setEnabled(on);
|
||||
}
|
||||
|
||||
private void initUI() {
|
||||
{
|
||||
final VerticalPanel vp = new VerticalPanel();
|
||||
final Label descHdr = new Label(Util.C.headingDescription());
|
||||
descHdr.setStyleName("gerrit-SmallHeading");
|
||||
vp.add(descHdr);
|
||||
|
||||
descTxt = new TextArea();
|
||||
descTxt.setVisibleLines(6);
|
||||
descTxt.setCharacterWidth(60);
|
||||
new TextSaveButtonListener(descTxt, saveDesc);
|
||||
vp.add(descTxt);
|
||||
|
||||
saveDesc = new Button(Util.C.buttonSaveDescription());
|
||||
saveDesc.addClickListener(new ClickListener() {
|
||||
public void onClick(Widget sender) {
|
||||
final String txt = descTxt.getText().trim();
|
||||
Util.ADMIN_SVC.changeGroupDescription(groupId, txt,
|
||||
new GerritCallback<VoidResult>() {
|
||||
public void onSuccess(final VoidResult result) {
|
||||
saveDesc.setEnabled(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
vp.add(saveDesc);
|
||||
add(vp);
|
||||
}
|
||||
|
||||
{
|
||||
final Label memberHdr = new Label(Util.C.headingMembers());
|
||||
memberHdr.setStyleName("gerrit-SmallHeading");
|
||||
add(memberHdr);
|
||||
}
|
||||
|
||||
{
|
||||
final FlowPanel fp = new FlowPanel();
|
||||
fp.setStyleName("gerrit-ProjectWatchPanel-AddPanel");
|
||||
|
||||
nameTxtBox = new TextBox();
|
||||
nameTxt = new SuggestBox(new AccountSuggestOracle(), nameTxtBox);
|
||||
nameTxtBox.setVisibleLength(50);
|
||||
nameTxtBox.setText(Util.C.defaultAccountName());
|
||||
nameTxtBox.addStyleName("gerrit-InputFieldTypeHint");
|
||||
nameTxtBox.addFocusListener(new FocusListenerAdapter() {
|
||||
@Override
|
||||
public void onFocus(Widget sender) {
|
||||
if (Util.C.defaultAccountName().equals(nameTxtBox.getText())) {
|
||||
nameTxtBox.setText("");
|
||||
nameTxtBox.removeStyleName("gerrit-InputFieldTypeHint");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLostFocus(Widget sender) {
|
||||
if ("".equals(nameTxtBox.getText())) {
|
||||
nameTxtBox.setText(Util.C.defaultAccountName());
|
||||
nameTxtBox.addStyleName("gerrit-InputFieldTypeHint");
|
||||
}
|
||||
}
|
||||
});
|
||||
fp.add(nameTxt);
|
||||
|
||||
addMember = new Button(Util.C.buttonAddGroupMember());
|
||||
addMember.addClickListener(new ClickListener() {
|
||||
public void onClick(final Widget sender) {
|
||||
doAddNew();
|
||||
}
|
||||
});
|
||||
fp.add(addMember);
|
||||
add(fp);
|
||||
}
|
||||
|
||||
members = new MemberTable();
|
||||
add(members);
|
||||
{
|
||||
final FlowPanel fp = new FlowPanel();
|
||||
delMember = new Button(Util.C.buttonDeleteGroupMembers());
|
||||
delMember.addClickListener(new ClickListener() {
|
||||
public void onClick(final Widget sender) {
|
||||
members.deleteChecked();
|
||||
}
|
||||
});
|
||||
fp.add(delMember);
|
||||
add(fp);
|
||||
}
|
||||
}
|
||||
|
||||
private void display(final AccountGroupDetail result) {
|
||||
setTitleText(Util.M.group(result.group.getName()));
|
||||
descTxt.setText(result.group.getDescription());
|
||||
accounts = result.accounts;
|
||||
members.display(result.members);
|
||||
members.finishDisplay(true);
|
||||
}
|
||||
|
||||
void doAddNew() {
|
||||
final String nameEmail = nameTxt.getText();
|
||||
if (nameEmail == null || nameEmail.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
addMember.setEnabled(false);
|
||||
Util.ADMIN_SVC.addGroupMember(groupId, nameEmail,
|
||||
new GerritCallback<AccountGroupDetail>() {
|
||||
public void onSuccess(final AccountGroupDetail result) {
|
||||
addMember.setEnabled(true);
|
||||
nameTxt.setText("");
|
||||
if (result.accounts != null && result.members != null) {
|
||||
accounts.merge(result.accounts);
|
||||
for (final AccountGroupMember m : result.members) {
|
||||
members.insertMember(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable caught) {
|
||||
addMember.setEnabled(true);
|
||||
super.onFailure(caught);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class MemberTable extends FancyFlexTable<AccountGroupMember> {
|
||||
MemberTable() {
|
||||
table.setText(0, 2, Util.C.columnMember());
|
||||
table.setText(0, 3, Util.C.columnEmailAddress());
|
||||
table.setText(0, 4, Util.C.columnOwner());
|
||||
table.addTableListener(new TableListener() {
|
||||
public void onCellClicked(SourcesTableEvents sender, int row, int cell) {
|
||||
if (cell != 1 && getRowItem(row) != null) {
|
||||
movePointerTo(row);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
final FlexCellFormatter fmt = table.getFlexCellFormatter();
|
||||
fmt.addStyleName(0, 1, S_ICON_HEADER);
|
||||
fmt.addStyleName(0, 2, S_DATA_HEADER);
|
||||
fmt.addStyleName(0, 3, S_DATA_HEADER);
|
||||
fmt.addStyleName(0, 4, S_ICON_HEADER);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getRowItemKey(final AccountGroupMember item) {
|
||||
return item.getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onKeyPress(final char keyCode, final int modifiers) {
|
||||
if (super.onKeyPress(keyCode, modifiers)) {
|
||||
return true;
|
||||
}
|
||||
if (modifiers == 0) {
|
||||
switch (keyCode) {
|
||||
case 's':
|
||||
case 'c':
|
||||
toggleCurrentRow();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onOpenItem(final AccountGroupMember item) {
|
||||
toggleCurrentRow();
|
||||
}
|
||||
|
||||
private void toggleCurrentRow() {
|
||||
final CheckBox cb = (CheckBox) table.getWidget(getCurrentRow(), 1);
|
||||
cb.setChecked(!cb.isChecked());
|
||||
}
|
||||
|
||||
void deleteChecked() {
|
||||
final HashSet<AccountGroupMember.Key> ids =
|
||||
new HashSet<AccountGroupMember.Key>();
|
||||
for (int row = 1; row < table.getRowCount(); row++) {
|
||||
final AccountGroupMember k = getRowItem(row);
|
||||
if (k != null && ((CheckBox) table.getWidget(row, 1)).isChecked()) {
|
||||
ids.add(k.getKey());
|
||||
}
|
||||
}
|
||||
if (!ids.isEmpty()) {
|
||||
Util.ADMIN_SVC.deleteGroupMembers(ids,
|
||||
new GerritCallback<VoidResult>() {
|
||||
public void onSuccess(final VoidResult result) {
|
||||
for (int row = 1; row < table.getRowCount();) {
|
||||
final AccountGroupMember k = getRowItem(row);
|
||||
if (k != null && ids.contains(k.getKey())) {
|
||||
table.removeRow(row);
|
||||
} else {
|
||||
row++;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void insertMember(final AccountGroupMember k) {
|
||||
final int row = table.getRowCount();
|
||||
table.insertRow(row);
|
||||
populate(row, k);
|
||||
}
|
||||
|
||||
void display(final List<AccountGroupMember> result) {
|
||||
while (1 < table.getRowCount())
|
||||
table.removeRow(table.getRowCount() - 1);
|
||||
|
||||
for (final AccountGroupMember k : result) {
|
||||
final int row = table.getRowCount();
|
||||
table.insertRow(row);
|
||||
populate(row, k);
|
||||
}
|
||||
}
|
||||
|
||||
void populate(final int row, final AccountGroupMember k) {
|
||||
final Account.Id accountId = k.getAccountId();
|
||||
table.setWidget(row, 1, new CheckBox());
|
||||
table.setWidget(row, 2, AccountDashboardLink.link(accounts, accountId));
|
||||
table.setText(row, 3, accounts.get(accountId).getPreferredEmail());
|
||||
|
||||
final CheckBox owner = new CheckBox();
|
||||
owner.setChecked(k.isGroupOwner());
|
||||
owner.addClickListener(new ClickListener() {
|
||||
public void onClick(Widget sender) {
|
||||
final boolean oldValue = k.isGroupOwner();
|
||||
final boolean newValue = owner.isChecked();
|
||||
Util.ADMIN_SVC.changeGroupOwner(k.getKey(), newValue,
|
||||
new GerritCallback<VoidResult>() {
|
||||
public void onSuccess(final VoidResult result) {
|
||||
k.setGroupOwner(newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable caught) {
|
||||
owner.setChecked(oldValue);
|
||||
k.setGroupOwner(oldValue);
|
||||
super.onFailure(caught);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
table.setWidget(row, 4, owner);
|
||||
|
||||
final FlexCellFormatter fmt = table.getFlexCellFormatter();
|
||||
fmt.addStyleName(row, 1, S_ICON_CELL);
|
||||
fmt.addStyleName(row, 2, S_DATA_CELL);
|
||||
fmt.addStyleName(row, 3, S_DATA_CELL);
|
||||
fmt.addStyleName(row, 4, S_ICON_CELL);
|
||||
|
||||
setRowItem(row, k);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
// Copyright 2008 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.client.admin;
|
||||
|
||||
import com.google.gwt.i18n.client.Constants;
|
||||
|
||||
public interface AdminConstants extends Constants {
|
||||
String defaultAccountName();
|
||||
|
||||
String buttonDeleteGroupMembers();
|
||||
String buttonAddGroupMember();
|
||||
String buttonSaveDescription();
|
||||
|
||||
String headingDescription();
|
||||
String headingMembers();
|
||||
|
||||
String columnMember();
|
||||
String columnEmailAddress();
|
||||
String columnOwner();
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
defaultAccountName = Name or Email
|
||||
|
||||
buttonDeleteGroupMembers = Delete
|
||||
buttonAddGroupMember = Add
|
||||
buttonSaveDescription = Save Description
|
||||
|
||||
headingDescription = Description
|
||||
headingMembers = Members
|
||||
|
||||
columnMember = Member
|
||||
columnEmailAddress = Email Address
|
||||
columnOwner = Owner
|
@@ -0,0 +1,21 @@
|
||||
// Copyright 2008 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.client.admin;
|
||||
|
||||
import com.google.gwt.i18n.client.Messages;
|
||||
|
||||
public interface AdminMessages extends Messages {
|
||||
public String group(String name);
|
||||
}
|
@@ -0,0 +1 @@
|
||||
group = Group {0}
|
@@ -0,0 +1,46 @@
|
||||
// Copyright 2008 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.client.admin;
|
||||
|
||||
import com.google.gerrit.client.reviewdb.AccountGroup;
|
||||
import com.google.gerrit.client.reviewdb.AccountGroupMember;
|
||||
import com.google.gerrit.client.rpc.SignInRequired;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwtjsonrpc.client.RemoteJsonService;
|
||||
import com.google.gwtjsonrpc.client.VoidResult;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public interface AdminService extends RemoteJsonService {
|
||||
@SignInRequired
|
||||
void groupDetail(AccountGroup.Id groupId,
|
||||
AsyncCallback<AccountGroupDetail> callback);
|
||||
|
||||
@SignInRequired
|
||||
void changeGroupDescription(AccountGroup.Id groupId, String description,
|
||||
AsyncCallback<VoidResult> callback);
|
||||
|
||||
@SignInRequired
|
||||
void addGroupMember(AccountGroup.Id groupId, String nameOrEmail,
|
||||
AsyncCallback<AccountGroupDetail> callback);
|
||||
|
||||
@SignInRequired
|
||||
void deleteGroupMembers(Set<AccountGroupMember.Key> keys,
|
||||
AsyncCallback<VoidResult> callback);
|
||||
|
||||
@SignInRequired
|
||||
void changeGroupOwner(AccountGroupMember.Key key, boolean owner,
|
||||
AsyncCallback<VoidResult> callback);
|
||||
}
|
@@ -0,0 +1,177 @@
|
||||
// Copyright 2008 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.client.admin;
|
||||
|
||||
import com.google.gerrit.client.data.AccountInfoCacheFactory;
|
||||
import com.google.gerrit.client.reviewdb.Account;
|
||||
import com.google.gerrit.client.reviewdb.AccountGroup;
|
||||
import com.google.gerrit.client.reviewdb.AccountGroupMember;
|
||||
import com.google.gerrit.client.reviewdb.ReviewDb;
|
||||
import com.google.gerrit.client.rpc.BaseServiceImplementation;
|
||||
import com.google.gerrit.client.rpc.NoSuchEntityException;
|
||||
import com.google.gerrit.client.rpc.RpcUtil;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwtjsonrpc.client.VoidResult;
|
||||
import com.google.gwtorm.client.OrmException;
|
||||
import com.google.gwtorm.client.SchemaFactory;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class AdminServiceImpl extends BaseServiceImplementation implements
|
||||
AdminService {
|
||||
public AdminServiceImpl(final SchemaFactory<ReviewDb> rdf) {
|
||||
super(rdf);
|
||||
}
|
||||
|
||||
public void groupDetail(final AccountGroup.Id groupId,
|
||||
final AsyncCallback<AccountGroupDetail> callback) {
|
||||
run(callback, new Action<AccountGroupDetail>() {
|
||||
public AccountGroupDetail run(ReviewDb db) throws OrmException, Failure {
|
||||
assertAmGroupOwner(db, groupId);
|
||||
final AccountGroup group = db.accountGroups().get(groupId);
|
||||
if (group == null) {
|
||||
throw new Failure(new NoSuchEntityException());
|
||||
}
|
||||
|
||||
final AccountGroupDetail d = new AccountGroupDetail();
|
||||
d.load(db, new AccountInfoCacheFactory(db), group);
|
||||
return d;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void changeGroupDescription(final AccountGroup.Id groupId,
|
||||
final String description, final AsyncCallback<VoidResult> callback) {
|
||||
run(callback, new Action<VoidResult>() {
|
||||
public VoidResult run(final ReviewDb db) throws OrmException, Failure {
|
||||
assertAmGroupOwner(db, groupId);
|
||||
final AccountGroup group = db.accountGroups().get(groupId);
|
||||
if (group == null) {
|
||||
throw new Failure(new NoSuchEntityException());
|
||||
}
|
||||
group.setDescription(description);
|
||||
db.accountGroups().update(Collections.singleton(group));
|
||||
return VoidResult.INSTANCE;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void addGroupMember(final AccountGroup.Id groupId,
|
||||
final String nameOrEmail, final AsyncCallback<AccountGroupDetail> callback) {
|
||||
run(callback, new Action<AccountGroupDetail>() {
|
||||
public AccountGroupDetail run(ReviewDb db) throws OrmException, Failure {
|
||||
assertAmGroupOwner(db, groupId);
|
||||
final Account a = findAccount(db, nameOrEmail);
|
||||
final AccountGroupMember.Key key =
|
||||
new AccountGroupMember.Key(a.getId(), groupId);
|
||||
if (db.accountGroupMembers().get(key) != null) {
|
||||
return new AccountGroupDetail();
|
||||
}
|
||||
|
||||
final AccountGroupMember m = new AccountGroupMember(key);
|
||||
db.accountGroupMembers().insert(Collections.singleton(m));
|
||||
final AccountGroupDetail d = new AccountGroupDetail();
|
||||
d.loadOneMember(db, a, m);
|
||||
return d;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void deleteGroupMembers(final Set<AccountGroupMember.Key> keys,
|
||||
final AsyncCallback<VoidResult> callback) {
|
||||
run(callback, new Action<VoidResult>() {
|
||||
public VoidResult run(final ReviewDb db) throws OrmException, Failure {
|
||||
final Set<AccountGroup.Id> owned = myOwnedGroups(db);
|
||||
Boolean amAdmin = null;
|
||||
for (final AccountGroupMember.Key k : keys) {
|
||||
if (!owned.contains(k.getAccountGroupId())) {
|
||||
if (amAdmin == null) {
|
||||
amAdmin = amAdmin(db);
|
||||
}
|
||||
if (!amAdmin) {
|
||||
throw new Failure(new NoSuchEntityException());
|
||||
}
|
||||
}
|
||||
}
|
||||
for (final AccountGroupMember.Key k : keys) {
|
||||
final AccountGroupMember m = db.accountGroupMembers().get(k);
|
||||
if (m != null) {
|
||||
db.accountGroupMembers().delete(Collections.singleton(m));
|
||||
}
|
||||
}
|
||||
return VoidResult.INSTANCE;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void changeGroupOwner(final AccountGroupMember.Key key,
|
||||
final boolean owner, final AsyncCallback<VoidResult> callback) {
|
||||
run(callback, new Action<VoidResult>() {
|
||||
public VoidResult run(final ReviewDb db) throws OrmException, Failure {
|
||||
assertAmGroupOwner(db, key.getAccountGroupId());
|
||||
final AccountGroupMember m = db.accountGroupMembers().get(key);
|
||||
if (m == null) {
|
||||
throw new Failure(new NoSuchEntityException());
|
||||
}
|
||||
if (m.isGroupOwner() != owner) {
|
||||
m.setGroupOwner(owner);
|
||||
db.accountGroupMembers().update(Collections.singleton(m));
|
||||
}
|
||||
return VoidResult.INSTANCE;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static boolean amAdmin(final ReviewDb db) throws OrmException {
|
||||
final AccountGroup admin =
|
||||
db.accountGroups().get(new AccountGroup.NameKey("admin"));
|
||||
if (admin == null) {
|
||||
return false;
|
||||
}
|
||||
return db.accountGroupMembers().get(
|
||||
new AccountGroupMember.Key(RpcUtil.getAccountId(), admin.getId())) != null;
|
||||
}
|
||||
|
||||
private static void assertAmGroupOwner(final ReviewDb db,
|
||||
final AccountGroup.Id groupId) throws OrmException, Failure {
|
||||
final AccountGroupMember m =
|
||||
db.accountGroupMembers().get(
|
||||
new AccountGroupMember.Key(RpcUtil.getAccountId(), groupId));
|
||||
if ((m == null || !m.isGroupOwner()) && !amAdmin(db)) {
|
||||
throw new Failure(new NoSuchEntityException());
|
||||
}
|
||||
}
|
||||
|
||||
private static Set<AccountGroup.Id> myOwnedGroups(final ReviewDb db)
|
||||
throws OrmException {
|
||||
final HashSet<AccountGroup.Id> r = new HashSet<AccountGroup.Id>();
|
||||
for (final AccountGroupMember m : db.accountGroupMembers().owned(
|
||||
RpcUtil.getAccountId())) {
|
||||
r.add(m.getAccountGroupId());
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
private static Account findAccount(final ReviewDb db, final String nameOrEmail)
|
||||
throws OrmException, Failure {
|
||||
final Account r = Account.find(db, nameOrEmail);
|
||||
if (r == null) {
|
||||
throw new Failure(new NoSuchEntityException());
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
// Copyright 2008 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.client.admin;
|
||||
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwtjsonrpc.client.JsonUtil;
|
||||
|
||||
public class Util {
|
||||
public static final AdminConstants C = GWT.create(AdminConstants.class);
|
||||
public static final AdminMessages M = GWT.create(AdminMessages.class);
|
||||
public static final AdminService ADMIN_SVC;
|
||||
|
||||
static {
|
||||
ADMIN_SVC = GWT.create(AdminService.class);
|
||||
JsonUtil.bind(ADMIN_SVC, "rpc/AdminService");
|
||||
}
|
||||
}
|
@@ -70,4 +70,10 @@ public class AccountInfoCache {
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/** Merge the information from another cache into this one. */
|
||||
public void merge(final AccountInfoCache other) {
|
||||
assert this != EMPTY;
|
||||
accounts.putAll(other.accounts);
|
||||
}
|
||||
}
|
||||
|
@@ -89,6 +89,12 @@ public class AccountInfoCacheFactory {
|
||||
return a;
|
||||
}
|
||||
|
||||
/** Remember one account that was previously loaded. */
|
||||
public void put(final Account a) {
|
||||
toFetch.remove(a.getId());
|
||||
cache.put(a.getId(), a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an AccountInfoCache with the currently loaded Account entities.
|
||||
* <p>
|
||||
|
@@ -16,8 +16,11 @@ package com.google.gerrit.client.reviewdb;
|
||||
|
||||
import com.google.gwtorm.client.Column;
|
||||
import com.google.gwtorm.client.IntKey;
|
||||
import com.google.gwtorm.client.OrmException;
|
||||
import com.google.gwtorm.client.ResultSet;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.List;
|
||||
|
||||
/** Preferences and information about a single user. */
|
||||
public final class Account {
|
||||
@@ -27,6 +30,37 @@ public final class Account {
|
||||
/** Typical valid choices for the default context setting. */
|
||||
public static final short[] CONTEXT_CHOICES = {3, 10, 25, 50, 75, 100};
|
||||
|
||||
/**
|
||||
* Locate exactly one account matching the name or name/email string.
|
||||
*
|
||||
* @param db open database handle to use for the query.
|
||||
* @param nameOrEmail a string of the format
|
||||
* "Full Name <email@example>", or just the preferred email
|
||||
* address ("email@example"), or a full name.
|
||||
* @return the single account that matches; null if no account matches or
|
||||
* there are multiple candidates.
|
||||
*/
|
||||
public static Account find(final ReviewDb db, final String nameOrEmail)
|
||||
throws OrmException {
|
||||
final int lt = nameOrEmail.indexOf('<');
|
||||
final int gt = nameOrEmail.indexOf('>');
|
||||
if (lt >= 0 && gt > lt) {
|
||||
final String email = nameOrEmail.substring(lt + 1, gt);
|
||||
return one(db.accounts().byPreferredEmail(email));
|
||||
}
|
||||
|
||||
if (nameOrEmail.contains("@")) {
|
||||
return one(db.accounts().byPreferredEmail(nameOrEmail));
|
||||
}
|
||||
|
||||
return one(db.accounts().suggestByFullName(nameOrEmail, nameOrEmail, 2));
|
||||
}
|
||||
|
||||
private static Account one(final ResultSet<Account> rs) {
|
||||
final List<Account> r = rs.toList();
|
||||
return r.size() == 1 ? r.get(0) : null;
|
||||
}
|
||||
|
||||
/** Key local to Gerrit to identify a user. */
|
||||
public static class Id extends IntKey<com.google.gwtorm.client.Key<?>> {
|
||||
@Column
|
||||
|
@@ -28,4 +28,12 @@ public interface AccountAccess extends Access<Account, Account.Id> {
|
||||
|
||||
@Query("WHERE preferredEmail = ? LIMIT 2")
|
||||
ResultSet<Account> byPreferredEmail(String email) throws OrmException;
|
||||
|
||||
@Query("WHERE fullName >= ? AND fullName <= ? ORDER BY fullName LIMIT ?")
|
||||
ResultSet<Account> suggestByFullName(String nameA, String nameB, int limit)
|
||||
throws OrmException;
|
||||
|
||||
@Query("WHERE preferredEmail >= ? AND preferredEmail <= ? ORDER BY preferredEmail LIMIT ?")
|
||||
ResultSet<Account> suggestByPreferredEmail(String nameA, String nameB,
|
||||
int limit) throws OrmException;
|
||||
}
|
||||
|
@@ -65,6 +65,13 @@ public final class AccountGroup {
|
||||
protected void set(int newValue) {
|
||||
id = newValue;
|
||||
}
|
||||
|
||||
/** Parse an AccountGroup.Id out of a string representation. */
|
||||
public static Id parse(final String str) {
|
||||
final Id r = new Id();
|
||||
r.fromString(str);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
@Column
|
||||
|
@@ -41,6 +41,10 @@ public final class AccountGroupMember {
|
||||
return accountId;
|
||||
}
|
||||
|
||||
public AccountGroup.Id getAccountGroupId(){
|
||||
return groupId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.google.gwtorm.client.Key<?>[] members() {
|
||||
return new com.google.gwtorm.client.Key<?>[] {groupId};
|
||||
@@ -61,6 +65,10 @@ public final class AccountGroupMember {
|
||||
key = k;
|
||||
}
|
||||
|
||||
public AccountGroupMember.Key getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public Account.Id getAccountId() {
|
||||
return key.accountId;
|
||||
}
|
||||
|
@@ -28,6 +28,9 @@ public interface AccountGroupMemberAccess extends
|
||||
@Query("WHERE key.accountId = ?")
|
||||
ResultSet<AccountGroupMember> byAccount(Account.Id id) throws OrmException;
|
||||
|
||||
@Query("WHERE key.accountId = ? AND owner = 'Y'")
|
||||
ResultSet<AccountGroupMember> owned(Account.Id id) throws OrmException;
|
||||
|
||||
@Query("WHERE key.groupId = ?")
|
||||
ResultSet<AccountGroupMember> byGroup(AccountGroup.Id id) throws OrmException;
|
||||
}
|
||||
|
@@ -0,0 +1,62 @@
|
||||
// Copyright 2008 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.client.ui;
|
||||
|
||||
import com.google.gerrit.client.FormatUtil;
|
||||
import com.google.gerrit.client.RpcStatus;
|
||||
import com.google.gerrit.client.data.AccountInfo;
|
||||
import com.google.gerrit.client.rpc.GerritCallback;
|
||||
import com.google.gwt.user.client.ui.SuggestOracle;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** Suggestion Oracle for Account entities. */
|
||||
public class AccountSuggestOracle extends SuggestOracle {
|
||||
@Override
|
||||
public void requestSuggestions(final Request req, final Callback callback) {
|
||||
RpcStatus.hide(new Runnable() {
|
||||
public void run() {
|
||||
SuggestUtil.SVC.suggestAccount(req.getQuery(), req.getLimit(),
|
||||
new GerritCallback<List<AccountInfo>>() {
|
||||
public void onSuccess(final List<AccountInfo> result) {
|
||||
final ArrayList<AccountSuggestion> r =
|
||||
new ArrayList<AccountSuggestion>(result.size());
|
||||
for (final AccountInfo p : result) {
|
||||
r.add(new AccountSuggestion(p));
|
||||
}
|
||||
callback.onSuggestionsReady(req, new Response(r));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static class AccountSuggestion implements SuggestOracle.Suggestion {
|
||||
private final AccountInfo info;
|
||||
|
||||
AccountSuggestion(final AccountInfo k) {
|
||||
info = k;
|
||||
}
|
||||
|
||||
public String getDisplayString() {
|
||||
return FormatUtil.nameEmail(info);
|
||||
}
|
||||
|
||||
public String getReplacementString() {
|
||||
return FormatUtil.nameEmail(info);
|
||||
}
|
||||
}
|
||||
}
|
@@ -29,6 +29,7 @@ import com.google.gwt.user.client.ui.Image;
|
||||
import com.google.gwt.user.client.ui.KeyboardListener;
|
||||
import com.google.gwt.user.client.ui.KeyboardListenerAdapter;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -36,6 +37,7 @@ import java.util.Map.Entry;
|
||||
|
||||
public abstract class FancyFlexTable<RowItem> extends Composite implements
|
||||
HasFocus {
|
||||
protected static final String S_ACTIVE_ROW = "ActiveRow";
|
||||
protected static final String MY_STYLE = "gerrit-ChangeTable";
|
||||
protected static final String S_ICON_HEADER = "IconHeader";
|
||||
protected static final String S_DATA_HEADER = "DataHeader";
|
||||
@@ -170,9 +172,20 @@ public abstract class FancyFlexTable<RowItem> extends Composite implements
|
||||
}
|
||||
|
||||
protected void movePointerTo(final int newRow) {
|
||||
final CellFormatter fmt = table.getCellFormatter();
|
||||
if (currentRow >= 0) {
|
||||
final int n = table.getCellCount(currentRow);
|
||||
for (int cell = 0; cell < n; cell++) {
|
||||
fmt.removeStyleName(currentRow, cell, S_ACTIVE_ROW);
|
||||
}
|
||||
}
|
||||
if (newRow >= 0) {
|
||||
table.setWidget(newRow, C_ARROW, pointer);
|
||||
table.getCellFormatter().getElement(newRow, C_ARROW).scrollIntoView();
|
||||
final int n = table.getCellCount(newRow);
|
||||
for (int cell = 0; cell < n; cell++) {
|
||||
fmt.addStyleName(newRow, cell, S_ACTIVE_ROW);
|
||||
}
|
||||
fmt.getElement(newRow, C_ARROW).scrollIntoView();
|
||||
} else if (currentRow >= 0) {
|
||||
table.setWidget(currentRow, C_ARROW, null);
|
||||
}
|
||||
|
@@ -14,6 +14,7 @@
|
||||
|
||||
package com.google.gerrit.client.ui;
|
||||
|
||||
import com.google.gerrit.client.data.AccountInfo;
|
||||
import com.google.gerrit.client.reviewdb.Project;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwtjsonrpc.client.AllowCrossSiteRequest;
|
||||
@@ -25,4 +26,8 @@ public interface SuggestService extends RemoteJsonService {
|
||||
@AllowCrossSiteRequest
|
||||
void suggestProjectNameKey(String query, int limit,
|
||||
AsyncCallback<List<Project.NameKey>> callback);
|
||||
|
||||
@AllowCrossSiteRequest
|
||||
void suggestAccount(String query, int limit,
|
||||
AsyncCallback<List<AccountInfo>> callback);
|
||||
}
|
||||
|
@@ -14,6 +14,8 @@
|
||||
|
||||
package com.google.gerrit.client.ui;
|
||||
|
||||
import com.google.gerrit.client.data.AccountInfo;
|
||||
import com.google.gerrit.client.reviewdb.Account;
|
||||
import com.google.gerrit.client.reviewdb.Project;
|
||||
import com.google.gerrit.client.reviewdb.ReviewDb;
|
||||
import com.google.gerrit.client.rpc.BaseServiceImplementation;
|
||||
@@ -47,4 +49,28 @@ public class SuggestServiceImpl extends BaseServiceImplementation implements
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void suggestAccount(final String query, final int limit,
|
||||
final AsyncCallback<List<AccountInfo>> callback) {
|
||||
run(callback, new Action<List<AccountInfo>>() {
|
||||
public List<AccountInfo> run(final ReviewDb db) throws OrmException {
|
||||
final String a = query;
|
||||
final String b = a + "\uffff";
|
||||
final int max = 10;
|
||||
final int n = limit <= 0 ? max : Math.min(limit, max);
|
||||
|
||||
final List<AccountInfo> r = new ArrayList<AccountInfo>();
|
||||
for (final Account p : db.accounts().suggestByFullName(a, b, n)) {
|
||||
r.add(new AccountInfo(p));
|
||||
}
|
||||
if (r.size() < n) {
|
||||
for (final Account p : db.accounts().suggestByPreferredEmail(a, b,
|
||||
n - r.size())) {
|
||||
r.add(new AccountInfo(p));
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,56 @@
|
||||
// Copyright 2008 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.client.ui;
|
||||
|
||||
import com.google.gwt.user.client.ui.FocusWidget;
|
||||
import com.google.gwt.user.client.ui.KeyboardListenerAdapter;
|
||||
import com.google.gwt.user.client.ui.TextBoxBase;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
|
||||
/** Enables an action (e.g. a Button) if the text box is modified. */
|
||||
public class TextSaveButtonListener extends KeyboardListenerAdapter {
|
||||
private final TextBoxBase descText;
|
||||
private final FocusWidget descAction;
|
||||
|
||||
public TextSaveButtonListener(final TextBoxBase text, final FocusWidget action) {
|
||||
descText = text;
|
||||
descAction = action;
|
||||
|
||||
descText.addKeyboardListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onKeyPress(final Widget sender, final char key, final int mod) {
|
||||
if (mod == 0) {
|
||||
switch (key) {
|
||||
case KEY_UP:
|
||||
case KEY_DOWN:
|
||||
case KEY_LEFT:
|
||||
case KEY_RIGHT:
|
||||
case KEY_HOME:
|
||||
case KEY_END:
|
||||
case KEY_PAGEUP:
|
||||
case KEY_PAGEDOWN:
|
||||
case KEY_ALT:
|
||||
case KEY_CTRL:
|
||||
case KEY_SHIFT:
|
||||
break;
|
||||
default:
|
||||
descAction.setEnabled(descText.isEnabled());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -35,10 +35,11 @@
|
||||
}
|
||||
|
||||
.gerrit-InputFieldTypeHint {
|
||||
color: grey;
|
||||
color: grey;
|
||||
}
|
||||
|
||||
.gerrit-SmallHeading {
|
||||
margin-top: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@@ -178,6 +179,10 @@
|
||||
border-bottom: 1px solid #d4e9a9;
|
||||
}
|
||||
|
||||
.gerrit-ChangeTable td.ActiveRow {
|
||||
background: #ecffc0;
|
||||
}
|
||||
|
||||
.gerrit-ChangeTable .C_ID {
|
||||
width: 3.5em;
|
||||
text-align: right;
|
||||
@@ -507,8 +512,8 @@
|
||||
}
|
||||
|
||||
.gerrit-WatchedProjectPanel {
|
||||
margin-top: 10px;
|
||||
padding: 5px 5px 5px 5px;
|
||||
display: table;
|
||||
margin-top: 10px;
|
||||
padding: 5px 5px 5px 5px;
|
||||
display: table;
|
||||
border: 1px solid #B0BDCC;
|
||||
}
|
||||
|
@@ -0,0 +1,25 @@
|
||||
// Copyright 2008 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.server;
|
||||
|
||||
import com.google.gerrit.client.admin.AdminServiceImpl;
|
||||
|
||||
/** Publishes {@link AdminServiceImpl} over JSON. */
|
||||
public class AdminServiceSrv extends GerritJsonServlet {
|
||||
@Override
|
||||
protected Object createServiceHandle() throws Exception {
|
||||
return new AdminServiceImpl(GerritServer.getInstance().getDatabase());
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user