Redo KB categories as accordion view

This commit is contained in:
Timothy Hobbs 2020-01-07 13:33:06 +01:00
parent c95b24780e
commit 7fe6444f8f
7 changed files with 65 additions and 88 deletions

View File

@ -18,7 +18,7 @@ from django.utils import timezone
from helpdesk.lib import safe_template_context, process_attachments from helpdesk.lib import safe_template_context, process_attachments
from helpdesk.models import (Ticket, Queue, FollowUp, IgnoreEmail, TicketCC, from helpdesk.models import (Ticket, Queue, FollowUp, IgnoreEmail, TicketCC,
CustomField, TicketCustomFieldValue, TicketDependency, UserSettings) CustomField, TicketCustomFieldValue, TicketDependency, UserSettings, KBItem)
from helpdesk import settings as helpdesk_settings from helpdesk import settings as helpdesk_settings
User = get_user_model() User = get_user_model()
@ -197,7 +197,10 @@ class AbstractTicketForm(CustomFieldMixin, forms.Form):
return Queue.objects.get(id=int(self.cleaned_data['queue'])) return Queue.objects.get(id=int(self.cleaned_data['queue']))
def _create_ticket(self): def _create_ticket(self):
queue = self._get_queue() queue = Queue.objects.get(id=int(self.cleaned_data['queue']))
kbitem = None
if 'kbitem' in self.cleaned_data:
kbitem = KBItem.objects.get(id=int(self.cleaned_data['kbitem']))
ticket = Ticket(title=self.cleaned_data['title'], ticket = Ticket(title=self.cleaned_data['title'],
submitter_email=self.cleaned_data['submitter_email'], submitter_email=self.cleaned_data['submitter_email'],
@ -207,6 +210,7 @@ class AbstractTicketForm(CustomFieldMixin, forms.Form):
description=self.cleaned_data['body'], description=self.cleaned_data['body'],
priority=self.cleaned_data['priority'], priority=self.cleaned_data['priority'],
due_date=self.cleaned_data['due_date'], due_date=self.cleaned_data['due_date'],
kbitem=kbitem,
) )
return ticket, queue return ticket, queue
@ -337,13 +341,22 @@ class PublicTicketForm(AbstractTicketForm):
help_text=_('We will e-mail you when your ticket is updated.'), help_text=_('We will e-mail you when your ticket is updated.'),
) )
def __init__(self, hidden_fields=(), readonly_fields=(), *args, **kwargs): def __init__(self, hidden_fields=(), readonly_fields=(), kbcategory=None, *args, **kwargs):
""" """
Add any (non-staff) custom fields that are defined to the form Add any (non-staff) custom fields that are defined to the form
""" """
super(PublicTicketForm, self).__init__(*args, **kwargs) super(PublicTicketForm, self).__init__(*args, **kwargs)
self._add_form_custom_fields(False) self._add_form_custom_fields(False)
if kbcategory:
self.fields['kbitem'] = forms.ChoiceField(
widget=forms.Select(attrs={'class': 'form-control'}),
required=False,
label=_('Knowedge Base Item'),
choices=[(kbi.pk, kbi.title) for kbi in KBItem.objects.filter(category=kbcategory.pk)],
)
field_hide_table = { field_hide_table = {
'queue': 'HELPDESK_PUBLIC_TICKET_QUEUE', 'queue': 'HELPDESK_PUBLIC_TICKET_QUEUE',
'priority': 'HELPDESK_PUBLIC_TICKET_PRIORITY', 'priority': 'HELPDESK_PUBLIC_TICKET_PRIORITY',

View File

@ -559,6 +559,14 @@ class Ticket(models.Model):
default=mk_secret, default=mk_secret,
) )
kbitem = models.ForeignKey(
"KBItem",
blank=True,
null=True,
on_delete=models.CASCADE,
verbose_name=_('Knowledge base item the user was viewing when they created this ticket.'),
)
@property @property
def time_spent(self): def time_spent(self):
"""Return back total time spent on the ticket. This is calculated value """Return back total time spent on the ticket. This is calculated value
@ -1310,7 +1318,7 @@ class KBItem(models.Model):
def get_absolute_url(self): def get_absolute_url(self):
from django.urls import reverse from django.urls import reverse
return reverse('helpdesk:kb_item', args=(self.id,)) return str(reverse('helpdesk:kb_category', args=(self.category.slug,)))+"?kbitem="+str(self.pk)
def get_markdown(self): def get_markdown(self):
return get_markdown(self.answer) return get_markdown(self.answer)

View File

@ -19,29 +19,37 @@
<p>{{ category.description }}</p> <p>{{ category.description }}</p>
</div> </div>
</div> </div>
<div id="accordion">
{% for item in items %} {% for item in items %}
{% cycle 'one' 'two' 'three' as itemnumperrow silent %} <div class="card mb-3">
{% ifequal itemnumperrow 'one' %}<div class="card-deck">{% endifequal %} <button class="btn btn-link" data-toggle="collapse" data-target="#collapse{{item.id}}" aria-expanded="true" aria-controls="collapse{{item.id}}">
<div class="card"> <div class="card-header" id="header{{item.id}}">
<div class="card-header"> <h5 class="mb-0">
<h5 class="card-title">{{ item.title }}</h5> {{ item.title }}
</div> </h5>
<div class="card-body"> </div>
<p class="card-text">{{ item.question }}</p> </button>
<p class="card-text"> <div id="collapse{{item.id}}" class="collapse {% if item.id == selected_item %}show{% endif %}" aria-labelledby="header{{item.id}}" data-parent="#accordion">
{% blocktrans with item.get_absolute_url as url %}<a href='{{ url }}' class="btn btn-primary"> Go to answer <i class="fa fa-share"></i></a>{% endblocktrans %} <div class="card-body">
</p> <p class="card-text">{{ item.question }}</p>
<div class="well well-sm"> <p>{{ item.get_markdown }}</p>
<p>{% trans 'Rating' %}: {{ item.score }}</p> <div class="row">
<p>{% trans 'Last Update' %}: {{ item.last_updated|naturaltime }}</p> {% if request.user.pk %}
<div>
<a href='{% url "helpdesk:kb_vote" item.pk %}?vote=up'><button type="button" class="btn btn-success btn-circle btn-xl"><i class="fa fa-thumbs-up fa-lg"></i></button></a>
</div>
<div>
<a href='{% url "helpdesk:kb_vote" item.pk %}?vote=down'><button type="button" class="btn btn-danger btn-circle btn-xl"><i class="fa fa-thumbs-down fa-lg"></i></button></a>
</div>
{% blocktrans with recommendations=item.recommendations votes=item.votes %}{{ recommendations }} people found this answer useful of {{votes}}. {% endblocktrans %}
{% endif %}
</div> </div>
</p>
</div>
</div> </div>
</div> </div>
{% ifequal itemnumperrow 'three' %}</div>{% endifequal %}
{% endfor %} {% endfor %}
{% ifnotequal itemnumperrow 'three' %}</div>{% endifnotequal %} </div>
{% if category.queue %} {% if category.queue %}
<a href='{% url 'helpdesk:submit' %}?queue={{category.queue.pk}};_readonly_fields_=queue'><button type="button" class="btn btn-success btn-circle btn-xl float-right"><i class="fa fa-envelope fa-lg"></i> {% trans 'Get help with this topic' %}</button></a> <a href='{% url 'helpdesk:submit' %}?queue={{category.queue.pk}};_readonly_fields_=queue'><button type="button" class="btn btn-success btn-circle btn-xl float-right"><i class="fa fa-envelope fa-lg"></i> {% trans 'Get help with this topic' %}</button></a>
{% endif %} {% endif %}

View File

@ -1,51 +0,0 @@
{% extends "helpdesk/public_base.html" %}{% load i18n %}
{% block helpdesk_breadcrumb %}
<li class="breadcrumb-item">
<a href="{% url 'helpdesk:kb_index' %}">{% trans "Knowledgebase" %}</a>
</li>
<li class="breadcrumb-item">
<a href="{{ category.get_absolute_url }}">{% blocktrans with category.title as kbcat %}{{ kbcat }}{% endblocktrans %}</a>
</li>
<li class="breadcrumb-item active">Overview</li>
{% endblock %}
{% block helpdesk_body %}
<h2>{% trans 'Knowledgebase' %}: {% blocktrans with item.title as item %}{{ item }}{% endblocktrans %}</h2>
<div class="card mb-3">
<div class="card-header">
<i class="fas fa-question-circle"></i>
{{ item.question }}
</div>
<div class="card-body">
<p>{{ item.get_markdown }}</p>
</div>
<div class="card-footer">
<div class="row">
<div class="col-lg-2">
<p>{% trans "Did you find this article useful?" %}</p>
<div class="row">
<div class="col-lg-6">
<a href='vote/?vote=up'><button type="button" class="btn btn-success btn-circle btn-xl"><i class="fa fa-thumbs-up fa-lg"></i></button></a>
</div>
<div class="col-lg-6">
<a href='vote/?vote=down'><button type="button" class="btn btn-danger btn-circle btn-xl"><i class="fa fa-thumbs-down fa-lg"></i></button></a>
</div>
</div>
</div>
<div class="col-lg-10">
<p>{% trans "The results of voting by other readers of this article are below:" %}</p>
<ul>
<li>{% blocktrans with item.recommendations as recommendations %}Recommendations: {{ recommendations }}{% endblocktrans %}</li>
<li>{% blocktrans with item.votes as votes %}Votes: {{ votes }}{% endblocktrans %}</li>
<li>{% blocktrans with item.score as score %}Overall Rating: {{ score }}{% endblocktrans %}</li>
</ul>
</div>
</div>
</div>
</div>
<p>{% blocktrans with item.category.title as category_title and item.category.get_absolute_url as category_url %}View <a href='{{ category_url }}'>other <em>{{ category_title }}</em> articles</a>, or continue <a href='../'>viewing other knowledgebase articles</a>.{% endblocktrans %}</p>
{% endblock %}

View File

@ -232,10 +232,6 @@ if helpdesk_settings.HELPDESK_KB_ENABLED:
kb.index, kb.index,
name='kb_index'), name='kb_index'),
url(r'^kb/(?P<item>[0-9]+)/$',
kb.item,
name='kb_item'),
url(r'^kb/(?P<item>[0-9]+)/vote/$', url(r'^kb/(?P<item>[0-9]+)/vote/$',
kb.vote, kb.vote,
name='kb_vote'), name='kb_vote'),

View File

@ -27,18 +27,15 @@ def index(request):
def category(request, slug): def category(request, slug):
category = get_object_or_404(KBCategory, slug__iexact=slug) category = get_object_or_404(KBCategory, slug__iexact=slug)
items = category.kbitem_set.all() items = category.kbitem_set.all()
selected_item = request.GET.get('kbitem', None)
try:
selected_item = int(selected_item)
except ValueError:
pass
return render(request, 'helpdesk/kb_category.html', { return render(request, 'helpdesk/kb_category.html', {
'category': category, 'category': category,
'items': items, 'items': items,
'helpdesk_settings': helpdesk_settings, 'selected_item': selected_item,
})
def item(request, item):
item = get_object_or_404(KBItem, pk=item)
return render(request, 'helpdesk/kb_item.html', {
'category': item.category,
'item': item,
'helpdesk_settings': helpdesk_settings, 'helpdesk_settings': helpdesk_settings,
}) })

View File

@ -21,7 +21,7 @@ from helpdesk.decorators import protect_view, is_helpdesk_staff
import helpdesk.views.staff as staff import helpdesk.views.staff as staff
from helpdesk.forms import PublicTicketForm from helpdesk.forms import PublicTicketForm
from helpdesk.lib import text_is_spam from helpdesk.lib import text_is_spam
from helpdesk.models import CustomField, Ticket, Queue, UserSettings, KBCategory from helpdesk.models import CustomField, Ticket, Queue, UserSettings, KBCategory, KBItem
def create_ticket(request, *args, **kwargs): def create_ticket(request, *args, **kwargs):
@ -82,7 +82,7 @@ class BaseCreateTicketView(FormView):
if request.user.is_authenticated and request.user.email: if request.user.is_authenticated and request.user.email:
initial_data['submitter_email'] = request.user.email initial_data['submitter_email'] = request.user.email
query_param_fields = ['submitter_email', 'title', 'body', 'queue'] query_param_fields = ['submitter_email', 'title', 'body', 'queue', 'kbitem']
custom_fields = ["custom_%s" % f.name for f in CustomField.objects.filter(staff_only=False)] custom_fields = ["custom_%s" % f.name for f in CustomField.objects.filter(staff_only=False)]
query_param_fields += custom_fields query_param_fields += custom_fields
for qpf in query_param_fields: for qpf in query_param_fields:
@ -93,6 +93,12 @@ class BaseCreateTicketView(FormView):
kwargs = super().get_form_kwargs(*args, **kwargs) kwargs = super().get_form_kwargs(*args, **kwargs)
kwargs['hidden_fields'] = self.request.GET.get('_hide_fields_', '').split(',') kwargs['hidden_fields'] = self.request.GET.get('_hide_fields_', '').split(',')
kwargs['readonly_fields'] = self.request.GET.get('_readonly_fields_', '').split(',') kwargs['readonly_fields'] = self.request.GET.get('_readonly_fields_', '').split(',')
kbitem = self.request.GET.get('kbitem', None)
if kbitem:
try:
kwargs['kbcategory'] = KBItem.objects.get(pk=int(kbitem))
except (ValueError, KBItem.DoesNotExist):
pass
return kwargs return kwargs
def form_valid(self, form): def form_valid(self, form):