mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-14 17:58:59 +00:00
Merged DEV to HEAD (5.0)
87810: ACE-2030: Moved retired patches into retired-patches-context.xml 87811: ACE-2030: Clean up unused PatchDAO methods and SQL mappings git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@87814 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -27,7 +27,7 @@ import java.util.Set;
|
||||
|
||||
import org.alfresco.ibatis.IdsEntity;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.domain.CrcHelper;
|
||||
import org.alfresco.repo.domain.contentdata.ContentDataDAO;
|
||||
import org.alfresco.repo.domain.locale.LocaleDAO;
|
||||
import org.alfresco.repo.domain.node.ChildAssocEntity;
|
||||
import org.alfresco.repo.domain.patch.AbstractPatchDAOImpl;
|
||||
@@ -38,12 +38,10 @@ import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.util.Pair;
|
||||
import org.alfresco.util.ParameterCheck;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.ibatis.session.ResultContext;
|
||||
import org.apache.ibatis.session.ResultHandler;
|
||||
import org.apache.ibatis.session.RowBounds;
|
||||
import org.mybatis.spring.SqlSessionTemplate;
|
||||
|
||||
/**
|
||||
@@ -54,28 +52,13 @@ import org.mybatis.spring.SqlSessionTemplate;
|
||||
*/
|
||||
public class PatchDAOImpl extends AbstractPatchDAOImpl
|
||||
{
|
||||
@SuppressWarnings("unused")
|
||||
private static Log logger = LogFactory.getLog(PatchDAOImpl.class);
|
||||
|
||||
private static final String SELECT_ADM_MAX_NODE_ID = "alfresco.patch.select_admMaxNodeId";
|
||||
private static final String SELECT_ADM_OLD_CONTENT_PROPERTIES = "alfresco.patch.select_admOldContentProperties";
|
||||
private static final String SELECT_AUTHORITIES_AND_CRC = "alfresco.patch.select_authoritiesAndCrc";
|
||||
private static final String SELECT_PERMISSIONS_MAX_ACL_ID = "alfresco.patch.select_MaxAclId";
|
||||
private static final String SELECT_PERMISSIONS_DM_NODE_COUNT = "alfresco.patch.select_DmNodeCount";
|
||||
private static final String SELECT_PERMISSIONS_DM_NODE_COUNT_WITH_NEW_ACLS = "alfresco.patch.select_DmNodeCountWherePermissionsHaveChanged";
|
||||
private static final String SELECT_CHILD_ASSOCS_COUNT = "alfresco.patch.select_allChildAssocsCount";
|
||||
private static final String SELECT_CHILD_ASSOCS_MAX_ID = "alfresco.patch.select_maxChildAssocId";
|
||||
private static final String SELECT_CHILD_ASSOCS_FOR_CRCS = "alfresco.patch.select_allChildAssocsForCrcs";
|
||||
private static final String SELECT_NODES_BY_TYPE_AND_NAME_PATTERN = "alfresco.patch.select_nodesByTypeAndNamePattern";
|
||||
|
||||
private static final String UPDATE_ADM_OLD_CONTENT_PROPERTY = "alfresco.patch.update_admOldContentProperty";
|
||||
private static final String UPDATE_CONTENT_MIMETYPE_ID = "alfresco.patch.update_contentMimetypeId";
|
||||
private static final String UPDATE_CHILD_ASSOC_CRC = "alfresco.patch.update_childAssocCrc";
|
||||
private static final String UPDATE_CREATE_SIZE_CURRENT_PROPERTY = "alfresco.patch.update_CreateSizeCurrentProperty";
|
||||
|
||||
private static final String SELECT_OLD_ATTR_TENANTS = "alfresco.patch.select_oldAttrTenants";
|
||||
private static final String SELECT_OLD_ATTR_PBBS = "alfresco.patch.select_oldAttrPropertyBackedBeans";
|
||||
private static final String SELECT_OLD_ATTR_CHAINING_URS = "alfresco.patch.select_oldAttrChainingURS";
|
||||
private static final String SELECT_OLD_ATTR_CUSTOM_NAMES = "alfresco.patch.select_oldAttrCustomNames";
|
||||
|
||||
private static final String DROP_OLD_ATTR_LIST = "alfresco.patch.drop_oldAttrAlfListAttributeEntries";
|
||||
private static final String DROP_OLD_ATTR_MAP = "alfresco.patch.drop_oldAttrAlfMapAttributeEntries";
|
||||
@@ -99,7 +82,11 @@ public class PatchDAOImpl extends AbstractPatchDAOImpl
|
||||
private static final String SELECT_COUNT_NODES_WITH_TYPE_ID = "alfresco.patch.select_CountNodesWithTypeId";
|
||||
private static final String SELECT_CHILDREN_OF_THE_SHARED_SURFCONFIG_FOLDER = "alfresco.patch.select_ChildrenOfTheSharedSurfConfigFolder";
|
||||
|
||||
private QNameDAO qnameDAO;
|
||||
@SuppressWarnings("unused")
|
||||
private LocaleDAO localeDAO;
|
||||
@SuppressWarnings("unused")
|
||||
private ContentDataDAO contentDataDAO;
|
||||
|
||||
protected SqlSessionTemplate template;
|
||||
|
||||
@@ -108,19 +95,20 @@ public class PatchDAOImpl extends AbstractPatchDAOImpl
|
||||
this.template = sqlSessionTemplate;
|
||||
}
|
||||
|
||||
|
||||
private QNameDAO qnameDAO;
|
||||
|
||||
public void setQnameDAO(QNameDAO qnameDAO)
|
||||
{
|
||||
this.qnameDAO = qnameDAO;
|
||||
}
|
||||
|
||||
public void setLocaleDAO(LocaleDAO localeDAO)
|
||||
{
|
||||
this.localeDAO = localeDAO;
|
||||
}
|
||||
public void setContentDataDAO(ContentDataDAO contentDataDAO)
|
||||
{
|
||||
this.contentDataDAO = contentDataDAO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startBatch()
|
||||
{
|
||||
// TODO
|
||||
@@ -136,6 +124,7 @@ public class PatchDAOImpl extends AbstractPatchDAOImpl
|
||||
*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeBatch()
|
||||
{
|
||||
// TODO
|
||||
@@ -151,34 +140,14 @@ public class PatchDAOImpl extends AbstractPatchDAOImpl
|
||||
*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMaxAdmNodeID()
|
||||
{
|
||||
Long count = template.selectOne(SELECT_ADM_MAX_NODE_ID);
|
||||
return count == null ? 0L : count;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected List<Map<String, Object>> getAdmOldContentProperties(Long minNodeId, Long maxNodeId)
|
||||
{
|
||||
IdsEntity ids = new IdsEntity();
|
||||
ids.setIdOne(minNodeId);
|
||||
ids.setIdTwo(maxNodeId);
|
||||
return template.selectList(SELECT_ADM_OLD_CONTENT_PROPERTIES, ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateAdmOldContentProperty(Long nodeId, Long qnameId, Integer listIndex, Long localeId, Long longValue)
|
||||
{
|
||||
Map<String, Object> params = new HashMap<String, Object>(11);
|
||||
params.put("nodeId", nodeId);
|
||||
params.put("qnameId", qnameId);
|
||||
params.put("listIndex", listIndex);
|
||||
params.put("localeId", localeId);
|
||||
params.put("longValue", longValue);
|
||||
template.update(UPDATE_ADM_OLD_CONTENT_PROPERTY, params);
|
||||
}
|
||||
|
||||
public int updateContentMimetypeIds(Long oldMimetypeId, Long newMimetypeId)
|
||||
{
|
||||
Map<String, Object> params = new HashMap<String, Object>(11);
|
||||
@@ -188,175 +157,6 @@ public class PatchDAOImpl extends AbstractPatchDAOImpl
|
||||
}
|
||||
|
||||
@Override
|
||||
public int addSizeCurrentProp()
|
||||
{
|
||||
Long sizeCurrentPropQNameId = qnameDAO.getOrCreateQName(ContentModel.PROP_SIZE_CURRENT).getFirst();
|
||||
Long defaultLocaleId = localeDAO.getOrCreateDefaultLocalePair().getFirst();
|
||||
Long personTypeQNameId = qnameDAO.getOrCreateQName(ContentModel.TYPE_PERSON).getFirst();
|
||||
|
||||
SizeCurrentParams params = new SizeCurrentParams();
|
||||
params.setSizeCurrentQNameId(sizeCurrentPropQNameId);
|
||||
params.setDefaultLocaleId(defaultLocaleId);
|
||||
params.setPersonTypeQNameId(personTypeQNameId);
|
||||
|
||||
int rowsAffected = template.update(UPDATE_CREATE_SIZE_CURRENT_PROPERTY, params);
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Added " + rowsAffected + " cm:sizeCurrent properties.");
|
||||
}
|
||||
return rowsAffected;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long getMaxAclEntityId()
|
||||
{
|
||||
Long count = template.selectOne(SELECT_PERMISSIONS_MAX_ACL_ID, null);
|
||||
return count == null ? 0L : count;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long getDmNodeEntitiesCount()
|
||||
{
|
||||
Long count = template.selectOne(SELECT_PERMISSIONS_DM_NODE_COUNT, null);
|
||||
return count == null ? 0L : count;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long getDmNodeEntitiesCountWithNewACLs(Long above)
|
||||
{
|
||||
Map<String, Object> params = new HashMap<String, Object>(1);
|
||||
params.put("id", above);
|
||||
Long count = template.selectOne(SELECT_PERMISSIONS_DM_NODE_COUNT_WITH_NEW_ACLS, params);
|
||||
return count == null ? 0L : count;
|
||||
}
|
||||
|
||||
public List<String> getAuthoritiesWithNonUtf8Crcs()
|
||||
{
|
||||
final List<String> results = new ArrayList<String>(1000);
|
||||
ResultHandler resultHandler = new ResultHandler()
|
||||
{
|
||||
@SuppressWarnings("unchecked")
|
||||
public void handleResult(ResultContext context)
|
||||
{
|
||||
Map<String, Object> result = (Map<String, Object>) context.getResultObject();
|
||||
String authority = (String) result.get("authority");
|
||||
Long crc = (Long) result.get("crc");
|
||||
Long crcShouldBe = CrcHelper.getStringCrcPair(authority, 32, true, true).getSecond();
|
||||
if (!crcShouldBe.equals(crc))
|
||||
{
|
||||
// One to fix
|
||||
results.add(authority);
|
||||
}
|
||||
}
|
||||
};
|
||||
template.select(SELECT_AUTHORITIES_AND_CRC, resultHandler);
|
||||
// Done
|
||||
return results;
|
||||
}
|
||||
|
||||
public int getChildAssocCount()
|
||||
{
|
||||
return template.selectOne(SELECT_CHILD_ASSOCS_COUNT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getMaxChildAssocId()
|
||||
{
|
||||
Long maxAssocId = template.selectOne(SELECT_CHILD_ASSOCS_MAX_ID);
|
||||
return maxAssocId == null ? 0L : maxAssocId;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Map<String, Object>> getChildAssocsForCrcFix(
|
||||
Long minAssocId,
|
||||
Long stopAtAssocId,
|
||||
long rangeMultiplier,
|
||||
long maxIdRange,
|
||||
int maxResults)
|
||||
{
|
||||
ParameterCheck.mandatory("minAssocId", minAssocId);
|
||||
ParameterCheck.mandatory("stopAtAssocId", stopAtAssocId);
|
||||
/*
|
||||
* ALF-4529: Database connection problems when upgrading large sample 2.1.x data set
|
||||
* We have to set an upper bound on the query that is driven by an index
|
||||
* otherwise we get OOM on the resultset, even with a limit.
|
||||
* Since there can be voids in the sequence, we have to check if we have hit the max ID, yet.
|
||||
*/
|
||||
Long qnameId = qnameDAO.getOrCreateQName(ContentModel.PROP_NAME).getFirst();
|
||||
|
||||
int queryMaxResults = maxResults;
|
||||
List<Map<String, Object>> results = new ArrayList<Map<String,Object>>(maxResults);
|
||||
while (results.size() < maxResults && minAssocId <= stopAtAssocId)
|
||||
{
|
||||
// Avoid getting too few results because of voids.
|
||||
// On the other hand, the distribution of child assoc types can result in swathes of
|
||||
// the table containing voids and rows of no interest. So we ramp up the multiplier
|
||||
// to take larger and larger ID ranges in order to quickly walk through these zones.
|
||||
Long maxAssocId = minAssocId + Math.min(maxResults * rangeMultiplier, maxIdRange);
|
||||
|
||||
IdsEntity entity = new IdsEntity();
|
||||
entity.setIdOne(qnameId);
|
||||
entity.setIdTwo(minAssocId);
|
||||
entity.setIdThree(maxAssocId);
|
||||
|
||||
try
|
||||
{
|
||||
List<Map<String, Object>> rows = template.selectList(SELECT_CHILD_ASSOCS_FOR_CRCS, entity, new RowBounds(0, queryMaxResults));
|
||||
if (results.size() == 0 && rows.size() >= maxResults)
|
||||
{
|
||||
// We have all we need
|
||||
results = rows;
|
||||
break;
|
||||
}
|
||||
// Add these rows to the result
|
||||
results.addAll(rows);
|
||||
// Calculate new maxResults
|
||||
queryMaxResults = maxResults - results.size();
|
||||
// Move the minAssocId up to ensure we get new results
|
||||
// If we got fewer results than queryMaxResults, then there were too many voids and we
|
||||
// requery using the previous maxAssocId
|
||||
minAssocId = maxAssocId;
|
||||
// Double the range multiplier if we have a low hit-rate (<50% of desired size)
|
||||
// and we can avoid integer overflow
|
||||
if (rows.size() < queryMaxResults / 2 )
|
||||
{
|
||||
long newRangeMultiplier = rangeMultiplier * 2L;
|
||||
long newIdRange = maxResults * newRangeMultiplier;
|
||||
if (newIdRange > 0 && newIdRange < maxIdRange)
|
||||
{
|
||||
rangeMultiplier = newRangeMultiplier;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
// Hit a DB problem. Log all the details of the query so that parameters can be adjusted externally.
|
||||
String msg =
|
||||
"Failed to query for batch of alf_child_assoc rows; use a lower 'maxIdRange': \n" +
|
||||
" minAssocId: " + minAssocId + "\n" +
|
||||
" maxAssocId: " + maxAssocId + "\n" +
|
||||
" maxIdRange: " + maxIdRange + "\n" +
|
||||
" stopAtAssocId: " + stopAtAssocId + "\n" +
|
||||
" rangeMultiplier: " + rangeMultiplier + "\n" +
|
||||
" queryMaxResults: " + queryMaxResults;
|
||||
logger.error(msg);
|
||||
throw new RuntimeException(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
// Done
|
||||
return results;
|
||||
}
|
||||
|
||||
public int updateChildAssocCrc(Long assocId, Long childNodeNameCrc, Long qnameCrc)
|
||||
{
|
||||
Map<String, Object> params = new HashMap<String, Object>();
|
||||
params.put("id", assocId);
|
||||
params.put("childNodeNameCrc", childNodeNameCrc);
|
||||
params.put("qnameCrc", qnameCrc);
|
||||
return template.update(UPDATE_CHILD_ASSOC_CRC, params);
|
||||
}
|
||||
|
||||
public List<Pair<NodeRef, String>> getNodesOfTypeWithNamePattern(QName typeQName, String namePattern)
|
||||
{
|
||||
Pair<Long, QName> typeQNamePair = qnameDAO.getQName(typeQName);
|
||||
@@ -399,31 +199,6 @@ public class PatchDAOImpl extends AbstractPatchDAOImpl
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void getOldAttrTenantsImpl(ResultHandler resultHandler)
|
||||
{
|
||||
template.select(SELECT_OLD_ATTR_TENANTS, resultHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void getOldAttrPropertyBackedBeansImpl(ResultHandler resultHandler)
|
||||
{
|
||||
template.select(SELECT_OLD_ATTR_PBBS, resultHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void getOldAttrChainingURSImpl(ResultHandler resultHandler)
|
||||
{
|
||||
template.select(SELECT_OLD_ATTR_CHAINING_URS, resultHandler);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected List<String> getOldAttrCustomNamesImpl()
|
||||
{
|
||||
return template.selectList(SELECT_OLD_ATTR_CUSTOM_NAMES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void migrateOldAttrDropTables()
|
||||
{
|
||||
@@ -432,40 +207,7 @@ public class PatchDAOImpl extends AbstractPatchDAOImpl
|
||||
template.update(DROP_OLD_ATTR_GLOBAL);
|
||||
template.update(DROP_OLD_ATTR);
|
||||
}
|
||||
|
||||
/**
|
||||
* PostgreSQL-specific DAO
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 4.0
|
||||
*/
|
||||
public static class PostgreSQL extends PatchDAOImpl
|
||||
{
|
||||
@Override
|
||||
public void migrateOldAttrDropTables()
|
||||
{
|
||||
super.migrateOldAttrDropTables();
|
||||
template.update(DROP_OLD_ATTR_SEQ);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Oracle-specific DAO
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 4.0
|
||||
*/
|
||||
public static class Oracle extends PatchDAOImpl
|
||||
{
|
||||
@Override
|
||||
public void migrateOldAttrDropTables()
|
||||
{
|
||||
super.migrateOldAttrDropTables();
|
||||
template.update(DROP_OLD_ATTR_SEQ);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public List<Map<String, Object>> getAclsThatInheritFromNonPrimaryParent()
|
||||
{
|
||||
@@ -475,7 +217,6 @@ public class PatchDAOImpl extends AbstractPatchDAOImpl
|
||||
return rows;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public List<Map<String, Object>> getAclsThatInheritWithInheritanceUnset()
|
||||
{
|
||||
@@ -485,7 +226,6 @@ public class PatchDAOImpl extends AbstractPatchDAOImpl
|
||||
return rows;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public List<Map<String, Object>> getDefiningAclsThatDoNotInheritCorrectlyFromThePrimaryParent()
|
||||
{
|
||||
@@ -495,7 +235,6 @@ public class PatchDAOImpl extends AbstractPatchDAOImpl
|
||||
return rows;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public List<Map<String, Object>> getSharedAclsThatDoNotInheritCorrectlyFromThePrimaryParent()
|
||||
{
|
||||
@@ -505,7 +244,6 @@ public class PatchDAOImpl extends AbstractPatchDAOImpl
|
||||
return rows;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public List<Map<String, Object>> getSharedAclsThatDoNotInheritCorrectlyFromTheirDefiningAcl()
|
||||
{
|
||||
@@ -537,8 +275,6 @@ public class PatchDAOImpl extends AbstractPatchDAOImpl
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public List<Long> getNodesByTypeQNameId(Long typeQNameId, Long minNodeId, Long maxNodeId)
|
||||
{
|
||||
@@ -549,7 +285,6 @@ public class PatchDAOImpl extends AbstractPatchDAOImpl
|
||||
return template.selectList(SELECT_NODES_BY_TYPE_QNAME, params);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public List<Long> getNodesByTypeUriId(Long nsId, Long minNodeId, Long maxNodeId)
|
||||
{
|
||||
@@ -560,7 +295,6 @@ public class PatchDAOImpl extends AbstractPatchDAOImpl
|
||||
return template.selectList(SELECT_NODES_BY_TYPE_URI, params);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public List<Long> getNodesByAspectQNameId(Long aspectQNameId, Long minNodeId, Long maxNodeId)
|
||||
{
|
||||
@@ -571,7 +305,6 @@ public class PatchDAOImpl extends AbstractPatchDAOImpl
|
||||
return template.selectList(SELECT_NODES_BY_ASPECT_QNAME, params);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public List<Long> getNodesByContentPropertyMimetypeId(Long mimetypeId, Long minNodeId, Long maxNodeId)
|
||||
{
|
||||
@@ -604,7 +337,6 @@ public class PatchDAOImpl extends AbstractPatchDAOImpl
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<NodeRef> getChildrenOfTheSharedSurfConfigFolder(Long minNodeId, Long maxNodeId)
|
||||
{
|
||||
@@ -647,4 +379,36 @@ public class PatchDAOImpl extends AbstractPatchDAOImpl
|
||||
template.select(SELECT_CHILDREN_OF_THE_SHARED_SURFCONFIG_FOLDER, params, resultHandler);
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* PostgreSQL-specific DAO
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 4.0
|
||||
*/
|
||||
public static class PostgreSQL extends PatchDAOImpl
|
||||
{
|
||||
@Override
|
||||
public void migrateOldAttrDropTables()
|
||||
{
|
||||
super.migrateOldAttrDropTables();
|
||||
template.update(DROP_OLD_ATTR_SEQ);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Oracle-specific DAO
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 4.0
|
||||
*/
|
||||
public static class Oracle extends PatchDAOImpl
|
||||
{
|
||||
@Override
|
||||
public void migrateOldAttrDropTables()
|
||||
{
|
||||
super.migrateOldAttrDropTables();
|
||||
template.update(DROP_OLD_ATTR_SEQ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user