44
55use Illuminate \Database \Eloquent \ModelNotFoundException ;
66use Illuminate \Http \Request ;
7+ use Illuminate \Support \Facades \DB ;
78use Illuminate \Support \Facades \File ;
89use Illuminate \Support \Facades \Notification ;
910use Illuminate \Validation \Rule ;
2324use ProcessMaker \Models \SettingsMenus ;
2425use ProcessMaker \Models \User ;
2526use ProcessMaker \Notifications \BundleUpdatedNotification ;
27+ use ProcessMaker \Services \DevLink \BundleFingerprint ;
2628
2729class DevLinkController extends Controller
2830{
@@ -146,14 +148,21 @@ public function remoteBundles(Request $request, DevLink $devLink)
146148 return $ devLink ->remoteBundles ($ request ->input ('filter ' ));
147149 }
148150
149- public function createBundle (Request $ request )
151+ public function createBundle (Request $ request, BundleFingerprint $ fingerprint )
150152 {
151- $ bundle = new Bundle ();
152- $ bundle ->name = $ request ->input ('name ' );
153- $ bundle ->description = $ request ->input ('description ' );
154- $ bundle ->published = (bool ) $ request ->input ('published ' , false );
155- $ bundle ->version = 1 ;
156- $ bundle ->saveOrFail ();
153+ $ bundle = DB ::transaction (function () use ($ request , $ fingerprint ) {
154+ $ bundle = new Bundle ();
155+ $ bundle ->name = $ request ->input ('name ' );
156+ $ bundle ->description = $ request ->input ('description ' );
157+ $ bundle ->published = (bool ) $ request ->input ('published ' , false );
158+ $ bundle ->version = 1 ;
159+ $ bundle ->saveOrFail ();
160+
161+ $ bundle ->published_fingerprint = $ fingerprint ->calculate ($ bundle );
162+ $ bundle ->saveOrFail ();
163+
164+ return $ bundle ;
165+ });
157166
158167 return $ bundle ;
159168 }
@@ -170,12 +179,34 @@ public function updateBundle(Request $request, Bundle $bundle)
170179 return $ bundle ;
171180 }
172181
173- public function increaseBundleVersion (Bundle $ bundle )
182+ public function increaseBundleVersion (Bundle $ bundle, BundleFingerprint $ fingerprint )
174183 {
175- $ bundle ->notifyBundleUpdated ();
184+ $ bundle ->validateEditable ();
176185
177- $ bundle ->version = $ bundle ->version + 1 ;
178- $ bundle ->saveOrFail ();
186+ $ bundle = DB ::transaction (function () use ($ bundle , $ fingerprint ) {
187+ $ lockedBundle = Bundle::whereKey ($ bundle ->id )->lockForUpdate ()->firstOrFail ();
188+ $ lockedBundle ->validateEditable ();
189+ $ currentFingerprint = $ fingerprint ->calculate ($ lockedBundle );
190+
191+ // Legacy source bundles have no trustworthy published snapshot to backfill from.
192+ // Their first publication establishes the baseline; later identical attempts are blocked.
193+ if (
194+ $ lockedBundle ->published_fingerprint !== null
195+ && hash_equals ($ lockedBundle ->published_fingerprint , $ currentFingerprint )
196+ ) {
197+ throw ValidationException::withMessages ([
198+ '* ' => 'There are no changes to publish for this bundle. ' ,
199+ ]);
200+ }
201+
202+ $ lockedBundle ->version = $ lockedBundle ->version + 1 ;
203+ $ lockedBundle ->published_fingerprint = $ currentFingerprint ;
204+ $ lockedBundle ->saveOrFail ();
205+
206+ return $ lockedBundle ;
207+ });
208+
209+ $ bundle ->notifyBundleUpdated ();
179210
180211 return $ bundle ;
181212 }
@@ -202,10 +233,13 @@ public function bundleUpdated($bundleId, $token)
202233
203234 public function deleteBundle (Bundle $ bundle )
204235 {
205- $ bundle ->assets ()->delete ();
206- $ bundle ->settings ()->delete ();
207- $ bundle ->instances ()->delete ();
208- $ bundle ->delete ();
236+ DB ::transaction (function () use ($ bundle ) {
237+ $ lockedBundle = Bundle::whereKey ($ bundle ->id )->lockForUpdate ()->firstOrFail ();
238+ $ lockedBundle ->assets ()->delete ();
239+ $ lockedBundle ->settings ()->delete ();
240+ $ lockedBundle ->instances ()->delete ();
241+ $ lockedBundle ->delete ();
242+ });
209243 }
210244
211245 public function installRemoteBundle (Request $ request , DevLink $ devLink , $ remoteBundleId )
@@ -378,14 +412,20 @@ public function remoteBundleVersion(DevLink $devLink, $remoteBundleId)
378412
379413 public function deleteBundleAsset (BundleAsset $ bundleAsset )
380414 {
381- $ bundleAsset ->delete ();
415+ DB ::transaction (function () use ($ bundleAsset ) {
416+ Bundle::whereKey ($ bundleAsset ->bundle_id )->lockForUpdate ()->firstOrFail ();
417+ $ bundleAsset ->delete ();
418+ });
382419
383420 return response ()->json (['message ' => 'Bundle asset association deleted. ' ], 200 );
384421 }
385422
386423 public function deleteBundleSetting (BundleSetting $ bundleSetting )
387424 {
388- $ bundleSetting ->delete ();
425+ DB ::transaction (function () use ($ bundleSetting ) {
426+ Bundle::whereKey ($ bundleSetting ->bundle_id )->lockForUpdate ()->firstOrFail ();
427+ $ bundleSetting ->delete ();
428+ });
389429
390430 return response ()->json (['message ' => 'Bundle setting deleted. ' ], 200 );
391431 }
0 commit comments