From e187634c2ab6451995dc7a947566924185b33c63 Mon Sep 17 00:00:00 2001
From: Ghazi Triki <ghazi.triki@riadvice.tn>
Date: Fri, 14 Oct 2016 16:33:17 +0100
Subject: [PATCH] Using blank-presentation.pdf as default breakout room
 presentation when no current presentation is set to the parent room.

---
 .../core/apps/BreakoutRoomApp.scala           |   4 +-
 .../slides/blank-presentation.pdf             | Bin 0 -> 1868 bytes
 .../grails-app/conf/bigbluebutton.properties  | 441 +++++++++---------
 .../grails-app/conf/spring/resources.xml      |   1 +
 .../api/messaging/MeetingMessageHandler.java  |   2 +-
 .../PresentationUrlDownloadService.java       |  74 +--
 6 files changed, 267 insertions(+), 255 deletions(-)
 create mode 100644 bigbluebutton-config/slides/blank-presentation.pdf

diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/BreakoutRoomApp.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/BreakoutRoomApp.scala
index ac21be1cb2..0d8820440c 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/BreakoutRoomApp.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/BreakoutRoomApp.scala
@@ -41,8 +41,8 @@ trait BreakoutRoomApp extends SystemConfiguration {
     var i = 0
     // in very rare cases the presentation conversion generates an error, what should we do?
     // those cases where default.pdf is deleted from the whiteboard
-    val sourcePresentationId = presModel.getCurrentPresentation().get.id
-    val sourcePresentationSlide = presModel.getCurrentPage().get.num
+    val sourcePresentationId = if (!presModel.getCurrentPresentation().isEmpty) presModel.getCurrentPresentation().get.id else "blank"
+    val sourcePresentationSlide = if (!presModel.getCurrentPage().isEmpty) presModel.getCurrentPage().get.num else 0
     breakoutModel.pendingRoomsNumber = msg.rooms.length;
 
     for (room <- msg.rooms) {
diff --git a/bigbluebutton-config/slides/blank-presentation.pdf b/bigbluebutton-config/slides/blank-presentation.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..ebf6257403b88d9f0eeeb1dfcb096f0c9fc52a9d
GIT binary patch
literal 1868
zcmcIlO>f#j5Y2gi#atpKhp@ZGHg*){5NO({r6CZhl~7cz?KN=%UTT(x^wgi;8Dj&*
z>4y~60?C?vvpe(NoAu$KcX=XPE-(oKO!UCc&KMu4iC%z%ks;%^&vOmDtE`%u2|GV$
zI!_2l%XPvUQ>HQgK1&ubhBl?c9phcI%q_^BR9b4L(z%63Xm#*VFU+!tu@b83^L6x~
zV~g*=uVw;*a(gU?4+;~Hv;}_?>FXp@7iI-xfiW-iAaEV3q%iZB!X~$P?gCue0Xx25
z&LWV+$Z)4>TCJ=8p!12n2d{=KY@yYReJyDeK&b-a87oWe8Fs^nwhC1dNXfLH)2S(?
z4k$7Pj=BK9)hkQgq8wdhvdZgJC}d@;2GLO$w^pMfjPcPjvZaRQMvQ+{GZYj#Wc)Il
zT3w)6e7MZlJ@s^K5>2{&$`Wi*GX73ylY3GSrJR~#G*__(NgX0t7nxlQbkQ}lxyebr
z08i|z#UscyJG|m5Eb&-z#lpeCk0vJsqBh!fv*j@q;XVB=i}l+=J#W@_J^P<psfw3t
zPyVI)67_5OJ67L{C2K2f+pNt%R{tmc`s#K1?eh7eh;`@XSdr8Iq?@d*yX|Plru;Uu
zQw>Mzm}XEbo0+_)EJk{#j*z|-vMoaqIxYhGCi(3ej(W0;(XmIRm-}jx#gE{(f>wAE
z!VOlj3j8m=3$*Krz@LPBYv-q&KRrFQGMXg(V^Yf$ljEKFSrs~EIJf~5n+JT)YkQEw
zCPr?g2l8#qL(lHTq{MODi?uPU5er_3h0R>Ijr8vA=Xyx+Ud;EJIKP7y+{1N{mi<`Z
zHgf}HvVjYlxKK23QiKhhlw|z67F(!ns*Cc2Gs?c>je<AFRh}A<WF5R(hQ{Envwpwo
z!T3z2p4Snnr(_^S)KO_Ch*j!FaYrcM7pX{6PiohRR2s^}O|%y|Z6`>y&_O#%<8yfZ
W7I*7WP=zfmBb^{*hlg*jFWFBbfbT&7

literal 0
HcmV?d00001

diff --git a/bigbluebutton-web/grails-app/conf/bigbluebutton.properties b/bigbluebutton-web/grails-app/conf/bigbluebutton.properties
index 10e12539b3..1c9d5a130a 100755
--- a/bigbluebutton-web/grails-app/conf/bigbluebutton.properties
+++ b/bigbluebutton-web/grails-app/conf/bigbluebutton.properties
@@ -1,220 +1,221 @@
-#
-# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
-#
-# Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
-#
-# This program is free software; you can redistribute it and/or modify it under the
-# terms of the GNU Lesser General Public License as published by the Free Software
-# Foundation; either version 3.0 of the License, or (at your option) any later
-# version.
-#
-# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License along
-# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
-#
-
-#
-# These are the default properites for BigBlueButton Web application
-
-# Default loglevel. 
-appLogLevel=DEBUG
-
-#----------------------------------------------------
-# Directory where BigBlueButton stores uploaded slides
-presentationDir=/var/bigbluebutton
-
-#----------------------------------------------------
-# Directory where SWFTOOLS (pdf2swf, jpeg2swf, png2swf) are located
-swfToolsDir=/usr/bin
-
-#----------------------------------------------------
-# Directory where ImageMagick's convert executable is located
-imageMagickDir=/usr/bin
-
-#----------------------------------------------------
-# Use fullpath to ghostscript executable since the exec names are different
-# for each platform.
-ghostScriptExec=/usr/bin/gs
-
-#----------------------------------------------------
-# Fonts directory passed into PDF2SWF to support highlighting of texts
-# in the SWF slides.
-fontsDir=/usr/share/fonts
-
-#----------------------------------------------------
-# This is a workaround for a problem converting PDF files, referenced at 
-# http://groups.google.com/group/comp.lang.postscript/browse_thread/thread/c2e264ca76534ce0?pli=1
-noPdfMarkWorkaround=/etc/bigbluebutton/nopdfmark.ps
-
-#----------------------------------------------------
-# These will be copied in cases where the conversion process
-# fails to generate a slide from the uploaded presentation
-BLANK_SLIDE=/var/bigbluebutton/blank/blank-slide.swf
-BLANK_THUMBNAIL=/var/bigbluebutton/blank/blank-thumb.png
-
-#----------------------------------------------------
-# Number of minutes the conversion should take. If it takes
-# more than this time, cancel the conversion process.
-maxConversionTime=5
-
-#----------------------------------------------------
-# Maximum number of pages allowed for an uploaded presentation (default 100).
-maxNumPages=200
-
-#----------------------------------------------------
-# Maximum swf file size for load to the client (default 500000).
-MAX_SWF_FILE_SIZE=500000
-
-#----------------------------------------------------
-# Maximum allowed number of place object tags in the converted SWF, if exceeded the conversion will fallback to full BMP (default 8000)
-placementsThreshold=8000
-
-# Maximum allowed number of bitmap images in the converted SWF, if exceeded the conversion will fallback to full BMP (default 8000)
-imageTagThreshold=8000
-
-# Maximum allowed number of define text tags in the converted SWF, if exceeded the conversion will fallback to full BMP (default 2500)
-defineTextThreshold=2500
-
-#------------------------------------
-# Number of threads in the pool to do the presentation conversion.
-#------------------------------------
-numConversionThreads=2
-
-#----------------------------------------------------
-# Additional conversion of the presentation slides to SVG
-# to be used in the HTML5 client
-svgImagesRequired=false
-
-# Default number of digits for voice conference users joining through the PSTN.
-defaultNumDigitsForTelVoice=5
-
-#----------------------------------------------------
-# Default dial access number
-defaultDialAccessNumber=613-555-1234
-
-#----------------------------------------------------
-# Default welcome message to display when the participant joins the web
-# conference. This is only used for the old scheduling which will be
-# removed in the future. Use the API to create a conference.
-defaultWelcomeMessage=<br>Welcome to <b>%%CONFNAME%%</b>!<br><br>For help on using BigBlueButton see these (short) <a href="event:http://www.bigbluebutton.org/content/videos"><u>tutorial videos</u></a>.<br><br>To join the audio bridge click the headset icon (upper-left hand corner).  Use a headset to avoid causing background noise for others.<br>
-defaultWelcomeMessageFooter=This server is running <a href="http://docs.bigbluebutton.org/" target="_blank"><u>BigBlueButton</u></a>.
-
-# Default maximum number of users a meeting can have.
-# Current default is 0 (meeting doesn't have a user limit).
-defaultMaxUsers=0
-
-# Default duration of the meeting in minutes.
-# Current default is 0 (meeting doesn't end).
-defaultMeetingDuration=0
-
-# Remove the meeting from memory when the end API is called.
-# This allows 3rd-party apps to recycle the meeting right-away
-# instead of waiting for the meeting to expire (see below).
-removeMeetingWhenEnded=true
-
-# The number of minutes before the system removes the meeting from memory.
-defaultMeetingExpireDuration=1
-
-# The number of minutes the system waits when a meeting is created and when
-# a user joins. If after this period, a user hasn't joined, the meeting is
-# removed from memory.
-defaultMeetingCreateJoinDuration=5
-
-# Disable recording by default. 
-#   true - don't record even if record param in the api call is set to record
-#   false - when record param is passed from api, override this default
-disableRecordingDefault=false
-
-# Start recording when first user joins the meeting.
-# For backward compatibility with 0.81 where whole meeting
-# is recorded.  
-autoStartRecording=false
-
-# Allow the user to start/stop recording.
-allowStartStopRecording=true
-
-#----------------------------------------------------
-# This URL is where the BBB client is accessible. When a user sucessfully
-# enters a name and password, she is redirected here to load the client.
-bigbluebutton.web.serverURL=http://192.168.23.44
-
-
-#----------------------------------------------------
-# Assign URL where the logged-out participant will be redirected after sign-out.
-# If "default", it returns to bigbluebutton.web.serverURL
-bigbluebutton.web.logoutURL=default
-
-# The url of the BigBlueButton client. User's will be redirected here when
-# successfully joining the meeting.
-defaultClientUrl=${bigbluebutton.web.serverURL}/client/BigBlueButton.html
-#defaultClientUrl=http://192.168.0.235/3rd-party.html
-
-# The default avatar image to display if nothing is passed on the JOIN API (avatarURL)
-# call. This avatar is displayed if the user isn't sharing the webcam and
-# the option (displayAvatar) is enabled in config.xml
-defaultAvatarURL=${bigbluebutton.web.serverURL}/client/avatar.png
-
-# The URL of the default configuration
-defaultConfigURL=${bigbluebutton.web.serverURL}/client/conf/config.xml
-
-apiVersion=1.0
-
-# Salt which is used by 3rd-party apps to authenticate api calls
-securitySalt=a820d30da2db356124fce5bd5d8054b4
-
-# Directory where we drop the <meeting-id-recorded>.done file
-recordStatusDir=/var/bigbluebutton/recording/status/recorded
-
-redisHost=127.0.0.1
-redisPort=6379
-
-# The directory where the published/unpublised recordings are located. This is for
-# the get recording* api calls
-publishedDir=/var/bigbluebutton/published
-unpublishedDir=/var/bigbluebutton/unpublished
-
-# The directory where the pre-built configs are stored
-configDir=/var/bigbluebutton/configs
-
-# If the API is enabled.
-serviceEnabled = true
-
-# Test voiceBridge number
-testVoiceBridge=99999
-testConferenceMock=conference-mock-default
-
-#------------------------------------------------------
-# These properties are used to test the conversion process.
-# Conference name folder in ${presentationDir} (see above)
-beans.presentationService.testConferenceMock=${testConferenceMock}
-
-# Conference room folder in ${presentationDir}/${testConferenceMock}
-beans.presentationService.testRoomMock=conference-mock-default
-# Uploaded presentation name
-beans.presentationService.testPresentationName=appkonference
-# Uploaded presentation file
-beans.presentationService.testUploadedPresentation=appkonference.txt
-# Default Uploaded presentation file
-beans.presentationService.defaultUploadedPresentation=${bigbluebutton.web.serverURL}/default.pdf
-
-presentationBaseURL=${bigbluebutton.web.serverURL}/bigbluebutton/presentation
-
-#----------------------------------------------------
-# The URL where the presentations will be loaded from.
-#----------------------------------------------------
-beans.presentationService.presentationBaseUrl=${presentationBaseURL}
-#----------------------------------------------------
-# Inject values into grails service beans
-beans.presentationService.presentationDir=${presentationDir}
-
-#----------------------------------------------------
-# Specify which IPs can do cross domain requests
-accessControlAllowOrigin=${bigbluebutton.web.serverURL}
-
-#----------------------------------------------------
-# The lapsus of seconds for polling the BBB Server in order to check if it's down.
-# After 5 tries if there isn't response, it will be declared down
-checkBBBServerEvery=10
+#
+# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
+#
+# Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
+#
+# This program is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free Software
+# Foundation; either version 3.0 of the License, or (at your option) any later
+# version.
+#
+# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
+#
+
+#
+# These are the default properites for BigBlueButton Web application
+
+# Default loglevel. 
+appLogLevel=DEBUG
+
+#----------------------------------------------------
+# Directory where BigBlueButton stores uploaded slides
+presentationDir=/var/bigbluebutton
+
+#----------------------------------------------------
+# Directory where SWFTOOLS (pdf2swf, jpeg2swf, png2swf) are located
+swfToolsDir=/usr/bin
+
+#----------------------------------------------------
+# Directory where ImageMagick's convert executable is located
+imageMagickDir=/usr/bin
+
+#----------------------------------------------------
+# Use fullpath to ghostscript executable since the exec names are different
+# for each platform.
+ghostScriptExec=/usr/bin/gs
+
+#----------------------------------------------------
+# Fonts directory passed into PDF2SWF to support highlighting of texts
+# in the SWF slides.
+fontsDir=/usr/share/fonts
+
+#----------------------------------------------------
+# This is a workaround for a problem converting PDF files, referenced at 
+# http://groups.google.com/group/comp.lang.postscript/browse_thread/thread/c2e264ca76534ce0?pli=1
+noPdfMarkWorkaround=/etc/bigbluebutton/nopdfmark.ps
+
+#----------------------------------------------------
+# These will be copied in cases where the conversion process
+# fails to generate a slide from the uploaded presentation
+BLANK_SLIDE=/var/bigbluebutton/blank/blank-slide.swf
+BLANK_PRESENTATION=/var/bigbluebutton/blank/blank-presentation.pdf
+BLANK_THUMBNAIL=/var/bigbluebutton/blank/blank-thumb.png
+
+#----------------------------------------------------
+# Number of minutes the conversion should take. If it takes
+# more than this time, cancel the conversion process.
+maxConversionTime=5
+
+#----------------------------------------------------
+# Maximum number of pages allowed for an uploaded presentation (default 100).
+maxNumPages=200
+
+#----------------------------------------------------
+# Maximum swf file size for load to the client (default 500000).
+MAX_SWF_FILE_SIZE=500000
+
+#----------------------------------------------------
+# Maximum allowed number of place object tags in the converted SWF, if exceeded the conversion will fallback to full BMP (default 8000)
+placementsThreshold=8000
+
+# Maximum allowed number of bitmap images in the converted SWF, if exceeded the conversion will fallback to full BMP (default 8000)
+imageTagThreshold=8000
+
+# Maximum allowed number of define text tags in the converted SWF, if exceeded the conversion will fallback to full BMP (default 2500)
+defineTextThreshold=2500
+
+#------------------------------------
+# Number of threads in the pool to do the presentation conversion.
+#------------------------------------
+numConversionThreads=2
+
+#----------------------------------------------------
+# Additional conversion of the presentation slides to SVG
+# to be used in the HTML5 client
+svgImagesRequired=false
+
+# Default number of digits for voice conference users joining through the PSTN.
+defaultNumDigitsForTelVoice=5
+
+#----------------------------------------------------
+# Default dial access number
+defaultDialAccessNumber=613-555-1234
+
+#----------------------------------------------------
+# Default welcome message to display when the participant joins the web
+# conference. This is only used for the old scheduling which will be
+# removed in the future. Use the API to create a conference.
+defaultWelcomeMessage=<br>Welcome to <b>%%CONFNAME%%</b>!<br><br>For help on using BigBlueButton see these (short) <a href="event:http://www.bigbluebutton.org/content/videos"><u>tutorial videos</u></a>.<br><br>To join the audio bridge click the headset icon (upper-left hand corner).  Use a headset to avoid causing background noise for others.<br>
+defaultWelcomeMessageFooter=This server is running <a href="http://docs.bigbluebutton.org/" target="_blank"><u>BigBlueButton</u></a>.
+
+# Default maximum number of users a meeting can have.
+# Current default is 0 (meeting doesn't have a user limit).
+defaultMaxUsers=0
+
+# Default duration of the meeting in minutes.
+# Current default is 0 (meeting doesn't end).
+defaultMeetingDuration=0
+
+# Remove the meeting from memory when the end API is called.
+# This allows 3rd-party apps to recycle the meeting right-away
+# instead of waiting for the meeting to expire (see below).
+removeMeetingWhenEnded=true
+
+# The number of minutes before the system removes the meeting from memory.
+defaultMeetingExpireDuration=1
+
+# The number of minutes the system waits when a meeting is created and when
+# a user joins. If after this period, a user hasn't joined, the meeting is
+# removed from memory.
+defaultMeetingCreateJoinDuration=5
+
+# Disable recording by default. 
+#   true - don't record even if record param in the api call is set to record
+#   false - when record param is passed from api, override this default
+disableRecordingDefault=false
+
+# Start recording when first user joins the meeting.
+# For backward compatibility with 0.81 where whole meeting
+# is recorded.  
+autoStartRecording=false
+
+# Allow the user to start/stop recording.
+allowStartStopRecording=true
+
+#----------------------------------------------------
+# This URL is where the BBB client is accessible. When a user sucessfully
+# enters a name and password, she is redirected here to load the client.
+bigbluebutton.web.serverURL=http://192.168.23.44
+
+
+#----------------------------------------------------
+# Assign URL where the logged-out participant will be redirected after sign-out.
+# If "default", it returns to bigbluebutton.web.serverURL
+bigbluebutton.web.logoutURL=default
+
+# The url of the BigBlueButton client. User's will be redirected here when
+# successfully joining the meeting.
+defaultClientUrl=${bigbluebutton.web.serverURL}/client/BigBlueButton.html
+#defaultClientUrl=http://192.168.0.235/3rd-party.html
+
+# The default avatar image to display if nothing is passed on the JOIN API (avatarURL)
+# call. This avatar is displayed if the user isn't sharing the webcam and
+# the option (displayAvatar) is enabled in config.xml
+defaultAvatarURL=${bigbluebutton.web.serverURL}/client/avatar.png
+
+# The URL of the default configuration
+defaultConfigURL=${bigbluebutton.web.serverURL}/client/conf/config.xml
+
+apiVersion=1.0
+
+# Salt which is used by 3rd-party apps to authenticate api calls
+securitySalt=a820d30da2db356124fce5bd5d8054b4
+
+# Directory where we drop the <meeting-id-recorded>.done file
+recordStatusDir=/var/bigbluebutton/recording/status/recorded
+
+redisHost=127.0.0.1
+redisPort=6379
+
+# The directory where the published/unpublised recordings are located. This is for
+# the get recording* api calls
+publishedDir=/var/bigbluebutton/published
+unpublishedDir=/var/bigbluebutton/unpublished
+
+# The directory where the pre-built configs are stored
+configDir=/var/bigbluebutton/configs
+
+# If the API is enabled.
+serviceEnabled = true
+
+# Test voiceBridge number
+testVoiceBridge=99999
+testConferenceMock=conference-mock-default
+
+#------------------------------------------------------
+# These properties are used to test the conversion process.
+# Conference name folder in ${presentationDir} (see above)
+beans.presentationService.testConferenceMock=${testConferenceMock}
+
+# Conference room folder in ${presentationDir}/${testConferenceMock}
+beans.presentationService.testRoomMock=conference-mock-default
+# Uploaded presentation name
+beans.presentationService.testPresentationName=appkonference
+# Uploaded presentation file
+beans.presentationService.testUploadedPresentation=appkonference.txt
+# Default Uploaded presentation file
+beans.presentationService.defaultUploadedPresentation=${bigbluebutton.web.serverURL}/default.pdf
+
+presentationBaseURL=${bigbluebutton.web.serverURL}/bigbluebutton/presentation
+
+#----------------------------------------------------
+# The URL where the presentations will be loaded from.
+#----------------------------------------------------
+beans.presentationService.presentationBaseUrl=${presentationBaseURL}
+#----------------------------------------------------
+# Inject values into grails service beans
+beans.presentationService.presentationDir=${presentationDir}
+
+#----------------------------------------------------
+# Specify which IPs can do cross domain requests
+accessControlAllowOrigin=${bigbluebutton.web.serverURL}
+
+#----------------------------------------------------
+# The lapsus of seconds for polling the BBB Server in order to check if it's down.
+# After 5 tries if there isn't response, it will be declared down
+checkBBBServerEvery=10
diff --git a/bigbluebutton-web/grails-app/conf/spring/resources.xml b/bigbluebutton-web/grails-app/conf/spring/resources.xml
index 48cc70b20a..4c8319a9cd 100755
--- a/bigbluebutton-web/grails-app/conf/spring/resources.xml
+++ b/bigbluebutton-web/grails-app/conf/spring/resources.xml
@@ -61,6 +61,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
     <property name="presentationBaseURL" value="${presentationBaseURL}"/>
    	<property name="pageExtractor" ref="pageExtractor"/>  
     <property name="documentConversionService" ref="documentConversionService"/>
+  	<property name="blankPresentation" value="${BLANK_PRESENTATION}"/>
   </bean>
   
   <bean id="recordingService" class="org.bigbluebutton.api.RecordingService" >
diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MeetingMessageHandler.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MeetingMessageHandler.java
index 31b93ee866..9bfd168087 100755
--- a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MeetingMessageHandler.java
+++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MeetingMessageHandler.java
@@ -89,7 +89,7 @@ public class MeetingMessageHandler implements MessageHandler {
           }
           else if (EndBreakoutRoomRequest.NAME.equals(messageName)) {
             EndBreakoutRoomRequest msg = new Gson().fromJson(message, EndBreakoutRoomRequest.class);
-            log.info("Received an end breakout room request message for breakout meeting id=[{}]", msg.payload.meetingId);
+            log.info("Received end breakout room request message for breakout meeting id=[{}]", msg.payload.meetingId);
             for (MessageListener listener : listeners) {
               listener.handle(new EndBreakoutRoom(msg.payload.meetingId));
             }
diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/PresentationUrlDownloadService.java b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/PresentationUrlDownloadService.java
index f28a59b316..63836e91e4 100755
--- a/bigbluebutton-web/src/java/org/bigbluebutton/presentation/PresentationUrlDownloadService.java
+++ b/bigbluebutton-web/src/java/org/bigbluebutton/presentation/PresentationUrlDownloadService.java
@@ -27,6 +27,7 @@ public class PresentationUrlDownloadService {
     private DocumentConversionService documentConversionService;
     private String presentationBaseURL;
     private String presentationDir;
+    private String BLANK_PRESENTATION;
 
     public void processUploadedPresentation(UploadedPresentation uploadedPres) {
         documentConversionService.processDocument(uploadedPres);
@@ -43,11 +44,12 @@ public class PresentationUrlDownloadService {
     public void extractPage(String sourceMeetingId, String presentationId,
             Integer presentationSlide, String destinationMeetingId) {
 
-        // Construct the source meeting path
+        // Build the source meeting path
         File sourceMeetingPath = new File(presentationDir + File.separator
                 + sourceMeetingId + File.separator + sourceMeetingId
                 + File.separator + presentationId);
 
+        // Find the source meeting presentation file
         final String presentationFilter = presentationId;
         FilenameFilter pdfFilter = new FilenameFilter() {
             public boolean accept(File dir, String name) {
@@ -57,7 +59,7 @@ public class PresentationUrlDownloadService {
         };
 
         File[] matches = sourceMeetingPath.listFiles(pdfFilter);
-        if (matches.length != 1) {
+        if (matches != null && matches.length != 1) {
             // No PDF presentation was found, we look for an image presentation
             FilenameFilter imgFlter = new FilenameFilter() {
                 public boolean accept(File dir, String name) {
@@ -67,40 +69,44 @@ public class PresentationUrlDownloadService {
 
             matches = sourceMeetingPath.listFiles(imgFlter);
         }
-        if (matches.length != 1) {
-            log.info("Not matching PDF file with prefix {} found at {}",
+        File sourcePresentationFile;
+        if (matches == null || matches.length != 1) {
+            log.warn(
+                    "Not matching PDF file with prefix {} found at {}. Using the default blank PDF",
                     sourceMeetingId, sourceMeetingPath);
+            sourcePresentationFile = new File(BLANK_PRESENTATION);
         } else {
-            File sourcePresentationFile = matches[0];
-            String filenameExt = FilenameUtils
-                    .getExtension(sourcePresentationFile.getName());
-            String presId = generatePresentationId(presentationId);
-            String newFilename = Util.createNewFilename(presId, filenameExt);
-
-            File uploadDir = createPresentationDirectory(destinationMeetingId,
-                    presentationDir, presId);
-            String newFilePath = uploadDir.getAbsolutePath()
-                    + File.separatorChar + newFilename;
-            File newPresentation = new File(newFilePath);
-
-            if (sourcePresentationFile.getName().toLowerCase().endsWith("pdf")) {
-                pageExtractor.extractPage(sourcePresentationFile, new File(
-                        newFilePath), presentationSlide);
-            } else {
-                try {
-                    FileUtils.copyFile(sourcePresentationFile, newPresentation);
-                } catch (IOException e) {
-                    log.error("Could not copy presentation {} to {}",
-                            sourcePresentationFile.getAbsolutePath(),
-                            newPresentation.getAbsolutePath());
-                    e.printStackTrace();
-                }
+            sourcePresentationFile = matches[0];
+        }
+        // Build the target meeting path
+        String filenameExt = FilenameUtils.getExtension(sourcePresentationFile
+                .getName());
+        String presId = generatePresentationId(presentationId);
+        String newFilename = Util.createNewFilename(presId, filenameExt);
+
+        File uploadDir = createPresentationDirectory(destinationMeetingId,
+                presentationDir, presId);
+        String newFilePath = uploadDir.getAbsolutePath() + File.separatorChar
+                + newFilename;
+        File newPresentation = new File(newFilePath);
+
+        if (sourcePresentationFile.getName().toLowerCase().endsWith("pdf")) {
+            pageExtractor.extractPage(sourcePresentationFile, new File(
+                    newFilePath), presentationSlide);
+        } else {
+            try {
+                FileUtils.copyFile(sourcePresentationFile, newPresentation);
+            } catch (IOException e) {
+                log.error("Could not copy presentation {} to {}",
+                        sourcePresentationFile.getAbsolutePath(),
+                        newPresentation.getAbsolutePath());
+                e.printStackTrace();
             }
-
-            processUploadedFile(destinationMeetingId, presId, "default-"
-                    + presentationSlide.toString() + "." + filenameExt,
-                    newPresentation);
         }
+
+        processUploadedFile(destinationMeetingId, presId, "default-"
+                + presentationSlide.toString() + "." + filenameExt,
+                newPresentation);
     }
 
     public String generatePresentationId(String name) {
@@ -220,4 +226,8 @@ public class PresentationUrlDownloadService {
         this.documentConversionService = documentConversionService;
     }
 
+    public void setBlankPresentation(String blankPresentation) {
+        this.BLANK_PRESENTATION = blankPresentation;
+    }
+
 }
-- 
GitLab