Added display of records without login
This commit is contained in:
parent
2143b261ac
commit
81858e309d
12
.idea/dataSources.xml
Normal file
12
.idea/dataSources.xml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||||
|
<data-source source="LOCAL" name="mysql@localhost" uuid="5dc17320-52b3-48ce-b415-835ec0d6e7b6">
|
||||||
|
<driver-ref>mysql.8</driver-ref>
|
||||||
|
<synchronize>true</synchronize>
|
||||||
|
<jdbc-driver>com.mysql.cj.jdbc.Driver</jdbc-driver>
|
||||||
|
<jdbc-url>jdbc:mysql://localhost:3306/sarbaseapp</jdbc-url>
|
||||||
|
<working-dir>$ProjectFileDir$</working-dir>
|
||||||
|
</data-source>
|
||||||
|
</component>
|
||||||
|
</project>
|
6
.idea/jsLibraryMappings.xml
Normal file
6
.idea/jsLibraryMappings.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="JavaScriptLibraryMappings">
|
||||||
|
<file url="file://$PROJECT_DIR$" libraries="{leaflet-control-geocoder}" />
|
||||||
|
</component>
|
||||||
|
</project>
|
2
admin.py
2
admin.py
@ -23,10 +23,10 @@ class UserModelView(AdminModelView):
|
|||||||
|
|
||||||
admin = Admin(app, name='SAR Admin', template_mode='bootstrap3')
|
admin = Admin(app, name='SAR Admin', template_mode='bootstrap3')
|
||||||
admin.add_view(UserModelView(User, db.session))
|
admin.add_view(UserModelView(User, db.session))
|
||||||
admin.add_view(AdminModelView(Role, db.session))
|
|
||||||
admin.add_view(AdminModelView(SARCall, db.session))
|
admin.add_view(AdminModelView(SARCall, db.session))
|
||||||
admin.add_view(AdminModelView(Comment, db.session))
|
admin.add_view(AdminModelView(Comment, db.session))
|
||||||
admin.add_view(AdminModelView(GPSTrack, db.session))
|
admin.add_view(AdminModelView(GPSTrack, db.session))
|
||||||
|
admin.add_view(AdminModelView(Role, db.session, category="Dictionaries"))
|
||||||
admin.add_view(AdminModelView(SARCategory, db.session, category="Dictionaries"))
|
admin.add_view(AdminModelView(SARCategory, db.session, category="Dictionaries"))
|
||||||
admin.add_view(AdminModelView(SARStatus, db.session, category="Dictionaries"))
|
admin.add_view(AdminModelView(SARStatus, db.session, category="Dictionaries"))
|
||||||
admin.add_view(AdminModelView(SARResult, db.session, category="Dictionaries"))
|
admin.add_view(AdminModelView(SARResult, db.session, category="Dictionaries"))
|
||||||
|
2
app.py
2
app.py
@ -47,7 +47,7 @@ import dashboard
|
|||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
def hello_world(): # put application's code here
|
def hello_world(): # put application's code here
|
||||||
return redirect(url_for('dashboard'))
|
return redirect(url_for('list_sar'))
|
||||||
|
|
||||||
|
|
||||||
@app.route('/set_language/<lang_code>')
|
@app.route('/set_language/<lang_code>')
|
||||||
|
@ -30,6 +30,7 @@ class SARCall(db.Model):
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return self.title
|
return self.title
|
||||||
|
|
||||||
|
|
||||||
class User(UserMixin, db.Model):
|
class User(UserMixin, db.Model):
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
created = db.Column(db.DateTime, nullable=False, default=datetime.now)
|
created = db.Column(db.DateTime, nullable=False, default=datetime.now)
|
||||||
|
48
sar_calls.py
48
sar_calls.py
@ -1,10 +1,11 @@
|
|||||||
from dateutil import parser
|
from dateutil import parser
|
||||||
from flask import request, redirect, flash, render_template, url_for, jsonify, Response
|
from flask import request, redirect, flash, render_template, url_for, jsonify, Response
|
||||||
from flask_login import login_required, current_user
|
from flask_login import login_required, current_user
|
||||||
from sqlalchemy import or_
|
from sqlalchemy import or_, and_
|
||||||
|
from sqlalchemy.orm import aliased
|
||||||
|
|
||||||
from app import app, db
|
from app import app, db
|
||||||
from models import SARCall, Comment, GPSTrack, SARCategory, SARStatus, User, Role
|
from models import SARCall, Comment, GPSTrack, SARCategory, SARStatus, User, Role, SARResult
|
||||||
|
|
||||||
|
|
||||||
@app.route('/create_sar', methods=['GET', 'POST'])
|
@app.route('/create_sar', methods=['GET', 'POST'])
|
||||||
@ -14,7 +15,6 @@ def create_sar():
|
|||||||
statuses = SARStatus.query.order_by('id').all()
|
statuses = SARStatus.query.order_by('id').all()
|
||||||
managers = User.query.join(Role).filter(or_(Role.name == 'search manager', Role.name == 'admin')).all()
|
managers = User.query.join(Role).filter(or_(Role.name == 'search manager', Role.name == 'admin')).all()
|
||||||
|
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
start_date = parser.parse(request.form.get('start_date'))
|
start_date = parser.parse(request.form.get('start_date'))
|
||||||
category = request.form.get('category')
|
category = request.form.get('category')
|
||||||
@ -50,12 +50,20 @@ def create_sar():
|
|||||||
|
|
||||||
|
|
||||||
@app.route('/list_sar')
|
@app.route('/list_sar')
|
||||||
@login_required
|
|
||||||
def list_sar():
|
def list_sar():
|
||||||
sar_calls = SARCall.query.join(User, SARCall.search_officer_id == User.id).join(SARCategory,
|
is_logged_in = current_user.is_authenticated
|
||||||
SARCall.category == SARCategory.id).add_columns(
|
search_officer = aliased(User)
|
||||||
SARCategory, User, SARCall).all()
|
coordination_officer = aliased(User)
|
||||||
return render_template('list_sar.html', sar_calls=sar_calls)
|
|
||||||
|
sar_calls = (SARCall.query
|
||||||
|
.outerjoin(search_officer, and_ (SARCall.search_officer_id == search_officer.id, SARCall.search_officer_id != None))
|
||||||
|
.join(coordination_officer, SARCall.coordination_officer_id == coordination_officer.id)
|
||||||
|
.join(SARCategory, SARCall.category == SARCategory.id)
|
||||||
|
.join(SARStatus, SARCall.status == SARStatus.id)
|
||||||
|
.add_columns(SARCategory, SARCall, SARStatus)
|
||||||
|
.all())
|
||||||
|
|
||||||
|
return render_template('list_sar.html', sar_calls=sar_calls, is_logged_in=is_logged_in)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/edit_sar/<int:id>', methods=['GET', 'POST'])
|
@app.route('/edit_sar/<int:id>', methods=['GET', 'POST'])
|
||||||
@ -94,12 +102,27 @@ def edit_sar(id):
|
|||||||
|
|
||||||
@app.route('/sar_details/<int:id>')
|
@app.route('/sar_details/<int:id>')
|
||||||
def sar_details(id):
|
def sar_details(id):
|
||||||
sar = SARCall.query.get_or_404(id) # Fetch the SARCall record or return 404
|
is_logged_in = current_user.is_authenticated
|
||||||
|
search_officer = aliased(User)
|
||||||
|
coordination_officer = aliased(User)
|
||||||
|
|
||||||
|
sar = (SARCall.query
|
||||||
|
.outerjoin(search_officer,
|
||||||
|
and_(SARCall.search_officer_id == search_officer.id, SARCall.search_officer_id != None))
|
||||||
|
.join(coordination_officer, SARCall.coordination_officer_id == coordination_officer.id)
|
||||||
|
.join(SARCategory, SARCall.category == SARCategory.id)
|
||||||
|
.join(SARStatus, SARCall.status == SARStatus.id)
|
||||||
|
.outerjoin(SARResult, and_(SARCall.result == SARResult.id, SARCall.result != None))
|
||||||
|
.add_columns(SARCall, SARCategory, SARStatus, SARResult)
|
||||||
|
.filter(SARCall.id == id).first())
|
||||||
|
|
||||||
|
comments = Comment.query.filter_by(sar_call_id=id).all()
|
||||||
|
|
||||||
gpx_files = [id[0] for id in GPSTrack.query.with_entities(GPSTrack.id).filter_by(
|
gpx_files = [id[0] for id in GPSTrack.query.with_entities(GPSTrack.id).filter_by(
|
||||||
sar_call_id=id).all()] # Fetch all GPX files for this SARCall
|
sar_call_id=id).all()] # Fetch all GPX files for this SARCall
|
||||||
comments_with_gpx = []
|
comments_with_gpx = []
|
||||||
|
|
||||||
for comment in sar.comments:
|
for comment in comments:
|
||||||
gpx_tracks = GPSTrack.query.filter_by(comment_id=comment.id).all()
|
gpx_tracks = GPSTrack.query.filter_by(comment_id=comment.id).all()
|
||||||
for track in gpx_tracks:
|
for track in gpx_tracks:
|
||||||
comments_with_gpx.append({
|
comments_with_gpx.append({
|
||||||
@ -109,7 +132,9 @@ def sar_details(id):
|
|||||||
"comment": track.gpx_name
|
"comment": track.gpx_name
|
||||||
})
|
})
|
||||||
|
|
||||||
return render_template('sar_details.html', sar=sar, gpx_ids=gpx_files, comments_with_gpx=comments_with_gpx)
|
print(sar)
|
||||||
|
|
||||||
|
return render_template('sar_details.html', sar=sar, gpx_ids=gpx_files, comments_with_gpx=comments_with_gpx,is_logged_in=is_logged_in)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/delete_sar/<int:id>')
|
@app.route('/delete_sar/<int:id>')
|
||||||
@ -180,7 +205,6 @@ def upload_gpx():
|
|||||||
|
|
||||||
|
|
||||||
@app.route('/get_gpx/<int:gpx_id>')
|
@app.route('/get_gpx/<int:gpx_id>')
|
||||||
@login_required
|
|
||||||
def get_gpx(gpx_id):
|
def get_gpx(gpx_id):
|
||||||
gpx_file = GPSTrack.query.get_or_404(gpx_id)
|
gpx_file = GPSTrack.query.get_or_404(gpx_id)
|
||||||
return Response(gpx_file.gpx_data, mimetype='application/gpx+xml')
|
return Response(gpx_file.gpx_data, mimetype='application/gpx+xml')
|
||||||
|
@ -21,9 +21,11 @@
|
|||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/list_sar">{{ _('SAR records') }}</a>
|
<a class="nav-link" href="/list_sar">{{ _('SAR records') }}</a>
|
||||||
</li>
|
</li>
|
||||||
|
{% if current_user.is_authenticated %}
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/create_sar">{{ _('New record') }}</a>
|
<a class="nav-link" href="/create_sar">{{ _('New record') }}</a>
|
||||||
</li>
|
</li>
|
||||||
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<!-- Display user info and logout link if user is authenticated -->
|
<!-- Display user info and logout link if user is authenticated -->
|
||||||
|
@ -18,7 +18,10 @@
|
|||||||
<th>Category</th>
|
<th>Category</th>
|
||||||
<th>Created by</th>
|
<th>Created by</th>
|
||||||
<th>Manager</th>
|
<th>Manager</th>
|
||||||
|
|
||||||
|
{% if is_logged_in %}
|
||||||
<th>Actions</th>
|
<th>Actions</th>
|
||||||
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -26,25 +29,27 @@
|
|||||||
<tr class="clickable-row" data-href="{{ url_for('sar_details', id=sar.SARCall.id) }}">
|
<tr class="clickable-row" data-href="{{ url_for('sar_details', id=sar.SARCall.id) }}">
|
||||||
<td>{{ sar.SARCall.id }}</td>
|
<td>{{ sar.SARCall.id }}</td>
|
||||||
<td>{{ sar.SARCall.title }}</td>
|
<td>{{ sar.SARCall.title }}</td>
|
||||||
<td>{{ sar.SARCall.status }}</td>
|
<td>{{ sar.SARStatus.name }}</td>
|
||||||
<td>{{ sar.SARCall.start_date }}</td>
|
<td>{{ sar.SARCall.start_date }}</td>
|
||||||
<td>{{ sar.SARCall.finish_date }}</td>
|
<td>{{ sar.SARCall.finish_date }}</td>
|
||||||
<td>{{ sar.SARCategory.name }}</td>
|
<td>{{ sar.SARCategory.name }}</td>
|
||||||
<td>{{ sar.User.full_name }}</td>
|
<td>{{ sar.SARCall.coordination_officer.full_name }}</td>
|
||||||
<td>{{ sar.SARCall.manager }}</td>
|
<td>{{ sar.SARCall.search_officer.full_name }}</td>
|
||||||
|
{% if is_logged_in %}
|
||||||
<td>
|
<td>
|
||||||
<a href="{{ url_for('edit_sar', id=sar.SARCall.id) }}">
|
<a href="{{ url_for('edit_sar', id=sar.SARCall.id) }}">
|
||||||
<button type="button" class="btn btn-info">Edit</button>
|
<button type="button" class="btn btn-info">Edit</button>
|
||||||
</a> |
|
|
||||||
<a href="{{ url_for('delete_sar', id=sar.SARCall.id) }}">
|
|
||||||
<button type="button" class="btn btn-danger">Delete</button>
|
|
||||||
</a>
|
</a>
|
||||||
|
{# <a href="{{ url_for('delete_sar', id=sar.SARCall.id) }}">#}
|
||||||
|
{# <button type="button" class="btn btn-danger">Delete</button>#}
|
||||||
|
{# </a>#}
|
||||||
</td>
|
</td>
|
||||||
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<a href="/dashboard">Back to Dashboard</a>
|
{# <a href="/dashboard">Back to Dashboard</a>#}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="container mt-4">
|
<div class="container mt-4">
|
||||||
<h2 class="mb-4">#{{ sar.id }} : {{ sar.title }}</h2>
|
<h2 class="mb-4">#{{ sar.SARCall.id }} : {{ sar.SARCall.title }}</h2>
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<table>
|
<table>
|
||||||
@ -37,27 +37,32 @@
|
|||||||
<div class="card-mb5">
|
<div class="card-mb5">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div>
|
<div>
|
||||||
<p>Created at: {{ sar.created }}</p>
|
<p>Created at: {{ sar.SARCall.created }}</p>
|
||||||
<p>Last updated: {{ sar.updated }}</p>
|
<p>Last updated: {{ sar.SARCall.updated }}</p>
|
||||||
</div>
|
</div>
|
||||||
<p>Status: {{ sar.status }}</p>
|
<p><strong>Coordination manager:</strong> {{ sar.SARCall.coordination_officer.full_name }}</p>
|
||||||
<p>Category: {{ sar.category }}</p>
|
<p><strong>Search manager:</strong> {{ sar.SARCall.search_officer.full_name }}</p>
|
||||||
<p><strong>Start Date:</strong> {{ sar.start_date }}</p>
|
<p>Status: {{ sar.SARStatus.name }}</p>
|
||||||
<p><strong>Finish Date:</strong> {{ sar.finish_date }}</p>
|
{% if is_logged_in %}
|
||||||
<p>Result: {{ sar.result }}</p>
|
<p>Category: {{ sar.SARCategory.name }}</p>
|
||||||
<p><strong>Manager:</strong> {{ sar.manager_id }}</p>
|
{% endif %}
|
||||||
|
<p><strong>Start Date:</strong> {{ sar.SARCall.start_date }}</p>
|
||||||
|
<p><strong>Finish Date:</strong> {{ sar.SARCall.finish_date }}</p>
|
||||||
|
<p>Result: {{ sar.SARResult.name }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="card-mb5">
|
<div class="card-mb5">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<p>IPP longitude: {{ sar.longitude }} </p>
|
<p>IPP longitude: {{ sar.SARCall.longitude }} </p>
|
||||||
<p>IPP latitude: {{ sar.latitude }}</p>
|
<p>IPP latitude: {{ sar.SARCall.latitude }}</p>
|
||||||
<p>Longitude found: {{ sar.longitude_found }}</p>
|
<p>Longitude found: {{ sar.SARCall.longitude_found }}</p>
|
||||||
<p>Latitude found: {{ sar.latitude_found }}</p>
|
<p>Latitude found: {{ sar.SARCall.latitude_found }}</p>
|
||||||
<p>Description: {{ sar.description }}</p>
|
<p>Description: {{ sar.SARCall.description }}</p>
|
||||||
<p>Description private: {{ sar.description_hidden }}</p>
|
{% if is_logged_in %}
|
||||||
|
<p>Description private: {{ sar.SARCall.description_hidden }}</p>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@ -68,15 +73,17 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<a href="{{ url_for('edit_sar', id=sar.id) }}" class="btn btn-primary">Edit</a>
|
{% if is_logged_in %}
|
||||||
<a href="{{ url_for('delete_sar', id=sar.id) }}" class="btn btn-danger">Delete</a>
|
<a href="{{ url_for('edit_sar', id=sar.SARCall.id) }}" class="btn btn-primary">Edit</a>
|
||||||
|
<a href="{{ url_for('delete_sar', id=sar.SARCall.id) }}" class="btn btn-danger">Delete</a>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="map" class="mb-4"></div>
|
<div id="map" class="mb-4"></div>
|
||||||
|
|
||||||
|
|
||||||
<!-- Display Comments -->
|
<!-- Display Comments -->
|
||||||
{% for comment in sar.comments %}
|
{% for comment in sar.SARCall.comments %}
|
||||||
<div class="comment">
|
<div class="comment">
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
@ -97,7 +104,8 @@
|
|||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
{% if current_user.id == comment.user_id or current_user.id == 1 or current_user.id == sar.user_id %}
|
{% if is_logged_in %}
|
||||||
|
{% if current_user.id == comment.user_id or current_user.id == 1 or current_user.id == sar.SARCall.user_id %}
|
||||||
<button class="edit-comment-btn" data-comment-id="{{ comment.id }}"
|
<button class="edit-comment-btn" data-comment-id="{{ comment.id }}"
|
||||||
data-comment-text="{{ comment.text }}">Edit
|
data-comment-text="{{ comment.text }}">Edit
|
||||||
</button>
|
</button>
|
||||||
@ -113,6 +121,7 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -147,7 +156,8 @@
|
|||||||
|
|
||||||
|
|
||||||
<!-- Add Comment Form -->
|
<!-- Add Comment Form -->
|
||||||
<form action="{{ url_for('add_comment', sar_call_id=sar.id) }}" method="post" enctype="multipart/form-data">
|
{% if is_logged_in %}
|
||||||
|
<form action="{{ url_for('add_comment', sar_call_id=sar.SARCall.id) }}" method="post" enctype="multipart/form-data">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="text">Comment:</label>
|
<label for="text">Comment:</label>
|
||||||
<textarea name="text" class="form-control"></textarea>
|
<textarea name="text" class="form-control"></textarea>
|
||||||
@ -158,7 +168,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-primary">Add Comment</button>
|
<button type="submit" class="btn btn-primary">Add Comment</button>
|
||||||
</form>
|
</form>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- GPX File Upload Modal -->
|
<!-- GPX File Upload Modal -->
|
||||||
@ -183,7 +193,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<input type="hidden" id="commentIdForGPX" name="commentId">
|
<input type="hidden" id="commentIdForGPX" name="commentId">
|
||||||
<input type="hidden" id="sarIdForGPX" name="sarId" value="{{ sar.id }}">
|
<input type="hidden" id="sarIdForGPX" name="sarId" value="{{ sar.SARCall.id }}">
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="submit" class="btn btn-primary">Upload</button>
|
<button type="submit" class="btn btn-primary">Upload</button>
|
||||||
</div>
|
</div>
|
||||||
@ -209,12 +219,12 @@
|
|||||||
|
|
||||||
var gpxData = {{ gpx_ids | tojson }};
|
var gpxData = {{ gpx_ids | tojson }};
|
||||||
|
|
||||||
var map = L.map('map').setView([{{ sar.latitude }}, {{ sar.longitude }}], 13);
|
var map = L.map('map').setView([{{ sar.SARCall.latitude }}, {{ sar.SARCall.longitude }}], 13);
|
||||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||||
maxZoom: 19,
|
maxZoom: 19,
|
||||||
attribution: '© OpenStreetMap contributors'
|
attribution: '© OpenStreetMap contributors'
|
||||||
}).addTo(map);
|
}).addTo(map);
|
||||||
L.marker([{{ sar.latitude }}, {{ sar.longitude }}]).addTo(map);
|
L.marker([{{ sar.SARCall.latitude }}, {{ sar.SARCall.longitude }}]).addTo(map);
|
||||||
|
|
||||||
gpxData.forEach(function (id) {
|
gpxData.forEach(function (id) {
|
||||||
loadAndDisplayGPX(id);
|
loadAndDisplayGPX(id);
|
||||||
|
Loading…
Reference in New Issue
Block a user