PageRenderTime 48ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Backend/Core/Engine/Model.php

http://github.com/forkcms/forkcms
PHP | 913 lines | 503 code | 111 blank | 299 comment | 64 complexity | 0860b28c1404a34392f7e6a89da8d0ec MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, MIT, AGPL-3.0, LGPL-2.1, BSD-3-Clause
  1. <?php
  2. namespace Backend\Core\Engine;
  3. use Common\ModuleExtraType;
  4. use InvalidArgumentException;
  5. use Symfony\Component\Filesystem\Filesystem;
  6. use Symfony\Component\Finder\Finder;
  7. use Backend\Modules\Extensions\Engine\Model as BackendExtensionsModel;
  8. use Backend\Modules\Pages\Engine\Model as BackendPagesModel;
  9. use Backend\Core\Engine\Model as BackendModel;
  10. use Frontend\Core\Language\Language as FrontendLanguage;
  11. use Backend\Core\Language\Language as BackendLanguage;
  12. /**
  13. * In this file we store all generic functions that we will be using in the backend.
  14. */
  15. class Model extends \Common\Core\Model
  16. {
  17. /**
  18. * Checks the settings and optionally returns an array with warnings
  19. *
  20. * @return array
  21. */
  22. public static function checkSettings(): array
  23. {
  24. $warnings = [];
  25. // check if debug-mode is active
  26. if (BackendModel::getContainer()->getParameter('kernel.debug')) {
  27. $warnings[] = ['message' => BackendLanguage::err('DebugModeIsActive')];
  28. }
  29. // check for extensions warnings
  30. $warnings = array_merge($warnings, BackendExtensionsModel::checkSettings());
  31. return $warnings;
  32. }
  33. /**
  34. * Creates an URL for a given action and module
  35. * If you don't specify an action the current action will be used.
  36. * If you don't specify a module the current module will be used.
  37. * If you don't specify a language the current language will be used.
  38. *
  39. * @param string $action The action to build the URL for.
  40. * @param string $module The module to build the URL for.
  41. * @param string $language The language to use, if not provided we will use the working language.
  42. * @param array $parameters GET-parameters to use.
  43. * @param bool $encodeSquareBrackets Should the square brackets be allowed so we can use them in de datagrid?
  44. *
  45. * @throws \Exception If $action, $module or both are not set
  46. *
  47. * @return string
  48. */
  49. public static function createUrlForAction(
  50. string $action = null,
  51. string $module = null,
  52. string $language = null,
  53. array $parameters = null,
  54. bool $encodeSquareBrackets = true
  55. ): string {
  56. $language = $language ?? BackendLanguage::getWorkingLanguage();
  57. // checking if we have an url, because in a cronjob we don't have one
  58. if (self::getContainer()->has('url')) {
  59. // grab the URL from the reference
  60. $url = self::getContainer()->get('url');
  61. $action = $action ?? $url->getAction();
  62. $module = $module ?? $url->getModule();
  63. }
  64. // error checking
  65. if ($action === null || $module === null) {
  66. throw new \Exception('Action and Module must not be empty when creating an url.');
  67. }
  68. $parameters['token'] = self::getToken();
  69. if (self::requestIsAvailable()) {
  70. $queryParameterBag = self::getRequest()->query;
  71. // add offset, order & sort (only if not yet manually added)
  72. if (!isset($parameters['offset']) && $queryParameterBag->has('offset')) {
  73. $parameters['offset'] = $queryParameterBag->getInt('offset');
  74. }
  75. if (!isset($parameters['order']) && $queryParameterBag->has('order')) {
  76. $parameters['order'] = $queryParameterBag->get('order');
  77. }
  78. if (!isset($parameters['sort']) && $queryParameterBag->has('sort')) {
  79. $parameters['sort'] = $queryParameterBag->get('sort');
  80. }
  81. }
  82. $queryString = '?' . http_build_query($parameters);
  83. if (!$encodeSquareBrackets) {
  84. // we use things like [id] to parse database column data in so we need to unescape those
  85. $queryString = str_replace([urlencode('['), urlencode(']')], ['[', ']'], $queryString);
  86. }
  87. return self::get('router')->generate(
  88. 'backend',
  89. [
  90. '_locale' => $language,
  91. 'module' => self::camelCaseToLowerSnakeCase($module),
  92. 'action' => self::camelCaseToLowerSnakeCase($action),
  93. ]
  94. ) . $queryString;
  95. }
  96. /**
  97. * @param string $string
  98. *
  99. * @return string
  100. */
  101. public static function camelCaseToLowerSnakeCase(string $string): string
  102. {
  103. return mb_strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $string));
  104. }
  105. /**
  106. * Delete a page extra by module, type or data.
  107. *
  108. * Data is a key/value array. Example: array(id => 23, language => nl);
  109. *
  110. * @param string $module The module wherefore the extra exists.
  111. * @param string $type The type of extra, possible values are block, homepage, widget.
  112. * @param array $data Extra data that exists.
  113. */
  114. public static function deleteExtra(string $module = null, string $type = null, array $data = null): void
  115. {
  116. // init
  117. $query = 'SELECT i.id, i.data FROM modules_extras AS i WHERE 1';
  118. $parameters = [];
  119. // module
  120. if ($module !== null) {
  121. $query .= ' AND i.module = ?';
  122. $parameters[] = $module;
  123. }
  124. // type
  125. if ($type !== null) {
  126. $query .= ' AND i.type = ?';
  127. $parameters[] = $type;
  128. }
  129. // get extras
  130. $extras = (array) self::getContainer()->get('database')->getRecords($query, $parameters);
  131. // loop found extras
  132. foreach ($extras as $extra) {
  133. // get extra data
  134. $extraData = $extra['data'] !== null ? (array) unserialize($extra['data']) : null;
  135. // if we have $data parameter set and $extraData not null we should not delete such extra
  136. if ($data !== null && $extraData === null) {
  137. continue;
  138. }
  139. if ($data !== null && $extraData !== null) {
  140. foreach ($data as $dataKey => $dataValue) {
  141. if (isset($extraData[$dataKey]) && $dataValue !== $extraData[$dataKey]) {
  142. continue 2;
  143. }
  144. }
  145. }
  146. self::deleteExtraById($extra['id']);
  147. }
  148. }
  149. /**
  150. * Delete a page extra by its id
  151. *
  152. * @param int $id The id of the extra to delete.
  153. * @param bool $deleteBlock Should the block be deleted? Default is false.
  154. */
  155. public static function deleteExtraById(int $id, bool $deleteBlock = false): void
  156. {
  157. self::getContainer()->get('database')->delete('modules_extras', 'id = ?', $id);
  158. if ($deleteBlock) {
  159. self::getContainer()->get('database')->delete('pages_blocks', 'extra_id = ?', $id);
  160. return;
  161. }
  162. self::getContainer()->get('database')->update(
  163. 'pages_blocks',
  164. ['extra_id' => null],
  165. 'extra_id = ?',
  166. $id
  167. );
  168. }
  169. /**
  170. * Delete all extras for a certain value in the data array of that module_extra.
  171. *
  172. * @param string $module The module for the extra.
  173. * @param string $field The field of the data you want to check the value for.
  174. * @param string $value The value to check the field for.
  175. * @param string $action In case you want to search for a certain action.
  176. */
  177. public static function deleteExtrasForData(
  178. string $module,
  179. string $field,
  180. string $value,
  181. string $action = null
  182. ): void {
  183. $ids = self::getExtrasForData($module, $field, $value, $action);
  184. // we have extras
  185. if (!empty($ids)) {
  186. // delete extras
  187. self::getContainer()->get('database')->delete('modules_extras', 'id IN (' . implode(',', $ids) . ')');
  188. }
  189. }
  190. /**
  191. * Generate a random string
  192. *
  193. * @param int $length Length of random string.
  194. * @param bool $numeric Use numeric characters.
  195. * @param bool $lowercase Use alphanumeric lowercase characters.
  196. * @param bool $uppercase Use alphanumeric uppercase characters.
  197. * @param bool $special Use special characters.
  198. *
  199. * @return string
  200. */
  201. public static function generateRandomString(
  202. int $length = 15,
  203. bool $numeric = true,
  204. bool $lowercase = true,
  205. bool $uppercase = true,
  206. bool $special = true
  207. ): string {
  208. $characters = '';
  209. $string = '';
  210. // possible characters
  211. if ($numeric) {
  212. $characters .= '1234567890';
  213. }
  214. if ($lowercase) {
  215. $characters .= 'abcdefghijklmnopqrstuvwxyz';
  216. }
  217. if ($uppercase) {
  218. $characters .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  219. }
  220. if ($special) {
  221. $characters .= '-_.:;,?!@#&=)([]{}*+%$';
  222. }
  223. // get random characters
  224. for ($i = 0; $i < $length; ++$i) {
  225. // random index
  226. $index = random_int(0, mb_strlen($characters));
  227. // add character to salt
  228. $string .= mb_substr($characters, $index, 1, self::getContainer()->getParameter('kernel.charset'));
  229. }
  230. return $string;
  231. }
  232. /**
  233. * Fetch the list of long date formats including examples of these formats.
  234. *
  235. * @return array
  236. */
  237. public static function getDateFormatsLong(): array
  238. {
  239. $possibleFormats = [];
  240. // loop available formats
  241. foreach ((array) self::get('fork.settings')->get('Core', 'date_formats_long') as $format) {
  242. // get date based on given format
  243. $possibleFormats[$format] = \SpoonDate::getDate(
  244. $format,
  245. null,
  246. Authentication::getUser()->getSetting('interface_language')
  247. );
  248. }
  249. return $possibleFormats;
  250. }
  251. /**
  252. * Fetch the list of short date formats including examples of these formats.
  253. *
  254. * @return array
  255. */
  256. public static function getDateFormatsShort(): array
  257. {
  258. $possibleFormats = [];
  259. // loop available formats
  260. foreach ((array) self::get('fork.settings')->get('Core', 'date_formats_short') as $format) {
  261. // get date based on given format
  262. $possibleFormats[$format] = \SpoonDate::getDate(
  263. $format,
  264. null,
  265. Authentication::getUser()->getSetting('interface_language')
  266. );
  267. }
  268. return $possibleFormats;
  269. }
  270. public static function getExtras(array $ids): array
  271. {
  272. // get database
  273. $database = self::getContainer()->get('database');
  274. array_walk($ids, 'intval');
  275. // create an array with an equal amount of question marks as ids provided
  276. $extraIdPlaceHolders = array_fill(0, count($ids), '?');
  277. // get extras
  278. return (array) $database->getRecords(
  279. 'SELECT i.*
  280. FROM modules_extras AS i
  281. WHERE i.id IN (' . implode(', ', $extraIdPlaceHolders) . ')',
  282. $ids
  283. );
  284. }
  285. /**
  286. * Get extras for data
  287. *
  288. * @param string $module The module for the extra.
  289. * @param string $key The key of the data you want to check the value for.
  290. * @param string $value The value to check the key for.
  291. * @param string $action In case you want to search for a certain action.
  292. *
  293. * @return array The ids for the extras.
  294. */
  295. public static function getExtrasForData(string $module, string $key, string $value, string $action = null): array
  296. {
  297. $query = 'SELECT i.id, i.data
  298. FROM modules_extras AS i
  299. WHERE i.module = ? AND i.data != ?';
  300. $parameters = [$module, 'NULL'];
  301. // Filter on the action if it is given.
  302. if ($action !== null) {
  303. $query .= ' AND i.action = ?';
  304. $parameters[] = $action;
  305. }
  306. $moduleExtras = (array) self::getContainer()->get('database')->getPairs($query, $parameters);
  307. // No module extra's found
  308. if (empty($moduleExtras)) {
  309. return [];
  310. }
  311. return array_keys(
  312. array_filter(
  313. $moduleExtras,
  314. function (?string $serializedData) use ($key, $value) {
  315. $data = $serializedData === null ? [] : unserialize($serializedData);
  316. return isset($data[$key]) && (string) $data[$key] === $value;
  317. }
  318. )
  319. );
  320. }
  321. /**
  322. * Get the page-keys
  323. *
  324. * @param string $language The language to use, if not provided we will use the working language.
  325. *
  326. * @return array
  327. */
  328. public static function getKeys(string $language = null): array
  329. {
  330. if ($language === null) {
  331. $language = BackendLanguage::getWorkingLanguage();
  332. }
  333. return BackendPagesModel::getCacheBuilder()->getKeys($language);
  334. }
  335. /**
  336. * Get the modules that are available on the filesystem
  337. *
  338. * @param bool $includeCore Should core be included as a module?
  339. *
  340. * @return array
  341. */
  342. public static function getModulesOnFilesystem(bool $includeCore = true): array
  343. {
  344. $modules = $includeCore ? ['Core'] : [];
  345. $finder = new Finder();
  346. $directories = $finder->directories()->in(__DIR__ . '/../../Modules')->depth('==0');
  347. foreach ($directories as $directory) {
  348. $modules[] = $directory->getBasename();
  349. }
  350. return $modules;
  351. }
  352. /**
  353. * Fetch the list of modules, but for a dropdown.
  354. *
  355. * @return array
  356. */
  357. public static function getModulesForDropDown(): array
  358. {
  359. $dropDown = ['Core' => 'Core'];
  360. // fetch modules
  361. $modules = self::getModules();
  362. // loop and add into the return-array (with correct label)
  363. foreach ($modules as $module) {
  364. $dropDown[$module] = \SpoonFilter::ucfirst(BackendLanguage::lbl(\SpoonFilter::toCamelCase($module)));
  365. }
  366. return $dropDown;
  367. }
  368. /**
  369. * Get the navigation-items
  370. *
  371. * @param string $language The language to use, if not provided we will use the working language.
  372. *
  373. * @return array
  374. */
  375. public static function getNavigation(string $language = null): array
  376. {
  377. if ($language === null) {
  378. $language = BackendLanguage::getWorkingLanguage();
  379. }
  380. $cacheBuilder = BackendPagesModel::getCacheBuilder();
  381. return $cacheBuilder->getNavigation($language);
  382. }
  383. /**
  384. * Fetch the list of number formats including examples of these formats.
  385. *
  386. * @return array
  387. */
  388. public static function getNumberFormats(): array
  389. {
  390. return (array) self::get('fork.settings')->get('Core', 'number_formats');
  391. }
  392. /**
  393. * Fetch the list of time formats including examples of these formats.
  394. *
  395. * @return array
  396. */
  397. public static function getTimeFormats(): array
  398. {
  399. $possibleFormats = [];
  400. $interfaceLanguage = Authentication::getUser()->getSetting('interface_language');
  401. foreach (self::get('fork.settings')->get('Core', 'time_formats') as $format) {
  402. $possibleFormats[$format] = \SpoonDate::getDate($format, null, $interfaceLanguage);
  403. }
  404. return $possibleFormats;
  405. }
  406. /**
  407. * Get the token which will protect us
  408. *
  409. * @return string
  410. */
  411. public static function getToken(): string
  412. {
  413. if (self::getSession()->has('csrf_token') && self::getSession()->get('csrf_token') !== '') {
  414. return self::getSession()->get('csrf_token');
  415. }
  416. $token = self::generateRandomString(10, true, true, false, false);
  417. self::getSession()->set('csrf_token', $token);
  418. return $token;
  419. }
  420. /**
  421. * Get URL for a given pageId
  422. *
  423. * @param int $pageId The id of the page to get the URL for.
  424. * @param string $language The language to use, if not provided we will use the working language.
  425. *
  426. * @return string
  427. */
  428. public static function getUrl(int $pageId, string $language = null): string
  429. {
  430. if ($language === null) {
  431. $language = BackendLanguage::getWorkingLanguage();
  432. }
  433. // Prepend the language if the site is multi language
  434. $url = self::getContainer()->getParameter('site.multilanguage') ? '/' . $language . '/' : '/';
  435. // get the menuItems
  436. $keys = self::getKeys($language);
  437. // get the URL, if it doesn't exist return 404
  438. if (!isset($keys[$pageId])) {
  439. return self::getUrl(BackendModel::ERROR_PAGE_ID, $language);
  440. }
  441. // return the unique URL!
  442. return urldecode($url . $keys[$pageId]);
  443. }
  444. /**
  445. * Get the URL for a give module & action combination
  446. *
  447. * @param string $module The module wherefore the URL should be build.
  448. * @param string $action The specific action wherefore the URL should be build.
  449. * @param string $language The language wherein the URL should be retrieved,
  450. * if not provided we will load the language that was provided in the URL.
  451. * @param array $data An array with keys and values that partially or fully match the data of the block.
  452. * If it matches multiple versions of that block it will just return the first match.
  453. *
  454. * @return string
  455. */
  456. public static function getUrlForBlock(
  457. string $module,
  458. string $action = null,
  459. string $language = null,
  460. array $data = null
  461. ): string {
  462. if ($language === null) {
  463. $language = BackendLanguage::getWorkingLanguage();
  464. }
  465. $pageIdForUrl = null;
  466. $navigation = self::getNavigation($language);
  467. $dataMatch = false;
  468. // loop types
  469. foreach ($navigation as $level) {
  470. // loop level
  471. foreach ($level as $pages) {
  472. // loop pages
  473. foreach ($pages as $pageId => $properties) {
  474. // only process pages with extra_blocks that are visible
  475. if (!isset($properties['extra_blocks']) || $properties['hidden']) {
  476. continue;
  477. }
  478. // loop extras
  479. foreach ($properties['extra_blocks'] as $extra) {
  480. // direct link?
  481. if ($extra['module'] === $module && $extra['action'] === $action && $extra['action'] !== null) {
  482. // if there is data check if all the requested data matches the extra data
  483. if ($data !== null && isset($extra['data'])
  484. && array_intersect_assoc($data, (array) $extra['data']) !== $data
  485. ) {
  486. // It is the correct action but has the wrong data
  487. continue;
  488. }
  489. // exact page was found, so return
  490. return self::getUrl($properties['page_id'], $language);
  491. }
  492. if ($extra['module'] === $module && $extra['action'] === null) {
  493. // if there is data check if all the requested data matches the extra data
  494. if ($data !== null && isset($extra['data'])) {
  495. if (array_intersect_assoc($data, (array) $extra['data']) !== $data) {
  496. // It is the correct module but has the wrong data
  497. continue;
  498. }
  499. $pageIdForUrl = (int) $pageId;
  500. $dataMatch = true;
  501. }
  502. if ($data === null && $extra['data'] === null) {
  503. $pageIdForUrl = (int) $pageId;
  504. $dataMatch = true;
  505. }
  506. if (!$dataMatch) {
  507. $pageIdForUrl = (int) $pageId;
  508. }
  509. }
  510. }
  511. }
  512. }
  513. }
  514. // Page not found so return the 404 url
  515. if ($pageIdForUrl === null) {
  516. return self::getUrl(self::ERROR_PAGE_ID, $language);
  517. }
  518. $url = self::getUrl($pageIdForUrl, $language);
  519. // set locale with force
  520. FrontendLanguage::setLocale($language, true);
  521. // append action
  522. if ($action !== null) {
  523. $url .= '/' . urldecode(FrontendLanguage::act(\SpoonFilter::toCamelCase($action)));
  524. }
  525. // return the unique URL!
  526. return $url;
  527. }
  528. /**
  529. * Image Delete
  530. *
  531. * @param string $module Module name.
  532. * @param string $filename Filename.
  533. * @param string $subDirectory Subdirectory.
  534. * @param array $fileSizes Possible file sizes.
  535. */
  536. public static function imageDelete(
  537. string $module,
  538. string $filename,
  539. string $subDirectory = '',
  540. array $fileSizes = null
  541. ): void {
  542. if (empty($fileSizes)) {
  543. $model = get_class_vars('Backend' . \SpoonFilter::toCamelCase($module) . 'Model');
  544. $fileSizes = $model['fileSizes'];
  545. }
  546. // also include the source directory
  547. $fileSizes[] = 'source';
  548. $baseDirectory = FRONTEND_FILES_PATH . '/' . $module . (empty($subDirectory) ? '/' : '/' . $subDirectory . '/');
  549. $filesystem = new Filesystem();
  550. array_walk(
  551. $fileSizes,
  552. function (string $sizeDirectory) use ($baseDirectory, $filename, $filesystem) {
  553. $fullPath = $baseDirectory . basename($sizeDirectory) . '/' . $filename;
  554. if (is_file($fullPath)) {
  555. $filesystem->remove($fullPath);
  556. }
  557. }
  558. );
  559. }
  560. /**
  561. * Insert extra
  562. *
  563. * @param ModuleExtraType $type What type do you want to insert, 'homepage', 'block' or 'widget'.
  564. * @param string $module The module you are inserting this extra for.
  565. * @param string $action The action this extra will use.
  566. * @param string $label Label which will be used when you want to connect this block.
  567. * @param array $data Containing extra variables.
  568. * @param bool $hidden Should this extra be visible in frontend or not?
  569. * @param int $sequence
  570. *
  571. * @throws Exception If extra type is not allowed
  572. *
  573. * @return int The new extra id
  574. */
  575. public static function insertExtra(
  576. ModuleExtraType $type,
  577. string $module,
  578. string $action = null,
  579. string $label = null,
  580. array $data = null,
  581. bool $hidden = false,
  582. int $sequence = null
  583. ): int {
  584. // return id for inserted extra
  585. return self::get('database')->insert(
  586. 'modules_extras',
  587. [
  588. 'module' => $module,
  589. 'type' => $type,
  590. 'label' => $label ?? $module, // if label is empty, fallback to module
  591. 'action' => $action ?? null,
  592. 'data' => $data === null ? null : serialize($data),
  593. 'hidden' => $hidden,
  594. 'sequence' => $sequence ?? self::getNextModuleExtraSequenceForModule($module),
  595. ]
  596. );
  597. }
  598. /**
  599. * This returns the identifier for the editor the logged in user prefers to use in forms.
  600. *
  601. * @return string
  602. */
  603. public static function getPreferredEditor(): string
  604. {
  605. $defaultPreferredEditor = self::getContainer()->getParameter('fork.form.default_preferred_editor');
  606. if (!Authentication::isLoggedIn()) {
  607. return $defaultPreferredEditor;
  608. }
  609. return Authentication::getUser()->getSetting('preferred_editor', $defaultPreferredEditor);
  610. }
  611. /**
  612. * @param string $module
  613. *
  614. * @return int
  615. */
  616. private static function getNextModuleExtraSequenceForModule(string $module): int
  617. {
  618. $database = self::get('database');
  619. // set next sequence number for this module
  620. $sequence = (int) $database->getVar(
  621. 'SELECT MAX(sequence) + 1 FROM modules_extras WHERE module = ?',
  622. [$module]
  623. );
  624. // this is the first extra for this module: generate new 1000-series
  625. if ($sequence > 0) {
  626. return $sequence;
  627. }
  628. return (int) $database->getVar(
  629. 'SELECT CEILING(MAX(sequence) / 1000) * 1000 FROM modules_extras'
  630. );
  631. }
  632. /**
  633. * Is module installed?
  634. *
  635. * @param string $module
  636. *
  637. * @return bool
  638. */
  639. public static function isModuleInstalled(string $module): bool
  640. {
  641. return in_array($module, self::getModules(), true);
  642. }
  643. /**
  644. * Submit ham, this call is intended for the marking of false positives, things that were incorrectly marked as
  645. * spam.
  646. *
  647. * @param string $userIp IP address of the comment submitter.
  648. * @param string $userAgent User agent information.
  649. * @param string $content The content that was submitted.
  650. * @param string $author Submitted name with the comment.
  651. * @param string $email Submitted email address.
  652. * @param string $url Commenter URL.
  653. * @param string $permalink The permanent location of the entry the comment was submitted to.
  654. * @param string $type May be blank, comment, trackback, pingback, or a made up value like "registration".
  655. * @param string $referrer The content of the HTTP_REFERER header should be sent here.
  656. * @param array $others Other data (the variables from $_SERVER).
  657. *
  658. * @throws Exception
  659. *
  660. * @return bool If everything went fine, true will be returned, otherwise an exception will be triggered.
  661. */
  662. public static function submitHam(
  663. string $userIp,
  664. string $userAgent,
  665. string $content,
  666. string $author = null,
  667. string $email = null,
  668. string $url = null,
  669. string $permalink = null,
  670. string $type = null,
  671. string $referrer = null,
  672. array $others = null
  673. ): bool {
  674. try {
  675. $akismet = self::getAkismet();
  676. } catch (InvalidArgumentException $invalidArgumentException) {
  677. return false;
  678. }
  679. // try it to decide it the item is spam
  680. try {
  681. // check with Akismet if the item is spam
  682. return $akismet->submitHam(
  683. $userIp,
  684. $userAgent,
  685. $content,
  686. $author,
  687. $email,
  688. $url,
  689. $permalink,
  690. $type,
  691. $referrer,
  692. $others
  693. );
  694. } catch (Exception $e) {
  695. if (BackendModel::getContainer()->getParameter('kernel.debug')) {
  696. throw $e;
  697. }
  698. }
  699. return false;
  700. }
  701. /**
  702. * Submit spam, his call is for submitting comments that weren't marked as spam but should have been.
  703. *
  704. * @param string $userIp IP address of the comment submitter.
  705. * @param string $userAgent User agent information.
  706. * @param string $content The content that was submitted.
  707. * @param string $author Submitted name with the comment.
  708. * @param string $email Submitted email address.
  709. * @param string $url Commenter URL.
  710. * @param string $permalink The permanent location of the entry the comment was submitted to.
  711. * @param string $type May be blank, comment, trackback, pingback, or a made up value like "registration".
  712. * @param string $referrer The content of the HTTP_REFERER header should be sent here.
  713. * @param array $others Other data (the variables from $_SERVER).
  714. *
  715. * @throws Exception
  716. *
  717. * @return bool If everything went fine true will be returned, otherwise an exception will be triggered.
  718. */
  719. public static function submitSpam(
  720. string $userIp,
  721. string $userAgent,
  722. string $content,
  723. string $author = null,
  724. string $email = null,
  725. string $url = null,
  726. string $permalink = null,
  727. string $type = null,
  728. string $referrer = null,
  729. array $others = null
  730. ): bool {
  731. try {
  732. $akismet = self::getAkismet();
  733. } catch (InvalidArgumentException $invalidArgumentException) {
  734. return false;
  735. }
  736. // try it to decide it the item is spam
  737. try {
  738. // check with Akismet if the item is spam
  739. return $akismet->submitSpam(
  740. $userIp,
  741. $userAgent,
  742. $content,
  743. $author,
  744. $email,
  745. $url,
  746. $permalink,
  747. $type,
  748. $referrer,
  749. $others
  750. );
  751. } catch (Exception $e) {
  752. if (BackendModel::getContainer()->getParameter('kernel.debug')) {
  753. throw $e;
  754. }
  755. }
  756. return false;
  757. }
  758. /**
  759. * Update extra
  760. *
  761. * @param int $id The id for the extra.
  762. * @param string $key The key you want to update.
  763. * @param mixed $value The new value.
  764. *
  765. * @throws Exception If key parameter is not allowed
  766. */
  767. public static function updateExtra(int $id, string $key, $value): void
  768. {
  769. // define allowed keys
  770. $allowedKeys = ['label', 'action', 'data', 'hidden', 'sequence'];
  771. // key is not allowed
  772. if (!in_array($key, $allowedKeys, true)) {
  773. throw new Exception('The key ' . $key . ' can\'t be updated.');
  774. }
  775. // key is 'data' and value is not serialized
  776. if ($key === 'data' && is_array($value)) {
  777. // serialize value
  778. $value = $value === null ? null : serialize($value);
  779. }
  780. self::getContainer()->get('database')->update('modules_extras', [$key => $value], 'id = ?', [$id]);
  781. }
  782. /**
  783. * Update extra data
  784. *
  785. * @param int $id The id for the extra.
  786. * @param string $key The key in the data you want to update.
  787. * @param string|array $value The new value.
  788. */
  789. public static function updateExtraData(int $id, string $key, $value): void
  790. {
  791. $database = self::getContainer()->get('database');
  792. $serializedData = (string) $database->getVar(
  793. 'SELECT i.data
  794. FROM modules_extras AS i
  795. WHERE i.id = ?',
  796. [$id]
  797. );
  798. $data = empty($serializedData) ? [] : unserialize($serializedData);
  799. $data[$key] = $value;
  800. $database->update('modules_extras', ['data' => serialize($data)], 'id = ?', [$id]);
  801. }
  802. }