2020
2121class GeoroadBookController extends AbstractController
2222{
23+ private const int COVER_IMAGE_MAX_BYTES = 5 * 1024 * 1024 ;
24+
25+ /** @var array<string, string> */
26+ private const array COVER_IMAGE_MIME_EXTENSIONS = [
27+ 'image/jpeg ' => 'jpg ' ,
28+ 'image/png ' => 'png ' ,
29+ 'image/webp ' => 'webp ' ,
30+ ];
31+
2332 /**
2433 * @param array<string, string> $locales
2534 * @param list<string> $availableSorts
@@ -101,10 +110,13 @@ public function upload(Request $request): JsonResponse
101110 {
102111 $ payload = $ request ->getPayload ();
103112
104- $ gpx = (string ) $ payload ->get ('gpx ' , '' );
105- $ referenceCode = (string ) $ payload ->get ('referenceCode ' , '' );
106- $ locale = $ payload ->get ('locale ' );
107- $ themeKey = (string ) $ payload ->get ('theme ' , array_key_first ($ this ->themes ));
113+ $ gpx = (string ) $ payload ->get ('gpx ' , '' );
114+ $ referenceCode = (string ) $ payload ->get ('referenceCode ' , '' );
115+ $ locale = $ payload ->get ('locale ' );
116+ $ themeKey = (string ) $ payload ->get ('theme ' , array_key_first ($ this ->themes ));
117+ $ coverTitle = trim ((string ) $ payload ->get ('cover_title ' , '' ));
118+ $ coverDescription = trim ((string ) $ payload ->get ('cover_description ' , '' ));
119+ $ coverImageDataUrl = trim ((string ) $ payload ->get ('cover_image ' , '' ));
108120
109121 if ($ gpx === '' && $ referenceCode === '' ) {
110122 return $ this ->json (['success ' => false , 'message ' => 'A GPX file or a Pocket Query is missing. ' ]);
@@ -122,6 +134,18 @@ public function upload(Request $request): JsonResponse
122134 $ themeKey = array_key_first ($ this ->themes );
123135 }
124136
137+ $ coverImage = null ;
138+ if ($ coverImageDataUrl !== '' ) {
139+ if (!preg_match ('#^data:(image/(?:jpeg|png|webp));base64,(.+)$#s ' , $ coverImageDataUrl , $ m )) {
140+ return $ this ->json (['success ' => false , 'message ' => 'Cover image format is not supported. Use JPEG, PNG or WebP. ' ]);
141+ }
142+ $ binary = base64_decode ($ m [2 ], true );
143+ if ($ binary === false || $ binary === '' || strlen ($ binary ) > self ::COVER_IMAGE_MAX_BYTES ) {
144+ return $ this ->json (['success ' => false , 'message ' => 'Cover image is invalid or exceeds the 5 MB limit. ' ]);
145+ }
146+ $ coverImage = ['binary ' => $ binary , 'extension ' => self ::COVER_IMAGE_MIME_EXTENSIONS [$ m [1 ]]];
147+ }
148+
125149 if ($ referenceCode !== '' ) {
126150 try {
127151 $ gpx = $ this ->downloadPocketQuery ($ referenceCode );
@@ -173,6 +197,15 @@ public function upload(Request $request): JsonResponse
173197 return $ this ->json (['success ' => false , 'message ' => 'Unable to save the GPX file. ' ]);
174198 }
175199
200+ $ cover = null ;
201+ if ($ coverTitle !== '' ) {
202+ $ cover = [
203+ 'title ' => $ coverTitle ,
204+ 'description ' => $ coverDescription !== '' ? $ coverDescription : null ,
205+ 'image ' => $ coverImage !== null ? $ roadbook ->saveCoverImage ($ coverImage ['binary ' ], $ coverImage ['extension ' ]) : null ,
206+ ];
207+ }
208+
176209 $ options = [
177210 'display_note ' => $ bool ($ payload ->get ('note ' )),
178211 'display_long_desc ' => $ bool ($ payload ->get ('long_desc ' )),
@@ -206,7 +239,7 @@ public function upload(Request $request): JsonResponse
206239 }
207240 }
208241
209- $ roadbook ->setContent ($ this ->renderer ->render ($ caches , $ locale , $ options ), $ locale )->cleanHtml ();
242+ $ roadbook ->setContent ($ this ->renderer ->render ($ caches , $ locale , $ options, $ cover ), $ locale )->cleanHtml ();
210243
211244 if ($ displayToc ) {
212245 $ roadbook ->addToc ();
0 commit comments