mirror of
https://gitea.mueller.network/extern/django-helpdesk.git
synced 2024-12-25 08:09:12 +01:00
fix(kb): no db
This commit is contained in:
parent
909b7a5533
commit
347a70e497
@ -1,10 +1,13 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from helpdesk.models import Queue, Ticket, FollowUp, PreSetReply, KBCategory
|
from helpdesk.models import Queue, Ticket, FollowUp, PreSetReply
|
||||||
from helpdesk.models import EscalationExclusion, EmailTemplate, KBItem
|
from helpdesk.models import EscalationExclusion, EmailTemplate
|
||||||
from helpdesk.models import TicketChange, KBIAttachment, FollowUpAttachment, IgnoreEmail
|
from helpdesk.models import TicketChange, KBIAttachment, FollowUpAttachment, IgnoreEmail
|
||||||
from helpdesk.models import CustomField
|
from helpdesk.models import CustomField
|
||||||
|
from helpdesk import settings as helpdesk_settings
|
||||||
|
if helpdesk_settings.HELPDESK_KB_ENABLED:
|
||||||
|
from helpdesk.models import KBCategory
|
||||||
|
from helpdesk.models import KBItem
|
||||||
|
|
||||||
@admin.register(Queue)
|
@admin.register(Queue)
|
||||||
class QueueAdmin(admin.ModelAdmin):
|
class QueueAdmin(admin.ModelAdmin):
|
||||||
@ -68,13 +71,18 @@ class FollowUpAdmin(admin.ModelAdmin):
|
|||||||
ticket_get_ticket_for_url.short_description = _('Slug')
|
ticket_get_ticket_for_url.short_description = _('Slug')
|
||||||
|
|
||||||
|
|
||||||
@admin.register(KBItem)
|
if helpdesk_settings.HELPDESK_KB_ENABLED:
|
||||||
class KBItemAdmin(admin.ModelAdmin):
|
@admin.register(KBItem)
|
||||||
list_display = ('category', 'title', 'last_updated', 'team', 'order', 'enabled')
|
class KBItemAdmin(admin.ModelAdmin):
|
||||||
inlines = [KBIAttachmentInline]
|
list_display = ('category', 'title', 'last_updated', 'team', 'order', 'enabled')
|
||||||
readonly_fields = ('voted_by', 'downvoted_by')
|
inlines = [KBIAttachmentInline]
|
||||||
|
readonly_fields = ('voted_by', 'downvoted_by')
|
||||||
|
|
||||||
list_display_links = ('title',)
|
list_display_links = ('title',)
|
||||||
|
|
||||||
|
@admin.register(KBCategory)
|
||||||
|
class KBCategoryAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('name', 'title', 'slug', 'public')
|
||||||
|
|
||||||
|
|
||||||
@admin.register(CustomField)
|
@admin.register(CustomField)
|
||||||
@ -93,10 +101,5 @@ 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)
|
||||||
|
@ -18,9 +18,13 @@ 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, KBItem)
|
CustomField, TicketCustomFieldValue, TicketDependency, UserSettings)
|
||||||
from helpdesk import settings as helpdesk_settings
|
from helpdesk import settings as helpdesk_settings
|
||||||
|
|
||||||
|
|
||||||
|
if helpdesk_settings.HELPDESK_KB_ENABLED:
|
||||||
|
from helpdesk.models import (KBItem)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
@ -231,13 +235,14 @@ class AbstractTicketForm(CustomFieldMixin, forms.Form):
|
|||||||
|
|
||||||
def __init__(self, kbcategory=None, *args, **kwargs):
|
def __init__(self, kbcategory=None, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
if kbcategory:
|
if helpdesk_settings.HELPDESK_KB_ENABLED:
|
||||||
self.fields['kbitem'] = forms.ChoiceField(
|
if kbcategory:
|
||||||
widget=forms.Select(attrs={'class': 'form-control'}),
|
self.fields['kbitem'] = forms.ChoiceField(
|
||||||
required=False,
|
widget=forms.Select(attrs={'class': 'form-control'}),
|
||||||
label=_('Knowledge Base Item'),
|
required=False,
|
||||||
choices=[(kbi.pk, kbi.title) for kbi in KBItem.objects.filter(category=kbcategory.pk, enabled=True)],
|
label=_('Knowledge Base Item'),
|
||||||
)
|
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):
|
||||||
if staff_only_filter is None:
|
if staff_only_filter is None:
|
||||||
|
@ -1275,166 +1275,166 @@ class EmailTemplate(models.Model):
|
|||||||
verbose_name = _('e-mail template')
|
verbose_name = _('e-mail template')
|
||||||
verbose_name_plural = _('e-mail templates')
|
verbose_name_plural = _('e-mail templates')
|
||||||
|
|
||||||
|
if helpdesk_settings.HELPDESK_KB_ENABLED:
|
||||||
|
class KBCategory(models.Model):
|
||||||
|
"""
|
||||||
|
Lets help users help themselves: the Knowledge Base is a categorised
|
||||||
|
listing of questions & answers.
|
||||||
|
"""
|
||||||
|
|
||||||
class KBCategory(models.Model):
|
name = models.CharField(
|
||||||
"""
|
_('Name of the category'),
|
||||||
Lets help users help themselves: the Knowledge Base is a categorised
|
max_length=100,
|
||||||
listing of questions & answers.
|
)
|
||||||
"""
|
|
||||||
|
|
||||||
name = models.CharField(
|
title = models.CharField(
|
||||||
_('Name of the category'),
|
_('Title on knowledgebase page'),
|
||||||
max_length=100,
|
max_length=100,
|
||||||
)
|
)
|
||||||
|
|
||||||
title = models.CharField(
|
slug = models.SlugField(
|
||||||
_('Title on knowledgebase page'),
|
_('Slug'),
|
||||||
max_length=100,
|
)
|
||||||
)
|
|
||||||
|
|
||||||
slug = models.SlugField(
|
description = models.TextField(
|
||||||
_('Slug'),
|
_('Description'),
|
||||||
)
|
)
|
||||||
|
|
||||||
description = models.TextField(
|
queue = models.ForeignKey(
|
||||||
_('Description'),
|
Queue,
|
||||||
)
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
verbose_name=_('Default queue when creating a ticket after viewing this category.'),
|
||||||
|
)
|
||||||
|
|
||||||
queue = models.ForeignKey(
|
public = models.BooleanField(
|
||||||
Queue,
|
default=True,
|
||||||
blank=True,
|
verbose_name=_("Is KBCategory publicly visible?")
|
||||||
null=True,
|
)
|
||||||
on_delete=models.CASCADE,
|
|
||||||
verbose_name=_('Default queue when creating a ticket after viewing this category.'),
|
|
||||||
)
|
|
||||||
|
|
||||||
public = models.BooleanField(
|
def __str__(self):
|
||||||
default=True,
|
return '%s' % self.name
|
||||||
verbose_name=_("Is KBCategory publicly visible?")
|
|
||||||
)
|
|
||||||
|
|
||||||
def __str__(self):
|
class Meta:
|
||||||
return '%s' % self.name
|
ordering = ('title',)
|
||||||
|
verbose_name = _('Knowledge base category')
|
||||||
|
verbose_name_plural = _('Knowledge base categories')
|
||||||
|
|
||||||
class Meta:
|
def get_absolute_url(self):
|
||||||
ordering = ('title',)
|
from django.urls import reverse
|
||||||
verbose_name = _('Knowledge base category')
|
return reverse('helpdesk:kb_category', kwargs={'slug': self.slug})
|
||||||
verbose_name_plural = _('Knowledge base categories')
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
|
||||||
from django.urls import reverse
|
|
||||||
return reverse('helpdesk:kb_category', kwargs={'slug': self.slug})
|
|
||||||
|
|
||||||
|
|
||||||
class KBItem(models.Model):
|
class KBItem(models.Model):
|
||||||
"""
|
"""
|
||||||
An item within the knowledgebase. Very straightforward question/answer
|
An item within the knowledgebase. Very straightforward question/answer
|
||||||
style system.
|
style system.
|
||||||
"""
|
"""
|
||||||
voted_by = models.ManyToManyField(
|
voted_by = models.ManyToManyField(
|
||||||
settings.AUTH_USER_MODEL,
|
settings.AUTH_USER_MODEL,
|
||||||
related_name='votes',
|
related_name='votes',
|
||||||
)
|
)
|
||||||
downvoted_by = models.ManyToManyField(
|
downvoted_by = models.ManyToManyField(
|
||||||
settings.AUTH_USER_MODEL,
|
settings.AUTH_USER_MODEL,
|
||||||
related_name='downvotes',
|
related_name='downvotes',
|
||||||
)
|
)
|
||||||
category = models.ForeignKey(
|
category = models.ForeignKey(
|
||||||
KBCategory,
|
KBCategory,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
verbose_name=_('Category'),
|
verbose_name=_('Category'),
|
||||||
)
|
)
|
||||||
|
|
||||||
title = models.CharField(
|
title = models.CharField(
|
||||||
_('Title'),
|
_('Title'),
|
||||||
max_length=100,
|
max_length=100,
|
||||||
)
|
)
|
||||||
|
|
||||||
question = models.TextField(
|
question = models.TextField(
|
||||||
_('Question'),
|
_('Question'),
|
||||||
)
|
)
|
||||||
|
|
||||||
answer = models.TextField(
|
answer = models.TextField(
|
||||||
_('Answer'),
|
_('Answer'),
|
||||||
)
|
)
|
||||||
|
|
||||||
votes = models.IntegerField(
|
votes = models.IntegerField(
|
||||||
_('Votes'),
|
_('Votes'),
|
||||||
help_text=_('Total number of votes cast for this item'),
|
help_text=_('Total number of votes cast for this item'),
|
||||||
default=0,
|
default=0,
|
||||||
)
|
)
|
||||||
|
|
||||||
recommendations = models.IntegerField(
|
recommendations = models.IntegerField(
|
||||||
_('Positive Votes'),
|
_('Positive Votes'),
|
||||||
help_text=_('Number of votes for this item which were POSITIVE.'),
|
help_text=_('Number of votes for this item which were POSITIVE.'),
|
||||||
default=0,
|
default=0,
|
||||||
)
|
)
|
||||||
|
|
||||||
last_updated = models.DateTimeField(
|
last_updated = models.DateTimeField(
|
||||||
_('Last Updated'),
|
_('Last Updated'),
|
||||||
help_text=_('The date on which this question was most recently changed.'),
|
help_text=_('The date on which this question was most recently changed.'),
|
||||||
blank=True,
|
blank=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
team = models.ForeignKey(
|
team = models.ForeignKey(
|
||||||
helpdesk_settings.HELPDESK_TEAMS_MODEL,
|
helpdesk_settings.HELPDESK_TEAMS_MODEL,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
verbose_name=_('Team'),
|
verbose_name=_('Team'),
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
order = models.PositiveIntegerField(
|
order = models.PositiveIntegerField(
|
||||||
_('Order'),
|
_('Order'),
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
enabled = models.BooleanField(
|
enabled = models.BooleanField(
|
||||||
_('Enabled to display to users'),
|
_('Enabled to display to users'),
|
||||||
default=True,
|
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()
|
||||||
return super(KBItem, self).save(*args, **kwargs)
|
return super(KBItem, self).save(*args, **kwargs)
|
||||||
|
|
||||||
def get_team(self):
|
def get_team(self):
|
||||||
return helpdesk_settings.HELPDESK_KBITEM_TEAM_GETTER(self)
|
return helpdesk_settings.HELPDESK_KBITEM_TEAM_GETTER(self)
|
||||||
|
|
||||||
def _score(self):
|
def _score(self):
|
||||||
""" Return a score out of 10 or Unrated if no votes """
|
""" Return a score out of 10 or Unrated if no votes """
|
||||||
if self.votes > 0:
|
if self.votes > 0:
|
||||||
return (self.recommendations / self.votes) * 10
|
return (self.recommendations / self.votes) * 10
|
||||||
else:
|
else:
|
||||||
return _('Unrated')
|
return _('Unrated')
|
||||||
score = property(_score)
|
score = property(_score)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '%s: %s' % (self.category.title, self.title)
|
return '%s: %s' % (self.category.title, self.title)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('order', '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')
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
return str(reverse('helpdesk:kb_category', args=(self.category.slug,))) + "?kbitem=" + str(self.pk)
|
return str(reverse('helpdesk:kb_category', args=(self.category.slug,))) + "?kbitem=" + str(self.pk)
|
||||||
|
|
||||||
def query_url(self):
|
def query_url(self):
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
return str(reverse('helpdesk:list')) + "?kbitem=" + str(self.pk)
|
return str(reverse('helpdesk:list')) + "?kbitem=" + str(self.pk)
|
||||||
|
|
||||||
def num_open_tickets(self):
|
def num_open_tickets(self):
|
||||||
return Ticket.objects.filter(kbitem=self, status__in=(1, 2)).count()
|
return Ticket.objects.filter(kbitem=self, status__in=(1, 2)).count()
|
||||||
|
|
||||||
def unassigned_tickets(self):
|
def unassigned_tickets(self):
|
||||||
return Ticket.objects.filter(kbitem=self, status__in=(1, 2), assigned_to__isnull=True)
|
return Ticket.objects.filter(kbitem=self, status__in=(1, 2), assigned_to__isnull=True)
|
||||||
|
|
||||||
def get_markdown(self):
|
def get_markdown(self):
|
||||||
return get_markdown(self.answer)
|
return get_markdown(self.answer)
|
||||||
|
|
||||||
|
|
||||||
class SavedSearch(models.Model):
|
class SavedSearch(models.Model):
|
||||||
|
@ -14,7 +14,13 @@ from django.views.generic import TemplateView
|
|||||||
|
|
||||||
from helpdesk.decorators import helpdesk_staff_member_required, protect_view
|
from helpdesk.decorators import helpdesk_staff_member_required, protect_view
|
||||||
from helpdesk import settings as helpdesk_settings
|
from helpdesk import settings as helpdesk_settings
|
||||||
from helpdesk.views import feeds, staff, public, kb, login
|
from helpdesk.views import feeds, staff, public, login
|
||||||
|
from helpdesk import settings as helpdesk_settings
|
||||||
|
|
||||||
|
|
||||||
|
if helpdesk_settings.HELPDESK_KB_ENABLED:
|
||||||
|
from helpdesk.views import kb
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# TODO: why is it imported? due to some side-effect or by mistake?
|
# TODO: why is it imported? due to some side-effect or by mistake?
|
||||||
import helpdesk.tasks # NOQA
|
import helpdesk.tasks # NOQA
|
||||||
|
@ -50,9 +50,12 @@ from helpdesk.lib import (
|
|||||||
)
|
)
|
||||||
from helpdesk.models import (
|
from helpdesk.models import (
|
||||||
Ticket, Queue, FollowUp, TicketChange, PreSetReply, FollowUpAttachment, SavedSearch,
|
Ticket, Queue, FollowUp, TicketChange, PreSetReply, FollowUpAttachment, SavedSearch,
|
||||||
IgnoreEmail, TicketCC, TicketDependency, UserSettings, KBItem, CustomField, TicketCustomFieldValue,
|
IgnoreEmail, TicketCC, TicketDependency, UserSettings, CustomField, TicketCustomFieldValue,
|
||||||
)
|
)
|
||||||
from helpdesk import settings as helpdesk_settings
|
from helpdesk import settings as helpdesk_settings
|
||||||
|
if helpdesk_settings.HELPDESK_KB_ENABLED:
|
||||||
|
from helpdesk.models import (KBItem)
|
||||||
|
|
||||||
import helpdesk.views.abstract_views as abstract_views
|
import helpdesk.views.abstract_views as abstract_views
|
||||||
from helpdesk.views.permissions import MustBeStaffMixin
|
from helpdesk.views.permissions import MustBeStaffMixin
|
||||||
from ..lib import format_time_spent
|
from ..lib import format_time_spent
|
||||||
|
Loading…
Reference in New Issue
Block a user