diff --git a/docs/api.rst b/docs/api.rst deleted file mode 100644 index b1dfba5a..00000000 --- a/docs/api.rst +++ /dev/null @@ -1,8 +0,0 @@ -Ticket API -========== - -*Warning*: The django-helpdesk API is deprecated, and no longer maintained. See https://github.com/django-helpdesk/django-helpdesk/issues/198 for more details. - -The API will be removed in January 2016 - you should instead build an integration with eg django-rest-framework. - -For details on the current API including usage instructions and command syntax, see the file ``templates/helpdesk/api_help.html``, or visit http://helpdesk/api/help/. diff --git a/docs/index.rst b/docs/index.rst index 34a62228..483d5d84 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -16,7 +16,6 @@ Contents settings spam custom_fields - api contributing @@ -49,7 +48,7 @@ Customers (who are not 'staff' users in Django) can: 3. Review open and closed requests they submitted Staff Capabilities -~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~ If a user is a staff member, they get general helpdesk access, including: diff --git a/docs/settings.rst b/docs/settings.rst index 9929d540..c60e3936 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -42,7 +42,6 @@ If you want to override the default settings for your users, create ``HELPDESK_D 'email_on_ticket_assign': True, 'email_on_ticket_change': True, 'login_view_ticketlist': True, - 'email_on_ticket_apichange': True, 'tickets_per_page': 25 } diff --git a/helpdesk/forms.py b/helpdesk/forms.py index 090d5455..22917975 100644 --- a/helpdesk/forms.py +++ b/helpdesk/forms.py @@ -532,12 +532,6 @@ class UserSettingsForm(forms.Form): required=False, ) - email_on_ticket_apichange = forms.BooleanField( - label=_('E-mail me when a ticket is changed via the API?'), - help_text=_('If a ticket is altered by the API, do you want to receive an e-mail?'), - required=False, - ) - tickets_per_page = forms.IntegerField( label=_('Number of tickets to show per page'), help_text=_('How many tickets do you want to see on the Ticket List page?'), diff --git a/helpdesk/settings.py b/helpdesk/settings.py index 606af04f..a1ca3c0b 100644 --- a/helpdesk/settings.py +++ b/helpdesk/settings.py @@ -17,7 +17,6 @@ if not isinstance(DEFAULT_USER_SETTINGS, dict): 'email_on_ticket_assign': True, 'email_on_ticket_change': True, 'login_view_ticketlist': True, - 'email_on_ticket_apichange': True, 'tickets_per_page': 25 } diff --git a/helpdesk/templates/helpdesk/base.html b/helpdesk/templates/helpdesk/base.html index 51749edd..6ce6644e 100644 --- a/helpdesk/templates/helpdesk/base.html +++ b/helpdesk/templates/helpdesk/base.html @@ -66,7 +66,6 @@ diff --git a/helpdesk/templates/helpdesk/help_api.html b/helpdesk/templates/helpdesk/help_api.html deleted file mode 100644 index 62dffcd9..00000000 --- a/helpdesk/templates/helpdesk/help_api.html +++ /dev/null @@ -1,277 +0,0 @@ -{% extends "helpdesk/help_base.html" %} - -{% block title %}django-helpdesk API Documentation{% endblock %} -{% block heading %}django-helpdesk API Documentation{% endblock %} - -{% block content %} -

Contents

- - -

Deprecation Warning

- -

This API has been deprecated and will be removed in January 2016. Please See the GitHub Issue Tracker for more details.

-

Do not build new integrations using this API.

- -

We recommend using django-rest-framework or similar for all integrations.

- -

Introduction

- -

django-helpdesk provides a powerful API to allow you to interact with your helpdesk tickets by a means not otherwise provided by the helpdesk.

- -

For example, you may use this API to implement a system to automatically open a ticket when an invoice is raised in your invoicing system, or to automatically close a ticket from an instant messenger application.

- -

Your use of this system is open-ended: most business cases should be addressible with a little bit of coding to allow you to interact nicely with your helpdesk.

- -

Request Basics & Authentication

- -

All requests to the API must be made using HTTP POST requests. Any request that is not made using POST will raise an error.

- -

Your requests must be made up of the following elements:

- -
    -
  1. A method, or action. This tells the API what core functionality to execute.
  2. -
  3. A username and password which are valid and active within your helpdesk system. You may wish to create a specific API user just for API usage.
  4. -
  5. A set of data to be saved into the database. This data will vary from request to request, and is outlined in Methods below.
  6. -
- -

To build your request, send a HTTP POST request to {% url 'helpdesk:api' "method" %}, where method is the name of a valid method from the list below.

- -

Your POST must include both user and password parameters.

- -

A sample request for the method hold_ticket may look like this:

- - - -

To complete this from a command-line using the cURL application, you may use a command such as this:

- -
/usr/bin/curl {% url 'helpdesk:api' "hold_ticket" %} --data "user=susan&password=fido&ticket=31794"
- -

In PHP, providing you have access to the cURL libraries, you may use code such as this:

- -
<?php
-$api = curl_init();
-curl_setopt($api, CURLOPT_URL, "{% url 'helpdesk:api' "hold_ticket" %}");
-curl_setopt($api, CURLOPT_POST, 1);
-curl_setopt($api, CURLOPT_POSTFIELDS, "user=susan&password=fido&ticket=31794");
-$result = curl_exec($api);
-curl_close($api);
-echo $result;
-?>
- -

Note that cURL expects all data to be urlencoded, this is left as an exercise for the reader.

- -

Responses

- -

The API system makes proper use of the following HTTP response codes:

- -
-
200
-
OK - Data updated successfully
- -
400
-
ERROR - Generic error. See returned text for details
- -
404
-
ERROR - Data not found (eg, incorrect ticket). See returned text for details
- -
403
-
ERROR - Invalid permissions (eg, incorrect username and/or password)
- -
405
-
ERROR - Invalid method. You probably tried using GET, PUT or DELETE however we require POST.
-
- -

Responses will have one of two content-types:

- -
-
text/plain
-
Any error messages, or simple responses (eg a ticket ID)
- -
text/json
-
Any complex responses, such as a list of data.
-
- -

Method Documentation

- -

The following public methods are available for use via the API. Each of them requires a valid request and authentication, and each has it's own parameters as described below.

- - - -

create_ticket

- -

This method creates a new helpdesk ticket.

- -

Parameters

- -
-
queue
-
Queue ID (use list_queues to get queue ID's) - this is an integer field.
- -
title
-
Title or header of this ticket. Character field, maximum 100 characters.
- -
submitter_email
-
(Optional) e-mail address of the person submitting this ticket. This e-mail address will receive copies of all public updates to this ticket, and will receive a notification when the ticket is created.
- -
assigned_to
-
(Optional) Integer ID of the user to which this ticket should be assigned. Use find_user to find a user ID from a username.
- -
priority
-
(Optional) Priority as an integer from 1 (high) to 5 (low). Defaults to 3 if no priority given.
-
- -

Response

- -

This method responds with plain-text.

- -

If you receive a 200 OK response, then the content of the response will be the ticket ID.

- -

delete_ticket

- -

When given a ticket ID and confirmation, this method will delete a ticket entirely. This also deletes any followups, attachments, and other details.

- -

Parameters

- -
-
ticket
-
The numeric ticket ID to be deleted
- -
confirm
-
You must provide this field, with any value, to enable deletion to continue
-
- -

Response

- -

A standard 200 OK response is given on success, or an error message on failure.

- -

hold_ticket

- -

If a ticket needs to be placed on hold, preventing it from being escalated, use this method.

- -

Parameters

-
-
ticket
-
The numeric ticket ID to be placed on hold
-
- -

Response

- -

A standard 200 OK response is given on success, or an error message on failure.

- - -

unhold_ticket

- -

If a ticket is currently on hold and you wish to remove that hold, use this method.

- -

Parameters

-
-
ticket
-
The numeric ticket ID to be taken off hold
-
- -

Response

- -

A standard 200 OK response is given on success, or an error message on failure.

- - -

add_followup

- -

This method adds a comment / followup to a ticket. The followup can be public, in which case it is e-mailed to the submitter, or private. The followup will also be sent to others involved in the ticket: The owner and the queue notification / CC address.

- -

Parameters

- -
-
ticket
-
The numeric ticket ID to which this followup should be added
- -
message
-
Text of 'unlimited' length - optionally formatted with HTML - to add to the message.
- -
public
-
Either 'y' for public, or 'n' for private. This is optional, and it is assumed that followups are private if it is not provided. Private tickets are not e-mailed to the ticket submitter.
-
- -

Response

- -

A standard 200 OK response is given on success, or an error message on failure.

- - -

resolve

- -

This method adds a resolution to a ticket and marks it as resolved. The resolution will be e-mailed to everybody involved with the ticket, including the submitter.

- -

Parameters

- -
-
ticket
-
The numeric ticket ID to which this followup should be added
- -
resolution
-
Text of 'unlimited' length - optionally formatted with HTML. This is the resolution for this ticket.
-
- -

Response

- -

A standard 200 OK response is given on success, or an error message on failure.

- - -

list_queues

- -

This method provides a JSON-parsable list of queues, letting you access the individual queue ID in order to create tickets.

- -

Response

- -

This method responds with json.

- -

It provides a list of queues in JSON format. The fields provided are ID and Title.

- - -

find_user

- -

When given a username, this method provides the related numeric user ID - commonly used when creating or reassigning tickets.

- -

Parameters

- -
-
username
-
The case-sensitive username of the user for which you require the user ID
-
- -

Response

- -

This method responds with plain-text.

- -

If you receive a 200 OK response, then the content of the response will be the users ID.

-{% endblock %} diff --git a/helpdesk/urls.py b/helpdesk/urls.py index 833a89df..22c0e229 100644 --- a/helpdesk/urls.py +++ b/helpdesk/urls.py @@ -13,7 +13,7 @@ from django.contrib.auth import views as auth_views from django.views.generic import TemplateView from helpdesk import settings as helpdesk_settings -from helpdesk.views import feeds, staff, public, api, kb +from helpdesk.views import feeds, staff, public, kb class DirectTemplateView(TemplateView): @@ -183,10 +183,6 @@ urlpatterns += [ urlpatterns += [ - url(r'^api/(?P[a-z_-]+)/$', - api.api, - name='api'), - url(r'^login/$', auth_views.login, {'template_name': 'helpdesk/registration/login.html'}, @@ -218,10 +214,6 @@ if helpdesk_settings.HELPDESK_KB_ENABLED: ] urlpatterns += [ - url(r'^api/$', - TemplateView.as_view(template_name='helpdesk/help_api.html'), - name='api_help'), - url(r'^help/context/$', TemplateView.as_view(template_name='helpdesk/help_context.html'), name='help_context'), diff --git a/helpdesk/views/api.py b/helpdesk/views/api.py deleted file mode 100644 index 6de9d34f..00000000 --- a/helpdesk/views/api.py +++ /dev/null @@ -1,340 +0,0 @@ -""" .. -django-helpdesk - A Django powered ticket tracker for small enterprise. - -(c) Copyright 2008 Jutda. All Rights Reserved. See LICENSE for details. - -api.py - Wrapper around API calls, and core functions to provide complete - API to third party applications. - -The API documentation can be accessed by visiting http://helpdesk/api/help/ -(obviously, substitute helpdesk for your django-helpdesk URI), or by reading -through templates/helpdesk/help_api.html. -""" - -from django.contrib.auth import authenticate -from django.contrib.auth import get_user_model -from django.http import HttpResponse -from django.shortcuts import render -import simplejson -from django.views.decorators.csrf import csrf_exempt - -try: - from django.utils import timezone -except ImportError: - from datetime import datetime as timezone - -from helpdesk.forms import TicketForm -from helpdesk.lib import send_templated_mail, safe_template_context -from helpdesk.models import Ticket, Queue, FollowUp - -import warnings - -User = get_user_model() - -STATUS_OK = 200 - -STATUS_ERROR = 400 -STATUS_ERROR_NOT_FOUND = 404 -STATUS_ERROR_PERMISSIONS = 403 -STATUS_ERROR_BADMETHOD = 405 - - -@csrf_exempt -def api(request, method): - """ - Regardless of any other paramaters, we provide a help screen - to the user if they requested one. - - If the user isn't looking for help, then we enforce a few conditions: - * The request must be sent via HTTP POST - * The request must contain a 'user' and 'password' which - must be valid users - * The method must match one of the public methods of the API class. - - - THIS IS DEPRECATED AS OF DECEMBER 2015 AND WILL BE REMOVED IN JANUARY 2016. - SEE https://github.com/django-helpdesk/django-helpdesk/issues/198 FOR DETAILS - - """ - - warnings.warn("django-helpdesk API will be removed in January 2016. " - "See https://github.com/django-helpdesk/django-helpdesk/issues/198 for details.", - category=DeprecationWarning) - - if method == 'help': - return render(request, template_name='helpdesk/help_api.html') - - if request.method != 'POST': - return api_return(STATUS_ERROR_BADMETHOD) - - # TODO: Move away from having the username & password in every request. - request.user = authenticate( - username=request.POST.get('user', False), - password=request.POST.get('password'), - ) - - if request.user is None: - return api_return(STATUS_ERROR_PERMISSIONS) - - api = API(request) - if hasattr(api, 'api_public_%s' % method): - return getattr(api, 'api_public_%s' % method)() - - return api_return(STATUS_ERROR) - - -def api_return(status, text='', json=False): - content_type = 'text/plain' - if status == STATUS_OK and json: - content_type = 'text/json' - - if text is None: - if status == STATUS_ERROR: - text = 'Error' - elif status == STATUS_ERROR_NOT_FOUND: - text = 'Resource Not Found' - elif status == STATUS_ERROR_PERMISSIONS: - text = 'Invalid username or password' - elif status == STATUS_ERROR_BADMETHOD: - text = 'Invalid request method' - elif status == STATUS_OK: - text = 'OK' - - r = HttpResponse(status=status, content=text, content_type=content_type) - - if status == STATUS_ERROR_BADMETHOD: - r.Allow = 'POST' - - return r - - -class API: - - def __init__(self, request): - self.request = request - - def api_public_create_ticket(self): - form = TicketForm(self.request.POST) - form.fields['queue'].choices = [[q.id, q.title] for q in Queue.objects.all()] - form.fields['assigned_to'].choices = [[u.id, u.get_username()] for u in User.objects.filter(is_active=True)] - - if form.is_valid(): - ticket = form.save(user=self.request.user) - return api_return(STATUS_OK, "%s" % ticket.id) - else: - return api_return(STATUS_ERROR, text=form.errors.as_text()) - - def api_public_list_queues(self): - return api_return(STATUS_OK, simplejson.dumps([ - {"id": "%s" % q.id, "title": "%s" % q.title} - for q in Queue.objects.all() - ]), json=True) - - def api_public_find_user(self): - username = self.request.POST.get('username', False) - - try: - u = User.objects.get(username=username) - return api_return(STATUS_OK, "%s" % u.id) - - except User.DoesNotExist: - return api_return(STATUS_ERROR, "Invalid username provided") - - def api_public_delete_ticket(self): - if not self.request.POST.get('confirm', False): - return api_return(STATUS_ERROR, "No confirmation provided") - - try: - ticket = Ticket.objects.get(id=self.request.POST.get('ticket', False)) - except Ticket.DoesNotExist: - return api_return(STATUS_ERROR, "Invalid ticket ID") - - ticket.delete() - - return api_return(STATUS_OK) - - def api_public_hold_ticket(self): - try: - ticket = Ticket.objects.get(id=self.request.POST.get('ticket', False)) - except Ticket.DoesNotExist: - return api_return(STATUS_ERROR, "Invalid ticket ID") - - ticket.on_hold = True - ticket.save() - - return api_return(STATUS_OK) - - def api_public_unhold_ticket(self): - try: - ticket = Ticket.objects.get(id=self.request.POST.get('ticket', False)) - except Ticket.DoesNotExist: - return api_return(STATUS_ERROR, "Invalid ticket ID") - - ticket.on_hold = False - ticket.save() - - return api_return(STATUS_OK) - - def api_public_add_followup(self): - try: - ticket = Ticket.objects.get(id=self.request.POST.get('ticket', False)) - except Ticket.DoesNotExist: - return api_return(STATUS_ERROR, "Invalid ticket ID") - - message = self.request.POST.get('message', None) - public = self.request.POST.get('public', 'n') - - if public not in ['y', 'n']: - return api_return(STATUS_ERROR, "Invalid 'public' flag") - - if not message: - return api_return(STATUS_ERROR, "Blank message") - - f = FollowUp( - ticket=ticket, - date=timezone.now(), - comment=message, - user=self.request.user, - title='Comment Added', - ) - - if public: - f.public = True - - f.save() - - context = safe_template_context(ticket) - context['comment'] = f.comment - - messages_sent_to = [] - - if public and ticket.submitter_email: - send_templated_mail( - 'updated_submitter', - context, - recipients=ticket.submitter_email, - sender=ticket.queue.from_address, - fail_silently=True, - ) - messages_sent_to.append(ticket.submitter_email) - - if public: - for cc in ticket.ticketcc_set.all(): - if cc.email_address not in messages_sent_to: - send_templated_mail( - 'updated_submitter', - context, - recipients=cc.email_address, - sender=ticket.queue.from_address, - fail_silently=True, - ) - messages_sent_to.append(cc.email_address) - - if ticket.queue.updated_ticket_cc and ticket.queue.updated_ticket_cc not in messages_sent_to: - send_templated_mail( - 'updated_cc', - context, - recipients=ticket.queue.updated_ticket_cc, - sender=ticket.queue.from_address, - fail_silently=True, - ) - messages_sent_to.append(ticket.queue.updated_ticket_cc) - - if ( - ticket.assigned_to and - self.request.user != ticket.assigned_to and - ticket.assigned_to.usersettings.settings.get('email_on_ticket_apichange', False) and - ticket.assigned_to.email and - ticket.assigned_to.email not in messages_sent_to - ): - send_templated_mail( - 'updated_owner', - context, - recipients=ticket.assigned_to.email, - sender=ticket.queue.from_address, - fail_silently=True, - ) - - ticket.save() - - return api_return(STATUS_OK) - - def api_public_resolve(self): - try: - ticket = Ticket.objects.get(id=self.request.POST.get('ticket', False)) - except Ticket.DoesNotExist: - return api_return(STATUS_ERROR, "Invalid ticket ID") - - resolution = self.request.POST.get('resolution', None) - - if not resolution: - return api_return(STATUS_ERROR, "Blank resolution") - - f = FollowUp( - ticket=ticket, - date=timezone.now(), - comment=resolution, - user=self.request.user, - title='Resolved', - public=True, - ) - f.save() - - context = safe_template_context(ticket) - context['resolution'] = f.comment - - # subject = '%s %s (Resolved)' % (ticket.ticket, ticket.title) - - messages_sent_to = [] - - if ticket.submitter_email: - send_templated_mail( - 'resolved_submitter', - context, - recipients=ticket.submitter_email, - sender=ticket.queue.from_address, - fail_silently=True, - ) - messages_sent_to.append(ticket.submitter_email) - - for cc in ticket.ticketcc_set.all(): - if cc.email_address not in messages_sent_to: - send_templated_mail( - 'resolved_submitter', - context, - recipients=cc.email_address, - sender=ticket.queue.from_address, - fail_silently=True, - ) - messages_sent_to.append(cc.email_address) - - if ticket.queue.updated_ticket_cc and ticket.queue.updated_ticket_cc not in messages_sent_to: - send_templated_mail( - 'resolved_cc', - context, - recipients=ticket.queue.updated_ticket_cc, - sender=ticket.queue.from_address, - fail_silently=True, - ) - messages_sent_to.append(ticket.queue.updated_ticket_cc) - - if ticket.assigned_to and \ - self.request.user != ticket.assigned_to and \ - getattr(ticket.assigned_to.usersettings.settings, - 'email_on_ticket_apichange', False) and \ - ticket.assigned_to.email and \ - ticket.assigned_to.email not in messages_sent_to: - send_templated_mail( - 'resolved_resolved', - context, - recipients=ticket.assigned_to.email, - sender=ticket.queue.from_address, - fail_silently=True, - ) - - ticket.resoltuion = f.comment - ticket.status = Ticket.RESOLVED_STATUS - - ticket.save() - - return api_return(STATUS_OK)