HEX
Server: Apache
System: Linux WWW 6.1.0-40-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.153-1 (2025-09-20) x86_64
User: web11 (1011)
PHP: 8.2.29
Disabled: NONE
Upload Files
File: /var/www/apklausos/application/libraries/ExtensionInstaller/QuestionThemeInstaller.php
<?php

namespace LimeSurvey\ExtensionInstaller;

use Exception;
use Throwable;
use ExtensionConfig;
use QuestionTheme;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;

class QuestionThemeInstaller extends ExtensionInstaller
{
    /**
     * @return ExtensionConfig
     * @todo Move to parent class?
     */
    public function getConfig()
    {
        assert(!empty($this->fileFetcher), 'File fetcher must be set');

        return $this->fileFetcher->getConfig();
    }

    /**
     * Installs new question theme.
     * Assumes it has been checked that is not already installed. If it is, use update() instead.
     *
     * @return void
     */
    public function install()
    {
        $extConfig = $this->getConfig();
        $destdir = App()->getConfig('userquestionthemerootdir') . DIRECTORY_SEPARATOR . $extConfig->getName();

        if ($this->fileFetcher->move($destdir)) {
            $questionTheme = new QuestionTheme();
            $xmlFolder = $this->getXmlFolder($destdir);
            if (empty($xmlFolder)) {
                throw new Exception('Found no xml folder for question theme');
            }
            $questionTheme->importManifest($xmlFolder, false, true);
            $this->fileFetcher->abort();
        } else {
            throw new Exception('Could not move files.');
        }

        // Question themes that apply to more than one question type, are technically different themes but can be distributed
        // in the same ZIP. So we must try to install all the available themes in the folder.
        /*
        $nrOfImportedThemes = 0;
        $directory = new RecursiveDirectoryIterator($destdir);
        $iterator = new RecursiveIteratorIterator($directory);
        $importErrors = [];
        foreach ($iterator as $info) {
            if ($info->isFile() && $info->getBasename() == 'config.xml') {
                $questionConfigFilePath = dirname($info->getPathname());
                $sQuestionThemeTitle = null;
                try {
                    $questionTheme = new QuestionTheme();
                    $sQuestionThemeTitle = $questionTheme->importManifest($questionConfigFilePath, false, true);
                } catch (Throwable $t) {
                    $sThemeDirectoryName = $questionTheme->getThemeDirectoryPath($questionConfigFilePath . "/config.xml");
                    $importErrors[$sThemeDirectoryName] = $t->getMessage();
                }
                if (!empty($sQuestionThemeTitle)) {
                    $nrOfImportedThemes++;
                }
            }
        }
        if ($nrOfImportedThemes == 0) {
            rmdirr($destdir);
            App()->setFlashMessage(
                gT("An error occurred while generating the Question theme"),
                'error'
            );
            $this->getController()->redirect(array("themeOptions/index#questionthemes"));
        }
        if (count($importErrors) > 0) {
            Yii::app()->setFlashMessage(gT("Some of the themes couldn't be imported."), 'error');
        }
         */
    }

    /**
     * Update an existing question theme.
     *
     * @throws Exception
     */
    public function update()
    {
        $extConfig = $this->getConfig();
        $destdir = App()->getConfig('userquestionthemerootdir') . DIRECTORY_SEPARATOR . $extConfig->getName();

        if ($this->fileFetcher->move($destdir)) {
            $questionTheme =  QuestionTheme::model()->findByAttributes(['name' => $extConfig->getName()]);
            if (empty($questionTheme)) {
                throw new Exception('Tried to update question theme but found no theme with name ' . $extConfig->getName());
            }
            $xmlFolder = $this->getXmlFolder($destdir);
            if (empty($xmlFolder)) {
                throw new Exception('Found no xml folder for question theme');
            }
            $questionTheme->importManifest($xmlFolder, false, true);
            $this->fileFetcher->abort();
        } else {
            throw new Exception('Could not move files.');
        }
    }

    /**
     * @todo
     */
    public function uninstall()
    {
        throw new Exception('Not implemented');
    }

    /**
     * Returns absolute path of folder inside $destdir that has config.xml in it.
     *
     * @param string $dir Root dir of question theme
     * @return string|null Folder as string if config.xml is found; otherwise null
     */
    protected function getXmlFolder($dir)
    {
        $it = new RecursiveDirectoryIterator($dir);
        // @see https://stackoverflow.com/questions/1860393/recursive-file-search-php
        foreach (new RecursiveIteratorIterator($it) as $key => $file) {
            // @see https://stackoverflow.com/questions/619610/whats-the-most-efficient-test-of-whether-a-php-string-ends-with-another-string?lq=1
            if (stripos(strrev((string) $file), strrev('config.xml')) === 0) {
                return dirname((string) $key);
            }
        }
        return null;
    }
}