Added zendesk submission flow
This commit is contained in:
parent
219d5b998f
commit
e57dcac2d2
|
@ -412,3 +412,5 @@ PHONE_PROVIDER_1_SECRET = os.environ.get("PHONE_PROVIDER_1_SECRET")
|
||||||
|
|
||||||
PHONE_PROVIDER_2_HEADER = os.environ.get("PHONE_PROVIDER_2_HEADER")
|
PHONE_PROVIDER_2_HEADER = os.environ.get("PHONE_PROVIDER_2_HEADER")
|
||||||
PHONE_PROVIDER_2_SECRET = os.environ.get("PHONE_PROVIDER_2_SECRET")
|
PHONE_PROVIDER_2_SECRET = os.environ.get("PHONE_PROVIDER_2_SECRET")
|
||||||
|
|
||||||
|
ZENDESK_HOST=os.environ.get('ZENDESK_HOST', 'noone.zendesk.com')
|
||||||
|
|
|
@ -1,27 +1,60 @@
|
||||||
import json
|
import json
|
||||||
|
import urllib.parse
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from flask import render_template, request, flash, url_for, redirect
|
from flask import render_template, request, flash, url_for, redirect
|
||||||
from flask_login import login_required, current_user
|
from flask_login import login_required, current_user
|
||||||
|
from werkzeug.datastructures import FileStorage
|
||||||
|
|
||||||
from app.dashboard.base import dashboard_bp
|
from app.dashboard.base import dashboard_bp
|
||||||
from app.db import Session
|
|
||||||
from app.log import LOG
|
from app.log import LOG
|
||||||
from app.models import Alias, Mailbox
|
from app.models import Mailbox
|
||||||
|
from app.config import ZENDESK_HOST
|
||||||
|
|
||||||
|
VALID_MIME_TYPES = ['text/plain', 'message/rfc822']
|
||||||
|
|
||||||
|
|
||||||
@dashboard_bp.route("/support", methods=["GET"])
|
@dashboard_bp.route("/support", methods=["GET"])
|
||||||
@login_required
|
@login_required
|
||||||
def show_support_dialog():
|
def show_support_dialog():
|
||||||
return render_template( "dashboard/support.html" )
|
mailbox = Mailbox.get(current_user.default_mailbox_id)
|
||||||
|
return render_template("dashboard/support.html", ticketEmail=mailbox.email)
|
||||||
|
|
||||||
|
|
||||||
def createZendeskTicket(email: str, contents: str):
|
def upload_file_to_zendesk(file: FileStorage) -> Union[None, str]:
|
||||||
|
if file.mimetype not in VALID_MIME_TYPES and not file.mimetype.startswith('image/'):
|
||||||
|
flash('File {} is not an image, text or an email'.format(file.filename), "warning")
|
||||||
|
return None
|
||||||
|
escaped_filename = urllib.parse.urlencode({'filename': file.filename})
|
||||||
|
url = 'https://{}/api/v2/uploads?{}'.format(ZENDESK_HOST, escaped_filename)
|
||||||
|
headers = {'content-type': file.mimetype}
|
||||||
|
response = requests.post(url, headers=headers, data=file.stream)
|
||||||
|
if response.status_code != 201:
|
||||||
|
if response.status_code == 401 or 422:
|
||||||
|
LOG.debug('Could not authenticate')
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
LOG.debug('Problem with the request. Status ' + str(response.status_code))
|
||||||
|
return None
|
||||||
|
data = response.json()
|
||||||
|
return data['upload']['token']
|
||||||
|
|
||||||
|
|
||||||
|
def create_zendesk_request(email: str, contents: str, files: [FileStorage]) -> bool:
|
||||||
|
tokens = []
|
||||||
|
for file in files:
|
||||||
|
token = upload_file_to_zendesk(file)
|
||||||
|
if token is None:
|
||||||
|
return False
|
||||||
|
tokens.append(token)
|
||||||
data = {
|
data = {
|
||||||
'request': {
|
'request': {
|
||||||
'subject': 'Ticket created for user {}'.format(current_user.id),
|
'subject': 'Ticket created for user {}'.format(current_user.id),
|
||||||
'comment': {
|
'comment': {
|
||||||
'type': 'Comment',
|
'type': 'Comment',
|
||||||
'body': contents
|
'body': contents,
|
||||||
|
'uploads': tokens
|
||||||
},
|
},
|
||||||
'requester': {
|
'requester': {
|
||||||
'name': "SimpleLogin user {}".format(current_user.id),
|
'name': "SimpleLogin user {}".format(current_user.id),
|
||||||
|
@ -29,17 +62,19 @@ def createZendeskTicket(email: str, contents: str):
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
url = 'https://simplelogin.zendesk.com/api/v2/requests.json'
|
url = 'https://{}/api/v2/requests.json'.format(ZENDESK_HOST)
|
||||||
headers = {'content-type': 'application/json'}
|
headers = {'content-type': 'application/json'}
|
||||||
r = requests.post(url, data=json.dumps(data), headers=headers)
|
r = requests.post(url, data=json.dumps(data), headers=headers)
|
||||||
if r.status_code != 201:
|
if r.status_code != 201:
|
||||||
if r.status_code == 401 or 422:
|
if r.status_code == 401 or 422:
|
||||||
LOG.debug('Could not authenticate')
|
LOG.debug('Could not authenticate')
|
||||||
|
return False
|
||||||
else:
|
else:
|
||||||
LOG.debug('Problem with the request. Status ' + str(r.status_code))
|
LOG.debug('Problem with the request. Status ' + str(r.status_code))
|
||||||
else:
|
return False
|
||||||
flash("Ticket was created. You should receive an email notification", "success")
|
flash("Ticket was created. You should receive an email notification", "success")
|
||||||
LOG.debug('Ticket created')
|
LOG.debug('Ticket created')
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
@dashboard_bp.route("/support", methods=["POST"])
|
@dashboard_bp.route("/support", methods=["POST"])
|
||||||
|
@ -53,5 +88,6 @@ def process_support_dialog():
|
||||||
if not email:
|
if not email:
|
||||||
flash("Please add an email", "warning")
|
flash("Please add an email", "warning")
|
||||||
return render_template("dashboard/support.html", ticketContents=contents)
|
return render_template("dashboard/support.html", ticketContents=contents)
|
||||||
createZendeskTicket(email, contents)
|
if create_zendesk_request(email, contents, request.files.getlist('ticketFiles')):
|
||||||
return redirect(url_for('dashboard.index'))
|
return render_template("dashboard/support_ticket_created.html", ticketEmail=email)
|
||||||
|
return render_template("dashboard/support.html", ticketEmail=email, ticketContents=contents)
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
<script>
|
<script>
|
||||||
$( document ).ready(function() {
|
$( document ).ready(function() {
|
||||||
var app = new Vue({
|
var app = new Vue({
|
||||||
el: '#supportZendeskForm',
|
el: '#aliasGroup',
|
||||||
data: {
|
data: {
|
||||||
ticketEmail: document.querySelector("input[name=ticketEmail]").value
|
ticketEmail: document.querySelector("input[name=ticketEmail]").value
|
||||||
},
|
},
|
||||||
|
@ -33,6 +33,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('.custom-file input').change(function (e) {
|
||||||
|
var files = [];
|
||||||
|
for (var i = 0; i < $(this)[0].files.length; i++) {
|
||||||
|
files.push($(this)[0].files[i].name);
|
||||||
|
}
|
||||||
|
$(this).next('.custom-file-label').html(files.join(', '));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -50,7 +60,7 @@
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
A support ticket will be created in Zendesk. Please do not include any sensitive information in the ticket.
|
A support ticket will be created in Zendesk. Please do not include any sensitive information in the ticket.
|
||||||
</div>
|
</div>
|
||||||
<form id="supportZendeskForm" method="post">
|
<form id="supportZendeskForm" method="post" enctype="multipart/form-data">
|
||||||
<div class="mt-4 mb-5">
|
<div class="mt-4 mb-5">
|
||||||
<label for="issueDescription" class="form-label">What happened?</label>
|
<label for="issueDescription" class="form-label">What happened?</label>
|
||||||
<textarea class="form-control" name="ticketContents" id="issueDescription" rows="3" placeholder="Please provide as much information as possible. For example which alias(es), mailbox(es) ar affected, if this is a persistent issue...">{{- ticketContents or '' -}}</textarea>
|
<textarea class="form-control" name="ticketContents" id="issueDescription" rows="3" placeholder="Please provide as much information as possible. For example which alias(es), mailbox(es) ar affected, if this is a persistent issue...">{{- ticketContents or '' -}}</textarea>
|
||||||
|
@ -58,8 +68,9 @@
|
||||||
<div class="mt-5 font-weight-bold">
|
<div class="mt-5 font-weight-bold">
|
||||||
Attach files to support request
|
Attach files to support request
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mt-1 text-muted">Only images, text and emails are accepted</div>
|
||||||
<div class="custom-file mt-2">
|
<div class="custom-file mt-2">
|
||||||
<input type="file" class="custom-file-input" name="ticketFiles" id="ticketFileGroup" multiple>
|
<input type="file" class="custom-file-input" name="ticketFiles" id="ticketFileGroup" multiple="multiple">
|
||||||
<label class="custom-file-label" for="ticketFileGroup">Choose file</label>
|
<label class="custom-file-label" for="ticketFileGroup">Choose file</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-5 font-weight-bold">
|
<div class="mt-5 font-weight-bold">
|
||||||
|
@ -68,7 +79,7 @@
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
Conversations related to this ticket will be sent to this address. Feel free to use an alias here.
|
Conversations related to this ticket will be sent to this address. Feel free to use an alias here.
|
||||||
</div>
|
</div>
|
||||||
<div class="input-group mb-3">
|
<div class="input-group mb-3" id="aliasGroup">
|
||||||
<input type="text" class="form-control" placeholder="Email" value="{{ ticketEmail }}" name="ticketEmail" v-model='ticketEmail' aria-label="Email to send responses to" aria-describedby="button-addon2">
|
<input type="text" class="form-control" placeholder="Email" value="{{ ticketEmail }}" name="ticketEmail" v-model='ticketEmail' aria-label="Email to send responses to" aria-describedby="button-addon2">
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<button class="btn btn-outline-primary" type="button" @click="generateRandomAlias" id="button-addon2">Generate a random alias</button>
|
<button class="btn btn-outline-primary" type="button" @click="generateRandomAlias" id="button-addon2">Generate a random alias</button>
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
{% extends 'default.html' %}
|
||||||
|
|
||||||
|
{% set active_page = None %}
|
||||||
|
|
||||||
|
{% block title %}
|
||||||
|
Support
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block head %}
|
||||||
|
<style>
|
||||||
|
.card-title {
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 3px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block default_content %}
|
||||||
|
|
||||||
|
<div class="col pb-3">
|
||||||
|
<!-- Current plan -->
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="card-title mb-3">Support ticket has been created for {{ ticketEmail }}</div>
|
||||||
|
<div class="mt-2">
|
||||||
|
Head back to <a href="/dashboard">your dashboard</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -66,6 +66,7 @@
|
||||||
href="https://github.com/simple-login/app/projects/1">Roadmap</a></li>
|
href="https://github.com/simple-login/app/projects/1">Roadmap</a></li>
|
||||||
<li><a class="list-group-item text-white footer-item"
|
<li><a class="list-group-item text-white footer-item"
|
||||||
href="https://simplelogin.io/contact/">Contact Us</a></li>
|
href="https://simplelogin.io/contact/">Contact Us</a></li>
|
||||||
|
<li><a class="list-group-item text-white footer-item" href="/dashboard/support">Support</a></li>
|
||||||
<li><a class="list-group-item text-white footer-item"
|
<li><a class="list-group-item text-white footer-item"
|
||||||
href="https://simplelogin.io/imprint/">Imprint</a></li>
|
href="https://simplelogin.io/imprint/">Imprint</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
Loading…
Reference in New Issue