FormHelper.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. <?php
  2. namespace Drupal\simple_sitemap\Form;
  3. use Drupal\Core\Form\FormStateInterface;
  4. use Drupal\Core\StringTranslation\StringTranslationTrait;
  5. use Drupal\simple_sitemap\EntityHelper;
  6. use Drupal\simple_sitemap\Simplesitemap;
  7. use Drupal\Core\Session\AccountProxyInterface;
  8. /**
  9. * Class FormHelper
  10. * @package Drupal\simple_sitemap\Form
  11. */
  12. class FormHelper {
  13. use StringTranslationTrait;
  14. const PRIORITY_DEFAULT = 0.5;
  15. const PRIORITY_HIGHEST = 10;
  16. const PRIORITY_DIVIDER = 10;
  17. /**
  18. * @var \Drupal\simple_sitemap\Simplesitemap
  19. */
  20. protected $generator;
  21. /**
  22. * @var \Drupal\simple_sitemap\EntityHelper
  23. */
  24. protected $entityHelper;
  25. /**
  26. * @var \Drupal\Core\Session\AccountProxyInterface
  27. */
  28. protected $currentUser;
  29. /**
  30. * @var \Drupal\Core\Form\FormState
  31. */
  32. protected $formState;
  33. /**
  34. * @var string|null
  35. */
  36. protected $entityCategory;
  37. /**
  38. * @var string
  39. */
  40. protected $entityTypeId;
  41. /**
  42. * @var string
  43. */
  44. protected $bundleName;
  45. /**
  46. * @var string
  47. */
  48. protected $instanceId;
  49. /**
  50. * @var string
  51. */
  52. protected $variant;
  53. /**
  54. * @var array
  55. */
  56. protected $bundleSettings;
  57. protected static $allowedFormOperations = [
  58. 'default',
  59. 'edit',
  60. 'add',
  61. 'register',
  62. ];
  63. protected static $changefreqValues = [
  64. 'always',
  65. 'hourly',
  66. 'daily',
  67. 'weekly',
  68. 'monthly',
  69. 'yearly',
  70. 'never',
  71. ];
  72. protected static $valuesToCheck = [
  73. 'simple_sitemap_variant',
  74. 'simple_sitemap_index_content',
  75. 'simple_sitemap_priority',
  76. 'simple_sitemap_changefreq',
  77. 'simple_sitemap_include_images',
  78. 'simple_sitemap_regenerate_now',
  79. ];
  80. /**
  81. * FormHelper constructor.
  82. * @param \Drupal\simple_sitemap\Simplesitemap $generator
  83. * @param \Drupal\simple_sitemap\EntityHelper $entityHelper
  84. * @param \Drupal\Core\Session\AccountProxyInterface $current_user
  85. */
  86. public function __construct(
  87. Simplesitemap $generator,
  88. EntityHelper $entityHelper,
  89. AccountProxyInterface $current_user
  90. ) {
  91. $this->generator = $generator;
  92. $this->entityHelper = $entityHelper;
  93. $this->currentUser = $current_user;
  94. }
  95. /**
  96. * @param \Drupal\Core\Form\FormStateInterface $form_state
  97. * @return bool
  98. */
  99. public function processForm(FormStateInterface $form_state) {
  100. $this->formState = $form_state;
  101. $this->cleanUpFormInfo();
  102. $this->getEntityDataFromFormEntity();
  103. $this->negotiateVariant();
  104. return $this->supports();
  105. }
  106. /**
  107. * @param string $entity_category
  108. * @return $this
  109. */
  110. public function setEntityCategory($entity_category) {
  111. $this->entityCategory = $entity_category;
  112. return $this;
  113. }
  114. /**
  115. * @return null|string
  116. */
  117. public function getEntityCategory() {
  118. return $this->entityCategory;
  119. }
  120. /**
  121. * @param string $entity_type_id
  122. * @return $this
  123. */
  124. public function setEntityTypeId($entity_type_id) {
  125. $this->entityTypeId = $entity_type_id;
  126. return $this;
  127. }
  128. /**
  129. * @return string
  130. */
  131. public function getEntityTypeId() {
  132. return $this->entityTypeId;
  133. }
  134. /**
  135. * @param string $bundle_name
  136. * @return $this
  137. */
  138. public function setBundleName($bundle_name) {
  139. $this->bundleName = $bundle_name;
  140. return $this;
  141. }
  142. /**
  143. * @return string
  144. */
  145. public function getBundleName() {
  146. return $this->bundleName;
  147. }
  148. /**
  149. * @param string $instance_id
  150. * @return $this
  151. */
  152. public function setInstanceId($instance_id) {
  153. $this->instanceId = $instance_id;
  154. return $this;
  155. }
  156. /**
  157. * @return string
  158. */
  159. public function getInstanceId() {
  160. return $this->instanceId;
  161. }
  162. /**
  163. * @return bool
  164. */
  165. protected function supports() {
  166. // Do not alter the form if user lacks certain permissions.
  167. if (!$this->currentUser->hasPermission('administer sitemap settings')) {
  168. return FALSE;
  169. }
  170. // Do not alter the form if it is irrelevant to sitemap generation.
  171. elseif (empty($this->getEntityCategory())) {
  172. return FALSE;
  173. }
  174. // Do not alter the form if entity is not enabled in sitemap settings.
  175. elseif (!$this->generator->entityTypeIsEnabled($this->getEntityTypeId())) {
  176. return FALSE;
  177. }
  178. // Do not alter the form, if sitemap is disabled for the entity type of this
  179. // entity instance.
  180. elseif ($this->getEntityCategory() === 'instance') {
  181. if (NULL === $this->variant || !$this->generator
  182. ->setVariants($this->variant)
  183. ->bundleIsIndexed($this->getEntityTypeId(), $this->getBundleName())) {
  184. return FALSE;
  185. }
  186. }
  187. return TRUE;
  188. }
  189. /**
  190. * @param array $form_fragment
  191. */
  192. public function displayRegenerateNow(&$form_fragment) {
  193. $form_fragment['simple_sitemap_regenerate_now'] = [
  194. '#type' => 'checkbox',
  195. '#title' => $this->t('Regenerate sitemap after hitting <em>Save</em>'),
  196. '#description' => $this->t('This setting will regenerate all sitemaps including the above changes.'),
  197. '#default_value' => FALSE,
  198. ];
  199. if ($this->generator->getSetting('cron_generate')) {
  200. $form_fragment['simple_sitemap_regenerate_now']['#description'] .= '</br>' . $this->t('Otherwise the sitemap will be regenerated during a future cron run.');
  201. }
  202. }
  203. protected function negotiateVariant() {
  204. $all_bundle_settings = $this->generator->setVariants(TRUE)
  205. ->getBundleSettings($this->getEntityTypeId(), $this->getBundleName(), FALSE, TRUE);
  206. $this->bundleSettings = NULL !== ($variant = key($all_bundle_settings))
  207. ? $all_bundle_settings[$variant]
  208. : [];
  209. $this->variant = $variant;
  210. }
  211. /**
  212. * @param array $form_fragment
  213. * @param bool $multiple
  214. * @return $this
  215. */
  216. public function displayEntitySettings(&$form_fragment, $multiple = FALSE) {
  217. $prefix = $multiple ? $this->getEntityTypeId() . '_' : '';
  218. $settings = $this->getEntityCategory() === 'instance' && NULL !== $this->variant && NULL !== $this->getInstanceId()
  219. ? $this->generator->setVariants($this->variant)->getEntityInstanceSettings($this->getEntityTypeId(), $this->getInstanceId())
  220. : $this->bundleSettings;
  221. Simplesitemap::supplementDefaultSettings('entity', $settings);
  222. $bundle_name = !empty($this->getBundleName()) ? $this->getBundleName() : $this->t('undefined');
  223. // Index
  224. if (!$multiple) {
  225. $form_fragment[$prefix . 'simple_sitemap_index_content'] = [
  226. '#type' => 'radios',
  227. '#default_value' => (int) $settings['index'],
  228. '#options' => [
  229. 0 => $this->getEntityCategory() === 'instance'
  230. ? $this->t('Do not index this @bundle entity', ['@bundle' => $bundle_name])
  231. : $this->t('Do not index entities of this type'),
  232. 1 => $this->getEntityCategory() === 'instance'
  233. ? $this->t('Index this @bundle entity', ['@bundle' => $bundle_name])
  234. : $this->t('Index entities of this type'),
  235. ],
  236. ];
  237. if ($this->getEntityCategory() === 'instance' && isset($this->bundleSettings['index'])) {
  238. $form_fragment[$prefix . 'simple_sitemap_index_content']['#options'][(int) $this->bundleSettings['index']] .= ' <em>(' . $this->t('default') . ')</em>';
  239. }
  240. }
  241. // Variant
  242. $form_fragment[$prefix . 'simple_sitemap_variant'] = [
  243. '#type' => 'select',
  244. '#title' => $this->t('Sitemap variant'),
  245. '#description' => $this->t('The sitemap variant entities of this type are to be indexed in.'),
  246. '#options' => array_map(
  247. function($variant) { return $this->t($variant['label']); },
  248. $this->generator->getSitemapManager()->getSitemapVariants(NULL, FALSE)
  249. ),
  250. '#default_value' => $this->variant,
  251. '#states' => [
  252. 'visible' => !$multiple
  253. ? [':input[name="' . $prefix . 'simple_sitemap_index_content"]' => ['value' => 1]]
  254. : [':input[name="' . $prefix . 'enabled"]' => ['checked' => TRUE]],
  255. 'required' => !$multiple // todo Should implement server side validation on top of this.
  256. ? [':input[name="' . $prefix . 'simple_sitemap_index_content"]' => ['value' => 1]]
  257. : [':input[name="' . $prefix . 'enabled"]' => ['checked' => TRUE]],
  258. ],
  259. '#disabled' => $this->getEntityCategory() === 'instance'
  260. ];
  261. // Priority
  262. $form_fragment[$prefix . 'simple_sitemap_priority'] = [
  263. '#type' => 'select',
  264. '#title' => $this->t('Priority'),
  265. '#description' => $this->getEntityCategory() === 'instance'
  266. ? $this->t('The priority this @bundle entity will have in the eyes of search engine bots.', ['@bundle' => $bundle_name])
  267. : $this->t('The priority entities of this type will have in the eyes of search engine bots.'),
  268. '#default_value' => $settings['priority'],
  269. '#options' => $this->getPrioritySelectValues(),
  270. '#states' => [
  271. 'visible' => !$multiple
  272. ? [':input[name="' . $prefix . 'simple_sitemap_index_content"]' => ['value' => 1]]
  273. : [':input[name="' . $prefix . 'enabled"]' => ['checked' => TRUE]],
  274. ],
  275. ];
  276. if ($this->getEntityCategory() === 'instance' && isset($this->bundleSettings['priority'])) {
  277. $form_fragment[$prefix . 'simple_sitemap_priority']['#options'][$this->formatPriority($this->bundleSettings['priority'])] .= ' (' . $this->t('default') . ')';
  278. }
  279. // Changefreq
  280. $form_fragment[$prefix . 'simple_sitemap_changefreq'] = [
  281. '#type' => 'select',
  282. '#title' => $this->t('Change frequency'),
  283. '#description' => $this->getEntityCategory() === 'instance'
  284. ? $this->t('The frequency with which this @bundle entity changes. Search engine bots may take this as an indication of how often to index it.', ['@bundle' => $bundle_name])
  285. : $this->t('The frequency with which entities of this type change. Search engine bots may take this as an indication of how often to index them.'),
  286. '#default_value' => $settings['changefreq'],
  287. '#options' => $this->getChangefreqSelectValues(),
  288. '#states' => [
  289. 'visible' => !$multiple
  290. ? [':input[name="' . $prefix . 'simple_sitemap_index_content"]' => ['value' => 1]]
  291. : [':input[name="' . $prefix . 'enabled"]' => ['checked' => TRUE]],
  292. ],
  293. ];
  294. if ($this->getEntityCategory() === 'instance' && isset($this->bundleSettings['changefreq'])) {
  295. $form_fragment[$prefix . 'simple_sitemap_changefreq']['#options'][$this->bundleSettings['changefreq']] .= ' (' . $this->t('default') . ')';
  296. }
  297. // Images
  298. $form_fragment[$prefix . 'simple_sitemap_include_images'] = [
  299. '#type' => 'select',
  300. '#title' => $this->t('Include images'),
  301. '#description' => $this->getEntityCategory() === 'instance'
  302. ? $this->t('Determines if images referenced by this @bundle entity should be included in the sitemap.', ['@bundle' => $bundle_name])
  303. : $this->t('Determines if images referenced by entities of this type should be included in the sitemap.'),
  304. '#default_value' => (int) $settings['include_images'],
  305. '#options' => [0 => $this->t('No'), 1 => $this->t('Yes')],
  306. '#states' => [
  307. 'visible' => !$multiple
  308. ? [':input[name="' . $prefix . 'simple_sitemap_index_content"]' => ['value' => 1]]
  309. : [':input[name="' . $prefix . 'enabled"]' => ['checked' => TRUE]],
  310. ],
  311. ];
  312. if ($this->getEntityCategory() === 'instance' && isset($this->bundleSettings['include_images'])) {
  313. $form_fragment[$prefix . 'simple_sitemap_include_images']['#options'][(int) $this->bundleSettings['include_images']] .= ' (' . $this->t('default') . ')';
  314. }
  315. return $this;
  316. }
  317. /**
  318. * Checks if this particular form is a bundle form, or a bundle instance form
  319. * and gathers sitemap settings from the database.
  320. *
  321. * @return bool
  322. * TRUE if this is a bundle or bundle instance form, FALSE otherwise.
  323. */
  324. protected function getEntityDataFromFormEntity() {
  325. if (!$form_entity = $this->getFormEntity()) {
  326. return FALSE;
  327. }
  328. $entity_type_id = $form_entity->getEntityTypeId();
  329. $sitemap_entity_types = $this->entityHelper->getSupportedEntityTypes();
  330. if (isset($sitemap_entity_types[$entity_type_id])) {
  331. $this->setEntityCategory('instance');
  332. }
  333. else {
  334. /** @var \Drupal\Core\Entity\EntityType $sitemap_entity_type */
  335. foreach ($sitemap_entity_types as $sitemap_entity_type) {
  336. if ($sitemap_entity_type->getBundleEntityType() === $entity_type_id) {
  337. $this->setEntityCategory('bundle');
  338. break;
  339. }
  340. }
  341. }
  342. // Menu fix.
  343. $this->setEntityCategory(NULL === $this->getEntityCategory() && $entity_type_id === 'menu' ? 'bundle' : $this->getEntityCategory());
  344. switch ($this->getEntityCategory()) {
  345. case 'bundle':
  346. $this->setEntityTypeId($this->entityHelper->getBundleEntityTypeId($form_entity));
  347. $this->setBundleName($form_entity->id());
  348. $this->setInstanceId(NULL);
  349. break;
  350. case 'instance':
  351. $this->setEntityTypeId($entity_type_id);
  352. $this->setBundleName($this->entityHelper->getEntityInstanceBundleName($form_entity));
  353. // New menu link's id is '' instead of NULL, hence checking for empty.
  354. $this->setInstanceId(!empty($form_entity->id()) ? $form_entity->id() : NULL);
  355. break;
  356. default:
  357. return FALSE;
  358. }
  359. return TRUE;
  360. }
  361. /**
  362. * Gets the object entity of the form if available.
  363. *
  364. * @return \Drupal\Core\Entity\Entity|false
  365. * Entity or FALSE if non-existent or if form operation is
  366. * 'delete'.
  367. */
  368. protected function getFormEntity() {
  369. $form_object = $this->formState->getFormObject();
  370. if (NULL !== $form_object
  371. && method_exists($form_object, 'getOperation')
  372. && method_exists($form_object, 'getEntity')
  373. && in_array($form_object->getOperation(), self::$allowedFormOperations)) {
  374. return $form_object->getEntity();
  375. }
  376. return FALSE;
  377. }
  378. /**
  379. * Removes gathered form information from service object.
  380. *
  381. * Needed because this service may contain form info from the previous
  382. * operation when revived from the container.
  383. */
  384. protected function cleanUpFormInfo() {
  385. $this->entityCategory = NULL;
  386. $this->entityTypeId = NULL;
  387. $this->bundleName = NULL;
  388. $this->instanceId = NULL;
  389. $this->variant = NULL;
  390. $this->bundleSettings = NULL;
  391. }
  392. /**
  393. * Gets new entity Id after entity creation.
  394. * To be used in an entity form submit.
  395. *
  396. * @return int
  397. * Entity ID.
  398. */
  399. public function getFormEntityId() {
  400. return $this->formState->getFormObject()->getEntity()->id();
  401. }
  402. /**
  403. * Checks if simple_sitemap values have been changed after submitting the form.
  404. * To be used in an entity form submit.
  405. *
  406. * @param $form
  407. * @param array $values
  408. *
  409. * @return bool
  410. * TRUE if simple_sitemap form values have been altered by the user.
  411. */
  412. public function valuesChanged($form, array $values) {
  413. foreach (self::$valuesToCheck as $field_name) {
  414. if (!isset($form['simple_sitemap'][$field_name]['#default_value'])
  415. || (isset($values[$field_name]) && $values[$field_name] != $form['simple_sitemap'][$field_name]['#default_value'])) {
  416. return TRUE;
  417. }
  418. }
  419. return FALSE;
  420. }
  421. /**
  422. * Gets the values needed to display the priority dropdown setting.
  423. *
  424. * @return array
  425. * Select options.
  426. */
  427. public function getPrioritySelectValues() {
  428. $options = [];
  429. foreach (range(0, self::PRIORITY_HIGHEST) as $value) {
  430. $value = $this->formatPriority($value / self::PRIORITY_DIVIDER);
  431. $options[$value] = $value;
  432. }
  433. return $options;
  434. }
  435. /**
  436. * Gets the values needed to display the changefreq dropdown setting.
  437. *
  438. * @return array
  439. * Select options.
  440. */
  441. public function getChangefreqSelectValues() {
  442. $options = ['' => $this->t('- Not specified -')];
  443. foreach (self::$changefreqValues as $setting) {
  444. $options[$setting] = $this->t($setting);
  445. }
  446. return $options;
  447. }
  448. /**
  449. * @return array
  450. */
  451. public static function getChangefreqOptions() {
  452. return self::$changefreqValues;
  453. }
  454. /**
  455. * @param string $priority
  456. * @return string
  457. */
  458. public function formatPriority($priority) {
  459. return number_format((float) $priority, 1, '.', '');
  460. }
  461. /**
  462. * @param string|int $priority
  463. * @return bool
  464. */
  465. public static function isValidPriority($priority) {
  466. return is_numeric($priority) && $priority >= 0 && $priority <= 1;
  467. }
  468. /**
  469. * @param string $changefreq
  470. * @return bool
  471. */
  472. public static function isValidChangefreq($changefreq) {
  473. return in_array($changefreq, self::$changefreqValues);
  474. }
  475. }