[FIX] *: leftover deprecation warnings
@Julien00859 already made a pass on this at some point but there's a bunch of warnings either dynamic or from not the stdlib left over. Some of them can't really be fixed in-code and have been explicitly ignored instead: * `setuptools` uses the deprecated `imp` module until v41.6.0 * `zeep` uses defusedxml.lxml which was never intended to be a public / production API, not sure what to do there (cf mvantellingen/python-zeep#956) * there are a ton of invalid escapes in non-raw string literals, they're unlikely to ever be relevant so ignore them closes odoo/odoo#44164 Related: odoo/enterprise#8130 Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
This commit is contained in:
@@ -66,7 +66,7 @@ class OAuthLogin(Home):
|
||||
scope=provider['scope'],
|
||||
state=json.dumps(state),
|
||||
)
|
||||
provider['auth_link'] = "%s?%s" % (provider['auth_endpoint'], werkzeug.url_encode(params))
|
||||
provider['auth_link'] = "%s?%s" % (provider['auth_endpoint'], werkzeug.urls.url_encode(params))
|
||||
return providers
|
||||
|
||||
def get_state(self, provider):
|
||||
@@ -76,7 +76,7 @@ class OAuthLogin(Home):
|
||||
state = dict(
|
||||
d=request.session.db,
|
||||
p=provider['id'],
|
||||
r=werkzeug.url_quote_plus(redirect),
|
||||
r=werkzeug.urls.url_quote_plus(redirect),
|
||||
)
|
||||
token = request.params.get('token')
|
||||
if token:
|
||||
@@ -134,7 +134,7 @@ class OAuthController(http.Controller):
|
||||
cr.commit()
|
||||
action = state.get('a')
|
||||
menu = state.get('m')
|
||||
redirect = werkzeug.url_unquote_plus(state['r']) if state.get('r') else False
|
||||
redirect = werkzeug.urls.url_unquote_plus(state['r']) if state.get('r') else False
|
||||
url = '/web'
|
||||
if redirect:
|
||||
url = redirect
|
||||
|
||||
@@ -74,7 +74,7 @@ class GoogleDrive(models.Model):
|
||||
|
||||
try:
|
||||
req = requests.post(
|
||||
'https://spreadsheets.google.com/feeds/cells/%s/od6/private/full/batch?%s' % (spreadsheet_key, werkzeug.url_encode({'v': 3, 'access_token': access_token})),
|
||||
'https://spreadsheets.google.com/feeds/cells/%s/od6/private/full/batch?%s' % (spreadsheet_key, werkzeug.urls.url_encode({'v': 3, 'access_token': access_token})),
|
||||
data=request,
|
||||
headers={'content-type': 'application/atom+xml', 'If-Match': '*'},
|
||||
timeout=TIMEOUT,
|
||||
|
||||
@@ -4,14 +4,11 @@
|
||||
import base64
|
||||
from random import choice
|
||||
from string import digits
|
||||
import itertools
|
||||
from werkzeug import url_encode
|
||||
import pytz
|
||||
from werkzeug.urls import url_encode
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import ValidationError, AccessError
|
||||
from odoo.modules.module import get_module_resource
|
||||
from odoo.addons.resource.models.resource_mixin import timezone_datetime
|
||||
|
||||
|
||||
class HrEmployeePrivate(models.Model):
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from werkzeug import url_encode
|
||||
from werkzeug.urls import url_encode
|
||||
|
||||
|
||||
class HrExpenseSheetRegisterPaymentWizard(models.TransientModel):
|
||||
|
||||
@@ -7,7 +7,9 @@ import os
|
||||
import re
|
||||
import traceback
|
||||
import unicodedata
|
||||
import werkzeug
|
||||
import werkzeug.exceptions
|
||||
import werkzeug.routing
|
||||
import werkzeug.urls
|
||||
|
||||
# optional python-slugify import (https://github.com/un33k/python-slugify)
|
||||
try:
|
||||
@@ -550,8 +552,8 @@ class IrHttp(models.AbstractModel):
|
||||
return cls._handle_exception(e)
|
||||
|
||||
if getattr(request, 'is_frontend_multilang', False) and request.httprequest.method in ('GET', 'HEAD'):
|
||||
generated_path = werkzeug.url_unquote_plus(path)
|
||||
current_path = werkzeug.url_unquote_plus(request.httprequest.path)
|
||||
generated_path = werkzeug.urls.url_unquote_plus(path)
|
||||
current_path = werkzeug.urls.url_unquote_plus(request.httprequest.path)
|
||||
if generated_path != current_path:
|
||||
if request.lang != cls._get_default_lang():
|
||||
path = '/' + request.lang.url_code + path
|
||||
|
||||
@@ -148,7 +148,7 @@ class ResPartnerBank(models.Model):
|
||||
ref_type,
|
||||
reference,
|
||||
communication)
|
||||
qr_code_url = '/report/barcode/?type=%s&value=%s&width=%s&height=%s&humanreadable=1' % ('QR', werkzeug.url_quote_plus(qr_code_string), 256, 256)
|
||||
qr_code_url = '/report/barcode/?type=%s&value=%s&width=%s&height=%s&humanreadable=1' % ('QR', werkzeug.urls.url_quote_plus(qr_code_string), 256, 256)
|
||||
return qr_code_url
|
||||
|
||||
def validate_swiss_code_arguments(self, currency, debitor):
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
import base64
|
||||
import logging
|
||||
import psycopg2
|
||||
import werkzeug
|
||||
import werkzeug.utils
|
||||
import werkzeug.wrappers
|
||||
|
||||
from werkzeug import url_encode
|
||||
from werkzeug.urls import url_encode
|
||||
|
||||
from odoo import api, http, registry, SUPERUSER_ID, _
|
||||
from odoo.exceptions import AccessError
|
||||
|
||||
@@ -24,7 +24,6 @@ except ImportError:
|
||||
from collections import namedtuple
|
||||
from email.message import EmailMessage
|
||||
from lxml import etree
|
||||
from werkzeug import url_encode
|
||||
from werkzeug import urls
|
||||
|
||||
from odoo import _, api, exceptions, fields, models, tools, registry, SUPERUSER_ID
|
||||
@@ -2482,7 +2481,7 @@ class MailThread(models.AbstractModel):
|
||||
token = self._notify_encode_link(base_link, params)
|
||||
params['token'] = token
|
||||
|
||||
link = '%s?%s' % (base_link, url_encode(params))
|
||||
link = '%s?%s' % (base_link, urls.url_encode(params))
|
||||
if self:
|
||||
link = self[0].get_base_url() + link
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ from unicodedata import normalize
|
||||
|
||||
import requests
|
||||
from lxml import etree, objectify
|
||||
from werkzeug import urls, url_encode
|
||||
from werkzeug import urls
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.addons.payment.models.payment_acquirer import ValidationError
|
||||
@@ -177,7 +177,7 @@ class PaymentAcquirerOgone(models.Model):
|
||||
'DECLINEURL': urls.url_join(base_url, OgoneController._decline_url),
|
||||
'EXCEPTIONURL': urls.url_join(base_url, OgoneController._exception_url),
|
||||
'CANCELURL': urls.url_join(base_url, OgoneController._cancel_url),
|
||||
'PARAMPLUS': url_encode(param_plus),
|
||||
'PARAMPLUS': urls.url_encode(param_plus),
|
||||
}
|
||||
if self.save_token in ['ask', 'always']:
|
||||
temp_ogone_tx_values.update({
|
||||
@@ -365,7 +365,7 @@ class PaymentTxOgone(models.Model):
|
||||
'ECI': 9, # Recurring (from eCommerce)
|
||||
'ALIAS': self.payment_token_id.acquirer_ref,
|
||||
'RTIMEOUT': 30,
|
||||
'PARAMPLUS': url_encode(param_plus),
|
||||
'PARAMPLUS': urls.url_encode(param_plus),
|
||||
'EMAIL': self.partner_id.email or '',
|
||||
'CN': self.partner_id.name or '',
|
||||
}
|
||||
|
||||
@@ -352,8 +352,7 @@ class TestProductAttributeValueConfig(TestProductAttributeValueCommon):
|
||||
self.assertEqual(next(gen), computer_ssd_512 + computer_ram_32 + computer_hdd_1)
|
||||
self.assertEqual(next(gen), computer_ssd_512 + computer_ram_32 + computer_hdd_2)
|
||||
self.assertEqual(next(gen), computer_ssd_512 + computer_ram_32 + computer_hdd_4)
|
||||
with self.assertRaises(StopIteration):
|
||||
next(gen)
|
||||
self.assertIsNone(next(gen, None))
|
||||
|
||||
# Give priority to ram_16 but it is not allowed by hdd_1 so it should return hhd_2 instead
|
||||
# Test invalidate_cache on product.attribute.value write
|
||||
@@ -392,8 +391,7 @@ class TestProductAttributeValueConfig(TestProductAttributeValueCommon):
|
||||
self._add_exclude(computer_ram_32, computer_hdd_4)
|
||||
self.assertEqual(self.computer._get_first_possible_combination(), self.env['product.template.attribute.value'])
|
||||
gen = self.computer._get_possible_combinations()
|
||||
with self.assertRaises(StopIteration):
|
||||
next(gen)
|
||||
self.assertIsNone(next(gen, None))
|
||||
|
||||
def test_get_closest_possible_combinations(self):
|
||||
computer_ssd_256 = self._get_product_template_attribute_value(self.ssd_256)
|
||||
@@ -417,8 +415,7 @@ class TestProductAttributeValueConfig(TestProductAttributeValueCommon):
|
||||
self.assertEqual(next(gen), computer_ssd_256 + computer_ram_32 + computer_hdd_1)
|
||||
self.assertEqual(next(gen), computer_ssd_512 + computer_ram_8 + computer_hdd_1)
|
||||
self.assertEqual(next(gen), computer_ssd_512 + computer_ram_32 + computer_hdd_1)
|
||||
with self.assertRaises(StopIteration):
|
||||
next(gen)
|
||||
self.assertIsNone(next(gen, None))
|
||||
|
||||
# CASE contains computer_hdd_2
|
||||
self.assertEqual(self.computer._get_closest_possible_combination(computer_hdd_2),
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
try:
|
||||
from configparser import ConfigParser
|
||||
except ImportError:
|
||||
# python2 import
|
||||
from ConfigParser import ConfigParser
|
||||
from configparser import ConfigParser
|
||||
from os.path import join as opj
|
||||
import os
|
||||
import werkzeug
|
||||
import werkzeug.urls
|
||||
|
||||
import odoo
|
||||
from odoo import models, fields
|
||||
@@ -84,7 +80,7 @@ class IrTranslation(models.Model):
|
||||
'project': project,
|
||||
'lang': lang_code,
|
||||
'module': translation.module,
|
||||
'src': "text:'" + werkzeug.url_quote_plus(
|
||||
'src': "text:'" + werkzeug.urls.url_quote_plus(
|
||||
translation.src[:50].replace("\n", "").replace("'", "")
|
||||
) + "'",
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import werkzeug.utils
|
||||
import werkzeug.wrappers
|
||||
import werkzeug.wsgi
|
||||
from collections import OrderedDict, defaultdict, Counter
|
||||
from werkzeug.urls import url_decode, iri_to_uri
|
||||
from werkzeug.urls import url_encode, url_decode, iri_to_uri
|
||||
from lxml import etree
|
||||
import unicodedata
|
||||
|
||||
@@ -1257,7 +1257,7 @@ class Session(http.Controller):
|
||||
'state': json.dumps({'d': request.db, 'u': ICP.get_param('web.base.url')}),
|
||||
'scope': 'userinfo',
|
||||
}
|
||||
return 'https://accounts.odoo.com/oauth2/auth?' + werkzeug.url_encode(params)
|
||||
return 'https://accounts.odoo.com/oauth2/auth?' + url_encode(params)
|
||||
|
||||
@http.route('/web/session/destroy', type='json', auth="user")
|
||||
def destroy(self):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from werkzeug import url_quote
|
||||
from werkzeug.urls import url_quote
|
||||
|
||||
from odoo import api, models, fields, tools
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import os
|
||||
import logging
|
||||
import pytz
|
||||
import requests
|
||||
import werkzeug.urls
|
||||
import werkzeug.utils
|
||||
import werkzeug.wrappers
|
||||
|
||||
@@ -51,9 +52,9 @@ class QueryURL(object):
|
||||
paths[key] = u"%s" % value
|
||||
elif value:
|
||||
if isinstance(value, list) or isinstance(value, set):
|
||||
fragments.append(werkzeug.url_encode([(key, item) for item in value]))
|
||||
fragments.append(werkzeug.urls.url_encode([(key, item) for item in value]))
|
||||
else:
|
||||
fragments.append(werkzeug.url_encode([(key, value)]))
|
||||
fragments.append(werkzeug.urls.url_encode([(key, value)]))
|
||||
for key in path_args:
|
||||
value = paths.get(key)
|
||||
if value is not None:
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import werkzeug
|
||||
import werkzeug.urls
|
||||
|
||||
from odoo import models, fields
|
||||
|
||||
|
||||
def urlplus(url, params):
|
||||
return werkzeug.Href(url)(params or None)
|
||||
|
||||
|
||||
class Partner(models.Model):
|
||||
_name = 'res.partner'
|
||||
_inherit = ['res.partner', 'website.published.multi.mixin']
|
||||
@@ -27,14 +22,14 @@ class Partner(models.Model):
|
||||
'sensor': 'false',
|
||||
'key': google_maps_api_key,
|
||||
}
|
||||
return urlplus('//maps.googleapis.com/maps/api/staticmap', params)
|
||||
return '//maps.googleapis.com/maps/api/staticmap?'+werkzeug.urls.url_encode(params)
|
||||
|
||||
def google_map_link(self, zoom=10):
|
||||
params = {
|
||||
'q': '%s, %s %s, %s' % (self.street or '', self.city or '', self.zip or '', self.country_id and self.country_id.display_name or ''),
|
||||
'z': zoom,
|
||||
}
|
||||
return urlplus('https://maps.google.com/maps', params)
|
||||
return 'https://maps.google.com/maps' + werkzeug.urls.url_encode(params)
|
||||
|
||||
def _get_name(self):
|
||||
name = super(Partner, self)._get_name()
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import datetime
|
||||
import werkzeug
|
||||
import werkzeug.urls
|
||||
|
||||
from collections import OrderedDict
|
||||
from werkzeug.exceptions import NotFound
|
||||
@@ -308,7 +308,7 @@ class WebsiteCrmPartnerAssign(WebsitePartnerPage):
|
||||
'google_map_partner_ids': google_map_partner_ids,
|
||||
'pager': pager,
|
||||
'searches': post,
|
||||
'search_path': "%s" % werkzeug.url_encode(post),
|
||||
'search_path': "%s" % werkzeug.urls.url_encode(post),
|
||||
'google_maps_api_key': google_maps_api_key,
|
||||
}
|
||||
return request.render("website_crm_partner_assign.index", values, status=partners and 200 or 404)
|
||||
|
||||
@@ -132,7 +132,7 @@ class WebsiteCustomer(http.Controller):
|
||||
'google_map_partner_ids': google_map_partner_ids,
|
||||
'pager': pager,
|
||||
'post': post,
|
||||
'search_path': "?%s" % werkzeug.url_encode(post),
|
||||
'search_path': "?%s" % werkzeug.urls.url_encode(post),
|
||||
'tag': tag,
|
||||
'tags': tags,
|
||||
'google_maps_api_key': google_maps_api_key,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import werkzeug
|
||||
import werkzeug.urls
|
||||
import json
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
@@ -147,7 +147,7 @@ class Event(models.Model):
|
||||
}
|
||||
if self.address_id:
|
||||
params.update(location=self.sudo().address_id.contact_address.replace('\n', ' '))
|
||||
encoded_params = werkzeug.url_encode(params)
|
||||
encoded_params = werkzeug.urls.url_encode(params)
|
||||
google_url = GOOGLE_CALENDAR_URL + encoded_params
|
||||
iCal_url = '/event/%d/ics?%s' % (self.id, encoded_params)
|
||||
return {'google_url': google_url, 'iCal_url': iCal_url}
|
||||
|
||||
@@ -155,7 +155,7 @@ class WebsiteMembership(http.Controller):
|
||||
'google_map_partner_ids': google_map_partner_ids,
|
||||
'pager': pager,
|
||||
'post': post,
|
||||
'search': "?%s" % werkzeug.url_encode(post),
|
||||
'search': "?%s" % werkzeug.urls.url_encode(post),
|
||||
'search_count': count_members,
|
||||
'google_maps_api_key': google_maps_api_key,
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import uuid
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from difflib import HtmlDiff
|
||||
|
||||
import werkzeug
|
||||
import werkzeug, werkzeug.urls
|
||||
from lxml import etree
|
||||
from lxml.etree import LxmlError
|
||||
from lxml.builder import E
|
||||
@@ -1595,7 +1595,7 @@ actual arch.
|
||||
debug=request.session.debug if request else '',
|
||||
test_mode_enabled=bool(config['test_enable'] or config['test_file']),
|
||||
json=json_scriptsafe,
|
||||
quote_plus=werkzeug.url_quote_plus,
|
||||
quote_plus=werkzeug.urls.url_quote_plus,
|
||||
time=time,
|
||||
datetime=datetime,
|
||||
relativedelta=relativedelta,
|
||||
|
||||
@@ -132,7 +132,7 @@ class ResPartnerBank(models.Model):
|
||||
if comment:
|
||||
communication = (comment[:137] + '...') if len(comment) > 140 else comment
|
||||
qr_code_string = 'BCD\n001\n1\nSCT\n%s\n%s\n%s\nEUR%s\n\n\n%s' % (self.bank_bic or "", self.company_id.name, self.acc_number, amount, communication)
|
||||
qr_code_url = '/report/barcode/?type=%s&value=%s&width=%s&height=%s&humanreadable=1' % ('QR', werkzeug.url_quote_plus(qr_code_string), 128, 128)
|
||||
qr_code_url = '/report/barcode/?' + werkzeug.urls.url_encode({'type': 'QR', 'value': qr_code_string, 'width': 128, 'height': 128, 'humanreadable': 1})
|
||||
return qr_code_url
|
||||
|
||||
def _validate_qr_code_arguments(self):
|
||||
|
||||
@@ -296,8 +296,9 @@ class Users(models.Model):
|
||||
|
||||
def _set_password(self):
|
||||
ctx = self._crypt_context()
|
||||
hash_password = ctx.hash if hasattr(ctx, 'hash') else ctx.encrypt
|
||||
for user in self:
|
||||
self._set_encrypted_password(user.id, ctx.encrypt(user.password))
|
||||
self._set_encrypted_password(user.id, hash_password(user.password))
|
||||
|
||||
def _set_encrypted_password(self, uid, pw):
|
||||
assert self._crypt_context().identify(pw) != 'plaintext'
|
||||
|
||||
@@ -9,8 +9,9 @@ class TestBasicInheritance(common.TransactionCase):
|
||||
|
||||
record = env['extension.0'].create({})
|
||||
|
||||
self.assertDictContainsSubset(
|
||||
reference =\
|
||||
{'name': "A", 'description': "Extended"}
|
||||
,
|
||||
values =\
|
||||
record.read()[0]
|
||||
)
|
||||
|
||||
self.assertLess(reference.items(), values.items())
|
||||
|
||||
+1
-1
@@ -167,7 +167,7 @@ def local_redirect(path, query=None, keep_hash=False, code=303):
|
||||
if not query:
|
||||
query = {}
|
||||
if query:
|
||||
url += '?' + werkzeug.url_encode(query)
|
||||
url += '?' + urls.url_encode(query)
|
||||
return werkzeug.utils.redirect(url, code)
|
||||
|
||||
def redirect_with_hash(url, code=303):
|
||||
|
||||
+1
-1
@@ -5625,7 +5625,7 @@ Record ids: %(records)s
|
||||
return set(self._ids) >= set(other._ids)
|
||||
|
||||
def __int__(self):
|
||||
return self.id
|
||||
return self.id or 0
|
||||
|
||||
def __str__(self):
|
||||
return "%s%s" % (self._name, getattr(self, '_ids', ""))
|
||||
|
||||
@@ -3,26 +3,20 @@
|
||||
|
||||
import ast
|
||||
import collections
|
||||
import imp
|
||||
import importlib
|
||||
import inspect
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
import pkg_resources
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import types
|
||||
import unittest
|
||||
import threading
|
||||
from operator import itemgetter
|
||||
from os.path import join as opj
|
||||
|
||||
import odoo
|
||||
import odoo.tools as tools
|
||||
import odoo.release as release
|
||||
from odoo import SUPERUSER_ID, api
|
||||
from odoo.tools import pycompat
|
||||
|
||||
MANIFEST_NAMES = ('__manifest__.py', '__openerp__.py')
|
||||
|
||||
+12
-4
@@ -6,14 +6,12 @@ import logging.handlers
|
||||
import os
|
||||
import platform
|
||||
import pprint
|
||||
from . import release
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
import warnings
|
||||
|
||||
import psycopg2
|
||||
|
||||
import odoo
|
||||
from . import release
|
||||
from . import sql_db
|
||||
from . import tools
|
||||
|
||||
@@ -130,6 +128,16 @@ def init_logger():
|
||||
logging.addLevelName(25, "INFO")
|
||||
logging.captureWarnings(True)
|
||||
|
||||
# enable deprecation warnings (disabled by default)
|
||||
warnings.filterwarnings('once', category=DeprecationWarning)
|
||||
# ignore deprecation warnings from invalid escape (there's a ton and it's
|
||||
# pretty likely a super low-value signal)
|
||||
warnings.filterwarnings('ignore', r'^invalid escape sequence \\.', category=DeprecationWarning)
|
||||
# ignore warning from older setuptools version using imp
|
||||
warnings.filterwarnings('ignore', category=DeprecationWarning, module='setuptools.depends')
|
||||
# ignore warning from zeep using defusedxml.lxml
|
||||
warnings.filterwarnings('ignore', category=DeprecationWarning, module='zeep.loader')
|
||||
|
||||
from .tools.translate import resetlocale
|
||||
resetlocale()
|
||||
|
||||
|
||||
@@ -694,7 +694,8 @@ class configmanager(object):
|
||||
return os.path.join(self['data_dir'], 'filestore', dbname)
|
||||
|
||||
def set_admin_password(self, new_password):
|
||||
self.options['admin_passwd'] = crypt_context.encrypt(new_password)
|
||||
hash_password = crypt_context.hash if hasattr(crypt_context, 'hash') else crypt_context.encrypt
|
||||
self.options['admin_passwd'] = hash_password(new_password)
|
||||
|
||||
def verify_admin_password(self, password):
|
||||
"""Verifies the super-admin password, possibly updating the stored hash if needed"""
|
||||
|
||||
Reference in New Issue
Block a user