mirror of
https://repository.entgra.net/community/device-mgt-core.git
synced 2025-10-06 02:01:45 +00:00
Merge pull request #957 from amalhub/application-mgt
Adding Device-Application mapping implementation
This commit is contained in:
commit
e09e1ec68a
@ -24,10 +24,7 @@ import org.wso2.carbon.device.application.mgt.common.Application;
|
|||||||
import org.wso2.carbon.device.application.mgt.common.InstallationDetails;
|
import org.wso2.carbon.device.application.mgt.common.InstallationDetails;
|
||||||
|
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.*;
|
||||||
import javax.ws.rs.POST;
|
|
||||||
import javax.ws.rs.Path;
|
|
||||||
import javax.ws.rs.Produces;
|
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
@ -111,7 +108,88 @@ public interface SubscriptionManagementAPI {
|
|||||||
Response installApplication(
|
Response installApplication(
|
||||||
@ApiParam(
|
@ApiParam(
|
||||||
name = "installationDetails",
|
name = "installationDetails",
|
||||||
value = "The application ID and list the devices/users/roles",
|
value = "The application ID and list of devices/users/roles",
|
||||||
required = true)
|
required = true)
|
||||||
@Valid InstallationDetails installationDetails);
|
@Valid InstallationDetails installationDetails);
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/uninstall-application")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
@ApiOperation(
|
||||||
|
consumes = MediaType.APPLICATION_JSON,
|
||||||
|
produces = MediaType.APPLICATION_JSON,
|
||||||
|
httpMethod = "POST",
|
||||||
|
value = "Uninstall an application",
|
||||||
|
notes = "This will uninstall an application to a given list of devices/users/roles",
|
||||||
|
tags = "Subscription Management",
|
||||||
|
extensions = {
|
||||||
|
@Extension(properties = {
|
||||||
|
@ExtensionProperty(name = SCOPE, value = "perm:subscription:uninstall")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@ApiResponses(
|
||||||
|
value = {
|
||||||
|
@ApiResponse(
|
||||||
|
code = 200,
|
||||||
|
message = "OK. \n Successfully uninstalled the application.",
|
||||||
|
response = Application.class),
|
||||||
|
@ApiResponse(
|
||||||
|
code = 304,
|
||||||
|
message = "Not Modified. \n " +
|
||||||
|
"Empty body because the application is already uninstalled."),
|
||||||
|
@ApiResponse(
|
||||||
|
code = 500,
|
||||||
|
message = "Internal Server Error. \n Error occurred while installing the application.",
|
||||||
|
response = ErrorResponse.class)
|
||||||
|
})
|
||||||
|
Response uninstallApplication(
|
||||||
|
@ApiParam(
|
||||||
|
name = "installationDetails",
|
||||||
|
value = "The application ID and list of devices/users/roles",
|
||||||
|
required = true)
|
||||||
|
@Valid InstallationDetails installationDetails);
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/application/{applicationUUID}/device/{deviceId}")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
@ApiOperation(
|
||||||
|
consumes = MediaType.APPLICATION_JSON,
|
||||||
|
produces = MediaType.APPLICATION_JSON,
|
||||||
|
httpMethod = "GET",
|
||||||
|
value = "Get an application",
|
||||||
|
notes = "This will return an application to a given valid token",
|
||||||
|
tags = "Subscription Management",
|
||||||
|
extensions = {
|
||||||
|
@Extension(properties = {
|
||||||
|
@ExtensionProperty(name = SCOPE, value = "perm:subscription:getApplication")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@ApiResponses(
|
||||||
|
value = {
|
||||||
|
@ApiResponse(
|
||||||
|
code = 200,
|
||||||
|
message = "OK. \n Successfully installed the application.",
|
||||||
|
response = Application.class),
|
||||||
|
@ApiResponse(
|
||||||
|
code = 304,
|
||||||
|
message = "Not Modified. \n " +
|
||||||
|
"Empty body because the application is already installed."),
|
||||||
|
@ApiResponse(
|
||||||
|
code = 500,
|
||||||
|
message = "Internal Server Error. \n Error occurred while fetching the application.",
|
||||||
|
response = ErrorResponse.class)
|
||||||
|
})
|
||||||
|
Response getApplication(
|
||||||
|
@ApiParam(
|
||||||
|
name = "applicationUUID",
|
||||||
|
value = "Application ID")
|
||||||
|
@QueryParam("applicationUUID") String applicationUUID,
|
||||||
|
@ApiParam(
|
||||||
|
name = "deviceId",
|
||||||
|
value = "The device ID")
|
||||||
|
@QueryParam("deviceId") String deviceId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -64,9 +64,22 @@ public class SubscriptionManagementAPIImpl implements SubscriptionManagementAPI{
|
|||||||
}
|
}
|
||||||
return Response.status(Response.Status.OK).entity(response).build();
|
return Response.status(Response.Status.OK).entity(response).build();
|
||||||
} catch (ApplicationManagementException e) {
|
} catch (ApplicationManagementException e) {
|
||||||
String msg = "Error occurred while creating the application";
|
String msg = "Error occurred while installing the application";
|
||||||
log.error(msg, e);
|
log.error(msg, e);
|
||||||
return Response.status(Response.Status.BAD_REQUEST).build();
|
return Response.status(Response.Status.BAD_REQUEST).build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response uninstallApplication(@ApiParam(name = "installationDetails", value = "The application ID and list" +
|
||||||
|
" of devices/users/roles", required = true) @Valid InstallationDetails installationDetails) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response getApplication(@ApiParam(name = "applicationUUID", value = "Application ID") String
|
||||||
|
applicationUUID, @ApiParam(name = "deviceId", value = "The device ID")
|
||||||
|
String deviceId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -95,4 +95,16 @@
|
|||||||
<url>/application-mgt/subscription</url>
|
<url>/application-mgt/subscription</url>
|
||||||
<method>POST</method>
|
<method>POST</method>
|
||||||
</Permission>
|
</Permission>
|
||||||
|
<Permission>
|
||||||
|
<name>Uninstall Application</name>
|
||||||
|
<path>/device-mgt/subscription/uninstall</path>
|
||||||
|
<url>/application-mgt/subscription</url>
|
||||||
|
<method>POST</method>
|
||||||
|
</Permission>
|
||||||
|
<Permission>
|
||||||
|
<name>Get Application</name>
|
||||||
|
<path>/device-mgt/subscription/getApplication</path>
|
||||||
|
<url>/application-mgt/subscription</url>
|
||||||
|
<method>GET</method>
|
||||||
|
</Permission>
|
||||||
</PermissionConfiguration>
|
</PermissionConfiguration>
|
||||||
|
|||||||
@ -29,7 +29,7 @@ public class DeviceIdentifier {
|
|||||||
name = "id",
|
name = "id",
|
||||||
value = "Identity of the device.",
|
value = "Identity of the device.",
|
||||||
required = true,
|
required = true,
|
||||||
example = "123456")
|
example = "d24f870f390352a4")
|
||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
@ApiModelProperty(
|
@ApiModelProperty(
|
||||||
|
|||||||
@ -22,5 +22,8 @@ package org.wso2.carbon.device.application.mgt.core.dao;
|
|||||||
* This interface provides the list of operations that are supported with subscription database.
|
* This interface provides the list of operations that are supported with subscription database.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
import org.wso2.carbon.device.application.mgt.common.exception.ApplicationManagementException;
|
||||||
|
|
||||||
public interface SubscriptionDAO {
|
public interface SubscriptionDAO {
|
||||||
|
int addDeviceApplicationMapping(String deviceIdentifier, String applicationUUID, boolean installed) throws ApplicationManagementException;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,6 +26,7 @@ import org.wso2.carbon.device.application.mgt.core.dao.ApplicationDAO;
|
|||||||
import org.wso2.carbon.device.application.mgt.core.dao.ApplicationReleaseDAO;
|
import org.wso2.carbon.device.application.mgt.core.dao.ApplicationReleaseDAO;
|
||||||
import org.wso2.carbon.device.application.mgt.core.dao.LifecycleStateDAO;
|
import org.wso2.carbon.device.application.mgt.core.dao.LifecycleStateDAO;
|
||||||
import org.wso2.carbon.device.application.mgt.core.dao.PlatformDAO;
|
import org.wso2.carbon.device.application.mgt.core.dao.PlatformDAO;
|
||||||
|
import org.wso2.carbon.device.application.mgt.core.dao.SubscriptionDAO;
|
||||||
import org.wso2.carbon.device.application.mgt.core.dao.VisibilityDAO;
|
import org.wso2.carbon.device.application.mgt.core.dao.VisibilityDAO;
|
||||||
import org.wso2.carbon.device.application.mgt.core.dao.impl.application.GenericApplicationDAOImpl;
|
import org.wso2.carbon.device.application.mgt.core.dao.impl.application.GenericApplicationDAOImpl;
|
||||||
import org.wso2.carbon.device.application.mgt.core.dao.impl.application.release.GenericApplicationReleaseDAOImpl;
|
import org.wso2.carbon.device.application.mgt.core.dao.impl.application.release.GenericApplicationReleaseDAOImpl;
|
||||||
@ -34,6 +35,7 @@ import org.wso2.carbon.device.application.mgt.core.dao.impl.lifecyclestate.Gener
|
|||||||
import org.wso2.carbon.device.application.mgt.core.dao.impl.platform.GenericPlatformDAOImpl;
|
import org.wso2.carbon.device.application.mgt.core.dao.impl.platform.GenericPlatformDAOImpl;
|
||||||
import org.wso2.carbon.device.application.mgt.core.dao.impl.platform.OracleMsSQLPlatformDAOImpl;
|
import org.wso2.carbon.device.application.mgt.core.dao.impl.platform.OracleMsSQLPlatformDAOImpl;
|
||||||
import org.wso2.carbon.device.application.mgt.core.dao.impl.visibility.GenericVisibilityDAOImpl;
|
import org.wso2.carbon.device.application.mgt.core.dao.impl.visibility.GenericVisibilityDAOImpl;
|
||||||
|
import org.wso2.carbon.device.application.mgt.core.dao.impl.subscription.GenericSubscriptionDAOImpl;
|
||||||
import org.wso2.carbon.device.application.mgt.core.exception.ApplicationManagementDAOException;
|
import org.wso2.carbon.device.application.mgt.core.exception.ApplicationManagementDAOException;
|
||||||
import org.wso2.carbon.device.application.mgt.core.util.ApplicationMgtDatabaseCreator;
|
import org.wso2.carbon.device.application.mgt.core.util.ApplicationMgtDatabaseCreator;
|
||||||
import org.wso2.carbon.device.application.mgt.core.util.ConnectionManagerUtil;
|
import org.wso2.carbon.device.application.mgt.core.util.ConnectionManagerUtil;
|
||||||
@ -144,6 +146,24 @@ public class DAOFactory {
|
|||||||
throw new IllegalStateException("Database engine has not initialized properly.");
|
throw new IllegalStateException("Database engine has not initialized properly.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To get the instance of SubscriptionDAOImplementation of the particular database engine.
|
||||||
|
* @return GenericSubscriptionDAOImpl
|
||||||
|
*/
|
||||||
|
public static SubscriptionDAO getSubscriptionDAO() {
|
||||||
|
if (databaseEngine != null) {
|
||||||
|
switch (databaseEngine) {
|
||||||
|
case Constants.DataBaseTypes.DB_TYPE_H2:
|
||||||
|
case Constants.DataBaseTypes.DB_TYPE_MYSQL:
|
||||||
|
case Constants.DataBaseTypes.DB_TYPE_POSTGRESQL:
|
||||||
|
return new GenericSubscriptionDAOImpl();
|
||||||
|
default:
|
||||||
|
throw new UnsupportedDatabaseEngineException("Unsupported database engine : " + databaseEngine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("Database engine has not initialized properly.");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method initializes the databases by creating the database.
|
* This method initializes the databases by creating the database.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||||
|
*
|
||||||
|
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||||
|
* Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.wso2.carbon.device.application.mgt.core.dao.impl.subscription;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.wso2.carbon.device.application.mgt.common.exception.ApplicationManagementException;
|
||||||
|
import org.wso2.carbon.device.application.mgt.core.dao.SubscriptionDAO;
|
||||||
|
import org.wso2.carbon.device.application.mgt.core.dao.common.Util;
|
||||||
|
import org.wso2.carbon.device.application.mgt.core.dao.impl.AbstractDAOImpl;
|
||||||
|
|
||||||
|
import java.sql.*;
|
||||||
|
|
||||||
|
public class GenericSubscriptionDAOImpl extends AbstractDAOImpl implements SubscriptionDAO {
|
||||||
|
private static Log log = LogFactory.getLog(GenericSubscriptionDAOImpl.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int addDeviceApplicationMapping(String deviceIdentifier, String applicationUUID, boolean installed) throws
|
||||||
|
ApplicationManagementException {
|
||||||
|
Connection conn;
|
||||||
|
PreparedStatement stmt = null;
|
||||||
|
ResultSet rs = null;
|
||||||
|
int mappingId = -1;
|
||||||
|
try {
|
||||||
|
conn = this.getDBConnection();
|
||||||
|
String sql = "SELECT ID FROM APPM_DEVICE_APPLICATION_MAPPING WHERE DEVICE_IDENTIFIER = ? AND " +
|
||||||
|
"APPLICATION_UUID = ?";
|
||||||
|
stmt = conn.prepareStatement(sql);
|
||||||
|
stmt.setString(1, deviceIdentifier);
|
||||||
|
stmt.setString(2, applicationUUID);
|
||||||
|
rs = stmt.executeQuery();
|
||||||
|
|
||||||
|
if (!rs.next()) {
|
||||||
|
sql = "INSERT INTO APPM_DEVICE_APPLICATION_MAPPING (DEVICE_IDENTIFIER, APPLICATION_UUID, " +
|
||||||
|
"INSTALLED) VALUES (?, ?, ?)";
|
||||||
|
stmt = conn.prepareStatement(sql, new String[]{"id"});
|
||||||
|
stmt.setString(1, deviceIdentifier);
|
||||||
|
stmt.setString(2, applicationUUID);
|
||||||
|
stmt.setBoolean(3, installed);
|
||||||
|
stmt.executeUpdate();
|
||||||
|
|
||||||
|
rs = stmt.getGeneratedKeys();
|
||||||
|
if (rs.next()) {
|
||||||
|
mappingId = rs.getInt(1);
|
||||||
|
}
|
||||||
|
return mappingId;
|
||||||
|
} else {
|
||||||
|
log.warn("Device[" + deviceIdentifier + "] application[" + applicationUUID + "] mapping already " +
|
||||||
|
"exists in the DB");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new ApplicationManagementException("Error occurred while adding device application mapping to DB", e);
|
||||||
|
} finally {
|
||||||
|
Util.cleanupResources(stmt, rs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -22,7 +22,12 @@ import org.apache.commons.logging.LogFactory;
|
|||||||
import org.wso2.carbon.device.application.mgt.common.DeviceIdentifier;
|
import org.wso2.carbon.device.application.mgt.common.DeviceIdentifier;
|
||||||
import org.wso2.carbon.device.application.mgt.common.exception.ApplicationManagementException;
|
import org.wso2.carbon.device.application.mgt.common.exception.ApplicationManagementException;
|
||||||
import org.wso2.carbon.device.application.mgt.common.services.SubscriptionManager;
|
import org.wso2.carbon.device.application.mgt.common.services.SubscriptionManager;
|
||||||
|
import org.wso2.carbon.device.application.mgt.core.dao.common.DAOFactory;
|
||||||
|
import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException;
|
||||||
|
import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -37,13 +42,29 @@ public class SubscriptionManagerImpl implements SubscriptionManager {
|
|||||||
List<DeviceIdentifier> deviceList)
|
List<DeviceIdentifier> deviceList)
|
||||||
throws ApplicationManagementException {
|
throws ApplicationManagementException {
|
||||||
log.info("Install application: " + applicationUUID + " to: " + deviceList.size() + " devices.");
|
log.info("Install application: " + applicationUUID + " to: " + deviceList.size() + " devices.");
|
||||||
|
List<DeviceIdentifier> failedDeviceList = new ArrayList<>(deviceList);
|
||||||
for (DeviceIdentifier device : deviceList) {
|
for (DeviceIdentifier device : deviceList) {
|
||||||
String deviceId = device.getId();
|
org.wso2.carbon.device.mgt.common.DeviceIdentifier deviceIdentifier = new org.wso2.carbon.device.mgt
|
||||||
//Todo: implementation, validations
|
.common.DeviceIdentifier(device.getId(), device.getType());
|
||||||
//Todo: generating one time download link for the application and put install operation to device.
|
try {
|
||||||
//Todo: Store the mappings in DB.
|
DeviceManagementDAOFactory.openConnection();
|
||||||
|
if (DeviceManagementDAOFactory.getDeviceDAO().getDevice(deviceIdentifier).isEmpty()) {
|
||||||
|
log.error("Device with ID: " + device.getId() + " not found to install the application.");
|
||||||
|
} else {
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("Installing application to : " + device.getId());
|
||||||
}
|
}
|
||||||
return deviceList;
|
//Todo: generating one time download link for the application and put install operation to device.
|
||||||
|
DAOFactory.getSubscriptionDAO().addDeviceApplicationMapping(device.getId(), applicationUUID, false);
|
||||||
|
failedDeviceList.remove(device);
|
||||||
|
}
|
||||||
|
} catch (DeviceManagementDAOException | SQLException e) {
|
||||||
|
throw new ApplicationManagementException("Error locating device.", e);
|
||||||
|
} finally {
|
||||||
|
DeviceManagementDAOFactory.closeConnection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return failedDeviceList;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -52,6 +73,7 @@ public class SubscriptionManagerImpl implements SubscriptionManager {
|
|||||||
log.info("Install application: " + applicationUUID + " to: " + userList.size() + " users.");
|
log.info("Install application: " + applicationUUID + " to: " + userList.size() + " users.");
|
||||||
for (String user : userList) {
|
for (String user : userList) {
|
||||||
//Todo: implementation
|
//Todo: implementation
|
||||||
|
//Todo: get the device list and call installApplicationForDevices
|
||||||
}
|
}
|
||||||
return userList;
|
return userList;
|
||||||
}
|
}
|
||||||
@ -62,6 +84,7 @@ public class SubscriptionManagerImpl implements SubscriptionManager {
|
|||||||
log.info("Install application: " + applicationUUID + " to: " + roleList.size() + " users.");
|
log.info("Install application: " + applicationUUID + " to: " + roleList.size() + " users.");
|
||||||
for (String role : roleList) {
|
for (String role : roleList) {
|
||||||
//Todo: implementation
|
//Todo: implementation
|
||||||
|
//Todo: get the device list and call installApplicationForDevices
|
||||||
}
|
}
|
||||||
return roleList;
|
return roleList;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -390,6 +390,20 @@ CREATE TABLE IF NOT EXISTS `APPM_SUBSCRIPTION_PROPERTIES` (
|
|||||||
ON UPDATE NO ACTION)
|
ON UPDATE NO ACTION)
|
||||||
ENGINE = InnoDB;
|
ENGINE = InnoDB;
|
||||||
|
|
||||||
|
-- -----------------------------------------------------
|
||||||
|
-- Table `APPM_DEVICE_APPLICATION_MAPPING`
|
||||||
|
-- -----------------------------------------------------
|
||||||
|
CREATE TABLE IF NOT EXISTS `APPM_DEVICE_APPLICATION_MAPPING` (
|
||||||
|
`ID` INT AUTO_INCREMENT NOT NULL,
|
||||||
|
`DEVICE_IDENTIFIER` VARCHAR(255) NOT NULL,
|
||||||
|
`APPLICATION_UUID` VARCHAR(100) NOT NULL,
|
||||||
|
`INSTALLED` BOOLEAN NOT NULL,
|
||||||
|
`SENT_AT` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (ID),
|
||||||
|
CONSTRAINT `fk_appm_application` FOREIGN KEY (`APPLICATION_UUID`) REFERENCES
|
||||||
|
APPM_APPLICATION (`UUID`) ON DELETE NO ACTION ON UPDATE NO ACTION,
|
||||||
|
UNIQUE KEY `device_app_mapping` (`DEVICE_IDENTIFIER`, `APPLICATION_UUID`)
|
||||||
|
) ENGINE = InnoDB;
|
||||||
|
|
||||||
SET SQL_MODE=@OLD_SQL_MODE;
|
SET SQL_MODE=@OLD_SQL_MODE;
|
||||||
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
|
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user