Languages

Previous versions

1.2
1.1

Source code for plmapp.views.document

#-!- coding:utf-8 -!-

############################################################################
# openPLM - open source PLM
# Copyright 2010 Philippe Joulaud, Pierre Cosquer
#
# This file is part of openPLM.
#
#    openPLM is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    openPLM is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with openPLM.  If not, see <http://www.gnu.org/licenses/>.
#
# Contact :
#    Philippe Joulaud : ninoo.fr@gmail.com
#    Pierre Cosquer : pcosquer@linobject.com
################################################################################

import os
import glob
import tempfile
from mimetypes import guess_type

from django.conf import settings
from django.http import (HttpResponseRedirect, HttpResponse, Http404,
                        HttpResponseForbidden,
                        HttpResponseBadRequest, StreamingHttpResponse)
from django.utils.translation import ugettext_lazy as _
from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django.contrib import messages

import openPLM.plmapp.models as models
import openPLM.plmapp.forms as forms
from openPLM.plmapp.utils.archive import ARCHIVE_FORMATS
from openPLM.plmapp.views.base import (get_obj, get_obj_from_form, get_id_card_data,
    get_obj_by_id, handle_errors, get_generic_data,  secure_required)
from openPLM.plmapp.controllers import UserController
from openPLM.plmapp.utils import r2r
from openPLM.plmapp.filehandlers.progressbarhandler import (ProgressBarUploadHandler,
    get_upload_suffix)


@handle_errors
[docs]def display_parts(request, obj_type, obj_ref, obj_revi): """ Attached parts view. That view displays the parts attached to the selected object that must be a document. :url: :samp:`/object/{obj_type}/{obj_ref}/{obj_revi}/parts/` .. include:: views_params.txt **Template:** :file:`documents/parts.html` **Context:** ``RequestContext`` ``parts`` a queryset of :class:`.DocumentPartLink` bound to the document ``parts_formset`` a formset to detach parts ``forms`` a dictionary (link_id -> form) to get the form related to a link (a part may not be "detachable") """ obj, ctx = get_generic_data(request, obj_type, obj_ref, obj_revi) if not hasattr(obj, "get_attached_parts"): return HttpResponseBadRequest("object must be a document") if request.method == "POST": formset = forms.get_rel_part_formset(obj, request.POST) if formset.is_valid(): obj.update_rel_part(formset) return HttpResponseRedirect(".") else: formset = forms.get_rel_part_formset(obj) rforms = dict((form.instance.id, form) for form in formset.forms) parts = obj.get_attached_parts() ctx.update({'current_page':'parts', 'parts': parts, 'forms' : rforms, 'parts_formset': formset}) if request.session.get("as_table"): ctx.update(get_id_card_data([p.id for p in parts])) return r2r('documents/parts.html', ctx, request)
@handle_errors
[docs]def add_part(request, obj_type, obj_ref, obj_revi): """ View to attach a part to a document. :url: :samp:`/object/{obj_type}/{obj_ref}/{obj_revi}/parts/add/` .. include:: views_params.txt **Template:** :file:`documents/parts_add.html` **Context:** ``RequestContext`` ``add_part_form`` a form to attach a part (:class:`.AddPartForm`) ``link_creation`` Set to True ``attach`` set to (*obj*, "attach_part") """ obj, ctx = get_generic_data(request, obj_type, obj_ref, obj_revi) if request.POST: add_part_form = forms.AddPartForm(request.POST) if add_part_form.is_valid(): part_obj = get_obj_from_form(add_part_form, request.user) obj.attach_to_part(part_obj) messages.info(request, _(u"The part has been successfully attached to the document.")) return HttpResponseRedirect(obj.plmobject_url + "parts/") else: add_part_form = forms.AddPartForm() ctx.update({'link_creation': True, 'add_part_form': add_part_form, 'attach' : (obj, "attach_part") }) return r2r('documents/parts_add.html', ctx, request)
@handle_errors
[docs]def delete_part(request, obj_type, obj_ref, obj_revi): """ View to detach a part referred by the POST parameter ``plmobject``. :url: :samp:`/object/{obj_type}/{obj_ref}/{obj_revi}/parts/delete/` .. include:: views_params.txt """ obj, ctx = get_generic_data(request, obj_type, obj_ref, obj_revi) if request.POST: part_id = int(request.POST["plmobject"]) part = get_obj_by_id(part_id, request.user) obj.detach_part(part) msg = _("The part {part.type}/{part.reference}/{part.revision} has been detached.") messages.info(request, msg.format(part=part)) return HttpResponseRedirect(obj.plmobject_url + "parts/")
@handle_errors
[docs]def display_files(request, obj_type, obj_ref, obj_revi): """ Files view. That view displays files of the given document. :url: :samp:`/object/{obj_type}/{obj_ref}/{obj_revi}/files/` .. include:: views_params.txt **Template:** :file:`documents/files.html` **Context:** ``RequestContext`` ``file_formset`` a formset to remove files ``archive_formats`` list of available archive formats ``add_file_form`` form to add a file """ obj, ctx = get_generic_data(request, obj_type, obj_ref, obj_revi) if not hasattr(obj, "files"): return HttpResponseBadRequest("object must be a document") if request.method == "POST": if request.FILES: # from a browser where js is disabled return add_file(request, obj_type, obj_ref, obj_revi) formset = forms.get_file_formset(obj, request.POST) if formset.is_valid(): obj.update_file(formset) return HttpResponseRedirect(".") else: formset = forms.get_file_formset(obj) add_file_form = forms.AddFileForm() ctx.update({'current_page':'files', 'file_formset': formset, 'archive_formats' : ARCHIVE_FORMATS, 'deprecated_files' : obj.deprecated_files.filter(last_revision__isnull=True), 'add_file_form': add_file_form, }) return r2r('documents/files.html', ctx, request)
@handle_errors
[docs]def upload_and_create(request, obj_ref): obj, ctx = get_generic_data(request) if not obj.profile.is_contributor: raise ValueError("You are not a contributor") if request.method == "POST": if request.FILES: # from a browser where js is disabled return add_file(request, "User", obj.username, "-") add_file_form = forms.AddFileForm() ctx.update({ 'add_file_form': add_file_form, 'object_reference': "-", 'object_type': _("Upload"), }) return r2r('users/files.html', ctx, request)
def _get_redirect_url(obj, added_files): if isinstance(obj, UserController): dtype = models.get_best_document_type(added_files) pfiles = "&".join("pfiles=%d" % f.id for f in added_files) url = "/object/create/?type=%s&%s" % (dtype, pfiles) else: url = obj.plmobject_url + "files/" return url @csrf_protect @handle_errors(undo="..")
[docs]def add_file(request, obj_type, obj_ref, obj_revi): """ That view displays the form to upload a file. .. note:: This view show a simple form (no javascript) and is here :url: :samp:`/object/{obj_type}/{obj_ref}/{obj_revi}/files/add/` .. include:: views_params.txt **Template:** :file:`documents/files_add_noscript.html` **Context:** ``RequestContext`` ``add_file_form`` form to add a file """ obj, ctx = get_generic_data(request, obj_type, obj_ref, obj_revi) if request.method == "POST": add_file_form = forms.AddFileForm(request.POST, request.FILES) if add_file_form.is_valid(): added_files = [] for fkey, f in request.FILES.iteritems(): added_files.append(obj.add_file(request.FILES[fkey])) return HttpResponseRedirect(_get_redirect_url(obj, added_files)) else: if obj_type != "User" and 'file_name' in request.GET: f_name = request.GET['file_name'].encode("utf-8") if obj.has_standard_related_locked(f_name): return HttpResponse("true:Native file has a standard related locked file.") else: return HttpResponse("false:") add_file_form = forms.AddFileForm() if obj_type == "User": ctx["object_reference"] = "-" ctx["object_type"] = _("Upload") del ctx["object_menu"] ctx['add_file_form'] = add_file_form return r2r('documents/files_add_noscript.html', ctx, request)
@csrf_exempt
[docs]def up_file(request, obj_type, obj_ref, obj_revi): """ This view process the file(s) upload. The upload is done asynchronously. :url: :samp:`/object/{obj_type}/{obj_ref}/{obj_revi}/files/up/` .. include:: views_params.txt :post params: files uploaded files :get params: list of pair (filename, id) The response contains "failed" if the submitted form is not valid. """ request.upload_handlers.insert(0, ProgressBarUploadHandler(request)) return _up_file(request, obj_type, obj_ref, obj_revi)
@csrf_protect @handle_errors def _up_file(request, obj_type, obj_ref, obj_revi): obj, ctx = get_generic_data(request, obj_type, obj_ref, obj_revi) if request.method == "POST": add_file_form = forms.AddFileForm(request.POST, request.FILES) if add_file_form.is_valid(): added_files = [] for fkey, f in request.FILES.iteritems(): added_files.append(obj.add_file(request.FILES[fkey])) return HttpResponse(_get_redirect_url(obj, added_files)) else: return HttpResponse("failed") @handle_errors @csrf_protect
[docs]def up_progress(request, obj_type, obj_ref, obj_revi): """ Show upload progress for a given progress_id :url: :samp:`/object/{obj_type}/{obj_ref}/{obj_revi}/files/_up/` .. include:: views_params.txt :get params: X-Progress-ID progress id to search f_size size of the original file The response contains the uploaded size and a status : * waiting if the corresponding file has not been created yet * writing if the file is being written * linking if the size of the uploaded file equals the size of the original """ obj, ctx = get_generic_data(request, obj_type, obj_ref, obj_revi) ret = "" suffix = get_upload_suffix(request.GET['X-Progress-ID']) tempdir = settings.FILE_UPLOAD_TEMP_DIR or tempfile.gettempdir() f = glob.glob(os.path.join(tempdir, "*" + suffix)) if f: ret = str(os.path.getsize(f[0])) if not ret: ret = "0:waiting" else: if ret == request.GET['f_size']: ret += ":linking" else: ret += ":writing" return HttpResponse(ret)
@csrf_exempt
[docs]def get_checkin_file(request, obj_type, obj_ref, obj_revi, file_id_value): """ Process to the checkin asynchronously in order to show progress when the checked-in file is uploaded. Calls :func:`.checkin_file` . """ request.upload_handlers.insert(0, ProgressBarUploadHandler(request)) return checkin_file(request, obj_type, obj_ref, obj_revi,file_id_value)
@handle_errors(undo="../..") @csrf_protect
[docs]def checkin_file(request, obj_type, obj_ref, obj_revi, file_id_value): """ Manage html page for the files (:class:`DocumentFile`) checkin in the selected object. It computes a context dictionary based on .. include:: views_params.txt :param file_id_value: :attr:`.DocumentFile.id` :type file_id_value: str :return: a :class:`django.http.HttpResponse` """ obj, ctx = get_generic_data(request, obj_type, obj_ref, obj_revi) if request.POST: checkin_file_form = forms.AddFileForm(request.POST, request.FILES) if checkin_file_form.is_valid(): obj.checkin(models.DocumentFile.objects.get(id=file_id_value), request.FILES["filename"]) return HttpResponseRedirect(obj.plmobject_url + "files/") else: checkin_file_form = forms.AddFileForm() ctx['add_file_form'] = checkin_file_form return r2r('documents/files_add_noscript.html', ctx, request)
@handle_errors
[docs]def file_revisions(request, docfile_id): """ View to download a document file. :param request: :class:`django.http.QueryDict` :param docfile_id: :attr:`.DocumentFile.id` :type docfile_id: str :return: a :class:`django.http.HttpResponse` """ doc_file = models.DocumentFile.objects.get(id=docfile_id) obj, ctx = get_generic_data(request, doc_file.document.type, doc_file.document.reference, doc_file.document.revision) last_revision = doc_file.last_revision if doc_file.last_revision else doc_file doc_files = last_revision.older_files.order_by("-revision") ctx["last_revision"] = last_revision ctx["doc_files"] = doc_files checkin_file_form = forms.AddFileForm() ctx['add_file_form'] = checkin_file_form ctx["action"] = "%sfiles/checkin/%d/" % (obj.plmobject_url, last_revision.id) return r2r("documents/file_revisions.html", ctx, request)
@handle_errors
[docs]def checkout_file(request, obj_type, obj_ref, obj_revi, docfile_id): """ Manage html page for the files (:class:`DocumentFile`) checkout from the selected object. It locks the :class:`DocumentFile` and, after, calls :func:`.views.download` .. include:: views_params.txt :param docfile_id: :attr:`.DocumentFile.id` :type docfile_id_value: str """ obj = get_obj(obj_type, obj_ref, obj_revi, request.user) doc_file = models.DocumentFile.objects.get(id=docfile_id) obj.lock(doc_file) return download(request, docfile_id)
@handle_errors
[docs]def download(request, docfile_id): """ View to download a document file. :param request: :class:`django.http.QueryDict` :param docfile_id: :attr:`.DocumentFile.id` :type docfile_id: str :return: a :class:`django.http.HttpResponse` """ doc_file = models.DocumentFile.objects.get(id=docfile_id) ctrl = get_obj_by_id(int(doc_file.document.id), request.user) ctrl.check_readable() return serve(ctrl, doc_file, "view" in request.GET)
@secure_required
[docs]def public_download(request, docfile_id): """ View to download a published document file. It returns an :class: `HttpResponseForbidden` if the document is not published. :param request: :class:`django.http.QueryDict` :param docfile_id: :attr:`.DocumentFile.id` :type docfile_id: str :return: a :class:`django.http.HttpResponse` """ doc_file = models.DocumentFile.objects.get(id=docfile_id) ctrl = get_obj_by_id(int(doc_file.document.id), request.user) if request.user.is_authenticated(): if not ctrl.published and not ctrl.check_restricted_readable(False): raise Http404 elif not ctrl.published: return HttpResponseForbidden() return serve(ctrl, doc_file)
[docs]def serve(ctrl, doc_file, view=False): name = doc_file.filename.encode("utf-8", "ignore") content_type = guess_type(name, False)[0] if not content_type: content_type = 'application/octet-stream' f, size = ctrl.get_content_and_size(doc_file) response = StreamingHttpResponse(f, content_type=content_type) response["Content-Length"] = size if not view: response['Content-Disposition'] = 'attachment; filename="%s"' % name return response