mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-11-03 22:55:43 +00:00 
			
		
		
		
	Support image uploads in the "notes" markdown fields
- Implemented using the existing EasyMDE library - Copy / paste support - Drag / drop support
This commit is contained in:
		@@ -21,8 +21,8 @@ from InvenTree.api import BulkDeleteMixin
 | 
			
		||||
from InvenTree.config import CONFIG_LOOKUPS
 | 
			
		||||
from InvenTree.filters import ORDER_FILTER, SEARCH_ORDER_FILTER
 | 
			
		||||
from InvenTree.helpers import inheritors
 | 
			
		||||
from InvenTree.mixins import (ListAPI, RetrieveAPI, RetrieveUpdateAPI,
 | 
			
		||||
                              RetrieveUpdateDestroyAPI)
 | 
			
		||||
from InvenTree.mixins import (ListAPI, ListCreateAPI, RetrieveAPI,
 | 
			
		||||
                              RetrieveUpdateAPI, RetrieveUpdateDestroyAPI)
 | 
			
		||||
from InvenTree.permissions import IsSuperuser
 | 
			
		||||
from plugin.models import NotificationUserSetting
 | 
			
		||||
from plugin.serializers import NotificationUserSettingSerializer
 | 
			
		||||
@@ -440,6 +440,20 @@ class ConfigDetail(RetrieveAPI):
 | 
			
		||||
        return {key: value}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NotesImageList(ListCreateAPI):
 | 
			
		||||
    """List view for all notes images."""
 | 
			
		||||
 | 
			
		||||
    queryset = common.models.NotesImage.objects.all()
 | 
			
		||||
    serializer_class = common.serializers.NotesImageSerializer
 | 
			
		||||
    permission_classes = [permissions.IsAuthenticated, ]
 | 
			
		||||
 | 
			
		||||
    def perform_create(self, serializer):
 | 
			
		||||
        """Create (upload) a new notes image"""
 | 
			
		||||
        image = serializer.save()
 | 
			
		||||
        image.user = self.request.user
 | 
			
		||||
        image.save()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
settings_api_urls = [
 | 
			
		||||
    # User settings
 | 
			
		||||
    re_path(r'^user/', include([
 | 
			
		||||
@@ -473,6 +487,9 @@ common_api_urls = [
 | 
			
		||||
    # Webhooks
 | 
			
		||||
    path('webhook/<slug:endpoint>/', WebhookView.as_view(), name='api-webhook'),
 | 
			
		||||
 | 
			
		||||
    # Uploaded images for notes
 | 
			
		||||
    re_path(r'^notes-image-upload/', NotesImageList.as_view(), name='api-notes-image-list'),
 | 
			
		||||
 | 
			
		||||
    # Currencies
 | 
			
		||||
    re_path(r'^currency/', include([
 | 
			
		||||
        re_path(r'^exchange/', CurrencyExchangeView.as_view(), name='api-currency-exchange'),
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										25
									
								
								InvenTree/common/migrations/0017_notesimage.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								InvenTree/common/migrations/0017_notesimage.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
# Generated by Django 3.2.18 on 2023-04-17 05:54
 | 
			
		||||
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
import django.db.models.deletion
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
 | 
			
		||||
        ('common', '0016_alter_notificationentry_updated'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.CreateModel(
 | 
			
		||||
            name='NotesImage',
 | 
			
		||||
            fields=[
 | 
			
		||||
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
 | 
			
		||||
                ('image', models.ImageField(help_text='Image file', upload_to='notes_images', verbose_name='Image')),
 | 
			
		||||
                ('date', models.DateTimeField(auto_now_add=True)),
 | 
			
		||||
                ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
 | 
			
		||||
            ],
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
@@ -2642,3 +2642,27 @@ class NewsFeedEntry(models.Model):
 | 
			
		||||
        help_text=_('Was this news item read?'),
 | 
			
		||||
        default=False
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def rename_notes_image(instance, filename):
 | 
			
		||||
    """Function for renaming uploading image file. Will store in the 'notes' directory."""
 | 
			
		||||
 | 
			
		||||
    fname = os.path.basename(filename)
 | 
			
		||||
    return os.path.join('notes', fname)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NotesImage(models.Model):
 | 
			
		||||
    """Model for storing uploading images for the 'notes' fields of various models.
 | 
			
		||||
 | 
			
		||||
    Simply stores the image file, for use in the 'notes' field (of any models which support markdown)
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    image = models.ImageField(
 | 
			
		||||
        upload_to='notes_images',
 | 
			
		||||
        verbose_name=_('Image'),
 | 
			
		||||
        help_text=_('Image file'),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
 | 
			
		||||
 | 
			
		||||
    date = models.DateTimeField(auto_now_add=True)
 | 
			
		||||
 
 | 
			
		||||
@@ -5,9 +5,10 @@ from django.urls import reverse
 | 
			
		||||
from rest_framework import serializers
 | 
			
		||||
 | 
			
		||||
from common.models import (InvenTreeSetting, InvenTreeUserSetting,
 | 
			
		||||
                           NewsFeedEntry, NotificationMessage)
 | 
			
		||||
                           NewsFeedEntry, NotesImage, NotificationMessage)
 | 
			
		||||
from InvenTree.helpers import construct_absolute_url, get_objectreference
 | 
			
		||||
from InvenTree.serializers import InvenTreeModelSerializer
 | 
			
		||||
from InvenTree.serializers import (InvenTreeAttachmentSerializerField,
 | 
			
		||||
                                   InvenTreeModelSerializer)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SettingsSerializer(InvenTreeModelSerializer):
 | 
			
		||||
@@ -230,3 +231,25 @@ class ConfigSerializer(serializers.Serializer):
 | 
			
		||||
        if not isinstance(instance, str):
 | 
			
		||||
            instance = list(instance.keys())[0]
 | 
			
		||||
        return {'key': instance, **self.instance[instance]}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NotesImageSerializer(InvenTreeModelSerializer):
 | 
			
		||||
    """Serializer for the NotesImage model."""
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        """Meta options for NotesImageSerializer."""
 | 
			
		||||
 | 
			
		||||
        model = NotesImage
 | 
			
		||||
        fields = [
 | 
			
		||||
            'pk',
 | 
			
		||||
            'image',
 | 
			
		||||
            'user',
 | 
			
		||||
            'date',
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
        read_only_fields = [
 | 
			
		||||
            'date',
 | 
			
		||||
            'user',
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
    image = InvenTreeAttachmentSerializerField(required=True)
 | 
			
		||||
 
 | 
			
		||||
@@ -380,6 +380,10 @@ function renderLink(text, url, options={}) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Configure an EasyMDE editor for the given element,
 | 
			
		||||
 * allowing markdown editing of the notes field.
 | 
			
		||||
 */
 | 
			
		||||
function setupNotesField(element, url, options={}) {
 | 
			
		||||
 | 
			
		||||
    var editable = options.editable || false;
 | 
			
		||||
@@ -419,12 +423,25 @@ function setupNotesField(element, url, options={}) {
 | 
			
		||||
        element: document.getElementById(element),
 | 
			
		||||
        initialValue: initial,
 | 
			
		||||
        toolbar: toolbar_icons,
 | 
			
		||||
        uploadImage: true,
 | 
			
		||||
        imagePathAbsolute: true,
 | 
			
		||||
        imageUploadFunction: function(imageFile, onSuccess, onError) {
 | 
			
		||||
            // Attempt to upload the image to the InvenTree server
 | 
			
		||||
            var form_data = new FormData();
 | 
			
		||||
 | 
			
		||||
            form_data.append('image', imageFile);
 | 
			
		||||
 | 
			
		||||
            inventreeFormDataUpload('{% url "api-notes-image-list" %}', form_data, {
 | 
			
		||||
                success: function(response) {
 | 
			
		||||
                    console.log("Uploading image:", response.image);
 | 
			
		||||
                    onSuccess(response.image);
 | 
			
		||||
                },
 | 
			
		||||
                error: function(xhr, status, error) {
 | 
			
		||||
                    onError(error);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        shortcuts: [],
 | 
			
		||||
        renderingConfig: {
 | 
			
		||||
            markedOptions: {
 | 
			
		||||
                sanitize: true,
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user