@@ -10,7 +10,8 @@ r24: { | |||
text: ~ | |||
security: ~ | |||
fixes: ~ | |||
changes: ~ | |||
changes: | |||
- Move repository deletion functions to the edit repository page AND allow deletion to be disabled (pr-180, ticket-67) | |||
additions: | |||
- Add FORK_REPOSITORY RPC request type (issue-371, pr-161, ticket-65) | |||
- Add object type (ot) parameter for RSS queries to retrieve tag details (pr-165, ticket-66) | |||
@@ -18,6 +19,8 @@ r24: { | |||
contributors: | |||
- Manisha Gayathri | |||
- Gerard Smyth | |||
settings: | |||
- { name: 'web.allowDeletingNonEmptyRepositories', defaultValue: 'true' } | |||
} | |||
# |
@@ -766,7 +766,7 @@ web.authenticateAdminPages = true | |||
# SINCE 0.5.0 | |||
web.allowCookieAuthentication = true | |||
# Allow deleting of non empty repositories through the user interface. | |||
# Allow deletion of non-empty repositories. This is enforced for all delete vectors. | |||
# | |||
# SINCE 1.6.0 | |||
web.allowDeletingNonEmptyRepositories = true |
@@ -1014,6 +1014,11 @@ public class GitblitManager implements IGitblit { | |||
repositoryManager.updateConfiguration(r, repository); | |||
} | |||
@Override | |||
public boolean canDelete(RepositoryModel model) { | |||
return repositoryManager.canDelete(model); | |||
} | |||
@Override | |||
public boolean deleteRepositoryModel(RepositoryModel model) { | |||
return repositoryManager.deleteRepositoryModel(model); |
@@ -338,6 +338,15 @@ public interface IRepositoryManager extends IManager { | |||
*/ | |||
void updateConfiguration(Repository r, RepositoryModel repository); | |||
/** | |||
* Returns true if the repository can be deleted. | |||
* | |||
* @param model | |||
* @return true if the repository can be deleted | |||
* @since 1.6.0 | |||
*/ | |||
boolean canDelete(RepositoryModel model); | |||
/** | |||
* Deletes the repository from the file system and removes the repository | |||
* permission from all repository users. |
@@ -1532,6 +1532,17 @@ public class RepositoryManager implements IRepositoryManager { | |||
} | |||
} | |||
/** | |||
* Returns true if the repository can be deleted. | |||
* | |||
* @return true if the repository can be deleted | |||
*/ | |||
@Override | |||
public boolean canDelete(RepositoryModel repository) { | |||
return settings.getBoolean(Keys.web.allowDeletingNonEmptyRepositories, true) | |||
|| !repository.hasCommits; | |||
} | |||
/** | |||
* Deletes the repository from the file system and removes the repository | |||
* permission from all repository users. | |||
@@ -1553,6 +1564,12 @@ public class RepositoryManager implements IRepositoryManager { | |||
*/ | |||
@Override | |||
public boolean deleteRepository(String repositoryName) { | |||
RepositoryModel repository = getRepositoryModel(repositoryName); | |||
if (!canDelete(repository)) { | |||
logger.warn("Attempt to delete {} rejected!", repositoryName); | |||
return false; | |||
} | |||
try { | |||
close(repositoryName); | |||
// clear the repository cache |
@@ -112,7 +112,7 @@ | |||
<div class="row"> | |||
<div class="span12"> | |||
<div class="form-actions"><input class="btn btn-appmenu" type="submit" value="Save" wicket:message="value:gb.save" wicket:id="save" /> <input class="btn" type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" /></div> | |||
<div class="form-actions"><input class="btn btn-appmenu" type="submit" value="Save" wicket:message="value:gb.save" wicket:id="save" /> <input class="btn" type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" /> <input class="btn btn-danger" type="submit" value="Delete" wicket:message="value:gb.delete" wicket:id="delete" /></div> | |||
</div> | |||
</div> | |||
@@ -42,6 +42,7 @@ import org.apache.wicket.markup.html.form.Form; | |||
import org.apache.wicket.markup.html.form.IChoiceRenderer; | |||
import org.apache.wicket.markup.html.form.RadioChoice; | |||
import org.apache.wicket.markup.html.form.TextField; | |||
import org.apache.wicket.markup.html.link.Link; | |||
import org.apache.wicket.markup.html.list.ListItem; | |||
import org.apache.wicket.markup.html.list.ListView; | |||
import org.apache.wicket.model.CompoundPropertyModel; | |||
@@ -68,6 +69,7 @@ import com.gitblit.utils.StringUtils; | |||
import com.gitblit.wicket.GitBlitWebSession; | |||
import com.gitblit.wicket.StringChoiceRenderer; | |||
import com.gitblit.wicket.WicketUtils; | |||
import com.gitblit.wicket.panels.BasePanel.JavascriptEventConfirmation; | |||
import com.gitblit.wicket.panels.BulletListPanel; | |||
import com.gitblit.wicket.panels.RegistrantPermissionsPanel; | |||
@@ -614,6 +616,38 @@ public class EditRepositoryPage extends RootSubPage { | |||
cancel.setDefaultFormProcessing(false); | |||
form.add(cancel); | |||
// the user can delete if deletions are allowed AND the user is an admin or the personal owner | |||
// assigned ownership is not sufficient to allow deletion | |||
boolean canDelete = !isCreate && app().repositories().canDelete(repositoryModel) | |||
&& (user.canAdmin() || user.isMyPersonalRepository(repositoryModel.name)); | |||
Link<Void> delete = new Link<Void>("delete") { | |||
private static final long serialVersionUID = 1L; | |||
@Override | |||
public void onClick() { | |||
RepositoryModel latestModel = app().repositories().getRepositoryModel(repositoryModel.name); | |||
boolean canDelete = app().repositories().canDelete(latestModel); | |||
if (canDelete) { | |||
if (app().repositories().deleteRepositoryModel(latestModel)) { | |||
info(MessageFormat.format(getString("gb.repositoryDeleted"), latestModel)); | |||
setResponsePage(RepositoriesPage.class); | |||
} else { | |||
error(MessageFormat.format(getString("gb.repositoryDeleteFailed"), latestModel)); | |||
} | |||
} else { | |||
error(MessageFormat.format(getString("gb.repositoryDeleteFailed"), latestModel)); | |||
} | |||
} | |||
}; | |||
if (canDelete) { | |||
delete.add(new JavascriptEventConfirmation("onclick", MessageFormat.format( | |||
getString("gb.deleteRepository"), repositoryModel))); | |||
} | |||
form.add(delete.setVisible(canDelete)); | |||
add(form); | |||
} | |||
@@ -5,15 +5,6 @@ | |||
lang="en"> | |||
<wicket:panel> | |||
<wicket:fragment wicket:id="repositoryAdminLinks"> | |||
<span class="link"> | |||
<a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a> | |||
| <a wicket:id="log"><wicket:message key="gb.log"></wicket:message></a> | |||
| <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> | |||
<wicket:fragment wicket:id="repositoryOwnerLinks"> | |||
<span class="link"> | |||
<a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a> |
@@ -15,7 +15,6 @@ | |||
*/ | |||
package com.gitblit.wicket.panels; | |||
import java.text.MessageFormat; | |||
import java.util.Map; | |||
import org.apache.wicket.Component; | |||
@@ -24,7 +23,6 @@ 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.html.link.ExternalLink; | |||
import org.apache.wicket.markup.html.link.Link; | |||
import org.apache.wicket.markup.html.panel.Fragment; | |||
import com.gitblit.Constants.AccessRestrictionType; | |||
@@ -129,54 +127,10 @@ public class ProjectRepositoryPanel extends BasePanel { | |||
user = UserModel.ANONYMOUS; | |||
} | |||
Fragment repositoryLinks; | |||
boolean showOwner = entry.isOwner(user.username); | |||
// owner of personal repository gets admin powers | |||
boolean showAdmin = isAdmin || entry.isUsersPersonalRepository(user.username); | |||
if (showAdmin || showOwner) { | |||
repositoryLinks = new Fragment("repositoryLinks", showAdmin ? "repositoryAdminLinks" | |||
: "repositoryOwnerLinks", this); | |||
if (user.canAdmin(entry)) { | |||
repositoryLinks = new Fragment("repositoryLinks", "repositoryOwnerLinks", this); | |||
repositoryLinks.add(new BookmarkablePageLink<Void>("editRepository", EditRepositoryPage.class, | |||
WicketUtils.newRepositoryParameter(entry.name))); | |||
if (showAdmin) { | |||
Link<Void> deleteLink = new Link<Void>("deleteRepository") { | |||
private static final long serialVersionUID = 1L; | |||
@Override | |||
public void onClick() { | |||
// refresh the model | |||
RepositoryModel model = app().repositories().getRepositoryModel(entry.name); | |||
if (isDeleteAllowed(model) && | |||
app().repositories().deleteRepositoryModel(model)) { | |||
// redirect to the owning page | |||
if (model.isPersonalRepository()) { | |||
setResponsePage(getPage().getClass(), WicketUtils.newUsernameParameter(model.projectPath.substring(1))); | |||
} else { | |||
setResponsePage(getPage().getClass(), WicketUtils.newProjectParameter(model.projectPath)); | |||
} | |||
} else { | |||
error(MessageFormat.format(getString("gb.repositoryDeleteFailed"), model)); | |||
} | |||
} | |||
@Override | |||
public boolean isEnabled() { | |||
return isDeleteAllowed(entry); | |||
} | |||
private boolean isDeleteAllowed( | |||
final RepositoryModel model) { | |||
return app().settings().getBoolean(Keys.web.allowDeletingNonEmptyRepositories, true) | |||
|| !model.hasCommits; | |||
} | |||
}; | |||
if (deleteLink.isEnabled()) { | |||
deleteLink.add(new JavascriptEventConfirmation("onclick", MessageFormat.format( | |||
localizer.getString("gb.deleteRepository", parent), entry))); | |||
} | |||
repositoryLinks.add(deleteLink); | |||
} | |||
} else { | |||
repositoryLinks = new Fragment("repositoryLinks", "repositoryUserLinks", this); | |||
} |
@@ -42,10 +42,6 @@ | |||
</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> | |||
<wicket:fragment wicket:id="repositoryOwnerLinks"> | |||
<span class="link"><a wicket:id="editRepository"><wicket:message key="gb.edit">[edit]</wicket:message></a></span> | |||
</wicket:fragment> |
@@ -15,7 +15,6 @@ | |||
*/ | |||
package com.gitblit.wicket.panels; | |||
import java.text.MessageFormat; | |||
import java.util.ArrayList; | |||
import java.util.Collections; | |||
import java.util.Comparator; | |||
@@ -320,53 +319,7 @@ public class RepositoriesPanel extends BasePanel { | |||
WicketUtils.setHtmlTooltip(lastChangeLabel, getString("gb.author") + ": " + entry.lastChangeAuthor); | |||
} | |||
boolean showOwner = user != null && entry.isOwner(user.username); | |||
boolean myPersonalRepository = showOwner && entry.isUsersPersonalRepository(user.username); | |||
if (showAdmin || myPersonalRepository) { | |||
Fragment repositoryLinks = new Fragment("repositoryLinks", | |||
"repositoryAdminLinks", this); | |||
repositoryLinks.add(new BookmarkablePageLink<Void>("editRepository", | |||
EditRepositoryPage.class, WicketUtils | |||
.newRepositoryParameter(entry.name))); | |||
Link<Void> deleteLink = new Link<Void>("deleteRepository") { | |||
private static final long serialVersionUID = 1L; | |||
@Override | |||
public void onClick() { | |||
// refresh the model | |||
RepositoryModel model = app().repositories().getRepositoryModel(entry.name); | |||
if (isDeleteAllowed(model) && | |||
app().repositories().deleteRepositoryModel(model)) { | |||
if (dp instanceof SortableRepositoriesProvider) { | |||
info(MessageFormat.format(getString("gb.repositoryDeleted"), model)); | |||
((SortableRepositoriesProvider) dp).remove(model); | |||
} else { | |||
setResponsePage(getPage().getClass(), getPage().getPageParameters()); | |||
} | |||
} else { | |||
error(MessageFormat.format(getString("gb.repositoryDeleteFailed"), model)); | |||
} | |||
} | |||
@Override | |||
public boolean isEnabled() { | |||
return isDeleteAllowed(entry); | |||
} | |||
private boolean isDeleteAllowed( | |||
final RepositoryModel model) { | |||
return app().settings().getBoolean(Keys.web.allowDeletingNonEmptyRepositories, true) | |||
|| !model.hasCommits; | |||
} | |||
}; | |||
if (deleteLink.isEnabled()) { | |||
deleteLink.add(new JavascriptEventConfirmation("onclick", MessageFormat.format( | |||
getString("gb.deleteRepository"), entry))); | |||
} | |||
repositoryLinks.add(deleteLink); | |||
row.add(repositoryLinks); | |||
} else if (showOwner) { | |||
if (user != null && user.canAdmin(entry)) { | |||
Fragment repositoryLinks = new Fragment("repositoryLinks", | |||
"repositoryOwnerLinks", this); | |||
repositoryLinks.add(new BookmarkablePageLink<Void>("editRepository", |