mirror of
https://github.com/libretro/RetroArch
synced 2025-03-18 04:21:19 +00:00
Merge pull request #10844 from jdgleaver/manual-scan-recursive
(Manual Content Scanner) Add option to disable recursive scanning
This commit is contained in:
commit
4e49114c5c
@ -4472,6 +4472,10 @@ MSG_HASH(
|
|||||||
MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_FILE_EXTS,
|
MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_FILE_EXTS,
|
||||||
"manual_content_scan_file_exts"
|
"manual_content_scan_file_exts"
|
||||||
)
|
)
|
||||||
|
MSG_HASH(
|
||||||
|
MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_SEARCH_RECURSIVELY,
|
||||||
|
"manual_content_scan_search_recursively"
|
||||||
|
)
|
||||||
MSG_HASH(
|
MSG_HASH(
|
||||||
MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_SEARCH_ARCHIVES,
|
MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_SEARCH_ARCHIVES,
|
||||||
"manual_content_scan_search_archives"
|
"manual_content_scan_search_archives"
|
||||||
|
@ -5277,6 +5277,14 @@ MSG_HASH(
|
|||||||
MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_FILE_EXTS,
|
MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_FILE_EXTS,
|
||||||
"List of file types to include in the scan, separated by spaces. If empty, includes all file types, or if a core is specified, all files supported by the core."
|
"List of file types to include in the scan, separated by spaces. If empty, includes all file types, or if a core is specified, all files supported by the core."
|
||||||
)
|
)
|
||||||
|
MSG_HASH(
|
||||||
|
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_SEARCH_RECURSIVELY,
|
||||||
|
"Scan Recursively"
|
||||||
|
)
|
||||||
|
MSG_HASH(
|
||||||
|
MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_SEARCH_RECURSIVELY,
|
||||||
|
"When enabled, all subdirectories of the specified 'Content Directory' will be included in the scan."
|
||||||
|
)
|
||||||
MSG_HASH(
|
MSG_HASH(
|
||||||
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_SEARCH_ARCHIVES,
|
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_SEARCH_ARCHIVES,
|
||||||
"Scan Inside Archives"
|
"Scan Inside Archives"
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
* with a manual content scan */
|
* with a manual content scan */
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
bool search_recursively;
|
||||||
bool search_archives;
|
bool search_archives;
|
||||||
bool filter_dat_content;
|
bool filter_dat_content;
|
||||||
bool overwrite_playlist;
|
bool overwrite_playlist;
|
||||||
@ -68,6 +69,7 @@ typedef struct
|
|||||||
* are not thread safe, but we only access them when pushing a
|
* are not thread safe, but we only access them when pushing a
|
||||||
* task, not in the task thread itself, so all is well) */
|
* task, not in the task thread itself, so all is well) */
|
||||||
static scan_settings_t scan_settings = {
|
static scan_settings_t scan_settings = {
|
||||||
|
true, /* search_recursively */
|
||||||
false, /* search_archives */
|
false, /* search_archives */
|
||||||
false, /* filter_dat_content */
|
false, /* filter_dat_content */
|
||||||
false, /* overwrite_playlist */
|
false, /* overwrite_playlist */
|
||||||
@ -132,6 +134,13 @@ size_t manual_content_scan_get_dat_file_path_size(void)
|
|||||||
return sizeof(scan_settings.dat_file_path);
|
return sizeof(scan_settings.dat_file_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Returns a pointer to the internal
|
||||||
|
* 'search_recursively' bool */
|
||||||
|
bool *manual_content_scan_get_search_recursively_ptr(void)
|
||||||
|
{
|
||||||
|
return &scan_settings.search_recursively;
|
||||||
|
}
|
||||||
|
|
||||||
/* Returns a pointer to the internal
|
/* Returns a pointer to the internal
|
||||||
* 'search_archives' bool */
|
* 'search_archives' bool */
|
||||||
bool *manual_content_scan_get_search_archives_ptr(void)
|
bool *manual_content_scan_get_search_archives_ptr(void)
|
||||||
@ -866,6 +875,9 @@ bool manual_content_scan_get_task_config(
|
|||||||
sizeof(task_config->dat_file_path));
|
sizeof(task_config->dat_file_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Copy 'search recursively' setting */
|
||||||
|
task_config->search_recursively = scan_settings.search_recursively;
|
||||||
|
|
||||||
/* Copy 'search inside archives' setting */
|
/* Copy 'search inside archives' setting */
|
||||||
task_config->search_archives = scan_settings.search_archives;
|
task_config->search_archives = scan_settings.search_archives;
|
||||||
|
|
||||||
@ -911,15 +923,14 @@ struct string_list *manual_content_scan_get_content_list(manual_content_scan_tas
|
|||||||
include_compressed = (!filter_exts || task_config->search_archives);
|
include_compressed = (!filter_exts || task_config->search_archives);
|
||||||
|
|
||||||
/* Get directory listing
|
/* Get directory listing
|
||||||
* > Exclude directories and hidden files
|
* > Exclude directories and hidden files */
|
||||||
* > Scan recursively */
|
|
||||||
dir_list = dir_list_new(
|
dir_list = dir_list_new(
|
||||||
task_config->content_dir,
|
task_config->content_dir,
|
||||||
filter_exts ? task_config->file_exts : NULL,
|
filter_exts ? task_config->file_exts : NULL,
|
||||||
false, /* include_dirs */
|
false, /* include_dirs */
|
||||||
false, /* include_hidden */
|
false, /* include_hidden */
|
||||||
include_compressed,
|
include_compressed,
|
||||||
true /* recursive */
|
task_config->search_recursively
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Sanity check */
|
/* Sanity check */
|
||||||
|
@ -78,6 +78,7 @@ typedef struct
|
|||||||
char file_exts[PATH_MAX_LENGTH];
|
char file_exts[PATH_MAX_LENGTH];
|
||||||
char dat_file_path[PATH_MAX_LENGTH];
|
char dat_file_path[PATH_MAX_LENGTH];
|
||||||
bool core_set;
|
bool core_set;
|
||||||
|
bool search_recursively;
|
||||||
bool search_archives;
|
bool search_archives;
|
||||||
bool filter_dat_content;
|
bool filter_dat_content;
|
||||||
bool overwrite_playlist;
|
bool overwrite_playlist;
|
||||||
@ -117,6 +118,10 @@ char *manual_content_scan_get_dat_file_path_ptr(void);
|
|||||||
* 'dat_file_path' string */
|
* 'dat_file_path' string */
|
||||||
size_t manual_content_scan_get_dat_file_path_size(void);
|
size_t manual_content_scan_get_dat_file_path_size(void);
|
||||||
|
|
||||||
|
/* Returns a pointer to the internal
|
||||||
|
* 'search_recursively' bool */
|
||||||
|
bool *manual_content_scan_get_search_recursively_ptr(void);
|
||||||
|
|
||||||
/* Returns a pointer to the internal
|
/* Returns a pointer to the internal
|
||||||
* 'search_archives' bool */
|
* 'search_archives' bool */
|
||||||
bool *manual_content_scan_get_search_archives_ptr(void);
|
bool *manual_content_scan_get_search_archives_ptr(void);
|
||||||
|
@ -829,6 +829,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_manual_content_scan_system_name,
|
|||||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_manual_content_scan_system_name_custom, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_SYSTEM_NAME_CUSTOM)
|
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_manual_content_scan_system_name_custom, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_SYSTEM_NAME_CUSTOM)
|
||||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_manual_content_scan_core_name, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_CORE_NAME)
|
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_manual_content_scan_core_name, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_CORE_NAME)
|
||||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_manual_content_scan_file_exts, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_FILE_EXTS)
|
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_manual_content_scan_file_exts, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_FILE_EXTS)
|
||||||
|
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_manual_content_scan_search_recursively, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_SEARCH_RECURSIVELY)
|
||||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_manual_content_scan_search_archives, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_SEARCH_ARCHIVES)
|
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_manual_content_scan_search_archives, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_SEARCH_ARCHIVES)
|
||||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_manual_content_scan_dat_file, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_DAT_FILE)
|
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_manual_content_scan_dat_file, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_DAT_FILE)
|
||||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_manual_content_scan_dat_file_filter, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_DAT_FILE_FILTER)
|
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_manual_content_scan_dat_file_filter, MENU_ENUM_SUBLABEL_MANUAL_CONTENT_SCAN_DAT_FILE_FILTER)
|
||||||
@ -3639,6 +3640,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
|
|||||||
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_FILE_EXTS:
|
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_FILE_EXTS:
|
||||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_manual_content_scan_file_exts);
|
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_manual_content_scan_file_exts);
|
||||||
break;
|
break;
|
||||||
|
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_SEARCH_RECURSIVELY:
|
||||||
|
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_manual_content_scan_search_recursively);
|
||||||
|
break;
|
||||||
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_SEARCH_ARCHIVES:
|
case MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_SEARCH_ARCHIVES:
|
||||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_manual_content_scan_search_archives);
|
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_manual_content_scan_search_archives);
|
||||||
break;
|
break;
|
||||||
|
@ -4190,6 +4190,12 @@ static bool menu_displaylist_parse_manual_content_scan_list(
|
|||||||
false) == 0)
|
false) == 0)
|
||||||
count++;
|
count++;
|
||||||
|
|
||||||
|
/* Search recursively */
|
||||||
|
if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(info->list,
|
||||||
|
MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_SEARCH_RECURSIVELY, PARSE_ONLY_BOOL,
|
||||||
|
false) == 0)
|
||||||
|
count++;
|
||||||
|
|
||||||
/* Search inside archive files */
|
/* Search inside archive files */
|
||||||
if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(info->list,
|
if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(info->list,
|
||||||
MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_SEARCH_ARCHIVES, PARSE_ONLY_BOOL,
|
MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_SEARCH_ARCHIVES, PARSE_ONLY_BOOL,
|
||||||
|
@ -17523,6 +17523,21 @@ static bool setting_append_list(
|
|||||||
SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ALLOW_INPUT);
|
SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ALLOW_INPUT);
|
||||||
(*list)[list_info->index - 1].ui_type = ST_UI_TYPE_STRING_LINE_EDIT;
|
(*list)[list_info->index - 1].ui_type = ST_UI_TYPE_STRING_LINE_EDIT;
|
||||||
|
|
||||||
|
CONFIG_BOOL(
|
||||||
|
list, list_info,
|
||||||
|
manual_content_scan_get_search_recursively_ptr(),
|
||||||
|
MENU_ENUM_LABEL_MANUAL_CONTENT_SCAN_SEARCH_RECURSIVELY,
|
||||||
|
MENU_ENUM_LABEL_VALUE_MANUAL_CONTENT_SCAN_SEARCH_RECURSIVELY,
|
||||||
|
true,
|
||||||
|
MENU_ENUM_LABEL_VALUE_OFF,
|
||||||
|
MENU_ENUM_LABEL_VALUE_ON,
|
||||||
|
&group_info,
|
||||||
|
&subgroup_info,
|
||||||
|
parent_group,
|
||||||
|
general_write_handler,
|
||||||
|
general_read_handler,
|
||||||
|
SD_FLAG_NONE);
|
||||||
|
|
||||||
CONFIG_BOOL(
|
CONFIG_BOOL(
|
||||||
list, list_info,
|
list, list_info,
|
||||||
manual_content_scan_get_search_archives_ptr(),
|
manual_content_scan_get_search_archives_ptr(),
|
||||||
|
@ -2866,6 +2866,7 @@ enum msg_hash_enums
|
|||||||
MENU_LABEL(MANUAL_CONTENT_SCAN_SYSTEM_NAME_CUSTOM),
|
MENU_LABEL(MANUAL_CONTENT_SCAN_SYSTEM_NAME_CUSTOM),
|
||||||
MENU_LABEL(MANUAL_CONTENT_SCAN_CORE_NAME),
|
MENU_LABEL(MANUAL_CONTENT_SCAN_CORE_NAME),
|
||||||
MENU_LABEL(MANUAL_CONTENT_SCAN_FILE_EXTS),
|
MENU_LABEL(MANUAL_CONTENT_SCAN_FILE_EXTS),
|
||||||
|
MENU_LABEL(MANUAL_CONTENT_SCAN_SEARCH_RECURSIVELY),
|
||||||
MENU_LABEL(MANUAL_CONTENT_SCAN_SEARCH_ARCHIVES),
|
MENU_LABEL(MANUAL_CONTENT_SCAN_SEARCH_ARCHIVES),
|
||||||
MENU_LABEL(MANUAL_CONTENT_SCAN_DAT_FILE),
|
MENU_LABEL(MANUAL_CONTENT_SCAN_DAT_FILE),
|
||||||
MENU_LABEL(MANUAL_CONTENT_SCAN_DAT_FILE_FILTER),
|
MENU_LABEL(MANUAL_CONTENT_SCAN_DAT_FILE_FILTER),
|
||||||
|
@ -106,6 +106,64 @@ static void free_manual_content_scan_handle(manual_scan_handle_t *manual_scan)
|
|||||||
manual_scan = NULL;
|
manual_scan = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void cb_task_manual_content_scan(
|
||||||
|
retro_task_t *task, void *task_data,
|
||||||
|
void *user_data, const char *err)
|
||||||
|
{
|
||||||
|
manual_scan_handle_t *manual_scan = NULL;
|
||||||
|
playlist_t *cached_playlist = playlist_get_cached();
|
||||||
|
#if defined(RARCH_INTERNAL) && defined(HAVE_MENU)
|
||||||
|
menu_ctx_environment_t menu_environ;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!task)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
manual_scan = (manual_scan_handle_t*)task->state;
|
||||||
|
|
||||||
|
if (!manual_scan)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
/* If the manual content scan task has modified the
|
||||||
|
* currently cached playlist, then it must be re-cached
|
||||||
|
* (otherwise changes will be lost if the currently
|
||||||
|
* cached playlist is saved to disk for any reason...) */
|
||||||
|
if (cached_playlist)
|
||||||
|
{
|
||||||
|
if (string_is_equal(
|
||||||
|
manual_scan->task_config->playlist_file,
|
||||||
|
playlist_get_conf_path(cached_playlist)))
|
||||||
|
{
|
||||||
|
playlist_free_cached();
|
||||||
|
playlist_init_cached(
|
||||||
|
manual_scan->task_config->playlist_file, COLLECTION_SIZE,
|
||||||
|
manual_scan->use_old_format, manual_scan->compress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
/* When creating playlists, the playlist tabs of
|
||||||
|
* any active menu driver must be refreshed */
|
||||||
|
#if defined(RARCH_INTERNAL) && defined(HAVE_MENU)
|
||||||
|
menu_environ.type = MENU_ENVIRON_RESET_HORIZONTAL_LIST;
|
||||||
|
menu_environ.data = NULL;
|
||||||
|
|
||||||
|
menu_driver_ctl(RARCH_MENU_CTL_ENVIRONMENT, &menu_environ);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void task_manual_content_scan_free(retro_task_t *task)
|
||||||
|
{
|
||||||
|
manual_scan_handle_t *manual_scan = NULL;
|
||||||
|
|
||||||
|
if (!task)
|
||||||
|
return;
|
||||||
|
|
||||||
|
manual_scan = (manual_scan_handle_t*)task->state;
|
||||||
|
|
||||||
|
free_manual_content_scan_handle(manual_scan);
|
||||||
|
}
|
||||||
|
|
||||||
static void task_manual_content_scan_handler(retro_task_t *task)
|
static void task_manual_content_scan_handler(retro_task_t *task)
|
||||||
{
|
{
|
||||||
manual_scan_handle_t *manual_scan = NULL;
|
manual_scan_handle_t *manual_scan = NULL;
|
||||||
@ -299,7 +357,6 @@ static void task_manual_content_scan_handler(retro_task_t *task)
|
|||||||
break;
|
break;
|
||||||
case MANUAL_SCAN_END:
|
case MANUAL_SCAN_END:
|
||||||
{
|
{
|
||||||
playlist_t *cached_playlist = playlist_get_cached();
|
|
||||||
char task_title[PATH_MAX_LENGTH];
|
char task_title[PATH_MAX_LENGTH];
|
||||||
|
|
||||||
task_title[0] = '\0';
|
task_title[0] = '\0';
|
||||||
@ -315,23 +372,6 @@ static void task_manual_content_scan_handler(retro_task_t *task)
|
|||||||
manual_scan->use_old_format,
|
manual_scan->use_old_format,
|
||||||
manual_scan->compress);
|
manual_scan->compress);
|
||||||
|
|
||||||
/* If this is the currently cached playlist, then
|
|
||||||
* it must be re-cached (otherwise changes will be
|
|
||||||
* lost if the currently cached playlist is saved
|
|
||||||
* to disk for any reason...) */
|
|
||||||
if (cached_playlist)
|
|
||||||
{
|
|
||||||
if (string_is_equal(
|
|
||||||
manual_scan->task_config->playlist_file,
|
|
||||||
playlist_get_conf_path(cached_playlist)))
|
|
||||||
{
|
|
||||||
playlist_free_cached();
|
|
||||||
playlist_init_cached(
|
|
||||||
manual_scan->task_config->playlist_file, COLLECTION_SIZE,
|
|
||||||
manual_scan->use_old_format, manual_scan->compress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update progress display */
|
/* Update progress display */
|
||||||
task_free_title(task);
|
task_free_title(task);
|
||||||
|
|
||||||
@ -355,8 +395,6 @@ task_finished:
|
|||||||
|
|
||||||
if (task)
|
if (task)
|
||||||
task_set_finished(task, true);
|
task_set_finished(task, true);
|
||||||
|
|
||||||
free_manual_content_scan_handle(manual_scan);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool task_manual_content_scan_finder(retro_task_t *task, void *user_data)
|
static bool task_manual_content_scan_finder(retro_task_t *task, void *user_data)
|
||||||
@ -377,19 +415,6 @@ static bool task_manual_content_scan_finder(retro_task_t *task, void *user_data)
|
|||||||
(const char*)user_data, manual_scan->task_config->playlist_file);
|
(const char*)user_data, manual_scan->task_config->playlist_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cb_task_manual_content_scan_refresh_menu(
|
|
||||||
retro_task_t *task, void *task_data,
|
|
||||||
void *user_data, const char *err)
|
|
||||||
{
|
|
||||||
#if defined(RARCH_INTERNAL) && defined(HAVE_MENU)
|
|
||||||
menu_ctx_environment_t menu_environ;
|
|
||||||
menu_environ.type = MENU_ENVIRON_RESET_HORIZONTAL_LIST;
|
|
||||||
menu_environ.data = NULL;
|
|
||||||
|
|
||||||
menu_driver_ctl(RARCH_MENU_CTL_ENVIRONMENT, &menu_environ);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool task_push_manual_content_scan(void)
|
bool task_push_manual_content_scan(void)
|
||||||
{
|
{
|
||||||
task_finder_data_t find_data;
|
task_finder_data_t find_data;
|
||||||
@ -468,7 +493,8 @@ bool task_push_manual_content_scan(void)
|
|||||||
task->title = strdup(task_title);
|
task->title = strdup(task_title);
|
||||||
task->alternative_look = true;
|
task->alternative_look = true;
|
||||||
task->progress = 0;
|
task->progress = 0;
|
||||||
task->callback = cb_task_manual_content_scan_refresh_menu;
|
task->callback = cb_task_manual_content_scan;
|
||||||
|
task->cleanup = task_manual_content_scan_free;
|
||||||
|
|
||||||
/* > Push task */
|
/* > Push task */
|
||||||
task_queue_push(task);
|
task_queue_push(task);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user