PageRenderTime 46ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/main/views.py

https://github.com/zoulema/formhub
Python | 1324 lines | 1261 code | 39 blank | 24 comment | 75 complexity | 6c3ab82d8e66a38e10a958a8a507b6df MD5 | raw file
Possible License(s): BSD-3-Clause

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

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

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