diff --git a/administrator/components/com_admin/script.php b/administrator/components/com_admin/script.php
index 039caafc6bc9e..283915c1b0da0 100644
--- a/administrator/components/com_admin/script.php
+++ b/administrator/components/com_admin/script.php
@@ -567,6 +567,12 @@ protected function updateManifestCaches()
{
$extensions = ExtensionHelper::getCoreExtensions();
+ // If we have the search package around, it may not have a manifest cache entry after upgrades from 3.x, so add it to the list
+ if (File::exists(JPATH_ROOT . '/administrator/manifests/packages/pkg_search.xml'))
+ {
+ $extensions[] = array('package', 'pkg_search', '', 0);
+ }
+
// Attempt to refresh manifest caches
$db = Factory::getDbo();
$query = $db->getQuery(true)
@@ -7386,6 +7392,18 @@ public function deleteUnexistingFiles($dryRun = false, $suppressOutput = false)
$this->fixFilenameCasing();
+ /*
+ * Needed for updates from 3.10
+ * If com_search doesn't exist then assume we can delete the search package manifest (included in the update packages)
+ * We deliberately check for the presence of the files in case people have previously uninstalled their search extension
+ * but an update has put the files back. In that case it exists even if they don't believe in it!
+ */
+ if (!File::exists(JPATH_ROOT . '/administrator/components/com_search/search.php')
+ && File::exists(JPATH_ROOT . '/administrator/manifests/packages/pkg_search.xml'))
+ {
+ File::delete(JPATH_ROOT . '/administrator/manifests/packages/pkg_search.xml');
+ }
+
if ($suppressOutput === false && \count($status['folders_errors']))
{
echo implode('
', $status['folders_errors']);
diff --git a/administrator/components/com_admin/sql/updates/mysql/4.0.0-2021-08-17.sql b/administrator/components/com_admin/sql/updates/mysql/4.0.0-2021-08-17.sql
new file mode 100644
index 0000000000000..1522d67fc3869
--- /dev/null
+++ b/administrator/components/com_admin/sql/updates/mysql/4.0.0-2021-08-17.sql
@@ -0,0 +1,16 @@
+INSERT INTO `#__extensions` (`name`, `type`, `element`, `folder`, `client_id`, `enabled`, `access`, `protected`, `manifest_cache`, `params`, `custom_data`, `checked_out`, `checked_out_time`, `ordering`, `state`) VALUES
+('search', 'package', 'pkg_search', '', 0, 1, 1, 0, '', '', '', 0, NULL, 0, 0);
+
+UPDATE `#__extensions` SET `package_id` = (SELECT a.`extension_id` FROM `#__extensions` a WHERE a.`type`='package' AND a.`element`='pkg_search') WHERE `element` = 'com_search' AND `type` = 'component';
+UPDATE `#__extensions` SET `package_id` = (SELECT a.`extension_id` FROM `#__extensions` a WHERE a.`type`='package' AND a.`element`='pkg_search') WHERE `element` = 'mod_search' AND `type` = 'module' AND `client_id` = 0;
+UPDATE `#__extensions` SET `package_id` = (SELECT a.`extension_id` FROM `#__extensions` a WHERE a.`type`='package' AND a.`element`='pkg_search') WHERE `element` = 'categories' AND `type` = 'plugin' AND `folder` = 'search';
+UPDATE `#__extensions` SET `package_id` = (SELECT a.`extension_id` FROM `#__extensions` a WHERE a.`type`='package' AND a.`element`='pkg_search') WHERE `element` = 'contacts' AND `type` = 'plugin' AND `folder` = 'search';
+UPDATE `#__extensions` SET `package_id` = (SELECT a.`extension_id` FROM `#__extensions` a WHERE a.`type`='package' AND a.`element`='pkg_search') WHERE `element` = 'content' AND `type` = 'plugin' AND `folder` = 'search';
+UPDATE `#__extensions` SET `package_id` = (SELECT a.`extension_id` FROM `#__extensions` a WHERE a.`type`='package' AND a.`element`='pkg_search') WHERE `element` = 'newsfeeds' AND `type` = 'plugin' AND `folder` = 'search';
+UPDATE `#__extensions` SET `package_id` = (SELECT a.`extension_id` FROM `#__extensions` a WHERE a.`type`='package' AND a.`element`='pkg_search') WHERE `element` = 'tags' AND `type` = 'plugin' AND `folder` = 'search';
+
+INSERT INTO `#__update_sites` (`name`, `type`, `location`, `enabled`) VALUES
+('Search Update Site', 'extension', 'https://raw.githubusercontent.com/joomla-extensions/search/main/manifest.xml', 1);
+
+INSERT INTO `#__update_sites_extensions` (`update_site_id`, `extension_id`) VALUES
+((SELECT `update_site_id` FROM `#__update_sites` WHERE `name` = 'Search Update Site'), (SELECT `extension_id` FROM `#__extensions` WHERE `element` = 'pkg_search' AND `type` = 'package'));
diff --git a/administrator/components/com_admin/sql/updates/postgresql/4.0.0-2021-08-17.sql b/administrator/components/com_admin/sql/updates/postgresql/4.0.0-2021-08-17.sql
new file mode 100644
index 0000000000000..ea3f7f58f5ecb
--- /dev/null
+++ b/administrator/components/com_admin/sql/updates/postgresql/4.0.0-2021-08-17.sql
@@ -0,0 +1,16 @@
+INSERT INTO "#__extensions" ("name", "type", "element", "folder", "client_id", "enabled", "access", "protected", "manifest_cache", "params", "custom_data", "checked_out", "checked_out_time", "ordering", "state") VALUES
+('search', 'package', 'pkg_search', '', 0, 1, 1, 0, '', '', '', 0, NULL, 0, 0);
+
+UPDATE "#__extensions" SET "package_id" = sub.extension_id FROM (SELECT "extension_id" FROM "#__extensions" WHERE "type"='package' AND "element"='pkg_search') AS sub WHERE "element" = 'com_search' AND "type" = 'component';
+UPDATE "#__extensions" SET "package_id" = sub.extension_id FROM (SELECT "extension_id" FROM "#__extensions" WHERE "type"='package' AND "element"='pkg_search') AS sub WHERE "element" = 'mod_search' AND "type" = 'module' AND "client_id" = 0;
+UPDATE "#__extensions" SET "package_id" = sub.extension_id FROM (SELECT "extension_id" FROM "#__extensions" WHERE "type"='package' AND "element"='pkg_search') AS sub WHERE "element" = 'categories' AND "type" = 'plugin' AND "folder" = 'search';
+UPDATE "#__extensions" SET "package_id" = sub.extension_id FROM (SELECT "extension_id" FROM "#__extensions" WHERE "type"='package' AND "element"='pkg_search') AS sub WHERE "element" = 'contacts' AND "type" = 'plugin' AND "folder" = 'search';
+UPDATE "#__extensions" SET "package_id" = sub.extension_id FROM (SELECT "extension_id" FROM "#__extensions" WHERE "type"='package' AND "element"='pkg_search') AS sub WHERE "element" = 'content' AND "type" = 'plugin' AND "folder" = 'search';
+UPDATE "#__extensions" SET "package_id" = sub.extension_id FROM (SELECT "extension_id" FROM "#__extensions" WHERE "type"='package' AND "element"='pkg_search') AS sub WHERE "element" = 'newsfeeds' AND "type" = 'plugin' AND "folder" = 'search';
+UPDATE "#__extensions" SET "package_id" = sub.extension_id FROM (SELECT "extension_id" FROM "#__extensions" WHERE "type"='package' AND "element"='pkg_search') AS sub WHERE "element" = 'tags' AND "type" = 'plugin' AND "folder" = 'search';
+
+INSERT INTO "#__update_sites" ("name", "type", "location", "enabled") VALUES
+('Search Update Site', 'extension', 'https://raw.githubusercontent.com/joomla-extensions/search/main/manifest.xml', 1);
+
+INSERT INTO "#__update_sites_extensions" ("update_site_id", "extension_id") VALUES
+((SELECT "update_site_id" FROM "#__update_sites" WHERE "name" = 'Search Update Site'), (SELECT "extension_id" FROM "#__extensions" WHERE "element" = 'pkg_search' AND "type" = 'package'));
diff --git a/administrator/manifests/packages/pkg_search.xml b/administrator/manifests/packages/pkg_search.xml
new file mode 100644
index 0000000000000..ee39069e152a7
--- /dev/null
+++ b/administrator/manifests/packages/pkg_search.xml
@@ -0,0 +1,33 @@
+
+
+ pkg_search
+ search
+ 17.08.2021
+ Joomla! Project
+ (C) 2021 Open Source Matters. All rights reserved.
+ admin@joomla.org
+ www.joomla.org
+ Joomla! Project
+ admin@joomla.org
+ www.joomla.org
+ 4.0.0
+ GNU General Public License version 2 or later; see LICENSE.txt
+ script.php
+
+
+ com_search.zip
+ mod_search.zip
+ plg_search_categories.zip
+ plg_search_contacts.zip
+ plg_search_content.zip
+ plg_search_newsfeeds.zip
+ plg_search_tags.zip
+
+
+ en-GB/en-GB.pkg_search.sys.ini
+
+
+
+ https://raw.githubusercontent.com/joomla-extensions/search/main/manifest.xml
+
+
diff --git a/build/build.php b/build/build.php
index ee315e9710863..d87de7da8b490 100644
--- a/build/build.php
+++ b/build/build.php
@@ -545,6 +545,9 @@ function clean_composer(string $dir)
echo "Build full package files.\n";
chdir($time);
+// The search package manifest should not be present for new installs, temporarily move it
+system('mv administrator/manifests/packages/pkg_search.xml ../pkg_search.xml');
+
// Create full archive packages.
if (!$excludeBzip2)
{
@@ -592,6 +595,9 @@ function clean_composer(string $dir)
system('rm images/joomla_black.png');
system('rm images/powered_by.png');
+// Move the search manifest back
+system('mv ../pkg_search.xml administrator/manifests/packages/pkg_search.xml');
+
if (!$excludeBzip2)
{
$packageName = 'Joomla_' . $fullVersion . '-' . $packageStability . '-Update_Package.tar.bz2';