123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 |
- import inspect
- import os
- import warnings
- from datetime import datetime
- from django.conf import settings
- from django.http import HttpResponse, JsonResponse
- from django.shortcuts import render
- from django.utils.html import escape
- from django.utils.module_loading import import_string
- from django.views import generic
- from django.views.decorators.csrf import csrf_exempt
- from ckeditor_uploader import utils
- from ckeditor_uploader.backends import get_backend
- from ckeditor_uploader.forms import SearchForm
- from ckeditor_uploader.utils import storage
- from .utils import is_valid_image_extension
- def _get_user_path(user):
- user_path = ""
- # If CKEDITOR_RESTRICT_BY_USER is True upload file to user specific path.
- RESTRICT_BY_USER = getattr(settings, "CKEDITOR_RESTRICT_BY_USER", False)
- if RESTRICT_BY_USER:
- try:
- user_prop = getattr(user, RESTRICT_BY_USER)
- except (AttributeError, TypeError):
- user_prop = getattr(user, "get_username")
- if callable(user_prop):
- user_path = user_prop()
- else:
- user_path = user_prop
- return str(user_path)
- def get_upload_filename(upload_name, request):
- user_path = _get_user_path(request.user)
- # Generate date based path to put uploaded file.
- # If CKEDITOR_RESTRICT_BY_DATE is True upload file to date specific path.
- if getattr(settings, "CKEDITOR_RESTRICT_BY_DATE", True):
- date_path = datetime.now().strftime("%Y/%m/%d")
- else:
- date_path = ""
- # Complete upload path (upload_path + date_path).
- upload_path = os.path.join(settings.CKEDITOR_UPLOAD_PATH, user_path, date_path)
- if getattr(settings, "CKEDITOR_UPLOAD_SLUGIFY_FILENAME", True) and not hasattr(
- settings, "CKEDITOR_FILENAME_GENERATOR"
- ):
- upload_name = utils.slugify_filename(upload_name)
- if hasattr(settings, "CKEDITOR_FILENAME_GENERATOR"):
- generator = import_string(settings.CKEDITOR_FILENAME_GENERATOR)
- # Does the generator accept a request argument?
- try:
- inspect.signature(generator).bind(upload_name, request)
- except TypeError:
- # Does the generator accept only an upload_name argument?
- try:
- inspect.signature(generator).bind(upload_name)
- except TypeError:
- warnings.warn(
- "Update %s() to accept the arguments `filename, request`."
- % settings.CKEDITOR_FILENAME_GENERATOR
- )
- else:
- warnings.warn(
- "Update %s() to accept a second `request` argument."
- % settings.CKEDITOR_FILENAME_GENERATOR,
- PendingDeprecationWarning,
- )
- upload_name = generator(upload_name)
- else:
- upload_name = generator(upload_name, request)
- return storage.get_available_name(os.path.join(upload_path, upload_name))
- class ImageUploadView(generic.View):
- http_method_names = ["post"]
- def post(self, request, **kwargs):
- """
- Uploads a file and send back its URL to CKEditor.
- """
- uploaded_file = request.FILES["upload"]
- backend = get_backend()
- ck_func_num = request.GET.get("CKEditorFuncNum")
- if ck_func_num:
- ck_func_num = escape(ck_func_num)
- filewrapper = backend(storage, uploaded_file)
- allow_nonimages = getattr(settings, "CKEDITOR_ALLOW_NONIMAGE_FILES", True)
- # Throws an error when an non-image file are uploaded.
- if not filewrapper.is_image and not allow_nonimages:
- return HttpResponse(
- """
- <script type='text/javascript'>
- window.parent.CKEDITOR.tools.callFunction({}, '', 'Invalid file type.');
- </script>""".format(
- ck_func_num
- )
- )
- filepath = get_upload_filename(uploaded_file.name, request)
- saved_path = filewrapper.save_as(filepath)
- url = utils.get_media_url(saved_path)
- if ck_func_num:
- # Respond with Javascript sending ckeditor upload url.
- return HttpResponse(
- """
- <script type='text/javascript'>
- window.parent.CKEDITOR.tools.callFunction({}, '{}');
- </script>""".format(
- ck_func_num, url
- )
- )
- else:
- _, filename = os.path.split(saved_path)
- retdata = {"url": url, "uploaded": "1", "fileName": filename}
- return JsonResponse(retdata)
- upload = csrf_exempt(ImageUploadView.as_view())
- def get_image_files(user=None, path=""):
- """
- Recursively walks all dirs under upload dir and generates a list of
- full paths for each file found.
- """
- # If a user is provided and CKEDITOR_RESTRICT_BY_USER is True,
- # limit images to user specific path, but not for superusers.
- STORAGE_DIRECTORIES = 0
- STORAGE_FILES = 1
- # allow browsing from anywhere if user is superuser
- # otherwise use the user path
- if user and not user.is_superuser:
- user_path = _get_user_path(user)
- else:
- user_path = ""
- browse_path = os.path.join(settings.CKEDITOR_UPLOAD_PATH, user_path, path)
- try:
- storage_list = storage.listdir(browse_path)
- except NotImplementedError:
- return
- except OSError:
- return
- for filename in storage_list[STORAGE_FILES]:
- if os.path.splitext(filename)[0].endswith("_thumb") or os.path.basename(
- filename
- ).startswith("."):
- continue
- filename = os.path.join(browse_path, filename)
- yield filename
- for directory in storage_list[STORAGE_DIRECTORIES]:
- if directory.startswith("."):
- continue
- directory_path = os.path.join(path, directory)
- yield from get_image_files(user=user, path=directory_path)
- def get_files_browse_urls(user=None):
- """
- Recursively walks all dirs under upload dir and generates a list of
- thumbnail and full image URL's for each file found.
- """
- files = []
- for filename in get_image_files(user=user):
- src = utils.get_media_url(filename)
- if getattr(settings, "CKEDITOR_IMAGE_BACKEND", None):
- if is_valid_image_extension(src):
- thumb = utils.get_media_url(utils.get_thumb_filename(filename))
- else:
- thumb = utils.get_icon_filename(filename)
- visible_filename = os.path.split(filename)[1]
- if len(visible_filename) > 20:
- visible_filename = visible_filename[0:19] + "..."
- else:
- thumb = src
- visible_filename = os.path.split(filename)[1]
- files.append(
- {
- "thumb": thumb,
- "src": src,
- "is_image": is_valid_image_extension(src),
- "visible_filename": visible_filename,
- }
- )
- return files
- def browse(request):
- files = get_files_browse_urls(request.user)
- if request.method == "POST":
- form = SearchForm(request.POST)
- if form.is_valid():
- query = form.cleaned_data.get("q", "").lower()
- files = list(
- filter(lambda d: query in d["visible_filename"].lower(), files)
- )
- else:
- form = SearchForm()
- show_dirs = getattr(settings, "CKEDITOR_BROWSE_SHOW_DIRS", False)
- dir_list = sorted({os.path.dirname(f["src"]) for f in files}, reverse=True)
- # Ensures there are no objects created from Thumbs.db files - ran across
- # this problem while developing on Windows
- if os.name == "nt":
- files = [f for f in files if os.path.basename(f["src"]) != "Thumbs.db"]
- context = {"show_dirs": show_dirs, "dirs": dir_list, "files": files, "form": form}
- return render(request, "ckeditor/browse.html", context)
|