add a URL to upload files
This commit is contained in:
parent
d061110d88
commit
dcdb3d87be
3 changed files with 91 additions and 8 deletions
|
@ -3,6 +3,7 @@ from django.conf import settings
|
|||
|
||||
from .models import Think
|
||||
|
||||
|
||||
class CreateThinkForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Think
|
||||
|
@ -15,11 +16,13 @@ class CreateThinkForm(forms.ModelForm):
|
|||
instance.root.mkdir(exist_ok=True,parents=True)
|
||||
return instance
|
||||
|
||||
|
||||
class RemixThinkForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Think
|
||||
fields = []
|
||||
|
||||
|
||||
class RenameThinkForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Think
|
||||
|
@ -49,6 +52,7 @@ class RenameThinkForm(forms.ModelForm):
|
|||
instance = super().save(commit)
|
||||
return instance
|
||||
|
||||
|
||||
class SaveFileForm(forms.ModelForm):
|
||||
path = forms.CharField()
|
||||
content = forms.CharField(required=False, widget=forms.Textarea)
|
||||
|
@ -72,6 +76,7 @@ class SaveFileForm(forms.ModelForm):
|
|||
|
||||
return super().save(commit)
|
||||
|
||||
|
||||
class RenameFileForm(forms.ModelForm):
|
||||
path = forms.CharField()
|
||||
newpath = forms.CharField()
|
||||
|
@ -100,6 +105,7 @@ class RenameFileForm(forms.ModelForm):
|
|||
|
||||
return super().save(commit)
|
||||
|
||||
|
||||
class DeleteFileForm(forms.ModelForm):
|
||||
path = forms.CharField()
|
||||
|
||||
|
@ -121,6 +127,7 @@ class DeleteFileForm(forms.ModelForm):
|
|||
|
||||
return super().save(commit)
|
||||
|
||||
|
||||
class RunCommandForm(forms.ModelForm):
|
||||
command = forms.CharField()
|
||||
|
||||
|
@ -128,9 +135,51 @@ class RunCommandForm(forms.ModelForm):
|
|||
model = Think
|
||||
fields = []
|
||||
|
||||
|
||||
class GitCommitForm(forms.ModelForm):
|
||||
message = forms.CharField()
|
||||
|
||||
class Meta:
|
||||
model = Think
|
||||
fields = []
|
||||
|
||||
|
||||
class MultipleFileInput(forms.ClearableFileInput):
|
||||
allow_multiple_selected = True
|
||||
|
||||
|
||||
class MultipleFileField(forms.FileField):
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs.setdefault("widget", MultipleFileInput())
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def clean(self, data, initial=None):
|
||||
single_file_clean = super().clean
|
||||
if isinstance(data, (list, tuple)):
|
||||
result = [single_file_clean(d, initial) for d in data]
|
||||
else:
|
||||
result = [single_file_clean(data, initial)]
|
||||
return result
|
||||
|
||||
|
||||
class UploadFileForm(forms.ModelForm):
|
||||
files = MultipleFileField()
|
||||
|
||||
class Meta:
|
||||
model = Think
|
||||
fields = []
|
||||
|
||||
|
||||
def save(self, commit=False):
|
||||
think = self.instance
|
||||
|
||||
files = self.cleaned_data['files']
|
||||
|
||||
for uf in files:
|
||||
path = think.file_path(uf.name)
|
||||
print(uf, path)
|
||||
with open(path, 'wb') as df:
|
||||
for chunk in uf.chunks():
|
||||
df.write(chunk)
|
||||
|
||||
return super().save(commit)
|
||||
|
|
|
@ -12,6 +12,7 @@ urlpatterns = [
|
|||
path('think/<slug:slug>/rename-file', RenameFileView.as_view(), name='rename_file'),
|
||||
path('think/<slug:slug>/delete-file', DeleteFileView.as_view(), name='delete_file'),
|
||||
path('think/<slug:slug>/run-command', RunCommandView.as_view(), name='run_command'),
|
||||
path('think/<slug:slug>/upload-files', UploadFileView.as_view(), name='upload_files'),
|
||||
path('think/<slug:slug>/log', LogView.as_view(), name='log'),
|
||||
path('think/<slug:slug>/jj/status', JJStatusView.as_view(), name='jj_status'),
|
||||
path('think/<slug:slug>/jj/commit', JJCommitView.as_view(), name='jj_commit'),
|
||||
|
|
|
@ -6,6 +6,7 @@ from django.views import generic
|
|||
from django.urls import reverse
|
||||
from itertools import groupby
|
||||
import json
|
||||
import mimetypes
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import shlex
|
||||
|
@ -16,6 +17,7 @@ from .make import ThingMaker
|
|||
from .models import Think
|
||||
from .random_slug import random_slug
|
||||
|
||||
|
||||
class LoginRequiredMixin(AccessMixin):
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if not request.user.is_authenticated:
|
||||
|
@ -25,10 +27,12 @@ class LoginRequiredMixin(AccessMixin):
|
|||
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
|
||||
class ThinkMixin(LoginRequiredMixin):
|
||||
model = Think
|
||||
context_object_name = 'think'
|
||||
|
||||
|
||||
class IndexView(ThinkMixin, generic.ListView):
|
||||
template_name = 'thinks/index.html'
|
||||
|
||||
|
@ -50,10 +54,12 @@ class IndexView(ThinkMixin, generic.ListView):
|
|||
return JsonResponse({'templates': [t.as_json() for t in context['templates']], 'thinks': [t.as_json() for t in context['thinks']]})
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
|
||||
class CreateThinkView(ThinkMixin, generic.CreateView):
|
||||
template_name = 'thinks/new.html'
|
||||
form_class = forms.CreateThinkForm
|
||||
|
||||
|
||||
class RemixThinkView(ThinkMixin, generic.UpdateView):
|
||||
template_name = 'thinks/remix.html'
|
||||
form_class = forms.RemixThinkForm
|
||||
|
@ -79,6 +85,7 @@ class RemixThinkView(ThinkMixin, generic.UpdateView):
|
|||
return redirect(think.get_absolute_url())
|
||||
|
||||
|
||||
|
||||
class ThinkView(ThinkMixin, generic.DetailView):
|
||||
template_name = "thinks/think.html"
|
||||
|
||||
|
@ -124,28 +131,37 @@ class ThinkView(ThinkMixin, generic.DetailView):
|
|||
|
||||
files = sorted(files, key=lambda x: x['name'].lower())
|
||||
|
||||
if path is not None and path.is_file():
|
||||
with open(path) as f:
|
||||
content = f.read()
|
||||
else:
|
||||
content = ''
|
||||
|
||||
data = context['think_editor_data'] = {
|
||||
'preview_url': think.get_static_url(),
|
||||
'slug': think.slug,
|
||||
'files': files,
|
||||
'file_path': str(relpath),
|
||||
'file_content': content,
|
||||
'is_dir': path is None or path.is_dir(),
|
||||
'no_preview': self.request.GET.get('no-preview') is not None,
|
||||
}
|
||||
|
||||
if path is not None and path.is_file():
|
||||
mime_types = {
|
||||
'.elm': 'text/application+elm',
|
||||
}
|
||||
mime_type, encoding = mimetypes.guess_type(path)
|
||||
if mime_type is None:
|
||||
mime_type = mime_types.get(path.suffix, 'text/plain')
|
||||
category, filetype = mime_type.split('/') if mime_type is not None else ('text', 'plain')
|
||||
binary_categories = ['audio', 'video', 'image']
|
||||
is_binary = category in binary_categories and mime_type != 'image/svg+xml'
|
||||
data['mime_type'] = mime_type
|
||||
if not is_binary:
|
||||
with open(path) as f:
|
||||
data['file_content'] = f.read()
|
||||
|
||||
if path is not None and path.suffix == '.elm':
|
||||
with open('public/elm-packages.json') as f:
|
||||
data['elm_packages'] = json.load(f)
|
||||
|
||||
return context
|
||||
|
||||
|
||||
class RenameThinkView(ThinkMixin, generic.UpdateView):
|
||||
form_class = forms.RenameThinkForm
|
||||
template_name = 'thinks/rename.html'
|
||||
|
@ -160,18 +176,22 @@ class RenameThinkView(ThinkMixin, generic.UpdateView):
|
|||
|
||||
return context
|
||||
|
||||
|
||||
class DeleteThinkView(ThinkMixin, generic.DeleteView):
|
||||
template_name = 'thinks/delete.html'
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('index')
|
||||
|
||||
|
||||
class ReadFileView(ThinkMixin, generic.DetailView):
|
||||
def get(self, request, *args, **kwargs):
|
||||
think = self.get_object()
|
||||
relpath = self.kwargs['path']
|
||||
path = think.root / relpath
|
||||
print(path)
|
||||
if not path.is_relative_to(think.root):
|
||||
raise Exception(f"Bad path: {relpath}")
|
||||
return redirect(think.get_static_url() + '/' + relpath)
|
||||
|
||||
|
||||
class SaveFileView(ThinkMixin, generic.UpdateView):
|
||||
|
@ -191,6 +211,7 @@ class SaveFileView(ThinkMixin, generic.UpdateView):
|
|||
def get_success_url(self):
|
||||
return self.object.get_absolute_url()+'?path='+str(self.path)
|
||||
|
||||
|
||||
class RenameFileView(ThinkMixin, generic.UpdateView):
|
||||
form_class = forms.RenameFileForm
|
||||
template_name = 'thinks/rename_file.html'
|
||||
|
@ -203,6 +224,7 @@ class RenameFileView(ThinkMixin, generic.UpdateView):
|
|||
def get_success_url(self):
|
||||
return self.object.get_absolute_url()+'?path='+str(self.path)
|
||||
|
||||
|
||||
class DeleteFileView(ThinkMixin, generic.UpdateView):
|
||||
form_class = forms.DeleteFileForm
|
||||
template_name = 'thinks/delete_file.html'
|
||||
|
@ -214,6 +236,7 @@ class DeleteFileView(ThinkMixin, generic.UpdateView):
|
|||
def get_success_url(self):
|
||||
return self.object.get_absolute_url()+'?path='+str(self.path.parent)
|
||||
|
||||
|
||||
class RunCommandView(ThinkMixin, generic.UpdateView):
|
||||
form_class = forms.RunCommandForm
|
||||
|
||||
|
@ -228,6 +251,14 @@ class RunCommandView(ThinkMixin, generic.UpdateView):
|
|||
)
|
||||
return JsonResponse({'stdout': res.stdout, 'stderr': res.stderr})
|
||||
|
||||
|
||||
class UploadFileView(ThinkMixin, generic.UpdateView):
|
||||
form_class = forms.UploadFileForm
|
||||
|
||||
def get_success_url(self):
|
||||
return self.object.get_absolute_url()
|
||||
|
||||
|
||||
class LogView(ThinkMixin, generic.DetailView):
|
||||
template_name = 'thinks/think.html'
|
||||
|
||||
|
@ -236,11 +267,13 @@ class LogView(ThinkMixin, generic.DetailView):
|
|||
|
||||
return HttpResponse(think.get_log(), content_type='text/plain; charset=utf-8')
|
||||
|
||||
|
||||
class JJStatusView(ThinkMixin, generic.detail.DetailView):
|
||||
def get(self, request, *args, **kwargs):
|
||||
status = self.get_object().jj_controller.status()
|
||||
return JsonResponse({'status': status})
|
||||
|
||||
|
||||
class JJCommitView(ThinkMixin, generic.UpdateView):
|
||||
form_class = forms.GitCommitForm
|
||||
|
||||
|
|
Loading…
Reference in a new issue