views.py 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. import mimetypes
  2. import os
  3. from itertools import chain
  4. from datetime import datetime
  5. from django.http import Http404, HttpResponse, HttpResponsePermanentRedirect, JsonResponse
  6. from django.contrib.auth.decorators import login_required
  7. from django.contrib.contenttypes.models import ContentType
  8. from django.core.paginator import Paginator, InvalidPage, EmptyPage, PageNotAnInteger
  9. from django.shortcuts import redirect, render
  10. from django.utils.translation import ungettext, gettext_lazy as _
  11. from icalendar import Calendar
  12. from wagtail.admin import messages
  13. from wagtail.search.backends import db, get_search_backend
  14. from wagtail.search.models import Query
  15. from coderedcms import utils
  16. from coderedcms.forms import SearchForm
  17. from coderedcms.models import (
  18. CoderedPage,
  19. CoderedEventPage,
  20. get_page_models,
  21. GeneralSettings,
  22. LayoutSettings
  23. )
  24. from coderedcms.importexport import convert_csv_to_json, import_pages, ImportPagesFromCSVFileForm
  25. from coderedcms.settings import cr_settings
  26. def search(request):
  27. """
  28. Searches pages across the entire site.
  29. """
  30. search_form = SearchForm(request.GET)
  31. pagetypes = []
  32. results = None
  33. results_paginated = None
  34. if search_form.is_valid():
  35. search_query = search_form.cleaned_data['s']
  36. search_model = search_form.cleaned_data['t']
  37. # get all codered models
  38. pagemodels = sorted(get_page_models(), key=lambda k: k.search_name)
  39. # get filterable models
  40. for model in pagemodels:
  41. if model.search_filterable:
  42. pagetypes.append(model)
  43. # get backend
  44. backend = get_search_backend()
  45. # DB search. Since this backend can't handle inheritance or scoring,
  46. # search specified page types in the desired order and chain the results together.
  47. # This provides better search results than simply searching limited fields on CoderedPage.
  48. db_models = []
  49. if backend.__class__ == db.SearchBackend:
  50. for model in get_page_models():
  51. if model.search_db_include:
  52. db_models.append(model)
  53. db_models = sorted(db_models, reverse=True, key=lambda k: k.search_db_boost)
  54. if backend.__class__ == db.SearchBackend and db_models:
  55. for model in db_models:
  56. # if search_model is provided, only search on that model
  57. if not search_model or search_model == ContentType.objects.get_for_model(model).model: # noqa
  58. curr_results = model.objects.live().search(search_query)
  59. if results:
  60. results = list(chain(results, curr_results))
  61. else:
  62. results = curr_results
  63. # Fallback for any other search backend
  64. else:
  65. if search_model:
  66. try:
  67. model = ContentType.objects.get(model=search_model).model_class()
  68. results = model.objects.live().search(search_query)
  69. except search_model.DoesNotExist:
  70. results = None
  71. else:
  72. results = CoderedPage.objects.live().order_by('-last_published_at').search(search_query) # noqa
  73. # paginate results
  74. if results:
  75. paginator = Paginator(results, GeneralSettings.for_site(
  76. request.site).search_num_results)
  77. page = request.GET.get('p', 1)
  78. try:
  79. results_paginated = paginator.page(page)
  80. except PageNotAnInteger:
  81. results_paginated = paginator.page(1)
  82. except EmptyPage:
  83. results_paginated = paginator.page(1)
  84. except InvalidPage:
  85. results_paginated = paginator.page(1)
  86. # Log the query so Wagtail can suggest promoted results
  87. Query.get(search_query).add_hit()
  88. # Render template
  89. return render(request, 'coderedcms/pages/search.html', {
  90. 'request': request,
  91. 'pagetypes': pagetypes,
  92. 'form': search_form,
  93. 'results': results,
  94. 'results_paginated': results_paginated
  95. })
  96. @login_required
  97. def serve_protected_file(request, path):
  98. """
  99. Function that serves protected files uploaded from forms.
  100. """
  101. fullpath = os.path.join(cr_settings['PROTECTED_MEDIA_ROOT'], path)
  102. if os.path.isfile(fullpath):
  103. mimetype, encoding = mimetypes.guess_type(fullpath)
  104. with open(fullpath, 'rb') as f:
  105. response = HttpResponse(f.read(), content_type=mimetype)
  106. if encoding:
  107. response["Content-Encoding"] = encoding
  108. return response
  109. raise Http404()
  110. def favicon(request):
  111. icon = LayoutSettings.for_site(request.site).favicon
  112. if icon:
  113. return HttpResponsePermanentRedirect(icon.get_rendition('original').url)
  114. raise Http404()
  115. def robots(request):
  116. return render(
  117. request,
  118. 'robots.txt',
  119. content_type='text/plain'
  120. )
  121. def event_generate_single_ical_for_event(request):
  122. if request.method == "POST":
  123. event_pk = request.POST['event_pk']
  124. event_page_models = CoderedEventPage.__subclasses__()
  125. dt_start_str = utils.fix_ical_datetime_format(request.POST['datetime_start'])
  126. dt_end_str = utils.fix_ical_datetime_format(request.POST['datetime_end'])
  127. dt_start = datetime.strptime(dt_start_str, "%Y-%m-%dT%H:%M:%S%z") if dt_start_str else None
  128. dt_end = datetime.strptime(dt_end_str, "%Y-%m-%dT%H:%M:%S%z") if dt_end_str else None
  129. for event_page_model in event_page_models:
  130. try:
  131. event = event_page_model.objects.get(pk=event_pk)
  132. break
  133. except event_page_model.DoesNotExist:
  134. pass
  135. ical = Calendar()
  136. ical.add_component(event.create_single_ical(dt_start=dt_start, dt_end=dt_end))
  137. response = HttpResponse(ical.to_ical(), content_type="text/calendar")
  138. response['Filename'] = "{0}.ics".format(event.slug)
  139. response['Content-Disposition'] = 'attachment; filename={0}.ics'.format(event.slug)
  140. return response
  141. raise Http404()
  142. def event_generate_recurring_ical_for_event(request):
  143. if request.method == "POST":
  144. event_pk = request.POST['event_pk']
  145. event_page_models = CoderedEventPage.__subclasses__()
  146. for event_page_model in event_page_models:
  147. try:
  148. event = event_page_model.objects.get(pk=event_pk)
  149. break
  150. except event_page_model.DoesNotExist:
  151. pass
  152. ical = Calendar()
  153. for e in event.create_recurring_ical():
  154. ical.add_component(e)
  155. response = HttpResponse(ical.to_ical(), content_type="text/calendar")
  156. response['Filename'] = "{0}.ics".format(event.slug)
  157. response['Content-Disposition'] = 'attachment; filename={0}.ics'.format(event.slug)
  158. return response
  159. raise Http404()
  160. def event_generate_ical_for_calendar(request):
  161. if request.method == "POST":
  162. try:
  163. page = CoderedPage.objects.get(id=request.POST.get('page_id')).specific
  164. print(page)
  165. except ValueError:
  166. raise Http404
  167. ical = Calendar()
  168. for event_page in page.get_index_children():
  169. for e in event_page.specific.create_recurring_ical():
  170. ical.add_component(e)
  171. response = HttpResponse(ical.to_ical(), content_type="text/calendar")
  172. response['Filename'] = "calendar.ics"
  173. response['Content-Disposition'] = 'attachment; filename=calendar.ics'
  174. return response
  175. raise Http404()
  176. def event_get_calendar_events(request):
  177. if request.is_ajax():
  178. try:
  179. page = CoderedPage.objects.get(id=request.GET.get('pid')).specific
  180. except ValueError:
  181. raise Http404
  182. start_str = request.GET.get('start')
  183. start = datetime.strptime(start_str[:10], "%Y-%m-%d") if start_str else None
  184. end_str = request.GET.get('end')
  185. end = datetime.strptime(end_str[:10], "%Y-%m-%d") if end_str else None
  186. return JsonResponse(page.get_calendar_events(start=start, end=end), safe=False)
  187. raise Http404()
  188. @login_required
  189. def import_pages_from_csv_file(request):
  190. """
  191. Overwrite of the `import_pages` view from wagtailimportexport. By default, the `import_pages`
  192. view expects a json file to be uploaded. This view converts the uploaded csv into the json
  193. format that the importer expects.
  194. """
  195. if request.method == 'POST':
  196. form = ImportPagesFromCSVFileForm(request.POST, request.FILES)
  197. if form.is_valid():
  198. import_data = convert_csv_to_json(
  199. form.cleaned_data['file'].read().decode('utf-8').splitlines(),
  200. form.cleaned_data['page_type']
  201. )
  202. parent_page = form.cleaned_data['parent_page']
  203. try:
  204. page_count = import_pages(import_data, parent_page)
  205. except LookupError as e:
  206. messages.error(request, _(
  207. "Import failed: %(reason)s") % {'reason': e}
  208. )
  209. else:
  210. messages.success(request, ungettext(
  211. "%(count)s page imported.",
  212. "%(count)s pages imported.",
  213. page_count) % {'count': page_count}
  214. )
  215. return redirect('wagtailadmin_explore', parent_page.pk)
  216. else:
  217. form = ImportPagesFromCSVFileForm()
  218. return render(request, 'wagtailimportexport/import_from_csv.html', {
  219. 'form': form,
  220. })