@@ -90,6 +90,9 @@ public function update($installer)
9090 // Informational log only
9191 }
9292
93+ // Uninstall extensions before removing their files and folders
94+ $ this ->uninstallExtensions ();
95+
9396 // This needs to stay for 2.5 update compatibility
9497 $ this ->deleteUnexistingFiles ();
9598 $ this ->updateManifestCaches ();
@@ -201,6 +204,85 @@ protected function updateDatabaseMysql()
201204 }
202205 }
203206
207+ /**
208+ * Uninstall extensions and optionally migrate their parameters when
209+ * updating from a version older than 5.0.1.
210+ *
211+ * @return void
212+ *
213+ * @since __DEPLOY_VERSION__
214+ */
215+ protected function uninstallExtensions ()
216+ {
217+ // Don't uninstall extensions when not updating from a version older than 5.0.1
218+ if (empty ($ this ->fromVersion ) || version_compare ($ this ->fromVersion , '5.0.1 ' , 'ge ' )) {
219+ return true ;
220+ }
221+
222+ $ extensions = [
223+ /**
224+ * Define here the extensions to be uninstalled and optionally migrated on update.
225+ * For each extension, specify an associative array with following elements (key => value):
226+ * 'type' => Field `type` in the `#__extensions` table
227+ * 'element' => Field `element` in the `#__extensions` table
228+ * 'folder' => Field `folder` in the `#__extensions` table
229+ * 'client_id' => Field `client_id` in the `#__extensions` table
230+ * 'pre_function' => Name of an optional migration function to be called before
231+ * uninstalling, `null` if not used.
232+ */
233+ ];
234+
235+ $ db = Factory::getDbo ();
236+
237+ foreach ($ extensions as $ extension ) {
238+ $ row = $ db ->setQuery (
239+ $ db ->getQuery (true )
240+ ->select ($ db ->quoteName ('extension_id ' ))
241+ ->select ($ db ->quoteName ('params ' ))
242+ ->from ($ db ->quoteName ('#__extensions ' ))
243+ ->where ($ db ->quoteName ('type ' ) . ' = ' . $ db ->quote ($ extension ['type ' ]))
244+ ->where ($ db ->quoteName ('element ' ) . ' = ' . $ db ->quote ($ extension ['element ' ]))
245+ ->where ($ db ->quoteName ('folder ' ) . ' = ' . $ db ->quote ($ extension ['folder ' ]))
246+ ->where ($ db ->quoteName ('client_id ' ) . ' = ' . $ db ->quote ($ extension ['client_id ' ]))
247+ )->loadObject ();
248+
249+ // Skip migrating and uninstalling if the extension doesn't exist
250+ if (!$ row ) {
251+ continue ;
252+ }
253+
254+ // If there is a function for migration to be called before uninstalling, call it
255+ if ($ extension ['pre_function ' ] && method_exists ($ this , $ extension ['pre_function ' ])) {
256+ $ this ->{$ extension ['pre_function ' ]}($ row );
257+ }
258+
259+ try {
260+ $ db ->transactionStart ();
261+
262+ // Unlock and unprotect the plugin so we can uninstall it
263+ $ db ->setQuery (
264+ $ db ->getQuery (true )
265+ ->update ($ db ->quoteName ('#__extensions ' ))
266+ ->set ($ db ->quoteName ('locked ' ) . ' = 0 ' )
267+ ->set ($ db ->quoteName ('protected ' ) . ' = 0 ' )
268+ ->where ($ db ->quoteName ('extension_id ' ) . ' = :extension_id ' )
269+ ->bind (':extension_id ' , $ row ->extension_id , ParameterType::INTEGER )
270+ )->execute ();
271+
272+ // Uninstall the plugin
273+ $ installer = new Installer ();
274+ $ installer ->setDatabase ($ db );
275+ $ installer ->uninstall ($ extension ['type ' ], $ row ->extension_id );
276+
277+ $ db ->transactionCommit ();
278+ } catch (\Exception $ e ) {
279+ $ db ->transactionRollback ();
280+ echo Text::sprintf ('JLIB_DATABASE_ERROR_FUNCTION_FAILED ' , $ e ->getCode (), $ e ->getMessage ()) . '<br> ' ;
281+ throw $ e ;
282+ }
283+ }
284+ }
285+
204286 /**
205287 * Update the manifest caches
206288 *
0 commit comments