mirror of
https://github.com/HIllya51/LunaTranslator.git
synced 2025-01-13 15:43:53 +08:00
.
This commit is contained in:
parent
45294aaa3d
commit
3a0f44449c
@ -20,8 +20,7 @@ inline SECURITY_ATTRIBUTES allAccess = std::invoke([] // allows non-admin proces
|
|||||||
static SECURITY_DESCRIPTOR sd = {};
|
static SECURITY_DESCRIPTOR sd = {};
|
||||||
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
|
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
|
||||||
SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE);
|
SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE);
|
||||||
return SECURITY_ATTRIBUTES{ sizeof(SECURITY_ATTRIBUTES), &sd, FALSE };
|
return SECURITY_ATTRIBUTES{ sizeof(SECURITY_ATTRIBUTES), &sd, FALSE }; });
|
||||||
});
|
|
||||||
|
|
||||||
inline std::wstring StringToWideString(const std::string &text, UINT encoding = CP_UTF8)
|
inline std::wstring StringToWideString(const std::string &text, UINT encoding = CP_UTF8)
|
||||||
{
|
{
|
||||||
@ -36,3 +35,28 @@ inline std::string WideStringToString(const std::wstring &text, UINT cp = CP_UTF
|
|||||||
WideCharToMultiByte(cp, 0, text.c_str(), -1, buffer.data(), buffer.size(), nullptr, nullptr);
|
WideCharToMultiByte(cp, 0, text.c_str(), -1, buffer.data(), buffer.size(), nullptr, nullptr);
|
||||||
return buffer.data();
|
return buffer.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define CHECK_FAILURE(x) \
|
||||||
|
if (FAILED((x))) \
|
||||||
|
return (HRESULT)x;
|
||||||
|
#define CHECK_FAILURE_NORET(x) \
|
||||||
|
if (FAILED((x))) \
|
||||||
|
return ;
|
||||||
|
|
||||||
|
struct CO_INIT
|
||||||
|
{
|
||||||
|
HRESULT hr;
|
||||||
|
CO_INIT()
|
||||||
|
{
|
||||||
|
HRESULT hr = ::CoInitialize(NULL);
|
||||||
|
}
|
||||||
|
operator HRESULT()
|
||||||
|
{
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
~CO_INIT()
|
||||||
|
{
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
CoUninitialize();
|
||||||
|
}
|
||||||
|
};
|
113
cpp/implsapi.cpp
113
cpp/implsapi.cpp
@ -1,21 +1,15 @@
|
|||||||
|
|
||||||
std::optional<std::vector<byte>> _Speak(std::wstring &Content, const wchar_t *token, int voiceid, int rate, int volume)
|
std::optional<std::vector<byte>> _Speak(std::wstring &Content, const wchar_t *token, int voiceid, int rate, int volume)
|
||||||
{
|
{
|
||||||
if (FAILED(::CoInitialize(NULL)))
|
|
||||||
return {};
|
|
||||||
CComPtr<ISpVoice> pVoice = NULL;
|
|
||||||
std::optional<std::vector<byte>> ret = {};
|
std::optional<std::vector<byte>> ret = {};
|
||||||
do
|
[&]()
|
||||||
{
|
{
|
||||||
HRESULT hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&pVoice);
|
CO_INIT co;
|
||||||
if (FAILED(hr) || (NULL == pVoice))
|
CHECK_FAILURE_NORET(co);
|
||||||
{
|
CComPtr<ISpVoice> pVoice = NULL;
|
||||||
ret = {};
|
CHECK_FAILURE_NORET(pVoice.CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL));
|
||||||
break;
|
|
||||||
}
|
|
||||||
CComPtr<IEnumSpObjectTokens> pSpEnumTokens = NULL;
|
CComPtr<IEnumSpObjectTokens> pSpEnumTokens = NULL;
|
||||||
if (SUCCEEDED(SpEnumTokens(token, NULL, NULL, &pSpEnumTokens)))
|
CHECK_FAILURE_NORET(SpEnumTokens(token, NULL, NULL, &pSpEnumTokens));
|
||||||
{
|
|
||||||
ULONG ulTokensNumber = 0;
|
ULONG ulTokensNumber = 0;
|
||||||
pSpEnumTokens->GetCount(&ulTokensNumber);
|
pSpEnumTokens->GetCount(&ulTokensNumber);
|
||||||
ISpObjectToken *m_pISpObjectToken;
|
ISpObjectToken *m_pISpObjectToken;
|
||||||
@ -29,35 +23,11 @@ std::optional<std::vector<byte>> _Speak(std::wstring &Content, const wchar_t *to
|
|||||||
CComPtr<ISpStreamFormat> cpOldStream;
|
CComPtr<ISpStreamFormat> cpOldStream;
|
||||||
CSpStreamFormat originalFmt;
|
CSpStreamFormat originalFmt;
|
||||||
CComPtr<IStream> pMemStream;
|
CComPtr<IStream> pMemStream;
|
||||||
hr = pVoice->GetOutputStream(&cpOldStream);
|
CHECK_FAILURE_NORET(pVoice->GetOutputStream(&cpOldStream));
|
||||||
if (FAILED(hr) || (NULL == cpOldStream))
|
|
||||||
{
|
|
||||||
ret = {};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
originalFmt.AssignFormat(cpOldStream);
|
originalFmt.AssignFormat(cpOldStream);
|
||||||
// hr = SPBindToFile(FileName.c_str(), SPFM_CREATE_ALWAYS, &cpWavStream, &originalFmt.FormatId(), originalFmt.WaveFormatExPtr());
|
CHECK_FAILURE_NORET(CreateStreamOnHGlobal(NULL, TRUE, &pMemStream));
|
||||||
// if (FAILED(hr))
|
CHECK_FAILURE_NORET(cpWavStream.CoCreateInstance(CLSID_SpStream));
|
||||||
// {
|
CHECK_FAILURE_NORET(cpWavStream->SetBaseStream(pMemStream, SPDFID_WaveFormatEx, originalFmt.WaveFormatExPtr()));
|
||||||
// ret = false;
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
{
|
|
||||||
hr = ::CreateStreamOnHGlobal(NULL, TRUE, &pMemStream);
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
hr = cpWavStream.CoCreateInstance(CLSID_SpStream);
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
hr = cpWavStream->SetBaseStream(pMemStream, SPDFID_WaveFormatEx, originalFmt.WaveFormatExPtr());
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
ret = {};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pVoice->SetOutput(cpWavStream, TRUE);
|
pVoice->SetOutput(cpWavStream, TRUE);
|
||||||
pVoice->Speak(Content.c_str(), SPF_IS_XML, NULL);
|
pVoice->Speak(Content.c_str(), SPF_IS_XML, NULL);
|
||||||
|
|
||||||
@ -68,7 +38,7 @@ std::optional<std::vector<byte>> _Speak(std::wstring &Content, const wchar_t *to
|
|||||||
|
|
||||||
// After SAPI writes the stream, the stream position is at the end, so we need to set it to the beginning.
|
// After SAPI writes the stream, the stream position is at the end, so we need to set it to the beginning.
|
||||||
_LARGE_INTEGER a = {0};
|
_LARGE_INTEGER a = {0};
|
||||||
hr = cpWavStream->Seek(a, STREAM_SEEK_SET, NULL);
|
CHECK_FAILURE_NORET(cpWavStream->Seek(a, STREAM_SEEK_SET, NULL));
|
||||||
|
|
||||||
// get the base istream from the ispstream
|
// get the base istream from the ispstream
|
||||||
CComPtr<IStream> pIstream;
|
CComPtr<IStream> pIstream;
|
||||||
@ -79,7 +49,6 @@ std::optional<std::vector<byte>> _Speak(std::wstring &Content, const wchar_t *to
|
|||||||
pIstream->Stat(&stats, STATFLAG_NONAME);
|
pIstream->Stat(&stats, STATFLAG_NONAME);
|
||||||
|
|
||||||
ULONG sSize = stats.cbSize.QuadPart; // size of the data to be read
|
ULONG sSize = stats.cbSize.QuadPart; // size of the data to be read
|
||||||
auto wavfmt = *originalFmt.WaveFormatExPtr();
|
|
||||||
|
|
||||||
ULONG bytesRead; // this will tell the number of bytes that have been read
|
ULONG bytesRead; // this will tell the number of bytes that have been read
|
||||||
std::vector<byte> datas;
|
std::vector<byte> datas;
|
||||||
@ -96,78 +65,48 @@ std::optional<std::vector<byte>> _Speak(std::wstring &Content, const wchar_t *to
|
|||||||
ptr += 8;
|
ptr += 8;
|
||||||
memcpy(pBuffer + ptr, "\x12\x00\x00\x00", 4);
|
memcpy(pBuffer + ptr, "\x12\x00\x00\x00", 4);
|
||||||
ptr += 4;
|
ptr += 4;
|
||||||
memcpy(pBuffer + ptr, &wavfmt.wFormatTag, 2);
|
memcpy(pBuffer + ptr, originalFmt.WaveFormatExPtr(), sizeof(WAVEFORMATEX));
|
||||||
ptr += 2;
|
ptr += sizeof(WAVEFORMATEX);
|
||||||
memcpy(pBuffer + ptr, &wavfmt.nChannels, 2);
|
|
||||||
ptr += 2;
|
|
||||||
int freq = wavfmt.nSamplesPerSec;
|
|
||||||
memcpy(pBuffer + ptr, &freq, 4);
|
|
||||||
ptr += 4;
|
|
||||||
freq = wavfmt.nAvgBytesPerSec;
|
|
||||||
memcpy(pBuffer + ptr, &freq, 4);
|
|
||||||
ptr += 4;
|
|
||||||
memcpy(pBuffer + ptr, &wavfmt.nBlockAlign, 4);
|
|
||||||
ptr += 2;
|
|
||||||
memcpy(pBuffer + ptr, &wavfmt.wBitsPerSample, 2);
|
|
||||||
ptr += 2;
|
|
||||||
WORD _0 = 0;
|
|
||||||
|
|
||||||
memcpy(pBuffer + ptr, &_0, 2);
|
|
||||||
ptr += 2;
|
|
||||||
memcpy(pBuffer + ptr, "data", 4);
|
memcpy(pBuffer + ptr, "data", 4);
|
||||||
ptr += 4;
|
ptr += 4;
|
||||||
memcpy(pBuffer + ptr, &sSize, 4);
|
memcpy(pBuffer + ptr, &sSize, 4);
|
||||||
|
ptr += 4;
|
||||||
// read the data into the buffer
|
// read the data into the buffer
|
||||||
pIstream->Read(pBuffer + 46, sSize, &bytesRead);
|
pIstream->Read(pBuffer + ptr, sSize, &bytesRead);
|
||||||
|
|
||||||
/*uncomment the following to print the contents of the buffer
|
|
||||||
cout << "following data read \n";
|
|
||||||
for (int i = 0; i < sSize; i++)
|
|
||||||
cout << pBuffer[i] << " ";
|
|
||||||
cout << endl;
|
|
||||||
*/
|
|
||||||
ret = std::move(datas);
|
ret = std::move(datas);
|
||||||
}
|
}();
|
||||||
|
|
||||||
} while (0);
|
|
||||||
|
|
||||||
::CoUninitialize();
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::wstring> _List(const wchar_t *token)
|
std::vector<std::wstring> _List(const wchar_t *token)
|
||||||
{
|
{
|
||||||
if (FAILED(::CoInitialize(NULL)))
|
|
||||||
return {};
|
|
||||||
CComPtr<ISpVoice> pSpVoice = NULL;
|
|
||||||
std::vector<std::wstring> ret;
|
std::vector<std::wstring> ret;
|
||||||
|
[&]()
|
||||||
|
{
|
||||||
|
CO_INIT co;
|
||||||
|
CHECK_FAILURE_NORET(co);
|
||||||
|
CComPtr<ISpVoice> pSpVoice = NULL;
|
||||||
CComPtr<IEnumSpObjectTokens> pSpEnumTokens = NULL;
|
CComPtr<IEnumSpObjectTokens> pSpEnumTokens = NULL;
|
||||||
if (FAILED(CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&pSpVoice)))
|
CHECK_FAILURE_NORET(CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&pSpVoice));
|
||||||
{
|
CHECK_FAILURE_NORET(SpEnumTokens(token, NULL, NULL, &pSpEnumTokens));
|
||||||
return {};
|
|
||||||
}
|
|
||||||
if (SUCCEEDED(SpEnumTokens(token, NULL, NULL, &pSpEnumTokens)))
|
|
||||||
{
|
|
||||||
ULONG ulTokensNumber = 0;
|
ULONG ulTokensNumber = 0;
|
||||||
pSpEnumTokens->GetCount(&ulTokensNumber);
|
pSpEnumTokens->GetCount(&ulTokensNumber);
|
||||||
CComPtr<ISpObjectToken> m_pISpObjectToken;
|
CComPtr<ISpObjectToken> m_pISpObjectToken;
|
||||||
for (ULONG i = 0; i < ulTokensNumber; i++)
|
for (ULONG i = 0; i < ulTokensNumber; i++)
|
||||||
{
|
{
|
||||||
pSpEnumTokens->Item(i, &m_pISpObjectToken);
|
pSpEnumTokens->Item(i, &m_pISpObjectToken);
|
||||||
WCHAR *pszVoiceId = NULL;
|
CComHeapPtr<WCHAR> pszVoiceId;
|
||||||
LPWSTR pszVoiceName;
|
CComHeapPtr<WCHAR> pszVoiceName;
|
||||||
if (SUCCEEDED(m_pISpObjectToken->GetId(&pszVoiceId)))
|
if (SUCCEEDED(m_pISpObjectToken->GetId(&pszVoiceId)))
|
||||||
{
|
{
|
||||||
if (SUCCEEDED(m_pISpObjectToken->GetStringValue(NULL, &pszVoiceName)))
|
if (SUCCEEDED(m_pISpObjectToken->GetStringValue(NULL, &pszVoiceName)))
|
||||||
{
|
{
|
||||||
ret.emplace_back(pszVoiceName);
|
ret.emplace_back(pszVoiceName);
|
||||||
CoTaskMemFree(pszVoiceName);
|
|
||||||
}
|
|
||||||
CoTaskMemFree(pszVoiceId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
::CoUninitialize();
|
}();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
@ -211,48 +211,19 @@ HRESULT MWebBrowser::CreateBrowser(HWND hwndParent)
|
|||||||
{
|
{
|
||||||
m_hwndParent = hwndParent;
|
m_hwndParent = hwndParent;
|
||||||
|
|
||||||
HRESULT hr;
|
CHECK_FAILURE(OleCreate(CLSID_WebBrowser, IID_IOleObject, OLERENDER_DRAW, NULL,
|
||||||
hr = ::OleCreate(CLSID_WebBrowser, IID_IOleObject, OLERENDER_DRAW, NULL,
|
this, this, (void **)&m_ole_object));
|
||||||
this, this, (void **)&m_ole_object);
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
assert(0);
|
|
||||||
return hr;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_ole_object.QueryInterface(&m_pDocHostUIHandler);
|
m_ole_object.QueryInterface(&m_pDocHostUIHandler);
|
||||||
|
|
||||||
hr = m_ole_object->SetClientSite(this);
|
CHECK_FAILURE(m_ole_object->SetClientSite(this));
|
||||||
if (FAILED(hr))
|
CHECK_FAILURE(OleSetContainedObject(m_ole_object, TRUE));
|
||||||
{
|
|
||||||
assert(0);
|
|
||||||
return hr;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = ::OleSetContainedObject(m_ole_object, TRUE);
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
assert(0);
|
|
||||||
return hr;
|
|
||||||
}
|
|
||||||
|
|
||||||
RECT rc;
|
RECT rc;
|
||||||
::SetRectEmpty(&rc);
|
::SetRectEmpty(&rc);
|
||||||
hr = m_ole_object->DoVerb(OLEIVERB_INPLACEACTIVATE, NULL, this, 0,
|
CHECK_FAILURE(m_ole_object->DoVerb(OLEIVERB_INPLACEACTIVATE, NULL, this, 0,
|
||||||
m_hwndParent, &rc);
|
m_hwndParent, &rc));
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
assert(0);
|
|
||||||
return hr;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = m_ole_object.QueryInterface(&m_web_browser2);
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
assert(0);
|
|
||||||
return hr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
CHECK_FAILURE(m_ole_object.QueryInterface(&m_web_browser2));
|
||||||
HWND hwnd = GetControlWindow();
|
HWND hwnd = GetControlWindow();
|
||||||
|
|
||||||
DWORD exstyle = GetWindowLong(hwnd, GWL_EXSTYLE);
|
DWORD exstyle = GetWindowLong(hwnd, GWL_EXSTYLE);
|
||||||
|
@ -162,7 +162,8 @@ DECLARE_API bool check_window_viewable(HWND hwnd)
|
|||||||
DECLARE_API void GetSelectedText(void (*cb)(const wchar_t *))
|
DECLARE_API void GetSelectedText(void (*cb)(const wchar_t *))
|
||||||
{
|
{
|
||||||
#ifndef WINXP
|
#ifndef WINXP
|
||||||
CoInitialize(nullptr);
|
CO_INIT co;
|
||||||
|
CHECK_FAILURE_NORET(co);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 初始化 COM
|
// 初始化 COM
|
||||||
@ -212,6 +213,5 @@ DECLARE_API void GetSelectedText(void (*cb)(const wchar_t *))
|
|||||||
{
|
{
|
||||||
printf(e.what());
|
printf(e.what());
|
||||||
}
|
}
|
||||||
CoUninitialize();
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -1,50 +1,28 @@
|
|||||||
|
|
||||||
DECLARE_API void GetLnkTargetPath(wchar_t *lnkFilePath, wchar_t *path, wchar_t *tgtpath, wchar_t *iconpath, wchar_t *dirpath)
|
DECLARE_API void GetLnkTargetPath(const wchar_t *lnkFilePath, wchar_t *path, wchar_t *tgtpath, wchar_t *iconpath, wchar_t *dirpath)
|
||||||
{
|
{
|
||||||
wcscpy(path, L"");
|
wcscpy(path, L"");
|
||||||
wcscpy(tgtpath, L"");
|
wcscpy(tgtpath, L"");
|
||||||
wcscpy(iconpath, L"");
|
wcscpy(iconpath, L"");
|
||||||
CoInitialize(NULL);
|
wcscpy(dirpath, L"");
|
||||||
|
CO_INIT co;
|
||||||
|
CHECK_FAILURE_NORET(co);
|
||||||
CComPtr<IShellLink> shellLink;
|
CComPtr<IShellLink> shellLink;
|
||||||
HRESULT hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID *)&shellLink);
|
CHECK_FAILURE_NORET(CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID *)&shellLink));
|
||||||
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
CComPtr<IPersistFile> persistFile;
|
CComPtr<IPersistFile> persistFile;
|
||||||
auto hr = shellLink.QueryInterface(&persistFile);
|
CHECK_FAILURE_NORET(shellLink.QueryInterface(&persistFile));
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
WCHAR wsz[MAX_PATH];
|
WCHAR wsz[MAX_PATH];
|
||||||
StringCchCopy(wsz, MAX_PATH, lnkFilePath);
|
StringCchCopy(wsz, MAX_PATH, lnkFilePath);
|
||||||
|
|
||||||
hr = persistFile->Load(wsz, STGM_READ);
|
CHECK_FAILURE_NORET(persistFile->Load(lnkFilePath, STGM_READ));
|
||||||
|
|
||||||
if (SUCCEEDED(hr))
|
CHECK_FAILURE_NORET(shellLink->Resolve(NULL, SLR_NO_UI));
|
||||||
{
|
|
||||||
hr = shellLink->Resolve(NULL, SLR_NO_UI);
|
|
||||||
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
WIN32_FIND_DATA findData;
|
WIN32_FIND_DATA findData;
|
||||||
int x;
|
int x;
|
||||||
hr = shellLink->GetIconLocation(iconpath, MAX_PATH, &x);
|
shellLink->GetIconLocation(iconpath, MAX_PATH, &x);
|
||||||
if (FAILED(hr))
|
shellLink->GetArguments(tgtpath, MAX_PATH);
|
||||||
wcscpy(iconpath, L"");
|
shellLink->GetPath(path, MAX_PATH, &findData, SLGP_RAWPATH);
|
||||||
hr = shellLink->GetArguments(tgtpath, MAX_PATH);
|
shellLink->GetWorkingDirectory(dirpath, MAX_PATH);
|
||||||
if (FAILED(hr))
|
|
||||||
wcscpy(tgtpath, L"");
|
|
||||||
hr = shellLink->GetPath(path, MAX_PATH, &findData, SLGP_RAWPATH);
|
|
||||||
|
|
||||||
if (FAILED(hr))
|
|
||||||
wcscpy(path, L"");
|
|
||||||
hr = shellLink->GetWorkingDirectory(dirpath, MAX_PATH);
|
|
||||||
if (FAILED(hr))
|
|
||||||
wcscpy(path, L"");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CoUninitialize();
|
|
||||||
}
|
}
|
@ -7,9 +7,6 @@
|
|||||||
#include <wrl/implements.h>
|
#include <wrl/implements.h>
|
||||||
using namespace Microsoft::WRL;
|
using namespace Microsoft::WRL;
|
||||||
#include <WebView2.h>
|
#include <WebView2.h>
|
||||||
#define CHECK_FAILURE(x) \
|
|
||||||
if (FAILED((x))) \
|
|
||||||
return x;
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
DECLARE_API void set_transparent_background(void *m_host)
|
DECLARE_API void set_transparent_background(void *m_host)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user