diff --git a/scm-core/src/main/java/sonia/scm/ScmClientConfig.java b/scm-core/src/main/java/sonia/scm/ScmClientConfig.java index 9cfcd86d25..0e6b4ed023 100644 --- a/scm-core/src/main/java/sonia/scm/ScmClientConfig.java +++ b/scm-core/src/main/java/sonia/scm/ScmClientConfig.java @@ -33,6 +33,10 @@ package sonia.scm; +//~--- non-JDK imports -------------------------------------------------------- + +import sonia.scm.config.ScmConfiguration; + /** * Configuration object for a SCM-Manager * client (WebInterface, RestClient, ...). @@ -48,6 +52,20 @@ public class ScmClientConfig */ public ScmClientConfig() {} + /** + * Constructs {@link ScmClientConfig} object + * + * + * @param configuration SCM-Manager main configuration + * @since 1.14 + */ + public ScmClientConfig(ScmConfiguration configuration) + { + this.dateFormat = configuration.getDateFormat(); + this.disableGroupingGrid = configuration.isDisableGroupingGrid(); + this.enableRepositoryArchive = configuration.isEnableRepositoryArchive(); + } + /** * Constructs {@link ScmClientConfig} object * @@ -100,6 +118,18 @@ public class ScmClientConfig return disableGroupingGrid; } + /** + * Returns true if the repository archive is disabled. + * + * + * @return true if the repository archive is disabled + * @since 1.14 + */ + public boolean isEnableRepositoryArchive() + { + return enableRepositoryArchive; + } + //~--- set methods ---------------------------------------------------------- /** @@ -127,11 +157,26 @@ public class ScmClientConfig this.disableGroupingGrid = disableGroupingGrid; } + /** + * Enable or disable the repository archive. Default is disabled. + * + * + * @param enableRepositoryArchive true to disable the repository archive + * @since 1.14 + */ + public void setEnableRepositoryArchive(boolean enableRepositoryArchive) + { + this.enableRepositoryArchive = enableRepositoryArchive; + } + //~--- fields --------------------------------------------------------------- /** Field description */ private String dateFormat; + /** Field description */ + private boolean enableRepositoryArchive = true; + /** Field description */ private boolean disableGroupingGrid = true; } diff --git a/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java b/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java index 1d8163e14b..3d61e77fb8 100644 --- a/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java +++ b/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java @@ -165,6 +165,7 @@ public class ScmConfiguration this.forceBaseUrl = other.forceBaseUrl; this.baseUrl = other.baseUrl; this.disableGroupingGrid = other.disableGroupingGrid; + this.enableRepositoryArchive = other.enableRepositoryArchive; // deprecated fields this.sslPort = other.sslPort; @@ -381,6 +382,18 @@ public class ScmConfiguration return enableProxy; } + /** + * Returns true if the repository archive is enabled. + * + * + * @return true if the repository archive is enabled + * @since 1.14 + */ + public boolean isEnableRepositoryArchive() + { + return enableRepositoryArchive; + } + /** * Returns true if ssl is enabled. * @@ -499,6 +512,18 @@ public class ScmConfiguration this.enableProxy = enableProxy; } + /** + * Enable or disable the repository archive. Default is disabled. + * + * + * @param enableRepositoryArchive true to disable the repository archive + * @since 1.14 + */ + public void setEnableRepositoryArchive(boolean enableRepositoryArchive) + { + this.enableRepositoryArchive = enableRepositoryArchive; + } + /** * Method description * @@ -682,6 +707,9 @@ public class ScmConfiguration private Set listeners = new HashSet(); + /** Field description */ + private boolean enableRepositoryArchive = false; + /** Field description */ private boolean disableGroupingGrid = false; diff --git a/scm-core/src/main/java/sonia/scm/repository/PermissionUtil.java b/scm-core/src/main/java/sonia/scm/repository/PermissionUtil.java index 17e684d5e7..dc6a48b118 100644 --- a/scm-core/src/main/java/sonia/scm/repository/PermissionUtil.java +++ b/scm-core/src/main/java/sonia/scm/repository/PermissionUtil.java @@ -37,6 +37,10 @@ package sonia.scm.repository; import com.google.inject.Provider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sonia.scm.config.ScmConfiguration; import sonia.scm.security.ScmSecurityException; import sonia.scm.user.User; import sonia.scm.util.AssertUtil; @@ -54,6 +58,14 @@ import java.util.List; public class PermissionUtil { + /** + * the logger for PermissionUtil + */ + private static final Logger logger = + LoggerFactory.getLogger(PermissionUtil.class); + + //~--- methods -------------------------------------------------------------- + /** * Method description * @@ -151,6 +163,40 @@ public class PermissionUtil return result; } + /** + * Returns true if the repository is writable. + * + * + * @param configuration SCM-Manager main configuration + * @param repository repository to check + * @param securityContext current user security context + * + * @return true if the repository is writable + * @since 1.14 + */ + public static boolean isWritable(ScmConfiguration configuration, + Repository repository, + WebSecurityContext securityContext) + { + boolean permitted = false; + + if (configuration.isEnableRepositoryArchive() && repository.isArchived()) + { + if (logger.isWarnEnabled()) + { + logger.warn("{} is archived and is not writeable", + repository.getName()); + } + } + else + { + permitted = PermissionUtil.hasPermission(repository, securityContext, + PermissionType.WRITE); + } + + return permitted; + } + /** * Method description * diff --git a/scm-core/src/main/java/sonia/scm/repository/Repository.java b/scm-core/src/main/java/sonia/scm/repository/Repository.java index bfd0d5e559..4dbd35f105 100644 --- a/scm-core/src/main/java/sonia/scm/repository/Repository.java +++ b/scm-core/src/main/java/sonia/scm/repository/Repository.java @@ -345,6 +345,18 @@ public class Repository extends BasicPropertiesAware implements ModelObject return url; } + /** + * Returns true if the repository is archived. + * + * + * @return true if the repository is archived + * @since 1.14 + */ + public boolean isArchived() + { + return archived; + } + /** * Returns true if the {@link Repository} is public readable. * @@ -377,6 +389,18 @@ public class Repository extends BasicPropertiesAware implements ModelObject //~--- set methods ---------------------------------------------------------- + /** + * Archive or un archive this repository. + * + * + * @param archived true to enable archive + * @since 1.14 + */ + public void setArchived(boolean archived) + { + this.archived = archived; + } + /** * Sets the contact of the {@link Repository}. The contact address should be * a email address of a person who is responsible for the {@link Repository}. @@ -516,6 +540,9 @@ public class Repository extends BasicPropertiesAware implements ModelObject @XmlElement(name = "public") private boolean publicReadable = false; + /** Field description */ + private boolean archived = false; + /** Field description */ private String type; diff --git a/scm-core/src/main/java/sonia/scm/web/filter/PermissionFilter.java b/scm-core/src/main/java/sonia/scm/web/filter/PermissionFilter.java index 5f37f222b4..181c743232 100644 --- a/scm-core/src/main/java/sonia/scm/web/filter/PermissionFilter.java +++ b/scm-core/src/main/java/sonia/scm/web/filter/PermissionFilter.java @@ -41,6 +41,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.SCMContext; +import sonia.scm.config.ScmConfiguration; import sonia.scm.repository.PermissionType; import sonia.scm.repository.PermissionUtil; import sonia.scm.repository.Repository; @@ -76,10 +77,14 @@ public abstract class PermissionFilter extends HttpFilter * Constructs ... * * + * + * @param configuration * @param securityContextProvider */ - public PermissionFilter(Provider securityContextProvider) + public PermissionFilter(ScmConfiguration configuration, + Provider securityContextProvider) { + this.configuration = configuration; this.securityContextProvider = securityContextProvider; } @@ -139,10 +144,7 @@ public abstract class PermissionFilter extends HttpFilter { boolean writeRequest = isWriteRequest(request); - if (PermissionUtil.hasPermission(repository, securityContext, - writeRequest - ? PermissionType.WRITE - : PermissionType.READ)) + if (hasPermission(repository, securityContext, writeRequest)) { chain.doFilter(request, response); } @@ -213,8 +215,43 @@ public abstract class PermissionFilter extends HttpFilter } } + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param repository + * @param securityContext + * @param writeRequest + * + * @return + */ + private boolean hasPermission(Repository repository, + WebSecurityContext securityContext, + boolean writeRequest) + { + boolean permitted = false; + + if (writeRequest) + { + permitted = PermissionUtil.isWritable(configuration, repository, + securityContext); + } + else + { + permitted = PermissionUtil.hasPermission(repository, securityContext, + PermissionType.READ); + } + + return permitted; + } + //~--- fields --------------------------------------------------------------- /** Field description */ protected Provider securityContextProvider; + + /** Field description */ + private ScmConfiguration configuration; } diff --git a/scm-core/src/main/java/sonia/scm/web/filter/ProviderPermissionFilter.java b/scm-core/src/main/java/sonia/scm/web/filter/ProviderPermissionFilter.java index 3b728da799..e1b233f5ab 100644 --- a/scm-core/src/main/java/sonia/scm/web/filter/ProviderPermissionFilter.java +++ b/scm-core/src/main/java/sonia/scm/web/filter/ProviderPermissionFilter.java @@ -44,6 +44,7 @@ import sonia.scm.web.security.WebSecurityContext; //~--- JDK imports ------------------------------------------------------------ import javax.servlet.http.HttpServletRequest; +import sonia.scm.config.ScmConfiguration; /** * @@ -61,10 +62,11 @@ public abstract class ProviderPermissionFilter extends PermissionFilter * @param repositoryProvider */ public ProviderPermissionFilter( + ScmConfiguration configuration, Provider securityContextProvider, RepositoryProvider repositoryProvider) { - super(securityContextProvider); + super(configuration, securityContextProvider); this.repositoryProvider = repositoryProvider; } diff --git a/scm-core/src/main/java/sonia/scm/web/filter/RegexPermissionFilter.java b/scm-core/src/main/java/sonia/scm/web/filter/RegexPermissionFilter.java index d3e95e9b99..0deac57121 100644 --- a/scm-core/src/main/java/sonia/scm/web/filter/RegexPermissionFilter.java +++ b/scm-core/src/main/java/sonia/scm/web/filter/RegexPermissionFilter.java @@ -37,6 +37,7 @@ package sonia.scm.web.filter; import com.google.inject.Provider; +import sonia.scm.config.ScmConfiguration; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryManager; import sonia.scm.web.security.WebSecurityContext; @@ -65,14 +66,17 @@ public abstract class RegexPermissionFilter extends PermissionFilter * Constructs ... * * + * + * @param configuration * @param securityContextProvider * @param repositoryManager */ public RegexPermissionFilter( + ScmConfiguration configuration, Provider securityContextProvider, RepositoryManager repositoryManager) { - super(securityContextProvider); + super(configuration, securityContextProvider); this.repositoryManager = repositoryManager; } diff --git a/scm-core/src/test/java/sonia/scm/repository/PermissionUtilTest.java b/scm-core/src/test/java/sonia/scm/repository/PermissionUtilTest.java index 25acd561de..ae485a66bc 100644 --- a/scm-core/src/test/java/sonia/scm/repository/PermissionUtilTest.java +++ b/scm-core/src/test/java/sonia/scm/repository/PermissionUtilTest.java @@ -35,8 +35,10 @@ package sonia.scm.repository; //~--- non-JDK imports -------------------------------------------------------- +import org.junit.Before; import org.junit.Test; +import sonia.scm.config.ScmConfiguration; import sonia.scm.security.ScmSecurityException; import sonia.scm.user.User; import sonia.scm.web.security.WebSecurityContext; @@ -65,17 +67,7 @@ public class PermissionUtilTest */ public PermissionUtilTest() { - repository = new Repository(); admams.getUser().setAdmin(true); - - Permission[] permissions = new Permission[] { - new Permission("dent", PermissionType.READ), - new Permission("perfect", - PermissionType.WRITE), - new Permission("marvin", - PermissionType.OWNER) }; - - repository.setPermissions(Arrays.asList(permissions)); } //~--- methods -------------------------------------------------------------- @@ -108,6 +100,25 @@ public class PermissionUtilTest PermissionUtil.assertPermission(repository, admams, PermissionType.OWNER); } + /** + * Method description + * + */ + @Before + public void before() + { + repository = new Repository(); + + Permission[] permissions = new Permission[] { + new Permission("dent", PermissionType.READ), + new Permission("perfect", + PermissionType.WRITE), + new Permission("marvin", + PermissionType.OWNER) }; + + repository.setPermissions(Arrays.asList(permissions)); + } + /** * Method description * @@ -161,6 +172,31 @@ public class PermissionUtilTest PermissionType.OWNER)); } + /** + * Method description + * + */ + @Test + public void testIsWritable() + { + ScmConfiguration configuration = new ScmConfiguration(); + + configuration.setEnableRepositoryArchive(true); + assertTrue(PermissionUtil.isWritable(configuration, repository, perfect)); + repository.setArchived(true); + assertFalse(PermissionUtil.isWritable(configuration, repository, perfect)); + assertFalse(PermissionUtil.isWritable(configuration, repository, admams)); + configuration.setEnableRepositoryArchive(false); + assertTrue(PermissionUtil.isWritable(configuration, repository, perfect)); + assertTrue(PermissionUtil.isWritable(configuration, repository, admams)); + assertFalse(PermissionUtil.isWritable(configuration, repository, dent)); + configuration.setEnableRepositoryArchive(true); + repository.setArchived(false); + assertTrue(PermissionUtil.isWritable(configuration, repository, perfect)); + assertTrue(PermissionUtil.isWritable(configuration, repository, admams)); + assertFalse(PermissionUtil.isWritable(configuration, repository, dent)); + } + //~--- get methods ---------------------------------------------------------- /** diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitPermissionFilter.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitPermissionFilter.java index 6986848a5f..6842f24566 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitPermissionFilter.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitPermissionFilter.java @@ -46,6 +46,7 @@ import sonia.scm.web.security.WebSecurityContext; //~--- JDK imports ------------------------------------------------------------ import javax.servlet.http.HttpServletRequest; +import sonia.scm.config.ScmConfiguration; /** * @@ -79,10 +80,11 @@ public class GitPermissionFilter extends ProviderPermissionFilter */ @Inject public GitPermissionFilter( + ScmConfiguration configuration, Provider securityContextProvider, RepositoryProvider repositoryProvider) { - super(securityContextProvider, repositoryProvider); + super(configuration, securityContextProvider, repositoryProvider); } //~--- get methods ---------------------------------------------------------- diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgPermissionFilter.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgPermissionFilter.java index b759c29e99..5df19d5042 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgPermissionFilter.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgPermissionFilter.java @@ -46,6 +46,7 @@ import sonia.scm.web.security.WebSecurityContext; //~--- JDK imports ------------------------------------------------------------ import javax.servlet.http.HttpServletRequest; +import sonia.scm.config.ScmConfiguration; /** * @@ -64,10 +65,11 @@ public class HgPermissionFilter extends ProviderPermissionFilter */ @Inject public HgPermissionFilter( + ScmConfiguration configuration, Provider securityContextProvider, RepositoryProvider repositoryProvider) { - super(securityContextProvider, repositoryProvider); + super(configuration, securityContextProvider, repositoryProvider); } //~--- get methods ---------------------------------------------------------- diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnPermissionFilter.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnPermissionFilter.java index 99cba65c93..60b06bdca3 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnPermissionFilter.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/web/SvnPermissionFilter.java @@ -39,6 +39,7 @@ import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; +import sonia.scm.config.ScmConfiguration; import sonia.scm.repository.RepositoryProvider; import sonia.scm.web.filter.ProviderPermissionFilter; import sonia.scm.web.security.WebSecurityContext; @@ -72,15 +73,18 @@ public class SvnPermissionFilter extends ProviderPermissionFilter * * * + * + * @param configuration * @param securityContextProvider * @param repository */ @Inject public SvnPermissionFilter( + ScmConfiguration configuration, Provider securityContextProvider, RepositoryProvider repository) { - super(securityContextProvider, repository); + super(configuration, securityContextProvider, repository); } //~--- get methods ---------------------------------------------------------- diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AuthenticationResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AuthenticationResource.java index 97ce14b0c0..14e57ba031 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AuthenticationResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AuthenticationResource.java @@ -251,8 +251,7 @@ public class AuthenticationResource return new ScmState(contextProvider, securityContext, repositoryManger.getConfiguredTypes(), userManager.getDefaultType(), - new ScmClientConfig(configuration.getDateFormat(), - configuration.isDisableGroupingGrid())); + new ScmClientConfig(configuration)); } //~--- fields --------------------------------------------------------------- diff --git a/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java b/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java index a3574c79e3..a70d79be82 100644 --- a/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java +++ b/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java @@ -46,6 +46,25 @@ import sonia.scm.ConfigurationException; import sonia.scm.HandlerEvent; import sonia.scm.SCMContextProvider; import sonia.scm.Type; + +import sonia.scm.config.ScmConfiguration; +import sonia.scm.repository.AbstractRepositoryManager; +import sonia.scm.repository.BlameViewer; +import sonia.scm.repository.ChangesetViewer; +import sonia.scm.repository.DiffViewer; +import sonia.scm.repository.PermissionType; +import sonia.scm.repository.PermissionUtil; +import sonia.scm.repository.Repository; +import sonia.scm.repository.RepositoryAllreadyExistExeption; +import sonia.scm.repository.RepositoryBrowser; +import sonia.scm.repository.RepositoryException; +import sonia.scm.repository.RepositoryHandler; +import sonia.scm.repository.RepositoryHandlerNotFoundException; +import sonia.scm.repository.RepositoryHook; +import sonia.scm.repository.RepositoryHookEvent; +import sonia.scm.repository.RepositoryListener; +import sonia.scm.repository.RepositoryNotFoundException; + import sonia.scm.security.ScmSecurityException; import sonia.scm.util.AssertUtil; import sonia.scm.util.CollectionAppender; @@ -92,6 +111,8 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager * * * + * + * @param configuration * @param contextProvider * @param securityContextProvider * @param repositoryDAO @@ -101,12 +122,13 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager */ @Inject public DefaultRepositoryManager( - SCMContextProvider contextProvider, + ScmConfiguration configuration, SCMContextProvider contextProvider, Provider securityContextProvider, RepositoryDAO repositoryDAO, Set handlerSet, Provider> repositoryListenersProvider, Provider> repositoryHooksProvider) { + this.configuration = configuration; this.securityContextProvider = securityContextProvider; this.repositoryDAO = repositoryDAO; this.repositoryListenersProvider = repositoryListenersProvider; @@ -213,6 +235,12 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager assertIsOwner(repository); + if (configuration.isEnableRepositoryArchive() &&!repository.isArchived()) + { + throw new RepositoryException( + "Repository could not deleted, because it is not archived."); + } + if (repositoryDAO.contains(repository)) { getHandler(repository).delete(repository); @@ -932,6 +960,9 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager //~--- fields --------------------------------------------------------------- + /** Field description */ + private ScmConfiguration configuration; + /** Field description */ private Map handlerMap; diff --git a/scm-webapp/src/main/webapp/resources/images/archive.png b/scm-webapp/src/main/webapp/resources/images/archive.png new file mode 100755 index 0000000000..8443c23eb9 Binary files /dev/null and b/scm-webapp/src/main/webapp/resources/images/archive.png differ diff --git a/scm-webapp/src/main/webapp/resources/js/config/sonia.config.scmconfigpanel.js b/scm-webapp/src/main/webapp/resources/js/config/sonia.config.scmconfigpanel.js index 97710cfabf..d82c7a33a2 100644 --- a/scm-webapp/src/main/webapp/resources/js/config/sonia.config.scmconfigpanel.js +++ b/scm-webapp/src/main/webapp/resources/js/config/sonia.config.scmconfigpanel.js @@ -57,6 +57,7 @@ Sonia.config.ScmConfigPanel = Ext.extend(Sonia.config.ConfigPanel,{ baseUrlText: 'Base Url', forceBaseUrlText: 'Force Base Url', disableGroupingGridText: 'Disable repository Groups', + enableRepositoryArchiveText: 'Enable repository archive', // help @@ -81,7 +82,7 @@ Sonia.config.ScmConfigPanel = Ext.extend(Sonia.config.ConfigPanel,{ baseUrlHelpText: 'The url of the application (with context path) i.e. http://localhost:8080/scm', forceBaseUrlHelpText: 'Redirects to the base url if the request comes from a other url', disableGroupingGridHelpText: 'Disable repository Groups. A complete page reload is required after a change of this value.', - + enableRepositoryArchiveHelpText: 'Enable repository archives. A complete page reload is required after a change of this value.', initComponent: function(){ @@ -108,6 +109,12 @@ Sonia.config.ScmConfigPanel = Ext.extend(Sonia.config.ConfigPanel,{ name: 'disableGroupingGrid', inputValue: 'true', helpText: this.disableGroupingGridHelpText + },{ + xtype: 'checkbox', + fieldLabel: this.enableRepositoryArchiveText, + name: 'enableRepositoryArchive', + inputValue: 'true', + helpText: this.enableRepositoryArchiveHelpText },{ xtype: 'textfield', fieldLabel: this.dateFormatText, diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.grid.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.grid.js index edf4f81aa6..6a294dcef5 100644 --- a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.grid.js +++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.grid.js @@ -36,11 +36,23 @@ Sonia.repository.Grid = Ext.extend(Sonia.rest.Grid, { colDescriptionText: 'Description', colCreationDateText: 'Creation date', colUrlText: 'Url', + colArchiveText: 'Archive', emptyText: 'No repository is configured', formTitleText: 'Repository Form', unknownType: 'Unknown', + archiveIcon: 'resources/images/archive.png', + + filterRequest: null, + + /** + * @deprecated use filterRequest + */ searchValue: null, + + /** + * @deprecated use filterRequest + */ typeFilter: null, // TODO find better text @@ -82,6 +94,8 @@ Sonia.repository.Grid = Ext.extend(Sonia.rest.Grid, { name:'permissions' },{ name: 'properties' + },{ + name: 'archived' }] }), sortInfo: { @@ -139,6 +153,14 @@ Sonia.repository.Grid = Ext.extend(Sonia.rest.Grid, { dataIndex: 'url', renderer: this.renderUrl, width: 250 + },{ + id: 'Archive', + header: this.colArchiveText, + dataIndex: 'archived', + width: 40, + hidden: ! state.clientConfig.enableRepositoryArchive, + renderer: this.renderArchive, + scope: this },{ id: 'group', dataIndex: 'group', @@ -158,6 +180,13 @@ Sonia.repository.Grid = Ext.extend(Sonia.rest.Grid, { } console.debug( msg ); } + + if ( state.clientConfig.enableRepositoryArchive ){ + if ( !this.filterRequest ){ + this.filterRequest = {}; + } + this.filterRequest.archived = false; + } var config = { autoExpandColumn: 'description', @@ -179,6 +208,8 @@ Sonia.repository.Grid = Ext.extend(Sonia.rest.Grid, { groupTextTpl: '{group} ({[values.rs.length]} {[values.rs.length > 1 ? "Repositories" : "Repository"]})' }) }; + + this.addEvents('repositorySelected'); Ext.apply(this, Ext.apply(this.initialConfig, config)); Sonia.repository.Grid.superclass.initComponent.apply(this, arguments); @@ -188,6 +219,10 @@ Sonia.repository.Grid = Ext.extend(Sonia.rest.Grid, { } }, + renderArchive: function(v){ + return v ? ' + v + ' : ''; + }, + convertToGroup: function(v, data){ var name = data.name; var i = name.lastIndexOf('/'); @@ -220,6 +255,9 @@ Sonia.repository.Grid = Ext.extend(Sonia.rest.Grid, { this.filterStore(); } this.ready = true; + if (this.filterRequest){ + this.filterByRequest(); + } }, onFallBelowMinHeight: function(height, minHeight){ @@ -233,23 +271,63 @@ Sonia.repository.Grid = Ext.extend(Sonia.rest.Grid, { this.ownerCt.doLayout(); }, + getFilterRequest: function(){ + if ( ! this.filterRequest ){ + this.filterRequest = {}; + } + return this.filterRequest; + }, + + /** + * @deprecated use filterByRequest + */ search: function(value){ this.searchValue = value; this.filterStore(); }, - + + /** + * @deprecated use filterByRequest + */ filter: function(type){ this.typeFilter = type; this.filterStore(); }, clearStoreFilter: function(){ + this.filterRequest = null; this.searchValue = null; this.typeFilter = null; this.getStore().clearFilter(); }, + filterByRequest: function(){ + if (debug){ + console.debug('filter repository store by request:'); + console.debug(this.filterRequest); + } + var store = this.getStore(); + if ( ! this.filterRequest ){ + store.clearFilter(); + } else { + var query = this.filterRequest.query; + if ( query ){ + query = query.toLowerCase(); + } + var archived = ! state.clientConfig.enableRepositoryArchive || this.filterRequest.archived; + store.filterBy(function(rec){ + var desc = rec.get('description'); + return (! query || rec.get('name').toLowerCase().indexOf(query) >= 0 || + (desc && desc.toLowerCase().indexOf(query) >= 0)) && + (! this.filterRequest.type || rec.get('type') == this.filterRequest.type) && + (archived || ! rec.get('archived')); + }, this); + } + }, + /** + * @deprecated use filterByRequest + */ filterStore: function(){ var store = this.getStore(); if ( ! this.searchValue && ! this.typeFilter ){ @@ -268,6 +346,9 @@ Sonia.repository.Grid = Ext.extend(Sonia.rest.Grid, { } }, + /** + * TODO move to panel + */ selectItem: function(item){ if ( debug ){ console.debug( item.name + ' selected' ); @@ -276,14 +357,17 @@ Sonia.repository.Grid = Ext.extend(Sonia.rest.Grid, { if ( this.parentPanel ){ this.parentPanel.updateHistory(item); } + + var owner = Sonia.repository.isOwner(item); + + this.fireEvent('repositorySelected', item, owner); var infoPanel = main.getInfoPanel(item.type); infoPanel.item = item; var panels = [infoPanel]; - if ( Sonia.repository.isOwner(item) ){ - Ext.getCmp('repoRmButton').setDisabled(false); + if ( owner ){ panels.push({ item: item, xtype: 'repositorySettingsForm', diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.panel.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.panel.js index 59c45c887a..ce729e0f9b 100644 --- a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.panel.js +++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.panel.js @@ -34,11 +34,21 @@ Sonia.repository.Panel = Ext.extend(Sonia.rest.Panel, { titleText: 'Repository Form', emptyText: 'Add or select an Repository', + + // TODO i18n + archiveText: 'Archive', + unarchiveText: 'Unarchive', + archiveTitleText: 'Archive Repository', + archiveMsgText: 'Archive Repository "{0}"?', + errorArchiveMsgText: 'Repository archival failed', + removeTitleText: 'Remove Repository', removeMsgText: 'Remove Repository "{0}"?', errorTitleText: 'Error', errorMsgText: 'Repository deletion failed', + archiveIcon: 'resources/images/archive.png', + repositoryGrid: null, initComponent: function(){ @@ -70,6 +80,20 @@ Sonia.repository.Panel = Ext.extend(Sonia.rest.Panel, { handler: this.showAddForm }); } + + // repository archive + if (state.clientConfig.enableRepositoryArchive){ + toolbar.push({ + xtype: 'tbbutton', + id: 'repoArchiveButton', + disabled: true, + text: this.archiveText, + icon: this.archiveIcon, + scope: this, + handler: this.toggleArchive + }); + } + toolbar.push({ xtype: 'tbbutton', id: 'repoRmButton', @@ -124,8 +148,26 @@ Sonia.repository.Panel = Ext.extend(Sonia.rest.Panel, { fn: this.search, scope: this } - } + } }); + + // repository archive + if (state.clientConfig.enableRepositoryArchive){ + toolbar.push(' ',{ + id: 'displayArchived', + xtype: 'checkbox', + listeners: { + check: { + fn: this.filterByArchived, + scope: this + } + } + },{ + xtype: 'label', + text: 'Archive', + cls: 'ytb-text' + }) + } var config = { tbar: toolbar, @@ -133,7 +175,13 @@ Sonia.repository.Panel = Ext.extend(Sonia.rest.Panel, { id: 'repositoryGrid', xtype: 'repositoryGrid', region: 'center', - parentPanel: this + parentPanel: this, + listeners: { + repositorySelected: { + fn: this.onRepositorySelection, + scope: this + } + } },{ id: 'repositoryEditPanel', xtype: 'tabpanel', @@ -170,57 +218,144 @@ Sonia.repository.Panel = Ext.extend(Sonia.rest.Panel, { Sonia.History.add(token); }, + filterByArchived: function(checkbox, checked){ + var grid = this.getGrid(); + grid.getFilterRequest().archived = checked; + grid.filterByRequest(); + }, + filterByType: function(combo, rec){ - this.getGrid().filter(rec.get('name')); + var grid = this.getGrid(); + grid.getFilterRequest().type = rec.get('name'); + grid.filterByRequest(); }, search: function(field){ - this.getGrid().search(field.getValue()); + var grid = this.getGrid(); + grid.getFilterRequest().query = field.getValue(); + grid.filterByRequest(); }, - - removeRepository: function(){ + + getSelectedRepository: function(){ + var repository = null; var grid = this.getGrid(); var selected = grid.getSelectionModel().getSelected(); if ( selected ){ - var item = selected.data; - var url = restUrl + 'repositories/' + item.id + '.json'; - - Ext.MessageBox.show({ - title: this.removeTitleText, - msg: String.format(this.removeMsgText, item.name), - buttons: Ext.MessageBox.OKCANCEL, - icon: Ext.MessageBox.QUESTION, - fn: function(result){ - if ( result == 'ok' ){ + repository = selected.data; + } else if (debug) { + console.debug( 'no repository selected' ); + } + return repository; + }, + + executeRemoteCall: function(title, message, method, url, data, failureCallback){ + Ext.MessageBox.show({ + title: title, + msg: message, + buttons: Ext.MessageBox.OKCANCEL, + icon: Ext.MessageBox.QUESTION, + fn: function(result){ + if ( result == 'ok' ){ - if ( debug ){ - console.debug( 'remove repository ' + item.name ); - } + if ( debug ){ + console.debug('call repository repository action '+ method + ' on ' + url ); + } + + var el = this.el; + var tid = setTimeout( function(){el.mask('Loading ...');}, 100); - Ext.Ajax.request({ - url: url, - method: 'DELETE', - scope: this, - success: function(){ - this.reload(); - this.resetPanel(); - }, - failure: function(result){ - main.handleRestFailure( - result, - this.errorTitleText, - this.errorMsgText - ); - } - }); + if (data && data.group){ + delete data.group; } - }, - scope: this - }); + Ext.Ajax.request({ + url: url, + method: method, + jsonData: data, + scope: this, + success: function(){ + this.reload(); + this.resetPanel(); + clearTimeout(tid); + el.unmask(); + }, + failure: function(result){ + clearTimeout(tid); + el.unmask(); + failureCallback.call(this, result); + } + }); + } // canceled + }, + scope: this + }); + }, + + toggleArchive: function(){ + var item = this.getSelectedRepository(); + if ( item ){ + console.debug(item); + + item.archived = ! item.archived; + if (debug){ + console.debug('toggle repository ' + item.name + ' archive to ' + item.archived); + } + + var url = restUrl + 'repositories/' + item.id + '.json'; + this.executeRemoteCall(this.archiveTitleText, + String.format(this.archiveMsgText, item.name), + 'PUT', url, item, function(result){ + main.handleFailure( + result.status, + this.errorTitleText, + this.errorArchiveMsgText + ); + } + ); + } + }, - } else if ( debug ){ - console.debug( 'no repository selected' ); + removeRepository: function(){ + var item = this.getSelectedRepository(); + if ( item ){ + if ( debug ){ + console.debug( 'remove repository ' + item.name ); + } + + var url = restUrl + 'repositories/' + item.id + '.json'; + this.executeRemoteCall(this.archiveTitleText, + String.format(this.archiveMsgText, item.name), + 'DELETE', url, null, function(result){ + main.handleFailure( + result.status, + this.errorTitleText, + this.errorMsgText + ); + } + ); + } + }, + + onRepositorySelection: function(item, owner){ + if ( owner ){ + if (state.clientConfig.enableRepositoryArchive){ + var archiveBt = Ext.getCmp('repoArchiveButton'); + if ( item.archived ){ + archiveBt.setText(this.unarchiveText); + Ext.getCmp('repoRmButton').setDisabled(false); + } else { + archiveBt.setText(this.archiveText); + Ext.getCmp('repoRmButton').setDisabled(true); + } + archiveBt.setDisabled(false); + } else { + Ext.getCmp('repoRmButton').setDisabled(false); + } + } else { + Ext.getCmp('repoRmButton').setDisabled(false); + if (state.clientConfig.enableRepositoryArchive){ + Ext.getCmp('repoArchiveButton').setDisabled(false); + } } }, @@ -271,6 +406,10 @@ Sonia.repository.Panel = Ext.extend(Sonia.rest.Panel, { reload: function(){ this.getGrid().reload(); + var repo = this.getSelectedRepository(); + if ( repo ){ + this.onRepositorySelection(repo, Sonia.repository.isOwner(repo)); + } } }); diff --git a/scm-webapp/src/test/java/sonia/scm/repository/DefaultRepositoryManagerTest.java b/scm-webapp/src/test/java/sonia/scm/repository/DefaultRepositoryManagerTest.java index 0b2d848234..9ee0445345 100644 --- a/scm-webapp/src/test/java/sonia/scm/repository/DefaultRepositoryManagerTest.java +++ b/scm-webapp/src/test/java/sonia/scm/repository/DefaultRepositoryManagerTest.java @@ -41,6 +41,7 @@ import org.junit.Test; import sonia.scm.Type; import sonia.scm.repository.xml.XmlRepositoryDAO; +import sonia.scm.config.ScmConfiguration; import sonia.scm.store.JAXBStoreFactory; import sonia.scm.store.StoreFactory; import sonia.scm.util.MockUtil; @@ -134,7 +135,9 @@ public class DefaultRepositoryManagerTest extends RepositoryManagerTestBase XmlRepositoryDAO repositoryDAO = new XmlRepositoryDAO(factory); - return new DefaultRepositoryManager(contextProvider, + ScmConfiguration configuration = new ScmConfiguration(); + + return new DefaultRepositoryManager(configuration, contextProvider, MockUtil.getAdminSecurityContextProvider(), repositoryDAO, handlerSet, listenerProvider, hookProvider); }