text: ~ | text: ~ | ||||
security: ~ | security: ~ | ||||
fixes: ~ | fixes: ~ | ||||
changes: ~ | |||||
changes: | |||||
- Move repository deletion functions to the edit repository page AND allow deletion to be disabled (pr-180, ticket-67) | |||||
additions: | additions: | ||||
- Add FORK_REPOSITORY RPC request type (issue-371, pr-161, ticket-65) | - 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) | - Add object type (ot) parameter for RSS queries to retrieve tag details (pr-165, ticket-66) | ||||
contributors: | contributors: | ||||
- Manisha Gayathri | - Manisha Gayathri | ||||
- Gerard Smyth | - Gerard Smyth | ||||
settings: | |||||
- { name: 'web.allowDeletingNonEmptyRepositories', defaultValue: 'true' } | |||||
} | } | ||||
# | # |
# SINCE 0.5.0 | # SINCE 0.5.0 | ||||
web.allowCookieAuthentication = true | 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 | # SINCE 1.6.0 | ||||
web.allowDeletingNonEmptyRepositories = true | web.allowDeletingNonEmptyRepositories = true |
repositoryManager.updateConfiguration(r, repository); | repositoryManager.updateConfiguration(r, repository); | ||||
} | } | ||||
@Override | |||||
public boolean canDelete(RepositoryModel model) { | |||||
return repositoryManager.canDelete(model); | |||||
} | |||||
@Override | @Override | ||||
public boolean deleteRepositoryModel(RepositoryModel model) { | public boolean deleteRepositoryModel(RepositoryModel model) { | ||||
return repositoryManager.deleteRepositoryModel(model); | return repositoryManager.deleteRepositoryModel(model); |
*/ | */ | ||||
void updateConfiguration(Repository r, RepositoryModel repository); | 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 | * Deletes the repository from the file system and removes the repository | ||||
* permission from all repository users. | * permission from all repository users. |
} | } | ||||
} | } | ||||
/** | |||||
* 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 | * Deletes the repository from the file system and removes the repository | ||||
* permission from all repository users. | * permission from all repository users. | ||||
*/ | */ | ||||
@Override | @Override | ||||
public boolean deleteRepository(String repositoryName) { | public boolean deleteRepository(String repositoryName) { | ||||
RepositoryModel repository = getRepositoryModel(repositoryName); | |||||
if (!canDelete(repository)) { | |||||
logger.warn("Attempt to delete {} rejected!", repositoryName); | |||||
return false; | |||||
} | |||||
try { | try { | ||||
close(repositoryName); | close(repositoryName); | ||||
// clear the repository cache | // clear the repository cache |
<div class="row"> | <div class="row"> | ||||
<div class="span12"> | <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> | ||||
</div> | </div> | ||||
import org.apache.wicket.markup.html.form.IChoiceRenderer; | import org.apache.wicket.markup.html.form.IChoiceRenderer; | ||||
import org.apache.wicket.markup.html.form.RadioChoice; | import org.apache.wicket.markup.html.form.RadioChoice; | ||||
import org.apache.wicket.markup.html.form.TextField; | 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.ListItem; | ||||
import org.apache.wicket.markup.html.list.ListView; | import org.apache.wicket.markup.html.list.ListView; | ||||
import org.apache.wicket.model.CompoundPropertyModel; | import org.apache.wicket.model.CompoundPropertyModel; | ||||
import com.gitblit.wicket.GitBlitWebSession; | import com.gitblit.wicket.GitBlitWebSession; | ||||
import com.gitblit.wicket.StringChoiceRenderer; | import com.gitblit.wicket.StringChoiceRenderer; | ||||
import com.gitblit.wicket.WicketUtils; | import com.gitblit.wicket.WicketUtils; | ||||
import com.gitblit.wicket.panels.BasePanel.JavascriptEventConfirmation; | |||||
import com.gitblit.wicket.panels.BulletListPanel; | import com.gitblit.wicket.panels.BulletListPanel; | ||||
import com.gitblit.wicket.panels.RegistrantPermissionsPanel; | import com.gitblit.wicket.panels.RegistrantPermissionsPanel; | ||||
cancel.setDefaultFormProcessing(false); | cancel.setDefaultFormProcessing(false); | ||||
form.add(cancel); | 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); | add(form); | ||||
} | } | ||||
lang="en"> | lang="en"> | ||||
<wicket:panel> | <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"> | <wicket:fragment wicket:id="repositoryOwnerLinks"> | ||||
<span class="link"> | <span class="link"> | ||||
<a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a> | <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a> |
*/ | */ | ||||
package com.gitblit.wicket.panels; | package com.gitblit.wicket.panels; | ||||
import java.text.MessageFormat; | |||||
import java.util.Map; | import java.util.Map; | ||||
import org.apache.wicket.Component; | import org.apache.wicket.Component; | ||||
import org.apache.wicket.markup.html.basic.Label; | import org.apache.wicket.markup.html.basic.Label; | ||||
import org.apache.wicket.markup.html.link.BookmarkablePageLink; | import org.apache.wicket.markup.html.link.BookmarkablePageLink; | ||||
import org.apache.wicket.markup.html.link.ExternalLink; | 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 org.apache.wicket.markup.html.panel.Fragment; | ||||
import com.gitblit.Constants.AccessRestrictionType; | import com.gitblit.Constants.AccessRestrictionType; | ||||
user = UserModel.ANONYMOUS; | user = UserModel.ANONYMOUS; | ||||
} | } | ||||
Fragment repositoryLinks; | 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, | repositoryLinks.add(new BookmarkablePageLink<Void>("editRepository", EditRepositoryPage.class, | ||||
WicketUtils.newRepositoryParameter(entry.name))); | 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 { | } else { | ||||
repositoryLinks = new Fragment("repositoryLinks", "repositoryUserLinks", this); | repositoryLinks = new Fragment("repositoryLinks", "repositoryUserLinks", this); | ||||
} | } |
</div> | </div> | ||||
</wicket:fragment> | </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"> | <wicket:fragment wicket:id="repositoryOwnerLinks"> | ||||
<span class="link"><a wicket:id="editRepository"><wicket:message key="gb.edit">[edit]</wicket:message></a></span> | <span class="link"><a wicket:id="editRepository"><wicket:message key="gb.edit">[edit]</wicket:message></a></span> | ||||
</wicket:fragment> | </wicket:fragment> |
*/ | */ | ||||
package com.gitblit.wicket.panels; | package com.gitblit.wicket.panels; | ||||
import java.text.MessageFormat; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.Collections; | import java.util.Collections; | ||||
import java.util.Comparator; | import java.util.Comparator; | ||||
WicketUtils.setHtmlTooltip(lastChangeLabel, getString("gb.author") + ": " + entry.lastChangeAuthor); | 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", | Fragment repositoryLinks = new Fragment("repositoryLinks", | ||||
"repositoryOwnerLinks", this); | "repositoryOwnerLinks", this); | ||||
repositoryLinks.add(new BookmarkablePageLink<Void>("editRepository", | repositoryLinks.add(new BookmarkablePageLink<Void>("editRepository", |