Merge pull request #1 from auto-mat/kb_forms

Vylepšení znalostní báze pro DPNK 2020
This commit is contained in:
Timothy Hobbs 2020-02-27 11:19:11 +00:00 committed by GitHub
commit 4acbb32ef5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 136 additions and 19 deletions

View File

@ -70,7 +70,7 @@ class FollowUpAdmin(admin.ModelAdmin):
@admin.register(KBItem) @admin.register(KBItem)
class KBItemAdmin(admin.ModelAdmin): class KBItemAdmin(admin.ModelAdmin):
list_display = ('category', 'title', 'last_updated', 'team', ) list_display = ('category', 'title', 'last_updated', 'team', 'order', 'enabled')
inlines = [KBIAttachmentInline] inlines = [KBIAttachmentInline]
readonly_fields = ('voted_by', 'downvoted_by') readonly_fields = ('voted_by', 'downvoted_by')
@ -93,6 +93,10 @@ class IgnoreEmailAdmin(admin.ModelAdmin):
list_display = ('name', 'queue_list', 'email_address', 'keep_in_mailbox') list_display = ('name', 'queue_list', 'email_address', 'keep_in_mailbox')
@admin.register(KBCategory)
class KBCategoryAdmin(admin.ModelAdmin):
list_display = ('name', 'title', 'slug', 'public')
admin.site.register(PreSetReply) admin.site.register(PreSetReply)
admin.site.register(EscalationExclusion) admin.site.register(EscalationExclusion)
admin.site.register(KBCategory)

View File

@ -183,8 +183,8 @@ class AbstractTicketForm(CustomFieldMixin, forms.Form):
self.fields['kbitem'] = forms.ChoiceField( self.fields['kbitem'] = forms.ChoiceField(
widget=forms.Select(attrs={'class': 'form-control'}), widget=forms.Select(attrs={'class': 'form-control'}),
required=False, required=False,
label=_('Knowedge Base Item'), label=_('Knowledge Base Item'),
choices=[(kbi.pk, kbi.title) for kbi in KBItem.objects.filter(category=kbcategory.pk)], choices=[(kbi.pk, kbi.title) for kbi in KBItem.objects.filter(category=kbcategory.pk, enabled=True)],
) )
def _add_form_custom_fields(self, staff_only_filter=None): def _add_form_custom_fields(self, staff_only_filter=None):

View File

@ -0,0 +1,33 @@
# Generated by Django 2.2.10 on 2020-02-25 11:21
from django.db import migrations, models
def copy_title(apps, schema_editor):
KBCategory = apps.get_model("helpdesk", "KBCategory")
KBCategory.objects.update(name=models.F('title'))
class Migration(migrations.Migration):
dependencies = [
('helpdesk', '0029_kbcategory_public'),
]
operations = [
migrations.AddField(
model_name='kbcategory',
name='name',
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Name of the category'),
),
migrations.AlterField(
model_name='kbcategory',
name='title',
field=models.CharField(max_length=100, verbose_name='Title on knowledgebase page'),
),
migrations.RunPython(copy_title, migrations.RunPython.noop),
migrations.AlterField(
model_name='kbcategory',
name='name',
field=models.CharField(blank=False, max_length=100, null=False, verbose_name='Name of the category'),
),
]

View File

@ -0,0 +1,22 @@
# Generated by Django 2.2.10 on 2020-02-25 13:40
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('helpdesk', '0030_add_kbcategory_name'),
]
operations = [
migrations.AlterModelOptions(
name='kbitem',
options={'ordering': ('order', 'title'), 'verbose_name': 'Knowledge base item', 'verbose_name_plural': 'Knowledge base items'},
),
migrations.AddField(
model_name='kbitem',
name='order',
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='Order'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.10 on 2020-02-25 13:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('helpdesk', '0031_auto_20200225_1440'),
]
operations = [
migrations.AddField(
model_name='kbitem',
name='enabled',
field=models.BooleanField(default=True, verbose_name='Enabled to display to users'),
),
]

View File

@ -1215,8 +1215,13 @@ class KBCategory(models.Model):
listing of questions & answers. listing of questions & answers.
""" """
name = models.CharField(
_('Name of the category'),
max_length=100,
)
title = models.CharField( title = models.CharField(
_('Title'), _('Title on knowledgebase page'),
max_length=100, max_length=100,
) )
@ -1242,7 +1247,7 @@ class KBCategory(models.Model):
) )
def __str__(self): def __str__(self):
return '%s' % self.title return '%s' % self.name
class Meta: class Meta:
ordering = ('title',) ordering = ('title',)
@ -1312,6 +1317,17 @@ class KBItem(models.Model):
null=True, null=True,
) )
order = models.PositiveIntegerField(
_('Order'),
blank=True,
null=True,
)
enabled = models.BooleanField(
_('Enabled to display to users'),
default=True,
)
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if not self.last_updated: if not self.last_updated:
self.last_updated = timezone.now() self.last_updated = timezone.now()
@ -1328,7 +1344,7 @@ class KBItem(models.Model):
return '%s: %s' % (self.category.title, self.title) return '%s: %s' % (self.category.title, self.title)
class Meta: class Meta:
ordering = ('title',) ordering = ('order', 'title',)
verbose_name = _('Knowledge base item') verbose_name = _('Knowledge base item')
verbose_name_plural = _('Knowledge base items') verbose_name_plural = _('Knowledge base items')

View File

@ -242,6 +242,10 @@ body.fixed-nav.sidebar-toggled #content-wrapper {
overflow-y: auto; overflow-y: auto;
} }
.card-text {
font-weight: bold;
}
.card-body-icon { .card-body-icon {
position: absolute; position: absolute;
z-index: 0; z-index: 0;

View File

@ -27,3 +27,6 @@
<!-- Custom Theme JavaScript --> <!-- Custom Theme JavaScript -->
<script src="{% static 'helpdesk/js/sb-admin.js' %}"></script> <script src="{% static 'helpdesk/js/sb-admin.js' %}"></script>
{% block js_bottom %}
{% endblock %}

View File

@ -1,35 +1,42 @@
{% load i18n %} {% load i18n %}
<h2>{% trans 'Knowledgebase Category' %}:{% blocktrans with category.title as kbcat %}{{ kbcat }}{% endblocktrans %}</h2> {% block header %}
<p>{{ category.description }}</p> <h2>{% blocktrans with category.title as kbcat %}{{ kbcat }}{% endblocktrans %}</h2>
{{ category.description|linebreaks }}
{% endblock %}
{% block item_list %}
<div id="accordion"> <div id="accordion">
{% for item in items %} {% for item in items %}
<div class="card mb-3"> <div class="card mb-3">
<button class="btn btn-link" data-toggle="collapse" data-target="#collapse{{item.id}}" aria-expanded="true" aria-controls="collapse{{item.id}}"> <div class="btn btn-link" data-toggle="collapse" data-target="#collapse{{item.id}}" role="region" aria-expanded="true" aria-controls="collapse{{item.id}}">
<div class="card-header" id="header{{item.id}}"> <div class="card-header" id="header{{item.id}}">
<h5 class="mb-0"> <h5 class="mb-0">
{{ item.title }} {{ item.title }}
</h5> </h5>
</div> </div>
</button> </div>
<div id="collapse{{item.id}}" class="collapse {% if item.id == selected_item %}show{% endif %}" aria-labelledby="header{{item.id}}" data-parent="#accordion"> <div id="collapse{{item.id}}" class="collapse {% if item.id == selected_item %}show{% endif %}" role="region" aria-labelledby="header{{item.id}}" data-parent="#accordion">
{% block card_body %}
<div class="card-body"> <div class="card-body">
<p class="card-text">{{ item.question }}</p> <p class="card-text">{{ item.question }}</p>
<p>{{ item.get_markdown }}</p> <div class="card-answer">
{{ item.get_markdown }}
</div>
<div class="row"> <div class="row">
{% if request.user.pk %} {% if request.user.pk %}
<div class="col-sm"> <div class="col-sm">
<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> <a href='{% url "helpdesk:kb_vote" item.pk %}?vote=up'><div class="btn btn-success btn-circle btn-xl"><i class="fa fa-thumbs-up fa-lg"></i></div></a>
<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> <a href='{% url "helpdesk:kb_vote" item.pk %}?vote=down'><div class="btn btn-danger btn-circle btn-xl"><i class="fa fa-thumbs-down fa-lg"></i></div></a>
</div> </div>
{% endif %} {% endif %}
{% if staff %} {% if staff %}
<a href='{% url 'helpdesk:list' %}?kbitem={{item.id}}' class="col-sm"> <a href='{% url 'helpdesk:list' %}?kbitem={{item.id}}' class="col-sm">
<button type="button" class="btn btn-success btn-circle btn-xl float-right"><i class="fa fa-search fa-lg"></i> {{item.num_open_tickets}} {% trans 'open tickets' %}</button> <div class="btn btn-success btn-circle btn-xl float-right"><i class="fa fa-search fa-lg"></i> {{item.num_open_tickets}} {% trans 'open tickets' %}</div>
</a> </a>
{% endif %} {% endif %}
<a href='{% if iframe %}{% url 'helpdesk:submit_iframe' %}{% else %}{% url 'helpdesk:submit' %}{%endif%}?{% if category.queue %}queue={{category.queue.pk}};_readonly_fields_=queue;{%endif%}kbitem={{item.id}};{{query_param_string}}' class="col-sm"> <a href='{% if iframe %}{% url 'helpdesk:submit_iframe' %}{% else %}{% url 'helpdesk:submit' %}{%endif%}?{% if category.queue %}queue={{category.queue.pk}};_readonly_fields_=queue;{%endif%}kbitem={{item.id}};{{query_param_string}}' class="col-sm">
<button type="button" class="btn btn-success btn-circle btn-xl float-right"><i class="fa fa-envelope fa-lg"></i> {% trans 'Contact a human' %}</button> <div class="btn btn-success btn-circle btn-xl float-right"><i class="fa fa-envelope fa-lg"></i> {% trans 'Contact a human' %}</div>
</a> </a>
</div> </div>
<div> <div>
@ -39,12 +46,18 @@
</div> </div>
</div> </div>
{% endblock %}
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
{% endblock %}
{% block footer %}
{% endblock %}
{% if category.queue %} {% if category.queue %}
<a href='{% if iframe %}{% url 'helpdesk:submit_iframe' %}{% else %}{% url 'helpdesk:submit' %}{%endif%}?queue={{category.queue.pk}};_readonly_fields_=queue;{{query_param_string}}'> <a href='{% if iframe %}{% url 'helpdesk:submit_iframe' %}{% else %}{% url 'helpdesk:submit' %}{%endif%}?queue={{category.queue.pk}};_readonly_fields_=queue;{{query_param_string}}'>
<button type="button" class="btn btn-danger btn-circle btn-xl float-right"><i class="fa fa-envelope fa-lg"></i> {% trans 'Contact a human' %}</button> {% block submit_button %}
<div class="btn btn-danger btn-circle btn-xl float-right"><i class="fa fa-envelope fa-lg"></i> {% trans 'Contact a human' %}</div>
{% endblock %}
</a> </a>
{% endif %} {% endif %}

View File

@ -1,5 +1,6 @@
{% load i18n %} {% load i18n %}
{% load saved_queries %} {% load saved_queries %}
<!DOCTYPE html>
<head> <head>
{% include 'helpdesk/base-head.html' %} {% include 'helpdesk/base-head.html' %}
{% block helpdesk_head %}{% endblock %} {% block helpdesk_head %}{% endblock %}

View File

@ -3,7 +3,9 @@
{% with request|load_helpdesk_settings as helpdesk_settings %} {% with request|load_helpdesk_settings as helpdesk_settings %}
{% if helpdesk_settings.HELPDESK_SUBMIT_A_TICKET_PUBLIC %} {% if helpdesk_settings.HELPDESK_SUBMIT_A_TICKET_PUBLIC %}
{% block form_header %}
<p>{% trans "Unless otherwise stated, all fields are required." %} {% trans "Please provide as descriptive a title and description as possible." %}</p> <p>{% trans "Unless otherwise stated, all fields are required." %} {% trans "Please provide as descriptive a title and description as possible." %}</p>
{% endblock %}
<form method='post' enctype='multipart/form-data'> <form method='post' enctype='multipart/form-data'>
{{ form|bootstrap4form }} {{ form|bootstrap4form }}
<button type="submit" class="btn btn-primary btn-lg btn-block"><i class="fa fa-send"></i>&nbsp;{% trans "Submit Ticket" %}</button> <button type="submit" class="btn btn-primary btn-lg btn-block"><i class="fa fa-send"></i>&nbsp;{% trans "Submit Ticket" %}</button>

View File

@ -1,5 +1,6 @@
{% load i18n %} {% load i18n %}
{% load saved_queries %} {% load saved_queries %}
<!DOCTYPE html>
<head> <head>
{% include 'helpdesk/base-head.html' %} {% include 'helpdesk/base-head.html' %}
</head> </head>

View File

@ -30,7 +30,7 @@ def category(request, slug, iframe=False):
category = get_object_or_404(KBCategory, slug__iexact=slug) category = get_object_or_404(KBCategory, slug__iexact=slug)
if not user.huser_from_request(request).can_access_kbcategory(category): if not user.huser_from_request(request).can_access_kbcategory(category):
raise Http404 raise Http404
items = category.kbitem_set.all() items = category.kbitem_set.filter(enabled=True)
selected_item = request.GET.get('kbitem', None) selected_item = request.GET.get('kbitem', None)
try: try:
selected_item = int(selected_item) selected_item = int(selected_item)