first commit
This commit is contained in:
@@ -0,0 +1,258 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @defgroup site Site
|
||||
* Site-related concerns such as the Site object and version management.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file classes/site/Site.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class Site
|
||||
*
|
||||
* @ingroup site
|
||||
*
|
||||
* @see SiteDAO
|
||||
*
|
||||
* @brief Describes system-wide site properties.
|
||||
*/
|
||||
|
||||
namespace PKP\site;
|
||||
|
||||
use PKP\facades\Locale;
|
||||
use PKP\i18n\LocaleMetadata;
|
||||
|
||||
class Site extends \PKP\core\DataObject
|
||||
{
|
||||
/**
|
||||
* Return associative array of all locales supported by the site.
|
||||
* These locales are used to provide a language toggle on the main site pages.
|
||||
*
|
||||
* @param int $langLocaleStatus The const value of one of LocaleMetadata:LANGUAGE_LOCALE_*
|
||||
*/
|
||||
public function getSupportedLocaleNames(int $langLocaleStatus = LocaleMetadata::LANGUAGE_LOCALE_WITHOUT): array
|
||||
{
|
||||
static $supportedLocales;
|
||||
if (isset($supportedLocales)) {
|
||||
return $supportedLocales;
|
||||
}
|
||||
|
||||
$supportedLocales = Locale::getFormattedDisplayNames($this->getSupportedLocales(), null, $langLocaleStatus);
|
||||
|
||||
asort($supportedLocales);
|
||||
return $supportedLocales;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return associative array of all locales currently by the site.
|
||||
* These locales are used to provide a language toggle on the main site pages.
|
||||
*
|
||||
* @param int $langLocaleStatus The const value of one of LocaleMetadata:LANGUAGE_LOCALE_*
|
||||
*/
|
||||
public function getInstalledLocaleNames(int $langLocaleStatus = LocaleMetadata::LANGUAGE_LOCALE_WITH): array
|
||||
{
|
||||
static $installedLocales;
|
||||
if (isset($installedLocales)) {
|
||||
return $installedLocales;
|
||||
}
|
||||
|
||||
$installedLocales = Locale::getFormattedDisplayNames($this->getInstalledLocales(), null, $langLocaleStatus);
|
||||
|
||||
asort($installedLocales);
|
||||
return $installedLocales;
|
||||
}
|
||||
|
||||
//
|
||||
// Get/set methods
|
||||
//
|
||||
|
||||
/**
|
||||
* Get site title.
|
||||
*
|
||||
* @param string $locale Locale code to return, if desired.
|
||||
*/
|
||||
public function getTitle($locale = null)
|
||||
{
|
||||
return $this->getData('title', $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get localized site title.
|
||||
*/
|
||||
public function getLocalizedTitle()
|
||||
{
|
||||
return $this->getLocalizedData('title');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get "localized" site page title (if applicable).
|
||||
*
|
||||
* @return array|string
|
||||
*
|
||||
* @deprecated 3.3.0
|
||||
*/
|
||||
public function getLocalizedPageHeaderTitle()
|
||||
{
|
||||
if ($this->getLocalizedData('pageHeaderTitleImage')) {
|
||||
return $this->getLocalizedData('pageHeaderTitleImage');
|
||||
}
|
||||
if ($this->getData('pageHeaderTitleImage', Locale::getPrimaryLocale())) {
|
||||
return $this->getData('pageHeaderTitleImage', Locale::getPrimaryLocale());
|
||||
}
|
||||
if ($this->getLocalizedData('title')) {
|
||||
return $this->getLocalizedData('title');
|
||||
}
|
||||
if ($this->getData('title', Locale::getPrimaryLocale())) {
|
||||
return $this->getData('title', Locale::getPrimaryLocale());
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get redirect
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getRedirect()
|
||||
{
|
||||
return $this->getData('redirect');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set redirect
|
||||
*
|
||||
* @param int $redirect
|
||||
*/
|
||||
public function setRedirect($redirect)
|
||||
{
|
||||
$this->setData('redirect', (int)$redirect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get localized site about statement.
|
||||
*/
|
||||
public function getLocalizedAbout()
|
||||
{
|
||||
return $this->getLocalizedData('about');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get localized site contact name.
|
||||
*/
|
||||
public function getLocalizedContactName()
|
||||
{
|
||||
return $this->getLocalizedData('contactName');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get localized site contact email.
|
||||
*/
|
||||
public function getLocalizedContactEmail()
|
||||
{
|
||||
return $this->getLocalizedData('contactEmail');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get minimum password length.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getMinPasswordLength()
|
||||
{
|
||||
return $this->getData('minPasswordLength');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set minimum password length.
|
||||
*
|
||||
* @param int $minPasswordLength
|
||||
*/
|
||||
public function setMinPasswordLength($minPasswordLength)
|
||||
{
|
||||
$this->setData('minPasswordLength', $minPasswordLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get primary locale.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPrimaryLocale()
|
||||
{
|
||||
return $this->getData('primaryLocale');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set primary locale.
|
||||
*
|
||||
* @param string $primaryLocale
|
||||
*/
|
||||
public function setPrimaryLocale($primaryLocale)
|
||||
{
|
||||
$this->setData('primaryLocale', $primaryLocale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get installed locales.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getInstalledLocales()
|
||||
{
|
||||
return $this->getData('installedLocales') ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set installed locales.
|
||||
*
|
||||
* @param array $installedLocales
|
||||
*/
|
||||
public function setInstalledLocales($installedLocales)
|
||||
{
|
||||
$this->setData('installedLocales', $installedLocales);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get array of all supported locales (for static text).
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSupportedLocales()
|
||||
{
|
||||
return $this->getData('supportedLocales') ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set array of all supported locales (for static text).
|
||||
*
|
||||
* @param array $supportedLocales
|
||||
*/
|
||||
public function setSupportedLocales($supportedLocales)
|
||||
{
|
||||
$this->setData('supportedLocales', $supportedLocales);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the unique site ID.
|
||||
*/
|
||||
public function getUniqueSiteID(): ?string
|
||||
{
|
||||
return $this->getData('uniqueSiteId');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the unique site ID.
|
||||
*/
|
||||
public function setUniqueSiteID(string $uniqueSiteId): void
|
||||
{
|
||||
$this->setData('uniqueSiteId', $uniqueSiteId);
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\site\Site', '\Site');
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/site/SiteDAO.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class SiteDAO
|
||||
*
|
||||
* @ingroup site
|
||||
*
|
||||
* @see Site
|
||||
*
|
||||
* @brief Operations for retrieving and modifying the Site object.
|
||||
*/
|
||||
|
||||
namespace PKP\site;
|
||||
|
||||
use APP\core\Services;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use PKP\services\PKPSchemaService;
|
||||
|
||||
class SiteDAO extends \PKP\db\DAO
|
||||
{
|
||||
/** @var array Maps schema properties for the primary table to their column names */
|
||||
public $primaryTableColumns = [
|
||||
'redirect' => 'redirect',
|
||||
'primaryLocale' => 'primary_locale',
|
||||
'minPasswordLength' => 'min_password_length',
|
||||
'installedLocales' => 'installed_locales',
|
||||
'supportedLocales' => 'supported_locales',
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve site information.
|
||||
*
|
||||
* @return ?Site
|
||||
*/
|
||||
public function getSite()
|
||||
{
|
||||
$result = $this->retrieve(
|
||||
'SELECT * FROM site'
|
||||
);
|
||||
|
||||
if ($row = (array) $result->current()) {
|
||||
return $this->_fromRow($row);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate and return a new DataObject.
|
||||
*
|
||||
* @return Site
|
||||
*/
|
||||
public function newDataObject()
|
||||
{
|
||||
return new Site();
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc SchemaDAO::_fromRow()
|
||||
*/
|
||||
public function _fromRow($primaryRow, $callHook = true)
|
||||
{
|
||||
$schemaService = Services::get('schema');
|
||||
$schema = $schemaService->get(PKPSchemaService::SCHEMA_SITE);
|
||||
|
||||
$site = $this->newDataObject();
|
||||
|
||||
foreach ($this->primaryTableColumns as $propName => $column) {
|
||||
if (isset($primaryRow[$column])) {
|
||||
// Backwards-compatible handling of the installedLocales and
|
||||
// supportedLocales data. Before 3.2, these were stored as colon-separated
|
||||
// strings (eg - en:fr_CA:ar). In 3.2, these are migrated to
|
||||
// serialized arrays defined by the site.json schema. However, some of the
|
||||
// older upgrade scripts use site data before the migration is performed,
|
||||
// so SiteDAO must be able to return the correct array before the data
|
||||
// is migrated. This code checks the format and converts the old data so
|
||||
// that calls to $site->getInstalledLocales() and
|
||||
// $site->getSupportedLocales() return an appropriate array.
|
||||
if (in_array($column, ['installed_locales', 'supported_locales']) &&
|
||||
!is_null($primaryRow[$column]) && strpos($primaryRow[$column], '{') === false && is_null(json_decode($primaryRow[$column]))) {
|
||||
$site->setData($propName, explode(':', $primaryRow[$column]));
|
||||
} else {
|
||||
$site->setData(
|
||||
$propName,
|
||||
$this->convertFromDb($primaryRow[$column], $schema->properties->{$propName}->type)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result = $this->retrieve('SELECT * FROM site_settings');
|
||||
|
||||
foreach ($result as $settingRow) {
|
||||
$settingRow = (array) $settingRow;
|
||||
if (!empty($schema->properties->{$settingRow['setting_name']})) {
|
||||
$site->setData(
|
||||
$settingRow['setting_name'],
|
||||
$this->convertFromDB(
|
||||
$settingRow['setting_value'],
|
||||
$schema->properties->{$settingRow['setting_name']}->type
|
||||
),
|
||||
empty($settingRow['locale']) ? null : $settingRow['locale']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $site;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert site information.
|
||||
*
|
||||
* @param Site $site
|
||||
*/
|
||||
public function insertSite($site)
|
||||
{
|
||||
$type = 'array';
|
||||
$returner = $this->update(
|
||||
'INSERT INTO site
|
||||
(redirect, min_password_length, primary_locale, installed_locales, supported_locales)
|
||||
VALUES
|
||||
(?, ?, ?, ?, ?)',
|
||||
[
|
||||
$site->getRedirect(),
|
||||
(int) $site->getMinPasswordLength(),
|
||||
$site->getPrimaryLocale(),
|
||||
$this->convertToDB($site->getInstalledLocales(), $type),
|
||||
$this->convertToDB($site->getInstalledLocales(), $type),
|
||||
]
|
||||
);
|
||||
return $returner;
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc SchemaDAO::updateObject
|
||||
*/
|
||||
public function updateObject($site)
|
||||
{
|
||||
$schemaService = Services::get('schema');
|
||||
$schema = $schemaService->get(PKPSchemaService::SCHEMA_SITE);
|
||||
$sanitizedProps = $schemaService->sanitize(PKPSchemaService::SCHEMA_SITE, $site->_data);
|
||||
|
||||
$set = $params = [];
|
||||
foreach ($this->primaryTableColumns as $propName => $column) {
|
||||
$set[] = $column . ' = ?';
|
||||
$params[] = $this->convertToDb($sanitizedProps[$propName], $schema->properties->{$propName}->type);
|
||||
}
|
||||
$this->update('UPDATE site SET ' . join(',', $set), $params);
|
||||
|
||||
$deleteSettings = [];
|
||||
foreach ($schema->properties as $propName => $propSchema) {
|
||||
if (array_key_exists($propName, $this->primaryTableColumns)) {
|
||||
continue;
|
||||
} elseif (!isset($sanitizedProps[$propName])) {
|
||||
$deleteSettings[] = $propName;
|
||||
continue;
|
||||
}
|
||||
if (!empty($propSchema->multilingual)) {
|
||||
foreach ($sanitizedProps[$propName] as $localeKey => $localeValue) {
|
||||
// Delete rows with a null value
|
||||
if (is_null($localeValue)) {
|
||||
$this->update('DELETE FROM site_settings WHERE setting_name = ? AND locale = ?', [
|
||||
$propName,
|
||||
$localeKey,
|
||||
]);
|
||||
} else {
|
||||
DB::table('site_settings')->updateOrInsert(
|
||||
['locale' => $localeKey, 'setting_name' => $propName],
|
||||
['setting_value' => $this->convertToDB($localeValue, $schema->properties->{$propName}->type)]
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DB::table('site_settings')->updateOrInsert(
|
||||
['locale' => '', 'setting_name' => $propName],
|
||||
['setting_value' => $this->convertToDB($sanitizedProps[$propName], $schema->properties->{$propName}->type)]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (count($deleteSettings)) {
|
||||
$deleteSettingNames = join(',', array_map(function ($settingName) {
|
||||
return "'{$settingName}'";
|
||||
}, $deleteSettings));
|
||||
$this->update("DELETE FROM site_settings WHERE setting_name in ({$deleteSettingNames})");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\site\SiteDAO', '\SiteDAO');
|
||||
}
|
||||
@@ -0,0 +1,390 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/site/Version.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class Version
|
||||
*
|
||||
* @ingroup site
|
||||
*
|
||||
* @see VersionDAO
|
||||
*
|
||||
* @brief Describes system version history.
|
||||
*/
|
||||
|
||||
namespace PKP\site;
|
||||
|
||||
use APP\core\Application;
|
||||
use Composer\Semver\Semver;
|
||||
use PKP\core\Core;
|
||||
|
||||
class Version extends \PKP\core\DataObject
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct(
|
||||
$major,
|
||||
$minor,
|
||||
$revision,
|
||||
$build,
|
||||
$dateInstalled,
|
||||
$current,
|
||||
$productType,
|
||||
$product,
|
||||
$productClassName,
|
||||
$lazyLoad,
|
||||
$sitewide
|
||||
) {
|
||||
parent::__construct();
|
||||
|
||||
// Initialize object
|
||||
$this->setMajor($major);
|
||||
$this->setMinor($minor);
|
||||
$this->setRevision($revision);
|
||||
$this->setBuild($build);
|
||||
$this->setDateInstalled($dateInstalled);
|
||||
$this->setCurrent($current);
|
||||
$this->setProductType($productType);
|
||||
$this->setProduct($product);
|
||||
$this->setProductClassName($productClassName);
|
||||
$this->setLazyLoad($lazyLoad);
|
||||
$this->setSitewide($sitewide);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare this version with another version.
|
||||
* Returns:
|
||||
* < 0 if this version is lower
|
||||
* 0 if they are equal
|
||||
* > 0 if this version is higher
|
||||
*
|
||||
* @param string|Version $version the version to compare against
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function compare($version)
|
||||
{
|
||||
if (is_object($version)) {
|
||||
return $this->compare($version->getVersionString());
|
||||
}
|
||||
return version_compare($this->getVersionString(), $version);
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method to return a new version from a version string of the form "W.X.Y.Z".
|
||||
*
|
||||
* @param string $versionString
|
||||
* @param string $productType
|
||||
* @param string $product
|
||||
* @param string $productClass
|
||||
* @param int $lazyLoad
|
||||
* @param int $sitewide
|
||||
*
|
||||
* @return Version
|
||||
*/
|
||||
public static function fromString($versionString, $productType = null, $product = null, $productClass = '', $lazyLoad = 0, $sitewide = 1)
|
||||
{
|
||||
$versionArray = explode('.', $versionString);
|
||||
|
||||
if (!$product && !$productType) {
|
||||
$application = Application::get();
|
||||
$product = $application->getName();
|
||||
$productType = 'core';
|
||||
}
|
||||
|
||||
$version = new Version(
|
||||
(isset($versionArray[0]) ? (int) $versionArray[0] : 0),
|
||||
(isset($versionArray[1]) ? (int) $versionArray[1] : 0),
|
||||
(isset($versionArray[2]) ? (int) $versionArray[2] : 0),
|
||||
(isset($versionArray[3]) ? (int) $versionArray[3] : 0),
|
||||
Core::getCurrentDate(),
|
||||
1,
|
||||
$productType,
|
||||
$product,
|
||||
$productClass,
|
||||
$lazyLoad,
|
||||
$sitewide
|
||||
);
|
||||
|
||||
return $version;
|
||||
}
|
||||
|
||||
//
|
||||
// Get/set methods
|
||||
//
|
||||
|
||||
/**
|
||||
* Get major version.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getMajor()
|
||||
{
|
||||
return $this->getData('major');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set major version.
|
||||
*
|
||||
* @param int $major
|
||||
*/
|
||||
public function setMajor($major)
|
||||
{
|
||||
$this->setData('major', $major);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get minor version.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getMinor()
|
||||
{
|
||||
return $this->getData('minor');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set minor version.
|
||||
*
|
||||
* @param int $minor
|
||||
*/
|
||||
public function setMinor($minor)
|
||||
{
|
||||
$this->setData('minor', $minor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get revision version.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getRevision()
|
||||
{
|
||||
return $this->getData('revision');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set revision version.
|
||||
*
|
||||
* @param int $revision
|
||||
*/
|
||||
public function setRevision($revision)
|
||||
{
|
||||
$this->setData('revision', $revision);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get build version.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getBuild()
|
||||
{
|
||||
return $this->getData('build');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set build version.
|
||||
*
|
||||
* @param int $build
|
||||
*/
|
||||
public function setBuild($build)
|
||||
{
|
||||
$this->setData('build', $build);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get date installed.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDateInstalled()
|
||||
{
|
||||
return $this->getData('dateInstalled');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set date installed.
|
||||
*
|
||||
* @param string $dateInstalled
|
||||
*/
|
||||
public function setDateInstalled($dateInstalled)
|
||||
{
|
||||
$this->setData('dateInstalled', $dateInstalled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current version.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getCurrent()
|
||||
{
|
||||
return $this->getData('current');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if current version.
|
||||
*
|
||||
* @param int $current
|
||||
*/
|
||||
public function setCurrent($current)
|
||||
{
|
||||
$this->setData('current', $current);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get product type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getProductType()
|
||||
{
|
||||
return $this->getData('productType');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set product type.
|
||||
*
|
||||
* @param string $productType
|
||||
*/
|
||||
public function setProductType($productType)
|
||||
{
|
||||
$this->setData('productType', $productType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get product name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getProduct()
|
||||
{
|
||||
return $this->getData('product');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set product name.
|
||||
*
|
||||
* @param string $product
|
||||
*/
|
||||
public function setProduct($product)
|
||||
{
|
||||
$this->setData('product', $product);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the product's class name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getProductClassName()
|
||||
{
|
||||
return $this->getData('productClassName');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the product's class name
|
||||
*
|
||||
* @param string $productClassName
|
||||
*/
|
||||
public function setProductClassName($productClassName)
|
||||
{
|
||||
$this->setData('productClassName', $productClassName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the lazy load flag for this product
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getLazyLoad()
|
||||
{
|
||||
return $this->getData('lazyLoad');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the lazy load flag for this product
|
||||
*
|
||||
* @param bool $lazyLoad
|
||||
*/
|
||||
public function setLazyLoad($lazyLoad)
|
||||
{
|
||||
$this->setData('lazyLoad', $lazyLoad);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sitewide flag for this product
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getSitewide()
|
||||
{
|
||||
return $this->getData('sitewide');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the sitewide flag for this product
|
||||
*
|
||||
* @param bool $sitewide
|
||||
*/
|
||||
public function setSitewide($sitewide)
|
||||
{
|
||||
$this->setData('sitewide', $sitewide);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return complete version string.
|
||||
*
|
||||
* @param bool True (default) iff a numeric (comparable) version is to be returned.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getVersionString($numeric = true)
|
||||
{
|
||||
$numericVersion = sprintf('%d.%d.%d.%d', $this->getMajor(), $this->getMinor(), $this->getRevision(), $this->getBuild());
|
||||
if (!$numeric && $this->getProduct() == 'omp' && preg_match('/^0\.9\.9\./', $numericVersion)) {
|
||||
return ('1.0 Beta');
|
||||
}
|
||||
if (!$numeric && $this->getProduct() == 'ojs2' && preg_match('/^2\.9\.0\./', $numericVersion)) {
|
||||
return ('3.0 Alpha 1');
|
||||
}
|
||||
if (!$numeric && $this->getProduct() == 'ojs2' && preg_match('/^2\.9\.9\.0/', $numericVersion)) {
|
||||
return ('3.0 Beta 1');
|
||||
}
|
||||
if (!$numeric && $this->getProduct() == 'ops' && preg_match('/^3\.2\.0\.0/', $numericVersion)) {
|
||||
return ('3.2.0 Beta');
|
||||
}
|
||||
|
||||
return $numericVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the Version is compatible with the given string of constraints for the version,
|
||||
* formatted per composer/semver specifications;
|
||||
* c.f. https://getcomposer.org/doc/articles/versions.md#writing-version-constraints
|
||||
* Returns:
|
||||
* true iff the version given is compatible with this version
|
||||
* false iff the version given is incompatible with this version
|
||||
*
|
||||
* @param string $constraints the string of constraints for the version to be checked against
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isCompatible($constraints)
|
||||
{
|
||||
$semver = new semver();
|
||||
$version = $this->getVersionString();
|
||||
|
||||
return $semver->satisfies($version, $constraints);
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\site\Version', '\Version');
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/site/VersionCheck.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class VersionCheck
|
||||
*
|
||||
* @ingroup site
|
||||
*
|
||||
* @see Version
|
||||
*
|
||||
* @brief Provides methods to check for the latest version of the application.
|
||||
*/
|
||||
|
||||
namespace PKP\site;
|
||||
|
||||
use APP\core\Application;
|
||||
use DateInterval;
|
||||
use Exception;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use PKP\config\Config;
|
||||
use PKP\core\PKPString;
|
||||
use PKP\db\DAORegistry;
|
||||
use PKP\file\FileManager;
|
||||
use SimpleXMLElement;
|
||||
|
||||
class VersionCheck
|
||||
{
|
||||
/** Max lifetime for the version cache */
|
||||
protected const MAX_CACHE_LIFETIME = '1 year';
|
||||
|
||||
public const VERSION_CODE_PATH = 'dbscripts/xml/version.xml';
|
||||
|
||||
/**
|
||||
* Return information about the latest available version.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getLatestVersion()
|
||||
{
|
||||
$application = Application::get();
|
||||
$includeId = Application::isInstalled() &&
|
||||
!Application::isUpgrading() &&
|
||||
Config::getVar('general', 'enable_beacon', true);
|
||||
|
||||
if ($includeId) {
|
||||
$uniqueSiteId = Application::get()->getUUID();
|
||||
} else {
|
||||
$uniqueSiteId = null;
|
||||
}
|
||||
|
||||
$request = $application->getRequest();
|
||||
return self::parseVersionXML(
|
||||
$application->getVersionDescriptorUrl() .
|
||||
($includeId ? '?id=' . urlencode($uniqueSiteId) .
|
||||
'&oai=' . urlencode($request->url('index', 'oai'))
|
||||
: '')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the currently installed database version.
|
||||
*
|
||||
* @return Version
|
||||
*/
|
||||
public static function getCurrentDBVersion()
|
||||
{
|
||||
$versionDao = DAORegistry::getDAO('VersionDAO'); /** @var VersionDAO $versionDao */
|
||||
return $versionDao->getCurrentVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current code version.
|
||||
*/
|
||||
public static function getCurrentCodeVersion(): ?Version
|
||||
{
|
||||
return self::parseVersionXML(self::VERSION_CODE_PATH)['version'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse information from a version XML file.
|
||||
*/
|
||||
public static function parseVersionXML(string $path): ?array
|
||||
{
|
||||
$isVirtual = FileManager::isVirtualPath($path);
|
||||
$key = __METHOD__ . static::MAX_CACHE_LIFETIME . $path . ($isVirtual ? '' : filemtime($path));
|
||||
$expiration = $isVirtual ? 0 : DateInterval::createFromDateString(static::MAX_CACHE_LIFETIME);
|
||||
$version = Cache::remember($key, $expiration, function () use ($path) {
|
||||
$xml = new SimpleXMLElement(FileManager::getStream($path));
|
||||
$version = [];
|
||||
foreach (['application', 'class', 'type', 'release', 'tag', 'date', 'info', 'package', 'lazy-load', 'sitewide'] as $name) {
|
||||
if (isset($xml->$name)) {
|
||||
$version[$name] = (string) $xml->$name;
|
||||
}
|
||||
}
|
||||
$version['sitewide'] = (int) ($version['sitewide'] ?? 0);
|
||||
$version['lazy-load'] = (int) ($version['lazy-load'] ?? 0);
|
||||
if (isset($xml->patch)) {
|
||||
$version['patch'] = [];
|
||||
foreach ($xml->patch as $patch) {
|
||||
$version['patch'][$patch['from']] = (string) $patch;
|
||||
}
|
||||
}
|
||||
return $version;
|
||||
});
|
||||
|
||||
// Built outside of the cache section to avoid serializing the Version (which would need a __set_state implementation)
|
||||
if (isset($version['release']) && isset($version['application'])) {
|
||||
$version['version'] = Version::fromString(
|
||||
$version['release'] ?? '',
|
||||
$version['type'] ?? '',
|
||||
$version['application'] ?? '',
|
||||
$version['class'] ?? '',
|
||||
$version['lazy-load'],
|
||||
$version['sitewide']
|
||||
);
|
||||
}
|
||||
return $version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the applicable patch for the current code version (if available).
|
||||
*
|
||||
* @param array $versionInfo as returned by parseVersionXML()
|
||||
* @param Version $codeVersion as returned by getCurrentCodeVersion()
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getPatch($versionInfo, $codeVersion = null)
|
||||
{
|
||||
if (!isset($codeVersion)) {
|
||||
$codeVersion = self::getCurrentCodeVersion();
|
||||
}
|
||||
if (isset($versionInfo['patch'][$codeVersion->getVersionString()])) {
|
||||
return $versionInfo['patch'][$codeVersion->getVersionString()];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given version file exists and whether it
|
||||
* contains valid data. Returns a Version object if everything
|
||||
* is ok, otherwise throws an Exception.
|
||||
*
|
||||
* @param string $versionFile
|
||||
*
|
||||
* @return Version
|
||||
*/
|
||||
public static function getValidPluginVersionInfo($versionFile)
|
||||
{
|
||||
$fileManager = new FileManager();
|
||||
if ($fileManager->fileExists($versionFile)) {
|
||||
$versionInfo = self::parseVersionXML($versionFile);
|
||||
} else {
|
||||
throw new Exception(__('manager.plugins.versionFileNotFound'));
|
||||
}
|
||||
|
||||
// Validate plugin name and type to avoid abuse
|
||||
$productType = explode('.', $versionInfo['type']);
|
||||
if (count($productType) != 2 || $productType[0] != 'plugins') {
|
||||
throw new Exception(__('manager.plugins.versionFileInvalid'));
|
||||
}
|
||||
|
||||
$pluginVersion = $versionInfo['version'];
|
||||
$namesToValidate = [$pluginVersion->getProduct(), $productType[1]];
|
||||
foreach ($namesToValidate as $nameToValidate) {
|
||||
if (!PKPString::regexp_match('/[a-z][a-zA-Z0-9]+/', $nameToValidate)) {
|
||||
throw new Exception(__('manager.plugins.versionFileInvalid'));
|
||||
}
|
||||
}
|
||||
|
||||
return $pluginVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the application's version against the latest version
|
||||
* on the PKP servers.
|
||||
*
|
||||
* @return string|false Version description or false if no newer version
|
||||
*/
|
||||
public static function checkIfNewVersionExists()
|
||||
{
|
||||
$versionInfo = self::getLatestVersion();
|
||||
$latestVersion = $versionInfo['release'];
|
||||
|
||||
$currentVersion = self::getCurrentDBVersion();
|
||||
if ($currentVersion->compare($latestVersion) < 0) {
|
||||
return $latestVersion;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\site\VersionCheck', '\VersionCheck');
|
||||
define('VERSION_CODE_PATH', VersionCheck::VERSION_CODE_PATH);
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/site/VersionDAO.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class VersionDAO
|
||||
*
|
||||
* @ingroup site
|
||||
*
|
||||
* @see Version
|
||||
*
|
||||
* @brief Operations for retrieving and modifying Version objects.
|
||||
*/
|
||||
|
||||
namespace PKP\site;
|
||||
|
||||
use APP\core\Application;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use PKP\core\Core;
|
||||
use PKP\plugins\Hook;
|
||||
|
||||
class VersionDAO extends \PKP\db\DAO
|
||||
{
|
||||
/**
|
||||
* Retrieve the current version.
|
||||
*/
|
||||
public function getCurrentVersion(?string $productType = null, ?string $product = null): ?Version
|
||||
{
|
||||
if (!$productType || !$product) {
|
||||
$application = Application::get();
|
||||
$productType = 'core';
|
||||
$product = $application->getName();
|
||||
}
|
||||
|
||||
$result = $this->retrieve(
|
||||
'SELECT * FROM versions WHERE current = 1 AND product_type = ? AND product = ?',
|
||||
[$productType, $product]
|
||||
);
|
||||
$row = (array) $result->current();
|
||||
return $row ? $this->_returnVersionFromRow($row) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the complete version history, ordered by date (most recent first).
|
||||
*
|
||||
* @return Version[] Versions
|
||||
*/
|
||||
public function getVersionHistory(?string $productType = null, ?string $product = null): array
|
||||
{
|
||||
$versions = [];
|
||||
|
||||
if (!$productType || !$product) {
|
||||
$application = Application::get();
|
||||
$productType = 'core';
|
||||
$product = $application->getName();
|
||||
}
|
||||
|
||||
$result = $this->retrieve(
|
||||
'SELECT * FROM versions WHERE product_type = ? AND product = ? ORDER BY date_installed DESC',
|
||||
[$productType, $product]
|
||||
);
|
||||
|
||||
foreach ($result as $row) {
|
||||
$versions[] = $this->_returnVersionFromRow((array) $row);
|
||||
}
|
||||
return $versions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function to return a Version object from a row.
|
||||
*/
|
||||
public function _returnVersionFromRow($row): Version
|
||||
{
|
||||
$version = new Version(
|
||||
$row['major'],
|
||||
$row['minor'],
|
||||
$row['revision'],
|
||||
$row['build'],
|
||||
$this->datetimeFromDB($row['date_installed']),
|
||||
$row['current'],
|
||||
($row['product_type'] ?? null),
|
||||
($row['product'] ?? null),
|
||||
($row['product_class_name'] ?? ''),
|
||||
($row['lazy_load'] ?? 0),
|
||||
($row['sitewide'] ?? 0)
|
||||
);
|
||||
|
||||
Hook::call('VersionDAO::_returnVersionFromRow', [&$version, &$row]);
|
||||
|
||||
return $version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new version.
|
||||
*/
|
||||
public function insertVersion(Version $version, bool $isPlugin = false): int
|
||||
{
|
||||
$isNewVersion = true;
|
||||
|
||||
if ($version->getCurrent()) {
|
||||
// Find out whether the last installed version is the same as the
|
||||
// one to be inserted.
|
||||
$versionHistory = $this->getVersionHistory($version->getProductType(), $version->getProduct());
|
||||
|
||||
$oldVersion = array_shift($versionHistory);
|
||||
if ($oldVersion) {
|
||||
if ($version->compare($oldVersion) == 0) {
|
||||
// The old and the new current versions are the same so we need
|
||||
// to update the existing version entry.
|
||||
$isNewVersion = false;
|
||||
} elseif ($version->compare($oldVersion) == 1) {
|
||||
// Version to insert is newer than the existing version entry.
|
||||
// We reset existing entry.
|
||||
$this->update('UPDATE versions SET current = 0 WHERE current = 1 AND product = ?', [$version->getProduct()]);
|
||||
} else {
|
||||
// We do not support downgrades.
|
||||
fatalError('You are trying to downgrade the product "' . $version->getProduct() . '" from version [' . $oldVersion->getVersionString(false) . '] to version [' . $version->getVersionString(false) . ']. Downgrades are not supported.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($isNewVersion) {
|
||||
// We only change the install date when we insert new
|
||||
// version entries.
|
||||
if ($version->getDateInstalled() == null) {
|
||||
$version->setDateInstalled(Core::getCurrentDate());
|
||||
}
|
||||
|
||||
// Insert new version entry
|
||||
return $this->update(
|
||||
sprintf(
|
||||
'INSERT INTO versions
|
||||
(major, minor, revision, build, date_installed, current, product_type, product, product_class_name, lazy_load, sitewide)
|
||||
VALUES
|
||||
(?, ?, ?, ?, %s, ?, ?, ?, ?, ?, ?)',
|
||||
$this->datetimeToDB($version->getDateInstalled())
|
||||
),
|
||||
[
|
||||
(int) $version->getMajor(),
|
||||
(int) $version->getMinor(),
|
||||
(int) $version->getRevision(),
|
||||
(int) $version->getBuild(),
|
||||
(int) $version->getCurrent(),
|
||||
$version->getProductType(),
|
||||
$version->getProduct(),
|
||||
$version->getProductClassName(),
|
||||
($version->getLazyLoad() ? 1 : 0),
|
||||
($version->getSitewide() ? 1 : 0)
|
||||
]
|
||||
);
|
||||
} else {
|
||||
// Update existing version entry
|
||||
return $this->update(
|
||||
'UPDATE versions SET current = ?, product_class_name = ?, lazy_load = ?, sitewide = ?
|
||||
WHERE product_type = ? AND product = ? AND major = ? AND minor = ? AND revision = ? AND build = ?',
|
||||
[
|
||||
(int) $version->getCurrent(),
|
||||
$version->getProductClassName(),
|
||||
($version->getLazyLoad() ? 1 : 0),
|
||||
($version->getSitewide() ? 1 : 0),
|
||||
$version->getProductType(),
|
||||
$version->getProduct(),
|
||||
(int) $version->getMajor(),
|
||||
(int) $version->getMinor(),
|
||||
(int) $version->getRevision(),
|
||||
(int) $version->getBuild()
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all currently enabled products within the
|
||||
* given context as a two dimensional array with the
|
||||
* first key representing the product type, the second
|
||||
* key the product name and the value the product version.
|
||||
*/
|
||||
public function getCurrentProducts(?int $contextId): array
|
||||
{
|
||||
$result = $this->retrieve(
|
||||
'SELECT v.*
|
||||
FROM versions v
|
||||
LEFT JOIN plugin_settings ps
|
||||
ON LOWER(v.product_class_name) = ps.plugin_name
|
||||
AND ps.setting_name = \'enabled\'
|
||||
' . ($contextId !== null ? ' AND (context_id = ? OR v.sitewide = 1) ' : '') . '
|
||||
WHERE v.current = 1 AND (ps.setting_value = \'1\' OR v.lazy_load <> 1)',
|
||||
$contextId !== null ? [$contextId] : [],
|
||||
false
|
||||
);
|
||||
|
||||
$productArray = [];
|
||||
foreach ($result as $row) {
|
||||
$productArray[$row->product_type][$row->product] = $this->_returnVersionFromRow((array) $row);
|
||||
}
|
||||
return $productArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable a product by setting its 'current' column to 0
|
||||
*
|
||||
* @param string $productType
|
||||
* @param string $product
|
||||
*/
|
||||
public function disableVersion($productType, $product): void
|
||||
{
|
||||
$this->update(
|
||||
'UPDATE versions SET current = 0 WHERE current = 1 AND product_type = ? AND product = ?',
|
||||
[$productType, $product]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get installation date of the given version or the first version used after that
|
||||
*
|
||||
* @param int $version Version number, without '.' as separator, i.e. in the form major*1000+minor*100+revision*10+build
|
||||
*/
|
||||
public function getInstallationDate(int $version): string
|
||||
{
|
||||
$product = Application::get()->getName();
|
||||
$dateInstalledArray = DB::select(
|
||||
"SELECT date_installed
|
||||
FROM versions
|
||||
WHERE major*1000+minor*100+revision*10+build IN
|
||||
(SELECT MIN(major*1000+minor*100+revision*10+build)
|
||||
FROM versions vt
|
||||
WHERE vt.product_type = 'core' AND vt.product = ? AND vt.major*1000+vt.minor*100+vt.revision*10+vt.build >= ?)
|
||||
AND product_type = 'core' AND product = ?
|
||||
",
|
||||
[$product, $version, $product]
|
||||
);
|
||||
return current($dateInstalledArray)->date_installed;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\site\VersionDAO', '\VersionDAO');
|
||||
}
|
||||
Reference in New Issue
Block a user