|
|
@@ -3,7 +3,9 @@
|
|
|
namespace Opentalent\OtAdmin\Controller;
|
|
|
|
|
|
use http\Exception\RuntimeException;
|
|
|
+use Opentalent\OtAdmin\Exception\NoSuchWebsiteException;
|
|
|
use Opentalent\OtCore\Cache\OtCacheManager;
|
|
|
+use Opentalent\OtCore\Controller\ActionController;
|
|
|
use Opentalent\OtCore\Domain\Model\Organization;
|
|
|
use Opentalent\OtCore\Domain\Repository\OrganizationRepository;
|
|
|
use Opentalent\OtCore\Exception\ApiRequestException;
|
|
|
@@ -12,7 +14,6 @@ use PDO;
|
|
|
use Symfony\Component\Yaml\Yaml;
|
|
|
use TYPO3\CMS\Core\Crypto\Random;
|
|
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
|
|
-use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
|
|
use TYPO3\CMS\Extbase\Object\ObjectManager;
|
|
|
|
|
|
|
|
|
@@ -63,6 +64,12 @@ class SiteController extends ActionController
|
|
|
"manager" => 3, // Association writer full
|
|
|
];
|
|
|
|
|
|
+ // Websites statuses
|
|
|
+ const STATUS_NO_SUCH_WEBSITE = 0;
|
|
|
+ const STATUS_EXISTING = 1;
|
|
|
+ const STATUS_EXISTING_DELETED = 2;
|
|
|
+ const STATUS_EXISTING_HIDDEN = 3;
|
|
|
+
|
|
|
const MODE_PROD = 1;
|
|
|
const MODE_DEV = 1;
|
|
|
|
|
|
@@ -592,8 +599,7 @@ class SiteController extends ActionController
|
|
|
$renamed = [];
|
|
|
|
|
|
try {
|
|
|
- $repository = GeneralUtility::makeInstance(ObjectManager::class)->get(OtPageRepository::class);
|
|
|
- $pages = $repository->getAllSubpagesForPage($rootUid);
|
|
|
+ $pages = $this->otPageRepository->getAllSubpagesForPage($rootUid);
|
|
|
foreach($pages as $page) {
|
|
|
$this->delete('tt_content', 'pid', $page['uid'], $hard);
|
|
|
$this->delete('pages', 'uid', $page['uid'], $hard);
|
|
|
@@ -768,8 +774,7 @@ class SiteController extends ActionController
|
|
|
$renamed = [];
|
|
|
|
|
|
try {
|
|
|
- $repository = GeneralUtility::makeInstance(ObjectManager::class)->get(OtPageRepository::class);
|
|
|
- $pages = $repository->getAllSubpagesForPage($rootUid);
|
|
|
+ $pages = $this->otPageRepository->getAllSubpagesForPage($rootUid);
|
|
|
|
|
|
foreach($pages as $page) {
|
|
|
$queryBuilder = $this->connectionPool->getQueryBuilderForTable('tt_content');
|
|
|
@@ -889,8 +894,10 @@ class SiteController extends ActionController
|
|
|
* @param int $organizationId the organization's id whom site cache should be cleared
|
|
|
* @param bool $clearAll if true, all caches will be cleared, and not only the frontend one
|
|
|
* @return int
|
|
|
+ * @throws NoSuchWebsiteException
|
|
|
*/
|
|
|
- public function clearSiteCacheAction($organizationId, $clearAll=false) {
|
|
|
+ public function clearSiteCacheAction(int $organizationId, $clearAll=false): int
|
|
|
+ {
|
|
|
$rootUid = $this->findRootUidFor($organizationId);
|
|
|
|
|
|
OtCacheManager::clearSiteCache($rootUid, $clearAll);
|
|
|
@@ -898,6 +905,152 @@ class SiteController extends ActionController
|
|
|
return $rootUid;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Pack the status informations into an array for the getSiteStatusAction method
|
|
|
+ *
|
|
|
+ * @param int $organizationId
|
|
|
+ * @param int $code
|
|
|
+ * @param int|null $rootUid
|
|
|
+ * @param array|null $warnings
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ private function buildStatusResult(
|
|
|
+ int $organizationId,
|
|
|
+ int $code,
|
|
|
+ ?int $rootUid = null,
|
|
|
+ ?array $warnings = null): array
|
|
|
+ {
|
|
|
+ $message = "";
|
|
|
+ if($code == self::STATUS_NO_SUCH_WEBSITE) {
|
|
|
+ $message = 'No website were found for organization ' . $organizationId;
|
|
|
+ } elseif ($code == self::STATUS_EXISTING) {
|
|
|
+ $message = 'A website exists for organization ' . $organizationId;
|
|
|
+ }
|
|
|
+ elseif ($code == self::STATUS_EXISTING_DELETED) {
|
|
|
+ $message = 'A website exists for organization ' . $organizationId . ', but has been deleted';
|
|
|
+ }
|
|
|
+ elseif ($code == self::STATUS_EXISTING_HIDDEN) {
|
|
|
+ $message = 'A website exists for organization ' . $organizationId . ', but is hidden or has a restricted access';
|
|
|
+ }
|
|
|
+
|
|
|
+ $title = '';
|
|
|
+ if ($rootUid !== null) {
|
|
|
+ $title = $this->otPageRepository->getPage($rootUid)['title'];
|
|
|
+ }
|
|
|
+
|
|
|
+ $status = [
|
|
|
+ 'organization_id' => $organizationId,
|
|
|
+ 'code' => $code,
|
|
|
+ 'message' => $message,
|
|
|
+ 'root_uid' => $rootUid,
|
|
|
+ 'title' => $title
|
|
|
+ ];
|
|
|
+ if ($warnings !== null) {
|
|
|
+ $status['warnings'] = $warnings;
|
|
|
+ }
|
|
|
+ return $status;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Perform a full scan of the website and returns a list of warnings
|
|
|
+ *
|
|
|
+ * @param int $organizationId
|
|
|
+ * @param int $rootUid
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ private function scanSite(int $organizationId, int $rootUid) {
|
|
|
+
|
|
|
+ $warnings = [];
|
|
|
+
|
|
|
+ // Who is the expected owner among the be_users? there should be only one.
|
|
|
+ $queryBuilder = $this->connectionPool->getQueryBuilderForTable('pages');
|
|
|
+ $queryBuilder->getRestrictions()->removeAll();
|
|
|
+ $beUsers = $queryBuilder
|
|
|
+ ->select('uid', 'username')
|
|
|
+ ->from('be_users')
|
|
|
+ ->where('FIND_IN_SET(' . $rootUid . ', db_mountpoints) > 0')
|
|
|
+ ->execute()
|
|
|
+ ->fetchAll();
|
|
|
+
|
|
|
+ $owner = null;
|
|
|
+ if (count($beUsers) > 1) {
|
|
|
+ $warnings[] = 'Website is mounted on more than one be_user: ' .
|
|
|
+ join(', ', array_map(function($u) { return $u['username']; } ,$beUsers));
|
|
|
+ } elseif (count($beUsers) == 0) {
|
|
|
+ $warnings[] = 'Website is not mounted on any be_user';
|
|
|
+ } else {
|
|
|
+ $owner = $beUsers[0];
|
|
|
+ }
|
|
|
+
|
|
|
+ // scan pages
|
|
|
+ $pages = $this->otPageRepository->getAllSitePages($rootUid);
|
|
|
+
|
|
|
+ foreach ($pages as $page) {
|
|
|
+
|
|
|
+ // Is it the correct owner?
|
|
|
+ if ($owner !== null && $page['perms_userid'] != $owner['uid']) {
|
|
|
+ $warnings[] = 'Page ' . $page['uid'] . ' has wrong owner';
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ return $warnings;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Get the current status of the organization's website
|
|
|
+ *
|
|
|
+ * The result is an array of the form:
|
|
|
+ *
|
|
|
+ * [
|
|
|
+ * 'organization_id' => int,
|
|
|
+ * 'code' => int,
|
|
|
+ * 'message' => string,
|
|
|
+ * 'root_uid' => ?int,
|
|
|
+ * 'site_title' => ?string,
|
|
|
+ * ('warnings' => array)
|
|
|
+ * ]
|
|
|
+ *
|
|
|
+ * If $statusOnly is true, the warnings entry won't be added
|
|
|
+ *
|
|
|
+ * The code value is among:
|
|
|
+ *
|
|
|
+ * - STATUS_NO_SUCH_WEBSITE
|
|
|
+ * - STATUS_EXISTING
|
|
|
+ * - STATUS_EXISTING_DELETED
|
|
|
+ * - STATUS_EXISTING_HIDDEN
|
|
|
+ *
|
|
|
+ * @param int $organizationId the organization's id whom site cache should be cleared
|
|
|
+ * @param bool $fullScan If true, a 'warnings' entry will be added to the result, and a full scan of
|
|
|
+ * the website pages will be performed.
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public function getSiteStatusAction(int $organizationId, bool $fullScan = false): array
|
|
|
+ {
|
|
|
+ try {
|
|
|
+ $rootUid = $this->findRootUidFor($organizationId);
|
|
|
+ } catch (NoSuchWebsiteException $e) {
|
|
|
+ return $this->buildStatusResult($organizationId, self::STATUS_NO_SUCH_WEBSITE);
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($rootUid['deleted']) {
|
|
|
+ return $this->buildStatusResult($organizationId, self::STATUS_EXISTING_DELETED, $rootUid);
|
|
|
+ }
|
|
|
+ if ($rootUid['hidden'] || ($rootUid['fe_group'] < 0)) {
|
|
|
+ return $this->buildStatusResult($organizationId, self::STATUS_EXISTING_HIDDEN, $rootUid);
|
|
|
+ }
|
|
|
+
|
|
|
+ $warnings = null;
|
|
|
+ if ($fullScan) {
|
|
|
+ // ** Look for potential issues
|
|
|
+ $warnings = $this->scanSite($organizationId, $rootUid);
|
|
|
+ }
|
|
|
+ return $this->buildStatusResult($organizationId, self::STATUS_EXISTING, $rootUid, $warnings);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
/**
|
|
|
* Retrieve the Organization object from the repository and then,
|
|
|
* from the Opentalent API
|
|
|
@@ -915,9 +1068,11 @@ class SiteController extends ActionController
|
|
|
|
|
|
/**
|
|
|
* Try to find the root page uid of the organization's website and return it.
|
|
|
+ * Throw a Opentalent\OtAdmin\NoSuchWebsiteException exception if the website does not exist.
|
|
|
*
|
|
|
* @param $organizationId
|
|
|
* @return int
|
|
|
+ * @throws NoSuchWebsiteException
|
|
|
*/
|
|
|
private function findRootUidFor($organizationId) {
|
|
|
$queryBuilder = $this->connectionPool->getQueryBuilderForTable('pages');
|
|
|
@@ -933,22 +1088,7 @@ class SiteController extends ActionController
|
|
|
if ($rootUid > 0) {
|
|
|
return $rootUid;
|
|
|
}
|
|
|
-
|
|
|
- $organization = $this->fetchOrganization($organizationId);
|
|
|
- $queryBuilder = $this->connectionPool->getQueryBuilderForTable('pages');
|
|
|
- $rootUid = $queryBuilder
|
|
|
- ->count('uid')
|
|
|
- ->from('pages')
|
|
|
- ->where('is_siteroot=1')
|
|
|
- ->andWhere($queryBuilder->expr()->eq('title', $queryBuilder->createNamedParameter($organization->getName())))
|
|
|
- ->andWhere('tx_opentalent_structure_id=null')
|
|
|
- ->execute()
|
|
|
- ->fetchColumn(0);
|
|
|
-
|
|
|
- if ($rootUid > 0) {
|
|
|
- return $rootUid;
|
|
|
- }
|
|
|
- throw new \RuntimeException("The website of this organization can not be found");
|
|
|
+ throw new NoSuchWebsiteException("The website of this organization can not be found");
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -1235,23 +1375,28 @@ class SiteController extends ActionController
|
|
|
* @param string $domain
|
|
|
*/
|
|
|
private function writeConfigFile(int $organizationId, int $rootUid, string $domain) {
|
|
|
- $folder_id = explode('.', $domain)[0] . '_' . $organizationId;
|
|
|
- $config_dir = $_ENV['TYPO3_PATH_APP'] . "/config/sites/" . $folder_id;
|
|
|
|
|
|
+ $subdomain = explode('.', $domain)[0];
|
|
|
+
|
|
|
+ $config_dir = $_ENV['TYPO3_PATH_APP'] . "/config/sites/" . $subdomain . '_' . $organizationId;
|
|
|
$config_filename = $config_dir . "/config.yaml";
|
|
|
+
|
|
|
if (file_exists($config_filename)) {
|
|
|
- throw new \RuntimeException("A file " . $config_filename . " already exists. Abort.");
|
|
|
+ throw new \RuntimeException("A file named " . $config_filename . " already exists. Abort.");
|
|
|
}
|
|
|
|
|
|
- $config = ['base'=> 'https://' . $domain,
|
|
|
- 'baseVariants'=>[],
|
|
|
+ $config = ['base' => 'https://' . $domain,
|
|
|
+ 'baseVariants'=>[
|
|
|
+ ['base' => $subdomain . '/',
|
|
|
+ 'condition' => 'applicationContext == "Development"']
|
|
|
+ ],
|
|
|
'errorHandling'=>[
|
|
|
['errorCode'=>'404',
|
|
|
- 'errorHandler'=>'PHP',
|
|
|
- 'errorPhpClassFQCN'=>'Opentalent\OtTemplating\Page\ErrorHandler'],
|
|
|
+ 'errorHandler'=>'PHP',
|
|
|
+ 'errorPhpClassFQCN'=>'Opentalent\OtTemplating\Page\ErrorHandler'],
|
|
|
['errorCode'=>'403',
|
|
|
- 'errorHandler'=>'PHP',
|
|
|
- 'errorPhpClassFQCN'=>'Opentalent\OtTemplating\Page\ErrorHandler'],
|
|
|
+ 'errorHandler'=>'PHP',
|
|
|
+ 'errorPhpClassFQCN'=>'Opentalent\OtTemplating\Page\ErrorHandler'],
|
|
|
],
|
|
|
'flux_content_types'=>'',
|
|
|
'flux_page_templates'=>'',
|