CheckForOutdatedExtensions.php 10.8 KB
Newer Older
1
<?php
2 3 4

namespace T3o\TerFe2\Task;

5
/*
6
 * This file is part of the TYPO3 CMS project.
7
 *
8 9 10
 * It is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License, either version 2
 * of the License, or any later version.
11
 *
12 13
 * For the full copyright and license information, please read the
 * LICENSE.txt file that was distributed with this source code.
14
 *
15 16 17
 * The TYPO3 project - inspiring people to share!
 */

18
use ApacheSolrForTypo3\Solr\IndexQueue\Queue;
19
use Psr\Log\LoggerInterface;
20
use TYPO3\CMS\Core\Database\ConnectionPool;
21
use TYPO3\CMS\Core\Log\LogManager;
22
use TYPO3\CMS\Core\Utility\GeneralUtility;
23
use TYPO3\CMS\Core\Utility\VersionNumberUtility;
24 25 26
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager;
use TYPO3\CMS\Extbase\Persistence\Generic\Session;
27
use TYPO3\CMS\Extbase\Scheduler\Task;
28 29

/**
30
 * Class CheckForOutdatedExtensions
31
 */
32
class CheckForOutdatedExtensions extends Task
33 34 35
{

    /**
36
     * @var \TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager
37 38 39 40
     */
    protected $persistenceManager;

    /**
41
     * @var \T3o\TerFe2\Domain\Repository\VersionRepository
42 43 44 45 46 47 48 49 50
     */
    protected $versionRepository;

    /**
     * @var array
     */
    protected $coreVersions;

    /**
51
     * @var \TYPO3\CMS\Extbase\Object\ObjectManager
52 53 54 55
     */
    protected $objectManager;

    /**
56
     * @var \TYPO3\CMS\Extbase\Persistence\Generic\Session
57 58 59 60 61 62
     */
    protected $session;

    /**
     * @var array
     */
63
    protected $supportedCoreVersions = [];
64 65 66 67 68 69 70

    /**
     * @var int
     */
    protected $releaseDateOfOldestSupportedTypo3Version;

    /**
71
     * @var \ApacheSolrForTypo3\Solr\IndexQueue\Queue
72 73 74
     */
    protected $solrIndexQueue;

75 76 77 78 79
    /**
     * @var LoggerInterface
     */
    protected $logger;

80 81 82 83 84
    /**
     * Initialize Task
     */
    public function initializeTask()
    {
85 86 87
        $this->objectManager = GeneralUtility::makeInstance(ObjectManager::class);
        $this->persistenceManager = $this->objectManager->get(PersistenceManager::class);
        $this->session = $this->objectManager->get(Session::class);
88
        $this->versionRepository = $this->objectManager->get(\T3o\TerFe2\Domain\Repository\VersionRepository::class);
Markus Sommer's avatar
Markus Sommer committed
89 90 91 92
        $this->coreVersions = json_decode(
            GeneralUtility::getURL(PATH_site . $GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'] . 'currentcoredata.json'),
            true
        );
93
        $this->solrIndexQueue = $this->objectManager->get(Queue::class);
94 95 96 97 98 99 100 101 102
    }

    /**
     * Execute Task
     *
     * @return bool
     */
    public function execute()
    {
103 104 105
        $this->logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__);

        $this->logger->info('Task CheckForOutdatedExtensions started');
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120

        $this->initializeTask();

        // Find all extension versions which are not outdated.
        $versions = $this->getNotOutdatedAndSecureVersions();

        $this->getLatestAndOldestSupportedTypo3Versions();

        $this->releaseDateOfOldestSupportedTypo3Version = $this->getReleaseDateOfOldestSupportedTypo3Version();

        // Foreach extension
        foreach ($versions as $version) {
            $this->checkVersion($version);
        }

121 122
        $this->logger->info('Task CheckForOutdatedExtensions finished');

123
        return true;
124 125 126 127 128 129 130 131 132
    }

    /**
     * Get not outdated extensions
     *
     * @return mixed
     */
    public function getNotOutdatedAndSecureVersions()
    {
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
        $tableName = 'tx_terfe2_domain_model_version';
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
            ->getQueryBuilderForTable($tableName);
        $queryBuilder->getRestrictions()->removeAll();

        $statement = $queryBuilder
            ->select('uid')
            ->from($tableName)
            ->where(
                $queryBuilder->expr()->eq('deleted', 0),
                $queryBuilder->expr()->eq('hidden', 0),
                $queryBuilder->expr()->gte('review_state', 0)
            )
            ->orderBy('upload_date', 'ASC')
            ->execute();
        $statement->execute();

        return $statement->fetchAll(\PDO::FETCH_ASSOC);
151 152 153 154 155 156 157 158 159 160
    }

    /**
     * Get the release date of the oldest supported TYPO3 Version.
     *
     * @return int
     */
    public function getReleaseDateOfOldestSupportedTypo3Version()
    {
        $oldestMinorVersion = explode('.', $this->supportedCoreVersions['oldest']);
161
        $oldestMinorVersion = $oldestMinorVersion[0];
162 163 164 165 166 167 168 169 170

        $releaseDate = $this->coreVersions[$oldestMinorVersion]['releases'][$this->supportedCoreVersions['oldest']]['date'];

        return strtotime($releaseDate);
    }

    /**
     * Get the latest and oldest supported TYPO3 Versions.
     *
171
     * @throws \RuntimeException
172 173 174
     */
    public function getLatestAndOldestSupportedTypo3Versions()
    {
175 176
        if ($this->coreVersions === null) {
            throw new \RuntimeException('typo3.org JSON not accessible!', 1399140291);
177 178 179 180
        }
        // Collect currently supported core versions
        $oldestSupportedCoreVersion = '99.99.99';
        $latestSupportedCoreVersion = '0.0.0';
181
        $allSupportedCoreVersions = [];
182 183 184 185

        foreach ($this->coreVersions as $version => $coreInfo) {
            // Only use keys that represent a branch number
            if (preg_match('/^\d+\.\d+$/', $version) || preg_match('/^\d+$/', $version)) {
186
                if ($coreInfo['active'] == true) {
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
                    $allSupportedCoreVersions[] = $version;

                    // Checks the latest version
                    $latestBranchVersion = $coreInfo['latest'];
                    if (!preg_match('/dev|alpha/', $latestBranchVersion)) {
                        if (version_compare($latestSupportedCoreVersion, $latestBranchVersion, '<')) {
                            $latestSupportedCoreVersion = $latestBranchVersion;
                        }
                    }

                    // Check the oldest active version
                    if (version_compare($version . '.0', $oldestSupportedCoreVersion, '<')) {
                        $oldestSupportedCoreVersion = $version;
                    }
                }
            }
        }

        // get first beta of oldest active version
        $oldestSupportedCoreVersionReleases = array_reverse($this->coreVersions[$oldestSupportedCoreVersion]['releases']);
        foreach ($oldestSupportedCoreVersionReleases as $subVersion => $subVersionInfo) {
            if (!preg_match('/dev|alpha/', $subVersion)) {
                $oldestSupportedCoreVersion = $subVersion;
                break;
            }
        }

214
        $this->supportedCoreVersions = [
215 216 217
            'latest' => $latestSupportedCoreVersion,
            'oldest' => $oldestSupportedCoreVersion,
            'all' => $allSupportedCoreVersions,
218
        ];
219 220 221
    }

    /**
222
     * @param \T3o\TerFe2\Domain\Model\Relation $dependency
223
     *
224
     * @return bool
225 226 227
     */
    public function isVersionDependingOnAnActiveSupportedTypo3Version($dependency)
    {
228
        $result = false;
229

230
        if ($dependency instanceof \T3o\TerFe2\Domain\Model\Relation) {
231 232 233 234 235 236
            $extensionMinimumVersion = $dependency->getMinimumVersion();
            $extensionMaximumVersion = $dependency->getMaximumVersion();

            foreach ($this->supportedCoreVersions['all'] as $version) {
                $version = (string)$version;
                // gets core version x.x.0
237 238
                $supportedMinimumVersion = VersionNumberUtility::convertVersionNumberToInteger($version . '.0');
                $extensionMinimumVersionAsString = VersionNumberUtility::convertIntegerToVersionNumber($extensionMinimumVersion);
239 240 241 242 243

                /*
                 * checks if extension dependency lies within the first release of the main release version
                 * or extension minimum version begins with main release version
                 */
Markus Sommer's avatar
Markus Sommer committed
244
                if (($supportedMinimumVersion >= $extensionMinimumVersion || strpos($extensionMinimumVersionAsString, $version) === 0)
245 246
                    && $supportedMinimumVersion <= $extensionMaximumVersion
                ) {
247
                    $result = true;
248 249 250 251 252 253 254 255 256 257 258
                    break;
                }
            }
        }

        return $result;
    }

    /**
     * check if the given version is outdated and mark it in database
     *
259
     * @param int $versionRecord
260
     */
261
    protected function checkVersion($versionRecord)
262
    {
263
        /** @var \T3o\TerFe2\Domain\Model\Version $version */
264
        $version = $this->versionRepository->findByUid($versionRecord['uid']);
265

266
        if (!$version instanceof \T3o\TerFe2\Domain\Model\Version) {
267 268 269
            return;
        }

270
        $isOutdated = false;
271

272 273
        if ($version->getUploadDate() === null) {
            $isOutdated = true;
274
        // Check if date is set
275
        } elseif ($version->getUploadDate() < $this->releaseDateOfOldestSupportedTypo3Version) {
276
            $isOutdated = true;
277
        // Check upload date against oldestActiveTYPO3Version first release date.
278
        } elseif (!$this->isVersionDependingOnAnActiveSupportedTypo3Version($version->getTypo3Dependency())) {
279
            $isOutdated = true;
280 281 282 283
            // Check against dependency against TYPO3 not actively supported
        }

        if ($isOutdated) {
284 285 286 287 288 289 290 291 292 293
            $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);

            $queryBuilder = $connectionPool->getQueryBuilderForTable('tx_terfe2_domain_model_version');
            $queryBuilder
                ->update('tx_terfe2_domain_model_version')
                ->set('review_state', -2)
                ->where(
                    $queryBuilder->expr()->eq('uid', $version->getUid())
                )
                ->execute();
294 295

            if ($version->getExtension() && $version->getExtension()->getUid()) {
296 297 298 299 300 301 302 303 304
                $queryBuilder = $connectionPool->getQueryBuilderForTable('tx_terfe2_domain_model_extension');
                $queryBuilder
                    ->update('tx_terfe2_domain_model_extension')
                    ->set('tstamp', time())
                    ->where(
                        $queryBuilder->expr()->eq('uid', $version->getUid())
                    )
                    ->execute();

305 306 307 308 309 310 311 312 313 314 315 316
                $this->solrIndexQueue->updateItem('tx_terfe2_domain_model_extension', $version->getExtension()->getUid());
            }
        }

        $this->cleanupMemory($version);
    }

    /**
     * free some memory after checking a version
     *
     * prevent memory leaks on the long running scheduler task
     *
317
     * @param \T3o\TerFe2\Domain\Model\Version $version
318 319 320
     */
    public function cleanupMemory($version)
    {
321
        $this->session->unregisterReconstitutedEntity($version);
322
        foreach ($version->getSoftwareRelations() as $relation) {
323
            /** @var $relation \T3o\TerFe2\Domain\Model\Relation */
324
            $this->session->unregisterReconstitutedEntity($relation);
325 326
        }
    }
327
}