5f27a2ab94c39c4d332c871a04582287aee6938e
[redakcja.git] / apps / api / handlers / text_handler.py
1 # -*- encoding: utf-8 -*-
2
3 __author__= "Ɓukasz Rekucki"
4 __date__ = "$2009-10-19 14:34:42$"
5 __doc__ = "Module documentation."
6
7 #import api.forms as forms
8 #import api.response as response
9 #
10 #from api.utils import validate_form, hglibrary
11 #from api.models import PartCache
12 #
13
14 #
15 #from piston.handler import BaseHandler
16 #
17 #from wlrepo import *
18
19 import re
20 from library_handlers import *
21
22 import librarian
23 from librarian import parser
24
25 #
26 # Document Text View
27 #
28
29 XINCLUDE_REGEXP = r"""<(?:\w+:)?include\s+[^>]*?href=("|')wlrepo://(?P<link>[^\1]+?)\1\s*[^>]*?>"""
30 #
31 #
32 #
33
34 class DocumentTextHandler(BaseHandler):
35     allowed_methods = ('GET', 'POST')
36
37     @validate_form(forms.TextRetrieveForm, 'GET')
38     @hglibrary
39     def read(self, request, form, docid, lib):
40         """Read document as raw text"""
41         try:
42             revision = form.cleaned_data['revision']
43             chunk = form.cleaned_data['chunk']
44             user = form.cleaned_data['user'] or request.user.username
45             format = form.cleaned_data['format']
46
47             document = lib.document_for_revision(revision)
48
49             if document.id != docid:
50                 return response.BadRequest().django_response({
51                     'reason': 'name-mismatch',
52                     'message': 'Provided revision is not valid for this document'
53                 })
54
55             if document.owner != user:
56                 return response.BadRequest().django_response({
57                     'reason': 'user-mismatch',
58                     'message': "Provided revision doesn't belong to user %s" % user
59                 })
60
61             for error in check_user(request, user):
62                 return error
63
64             if not chunk:
65                 return document.data('xml')
66
67             xdoc = parser.WLDocument.from_string(document.data('xml'),\
68                 parse_dublincore=False)
69
70             xchunk = xdoc.chunk(chunk)
71
72             if xchunk is None:
73                 return response.EntityNotFound().django_response({
74                       'reason': 'no-chunk-in-document',
75                       'path': chunk
76                 })
77
78             return librarian.serialize_children(xchunk, format=format)
79
80         except librarian.ParseError, e:
81             return response.EntityNotFound().django_response({
82                 'reason': 'invalid-document-state',
83                 'exception': type(e),
84                 'message': e.message
85             })
86         except (EntryNotFound, RevisionNotFound), e:
87             return response.EntityNotFound().django_response({
88                 'reason': 'not-found',
89                 'exception': type(e), 'message': e.message
90             })
91
92     @validate_form(forms.TextUpdateForm, 'POST')
93     @hglibrary
94     def create(self, request, form, docid, lib):
95         lock = lib.lock();
96         try:
97             revision = form.cleaned_data['revision']
98             msg = form.cleaned_data['message']
99             user = form.cleaned_data['user'] or request.user.username
100
101             # do not allow changing not owned documents
102             # (for now... )
103
104             if user != request.user.username:
105                 return response.AccessDenied().django_response({
106                     'reason': 'insufficient-priviliges',
107                 })
108
109             current = lib.document(docid, user)
110             orig = lib.document_for_revision(revision)
111
112             if current != orig:
113                 return response.EntityConflict().django_response({
114                         "reason": "out-of-date",
115                         "provided_revision": orig.revision,
116                         "latest_revision": current.revision })
117
118             if form.cleaned_data['contents']:
119                 data = form.cleaned_data['contents']
120             else:
121                 chunks = form.cleaned_data['chunks']
122                 data = current.data('xml')
123                 log.info(data[:600])
124                 log.info(chunks)
125
126                 xdoc = parser.WLDocument.from_string(data)               
127                 errors = xdoc.merge_chunks(chunks)
128
129                 if len(errors):
130                     return response.EntityConflict().django_response({
131                             "reason": "invalid-chunks",
132                             "message": "Unable to merge following parts into the document: %s " % ",".join(errors)
133                     })
134
135                 data = xdoc.serialize()
136
137
138             # try to find any Xinclude tags
139             includes = [m.groupdict()['link'] for m in (re.finditer(\
140                 XINCLUDE_REGEXP, data, flags=re.UNICODE) or []) ]
141
142             log.info("INCLUDES: %s", includes)
143
144             # TODO: provide useful routines to make this simpler
145             def xml_update_action(lib, resolve):
146                 try:
147                     f = lib._fileopen(resolve('parts'), 'r')
148                     stored_includes = json.loads(f.read())
149                     f.close()
150                 except:
151                     stored_includes = []
152
153                 if stored_includes != includes:
154                     f = lib._fileopen(resolve('parts'), 'w+')
155                     f.write(json.dumps(includes))
156                     f.close()
157
158                     lib._fileadd(resolve('parts'))
159
160                     # update the parts cache
161                     PartCache.update_cache(docid, current.owner,\
162                         stored_includes, includes)
163
164                 # now that the parts are ok, write xml
165                 f = lib._fileopen(resolve('xml'), 'w+')
166                 f.write(data.encode('utf-8'))
167                 f.close()
168
169             ndoc = None
170             ndoc = current.invoke_and_commit(\
171                 xml_update_action, lambda d: (msg, user) )
172
173             try:
174                 # return the new revision number
175                 return response.SuccessAllOk().django_response({
176                     "document": ndoc.id,
177                     "user": user,
178                     "subview": "xml",
179                     "previous_revision": current.revision,
180                     "revision": ndoc.revision,
181                     'timestamp': ndoc.revision.timestamp,
182                     "url": reverse("doctext_view", args=[ndoc.id])
183                 })
184             except Exception, e:
185                 if ndoc: lib._rollback()
186                 raise e
187         except RevisionNotFound, e:
188             return response.EntityNotFound(mimetype="text/plain").\
189                 django_response(e.message)
190         finally:
191                 lock.release()