Просмотр исходного кода

Implemented New Repository for personal repositories

tags/v1.2.0
James Moger 11 лет назад
Родитель
Сommit
6662e38a4e

+ 3
- 3
docs/04_releases.mkd Просмотреть файл

@@ -20,10 +20,10 @@ If you are updating from an earlier release AND you have indexed branches with t
#### additions
- Added simple project pages. A project is a subfolder off the *git.repositoriesFolder*.
- Added support for personal repositories. This builds on the simple project pages.
Personal repositories are stored in *git.repositoriesFolder*/*~username*. Each user with personal repositories will have a user page, something like the GitHub profile page. Personal repositories have all the same features as common repositories.
- Added support for personal repositories.
Personal repositories can be created by accounts with the *create* permission and are stored in *git.repositoriesFolder/~username*. Each user with personal repositories will have a user page, something like the GitHub profile page. Personal repositories have all the same features as common repositories, except personal repositories can be renamed by their owner.
- Added support for server-side forking of a repository to a personal repository (issue 137)
In order to fork a repository to a personal clone, the user account must have the *fork* permission **and** the repository must *allow forks*. The clone inherits the access restrictions of its origin. i.e. if Team A has access to the origin repository, then by default Team A also has access to the fork. This is to facilitate collaboration. However, the fork owner may change access to the fork and add/remove users/teams, etc as required.
In order to fork a repository, the user account must have the *fork* permission **and** the repository must *allow forks*. The clone inherits the access restrictions of its origin. i.e. if Team A has access to the origin repository, then by default Team A also has access to the fork. This is to facilitate collaboration. The fork owner may change access to the fork and add/remove users/teams, etc as required __however__ it should be noted that all personal forks will be enumerated in the fork network regardless of access view restrictions. If you really must have an invisible fork, the clone it locally, create a new repository for your invisible fork, and push it back.
- Added support for X-Forwarded-Context for Apache subdomain proxy configurations (issue 135)
- Delete branch feature (issue 121, Github/ajermakovics)
- Added line links to blob view at the expense of zebra striping (issue 130)

+ 4
- 0
src/com/gitblit/ConfigUserService.java Просмотреть файл

@@ -753,6 +753,9 @@ public class ConfigUserService implements IUserService {
if (model.canFork) {
roles.add(Constants.FORK_ROLE);
}
if (model.canCreate) {
roles.add(Constants.CREATE_ROLE);
}
if (model.excludeFromFederation) {
roles.add(Constants.NOT_FEDERATED_ROLE);
}
@@ -862,6 +865,7 @@ public class ConfigUserService implements IUserService {
USER, username, ROLE)));
user.canAdmin = roles.contains(Constants.ADMIN_ROLE);
user.canFork = roles.contains(Constants.FORK_ROLE);
user.canCreate = roles.contains(Constants.CREATE_ROLE);
user.excludeFromFederation = roles.contains(Constants.NOT_FEDERATED_ROLE);
// repository memberships

+ 2
- 0
src/com/gitblit/Constants.java Просмотреть файл

@@ -43,6 +43,8 @@ public class Constants {
public static final String ADMIN_ROLE = "#admin";
public static final String FORK_ROLE = "#fork";
public static final String CREATE_ROLE = "#create";
public static final String NOT_FEDERATED_ROLE = "#notfederated";

+ 5
- 0
src/com/gitblit/FileUserService.java Просмотреть файл

@@ -236,6 +236,8 @@ public class FileUserService extends FileSettings implements IUserService {
model.canAdmin = true;
} else if (role.equalsIgnoreCase(Constants.FORK_ROLE)) {
model.canFork = true;
} else if (role.equalsIgnoreCase(Constants.CREATE_ROLE)) {
model.canCreate = true;
} else if (role.equalsIgnoreCase(Constants.NOT_FEDERATED_ROLE)) {
model.excludeFromFederation = true;
}
@@ -288,6 +290,9 @@ public class FileUserService extends FileSettings implements IUserService {
if (model.canFork) {
roles.add(Constants.FORK_ROLE);
}
if (model.canCreate) {
roles.add(Constants.CREATE_ROLE);
}
if (model.excludeFromFederation) {
roles.add(Constants.NOT_FEDERATED_ROLE);
}

+ 6
- 1
src/com/gitblit/client/EditUserDialog.java Просмотреть файл

@@ -80,6 +80,8 @@ public class EditUserDialog extends JDialog {
private JCheckBox canAdminCheckbox;
private JCheckBox canForkCheckbox;
private JCheckBox canCreateCheckbox;
private JCheckBox notFederatedCheckbox;
@@ -128,7 +130,8 @@ public class EditUserDialog extends JDialog {
displayNameField = new JTextField(anUser.displayName == null ? "" : anUser.displayName, 25);
emailAddressField = new JTextField(anUser.emailAddress == null ? "" : anUser.emailAddress, 25);
canAdminCheckbox = new JCheckBox(Translation.get("gb.canAdminDescription"), anUser.canAdmin);
canForkCheckbox = new JCheckBox(Translation.get("gb.canForkDescription"), anUser.canFork);
canForkCheckbox = new JCheckBox(Translation.get("gb.canForkDescription"), anUser.canFork);
canCreateCheckbox = new JCheckBox(Translation.get("gb.canCreateDescription"), anUser.canCreate);
notFederatedCheckbox = new JCheckBox(
Translation.get("gb.excludeFromFederationDescription"),
anUser.excludeFromFederation);
@@ -149,6 +152,7 @@ public class EditUserDialog extends JDialog {
fieldsPanel.add(newFieldPanel(Translation.get("gb.emailAddress"), emailAddressField));
fieldsPanel.add(newFieldPanel(Translation.get("gb.canAdmin"), canAdminCheckbox));
fieldsPanel.add(newFieldPanel(Translation.get("gb.canFork"), canForkCheckbox));
fieldsPanel.add(newFieldPanel(Translation.get("gb.canCreate"), canCreateCheckbox));
fieldsPanel.add(newFieldPanel(Translation.get("gb.excludeFromFederation"),
notFederatedCheckbox));
@@ -311,6 +315,7 @@ public class EditUserDialog extends JDialog {
user.canAdmin = canAdminCheckbox.isSelected();
user.canFork = canForkCheckbox.isSelected();
user.canCreate = canCreateCheckbox.isSelected();
user.excludeFromFederation = notFederatedCheckbox.isSelected();
user.repositories.clear();

+ 18
- 0
src/com/gitblit/models/UserModel.java Просмотреть файл

@@ -46,6 +46,7 @@ public class UserModel implements Principal, Serializable, Comparable<UserModel>
public String emailAddress;
public boolean canAdmin;
public boolean canFork;
public boolean canCreate;
public boolean excludeFromFederation;
public final Set<String> repositories = new HashSet<String>();
public final Set<TeamModel> teams = new HashSet<TeamModel>();
@@ -174,6 +175,23 @@ public class UserModel implements Principal, Serializable, Comparable<UserModel>
}
return displayName;
}
public String getPersonalPath() {
return "~" + username;
}
@Override
public int hashCode() {
return username.hashCode();
}
@Override
public boolean equals(Object o) {
if (o instanceof UserModel) {
return username.equals(((UserModel) o).username);
}
return false;
}
@Override
public String toString() {

+ 4
- 1
src/com/gitblit/wicket/GitBlitWebApp.properties Просмотреть файл

@@ -337,4 +337,7 @@ gb.noForks = {0} has no forks
gb.forkNotAuthorized = sorry, you are not authorized to fork {0}
gb.forkInProgress = fork in progress
gb.preparingFork = preparing your fork...
gb.isFork = is fork
gb.isFork = is fork
gb.canCreate = can create
gb.canCreateDescription = can create personal repositories
gb.illegalPersonalRepositoryLocation = your personal repository must be located at \"{0}\"

+ 45
- 6
src/com/gitblit/wicket/pages/EditRepositoryPage.java Просмотреть файл

@@ -80,6 +80,19 @@ public class EditRepositoryPage extends RootSubPage {
model.accessRestriction = AccessRestrictionType.fromName(restriction);
String authorization = GitBlit.getString(Keys.git.defaultAuthorizationControl, null);
model.authorizationControl = AuthorizationControl.fromName(authorization);
GitBlitWebSession session = GitBlitWebSession.get();
UserModel user = session.getUser();
if (user != null && user.canCreate && !user.canAdmin) {
// personal create permissions, inject personal repository path
model.name = user.getPersonalPath() + "/";
model.projectPath = user.getPersonalPath();
model.owner = user.username;
// personal repositories are private by default
model.accessRestriction = AccessRestrictionType.VIEW;
model.authorizationControl = AuthorizationControl.NAMED;
}
setupPage(model);
}
@@ -103,8 +116,15 @@ public class EditRepositoryPage extends RootSubPage {
List<String> preReceiveScripts = new ArrayList<String>();
List<String> postReceiveScripts = new ArrayList<String>();
GitBlitWebSession session = GitBlitWebSession.get();
final UserModel user = session.getUser() == null ? UserModel.ANONYMOUS : session.getUser();
if (isCreate) {
super.setupPage(getString("gb.newRepository"), "");
if (user.canAdmin) {
super.setupPage(getString("gb.newRepository"), "");
} else {
super.setupPage(getString("gb.newRepository"), user.getDisplayName());
}
} else {
super.setupPage(getString("gb.edit"), repositoryModel.name);
if (repositoryModel.accessRestriction.exceeds(AccessRestrictionType.NONE)) {
@@ -195,11 +215,14 @@ public class EditRepositoryPage extends RootSubPage {
protected void onSubmit() {
try {
// confirm a repository name was entered
if (StringUtils.isEmpty(repositoryModel.name)) {
if (repositoryModel.name == null && StringUtils.isEmpty(repositoryModel.name)) {
error(getString("gb.pleaseSetRepositoryName"));
return;
}
// ensure name is trimmed
repositoryModel.name = repositoryModel.name.trim();
// automatically convert backslashes to forward slashes
repositoryModel.name = repositoryModel.name.replace('\\', '/');
// Automatically replace // with /
@@ -229,6 +252,22 @@ public class EditRepositoryPage extends RootSubPage {
c));
return;
}
if (user.canCreate && !user.canAdmin) {
// ensure repository name begins with the user's path
if (!repositoryModel.name.startsWith(user.getPersonalPath())) {
error(MessageFormat.format(getString("gb.illegalPersonalRepositoryLocation"),
user.getPersonalPath()));
return;
}
if (repositoryModel.name.equals(user.getPersonalPath())) {
// reset path prefix and show error
repositoryModel.name = user.getPersonalPath() + "/";
error(getString("gb.pleaseSetRepositoryName"));
return;
}
}
// confirm access restriction selection
if (repositoryModel.accessRestriction == null) {
@@ -339,7 +378,7 @@ public class EditRepositoryPage extends RootSubPage {
form.add(new SimpleAttributeModifier("autocomplete", "off"));
// field names reflective match RepositoryModel fields
form.add(new TextField<String>("name").setEnabled(isCreate || isAdmin));
form.add(new TextField<String>("name").setEnabled(isCreate || isAdmin || repositoryModel.isUsersPersonalRepository(user.username)));
form.add(new TextField<String>("description"));
form.add(new DropDownChoice<String>("owner", GitBlit.self().getAllUsernames())
.setEnabled(GitBlitWebSession.get().canAdmin()));
@@ -435,8 +474,8 @@ public class EditRepositoryPage extends RootSubPage {
}
if (isCreate) {
// Create Repository
if (!user.canAdmin) {
// Only Administrators May Create
if (!user.canCreate && !user.canAdmin) {
// Only administrators or permitted users may create
error(getString("gb.errorOnlyAdminMayCreateRepository"), true);
}
} else {

+ 2
- 1
src/com/gitblit/wicket/pages/EditUserPage.html Просмотреть файл

@@ -18,7 +18,8 @@
<tr><th><wicket:message key="gb.emailAddress"></wicket:message></th><td class="edit"><input type="text" wicket:id="emailAddress" size="30" tabindex="5" /></td></tr>
<tr><th><wicket:message key="gb.canAdmin"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="canAdmin" tabindex="6" /> &nbsp;<span class="help-inline"><wicket:message key="gb.canAdminDescription"></wicket:message></span></label></td></tr>
<tr><th><wicket:message key="gb.canFork"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="canFork" tabindex="7" /> &nbsp;<span class="help-inline"><wicket:message key="gb.canForkDescription"></wicket:message></span></label></td></tr>
<tr><th><wicket:message key="gb.excludeFromFederation"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="excludeFromFederation" tabindex="8" /> &nbsp;<span class="help-inline"><wicket:message key="gb.excludeFromFederationDescription"></wicket:message></span></label></td></tr>
<tr><th><wicket:message key="gb.canCreate"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="canCreate" tabindex="8" /> &nbsp;<span class="help-inline"><wicket:message key="gb.canCreateDescription"></wicket:message></span></label></td></tr>
<tr><th><wicket:message key="gb.excludeFromFederation"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="excludeFromFederation" tabindex="9" /> &nbsp;<span class="help-inline"><wicket:message key="gb.excludeFromFederationDescription"></wicket:message></span></label></td></tr>
<tr><td colspan="2" style="padding-top:15px;"><h3><wicket:message key="gb.accessPermissions"></wicket:message> &nbsp;<small><wicket:message key="gb.accessPermissionsForUserDescription"></wicket:message></small></h3></td></tr>
<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>
<tr><td colspan="2"><hr></hr></td></tr>

+ 1
- 0
src/com/gitblit/wicket/pages/EditUserPage.java Просмотреть файл

@@ -232,6 +232,7 @@ public class EditUserPage extends RootSubPage {
form.add(new TextField<String>("emailAddress").setEnabled(editEmailAddress));
form.add(new CheckBox("canAdmin"));
form.add(new CheckBox("canFork"));
form.add(new CheckBox("canCreate"));
form.add(new CheckBox("excludeFromFederation"));
form.add(repositories);
form.add(teams.setEnabled(editTeams));

+ 1
- 1
src/com/gitblit/wicket/pages/RepositoriesPage.java Просмотреть файл

@@ -82,7 +82,7 @@ public class RepositoriesPage extends RootPage {
List<RepositoryModel> repositories = getRepositories(params);
RepositoriesPanel repositoriesPanel = new RepositoriesPanel("repositoriesPanel", showAdmin,
repositories, true, getAccessRestrictions());
true, repositories, true, getAccessRestrictions());
// push the panel down if we are hiding the admin controls and the
// welcome message
if (!showAdmin && !repositoriesMessage.isVisible()) {

+ 1
- 1
src/com/gitblit/wicket/pages/ReviewProposalPage.java Просмотреть файл

@@ -92,7 +92,7 @@ public class ReviewProposalPage extends RootSubPage {
List<RepositoryModel> repositories = new ArrayList<RepositoryModel>(
proposal.repositories.values());
RepositoriesPanel repositoriesPanel = new RepositoriesPanel("repositoriesPanel", false,
repositories, false, getAccessRestrictions());
false, repositories, false, getAccessRestrictions());
add(repositoriesPanel);
}

+ 1
- 1
src/com/gitblit/wicket/pages/SendProposalPage.java Просмотреть файл

@@ -146,7 +146,7 @@ public class SendProposalPage extends RootSubPage {
List<RepositoryModel> repositories = new ArrayList<RepositoryModel>(
proposal.repositories.values());
RepositoriesPanel repositoriesPanel = new RepositoriesPanel("repositoriesPanel", false,
repositories, false, getAccessRestrictions());
false, repositories, false, getAccessRestrictions());
add(repositoriesPanel);
}
}

+ 6
- 0
src/com/gitblit/wicket/pages/UserPage.html Просмотреть файл

@@ -18,6 +18,12 @@
</div>
<div class="span8">
<div class="pull-right">
<a class="btn-small" wicket:id="newRepository" style="padding-right:0px;">
<i class="icon icon-plus-sign"></i>
<wicket:message key="gb.newRepository"></wicket:message>
</a>
</div>
<div class="tabbable">
<!-- tab titles -->
<ul class="nav nav-tabs">

+ 9
- 0
src/com/gitblit/wicket/pages/UserPage.java Просмотреть файл

@@ -22,6 +22,7 @@ import java.util.List;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.DataView;
import org.apache.wicket.markup.repeater.data.ListDataProvider;
@@ -99,6 +100,14 @@ public class UserPage extends RootPage {
PersonIdent person = new PersonIdent(user.getDisplayName(), user.emailAddress);
add(new GravatarImage("gravatar", person, 210));
UserModel sessionUser = GitBlitWebSession.get().getUser();
if (sessionUser != null && user.canCreate && sessionUser.equals(user)) {
// user can create personal repositories
add(new BookmarkablePageLink<Void>("newRepository", EditRepositoryPage.class));
} else {
add(new Label("newRepository").setVisible(false));
}
List<RepositoryModel> repositories = getRepositories(params);
Collections.sort(repositories, new Comparator<RepositoryModel>() {

+ 11
- 1
src/com/gitblit/wicket/panels/RepositoriesPanel.html Просмотреть файл

@@ -7,7 +7,7 @@
<body>
<wicket:panel>
<div wicket:id="adminPanel">[admin links]</div>
<div wicket:id="managementPanel">[management links]</div>
<table class="repositories">
<span wicket:id="headerContent"></span>
@@ -32,6 +32,16 @@
</div>
</wicket:fragment>
<wicket:fragment wicket:id="personalLinks">
<!-- page nav links -->
<div class="admin_nav">
<a class="btn-small" wicket:id="newRepository" style="padding-right:0px;">
<i class="icon icon-plus-sign"></i>
<wicket:message key="gb.newRepository"></wicket:message>
</a>
</div>
</wicket:fragment>
<wicket:fragment wicket:id="repositoryAdminLinks">
<span class="link"><a wicket:id="editRepository"><wicket:message key="gb.edit">[edit]</wicket:message></a> | <a wicket:id="deleteRepository"><wicket:message key="gb.delete">[delete]</wicket:message></a></span>
</wicket:fragment>

+ 25
- 13
src/com/gitblit/wicket/panels/RepositoriesPanel.java Просмотреть файл

@@ -64,7 +64,7 @@ public class RepositoriesPanel extends BasePanel {
private static final long serialVersionUID = 1L;
public RepositoriesPanel(String wicketId, final boolean showAdmin,
public RepositoriesPanel(String wicketId, final boolean showAdmin, final boolean showManagement,
List<RepositoryModel> models, boolean enableLinks,
final Map<AccessRestrictionType, String> accessRestrictionTranslations) {
super(wicketId);
@@ -76,19 +76,31 @@ public class RepositoriesPanel extends BasePanel {
final IDataProvider<RepositoryModel> dp;
Fragment adminLinks = new Fragment("adminPanel", "adminLinks", this);
adminLinks.add(new Link<Void>("clearCache") {
Fragment managementLinks;
if (showAdmin) {
// user is admin
managementLinks = new Fragment("managementPanel", "adminLinks", this);
managementLinks.add(new Link<Void>("clearCache") {
private static final long serialVersionUID = 1L;
private static final long serialVersionUID = 1L;
@Override
public void onClick() {
GitBlit.self().resetRepositoryListCache();
setResponsePage(RepositoriesPage.class);
}
}.setVisible(GitBlit.getBoolean(Keys.git.cacheRepositoryList, true)));
adminLinks.add(new BookmarkablePageLink<Void>("newRepository", EditRepositoryPage.class));
add(adminLinks.setVisible(showAdmin));
@Override
public void onClick() {
GitBlit.self().resetRepositoryListCache();
setResponsePage(RepositoriesPage.class);
}
}.setVisible(GitBlit.getBoolean(Keys.git.cacheRepositoryList, true)));
managementLinks.add(new BookmarkablePageLink<Void>("newRepository", EditRepositoryPage.class));
add(managementLinks);
} else if (showManagement && user != null && user.canCreate) {
// user can create personal repositories
managementLinks = new Fragment("managementPanel", "personalLinks", this);
managementLinks.add(new BookmarkablePageLink<Void>("newRepository", EditRepositoryPage.class));
add(managementLinks);
} else {
// user has no management permissions
add (new Label("managementPanel").setVisible(false));
}
if (GitBlit.getString(Keys.web.repositoryListType, "flat").equalsIgnoreCase("grouped")) {
List<RepositoryModel> rootRepositories = new ArrayList<RepositoryModel>();
@@ -297,7 +309,7 @@ public class RepositoriesPanel extends BasePanel {
row.add(lastChangeLabel);
WicketUtils.setCssClass(lastChangeLabel, getTimeUtils().timeAgoCss(entry.lastChange));
boolean showOwner = user != null && user.username.equalsIgnoreCase(entry.owner);
boolean showOwner = user != null && entry.isOwner(user.username);
boolean myPersonalRepository = showOwner && entry.isUsersPersonalRepository(user.username);
if (showAdmin || myPersonalRepository) {
Fragment repositoryLinks = new Fragment("repositoryLinks",

Загрузка…
Отмена
Сохранить