Finalizing image handling in tiptap
This commit is contained in:
parent
50808b27b0
commit
1f15181995
@ -1,10 +1,13 @@
|
||||
#######################Django related imports####################
|
||||
import os
|
||||
import random
|
||||
from rest_framework import generics, status
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.parsers import MultiPartParser, FormParser
|
||||
from django.core.files.storage import default_storage
|
||||
from rest_framework import generics, status
|
||||
import random
|
||||
from django.conf import settings
|
||||
from django.http import JsonResponse
|
||||
#################################################################
|
||||
#API related imports
|
||||
from .models import (
|
||||
@ -113,77 +116,50 @@ class MediaUpload(APIView):
|
||||
files = request.FILES.getlist('media')
|
||||
resource_type = file_serializer.validated_data['resource_type']
|
||||
resource_id = file_serializer.validated_data['resource_id']
|
||||
file_path_base = f'static/rangolio_data'
|
||||
file_path_base = f'rangolio_data'
|
||||
|
||||
for f in files:
|
||||
file_unique_slug = ''.join(random.choices('ABCDEabcde1234', k=5))
|
||||
file_path = f"{file_path_base}/{resource_type}/{resource_id}/media/{file_unique_slug+resource_id+f.name}"
|
||||
file_unique_slug = ''.join(random.choices('ABCDEabcde12345', k=6))
|
||||
if resource_id != resource_type:
|
||||
file_path = f"{file_path_base}/{resource_type}/{resource_id}/media/{file_unique_slug+resource_id+f.name}"
|
||||
else:
|
||||
file_path = f"{file_path_base}/{resource_type}/media/{file_unique_slug+resource_id+f.name}"
|
||||
default_storage.save(file_path, f)
|
||||
|
||||
return Response(file_serializer.data, status=status.HTTP_201_CREATED)
|
||||
else:
|
||||
return Response(file_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
class ListMedia(APIView):
|
||||
def get(self, request, resource_type, resource_id, format=None):
|
||||
if resource_id != resource_type:
|
||||
media_folder = os.path.join(settings.MEDIA_ROOT, 'rangolio_data', resource_type, resource_id, 'media')
|
||||
else:
|
||||
media_folder = os.path.join(settings.MEDIA_ROOT, 'rangolio_data', resource_type, 'media')
|
||||
|
||||
if not os.path.exists(media_folder):
|
||||
return Response({'error': 'Media directory not found'}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
'''
|
||||
class ETLFunctions(GenericAPIView):
|
||||
media_files = [f for f in os.listdir(media_folder) if f.endswith(('.png', '.jpg', '.jpeg'))]
|
||||
if resource_id != resource_type:
|
||||
media_urls = [request.build_absolute_uri(f'{settings.MEDIA_URL}rangolio_data/{resource_type}/{resource_id}/media/' + f) for f in media_files]
|
||||
else:
|
||||
media_urls = [request.build_absolute_uri(f'{settings.MEDIA_URL}rangolio_data/{resource_type}/media/' + f) for f in media_files]
|
||||
|
||||
serializer_class = ETLData
|
||||
return Response({'media': media_urls}, status=status.HTTP_200_OK)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
data = serializer.data
|
||||
if data['operation'] == "create-folder":
|
||||
os.mkdir('../Analysis/'+data['postData'])
|
||||
with open('../Analysis/'+data['postData']+'/'+data['postData']+'-run.log', 'w') as fp:
|
||||
pass
|
||||
fp.close()
|
||||
def delete(self, request, resource_type, resource_id, format=None):
|
||||
if resource_id != resource_type:
|
||||
media_folder = os.path.join(settings.MEDIA_ROOT, 'rangolio_data', resource_type, resource_id, 'media')
|
||||
else:
|
||||
media_folder = os.path.join(settings.MEDIA_ROOT, 'rangolio_data', resource_type, 'media')
|
||||
file_name = request.query_params.get('file')
|
||||
if not file_name or not file_name.endswith(('.png', '.jpg', '.jpeg')):
|
||||
return Response({'error': 'Invalid or no file name provided'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
if data['operation'] == "rename-folder":
|
||||
os.rename('../Analysis/'+data['oldTitle'], '../Analysis/'+data['postData'])
|
||||
|
||||
if data['operation'] == "create-partition-file":
|
||||
print(os.getcwd())
|
||||
print(os.listdir(os.getcwd()))
|
||||
partInfo = (data['postData'].split('|'))
|
||||
with open(f'"{partInfo[0]}.part"', "w") as fpp:
|
||||
pass
|
||||
fpp.close()
|
||||
partitionFile = open(f'"{partInfo[0]}.part"', "w+")
|
||||
partitionFile.write(partInfo[1])
|
||||
partitionFile.close()
|
||||
|
||||
if data['operation'] == "move-file":
|
||||
pass
|
||||
|
||||
return Response("Success", status=status.HTTP_200_OK)
|
||||
|
||||
class BioTools(APIView):
|
||||
def get(self, request):
|
||||
params = request.GET.get('function', '')
|
||||
params = params.split(";")
|
||||
output = subprocess.check_output(f'seqmagick extract-ids ../Analysis/"{params[1]}"/"{(params[2])[:-1]}"', shell=True)
|
||||
outgroups = (output.decode("utf-8")).split('\n')
|
||||
outgroups = outgroups[:len(outgroups)-1]
|
||||
return Response({'outgroups': outgroups})
|
||||
|
||||
class CommandRunner(GenericAPIView):
|
||||
|
||||
serializer_class = InterimData
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
data = serializer.data
|
||||
runLogFile = f'../Analysis/{data["nodeName"]}/{data["nodeName"]}-run.log'
|
||||
|
||||
try:
|
||||
commands = ast.literal_eval(data['finalParameter'])
|
||||
for key, value in commands.items():
|
||||
process = subprocess.Popen(value+f" > {runLogFile}", shell=True)
|
||||
except:
|
||||
process = subprocess.Popen(data['finalParameter']+f" > {runLogFile}", shell=True)
|
||||
return Response("Command successfully sent for execution", status=status.HTTP_200_OK)
|
||||
'''
|
||||
file_path = os.path.join(media_folder, file_name)
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
return Response({'message': 'File deleted successfully'}, status=status.HTTP_204_NO_CONTENT)
|
||||
else:
|
||||
return Response({'error': 'File not found'}, status=status.HTTP_404_NOT_FOUND)
|
||||
@ -159,6 +159,8 @@ STATIC_URL = 'static/'
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
|
||||
MEDIA_URL = '/media/'
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
|
||||
|
||||
CSRF_TRUSTED_ORIGINS = [
|
||||
'http://localhost:3000',
|
||||
|
||||
@ -16,8 +16,8 @@ Including another URLconf
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.urls import path, re_path
|
||||
from django.conf.urls import include
|
||||
from .views import my_form
|
||||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
from apimanager.views import (
|
||||
UserDataUpdateAPIView,
|
||||
UserDataListAPIView,
|
||||
@ -32,7 +32,8 @@ from apimanager.views import (
|
||||
BlogRetrieveAPIView,
|
||||
BlogDeleteAPIView,
|
||||
BlogsByCategoryAPIView,
|
||||
MediaUpload
|
||||
MediaUpload,
|
||||
ListMedia
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
@ -51,4 +52,6 @@ urlpatterns = [
|
||||
path('data/blog/update/<slug:blog_id>/', BlogUpdateAPIView.as_view(), name='blog-update-view'),
|
||||
path('data/blog/delete/<slug:blog_id>/', BlogDeleteAPIView.as_view(), name='blog-delete-view'),
|
||||
path('data/upload/', MediaUpload.as_view(), name='media-upload'),
|
||||
path('data/media/<str:resource_type>/<str:resource_id>/', ListMedia.as_view(), name='list-media'),
|
||||
]
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 593 KiB |
@ -55,7 +55,7 @@ function HomePage(props) {
|
||||
This field cannot be empty
|
||||
</FormFeedback>:''}
|
||||
</InputGroup>
|
||||
<EditorComponent GlobalTheme={GlobalTheme} ThemeConfig={ThemeConfig} content={UserData.introContent} setContent={setIntroContent}/>
|
||||
<EditorComponent notificationToggler={props.notificationToggler} GlobalTheme={GlobalTheme} ThemeConfig={ThemeConfig} content={UserData.introContent} setContent={setIntroContent} resourceType='homepage' resourceId='homepage'/>
|
||||
</>
|
||||
<ButtonGroup className={`mt-4`}>
|
||||
<Button onClick={() => setInfo()} color={ThemeConfig[GlobalTheme].buttonColor} outline>Save Data</Button>
|
||||
|
||||
52
frontend/src/components/editable/shared/media-lister.jsx
Normal file
52
frontend/src/components/editable/shared/media-lister.jsx
Normal file
@ -0,0 +1,52 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Button } from 'reactstrap';
|
||||
import EditableDataService from '../../../services/editable-data-service';
|
||||
|
||||
function MediaLister(props) {
|
||||
const [media, setMedia] = useState([]);
|
||||
|
||||
const fetchMedia = async () => {
|
||||
try {
|
||||
const response = await EditableDataService.getData(`/data/media/${props.resourceType}/${props.resourceId}/`);
|
||||
setMedia(response.data.media);
|
||||
} catch (error) {
|
||||
props.notificationToggler('Error fetching media', 'danger')
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchMedia();
|
||||
}, []);
|
||||
|
||||
const deleteMedia = (mediaUrl) => {
|
||||
// Extract the file name from the mediaUrl
|
||||
const fileName = mediaUrl.substring(mediaUrl.lastIndexOf('/') + 1);
|
||||
|
||||
EditableDataService.deleteData(`/data/media/${props.resourceType}/${props.resourceId}/?file=${fileName}`)
|
||||
.then(() => {
|
||||
props.notificationToggler('Media deleted')
|
||||
fetchMedia()
|
||||
})
|
||||
.catch(error => {
|
||||
props.notificationToggler('Error deleting media', 'danger')
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h4>
|
||||
Choose media to insert
|
||||
</h4>
|
||||
{media.map(image => (
|
||||
<div className={'mb-2'} style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(150px, 1fr))', gap: '1px' }} key={image}>
|
||||
<img src={image} style={{ width: '150px', height: 'auto' }} />
|
||||
<Button color={'success'} onClick={() => props.setMedia(image)} style={{ cursor: 'pointer' }}>Upload</Button>
|
||||
<Button color={'danger'} onClick={() => deleteMedia(image)} style={{ cursor: 'pointer' }}>Remove</Button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default MediaLister;
|
||||
@ -1,5 +1,6 @@
|
||||
import React, { useState } from 'react';
|
||||
import FileComponent from './file-component.jsx';
|
||||
import MediaLister from './media-lister.jsx';
|
||||
import { Button, ButtonGroup, Modal, ModalHeader, ModalBody, ModalFooter} from 'reactstrap';
|
||||
|
||||
function MediaUpload(props) {
|
||||
@ -36,9 +37,7 @@ function MediaUpload(props) {
|
||||
<div className="mt-3">
|
||||
{ action === 'insert' ?
|
||||
<div>
|
||||
<h4>
|
||||
Choose media to insert
|
||||
</h4>
|
||||
<MediaLister setMedia={props.setMedia} notificationToggler={props.notificationToggler} resourceType={props.resourceType} resourceId={props.resourceId} />
|
||||
</div>:
|
||||
<div>
|
||||
<FileComponent notificationToggler={props.notificationToggler} resourceType={props.resourceType} resourceId={props.resourceId} />
|
||||
|
||||
@ -24,7 +24,7 @@ function ImageNode(props) {
|
||||
return (
|
||||
<NodeViewWrapper className={className} data-drag-handle>
|
||||
<div className="image-container">
|
||||
<img onClick={() => onEditAlt()} className='mx-auto d-block' src={src} alt={alt} />
|
||||
<img className='mx-auto d-block' src={src} alt={alt} />
|
||||
<div className="image-overlay">
|
||||
<span className="image-text mx-auto d-block">
|
||||
{ alt ?
|
||||
|
||||
@ -29,6 +29,16 @@ import MediaUpload from './media-upload.jsx'
|
||||
const MenuBar = (props) => {
|
||||
const { editor } = useCurrentEditor()
|
||||
|
||||
const addMedia = (url) => {
|
||||
editor.commands.setImage({
|
||||
src: url,
|
||||
});
|
||||
};
|
||||
|
||||
if (!editor) {
|
||||
return null
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (editor){
|
||||
const handleChange = () => {
|
||||
@ -321,6 +331,7 @@ const MenuBar = (props) => {
|
||||
>
|
||||
<FontAwesomeIcon icon={faImage}/>
|
||||
</Button>
|
||||
<MediaUpload setMedia={addMedia} notificationToggler={props.notificationToggler} modal={props.modal} toggle={props.toggle} resourceType={props.resourceType} resourceId={props.resourceId}></MediaUpload>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -361,8 +372,7 @@ export default (props) => {
|
||||
if (props.content && GlobalTheme && ThemeConfig)
|
||||
return (
|
||||
<>
|
||||
<MediaUpload notificationToggler={props.notificationToggler} modal={modal} toggle={toggle} resourceType={props.resourceType} resourceId={props.resourceId}></MediaUpload>
|
||||
<EditorProvider slotBefore={<MenuBar modal={modal} toggle={toggle} setContent={props.setContent} GlobalTheme={GlobalTheme} ThemeConfig={ThemeConfig}/>} extensions={extensions} content={props.content}></EditorProvider>
|
||||
<EditorProvider slotBefore={<MenuBar resourceType={props.resourceType} resourceId={props.resourceId} modal={modal} toggle={toggle} notificationToggler={props.notificationToggler} setContent={props.setContent} GlobalTheme={GlobalTheme} ThemeConfig={ThemeConfig}/>} extensions={extensions} content={props.content}></EditorProvider>
|
||||
</>
|
||||
)
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user