From ecbb91b86100bd90d931c7caad92fd810720a9b7 Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Mon, 27 Jan 2020 17:37:59 +0100 Subject: [PATCH] Private kb categories --- docs/index.rst | 1 + docs/install.rst | 4 +++ helpdesk/migrations/0028_kbitem_team.py | 20 +++++++++++++ helpdesk/migrations/0029_kbcategory_public.py | 18 ++++++++++++ helpdesk/models.py | 15 ++++++++++ helpdesk/user.py | 29 +++++++++++++++---- helpdesk/views/kb.py | 9 ++++-- helpdesk/views/public.py | 3 +- requirements.txt | 1 + 9 files changed, 91 insertions(+), 9 deletions(-) create mode 100644 helpdesk/migrations/0028_kbitem_team.py create mode 100644 helpdesk/migrations/0029_kbcategory_public.py diff --git a/docs/index.rst b/docs/index.rst index 1757da4d..a3636cba 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -15,6 +15,7 @@ Contents settings spam custom_fields + integration contributing license diff --git a/docs/install.rst b/docs/install.rst index e0b0d1f8..9eaba067 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -58,7 +58,11 @@ errors with trying to create User settings. 'django.contrib.admin', # Required for helpdesk admin/maintenance 'django.contrib.humanize', # Required for elapsed time formatting 'bootstrap4form', # Required for nicer formatting of forms with the default templates + 'account', # Required by pinax-teams + 'pinax.inviations', # required by pinax-teams + 'pinax.teams', # team support 'helpdesk', # This is us! + 'reversion', # required by pinax-teams ) Your ``settings.py`` file should also define a ``SITE_ID`` that allows multiple projects to share diff --git a/helpdesk/migrations/0028_kbitem_team.py b/helpdesk/migrations/0028_kbitem_team.py new file mode 100644 index 00000000..7801e585 --- /dev/null +++ b/helpdesk/migrations/0028_kbitem_team.py @@ -0,0 +1,20 @@ +# Generated by Django 2.2.9 on 2020-01-27 15:01 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('pinax_teams', '0004_auto_20170511_0856'), + ('helpdesk', '0027_auto_20200107_1221'), + ] + + operations = [ + migrations.AddField( + model_name='kbitem', + name='team', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='pinax_teams.Team', verbose_name='Team'), + ), + ] diff --git a/helpdesk/migrations/0029_kbcategory_public.py b/helpdesk/migrations/0029_kbcategory_public.py new file mode 100644 index 00000000..1cca37be --- /dev/null +++ b/helpdesk/migrations/0029_kbcategory_public.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.9 on 2020-01-27 16:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('helpdesk', '0028_kbitem_team'), + ] + + operations = [ + migrations.AddField( + model_name='kbcategory', + name='public', + field=models.BooleanField(default=True, verbose_name='Is KBCategory publicly visible?'), + ), + ] diff --git a/helpdesk/models.py b/helpdesk/models.py index 41bf756b..fd6979f3 100644 --- a/helpdesk/models.py +++ b/helpdesk/models.py @@ -25,6 +25,8 @@ from django.utils.safestring import mark_safe from markdown import markdown from markdown.extensions import Extension +import pinax.teams.models + import uuid @@ -1234,6 +1236,11 @@ class KBCategory(models.Model): verbose_name=_('Default queue when creating a ticket after viewing this category.'), ) + public = models.BooleanField( + default=True, + verbose_name=_("Is KBCategory publicly visible?") + ) + def __str__(self): return '%s' % self.title @@ -1297,6 +1304,14 @@ class KBItem(models.Model): blank=True, ) + team = models.ForeignKey( + pinax.teams.models.Team, + on_delete=models.CASCADE, + verbose_name=_('Team'), + blank=True, + null=True, + ) + def save(self, *args, **kwargs): if not self.last_updated: self.last_updated = timezone.now() diff --git a/helpdesk/user.py b/helpdesk/user.py index 32091d8e..8f3ec088 100644 --- a/helpdesk/user.py +++ b/helpdesk/user.py @@ -1,11 +1,16 @@ from helpdesk.models import ( Ticket, - Queue + Queue, + KBCategory, ) from helpdesk import settings as helpdesk_settings +def huser_from_request(req): + return HelpdeskUser(req.user) + + class HelpdeskUser: def __init__(self, user): self.user = user @@ -30,9 +35,19 @@ class HelpdeskUser: else: return all_queues + def get_kb_categories(self): + categories = [] + for cat in KBCategory.objects.all(): + if self.can_access_kbcategory(cat): + categories.append(cat) + return categories + def get_tickets_in_queues(self): return Ticket.objects.filter(queue__in=self.get_queues()) + def has_full_access(self): + return self.user.is_superuser or self.user.is_staff + def can_access_queue(self, queue): """Check if a certain user can access a certain queue. @@ -40,11 +55,10 @@ class HelpdeskUser: :param queue: The django-helpdesk Queue instance :return: True if the user has permission (either by default or explicitly), false otherwise """ - user = self.user - if user.is_superuser or not helpdesk_settings.HELPDESK_ENABLE_PER_QUEUE_STAFF_PERMISSION: + if self.has_full_access(): return True else: - return user.has_perm(queue.permission_name) + return helpdesk_settings.HELPDESK_ENABLE_PER_QUEUE_STAFF_PERMISSION and self.user.has_perm(queue.permission_name) def can_access_ticket(self, ticket): """Check to see if the user has permission to access @@ -52,8 +66,13 @@ class HelpdeskUser: user = self.user if self.can_access_queue(ticket.queue): return True - elif user.is_superuser or user.is_staff or \ + elif self.has_full_access() or \ (ticket.assigned_to and user.id == ticket.assigned_to.id): return True else: return False + + def can_access_kbcategory(self, category): + if category.public: + return True + return self.has_full_access() or (category.queue and self.can_access_queue(category.queue)) diff --git a/helpdesk/views/kb.py b/helpdesk/views/kb.py index a5d592a7..bb9d45ce 100644 --- a/helpdesk/views/kb.py +++ b/helpdesk/views/kb.py @@ -8,25 +8,28 @@ views/kb.py - Public-facing knowledgebase views. The knowledgebase is a resolutions to common problems. """ -from django.http import HttpResponseRedirect +from django.http import HttpResponseRedirect, Http404 from django.shortcuts import render, get_object_or_404 from django.views.decorators.clickjacking import xframe_options_exempt from helpdesk import settings as helpdesk_settings +from helpdesk import user from helpdesk.models import KBCategory, KBItem def index(request): - category_list = KBCategory.objects.all() + huser = user.huser_from_request(request) # TODO: It'd be great to have a list of most popular items here. return render(request, 'helpdesk/kb_index.html', { - 'kb_categories': category_list, + 'kb_categories': huser.get_kb_categories(), 'helpdesk_settings': helpdesk_settings, }) def category(request, slug, iframe=False): category = get_object_or_404(KBCategory, slug__iexact=slug) + if not user.huser_from_request(request).can_access_kbcategory(category): + raise Http404 items = category.kbitem_set.all() selected_item = request.GET.get('kbitem', None) try: diff --git a/helpdesk/views/public.py b/helpdesk/views/public.py index 8a2846fb..3ef9b1e9 100644 --- a/helpdesk/views/public.py +++ b/helpdesk/views/public.py @@ -25,6 +25,7 @@ import helpdesk.views.abstract_views as abstract_views from helpdesk.forms import PublicTicketForm from helpdesk.lib import text_is_spam from helpdesk.models import CustomField, Ticket, Queue, UserSettings, KBCategory, KBItem +from helpdesk.user import huser_from_request def create_ticket(request, *args, **kwargs): @@ -129,7 +130,7 @@ class Homepage(CreateTicketView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context['kb_categories'] = KBCategory.objects.all() + context['kb_categories'] = huser_from_request(self.request).get_kb_categories() return context diff --git a/requirements.txt b/requirements.txt index 119de8dc..0455b855 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,3 +12,4 @@ pytz six djangorestframework django-model-utils +pinax-teams