33namespace ProcessMaker \Http \Controllers \Api ;
44
55use Illuminate \Database \Eloquent \ModelNotFoundException ;
6+ use Illuminate \Http \Client \ConnectionException ;
7+ use Illuminate \Http \Client \RequestException ;
68use Illuminate \Http \Request ;
7- use Illuminate \Support \Facades \File ;
89use Illuminate \Support \Facades \Notification ;
910use Illuminate \Validation \Rule ;
1011use ProcessMaker \Events \CustomizeUiUpdated ;
1112use ProcessMaker \Exception \ValidationException ;
1213use ProcessMaker \Http \Controllers \Controller ;
14+ use ProcessMaker \Http \Requests \DevLinkStoreRequest ;
1315use ProcessMaker \Http \Resources \ApiCollection ;
14- use ProcessMaker \Jobs \CompileSass ;
1516use ProcessMaker \Jobs \CompileUI ;
1617use ProcessMaker \Jobs \DevLinkInstall ;
1718use ProcessMaker \Models \Bundle ;
2324use ProcessMaker \Models \SettingsMenus ;
2425use ProcessMaker \Models \User ;
2526use ProcessMaker \Notifications \BundleUpdatedNotification ;
27+ use ProcessMaker \Package \PackageDynamicUI \Models \Dashboard ;
28+ use ProcessMaker \Package \PackageDynamicUI \Models \Menu ;
2629
2730class DevLinkController extends Controller
2831{
@@ -56,27 +59,38 @@ public function show(DevLink $devLink)
5659 return $ devLink ;
5760 }
5861
59- public function store (Request $ request )
62+ public function store (DevLinkStoreRequest $ request )
6063 {
61- $ request ->validate ([
62- ' name ' => [ ' required ' ],
63- ' url ' => [ ' required ' , 'url ' ],
64- ] );
65- $ devLink = DevLink:: where ( ' name ' , $ request -> input ( ' name ' ))-> first ();
66- if ( $ devLink ) {
67- $ devLink -> url = $ request -> input ( 'url ' );
68- } else {
69- $ devLink = new DevLink ();
70- $ devLink -> name = $ request -> input ( ' name ' );
71- $ devLink -> url = $ request -> input ( ' url ' );
64+ $ normalizedUrl = $ this -> normalizeUrl ( $ request ->input ( ' url ' ));
65+ $ existingDevLink = DevLink:: query ()
66+ -> get ([ ' id ' , ' name ' , 'url ' ])
67+ -> first ( fn ( DevLink $ devLink ) => $ this -> normalizeUrl ( $ devLink -> url ) === $ normalizedUrl );
68+ if ( $ existingDevLink ) {
69+ throw ValidationException:: withMessages ([
70+ 'url ' => __ (
71+ ' This instance is already linked as :name. Open or reconnect the existing connection. ' ,
72+ [ ' name ' => $ existingDevLink -> name ]
73+ ),
74+ ] );
7275 }
76+
77+ $ devLink = new DevLink ();
78+ $ devLink ->name = $ request ->input ('name ' );
79+ $ devLink ->url = $ normalizedUrl ;
7380 $ devLink ->saveOrFail ();
7481
7582 return $ devLink ;
7683 }
7784
7885 public function update (Request $ request , DevLink $ devLink )
7986 {
87+ $ request ->merge ([
88+ 'name ' => trim ((string ) $ request ->input ('name ' )),
89+ ]);
90+ $ request ->validate ([
91+ 'name ' => ['required ' , 'string ' , Rule::unique ('dev_links ' , 'name ' )->ignore ($ devLink ->id )],
92+ ]);
93+
8094 $ devLink ->name = $ request ->input ('name ' );
8195 $ devLink ->saveOrFail ();
8296
@@ -180,7 +194,7 @@ public function increaseBundleVersion(Bundle $bundle)
180194 return $ bundle ;
181195 }
182196
183- public function bundleUpdated ($ bundleId , $ token )
197+ public function bundleUpdated (int $ bundleId , string $ token )
184198 {
185199 try {
186200 $ bundle = Bundle::where ('remote_id ' , $ bundleId )->firstOrFail ();
@@ -208,7 +222,7 @@ public function deleteBundle(Bundle $bundle)
208222 $ bundle ->delete ();
209223 }
210224
211- public function installRemoteBundle (Request $ request , DevLink $ devLink , $ remoteBundleId )
225+ public function installRemoteBundle (Request $ request , DevLink $ devLink , int $ remoteBundleId )
212226 {
213227 $ updateType = $ request ->input ('updateType ' , DevLinkInstall::MODE_UPDATE );
214228 DevLinkInstall::dispatch (
@@ -342,7 +356,7 @@ public function addSharedAsset(Request $request)
342356 $ sharedAsset ->saveOrFail ();
343357 }
344358
345- public function removeSharedAsset ($ id )
359+ public function removeSharedAsset (int $ id )
346360 {
347361 $ deleted = Setting::destroy ($ id );
348362
@@ -371,9 +385,41 @@ public function installRemoteAsset(Request $request, DevLink $devLink)
371385 ];
372386 }
373387
374- public function remoteBundleVersion (DevLink $ devLink , $ remoteBundleId )
388+ public function remoteBundleVersion (DevLink $ devLink , int $ remoteBundleId )
389+ {
390+ try {
391+ $ payload = $ devLink ->remoteBundle ($ remoteBundleId )->json ();
392+ } catch (RequestException |ConnectionException $ e ) {
393+ return [
394+ 'available ' => false ,
395+ 'version ' => null ,
396+ ];
397+ }
398+
399+ if (!is_array ($ payload )) {
400+ return [
401+ 'available ' => false ,
402+ 'version ' => null ,
403+ ];
404+ }
405+
406+ $ payload ['available ' ] = true ;
407+
408+ return $ payload ;
409+ }
410+
411+ private function normalizeUrl (string $ url ): string
375412 {
376- return $ devLink ->remoteBundle ($ remoteBundleId );
413+ $ parts = parse_url (trim ($ url ));
414+ $ scheme = strtolower ($ parts ['scheme ' ]);
415+ $ host = strtolower ($ parts ['host ' ]);
416+ $ port = $ parts ['port ' ] ?? null ;
417+
418+ if (($ scheme === 'http ' && $ port === 80 ) || ($ scheme === 'https ' && $ port === 443 )) {
419+ $ port = null ;
420+ }
421+
422+ return $ scheme . ':// ' . $ host . ($ port === null ? '' : ': ' . $ port );
377423 }
378424
379425 public function deleteBundleAsset (BundleAsset $ bundleAsset )
@@ -390,80 +436,70 @@ public function deleteBundleSetting(BundleSetting $bundleSetting)
390436 return response ()->json (['message ' => 'Bundle setting deleted. ' ], 200 );
391437 }
392438
393- public function getBundleSetting (Bundle $ bundle , $ settingKey )
439+ public function getBundleSetting (Bundle $ bundle , string $ settingKey )
440+ {
441+ return $ bundle ->settings ()->where ('setting ' , $ settingKey )->first ();
442+ }
443+
444+ public function getBundleSettingPreview (Bundle $ bundle , string $ settingKey ): array
394445 {
395- $ setting = $ bundle -> settings ()-> where ( ' setting ' , $ settingKey )-> first ( );
446+ abort_unless ( in_array ( $ settingKey , [ ' ui_dashboards ' , ' ui_menus ' ], true ), 404 );
396447
397- return $ setting ;
448+ return $ bundle -> settingPreview ( $ settingKey ) ;
398449 }
399450
400- public function getBundleAllSettings ($ settingKey )
451+ public function getBundleAllSettings (string $ settingKey )
401452 {
402- if ($ settingKey === 'ui_settings ' ) {
403- return Setting::whereIn ('key ' , ['css-override ' , 'login-footer ' , 'logo-alt-text ' ])
404- ->get ();
453+ return match ($ settingKey ) {
454+ 'ui_dashboards ' => $ this ->getAvailableDashboards (),
455+ 'ui_menus ' => $ this ->getAvailableMenus (),
456+ 'ui_settings ' => Setting::whereIn ('key ' , ['css-override ' , 'login-footer ' , 'logo-alt-text ' ])->get (),
457+ default => Setting::where ([
458+ ['group_id ' , SettingsMenus::getId ($ settingKey )],
459+ ['hidden ' , 0 ],
460+ ])->get (),
461+ };
462+ }
463+
464+ private function getAvailableDashboards ()
465+ {
466+ if (!class_exists (Dashboard::class)) {
467+ return [];
405468 }
406469
407- return Setting::where ([
408- ['group_id ' , SettingsMenus::getId ($ settingKey )],
409- ['hidden ' , 0 ],
410- ])->get ();
470+ return Dashboard::query ()
471+ ->orderBy ('title ' )
472+ ->get (['id ' , 'title ' ])
473+ ->map (function (Dashboard $ dashboard ) {
474+ return [
475+ 'key ' => $ dashboard ->id ,
476+ 'name ' => $ dashboard ->title ,
477+ ];
478+ })
479+ ->values ();
411480 }
412481
413- public function refreshUi ()
482+ private function getAvailableMenus ()
414483 {
415- CompileUI::dispatch (auth ()->user ()?->id);
416- CustomizeUiUpdated::dispatch ([], [], false );
484+ if (!class_exists (Menu::class)) {
485+ return [];
486+ }
487+
488+ return Menu::query ()
489+ ->orderBy ('name ' )
490+ ->get (['id ' , 'name ' ])
491+ ->map (function (Menu $ menu ) {
492+ return [
493+ 'key ' => $ menu ->id ,
494+ 'name ' => $ menu ->name ,
495+ ];
496+ })
497+ ->values ();
417498 }
418499
419- private function writeColors ( $ data )
500+ public function refreshUi ( Request $ request )
420501 {
421- // Now generate the _colors.scss file
422- $ contents = "// Changed theme colors \n" ;
423- foreach ($ data as $ value ) {
424- $ contents .= $ value ->id . ': ' . $ value ->value . "; \n" ;
425- }
426- File::put (app ()->resourcePath ('sass ' ) . '/_colors.scss ' , $ contents );
427- }
428-
429- /**
430- * Write variables font in file
431- *
432- * @param $sansSerif
433- * @param $serif
434- */
435- private function writeFonts ($ sansSerif )
436- {
437- $ sansSerif = $ sansSerif ? $ sansSerif : $ this ->sansSerifFontDefault ();
438- // Generate the _fonts.scss file
439- $ contents = "// Changed theme fonts \n" ;
440- $ contents .= '$font-family-sans-serif: ' . $ sansSerif ['id ' ] . " !default; \n" ;
441- File::put (app ()->resourcePath ('sass ' ) . '/_fonts.scss ' , $ contents );
442- }
443-
444- /**
445- * run jobs compile
446- */
447- private function compileSass ($ userId )
448- {
449- // Compile the Sass files
450- $ this ->dispatch (new CompileSass ([
451- 'tag ' => 'sidebar ' ,
452- 'origin ' => 'resources/sass/sidebar/sidebar.scss ' ,
453- 'target ' => 'public/css/sidebar.css ' ,
454- 'user ' => $ userId ,
455- ]));
456- $ this ->dispatch (new CompileSass ([
457- 'tag ' => 'app ' ,
458- 'origin ' => 'resources/sass/app.scss ' ,
459- 'target ' => 'public/css/app.css ' ,
460- 'user ' => $ userId ,
461- ]));
462- $ this ->dispatch (new CompileSass ([
463- 'tag ' => 'queues ' ,
464- 'origin ' => 'resources/sass/admin/queues.scss ' ,
465- 'target ' => 'public/css/admin/queues.css ' ,
466- 'user ' => $ userId ,
467- ]));
502+ CompileUI::dispatch ($ request ->user ()?->id);
503+ CustomizeUiUpdated::dispatch ([], [], false );
468504 }
469505}
0 commit comments