2017-11-26

This commit is contained in:
Visman 2017-11-26 18:56:54 +07:00
parent 37f80f6fb8
commit 7e91738c70
40 changed files with 1421 additions and 1429 deletions

View file

@ -41,7 +41,7 @@ class Install
. substr($uri, 0, (int) strrpos($uri, '/'));
$this->c->Lang->load('common', $this->c->config->o_default_lang);
$this->c->user = new User(['id' => 2, 'group_id' => $this->c->GROUP_ADMIN], $this->c);
$this->c->user = $this->ModelUser->setAttrs(['id' => 2, 'group_id' => $this->c->GROUP_ADMIN]);
$r = $this->c->Router;
$r->add('GET', '/install', 'Install:install', 'Install');

163
app/Core/FuncAll.php Normal file
View file

@ -0,0 +1,163 @@
<?php
namespace ForkBB\Core;
use ForkBB\Models\Model;
class FuncAll
{
/**
* Контейнер
* @var Container
*/
protected $c;
/**
* Модель вызова
* @var Model
*/
protected $model;
/**
* Метод вызова
* @var string
*/
protected $method;
/**
* Аргументы вызова
* @var array
*/
protected $args;
/**
* Конструктор
*
* @param Container $container
*/
public function __construct(Container $container)
{
$this->c = $container;
}
/**
* Настройка вызова
*
* @param string $method
* @param Model $model
* @param array ...$args
*
* @return FuncAll
*/
public function setModel($method, Model $model, ...$args)
{
$this->model = $model;
$this->method = $method;
$this->args = $args;
return $this;
}
/**
* Обработака вызова
*
* @param string $name
*
* @return mixed
*/
public function __get($name)
{
$data = $this->model->{$name};
return $this->{$this->method}($data, ...$this->args);
}
/**
* Цензура
*
* @param string $str
*
* @return string
*/
public function cens($str)
{
return $this->c->censorship->censor($str);
}
/**
* Возвращает число в формате языка текущего пользователя
*
* @param mixed $number
* @param int $decimals
*
* @return string
*/
protected function num($number, $decimals = 0)
{
return is_numeric($number)
? number_format($number, $decimals, __('lang_decimal_point'), __('lang_thousands_sep'))
: 'not a number';
}
/**
* Возвращает даты/время в формате текущего пользователя
*
* @param int $timestamp
* @param bool $dateOnly
* @param string $dateFormat
* @param string $timeFormat
* @param bool $timeOnly
* @param bool $noText
*
* @return string
*/
protected function dt($timestamp, $dateOnly = false, $dateFormat = null, $timeFormat = null, $timeOnly = false, $noText = false)
{
if (empty($timestamp)) {
return __('Never');
}
$user = $this->c->user;
$diff = ($user->timezone + $user->dst) * 3600;
$timestamp += $diff;
if (null === $dateFormat) {
$dateFormat = $this->c->DATE_FORMATS[$user->date_format];
}
if(null === $timeFormat) {
$timeFormat = $this->c->TIME_FORMATS[$user->time_format];
}
$date = gmdate($dateFormat, $timestamp);
if(! $noText) {
$now = time() + $diff;
if ($date == gmdate($dateFormat, $now)) {
$date = __('Today');
} elseif ($date == gmdate($dateFormat, $now - 86400)) {
$date = __('Yesterday');
}
}
if ($dateOnly) {
return $date;
} elseif ($timeOnly) {
return gmdate($timeFormat, $timestamp);
} else {
return $date . ' ' . gmdate($timeFormat, $timestamp);
}
}
/**
* Преобразует timestamp в YYYY-MM-DDTHH:mm:ss.sssZ
*
* @param int $timestamp
*
* @return string
*/
public function utc($timestamp)
{
return gmdate('Y-m-d\TH:i:s\Z', $timestamp);
}
}

View file

@ -1,148 +0,0 @@
<?php
namespace ForkBB\Models\Actions;
use ForkBB\Core\Container;
use ForkBB\Models\User;
class CacheGenerator
{
/**
* Контейнер
* @var Container
*/
protected $c;
/**
* Конструктор
* @param Container $container
*/
public function __construct(Container $container)
{
$this->c = $container;
}
/**
* Возвращает массив конфигурации форума
* @return array
*/
public function config()
{
return $this->c->DB->query('SELECT conf_name, conf_value FROM ::config')->fetchAll(\PDO::FETCH_KEY_PAIR);
}
/**
* Возвращает массив банов
* @return array
*/
public function bans()
{
return $this->c->DB->query('SELECT id, username, ip, email, message, expire FROM ::bans')->fetchAll();
}
/**
* Возвращает массив слов попадающий под цензуру
* @return array
*/
public function censoring()
{
$stmt = $this->c->DB->query('SELECT search_for, replace_with FROM ::censoring');
$search = $replace = [];
while ($row = $stmt->fetch()) {
$replace[] = $row['replace_with'];
$search[] = '%(?<![\p{L}\p{N}])('.str_replace('\*', '[\p{L}\p{N}]*?', preg_quote($row['search_for'], '%')).')(?![\p{L}\p{N}])%iu';
}
return [$search, $replace];
}
/**
* Возвращает информацию о последнем зарегистрированном пользователе и
* общем числе пользователей
* @return array
*/
public function usersInfo()
{
$stats = [];
$stats['total_users'] = $this->c->DB->query('SELECT COUNT(id)-1 FROM ::users WHERE group_id!=?i', [$this->c->GROUP_UNVERIFIED])->fetchColumn();
$stats['last_user'] = $this->c->DB->query('SELECT id, username FROM ::users WHERE group_id!=?i ORDER BY registered DESC LIMIT 1', [$this->c->GROUP_UNVERIFIED])->fetch();
return $stats;
}
/**
* Возвращает спимок id админов
* @return array
*/
public function admins()
{
return $this->c->DB->query('SELECT id FROM ::users WHERE group_id=?i', [$this->c->GROUP_ADMIN])->fetchAll(\PDO::FETCH_COLUMN);
}
/**
* Возвращает массив с описанием смайлов
* @return array
*/
public function smilies()
{
return $this->c->DB->query('SELECT text, image FROM ::smilies ORDER BY disp_position')->fetchAll(\PDO::FETCH_KEY_PAIR); //???? text уникальное?
}
/**
* Возвращает массив карты базы данных
*/
public function dbMap()
{
return $this->c->DB->getMap();
}
/**
* Возвращает массив с описанием форумов для текущего пользователя
* @return array
*/
public function forums(User $user)
{
$stmt = $this->c->DB->query('SELECT g_read_board FROM ::groups WHERE g_id=?i:id', [':id' => $user->group_id]);
$read = $stmt->fetchColumn();
$stmt->closeCursor();
$tree = $desc = $asc = [];
if ($read) {
$stmt = $this->c->DB->query('SELECT c.id AS cid, c.cat_name, f.id AS fid, f.forum_name, f.redirect_url, f.parent_forum_id, f.disp_position, fp.post_topics, fp.post_replies FROM ::categories AS c INNER JOIN ::forums AS f ON c.id=f.cat_id LEFT JOIN ::forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id=?i:gid) WHERE fp.read_forum IS NULL OR fp.read_forum=1 ORDER BY c.disp_position, c.id, f.disp_position', [':gid' => $user->group_id]);
while ($f = $stmt->fetch()) {
$tree[$f['parent_forum_id']][$f['fid']] = $f;
}
$this->forumsDesc($desc, $tree);
$this->forumsAsc($asc, $tree);
}
return [$tree, $desc, $asc];
}
protected function forumsDesc(&$list, $tree, $node = 0)
{
if (empty($tree[$node])) {
return;
}
foreach ($tree[$node] as $id => $forum) {
$list[$id] = $node ? array_merge([$node], $list[$node]) : []; //????
$list[$id]['forum_name'] = $forum['forum_name'];
$this->forumsDesc($list, $tree, $id);
}
}
protected function forumsAsc(&$list, $tree, $nodes = [0])
{
$list[$nodes[0]][] = $nodes[0];
if (empty($tree[$nodes[0]])) {
return;
}
foreach ($tree[$nodes[0]] as $id => $forum) {
$temp = [$id];
foreach ($nodes as $i) {
$list[$i][] = $id;
$temp[] = $i;
}
$this->forumsAsc($list, $tree, $temp);
}
}
}

View file

@ -1,83 +0,0 @@
<?php
namespace ForkBB\Models\Actions;
use ForkBB\Core\Cache;
use ForkBB\Core\Container;
use InvalidArgumentException;
class CacheLoader
{
/**
* Контейнер
* @var Container
*/
protected $c;
/**
* @var ForkBB\Core\Cache
*/
protected $cache;
/**
* Конструктор
* @param Cache $cache
* @param Container $container
*/
public function __construct(Cache $cache, Container $container)
{
$this->cache = $cache;
$this->c = $container;
}
/**
* Загрузка данных из кэша (генерация кэша при отсутствии или по требованию)
* @param string $key
* @param bool $update
* @return mixed
* @throws \InvalidArgumentException
*/
public function load($key, $update = false)
{
if (preg_match('%\p{Lu}%u', $key)) {
throw new InvalidArgumentException('The key must not contain uppercase letters');
}
if (! $update && $this->cache->has($key)) {
return $this->cache->get($key);
} else {
$value = $this->c->{'get ' . $key};
$this->cache->set($key, $value);
if ($update) {
$this->c->$key = $value;
}
return $value;
}
}
/**
* Загрузка данных по разделам из кэша (генерация кэша при условии)
* @return array
*/
public function loadForums()
{
$mark = $this->cache->get('forums_mark');
$key = 'forums_' . $this->c->user->group_id;
if (empty($mark)) {
$this->cache->set('forums_mark', time());
$value = $this->c->{'get forums'};
$this->cache->set($key, [time(), $value]);
return $value;
}
$result = $this->cache->get($key);
if (empty($result) || $result[0] < $mark) {
$value = $this->c->{'get forums'};
$this->cache->set($key, [time(), $value]);
return $value;
}
return $result[1];
}
}

View file

@ -1,104 +0,0 @@
<?php
namespace ForkBB\Models\Actions;
use ForkBB\Core\Cache;
class CacheStopwords
{
/**
* @var ForkBB\Core\Cache
*/
protected $cache;
/**
* @var array
*/
protected $files;
/**
* @var string
*/
protected $id;
/**
* Конструктор
*
* @param ForkBB\Core\Cache $cache
*/
public function __construct(Cache $cache)
{
$this->cache = $cache;
}
/**
* Возвращает массив слов, которые не участвуют в поиске
*
* @return array
*/
public function load()
{
$arr = $this->cache->get('stopwords');
if (isset($arr['id'])
&& isset($arr['stopwords'])
&& $arr['id'] === $this->generateId()
) {
return $arr['stopwords'];
} else {
return $this->regeneration();
}
}
/**
* Генерация id кэша на основе найденных файлов stopwords.txt
*
* @return string
*/
protected function generateId()
{
if (! empty($this->id)) {
return $this->id;
}
$files = glob(PUN_ROOT . 'lang/*/stopwords.txt');
if ($files === false) {
return 'cache_id_error';
}
$this->files = $files;
$hash = [];
foreach ($files as $file) {
$hash[] = $file;
$hash[] = filemtime($file);
}
return $this->id = sha1(implode('|', $hash));
}
/**
* Регенерация кэша массива слов с возвращением результата
*
* @return array
*/
protected function regeneration()
{
$id = $this->generateId();
if (! is_array($this->files)) {
return [];
}
$stopwords = [];
foreach ($this->files as $file) {
$stopwords = array_merge($stopwords, file($file));
}
// Tidy up and filter the stopwords
$stopwords = array_map('trim', $stopwords);
$stopwords = array_filter($stopwords);
$this->cache->set('stopwords', ['id' => $id, 'stopwords' => $stopwords]);
return $stopwords;
}
}

View file

@ -2,12 +2,11 @@
namespace ForkBB\Models;
use ForkBB\Core\Container;
use ForkBB\Models\Model;
use InvalidArgumentException;
use RuntimeException;
abstract class DataModel extends Model
class DataModel extends Model
{
/**
* Массив флагов измененных свойств модели
@ -15,18 +14,6 @@ abstract class DataModel extends Model
*/
protected $modified = [];
/**
* Конструктор
*
* @param array $attrs
* @param Container $container
*/
public function __construct(array $attrs, Container $container)
{
parent::__construct($container);
$this->a = $attrs;
}
/**
* Устанавливает значения для свойств
*
@ -36,11 +23,35 @@ abstract class DataModel extends Model
*/
public function setAttrs(array $attrs)
{
$this->a = $attrs; //????
$this->a = $attrs; //????
$this->aCalc = [];
$this->modified = [];
return $this;
}
/**
* Перезапись свойст модели
*
* @param array $attrs
*
* @return DataModel
*/
public function replAttrs(array $attrs)
{
foreach ($attrs as $key => $val) {
$this->{'__' . $key} = $val; //????
unset($this->aCalc['key']);
}
$modified = array_diff(array_keys($this->modified), array_keys($attrs));
$this->modified = [];
foreach ($modified as $key) {
$this->modified[$key] = true;
}
return $this;
}
/**
* Возвращает значения свойств в массиве
*
@ -77,15 +88,13 @@ abstract class DataModel extends Model
*/
public function __set($name, $val)
{
// запись свойства без отслеживания изменений
// без отслеживания
if (strpos($name, '__') === 0) {
return parent::__set(substr($name, 2), $val);
}
$old = isset($this->a[$name]) ? $this->a[$name] : null;
parent::__set($name, $val);
if ($old !== $this->a[$name]) {
$name = substr($name, 2);
// с отслеживанием
} else {
$this->modified[$name] = true;
}
return parent::__set($name, $val);
}
}

View file

@ -4,28 +4,11 @@ namespace ForkBB\Models;
use ForkBB\Models\DataModel;
use ForkBB\Core\Container;
use RuntimeException;
class Forum extends DataModel
{
/**
* @param array $attrs
*
* @return Forum
*/
public function replAtttrs(array $attrs)
{
foreach ($attrs as $key => $val) {
$this->{'__' . $key} = $val; //????
}
$modified = array_diff(array_keys($this->modified), array_keys($attrs));
$this->modified = [];
foreach ($modified as $key) {
$this->modified[$key] = true;
}
return $this;
}
protected function getSubforums()
protected function getsubforums()
{
$sub = [];
if (! empty($this->a['subforums'])) {
@ -36,7 +19,7 @@ class Forum extends DataModel
return $sub;
}
protected function getDescendants()
protected function getdescendants()
{
$all = [];
if (! empty($this->a['descendants'])) {
@ -47,8 +30,181 @@ class Forum extends DataModel
return $all;
}
protected function getParent()
protected function getparent()
{
return $this->c->forums->forum($this->parent_forum_id);
}
protected function getlink()
{
return $this->c->Router->link('Forum', ['id' => $this->id, 'name' => $this->forum_name]);
}
protected function getmoderators()
{
if (empty($this->a['moderators'])) {
return [];
}
$moderators = [];
$mods = unserialize($this->a['moderators']);
foreach ($mods as $name => $id) {
if ($this->c->user->g_view_users == '1') {
$moderators[$id] = [
$this->c->Router->link('User', [
'id' => $id,
'name' => $name,
]),
$name
];
} else {
$moderators[$id] = $name;
}
}
return $moderators;
}
protected function gettree()
{
if (empty($this->a['tree'])) {
$numT = (int) $this->num_topics;
$numP = (int) $this->num_posts;
$time = (int) $this->last_post;
$postId = (int) $this->last_post_id;
$poster = $this->last_poster;
$topic = $this->last_topic;
$fnew = $this->newMessages;
foreach ($this->descendants as $chId => $children) {
$fnew = $fnew || $children->newMessages;
$numT += $children->num_topics;
$numP += $children->num_posts;
if ($children->last_post > $time) {
$time = $children->last_post;
$postId = $children->last_post_id;
$poster = $children->last_poster;
$topic = $children->last_topic;
}
}
$this->a['tree'] = $this->c->ModelForum->setAttrs([
'num_topics' => $numT,
'num_posts' => $numP,
'last_post' => $time,
'last_post_id' => $postId,
'last_poster' => $poster,
'last_topic' => $topic,
'newMessages' => $fnew,
'last_post_link' => empty($postId) ? '' : $this->c->Router->link('ViewPost', ['id' => $postId]),
]);
}
return $this->a['tree'];
}
/**
* @param int $page
*
* @return bool
*/
public function hasPage($page)
{
if (null === $this->num_topics) {
throw new RuntimeException('The model does not have the required data');
}
if (empty($this->num_topics)) {
if ($page !== 1) {
return false;
}
$this->page = 1;
$this->pages = 1;
$this->offset = 0;
} else {
$pages = ceil($this->num_topics / $this->c->user->disp_topics);
if ($page < 1 || $page > $pages) {
return false;
}
$this->page = $page;
$this->pages = $pages;
$this->offset = ($page - 1) * $this->c->user->disp_topics;
}
return true;
}
/**
* @return array
*/
public function topics()
{
if (null === $this->page) {
throw new RuntimeException('The model does not have the required data');
}
if (empty($this->num_topics)) {
return [];
}
switch ($this->sort_by) {
case 1:
$sortBy = 'posted DESC';
break;
case 2:
$sortBy = 'subject ASC';
break;
case 0:
default:
$sortBy = 'last_post DESC';
break;
}
$vars = [
':fid' => $this->id,
':offset' => $this->offset,
':rows' => $this->c->user->disp_topics,
];
$sql = "SELECT id
FROM ::topics
WHERE forum_id=?i:fid
ORDER BY sticky DESC, {$sortBy}, id DESC
LIMIT ?i:offset, ?i:rows";
$ids = $this->c->DB->query($sql, $vars)->fetchAll(\PDO::FETCH_COLUMN);
if (empty($ids)) {
return []; //????
}
$vars = [
':uid' => $this->c->user->id,
':ids' => $ids,
];
if (! $this->c->user->isGuest && $this->c->config->o_show_dot == '1') {
$dots = $this->c->DB
->query('SELECT topic_id FROM ::posts WHERE poster_id=?i:uid AND topic_id IN (?ai:ids) GROUP BY topic_id', $vars)
->fetchAll(\PDO::FETCH_COLUMN);
$dots = array_flip($dots);
} else {
$dots = [];
}
if ($this->c->user->isGuest) {
$sql = "SELECT t.*
FROM ::topics AS t
WHERE t.id IN(?ai:ids)
ORDER BY t.sticky DESC, t.{$sortBy}, t.id DESC";
} else {
$sql = "SELECT t.*, mot.mt_last_visit, mot.mt_last_read
FROM ::topics AS t
LEFT JOIN ::mark_of_topic AS mot ON (mot.uid=?i:uid AND t.id=mot.tid)
WHERE t.id IN (?ai:ids)
ORDER BY t.sticky DESC, t.{$sortBy}, t.id DESC";
}
$topics = $this->c->DB->query($sql, $vars)->fetchAll();
foreach ($topics as &$cur) {
$cur['dot'] = isset($dots[$cur['id']]);
$cur = $this->c->ModelTopic->setAttrs($cur);
}
unset($cur);
return $topics;
}
}

View file

@ -8,39 +8,35 @@ use RuntimeException;
class ForumList extends Model
{
/**
* Загружает список доступных разделов для текущего пользователя из кеша/БД
* Заполняет модель данными
*
* @param int $gid
*
* @return ForumList
*/
public function init()
public function init($gid = 0)
{
if (empty($gid)) {
$gid = $this->c->user->group_id;
}
$mark = $this->c->Cache->get('forums_mark');
if (empty($mark)) {
$this->c->Cache->set('forums_mark', time());
return $this->load();
$list = $this->refresh($gid);
} else {
$result = $this->c->Cache->get('forums_' . $gid);
if (empty($result['time']) || $result['time'] < $mark) {
$list = $this->refresh($gid);
} else {
$list = $result['list'];
}
}
$result = $this->c->Cache->get('forums_' . $this->c->user->group_id);
if (empty($result['time']) || $result['time'] < $mark) {
return $this->load();
}
$this->list = $result['list']; //????
$this->list = $list; //????
return $this;
}
/**
* Проверяет доступность раздела
*
* @param int $id
*
* @return bool
*/
public function isAvailable($id)
{
return isset($this->list[$id]); //????
}
/**
*
* @param int $id
@ -51,7 +47,7 @@ class ForumList extends Model
{
if (isset($this->forums[$id])) {
return $this->forums[$id];
} elseif ($this->isAvailable($id)) {
} elseif (isset($this->list[$id])) {
$forum = $this->c->ModelForum->setAttrs($this->list[$id]);
$this->a['forums'][$id] = $forum; //????
return $forum;

View file

@ -1,93 +0,0 @@
<?php
namespace ForkBB\Models\ForumList;
use ForkBB\Models\MethodModel;
class Load extends MethodModel
{
/**
* @var array
*/
protected $list = [];
/**
* Заполняет модель данными из БД для текущего пользователя
* Создает кеш
*
* @return ForumList
*/
public function load()
{
$this->getList();
$this->model->list = $this->list; //????
$this->c->Cache->set('forums_' . $this->c->user->group_id, [
'time' => time(),
'list' => $this->list,
]);
return $this->model;
}
/**
* Получает данные из БД
*/
protected function getList()
{
if ($this->c->user->g_read_board != '1') {
return;
}
$list = [];
$vars = [':gid' => $this->c->user->group_id];
$sql = 'SELECT c.id AS cid, c.cat_name, f.id, f.forum_name, f.redirect_url, f.parent_forum_id,
f.disp_position, fp.post_topics, fp.post_replies
FROM ::categories AS c
INNER JOIN ::forums AS f ON c.id=f.cat_id
LEFT JOIN ::forum_perms AS fp ON (fp.group_id=?i:gid AND fp.forum_id=f.id)
WHERE fp.read_forum IS NULL OR fp.read_forum=1
ORDER BY c.disp_position, c.id, f.disp_position';
$stmt = $this->c->DB->query($sql, $vars);
while ($row = $stmt->fetch()) {
$list[$row['id']] = $row;
}
if (empty($list)) {
return;
}
$this->createList($list);
}
/**
* Формирует список доступных разделов
*
* @param array $list
* @param int $parent
*
* @return array
*/
protected function createList(array $list, $parent = 0)
{
$sub = [];
$all = [];
foreach ($list as $id => $f) {
if ($parent === $id || $parent !== $f['parent_forum_id']) {
continue;
}
$sub[] = $id;
$all = array_merge($this->createList($list, $id), $all);
}
if ($parent === 0) {
if (empty($sub)) {
return [];
}
$list[0]['id'] = $parent;
$list[0]['ready'] = true;
}
$all = array_merge($sub, $all);
$list[$parent]['subforums'] = $sub ?: null;
$list[$parent]['descendants'] = $all ?: null;
$this->list[$parent] = array_filter($list[$parent], function($val) {
return $val !== null;
});
return $all;
}
}

View file

@ -7,7 +7,7 @@ use ForkBB\Models\MethodModel;
class LoadTree extends MethodModel
{
/**
* Загружает данные в модели разделов для указанного раздела и всех его потомков
* Загружает данные в модели для указанного раздела и всех его потомков
*
* @param int $rootId
*
@ -22,25 +22,25 @@ class LoadTree extends MethodModel
$list = [];
if (! $root->ready) {
$list[] = $rootId;
$list[$rootId] = $root;
}
foreach ($root->descendants as $id => $descendant) {
if (! $descendant->ready) {
$list[] = $id;
$list[$id] = $descendant;
}
}
$this->loadData($list);
if (! $this->c->user->isGuest) {
$this->checkForNew(array_keys($root->descendants)); //????
$this->checkForNew($root->descendants);
}
return $root;
}
/**
* Загружает данные из БД по списку id разделов
* Загружает данные из БД по списку разделов
*
* @param array $list
*/
@ -52,7 +52,7 @@ class LoadTree extends MethodModel
$vars = [
':uid' => $this->c->user->id,
':forums' => $list,
':forums' => array_keys($list),
];
if ($this->c->user->isGuest) {
@ -79,12 +79,12 @@ class LoadTree extends MethodModel
$stmt = $this->c->DB->query($sql, $vars);
while ($cur = $stmt->fetch()) {
$this->model->forum($cur['id'])->replAtttrs($cur)->ready = true;
$list[$cur['id']]->replAttrs($cur)->__ready = true;
}
}
/**
* Проверяет наличие новых сообщений в разделах по списку id
* Проверяет наличие новых сообщений в разделах по их списку
*
* @param array $list
*/
@ -97,8 +97,7 @@ class LoadTree extends MethodModel
// предварительная проверка разделов
$time = [];
$max = max((int) $this->c->user->last_visit, (int) $this->c->user->u_mark_all_read);
foreach ($list as $id) {
$forum = $this->model->forum($id);
foreach ($list as $forum) {
$t = max($max, (int) $forum->mf_mark_all_read);
if ($forum->last_post > $t) {
$time[$id] = $t;
@ -125,7 +124,7 @@ class LoadTree extends MethodModel
$stmt = $this->c->DB->query($sql, $vars);
while ($cur = $stmt->fetch()) {
if ($cur['last_post'] > $time[$cur['forum_id']]) {
$this->model->forum($cur['forum_id'])->newMessages = true; //????
$list[$cur['forum_id']]->__newMessages = true; //????
}
}
}

View file

@ -0,0 +1,97 @@
<?php
namespace ForkBB\Models\ForumList;
use ForkBB\Models\MethodModel;
class Refresh extends MethodModel
{
/**
* @var array
*/
protected $list = [];
/**
* Возвращает список доступных разделов для группы
* Обновляет кеш
*
* @param int $gid
*
* @return array
*/
public function refresh($gid)
{
$vars = [
':gid' => $gid,
];
if ($this->c->user->group_id === $gid) {
$read = $this->c->user->g_read_board;
} else {
$sql = 'SELECT g_read_board FROM ::groups WHERE g_id=?i:gid';
$read = $this->c->DB->query($sql, $vars)->fetchColumn();
}
if ($read == '1') {
$list = [];
$sql = 'SELECT f.cat_id, c.cat_name, f.id, f.forum_name, f.redirect_url, f.parent_forum_id,
f.disp_position, fp.post_topics, fp.post_replies
FROM ::categories AS c
INNER JOIN ::forums AS f ON c.id=f.cat_id
LEFT JOIN ::forum_perms AS fp ON (fp.group_id=?i:gid AND fp.forum_id=f.id)
WHERE fp.read_forum IS NULL OR fp.read_forum=1
ORDER BY c.disp_position, c.id, f.disp_position';
$stmt = $this->c->DB->query($sql, $vars);
while ($row = $stmt->fetch()) {
$list[$row['id']] = $row;
}
if (! empty($list)) {
$this->createList($list);
}
}
$this->c->Cache->set('forums_' . $gid, [
'time' => time(),
'list' => $this->list,
]);
return $this->list;
}
/**
* Формирует список доступных разделов
*
* @param array $list
* @param int $parent
*
* @return array
*/
protected function createList(array $list, $parent = 0)
{
$sub = [];
$all = [];
foreach ($list as $id => $f) {
if ($parent === $id || $parent !== $f['parent_forum_id']) {
continue;
}
$sub[] = $id;
$all = array_merge($this->createList($list, $id), $all);
}
if ($parent === 0) {
if (empty($sub)) {
return [];
}
$list[0]['id'] = $parent;
$list[0]['ready'] = true;
}
$all = array_merge($sub, $all);
$list[$parent]['subforums'] = $sub ?: null;
$list[$parent]['descendants'] = $all ?: null;
$this->list[$parent] = array_filter($list[$parent], function($val) {
return $val !== null;
});
return $all;
}
}

View file

@ -3,10 +3,9 @@
namespace ForkBB\Models;
use ForkBB\Core\Container;
use InvalidArgumentException;
use RuntimeException;
abstract class Model
class Model
{
/**
* Контейнер
@ -20,6 +19,12 @@ abstract class Model
*/
protected $a = [];
/**
* Вычисленные данные модели
* @var array
*/
protected $aCalc = [];
/**
* Конструктор
*
@ -39,7 +44,9 @@ abstract class Model
*/
public function __isset($name)
{
return isset($this->a[$name]); //???? array_key_exists($name, $this->a)
return array_key_exists($name, $this->a)
|| array_key_exists($name, $this->aCalc)
|| method_exists($this, 'get' . $name);
}
/**
@ -49,7 +56,8 @@ abstract class Model
*/
public function __unset($name)
{
unset($this->a[$name]);
unset($this->a[$name]); //????
unset($this->aCalc[$name]); //????
}
/**
@ -60,8 +68,9 @@ abstract class Model
*/
public function __set($name, $val)
{
$method = 'set' . ucfirst($name);
if (method_exists($this, $method)) {
unset($this->aCalc[$name]);
if (method_exists($this, $method = 'set' . $name)) {
$this->$method($val);
} else {
$this->a[$name] = $val;
@ -77,9 +86,10 @@ abstract class Model
*/
public function __get($name)
{
$method = 'get' . ucfirst($name);
if (method_exists($this, $method)) {
return $this->$method();
if (array_key_exists($name, $this->aCalc)) {
return $this->aCalc[$name];
} elseif (method_exists($this, $method = 'get' . $name)) {
return $this->aCalc[$name] = $this->$method();
} else {
return isset($this->a[$name]) ? $this->a[$name] : null;
}
@ -112,4 +122,24 @@ abstract class Model
return $factory->$name(...$args);
}
}
public function cens()
{
return $this->c->FuncAll->setModel('cens', $this);
}
public function num($decimals = 0)
{
return $this->c->FuncAll->setModel('num', $this, $decimals);
}
public function dt($dateOnly = false, $dateFormat = null, $timeFormat = null, $timeOnly = false, $noText = false)
{
return $this->c->FuncAll->setModel('dt', $this, $dateOnly, $dateFormat, $timeFormat, $timeOnly, $noText);
}
public function utc()
{
return $this->c->FuncAll->setModel('utc', $this);
}
}

View file

@ -2,46 +2,34 @@
namespace ForkBB\Models;
use ForkBB\Core\Container;
use ForkBB\Models\Model;
use ForkBB\Models\User;
use ForkBB\Models\Page;
class Online extends Model
{
/**
* Конструктор
*
* @param Container $container
*/
public function __construct(Container $container)
{
parent::__construct($container);
$this->users = [];
$this->guests = [];
$this->bots = [];
}
/**
* Обработка данных пользователей онлайн
* Обновление данных текущего пользователя
* Возврат данных по пользователям онлайн
*
* @param Page $page
*
* @return Online
*/
public function calc(Page $page)
{
if ($this->done) {
return;
return $this;
}
$this->done = true;
$position = $page->onlinePos;
if (null === $position) {
return;
return $this;
}
$type = $page->onlineType;
$filter = $page->onlineFilter;
$detail = $page->onlineDetail && $this->c->config->o_users_online == '1';
$filter = $page->onlineFilter;
$this->updateUser($position);
@ -49,69 +37,67 @@ class Online extends Model
$now = time();
$tOnline = $now - $this->c->config->o_timeout_online;
$tVisit = $now - $this->c->config->o_timeout_visit;
$online = [];
$users = [];
$guests = [];
$bots = [];
$deleteG = false;
$deleteU = false;
$setIdle = false;
if ($this->c->config->o_users_online == '1' && $type) {
$stmt = $this->c->DB->query('SELECT user_id, ident, logged, idle, o_position, o_name FROM ::online ORDER BY logged');
} elseif ($type) {
$stmt = $this->c->DB->query('SELECT user_id, ident, logged, idle FROM ::online ORDER BY logged');
if ($detail) {
$sql = 'SELECT user_id, ident, logged, o_position, o_name FROM ::online ORDER BY logged';
} else {
$stmt = $this->c->DB->query('SELECT user_id, ident, logged, idle FROM ::online WHERE logged<?i:online', [':online' => $tOnline]);
$sql = 'SELECT user_id, ident, logged FROM ::online ORDER BY logged';
}
while ($cur = $stmt->fetch()) {
$stmt = $this->c->DB->query($sql);
while ($cur = $stmt->fetch()) {
// посетитель уже не онлайн (или почти не онлайн)
if ($cur['logged'] < $tOnline) {
// пользователь
if ($cur['user_id'] > 1) {
if ($cur['logged'] < $tVisit) {
$deleteU = true;
$this->c->DB->exec('UPDATE ::users SET last_visit=?i:last WHERE id=?i:id', [':last' => $cur['logged'], ':id' => $cur['user_id']]);
} elseif ($cur['idle'] == '0') {
$setIdle = true;
$this->c->DB->exec('UPDATE ::users SET last_visit=?i:last WHERE id=?i:id', [':last' => $cur['logged'], ':id' => $cur['user_id']]); //????
}
// гость
} else {
$deleteG = true;
}
continue;
}
// обработка посетителя для вывода статистики
} elseif ($type) {
++$all;
// пользователи онлайн и общее количество
$online[$cur['user_id']] = true;
++$all;
// включен фильтр и проверка не пройдена
if ($filter && $cur['o_position'] !== $position) {
continue;
}
if (! $detail) {
continue;
}
// пользователь
if ($cur['user_id'] > 1) {
$users[$cur['user_id']] = [
'name' => $cur['ident'],
'logged' => $cur['logged'],
];
// гость
} elseif ($cur['o_name'] == '') {
$guests[] = [
'name' => $cur['ident'],
'logged' => $cur['logged'],
];
// бот
} else {
$bots[$cur['o_name']][] = [
'name' => $cur['ident'],
'logged' => $cur['logged'],
];
}
// просто +1 к общему числу посетителей
// включен фильтр и проверка не пройдена
if ($filter && $cur['o_position'] !== $position) {
continue;
}
// пользователь
if ($cur['user_id'] > 1) {
$users[$cur['user_id']] = [
'name' => $cur['ident'],
'logged' => $cur['logged'],
];
// гость
} elseif ($cur['o_name'] == '') {
$guests[] = [
'name' => $cur['ident'],
'logged' => $cur['logged'],
];
// бот
} else {
++$all;
$bots[$cur['o_name']][] = [
'name' => $cur['ident'],
'logged' => $cur['logged'],
];
}
}
@ -125,20 +111,26 @@ class Online extends Model
$this->c->DB->exec('DELETE FROM ::online WHERE user_id=1 AND logged<?i:online', [':online' => $tOnline]);
}
// обновление idle
if ($setIdle) {
$this->c->DB->exec('UPDATE ::online SET idle=1 WHERE logged<?i:online', [':online' => $tOnline]);
}
// обновление максимального значение пользоватеелй онлайн
if ($this->c->config->st_max_users < $all) {
$this->c->config->st_max_users = $all;
$this->c->config->st_max_users_time = $now;
$this->c->config->save();
}
$this->users = $users;
$this->guests = $guests;
$this->bots = $bots;
$this->all = $all;
$this->detail = $detail;
unset($online[1]);
$this->online = $online;
if ($detail) {
$this->users = $users;
$this->guests = $guests;
$this->bots = $bots;
}
return $this;
}
/**
@ -148,14 +140,13 @@ class Online extends Model
*/
protected function updateUser($position)
{
$now = time();
// гость
if ($this->c->user->isGuest) {
$vars = [
':logged' => time(),
':pos' => $position,
':name' => (string) $this->c->user->isBot,
':ip' => $this->c->user->ip
':pos' => $position,
':name' => (string) $this->c->user->isBot,
':ip' => $this->c->user->ip
];
if ($this->c->user->isLogged) {
$this->c->DB->exec('UPDATE ::online SET logged=?i:logged, o_position=?s:pos, o_name=?s:name WHERE user_id=1 AND ident=?s:ip', $vars);
@ -166,12 +157,12 @@ class Online extends Model
// пользователь
$vars = [
':logged' => time(),
':pos' => $position,
':id' => $this->c->user->id,
':name' => $this->c->user->username,
':pos' => $position,
':id' => $this->c->user->id,
':name' => $this->c->user->username,
];
if ($this->c->user->isLogged) {
$this->c->DB->exec('UPDATE ::online SET logged=?i:logged, idle=0, o_position=?s:pos WHERE user_id=?i:id', $vars);
$this->c->DB->exec('UPDATE ::online SET logged=?i:logged, o_position=?s:pos WHERE user_id=?i:id', $vars);
} else {
$this->c->DB->exec('INSERT INTO ::online (user_id, ident, logged, o_position) SELECT ?i:id, ?s:name, ?i:logged, ?s:pos FROM ::groups WHERE NOT EXISTS (SELECT 1 FROM ::online WHERE user_id=?i:id) LIMIT 1', $vars);
}

View file

@ -0,0 +1,56 @@
<?php
namespace ForkBB\Models\Online;
use ForkBB\Models\MethodModel;
class Info extends MethodModel
{
/**
* Получение информации об онлайн посетителях
*
* @return null|Online
*/
public function info()
{
if (! $this->model->detail) {
return null;
}
$this->model->maxNum = $this->c->config->st_max_users;
$this->model->maxTime = $this->c->config->st_max_users_time;
$info = [];
if ($this->c->user->g_view_users == '1') {
foreach ($this->model->users as $id => $user) {
$info[] = [
$this->c->Router->link('User', [
'id' => $id,
'name' => $user['name'],
]),
$user['name'],
];
}
} else {
foreach ($this->model->users as $user) {
$info[] = $user['name'];
}
}
$this->model->numUsers = count($info);
$s = 0;
foreach ($this->model->bots as $bot => $arr) {
$count = count($arr);
$s += $count;
if ($count > 1) {
$info[] = '[Bot] ' . $bot . ' (' . $count . ')';
} else {
$info[] = '[Bot] ' . $bot;
}
}
$this->model->numGuests = $s + count($this->model->guests);
$this->model->info = $info;
return $this->model;
}
}

View file

@ -26,12 +26,8 @@ abstract class Page extends Model
# $this->titles = []; # array Массив титула страницы | setTitles()
$this->fIswev = []; # array Массив info, success, warning, error, validation информации
# $this->onlinePos = ''; # null|string Позиция для таблицы онлайн текущего пользователя
$this->onlineType = false; # bool Тип обработки пользователей онлайн
# Если false, то идет обновление данных
# Если true, то идет возврат данных (смотрите onlineFilter)
$this->onlineFilter = true; # bool Тип возврата данных при onlineType === true
# Если true, то из online должны вернутся только пользователи находящиеся на этой же странице
# Если false, то все пользователи online
$this->onlineDetail = false; # bool Формировать данные по посетителям online или нет
$this->onlineFilter = true; # bool Посетители только по текущей странице или по всем
# $this->robots = ''; # string Переменная для meta name="robots"
# $this->canonical = ''; # string Переменная для link rel="canonical"
@ -140,7 +136,7 @@ abstract class Page extends Model
*
* @return string
*/
protected function getPageTitle(array $titles = [])
protected function getpageTitle(array $titles = [])
{
if (empty($titles)) {
$titles = $this->titles;
@ -155,7 +151,7 @@ abstract class Page extends Model
*
* @return array
*/
protected function getPageHeaders()
protected function getpageHeaders()
{
$headers = ['link rel="stylesheet" type="text/css" href="' . $this->c->PUBLIC_URL . '/style/' . $this->c->user->style . '/style.css' . '"'];
if ($this->robots) {
@ -173,7 +169,7 @@ abstract class Page extends Model
*
* @return array
*/
protected function getHttpHeaders()
protected function gethttpHeaders()
{
$headers = $this->a['httpHeaders'];
if (! empty($status = $this->httpStatus())) {
@ -216,7 +212,7 @@ abstract class Page extends Model
*
* @param string @val
*/
public function setTitles($val)
public function settitles($val)
{
if (empty($this->a['titles'])) {
$this->a['titles'] = [$val];

View file

@ -59,7 +59,7 @@ class Statistics extends Admin
}
// Get number of current visitors
$this->numOnline = $this->c->DB->query('SELECT COUNT(user_id) FROM ::online WHERE idle=0')->fetchColumn();
$this->numOnline = $this->c->Online->calc($this)->all;
$stat = $this->c->DB->statistics();
$this->dbVersion = $stat['db'];

View file

@ -2,10 +2,13 @@
namespace ForkBB\Models\Pages;
use ForkBB\Models\Model;
trait CrumbTrait
{
/**
* Возвращает массив хлебных крошек
* Заполняет массив титула страницы
*
* @param mixed $args
*
@ -17,84 +20,31 @@ trait CrumbTrait
$active = true;
foreach ($args as $arg) {
if (isset($arg->forum_name)) {
while ($arg->id > 0) {
$this->titles = $arg->forum_name;
$crumbs[] = [
$this->c->Router->link('Forum', ['id' => $arg->id, 'name' => $arg->forum_name]),
$arg->forum_name,
$active,
];
// Раздел или топик
if ($arg instanceof Model) {
while (null !== $arg->parent && $arg->link) {
if (isset($arg->forum_name)) {
$name = $arg->forum_name;
} elseif (isset($arg->subject)) {
$name = $arg->cens()->subject;
} else {
$name = 'no name';
}
$this->titles = $name;
$crumbs[] = [$arg->link, $name, $active];
$active = null;
$arg = $arg->parent;
}
// Строка
} else {
$this->titles = (string) $arg;
$crumbs[] = [
null,
(string) $arg,
$active,
];
$crumbs[] = [null, (string) $arg, $active];
}
/*
if (is_array($arg)) {
$cur = array_shift($arg);
// массив разделов
if (is_array($cur)) {
$id = $arg[0];
while (true) {
$this->titles = $cur[$id]['forum_name'];
$crumbs[] = [
$this->c->Router->link('Forum', ['id' => $id, 'name' => $cur[$id]['forum_name']]),
$cur[$id]['forum_name'],
$active,
];
$active = null;
if (! isset($cur[$id][0])) {
break;
}
$id = $cur[$id][0];
}
// отдельная страница
} else {
// определение названия
if (isset($arg[1])) {
$vars = $arg[0];
$name = $arg[1];
} elseif (is_string($arg[0])) {
$vars = [];
$name = $arg[0];
} elseif (isset($arg[0]['name'])) {
$vars = $arg[0];
$name = $arg[0]['name'];
} else {
continue;
}
$this->titles = $name;
$crumbs[] = [
$this->c->Router->link($cur, $vars),
$name,
$active,
];
}
// предположительно идет только название, без ссылки
} else {
$this->titles = (string) $arg;
$crumbs[] = [
null,
(string) $arg,
$active,
];
}
*/
$active = null;
}
// главная страница
$crumbs[] = [
$this->c->Router->link('Index'),
__('Index'),
$active,
];
$crumbs[] = [$this->c->Router->link('Index'), __('Index'), $active];
return array_reverse($crumbs);
}

View file

@ -6,7 +6,6 @@ use ForkBB\Models\Page;
class Forum extends Page
{
use ForumsTrait;
use CrumbTrait;
/**
@ -29,143 +28,37 @@ class Forum extends Page
return $this->c->Redirect->url($forum->redirect_url);
}
$user = $this->c->user;
$page = isset($args['page']) ? (int) $args['page'] : 1;
if (empty($forum->num_topics)) {
// попытка открыть страницу которой нет
if ($page !== 1) {
return $this->c->Message->message('Bad request');
}
$pages = 1;
$offset = 0;
$topics = null;
} else {
$pages = ceil($forum->num_topics / $user->disp_topics);
// попытка открыть страницу которой нет
if ($page < 1 || $page > $pages) {
return $this->c->Message->message('Bad request');
}
$offset = ($page - 1) * $user->disp_topics;
switch ($forum->sort_by) {
case 1:
$sortBy = 'posted DESC';
break;
case 2:
$sortBy = 'subject ASC';
break;
case 0:
default:
$sortBy = 'last_post DESC';
break;
}
$vars = [
':fid' => $args['id'],
':offset' => $offset,
':rows' => $user->disp_topics,
];
$topics = $this->c->DB
->query("SELECT id FROM ::topics WHERE forum_id=?i:fid ORDER BY sticky DESC, {$sortBy}, id DESC LIMIT ?i:offset, ?i:rows", $vars)
->fetchAll(\PDO::FETCH_COLUMN);
if (! $forum->hasPage($page)) {
return $this->c->Message->message('Bad request');
}
if (! empty($topics)) {
$vars = [
':uid' => $user->id,
':topics' => $topics,
];
$topics = $forum->topics();
$user = $this->c->user;
if (! $user->isGuest && $this->c->config->o_show_dot == '1') {
$dots = $this->c->DB
->query('SELECT topic_id FROM ::posts WHERE poster_id=?i:uid AND topic_id IN (?ai:topics) GROUP BY topic_id', $vars)
->fetchAll(\PDO::FETCH_COLUMN);
$dots = array_flip($dots);
} else {
$dots = [];
}
if (! $user->isGuest) {
$lower = max((int) $user->u_mark_all_read, (int) $forum->mf_mark_all_read);
$upper = max($lower, (int) $user->last_visit);
}
if ($user->isGuest) {
$sql = "SELECT id, poster, subject, posted, last_post, last_post_id, last_poster, num_views, num_replies, closed, sticky, moved_to, poll_type FROM ::topics WHERE id IN(?ai:topics) ORDER BY sticky DESC, {$sortBy}, id DESC";
} else {
$sql = "SELECT t.id, t.poster, t.subject, t.posted, t.last_post, t.last_post_id, t.last_poster, t.num_views, t.num_replies, t.closed, t.sticky, t.moved_to, t.poll_type, mot.mt_last_visit, mot.mt_last_read FROM ::topics AS t LEFT JOIN ::mark_of_topic AS mot ON (mot.uid=?i:uid AND t.id=mot.tid) WHERE t.id IN (?ai:topics) ORDER BY t.sticky DESC, t.{$sortBy}, t.id DESC";
}
$topics = $this->c->DB->query($sql, $vars)->fetchAll();
foreach ($topics as &$cur) {
$cur['subject'] = $this->c->censorship->censor($cur['subject']);
// перенос темы
if ($cur['moved_to']) {
$cur['link'] = $this->c->Router->link('Topic', ['id' => $cur['moved_to'], 'name' => $cur['subject']]);
$cur['link_last'] = null;
$cur['link_new'] = null;
$cur['link_unread'] = null;
$cur['dot'] = false;
continue;
}
// страницы темы
$tPages = ceil(($cur['num_replies'] + 1) / $user->disp_posts);
if ($tPages > 1) {
$cur['pages'] = $this->c->Func->paginate($tPages, -1, 'Topic', ['id' => $cur['id'], 'name' => $cur['subject']]);
} else {
$cur['pages'] = null;
}
$cur['link'] = $this->c->Router->link('Topic', ['id' => $cur['id'], 'name' => $cur['subject']]);
$cur['link_last'] = $this->c->Router->link('ViewPost', ['id' => $cur['last_post_id']]);
$cur['views'] = $this->c->config->o_topic_views == '1' ? $this->number($cur['num_views']) : null;
$cur['replies'] = $this->number($cur['num_replies']);
$time = $cur['last_post'];
$cur['last_post'] = $this->time($cur['last_post']);
// для гостя пусто
if ($user->isGuest) {
$cur['link_new'] = null;
$cur['link_unread'] = null;
$cur['dot'] = false;
continue;
}
// новые сообщения
if ($time > max($upper, (int) $cur['mt_last_visit'])) {
$cur['link_new'] = $this->c->Router->link('TopicViewNew', ['id' => $cur['id']]);
} else {
$cur['link_new'] = null;
}
// не прочитанные сообщения
if ($time > max($lower, (int) $cur['mt_last_read'])) {
$cur['link_unread'] = $this->c->Router->link('TopicViewUnread', ['id' => $cur['id']]);
} else {
$cur['link_unread'] = null;
}
// активность пользователя в теме
$cur['dot'] = isset($dots[$cur['id']]);
}
unset($cur);
if (! $user->isGuest) {
$lower = max((int) $user->u_mark_all_read, (int) $forum->mf_mark_all_read);
$upper = max($lower, (int) $user->last_visit);
}
$moders = empty($forum->moderators) ? [] : array_flip(unserialize($forum->moderators));
if (empty($topics)) {
$this->a['fIswev']['i'][] = __('Empty forum');
}
$newOn = $forum->post_topics == 1
|| (null === $forum->post_topics && $user->g_post_topics == 1)
|| $user->isAdmin
|| ($user->isAdmMod && isset($moders[$user->id]));
|| ($user->isAdmMod && isset($forum->moderators[$user->id]));
$this->fIndex = 'index';
$this->nameTpl = 'forum';
$this->onlinePos = 'forum-' . $args['id'];
$this->canonical = $this->c->Router->link('Forum', ['id' => $args['id'], 'name' => $forum->forum_name, 'page' => $page]);
$this->forums = $this->forumsData($args['id']);
$this->canonical = $this->c->Router->link('Forum', ['id' => $args['id'], 'name' => $forum->forum_name, 'page' => $forum->page]);
$this->forum = $forum;
$this->forums = $forum->subforums;
$this->topics = $topics;
$this->crumbs = $this->crumbs($forum);
$this->forumName = $forum->forum_name;
$this->newTopic = $newOn ? $this->c->Router->link('NewTopic', ['id' => $args['id']]) : null;
$this->pages = $this->c->Func->paginate($pages, $page, 'Forum', ['id' => $args['id'], 'name' => $forum->forum_name]);
$this->pages = $this->c->Func->paginate($forum->pages, $forum->page, 'Forum', ['id' => $args['id'], 'name' => $forum->forum_name]);
return $this;
}

View file

@ -1,102 +0,0 @@
<?php
namespace ForkBB\Models\Pages;
trait ForumsTrait
{
/**
* Получение данных по разделам
*
* @param int $rootId
*
* @return array
*/
protected function forumsData($rootId = 0)
{
$root = $this->c->forums->loadTree($rootId);
if (empty($root)) {
return [];
}
$r = $this->c->Router;
// формированием таблицы разделов
$result = [];
foreach ($root->subforums as $forumId => $forum) {
// модераторы
$moderators = [];
if (! empty($forum->moderators)) {
$mods = unserialize($forum->moderators);
foreach ($mods as $name => $id) {
if ($this->c->user->g_view_users == '1') {
$moderators[] = [
$r->link('User', [
'id' => $id,
'name' => $name,
]),
$name
];
} else {
$moderators[] = $name;
}
}
}
// список подразделов
$subForums = [];
foreach ($forum->subforums as $subId => $subforum) {
$subForums[] = [
$r->link('Forum', [
'id' => $subId,
'name' => $subforum->forum_name,
]),
$subforum->forum_name,
];
}
// статистика по разделам
$numT = (int) $forum->num_topics;
$numP = (int) $forum->num_posts;
$time = (int) $forum->last_post;
$postId = (int) $forum->last_post_id;
$poster = $forum->last_poster;
$topic = $forum->last_topic;
$fnew = $forum->newMessages;
foreach ($forum->descendants as $chId => $children) {
$fnew = $fnew || $children->newMessages;
$numT += $children->num_topics;
$numP += $children->num_posts;
if ($children->last_post > $time) {
$time = $children->last_post;
$postId = $children->last_post_id;
$poster = $children->last_poster;
$topic = $children->last_topic;
}
}
$result[$forum->cid]['name'] = $forum->cat_name;
$result[$forum->cid]['forums'][] = [
'fid' => $forumId,
'forum_name' => $forum->forum_name,
'forum_desc' => $forum->forum_desc,
'forum_link' => $r->link('Forum', [
'id' => $forumId,
'name' => $forum->forum_name,
]),
'redirect_url' => $forum->redirect_url,
'subforums' => $subForums,
'moderators' => $moderators,
'num_topics' => $numT,
'num_posts' => $numP,
'topics' => $this->number($numT),
'posts' => $this->number($numP),
'last_post' => $this->time($time),
'last_post_id' => $postId > 0 ? $r->link('ViewPost', ['id' => $postId]) : null,
'last_poster' => $poster,
'last_topic' => $this->c->censorship->censor($topic),
'new' => $fnew,
];
}
return $result;
}
}

View file

@ -6,9 +6,6 @@ use ForkBB\Models\Page;
class Index extends Page
{
use ForumsTrait;
use OnlineTrait;
/**
* Подготовка данных для шаблона
*
@ -19,31 +16,35 @@ class Index extends Page
$this->c->Lang->load('index');
$this->c->Lang->load('subforums');
$stats = [];
$stats['total_users'] = $this->number($this->c->stats->userTotal);
$stats['total_posts'] = $this->number($this->c->stats->postTotal);
$stats['total_topics'] = $this->number($this->c->stats->topicTotal);
if ($this->c->user->g_view_users == '1') {
$stats['newest_user'] = [
$this->c->Router->link('User', [
// крайний пользователь // ???? может в stats переместить?
$this->c->stats->userLast = $this->c->user->g_view_users == '1'
? [ $this->c->Router->link('User', [
'id' => $this->c->stats->userLast['id'],
'name' => $this->c->stats->userLast['username'],
]),
$this->c->stats->userLast['username']
];
$this->c->stats->userLast['username'],
] : $this->c->stats->userLast['username'];
// для таблицы разделов
$root = $this->c->forums->loadTree(0);
$forums = empty($root) ? [] : $root->subforums;
$ctgs = [];
if (empty($forums)) {
$this->a['fIswev']['i'][] = __('Empty board');
} else {
$stats['newest_user'] = $this->c->stats->userLast['username'];
foreach($forums as $forum) {
$ctgs[$forum->cat_id][] = $forum;
}
}
$this->nameTpl = 'index';
$this->onlinePos = 'index';
$this->onlineType = true;
$this->onlineDetail = true;
$this->onlineFilter = false;
$this->canonical = $this->c->Router->link('Index');
$this->stats = $stats;
$this->online = $this->usersOnlineInfo();
$this->forums = $this->forumsData();
$this->stats = $this->c->stats;
$this->online = $this->c->Online->calc($this)->info();
$this->categoryes = $ctgs;
return $this;
}

View file

@ -483,7 +483,7 @@ class Install extends Page
'user_id' => ['INT(10) UNSIGNED', false, 1],
'ident' => ['VARCHAR(200)', false, ''],
'logged' => ['INT(10) UNSIGNED', false, 0],
'idle' => ['TINYINT(1)', false, 0],
'idle' => ['TINYINT(1)', false, 0], //????
'last_post' => ['INT(10) UNSIGNED', true],
'last_search' => ['INT(10) UNSIGNED', true],
'witt_data' => ['VARCHAR(255)', false, ''], //????
@ -496,7 +496,7 @@ class Install extends Page
'INDEXES' => [
'ident_idx' => ['ident'],
'logged_idx' => ['logged'],
'o_position_idx' => ['o_position'],
'o_position_idx' => ['o_position'], //????
],
'ENGINE' => $this->DBEngine,
];

View file

@ -1,62 +0,0 @@
<?php
namespace ForkBB\Models\Pages;
trait OnlineTrait
{
/**
* Получение информации об онлайн посетителях
* @return null|array
*/
protected function usersOnlineInfo()
{
if ($this->c->config->o_users_online == '1') {
// данные онлайн посетителей
$this->c->Online->calc($this);
$users = $this->c->Online->users; //????
$guests = $this->c->Online->guests;
$bots = $this->c->Online->bots;
$list = [];
$data = [
'max' => $this->number($this->c->config->st_max_users),
'max_time' => $this->time($this->c->config->st_max_users_time),
];
if ($this->c->user->g_view_users == '1') {
foreach ($users as $id => $cur) {
$list[] = [
$this->c->Router->link('User', [
'id' => $id,
'name' => $cur['name'],
]),
$cur['name'],
];
}
} else {
foreach ($users as $cur) {
$list[] = $cur['name'];
}
}
$data['number_of_users'] = $this->number(count($users));
$s = 0;
foreach ($bots as $name => $cur) {
$count = count($cur);
$s += $count;
if ($count > 1) {
$list[] = '[Bot] ' . $name . ' (' . $count . ')';
} else {
$list[] = '[Bot] ' . $name;
}
}
$s += count($guests);
$data['number_of_guests'] = $this->number($s);
$data['list'] = $list;
return $data;
} else {
$this->onlineType = false;
return null;
}
}
}

View file

@ -6,16 +6,8 @@ use ForkBB\Models\Page;
class Topic extends Page
{
use UsersTrait;
use OnlineTrait;
use CrumbTrait;
/**
* Данные по текущей теме
* @var array
*/
protected $curTopic;
/**
* Переход к первому новому сообщению темы (или в конец)
*
@ -25,35 +17,7 @@ class Topic extends Page
*/
public function viewNew(array $args)
{
$topic = $this->curTopic($args['id']);
if (false === $topic) {
return $this->c->Message->message('Bad request');
}
if (! $this->c->user->isGuest) {
$upper = max(
(int) $this->c->user->last_visit,
(int) $this->c->user->u_mark_all_read,
(int) $topic['mf_mark_all_read'],
(int) $topic['mt_last_visit']
);
if ($upper < $topic['last_post']) {
$vars = [
':tid' => $args['id'],
':visit' => $upper,
];
$sql = 'SELECT MIN(id) FROM ::posts WHERE topic_id=?i:tid AND posted>?i:visit';
$pid = $this->c->DB->query($sql, $vars)->fetchColumn();
if (! empty($pid)) {
return $this->c->Redirect->page('ViewPost', ['id' => $pid]);
}
}
}
return $this->viewLast(['id' => $topic['id']]);
return $this->view('new', $args);
}
/**
@ -65,34 +29,7 @@ class Topic extends Page
*/
public function viewUnread(array $args)
{
$topic = $this->curTopic($args['id']);
if (false === $topic) {
return $this->c->Message->message('Bad request');
}
if (! $this->c->user->isGuest) {
$lower = max(
(int) $this->c->user->u_mark_all_read,
(int) $topic['mf_mark_all_read'],
(int) $topic['mt_last_read']
);
if ($lower < $topic['last_post']) {
$vars = [
':tid' => $args['id'],
':visit' => $lower,
];
$sql = 'SELECT MIN(id) FROM ::posts WHERE topic_id=?i:tid AND posted>?i:visit';
$pid = $this->c->DB->query($sql, $vars)->fetchColumn();
if (! empty($pid)) {
return $this->c->Redirect->page('ViewPost', ['id' => $pid]);
}
}
}
return $this->viewLast(['id' => $topic['id']]);
return $this->view('unread', $args);
}
/**
@ -104,100 +41,9 @@ class Topic extends Page
*/
public function viewLast(array $args)
{
$topic = $this->curTopic($args['id']);
if (false === $topic) {
return $this->c->Message->message('Bad request');
}
return $this->view('last', $args);
}
$vars = [
':tid' => $args['id'],
];
$sql = 'SELECT MAX(id) FROM ::posts WHERE topic_id=?i:tid';
$pid = $this->c->DB->query($sql, $vars)->fetchColumn();
// нет ни одного сообщения в теме
if (empty($pid)) {
return $this->c->Message->message('Bad request');
}
return $this->c->Redirect->page('ViewPost', ['id' => $pid]);
}
/**
* Получение данных по текущей теме
*
* @param mixed $id
* @param mixed $pid
*
* @return bool|array
*/
protected function curTopic($id, $pid = null)
{
if ($this->curTopic) {
return $this->curTopic;
}
if (isset($pid)) {
$vars = [
':pid' => $pid,
':uid' => $this->c->user->id,
];
if ($this->c->user->isGuest) {
$sql = 'SELECT t.*, f.moderators, 0 AS is_subscribed, 0 AS mf_mark_all_read, 0 AS mt_last_visit, 0 AS mt_last_read
FROM ::topics AS t
INNER JOIN ::forums AS f ON f.id=t.forum_id
INNER JOIN ::posts AS p ON t.id=p.topic_id
WHERE p.id=?i:pid AND t.moved_to IS NULL';
} else {
$sql = 'SELECT t.*, f.moderators, s.user_id AS is_subscribed, mof.mf_mark_all_read, mot.mt_last_visit, mot.mt_last_read
FROM ::topics AS t
INNER JOIN ::forums AS f ON f.id=t.forum_id
INNER JOIN ::posts AS p ON t.id=p.topic_id
LEFT JOIN ::topic_subscriptions AS s ON (t.id=s.topic_id AND s.user_id=?i:uid)
LEFT JOIN ::mark_of_forum AS mof ON (mof.uid=?i:uid AND f.id=mof.fid)
LEFT JOIN ::mark_of_topic AS mot ON (mot.uid=?i:uid AND t.id=mot.tid)
WHERE p.id=?i:pid AND t.moved_to IS NULL';
}
} else {
$vars = [
':tid' => $id,
':uid' => $this->c->user->id,
];
if ($this->c->user->isGuest) {
$sql = 'SELECT t.*, f.moderators, 0 AS is_subscribed, 0 AS mf_mark_all_read, 0 AS mt_last_visit, 0 AS mt_last_read
FROM ::topics AS t
INNER JOIN ::forums AS f ON f.id=t.forum_id
WHERE t.id=?i:tid AND t.moved_to IS NULL';
} else {
$sql = 'SELECT t.*, f.moderators, s.user_id AS is_subscribed, mof.mf_mark_all_read, mot.mt_last_visit, mot.mt_last_read
FROM ::topics AS t
INNER JOIN ::forums AS f ON f.id=t.forum_id
LEFT JOIN ::topic_subscriptions AS s ON (t.id=s.topic_id AND s.user_id=?i:uid)
LEFT JOIN ::mark_of_forum AS mof ON (mof.uid=?i:uid AND f.id=mof.fid)
LEFT JOIN ::mark_of_topic AS mot ON (mot.uid=?i:uid AND t.id=mot.tid)
WHERE t.id=?i:tid AND t.moved_to IS NULL';
}
}
$topic = $this->c->DB->query($sql, $vars)->fetch();
// тема отсутствует или недоступна
if (empty($topic)) {
return false;
}
list($fTree, $fDesc, $fAsc) = $this->c->forums;
// раздел отсутствует в доступных
if (empty($fDesc[$topic['forum_id']])) {
return false;
}
$this->curTopic = $topic;
return $topic;
}
/**
* Просмотр темы по номеру сообщения
@ -208,11 +54,7 @@ class Topic extends Page
*/
public function viewPost(array $args)
{
$topic = $this->curTopic(null, $args['id']);
if (false === $topic) {
return $this->c->Message->message('Bad request');
}
return $this->view($topic, $args['id']);
return $this->view('post', $args);
}
/**
@ -224,65 +66,83 @@ class Topic extends Page
*/
public function viewTopic(array $args)
{
$topic = $this->curTopic($args['id']);
if (false === $topic) {
return $this->c->Message->message('Bad request');
return $this->view('topic', $args);
}
/**
* @param string $type
* @param Models\Topic $topic
*
* @param Page
*/
protected function go($type, $topic)
{
switch ($type) {
case 'new':
$pid = $topic->post_new;
break;
case 'unread':
$pid = $topic->post_unread;
break;
case 'last':
$pid = $topic->last_post_id;
break;
default:
return $this->c->Message->message('Bad request');
}
if ($pid) {
return $this->c->Redirect->page('ViewPost', ['id' => $pid]);
} else {
return $this->c->Redirect->page('ViewPost', ['id' => $topic->last_post_id]);
}
$page = isset($args['page']) ? (int) $args['page'] : 1;
return $this->view($topic, null, $page);
}
/**
* Подготовка данных для шаблона
*
* @param array $topic
* @param int|null $pid
* @param int|null $page
* @param string $type
* @param array $args
*
* @return Page
*/
protected function view(array $topic, $pid, $page = null)
{
$user = $this->c->user;
if (null === $page) {
$vars = [
':tid' => $topic['id'],
':pid' => $pid,
];
$sql = 'SELECT COUNT(id) FROM ::posts WHERE topic_id=?i:tid AND id<?i:pid';
$num = 1 + $this->c->DB->query($sql, $vars)->fetchColumn();
$page = ceil($num / $user->disp_posts);
protected function view($type, array $args)
{
$topic = $this->c->ModelTopic->load((int) $args['id'], $type === 'post');
if (! $topic->id || ! $topic->last_post_id) {
return $this->c->Message->message('Bad request');
}
if ($topic->moved_to) {
return $this->c->Redirect->page('Topic', ['id' => $topic->moved_to]);
}
$pages = ceil(($topic['num_replies'] + 1) / $user->disp_posts);
// попытка открыть страницу которой нет
if ($page < 1 || $page > $pages) {
switch ($type) {
case 'topic':
$topic->page = isset($args['page']) ? (int) $args['page'] : 1;
break;
case 'post':
$topic->calcPage((int) $args['id']);
break;
default:
return $this->go($type, $topic);
}
if (! $topic->hasPage()) {
return $this->c->Message->message('Bad request');
}
$offset = ($page - 1) * $user->disp_posts;
$vars = [
':tid' => $topic['id'],
':offset' => $offset,
':rows' => $user->disp_posts,
];
$sql = 'SELECT id
FROM ::posts
WHERE topic_id=?i:tid
ORDER BY id LIMIT ?i:offset, ?i:rows';
$ids = $this->c->DB->query($sql, $vars)->fetchAll(\PDO::FETCH_COLUMN);
// нарушена синхронизация количества сообщений в темах
if (empty($ids)) {
return $this->viewLast(['id' => $topic['id']]);
if (! $posts = $topic->posts()) {
return $this->go('last', $topic);
}
$this->c->Lang->load('topic');
$user = $this->c->user;
/*
list($fTree, $fDesc, $fAsc) = $this->c->forums;
$moders = empty($topic['moderators']) ? [] : array_flip(unserialize($topic['moderators']));
@ -305,35 +165,6 @@ class Topic extends Page
$newOn = true;
}
// приклейка первого сообщения темы
$stickFP = (! empty($topic['stick_fp']) || ! empty($topic['poll_type']));
if ($stickFP) {
$ids[] = $topic['first_post_id'];
}
$vars = [
':ids' => $ids,
];
$sql = 'SELECT id, message, poster, posted
FROM ::warnings
WHERE id IN (?ai:ids)';
$warnings = $this->c->DB->query($sql, $vars)->fetchAll(\PDO::FETCH_GROUP);
$vars = [
':ids' => $ids,
];
$sql = 'SELECT u.warning_all, u.gender, u.email, u.title, u.url, u.location, u.signature,
u.email_setting, u.num_posts, u.registered, u.admin_note, u.messages_enable,
p.id, p.poster as username, p.poster_id, p.poster_ip, p.poster_email, p.message,
p.hide_smilies, p.posted, p.edited, p.edited_by, p.edit_post, p.user_agent,
g.g_id, g.g_user_title, g.g_promote_next_group, g.g_pm
FROM ::posts AS p
INNER JOIN ::users AS u ON u.id=p.poster_id
INNER JOIN ::groups AS g ON g.g_id=u.group_id
WHERE p.id IN (?ai:ids) ORDER BY p.id';
$stmt = $this->c->DB->query($sql, $vars);
// парсер и его настройка для сообщений
$bbcodes = include $this->c->DIR_CONFIG . '/defaultBBCode.php';
@ -489,14 +320,14 @@ class Topic extends Page
$topic['subject'] = $this->c->censorship->censor($topic['subject']);
*/
// данные для формы быстрого ответа
$form = null;
if ($newOn && $this->c->config->o_quickpost == '1') {
if ($topic->post_replies && $this->c->config->o_quickpost == '1') {
$form = [
'action' => $this->c->Router->link('NewReply', ['id' => $topic['id']]),
'action' => $this->c->Router->link('NewReply', ['id' => $topic->id]),
'hidden' => [
'token' => $this->c->Csrf->create('NewReply', ['id' => $topic['id']]),
'token' => $this->c->Csrf->create('NewReply', ['id' => $topic->id]),
],
'sets' => [],
'btns' => [
@ -557,54 +388,48 @@ class Topic extends Page
}
}
$this->nameTpl = 'topic';
$this->onlinePos = 'topic-' . $topic['id'];
$this->onlineType = true;
$this->canonical = $this->c->Router->link('Topic', ['id' => $topic['id'], 'name' => $topic['subject'], 'page' => $page]);
$this->topic = $topic;
$this->posts = $posts;
$this->signs = $signs;
$this->warnings = $warnings;
$this->crumbs = $this->crumbs(
['Topic', ['id' => $topic['id'], 'name' => $topic['subject']]],
[$fDesc, $topic['forum_id']]
);
$this->NewReply = $newOn ? $this->c->Router->link('NewReply', ['id' => $topic['id']]) : $newOn;
$this->stickFP = $stickFP;
$this->pages = $this->c->Func->paginate($pages, $page, 'Topic', ['id' => $topic['id'], 'name' => $topic['subject']]);
$this->online = $this->usersOnlineInfo();
$this->stats = null;
$this->form = $form;
$this->nameTpl = 'topic';
$this->onlinePos = 'topic-' . $topic->id;
$this->onlineDetail = true;
$this->canonical = $this->c->Router->link('Topic', ['id' => $topic->id, 'name' => $topic->cens()->subject, 'page' => $topic->page]);
$this->topic = $topic;
$this->posts = $posts;
$this->crumbs = $this->crumbs($topic);
$this->NewReply = $topic->post_replies ? $this->c->Router->link('NewReply', ['id' => $topic->id]) : null;
$this->pages = $topic->pages;
$this->online = $this->c->Online->calc($this)->info();
$this->stats = null;
$this->form = $form;
if ($this->c->config->o_topic_views == '1') {
$vars = [
':tid' => $topic['id'],
':tid' => $topic->id,
];
$sql = 'UPDATE ::topics SET num_views=num_views+1 WHERE id=?i:tid';
$this->c->DB->query($sql, $vars);
}
/*
if (! $user->isGuest) {
$vars = [
':uid' => $user->id,
':tid' => $topic['id'],
':read' => $topic['mt_last_read'],
':visit' => $topic['mt_last_visit'],
':tid' => $topic->id,
':read' => $topic->mt_last_read,
':visit' => $topic->mt_last_visit,
];
$flag = false;
$lower = max((int) $user->u_mark_all_read, (int) $topic['mf_mark_all_read'], (int) $topic['mt_last_read']); //????
$lower = max((int) $user->u_mark_all_read, (int) $topic->mf_mark_all_read, (int) $topic->mt_last_read); //????
if ($timeMax > $lower) {
$vars[':read'] = $timeMax;
$flag = true;
}
$upper = max($lower, (int) $topic['mt_last_visit'], (int) $user->last_visit); //????
if ($topic['last_post'] > $upper) {
$vars[':visit'] = $topic['last_post'];
$upper = max($lower, (int) $topic->mt_last_visit, (int) $user->last_visit); //????
if ($topic->last_post > $upper) {
$vars[':visit'] = $topic->last_post;
$flag = true;
}
if ($flag) {
if (empty($topic['mt_last_read']) && empty($topic['mt_last_visit'])) {
if (empty($topic->mt_last_read) && empty($topic->mt_last_visit)) {
$this->c->DB->exec('INSERT INTO ::mark_of_topic (uid, tid, mt_last_visit, mt_last_read)
SELECT ?i:uid, ?i:tid, ?i:visit, ?i:read
FROM ::groups
@ -619,7 +444,7 @@ class Topic extends Page
}
}
}
*/
return $this;
}
}

View file

@ -1,61 +0,0 @@
<?php
namespace ForkBB\Models\Pages;
trait UsersTrait
{
/**
* Имена забаненных пользователей
*
* @var array
*/
protected $userBanNames;
/**
* Определение титула для пользователя
*
* @param array $data
*
* @return string
*/
protected function userGetTitle(array $data)
{
if (! isset($this->userBanNames)) {
$this->userBanNames = $this->c->bans->userList; //????
}
if (isset($this->userBanNames[mb_strtolower($data['username'])])) { //????
return __('Banned');
} elseif ($data['title'] != '') {
return $data['title'];
} elseif ($data['g_user_title'] != '') {
return $data['g_user_title'];
} elseif ($data['g_id'] == $this->c->GROUP_GUEST) {
return __('Guest');
} else {
return __('Member');
}
}
/**
* Определение ссылки на аватарку
*
* @param int $id
*
* @return string|null
*/
protected function userGetAvatarLink($id)
{
$filetypes = array('jpg', 'gif', 'png');
foreach ($filetypes as $type) {
$path = $this->c->DIR_PUBLIC . "/{$this->c->config->o_avatars_dir}/{$id}.{$type}";
if (file_exists($path) && getimagesize($path)) {
return $this->c->PUBLIC_URL . "/{$this->c->config->o_avatars_dir}/{$id}.{$type}";
}
}
return null;
}
}

80
app/Models/Post.php Normal file
View file

@ -0,0 +1,80 @@
<?php
namespace ForkBB\Models;
use ForkBB\Models\DataModel;
use ForkBB\Core\Container;
use RuntimeException;
class Post extends DataModel
{
protected function getlink()
{
return $this->c->Router->link('ViewPost', ['id' => $this->id]);
}
protected function getuser()
{
$attrs = $this->a; //????
$attrs['id'] = $attrs['poster_id'];
return $this->c->ModelUser->setAttrs($attrs);
}
protected function getshowUserLink()
{
return $this->c->user->g_view_users == '1';
}
protected function getshowUserAvatar()
{
return $this->c->config->o_avatars == '1' && $this->c->user->show_avatars == '1';
}
protected function getshowUserInfo()
{
return $this->c->config->o_show_user_info == '1';
}
protected function getshowSignature()
{
return $this->c->config->o_signatures == '1' && $this->c->user->show_sig == '1';
}
protected function getcontrols()
{
$user = $this->c->user;
$controls = [];
$vars = ['id' => $this->id];
if (! $user->isAdmin && ! $user->isGuest) {
$controls['report'] = [$this->c->Router->link('ReportPost', $vars), 'Report'];
}
if ($user->isAdmin
|| ($user->isAdmMod
&& ! $this->user->isAdmin
&& isset($this->parent->parent->moderators[$user->id])
)
) {
$controls['delete'] = [$this->c->Router->link('DeletePost', $vars), 'Delete'];
$controls['edit'] = [$this->c->Router->link('EditPost', $vars), 'Edit'];
} elseif ($this->parent->closed != '1'
&& $this->user->id == $user->id
&& ($user->g_deledit_interval == '0'
|| $this->edit_post == '1'
|| time() - $this->posted < $user->g_deledit_interval
)
) {
if (($this->id == $this->parent->first_post_id && $user->g_delete_topics == '1')
|| ($this->id != $this->parent->first_post_id && $user->g_delete_posts == '1')
) {
$controls['delete'] = [$this->c->Router->link('DeletePost', $vars), 'Delete'];
}
if ($user->g_edit_posts == '1') {
$controls['edit'] = [$this->c->Router->link('EditPost', $vars), 'Edit'];
}
}
if ($this->parent->post_replies) {
$controls['quote'] = [$this->c->Router->link('NewReply', ['id' => $this->parent->id, 'quote' => $this->id]), 'Reply'];
}
return $controls;
}
}

View file

@ -3,6 +3,7 @@
namespace ForkBB\Models;
use ForkBB\Models\Model;
use PDO;
class Stats extends Model
{
@ -21,9 +22,7 @@ class Stats extends Model
$this->load();
}
list($topics, $posts) = $this->c->DB->query('SELECT SUM(num_topics), SUM(num_posts) FROM ::forums')->fetch(\PDO::FETCH_NUM);
$this->postTotal = $posts;
$this->topicTotal = $topics;
list($this->topicTotal, $this->postTotal) = $this->c->DB->query('SELECT SUM(num_topics), SUM(num_posts) FROM ::forums')->fetch(PDO::FETCH_NUM);
return $this;
}

274
app/Models/Topic.php Normal file
View file

@ -0,0 +1,274 @@
<?php
namespace ForkBB\Models;
use ForkBB\Models\DataModel;
use ForkBB\Core\Container;
use RuntimeException;
class Topic extends DataModel
{
protected function getpost_replies()
{
if ($this->c->user->isAdmin) {
return true;
} elseif ($this->closed || $this->c->user->isBot) {
return false;
} elseif ($this->parent->post_replies == '1'
|| (null === $this->parent->post_replies && $this->c->user->g_post_replies == '1')
|| ($this->c->user->isAdmMod && isset($this->parent->moderators[$this->c->user->id]))
) {
return true;
} else {
return false;
}
}
protected function getnum_views()
{
return $this->c->config->o_topic_views == '1' ? $this->a['num_views'] : null;
}
protected function getparent()
{
return $this->c->forums->forum($this->forum_id);
}
protected function getlink()
{
if ($this->moved_to) {
return $this->c->Router->link('Topic', ['id' => $this->moved_to, 'name' => $this->cens()->subject]);
} else {
return $this->c->Router->link('Topic', ['id' => $this->id, 'name' => $this->cens()->subject]);
}
}
protected function getlink_last()
{
if ($this->moved_to) {
return null;
} else {
return $this->c->Router->link('ViewPost', ['id' => $this->last_post_id]);
}
}
protected function getlink_new()
{
if ($this->c->user->isGuest || $this->moved_to) {
return null;
}
if ($this->last_post > max(
(int) $this->c->user->u_mark_all_read,
(int) $this->parent->mf_mark_all_read,
(int) $this->c->user->last_visit,
(int) $this->mt_last_visit)
) {
return $this->c->Router->link('TopicViewNew', ['id' => $this->id]);
} else {
return null;
}
}
protected function getpost_new()
{
if ($this->c->user->isGuest || $this->moved_to) {
return null;
}
$upper = max(
(int) $this->c->user->u_mark_all_read,
(int) $this->parent->mf_mark_all_read,
(int) $this->c->user->last_visit,
(int) $this->mt_last_visit
);
if ($this->last_post > $upper) {
$vars = [
':tid' => $this->id,
':visit' => $upper,
];
$sql = 'SELECT MIN(id) FROM ::posts WHERE topic_id=?i:tid AND posted>?i:visit';
$pid = $this->c->DB->query($sql, $vars)->fetchColumn();
if (! empty($pid)) {
return $pid;
}
}
return null;
}
protected function getlink_unread()
{
if ($this->c->user->isGuest || $this->moved_to) {
return null;
}
if ($this->last_post > max(
(int) $this->c->user->u_mark_all_read,
(int) $this->parent->mf_mark_all_read,
(int) $this->mt_last_read)
) {
return $this->c->Router->link('TopicViewUnread', ['id' => $this->id]);
} else {
return null;
}
}
protected function getpost_unread()
{
if ($this->c->user->isGuest || $this->moved_to) {
return null;
}
$lower = max(
(int) $this->c->user->u_mark_all_read,
(int) $this->parent->mf_mark_all_read,
(int) $this->mt_last_read
);
if ($this->last_post > $lower) {
$vars = [
':tid' => $this->id,
':visit' => $lower,
];
$sql = 'SELECT MIN(id) FROM ::posts WHERE topic_id=?i:tid AND posted>?i:visit';
$pid = $this->c->DB->query($sql, $vars)->fetchColumn();
if (! empty($pid)) {
return $pid;
}
}
return null;
}
protected function getnum_pages()
{
if (null === $this->num_replies) {
throw new RuntimeException('The model does not have the required data');
}
return (int) ceil(($this->num_replies + 1) / $this->c->user->disp_posts);
}
/**
* @returm array
*/
protected function getpages()
{
$page = (int) $this->page;
if ($page < 1 && $this->num_pages === 1) {
return [];
} else {
return $this->c->Func->paginate($this->num_pages, $page, 'Topic', ['id' => $this->id, 'name' => $this->cens()->subject]);
}
}
/**
* @return bool
*/
public function hasPage()
{
return $this->page > 0 && $this->page <= $this->num_pages;
}
/**
* @param int $pid
*/
public function calcPage($pid)
{
$vars = [
':tid' => $this->id,
':pid' => $pid,
];
$sql = 'SELECT COUNT(p.id) AS num, j.id AS flag
FROM ::posts AS p
INNER JOIN ::posts AS j ON (j.topic_id=?i:tid AND j.id=?i:pid)
WHERE p.topic_id=?i:tid AND p.id<?i:pid';
$result = $this->c->DB->query($sql, $vars)->fetch();
$this->page = empty($result['flag']) ? null : (int) ceil(($result['num'] + 1) / $this->c->user->disp_posts);
}
/**
* @return array
*/
public function posts()
{
if (! $this->hasPage()) {
throw new RuntimeException('The model does not have the required data');
}
$offset = ($this->page - 1) * $this->c->user->disp_posts;
$vars = [
':tid' => $this->id,
':offset' => $offset,
':rows' => $this->c->user->disp_posts,
];
$sql = 'SELECT id
FROM ::posts
WHERE topic_id=?i:tid
ORDER BY id LIMIT ?i:offset, ?i:rows';
$ids = $this->c->DB->query($sql, $vars)->fetchAll(\PDO::FETCH_COLUMN);
if (empty($ids)) {
return [];
}
// приклейка первого сообщения темы
if ($this->stick_fp || $this->poll_type) {
$ids[] = $this->first_post_id;
}
$vars = [
':ids' => $ids,
];
$sql = 'SELECT id, message, poster, posted
FROM ::warnings
WHERE id IN (?ai:ids)';
$warnings = $this->c->DB->query($sql, $vars)->fetchAll(\PDO::FETCH_GROUP);
$vars = [
':ids' => $ids,
];
$sql = 'SELECT u.warning_all, u.gender, u.email, u.title, u.url, u.location, u.signature,
u.email_setting, u.num_posts, u.registered, u.admin_note, u.messages_enable,
u.group_id,
p.id, p.poster as username, p.poster_id, p.poster_ip, p.poster_email, p.message,
p.hide_smilies, p.posted, p.edited, p.edited_by, p.edit_post, p.user_agent,
g.g_user_title, g.g_promote_next_group, g.g_pm
FROM ::posts AS p
INNER JOIN ::users AS u ON u.id=p.poster_id
INNER JOIN ::groups AS g ON g.g_id=u.group_id
WHERE p.id IN (?ai:ids) ORDER BY p.id';
$posts = $this->c->DB->query($sql, $vars)->fetchAll();
$postCount = 0;
$timeMax = 0;
foreach ($posts as &$cur) {
if ($cur['posted'] > $timeMax) {
$timeMax = $cur['posted'];
}
// номер сообшения в теме
if ($cur['id'] == $this->first_post_id && $offset > 0) {
$cur['postNumber'] = 1;
} else {
++$postCount;
$cur['postNumber'] = $offset + $postCount;
}
if (isset($warnings[$cur['id']])) {
$cur['warnings'] = $warnings[$cur['id']];
}
$cur['parent'] = $this;
$cur = $this->c->ModelPost->setAttrs($cur);
}
unset($cur);
return $posts;
}
}

93
app/Models/Topic/Load.php Normal file
View file

@ -0,0 +1,93 @@
<?php
namespace ForkBB\Models\Topic;
use ForkBB\Models\MethodModel;
class Load extends MethodModel
{
/**
* Заполняет модель данными из БД
*
* @param int $id
* @param bool $isPost
*
* @return Topic
*/
public function load($id, $isPost = false)
{
if ($isPost) {
$vars = [
':pid' => $id,
':uid' => $this->c->user->id,
];
if ($this->c->user->isGuest) {
$sql = 'SELECT t.*, f.moderators
FROM ::topics AS t
INNER JOIN ::forums AS f ON f.id=t.forum_id
INNER JOIN ::posts AS p ON t.id=p.topic_id
WHERE p.id=?i:pid';
} else {
$sql = 'SELECT t.*, f.moderators, s.user_id AS is_subscribed, mof.mf_mark_all_read, mot.mt_last_visit, mot.mt_last_read
FROM ::topics AS t
INNER JOIN ::forums AS f ON f.id=t.forum_id
INNER JOIN ::posts AS p ON t.id=p.topic_id
LEFT JOIN ::topic_subscriptions AS s ON (t.id=s.topic_id AND s.user_id=?i:uid)
LEFT JOIN ::mark_of_forum AS mof ON (mof.uid=?i:uid AND f.id=mof.fid)
LEFT JOIN ::mark_of_topic AS mot ON (mot.uid=?i:uid AND t.id=mot.tid)
WHERE p.id=?i:pid';
}
} else {
$vars = [
':tid' => $id,
':uid' => $this->c->user->id,
];
if ($this->c->user->isGuest) {
$sql = 'SELECT t.*, f.moderators
FROM ::topics AS t
INNER JOIN ::forums AS f ON f.id=t.forum_id
WHERE t.id=?i:tid';
} else {
$sql = 'SELECT t.*, f.moderators, s.user_id AS is_subscribed, mof.mf_mark_all_read, mot.mt_last_visit, mot.mt_last_read
FROM ::topics AS t
INNER JOIN ::forums AS f ON f.id=t.forum_id
LEFT JOIN ::topic_subscriptions AS s ON (t.id=s.topic_id AND s.user_id=?i:uid)
LEFT JOIN ::mark_of_forum AS mof ON (mof.uid=?i:uid AND f.id=mof.fid)
LEFT JOIN ::mark_of_topic AS mot ON (mot.uid=?i:uid AND t.id=mot.tid)
WHERE t.id=?i:tid';
}
}
$data = $this->c->DB->query($sql, $vars)->fetch();
// тема отсутствует или недоступна
if (empty($data)) {
return $this->emptyTopic();
}
$forForum['moderators'] = $data['moderators'];
unset($data['moderators']);
if (! $this->c->user->isGuest) {
$forForum['mf_mark_all_read'] = $data['mf_mark_all_read'];
unset($data['mf_mark_all_read']);
}
$this->model->setAttrs($data);
$forum = $this->model->parent;
// раздел не доступен
if (empty($forum)) {
return $this->emptyTopic();
}
$forum->replAttrs($forForum);
return $this->model;
}
protected function emptyTopic()
{
return $this->model->setAttrs([]);
}
}

View file

@ -16,49 +16,48 @@ class User extends DataModel
/**
* Конструктор
*
* @param array $data
* @param Container $container
*/
public function __construct(array $data = [], Container $container)
public function __construct(Container $container)
{
$this->now = time();
parent::__construct($data, $container);
parent::__construct($container);
}
protected function getIsUnverified()
protected function getisUnverified()
{
return $this->group_id == $this->c->GROUP_UNVERIFIED;
}
protected function getIsGuest()
protected function getisGuest()
{
return $this->group_id == $this->c->GROUP_GUEST
|| $this->id < 2
|| $this->group_id == $this->c->GROUP_UNVERIFIED;
}
protected function getIsAdmin()
protected function getisAdmin()
{
return $this->group_id == $this->c->GROUP_ADMIN;
}
protected function getIsAdmMod()
protected function getisAdmMod()
{
return $this->group_id == $this->c->GROUP_ADMIN
|| $this->g_moderator == '1';
}
protected function getLogged()
protected function getlogged()
{
return empty($this->a['logged']) ? $this->now : $this->a['logged'];
}
protected function getIsLogged()
protected function getisLogged()
{
return ! empty($this->a['logged']);
}
protected function getLanguage()
protected function getlanguage()
{
$langs = $this->c->Func->getLangs();
@ -73,7 +72,7 @@ class User extends DataModel
}
}
protected function getStyle()
protected function getstyle()
{
$styles = $this->c->Func->getStyles();
@ -87,4 +86,44 @@ class User extends DataModel
return isset($styles[0]) ? $styles[0] : 'ForkBB';
}
}
protected function getlink()
{
return $this->c->Router->link('User', ['id' => $this->id, 'name' => $this->username]);
}
protected function getavatar()
{
$filetypes = array('jpg', 'gif', 'png');
foreach ($filetypes as $type) {
$path = $this->c->DIR_PUBLIC . "/{$this->c->config->o_avatars_dir}/{$this->id}.{$type}";
if (file_exists($path) && getimagesize($path)) {
return $this->c->PUBLIC_URL . "/{$this->c->config->o_avatars_dir}/{$this->id}.{$type}";
}
}
return null;
}
public function title()
{
if (isset($this->c->bans->userList[mb_strtolower($this->username)])) { //????
return __('Banned');
} elseif ($this->title != '') {
return $this->cens()->title;
} elseif ($this->g_user_title != '') {
return $this->cens()->g_user_title;
} elseif ($this->isGuest) {
return __('Guest');
} else {
return __('Member');
}
}
protected function getonline()
{
return isset($this->c->Online->online[$this->id]);
}
}

View file

@ -78,13 +78,13 @@ class LoadUserFromCookie extends MethodModel
$data = null;
$ip = $this->getIp();
if ($id > 1) {
$data = $this->c->DB->query('SELECT u.*, g.*, o.logged, o.idle FROM ::users AS u INNER JOIN ::groups AS g ON u.group_id=g.g_id LEFT JOIN ::online AS o ON o.user_id=u.id WHERE u.id=?i:id', [':id' => $id])->fetch();
$data = $this->c->DB->query('SELECT u.*, g.*, o.logged FROM ::users AS u INNER JOIN ::groups AS g ON u.group_id=g.g_id LEFT JOIN ::online AS o ON o.user_id=u.id WHERE u.id=?i:id', [':id' => $id])->fetch();
}
if (empty($data['id'])) {
$data = $this->c->DB->query('SELECT u.*, g.*, o.logged, o.last_post, o.last_search FROM ::users AS u INNER JOIN ::groups AS g ON u.group_id=g.g_id LEFT JOIN ::online AS o ON (o.user_id=1 AND o.ident=?s:ip) WHERE u.id=1', [':ip' => $ip])->fetch();
}
if (empty($data['id'])) {
throw new RuntimeException('Unable to fetch guest information. Your database must contain both a guest user and a guest user group.');
if (empty($data['id'])) {
throw new RuntimeException('Unable to fetch guest information. Your database must contain both a guest user and a guest user group');
}
}
$this->model->setAttrs($data);
$this->model->__ip = $ip;

View file

@ -26,7 +26,7 @@ if (file_exists(__DIR__ . '/config/main.php')) {
} elseif (file_exists(__DIR__ . '/config/install.php')) {
$c = new Container(include __DIR__ . '/config/install.php');
} else {
throw new RuntimeException('Application is not configured.');
throw new RuntimeException('Application is not configured');
}
require __DIR__ . '/functions.php';
@ -60,7 +60,7 @@ if (null !== $page->onlinePos) {
$c->Online->calc($page);
}
$tpl = $c->View->rendering($page);
if ($tpl !== null && $c->DEBUG > 0) {
if (null !== $tpl && $c->DEBUG > 0) {
$debug = $c->View->rendering($c->Debug->debug());
$tpl = str_replace('<!-- debuginfo -->', $debug, $tpl);
}

View file

@ -79,7 +79,7 @@ msgid "Server load label"
msgstr "Server load"
msgid "Server load data"
msgstr "%s - %s user(s) online"
msgstr "%1$s - %2$s visitor(s) online"
msgid "Environment label"
msgstr "Environment"

View file

@ -262,4 +262,4 @@ msgid "Report flood help"
msgstr "Количество секунд, которые необходимо подождать до отправления следующего сигнала. Поставьте 0 чтобы выключить ограничение."
msgid "Moderator info"
msgstr "Пожалуйста обратите внимание, что пока пользователь не назначен модератором конкретного раздела, он не сможет реализовать свои модераторские права. Назначение производится в разделе "Модерация" пользовательского профиля."
msgstr "Пожалуйста, обратите внимание, что пока пользователь не назначен модератором конкретного раздела, он не сможет реализовать свои модераторские права. Назначение производится в разделе "Модерация" пользовательского профиля."

View file

@ -79,7 +79,7 @@ msgid "Server load label"
msgstr "Загрузка сервера"
msgid "Server load data"
msgstr "%s - %s пользователей online"
msgstr "%1$s - %2$s посетитель(ей)"
msgid "Environment label"
msgstr "Окружение"

View file

@ -34,14 +34,13 @@
</nav>
@endsection
@extends('layouts/main')
@if($p->forums)
@if($forums = $p->forums)
<div class="f-nav-links">
@yield('crumbs')
</div>
<section class="f-subforums">
<ol class="f-ftlist">
@foreach($p->forums as $id => $cat)
<li id="id-subforums{!! $id !!}" class="f-category">
<li id="id-subforums{!! $p->forum->id !!}" class="f-category">
<h2>{{ __('Sub forum', 2) }}</h2>
<ol class="f-table">
<li class="f-row f-thead" value="0">
@ -52,7 +51,6 @@
@include('layouts/subforums')
</ol>
</li>
@endforeach
</ol>
</section>
@endif
@ -65,13 +63,9 @@
</div>
@endif
</div>
@if(!$p->topics)
<section class="f-main f-message">
<h2>{!! __('Empty forum') !!}</h2>
</section>
@else
@if($p->topics)
<section class="f-main f-forum">
<h2>{{ $p->forumName }}</h2>
<h2>{{ $p->forum->forum_name }}</h2>
<div class="f-ftlist">
<ol class="f-table">
<li class="f-row f-thead" value="0">
@ -80,50 +74,50 @@
<div class="f-hcell f-clast">{!! __('Last post') !!}</div>
</li>
@foreach($p->topics as $topic)
@if($topic['moved_to'])
<li id="topic-{!! $topic['id']!!}" class="f-row f-fredir">
@if($topic->moved_to)
<li id="topic-{!! $topic->id !!}" class="f-row f-fredir">
<div class="f-cell f-cmain">
<div class="f-ficon"></div>
<div class="f-finfo">
<h3><span class="f-fredirtext">{!! __('Moved') !!}</span> <a class="f-ftname" href="{!! $topic['link'] !!}">{{ $topic['subject'] }}</a></h3>
<h3><span class="f-fredirtext">{!! __('Moved') !!}</span> <a class="f-ftname" href="{!! $topic->link !!}">{{ $topic->cens()->subject }}</a></h3>
</div>
</div>
</li>
@else
<li id="topic-{!! $topic['id'] !!}" class="f-row<!-- inline -->
@if($topic['link_new']) f-fnew
<li id="topic-{!! $topic->id !!}" class="f-row<!-- inline -->
@if($topic->link_new) f-fnew
@endif
@if($topic['link_unread']) f-funread
@if($topic->link_unread) f-funread
@endif
@if($topic['sticky']) f-fsticky
@if($topic->sticky) f-fsticky
@endif
@if($topic['closed']) f-fclosed
@if($topic->closed) f-fclosed
@endif
@if($topic['poll_type']) f-fpoll
@if($topic->poll_type) f-fpoll
@endif
@if($topic['dot']) f-fposted
@if($topic->dot) f-fposted
@endif
"><!-- endinline -->
<div class="f-cell f-cmain">
<div class="f-ficon"></div>
<div class="f-finfo">
<h3>
@if($topic['dot'])
@if($topic->dot)
<span class="f-tdot">·</span>
@endif
@if($topic['sticky'])
@if($topic->sticky)
<span class="f-stickytxt">{!! __('Sticky') !!}</span>
@endif
@if($topic['closed'])
@if($topic->closed)
<span class="f-closedtxt">{!! __('Closed') !!}</span>
@endif
@if($topic['poll_type'])
@if($topic->poll_type)
<span class="f-polltxt">{!! __('Poll') !!}</span>
@endif
<a class="f-ftname" href="{!! $topic['link'] !!}">{{ $topic['subject'] }}</a>
@if($topic['pages'])
<a class="f-ftname" href="{!! $topic->link !!}">{{ $topic->cens()->subject }}</a>
@if($topic->pages)
<span class="f-tpages">
@foreach($topic['pages'] as $cur)
@foreach($topic->pages as $cur)
@if($cur[1] === 'space')
<span class="f-page f-pspacer">{!! __('Spacer') !!}</span>
@else
@ -132,25 +126,25 @@
@endforeach
</span>
@endif
@if($topic['link_new'])
<span class="f-newtxt"><a href="{!! $topic['link_new'] !!}" title="{!! __('New posts info') !!}">{!! __('New posts') !!}</a></span>
@if($topic->link_new)
<span class="f-newtxt"><a href="{!! $topic->link_new !!}" title="{!! __('New posts info') !!}">{!! __('New posts') !!}</a></span>
@endif
</h3>
<p class="f-cmposter">{!! __('by') !!} {{ $topic['poster'] }}</p>
<p class="f-cmposter">{!! __('by') !!} {{ $topic->poster }}</p>
</div>
</div>
<div class="f-cell f-cstats">
<ul>
<li>{!! __('%s Reply', $topic['num_replies'], $topic['replies']) !!}</li>
@if($topic['views'])
<li>{!! __('%s View', $topic['num_views'], $topic['views']) !!}</li>
<li>{!! __('%s Reply', $topic->num_replies, $topic->num()->num_replies) !!}</li>
@if($topic->num_views)
<li>{!! __('%s View', $topic->num_views, $topic->num()->num_views) !!}</li>
@endif
</ul>
</div>
<div class="f-cell f-clast">
<ul>
<li class="f-cltopic"><a href="{!! $topic['link_last'] !!}" title="&quot;{{ $topic['subject'] }}&quot; - {!! __('Last post') !!}">{{ $topic['last_post'] }}</a></li>
<li class="f-clposter">{!! __('by') !!} {{ $topic['last_poster'] }}</li>
<li class="f-cltopic"><a href="{!! $topic->link_last !!}" title="&quot;{{ $topic->cens()->subject }}&quot; - {!! __('Last post') !!}">{{ $topic->dt()->last_post }}</a></li>
<li class="f-clposter">{!! __('by') !!} {{ $topic->last_poster }}</li>
</ul>
</div>
</li>

View file

@ -1,10 +1,10 @@
@extends('layouts/main')
@if($p->forums)
@if($p->categoryes)
<section class="f-main">
<ol class="f-ftlist">
@foreach($p->forums as $id => $cat)
@foreach($p->categoryes as $id => $forums)
<li id="cat-{!! $id !!}" class="f-category">
<h2>{{ $cat['name'] }}</h2>
<h2>{{ current($forums)->cat_name }}</h2>
<ol class="f-table">
<li class="f-row f-thead" value="0">
<div class="f-hcell f-cmain">{!! __('Forum') !!}</div>
@ -17,9 +17,5 @@
@endforeach
</ol>
</section>
@else
<section class="f-main f-message">
<h2>{!! __('Empty board') !!}</h2>
</section>
@endif
@include('layouts/stats')

View file

@ -4,29 +4,31 @@
@if($p->stats)
<dl class="right">
<dt>{!! __('Board stats') !!}</dt>
<dd>{!! __('No of users') !!} <strong>{!! $p->stats['total_users'] !!}</strong></dd>
<dd>{!! __('No of topics') !!} <strong>{!! $p->stats['total_topics'] !!}</strong></dd>
<dd>{!! __('No of posts') !!} <strong>{!! $p->stats['total_posts'] !!}</strong></dd>
<dd>{!! __('No of users') !!} <strong>{!! $p->stats->num()->userTotal !!}</strong></dd>
<dd>{!! __('No of topics') !!} <strong>{!! $p->stats->num()->topicTotal !!}</strong></dd>
<dd>{!! __('No of posts') !!} <strong>{!! $p->stats->num()->postTotal !!}</strong></dd>
</dl>
@endif
<dl class="left">
<dt>{!! __('User info') !!}</dt>
@if($p->stats && is_string($p->stats['newest_user']))
<dd>{!! __('Newest user') !!} {{ $p->stats['newest_user'] }}</dd>
@elseif($p->stats)
<dd>{!! __('Newest user') !!} <a href="{!! $p->stats['newest_user'][0] !!}">{{ $p->stats['newest_user'][1] }}</a></dd>
@if($p->stats)
@if(is_string($p->stats->userLast))
<dd>{!! __('Newest user') !!} {{ $p->stats->userLast }}</dd>
@else
<dd>{!! __('Newest user') !!} <a href="{!! $p->stats->userLast[0] !!}">{{ $p->stats->userLast[1] }}</a></dd>
@endif
@endif
@if($p->online)
<dd>{!! __('Visitors online', $p->online['number_of_users'], $p->online['number_of_guests']) !!}</dd>
<dd>{!! __('Visitors online', $p->online->num()->numUsers, $p->online->num()->numGuests) !!}</dd>
@endif
@if($p->stats)
<dd>{!! __('Most online', $p->online['max'], $p->online['max_time']) !!}</dd>
<dd>{!! __('Most online', $p->online->num()->maxNum, $p->online->dt()->maxTime) !!}</dd>
@endif
</dl>
@if($p->online && $p->online['list'])
@if($p->online && $p->online->info)
<dl class="f-inline f-onlinelist"><!-- inline -->
<dt>{!! __('Online users') !!}</dt>
@foreach($p->online['list'] as $cur)
@foreach($p->online->info as $cur)
@if(is_string($cur))
<dd>{{ $cur }}</dd>
@else

View file

@ -1,46 +1,46 @@
@foreach($cat['forums'] as $cur)
@if($cur['redirect_url'])
<li id="forum-{!! $cur['fid']!!}" class="f-row f-fredir">
@foreach($forums as $cur)
@if($cur->redirect_url)
<li id="forum-{!! $cur->id !!}" class="f-row f-fredir">
<div class="f-cell f-cmain">
<div class="f-ficon"></div>
<div class="f-finfo">
<h3><span class="f-fredirtext">{!! __('Link to') !!}</span> <a class="f-ftname" href="{!! $cur['redirect_url'] !!}">{{ $cur['forum_name'] }}</a></h3>
@if($cur['forum_desc'])
<p class="f-fdesc">{!! $cur['forum_desc'] !!}</p>
<h3><span class="f-fredirtext">{!! __('Link to') !!}</span> <a class="f-ftname" href="{!! $cur->redirect_url !!}">{{ $cur->forum_name }}</a></h3>
@if($cur->forum_desc)
<p class="f-fdesc">{!! $cur->forum_desc !!}</p>
@endif
</div>
</div>
</li>
@else
@if($cur['new'])
<li id="forum-{!! $cur['fid'] !!}" class="f-row f-fnew">
@if($cur->tree->newMessages)
<li id="forum-{!! $cur->id !!}" class="f-row f-fnew">
@else
<li id="forum-{!! $cur['fid'] !!}" class="f-row">
<li id="forum-{!! $cur->id !!}" class="f-row">
@endif
<div class="f-cell f-cmain">
<div class="f-ficon"></div>
<div class="f-finfo">
<h3>
<a class="f-ftname" href="{!! $cur['forum_link'] !!}">{{ $cur['forum_name'] }}</a>
@if($cur['new'])
<a class="f-ftname" href="{!! $cur->link !!}">{{ $cur->forum_name }}</a>
@if($cur->tree->newMessages)
<span class="f-newtxt"><a href="">{!! __('New posts') !!}</a></span>
@endif
</h3>
@if($cur['subforums'])
@if($cur->subforums)
<dl class="f-inline f-fsub"><!-- inline -->
<dt>{!! __('Sub forum', count($cur['subforums'])) !!}</dt>
@foreach($cur['subforums'] as $sub)
<dd><a href="{!! $sub[0] !!}">{{ $sub[1] }}</a></dd>
<dt>{!! __('Sub forum', count($cur->subforums)) !!}</dt>
@foreach($cur->subforums as $sub)
<dd><a href="{!! $sub->link !!}">{{ $sub->forum_name }}</a></dd>
@endforeach
</dl><!-- endinline -->
@endif
@if($cur['forum_desc'])
<p class="f-fdesc">{!! $cur['forum_desc'] !!}</p>
@if($cur->forum_desc)
<p class="f-fdesc">{!! $cur->forum_desc !!}</p>
@endif
@if($cur['moderators'])
@if($cur->moderators)
<dl class="f-inline f-modlist"><!-- inline -->
<dt>{!! __('Moderated by', count($cur['moderators'])) !!}</dt>
@foreach($cur['moderators'] as $mod)
<dt>{!! __('Moderated by', count($cur->moderators)) !!}</dt>
@foreach($cur->moderators as $mod)
@if(is_string($mod))
<dd>{{ $mod }}</dd>
@else
@ -53,16 +53,16 @@
</div>
<div class="f-cell f-cstats">
<ul>
<li>{!! __('%s Topic', $cur['num_topics'], $cur['topics']) !!}</li>
<li>{!! __('%s Post', $cur['num_posts'], $cur['posts'])!!}</li>
<li>{!! __('%s Topic', $cur->tree->num_topics, $cur->tree->num()->num_topics) !!}</li>
<li>{!! __('%s Post', $cur->tree->num_posts, $cur->tree->num()->num_posts)!!}</li>
</ul>
</div>
<div class="f-cell f-clast">
<ul>
@if($cur['last_post_id'])
<li class="f-cltopic"><a href="{!! $cur['last_post_id'] !!}" title="&quot;{{ $cur['last_topic'] }}&quot; - {!! __('Last post') !!}">{{ $cur['last_topic'] }}</a></li>
<li class="f-clposter">{!! __('by') !!} {{ $cur['last_poster'] }}</li>
<li class="f-cltime">{!! $cur['last_post'] !!}</li>
@if($cur->tree->last_post_id)
<li class="f-cltopic"><a href="{!! $cur->tree->last_post_link !!}" title="&quot;{{ $cur->tree->cens()->last_topic }}&quot; - {!! __('Last post') !!}">{{ $cur->tree->cens()->last_topic }}</a></li>
<li class="f-clposter">{!! __('by') !!} {{ $cur->tree->last_poster }}</li>
<li class="f-cltime">{!! $cur->tree->dt()->last_post !!}</li>
@else
<li class="f-cltopic">{!! __('Never') !!}</li>
@endif

View file

@ -10,9 +10,9 @@
</ul>
@endsection
@section('linkpost')
@if($p->NewReply !== null)
@if($p->topic->post_replies || $p->topic->closed)
<div class="f-link-post">
@if($p->NewReply === false)
@if($p->topic->closed)
__('Topic closed')
@else
<a class="f-btn" href="{!! $p->NewReply !!}">{!! __('Post reply') !!}</a>
@ -40,7 +40,7 @@
@extends('layouts/main')
<div class="f-nav-links">
@yield('crumbs')
@if($p->NewReply || $p->pages)
@if($p->topic->post_replies || $p->topic->closed || $p->pages)
<div class="f-links-b clearfix">
@yield('pages')
@yield('linkpost')
@ -48,49 +48,55 @@
@endif
</div>
<section class="f-main f-topic">
<h2>{{ $p->topic['subject'] }}</h2>
<h2>{{ $p->topic->cens()->subject }}</h2>
@foreach($p->posts as $post)
<article id="p{!! $post['id'] !!}" class="f-post{!! $post['poster_gender'].$post['poster_online'] !!} clearfix">
<article id="p{!! $post->id !!}" class="clearfix f-post<!-- inline -->
@if($post->user->gender == 1) f-user-male
@elseif($post->user->gender == 2) f-user-female
@endif
@if($post->user->online) f-user-online
@endif
"><!-- endinline -->
<header class="f-post-header clearfix">
<h3>{{ $p->topic['subject'] }} - #{!! $post['post_number'] !!}</h3>
<span class="left"><time datetime="{{ $post['posted_utc'] }}">{{ $post['posted'] }}</time></span>
<span class="right"><a href="{!! $post['link'] !!}" rel="bookmark">#{!! $post['post_number'] !!}</a></span>
<h3>{{ $p->topic->cens()->subject }} - #{!! $post->postNumber !!}</h3>
<span class="left"><time datetime="{{ $post->utc()->posted }}">{{ $post->dt()->posted }}</time></span>
<span class="right"><a href="{!! $post->link !!}" rel="bookmark">#{!! $post->postNumber !!}</a></span>
</header>
<div class="f-post-body clearfix">
<address class="f-post-left clearfix">
<ul class="f-user-info">
@if($post['poster_link'])
<li class="f-username"><a href="{!! $post['poster_link'] !!}">{{ $post['poster'] }}</a></li>
@if($post->showUserLink)
<li class="f-username"><a href="{!! $post->user->link !!}">{{ $post->user->username }}</a></li>
@else
<li class="f-username">{{ $post['poster'] }}</li>
<li class="f-username">{{ $post->user->username }}</li>
@endif
@if($post['poster_avatar'])
@if($post->showUserAvatar)
<li class="f-avatar">
<img alt="{{ $post['poster'] }}" src="{!! $post['poster_avatar'] !!}">
<img alt="{{ $post->user->username }}" src="{!! $post->user->avatar !!}">
</li>
@endif
<li class="f-usertitle"><span>{{$post['poster_title']}}</span></li>
@if($post['poster_posts'])
<li class="f-postcount"><span>{!! __('%s post', $post['poster_num_posts'], $post['poster_posts']) !!}</span></li>
<li class="f-usertitle"><span>{{ $post->user->title() }}</span></li>
@if($post->showUserInfo)
<li class="f-postcount"><span>{!! __('%s post', $post->user->num_posts, $post->user->num()->num_posts) !!}</span></li>
@endif
</ul>
@if($post['poster_info_add'])
@if($post->showUserInfo)
<ul class="f-user-info-add">
<li><span>{!! __('Registered:') !!} {{ $post['poster_registered'] }}</span></li>
@if($post['poster_location'])
<li><span>{!! __('From') !!} {{ $post['poster_location'] }}</span></li>
<li><span>{!! __('Registered:') !!} {{ $post->user->dt(true)->registered }}</span></li>
@if($post->user->location)
<li><span>{!! __('From') !!} {{ $post->user->cens()->location }}</span></li>
@endif
<li><span></span></li>
</ul>
@endif
</address>
<div class="f-post-right f-post-main">
{!! $post['message'] !!}
{!! $post->message !!}
</div>
@if(isset($p->signs[$post['poster_id']]))
@if($post->showSignature && $post->user->signature)
<div class="f-post-right f-post-signature">
<hr>
{!! $p->signs[$post['poster_id']] !!}
{!! $post->user->signature !!}
</div>
@endif
</div>
@ -98,10 +104,10 @@
<div class="f-post-left">
<span></span>
</div>
@if($post['controls'])
@if($post->controls)
<div class="f-post-right clearfix">
<ul>
@foreach($post['controls'] as $key => $control)
@foreach($post->controls as $key => $control)
<li class="f-post{!! $key !!}"><a class="f-btn" href="{!! $control[0] !!}">{!! __($control[1]) !!}</a></li>
@endforeach
</ul>
@ -112,7 +118,7 @@
@endforeach
</section>
<div class="f-nav-links">
@if($p->NewReply || $p->pages)
@if($p->topic->post_replies || $p->topic->closed || $p->pages)
<div class="f-links-a clearfix">
@yield('linkpost')
@yield('pages')

View file

@ -19,6 +19,6 @@
"require": {
"php": ">=5.6.0",
"artoodetoo/dirk": "dev-master",
"MioVisman/Parserus": "^0.9.1"
"miovisman/parserus": "dev-master"
}
}