Model.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  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\Forum;
  10. use ForkBB\Models\DataModel;
  11. use ForkBB\Models\User\Model as User;
  12. use ForkBB\Models\Forum\Model as Forum;
  13. use PDO;
  14. use RuntimeException;
  15. use InvalidArgumentException;
  16. class Model extends DataModel
  17. {
  18. /**
  19. * Получение родительского раздела
  20. */
  21. protected function getparent(): ?Forum
  22. {
  23. if (
  24. null === $this->parent_forum_id
  25. && 0 !== $this->id
  26. ) {
  27. throw new RuntimeException('Parent is not defined');
  28. }
  29. return $this->c->forums->get($this->parent_forum_id);
  30. }
  31. /**
  32. * Возвращает название раздела
  33. */
  34. protected function getname(): ?string
  35. {
  36. return $this->forum_name;
  37. }
  38. /**
  39. * Статус возможности создания новой темы
  40. */
  41. protected function getcanCreateTopic(): bool
  42. {
  43. $user = $this->c->user;
  44. return 1 == $this->post_topics
  45. || (
  46. null === $this->post_topics
  47. && 1 == $user->g_post_topics
  48. )
  49. || $user->isAdmin
  50. || $user->isModerator($this);
  51. }
  52. /**
  53. * Статус возможности пометки всех тем прочтенными
  54. */
  55. protected function getcanMarkRead(): bool
  56. {
  57. return ! $this->c->user->isGuest; // ????
  58. }
  59. /**
  60. * Статус возможности использования подписок
  61. */
  62. protected function getcanSubscription(): bool
  63. {
  64. return '1' == $this->c->config->o_forum_subscriptions
  65. && $this->id > 0
  66. && ! $this->c->user->isGuest
  67. && ! $this->c->user->isUnverified;
  68. }
  69. /**
  70. * Получение массива подразделов
  71. */
  72. protected function getsubforums(): array
  73. {
  74. $sub = [];
  75. $attr = $this->getAttr('subforums');
  76. if (\is_array($attr)) {
  77. foreach ($attr as $id) {
  78. $sub[$id] = $this->c->forums->get($id);
  79. }
  80. }
  81. return $sub;
  82. }
  83. /**
  84. * Получение массива всех дочерних разделов
  85. */
  86. protected function getdescendants(): array
  87. {
  88. $all = [];
  89. $attr = $this->getAttr('descendants');
  90. if (\is_array($attr)) {
  91. foreach ($attr as $id) {
  92. $all[$id] = $this->c->forums->get($id);
  93. }
  94. }
  95. return $all;
  96. }
  97. /**
  98. * Ссылка на раздел
  99. */
  100. protected function getlink(): string
  101. {
  102. if (0 === $this->id) {
  103. return $this->c->Router->link('Index');
  104. } else {
  105. return $this->c->Router->link(
  106. 'Forum',
  107. [
  108. 'id' => $this->id,
  109. 'name' => $this->forum_name,
  110. ]
  111. );
  112. }
  113. }
  114. /**
  115. * Ссылка на поиск новых сообщений
  116. */
  117. protected function getlinkNew(): string
  118. {
  119. if (0 === $this->id) {
  120. return $this->c->Router->link(
  121. 'SearchAction',
  122. [
  123. 'action' => 'new',
  124. ]
  125. );
  126. } else {
  127. return $this->c->Router->link(
  128. 'SearchAction',
  129. [
  130. 'action' => 'new',
  131. 'forum' => $this->id,
  132. ]
  133. );
  134. }
  135. }
  136. /**
  137. * Ссылка на последнее сообщение в разделе
  138. */
  139. protected function getlinkLast(): ?string
  140. {
  141. if ($this->last_post_id < 1) {
  142. return null;
  143. } else {
  144. return $this->c->Router->link(
  145. 'ViewPost',
  146. [
  147. 'id' => $this->last_post_id,
  148. ]
  149. );
  150. }
  151. }
  152. /**
  153. * Ссылка на создание новой темы
  154. */
  155. protected function getlinkCreateTopic(): string
  156. {
  157. return $this->c->Router->link(
  158. 'NewTopic',
  159. [
  160. 'id' => $this->id,
  161. ]
  162. );
  163. }
  164. /**
  165. * Ссылка на пометку всех тем прочтенными
  166. */
  167. protected function getlinkMarkRead(): string
  168. {
  169. return $this->c->Router->link(
  170. 'MarkRead',
  171. [
  172. 'id' => $this->id,
  173. ]
  174. );
  175. }
  176. /**
  177. * Ссылка на подписку
  178. */
  179. protected function getlinkSubscribe(): ?string
  180. {
  181. if ($this->id < 1) {
  182. return null;
  183. } else {
  184. return $this->c->Router->link(
  185. 'ForumSubscription',
  186. [
  187. 'fid' => $this->id,
  188. 'type' => 'subscribe',
  189. ]
  190. );
  191. }
  192. }
  193. /**
  194. * Ссылка на отписку
  195. */
  196. protected function getlinkUnsubscribe(): ?string
  197. {
  198. if ($this->id < 1) {
  199. return null;
  200. } else {
  201. return $this->c->Router->link(
  202. 'ForumSubscription',
  203. [
  204. 'fid' => $this->id,
  205. 'type' => 'unsubscribe',
  206. ]
  207. );
  208. }
  209. }
  210. /**
  211. * Получение массива модераторов
  212. */
  213. protected function getmoderators(): array
  214. {
  215. $attr = $this->getAttr('moderators');
  216. if (
  217. empty($attr)
  218. || ! \is_array($attr)
  219. ) {
  220. return [];
  221. }
  222. if ('1' == $this->c->user->viewUsers) {
  223. foreach ($attr as $id => &$cur) {
  224. $cur = [
  225. 'name' => $cur,
  226. 'link' => $this->c->Router->link(
  227. 'User',
  228. [
  229. 'id' => $id,
  230. 'name' => $cur,
  231. ]
  232. ),
  233. ];
  234. }
  235. } else {
  236. foreach ($attr as $id => &$cur) {
  237. $cur = [
  238. 'name' => $cur,
  239. 'link' => null,
  240. ];
  241. }
  242. }
  243. unset($cur);
  244. return $attr;
  245. }
  246. /**
  247. * Добавляет указанных пользователей в список модераторов
  248. */
  249. public function modAdd(User ...$users): void
  250. {
  251. $attr = $this->getAttr('moderators');
  252. if (
  253. empty($attr)
  254. || ! \is_array($attr)
  255. ) {
  256. $attr = [];
  257. }
  258. foreach ($users as $user) {
  259. if (! $user instanceof User) {
  260. throw new InvalidArgumentException('Expected User');
  261. }
  262. $attr[$user->id] = $user->username;
  263. }
  264. $this->moderators = $attr;
  265. }
  266. /**
  267. * Удаляет указанных пользователей из списка модераторов
  268. */
  269. public function modDelete(User ...$users): void
  270. {
  271. $attr = $this->getAttr('moderators');
  272. if (
  273. empty($attr)
  274. || ! \is_array($attr)
  275. ) {
  276. return;
  277. }
  278. foreach ($users as $user) {
  279. if (! $user instanceof User) {
  280. throw new InvalidArgumentException('Expected User');
  281. }
  282. unset($attr[$user->id]);
  283. }
  284. $this->moderators = $attr;
  285. }
  286. /**
  287. * Возвращает общую статистику по дереву разделов с корнем в текущем разделе
  288. */
  289. protected function gettree(): Forum
  290. {
  291. $attr = $this->getAttr('tree');
  292. if (empty($attr)) { //????
  293. $numT = (int) $this->num_topics;
  294. $numP = (int) $this->num_posts;
  295. $time = (int) $this->last_post;
  296. $postId = (int) $this->last_post_id;
  297. $poster = $this->last_poster;
  298. $topic = $this->last_topic;
  299. $fnew = $this->newMessages;
  300. foreach ($this->descendants as $chId => $children) {
  301. $fnew = $fnew || $children->newMessages;
  302. $numT += $children->num_topics;
  303. $numP += $children->num_posts;
  304. if ($children->last_post > $time) {
  305. $time = $children->last_post;
  306. $postId = $children->last_post_id;
  307. $poster = $children->last_poster;
  308. $topic = $children->last_topic;
  309. }
  310. }
  311. $attr = $this->c->forums->create([
  312. 'num_topics' => $numT,
  313. 'num_posts' => $numP,
  314. 'last_post' => $time,
  315. 'last_post_id' => $postId,
  316. 'last_poster' => $poster,
  317. 'last_topic' => $topic,
  318. 'newMessages' => $fnew,
  319. ]);
  320. $this->setAttr('tree', $attr);
  321. }
  322. return $attr;
  323. }
  324. /**
  325. * Количество страниц в разделе
  326. */
  327. protected function getnumPages(): int
  328. {
  329. if (null === $this->num_topics) {
  330. throw new RuntimeException('The model does not have the required data');
  331. }
  332. return (int) ceil(($this->num_topics ?: 1) / $this->c->user->disp_topics);
  333. }
  334. /**
  335. * Массив страниц раздела
  336. */
  337. protected function getpagination(): array
  338. {
  339. return $this->c->Func->paginate(
  340. $this->numPages,
  341. $this->page,
  342. 'Forum',
  343. [
  344. 'id' => $this->id,
  345. 'name' => $this->forum_name,
  346. ]
  347. );
  348. }
  349. /**
  350. * Статус наличия установленной страницы в разделе
  351. */
  352. public function hasPage(): bool
  353. {
  354. return $this->page > 0 && $this->page <= $this->numPages;
  355. }
  356. /**
  357. * Возвращает массив тем с установленной страницы
  358. */
  359. public function pageData(): array
  360. {
  361. if (! $this->hasPage()) {
  362. throw new InvalidArgumentException('Bad number of displayed page');
  363. }
  364. if (empty($this->num_topics)) {
  365. return [];
  366. }
  367. switch ($this->sort_by) {
  368. case 1:
  369. $sortBy = 't.posted DESC';
  370. break;
  371. case 2:
  372. $sortBy = 't.subject ASC';
  373. break;
  374. default:
  375. $sortBy = 't.last_post DESC';
  376. break;
  377. }
  378. $vars = [
  379. ':fid' => $this->id,
  380. ':offset' => ($this->page - 1) * $this->c->user->disp_topics,
  381. ':rows' => $this->c->user->disp_topics,
  382. ];
  383. $query = "SELECT t.id
  384. FROM ::topics AS t
  385. WHERE t.forum_id=?i:fid
  386. ORDER BY t.sticky DESC, {$sortBy}, t.id DESC
  387. LIMIT ?i:offset, ?i:rows";
  388. $this->idsList = $this->c->DB->query($query, $vars)->fetchAll(PDO::FETCH_COLUMN);
  389. return empty($this->idsList) ? [] : $this->c->topics->view($this);
  390. }
  391. /**
  392. * Возвращает значения свойств в массиве
  393. */
  394. public function getAttrs(): array
  395. {
  396. $data = parent::getAttrs();
  397. $data['moderators'] = empty($data['moderators']) || ! \is_array($data['moderators'])
  398. ? ''
  399. : \json_encode($data['moderators']);
  400. return $data;
  401. }
  402. }