PMPost.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  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\PM;
  10. use ForkBB\Core\Validator;
  11. use ForkBB\Models\Page;
  12. use ForkBB\Models\Pages\PM\AbstractPM;
  13. use ForkBB\Models\Pages\PostFormTrait;
  14. use ForkBB\Models\Pages\PostValidatorTrait;
  15. use ForkBB\Models\PM\Cnst;
  16. use ForkBB\Models\PM\PPost;
  17. use ForkBB\Models\PM\PTopic;
  18. use ForkBB\Core\Exceptions\MailException;
  19. use InvalidArgumentException;
  20. use function \ForkBB\__;
  21. class PMPost extends AbstractPM
  22. {
  23. use PostFormTrait;
  24. use PostValidatorTrait;
  25. /**
  26. * Создание новой приватной темы или сообщения
  27. */
  28. public function post(array $args, string $method): Page
  29. {
  30. $this->args = $args;
  31. $quote = null;
  32. if (
  33. isset($args['more2'])
  34. && '' !== \trim($args['more2'], '1234567890')
  35. ) {
  36. $hash = $args['more2'];
  37. $temp = $args;
  38. unset($temp['more2']);
  39. if (1 !== $this->user->u_pm) {
  40. return $this->c->Message->message('PM off', true, 403);
  41. } elseif (! $this->c->Csrf->verify($hash, 'PMAction', $temp)) {
  42. return $this->c->Message->message($this->c->Csrf->getError());
  43. }
  44. $this->targetUser = $this->c->users->load($args['more1']);
  45. $this->newTopic = true;
  46. $this->formTitle = 'New PT title';
  47. $this->pmCrumbs[] = [$this->c->Router->link('PMAction', $args), 'New dialogue'];
  48. $topic = $this->pms->create(Cnst::PTOPIC);
  49. $topic->sender = $this->user;
  50. $topic->recipient = $this->targetUser;
  51. } elseif ($this->pms->accessTopic($args['more1'])) {
  52. $topic = $this->pms->load(Cnst::PTOPIC, $args['more1']);
  53. if (isset($args['more2'])) {
  54. $quote = (int) $args['more2'];
  55. }
  56. $this->targetUser = $topic->ztUser;
  57. $this->pms->area = $this->pms->inArea($topic);
  58. $this->newTopic = false;
  59. $this->formTitle = Cnst::ACTION_ARCHIVE === $this->pms->area ? 'New PM title archive' : 'New PM title';
  60. $this->pmCrumbs[] = [$this->c->Router->link('PMAction', $args), 'New message'];
  61. $this->pmCrumbs[] = $topic;
  62. } else {
  63. return $this->c->Message->message('Not Found', true, 404);
  64. }
  65. if ($topic->closed) {
  66. $this->fIswev = ['e', 'Dialogue is closed'];
  67. } elseif (2 === $topic->blockStatus) {
  68. $this->fIswev = ['e', 'You block addr'];
  69. } elseif (1 === $topic->blockStatus) {
  70. $this->fIswev = ['e', 'Addr block you'];
  71. } elseif (! $topic->actionsAllowed) {
  72. $this->fIswev = ['e', 'Target group pm off'];
  73. } elseif (! $topic->canReply) {
  74. $this->fIswev = ['e', 'Target pm off'];
  75. }
  76. $this->c->Lang->load('post');
  77. if ('POST' === $method) {
  78. $v = $this->messageValidatorPM(null, 'PMAction', $args, false, $this->newTopic);
  79. $isValid = $v->validation($_POST);
  80. if (
  81. $this->newTopic
  82. && ! $this->user->isAdmin
  83. ) {
  84. if (null !== $v->submit) {
  85. if (
  86. $this->targetUser->g_pm_limit > 0
  87. && $this->targetUser->u_pm_num_all >= $this->targetUser->g_pm_limit
  88. ) {
  89. $this->fIswev = ['e', 'Target is full'];
  90. } elseif (
  91. $this->user->g_pm_limit > 0
  92. && $this->user->u_pm_num_all >= $this->user->g_pm_limit
  93. ) {
  94. $this->fIswev = ['e', 'Active is full'];
  95. }
  96. } elseif (null !== $v->archive) {
  97. if (
  98. $this->user->g_pm_limit > 0
  99. && $this->pms->totalArchive >= $this->user->g_pm_limit
  100. ) {
  101. $this->fIswev = ['e', 'Archive is full'];
  102. }
  103. }
  104. }
  105. $this->fIswev = $v->getErrors();
  106. $args['_vars'] = $v->getData();
  107. if (
  108. empty($this->fIswev['e'])
  109. && $isValid
  110. && null === $v->preview
  111. && (
  112. null !== $v->submit
  113. || null !== $v->archive
  114. )
  115. ) {
  116. return $this->endPost($topic, $v);
  117. }
  118. if (
  119. null !== $v->preview
  120. && $isValid
  121. ) {
  122. $this->previewHtml = $this->c->censorship->censor(
  123. $this->c->Parser->parseMessage(null, (bool) $v->hide_smilies)
  124. );
  125. }
  126. } elseif ($quote) {
  127. $quote = $this->pms->load(Cnst::PPOST, $quote);
  128. if (
  129. ! $quote instanceof PPost
  130. || $topic !== $quote->parent
  131. ) {
  132. return $this->c->Message->message('Bad request');
  133. }
  134. $args['_vars'] = [
  135. 'message' => "[quote=\"{$quote->poster}\"]{$quote->message}[/quote]",
  136. ];
  137. unset($args['more2']);
  138. }
  139. $this->pmIndex = $this->pms->area;
  140. $this->nameTpl = 'pm/post';
  141. $this->form = $this->messageFormPM(null, 'PMAction', $args, false, $this->newTopic, false);
  142. $this->posts = $this->newTopic ? null : $topic->review();
  143. $this->postsTitle = 'Topic review';
  144. return $this;
  145. }
  146. protected function messageFormPM(?Model $model, string $marker, array $args, bool $edit, bool $first, bool $quick): array
  147. {
  148. $form = $this->messageForm($model, $marker, $args, $edit, $first, $quick);
  149. if ($this->newTopic) {
  150. $form['btns']['archive'] = [
  151. 'type' => 'submit',
  152. 'value' => __('Archive Send later'),
  153. ];
  154. } elseif (Cnst::ACTION_ARCHIVE === $this->pms->area) {
  155. $form['btns']['submit']['value'] = __('Save');
  156. }
  157. return $form;
  158. }
  159. protected function messageValidatorPM(?Model $model, string $marker, array $args, bool $edit, bool $first): Validator
  160. {
  161. $v = $this->messageValidator($model, $marker, $args, $edit, $first)
  162. ->addRules([
  163. 'archive' => $this->newTopic ? 'string' : 'absent',
  164. 'message' => 'required|string:trim|max:65535 bytes|check_message',
  165. ])->addArguments([
  166. 'submit.check_timeout' => $this->user->u_pm_last_post,
  167. ]);
  168. return $v;
  169. }
  170. /**
  171. * Создание приватной темы/сообщения
  172. */
  173. protected function endPost(PTopic $topic, Validator $v): Page
  174. {
  175. $now = \time();
  176. if ($this->newTopic) {
  177. $topic->subject = $v->subject;
  178. $topic->status = null !== $v->archive ? Cnst::PT_ARCHIVE : Cnst::PT_NORMAL;
  179. $this->pms->insert(Cnst::PTOPIC, $topic);
  180. }
  181. $post = $this->pms->create(Cnst::PPOST);
  182. $post->user = $this->user;
  183. $post->poster_ip = $this->user->ip;
  184. $post->message = $v->message;
  185. $post->hide_smilies = $v->hide_smilies ? 1 : 0;
  186. $post->posted = $now;
  187. $post->topic_id = $topic->id;
  188. $this->pms->insert(Cnst::PPOST, $post);
  189. if ($this->newTopic) {
  190. $topic->first_post_id = $post->id;
  191. }
  192. $this->pms->update(Cnst::PTOPIC, $topic->calcStat());
  193. // новый диалог в архив
  194. if (
  195. $this->newTopic
  196. && Cnst::PT_ARCHIVE === $topic->poster_status
  197. ) {
  198. $message = 'PM to archive Redirect';
  199. // новый диалог в активные
  200. } elseif ($this->newTopic) {
  201. $message = 'PM created Redirect';
  202. $this->user->u_pm_num_all += 1; // ???? может recalculate() ниже u_pm_last_post = $now?
  203. $this->pms->recalculate($this->targetUser);
  204. // сообщение в архивный диалог
  205. } elseif (Cnst::PT_ARCHIVE === $topic->poster_status) {
  206. $message = 'PM to archive Redirect';
  207. // сообщение в активный диалог
  208. } else {
  209. $message = 'PM sent Redirect';
  210. $this->pms->recalculate($this->targetUser);
  211. }
  212. $this->user->u_pm_last_post = $now;
  213. $this->c->users->update($this->user);
  214. // отправка уведомления
  215. $this->c->Online->calc($this);
  216. if ( // ????
  217. Cnst::PT_NORMAL === $topic->poster_status
  218. && 1 === $this->targetUser->u_pm_notify
  219. && 1 === $this->targetUser->email_confirmed
  220. && 0 === $this->c->bans->banFromName($this->targetUser->username)
  221. && ! $this->c->Online->isOnline($this->targetUser)
  222. ) {
  223. try {
  224. $this->c->Lang->load('common', $this->targetUser->language);
  225. $tplData = [
  226. 'fTitle' => $this->c->config->o_board_title,
  227. 'fMailer' => __(['Mailer', $this->c->config->o_board_title]),
  228. 'pmSubject' => $topic->subject,
  229. 'username' => $this->targetUser->username,
  230. 'sender' => $this->user->username,
  231. 'messageUrl' => $this->newTopic ? $topic->link : $post->link,
  232. ];
  233. $this->c->Mail
  234. ->reset()
  235. ->setMaxRecipients(1)
  236. ->setFolder($this->c->DIR_LANG)
  237. ->setLanguage($this->targetUser->language)
  238. ->setTo($this->targetUser->email, $this->targetUser->username)
  239. ->setFrom($this->c->config->o_webmaster_email, $tplData['fMailer'])
  240. ->setTpl('new_pm.tpl', $tplData)
  241. ->send();
  242. $this->c->Lang->load('common', $this->user->language); // ???? вынести?
  243. } catch (MailException $e) {
  244. $this->c->Log->error('PM: MailException', [
  245. 'exception' => $e,
  246. 'headers' => false,
  247. ]);
  248. }
  249. }
  250. // отправка уведомления
  251. return $this->c->Redirect->url($post->link)->message($message);
  252. }
  253. }