From 3caa90f226cd811b9b8adb15a966ffa2e3095f61 Mon Sep 17 00:00:00 2001 From: ShreyasSridhar24 <102874119+ShreyasSridhar24@users.noreply.github.com> Date: Thu, 11 Jul 2024 14:44:42 -0400 Subject: [PATCH 1/2] Take in pdf files, sanitize, serve --- coldfront/config/base.py | 3 ++ coldfront/config/urls.py | 2 ++ coldfront/core/project/forms.py | 3 ++ coldfront/core/project/models.py | 11 +++++-- .../project/project_attribute_create.html | 32 ++++++++++++++++++- coldfront/core/project/urls.py | 3 ++ coldfront/core/project/views.py | 14 ++++++-- coldfront/core/utils/validate.py | 17 ++++++++-- 8 files changed, 77 insertions(+), 8 deletions(-) diff --git a/coldfront/config/base.py b/coldfront/config/base.py index d9f71d972..6688f328b 100644 --- a/coldfront/config/base.py +++ b/coldfront/config/base.py @@ -155,3 +155,6 @@ # Add system site static files if os.path.isdir('/usr/share/coldfront/site/static'): STATICFILES_DIRS.insert(0, '/usr/share/coldfront/site/static') + +MEDIA_URL = '/media/' +MEDIA_ROOT = os.path.join(BASE_DIR, 'media') \ No newline at end of file diff --git a/coldfront/config/urls.py b/coldfront/config/urls.py index 353e3602e..249cf327e 100644 --- a/coldfront/config/urls.py +++ b/coldfront/config/urls.py @@ -5,6 +5,7 @@ from django.contrib import admin from django.urls import include, path from django.views.generic import TemplateView +from django.conf.urls.static import static import coldfront.core.portal.views as portal_views @@ -36,3 +37,4 @@ if 'django_su.backends.SuBackend' in settings.AUTHENTICATION_BACKENDS: urlpatterns.append(path('su/', include('django_su.urls'))) +urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/coldfront/core/project/forms.py b/coldfront/core/project/forms.py index beeea24e9..28820843f 100644 --- a/coldfront/core/project/forms.py +++ b/coldfront/core/project/forms.py @@ -142,6 +142,9 @@ def __init__(self, *args, **kwargs): super(ProjectAttributeAddForm, self).__init__(*args, **kwargs) user =(kwargs.get('initial')).get('user') self.fields['proj_attr_type'].queryset = self.fields['proj_attr_type'].queryset.order_by(Lower('name')) + self.fields['doc'].required = False + self.fields['value'].required = False + if not user.is_superuser: self.fields['proj_attr_type'].queryset = self.fields['proj_attr_type'].queryset.filter(is_private=False) diff --git a/coldfront/core/project/models.py b/coldfront/core/project/models.py index 45f9470ac..84849fcb6 100644 --- a/coldfront/core/project/models.py +++ b/coldfront/core/project/models.py @@ -400,6 +400,9 @@ def __str__(self): def __repr__(self) -> str: return str(self) + + + class Meta: ordering = ['name', ] @@ -418,6 +421,7 @@ class ProjectAttribute(TimeStampedModel): value = models.CharField(max_length=128) history = HistoricalRecords() + doc = models.FileField(default=False, upload_to='documents/') def save(self, *args, **kwargs): """ Saves the project attribute. """ @@ -433,8 +437,9 @@ def clean(self): self.proj_attr_type)) expected_value_type = self.proj_attr_type.attribute_type.name.strip() - - validator = AttributeValidator(self.value) + # if not self.doc: + validator = AttributeValidator(self.value, self.doc) + if expected_value_type == "Int": validator.validate_int() @@ -444,6 +449,8 @@ def clean(self): validator.validate_yes_no() elif expected_value_type == "Date": validator.validate_date() + elif expected_value_type == "Upload": + validator.validate_doc() def __str__(self): return '%s' % (self.proj_attr_type.name) diff --git a/coldfront/core/project/templates/project/project_attribute_create.html b/coldfront/core/project/templates/project/project_attribute_create.html index 39c7beb43..846d3d1c5 100644 --- a/coldfront/core/project/templates/project/project_attribute_create.html +++ b/coldfront/core/project/templates/project/project_attribute_create.html @@ -12,11 +12,41 @@ {% block content %}

Adding project attribute to {{ project }}

-
+ {% csrf_token %} {{ form | crispy }} + Back to Project
+
+ + {% endblock %} \ No newline at end of file diff --git a/coldfront/core/project/urls.py b/coldfront/core/project/urls.py index 748b6495c..b5c273679 100644 --- a/coldfront/core/project/urls.py +++ b/coldfront/core/project/urls.py @@ -1,4 +1,6 @@ from django.urls import path +from django.conf import settings +from django.conf.urls.static import static import coldfront.core.project.views as project_views @@ -26,3 +28,4 @@ path('/project-attribute-update/', project_views.ProjectAttributeUpdateView.as_view(), name='project-attribute-update'), ] +urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/coldfront/core/project/views.py b/coldfront/core/project/views.py index a71dda349..92ea67959 100644 --- a/coldfront/core/project/views.py +++ b/coldfront/core/project/views.py @@ -1169,7 +1169,7 @@ class ProjectNoteCreateView(LoginRequiredMixin, UserPassesTestMixin, CreateView) model = ProjectUserMessage fields = '__all__' template_name = 'project/project_note_create.html' - + enctype = 'multipart/form-data' def test_func(self): """ UserPassesTestMixin Tests""" @@ -1210,7 +1210,8 @@ class ProjectAttributeCreateView(LoginRequiredMixin, UserPassesTestMixin, Create model = ProjectAttribute form_class = ProjectAttributeAddForm template_name = 'project/project_attribute_create.html' - + enctype="multipart/form-data" + # print("kjenf") def test_func(self): """ UserPassesTestMixin Tests""" project_obj = get_object_or_404(Project, pk=self.kwargs.get('pk')) @@ -1233,7 +1234,6 @@ def get_initial(self): initial['project'] = get_object_or_404(Project, pk=pk) initial['user'] = self.request.user return initial - def get_form(self, form_class=None): """Return an instance of the form to be used in this view.""" form = super().get_form(form_class) @@ -1244,8 +1244,16 @@ def get_context_data(self, *args, **kwargs): pk = self.kwargs.get('pk') context = super().get_context_data(*args, **kwargs) context['project'] = get_object_or_404(Project, pk=pk) + text_based_input_attributes = [] + for m in ProjectAttribute.objects.all(): + # print(m.doc) + if(m.proj_attr_type.attribute_type.name != "Upload"): + text_based_input_attributes.append(m.proj_attr_type.attribute_type.name) + context["text_based_input_attributes"] = text_based_input_attributes + return context + def get_success_url(self): return reverse('project-detail', kwargs={'pk': self.object.project_id}) diff --git a/coldfront/core/utils/validate.py b/coldfront/core/utils/validate.py index 5597a1408..c74dc9994 100644 --- a/coldfront/core/utils/validate.py +++ b/coldfront/core/utils/validate.py @@ -3,11 +3,13 @@ from django.core.validators import MinValueValidator import formencode from formencode import validators, Invalid +import magic class AttributeValidator: - def __init__(self, value): + def __init__(self, value, doc): self.value = value + self.doc = doc def validate_int(self): try: @@ -38,4 +40,15 @@ def validate_date(self): datetime.datetime.strptime(self.value.strip(), "%Y-%m-%d") except: raise ValidationError( - f'Invalid Value {self.value}. Date must be in format YYYY-MM-DD and date must be today or later.') \ No newline at end of file + f'Invalid Value {self.value}. Date must be in format YYYY-MM-DD and date must be today or later.') + + def validate_doc(self): + # try: + if self.doc: + if self.doc.size > 10485760 : + raise ValidationError("This document exceeds size limits") + content_mime_type = magic.Magic(mime=True) + # file_type = content_mime_type.from_buffer(self.doc.read()) + if content_mime_type.from_buffer(self.doc.read()) != "application/pdf": + raise ValidationError("Invalid file type") + self.doc.seek(0) From 82230177ad3cb858838a691b77ae78b2bd689693 Mon Sep 17 00:00:00 2001 From: ShreyasSridhar24 <102874119+ShreyasSridhar24@users.noreply.github.com> Date: Thu, 25 Jul 2024 11:26:54 -0400 Subject: [PATCH 2/2] Add support for allocations --- coldfront/core/allocation/forms.py | 2 ++ coldfront/core/allocation/models.py | 16 +++++++++++++--- coldfront/core/allocation/urls.py | 4 +++- coldfront/core/allocation/views.py | 7 ++++++- 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/coldfront/core/allocation/forms.py b/coldfront/core/allocation/forms.py index 970024477..71c2c599a 100644 --- a/coldfront/core/allocation/forms.py +++ b/coldfront/core/allocation/forms.py @@ -259,3 +259,5 @@ class Meta: def __init__(self, *args, **kwargs): super(AllocationAttributeCreateForm, self).__init__(*args, **kwargs) self.fields['allocation_attribute_type'].queryset = self.fields['allocation_attribute_type'].queryset.order_by(Lower('name')) + self.fields['doc'].required = False + self.fields['value'].required = False diff --git a/coldfront/core/allocation/models.py b/coldfront/core/allocation/models.py index b89826b2c..1986eb677 100644 --- a/coldfront/core/allocation/models.py +++ b/coldfront/core/allocation/models.py @@ -12,7 +12,7 @@ from django.utils.module_loading import import_string from model_utils.models import TimeStampedModel from simple_history.models import HistoricalRecords - +import magic from coldfront.core.project.models import Project, ProjectPermission from coldfront.core.resource.models import Resource from coldfront.core.utils.common import import_from_settings @@ -412,6 +412,8 @@ class AllocationAttributeType(TimeStampedModel): is_private = models.BooleanField(default=True) is_changeable = models.BooleanField(default=False) history = HistoricalRecords() + + def __str__(self): return '%s' % (self.name) @@ -433,7 +435,7 @@ class AllocationAttribute(TimeStampedModel): allocation = models.ForeignKey(Allocation, on_delete=models.CASCADE) value = models.CharField(max_length=128) history = HistoricalRecords() - + doc = models.FileField(default=False, upload_to='documents/') def save(self, *args, **kwargs): """ Saves the allocation attribute. """ @@ -466,7 +468,15 @@ def clean(self): except ValueError: raise ValidationError( 'Invalid Value "%s" for "%s". Date must be in format YYYY-MM-DD' % (self.value, self.allocation_attribute_type.name)) - + elif expected_value_type == "Upload": + if self.doc: + if self.doc.size > 10485760 : + raise ValidationError("This document exceeds size limits") + content_mime_type = magic.Magic(mime=True) + # file_type = content_mime_type.from_buffer(self.doc.read()) + if content_mime_type.from_buffer(self.doc.read()) != "application/pdf": + raise ValidationError("Invalid file type") + self.doc.seek(0) def __str__(self): return '%s' % (self.allocation_attribute_type.name) diff --git a/coldfront/core/allocation/urls.py b/coldfront/core/allocation/urls.py index 9d0f747be..3cc75982b 100644 --- a/coldfront/core/allocation/urls.py +++ b/coldfront/core/allocation/urls.py @@ -1,7 +1,8 @@ from django.urls import path import coldfront.core.allocation.views as allocation_views - +from django.conf import settings +from django.conf.urls.static import static urlpatterns = [ path('', allocation_views.AllocationListView.as_view(), name='allocation-list'), path('project//create', @@ -45,3 +46,4 @@ path('allocation-account-list/', allocation_views.AllocationAccountListView.as_view(), name='allocation-account-list'), ] +urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) \ No newline at end of file diff --git a/coldfront/core/allocation/views.py b/coldfront/core/allocation/views.py index 216c3531c..ffb50b32a 100644 --- a/coldfront/core/allocation/views.py +++ b/coldfront/core/allocation/views.py @@ -775,7 +775,7 @@ class AllocationAttributeCreateView(LoginRequiredMixin, UserPassesTestMixin, Cre model = AllocationAttribute form_class = AllocationAttributeCreateForm template_name = 'allocation/allocation_allocationattribute_create.html' - + enctype="multipart/form-data" def test_func(self): """ UserPassesTestMixin Tests""" @@ -789,6 +789,11 @@ def get_context_data(self, **kwargs): pk = self.kwargs.get('pk') allocation_obj = get_object_or_404(Allocation, pk=pk) context['allocation'] = allocation_obj + text_based_input_attributes = [] + for m in AllocationAttribute.objects.all(): + if(m.proj_attr_type.attribute_type.name != "Upload"): + text_based_input_attributes.append(m.proj_attr_type.attribute_type.name) + context["text_based_input_attributes"] = text_based_input_attributes return context def get_initial(self):