[MERGE]: Improvements in Outlook and Thunderbird Plug ins
bzr revid: sbh@tinyerp.com-20101119073651-abx9d05b0n0akh8j
This commit is contained in:
@@ -50,121 +50,71 @@ def GetConn():
|
||||
d=Dispatch("Python.OpenERP.XMLRpcConn")
|
||||
return d
|
||||
class Configuration:
|
||||
def Init(self, application):
|
||||
self.mngr = None
|
||||
|
||||
def Close(self):
|
||||
self.mngr = None
|
||||
self.close()
|
||||
win32ui.MessageBox("Configuration","Configuration")
|
||||
|
||||
def OnClick(self, button, cancel):
|
||||
import win32ui
|
||||
try:
|
||||
self.mngr = manager.GetManager()
|
||||
self.mngr.ShowManager()
|
||||
mngr = manager.GetManager()
|
||||
mngr.ShowManager()
|
||||
except Exception,e:
|
||||
win32ui.MessageBox("Fail to Initialize dialog.\n"+str(e),"OpenERP Configuration", win32con.MB_ICONERROR)
|
||||
return cancel
|
||||
#
|
||||
class ViewPartners:
|
||||
def Init(self, application):
|
||||
self.mngr = None
|
||||
self.data = None
|
||||
self.outlook = None
|
||||
self.ex= None
|
||||
|
||||
def Close(self):
|
||||
self.mngr = None
|
||||
self.data = None
|
||||
self.outlook = None
|
||||
self.ex= None
|
||||
self.close()
|
||||
win32ui.MessageBox("ViewPartners","ViewPartners")
|
||||
|
||||
def OnClick(self, button, cancel):
|
||||
from win32com.client import Dispatch
|
||||
import win32con
|
||||
self.mngr = manager.GetManager()
|
||||
self.data = self.mngr.LoadConfig()
|
||||
self.outlook = Dispatch("Outlook.Application")
|
||||
self.ex = self.outlook.ActiveExplorer()
|
||||
if self.ex:
|
||||
is_login = str(data['login'])
|
||||
if is_login == 'False':
|
||||
win32ui.MessageBox("Please login to the database first", "OpenERP Connection", win32con.MB_ICONEXCLAMATION)
|
||||
elif self.ex.Selection.Count == 1 or ex.Selection.Count == 0:
|
||||
self.mngr = manager.GetManager()
|
||||
self.mngr.ShowManager("IDD_VIEW_PARTNER_DIALOG")
|
||||
elif self.ex.Selection.Count > 1:
|
||||
win32ui.MessageBox("Multiple selection not allowed. Please select only one mail at a time.","Open Contact",win32con.MB_ICONINFORMATION)
|
||||
return cancel
|
||||
#
|
||||
class OpenPartner:
|
||||
def Init(self, application):
|
||||
self.mngr = None
|
||||
self.data = None
|
||||
self.outlook = None
|
||||
self.ex= None
|
||||
|
||||
def Close(self):
|
||||
self.mngr = None
|
||||
self.data = None
|
||||
self.outlook = None
|
||||
self.ex= None
|
||||
self.close()
|
||||
win32ui.MessageBox("OpenPartner","OpenPartner")
|
||||
|
||||
def OnClick(self, button, cancel):
|
||||
import win32ui
|
||||
from manager import ustr
|
||||
self.mngr = manager.GetManager()
|
||||
self.data = self.mngr.LoadConfig()
|
||||
self.outlook = Dispatch("Outlook.Application")
|
||||
self.ex = self.outlook.ActiveExplorer()
|
||||
mngr = manager.GetManager()
|
||||
data = mngr.LoadConfig()
|
||||
outlook = Dispatch("Outlook.Application")
|
||||
ex = outlook.ActiveExplorer()
|
||||
if ex:
|
||||
is_login = str(data['login'])
|
||||
if is_login == 'False':
|
||||
win32ui.MessageBox("Please login to the database first", "OpenERP Connection", win32con.MB_ICONEXCLAMATION)
|
||||
elif self.ex.Selection.Count == 1:
|
||||
self.mngr = manager.GetManager()
|
||||
self.mngr.ShowManager("IDD_OPEN_PARTNER_DIALOG")
|
||||
elif self.ex.Selection.Count == 0:
|
||||
elif ex.Selection.Count == 1 or ex.Selection.Count == 0:
|
||||
mngr = manager.GetManager()
|
||||
mngr.ShowManager("IDD_VIEW_PARTNER_DIALOG")
|
||||
elif ex.Selection.Count > 1:
|
||||
win32ui.MessageBox("Multiple selection not allowed. Please select only one mail at a time.","Open Contact",win32con.MB_ICONINFORMATION)
|
||||
return cancel
|
||||
#
|
||||
class OpenPartner:
|
||||
def OnClick(self, button, cancel):
|
||||
import win32ui
|
||||
mngr = manager.GetManager()
|
||||
data = mngr.LoadConfig()
|
||||
outlook = Dispatch("Outlook.Application")
|
||||
ex = outlook.ActiveExplorer()
|
||||
if ex:
|
||||
is_login = str(data['login'])
|
||||
if is_login == 'False':
|
||||
win32ui.MessageBox("Please login to the database first", "OpenERP Connection", win32con.MB_ICONEXCLAMATION)
|
||||
elif ex.Selection.Count == 1:
|
||||
mngr = manager.GetManager()
|
||||
mngr.ShowManager("IDD_OPEN_PARTNER_DIALOG")
|
||||
elif ex.Selection.Count == 0:
|
||||
win32ui.MessageBox("No mail selected to push to OpenERP","Push to OpenERP",win32con.MB_ICONINFORMATION)
|
||||
elif self.ex.Selection.Count > 1:
|
||||
elif ex.Selection.Count > 1:
|
||||
win32ui.MessageBox("Multiple selection not allowed. Please select only one mail at a time.","Push to OpenERP",win32con.MB_ICONINFORMATION)
|
||||
return cancel
|
||||
#
|
||||
class OpenDocument:
|
||||
def Init(self, application):
|
||||
self.mngr = None
|
||||
self.data = None
|
||||
self.outlook = None
|
||||
self.ex= None
|
||||
|
||||
def Close(self):
|
||||
self.mngr = None
|
||||
self.data = None
|
||||
self.outlook = None
|
||||
self.ex= None
|
||||
self.close()
|
||||
win32ui.MessageBox("OpenDocument","OpenDocument")
|
||||
|
||||
def OnClick(self, button, cancel):
|
||||
from win32com.client import Dispatch
|
||||
import win32con
|
||||
self.mngr = manager.GetManager()
|
||||
self.data = self.mngr.LoadConfig()
|
||||
self.outlook = Dispatch("Outlook.Application")
|
||||
self.ex = self.outlook.ActiveExplorer()
|
||||
if self.ex:
|
||||
mngr = manager.GetManager()
|
||||
data = mngr.LoadConfig()
|
||||
outlook = Dispatch("Outlook.Application")
|
||||
ex = outlook.ActiveExplorer()
|
||||
if ex:
|
||||
is_login = str(data['login'])
|
||||
if is_login == 'False':
|
||||
win32ui.MessageBox("Please login to the database first", "OpenERP Connection", win32con.MB_ICONEXCLAMATION)
|
||||
elif self.ex.Selection.Count == 1 or ex.Selection.Count == 0:
|
||||
self.mngr = manager.GetManager()
|
||||
self.mngr.ShowManager("IDD_OPEN_DOCUEMNT_DIALOG")
|
||||
elif self.ex.Selection.Count > 1:
|
||||
elif ex.Selection.Count == 1 or ex.Selection.Count == 0:
|
||||
mngr = manager.GetManager()
|
||||
mngr.ShowManager("IDD_OPEN_DOCUEMNT_DIALOG")
|
||||
elif ex.Selection.Count > 1:
|
||||
win32ui.MessageBox("Multiple selection not allowed. Please select only one mail at a time.","Open Document",win32con.MB_ICONINFORMATION)
|
||||
return cancel
|
||||
#
|
||||
|
||||
@@ -272,15 +272,15 @@ class OKButtonProcessor(ButtonProcessor):
|
||||
return
|
||||
setConnAttribs(server, port, self.mngr)
|
||||
if str(NewConn.getitem('_running')) == 'False':
|
||||
msg = "No server running on host '%s' at port '%d'. Press ignore to still continue with this configuration?"%(server,port)
|
||||
r=win32ui.MessageBox(msg, "OpenERP Connection", win32con.MB_ABORTRETRYIGNORE | win32con.MB_ICONQUESTION)
|
||||
if r==3:
|
||||
resetConnAttribs(self.window)
|
||||
return
|
||||
elif r==4:
|
||||
self.OnClicked(id)
|
||||
elif r==5:
|
||||
setConnAttribs(server, port, self.mngr)
|
||||
msg = "No server running on host '%s' at port '%d'. Press ignore to still continue with this configuration?"%(server,port)
|
||||
r=win32ui.MessageBox(msg, "OpenERP Connection", win32con.MB_ABORTRETRYIGNORE | win32con.MB_ICONQUESTION)
|
||||
if r==3:
|
||||
resetConnAttribs(self.window)
|
||||
return
|
||||
elif r==4:
|
||||
self.OnClicked(id)
|
||||
elif r==5:
|
||||
setConnAttribs(server, port, self.mngr)
|
||||
win32gui.EndDialog(self.window.hwnd, id)
|
||||
|
||||
class DoneButtonProcessor(ButtonProcessor):
|
||||
@@ -661,7 +661,6 @@ def CreateCase(btnProcessor,*args):
|
||||
if not section:
|
||||
win32ui.MessageBox("Documents can not be created.", "Documents Setting", flag_excl)
|
||||
return
|
||||
|
||||
hwndList = win32gui.GetDlgItem(btnProcessor.window.hwnd, btnProcessor.other_ids[1])
|
||||
partner_ids=[]
|
||||
r = GetSelectedItems(hwndList)
|
||||
|
||||
@@ -25,7 +25,6 @@ import sys
|
||||
import chilkat
|
||||
import os
|
||||
from manager import ustr
|
||||
import win32ui
|
||||
import email
|
||||
cemail = chilkat.CkEmail()
|
||||
dt = chilkat.SYSTEMTIME()
|
||||
@@ -34,7 +33,6 @@ def generateEML(mail):
|
||||
body = mail.Body.encode("utf-8")
|
||||
recipients = mail.Recipients
|
||||
sender_email = mail.SenderEmailAddress
|
||||
sender_name = mail.SenderEmailAddress
|
||||
attachments=mail.Attachments
|
||||
|
||||
cemail = chilkat.CkEmail()
|
||||
@@ -69,7 +67,6 @@ def generateEML(mail):
|
||||
att_file = os.path.join(attachments_folder_path, fn)
|
||||
if os.path.exists(att_file):
|
||||
os.remove(att_file)
|
||||
f1 = att_file
|
||||
attachments[i].SaveAsFile(att_file)
|
||||
contentType = cemail.addFileAttachment(att_file)
|
||||
if (contentType == None ):
|
||||
@@ -93,4 +90,4 @@ def generateEML(mail):
|
||||
if (success == False):
|
||||
print cemail.lastErrorText()
|
||||
sys.exit()
|
||||
return new_mail
|
||||
return new_mail, eml_path
|
||||
@@ -104,10 +104,9 @@ class OpenERPManager:
|
||||
self.application_directory = os.path.dirname(this_filename)
|
||||
self.windows_data_directory = self.LocateDataDirectory()
|
||||
self.data_directory = self.windows_data_directory
|
||||
self.default_objects = [('Partners','res.partner',''),('Partner Address','res.partner.address',''), \
|
||||
('Account Invoices','account.invoice',''), ('Accounts','account.account',''), \
|
||||
('Projects', 'project.project',''),('Sale Orders','sale.order',''), \
|
||||
('Project Tasks','project.task',''), ('Products', 'product.product', '')]
|
||||
self.default_objects = [('Partners','res.partner',''),('Account Invoices','account.invoice',''), \
|
||||
('Products', 'product.product',''),('Sale Orders','sale.order',''), \
|
||||
('Leads','crm.lead','')]
|
||||
|
||||
self.config=self.LoadConfig()
|
||||
|
||||
|
||||
@@ -80,25 +80,25 @@ class XMLRpcConn(object):
|
||||
self.protocol=None
|
||||
|
||||
def getitem(self, attrib):
|
||||
v=self.__getattribute__(attrib)
|
||||
return str(v)
|
||||
v=self.__getattribute__(attrib)
|
||||
return str(v)
|
||||
|
||||
def setitem(self, attrib, value):
|
||||
return self.__setattr__(attrib, value)
|
||||
|
||||
def GetDBList(self):
|
||||
conn = xmlrpclib.ServerProxy(self._uri + '/xmlrpc/db')
|
||||
try:
|
||||
db_list = execute(conn, 'list')
|
||||
if db_list == False:
|
||||
self._running=False
|
||||
return []
|
||||
else:
|
||||
self._running=True
|
||||
except:
|
||||
db_list=-1
|
||||
self._running=True
|
||||
return db_list
|
||||
conn = xmlrpclib.ServerProxy(self._uri + '/xmlrpc/db')
|
||||
try:
|
||||
db_list = execute(conn, 'list')
|
||||
if db_list == False:
|
||||
self._running=False
|
||||
return []
|
||||
else:
|
||||
self._running=True
|
||||
except:
|
||||
db_list=-1
|
||||
self._running=True
|
||||
return db_list
|
||||
|
||||
def login(self,dbname, user, pwd):
|
||||
self._dbname = dbname
|
||||
@@ -133,35 +133,36 @@ class XMLRpcConn(object):
|
||||
break
|
||||
|
||||
def ArchiveToOpenERP(self, recs, mail):
|
||||
import win32con
|
||||
import win32con
|
||||
import win32ui
|
||||
conn = xmlrpclib.ServerProxy(self._uri + '/xmlrpc/object')
|
||||
flag = False
|
||||
new_msg = ext_msg =""
|
||||
message_id = referances = None
|
||||
try:
|
||||
session = win32com.client.Dispatch("MAPI.session")
|
||||
session.Logon('Outlook')
|
||||
objMessage = session.GetMessage(mail.EntryID, mail.Parent.StoreID)
|
||||
objFields = objMessage.Fields
|
||||
strheader = objFields.Item(mapitags.PR_TRANSPORT_MESSAGE_HEADERS)
|
||||
strheader = ustr(strheader).encode('iso-8859-1')
|
||||
headers = {}
|
||||
strheader = strheader.replace("\n ", " ").splitlines()
|
||||
for line in strheader:
|
||||
split_here = line.find(":")
|
||||
headers[line[:split_here]] = line[split_here:]
|
||||
temp1 = headers.get('Message-ID')
|
||||
temp2 = headers.get('Message-Id')
|
||||
referances = headers.get('References')
|
||||
if temp1 == None: message_id = temp2
|
||||
if temp2 == None: message_id = temp1
|
||||
startCut = message_id.find("<")
|
||||
endCut = message_id.find(">")
|
||||
message_id = message_id[startCut:endCut+1]
|
||||
if not referances == None:
|
||||
startCut = referances.find("<")
|
||||
endCut = referances.find(">")
|
||||
referances = referances[startCut:endCut+1]
|
||||
session = win32com.client.Dispatch("MAPI.session")
|
||||
session.Logon('Outlook')
|
||||
objMessage = session.GetMessage(mail.EntryID, mail.Parent.StoreID)
|
||||
objFields = objMessage.Fields
|
||||
strheader = objFields.Item(mapitags.PR_TRANSPORT_MESSAGE_HEADERS)
|
||||
strheader = ustr(strheader).encode('iso-8859-1')
|
||||
headers = {}
|
||||
strheader = strheader.replace("\n ", " ").splitlines()
|
||||
for line in strheader:
|
||||
split_here = line.find(":")
|
||||
headers[line[:split_here]] = line[split_here:]
|
||||
temp1 = headers.get('Message-ID')
|
||||
temp2 = headers.get('Message-Id')
|
||||
referances = headers.get('References')
|
||||
if temp1 == None: message_id = temp2
|
||||
if temp2 == None: message_id = temp1
|
||||
startCut = message_id.find("<")
|
||||
endCut = message_id.find(">")
|
||||
message_id = message_id[startCut:endCut+1]
|
||||
if not referances == None:
|
||||
startCut = referances.find("<")
|
||||
endCut = referances.find(">")
|
||||
referances = referances[startCut:endCut+1]
|
||||
except Exception,e:
|
||||
win32ui.MessageBox(str(e),"Archive To OpenERP")
|
||||
return
|
||||
@@ -195,7 +196,7 @@ class XMLRpcConn(object):
|
||||
}
|
||||
obj_list= ['crm.lead','project.issue','hr.applicant','res.partner']
|
||||
if rec[0] not in obj_list:
|
||||
ids = self.CreateEmailAttachment(rec,mail)
|
||||
self.CreateEmailAttachment(rec,mail)
|
||||
result = {}
|
||||
if attachments:
|
||||
result = self.MakeAttachment([rec], mail)
|
||||
@@ -269,7 +270,7 @@ class XMLRpcConn(object):
|
||||
id = -1
|
||||
try:
|
||||
conn = xmlrpclib.ServerProxy(self._uri+ '/xmlrpc/object')
|
||||
email=eml.generateEML(mail)
|
||||
email, path = eml.generateEML(mail)
|
||||
message_id = None
|
||||
session = win32com.client.Dispatch("MAPI.session")
|
||||
session.Logon('Outlook')
|
||||
@@ -447,11 +448,21 @@ class XMLRpcConn(object):
|
||||
return True
|
||||
|
||||
def SearchEmailResources(self, message_id):
|
||||
import win32ui
|
||||
conn = xmlrpclib.ServerProxy(self._uri+ '/xmlrpc/object')
|
||||
res_vals = []
|
||||
mail_id = execute( conn, 'execute', self._dbname, int(self._uid), self._pwd, 'mailgate.message', 'search', [('message_id','=',message_id)])
|
||||
ref_mail_id = None
|
||||
if not mail_id:
|
||||
return None
|
||||
ref_mail_id = execute( conn, 'execute', self._dbname, int(self._uid), self._pwd, 'mailgate.message', 'search', [('references','=',message_id)])
|
||||
if ref_mail_id:
|
||||
win32ui.MessageBox(str(ref_mail_id),"ref_mail_id")
|
||||
address = execute( conn, 'execute', self._dbname, int(self._uid), self._pwd, 'mailgate.message','read',ref_mail_id[0],['model','res_id'])
|
||||
win32ui.MessageBox(str(address),"address")
|
||||
for key, vals in address.items():
|
||||
res_vals.append([key,vals])
|
||||
return res_vals
|
||||
return None
|
||||
address = execute( conn, 'execute', self._dbname, int(self._uid), self._pwd, 'mailgate.message','read',mail_id[0],['model','res_id'])
|
||||
for key, vals in address.items():
|
||||
res_vals.append([key,vals])
|
||||
@@ -507,6 +518,8 @@ class XMLRpcConn(object):
|
||||
return country
|
||||
|
||||
def CreateEmailAttachment(self, rec, mail):
|
||||
import eml
|
||||
email, path = eml.generateEML(mail)
|
||||
conn = xmlrpclib.ServerProxy(self._uri+ '/xmlrpc/object')
|
||||
obj = rec[0]
|
||||
obj_id = rec[1]
|
||||
@@ -524,16 +537,12 @@ class XMLRpcConn(object):
|
||||
l = 64 - len(fn)
|
||||
f = fn.split('.')
|
||||
fn = f[0][0:l] + '.' + f[-1]
|
||||
fn = fn[:-4]+'.txt'
|
||||
f = open(fn,"w")
|
||||
body = mail.Body.encode("utf-8")
|
||||
f.writelines(body)
|
||||
f.close()
|
||||
f=open(fn,"rb")
|
||||
fn = fn[:-4]+".eml"
|
||||
f = open(path)
|
||||
content = "".join(f.readlines()).encode('base64')
|
||||
f.close()
|
||||
res['name'] = ustr((mail.Subject).replace(' ',''))
|
||||
res['datas_fname'] = ustr(fn)
|
||||
res['name'] = fn
|
||||
res['datas_fname'] = fn
|
||||
res['datas'] = content
|
||||
res['res_id'] = obj_id
|
||||
id = execute(conn,'execute',self._dbname,int(self._uid),self._pwd,'ir.attachment','create',res)
|
||||
|
||||
@@ -148,6 +148,7 @@ class thunderbird_partner(osv.osv_memory):
|
||||
dictcreate = dict(vals)
|
||||
ref_ids = str(dictcreate.get('ref_ids')).split(';')
|
||||
msg = dictcreate.get('message')
|
||||
mail = msg
|
||||
msg = self.pool.get('email.server.tools').parse_message(msg)
|
||||
server_tools_pool = self.pool.get('email.server.tools')
|
||||
message_id = msg.get('message-id', False)
|
||||
@@ -183,11 +184,11 @@ class thunderbird_partner(osv.osv_memory):
|
||||
l = 64 - len(fn)
|
||||
f = fn.split('.')
|
||||
fn = f[0][0:l] + '.' + f[-1]
|
||||
fn = fn[:-4]+'.txt'
|
||||
fn = fn[:-4]+'.eml'
|
||||
res['res_model'] = model
|
||||
res['name'] = msg.get('subject','NO-SUBJECT')
|
||||
res['name'] = msg.get('subject','NO-SUBJECT')+".eml"
|
||||
res['datas_fname'] = fn
|
||||
res['datas'] = base64.b64encode(msg.get('body'))
|
||||
res['datas'] = base64.b64encode(mail)
|
||||
res['res_id'] = res_id
|
||||
obj_attch.create(cr, uid, res)
|
||||
server_tools_pool.history_message(cr, uid, model, res_id, msg_new)
|
||||
@@ -203,8 +204,15 @@ class thunderbird_partner(osv.osv_memory):
|
||||
def search_message(self, cr, uid, message, context=None):
|
||||
#@param message: string of mail which is read from EML File
|
||||
#@return model,res_id
|
||||
references = []
|
||||
dictcreate = dict(message)
|
||||
message_id = dictcreate.get('message_id')
|
||||
msg = dictcreate.get('message')
|
||||
msg = self.pool.get('email.server.tools').parse_message(msg)
|
||||
message_id = msg.get('message-id')
|
||||
refs = msg.get('references',False)
|
||||
references = False
|
||||
if refs:
|
||||
references = refs.split()
|
||||
msg_pool = self.pool.get('mailgate.message')
|
||||
model = ''
|
||||
res_id = 0
|
||||
@@ -214,6 +222,13 @@ class thunderbird_partner(osv.osv_memory):
|
||||
msg = msg_pool.browse(cr, uid, msg_ids[0])
|
||||
model = msg.model
|
||||
res_id = msg.res_id
|
||||
else:
|
||||
if references :
|
||||
msg_ids = msg_pool.search(cr, uid, [('message_id','in',references)])
|
||||
if msg_ids and len(msg_ids):
|
||||
msg = msg_pool.browse(cr, uid, msg_ids[0])
|
||||
model = msg.model
|
||||
res_id = msg.res_id
|
||||
return (model,res_id)
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -24,9 +24,9 @@ pref("extensions.tiny.partnername","");
|
||||
pref("extensions.tiny.officeno","");
|
||||
pref("extensions.tiny.phoneno","");
|
||||
pref("extensions.tiny.address","");
|
||||
pref("extensions.tiny.listobject","Partner,Partner Address,Accout Invoice,Project,Product,Account,Sale Order,Project Task");
|
||||
pref("extensions.tiny.object","res.partner,res.partner.address,account.invoice,project.project,product.product,account.account,sale.order,project.task");
|
||||
pref("extensions.tiny.imagename","chrome://openerp_plugin/skin/Partner.png,chrome://openerp_plugin/skin/Contact.png,chrome://openerp_plugin/skin/Invoice.png,chrome://openerp_plugin/skin/Project.png,chrome://openerp_plugin/skin/Product.png,chrome://openerp_plugin/skin/Account.png,chrome://openerp_plugin/skin/Sale.png,chrome://openerp_plugin/skin/Case.png,chrome://openerp_plugin/skin/Task.png");
|
||||
pref("extensions.tiny.listobject","Partner,Accout Invoice,Product,Sale Order,Lead and opportunities");
|
||||
pref("extensions.tiny.object","res.partner,account.invoice,product.product,sale.order,crm.lead");
|
||||
pref("extensions.tiny.imagename","chrome://openerp_plugin/skin/Partner.png,chrome://openerp_plugin/skin/Invoice.png,chrome://openerp_plugin/skin/Product.png,chrome://openerp_plugin/skin/Sale.png,chrome://openerp_plugin/skin/Case.png");
|
||||
pref("extensions.tiny.attachmentlength","");
|
||||
pref("extensions.tiny.attachment","");
|
||||
pref("extensions.tiny.db_list","false");
|
||||
|
||||
Reference in New Issue
Block a user