SiteController.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. <?php
  2. namespace Opentalent\OtAdmin\Controller;
  3. use Opentalent\OtTemplating\Domain\Model\Organization;
  4. use Opentalent\OtTemplating\Domain\Repository\OrganizationRepository;
  5. use Opentalent\OtTemplating\Exception\ApiRequestException;
  6. use Psr\Log\LoggerAwareInterface;
  7. use Symfony\Component\Yaml\Yaml;
  8. use TYPO3\CMS\Core\Database\ConnectionPool;
  9. use TYPO3\CMS\Core\Utility\GeneralUtility;
  10. use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
  11. use TYPO3\CMS\Extbase\Object\ObjectManager;
  12. /**
  13. * The SiteController implements some admin-only operations
  14. * on Typo3 websites, like creation or update.
  15. */
  16. class SiteController extends ActionController
  17. {
  18. // Templates names
  19. const TEMPLATE_HOME = "OpenTalent.OtTemplating->home";
  20. const TEMPLATE_1COL = "OpenTalent.OtTemplating->1Col";
  21. const TEMPLATE_3COL = "OpenTalent.OtTemplating->home";
  22. const TEMPLATE_EVENTS = "OpenTalent.OtTemplating->events";
  23. const TEMPLATE_STRUCTURESEVENTS = "OpenTalent.OtTemplating->structuresEvents";
  24. const TEMPLATE_STRUCTURES = "OpenTalent.OtTemplating->structures";
  25. const TEMPLATE_CONTACT = "OpenTalent.OtTemplating->contact";
  26. const TEMPLATE_NEWS = "OpenTalent.OtTemplating->news";
  27. const TEMPLATE_MEMBERS = "OpenTalent.OtTemplating->members";
  28. const TEMPLATE_MEMBERSCA = "OpenTalent.OtTemplating->membersCa";
  29. const TEMPLATE_E404 = "OpenTalent.OtTemplating->e404";
  30. // Pages dokType values
  31. const DOK_PAGE = 1;
  32. const DOK_SHORTCUT = 4;
  33. const DOK_FOLDER = 116;
  34. // Contents CTypes
  35. const CTYPE_TEXT = 'text';
  36. const CTYPE_IMAGE = 'image';
  37. const CTYPE_TEXTPIC = 'textpic';
  38. const CTYPE_TEXTMEDIA = 'textmedia';
  39. const CTYPE_HTML = 'html';
  40. const CTYPE_HEADER = 'header';
  41. const CTYPE_UPLOADS = 'uploads';
  42. const CTYPE_LIST = 'list';
  43. const CTYPE_SITEMAP = 'menu_sitemap';
  44. // const DEFAULT_ROOT_PID = 134833;
  45. const DEFAULT_ROOT_PID = 11;
  46. // Default values
  47. const DEFAULT_THEME = 'Classic';
  48. const DEFAULT_COLOR = 'light-blue';
  49. /**
  50. * Doctrine connection pool
  51. * @var object|LoggerAwareInterface|\TYPO3\CMS\Core\SingletonInterface
  52. */
  53. private $cnnPool;
  54. /**
  55. * Index of the pages created
  56. * >> [slug => uid]
  57. * @var array
  58. */
  59. private $createdPagesIndex;
  60. public function __construct()
  61. {
  62. parent::__construct();
  63. $this->cnnPool = GeneralUtility::makeInstance(ConnectionPool::class);
  64. $this->createdPagesIndex = [];
  65. }
  66. /**
  67. * Creates a new website for the given organization, and
  68. * returns the root page uid of the newly created site
  69. *
  70. * @param int $organizationId
  71. * @return int Uid of the root page of the newly created website
  72. * @throws \RuntimeException
  73. */
  74. public function createSiteAction(int $organizationId) {
  75. // ** Get the organization object from the Opentalent API
  76. $manager = GeneralUtility::makeInstance(ObjectManager::class);
  77. $organizationRepository = GeneralUtility::makeInstance(
  78. OrganizationRepository::class,
  79. $manager
  80. );
  81. try {
  82. $organization = $organizationRepository->findById($organizationId);
  83. } catch (ApiRequestException $e) {
  84. throw new \RuntimeException('Unable to fetch the organization with id: ' . $organizationId);
  85. }
  86. // ** Test the existence of a website with this name and or organization id
  87. // Is there a site with this organization's name?
  88. $queryBuilder = $this->cnnPool->getQueryBuilderForTable('pages');
  89. $queryBuilder
  90. ->select('uid')
  91. ->from('pages')
  92. ->where($queryBuilder->expr()->eq('title', $queryBuilder->createNamedParameter($organization->getName())))
  93. ->andWhere('is_siteroot=1');
  94. $statement = $queryBuilder->execute();
  95. if ($statement->rowCount() > 0) {
  96. throw new \RuntimeException('A website with this name already exists: ' . $organization->getName());
  97. }
  98. // Is there a site with this organization's id?
  99. $queryBuilder = $this->cnnPool->getQueryBuilderForTable('pages');
  100. $statement = $queryBuilder
  101. ->select('uid')
  102. ->from('pages')
  103. ->where($queryBuilder->expr()->eq('tx_opentalent_structure_id', $queryBuilder->createNamedParameter($organization->getId())))
  104. ->andWhere('is_siteroot=1')
  105. ->execute();
  106. if ($statement->rowCount() > 0) {
  107. throw new \RuntimeException("A website with this organization's id already exists: " . $organization->getName());
  108. }
  109. // ** Create the new website
  110. // start transactions
  111. $tables = ['be_users', 'pages', 'sys_domain', 'sys_template', 'tt_content'];
  112. foreach ($tables as $table) {
  113. $this->cnnPool->getConnectionForTable($table)->beginTransaction();
  114. }
  115. $pages = [];
  116. try {
  117. // Create the site pages:
  118. // > Root page
  119. $rootUid = $this->insertRootPage($organization);
  120. // > 'Accueil' shortcut
  121. $this->insertPage(
  122. $organization,
  123. $rootUid,
  124. 'Accueil',
  125. '/accueil',
  126. '',
  127. [
  128. 'dokType' => self::DOK_SHORTCUT,
  129. 'shortcut' => $rootUid
  130. ]
  131. );
  132. // > 'Présentation' page
  133. $this->insertPage(
  134. $organization,
  135. $rootUid,
  136. 'Présentation',
  137. '/presentation'
  138. );
  139. // > 'Présentation > Qui sommes nous?' page (hidden by default)
  140. $this->insertPage(
  141. $organization,
  142. $this->createdPagesIndex['/presentation'],
  143. 'Qui sommes nous?',
  144. '/qui-sommes-nous',
  145. '',
  146. ['hidden' => 1]
  147. );
  148. // > 'Présentation > Les adhérents' page
  149. $this->insertPage(
  150. $organization,
  151. $this->createdPagesIndex['/presentation'],
  152. 'Les adhérents',
  153. '/les-adherents',
  154. self::TEMPLATE_MEMBERS
  155. );
  156. // > 'Présentation > Les membres du CA' page
  157. $this->insertPage(
  158. $organization,
  159. $this->createdPagesIndex['/presentation'],
  160. 'Les membres du CA',
  161. '/les-membres-du-ca',
  162. self::TEMPLATE_MEMBERSCA
  163. );
  164. // > 'Présentation > Historique' page (hidden by default)
  165. $this->insertPage(
  166. $organization,
  167. $this->createdPagesIndex['/presentation'],
  168. 'Historique',
  169. '/historique',
  170. '',
  171. ['hidden' => 1]
  172. );
  173. // > 'Actualités' page (hidden by default)
  174. $this->insertPage(
  175. $organization,
  176. $rootUid,
  177. 'Actualités',
  178. '/actualites',
  179. self::TEMPLATE_NEWS
  180. );
  181. // > 'Saison en cours' page
  182. $this->insertPage(
  183. $organization,
  184. $rootUid,
  185. 'Saison en cours',
  186. '/saison-en-cours'
  187. );
  188. // > 'Saison en cours > Les évènements' page
  189. $this->insertPage(
  190. $organization,
  191. $this->createdPagesIndex['/saison-en-cours'],
  192. 'Les évènements',
  193. '/les-evenements',
  194. self::TEMPLATE_EVENTS
  195. );
  196. // > 'Vie interne' page (restricted, hidden by default)
  197. $this->insertPage(
  198. $organization,
  199. $rootUid,
  200. 'Vie interne',
  201. '/vie-interne',
  202. '',
  203. [
  204. 'hidden' => 1,
  205. 'fe_group' => -2
  206. ]
  207. );
  208. // > 'Footer' page (not in the menu)
  209. $this->insertPage(
  210. $organization,
  211. $rootUid,
  212. 'Footer',
  213. '/footer',
  214. '',
  215. [
  216. 'dokType' => self::DOK_FOLDER,
  217. 'nav_hide' => 1
  218. ]
  219. );
  220. // > 'Footer > Contact' page
  221. $this->insertPage(
  222. $organization,
  223. $this->createdPagesIndex['/footer'],
  224. 'Contact',
  225. '/contact',
  226. self::TEMPLATE_CONTACT
  227. );
  228. // > 'Footer > Plan du site' page
  229. $this->insertPage(
  230. $organization,
  231. $this->createdPagesIndex['/footer'],
  232. 'Plan du site',
  233. '/plan-du-site'
  234. );
  235. // > 'Footer > Mentions légales' page
  236. $this->insertPage(
  237. $organization,
  238. $this->createdPagesIndex['/footer'],
  239. 'Mentions légales',
  240. '/mentions-legales'
  241. );
  242. // > 'Page introuvable' page (not in the menu, read-only)
  243. $this->insertPage(
  244. $organization,
  245. $rootUid,
  246. 'Page introuvable',
  247. '/page-introuvable',
  248. self::TEMPLATE_E404,
  249. [
  250. 'nav_hide' => 1,
  251. 'no_search' => 1
  252. ]
  253. );
  254. // Add content to these pages
  255. // >> root page content
  256. $this->insertContent(
  257. $rootUid,
  258. self::CTYPE_TEXTPIC,
  259. '<h1>Bienvenue sur le site de ' . $organization->getName() . '.</h1>',
  260. 0
  261. );
  262. // >> page 'qui sommes nous?'
  263. $this->insertContent(
  264. $this->createdPagesIndex['/qui-sommes-nous'],
  265. self::CTYPE_TEXT,
  266. 'Qui sommes nous ...',
  267. 0
  268. );
  269. // >> page 'historique'
  270. $this->insertContent(
  271. $this->createdPagesIndex['/historique'],
  272. self::CTYPE_TEXT,
  273. "Un peu d'histoire ...",
  274. 0
  275. );
  276. // >> page 'plan du site'
  277. $this->insertContent(
  278. $this->createdPagesIndex['/plan-du-site'],
  279. self::CTYPE_SITEMAP
  280. );
  281. // >> page 'mentions légales'
  282. $this->insertContent(
  283. $this->createdPagesIndex['/mentions-legales'],
  284. self::CTYPE_TEXT,
  285. '<p style="margin-bottom: 0cm"><b>Mentions Légales</b></p>',
  286. 0
  287. );
  288. // Build and update the domain
  289. $domain = $organization->getSubDomain() . '.opentalent.fr';
  290. $queryBuilder = $this->cnnPool->getQueryBuilderForTable('sys_domain');
  291. $queryBuilder->insert('sys_domain')
  292. ->values([
  293. 'pid' => $rootUid,
  294. 'domainName' => $domain
  295. ])
  296. ->execute();
  297. // Create the template
  298. $constants = "plugin.tx_ottemplating {" .
  299. " settings {" .
  300. " organization {" .
  301. " id = " . $organization->getId() .
  302. " name = " . $organization->getName() .
  303. " is_network = " . (1 ? $organization->getIsNetwork() : 0) .
  304. " email = " . $organization->getEmail() .
  305. " logoid = " . explode('/', $organization->getLogo(), -1)[0] .
  306. " twitter = " . $organization->getTwitter() .
  307. " facebook = " . $organization->getFacebook() .
  308. " }" .
  309. " network {" .
  310. " logo = " . $organization->getNetworkLogo() .
  311. " name = CMF" . $organization->getNetworkName() .
  312. " url = " . $organization->getNetworkUrl() .
  313. " }" .
  314. " }" .
  315. "}" .
  316. $queryBuilder = $this->cnnPool->getQueryBuilderForTable('sys_template');
  317. $queryBuilder->insert('sys_template')
  318. ->values([
  319. 'pid' => $rootUid,
  320. 'title' => $organization->getName(),
  321. 'sitetitle' => $organization->getName(),
  322. 'root' => 1,
  323. 'clear' => 3,
  324. 'include_static_file' => 'EXT:fluid_styled_content/Configuration/TypoScript/,EXT:fluid_styled_content/Configuration/TypoScript/Styling/,EXT:ot_widgets/Configuration/TypoScript,EXT:form/Configuration/TypoScript/,EXT:news/Configuration/TypoScript,EXT:ot_templating/Configuration/TypoScript',
  325. 'constants' => $constants
  326. ])
  327. ->execute();
  328. // Create the site config.yaml file
  329. $config = ['base'=> 'https://' . $domain,
  330. 'baseVariants'=>[],
  331. 'errorHandling'=>[
  332. ['errorCode'=>'404',
  333. 'errorHandler'=>'Page',
  334. 'errorContentSource'=>'t3://page?uid=' . $this->createdPagesIndex['/page-introuvable']],
  335. ['errorCode'=>'403',
  336. 'errorHandler'=>'Page',
  337. 'errorContentSource'=>'t3://page?uid=' . $this->createdPagesIndex['/page-introuvable']]
  338. ],
  339. 'flux_content_types'=>'',
  340. 'flux_page_templates'=>'',
  341. 'languages'=>[[
  342. 'title'=>'Fr',
  343. 'enabled'=>True,
  344. 'base'=>'/',
  345. 'typo3Language'=>'fr',
  346. 'locale'=>'fr_FR',
  347. 'iso-639-1'=>'fr',
  348. 'navigationTitle'=>'Fr',
  349. 'hreflang'=>'fr-FR',
  350. 'direction'=>'ltr',
  351. 'flag'=>'fr',
  352. 'languageId'=>'0',
  353. ]],
  354. 'rootPageId'=>$rootUid,
  355. 'routes'=>[]
  356. ];
  357. $yamlConfig = Yaml::dump($config, 4);
  358. // Create the contact form
  359. $contscatForm = "";
  360. // Create the BE user
  361. // Create the user_upload directory
  362. // throw new \Exception('not implemented');
  363. // Try to commit the result
  364. foreach ($tables as $table) {
  365. $commitSuccess = $this->cnnPool->getConnectionForTable($table)->commit();
  366. if (!$commitSuccess) {
  367. throw new \RuntimeException('Something went wrong while commiting the result');
  368. }
  369. }
  370. return $rootUid;
  371. } catch(\Exception $e) {
  372. // rollback
  373. foreach ($tables as $table) {
  374. $this->cnnPool->getConnectionForTable($table)->rollback();
  375. }
  376. throw $e;
  377. }
  378. }
  379. /**
  380. * Insert a new row in the 'pages' table of the Typo3 DB
  381. *
  382. * @param Organization $organization
  383. * @param int $pid
  384. * @param string $title
  385. * @param string $slug
  386. * @param string $template
  387. * @param array $moreValues
  388. * @return string
  389. */
  390. private function insertPage(Organization $organization,
  391. int $pid,
  392. string $title,
  393. string $slug,
  394. string $template = '',
  395. array $moreValues = []
  396. ) {
  397. $defaultValues = [
  398. 'pid' => $pid,
  399. 'perms_groupid' => 3,
  400. 'perms_user' => 27,
  401. 'cruser_id' => 1,
  402. 'dokType' => self::DOK_PAGE,
  403. 'title' => $title,
  404. 'slug' => $slug,
  405. 'backend_layout' => 'flux__grid',
  406. 'backend_layout_next_level' => 'flux__grid',
  407. 'tx_opentalent_structure_id' => $organization->getId()
  408. ];
  409. if ($template) {
  410. $defaultValues['tx_fed_page_controller_action'] = $template;
  411. $defaultValues['tx_fed_page_controller_action_sub'] = self::TEMPLATE_1COL;
  412. }
  413. $values = array_merge($defaultValues, $moreValues);
  414. $queryBuilder = $this->cnnPool->getQueryBuilderForTable('pages');
  415. $queryBuilder->insert('pages')
  416. ->values($values)
  417. ->execute();
  418. $uid = $queryBuilder->getConnection()->lastInsertId();
  419. $this->createdPagesIndex[$slug] = $uid;
  420. return $uid;
  421. }
  422. /**
  423. * Insert the root page of a new organization's website
  424. *
  425. * @param Organization $organization
  426. * @return string
  427. */
  428. private function insertRootPage(Organization $organization) {
  429. return $this->insertPage(
  430. $organization,
  431. self::DEFAULT_ROOT_PID,
  432. $organization->getName(),
  433. '/',
  434. self::TEMPLATE_HOME,
  435. [
  436. 'is_siteroot' => 1,
  437. 'TSconfig' => 'TCAdefaults.pages.tx_opentalent_structure_id =' . $organization->getId(),
  438. 'tx_opentalent_template' => self::DEFAULT_THEME,
  439. 'tx_opentalent_template_preferences' => '{"themeColor":"' . self::DEFAULT_COLOR . '","displayCarousel":"1"}'
  440. ]
  441. );
  442. }
  443. /**
  444. * Insert a new row in the 'tt_content' table of the Typo3 DB
  445. *
  446. * @param int $pid
  447. * @param string $cType
  448. * @param string $bodyText
  449. * @param int $colPos
  450. * @param array $moreValues
  451. */
  452. private function insertContent(int $pid,
  453. string $cType=self::CTYPE_TEXT,
  454. string $bodyText = '',
  455. int $colPos=0,
  456. array $moreValues = []) {
  457. $defaultValues = [
  458. 'pid' => $pid,
  459. 'cruser_id' => 1,
  460. 'CType' => $cType,
  461. 'colPos' => $colPos,
  462. 'bodyText' => $bodyText
  463. ];
  464. $values = array_merge($defaultValues, $moreValues);
  465. $queryBuilder = $this->cnnPool->getQueryBuilderForTable('tt_content');
  466. $queryBuilder->insert('tt_content')
  467. ->values($values)
  468. ->execute();
  469. }
  470. }