PageRenderTime 5ms CodeModel.GetById 3ms app.highlight 113ms 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

Large files files are truncated, but you can click here to view the full 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   

Large files files are truncated, but you can click here to view the full file