Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/Pootle-2.0.0/local_apps/pootle_app/views/language/view.py
blob: d9e51f7bf67f14b95103f641d8cd45fbf8036c00 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2008-2009 Zuza Software Foundation
#
# This file is part of Pootle.
#
# This program 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 2 of the License, or
# (at your option) any later version.
#
# This program 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 this program; if not, see <http://www.gnu.org/licenses/>.

import cStringIO
import os

from django.shortcuts import get_object_or_404
from django.http import HttpResponse
from django.utils import simplejson
from django.utils.translation import ugettext as _
from django.utils.translation import ungettext as _N
from django.core.exceptions import PermissionDenied

from pootle_misc.baseurl           import redirect
from pootle_app.models             import TranslationProject, Directory, store_iteration
from pootle_store.models import Store
from pootle_app.models.permissions import get_matching_permissions, check_permission
from pootle_app.models.profile     import get_profile
from pootle_app.views.language     import dispatch
from pootle_app.convert            import convert_table
from pootle_app                    import unit_update

from pootle_app.views.language.tp_translate import view as tp_translate_view
from pootle_app.views.language.tp_review import view as tp_review_view
from pootle_app.views.language.admin_permissions import view as tp_admin_permissions_view
from pootle_app.views.language.admin_files import view as tp_admin_files_view

from pootle_app.views.language.project_index import view as project_index_view
from pootle_app.views.language.translate_page import find_and_display
from pootle_app.views.language import search_forms

from pootle_app.views.language.translate_page import get_diff_codes
from pootle_app.views.language.translate_page import highlight_diffs
from pootle_app.views.language.translate_page import get_string_array

def get_translation_project(f):
    def decorated_f(request, language_code, project_code, *args, **kwargs):
        translation_project = get_object_or_404(TranslationProject, language__code=language_code, project__code=project_code)
        return f(request, translation_project, *args, **kwargs)
    return decorated_f

def set_request_context(f):
    def decorated_f(request, translation_project, *args, **kwargs):
        # For now, all permissions in a translation project are
        # relative to the root of that translation project.
        request.permissions = get_matching_permissions(
            get_profile(request.user), translation_project.directory)
        request.translation_project = translation_project
        return f(request, translation_project, *args, **kwargs)
    return decorated_f

################################################################################

@get_translation_project
@set_request_context
def translation_project_admin_permissions(request, translation_project):
    return tp_admin_permissions_view(request, translation_project)

@get_translation_project
@set_request_context
def translation_project_admin_files(request, translation_project):
    return tp_admin_files_view(request, translation_project)

@get_translation_project
@set_request_context
def translate_page(request, translation_project, dir_path):
    def next_store_item(search, store_name, item):
        return store_iteration.get_next_match(directory,
                                              store_name,
                                              item,
                                              search)

    def prev_store_item(search, store_name, item):
        return store_iteration.get_prev_match(directory,
                                              store_name,
                                              item,
                                              search)

    directory = translation_project.directory.get_relative(dir_path)
    return find_and_display(request, directory, next_store_item, prev_store_item)


@get_translation_project
@set_request_context
def project_index(request, translation_project, dir_path):
    directory = get_object_or_404(Directory, pootle_path=translation_project.directory.pootle_path + dir_path)
    return project_index_view(request, translation_project, directory)

def handle_translation_file(request, translation_project, file_path):
    pootle_path = translation_project.directory.pootle_path + (file_path or '')
    store = get_object_or_404(Store, pootle_path=pootle_path)
    def get_item(itr, item):
        try:
            return itr.next()
        except StopIteration:
            return item

    def next_store_item(search, store_name, item):
        if 0 <= item < store.getquickstats()['total']:
            return store, get_item(search.next_matches(store, item), item - 1)
        else:
            return store, store.getquickstats()['total'] - 1

    def prev_store_item(search, store_name, item):
        if store.getquickstats()['total'] > item > 0:
            return store, get_item(search.prev_matches(store, item), item + 1)
        else:
            return store, 0

    return find_and_display(request, store.parent, next_store_item, prev_store_item)


@get_translation_project
@set_request_context
def commit_file(request, translation_project, file_path):
    if not check_permission("commit", request):
        raise PermissionDenied(_("You do not have rights to commit files here"))
    pootle_path = translation_project.directory.pootle_path + file_path
    store = get_object_or_404(Store, pootle_path=pootle_path)
    result = translation_project.commitpofile(request, store)
    return redirect(dispatch.show_directory(request, translation_project.directory.pootle_path))

@get_translation_project
@set_request_context
def update_file(request, translation_project, file_path):
    if not check_permission("commit", request):
        raise PermissionDenied(_("You do not have rights to update files here"))
    pootle_path = translation_project.directory.pootle_path + file_path
    store = get_object_or_404(Store, pootle_path=pootle_path)
    result = translation_project.update_file(request, store)
    return redirect(dispatch.show_directory(request, translation_project.directory.pootle_path))

@get_translation_project
@set_request_context
def export_zip(request, translation_project, file_path):
    if not check_permission("archive", request):
        return redirect(translation_project.pootle_path,
                        message=_('You do not have the right to create ZIP archives.'))
    pootle_path = translation_project.pootle_path + (file_path or '')
    try:
        path_obj = Directory.objects.get(pootle_path=pootle_path)
    except Directory.DoesNotExist:
        path_obj = get_object_or_404(Store, pootle_path=pootle_path[:-1])
    stores = store_iteration.iter_stores(path_obj)
    archivecontents = translation_project.get_archive(stores)
    response = HttpResponse(archivecontents, content_type="application/zip")
    if file_path.endswith("/"):
        file_path = file_path[:-1]
    fish, file_path = os.path.split(file_path)
    archivename = '%s-%s' % (translation_project.project.code, translation_project.language.code)
    archivename += file_path + '.zip'
    response['Content-Disposition'] = 'attachment; filename=%s' % archivename
    return response

MIME_TYPES = {
    "po":  "text/x-gettext-translation; charset=%(encoding)s",
    "csv": "text/csv; charset=%(encoding)s",
    "xlf": "application/x-xliff; charset=%(encoding)s",
    "ts":  "application/x-linguist; charset=%(encoding)s",
    "mo":  "application/x-gettext-translation" }

@get_translation_project
@set_request_context
def export(request, translation_project, file_path, format):
    store = get_object_or_404(Store, pootle_path=translation_project.directory.pootle_path + file_path)
    encoding = getattr(store.file.store, "encoding", "UTF-8")
    content_type = MIME_TYPES[format] % dict(encoding=encoding)
    if format == translation_project.project.localfiletype:
        response = HttpResponse(str(store.file.store), content_type=content_type)
        response['Content-Disposition'] = 'attachment; filename=%s' % store.name
    else:
        convert_func = convert_table[translation_project.project.localfiletype, format]
        output_file = cStringIO.StringIO()
        input_file  = cStringIO.StringIO(str(store.file.store))
        convert_func(input_file, output_file, None)
        response = HttpResponse(output_file.getvalue(), content_type=content_type)
        filename, ext = os.path.splitext(store.name)
        filename += os.path.extsep + format
        response['Content-Disposition'] = 'attachment; filename=%s' % filename
    return response


@get_translation_project
@set_request_context
def handle_file(request, translation_project, file_path):
    return handle_translation_file(request, translation_project, file_path)

@get_translation_project
@set_request_context
def handle_suggestions(request, translation_project, file_path, item):
    """Handles accepts/rejects of suggestions selectively via AJAX, receiving
       and sending data in JSON format.

       Response attributes are described below:
        * "status": Indicates the status after trying the action.
                    Possible values: "error", "success".
        * "message": Status message of the transaction. Depending on the status
                    it will display an error message, or the number of
                    suggestions rejected/accepted.
        * "diffs": Updated diff for the current translation after performing
                   an action. If there are no suggestions pending, an empty
                   dict will be returned."""
    pootle_path = translation_project.pootle_path + file_path
    store = Store.objects.get(pootle_path=pootle_path)
    item = int(item)

    def get_pending_suggestions(item):
        """Gets pending suggestions for item in pofilename."""
        itemsuggestions = []
        suggestions = store.getsuggestions(item)
        for suggestion in suggestions:
            if suggestion.hasplural():
                itemsuggestions.append(suggestion.target.strings)
            else:
                itemsuggestions.append([suggestion.target])
        return itemsuggestions

    def get_updated_diffs(trans, suggestions):
        """Returns the diff between the current translation and the
           suggestions available after performing an accept/reject
           action.
           If no suggestions are available anymore, just return an
           empty list."""
        # No suggestions left, no output at all
        if len(suggs) == 0:
            return []
        else:
            diffcodes = {}
            forms = []
            for pluralitem, pluraltrans in enumerate(trans):
                pluraldiffcodes = [get_diff_codes(pluraltrans,
                                                  suggestion[pluralitem])
                                   for suggestion in suggestions]
                diffcodes[pluralitem] = pluraldiffcodes
                combineddiffs = reduce(list.__add__, pluraldiffcodes, [])
                transdiff = highlight_diffs(pluraltrans, combineddiffs,
                                            issrc=True)
                form = { "diff": transdiff }
                forms.append(form)
            return forms

    response = {}
    # Decode JSON data sent via POST
    data = simplejson.loads(request.POST.get("data", "{}"))
    if not data:
        response["status"] = "error"
        response["message"] = _("No suggestion data given.")
    else:
        response["del_ids"] = []
        rejects = data.get("rejects", [])
        reject_candidates = len(rejects)
        reject_count = 0
        accepts = data.get("accepts", [])
        accept_candidates = len(accepts)
        accept_count = 0

        for sugg in accepts:
            try:
                unit_update.accept_suggestion(store, item, int(sugg["id"]),
                                              sugg["newtrans"], request)
                response["del_ids"].append((item, sugg["id"]))
                response["accepted_id"] = (item, sugg["id"])
                accept_count += 1
            except ValueError, e:
                # Probably an issue with "item". The exception might tell us
                # everything we need, while no error will probably help the user
                response["message"] = e
            except PermissionDenied, e:
                response["message"] = e


        for sugg in reversed(rejects):
            try:
                unit_update.reject_suggestion(store, int(item), int(sugg["id"]),
                                              sugg["newtrans"], request)
                reject_count += 1
                response["del_ids"].append((int(item), sugg["id"]))
            except ValueError, e:
                # Probably an issue with "item". The exception might tell us
                # everything we need, while no error will probably help the user
                response["message"] = e
            except PermissionDenied, e:
                response["message"] = e

        response["status"] = (reject_candidates == reject_count and
                              accept_candidates == accept_count) and \
                              "success" or "error"

        if response["status"] == "success":
            amsg = ""
            rmsg = ""
            if accept_candidates != 0:
                amsg = _("Suggestion accepted.")
            if reject_candidates != 0:
                rmsg = _N("Suggestion rejected.",
                          "%d suggestions rejected.",
                          reject_count, reject_count)
            response["message"] = amsg + rmsg
            # Get updated diffs
            current_trans = get_string_array(store.file.getitem(item).target)
            suggs = get_pending_suggestions(item)
            response["diffs"] = get_updated_diffs(current_trans, suggs)

    response = simplejson.dumps(response, indent=4)
    return HttpResponse(response, mimetype="application/json")

@get_translation_project
@set_request_context
def tp_translate(request, translation_project, dir_path):
    directory = get_object_or_404(Directory, pootle_path=translation_project.directory.pootle_path + dir_path)
    return tp_translate_view(request, translation_project, directory)

@get_translation_project
@set_request_context
def tp_review(request, translation_project, dir_path):
    directory = get_object_or_404(Directory, pootle_path=translation_project.directory.pootle_path + dir_path)
    return tp_review_view(request, translation_project, directory)