PostValidatorTrait.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. <?php
  2. /**
  3. * This file is part of the ForkBB <https://github.com/forkbb>.
  4. *
  5. * @copyright (c) Visman <mio.visman@yandex.ru, https://github.com/MioVisman>
  6. * @license The MIT License (MIT)
  7. */
  8. declare(strict_types=1);
  9. namespace ForkBB\Models\Pages;
  10. use ForkBB\Core\Validator;
  11. use ForkBB\Models\Model;
  12. use function \ForkBB\__;
  13. trait PostValidatorTrait
  14. {
  15. /**
  16. * Дополнительная проверка subject
  17. */
  18. public function vCheckSubject(Validator $v, string $subject, $attr, bool $executive): string
  19. {
  20. // после цензуры заголовок темы путой
  21. if ('' == $this->c->censorship->censor($subject)) {
  22. $v->addError('No subject after censoring');
  23. // заголовок темы только заглавными буквами
  24. } elseif (
  25. ! $executive
  26. && 1 !== $this->c->config->b_subject_all_caps
  27. && \preg_match('%\p{Lu}%u', $subject)
  28. && ! \preg_match('%\p{Ll}%u', $subject)
  29. ) {
  30. $v->addError('All caps subject');
  31. }
  32. return $subject;
  33. }
  34. /**
  35. * Дополнительная проверка message
  36. */
  37. public function vCheckMessage(Validator $v, string $message, $attr, bool $executive): string
  38. {
  39. $prepare = null;
  40. // после цензуры текст сообщения пустой
  41. if ('' == $this->c->censorship->censor($message)) {
  42. $v->addError('No message after censoring');
  43. // проверка парсером
  44. } else {
  45. $prepare = true;
  46. $message = $this->c->Parser->prepare($message); //????
  47. foreach ($this->c->Parser->getErrors([], [], true) as $error) {
  48. $prepare = false;
  49. $v->addError($error);
  50. }
  51. }
  52. // текст сообщения только заглавными буквами
  53. if (
  54. true === $prepare
  55. && ! $executive
  56. && 1 !== $this->c->config->b_message_all_caps
  57. ) {
  58. $text = $this->c->Parser->getText();
  59. if (
  60. \preg_match('%\p{Lu}%u', $text)
  61. && ! \preg_match('%\p{Ll}%u', $text)
  62. ) {
  63. $v->addError('All caps message');
  64. }
  65. }
  66. return $message;
  67. }
  68. /**
  69. * Проверка времени ограничения флуда
  70. */
  71. public function vCheckTimeout(Validator $v, $submit, $attr, ?int $last)
  72. {
  73. if ($v->noValue($submit)) {
  74. return null;
  75. }
  76. $last = $last > 0 ? $last : $this->user->last_post;
  77. $time = \time() - $last;
  78. if ($time < $this->user->g_post_flood) {
  79. $v->addError(['Flood message', $this->user->g_post_flood - $time], FORK_MESS_ERR);
  80. }
  81. return $submit;
  82. }
  83. /**
  84. * Подготовка валидатора к проверке данных из формы создания темы/сообщения
  85. */
  86. protected function messageValidator(?Model $model, string $marker, array $args, bool $edit, bool $first): Validator
  87. {
  88. $this->c->Lang->load('validator');
  89. // обработка вложений + хак с добавление вложений в сообщение на лету
  90. if (\is_string($attMessage = $this->attachmentsProc($marker, $args))) {
  91. $_POST['message'] .= $attMessage;
  92. }
  93. $notPM = $this->fIndex !== self::FI_PM;
  94. if ($this->user->isGuest) {
  95. $ruleEmail = 1 === $this->c->config->b_force_guest_email ? 'required' : 'exist';
  96. $ruleEmail .= '|string:trim,empty|email:noban';
  97. $ruleUsername = 'required|string:trim|username';
  98. } else {
  99. $ruleEmail = 'absent';
  100. $ruleUsername = 'absent';
  101. }
  102. if (
  103. $notPM
  104. && (
  105. $this->user->isAdmin
  106. || $this->user->isModerator($model)
  107. )
  108. ) {
  109. if ($first) {
  110. $ruleStickTopic = 'checkbox';
  111. $ruleStickFP = 'checkbox';
  112. } else {
  113. $ruleStickTopic = 'absent';
  114. $ruleStickFP = 'absent';
  115. }
  116. if (
  117. ! $first
  118. && ! $edit
  119. ) {
  120. $ruleMergePost = 'checkbox';
  121. } else {
  122. $ruleMergePost = 'absent';
  123. }
  124. if (
  125. $edit
  126. && ! $model->user->isGuest
  127. && ! $model->user->isAdmin
  128. ) {
  129. $ruleEditPost = 'checkbox';
  130. } else {
  131. $ruleEditPost = 'absent';
  132. }
  133. $executive = true;
  134. } else {
  135. $ruleStickTopic = 'absent';
  136. $ruleStickFP = 'absent';
  137. $ruleMergePost = 'absent:1';
  138. $ruleEditPost = 'absent';
  139. $executive = false;
  140. }
  141. if ($first) {
  142. $ruleSubject = 'required|string:trim,spaces|min:1|max:70|' . ($executive ? '' : 'noURL|') . 'check_subject';
  143. } else {
  144. $ruleSubject = 'absent';
  145. }
  146. if (
  147. ! $edit
  148. && $notPM
  149. && 1 === $this->c->config->b_topic_subscriptions
  150. && $this->user->email_confirmed
  151. ) {
  152. $ruleSubscribe = 'checkbox';
  153. } else {
  154. $ruleSubscribe = 'absent';
  155. }
  156. if (1 === $this->c->config->b_smilies) {
  157. $ruleHideSmilies = 'checkbox';
  158. } else {
  159. $ruleHideSmilies = 'absent';
  160. }
  161. $ruleMessage = 'required|string:trim|max:' . $this->c->MAX_POST_SIZE . ($executive ? '' : '|noURL') . '|check_message';
  162. $v = $this->c->Validator->reset()
  163. ->addValidators([
  164. 'check_subject' => [$this, 'vCheckSubject'],
  165. 'check_message' => [$this, 'vCheckMessage'],
  166. 'check_timeout' => [$this, 'vCheckTimeout'],
  167. ])->addRules([
  168. 'token' => 'token:' . $marker,
  169. 'email' => $ruleEmail,
  170. 'username' => $ruleUsername,
  171. 'subject' => $ruleSubject,
  172. 'stick_topic' => $ruleStickTopic,
  173. 'stick_fp' => $ruleStickFP,
  174. 'merge_post' => $ruleMergePost,
  175. 'hide_smilies' => $ruleHideSmilies,
  176. 'edit_post' => $ruleEditPost,
  177. 'subscribe' => $ruleSubscribe,
  178. 'preview' => 'string',
  179. 'submit' => 'string|check_timeout',
  180. 'message' => $ruleMessage,
  181. ])->addAliases([
  182. 'email' => 'Email',
  183. 'username' => 'Username',
  184. 'subject' => 'Subject',
  185. 'message' => 'Message',
  186. ])->addArguments([
  187. 'token' => $args,
  188. 'subject.check_subject' => $executive,
  189. 'message.check_message' => $executive,
  190. 'email.email' => $this->user,
  191. ])->addMessages([
  192. 'username.login' => 'Login format',
  193. ]);
  194. if (
  195. $first
  196. && $notPM
  197. && $this->userRules->usePoll
  198. ) {
  199. $v->addValidators([
  200. 'check_poll' => [$this, 'vCheckPoll'],
  201. ])->addRules([
  202. 'poll_enable' => 'checkbox|check_poll',
  203. 'poll.duration' => 'integer|min:0|max:366',
  204. 'poll.hide_result' => 'checkbox',
  205. 'poll.question.*' => 'string:trim|max:240',
  206. 'poll.type.*' => 'integer|min:1|max:' . $this->c->config->i_poll_max_fields,
  207. 'poll.answer.*.*' => 'string:trim|max:240',
  208. ]);
  209. }
  210. return $v;
  211. }
  212. /**
  213. * Дополнительная проверка опроса
  214. */
  215. public function vCheckPoll(Validator $v, string|false $enable, $attr): string|false
  216. {
  217. if (
  218. false !== $enable
  219. && empty($v->getErrors())
  220. ) {
  221. $poll = $this->c->polls->create([
  222. 'question' => $v->poll['question'],
  223. 'answer' => $v->poll['answer'],
  224. 'type' => $v->poll['type'],
  225. ]);
  226. $result = $this->c->polls->revision($poll);
  227. if (true !== $result) {
  228. $v->addError($result);
  229. }
  230. }
  231. return $enable;
  232. }
  233. /**
  234. * Проверка вложений
  235. */
  236. public function vCheckAttach(Validator $v, array $files): array
  237. {
  238. $exts = \array_flip(\explode(',', $this->user->g_up_ext));
  239. $result = [];
  240. foreach ($files as $file) {
  241. if (isset($exts[$file->ext()])) {
  242. $result[] = $file;
  243. } else {
  244. $v->addError(['The %s extension is not allowed', $file->ext()]);
  245. }
  246. }
  247. return $result;
  248. }
  249. /**
  250. * Обрабатывает загруженные файлы
  251. */
  252. protected function attachmentsProc(string $marker, array $args): ?string
  253. {
  254. if (! $this->userRules->useUpload) {
  255. return null;
  256. }
  257. $v = $this->c->Validator->reset()
  258. ->addValidators([
  259. 'check_attach' => [$this, 'vCheckAttach'],
  260. ])->addRules([
  261. 'token' => 'token:' . $marker,
  262. 'attachments' => "file:multiple|max:{$this->user->g_up_size_kb}|check_attach",
  263. ])->addAliases([
  264. 'attachments' => 'Attachments',
  265. ])->addArguments([
  266. 'token' => $args,
  267. ])->addMessages([
  268. ]);
  269. if (! $v->validation($_FILES + $_POST)) {
  270. $this->fIswev = $v->getErrors();
  271. return null;
  272. } elseif (! \is_array($v->attachments)) {
  273. return null;
  274. }
  275. $result = "\n";
  276. foreach ($v->attachments as $file) {
  277. $data = $this->c->attachments->addFile($file);
  278. if (\is_array($data)) {
  279. $name = $file->name();
  280. if ($data['image']) {
  281. $result .= "[img]{$data['url']}[/img]\n"; // ={$name}
  282. } else {
  283. $result .= "[url={$data['url']}]{$name}[/url]\n";
  284. }
  285. }
  286. }
  287. return $result;
  288. }
  289. }