mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-17 15:53:28 +00:00
[search] added 'advanced search' for top-search-form and search-result page
This commit is contained in:
@@ -90,6 +90,7 @@ button {
|
||||
}
|
||||
input[type=radio] {
|
||||
height:auto;
|
||||
border:0;/*for ie*/
|
||||
}
|
||||
input::-webkit-outer-spin-button,
|
||||
input::-webkit-inner-spin-button {/*for input type="number" in chrome: to hide up/download arrow*/
|
||||
@@ -2222,13 +2223,13 @@ textarea:-moz-placeholder {/* for FF */
|
||||
}
|
||||
|
||||
/* search */
|
||||
.search-form {
|
||||
.search-form,
|
||||
#search-form .input_and_submit {
|
||||
padding-left:5px;
|
||||
height:24px;
|
||||
border:1px solid #ddd;
|
||||
border-radius:3px;
|
||||
background:#fff;
|
||||
/*overflow:hidden;for some chromium*/
|
||||
position:relative;
|
||||
}
|
||||
#top-search-form {
|
||||
@@ -2250,25 +2251,46 @@ textarea:-moz-placeholder {/* for FF */
|
||||
cursor:pointer;
|
||||
line-height:24px;
|
||||
}
|
||||
.search-scales {
|
||||
#advanced-search-form {
|
||||
position:absolute;
|
||||
padding:2px 0;
|
||||
width:500px;
|
||||
padding:18px;
|
||||
height:auto;
|
||||
font-weight:normal;
|
||||
background:#fff;
|
||||
box-shadow:0 0 10px #ddd;
|
||||
z-index:100;/* for pages with jquery tabs*/
|
||||
}
|
||||
#advanced-search-form .search-input {
|
||||
width:98%;
|
||||
padding:0 3px;
|
||||
border:1px solid #ddd;
|
||||
border-radius:3px;
|
||||
box-shadow:0 0 10px #ddd;
|
||||
}
|
||||
#advanced-search-form .submit {
|
||||
margin-bottom:0;
|
||||
}
|
||||
.search-scales {
|
||||
margin-top:8px;
|
||||
}
|
||||
.search-scales .item {
|
||||
padding:2px 6px;
|
||||
width:48%;
|
||||
}
|
||||
.search-scales .item-hover {
|
||||
color:#fff;
|
||||
background:#666;
|
||||
cursor:pointer;
|
||||
.search-repos {
|
||||
border-bottom:1px dotted #ddd;
|
||||
margin-bottom:3px;
|
||||
}
|
||||
.search-scales [class^="icon-"] {
|
||||
margin-right:5px;
|
||||
.custom-ftype-options {
|
||||
font-size:13px;
|
||||
padding:2px 0;
|
||||
background:#fafafa;
|
||||
}
|
||||
.custom-ftype-options .checkbox-label {
|
||||
display:inline-block;
|
||||
margin:0 10px 0 0;
|
||||
}
|
||||
.custom-ftype-options .fileext-input {
|
||||
padding:0 3px;
|
||||
width:70%;
|
||||
}
|
||||
.search-form .search-submit {
|
||||
width:30px;
|
||||
@@ -2279,17 +2301,26 @@ textarea:-moz-placeholder {/* for FF */
|
||||
color:#aaa;
|
||||
}
|
||||
#search-form {
|
||||
padding:7px 5px;
|
||||
height:auto;
|
||||
background:#f7f7f8;
|
||||
border:0;
|
||||
border-radius:2px;
|
||||
}
|
||||
#search-form .input_and_submit {
|
||||
display:inline-block;
|
||||
width:426px;
|
||||
*width:430px;
|
||||
}
|
||||
#search-form .advanced-search {
|
||||
color:#666;
|
||||
font-size:16px;
|
||||
cursor:pointer;
|
||||
margin-left:4px;
|
||||
}
|
||||
#search-form .search-input {
|
||||
width:392px;
|
||||
}
|
||||
#search-form-container {
|
||||
padding:7px 5px;
|
||||
background:#f7f7f8;
|
||||
border-radius:2px;
|
||||
}
|
||||
#search-results {
|
||||
padding-top:15px;
|
||||
}
|
||||
|
@@ -362,3 +362,15 @@ function disable(btn) {
|
||||
function enable(btn) {
|
||||
btn.removeAttr('disabled').removeClass('btn-disabled');
|
||||
}
|
||||
|
||||
// for browsers don't support array.indexOf
|
||||
if (!Array.indexOf) {
|
||||
Array.prototype.indexOf = function(obj){
|
||||
for(var i = 0; i < this.length; i++){
|
||||
if(this[i] == obj){
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@@ -71,24 +71,61 @@
|
||||
|
||||
{% if enable_file_search and request.user.is_authenticated %}
|
||||
<form id="top-search-form" method="get" action="{% url 'search' %}" class="search-form fright">
|
||||
<input class="search-input" name="q" placeholder="{% trans 'Search Files' %}" value="{{ keyword }}" />
|
||||
<input type="hidden" name="scale" value="all" />
|
||||
{% if search_repo_id %}
|
||||
<input type="hidden" name="search_repo_id" value="{{ search_repo_id }}" />
|
||||
{% endif %}
|
||||
<span class="icon-caret-down" title="{% trans "choose search scale" %}"></span><button type="submit" class="search-submit"><span class="icon-search"></span></button>
|
||||
<ul class="search-scales hide">
|
||||
<li class="item" data-value="all"><span class="icon-ok"></span>{% trans "In all libraries" %}</li>
|
||||
{% if search_repo_id %}
|
||||
<li class="item" data-value="current"><span class="icon-"></span>
|
||||
{% if search_wiki %}
|
||||
{% trans "In this wiki" %}
|
||||
<input class="search-input" name="q" placeholder="{% if search_wiki %}{% trans "Search files in this wiki" %}{% else %}{% trans "Search files in this library" %}{% endif %}" value="{{ keyword }}" />
|
||||
<input type="hidden" name="search_repo" value="{{ search_repo_id }}" />
|
||||
{% else %}
|
||||
{% trans "In this library" %}
|
||||
<input class="search-input" name="q" placeholder="{% trans 'Search Files' %}" value="{{ keyword }}" />
|
||||
{% endif %}
|
||||
</li>
|
||||
<span class="icon-caret-down" title="{% trans "advanced" %}"></span><button type="submit" class="search-submit"><span class="icon-search"></span></button>
|
||||
</form>
|
||||
|
||||
<form id="advanced-search-form" method="get" action="{% url 'search' %}" class="search-form hide">
|
||||
<input class="search-input" name="q" placeholder="{% trans 'Search Files' %}" value="{{ keyword }}" />
|
||||
<div class="search-scales">
|
||||
<div class="search-repos">
|
||||
{% if search_repo_id %}
|
||||
<label class="item"><input type="radio" name="search_repo" value="all" class="vam" /> <span class="vam">{% trans "In all libraries" %}</span></label>
|
||||
<label class="item"><input type="radio" name="search_repo" checked="checked" value="{{ search_repo_id }}" class="vam" /> <span class="vam">{% if search_wiki %}{% trans "In this wiki" %}{% else %}{% trans "In this library" %}{% endif %}</span></label>
|
||||
{% else %}
|
||||
{% if search_repo and repo %}
|
||||
<label class="item"><input type="radio" name="search_repo" value="all" class="vam" /> <span class="vam">{% trans "In all libraries" %}</span></label>
|
||||
<label class="item"><input type="radio" name="search_repo" value="{{repo.id}}" checked="checked" class="vam" /> <span class="vam">{% blocktrans with name=repo.name %}in {{name }}{% endblocktrans %}</span></label>
|
||||
{% else %}
|
||||
<label class="item"><input type="radio" name="search_repo" value="all" checked="checked" class="vam" /> <span class="vam">{% trans "In all libraries" %}</span></label>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="search-filetypes">
|
||||
{% if search_ftypes != 'custom' %}
|
||||
<label class="item"><input type="radio" name="search_ftypes" value="all" checked="checked" class="vam" /> <span class="vam">{% trans "All file types" %}</span></label>
|
||||
<label class="item"><input type="radio" name="search_ftypes" value="custom" class="vam" id="custom-search-ftypes" /> <span class="vam">{% trans "Custom file types" %}</span></label>
|
||||
{% else %}
|
||||
<label class="item"><input type="radio" name="search_ftypes" value="all" class="vam" /> <span class="vam">{% trans "All file types" %}</span></label>
|
||||
<label class="item"><input type="radio" name="search_ftypes" value="custom" checked="checked" class="vam" id="custom-search-ftypes" /> <span class="vam">{% trans "Custom file types" %}</span></label>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if search_ftypes != 'custom' %}
|
||||
<div class="custom-ftype-options hide">
|
||||
{% else %}
|
||||
<div class="custom-ftype-options">
|
||||
{% endif %}
|
||||
{# ftype value should be the same with utils/file_types.py #}
|
||||
<label class="checkbox-label"><span class="checkbox"><input type="checkbox" name="ftype" value="Text" class="checkbox-orig" /></span><span class="checkbox-option">{% trans "Text files" %}</span></label>
|
||||
<label class="checkbox-label"><span class="checkbox"><input type="checkbox" name="ftype" value="Document" class="checkbox-orig" /></span><span class="checkbox-option">{% trans "Documents" %}</span></label>
|
||||
<label class="checkbox-label"><span class="checkbox"><input type="checkbox" name="ftype" value="Image" class="checkbox-orig" /></span><span class="checkbox-option">{% trans "Images" %}</span></label>
|
||||
<label class="checkbox-label"><span class="checkbox"><input type="checkbox" name="ftype" value="Video" class="checkbox-orig" /></span><span class="checkbox-option">{% trans "Video" %}</span></label>
|
||||
<label class="checkbox-label"><span class="checkbox"><input type="checkbox" name="ftype" value="Audio" class="checkbox-orig" /></span><span class="checkbox-option">{% trans "Audio" %}</span></label>
|
||||
<label class="checkbox-label"><span class="checkbox"><input type="checkbox" name="ftype" value="SVG" class="checkbox-orig" /></span><span class="checkbox-option">{% trans "svg" %}</span></label>
|
||||
<label class="checkbox-label"><span class="checkbox"><input type="checkbox" name="ftype" value="PDF" class="checkbox-orig" /></span><span class="checkbox-option">{% trans "pdf" %}</span></label>
|
||||
<label class="checkbox-label"><span class="checkbox"><input type="checkbox" name="ftype" value="Sf" class="checkbox-orig" /></span><span class="checkbox-option">{% trans "seaf" %}</span></label>
|
||||
<label class="checkbox-label"><span class="checkbox"><input type="checkbox" name="ftype" value="Markdown" class="checkbox-orig" /></span><span class="checkbox-option">{% trans "markdown" %}</span></label>
|
||||
<br/>
|
||||
<input type="text" value="{{ input_fileexts }}" name="input_fexts" placeholder="{% trans "Input file extensions here, separate with ','" %}" class="fileext-input" />
|
||||
<p class="error hide">{% trans "Please select at least 1 file type or input at least 1 file extension" %}</p>
|
||||
</div>
|
||||
</div>
|
||||
<input type="submit" value="{% trans "Submit" %}" class="submit" />
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -204,40 +241,41 @@ $(document).ready(function(){
|
||||
});
|
||||
|
||||
{% if enable_file_search %}
|
||||
$('#top-search-form .icon-caret-down').click(function() {
|
||||
var form = $(this).parent();
|
||||
form.find('.search-scales').css({'right':form.find('.search-submit').outerWidth(true), 'top':form.outerHeight()}).toggleClass('hide');
|
||||
// search-form: top-search-form, advanced-search-form, search-form in search result page
|
||||
$('.search-form').submit(function() {
|
||||
if (!$.trim($(this).find('.search-input').val())) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
$('.search-scales li').hover(
|
||||
function() {
|
||||
$(this).addClass('item-hover');
|
||||
},
|
||||
function() {
|
||||
$(this).removeClass('item-hover');
|
||||
}
|
||||
).click(function() {
|
||||
$(this).parent().find('span').attr('class', 'icon-');
|
||||
$(this).children('span').attr('class', 'icon-ok');
|
||||
if (!pseudo_support) {
|
||||
$(this).parent().find('span').html('');
|
||||
setCon($(this).children('span'), 'icon-', fa_icons);
|
||||
}
|
||||
$('#top-search-form [name="scale"]').val($(this).data('value'));
|
||||
$(this).parent().addClass('hide');
|
||||
var search_input = $('#top-search-form .search-input');
|
||||
if ($.trim(search_input.val())) {
|
||||
$('#top-search-form').submit();
|
||||
} else {
|
||||
search_input.attr('placeholder', $(this).text().replace(/^&#x\w+;$/,'')); // replace(...) to rm icon text for some browsers
|
||||
if (!('placeholder' in document.createElement('input'))) { // for browsers which don't natively support 'placeholder' attr
|
||||
search_input.attr('value', search_input.attr('placeholder'));
|
||||
$('#top-search-form .icon-caret-down').click(function() {
|
||||
var form = $(this).parent(),
|
||||
q = form.find('[name="q"]').val(),
|
||||
advanced_form = $('#advanced-search-form');
|
||||
$('#header-inner').css({'position':'relative'});
|
||||
advanced_form.css({'right': 0, 'top':form.outerHeight(true)}).toggleClass('hide');
|
||||
if (!advanced_form.hasClass('hide') && q) {
|
||||
advanced_form.find('.search-input').val(q);
|
||||
}
|
||||
});
|
||||
$('.search-filetypes .item:first-child').click(function() {
|
||||
$(this).parents('.search-form').find('.custom-ftype-options').addClass('hide');
|
||||
});
|
||||
$('.search-filetypes .item:last-child').click(function() {
|
||||
$(this).parents('.search-form').find('.custom-ftype-options').removeClass('hide');
|
||||
});
|
||||
$('#advanced-search-form').submit(function() {
|
||||
if ($('#custom-search-ftypes').attr('checked') &&
|
||||
$('.custom-ftype-options .checkbox-checked').length == 0 &&
|
||||
!$.trim($('.custom-ftype-options .fileext-input').val())) {
|
||||
$(this).find('.error').removeClass('hide');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
$(document).click(function(e) {
|
||||
var target = e.target || event.srcElement;
|
||||
if (!$('.search-scales, #top-search-form .icon-caret-down').is(target) && !($('.search-scales').find('*').is(target))) {
|
||||
$('.search-scales').addClass('hide');
|
||||
var popup = $('#advanced-search-form');
|
||||
if (!popup.is(target) && !popup.find('*').is(target) && !$('#top-search-form .icon-caret-down').is(target)) {
|
||||
popup.addClass('hide');
|
||||
}
|
||||
});
|
||||
{% endif %}
|
||||
@@ -282,7 +320,9 @@ $(document).click(function(e) {
|
||||
{'name':'star', 'con':'f005'},
|
||||
{'name':'star-empty', 'con':'f006'},
|
||||
{'name':'caret-down', 'con':'f0d7'},
|
||||
{'name':'ok', 'con':'f00c'}
|
||||
{'name':'ok', 'con':'f00c'},
|
||||
{'name':'double-angle-up', 'con':'f102'},
|
||||
{'name':'double-angle-down', 'con':'f103'}
|
||||
];
|
||||
function setCon(icon, icon_class_prefix, icon_list) {
|
||||
for (var i = 0, len = icon_list.length; i < len; i++) {
|
||||
|
@@ -4,26 +4,20 @@
|
||||
{% load url from future %}
|
||||
|
||||
{% block right_panel %}
|
||||
<div id="search-form-container">
|
||||
<form method="get" action="{% url 'search' %}" class="search-form" id="search-form">
|
||||
<form method="get" action="{% url 'search' %}" class="search-form" id="search-form">
|
||||
<div class="input_and_submit">
|
||||
<input class="search-input" name="q" placeholder="{% trans 'Search Files' %}" value="{{ keyword }}" />
|
||||
<button type="submit" class="search-submit"><span class="icon-search"></span></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<span class="advanced-search vam icon-double-angle-{% if not search_repo and not search_ftypes %}down{% else %}up{% endif %}" title="{% trans "advanced" %}"></span>
|
||||
</form>
|
||||
|
||||
<div id="search-results">
|
||||
{% if not error %}
|
||||
{% if not results %}
|
||||
<p>{% trans 'No result found' %}
|
||||
{% if repo %}
|
||||
{% trans 'in repo' %} <a href="{% url 'repo' repo_id=repo.id %}">{{ repo.name }}</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
<p>{% trans 'No result found' %}</p>
|
||||
{% else %}
|
||||
<p class="tip">{% blocktrans count counter=total %}{{ total }} result{% plural %}{{ total }} results{% endblocktrans%}
|
||||
{% if repo %}
|
||||
{% trans 'found in repo' %} <a href="{% url 'repo' repo_id=repo.id %}">{{ repo.name }}</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
<p class="tip">{% blocktrans count counter=total %}{{ total }} result{% plural %}{{ total }} results{% endblocktrans%}</p>
|
||||
<ul id="search-results-list">
|
||||
{% for file in results %}
|
||||
<li class="search-results-item ovhd">
|
||||
@@ -78,5 +72,44 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block extra_script %}
|
||||
<script type="text/javascript">
|
||||
(function() {
|
||||
var form = $('#search-form');
|
||||
{% if search_repo or search_ftypes %}
|
||||
form.append($('#advanced-search-form .search-scales').clone(true));
|
||||
{% endif %}
|
||||
form.find('.advanced-search').click(function() {
|
||||
if (form.find('.search-scales').length == 0) {
|
||||
form.append($('#advanced-search-form .search-scales').clone(true));
|
||||
} else {
|
||||
form.find('.search-scales').toggleClass('hide');
|
||||
}
|
||||
var it = $(this),
|
||||
str = 'icon-double-angle-';
|
||||
if (it.hasClass(str + 'down')) {
|
||||
it.removeClass(str + 'down').addClass(str + 'up');
|
||||
} else {
|
||||
it.removeClass(str + 'up').addClass(str + 'down');
|
||||
}
|
||||
return false;
|
||||
});
|
||||
{% if custom_ftypes %}
|
||||
var custom_ftypes = [];
|
||||
{% for f in custom_ftypes %}
|
||||
custom_ftypes.push("{{f}}");
|
||||
{% endfor %}
|
||||
var ftype_options = form.find('[name="ftype"]');
|
||||
ftype_options.each(function() {
|
||||
if (custom_ftypes.indexOf($(this).val()) != -1) {
|
||||
$(this).attr('checked', true);
|
||||
$(this).parent().addClass('checkbox-checked');
|
||||
}
|
||||
});
|
||||
{% endif %}
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@@ -53,7 +53,7 @@ EMPTY_SHA1 = '0000000000000000000000000000000000000000'
|
||||
MAX_INT = 2147483647
|
||||
|
||||
PREVIEW_FILEEXT = {
|
||||
TEXT: ('ac', 'am', 'bat', 'c', 'cc', 'cmake', 'cpp', 'cs', 'css', 'diff', 'el', 'h', 'html', 'htm', 'java', 'js', 'json', 'less', 'make', 'org', 'php', 'pl', 'properties', 'py', 'rb', 'scala', 'script', 'sh', 'sql', 'txt', 'text', 'tex', 'vi', 'vim', 'xhtml', 'xml', 'log', 'csv', 'groovy', 'rst', 'patch', ),
|
||||
TEXT: ('ac', 'am', 'bat', 'c', 'cc', 'cmake', 'cpp', 'cs', 'css', 'diff', 'el', 'h', 'html', 'htm', 'java', 'js', 'json', 'less', 'make', 'org', 'php', 'pl', 'properties', 'py', 'rb', 'scala', 'script', 'sh', 'sql', 'txt', 'text', 'tex', 'vi', 'vim', 'xhtml', 'xml', 'log', 'csv', 'groovy', 'rst', 'patch'),
|
||||
IMAGE: ('gif', 'jpeg', 'jpg', 'png', 'ico'),
|
||||
DOCUMENT: ('doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx'),
|
||||
SVG: ('svg',),
|
||||
|
@@ -3,27 +3,41 @@ from django.template import RequestContext
|
||||
|
||||
from seaserv import get_repo
|
||||
from seahub.auth.decorators import login_required
|
||||
from seahub.utils import PREVIEW_FILEEXT
|
||||
from seahub.utils.search import search_file_by_name, search_repo_file_by_name
|
||||
|
||||
|
||||
def parse_suffixes(suffixes):
|
||||
if not suffixes:
|
||||
return None
|
||||
|
||||
l = []
|
||||
for suffix in suffixes.split(','):
|
||||
suffix = suffix.strip()
|
||||
if suffix:
|
||||
l.append(suffix)
|
||||
|
||||
print l
|
||||
|
||||
return l
|
||||
|
||||
@login_required
|
||||
def search(request):
|
||||
keyword = request.GET['q']
|
||||
suffixes = parse_suffixes(request.GET.get('suffixes', None))
|
||||
template = 'search_results.html'
|
||||
error = False
|
||||
|
||||
keyword = request.GET.get('q', None)
|
||||
if not keyword:
|
||||
return render_to_response(template, {
|
||||
'error': True,
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
# advanced search
|
||||
search_repo = request.GET.get('search_repo', None) # val: 'all' or 'search_repo_id'
|
||||
search_ftypes = request.GET.get('search_ftypes', None) # val: 'all' or 'custom'
|
||||
custom_ftypes = request.GET.getlist('ftype') # types like 'Image', 'Video'... same in utils/file_types.py
|
||||
input_fileexts = request.GET.get('input_fexts', '') # file extension input by the user
|
||||
|
||||
suffixes = None
|
||||
if search_ftypes == 'custom':
|
||||
suffixes = []
|
||||
if len(custom_ftypes) > 0:
|
||||
for ftp in custom_ftypes:
|
||||
if PREVIEW_FILEEXT.has_key(ftp):
|
||||
for ext in PREVIEW_FILEEXT[ftp]:
|
||||
suffixes.append(ext)
|
||||
|
||||
if input_fileexts:
|
||||
input_fexts = input_fileexts.split(',')
|
||||
for i_ext in input_fexts:
|
||||
i_ext = i_ext.strip()
|
||||
if i_ext:
|
||||
suffixes.append(i_ext)
|
||||
|
||||
current_page = int(request.GET.get('page', '1'))
|
||||
per_page= int(request.GET.get('per_page', '25'))
|
||||
@@ -31,10 +45,9 @@ def search(request):
|
||||
start = (current_page - 1) * per_page
|
||||
size = per_page
|
||||
|
||||
scale = request.GET.get('scale', None)
|
||||
repo_id = request.GET.get('search_repo_id', None)
|
||||
repo = None
|
||||
if scale == 'current' and repo_id:
|
||||
if search_repo and search_repo != 'all':
|
||||
repo_id = search_repo
|
||||
repo = get_repo(repo_id)
|
||||
if repo:
|
||||
results, total = search_repo_file_by_name(request, repo, keyword, suffixes, start, size)
|
||||
@@ -48,7 +61,7 @@ def search(request):
|
||||
else:
|
||||
has_more = False
|
||||
|
||||
return render_to_response('search_results.html', {
|
||||
return render_to_response(template, {
|
||||
'repo': repo,
|
||||
'keyword': keyword,
|
||||
'results': results,
|
||||
@@ -58,4 +71,9 @@ def search(request):
|
||||
'prev_page': current_page - 1,
|
||||
'next_page': current_page + 1,
|
||||
'per_page': per_page,
|
||||
'search_repo': search_repo,
|
||||
'search_ftypes': search_ftypes,
|
||||
'custom_ftypes': custom_ftypes,
|
||||
'input_fileexts': input_fileexts,
|
||||
'error': error,
|
||||
}, context_instance=RequestContext(request))
|
||||
|
Reference in New Issue
Block a user