views.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. import inspect
  2. import os
  3. import warnings
  4. from datetime import datetime
  5. from django.conf import settings
  6. from django.http import HttpResponse, JsonResponse
  7. from django.shortcuts import render
  8. from django.utils.html import escape
  9. from django.utils.module_loading import import_string
  10. from django.views import generic
  11. from django.views.decorators.csrf import csrf_exempt
  12. from ckeditor_uploader import utils
  13. from ckeditor_uploader.backends import get_backend
  14. from ckeditor_uploader.forms import SearchForm
  15. from ckeditor_uploader.utils import storage
  16. from .utils import is_valid_image_extension
  17. def _get_user_path(user):
  18. user_path = ""
  19. # If CKEDITOR_RESTRICT_BY_USER is True upload file to user specific path.
  20. RESTRICT_BY_USER = getattr(settings, "CKEDITOR_RESTRICT_BY_USER", False)
  21. if RESTRICT_BY_USER:
  22. try:
  23. user_prop = getattr(user, RESTRICT_BY_USER)
  24. except (AttributeError, TypeError):
  25. user_prop = getattr(user, "get_username")
  26. if callable(user_prop):
  27. user_path = user_prop()
  28. else:
  29. user_path = user_prop
  30. return str(user_path)
  31. def get_upload_filename(upload_name, request):
  32. user_path = _get_user_path(request.user)
  33. # Generate date based path to put uploaded file.
  34. # If CKEDITOR_RESTRICT_BY_DATE is True upload file to date specific path.
  35. if getattr(settings, "CKEDITOR_RESTRICT_BY_DATE", True):
  36. date_path = datetime.now().strftime("%Y/%m/%d")
  37. else:
  38. date_path = ""
  39. # Complete upload path (upload_path + date_path).
  40. upload_path = os.path.join(settings.CKEDITOR_UPLOAD_PATH, user_path, date_path)
  41. if getattr(settings, "CKEDITOR_UPLOAD_SLUGIFY_FILENAME", True) and not hasattr(
  42. settings, "CKEDITOR_FILENAME_GENERATOR"
  43. ):
  44. upload_name = utils.slugify_filename(upload_name)
  45. if hasattr(settings, "CKEDITOR_FILENAME_GENERATOR"):
  46. generator = import_string(settings.CKEDITOR_FILENAME_GENERATOR)
  47. # Does the generator accept a request argument?
  48. try:
  49. inspect.signature(generator).bind(upload_name, request)
  50. except TypeError:
  51. # Does the generator accept only an upload_name argument?
  52. try:
  53. inspect.signature(generator).bind(upload_name)
  54. except TypeError:
  55. warnings.warn(
  56. "Update %s() to accept the arguments `filename, request`."
  57. % settings.CKEDITOR_FILENAME_GENERATOR
  58. )
  59. else:
  60. warnings.warn(
  61. "Update %s() to accept a second `request` argument."
  62. % settings.CKEDITOR_FILENAME_GENERATOR,
  63. PendingDeprecationWarning,
  64. )
  65. upload_name = generator(upload_name)
  66. else:
  67. upload_name = generator(upload_name, request)
  68. return storage.get_available_name(os.path.join(upload_path, upload_name))
  69. class ImageUploadView(generic.View):
  70. http_method_names = ["post"]
  71. def post(self, request, **kwargs):
  72. """
  73. Uploads a file and send back its URL to CKEditor.
  74. """
  75. uploaded_file = request.FILES["upload"]
  76. backend = get_backend()
  77. ck_func_num = request.GET.get("CKEditorFuncNum")
  78. if ck_func_num:
  79. ck_func_num = escape(ck_func_num)
  80. filewrapper = backend(storage, uploaded_file)
  81. allow_nonimages = getattr(settings, "CKEDITOR_ALLOW_NONIMAGE_FILES", True)
  82. # Throws an error when an non-image file are uploaded.
  83. if not filewrapper.is_image and not allow_nonimages:
  84. return HttpResponse(
  85. """
  86. <script type='text/javascript'>
  87. window.parent.CKEDITOR.tools.callFunction({}, '', 'Invalid file type.');
  88. </script>""".format(
  89. ck_func_num
  90. )
  91. )
  92. filepath = get_upload_filename(uploaded_file.name, request)
  93. saved_path = filewrapper.save_as(filepath)
  94. url = utils.get_media_url(saved_path)
  95. if ck_func_num:
  96. # Respond with Javascript sending ckeditor upload url.
  97. return HttpResponse(
  98. """
  99. <script type='text/javascript'>
  100. window.parent.CKEDITOR.tools.callFunction({}, '{}');
  101. </script>""".format(
  102. ck_func_num, url
  103. )
  104. )
  105. else:
  106. _, filename = os.path.split(saved_path)
  107. retdata = {"url": url, "uploaded": "1", "fileName": filename}
  108. return JsonResponse(retdata)
  109. upload = csrf_exempt(ImageUploadView.as_view())
  110. def get_image_files(user=None, path=""):
  111. """
  112. Recursively walks all dirs under upload dir and generates a list of
  113. full paths for each file found.
  114. """
  115. # If a user is provided and CKEDITOR_RESTRICT_BY_USER is True,
  116. # limit images to user specific path, but not for superusers.
  117. STORAGE_DIRECTORIES = 0
  118. STORAGE_FILES = 1
  119. # allow browsing from anywhere if user is superuser
  120. # otherwise use the user path
  121. if user and not user.is_superuser:
  122. user_path = _get_user_path(user)
  123. else:
  124. user_path = ""
  125. browse_path = os.path.join(settings.CKEDITOR_UPLOAD_PATH, user_path, path)
  126. try:
  127. storage_list = storage.listdir(browse_path)
  128. except NotImplementedError:
  129. return
  130. except OSError:
  131. return
  132. for filename in storage_list[STORAGE_FILES]:
  133. if os.path.splitext(filename)[0].endswith("_thumb") or os.path.basename(
  134. filename
  135. ).startswith("."):
  136. continue
  137. filename = os.path.join(browse_path, filename)
  138. yield filename
  139. for directory in storage_list[STORAGE_DIRECTORIES]:
  140. if directory.startswith("."):
  141. continue
  142. directory_path = os.path.join(path, directory)
  143. yield from get_image_files(user=user, path=directory_path)
  144. def get_files_browse_urls(user=None):
  145. """
  146. Recursively walks all dirs under upload dir and generates a list of
  147. thumbnail and full image URL's for each file found.
  148. """
  149. files = []
  150. for filename in get_image_files(user=user):
  151. src = utils.get_media_url(filename)
  152. if getattr(settings, "CKEDITOR_IMAGE_BACKEND", None):
  153. if is_valid_image_extension(src):
  154. thumb = utils.get_media_url(utils.get_thumb_filename(filename))
  155. else:
  156. thumb = utils.get_icon_filename(filename)
  157. visible_filename = os.path.split(filename)[1]
  158. if len(visible_filename) > 20:
  159. visible_filename = visible_filename[0:19] + "..."
  160. else:
  161. thumb = src
  162. visible_filename = os.path.split(filename)[1]
  163. files.append(
  164. {
  165. "thumb": thumb,
  166. "src": src,
  167. "is_image": is_valid_image_extension(src),
  168. "visible_filename": visible_filename,
  169. }
  170. )
  171. return files
  172. def browse(request):
  173. files = get_files_browse_urls(request.user)
  174. if request.method == "POST":
  175. form = SearchForm(request.POST)
  176. if form.is_valid():
  177. query = form.cleaned_data.get("q", "").lower()
  178. files = list(
  179. filter(lambda d: query in d["visible_filename"].lower(), files)
  180. )
  181. else:
  182. form = SearchForm()
  183. show_dirs = getattr(settings, "CKEDITOR_BROWSE_SHOW_DIRS", False)
  184. dir_list = sorted({os.path.dirname(f["src"]) for f in files}, reverse=True)
  185. # Ensures there are no objects created from Thumbs.db files - ran across
  186. # this problem while developing on Windows
  187. if os.name == "nt":
  188. files = [f for f in files if os.path.basename(f["src"]) != "Thumbs.db"]
  189. context = {"show_dirs": show_dirs, "dirs": dir_list, "files": files, "form": form}
  190. return render(request, "ckeditor/browse.html", context)