source: main/trunk/openPLM/plmapp/views/api.py @ 368

Revision 368, 15.0 KB checked in by pcosquer, 9 years ago (diff)

views:create: restrict available groups to user's groups

Line 
1############################################################################
2# openPLM - open source PLM
3# Copyright 2010 Philippe Joulaud, Pierre Cosquer
4#
5# This file is part of openPLM.
6#
7#    openPLM is free software: you can redistribute it and/or modify
8#    it under the terms of the GNU General Public License as published by
9#    the Free Software Foundation, either version 3 of the License, or
10#    (at your option) any later version.
11#
12#    openPLM is distributed in the hope that it will be useful,
13#    but WITHOUT ANY WARRANTY; without even the implied warranty of
14#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15#    GNU General Public License for more details.
16#
17#    You should have received a copy of the GNU General Public License
18#    along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
19#
20# Contact :
21#    Philippe Joulaud : ninoo.fr@gmail.com
22#    Pierre Cosquer : pierre.cosquer@insa-rennes.fr
23################################################################################
24
25"""
26This modules contains all stuff related to the api
27
28.. seealso:: The public api :mod:`http_api`,
29"""
30
31import functools
32
33import django.forms
34from django.contrib.auth.decorators import user_passes_test
35from django.contrib.auth import authenticate, login
36from django.http import HttpResponseForbidden
37from django.contrib.csrf.middleware import csrf_exempt
38
39import openPLM.plmapp.models as models
40from openPLM.plmapp.controllers import get_controller, DocumentController
41import openPLM.plmapp.forms as forms
42from openPLM.plmapp.utils import get_next_revision
43from openPLM.plmapp.base_views import json_view, get_obj_by_id, object_to_dict
44
45#: Version of the API (value: ``'1.0'``)
46API_VERSION = "1.0"
47#: Decorator whichs requires that the user is login
48api_login_required = user_passes_test(lambda u: u.is_authenticated(),
49                                      login_url="/api/needlogin/")
50
51@json_view
52def need_login(request):
53    """ Helper function for :func:`api_login_required` """
54    return {'result' : 'error', 'error' : 'user must be login'}
55
56def login_json(func):
57    """
58    Decorator which requires a login user and converts returned value into
59    a json response.
60
61    This also checks if the user agent is ``"openplm"`` and, if not,
62    returns a 403 HTTP RESPONSE.
63    """
64   
65    json_func = json_view(func, API_VERSION)
66    @api_login_required
67    @csrf_exempt
68    @functools.wraps(func)
69    def wrapper(request, *args, **kwargs):
70        if request.META["HTTP_USER_AGENT"] != "openplm":
71            return HttpResponseForbidden()
72        return json_func(request, *args, **kwargs)
73    return wrapper
74
75
76@login_json
77def get_all_types(request):
78    """
79    Returns all the subtypes of :class:`.PLMObject` managed by the server.
80
81    :implements: :func:`http_api.types`
82    """
83    return {"types" : sorted(models.get_all_plmobjects().keys())}
84
85@login_json
86def get_all_docs(request):
87    """
88    Returns all  the types of :class:`.Document` managed by the server.
89
90    :implements: :func:`http_api.docs`
91    """
92    return {"types" : sorted(models.get_all_documents().keys())}
93
94@login_json
95def get_all_parts(request):
96    """
97    Returns all the types of :class:`.Part` managed by the server.
98
99    :implements: :func:`http_api.parts`
100    """
101    return {"types" : sorted(models.get_all_parts().keys())}
102
103@login_json
104def search(request, editable_only="true", with_file_only="true"):
105    """
106    Returns all objects matching a query.
107
108    :param editable_only: if ``"true"`` (the default), returns only editable objects
109    :param with_file_only: if ``"true"`` (the default), returns only documents with
110                           at least one file
111
112    :implements: :func:`http_api.search`
113    """
114    if request.GET and "type" in request.GET:
115        attributes_form = forms.TypeForm(request.GET)
116        if attributes_form.is_valid():
117            query_dict = {}
118            cls = models.get_all_plmobjects()[attributes_form.cleaned_data["type"]]
119            extra_attributes_form = forms.get_search_form(cls, request.GET)
120            results = cls.objects.all()
121            if extra_attributes_form.is_valid():
122                results = extra_attributes_form.search(results)
123                objects = []
124                for res in results:
125                    if editable_only == "false" or res.is_editable:
126                        if with_file_only == "true" and hasattr(res, "files") \
127                           and not bool(res.files):
128                            continue
129                        if editable_only == "true":
130                            obj = DocumentController(res, request.user)
131                            if not obj.check_permission("owner", False):
132                                continue
133                        objects.append(object_to_dict(res))
134                return {"objects" : objects}
135    return {"result": "error"}
136
137@login_json
138def create(request):
139    """
140    Creates a :class:`.PLMObject` and returns it
141
142    :implements: :func:`http_api.create`
143    """
144    try:
145        type_name = request.POST["type"]
146        cls = models.get_all_plmobjects()[type_name]
147    except KeyError:
148        return {"result" : "error", 'error' : 'bad type'}
149    form = forms.get_creation_form(request.user, cls, request.POST)
150    if form.is_valid():
151        controller_cls = get_controller(type_name)
152        controller = controller_cls.create_from_form(form, request.user)
153        ret = {"object" : object_to_dict(controller)}
154        return ret
155    else:
156        return {"result" : "error", "error" : form.errors.as_text()}
157
158@login_json
159def get_files(request, doc_id, all_files=False):
160    """
161    Returns the list of files of the :class:`.Document` identified by *doc_id*.
162    If *all_files* is False (the default), only unlocked files are returned.
163
164    :implements: :func:`http_api.files`
165   
166    :param request: the request
167    :param doc_id: id of a :class:`.Document`
168    :param all_files: boolean, False if only unlocked files should be returned
169    :returned fields: files, a list of files (see :ref:`http-api-file`)
170    """
171
172    document = models.Document.objects.get(id=doc_id)
173    document = models.get_all_plmobjects()[document.type].objects.get(id=doc_id)
174    files = []
175    for df in document.files:
176        if all_files or not df.locked:
177            files.append(dict(id=df.id, filename=df.filename, size=df.size))
178    return {"files" : files}
179
180@login_json
181def check_out(request, doc_id, df_id):
182    """
183    Locks the :class:`.DocumentFile` identified by *df_id* from
184    the :class:`.Document` identified by *doc_id*.
185
186    :implements: :func:`http_api.lock`
187   
188    :param request: the request
189    :param doc_id: id of a :class:`.Document`
190    :param df_id: id of a :class:`.DocumentFile`
191    :returned fields: None
192    """
193    doc = get_obj_by_id(doc_id, request.user)
194    df = models.DocumentFile.objects.get(id=df_id)
195    doc.lock(df)
196    return {}
197
198
199@login_json
200def check_in(request, doc_id, df_id):
201    """
202    Checks-in the :class:`.DocumentFile` identified by *df_id* from
203    the :class:`.Document` identified by *doc_id*
204
205    :implements: :func:`http_api.check_in`
206   
207    :param request: the request
208    :param doc_id: id of a :class:`.Document`
209    :param df_id: id of a :class:`.DocumentFile`
210    :returned fields: None
211    """
212    doc = get_obj_by_id(doc_id, request.user)
213    df = models.DocumentFile.objects.get(id=df_id)
214    form = forms.AddFileForm(request.POST, request.FILES)
215    if form.is_valid():
216        doc.checkin(df, request.FILES['filename'])
217    return {}
218
219@login_json
220def is_locked(request, doc_id, df_id):
221    """
222    Returns True if the :class:`.DocumentFile` identified by *df_id* from
223    the :class:`.Document` identified by *doc_id* is locked.
224
225    :implements: :func:`http_api.is_locked`
226   
227    :param request: the request
228    :param doc_id: id of a :class:`.Document`
229    :param df_id: id of a :class:`.DocumentFile`
230    :returned fields: locked, True if the file is locked.
231    """
232
233    doc = get_obj_by_id(doc_id, request.user)
234    df = models.DocumentFile.objects.get(id=df_id)
235    return {"locked" : df.locked}
236
237@login_json
238def unlock(request, doc_id, df_id):
239    """
240    Unlocks the :class:`.DocumentFile` identified by *df_id* from
241    the :class:`.Document` identified by *doc_id*.
242
243    :implements: :func:`http_api.unlock`
244   
245    :param request: the request
246    :param doc_id: id of a :class:`.Document`
247    :param df_id: id of a :class:`.DocumentFile`
248    :returned fields: None
249    """
250    doc = get_obj_by_id(doc_id, request.user)
251    df = models.DocumentFile.objects.get(id=df_id)
252    if df.locked:
253        doc.unlock(df)
254    return {}
255
256def field_to_type(field):
257    """
258    Converts *field* (a django FormField) to a type as described in
259    :ref:`http-api-types`.
260    """
261    types = {django.forms.IntegerField : "int",
262             django.forms.DecimalField : "decimal",
263             django.forms.FloatField : "float",
264             django.forms.BooleanField : "boolean",
265             django.forms.ChoiceField : "choice",
266           }
267    if type(field) in types:
268        return types[type(field)]
269    for key in types:
270        if isinstance(field, key):
271            return types[key]
272    return "text"
273
274def get_fields_from_form(form):
275    """
276    Returns a list of fields from *form* converted to the format described in
277    :ref:`http-api-fields`.
278    """
279    fields = []
280    for field_name, field in form.fields.items():
281        data = dict(name=field_name, label=field.label.capitalize(), initial=field.initial)
282        if callable(field.initial):
283            data["initial"] = field.initial()
284            if hasattr(data["initial"], "pk"):
285                data["initial"] = data["initial"].pk
286        data["type"] = field_to_type(field)
287        if hasattr(field, "choices"):
288            data["choices"] =  tuple(field.choices)
289        for attr in ("min_value", "max_value", "min_length", "max_length"):
290            if hasattr(field, attr):
291                data[attr] = getattr(field, attr)
292        fields.append(data)
293    return fields
294
295@login_json
296def get_search_fields(request, typename):
297    """
298    Returns search fields associated to *typename*.
299
300    :implements: :func:`http_api.search_fields`
301    """
302    try:
303        form = forms.get_search_form(models.get_all_plmobjects()[typename])
304    except KeyError:
305        return {"result" : "error", "fields" : []}
306    return {"fields" : get_fields_from_form(form)}
307
308@login_json
309def get_creation_fields(request, typename):
310    """
311    Returns creation fields associated to *typename*
312
313    :implements: :func:`http_api.creation_fields`
314    """
315    try:
316        form = forms.get_creation_form(request.user,
317                models.get_all_plmobjects()[typename])
318    except KeyError:
319        return {"result" : "error", "fields" : []}
320    return {"fields" : get_fields_from_form(form)}
321
322@csrf_exempt
323@json_view
324def api_login(request):
325    """
326    Authenticates the user
327
328    :implements: :func:`http_api.login`
329    """
330    username = request.POST['username']
331    password = request.POST['password']
332    user = authenticate(username=username, password=password)
333    if user is not None:
334        if user.is_active:
335            login(request, user)
336            return {"username" : username, "first_name" : user.first_name,
337                    "last_name" : user.last_name}
338        else:
339            return {"result" : 'error', 'error' : 'user is inactive'}
340    else:
341        return {"result" : 'error', 'error' : 'login invalid'}
342
343
344@login_json
345def test_login(request):
346    """
347    Tests if user is authenticated
348
349    :implement: :func:`http_api.testlogin`
350    """
351    # do nothing, if user is authenticated, json_view sets *result* to 'ok'
352    return {}
353
354@login_json
355def next_revision(request, doc_id):
356    """
357    Returns a possible new revision for the :class:`.Document` identified by
358    *doc_id*.
359   
360    :implements: :func:`http_api.next_revision`
361   
362    :param request: the request
363    :param doc_id: id of a :class:`.Document`
364    :returned fields: revision, the new revision (may be an empty string)
365
366    .. seealso:: :func:`.utils.get_next_revision` for possible results
367    """
368
369    doc = get_obj_by_id(doc_id, request.user)
370    return {"revision" : get_next_revision(doc.revision)}
371
372@login_json
373def revise(request, doc_id):
374    """
375    Makes a new revision of the :class:`.Document` identified by *doc_id*.
376
377    :implements: :func:`http_api.revise`
378   
379    :param request: the request
380    :param doc_id: id of a :class:`.Document`
381    :returned fields:
382        * doc, the new document (see :ref:`http-api-object`)
383        * files, a list of files (see :ref:`http-api-file`)
384    """
385   
386    doc = get_obj_by_id(doc_id, request.user)
387    form = forms.AddRevisionForm(request.POST)
388    if form.is_valid():
389        rev = doc.revise(form.cleaned_data["revision"])
390        ret = {"doc" : object_to_dict(rev)}
391        files = []
392        for df in rev.files:
393            files.append(dict(id=df.id, filename=df.filename, size=df.size))
394        ret["files"] = files
395        return ret
396    else:
397        return {"result" : 'error'}
398
399@login_json
400def is_revisable(request, doc_id):
401    """
402    Returns True if the :class:`.Document` identified by *doc_id* can be revised.
403
404    :implements: :func:`http_api.is_revisable`
405   
406    :param request: the request
407    :param doc_id: id of a :class:`.Document`
408    :returned fields: revisable, True if it can be revised
409    """
410
411    doc = get_obj_by_id(doc_id, request.user)
412    return {"revisable" : doc.is_revisable()}
413
414
415@login_json
416def attach_to_part(request, doc_id, part_id):
417    """
418    Links the :class:`.Document` identified by *doc_id* with the :class:`.Part`
419    identified by *part_id*.
420
421    :implements: :func:`http_api.attach_to_part`
422   
423    :param request: the request
424    :param doc_id: id of a :class:`.Document`
425    :param part_id: id of a :class:`.Part`
426    :returned fields: None
427    """
428    doc = get_obj_by_id(doc_id, request.user)
429    part = get_obj_by_id(part_id, request.user)
430    doc.attach_to_part(part)
431    return {}
432
433
434@login_json
435def add_file(request, doc_id):
436    """
437    Adds a file to the :class:`.Document` identified by *doc_id*.
438
439    :implements: :func:`http_api.add_file`
440   
441    :param request: the request
442    :param doc_id: id of a :class:`.Document`
443    :returned fields: doc_file, the file that has been had,
444                      see :ref:`http-api-file`.
445    """
446    doc = get_obj_by_id(doc_id, request.user)
447    form = forms.AddFileForm(request.POST, request.FILES)
448    df = doc.add_file(request.FILES["filename"])
449    return {"doc_file" : dict(id=df.id, filename=df.filename, size=df.size)}
450
451
452@login_json
453def add_thumbnail(request, doc_id, df_id):
454    """
455    Add a thumbnail the :class:`.DocumentFile` identified by *df_id* from
456    the :class:`.Document` identified by *doc_id*.
457
458    :implements: :func:`http_api.add_thumbnail`
459   
460    :param request: the request
461    :param doc_id: id of a :class:`.Document`
462    :param df_id: id of a :class:`.DocumentFile`
463    :returned fields: None
464    """
465
466    doc = get_obj_by_id(doc_id, request.user)
467    form = forms.AddFileForm(request.POST, request.FILES)
468    df = models.DocumentFile.objects.get(id=df_id)
469    doc.add_thumbnail(df, request.FILES["filename"])
470    return {}
471
Note: See TracBrowser for help on using the repository browser.