PageRenderTime 10ms CodeModel.GetById 2ms app.highlight 93ms RepoModel.GetById 1ms app.codeStats 1ms

/main/views.py

https://github.com/zoulema/formhub
Python | 1324 lines | 1261 code | 39 blank | 24 comment | 79 complexity | 6c3ab82d8e66a38e10a958a8a507b6df MD5 | raw file
   1from datetime import datetime
   2from django.contrib.contenttypes.models import ContentType
   3import os
   4import json
   5from django.db import IntegrityError
   6from django.core.urlresolvers import reverse
   7from django.core.files.storage import default_storage, get_storage_class
   8from django.contrib.auth.decorators import login_required
   9from django.contrib.auth.models import User
  10from django.contrib import messages
  11from django.http import HttpResponse, HttpResponseBadRequest, \
  12    HttpResponseRedirect, HttpResponseForbidden, HttpResponseNotFound,\
  13    HttpResponseServerError
  14from django.shortcuts import render_to_response, get_object_or_404
  15from django.template import loader, RequestContext
  16from django.utils.translation import ugettext as _
  17from django.views.decorators.http import require_GET, require_POST,\
  18    require_http_methods
  19from google_doc import GoogleDoc
  20from guardian.shortcuts import assign_perm, remove_perm, get_users_with_perms
  21
  22from main.forms import UserProfileForm, FormLicenseForm, DataLicenseForm,\
  23    SupportDocForm, QuickConverterFile, QuickConverterURL, QuickConverter,\
  24    SourceForm, PermissionForm, MediaForm, MapboxLayerForm, \
  25    ActivateSMSSupportFom
  26from main.models import UserProfile, MetaData
  27from odk_logger.models import Instance, XForm
  28from odk_logger.views import enter_data
  29from odk_viewer.models import DataDictionary, ParsedInstance
  30from odk_viewer.models.data_dictionary import upload_to
  31from odk_viewer.models.parsed_instance import GLOBAL_SUBMISSION_STATS,\
  32    DATETIME_FORMAT
  33from odk_viewer.views import survey_responses, attachment_url
  34from stats.models import StatsCount
  35from stats.tasks import stat_log
  36from utils.decorators import is_owner
  37from utils.logger_tools import response_with_mimetype_and_name, publish_form
  38from utils.user_auth import check_and_set_user, set_profile_data,\
  39    has_permission, helper_auth_helper, get_xform_and_perms,\
  40    check_and_set_user_and_form, add_cors_headers
  41from utils.log import audit_log, Actions
  42from main.models import AuditLog
  43from django.conf import settings
  44
  45from utils.viewer_tools import enketo_url
  46from utils.qrcode import generate_qrcode
  47
  48from sms_support.tools import check_form_sms_compatibility, is_sms_related
  49from sms_support.autodoc import get_autodoc_for
  50from sms_support.providers import providers_doc
  51
  52from registration.signals import user_registered
  53from django.dispatch import receiver
  54from rest_framework.authtoken.models import Token
  55
  56
  57@receiver(user_registered, dispatch_uid='auto_add_crowdform')
  58def auto_add_crowd_form_to_registered_user(sender, **kwargs):
  59    new_user = kwargs.get('user')
  60    if hasattr(settings, 'AUTO_ADD_CROWDFORM') and \
  61            settings.AUTO_ADD_CROWDFORM and \
  62            hasattr(settings, 'DEFAULT_CROWDFORM'):
  63        try:
  64            default_crowdform = settings.DEFAULT_CROWDFORM
  65            if isinstance(default_crowdform, dict) and\
  66                    'xform_username' in default_crowdform and\
  67                    'xform_id_string' in default_crowdform:
  68                xform = XForm.objects.get(
  69                    id_string=default_crowdform['xform_id_string'],
  70                    user__username=default_crowdform['xform_username'])
  71                MetaData.crowdform_users(xform, new_user.username)
  72        except XForm.DoesNotExist:
  73            pass
  74
  75
  76def home(request):
  77    if request.user.username:
  78        return HttpResponseRedirect(
  79            reverse(profile, kwargs={'username': request.user.username}))
  80    context = RequestContext(request)
  81    return render_to_response('home.html', context_instance=context)
  82
  83
  84@login_required
  85def login_redirect(request):
  86    return HttpResponseRedirect(reverse(profile,
  87                                kwargs={'username': request.user.username}))
  88
  89
  90@require_POST
  91@login_required
  92def clone_xlsform(request, username):
  93    """
  94    Copy a public/Shared form to a users list of forms.
  95    Eliminates the need to download Excel File and upload again.
  96    """
  97    to_username = request.user.username
  98    context = RequestContext(request)
  99    context.message = {'type': None, 'text': '....'}
 100
 101    def set_form():
 102        form_owner = request.POST.get('username')
 103        id_string = request.POST.get('id_string')
 104        xform = XForm.objects.get(user__username=form_owner,
 105                                  id_string=id_string)
 106        if len(id_string) > 0 and id_string[0].isdigit():
 107            id_string = '_' + id_string
 108        path = xform.xls.name
 109        if default_storage.exists(path):
 110            xls_file = upload_to(None, '%s%s.xls' % (
 111                                 id_string, XForm.CLONED_SUFFIX), to_username)
 112            xls_data = default_storage.open(path)
 113            xls_file = default_storage.save(xls_file, xls_data)
 114            context.message = u'%s-%s' % (form_owner, xls_file)
 115            survey = DataDictionary.objects.create(
 116                user=request.user,
 117                xls=xls_file
 118            ).survey
 119            # log to cloner's account
 120            audit = {}
 121            audit_log(
 122                Actions.FORM_CLONED, request.user, request.user,
 123                _("Cloned form '%(id_string)s'.") %
 124                {
 125                    'id_string': survey.id_string,
 126                }, audit, request)
 127            clone_form_url = reverse(
 128                show, kwargs={
 129                    'username': to_username,
 130                    'id_string': xform.id_string + XForm.CLONED_SUFFIX})
 131            return {
 132                'type': 'alert-success',
 133                'text': _(u'Successfully cloned to %(form_url)s into your '
 134                          u'%(profile_url)s') %
 135                {'form_url': u'<a href="%(url)s">%(id_string)s</a> ' % {
 136                 'id_string': survey.id_string,
 137                 'url': clone_form_url
 138                 },
 139                    'profile_url': u'<a href="%s">profile</a>.' %
 140                    reverse(profile, kwargs={'username': to_username})}
 141            }
 142    form_result = publish_form(set_form)
 143    if form_result['type'] == 'alert-success':
 144        # comment the following condition (and else)
 145        # when we want to enable sms check for all.
 146        # until then, it checks if form barely related to sms
 147        if is_sms_related(form_result.get('form_o')):
 148            form_result_sms = check_form_sms_compatibility(form_result)
 149            context.message_list = [form_result, form_result_sms]
 150        else:
 151            context.message = form_result
 152    else:
 153        context.message = form_result
 154    if request.is_ajax():
 155        res = loader.render_to_string(
 156            'message.html',
 157            context_instance=context).replace("'", r"\'").replace('\n', '')
 158        return HttpResponse(
 159            "$('#mfeedback').html('%s').show();" % res)
 160    else:
 161        return HttpResponse(context.message['text'])
 162
 163
 164def profile(request, username):
 165    context = RequestContext(request)
 166    content_user = get_object_or_404(User, username=username)
 167    context.form = QuickConverter()
 168    # xlsform submission...
 169    if request.method == 'POST' and request.user.is_authenticated():
 170        def set_form():
 171            form = QuickConverter(request.POST, request.FILES)
 172            survey = form.publish(request.user).survey
 173            audit = {}
 174            audit_log(
 175                Actions.FORM_PUBLISHED, request.user, content_user,
 176                _("Published form '%(id_string)s'.") %
 177                {
 178                    'id_string': survey.id_string,
 179                }, audit, request)
 180            enketo_webform_url = reverse(
 181                enter_data,
 182                kwargs={'username': username, 'id_string': survey.id_string}
 183            )
 184            return {
 185                'type': 'alert-success',
 186                'preview_url': reverse(enketo_preview, kwargs={
 187                    'username': username,
 188                    'id_string': survey.id_string
 189                }),
 190                'text': _(u'Successfully published %(form_id)s.'
 191                          u' <a href="%(form_url)s">Enter Web Form</a>'
 192                          u' or <a href="#preview-modal" data-toggle="modal">'
 193                          u'Preview Web Form</a>')
 194                % {'form_id': survey.id_string,
 195                    'form_url': enketo_webform_url},
 196                'form_o': survey
 197            }
 198        form_result = publish_form(set_form)
 199        if form_result['type'] == 'alert-success':
 200            # comment the following condition (and else)
 201            # when we want to enable sms check for all.
 202            # until then, it checks if form barely related to sms
 203            if is_sms_related(form_result.get('form_o')):
 204                form_result_sms = check_form_sms_compatibility(form_result)
 205                context.message_list = [form_result, form_result_sms]
 206            else:
 207                context.message = form_result
 208        else:
 209            context.message = form_result
 210
 211    # profile view...
 212    # for the same user -> dashboard
 213    if content_user == request.user:
 214        context.show_dashboard = True
 215        context.all_forms = content_user.xforms.count()
 216        context.form = QuickConverterFile()
 217        context.form_url = QuickConverterURL()
 218        context.odk_url = request.build_absolute_uri(
 219            "/%s" % request.user.username)
 220        xforms = XForm.objects.filter(user=content_user)\
 221            .select_related('user', 'surveys')
 222        context.user_xforms = xforms
 223        crowdforms = XForm.objects.filter(
 224            metadata__data_type=MetaData.CROWDFORM_USERS,
 225            metadata__data_value=username,)\
 226            .select_related('user')
 227        context.crowdforms = crowdforms
 228        # forms shared with user
 229        xfct = ContentType.objects.get(app_label='odk_logger', model='xform')
 230        xfs = content_user.userobjectpermission_set.filter(content_type=xfct)
 231        shared_forms_pks = list(set([xf.object_pk for xf in xfs]))
 232        context.forms_shared_with = XForm.objects.filter(
 233            pk__in=shared_forms_pks).exclude(user=content_user)\
 234            .select_related('user')
 235    # for any other user -> profile
 236    set_profile_data(context, content_user)
 237    return render_to_response("profile.html", context_instance=context)
 238
 239
 240def members_list(request):
 241    if not request.user.is_staff and not request.user.is_superuser:
 242        return HttpResponseForbidden(_(u'Forbidden.'))
 243    context = RequestContext(request)
 244    users = User.objects.all()
 245    context.template = 'people.html'
 246    context.users = users
 247    return render_to_response("people.html", context_instance=context)
 248
 249
 250@login_required
 251def profile_settings(request, username):
 252    context = RequestContext(request)
 253    content_user = check_and_set_user(request, username)
 254    context.content_user = content_user
 255    profile, created = UserProfile.objects.get_or_create(user=content_user)
 256    if request.method == 'POST':
 257        form = UserProfileForm(request.POST, instance=profile)
 258        if form.is_valid():
 259            # get user
 260            # user.email = cleaned_email
 261            form.instance.user.email = form.cleaned_data['email']
 262            form.instance.user.save()
 263            form.save()
 264            # todo: add string rep. of settings to see what changed
 265            audit = {}
 266            audit_log(
 267                Actions.PROFILE_SETTINGS_UPDATED, request.user, content_user,
 268                _("Profile settings updated."), audit, request)
 269            return HttpResponseRedirect(reverse(
 270                public_profile, kwargs={'username': request.user.username}
 271            ))
 272    else:
 273        form = UserProfileForm(
 274            instance=profile, initial={"email": content_user.email})
 275    return render_to_response("settings.html", {'form': form},
 276                              context_instance=context)
 277
 278
 279@require_GET
 280def public_profile(request, username):
 281    content_user = check_and_set_user(request, username)
 282    if isinstance(content_user, HttpResponseRedirect):
 283        return content_user
 284    context = RequestContext(request)
 285    set_profile_data(context, content_user)
 286    context.is_owner = request.user == content_user
 287    audit = {}
 288    audit_log(
 289        Actions.PUBLIC_PROFILE_ACCESSED, request.user, content_user,
 290        _("Public profile accessed."), audit, request)
 291    return render_to_response("profile.html", context_instance=context)
 292
 293
 294@login_required
 295def dashboard(request):
 296    context = RequestContext(request)
 297    context.form = QuickConverter()
 298    content_user = request.user
 299    set_profile_data(context, content_user)
 300    context.odk_url = request.build_absolute_uri("/%s" % request.user.username)
 301    return render_to_response("dashboard.html", context_instance=context)
 302
 303
 304@require_GET
 305def show(request, username=None, id_string=None, uuid=None):
 306    if uuid:
 307        xform = get_object_or_404(XForm, uuid=uuid)
 308        request.session['public_link'] = \
 309            xform.uuid if MetaData.public_link(xform) else False
 310        return HttpResponseRedirect(reverse(show, kwargs={
 311            'username': xform.user.username,
 312            'id_string': xform.id_string
 313        }))
 314    xform, is_owner, can_edit, can_view = get_xform_and_perms(
 315        username, id_string, request)
 316    # no access
 317    if not (xform.shared or can_view or request.session.get('public_link')):
 318        return HttpResponseRedirect(reverse(home))
 319    context = RequestContext(request)
 320    context.cloned = len(
 321        XForm.objects.filter(user__username=request.user.username,
 322                             id_string=id_string + XForm.CLONED_SUFFIX)
 323    ) > 0
 324    context.public_link = MetaData.public_link(xform)
 325    context.is_owner = is_owner
 326    context.can_edit = can_edit
 327    context.can_view = can_view or request.session.get('public_link')
 328    context.xform = xform
 329    context.content_user = xform.user
 330    context.base_url = "https://%s" % request.get_host()
 331    context.source = MetaData.source(xform)
 332    context.form_license = MetaData.form_license(xform).data_value
 333    context.data_license = MetaData.data_license(xform).data_value
 334    context.supporting_docs = MetaData.supporting_docs(xform)
 335    context.media_upload = MetaData.media_upload(xform)
 336    context.mapbox_layer = MetaData.mapbox_layer_upload(xform)
 337    if is_owner:
 338        context.sms_support_form = ActivateSMSSupportFom(
 339            initial={'enable_sms_support': xform.allows_sms,
 340                     'sms_id_string': xform.sms_id_string})
 341        if not xform.allows_sms:
 342            context.sms_compatible = check_form_sms_compatibility(
 343                None, json_survey=json.loads(xform.json))
 344        else:
 345            url_root = request.build_absolute_uri('/')[:-1]
 346            context.sms_providers_doc = providers_doc(
 347                url_root=url_root,
 348                username=username,
 349                id_string=id_string)
 350            context.url_root = url_root
 351        context.form_license_form = FormLicenseForm(
 352            initial={'value': context.form_license})
 353        context.data_license_form = DataLicenseForm(
 354            initial={'value': context.data_license})
 355        context.doc_form = SupportDocForm()
 356        context.source_form = SourceForm()
 357        context.media_form = MediaForm()
 358        context.mapbox_layer_form = MapboxLayerForm()
 359        users_with_perms = []
 360        for perm in get_users_with_perms(xform, attach_perms=True).items():
 361            has_perm = []
 362            if 'change_xform' in perm[1]:
 363                has_perm.append(_(u"Can Edit"))
 364            if 'view_xform' in perm[1]:
 365                has_perm.append(_(u"Can View"))
 366            users_with_perms.append((perm[0], u" | ".join(has_perm)))
 367        context.users_with_perms = users_with_perms
 368        context.permission_form = PermissionForm(username)
 369    if xform.allows_sms:
 370        context.sms_support_doc = get_autodoc_for(xform)
 371    return render_to_response("show.html", context_instance=context)
 372
 373
 374@require_GET
 375def api_token(request, username=None):
 376    user = get_object_or_404(User, username=username)
 377    context = RequestContext(request)
 378    context.token_key, created = Token.objects.get_or_create(user=user)
 379    return render_to_response("api_token.html", context_instance=context)
 380
 381
 382@require_http_methods(["GET", "OPTIONS"])
 383def api(request, username=None, id_string=None):
 384    """
 385    Returns all results as JSON.  If a parameter string is passed,
 386    it takes the 'query' parameter, converts this string to a dictionary, an
 387    that is then used as a MongoDB query string.
 388
 389    NOTE: only a specific set of operators are allow, currently $or and $and.
 390    Please send a request if you'd like another operator to be enabled.
 391
 392    NOTE: Your query must be valid JSON, double check it here,
 393    http://json.parser.online.fr/
 394
 395    E.g. api?query='{"last_name": "Smith"}'
 396    """
 397    if request.method == "OPTIONS":
 398        response = HttpResponse()
 399        add_cors_headers(response)
 400        return response
 401    helper_auth_helper(request)
 402    helper_auth_helper(request)
 403    xform, owner = check_and_set_user_and_form(username, id_string, request)
 404
 405    if not xform:
 406        return HttpResponseForbidden(_(u'Not shared.'))
 407
 408    try:
 409        args = {
 410            'username': username,
 411            'id_string': id_string,
 412            'query': request.GET.get('query'),
 413            'fields': request.GET.get('fields'),
 414            'sort': request.GET.get('sort')
 415        }
 416        if 'start' in request.GET:
 417            args["start"] = int(request.GET.get('start'))
 418        if 'limit' in request.GET:
 419            args["limit"] = int(request.GET.get('limit'))
 420        if 'count' in request.GET:
 421            args["count"] = True if int(request.GET.get('count')) > 0\
 422                else False
 423        cursor = ParsedInstance.query_mongo(**args)
 424    except ValueError, e:
 425        return HttpResponseBadRequest(e.__str__())
 426    records = list(record for record in cursor)
 427    response_text = json.dumps(records)
 428    if 'callback' in request.GET and request.GET.get('callback') != '':
 429        callback = request.GET.get('callback')
 430        response_text = ("%s(%s)" % (callback, response_text))
 431    response = HttpResponse(response_text, mimetype='application/json')
 432    add_cors_headers(response)
 433    return response
 434
 435
 436@require_GET
 437def public_api(request, username, id_string):
 438    """
 439    Returns public information about the form as JSON
 440    """
 441
 442    xform = get_object_or_404(XForm,
 443                              user__username=username, id_string=id_string)
 444
 445    _DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S'
 446    exports = {'username': xform.user.username,
 447               'id_string': xform.id_string,
 448               'bamboo_dataset': xform.bamboo_dataset,
 449               'shared': xform.shared,
 450               'shared_data': xform.shared_data,
 451               'downloadable': xform.downloadable,
 452               'is_crowd_form': xform.is_crowd_form,
 453               'title': xform.title,
 454               'date_created': xform.date_created.strftime(_DATETIME_FORMAT),
 455               'date_modified': xform.date_modified.strftime(_DATETIME_FORMAT),
 456               'uuid': xform.uuid,
 457               }
 458    response_text = json.dumps(exports)
 459    return HttpResponse(response_text, mimetype='application/json')
 460
 461
 462@login_required
 463def edit(request, username, id_string):
 464    xform = XForm.objects.get(user__username=username, id_string=id_string)
 465    owner = xform.user
 466
 467    if request.GET.get('crowdform'):
 468        crowdform_action = request.GET['crowdform']
 469        request_username = request.user.username
 470
 471        # ensure is crowdform
 472        if xform.is_crowd_form:
 473            if crowdform_action == 'delete':
 474                MetaData.objects.get(
 475                    xform__id_string=id_string,
 476                    data_value=request_username,
 477                    data_type=MetaData.CROWDFORM_USERS
 478                ).delete()
 479            elif crowdform_action == 'add':
 480                MetaData.crowdform_users(xform, request_username)
 481
 482            return HttpResponseRedirect(reverse(profile, kwargs={
 483                'username': request_username
 484            }))
 485
 486    if username == request.user.username or\
 487            request.user.has_perm('odk_logger.change_xform', xform):
 488        if request.POST.get('description'):
 489            audit = {
 490                'xform': xform.id_string
 491            }
 492            audit_log(
 493                Actions.FORM_UPDATED, request.user, owner,
 494                _("Description for '%(id_string)s' updated from "
 495                    "'%(old_description)s' to '%(new_description)s'.") %
 496                {
 497                    'id_string': xform.id_string,
 498                    'old_description': xform.description,
 499                    'new_description': request.POST['description']
 500                }, audit, request)
 501            xform.description = request.POST['description']
 502        elif request.POST.get('title'):
 503            audit = {
 504                'xform': xform.id_string
 505            }
 506            audit_log(
 507                Actions.FORM_UPDATED, request.user, owner,
 508                _("Title for '%(id_string)s' updated from "
 509                    "'%(old_title)s' to '%(new_title)s'.") %
 510                {
 511                    'id_string': xform.id_string,
 512                    'old_title': xform.title,
 513                    'new_title': request.POST.get('title')
 514                }, audit, request)
 515            xform.title = request.POST['title']
 516        elif request.POST.get('toggle_shared'):
 517            if request.POST['toggle_shared'] == 'data':
 518                audit = {
 519                    'xform': xform.id_string
 520                }
 521                audit_log(
 522                    Actions.FORM_UPDATED, request.user, owner,
 523                    _("Data sharing updated for '%(id_string)s' from "
 524                        "'%(old_shared)s' to '%(new_shared)s'.") %
 525                    {
 526                        'id_string': xform.id_string,
 527                        'old_shared': _("shared")
 528                        if xform.shared_data else _("not shared"),
 529                        'new_shared': _("shared")
 530                        if not xform.shared_data else _("not shared")
 531                    }, audit, request)
 532                xform.shared_data = not xform.shared_data
 533            elif request.POST['toggle_shared'] == 'form':
 534                audit = {
 535                    'xform': xform.id_string
 536                }
 537                audit_log(
 538                    Actions.FORM_UPDATED, request.user, owner,
 539                    _("Form sharing for '%(id_string)s' updated "
 540                        "from '%(old_shared)s' to '%(new_shared)s'.") %
 541                    {
 542                        'id_string': xform.id_string,
 543                        'old_shared': _("shared")
 544                        if xform.shared else _("not shared"),
 545                        'new_shared': _("shared")
 546                        if not xform.shared else _("not shared")
 547                    }, audit, request)
 548                xform.shared = not xform.shared
 549            elif request.POST['toggle_shared'] == 'active':
 550                audit = {
 551                    'xform': xform.id_string
 552                }
 553                audit_log(
 554                    Actions.FORM_UPDATED, request.user, owner,
 555                    _("Active status for '%(id_string)s' updated from "
 556                        "'%(old_shared)s' to '%(new_shared)s'.") %
 557                    {
 558                        'id_string': xform.id_string,
 559                        'old_shared': _("shared")
 560                        if xform.downloadable else _("not shared"),
 561                        'new_shared': _("shared")
 562                        if not xform.downloadable else _("not shared")
 563                    }, audit, request)
 564                xform.downloadable = not xform.downloadable
 565            elif request.POST['toggle_shared'] == 'crowd':
 566                audit = {
 567                    'xform': xform.id_string
 568                }
 569                audit_log(
 570                    Actions.FORM_UPDATED, request.user, owner,
 571                    _("Crowdform status for '%(id_string)s' updated from "
 572                        "'%(old_status)s' to '%(new_status)s'.") %
 573                    {
 574                        'id_string': xform.id_string,
 575                        'old_status': _("crowdform")
 576                        if not xform.is_crowd_form else _("not crowdform"),
 577                        'new_status': _("crowdform")
 578                        if xform.is_crowd_form else _("not crowdform"),
 579                    }, audit, request)
 580                if xform.is_crowd_form:
 581                    xform.is_crowd_form = False
 582                else:
 583                    xform.is_crowd_form = True
 584                    xform.shared = True
 585                    xform.shared_data = True
 586        elif request.POST.get('form-license'):
 587            audit = {
 588                'xform': xform.id_string
 589            }
 590            audit_log(
 591                Actions.FORM_UPDATED, request.user, owner,
 592                _("Form License for '%(id_string)s' updated to "
 593                    "'%(form_license)s'.") %
 594                {
 595                    'id_string': xform.id_string,
 596                    'form_license': request.POST['form-license'],
 597                }, audit, request)
 598            MetaData.form_license(xform, request.POST['form-license'])
 599        elif request.POST.get('data-license'):
 600            audit = {
 601                'xform': xform.id_string
 602            }
 603            audit_log(
 604                Actions.FORM_UPDATED, request.user, owner,
 605                _("Data license for '%(id_string)s' updated to "
 606                    "'%(data_license)s'.") %
 607                {
 608                    'id_string': xform.id_string,
 609                    'data_license': request.POST['data-license'],
 610                }, audit, request)
 611            MetaData.data_license(xform, request.POST['data-license'])
 612        elif request.POST.get('source') or request.FILES.get('source'):
 613            audit = {
 614                'xform': xform.id_string
 615            }
 616            audit_log(
 617                Actions.FORM_UPDATED, request.user, owner,
 618                _("Source for '%(id_string)s' updated to '%(source)s'.") %
 619                {
 620                    'id_string': xform.id_string,
 621                    'source': request.POST.get('source'),
 622                }, audit, request)
 623            MetaData.source(xform, request.POST.get('source'),
 624                            request.FILES.get('source'))
 625        elif request.POST.get('enable_sms_support_trigger') is not None:
 626            sms_support_form = ActivateSMSSupportFom(request.POST)
 627            if sms_support_form.is_valid():
 628                audit = {
 629                    'xform': xform.id_string
 630                }
 631                enabled = \
 632                    sms_support_form.cleaned_data.get('enable_sms_support')
 633                if enabled:
 634                    audit_action = Actions.SMS_SUPPORT_ACTIVATED
 635                    audit_message = _(u"SMS Support Activated on")
 636                else:
 637                    audit_action = Actions.SMS_SUPPORT_DEACTIVATED
 638                    audit_message = _(u"SMS Support Deactivated on")
 639                audit_log(
 640                    audit_action, request.user, owner,
 641                    audit_message
 642                    % {'id_string': xform.id_string}, audit, request)
 643                # stored previous states to be able to rollback form status
 644                # in case we can't save.
 645                pe = xform.allows_sms
 646                pid = xform.sms_id_string
 647                xform.allows_sms = enabled
 648                xform.sms_id_string = \
 649                    sms_support_form.cleaned_data.get('sms_id_string')
 650                compat = check_form_sms_compatibility(None,
 651                                                      json.loads(xform.json))
 652                if compat['type'] == 'alert-error':
 653                    xform.allows_sms = False
 654                    xform.sms_id_string = pid
 655                try:
 656                    xform.save()
 657                except IntegrityError:
 658                    # unfortunately, there's no feedback mechanism here
 659                    xform.allows_sms = pe
 660                    xform.sms_id_string = pid
 661
 662        elif request.FILES.get('media'):
 663            audit = {
 664                'xform': xform.id_string
 665            }
 666            audit_log(
 667                Actions.FORM_UPDATED, request.user, owner,
 668                _("Media added to '%(id_string)s'.") %
 669                {
 670                    'id_string': xform.id_string
 671                }, audit, request)
 672            for aFile in request.FILES.getlist("media"):
 673                MetaData.media_upload(xform, aFile)
 674        elif request.POST.get('map_name'):
 675            mapbox_layer = MapboxLayerForm(request.POST)
 676            if mapbox_layer.is_valid():
 677                audit = {
 678                    'xform': xform.id_string
 679                }
 680                audit_log(
 681                    Actions.FORM_UPDATED, request.user, owner,
 682                    _("Map layer added to '%(id_string)s'.") %
 683                    {
 684                        'id_string': xform.id_string
 685                    }, audit, request)
 686                MetaData.mapbox_layer_upload(xform, mapbox_layer.cleaned_data)
 687        elif request.FILES:
 688            audit = {
 689                'xform': xform.id_string
 690            }
 691            audit_log(
 692                Actions.FORM_UPDATED, request.user, owner,
 693                _("Supporting document added to '%(id_string)s'.") %
 694                {
 695                    'id_string': xform.id_string
 696                }, audit, request)
 697            MetaData.supporting_docs(xform, request.FILES['doc'])
 698        xform.update()
 699
 700        if request.is_ajax():
 701            return HttpResponse(_(u'Updated succeeded.'))
 702        else:
 703            return HttpResponseRedirect(reverse(show, kwargs={
 704                'username': username,
 705                'id_string': id_string
 706            }))
 707    return HttpResponseForbidden(_(u'Update failed.'))
 708
 709
 710def getting_started(request):
 711    context = RequestContext(request)
 712    context.template = 'getting_started.html'
 713    return render_to_response('base.html', context_instance=context)
 714
 715
 716def support(request):
 717    context = RequestContext(request)
 718    context.template = 'support.html'
 719    return render_to_response('base.html', context_instance=context)
 720
 721
 722def faq(request):
 723    context = RequestContext(request)
 724    context.template = 'faq.html'
 725    return render_to_response('base.html', context_instance=context)
 726
 727
 728def xls2xform(request):
 729    context = RequestContext(request)
 730    context.template = 'xls2xform.html'
 731    return render_to_response('base.html', context_instance=context)
 732
 733
 734def tutorial(request):
 735    context = RequestContext(request)
 736    context.template = 'tutorial.html'
 737    username = request.user.username if request.user.username else \
 738        'your-user-name'
 739    context.odk_url = request.build_absolute_uri("/%s" % username)
 740    return render_to_response('base.html', context_instance=context)
 741
 742
 743def resources(request):
 744    context = RequestContext(request)
 745    if 'fr' in request.LANGUAGE_CODE.lower():
 746        context.deck_id = 'a351f6b0a3730130c98b12e3c5740641'
 747    else:
 748        context.deck_id = '1a33a070416b01307b8022000a1de118'
 749    return render_to_response('resources.html', context_instance=context)
 750
 751
 752def about_us(request):
 753    context = RequestContext(request)
 754    context.a_flatpage = '/about-us/'
 755    username = request.user.username if request.user.username else \
 756        'your-user-name'
 757    context.odk_url = request.build_absolute_uri("/%s" % username)
 758    return render_to_response('base.html', context_instance=context)
 759
 760
 761def syntax(request):
 762    if 'fr' in request.LANGUAGE_CODE.lower():
 763        doc_id = '1EhJTsqX3noztyW-UdKRBABhIln6R3TAvXv58DTZWCU4'
 764    else:
 765        doc_id = '1xD5gSjeyjGjw-V9g5hXx7FWeasRvn-L6zeQJsNeAGBI'
 766    url = 'https://docs.google.com/document/pub?id=%s' % doc_id
 767    doc = GoogleDoc(url)
 768    context = RequestContext(request)
 769    context.content = doc.to_html()
 770    return render_to_response('base.html', context_instance=context)
 771
 772
 773def form_gallery(request):
 774    """
 775    Return a list of urls for all the shared xls files. This could be
 776    made a lot prettier.
 777    """
 778    context = RequestContext(request)
 779    if request.user.is_authenticated():
 780        context.loggedin_user = request.user
 781    context.shared_forms = XForm.objects.filter(shared=True)
 782    # build list of shared forms with cloned suffix
 783    id_strings_with_cloned_suffix = [
 784        x.id_string + XForm.CLONED_SUFFIX for x in context.shared_forms
 785    ]
 786    # build list of id_strings for forms this user has cloned
 787    context.cloned = [
 788        x.id_string.split(XForm.CLONED_SUFFIX)[0]
 789        for x in XForm.objects.filter(
 790            user__username=request.user.username,
 791            id_string__in=id_strings_with_cloned_suffix
 792        )
 793    ]
 794    return render_to_response('form_gallery.html', context_instance=context)
 795
 796
 797def download_metadata(request, username, id_string, data_id):
 798    xform = get_object_or_404(XForm,
 799                              user__username=username, id_string=id_string)
 800    owner = xform.user
 801    if username == request.user.username or xform.shared:
 802        data = get_object_or_404(MetaData, pk=data_id)
 803        file_path = data.data_file.name
 804        filename, extension = os.path.splitext(file_path.split('/')[-1])
 805        extension = extension.strip('.')
 806        dfs = get_storage_class()()
 807        if dfs.exists(file_path):
 808            audit = {
 809                'xform': xform.id_string
 810            }
 811            audit_log(
 812                Actions.FORM_UPDATED, request.user, owner,
 813                _("Document '%(filename)s' for '%(id_string)s' downloaded.") %
 814                {
 815                    'id_string': xform.id_string,
 816                    'filename': "%s.%s" % (filename, extension)
 817                }, audit, request)
 818            response = response_with_mimetype_and_name(
 819                data.data_file_type,
 820                filename, extension=extension, show_date=False,
 821                file_path=file_path)
 822            return response
 823        else:
 824            return HttpResponseNotFound()
 825    return HttpResponseForbidden(_(u'Permission denied.'))
 826
 827
 828@login_required()
 829def delete_metadata(request, username, id_string, data_id):
 830    xform = get_object_or_404(XForm,
 831                              user__username=username, id_string=id_string)
 832    owner = xform.user
 833    data = get_object_or_404(MetaData, pk=data_id)
 834    dfs = get_storage_class()()
 835    req_username = request.user.username
 836    if request.GET.get('del', False) and username == req_username:
 837        try:
 838            dfs.delete(data.data_file.name)
 839            data.delete()
 840            audit = {
 841                'xform': xform.id_string
 842            }
 843            audit_log(
 844                Actions.FORM_UPDATED, request.user, owner,
 845                _("Document '%(filename)s' deleted from '%(id_string)s'.") %
 846                {
 847                    'id_string': xform.id_string,
 848                    'filename': os.path.basename(data.data_file.name)
 849                }, audit, request)
 850            return HttpResponseRedirect(reverse(show, kwargs={
 851                'username': username,
 852                'id_string': id_string
 853            }))
 854        except Exception:
 855            return HttpResponseServerError()
 856    elif request.GET.get('map_name_del', False) and username == req_username:
 857        data.delete()
 858        audit = {
 859            'xform': xform.id_string
 860        }
 861        audit_log(
 862            Actions.FORM_UPDATED, request.user, owner,
 863            _("Map layer deleted from '%(id_string)s'.") %
 864            {
 865                'id_string': xform.id_string,
 866            }, audit, request)
 867        return HttpResponseRedirect(reverse(show, kwargs={
 868            'username': username,
 869            'id_string': id_string
 870        }))
 871    return HttpResponseForbidden(_(u'Permission denied.'))
 872
 873
 874def download_media_data(request, username, id_string, data_id):
 875    xform = get_object_or_404(
 876        XForm, user__username=username, id_string=id_string)
 877    owner = xform.user
 878    data = get_object_or_404(MetaData, id=data_id)
 879    dfs = get_storage_class()()
 880    if request.GET.get('del', False):
 881        if username == request.user.username:
 882            try:
 883                dfs.delete(data.data_file.name)
 884                data.delete()
 885                audit = {
 886                    'xform': xform.id_string
 887                }
 888                audit_log(
 889                    Actions.FORM_UPDATED, request.user, owner,
 890                    _("Media download '%(filename)s' deleted from "
 891                        "'%(id_string)s'.") %
 892                    {
 893                        'id_string': xform.id_string,
 894                        'filename': os.path.basename(data.data_file.name)
 895                    }, audit, request)
 896                return HttpResponseRedirect(reverse(show, kwargs={
 897                    'username': username,
 898                    'id_string': id_string
 899                }))
 900            except Exception:
 901                return HttpResponseServerError()
 902    else:
 903        if username:  # == request.user.username or xform.shared:
 904            file_path = data.data_file.name
 905            filename, extension = os.path.splitext(file_path.split('/')[-1])
 906            extension = extension.strip('.')
 907            if dfs.exists(file_path):
 908                audit = {
 909                    'xform': xform.id_string
 910                }
 911                audit_log(
 912                    Actions.FORM_UPDATED, request.user, owner,
 913                    _("Media '%(filename)s' downloaded from "
 914                        "'%(id_string)s'.") %
 915                    {
 916                        'id_string': xform.id_string,
 917                        'filename': os.path.basename(file_path)
 918                    }, audit, request)
 919                response = response_with_mimetype_and_name(
 920                    data.data_file_type,
 921                    filename, extension=extension, show_date=False,
 922                    file_path=file_path)
 923                return response
 924            else:
 925                return HttpResponseNotFound()
 926    return HttpResponseForbidden(_(u'Permission denied.'))
 927
 928
 929def form_photos(request, username, id_string):
 930    xform, owner = check_and_set_user_and_form(username, id_string, request)
 931
 932    if not xform:
 933        return HttpResponseForbidden(_(u'Not shared.'))
 934
 935    context = RequestContext(request)
 936    context.form_view = True
 937    context.content_user = owner
 938    context.xform = xform
 939    image_urls = []
 940
 941    for instance in xform.surveys.all():
 942        for attachment in instance.attachments.all():
 943            # skip if not image e.g video or file
 944            if not attachment.mimetype.startswith('image'):
 945                continue
 946
 947            data = {}
 948
 949            for i in ['small', 'medium', 'large', 'original']:
 950                url = reverse(attachment_url, kwargs={'size': i})
 951                url = '%s?media_file=%s' % (url, attachment.media_file.name)
 952                data[i] = url
 953
 954            image_urls.append(data)
 955
 956    context.images = image_urls
 957    context.profile, created = UserProfile.objects.get_or_create(user=owner)
 958    return render_to_response('form_photos.html', context_instance=context)
 959
 960
 961@require_POST
 962def set_perm(request, username, id_string):
 963    xform = get_object_or_404(XForm,
 964                              user__username=username, id_string=id_string)
 965    owner = xform.user
 966    if username != request.user.username\
 967            and not has_permission(xform, username, request):
 968        return HttpResponseForbidden(_(u'Permission denied.'))
 969    try:
 970        perm_type = request.POST['perm_type']
 971        for_user = request.POST['for_user']
 972    except KeyError:
 973        return HttpResponseBadRequest()
 974    if perm_type in ['edit', 'view', 'remove']:
 975        try:
 976            user = User.objects.get(username=for_user)
 977        except User.DoesNotExist:
 978            messages.add_message(
 979                request, messages.INFO,
 980                _(u"Wrong username <b>%s</b>." % for_user),
 981                extra_tags='alert-error')
 982        else:
 983            if perm_type == 'edit' and\
 984                    not user.has_perm('change_xform', xform):
 985                audit = {
 986                    'xform': xform.id_string
 987                }
 988                audit_log(
 989                    Actions.FORM_PERMISSIONS_UPDATED, request.user, owner,
 990                    _("Edit permissions on '%(id_string)s' assigned to "
 991                        "'%(for_user)s'.") %
 992                    {
 993                        'id_string': xform.id_string,
 994                        'for_user': for_user
 995                    }, audit, request)
 996                assign_perm('change_xform', user, xform)
 997            elif perm_type == 'view' and\
 998                    not user.has_perm('view_xform', xform):
 999                audit = {
1000                    'xform': xform.id_string
1001                }
1002                audit_log(
1003                    Actions.FORM_PERMISSIONS_UPDATED, request.user, owner,
1004                    _("View permissions on '%(id_string)s' "
1005                        "assigned to '%(for_user)s'.") %
1006                    {
1007                        'id_string': xform.id_string,
1008                        'for_user': for_user
1009                    }, audit, request)
1010                assign_perm('view_xform', user, xform)
1011            elif perm_type == 'remove':
1012                audit = {
1013                    'xform': xform.id_string
1014                }
1015                audit_log(
1016                    Actions.FORM_PERMISSIONS_UPDATED, request.user, owner,
1017                    _("All permissions on '%(id_string)s' "
1018                        "removed from '%(for_user)s'.") %
1019                    {
1020                        'id_string': xform.id_string,
1021                        'for_user': for_user
1022                    }, audit, request)
1023                remove_perm('change_xform', user, xform)
1024                remove_perm('view_xform', user, xform)
1025    elif perm_type == 'link':
1026        current = MetaData.public_link(xform)
1027        if for_user == 'all':
1028            MetaData.public_link(xform, True)
1029        elif for_user == 'none':
1030            MetaData.public_link(xform, False)
1031        elif for_user == 'toggle':
1032            MetaData.public_link(xform, not current)
1033        audit = {
1034            'xform': xform.id_string
1035        }
1036        audit_log(
1037            Actions.FORM_PERMISSIONS_UPDATED, request.user, owner,
1038            _("Public link on '%(id_string)s' %(action)s.") %
1039            {
1040                'id_string': xform.id_string,
1041                'action': "created"
1042                if for_user == "all" or
1043                (for_user == "toggle" and not current) else "removed"
1044            }, audit, request)
1045    if request.is_ajax():
1046        return HttpResponse(
1047            json.dumps(
1048                {'status': 'success'}), mimetype='application/json')
1049    return HttpResponseRedirect(reverse(show, kwargs={
1050        'username': username,
1051        'id_string': id_string
1052    }))
1053
1054
1055def show_submission(request, username, id_string, uuid):
1056    xform, is_owner, can_edit, can_view = get_xform_and_perms(
1057        username, id_string, request)
1058    owner = xform.user
1059    # no access
1060    if not (xform.shared_data or can_view or
1061            request.session.get('public_link') == xform.uuid):
1062        return HttpResponseRedirect(reverse(home))
1063    submission = get_object_or_404(Instance, uuid=uuid)
1064    audit = {
1065        'xform': xform.id_string
1066    }
1067    audit_log(
1068        Actions.SUBMISSION_ACCESSED, request.user, owner,
1069        _("Submission '%(uuid)s' on '%(id_string)s' accessed.") %
1070        {
1071            'id_string': xform.id_string,
1072            'uuid': uuid
1073        }, audit, request)
1074    return HttpResponseRedirect(reverse(
1075        survey_responses, kwargs={'instance_id': submission.pk}))
1076
1077
1078@require_POST
1079@login_required
1080def delete_data(request, username=None, id_string=None):
1081    xform, owner = check_and_set_user_and_form(username, id_string, request)
1082    response_text = u''
1083    if not xform:
1084        return HttpResponseForbidden(_(u'Not shared.'))
1085
1086    data_id = request.POST.get('id')
1087    if not data_id:
1088        return HttpResponseBadRequest(_(u"id must be specified"))
1089
1090    Instance.set_deleted_at(data_id)
1091    audit = {
1092        'xform': xform.id_string
1093    }
1094    audit_log(
1095        Actions.SUBMISSION_DELETED, request.user, owner,
1096        _("Deleted submission with id '%(record_id)s' "
1097            "on '%(id_string)s'.") %
1098        {
1099            'id_string': xform.id_string,
1100            'record_id': data_id
1101        }, audit, request)
1102    response_text = json.dumps({"success": "Deleted data %s" % data_id})
1103    if 'callback' in request.GET and request.GET.get('callback') != '':
1104        callback = request.GET.get('callback')
1105        response_text = ("%s(%s)" % (callback, response_text))
1106    return HttpResponse(response_text, mimetype='application/json')
1107
1108
1109@require_POST
1110@is_owner
1111def link_to_bamboo(request, username, id_string):
1112    xform = get_object_or_404(XForm,
1113                              user__username=username, id_string=id_string)
1114    owner = xform.user
1115    from utils.bamboo import (get_new_bamboo_dataset,
1116                              delete_bamboo_dataset, ensure_rest_service)
1117
1118    audit = {
1119        'xform': xform.id_string
1120    }
1121
1122    # try to delete the dataset first (in case it exists)
1123    if xform.bamboo_dataset and delete_bamboo_dataset(xform):
1124        xform.bamboo_dataset = u''
1125        xform.save()
1126        audit_log(
1127            Actions.BAMBOO_LINK_DELETED, request.user, owner,
1128            _("Bamboo link deleted on '%(id_string)s'.")
1129            % {'id_string': xform.id_string}, audit, request)
1130
1131    # create a new one from all the data
1132    dataset_id = get_new_bamboo_dataset(xform)
1133
1134    # update XForm
1135    xform.bamboo_dataset = dataset_id
1136    xform.save()
1137    ensure_rest_service(xform)
1138
1139    audit_log(
1140        Actions.BAMBOO_LINK_CREATED, request.user, owner,
1141        _("Bamboo link created on '%(id_string)s'.") %
1142        {
1143            'id_string': xform.id_string,
1144        }, audit, request)
1145
1146    return HttpResponseRedirect(reverse(show, kwargs={
1147        'username': username,
1148        'id_string': id_string
1149    }))
1150
1151
1152@require_POST
1153@is_owner
1154def update_xform(request, username, id_string):
1155    xform = get_object_or_404(
1156        XForm, user__username=username, id_string=id_string)
1157    owner = xform.user
1158
1159    def set_form():
1160        form = QuickConverter(request.POST, request.FILES)
1161        survey = form.publish(request.user, id_string).survey
1162        enketo_webform_url = reverse(
1163            enter_data,
1164            kwargs={'username': username, 'id_string': survey.id_string}
1165        )
1166        audit = {
1167            'xform': xform.id_string
1168        }
1169        audit_log(
1170            Actions.FORM_XLS_UPDATED, request.user, owner,
1171            _("XLS for '%(id_string)s' updated.") %
1172            {
1173                'id_string': xform.id_string,
1174            }, audit, request)
1175        return {
1176            'type': 'alert-success',
1177            'text': _(u'Successfully published %(form_id)s.'
1178                      u' <a href="%(form_url)s">Enter Web Form</a>'
1179                      u' or <a href="#preview-modal" data-toggle="modal">'
1180                      u'Preview Web Form</a>')
1181                    % {'form_id': survey.id_string,
1182                       'form_url': enketo_webform_url}
1183        }
1184    message = publish_form(set_form)
1185    messages.add_message(
1186        request, messages.INFO, message['text'], extra_tags=message['type'])
1187    return HttpResponseRedirect(reverse(show, kwargs={
1188        'username': username,
1189        'id_string': id_string
1190    }))
1191
1192
1193@is_owner
1194def activity(request, username):
1195    owner = get_object_or_404(User, username=username)
1196    context = RequestContext(request)
1197    context.user = owner
1198    return render_to_response('activity.html', context_instance=context)
1199
1200
1201def activity_fields(request):
1202    fields = [
1203        {
1204            'id': 'created_on',
1205            'label': _('Performed On'),
1206            'type': 'datetime',
1207            'searchable': False
1208        },
1209        {
1210            'id': 'action',
1211            'label': _('Action'),
1212            'type': 'string',
1213            'searchable': True,
1214            'options': sorted([Actions[e] for e in Actions.enums])
1215        },
1216        {
1217            'id': 'user',
1218            'label': 'Performed By',
1219            'type': 'string',
1220            'searchable': True
1221        },
1222        {
1223            'id': 'msg',
1224            'label': 'Description',
1225            'type': 'string',
1226            'searchable': True
1227        },
1228    ]
1229    response_text = json.dumps(fields)
1230    return HttpResponse(response_text, mimetype='application/json')
1231
1232
1233@is_owner
1234def activity_api(request, username):
1235    from bson.objectid import ObjectId
1236
1237    def stringify_unknowns(obj):
1238        if isinstance(obj, ObjectId):
1239            return str(obj)
1240        if isinstance(obj, datetime):
1241            return obj.strftime(DATETIME_FORMAT)
1242        #raise TypeError
1243        return None
1244    try:
1245        query_args = {
1246            'username': username,
1247            'query': json.loads(request.GET.get('query'))
1248            if request.GET.get('query') else {},
1249            'fields': json.loads(request.GET.get('fields'))
1250            if request.GET.get('fields') else [],
1251            'sort': json.loads(request.GET.get('sort'))
1252            if request.GET.get('sort') else {}
1253        }
1254        if 'start' in request.GET:
1255            query_args["start"] = int(request.GET.get('start'))
1256        if 'limit' in request.GET:
1257            query_args["limit"] = int(request.GET.get('limit'))
1258        if 'count' in request.GET:
1259            query_args["count"] = True \
1260                if int(request.GET.get('count')) > 0 else False
1261        cursor = AuditLog.query_mongo(**query_args)
1262    except ValueError, e:
1263        return HttpResponseBadRequest(e.__str__())
1264    records = list(record for record in cursor)
1265    response_text = json.dumps(records, default=stringify_unknowns)
1266    if 'callback' in request.GET and request.GET.get('callback') != '':
1267        callback = request.GET.get('callback')
1268        response_text = ("%s(%s)" % (callback, response_text))
1269    return HttpResponse(response_text, mimetype='application/json')
1270
1271
1272def qrcode(request, username, id_string):
1273    try:
1274        formhub_url = "http://%s/" % request.META['HTTP_HOST']
1275    except:
1276        formhub_url = "http://formhub.org/"
1277    formhub_url = formhub_url + username
1278    if settings.TESTING_MODE:
1279        formhub_url = "https://testserver.com/bob"
1280    results = _(u"Unexpected Error occured: No QRCODE generated")
1281    status = 200
1282    try:
1283        url = enketo_url(formhub_url, id_string)
1284    except Exception, e:
1285        error_msg = _(u"Error Generating QRCODE: %s" % e)
1286        results = """<div class="alert alert-error">%s</div>""" % error_msg
1287        status = 400
1288    else:
1289        if url:
1290            image = generate_qrcode(url)
1291            results = """<img class="qrcode" src="%s" alt="%s" />
1292                    </br><a href="%s" target="_blank">%s</a>""" \
1293                % (image, url, url, url)
1294        else:
1295            status = 400
1296    return HttpResponse(results, mimetype='text/html', status=status)
1297
1298
1299def enketo_preview(request, username, id_string):
1300    xform = get_object_or_404(
1301        XForm, user__username=username, id_string=id_string)
1302    owner = xform.user
1303    if not has_permission(xform, owner, request, xform.shared):
1304        return HttpResponseForbidden(_(u'Not shared.'))
1305    enekto_preview_url = \
1306        "%(enketo_url)s?server=%(profile_url)s&id=%(id_string)s" % {
1307            'enketo_url': settings.ENKETO_PREVIEW_URL,
1308            'profile_url': request.build_absolute_uri(
1309                reverse(profile, kwargs={'username': owner.username})),
1310            'id_string': xform.id_string
1311        }
1312    return HttpResponseRedirect(enekto_preview_url)
1313
1314
1315@require_GET
1316@login_required
1317def username_list(request):
1318    data = []
1319    query = request.GET.get('query', None)
1320    if query:
1321        users = User.objects.values('username')\
1322            .filter(username__startswith=query, is_active=True, pk__gte=0)
1323        data = [user['username'] for user in users]
1324    return HttpResponse(json.dumps(data), mimetype='application/json')