Apply states for projects (active, readonly and hidden)

This feature is the result of the discussion
about issue 349 which the project can assume
one of the states mentioned above.

Active state indicates the project is regular
and is the default value.

Read Only means that users can see the project
if read permission is granted, but all modification
operations are disabled.

Hidden means the project is not visible for
those who are not owners

Bug: issue 349
Change-Id: I2a8972701359c3792331712efc93cb6ded2065f5
This commit is contained in:
carloseduardo.baldacin
2011-07-13 11:27:14 -03:00
committed by Gustaf Lundh
parent a3b3e95ae7
commit acbf134d54
14 changed files with 148 additions and 26 deletions

View File

@@ -41,6 +41,7 @@ public class ChangeDetail {
protected List<ChangeMessage> messages;
protected PatchSet.Id currentPatchSetId;
protected PatchSetDetail currentDetail;
protected boolean canEdit;
public ChangeDetail() {
}
@@ -190,4 +191,12 @@ public class ChangeDetail {
public String getDescription() {
return currentDetail != null ? currentDetail.getInfo().getMessage() : "";
}
public void setCanEdit(boolean a) {
canEdit = a;
}
public boolean canEdit() {
return canEdit;
}
}

View File

@@ -22,6 +22,7 @@ public class ProjectDetail {
public boolean canModifyMergeType;
public boolean canModifyAgreements;
public boolean canModifyAccess;
public boolean canModifyState;
public ProjectDetail() {
}
@@ -38,6 +39,10 @@ public class ProjectDetail {
canModifyMergeType = cmmt;
}
public void setCanModifyState(final boolean cms) {
canModifyState = cms;
}
public void setCanModifyAgreements(final boolean cma) {
canModifyAgreements = cma;
}

View File

@@ -63,6 +63,10 @@ public interface AdminConstants extends Constants {
String projectSubmitType_MERGE_IF_NECESSARY();
String projectSubmitType_CHERRY_PICK();
String projectState_ACTIVE();
String projectState_READ_ONLY();
String projectState_HIDDEN();
String groupType_SYSTEM();
String groupType_INTERNAL();
String groupType_LDAP();

View File

@@ -43,6 +43,10 @@ projectSubmitType_MERGE_IF_NECESSARY = Merge If Necessary
projectSubmitType_MERGE_ALWAYS = Always Merge
projectSubmitType_CHERRY_PICK = Cherry Pick
projectState_ACTIVE = Active
projectState_READ_ONLY = Read Only
projectState_HIDDEN = Hidden
groupType_SYSTEM = System Group
groupType_INTERNAL = Internal Group
groupType_LDAP = LDAP Group

View File

@@ -39,6 +39,7 @@ public class ProjectInfoScreen extends ProjectScreen {
private Panel projectOptionsPanel;
private CheckBox requireChangeID;
private ListBox submitType;
private ListBox state;
private CheckBox useContentMerge;
private Panel agreementsPanel;
@@ -79,19 +80,22 @@ public class ProjectInfoScreen extends ProjectScreen {
new ScreenLoadCallback<ProjectDetail>(this) {
public void preDisplay(final ProjectDetail result) {
enableForm(result.canModifyAgreements,
result.canModifyDescription, result.canModifyMergeType);
result.canModifyDescription, result.canModifyMergeType, result.canModifyState);
saveProject.setVisible(
result.canModifyAgreements ||
result.canModifyDescription ||
result.canModifyMergeType);
result.canModifyMergeType ||
result.canModifyState);
display(result);
}
});
}
private void enableForm(final boolean canModifyAgreements,
final boolean canModifyDescription, final boolean canModifyMergeType) {
final boolean canModifyDescription, final boolean canModifyMergeType,
final boolean canModifyState) {
submitType.setEnabled(canModifyMergeType);
state.setEnabled(canModifyState);
useContentMerge.setEnabled(canModifyMergeType);
descTxt.setEnabled(canModifyDescription);
useContributorAgreements.setEnabled(canModifyAgreements);
@@ -130,6 +134,14 @@ public class ProjectInfoScreen extends ProjectScreen {
saveEnabler.listenTo(submitType);
projectOptionsPanel.add(submitType);
state = new ListBox();
for (final Project.State stateValue : Project.State.values()) {
state.addItem(Util.toLongString(stateValue), stateValue.name());
}
saveEnabler.listenTo(state);
projectOptionsPanel.add(state);
useContentMerge = new CheckBox(Util.C.useContentMerge(), true);
saveEnabler.listenTo(useContentMerge);
projectOptionsPanel.add(useContentMerge);
@@ -186,6 +198,17 @@ public class ProjectInfoScreen extends ProjectScreen {
}
}
private void setState(final Project.State newState) {
if (state != null) {
for (int i = 0; i < state.getItemCount(); i++) {
if (newState.name().equals(state.getValue(i))) {
state.setSelectedIndex(i);
break;
}
}
}
}
void display(final ProjectDetail result) {
project = result.project;
@@ -202,6 +225,7 @@ public class ProjectInfoScreen extends ProjectScreen {
useContentMerge.setValue(project.isUseContentMerge());
requireChangeID.setValue(project.isRequireChangeID());
setSubmitType(project.getSubmitType());
setState(project.getState());
saveProject.setEnabled(false);
}
@@ -216,14 +240,18 @@ public class ProjectInfoScreen extends ProjectScreen {
project.setSubmitType(Project.SubmitType.valueOf(submitType
.getValue(submitType.getSelectedIndex())));
}
if (state.getSelectedIndex() >= 0) {
project.setState(Project.State.valueOf(state
.getValue(state.getSelectedIndex())));
}
enableForm(false, false, false);
enableForm(false, false, false, false);
Util.PROJECT_SVC.changeProjectSettings(project,
new GerritCallback<ProjectDetail>() {
public void onSuccess(final ProjectDetail result) {
enableForm(result.canModifyAgreements,
result.canModifyDescription, result.canModifyMergeType);
result.canModifyDescription, result.canModifyMergeType, result.canModifyState);
display(result);
}
});

View File

@@ -53,4 +53,20 @@ public class Util {
return type.name();
}
}
public static String toLongString(final Project.State type) {
if (type == null) {
return "";
}
switch (type) {
case ACTIVE:
return C.projectState_ACTIVE();
case READ_ONLY:
return C.projectState_READ_ONLY();
case HIDDEN:
return C.projectState_HIDDEN();
default:
return type.name();
}
}
}

View File

@@ -164,9 +164,11 @@ class PatchSetComplexDisclosurePanel extends ComplexDisclosurePanel implements O
actionsPanel.setStyleName(Gerrit.RESOURCES.css().patchSetActions());
body.add(actionsPanel);
if (Gerrit.isSignedIn()) {
populateReviewAction();
if (changeDetail.isCurrentPatchSet(detail)) {
populateActions(detail);
if (changeDetail.canEdit()) {
populateReviewAction();
if (changeDetail.isCurrentPatchSet(detail)) {
populateActions(detail);
}
}
}
populateDiffAllActions(detail);

View File

@@ -125,6 +125,8 @@ public class ChangeDetailFactory extends Handler<ChangeDetail> {
detail.setCanRevert(change.getStatus() == Change.Status.MERGED && control.canAddPatchSet());
detail.setCanEdit(control.getRefControl().canWrite());
if (detail.getChange().getStatus().isOpen()) {
List<SubmitRecord> submitRecords = control.canSubmit(db, patch.getId());
for (SubmitRecord rec : submitRecords) {

View File

@@ -57,6 +57,7 @@ class ProjectDetailFactory extends Handler<ProjectDetail> {
detail.setCanModifyAgreements(userIsOwner);
detail.setCanModifyDescription(userIsOwner);
detail.setCanModifyMergeType(userIsOwner);
detail.setCanModifyState(userIsOwner);
return detail;
}
}

View File

@@ -75,6 +75,14 @@ public final class Project {
CHERRY_PICK;
}
public static enum State {
ACTIVE,
READ_ONLY,
HIDDEN;
}
protected NameKey name;
protected String description;
@@ -85,6 +93,8 @@ public final class Project {
protected SubmitType submitType;
protected State state;
protected NameKey parent;
protected boolean requireChangeID;
@@ -97,6 +107,7 @@ public final class Project {
public Project(Project.NameKey nameKey) {
name = nameKey;
submitType = SubmitType.MERGE_IF_NECESSARY;
state = State.ACTIVE;
}
public Project.NameKey getNameKey() {
@@ -155,6 +166,14 @@ public final class Project {
submitType = type;
}
public State getState() {
return state;
}
public void setState(final State newState) {
state = newState;
}
public void copySettingsFrom(final Project update) {
description = update.description;
useContributorAgreements = update.useContributorAgreements;
@@ -162,6 +181,7 @@ public final class Project {
useContentMerge = update.useContentMerge;
requireChangeID = update.requireChangeID;
submitType = update.submitType;
state = update.state;
}
public Project.NameKey getParent() {

View File

@@ -24,6 +24,7 @@ import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.reviewdb.AccountGroup;
import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.reviewdb.Project.State;
import com.google.gerrit.reviewdb.Project.SubmitType;
import com.google.gerrit.server.account.GroupCache;
@@ -66,9 +67,12 @@ public class ProjectConfig extends VersionedMetaData {
private static final String SUBMIT = "submit";
private static final String KEY_ACTION = "action";
private static final String KEY_MERGE_CONTENT = "mergeContent";
private static final String KEY_STATE = "state";
private static final SubmitType defaultSubmitAction =
SubmitType.MERGE_IF_NECESSARY;
private static final State defaultStateValue =
State.ACTIVE;
private Project.NameKey projectName;
private Project project;
@@ -217,6 +221,7 @@ public class ProjectConfig extends VersionedMetaData {
p.setSubmitType(getEnum(rc, SUBMIT, null, KEY_ACTION, defaultSubmitAction));
p.setUseContentMerge(getBoolean(rc, SUBMIT, KEY_MERGE_CONTENT, false));
p.setState(getEnum(rc, PROJECT, null, KEY_STATE, defaultStateValue));
accessSections = new HashMap<String, AccessSection>();
for (String refName : rc.getSubsections(ACCESS)) {
@@ -341,6 +346,8 @@ public class ProjectConfig extends VersionedMetaData {
set(rc, SUBMIT, null, KEY_ACTION, p.getSubmitType(), defaultSubmitAction);
set(rc, SUBMIT, null, KEY_MERGE_CONTENT, p.isUseContentMerge());
set(rc, PROJECT, null, KEY_STATE, p.getState(), null);
Set<AccountGroup.UUID> keepGroups = new HashSet<AccountGroup.UUID>();
AccessSection capability = accessSections.get(AccessSection.GLOBAL_CAPABILITIES);
if (capability != null) {

View File

@@ -192,10 +192,14 @@ public class ProjectControl {
return state.getProject();
}
private boolean isHidden() {
return getProject().getState().equals(Project.State.HIDDEN);
}
/** Can this user see this project exists? */
public boolean isVisible() {
return visibleForReplication()
|| canPerformOnAnyRef(Permission.READ);
return (visibleForReplication()
|| canPerformOnAnyRef(Permission.READ)) && !isHidden();
}
public boolean canAddRefs() {

View File

@@ -18,6 +18,7 @@ import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRange;
import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -97,8 +98,8 @@ public class RefControl {
/** Can this user see this reference exists? */
public boolean isVisible() {
return projectControl.visibleForReplication()
|| canPerform(Permission.READ);
return (projectControl.visibleForReplication() || canPerform(Permission.READ))
&& canRead();
}
/**
@@ -109,16 +110,16 @@ public class RefControl {
* ref
*/
public boolean canUpload() {
return projectControl
.controlForRef("refs/for/" + getRefName())
.canPerform(Permission.PUSH);
return projectControl.controlForRef("refs/for/" + getRefName())
.canPerform(Permission.PUSH)
&& canWrite();
}
/** @return true if this user can submit merge patch sets to this ref */
public boolean canUploadMerges() {
return projectControl
.controlForRef("refs/for/" + getRefName())
.canPerform(Permission.PUSH_MERGE);
return projectControl.controlForRef("refs/for/" + getRefName())
.canPerform(Permission.PUSH_MERGE)
&& canWrite();
}
/** @return true if this user can submit patch sets to this ref */
@@ -131,7 +132,8 @@ public class RefControl {
// granting of powers beyond submitting to the configuration.
return projectControl.isOwner();
}
return canPerform(Permission.SUBMIT);
return canPerform(Permission.SUBMIT)
&& canWrite();
}
/** @return true if the user can update the reference as a fast-forward. */
@@ -145,17 +147,28 @@ public class RefControl {
// granting of powers beyond pushing to the configuration.
return false;
}
return canPerform(Permission.PUSH);
return canPerform(Permission.PUSH)
&& canWrite();
}
/** @return true if the user can rewind (force push) the reference. */
public boolean canForceUpdate() {
return canPushWithForce() || canDelete();
return (canPushWithForce() || canDelete()) && canWrite();
}
public boolean canWrite() {
return getProjectControl().getProject().getState().equals(
Project.State.ACTIVE);
}
public boolean canRead() {
return getProjectControl().getProject().getState().equals(
Project.State.READ_ONLY) || canWrite();
}
private boolean canPushWithForce() {
if (GitRepositoryManager.REF_CONFIG.equals(refName)
&& !projectControl.isOwner()) {
if (!canWrite() || (GitRepositoryManager.REF_CONFIG.equals(refName)
&& !projectControl.isOwner())) {
// Pushing requires being at least project owner, in addition to push.
// Pushing configuration changes modifies the access control
// rules. Allowing this to be done by a non-project-owner opens
@@ -183,6 +196,9 @@ public class RefControl {
* @return {@code true} if the user specified can create a new Git ref
*/
public boolean canCreate(RevWalk rw, RevObject object) {
if (!canWrite()) {
return false;
}
boolean owner;
switch (getCurrentUser().getAccessPath()) {
case WEB_UI:
@@ -242,7 +258,7 @@ public class RefControl {
* @return {@code true} if the user specified can delete a Git ref.
*/
public boolean canDelete() {
if (GitRepositoryManager.REF_CONFIG.equals(refName)) {
if (!canWrite() || (GitRepositoryManager.REF_CONFIG.equals(refName))) {
// Never allow removal of the refs/meta/config branch.
// Deleting the branch would destroy all Gerrit specific
// metadata about the project, including its access rules.

View File

@@ -113,7 +113,9 @@ public class ProjectConfigTest extends LocalDiskRepositoryTestCase {
+ " submit = group Developers\n" //
+ "\tsubmit = group Staff\n" //
+ " upload = group Developers\n" //
+ " read = group Developers\n", text(rev, "project.config"));
+ " read = group Developers\n"//
+ "[project]\n"//
+ "\tstate = active\n", text(rev, "project.config"));
}
@Test
@@ -140,7 +142,9 @@ public class ProjectConfigTest extends LocalDiskRepositoryTestCase {
+ " submit = group People Who Can Submit\n" //
+ "\tsubmit = group Staff\n" //
+ " upload = group Developers\n" //
+ " read = group Developers\n", text(rev, "project.config"));
+ " read = group Developers\n"//
+ "[project]\n"//
+ "\tstate = active\n", text(rev, "project.config"));
}
private ProjectConfig read(RevCommit rev) throws IOException,