563 lines
14 KiB
PHP
563 lines
14 KiB
PHP
<?php
|
|
/**
|
|
* Akeeba Frontend Framework (FEF)
|
|
*
|
|
* @package fef
|
|
* @copyright (c) 2017-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
|
|
* @license GNU General Public License version 3, or later
|
|
*/
|
|
|
|
defined('_JEXEC') or die();
|
|
|
|
use Joomla\CMS\Date\Date as JDate;
|
|
use Joomla\CMS\Factory as JFactory;
|
|
use Joomla\CMS\Filesystem\File;
|
|
use Joomla\CMS\Filesystem\Folder;
|
|
use Joomla\CMS\Installer\Adapter\FileAdapter as JInstallerAdapterFile;
|
|
use Joomla\CMS\Installer\Installer as JInstaller;
|
|
use Joomla\CMS\Log\Log as JLog;
|
|
|
|
if (class_exists('file_fefInstallerScript'))
|
|
{
|
|
// WTAF?!
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Akeeba FEF Installation Script
|
|
*
|
|
* @noinspection PhpUnused
|
|
*/
|
|
class file_fefInstallerScript
|
|
{
|
|
/**
|
|
* The minimum PHP version required to install this extension
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $minimumPHPVersion = '7.2.0';
|
|
|
|
/**
|
|
* The minimum Joomla! version required to install this extension
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $minimumJoomlaVersion = '3.9.0';
|
|
|
|
/**
|
|
* The maximum Joomla! version this extension can be installed on
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $maximumJoomlaVersion = '4.999.999';
|
|
|
|
/**
|
|
* Joomla! pre-flight event. This runs before Joomla! installs or updates the component. This is our last chance to
|
|
* tell Joomla! if it should abort the installation.
|
|
*
|
|
* @param string $type Installation type (install, update, discover_install)
|
|
* @param JInstaller|JInstallerAdapterFile $parent Parent object
|
|
*
|
|
* @return boolean True to let the installation proceed, false to halt the installation
|
|
*/
|
|
public function preflight($type, $parent)
|
|
{
|
|
// Do not run on uninstall.
|
|
if ($type === 'uninstall')
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Check the minimum PHP version
|
|
if (!empty($this->minimumPHPVersion))
|
|
{
|
|
if (defined('PHP_VERSION'))
|
|
{
|
|
$version = PHP_VERSION;
|
|
}
|
|
elseif (function_exists('phpversion'))
|
|
{
|
|
$version = phpversion();
|
|
}
|
|
else
|
|
{
|
|
$version = '5.0.0'; // all bets are off!
|
|
}
|
|
|
|
if (!version_compare($version, $this->minimumPHPVersion, 'ge'))
|
|
{
|
|
$msg = "<p>You need PHP $this->minimumPHPVersion or later to install this package but you are currently using PHP $version</p>";
|
|
|
|
JLog::add($msg, JLog::WARNING, 'jerror');
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Check the minimum Joomla! version
|
|
if (!empty($this->minimumJoomlaVersion) && !version_compare(JVERSION, $this->minimumJoomlaVersion, 'ge'))
|
|
{
|
|
$jVersion = JVERSION;
|
|
$msg = "<p>You need Joomla! $this->minimumJoomlaVersion or later to install this package but you only have $jVersion installed.</p>";
|
|
|
|
JLog::add($msg, JLog::WARNING, 'jerror');
|
|
|
|
return false;
|
|
}
|
|
|
|
// Check the maximum Joomla! version
|
|
if (!empty($this->maximumJoomlaVersion) && !version_compare(JVERSION, $this->maximumJoomlaVersion, 'le'))
|
|
{
|
|
$jVersion = JVERSION;
|
|
$msg = <<< HTML
|
|
<h3>FEF is no longer needed on Joomla 5</h3>
|
|
<p>
|
|
<strong>Summary: FEF is no longer used on Joomla 5. Please uninstall it.</strong>
|
|
</p>
|
|
<hr/>
|
|
<p>
|
|
Akeeba FEF a.k.a. the Akeeba Front-End Framework was a CSS and JavaScript framework used by Akeeba Ltd with the Joomla 3 versions of our software.
|
|
</p>
|
|
<p>
|
|
Akeeba Ltd has stopped using the FEF framework for developing extensions. All of our extensions have new, Joomla 4 and later native versions which use the Bootstrap library, included in Joomla itself.
|
|
</p>
|
|
<p>
|
|
You can no longer install or update FEF on Joomla 5.0 and later (you have {$jVersion}). In fact, you just need to uninstall it.
|
|
</p>
|
|
HTML;
|
|
|
|
JLog::add($msg, JLog::WARNING, 'jerror');
|
|
|
|
return false;
|
|
}
|
|
|
|
// In case of an update, discovery etc I need to check if I am an update
|
|
if (($type == 'update') && !$this->amIAnUpdate($parent))
|
|
{
|
|
$msg = "<p>You already have a newer version of Akeeba Frontend Framework installed. If you want to downgrade please uninstall Akeeba Frontend Framework and install the older version.</p><p>If you see this message during the installation or update of an Akeeba extension please ignore it <em>and</em> the immediately following “Files Install: Custom install routine failure” message. They are expected but Joomla! won't allow us to prevent them from showing up.</p>";
|
|
|
|
JLog::add($msg, JLog::WARNING, 'jerror');
|
|
|
|
return false;
|
|
}
|
|
|
|
// Delete obsolete font files and folders
|
|
if ($type == 'update')
|
|
{
|
|
// Use pathnames relative to your site's root
|
|
$removeFiles = [
|
|
'files' => [
|
|
// Non-WOFF fonts are not shipped as of 1.0.1 since all modern browsers we target use WOFF
|
|
'media/fef/fonts/akeeba/Akeeba-Products.eot',
|
|
'media/fef/fonts/akeeba/Akeeba-Products.svg',
|
|
'media/fef/fonts/akeeba/Akeeba-Products.ttf',
|
|
'media/fef/fonts/Ionicon/ionicons.eot',
|
|
'media/fef/fonts/Ionicon/ionicons.svg',
|
|
'media/fef/fonts/Ionicon/ionicons.ttf',
|
|
// Files renamed in 1.0.8
|
|
'css/reset.min.css',
|
|
'css/style.min.css',
|
|
// JavaScript: Irrelevant for Joomla
|
|
'js/darkmode.js',
|
|
'js/darkmode.min.js',
|
|
'js/darkmode.map',
|
|
'js/Darkmode.min.js',
|
|
'js/Darkmode.map',
|
|
'js/menu.js',
|
|
'js/menu.min.js',
|
|
'js/menu.map',
|
|
'js/Menu.min.js',
|
|
'js/Menu.map',
|
|
// JavaScript: Uncompressed and map files
|
|
'js/dropdown.js',
|
|
'js/dropdown.map',
|
|
'js/Dropdown.map',
|
|
'js/loader.js',
|
|
'js/loader.map',
|
|
'js/Loader.map',
|
|
'js/tabs.js',
|
|
'js/tabs.map',
|
|
'js/Tabs.map',
|
|
],
|
|
'folders' => [
|
|
],
|
|
];
|
|
|
|
// Remove obsolete files and folders
|
|
$this->removeFilesAndFolders($removeFiles);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Runs after install, update or discover_update. In other words, it executes after Joomla! has finished installing
|
|
* or updating your component. This is the last chance you've got to perform any additional installations, clean-up,
|
|
* database updates and similar housekeeping functions.
|
|
*
|
|
* @param string $type install, update or discover_update
|
|
* @param JInstallerAdapterFile $parent Parent object
|
|
*
|
|
* @throws Exception
|
|
*
|
|
* @noinspection PhpUnusedParameterInspection
|
|
*/
|
|
public function postflight($type, JInstallerAdapterFile $parent)
|
|
{
|
|
// Do not run on uninstall.
|
|
if ($type === 'uninstall')
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Auto-uninstall this package when it is no longer needed.
|
|
if (($type != 'install') && ($this->countHardcodedDependencies() === 0))
|
|
{
|
|
$this->uninstallSelf($parent);
|
|
|
|
return true;
|
|
}
|
|
|
|
$this->bugfixFilesNotCopiedOnUpdate($parent);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Runs on uninstallation
|
|
*
|
|
* @param JInstallerAdapterFile $parent The parent object
|
|
*
|
|
* @throws RuntimeException If the uninstallation is not allowed
|
|
*
|
|
* @noinspection PhpUnusedParameterInspection
|
|
*/
|
|
public function uninstall($parent)
|
|
{
|
|
if (version_compare(JVERSION, '4.1.0', 'ge'))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check dependencies on FEF
|
|
$dependencyCount = $this->countHardcodedDependencies();
|
|
|
|
if ($dependencyCount)
|
|
{
|
|
$msg = "<p>You have $dependencyCount extension(s) depending on Akeeba Frontend Framework. The package cannot be uninstalled unless these extensions are uninstalled first.</p>";
|
|
|
|
JLog::add($msg, JLog::WARNING, 'jerror');
|
|
|
|
throw new RuntimeException($msg, 500);
|
|
}
|
|
|
|
Folder::delete(JPATH_SITE . '/media/fef');
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Removes obsolete files and folders
|
|
*
|
|
* @param array $removeList The files and directories to remove
|
|
*/
|
|
protected function removeFilesAndFolders($removeList)
|
|
{
|
|
// Remove files
|
|
if (isset($removeList['files']) && !empty($removeList['files']))
|
|
{
|
|
foreach ($removeList['files'] as $file)
|
|
{
|
|
$f = JPATH_ROOT . '/' . $file;
|
|
|
|
if (!is_file($f))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
File::delete($f);
|
|
}
|
|
}
|
|
|
|
// Remove folders
|
|
if (isset($removeList['folders']) && !empty($removeList['folders']))
|
|
{
|
|
foreach ($removeList['folders'] as $folder)
|
|
{
|
|
$f = JPATH_ROOT . '/' . $folder;
|
|
|
|
if (!is_dir($f))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
Folder::delete($f);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Is this package an update to the currently installed FEF? If not (we're a downgrade) we will return false
|
|
* and prevent the installation from going on.
|
|
*
|
|
* @param JInstallerAdapterFile $parent The parent object
|
|
*
|
|
* @return bool Am I an update to an existing version>
|
|
*/
|
|
protected function amIAnUpdate($parent)
|
|
{
|
|
$grandpa = $parent->getParent();
|
|
$source = $grandpa->getPath('source');
|
|
$target = JPATH_ROOT . '/media/fef';
|
|
|
|
if (!Folder::exists($source))
|
|
{
|
|
// WTF? I can't find myself. I can't install anything.
|
|
return false;
|
|
}
|
|
|
|
// If FEF is not really installed (someone removed the directory instead of uninstalling?) I have to install it.
|
|
if (!Folder::exists($target))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
$fefVersion = [];
|
|
|
|
if (File::exists($target . '/version.txt'))
|
|
{
|
|
$rawData = @file_get_contents($target . '/version.txt');
|
|
$rawData = ($rawData === false) ? "0.0.0\n2011-01-01\n" : $rawData;
|
|
$info = explode("\n", $rawData);
|
|
$fefVersion['installed'] = [
|
|
'version' => trim($info[0]),
|
|
'date' => new JDate(trim($info[1])),
|
|
];
|
|
}
|
|
else
|
|
{
|
|
$fefVersion['installed'] = [
|
|
'version' => '0.0',
|
|
'date' => new JDate('2011-01-01'),
|
|
];
|
|
}
|
|
|
|
$rawData = @file_get_contents($source . '/version.txt');
|
|
$rawData = ($rawData === false) ? "0.0.0\n2011-01-01\n" : $rawData;
|
|
$info = explode("\n", $rawData);
|
|
$fefVersion['package'] = [
|
|
'version' => trim($info[0]),
|
|
'date' => new JDate(trim($info[1])),
|
|
];
|
|
|
|
return $fefVersion['package']['date']->toUNIX() >= $fefVersion['installed']['date']->toUNIX();
|
|
}
|
|
|
|
/**
|
|
* Fix for Joomla bug: sometimes files are not copied on update.
|
|
*
|
|
* We have observed that ever since Joomla! 1.5.5, when Joomla! is performing an extension update some files /
|
|
* folders are not copied properly. This seems to be a bit random and seems to be more likely to happen the more
|
|
* added / modified files and folders you have. We are trying to work around it by retrying the copy operation
|
|
* ourselves WITHOUT going through the manifest, based entirely on the conventions we follow.
|
|
*
|
|
* @param \Joomla\CMS\Installer\Adapter\FileAdapter $parent
|
|
*/
|
|
protected function bugfixFilesNotCopiedOnUpdate($parent)
|
|
{
|
|
$source = $parent->getParent()->getPath('source');
|
|
$target = JPATH_SITE . '/media/fef';
|
|
|
|
$this->recursiveConditionalCopy($source, $target);
|
|
}
|
|
|
|
/**
|
|
* Recursively copy a bunch of files, but only if the source and target file have a different size.
|
|
*
|
|
* @param string $source Path to copy FROM
|
|
* @param string $dest Path to copy TO
|
|
* @param array $ignored List of entries to ignore (first level entries are taken into account)
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function recursiveConditionalCopy($source, $dest, $ignored = [])
|
|
{
|
|
// Make sure source and destination exist
|
|
if (!@is_dir($source))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!@is_dir($dest))
|
|
{
|
|
if (!@mkdir($dest, 0755))
|
|
{
|
|
Folder::create($dest, 0755);
|
|
}
|
|
}
|
|
|
|
if (!@is_dir($dest))
|
|
{
|
|
// Cannot create folder $dest
|
|
|
|
return;
|
|
}
|
|
|
|
// List the contents of the source folder
|
|
try
|
|
{
|
|
$di = new DirectoryIterator($source);
|
|
}
|
|
catch (Exception $e)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Process each entry
|
|
foreach ($di as $entry)
|
|
{
|
|
// Ignore dot dirs (. and ..)
|
|
if ($entry->isDot())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
$sourcePath = $entry->getPathname();
|
|
$fileName = $entry->getFilename();
|
|
|
|
// Do not copy ignored files
|
|
if (!empty($ignored) && in_array($fileName, $ignored))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// If it's a directory do a recursive copy
|
|
if ($entry->isDir())
|
|
{
|
|
$this->recursiveConditionalCopy($sourcePath, $dest . DIRECTORY_SEPARATOR . $fileName);
|
|
|
|
continue;
|
|
}
|
|
|
|
// If it's a file check if it's missing or identical
|
|
$mustCopy = false;
|
|
$targetPath = $dest . DIRECTORY_SEPARATOR . $fileName;
|
|
|
|
if (!@is_file($targetPath))
|
|
{
|
|
$mustCopy = true;
|
|
}
|
|
else
|
|
{
|
|
$sourceSize = @filesize($sourcePath);
|
|
$targetSize = @filesize($targetPath);
|
|
|
|
$mustCopy = $sourceSize != $targetSize;
|
|
|
|
if ((substr($targetPath, -4) === '.php') && function_exists('opcache_invalidate'))
|
|
{
|
|
opcache_invalidate($targetPath);
|
|
}
|
|
}
|
|
|
|
if (!$mustCopy)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!@copy($sourcePath, $targetPath))
|
|
{
|
|
File::copy($sourcePath, $targetPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Count the number of old FOF + FEF based extensions installed on this site
|
|
*
|
|
* @return int
|
|
*/
|
|
private function countHardcodedDependencies()
|
|
{
|
|
// Look for fof.xml in the backend directories of the following components
|
|
$hardcodedDependencies = [
|
|
'com_admintools',
|
|
'com_akeeba',
|
|
'com_ars',
|
|
'com_ats',
|
|
'com_compatibility',
|
|
'com_datacompliance',
|
|
'com_contactus',
|
|
'com_docimport',
|
|
'com_loginguard',
|
|
];
|
|
|
|
$count = 0;
|
|
|
|
foreach ($hardcodedDependencies as $component)
|
|
{
|
|
$filePath = JPATH_ADMINISTRATOR . '/components/' . $component . '/fof.xml';
|
|
|
|
if (@file_exists($filePath))
|
|
{
|
|
$count++;
|
|
}
|
|
}
|
|
|
|
return $count;
|
|
}
|
|
|
|
/**
|
|
* Uninstall this package.
|
|
*
|
|
* This runs on update when there are no more dependencies left.
|
|
*
|
|
* @param \Joomla\CMS\Installer\Adapter\FileAdapter $adapter
|
|
*
|
|
* @return void
|
|
*/
|
|
private function uninstallSelf($adapter)
|
|
{
|
|
$parent = $adapter->getParent();
|
|
|
|
if (empty($parent) || !property_exists($parent, 'extension'))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (version_compare(JVERSION, '4.0', 'lt'))
|
|
{
|
|
$db = \Joomla\CMS\Factory::getDbo();
|
|
}
|
|
else
|
|
{
|
|
$db = \Joomla\CMS\Factory::getContainer()->get('DatabaseDriver');
|
|
}
|
|
|
|
try
|
|
{
|
|
$query = $db->getQuery(true)
|
|
->select($db->quoteName('extension_id'))
|
|
->from($db->quoteName('#__extensions'))
|
|
->where($db->quoteName('type') . ' = ' . $db->quote('file'))
|
|
->where($db->quoteName('name') . ' = ' . $db->quote('file_fef'));
|
|
|
|
$id = $db->setQuery($query)->loadResult();
|
|
}
|
|
catch (Exception $e)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (empty($id))
|
|
{
|
|
return;
|
|
}
|
|
|
|
$msg = 'Automatically uninstalling FEF; this package is no longer required on your site.';
|
|
\Joomla\CMS\Log\Log::add($msg, \Joomla\CMS\Log\Log::INFO, 'jerror');
|
|
|
|
$parent->uninstall('file', $id);
|
|
}
|
|
}
|