Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature (api): Add preUploadedPresentation param to API's /create via GET #19116

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 16 additions & 1 deletion bbb-common-web/src/main/java/org/bigbluebutton/api/Util.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;

public final class Util {
Expand All @@ -20,6 +23,14 @@ private Util() {
throw new IllegalStateException("Utility class");
}

public static String extractFilenameFromUrl(String preUploadedPresentation) throws MalformedURLException {
URL url = new URL(preUploadedPresentation);
String filename = FilenameUtils.getName(url.getPath());
String extension = FilenameUtils.getExtension(url.getPath());
if (extension == null || extension.isEmpty()) return null;
return filename;
}

public static boolean isMeetingIdValidFormat(String id) {
Matcher matcher = MEETING_ID_PATTERN.matcher(id);
if (matcher.matches()) {
Expand Down Expand Up @@ -51,7 +62,11 @@ public static String generatePresentationId(String presFilename) {
}

public static String createNewFilename(String presId, String fileExt) {
return presId + "." + fileExt;
if (!fileExt.isEmpty()) {
return presId + "." + fileExt;
} else {
return presId;
}
}

public static File createPresentationDir(String meetingId, String presentationDir, String presentationId) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.bigbluebutton.presentation;

import org.bigbluebutton.api.domain.Extension;

import java.util.*;

import static org.bigbluebutton.presentation.FileTypeConstants.*;
Expand Down Expand Up @@ -43,6 +45,15 @@ public class MimeTypeUtils {
put(FileTypeConstants.SVG, Arrays.asList(SVG));
}
};

public String getExtensionBasedOnMimeType(String mimeType) {
return EXTENSIONS_MIME.entrySet()
.stream()
.filter(entry -> entry.getValue().contains(mimeType))
.map(Map.Entry::getKey)
.findFirst()
.orElse(null);
}

public Boolean extensionMatchMimeType(String mimeType, String finalExtension) {
finalExtension = finalExtension.toLowerCase();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,21 @@ public static String detectMimeType(File pres) {
return "";
}

public static String detectFileExtensionBasedOnMimeType(File pres) {
String mimeType = detectMimeType(pres);
return mimeTypeUtils.getExtensionBasedOnMimeType(mimeType);
}

public static Boolean isPresentationMimeTypeValid(File pres, String fileExtension) {
String mimeType = detectMimeType(pres);

if (mimeType.equals("")) {
log.error("Not able to detect mimeType.");
return false;
}

if (!mimeTypeUtils.getValidMimeTypes().contains(mimeType)) {
log.error("MimeType is not valid for this meeting, [{}]", mimeType);
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package org.bigbluebutton.web.controllers
import com.google.gson.Gson
import grails.web.context.ServletContextHolder
import groovy.json.JsonBuilder
import groovy.xml.MarkupBuilder
import org.apache.commons.codec.binary.Base64
import org.apache.commons.codec.digest.DigestUtils
import org.apache.commons.io.FilenameUtils
Expand Down Expand Up @@ -1389,6 +1390,38 @@ class ApiController {
def listOfPresentation = []
def presentationListHasCurrent = false

Boolean hasPresentationUrlInParameter = false


String[] pu = request.getParameterMap().get("preUploadedPresentation")
String[] puName = request.getParameterMap().get("preUploadedPresentationName")
if (pu != null) {
String preUploadedPresentation = pu[0]
hasPresentationUrlInParameter = true
def xmlString = new StringWriter()
def xml = new MarkupBuilder(xmlString)
String filename
if (puName == null) {
filename = Util.extractFilenameFromUrl(preUploadedPresentation)
if (filename == null) {
filename = "untitled"
}
} else {
filename = puName[0]
}
xml.document (
removable: "true",
downloadable: "false",
url: preUploadedPresentation,
filename: filename,
isPreUploadedPresentationFromParameter: "true"
)

def parsedXml = new XmlSlurper().parseText(xmlString.toString())

listOfPresentation << parsedXml
}

// This part of the code is responsible for organize the presentations in a certain order
// It selects the one that has the current=true, and put it in the 0th place.
// Afterwards, the 0th presentation is going to be uploaded first, which spares processing time
Expand All @@ -1397,9 +1430,16 @@ class ApiController {
log.warn("Insert Document API called without a payload - ignoring")
return;
}
listOfPresentation << [name: "default", current: true];

if (hasPresentationUrlInParameter) {
if (!preUploadedPresentationOverrideDefault) {
listOfPresentation << [name: "default", current: true]
}
} else {
listOfPresentation << [name: "default", current: true]
}
} else {
Boolean hasCurrent = false;
Boolean hasCurrent = hasPresentationUrlInParameter;
Boolean hasPresentationModule = false;
if (xmlModules.containsKey("presentation")) {
def modulePresentation = xmlModules.get("presentation")
Expand Down Expand Up @@ -1438,6 +1478,7 @@ class ApiController {
def Boolean isRemovable = true;
def Boolean isDownloadable = false;
def Boolean isDefaultPresentation = false;
def Boolean isPreUploadedPresentationFromParameter = false;

if (document.name != null && "default".equals(document.name)) {
if (presentationService.defaultUploadedPresentation) {
Expand All @@ -1446,11 +1487,15 @@ class ApiController {
}
downloadAndProcessDocument(presentationService.defaultUploadedPresentation, conf.getInternalId(),
document.current /* default presentation */, '', false,
true, isDefaultPresentation);
true, isDefaultPresentation, isPreUploadedPresentationFromParameter);
} else {
log.error "Default presentation could not be read, it is (" + presentationService.defaultUploadedPresentation + ")", "error"
}
} else {
if (!StringUtils.isEmpty(document.@isPreUploadedPresentationFromParameter.toString())) {
isPreUploadedPresentationFromParameter = java.lang.Boolean.parseBoolean(
document.@isPreUploadedPresentationFromParameter.toString());
}
// Extracting all properties inside the xml
if (!StringUtils.isEmpty(document.@removable.toString())) {
isRemovable = java.lang.Boolean.parseBoolean(document.@removable.toString());
Expand All @@ -1477,7 +1522,7 @@ class ApiController {
fileName = document.@filename.toString();
}
downloadAndProcessDocument(document.@url.toString(), conf.getInternalId(), isCurrent /* default presentation */,
fileName, isDownloadable, isRemovable, isDefaultPresentation);
fileName, isDownloadable, isRemovable, isDefaultPresentation, isPreUploadedPresentationFromParameter);
} else if (!StringUtils.isEmpty(document.@name.toString())) {
def b64 = new Base64()
def decodedBytes = b64.decode(document.text().getBytes())
Expand Down Expand Up @@ -1518,7 +1563,7 @@ class ApiController {
def presId = null

if (presFilename == "" || filenameExt == "") {
log.debug("Upload failed. Invalid filename " + presOrigFilename)
log.debug("Upload failed. Invalid filename " + presOrigFilename)
uploadFailReasons.add("invalid_filename")
uploadFailed = true
} else {
Expand Down Expand Up @@ -1561,7 +1606,8 @@ class ApiController {
}
}

def downloadAndProcessDocument(address, meetingId, current, fileName, isDownloadable, isRemovable, isDefaultPresentation) {
def downloadAndProcessDocument(address, meetingId, current, fileName, isDownloadable, isRemovable,
isDefaultPresentation, isPreUploadedPresentationFromParameter) {
log.debug("ApiController#downloadAndProcessDocument(${address}, ${meetingId}, ${fileName})");
String presOrigFilename;
if (StringUtils.isEmpty(fileName)) {
Expand All @@ -1586,7 +1632,7 @@ class ApiController {
def pres = null
def presId

if (presFilename == "" || filenameExt == "") {
if (presFilename == "" || (filenameExt == "" && !isPreUploadedPresentationFromParameter)) {
log.debug("presentation is null by default")
return
} else {
Expand All @@ -1603,6 +1649,22 @@ class ApiController {
uploadFailReasons.add("failed_to_download_file")
uploadFailed = true
}

if (isPreUploadedPresentationFromParameter && filenameExt.isEmpty()) {
String fileExtension = SupportedFileTypes.detectFileExtensionBasedOnMimeType(pres)
newFilename = Util.createNewFilename(presId, fileExtension)
newFilePath = uploadDir.absolutePath + File.separatorChar + newFilename
File destination = new File(newFilePath)
filenameExt = fileExtension
presFilename = Util.createNewFilename(presFilename, fileExtension)
if (pres.renameTo(destination)) {
log.info("Presentation coming from URL parameter is at ${destination.getAbsolutePath()}")
pres = destination
} else {
log.error("Error while renaming presentation from URL parameter to ${destination.getAbsolutePath()}, " +
"consider sending it through `/insertDocument`")
}
}
} else {
log.error("Null presentation directory meeting=[${meetingId}], presentationDir=[${presentationDir}], presId=[${presId}]")
uploadFailReasons.add("null_presentation_dir")
Expand Down