mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-09 15:05:44 +01:00
implement gzip compression for unbundle command
This commit is contained in:
@@ -81,6 +81,17 @@ public class ImportBundleSubCommand extends ImportSubCommand
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method description
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean isCompressed()
|
||||||
|
{
|
||||||
|
return compressed;
|
||||||
|
}
|
||||||
|
|
||||||
//~--- set methods ----------------------------------------------------------
|
//~--- set methods ----------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -94,6 +105,17 @@ public class ImportBundleSubCommand extends ImportSubCommand
|
|||||||
this.bundle = bundle;
|
this.bundle = bundle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method description
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param compressed
|
||||||
|
*/
|
||||||
|
public void setCompressed(boolean compressed)
|
||||||
|
{
|
||||||
|
this.compressed = compressed;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method description
|
* Method description
|
||||||
*
|
*
|
||||||
@@ -122,9 +144,13 @@ public class ImportBundleSubCommand extends ImportSubCommand
|
|||||||
{
|
{
|
||||||
ScmClientSession session = createSession();
|
ScmClientSession session = createSession();
|
||||||
|
|
||||||
Repository repository = session.getRepositoryHandler().importFromBundle(
|
ImportBundleRequest req = new ImportBundleRequest(getType(), name,
|
||||||
new ImportBundleRequest(
|
Files.asByteSource(bundle));
|
||||||
getType(), name, Files.asByteSource(bundle)));
|
|
||||||
|
req.setCompressed(compressed);
|
||||||
|
|
||||||
|
Repository repository =
|
||||||
|
session.getRepositoryHandler().importFromBundle(req);
|
||||||
|
|
||||||
printImportedRepository(repository);
|
printImportedRepository(repository);
|
||||||
}
|
}
|
||||||
@@ -141,6 +167,14 @@ public class ImportBundleSubCommand extends ImportSubCommand
|
|||||||
)
|
)
|
||||||
private File bundle;
|
private File bundle;
|
||||||
|
|
||||||
|
/** Field description */
|
||||||
|
@Option(
|
||||||
|
name = "--compressed",
|
||||||
|
usage = "optionRepositoryBundleCompressed",
|
||||||
|
aliases = { "-c" }
|
||||||
|
)
|
||||||
|
private boolean compressed = false;
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
@Option(
|
@Option(
|
||||||
name = "--name",
|
name = "--name",
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ optionRepositoryPublic = Repository public readable
|
|||||||
optionRepositoryArchive = Repository archived
|
optionRepositoryArchive = Repository archived
|
||||||
optionRemoteRepositoryUrl = Remote repository url
|
optionRemoteRepositoryUrl = Remote repository url
|
||||||
optionRepositoryBundle = Import repository from a bundle file (e.g. svn dump)
|
optionRepositoryBundle = Import repository from a bundle file (e.g. svn dump)
|
||||||
|
optionRepositoryBundleCompressed = Indicates that the bundle is gzip compressed
|
||||||
|
|
||||||
optionPermissionGroup = Group
|
optionPermissionGroup = Group
|
||||||
optionPermissionName = Group or user name
|
optionPermissionName = Group or user name
|
||||||
|
|||||||
@@ -26,8 +26,13 @@
|
|||||||
* http://bitbucket.org/sdorra/scm-manager
|
* http://bitbucket.org/sdorra/scm-manager
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
package sonia.scm.client;
|
package sonia.scm.client;
|
||||||
|
|
||||||
|
//~--- non-JDK imports --------------------------------------------------------
|
||||||
|
|
||||||
import com.google.common.io.ByteSource;
|
import com.google.common.io.ByteSource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -37,14 +42,21 @@ import com.google.common.io.ByteSource;
|
|||||||
*/
|
*/
|
||||||
public class ImportBundleRequest
|
public class ImportBundleRequest
|
||||||
{
|
{
|
||||||
private String type;
|
|
||||||
private String name;
|
|
||||||
private ByteSource bundle;
|
|
||||||
|
|
||||||
ImportBundleRequest()
|
/**
|
||||||
{
|
* Constructs ...
|
||||||
}
|
*
|
||||||
|
*/
|
||||||
|
ImportBundleRequest() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs ...
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param type
|
||||||
|
* @param name
|
||||||
|
* @param bundle
|
||||||
|
*/
|
||||||
public ImportBundleRequest(String type, String name, ByteSource bundle)
|
public ImportBundleRequest(String type, String name, ByteSource bundle)
|
||||||
{
|
{
|
||||||
this.type = type;
|
this.type = type;
|
||||||
@@ -52,19 +64,76 @@ public class ImportBundleRequest
|
|||||||
this.bundle = bundle;
|
this.bundle = bundle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getType()
|
//~--- get methods ----------------------------------------------------------
|
||||||
{
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName()
|
|
||||||
{
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method description
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
public ByteSource getBundle()
|
public ByteSource getBundle()
|
||||||
{
|
{
|
||||||
return bundle;
|
return bundle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method description
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getName()
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method description
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getType()
|
||||||
|
{
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method description
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean isCompressed()
|
||||||
|
{
|
||||||
|
return compressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
//~--- set methods ----------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method description
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param compressed
|
||||||
|
*/
|
||||||
|
public void setCompressed(boolean compressed)
|
||||||
|
{
|
||||||
|
this.compressed = compressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
//~--- fields ---------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Field description */
|
||||||
|
private ByteSource bundle;
|
||||||
|
|
||||||
|
/** Field description */
|
||||||
|
private boolean compressed = false;
|
||||||
|
|
||||||
|
/** Field description */
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/** Field description */
|
||||||
|
private String type;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ import java.io.InputStream;
|
|||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -80,6 +81,9 @@ public class JerseyRepositoryClientHandler
|
|||||||
/** Field description */
|
/** Field description */
|
||||||
private static final String PARAM_BUNDLE = "bundle";
|
private static final String PARAM_BUNDLE = "bundle";
|
||||||
|
|
||||||
|
/** Field description */
|
||||||
|
private static final String PARAM_COMPRESSED = "compressed";
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
private static final String PARAM_NAME = "name";
|
private static final String PARAM_NAME = "name";
|
||||||
|
|
||||||
@@ -113,7 +117,8 @@ public class JerseyRepositoryClientHandler
|
|||||||
public Repository importFromBundle(ImportBundleRequest request)
|
public Repository importFromBundle(ImportBundleRequest request)
|
||||||
{
|
{
|
||||||
WebResource r = client.resource(getImportUrl(request.getType(),
|
WebResource r = client.resource(getImportUrl(request.getType(),
|
||||||
IMPORT_TYPE_BUNDLE));
|
IMPORT_TYPE_BUNDLE)).queryParam(PARAM_COMPRESSED,
|
||||||
|
Boolean.toString(request.isCompressed()));
|
||||||
Repository repository = null;
|
Repository repository = null;
|
||||||
InputStream stream = null;
|
InputStream stream = null;
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The unbundle command can restore an empty repository from a bundle. The
|
* The unbundle command can restore an empty repository from a bundle. The
|
||||||
* bundle can be created with the {@link BundleCommandBuilder}.
|
* bundle can be created with the {@link BundleCommandBuilder}.
|
||||||
@@ -106,7 +108,7 @@ public final class UnbundleCommandBuilder
|
|||||||
"existing file is required");
|
"existing file is required");
|
||||||
|
|
||||||
UnbundleCommandRequest request =
|
UnbundleCommandRequest request =
|
||||||
new UnbundleCommandRequest(Files.asByteSource(inputFile));
|
createRequest(Files.asByteSource(inputFile));
|
||||||
|
|
||||||
logger.info("unbundle archive {} at {}", inputFile, repository.getId());
|
logger.info("unbundle archive {} at {}", inputFile, repository.getId());
|
||||||
|
|
||||||
@@ -130,8 +132,7 @@ public final class UnbundleCommandBuilder
|
|||||||
checkNotNull(inputStream, "input stream is required");
|
checkNotNull(inputStream, "input stream is required");
|
||||||
logger.info("unbundle archive from stream");
|
logger.info("unbundle archive from stream");
|
||||||
|
|
||||||
return unbundleCommand.unbundle(
|
return unbundleCommand.unbundle(createRequest(asByteSource(inputStream)));
|
||||||
new UnbundleCommandRequest(asByteSource(inputStream)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -151,9 +152,28 @@ public final class UnbundleCommandBuilder
|
|||||||
checkNotNull(byteSource, "byte source is required");
|
checkNotNull(byteSource, "byte source is required");
|
||||||
logger.info("unbundle from byte source");
|
logger.info("unbundle from byte source");
|
||||||
|
|
||||||
return unbundleCommand.unbundle(new UnbundleCommandRequest(byteSource));
|
return unbundleCommand.unbundle(createRequest(byteSource));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//~--- set methods ----------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set to {@code true} if bundle is gzip compressed. Default is {@code false}.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param compressed {@code true} if bundle is gzip compressed
|
||||||
|
*
|
||||||
|
* @return {@code this}
|
||||||
|
*/
|
||||||
|
public UnbundleCommandBuilder setCompressed(boolean compressed)
|
||||||
|
{
|
||||||
|
this.compressed = compressed;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//~--- methods --------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts an {@link InputStream} into a {@link ByteSource}.
|
* Converts an {@link InputStream} into a {@link ByteSource}.
|
||||||
*
|
*
|
||||||
@@ -175,6 +195,73 @@ public final class UnbundleCommandBuilder
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the {@link UnbundleCommandRequest}.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param source byte source
|
||||||
|
*
|
||||||
|
* @return the create request
|
||||||
|
*/
|
||||||
|
private UnbundleCommandRequest createRequest(ByteSource source)
|
||||||
|
{
|
||||||
|
ByteSource bs;
|
||||||
|
|
||||||
|
if (compressed)
|
||||||
|
{
|
||||||
|
logger.debug("decode gzip stream for unbundle command");
|
||||||
|
bs = new CompressedByteSource(source);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bs = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new UnbundleCommandRequest(bs);
|
||||||
|
}
|
||||||
|
|
||||||
|
//~--- inner classes --------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ByteSource which is able to handle gzip compressed resources.
|
||||||
|
*/
|
||||||
|
private static class CompressedByteSource extends ByteSource
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs ...
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param wrapped
|
||||||
|
*/
|
||||||
|
public CompressedByteSource(ByteSource wrapped)
|
||||||
|
{
|
||||||
|
this.wrapped = wrapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
//~--- methods ------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the stream for reading the compressed source.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return input stream
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public InputStream openStream() throws IOException
|
||||||
|
{
|
||||||
|
return new GZIPInputStream(wrapped.openStream());
|
||||||
|
}
|
||||||
|
|
||||||
|
//~--- fields -------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Field description */
|
||||||
|
private final ByteSource wrapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//~--- fields ---------------------------------------------------------------
|
//~--- fields ---------------------------------------------------------------
|
||||||
|
|
||||||
/** repository */
|
/** repository */
|
||||||
@@ -182,4 +269,7 @@ public final class UnbundleCommandBuilder
|
|||||||
|
|
||||||
/** unbundle command implementation */
|
/** unbundle command implementation */
|
||||||
private final UnbundleCommand unbundleCommand;
|
private final UnbundleCommand unbundleCommand;
|
||||||
|
|
||||||
|
/** Field description */
|
||||||
|
private boolean compressed = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,11 +87,13 @@ import java.util.List;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
|
import javax.ws.rs.DefaultValue;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.POST;
|
import javax.ws.rs.POST;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.PathParam;
|
import javax.ws.rs.PathParam;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.QueryParam;
|
||||||
import javax.ws.rs.WebApplicationException;
|
import javax.ws.rs.WebApplicationException;
|
||||||
import javax.ws.rs.core.Context;
|
import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.GenericEntity;
|
import javax.ws.rs.core.GenericEntity;
|
||||||
@@ -156,6 +158,7 @@ public class RepositoryImportResource
|
|||||||
* @param type repository type
|
* @param type repository type
|
||||||
* @param name name of the repository
|
* @param name name of the repository
|
||||||
* @param inputStream input bundle
|
* @param inputStream input bundle
|
||||||
|
* @param compressed true if the bundle is gzip compressed
|
||||||
*
|
*
|
||||||
* @return empty response with location header which points to the imported
|
* @return empty response with location header which points to the imported
|
||||||
* repository
|
* repository
|
||||||
@@ -166,9 +169,11 @@ public class RepositoryImportResource
|
|||||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||||
public Response importFromBundle(@Context UriInfo uriInfo,
|
public Response importFromBundle(@Context UriInfo uriInfo,
|
||||||
@PathParam("type") String type, @FormDataParam("name") String name,
|
@PathParam("type") String type, @FormDataParam("name") String name,
|
||||||
@FormDataParam("bundle") InputStream inputStream)
|
@FormDataParam("bundle") InputStream inputStream, @QueryParam("compressed")
|
||||||
|
@DefaultValue("false") boolean compressed)
|
||||||
{
|
{
|
||||||
Repository repository = doImportFromBundle(type, name, inputStream);
|
Repository repository = doImportFromBundle(type, name, inputStream,
|
||||||
|
compressed);
|
||||||
|
|
||||||
return buildResponse(uriInfo, repository);
|
return buildResponse(uriInfo, repository);
|
||||||
}
|
}
|
||||||
@@ -193,6 +198,7 @@ public class RepositoryImportResource
|
|||||||
* @param type repository type
|
* @param type repository type
|
||||||
* @param name name of the repository
|
* @param name name of the repository
|
||||||
* @param inputStream input bundle
|
* @param inputStream input bundle
|
||||||
|
* @param compressed true if the bundle is gzip compressed
|
||||||
*
|
*
|
||||||
* @return empty response with location header which points to the imported
|
* @return empty response with location header which points to the imported
|
||||||
* repository
|
* repository
|
||||||
@@ -204,13 +210,14 @@ public class RepositoryImportResource
|
|||||||
@Produces(MediaType.TEXT_HTML)
|
@Produces(MediaType.TEXT_HTML)
|
||||||
public Response importFromBundleUI(@PathParam("type") String type,
|
public Response importFromBundleUI(@PathParam("type") String type,
|
||||||
@FormDataParam("name") String name,
|
@FormDataParam("name") String name,
|
||||||
@FormDataParam("bundle") InputStream inputStream)
|
@FormDataParam("bundle") InputStream inputStream, @QueryParam("compressed")
|
||||||
|
@DefaultValue("false") boolean compressed)
|
||||||
{
|
{
|
||||||
Response response;
|
Response response;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
doImportFromBundle(type, name, inputStream);
|
doImportFromBundle(type, name, inputStream, compressed);
|
||||||
response = Response.ok(new RestActionUploadResult(true)).build();
|
response = Response.ok(new RestActionUploadResult(true)).build();
|
||||||
}
|
}
|
||||||
catch (WebApplicationException ex)
|
catch (WebApplicationException ex)
|
||||||
@@ -569,11 +576,12 @@ public class RepositoryImportResource
|
|||||||
* @param type repository type
|
* @param type repository type
|
||||||
* @param name name of the repository
|
* @param name name of the repository
|
||||||
* @param inputStream bundle stream
|
* @param inputStream bundle stream
|
||||||
|
* @param compressed true if the bundle is gzip compressed
|
||||||
*
|
*
|
||||||
* @return imported repository
|
* @return imported repository
|
||||||
*/
|
*/
|
||||||
private Repository doImportFromBundle(String type, String name,
|
private Repository doImportFromBundle(String type, String name,
|
||||||
InputStream inputStream)
|
InputStream inputStream, boolean compressed)
|
||||||
{
|
{
|
||||||
SecurityUtils.getSubject().checkRole(Role.ADMIN);
|
SecurityUtils.getSubject().checkRole(Role.ADMIN);
|
||||||
|
|
||||||
@@ -601,7 +609,7 @@ public class RepositoryImportResource
|
|||||||
|
|
||||||
logger.info("copied {} bytes to temp, start bundle import", length);
|
logger.info("copied {} bytes to temp, start bundle import", length);
|
||||||
service = serviceFactory.create(repository);
|
service = serviceFactory.create(repository);
|
||||||
service.getUnbundleCommand().unbundle(file);
|
service.getUnbundleCommand().setCompressed(compressed).unbundle(file);
|
||||||
}
|
}
|
||||||
catch (RepositoryException ex)
|
catch (RepositoryException ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ Sonia.repository.ImportPanel = Ext.extend(Ext.Panel, {
|
|||||||
|
|
||||||
importFileNameHelpText: 'The name of the repository in SCM-Manager.',
|
importFileNameHelpText: 'The name of the repository in SCM-Manager.',
|
||||||
importFileHelpText: 'Choose the dump file you want to import to SCM-Manager.',
|
importFileHelpText: 'Choose the dump file you want to import to SCM-Manager.',
|
||||||
|
importFileGZipCompressedHelpText: 'The file is gzip compressed.',
|
||||||
|
|
||||||
// tips
|
// tips
|
||||||
tipRepositoryType: 'Choose your repository type for the import.',
|
tipRepositoryType: 'Choose your repository type for the import.',
|
||||||
@@ -314,6 +315,11 @@ Sonia.repository.ImportPanel = Ext.extend(Ext.Panel, {
|
|||||||
buttonCfg: {
|
buttonCfg: {
|
||||||
iconCls: 'upload-icon'
|
iconCls: 'upload-icon'
|
||||||
}
|
}
|
||||||
|
},{
|
||||||
|
id: 'importFileGZipCompressed',
|
||||||
|
xtype: 'checkbox',
|
||||||
|
fieldLabel: 'GZip compressed',
|
||||||
|
helpText: this.importFileGZipCompressedHelpText
|
||||||
},{
|
},{
|
||||||
xtype: 'scmTip',
|
xtype: 'scmTip',
|
||||||
content: 'Please insert name and upload the repository file.',
|
content: 'Please insert name and upload the repository file.',
|
||||||
@@ -479,9 +485,10 @@ Sonia.repository.ImportPanel = Ext.extend(Ext.Panel, {
|
|||||||
},
|
},
|
||||||
|
|
||||||
importFromFile: function(layout, form){
|
importFromFile: function(layout, form){
|
||||||
|
var compressed = Ext.getCmp('importFileGZipCompressed').getValue();
|
||||||
var lbox = this.showLoadingBox();
|
var lbox = this.showLoadingBox();
|
||||||
form.submit({
|
form.submit({
|
||||||
url: restUrl + 'import/repositories/' + this.repositoryType + '/bundle.html',
|
url: restUrl + 'import/repositories/' + this.repositoryType + '/bundle.html?compressed=' + compressed,
|
||||||
scope: this,
|
scope: this,
|
||||||
success: function(form){
|
success: function(form){
|
||||||
lbox.hide();
|
lbox.hide();
|
||||||
|
|||||||
Reference in New Issue
Block a user