BackendService.php 16.2 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php

namespace SGalinski\SgMail\Service;

/***************************************************************
 *  Copyright notice
 *
 *  (c) sgalinski Internet Services (https://www.sgalinski.de)
 *
 *  All rights reserved
 *
 *  This script is part of the TYPO3 project. The TYPO3 project is
 *  free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  The GNU General Public License can be found at
 *  http://www.gnu.org/copyleft/gpl.html.
 *
 *  This script is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  This copyright notice MUST APPEAR in all copies of the script!
 ***************************************************************/

29
30
use Doctrine\DBAL\Driver\Mysqli\MysqliStatement;
use Doctrine\DBAL\Statement;
31
use SGalinski\SgMail\Domain\Model\Template;
32
use SGalinski\SgMail\Domain\Repository\FrontendUserGroupRepository;
Torsten Oppermann's avatar
Torsten Oppermann committed
33
use SGalinski\SgMail\Domain\Repository\MailRepository;
34
use SGalinski\SgMail\Domain\Repository\TemplateRepository;
35
use SGalinski\SgMail\Utility\ExtensionUtility;
36
37
38
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
use TYPO3\CMS\Backend\Template\Components\DocHeaderComponent;
use TYPO3\CMS\Backend\Utility\BackendUtility;
39
40
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
41
42
43
use TYPO3\CMS\Core\Imaging\Icon;
use TYPO3\CMS\Core\Imaging\IconFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
44
use TYPO3\CMS\Core\Utility\VersionNumberUtility;
45
use TYPO3\CMS\Extbase\Mvc\Request;
46
use TYPO3\CMS\Extbase\Object\ObjectManager;
47
use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager;
48
49
50
51
52
53
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;

/**
 * Backend Service class
 */
class BackendService {
Torsten Oppermann's avatar
Torsten Oppermann committed
54
	// options for the queue search filter
55
56
57
58
59
60
61
62
	const SENDER_FILTER_OPTION = 0;
	const RECIPIENT_FILTER_OPTION = 1;
	const SUBJECT_FILTER_OPTION = 2;
	const MAILTEXT_FILTER_OPTION = 3;
	const CC_FILTER_OPTION = 4;
	const BCC_FILTER_OPTION = 5;
	const FROM_NAME_FILTER_OPTION = 6;
	const REPLY_TO_NAME_FILTER_OPTION = 7;
63

64
	// constants for deetermining the backend mode
65
66
67
68
	const BACKEND_MODE_EDITOR = 'editor';
	const BACKEND_MODE_EDITOR_CONTROLLER = 'Mail';
	const BACKEND_MODE_QUEUE = 'queue';
	const BACKEND_MODE_QUEUE_CONTROLLER = 'Queue';
69
	const BACKEND_MODE_QUEUE_NEWSLETTER = 'Newsletter';
70

71
72
73
74
75
76
	/**
	 * Get all pages the be user has access to
	 *
	 * @return array
	 * @throws \InvalidArgumentException
	 */
77
	public static function getPages(): array {
78
		$out = [];
79
80
81
82
83
84

		$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
		$queryBuilder = $connectionPool->getQueryBuilderForTable('pages');
		$queryBuilder->getRestrictions()
			->removeAll()
			->add(GeneralUtility::makeInstance(DeletedRestriction::class));
85
		$queryBuilder->select('*')
86
87
			->from('pages')
			->where(
88
89
90
				$queryBuilder->expr()->eq(
					'is_siteroot',
					1
91
				)
92
93
94
95
96
97
98
99
100
101
102
103
			);

		if (!version_compare(VersionNumberUtility::getCurrentTypo3Version(), '9.0.0', '<')) {
			$queryBuilder->andWhere(
				$queryBuilder->expr()->eq(
					'sys_language_uid',
					0
				)
			);
		}

		$rows = $queryBuilder->execute()->fetchAll();
104

105
106
107
		foreach ($rows as $row) {
			$pageInfo = BackendUtility::readPageAccess($row['uid'], $GLOBALS['BE_USER']->getPagePermsClause(1));
			if ($pageInfo) {
108
				$rootline = BackendUtility::BEgetRootLine($pageInfo['uid'], '', TRUE);
109
110
				ksort($rootline);
				$path = '/root';
111
				foreach ($rootline as $page) {
112
113
114
					$path .= '/p' . dechex($page['uid']);
				}
				$pageInfo['path'] = $path;
115
116
117
118
				$out[] = $pageInfo;
			}
		}
		return $out;
119
120
121
122
123
124
125
126
127
128
	}

	/**
	 * create buttons for the backend module header
	 *
	 * @param DocHeaderComponent $docHeaderComponent
	 * @param Request $request
	 * @throws \InvalidArgumentException
	 * @throws \UnexpectedValueException
	 */
129
	public static function makeButtons($docHeaderComponent, $request) {
130
131
132
133
134
135
		/** @var ButtonBar $buttonBar */
		$buttonBar = $docHeaderComponent->getButtonBar();

		/** @var IconFactory $iconFactory */
		$iconFactory = GeneralUtility::makeInstance(IconFactory::class);

136
137
138
139
140
141
		if (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '9.0.0', '<')) {
			$locallangPath = 'LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:';
		} else {
			$locallangPath = 'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:';
		}

142
143
144
		// Refresh
		$refreshButton = $buttonBar->makeLinkButton()
			->setHref(GeneralUtility::getIndpEnv('REQUEST_URI'))
145
146
			->setTitle(
				LocalizationUtility::translate(
147
					$locallangPath . 'labels.reload'
148
149
				)
			)
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
			->setIcon($iconFactory->getIcon('actions-refresh', Icon::SIZE_SMALL));
		$buttonBar->addButton($refreshButton, ButtonBar::BUTTON_POSITION_RIGHT);

		// shortcut button
		$shortcutButton = $buttonBar->makeShortcutButton()
			->setModuleName($request->getPluginName())
			->setGetVariables(
				[
					'id',
					'M'
				]
			)
			->setSetVariables([]);

		$buttonBar->addButton($shortcutButton, ButtonBar::BUTTON_POSITION_RIGHT);
	}
Torsten Oppermann's avatar
Torsten Oppermann committed
166
167
168
169

	/**
	 * Retrieves the next site root in the page hierarchy from the current page
	 *
Torsten Oppermann's avatar
Torsten Oppermann committed
170
	 * @param int $currentPid
171
	 * @return int
Torsten Oppermann's avatar
Torsten Oppermann committed
172
	 */
173
	public static function getSiteRoot($currentPid): int {
Torsten Oppermann's avatar
Torsten Oppermann committed
174
175
		$rootLine = BackendUtility::BEgetRootLine((int) $currentPid);
		$siteRoot = ['uid' => 0];
176

Torsten Oppermann's avatar
Torsten Oppermann committed
177
		foreach ($rootLine as $page) {
178
			if ((int) $page['is_siteroot'] === 1) {
Torsten Oppermann's avatar
Torsten Oppermann committed
179
180
181
182
183
				$siteRoot = $page;
				break;
			}
		}

184
		return (int) $siteRoot['uid'];
Torsten Oppermann's avatar
Torsten Oppermann committed
185
	}
186

187
188
189
190
191
192
193
194
195
196
197
198
	/**
	 * Get the selected templates for the selected language
	 *
	 * @param string $selectedExtension
	 * @param string $selectedTemplate
	 * @param array $languages
	 * @param int $pid
	 * @return array
	 * @throws \InvalidArgumentException
	 */
	public static function getSelectedTemplates(
		$selectedExtension, $selectedTemplate, array $languages, $pid
199
	): array {
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
		$selectedTemplates = [];

		$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
		/** @var TemplateRepository $templateRepository */
		$templateRepository = $objectManager->get(TemplateRepository::class);

		foreach ($languages as $language) {
			$selectedTemplates[$language['isocode']] = $templateRepository->findOneByTemplate(
				$selectedExtension, $selectedTemplate, $language['isocode'], $pid
			);
		}

		return $selectedTemplates;
	}

215
216
217
218
219
220
	/**
	 * get an array of all the locales for the activated languages
	 *
	 * @return array
	 * @throws \InvalidArgumentException
	 */
221
	public static function getLanguages(): array {
222
223
		$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_language');
		$rows = $queryBuilder->select('*')
224
			->from('sys_language')->execute()->fetchAll();
225

226
227
		$languages = [];

228
		// adding default language
229
		$languages[] = ['uid' => 0, 'isocode' => MailTemplateService::DEFAULT_LANGUAGE, 'name' => LocalizationUtility::translate(
230
			'backend.language_default', 'SgMail'
231
		)];
232

233
		foreach ($rows as $language) {
234
235
236
237
238
			$languages[] = [
				'uid' => $language['uid'],
				'isocode' => $language['language_isocode'],
				'name' => $language['title']
			];
239
240
241
242
		}

		return $languages;
	}
243

244
245
246
	/**
	 * get an array of all the labels for the activated languages
	 *
247
	 * @param array $languages
248
249
250
	 * @return array
	 * @throws \InvalidArgumentException
	 */
251
	public static function getLanguageLabels(array $languages): array {
252
		$languageLabels = [];
253

254
255
		foreach ($languages as $language) {
			$languageLabels[$language['isocode']] = $language['name'];
256
257
		}

258
		return $languageLabels;
259
260
	}

261
262
263
264
265
266
	/**
	 * Returns the language array by a given ISO Code or NULL if it doesn't find
	 *
	 * @param $isoCode
	 * @return array|null
	 */
267
	public static function getLanguageByISOCode(string $isoCode) {
268
269
270
271
272
273
274
275
276
		$languages = static::getLanguages();
		foreach ($languages as $language) {
			if ($language['isocode'] === $isoCode) {
				return $language;
			}
		}
		return NULL;
	}

277
278
279
280
281
282
	/**
	 * Get the languages in an array suitable for filtering
	 *
	 * @return array
	 * @throws \InvalidArgumentException
	 */
283
	public static function getLanguagesForFilter(): array {
284
		$languages = self::getLanguages();
285
		array_unshift($languages, ['isocode' => '', 'name' => '']);
286

287
		$filterLanguages = [];
288
		if (\count($languages) > 0) {
289
290
			foreach ($languages as $language) {
				$filterLanguages[$language['isocode']] = $language['name'];
291
			}
292
293
294
295
296
297
		}

		return $filterLanguages;
	}

	/**
298
	 * Get the template keys in an array suitable for filtering
299
	 *
300
	 * @param int $pageUid
301
	 * @return array
302
303
304
	 * @throws \InvalidArgumentException
	 * @throws \BadFunctionCallException
	 * @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException
305
	 */
306
307
	public static function getTemplatesForFilter($pageUid): array {
		$pageUid = (int) $pageUid;
308
		$registerArray = self::getNonBlacklistedTemplates($pageUid);
309
		$templates = [];
310
		$registerService = GeneralUtility::makeInstance(RegisterService::class);
311
312
		foreach ($registerArray as $extensions) {
			foreach ($extensions as $template => $key) {
313
314
315
316
				$templates[$key['extension']][] = [
					'name' => $key['templateName'],
					'is_manual' => $registerService->isManuallyRegisteredTemplate($key['templateName']),
				];
317
318
319
320
321
322
			}
		}
		array_unshift($templates, '');

		return $templates;
	}
323
324
325
326
327
328
329
330
331
332
333
334
335
336

	/**
	 * Save or update the template in the DB, depending if it already exists or not
	 *
	 * @param int $pid
	 * @param string $selectedExtension
	 * @param string $selectedTemplate
	 * @param string $language
	 * @param array $templateData
	 * @return Template $template
	 * @throws \InvalidArgumentException
	 * @throws \TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException
	 * @throws \TYPO3\CMS\Extbase\Persistence\Exception\UnknownObjectException
	 */
337
338
	public static function saveTemplate($pid, $selectedExtension, $selectedTemplate, $language, $templateData
	): Template {
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
		$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
		$templateRepository = $objectManager->get(TemplateRepository::class);

		/** @var Template $template */
		$template = $templateRepository->findOneByTemplate(
			$selectedExtension, $selectedTemplate, $language, $pid
		);

		$templateAlreadyExists = TRUE;
		$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
		if ($template === NULL) {
			$templateAlreadyExists = FALSE;
			$template = $objectManager->get(Template::class);
		}

		$template->setExtensionKey($selectedExtension);
		$template->setTemplateName($selectedTemplate);
		$template->setLanguage($language);
Paul Ilea's avatar
Paul Ilea committed
357
		$template->setLayout($templateData['layout']);
358
359
360
361
362
363
364
		$template->setContent($templateData['content']);
		$template->setSubject($templateData['subject']);
		$template->setFromName($templateData['fromName']);
		$template->setFromMail($templateData['fromMail']);
		$template->setCc($templateData['cc']);
		$template->setBcc($templateData['bcc']);
		$template->setReplyTo($templateData['replyTo']);
365
		$template->setToAddress($templateData['toAddress']);
366
367
368
369
370
371
372

		if ($templateAlreadyExists) {
			$templateRepository->update($template);
		} else {
			$templateRepository->add($template);
		}

373
374
375
376
		$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
		$persistenceManager = $objectManager->get(PersistenceManager::class);
		$persistenceManager->persistAll();

377
378
		return $template;
	}
379

Torsten Oppermann's avatar
Torsten Oppermann committed
380
381
382
383
384
385
386
387
	/**
	 * Generate a csv string from the queues, respecting the given filters
	 *
	 * @param array $filters
	 * @return string
	 * @throws \TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException
	 * @throws \InvalidArgumentException
	 */
388
	public static function getCsvFromQueue(array $filters = []): string {
Torsten Oppermann's avatar
Torsten Oppermann committed
389
390
391
392
393
394
395
		$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
		/** @var MailRepository $mailRepository */
		$mailRepository = $objectManager->get(MailRepository::class);

		$pageUid = (int) GeneralUtility::_GP('id');
		/** @var array $queue */
		$queue = $mailRepository->findAllEntries($pageUid, 0, $filters);
396
		$totalQueueSize = \count($queue);
Torsten Oppermann's avatar
Torsten Oppermann committed
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
		$exportString = '';

		if ($totalQueueSize && $queue) {
			$ignoreFields = ['uid', 'pid', 'tstamp',
				'password', 'starttime', 'endtime', 'deleted', 'sent', 'priority', 'crdate', 'cruser_id', 'hidden'];
			$dateFields = ['tstamp', 'starttime', 'endtime', 'crdate'];

			$export = [[]];
			$first = TRUE;

			/** @var array $mail */
			foreach ($queue as $mail) {
				if ($first) {
					$first = FALSE;
					foreach ($mail as $field => $value) {
412
						if (!\in_array($field, $ignoreFields, TRUE)) {
Torsten Oppermann's avatar
Torsten Oppermann committed
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
							$label = isset($GLOBALS['TCA']['tx_sgmail_domain_model_mail']['columns'][$field]) ?
								$GLOBALS['TCA']['fe_users']['columns'][$field]['label'] : '';
							if (strpos($label, 'LLL:') === 0) {
								$label = $GLOBALS['LANG']->sL($label);
							}
							$export[0][] = $label ?: $field;
						}
					}
				}
			}

			$line = 1;
			/** @var array $mail */
			foreach ($queue as $mail) {
				foreach ($mail as $field => $value) {
428
429
					if (!\in_array($field, $ignoreFields, TRUE)) {
						if (\in_array($field, $dateFields, TRUE)) {
Torsten Oppermann's avatar
Torsten Oppermann committed
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
							$export[$line][] = $value ? date('d.m.Y', $value) : '';
						} else {
							$export[$line][] = (string) $value;
						}
					}
				}
				$line++;
			}

			foreach ($export as $line) {
				/** @var array $line */
				$fields = [];
				foreach ($line as $field) {
					$fields[] = '"' . $field . '"';
				}
				$exportString .= implode(',', $fields) . ';' . LF;
			}
		}

		return trim(preg_replace('/\s\s+/', ' ', strip_tags($exportString)));
	}
451
452
453
454
455
456
457
458

	/**
	 * Filter the register array to have only whitelisted templates for this domain
	 *
	 * @param int $siteRootId
	 * @throws \InvalidArgumentException
	 * @throws \BadFunctionCallException
	 * @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException
459
	 * @return array
460
	 */
461
	public static function getNonBlacklistedTemplates($siteRootId): array {
462
		$siteRootId = (int) $siteRootId;
463
464
465

		$registerService = GeneralUtility::makeInstance(RegisterService::class);
		$registerArray = $registerService->getRegisterArray();
466
		$extensionConfiguration = ExtensionUtility::getExtensionConfiguration();
467
468
469
470
471
472
473
474
475
		if (isset($extensionConfiguration['excludeTemplates']) && $extensionConfiguration['excludeTemplates'] !== '') {
			$excludedTemplatesWithSiteId = GeneralUtility::trimExplode(
				';', $extensionConfiguration['excludeTemplates'], TRUE
			);

			foreach ($excludedTemplatesWithSiteId as $currentSite) {
				$currentSiteBlacklist = GeneralUtility::trimExplode(',', $currentSite, TRUE);
				if ((int) $currentSiteBlacklist[0] === $siteRootId) {
					foreach ($currentSiteBlacklist as $excludedTemplate) {
476
						list($extensionKey, $templateName) = GeneralUtility::trimExplode('.', $excludedTemplate);
477
478
479
480
481
482
483
484
						if ($extensionKey && $templateName && isset($registerArray[$extensionKey][$templateName])) {
							unset($registerArray[$extensionKey][$templateName]);
						}
					}
				}
			}
		}

485
		// filter out excluded templates from all domains
486
487
488
		if (isset($extensionConfiguration['excludeTemplatesAllDomains']) &&
			$extensionConfiguration['excludeTemplatesAllDomains'] !== ''
		) {
489
			$excludedTemplates = GeneralUtility::trimExplode(
490
				',', $extensionConfiguration['excludeTemplatesAllDomains'], TRUE
491
492
			);
			foreach ($excludedTemplates as $excludedTemplate) {
493
				list($extensionKey, $templateName) = GeneralUtility::trimExplode('.', $excludedTemplate);
494
495
496
497
498
499
				if ($extensionKey && $templateName && isset($registerArray[$extensionKey][$templateName])) {
					unset($registerArray[$extensionKey][$templateName]);
				}
			}
		}

500
501
		return $registerArray;
	}
502
503
504

	/**
	 * Fetches an array of FE_Users by groups
505
	 *
506
	 * @param array $groupIds
507
	 * @return \Doctrine\DBAL\Driver\Statement|int|\TYPO3\CMS\Core\Database\Query\QueryBuilder
508
509
510
511
512
	 */
	public static function getRecipientsByGroups(array $groupIds) {
		if (count($groupIds) < 1) {
			return [];
		}
513
514
		$frontendUserGroupRepository =  GeneralUtility::makeInstance(FrontendUserGroupRepository::class);
		$groupIds = $frontendUserGroupRepository->getFullGroupIdsWithChildren($groupIds);
515
516
517
518
519
520
521
		$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
		$queryBuilder = $connectionPool->getQueryBuilderForTable('fe_users');
		$queryBuilder->select('*')
			->from('fe_users');
		foreach ($groupIds as $groupId) {
			$queryBuilder->orWhere($queryBuilder->expr()->inSet('usergroup', (int) $groupId));
		}
522
		$queryBuilder->groupBy('email');
523
		return $queryBuilder->execute();
524
	}
525
}