mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 10:54:24 -04:00
more uninstall stuff
This commit is contained in:
parent
fec1586d1f
commit
38d3eda196
@ -108,6 +108,10 @@ P3DInstance(P3D_request_ready_func *func,
|
|||||||
_allow_python_dev = false;
|
_allow_python_dev = false;
|
||||||
_keep_user_env = (_fparams.lookup_token_int("keep_user_env") != 0);
|
_keep_user_env = (_fparams.lookup_token_int("keep_user_env") != 0);
|
||||||
_auto_start = (_fparams.lookup_token_int("auto_start") != 0);
|
_auto_start = (_fparams.lookup_token_int("auto_start") != 0);
|
||||||
|
_auto_install = true;
|
||||||
|
if (_fparams.has_token("auto_install")) {
|
||||||
|
_auto_install = (_fparams.lookup_token_int("auto_install") != 0);
|
||||||
|
}
|
||||||
_auth_button_clicked = false;
|
_auth_button_clicked = false;
|
||||||
_failed = false;
|
_failed = false;
|
||||||
_session = NULL;
|
_session = NULL;
|
||||||
@ -119,6 +123,7 @@ P3DInstance(P3D_request_ready_func *func,
|
|||||||
_download_package_index = 0;
|
_download_package_index = 0;
|
||||||
_total_download_size = 0;
|
_total_download_size = 0;
|
||||||
_total_downloaded = 0;
|
_total_downloaded = 0;
|
||||||
|
_download_started = false;
|
||||||
_download_complete = false;
|
_download_complete = false;
|
||||||
|
|
||||||
INIT_LOCK(_request_lock);
|
INIT_LOCK(_request_lock);
|
||||||
@ -486,7 +491,7 @@ set_wparams(const P3DWindowParams &wparams) {
|
|||||||
_session->send_command(doc);
|
_session->send_command(doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_packages_ready() && _p3d_trusted) {
|
if (_p3d_trusted && get_packages_ready()) {
|
||||||
ready_to_start();
|
ready_to_start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1272,9 +1277,17 @@ void P3DInstance::
|
|||||||
play_button_clicked() {
|
play_button_clicked() {
|
||||||
if (_session == NULL && _p3d_trusted) {
|
if (_session == NULL && _p3d_trusted) {
|
||||||
set_button_image(IT_none);
|
set_button_image(IT_none);
|
||||||
set_background_image(IT_launch);
|
if (!_download_started) {
|
||||||
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
|
// Now we initiate the download.
|
||||||
inst_mgr->start_instance(this);
|
_auto_install = true;
|
||||||
|
_auto_start = true;
|
||||||
|
ready_to_install();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
set_background_image(IT_launch);
|
||||||
|
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
|
||||||
|
inst_mgr->start_instance(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1326,6 +1339,7 @@ auth_finished_main_thread() {
|
|||||||
void P3DInstance::
|
void P3DInstance::
|
||||||
uninstall() {
|
uninstall() {
|
||||||
if (_session != NULL) {
|
if (_session != NULL) {
|
||||||
|
_session->set_failed();
|
||||||
_session->shutdown();
|
_session->shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1333,7 +1347,13 @@ uninstall() {
|
|||||||
for (pi = _packages.begin(); pi != _packages.end(); ++pi) {
|
for (pi = _packages.begin(); pi != _packages.end(); ++pi) {
|
||||||
P3DPackage *package = (*pi);
|
P3DPackage *package = (*pi);
|
||||||
package->uninstall();
|
package->uninstall();
|
||||||
|
package->remove_instance(this);
|
||||||
}
|
}
|
||||||
|
_packages.clear();
|
||||||
|
|
||||||
|
_auto_install = false;
|
||||||
|
_instance_window_opened = false;
|
||||||
|
set_failed();
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
@ -1706,6 +1726,8 @@ mark_p3d_trusted() {
|
|||||||
// Only call this once.
|
// Only call this once.
|
||||||
assert(!_p3d_trusted);
|
assert(!_p3d_trusted);
|
||||||
|
|
||||||
|
_p3d_trusted = true;
|
||||||
|
|
||||||
// Extract the application desc file from the p3d file.
|
// Extract the application desc file from the p3d file.
|
||||||
stringstream sstream;
|
stringstream sstream;
|
||||||
if (!_mf_reader.extract_one(sstream, "p3d_info.xml")) {
|
if (!_mf_reader.extract_one(sstream, "p3d_info.xml")) {
|
||||||
@ -1717,8 +1739,6 @@ mark_p3d_trusted() {
|
|||||||
TiXmlDocument doc;
|
TiXmlDocument doc;
|
||||||
sstream >> doc;
|
sstream >> doc;
|
||||||
|
|
||||||
// This also starts required packages downloading. When all
|
|
||||||
// packages have been installed, we will start the instance.
|
|
||||||
scan_app_desc_file(&doc);
|
scan_app_desc_file(&doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1732,19 +1752,14 @@ mark_p3d_trusted() {
|
|||||||
strm << inst_mgr->get_unique_id();
|
strm << inst_mgr->get_unique_id();
|
||||||
_session_key = strm.str();
|
_session_key = strm.str();
|
||||||
|
|
||||||
// Until we've done all of the above processing, we haven't fully
|
|
||||||
// committed to setting the trusted flag. (Setting this flag down
|
|
||||||
// here instead of a few lines above avoids starting the instance in
|
|
||||||
// scan_app_desc_file(), before we've had a chance to finish
|
|
||||||
// processing this method.)
|
|
||||||
_p3d_trusted = true;
|
|
||||||
|
|
||||||
// Notify JS that we've accepted the trust of the p3d file.
|
// Notify JS that we've accepted the trust of the p3d file.
|
||||||
_panda_script_object->set_bool_property("trusted", true);
|
_panda_script_object->set_bool_property("trusted", true);
|
||||||
send_notify("onauth");
|
send_notify("onauth");
|
||||||
|
|
||||||
// Now that we're all set up, start the instance if we're already
|
// Now that we're all set up, start downloading the required
|
||||||
|
// packages, and then start the instance itself if we're already
|
||||||
// fully downloaded.
|
// fully downloaded.
|
||||||
|
add_packages();
|
||||||
if (get_packages_ready()) {
|
if (get_packages_ready()) {
|
||||||
mark_download_complete();
|
mark_download_complete();
|
||||||
}
|
}
|
||||||
@ -1815,6 +1830,11 @@ scan_app_desc_file(TiXmlDocument *doc) {
|
|||||||
if (xconfig->QueryIntAttribute("auto_start", &auto_start) == TIXML_SUCCESS) {
|
if (xconfig->QueryIntAttribute("auto_start", &auto_start) == TIXML_SUCCESS) {
|
||||||
_auto_start = (auto_start != 0);
|
_auto_start = (auto_start != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int auto_install = 0;
|
||||||
|
if (xconfig->QueryIntAttribute("auto_install", &auto_install) == TIXML_SUCCESS) {
|
||||||
|
_auto_install = (auto_install != 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nout << "_matches_run_origin = " << _matches_run_origin << "\n";
|
nout << "_matches_run_origin = " << _matches_run_origin << "\n";
|
||||||
@ -1831,15 +1851,36 @@ scan_app_desc_file(TiXmlDocument *doc) {
|
|||||||
// But finally, if the user has already clicked through the red
|
// But finally, if the user has already clicked through the red
|
||||||
// "auth" button, no need to present him/her with another green
|
// "auth" button, no need to present him/her with another green
|
||||||
// "play" button as well.
|
// "play" button as well.
|
||||||
|
_auto_install = true;
|
||||||
_auto_start = true;
|
_auto_start = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
nout << "_auto_start = " << _auto_start << "\n";
|
nout << "_auto_install = " << _auto_install
|
||||||
|
<< ", _auto_start = " << _auto_start << "\n";
|
||||||
|
|
||||||
if (_hidden && _got_wparams) {
|
if (_hidden && _got_wparams) {
|
||||||
_wparams.set_window_type(P3D_WT_hidden);
|
_wparams.set_window_type(P3D_WT_hidden);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!_matches_run_origin) {
|
||||||
|
nout << "Cannot run " << _p3d_basename << " from origin "
|
||||||
|
<< _origin_protocol << "//" << _origin_hostname
|
||||||
|
<< ":" << _origin_port << "\n";
|
||||||
|
set_failed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: P3DInstance::add_packages
|
||||||
|
// Access: Private
|
||||||
|
// Description: Adds the set of packages required by this p3d file to
|
||||||
|
// the _packages member. If _auto_install is true, this
|
||||||
|
// will also start downloading them.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
void P3DInstance::
|
||||||
|
add_packages() {
|
||||||
|
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
|
||||||
|
|
||||||
TiXmlElement *xrequires = _xpackage->FirstChildElement("requires");
|
TiXmlElement *xrequires = _xpackage->FirstChildElement("requires");
|
||||||
while (xrequires != NULL) {
|
while (xrequires != NULL) {
|
||||||
const char *name = xrequires->Attribute("name");
|
const char *name = xrequires->Attribute("name");
|
||||||
@ -1855,15 +1896,9 @@ scan_app_desc_file(TiXmlDocument *doc) {
|
|||||||
|
|
||||||
xrequires = xrequires->NextSiblingElement("requires");
|
xrequires = xrequires->NextSiblingElement("requires");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_matches_run_origin) {
|
|
||||||
nout << "Cannot run " << _p3d_basename << " from origin "
|
|
||||||
<< _origin_protocol << "//" << _origin_hostname
|
|
||||||
<< ":" << _origin_port << "\n";
|
|
||||||
set_failed();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: P3DInstance::find_alt_host_url
|
// Function: P3DInstance::find_alt_host_url
|
||||||
// Access: Private
|
// Access: Private
|
||||||
@ -2270,7 +2305,7 @@ make_splash_window() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_wparams.get_window_type() != P3D_WT_embedded &&
|
if (_wparams.get_window_type() != P3D_WT_embedded &&
|
||||||
!_stuff_to_download && _auto_start && _p3d_trusted) {
|
!_stuff_to_download && _auto_install && _auto_start && _p3d_trusted) {
|
||||||
// If it's a toplevel or fullscreen window, then we don't want a
|
// If it's a toplevel or fullscreen window, then we don't want a
|
||||||
// splash window unless we have stuff to download, or a button to
|
// splash window unless we have stuff to download, or a button to
|
||||||
// display.
|
// display.
|
||||||
@ -2514,41 +2549,59 @@ report_package_info_ready(P3DPackage *package) {
|
|||||||
_total_download_size += package->get_download_size();
|
_total_download_size += package->get_download_size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_downloading_packages.empty() && _download_complete) {
|
ready_to_install();
|
||||||
// We have already been here. Ignore it.
|
|
||||||
|
|
||||||
} else {
|
|
||||||
_download_complete = false;
|
|
||||||
_download_package_index = 0;
|
|
||||||
_total_downloaded = 0;
|
|
||||||
_download_begin = time(NULL);
|
|
||||||
|
|
||||||
nout << "Beginning install of " << _downloading_packages.size()
|
|
||||||
<< " packages, total " << _total_download_size
|
|
||||||
<< " bytes required.\n";
|
|
||||||
|
|
||||||
if (_downloading_packages.size() > 0) {
|
|
||||||
_stuff_to_download = true;
|
|
||||||
|
|
||||||
// Maybe it's time to open a splash window now.
|
|
||||||
make_splash_window();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_splash_window != NULL) {
|
|
||||||
_splash_window->set_install_progress(0.0, true, 0);
|
|
||||||
}
|
|
||||||
_panda_script_object->set_string_property("status", "downloading");
|
|
||||||
_panda_script_object->set_int_property("numDownloadingPackages", _downloading_packages.size());
|
|
||||||
_panda_script_object->set_int_property("totalDownloadSize", _total_download_size);
|
|
||||||
_panda_script_object->set_int_property("downloadElapsedSeconds", 0);
|
|
||||||
_panda_script_object->set_undefined_property("downloadRemainingSeconds");
|
|
||||||
send_notify("ondownloadbegin");
|
|
||||||
|
|
||||||
start_next_download();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: P3DInstance::ready_to_install
|
||||||
|
// Access: Private
|
||||||
|
// Description: Called when it's time to start the package download
|
||||||
|
// process.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
void P3DInstance::
|
||||||
|
ready_to_install() {
|
||||||
|
if (_downloading_packages.empty() && _download_complete) {
|
||||||
|
// We have already been here. Ignore it.
|
||||||
|
|
||||||
|
} else if (!_auto_install && !_download_started) {
|
||||||
|
// Not authorized to download yet. We're waiting for the user
|
||||||
|
// to acknowledge the download.
|
||||||
|
set_background_image(IT_ready);
|
||||||
|
set_button_image(IT_play_ready);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
_download_started = true;
|
||||||
|
_download_complete = false;
|
||||||
|
_download_package_index = 0;
|
||||||
|
_total_downloaded = 0;
|
||||||
|
_download_begin = time(NULL);
|
||||||
|
|
||||||
|
nout << "Beginning install of " << _downloading_packages.size()
|
||||||
|
<< " packages, total " << _total_download_size
|
||||||
|
<< " bytes required.\n";
|
||||||
|
|
||||||
|
if (_downloading_packages.size() > 0) {
|
||||||
|
_stuff_to_download = true;
|
||||||
|
|
||||||
|
// Maybe it's time to open a splash window now.
|
||||||
|
make_splash_window();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_splash_window != NULL) {
|
||||||
|
_splash_window->set_install_progress(0.0, true, 0);
|
||||||
|
}
|
||||||
|
_panda_script_object->set_string_property("status", "downloading");
|
||||||
|
_panda_script_object->set_int_property("numDownloadingPackages", _downloading_packages.size());
|
||||||
|
_panda_script_object->set_int_property("totalDownloadSize", _total_download_size);
|
||||||
|
_panda_script_object->set_int_property("downloadElapsedSeconds", 0);
|
||||||
|
_panda_script_object->set_undefined_property("downloadRemainingSeconds");
|
||||||
|
send_notify("ondownloadbegin");
|
||||||
|
|
||||||
|
start_next_download();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: P3DInstance::start_next_download
|
// Function: P3DInstance::start_next_download
|
||||||
// Access: Private
|
// Access: Private
|
||||||
@ -2645,7 +2698,7 @@ ready_to_start() {
|
|||||||
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
|
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
|
||||||
inst_mgr->start_instance(this);
|
inst_mgr->start_instance(this);
|
||||||
|
|
||||||
} else if (_splash_window != NULL) {
|
} else {
|
||||||
// We're fully downloaded, and waiting for the user to click play.
|
// We're fully downloaded, and waiting for the user to click play.
|
||||||
set_background_image(IT_ready);
|
set_background_image(IT_ready);
|
||||||
set_button_image(IT_play_ready);
|
set_button_image(IT_play_ready);
|
||||||
|
@ -171,6 +171,7 @@ private:
|
|||||||
void mark_p3d_untrusted();
|
void mark_p3d_untrusted();
|
||||||
void mark_p3d_trusted();
|
void mark_p3d_trusted();
|
||||||
void scan_app_desc_file(TiXmlDocument *doc);
|
void scan_app_desc_file(TiXmlDocument *doc);
|
||||||
|
void add_packages();
|
||||||
string find_alt_host_url(const string &host_url, const string &alt_host);
|
string find_alt_host_url(const string &host_url, const string &alt_host);
|
||||||
void get_host_info(P3DHost *host);
|
void get_host_info(P3DHost *host);
|
||||||
|
|
||||||
@ -186,6 +187,7 @@ private:
|
|||||||
void set_background_image(ImageType image_type);
|
void set_background_image(ImageType image_type);
|
||||||
void set_button_image(ImageType image_type);
|
void set_button_image(ImageType image_type);
|
||||||
void report_package_info_ready(P3DPackage *package);
|
void report_package_info_ready(P3DPackage *package);
|
||||||
|
void ready_to_install();
|
||||||
void start_next_download();
|
void start_next_download();
|
||||||
void mark_download_complete();
|
void mark_download_complete();
|
||||||
void ready_to_start();
|
void ready_to_start();
|
||||||
@ -263,6 +265,7 @@ private:
|
|||||||
bool _matches_script_origin;
|
bool _matches_script_origin;
|
||||||
bool _allow_python_dev;
|
bool _allow_python_dev;
|
||||||
bool _keep_user_env;
|
bool _keep_user_env;
|
||||||
|
bool _auto_install;
|
||||||
bool _auto_start;
|
bool _auto_start;
|
||||||
bool _auth_button_clicked;
|
bool _auth_button_clicked;
|
||||||
bool _failed;
|
bool _failed;
|
||||||
@ -306,6 +309,7 @@ private:
|
|||||||
size_t _total_download_size;
|
size_t _total_download_size;
|
||||||
size_t _total_downloaded;
|
size_t _total_downloaded;
|
||||||
time_t _download_begin;
|
time_t _download_begin;
|
||||||
|
bool _download_started;
|
||||||
bool _download_complete;
|
bool _download_complete;
|
||||||
|
|
||||||
// We keep the _panda3d pointer separately because it's so
|
// We keep the _panda3d pointer separately because it's so
|
||||||
|
@ -116,10 +116,6 @@ P3DPackage::
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void P3DPackage::
|
void P3DPackage::
|
||||||
activate_download() {
|
activate_download() {
|
||||||
if (_allow_data_download) {
|
|
||||||
// Already activated.
|
|
||||||
}
|
|
||||||
|
|
||||||
_allow_data_download = true;
|
_allow_data_download = true;
|
||||||
|
|
||||||
if (_ready) {
|
if (_ready) {
|
||||||
@ -243,11 +239,10 @@ uninstall() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now delete all of the directories too. Go in reverse order so we
|
// Now delete all of the directories too. They're already in
|
||||||
// remove deeper directories first.
|
// reverse order, so we remove deeper directories first.
|
||||||
vector<string>::reverse_iterator rci;
|
for (ci = dirname_contents.begin(); ci != dirname_contents.end(); ++ci) {
|
||||||
for (rci = dirname_contents.rbegin(); rci != dirname_contents.rend(); ++rci) {
|
string filename = (*ci);
|
||||||
string filename = (*rci);
|
|
||||||
string pathname = _package_dir + "/" + filename;
|
string pathname = _package_dir + "/" + filename;
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@ -276,6 +271,7 @@ uninstall() {
|
|||||||
_info_ready = false;
|
_info_ready = false;
|
||||||
_ready = false;
|
_ready = false;
|
||||||
_failed = false;
|
_failed = false;
|
||||||
|
_allow_data_download = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
@ -514,7 +510,7 @@ contents_file_redownload_finished(bool success) {
|
|||||||
// rest of the download process.
|
// rest of the download process.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void P3DPackage::
|
void P3DPackage::
|
||||||
host_got_contents_file(bool continue_download) {
|
host_got_contents_file() {
|
||||||
if (!_alt_host.empty()) {
|
if (!_alt_host.empty()) {
|
||||||
// If we have an alt host specification, maybe we need to change
|
// If we have an alt host specification, maybe we need to change
|
||||||
// the host now.
|
// the host now.
|
||||||
@ -548,23 +544,22 @@ host_got_contents_file(bool continue_download) {
|
|||||||
_package_dir += string("/") + _package_version;
|
_package_dir += string("/") + _package_version;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (continue_download) {
|
// Ensure the package directory exists; create it if it does not.
|
||||||
// Ensure the package directory exists; create it if it does not.
|
mkdir_complete(_package_dir, nout);
|
||||||
mkdir_complete(_package_dir, nout);
|
download_desc_file();
|
||||||
|
|
||||||
download_desc_file();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: P3DPackage::download_desc_file
|
// Function: P3DPackage::download_desc_file
|
||||||
// Access: Private
|
// Access: Private
|
||||||
// Description: Starts downloading the desc file for the package, if
|
// Description: Starts downloading the desc file for the package, if
|
||||||
// it's needed; or read to local version if it's fresh
|
// it's needed; or read the local version if it's fresh
|
||||||
// enough.
|
// enough.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void P3DPackage::
|
void P3DPackage::
|
||||||
download_desc_file() {
|
download_desc_file() {
|
||||||
|
assert(!_package_dir.empty());
|
||||||
|
|
||||||
// Attempt to check the desc file for freshness. If it already
|
// Attempt to check the desc file for freshness. If it already
|
||||||
// exists, and is consistent with the server contents file, we don't
|
// exists, and is consistent with the server contents file, we don't
|
||||||
// need to re-download it.
|
// need to re-download it.
|
||||||
|
@ -194,7 +194,7 @@ private:
|
|||||||
void contents_file_download_finished(bool success);
|
void contents_file_download_finished(bool success);
|
||||||
void redownload_contents_file(Download *download);
|
void redownload_contents_file(Download *download);
|
||||||
void contents_file_redownload_finished(bool success);
|
void contents_file_redownload_finished(bool success);
|
||||||
void host_got_contents_file(bool continue_download = true);
|
void host_got_contents_file();
|
||||||
|
|
||||||
void download_desc_file();
|
void download_desc_file();
|
||||||
void desc_file_download_finished(bool success);
|
void desc_file_download_finished(bool success);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user