diff --git a/_extensions/s2_blog/_include/Page/Day.php b/_extensions/s2_blog/_include/Page/Day.php
index 60aa0ed9..2d888e60 100644
--- a/_extensions/s2_blog/_include/Page/Day.php
+++ b/_extensions/s2_blog/_include/Page/Day.php
@@ -19,7 +19,7 @@ public function body (Request $request): ?Response
{
$params = $request->attributes->all();
- if ($this->inTemplate('')) {
+ if ($this->hasPlaceholder('')) {
$this->page['s2_blog_calendar'] = Lib::calendar($params['year'], $params['month'], $params['day']);
}
diff --git a/_extensions/s2_blog/_include/Page/Favorite.php b/_extensions/s2_blog/_include/Page/Favorite.php
index 66e24923..005c9bdc 100644
--- a/_extensions/s2_blog/_include/Page/Favorite.php
+++ b/_extensions/s2_blog/_include/Page/Favorite.php
@@ -25,7 +25,7 @@ public function body (Request $request): ?Response
$this->ensureTemplateIsLoaded();
- if ($this->inTemplate(''))
+ if ($this->hasPlaceholder(''))
$this->page['s2_blog_calendar'] = Lib::calendar(date('Y'), date('m'), '0');
$this->favorite_posts();
diff --git a/_extensions/s2_blog/_include/Page/HTML.php b/_extensions/s2_blog/_include/Page/HTML.php
index edd6550d..44cad437 100644
--- a/_extensions/s2_blog/_include/Page/HTML.php
+++ b/_extensions/s2_blog/_include/Page/HTML.php
@@ -49,7 +49,7 @@ public function handle(Request $request): ?Response
$this->page['head_title'] = empty($this->page['head_title']) ? S2_BLOG_TITLE : $this->page['head_title'] . ' - ' . S2_BLOG_TITLE;
- if (!isset($this->page['s2_blog_navigation']) && $this->inTemplate('')) {
+ if (!isset($this->page['s2_blog_navigation']) && $this->hasPlaceholder('')) {
$this->page['s2_blog_navigation'] = $this->blog_navigation();
}
diff --git a/_extensions/s2_blog/_include/Page/Main.php b/_extensions/s2_blog/_include/Page/Main.php
index 1e554fdf..9c3923bc 100644
--- a/_extensions/s2_blog/_include/Page/Main.php
+++ b/_extensions/s2_blog/_include/Page/Main.php
@@ -28,7 +28,7 @@ public function body (Request $request): ?Response
$this->template_id = $s2_blog_skip ? 'blog.php' : 'blog_main.php';
- if ($this->inTemplate(''))
+ if ($this->hasPlaceholder(''))
$this->page['s2_blog_calendar'] = Lib::calendar(date('Y'), date('m'), '0');
$this->last_posts($s2_blog_skip);
diff --git a/_extensions/s2_blog/_include/Page/Month.php b/_extensions/s2_blog/_include/Page/Month.php
index 4cd0fc94..91e16146 100644
--- a/_extensions/s2_blog/_include/Page/Month.php
+++ b/_extensions/s2_blog/_include/Page/Month.php
@@ -20,7 +20,7 @@ public function body (Request $request): ?Response
{
$params = $request->attributes->all();
- if ($this->inTemplate(''))
+ if ($this->hasPlaceholder(''))
$this->page['s2_blog_calendar'] = Lib::calendar($params['year'], $params['month'], 0);
$this->page['title'] = '';
diff --git a/_extensions/s2_blog/_include/Page/Post.php b/_extensions/s2_blog/_include/Page/Post.php
index 8a34694a..521bcd8f 100644
--- a/_extensions/s2_blog/_include/Page/Post.php
+++ b/_extensions/s2_blog/_include/Page/Post.php
@@ -22,7 +22,7 @@ public function body (Request $request): ?Response
{
$params = $request->attributes->all();
- if ($this->inTemplate(''))
+ if ($this->hasPlaceholder(''))
$this->page['s2_blog_calendar'] = Lib::calendar($params['year'], $params['month'], $params['day'], $params['url']);
$this->page['title'] = '';
@@ -95,7 +95,7 @@ private function get_post($year, $month, $day, $url)
$this->page['canonical_path'] = S2_BLOG_PATH . date('Y/m/d/', $row['create_time']) . $row['url'];
- $is_back_forward = $this->inTemplate('');
+ $is_back_forward = $this->hasPlaceholder('');
$queries = [];
if ($label) {
@@ -180,7 +180,7 @@ private function get_post($year, $month, $day, $url)
];
$this->page['commented'] = $row['commented'];
- if ($row['commented'] && S2_SHOW_COMMENTS && $this->inTemplate(''))
+ if ($row['commented'] && S2_SHOW_COMMENTS && $this->hasPlaceholder(''))
$this->page['comments'] = $this->get_comments($post_id);
$row['time'] = s2_date_time($row['create_time']);
@@ -191,7 +191,7 @@ private function get_post($year, $month, $day, $url)
$this->page['text'] = $this->renderPartial('post', $row);
- if ($this->inTemplate('')) {
+ if ($this->hasPlaceholder('')) {
/** @var RecommendationProvider $recommendationProvider */
$recommendationProvider = \Container::get(RecommendationProvider::class);
global $request_uri;
diff --git a/_extensions/s2_blog/_include/Page/Tag.php b/_extensions/s2_blog/_include/Page/Tag.php
index 490e85b7..04f852eb 100644
--- a/_extensions/s2_blog/_include/Page/Tag.php
+++ b/_extensions/s2_blog/_include/Page/Tag.php
@@ -23,7 +23,7 @@ public function body (Request $request): ?Response
{
$params = $request->attributes->all();
- if ($this->inTemplate('')) {
+ if ($this->hasPlaceholder('')) {
$this->page['s2_blog_calendar'] = Lib::calendar(date('Y'), date('m'), '0');
}
diff --git a/_extensions/s2_blog/_include/Page/Tags.php b/_extensions/s2_blog/_include/Page/Tags.php
index a138d196..4092db64 100644
--- a/_extensions/s2_blog/_include/Page/Tags.php
+++ b/_extensions/s2_blog/_include/Page/Tags.php
@@ -33,7 +33,7 @@ public function body (Request $request): ?Response
return new RedirectResponse(s2_link($request->getPathInfo() . '/'), Response::HTTP_MOVED_PERMANENTLY);
}
- if ($this->inTemplate(''))
+ if ($this->hasPlaceholder(''))
$this->page['s2_blog_calendar'] = Lib::calendar(date('Y'), date('m'), '0');
// The list of tags
diff --git a/_extensions/s2_blog/_include/Page/Year.php b/_extensions/s2_blog/_include/Page/Year.php
index cfca2907..22bfd513 100644
--- a/_extensions/s2_blog/_include/Page/Year.php
+++ b/_extensions/s2_blog/_include/Page/Year.php
@@ -20,7 +20,7 @@ public function body (Request $request): ?Response
{
$params = $request->attributes->all();
- if ($this->inTemplate(''))
+ if ($this->hasPlaceholder(''))
$this->page['s2_blog_calendar'] = Lib::calendar($params['year'], '', 0);
$this->page['title'] = '';
diff --git a/_extensions/s2_blog/hooks/fn_s2_parse_page_url_end.php b/_extensions/s2_blog/hooks/fn_s2_parse_page_url_end.php
index 9ed4b79a..893413e3 100644
--- a/_extensions/s2_blog/hooks/fn_s2_parse_page_url_end.php
+++ b/_extensions/s2_blog/hooks/fn_s2_parse_page_url_end.php
@@ -2,29 +2,32 @@
/**
* Hook fn_s2_parse_page_url_end
*
- * @copyright (C) 2023 Roman Parpalak
- * @license http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ * @copyright 2023-2024 Roman Parpalak
+ * @license MIT
* @package s2_blog
+ *
+ * @var int $articleId
+ * @var \S2\Cms\Controller\PageCommon $this
+ * @var \S2\Cms\Template\HtmlTemplate $template
*/
- if (!defined('S2_ROOT')) {
- die;
+if (!defined('S2_ROOT')) {
+ die;
}
-if ($this->inTemplate(''))
-{
- Lang::load('s2_blog', function ()
- {
- if (file_exists(S2_ROOT.'/_extensions/s2_blog'.'/lang/'.S2_LANGUAGE.'.php'))
- return require S2_ROOT.'/_extensions/s2_blog'.'/lang/'.S2_LANGUAGE.'.php';
- else
- return require S2_ROOT.'/_extensions/s2_blog'.'/lang/English.php';
- });
+if ($template->hasPlaceholder('')) {
+ Lang::load('s2_blog', static function () {
+ if (file_exists(S2_ROOT . '/_extensions/s2_blog' . '/lang/' . S2_LANGUAGE . '.php')) {
+ return require S2_ROOT . '/_extensions/s2_blog' . '/lang/' . S2_LANGUAGE . '.php';
+ }
+
+ return require S2_ROOT . '/_extensions/s2_blog' . '/lang/English.php';
+ });
- $s2_blog_tags = s2_extensions\s2_blog\Placeholder::blog_tags($id);
- $page['s2_blog_tags'] = empty($s2_blog_tags) ? '' : $this->renderPartial('menu_block', array(
- 'title' => Lang::get('See in blog', 's2_blog'),
- 'menu' => $s2_blog_tags,
- 'class' => 's2_blog_tags',
- ));
+ $s2_blog_tags = s2_extensions\s2_blog\Placeholder::blog_tags($articleId);
+ $template->putInPlaceholder('s2_blog_tags', empty($s2_blog_tags) ? '' : $this->viewer->render('menu_block', [
+ 'title' => Lang::get('See in blog', 's2_blog'),
+ 'menu' => $s2_blog_tags,
+ 'class' => 's2_blog_tags',
+ ]));
}
diff --git a/_extensions/s2_blog/hooks/idx_pre_get_queries.php b/_extensions/s2_blog/hooks/idx_pre_get_queries.php
index f7910662..e614ddc5 100644
--- a/_extensions/s2_blog/hooks/idx_pre_get_queries.php
+++ b/_extensions/s2_blog/hooks/idx_pre_get_queries.php
@@ -14,7 +14,7 @@
$s2_blog_placehoders = array();
foreach (array('s2_blog_last_comments', 's2_blog_last_discussions', 's2_blog_last_post') as $s2_blog_placehoder)
- if ($this->inTemplate(''))
+ if ($this->hasPlaceholder(''))
$s2_blog_placehoders[$s2_blog_placehoder] = 1;
if (!empty($s2_blog_placehoders))
@@ -29,7 +29,7 @@
if (isset($s2_blog_placehoders['s2_blog_last_comments']))
{
$s2_blog_recent_comments = s2_extensions\s2_blog\Placeholder::recent_comments();
- $replace[''] = empty($s2_blog_recent_comments) ? '' : $this->renderPartial('menu_comments', array(
+ $replace[''] = empty($s2_blog_recent_comments) ? '' : $this->viewer->render('menu_comments', array(
'title' => Lang::get('Last comments', 's2_blog'),
'menu' => $s2_blog_recent_comments,
));
@@ -37,7 +37,7 @@
if (isset($s2_blog_placehoders['s2_blog_last_discussions']))
{
$s2_blog_last_discussions = s2_extensions\s2_blog\Placeholder::recent_discussions();
- $replace[''] = empty($s2_blog_last_discussions) ? '' : $this->renderPartial('menu_block', array(
+ $replace[''] = empty($s2_blog_last_discussions) ? '' : $this->viewer->render('menu_block', array(
'title' => Lang::get('Last discussions', 's2_blog'),
'menu' => $s2_blog_last_discussions,
'class' => 's2_blog_last_discussions',
@@ -52,7 +52,7 @@
unset($s2_blog_post);
$replace[''] = implode('', $s2_blog_data);
}
-$replace[''] = $page['s2_blog_tags'] ?? '';
-$replace[''] = $page['s2_blog_calendar'] ?? '';
-$replace[''] = $page['s2_blog_navigation'] ?? '';
-$replace[''] = $page['s2_blog_back_forward'] ?? '';
+$replace[''] = $this->page['s2_blog_tags'] ?? '';
+$replace[''] = $this->page['s2_blog_calendar'] ?? '';
+$replace[''] = $this->page['s2_blog_navigation'] ?? '';
+$replace[''] = $this->page['s2_blog_back_forward'] ?? '';
diff --git a/_include/Page/Abstract.php b/_include/Page/Abstract.php
index 6594ef84..6f706268 100644
--- a/_include/Page/Abstract.php
+++ b/_include/Page/Abstract.php
@@ -51,7 +51,7 @@ public function ensureTemplateIsLoaded(): void
}
}
- public function inTemplate($placeholder): bool
+ public function hasPlaceholder($placeholder): bool
{
$this->ensureTemplateIsLoaded();
diff --git a/_include/Page/Common.php b/_include/Page/Common.php
deleted file mode 100644
index 37ee0764..00000000
--- a/_include/Page/Common.php
+++ /dev/null
@@ -1,596 +0,0 @@
-parse_page_url($request->getPathInfo());
- return $result ?? parent::handle($request);
- }
-
- private function tagged_articles ($id)
- {
- /** @var DbLayer $s2_db */
- $s2_db = \Container::get(DbLayer::class);
-
- $query = array(
- 'SELECT' => 't.tag_id as tag_id, name, t.url as url',
- 'FROM' => 'tags AS t',
- 'JOINS' => array(
- array(
- 'INNER JOIN' => 'article_tag AS atg',
- 'ON' => 'atg.tag_id = t.tag_id'
- )
- ),
- 'WHERE' => 'atg.article_id = '.$id
- );
- ($hook = s2_hook('fn_tagged_articles_pre_get_tags_qr')) ? eval($hook) : null;
- $result = $s2_db->buildAndQuery($query);
-
- $tag_names = $tag_urls = array();
- while ($row = $s2_db->fetchAssoc($result))
- {
- ($hook = s2_hook('fn_tagged_articles_loop_get_tags')) ? eval($hook) : null;
-
- $tag_names[$row['tag_id']] = $row['name'];
- $tag_urls[$row['tag_id']] = $row['url'];
- }
-
- if (empty($tag_urls))
- return '';
-
- $subquery = array(
- 'SELECT' => '1',
- 'FROM' => 'articles AS a1',
- 'WHERE' => 'a1.parent_id = atg.article_id AND a1.published = 1',
- 'LIMIT' => '1'
- );
- $raw_query1 = $s2_db->build($subquery);
-
- $query = array(
- 'SELECT' => 'title, tag_id, parent_id, url, a.id AS id, ('.$raw_query1.') IS NOT NULL AS children_exist',
- 'FROM' => 'articles AS a',
- 'JOINS' => array(
- array(
- 'INNER JOIN' => 'article_tag AS atg',
- 'ON' => 'a.id = atg.article_id'
- ),
- ),
- 'WHERE' => 'atg.tag_id IN ('.implode(', ', array_keys($tag_names)).') AND a.published = 1'
-// 'ORDER BY' => 'create_time' // no temp table is created but order by ID is almost the same
- );
- ($hook = s2_hook('fn_tagged_articles_pre_get_articles_qr')) ? eval($hook) : null;
- $result = $s2_db->buildAndQuery($query);
-
- // Build article lists that have the same tags as our article
-
- $create_tag_list = false;
-
- $titles = $parent_ids = $urls = $tag_ids = $original_ids = array();
- while ($row = $s2_db->fetchAssoc($result))
- {
- ($hook = s2_hook('fn_tagged_articles_get_articles_loop')) ? eval($hook) : null;
-
- if ($id <> $row['id'])
- $create_tag_list = true;
- $titles[] = $row['title'];
- $parent_ids[] = $row['parent_id'];
- $urls[] = urlencode($row['url']).(S2_USE_HIERARCHY && $row['children_exist'] ? '/' : '');
- $tag_ids[] = $row['tag_id'];
- $original_ids[] = $row['id'];
- }
-
- if (empty($urls))
- return '';
-
- if ($create_tag_list)
- $urls = Model::get_group_url($parent_ids, $urls);
-
- // Sorting all obtained article links into groups by each tag
- $art_by_tags = array();
-
- foreach ($urls as $k => $url)
- $art_by_tags[$tag_ids[$k]][] = array(
- 'title' => $titles[$k],
- 'link' => $url,
- 'is_current' => $original_ids[$k] == $id,
- );
-
- ($hook = s2_hook('fn_tagged_articles_pre_art_by_tags_merge')) ? eval($hook) : null;
-
- // Remove tags that have only one article
- foreach ($art_by_tags as $tag_id => $title_array)
- if (count($title_array) <= 1)
- unset($art_by_tags[$tag_id]);
-
- $output = array();
- ($hook = s2_hook('fn_tagged_articles_pre_menu_merge')) ? eval($hook) : null;
- foreach ($art_by_tags as $tag_id => $articles)
- $output[] = $this->renderPartial('menu_block', array(
- 'title' => sprintf(Lang::get('With this tag'), ''.$tag_names[$tag_id].''),
- 'menu' => $articles,
- 'class' => 'article_tags',
- ));
-
- ($hook = s2_hook('fn_tagged_articles_end')) ? eval($hook) : null;
- return !empty($output) ? implode("\n", $output) : '';
- }
-
- private function get_tags ($id)
- {
- /** @var DbLayer $s2_db */
- $s2_db = \Container::get(DbLayer::class);
-
- $query = array(
- 'SELECT' => 'name, url',
- 'FROM' => 'tags AS t',
- 'JOINS' => array(
- array(
- 'INNER JOIN' => 'article_tag AS at',
- 'ON' => 'at.tag_id = t.tag_id'
- )
- ),
- 'WHERE' => 'at.article_id = '.$id
- );
- ($hook = s2_hook('fn_tags_list_pre_get_tags_qr')) ? eval($hook) : null;
- $result = $s2_db->buildAndQuery($query);
-
- $tags = array();
- while ($row = $s2_db->fetchAssoc($result))
- $tags[] = array(
- 'title' => $row['name'],
- 'link' => s2_link('/'.S2_TAGS_URL.'/'.urlencode($row['url']).'/'),
- );
-
- if (empty($tags))
- return '';
-
- return $this->renderPartial('tags', array(
- 'title' => Lang::get('Tags'),
- 'tags' => $tags,
- ));
- }
-
-
- // Processes site pages
- private function parse_page_url ($request_uri): ?Response
- {
- /** @var DbLayer $s2_db */
- $s2_db = \Container::get(DbLayer::class);
-
- $page = &$this->page;
-
- $request_array = explode('/', $request_uri); // []/[dir1]/[dir2]/[dir3]/[file1]
-
- // Correcting trailing slash and the rest of URL
- if (!S2_USE_HIERARCHY && count($request_array) > 2) {
- return new RedirectResponse(s2_link('/'.$request_array[1]), Response::HTTP_MOVED_PERMANENTLY);
- }
-
- $was_end_slash = str_ends_with($request_uri, '/');
-
- $bread_crumbs = array();
-
- $parent_path = '';
- $parent_id = Model::ROOT_ID;
- $parent_num = count($request_array) - 1 - (int) $was_end_slash;
-
- $this->template_id = '';
-
- ($hook = s2_hook('fn_s2_parse_page_url_start')) ? eval($hook) : null;
-
- if (S2_USE_HIERARCHY)
- {
- $urls = array_unique($request_array);
- $urls = array_map(array($s2_db, 'escape'), $urls);
-
- $query = array(
- 'SELECT' => 'id, parent_id, title, template',
- 'FROM' => 'articles',
- 'WHERE' => 'url IN (\''.implode('\', \'', $urls).'\') AND published=1'
- );
- ($hook = s2_hook('fn_s2_parse_page_url_loop_pre_get_parents_query')) ? eval($hook) : null;
- $result = $s2_db->buildAndQuery($query);
-
- $nodes = $s2_db->fetchAssocAll($result);
-
- // Walking through the page parents
- // 1. We ensure all of them are published
- // 2. We build "bread crumbs"
- // 3. We determine the template of the page
- for ($i = 0; $i < $parent_num; $i++)
- {
- $parent_path .= urlencode($request_array[$i]).'/';
-
- $cur_node = array();
- $found_node_num = 0;
- foreach ($nodes as $node)
- {
- if ($node['parent_id'] == $parent_id)
- {
- $cur_node = $node;
- $found_node_num++;
- }
- }
-
- if ($found_node_num === 0) {
- throw new NotFoundException();
- }
- if ($found_node_num > 1)
- error(Lang::get('DB repeat items') . (defined('S2_DEBUG') ? ' (parent_id='.$parent_id.', url="'.s2_htmlencode($request_array[$i]).'")' : ''));
-
- ($hook = s2_hook('fn_s2_parse_page_url_loop_pre_build_stuff')) ? eval($hook) : null;
-
- $parent_id = $cur_node['id'];
- if ($cur_node['template'] != '')
- $this->template_id = $cur_node['template'];
-
- $bread_crumbs[] = array(
- 'link' => s2_link($parent_path),
- 'title' => $cur_node['title']
- );
- }
- }
- else
- {
- $parent_path = '/';
- $i = 1;
- }
- // Path to the requested page (without trailing slash)
- $current_path = $parent_path.urlencode($request_array[$i]);
-
- $subquery = array(
- 'SELECT' => '1',
- 'FROM' => 'articles AS a1',
- 'WHERE' => 'a1.parent_id = a.id AND a1.published = 1',
- 'LIMIT' => '1'
- );
- $raw_query_children = $s2_db->build($subquery);
-
- $subquery = array(
- 'SELECT' => 'u.name',
- 'FROM' => 'users AS u',
- 'WHERE' => 'u.id = a.user_id'
- );
- $raw_query_author = $s2_db->build($subquery);
-
- $query = array(
- 'SELECT' => 'a.id, a.title, a.meta_keys as meta_keywords, a.meta_desc as meta_description, a.excerpt as excerpt, a.pagetext as text, a.create_time as date, favorite, commented, template, ('.$raw_query_children.') IS NOT NULL AS children_exist, ('.$raw_query_author.') AS author',
- 'FROM' => 'articles AS a',
- 'WHERE' => 'url=\''.$s2_db->escape($request_array[$i]).'\''.(S2_USE_HIERARCHY ? ' AND parent_id='.$parent_id : '').' AND published=1'
- );
- ($hook = s2_hook('fn_s2_parse_page_url_pre_get_page')) ? eval($hook) : null;
- $result = $s2_db->buildAndQuery($query);
-
- $page = $s2_db->fetchAssoc($result);
-
- // Error handling
- if (!$page) {
- throw new NotFoundException();
- }
-
- if ($s2_db->fetchAssoc($result))
- error(Lang::get('DB repeat items') . (defined('S2_DEBUG') ? ' (parent_id='.$parent_id.', url="'.$request_array[$i].'")' : ''));
-
- if ($page['template'])
- $this->template_id = $page['template'];
-
- if (!$this->template_id)
- {
- if (S2_USE_HIERARCHY)
- {
- $bread_crumbs[] = array(
- 'link' => s2_link($parent_path),
- 'title' => $page['title'],
- );
-
- error(sprintf(Lang::get('Error no template'), implode('
', array_map(function ($a)
- {
- return ''.s2_htmlencode($a['title']).'';
- }, $bread_crumbs))));
- }
- else
- error(Lang::get('Error no template flat'));
- }
-
- if (S2_USE_HIERARCHY && $parent_num && $page['children_exist'] != $was_end_slash) {
- return new RedirectResponse(s2_link($current_path . (!$was_end_slash ? '/' : '')), Response::HTTP_MOVED_PERMANENTLY);
- }
-
- $page['canonical_path'] = $current_path.($was_end_slash ? '/' : '');
-
- $id = $page['id'];
- $bread_crumbs[] = array(
- 'title' => $page['title']
- );
- $page['title'] = s2_htmlencode($page['title']);
-
- if (!empty($page['author']))
- $page['author'] = s2_htmlencode($page['author']);
-
- if (S2_USE_HIERARCHY)
- {
- $page['path'] = $bread_crumbs;
-
- $page['link_navigation']['top'] = s2_link('/');
- if (count($bread_crumbs) > 1)
- {
- $page['link_navigation']['up'] = s2_link($parent_path);
- $page['section_link'] = ''.$bread_crumbs[count($bread_crumbs) - 2]['title'].'';
- }
- }
-
- ($hook = s2_hook('fn_s2_parse_page_url_pre_get_tpl')) ? eval($hook) : null;
-
- // Dealing with sections, subsections, neighbours
- if (S2_USE_HIERARCHY && $page['children_exist'] && ($this->inTemplate('') || $this->inTemplate('')|| $this->inTemplate('') || $this->inTemplate('')))
- {
- // It's a section. We have to fetch subsections and articles.
-
- // Fetching children
- $subquery = array(
- 'SELECT' => 'a1.id',
- 'FROM' => 'articles AS a1',
- 'WHERE' => 'a1.parent_id = a.id AND a1.published = 1',
- 'LIMIT' => '1'
- );
- $raw_query1 = $s2_db->build($subquery);
-
- $sort_order = SORT_DESC;
- $query = array(
- 'SELECT' => 'title, url, ('.$raw_query1.') IS NOT NULL AS children_exist, id, excerpt, favorite, create_time, parent_id',
- 'FROM' => 'articles AS a',
- 'WHERE' => 'parent_id = '.$id.' AND published = 1',
- 'ORDER BY' => 'priority'
- );
- ($hook = s2_hook('fn_s2_parse_page_url_pre_get_children_qr')) ? eval($hook) : null;
- $result = $s2_db->buildAndQuery($query);
-
- $subarticles = $subsections = $sort_array = array();
- while ($row = $s2_db->fetchAssoc($result))
- {
- if ($row['children_exist'])
- {
- // The child is a subsection
- $item = array(
- 'id' => $row['id'],
- 'title' => $row['title'],
- 'link' => s2_link($current_path . '/' . urlencode($row['url']) . '/'),
- 'date' => s2_date($row['create_time']),
- 'excerpt' => $row['excerpt'],
- 'favorite' => $row['favorite'],
- );
-
- ($hook = s2_hook('pc_parse_page_url_add_subsection')) ? eval($hook) : null;
-
- $subsections[] = $item;
- }
- else
- {
- // The child is an article
- $item = array(
- 'id' => $row['id'],
- 'title' => $row['title'],
- 'link' => s2_link($current_path . '/' . urlencode($row['url'])),
- 'date' => s2_date($row['create_time']),
- 'excerpt' => $row['excerpt'],
- 'favorite' => $row['favorite'],
- );
- $sort_field = $row['create_time'];
-
- ($hook = s2_hook('pc_parse_page_url_add_subarticle')) ? eval($hook) : null;
-
- $subarticles[] = $item;
- $sort_array[] = $sort_field;
- }
- }
-
- $sections_text = '';
- if (!empty($subsections))
- {
- // There are subsections in the section
- $page['menu_subsections'] = $this->renderPartial('menu_block', array(
- 'title' => Lang::get('Subsections'),
- 'menu' => $subsections,
- 'class' => 'menu_subsections',
- ));
-
- foreach ($subsections as $item)
- $sections_text .= $this->renderPartial('subarticles_item', $item);
- }
-
- $articles_text = '';
- if (!empty($subarticles))
- {
- // There are articles in the section
- $page['menu_children'] = $this->renderPartial('menu_block', array(
- 'title' => Lang::get('In this section'),
- 'menu' => $subarticles,
- 'class' => 'menu_children',
- ));
-
- ($sort_order == SORT_DESC) ? arsort($sort_array) : asort($sort_array);
-
- if (S2_MAX_ITEMS)
- {
- // Paging navigation
- $page_num = isset($_GET['p']) ? intval($_GET['p']) - 1 : 0;
- if ($page_num < 0)
- $page_num = 0;
-
- $start = $page_num * S2_MAX_ITEMS;
- if ($start >= count($subarticles))
- $page_num = $start = 0;
-
- $total_pages = ceil(1.0 * count($subarticles) / S2_MAX_ITEMS);
-
- $link_nav = array();
- $paging = s2_paging($page_num + 1, $total_pages, s2_link(str_replace('%', '%%', $current_path.'/'), array('p=%d')), $link_nav)."\n";
- foreach ($link_nav as $rel => $href)
- $page['link_navigation'][$rel] = $href;
-
- $i = 0;
- foreach ($sort_array as $index => $value)
- {
- if ($i < $start || $i >= $start + S2_MAX_ITEMS)
- unset($sort_array[$index]);
- $i++;
- }
- }
-
- foreach ($sort_array as $index => $value)
- $articles_text .= $this->renderPartial('subarticles_item', $subarticles[$index]);
-
- if (S2_MAX_ITEMS)
- $articles_text .= $paging;
- }
-
- $page['subcontent'] = $this->renderPartial('subarticles', array(
- 'articles' => $articles_text,
- 'sections' => $sections_text,
- ));
- }
-
- if (S2_USE_HIERARCHY && !$page['children_exist'] && ($this->inTemplate('') || $this->inTemplate('') || $this->inTemplate('')))
- {
- // It's an article. We have to fetch other articles in the parent section
-
- // Fetching "siblings"
- $subquery = array(
- 'SELECT' => '1',
- 'FROM' => 'articles AS a2',
- 'WHERE' => 'a2.parent_id = a.id AND a2.published = 1',
- 'LIMIT' => '1'
- );
- $raw_query_child_num = $s2_db->build($subquery);
-
- $query = array(
- 'SELECT' => 'title, url, id, excerpt, create_time, parent_id',
- 'FROM' => 'articles AS a',
- 'WHERE' => 'parent_id = '.$parent_id.' AND published=1 AND ('.$raw_query_child_num.') IS NULL',
- 'ORDER BY' => 'priority'
- );
- ($hook = s2_hook('fn_s2_parse_page_url_pre_get_neighbours_qr')) ? eval($hook) : null;
- $result = $s2_db->buildAndQuery($query);
-
- $neighbour_urls = $menu_articles = array();
- $i = 0;
- $curr_item = -1;
- while ($row = $s2_db->fetchAssoc($result))
- {
- // A neighbour
- $url = s2_link($parent_path.urlencode($row['url']));
-
- $menu_articles[] = array(
- 'title' => $row['title'],
- 'link' => $url,
- 'is_current' => $id == $row['id'],
- );
-
- if ($id == $row['id'])
- $curr_item = $i;
-
- $neighbour_urls[] = $url;
-
- ($hook = s2_hook('fn_s2_parse_page_url_add_neighbour')) ? eval($hook) : null;
-
- $i++;
- }
-
- if (count($bread_crumbs) > 1)
- $page['menu_siblings'] = $this->renderPartial('menu_block', array(
- 'title' => sprintf(Lang::get('More in this section'), ''.$bread_crumbs[count($bread_crumbs) - 2]['title'].''),
- 'menu' => $menu_articles,
- 'class' => 'menu_siblings',
- ));
-
- if ($curr_item != -1)
- {
- if (isset($neighbour_urls[$curr_item - 1]))
- $page['link_navigation']['prev'] = $neighbour_urls[$curr_item - 1];
- if (isset($neighbour_urls[$curr_item + 1]))
- $page['link_navigation']['next'] = $neighbour_urls[$curr_item + 1];
-
- $page['back_forward'] = array(
- 'up' => count($bread_crumbs) <= 1 ? null : array(
- 'title' => $bread_crumbs[count($bread_crumbs) - 2]['title'],
- 'link' => s2_link($parent_path),
- ),
- 'back' => empty($menu_articles[$curr_item - 1]) ? null : array(
- 'title' => $menu_articles[$curr_item - 1]['title'],
- 'link' => $menu_articles[$curr_item - 1]['link'],
- ),
- 'forward' => empty($menu_articles[$curr_item + 1]) ? null : array(
- 'title' => $menu_articles[$curr_item + 1]['title'],
- 'link' => $menu_articles[$curr_item + 1]['link'],
- ),
- );
- }
- }
-
- // Tags
- if ($this->inTemplate(''))
- $page['article_tags'] = $this->tagged_articles($id);
-
- if ($this->inTemplate(''))
- $page['tags'] = $this->get_tags($id);
-
- // Recommendations
- if ($this->inTemplate('')) {
- /** @var RecommendationProvider $recommendationProvider */
- $recommendationProvider = \Container::get(RecommendationProvider::class);
- global $request_uri; // ???
-
- [$recommendations, $log, $rawRecommendations] = $recommendationProvider->getRecommendations($request_uri, new ExternalId($id));
- $this->page['recommendations'] = $this->renderPartial('recommendations', [
- 'raw' => $rawRecommendations,
- 'content' => $recommendations,
- 'log' => $log,
- ]);
- }
-
- // Comments
- if ($page['commented'] && S2_SHOW_COMMENTS && $this->inTemplate(''))
- {
- $query = array(
- 'SELECT' => 'nick, time, email, show_email, good, text',
- 'FROM' => 'art_comments',
- 'WHERE' => 'article_id = '.$id.' AND shown = 1',
- 'ORDER BY' => 'time'
- );
- ($hook = s2_hook('fn_s2_parse_page_url_pre_get_comm_qr')) ? eval($hook) : null;
- $result = $s2_db->buildAndQuery($query);
-
- $comments = '';
-
- for ($i = 1; $row = $s2_db->fetchAssoc($result); $i++)
- {
- $row['i'] = $i;
- $comments .= $this->renderPartial('comment', $row);
- }
-
- if ($comments)
- $page['comments'] = $this->renderPartial('comments', array('comments' => $comments));
- }
-
- ($hook = s2_hook('fn_s2_parse_page_url_end')) ? eval($hook) : null;
-
- return null;
- }
-}
diff --git a/_include/src/Application.php b/_include/src/Application.php
index 5a9bcdf3..16279d38 100644
--- a/_include/src/Application.php
+++ b/_include/src/Application.php
@@ -13,6 +13,7 @@
use Psr\Log\LogLevel;
use S2\Cms\Config\DynamicConfigProvider;
use S2\Cms\Controller\NotFoundController;
+use S2\Cms\Controller\PageCommon;
use S2\Cms\Controller\PageFavorite;
use S2\Cms\Controller\PageTag;
use S2\Cms\Controller\PageTags;
@@ -229,6 +230,22 @@ private function buildContainer(): void
$provider->get('S2_TAGS_URL'),
);
});
+
+ $this->container->set(PageCommon::class, function (Container $container) {
+ /** @var DynamicConfigProvider $provider */
+ $provider = $container->get(DynamicConfigProvider::class);
+ return new PageCommon(
+ $container->get(DbLayer::class),
+ $container->get(HtmlTemplateProvider::class),
+ $container->get(RecommendationProvider::class),
+ $container->get(Viewer::class),
+ $provider->get('S2_USE_HIERARCHY') === '1',
+ $provider->get('S2_SHOW_COMMENTS') === '1',
+ $provider->get('S2_TAGS_URL'),
+ (int)$provider->get('S2_MAX_ITEMS'),
+ $container->getParameter('debug'),
+ );
+ });
}
private function addRoutes(): void
@@ -246,7 +263,7 @@ private function addRoutes(): void
$routes->add('favorite', new Route('/' . $favoriteUrl . '{slash?>}', ['_controller' => PageFavorite::class]));
$routes->add('tags', new Route('/' . $tagsUrl . '{slash?>}', ['_controller' => PageTags::class]));
$routes->add('tag', new Route('/' . $tagsUrl . '/{name}{slash?>}', ['_controller' => PageTag::class]));
- $routes->add('common', new Route('/{path<.*>}', ['_controller' => \Page_Common::class]));
+ $routes->add('common', new Route('/{path<.*>}', ['_controller' => PageCommon::class]));
$this->routes = $routes;
}
@@ -275,6 +292,7 @@ private function loadParameters(): array
'cache_dir' => S2_CACHE_DIR,
'log_dir' => (defined('S2_LOG_DIR') ? S2_LOG_DIR : S2_CACHE_DIR),
'base_url' => defined('S2_BASE_URL') ? S2_BASE_URL : null,
+ 'debug' => defined('S2_DEBUG'),
'debug_view' => defined('S2_DEBUG_VIEW'),
'redirect_map' => $GLOBALS['s2_redirect'] ?? [],
];
diff --git a/_include/src/Controller/PageCommon.php b/_include/src/Controller/PageCommon.php
new file mode 100644
index 00000000..474f403f
--- /dev/null
+++ b/_include/src/Controller/PageCommon.php
@@ -0,0 +1,597 @@
+getPathInfo();
+
+ $request_array = explode('/', $request_uri); // []/[dir1]/[dir2]/[dir3]/[file1]
+
+ // Correcting trailing slash and the rest of URL
+ if (!$this->useHierarchy && \count($request_array) > 2) {
+ return new RedirectResponse(s2_link('/' . $request_array[1]), Response::HTTP_MOVED_PERMANENTLY);
+ }
+
+ $was_end_slash = str_ends_with($request_uri, '/');
+
+ $bread_crumbs = [];
+
+ $parent_path = '';
+ $parent_id = \Model::ROOT_ID;
+ $parent_num = \count($request_array) - 1 - (int)$was_end_slash;
+
+ $template_id = '';
+
+ if ($this->useHierarchy) {
+ $urls = array_unique($request_array);
+ $urls = array_map([$this->dbLayer, 'escape'], $urls);
+
+ $query = [
+ 'SELECT' => 'id, parent_id, title, template',
+ 'FROM' => 'articles',
+ 'WHERE' => 'url IN (\'' . implode('\', \'', $urls) . '\') AND published=1'
+ ];
+ $result = $this->dbLayer->buildAndQuery($query);
+
+ $nodes = $this->dbLayer->fetchAssocAll($result);
+
+ /**
+ * Walking through the page parents
+ * 1. We ensure all of them are published
+ * 2. We build "bread crumbs"
+ * 3. We determine the template of the page
+ */
+ for ($i = 0; $i < $parent_num; $i++) {
+ $parent_path .= urlencode($request_array[$i]) . '/';
+
+ $cur_node = [];
+ $found_node_num = 0;
+ foreach ($nodes as $node) {
+ if ($node['parent_id'] == $parent_id) {
+ $cur_node = $node;
+ $found_node_num++;
+ }
+ }
+
+ if ($found_node_num === 0) {
+ throw new NotFoundException();
+ }
+ if ($found_node_num > 1) {
+ error(\Lang::get('DB repeat items') . ($this->debug ? ' (parent_id=' . $parent_id . ', url="' . s2_htmlencode($request_array[$i]) . '")' : ''));
+ }
+
+ $parent_id = $cur_node['id'];
+ if ($cur_node['template'] != '') {
+ $template_id = $cur_node['template'];
+ }
+
+ $bread_crumbs[] = [
+ 'link' => s2_link($parent_path),
+ 'title' => $cur_node['title']
+ ];
+ }
+ } else {
+ $parent_path = '/';
+ $i = 1;
+ }
+ // Path to the requested page (without trailing slash)
+ $current_path = $parent_path . urlencode($request_array[$i]);
+
+ $subquery = [
+ 'SELECT' => '1',
+ 'FROM' => 'articles AS a1',
+ 'WHERE' => 'a1.parent_id = a.id AND a1.published = 1',
+ 'LIMIT' => '1'
+ ];
+ $raw_query_children = $this->dbLayer->build($subquery);
+
+ $subquery = [
+ 'SELECT' => 'u.name',
+ 'FROM' => 'users AS u',
+ 'WHERE' => 'u.id = a.user_id'
+ ];
+ $raw_query_author = $this->dbLayer->build($subquery);
+
+ $query = [
+ 'SELECT' => 'a.id, a.title, a.meta_keys as meta_keywords, a.meta_desc as meta_description, a.excerpt as excerpt, a.pagetext as text, a.create_time as date, favorite, commented, template, (' . $raw_query_children . ') IS NOT NULL AS children_exist, (' . $raw_query_author . ') AS author',
+ 'FROM' => 'articles AS a',
+ 'WHERE' => 'url=\'' . $this->dbLayer->escape($request_array[$i]) . '\'' . ($this->useHierarchy ? ' AND parent_id=' . $parent_id : '') . ' AND published=1'
+ ];
+ $result = $this->dbLayer->buildAndQuery($query);
+
+ $page = $this->dbLayer->fetchAssoc($result);
+
+ // Error handling
+ if (!$page) {
+ throw new NotFoundException();
+ }
+
+ if ($this->dbLayer->fetchAssoc($result)) {
+ error(\Lang::get('DB repeat items') . ($this->debug ? ' (parent_id=' . $parent_id . ', url="' . $request_array[$i] . '")' : ''));
+ }
+
+ if ($page['template']) {
+ $template_id = $page['template'];
+ }
+
+ if ($template_id === '') {
+ if ($this->useHierarchy) {
+ $bread_crumbs[] = [
+ 'link' => s2_link($parent_path),
+ 'title' => $page['title'],
+ ];
+
+ error(sprintf(\Lang::get('Error no template'), implode('
', array_map(static function ($a) {
+ return '' . s2_htmlencode($a['title']) . '';
+ }, $bread_crumbs))));
+ } else {
+ error(\Lang::get('Error no template flat'));
+ }
+ }
+
+ if ($this->useHierarchy && $parent_num && $page['children_exist'] != $was_end_slash) {
+ return new RedirectResponse(s2_link($current_path . (!$was_end_slash ? '/' : '')), Response::HTTP_MOVED_PERMANENTLY);
+ }
+
+ $articleId = (int)$page['id'];
+ $template = $this->htmlTemplateProvider->getTemplate($template_id);
+ $template
+ ->putInPlaceholder('id', $articleId) // for comments form
+ ->putInPlaceholder('meta_keywords', $page['meta_keywords'])
+ ->putInPlaceholder('meta_description', $page['meta_description'])
+ ->putInPlaceholder('excerpt', $page['excerpt'])
+ ->putInPlaceholder('text', $page['text'])
+ ->putInPlaceholder('date', $page['date'])
+ ->putInPlaceholder('commented', $page['commented'])
+ ->putInPlaceholder('author', $page['author'])
+ ->putInPlaceholder('canonical_path', $current_path . ($was_end_slash ? '/' : ''))
+ ;
+
+ $bread_crumbs[] = [
+ 'title' => $page['title']
+ ];
+ $template->putInPlaceholder('title', s2_htmlencode($page['title']));
+
+ if (!empty($page['author'])) {
+ $template->putInPlaceholder('author', s2_htmlencode($page['author']));
+ }
+
+ if ($this->useHierarchy) {
+ foreach ($bread_crumbs as $crumb) {
+ $template->addBreadCrumb($crumb['title'], $crumb['link'] ?? null);
+ }
+ $template->setLink('top', s2_link('/'));
+
+ if (\count($bread_crumbs) > 1) {
+ $template->setLink('up', s2_link($parent_path));
+ $template->putInPlaceholder(
+ 'section_link',
+ '' . $bread_crumbs[\count($bread_crumbs) - 2]['title'] . ''
+ );
+ }
+ }
+
+ // Dealing with sections, subsections, neighbours
+ if (
+ $this->useHierarchy
+ && $page['children_exist']
+ && (
+ $template->hasPlaceholder('')
+ || $template->hasPlaceholder('')
+ || $template->hasPlaceholder('')
+ || $template->hasPlaceholder('')
+ )
+ ) {
+ // It's a section. We have to fetch subsections and articles.
+
+ // Fetching children
+ $subquery = [
+ 'SELECT' => 'a1.id',
+ 'FROM' => 'articles AS a1',
+ 'WHERE' => 'a1.parent_id = a.id AND a1.published = 1',
+ 'LIMIT' => '1'
+ ];
+ $raw_query1 = $this->dbLayer->build($subquery);
+
+ $sort_order = SORT_DESC;
+ $query = [
+ 'SELECT' => 'title, url, (' . $raw_query1 . ') IS NOT NULL AS children_exist, id, excerpt, favorite, create_time, parent_id',
+ 'FROM' => 'articles AS a',
+ 'WHERE' => 'parent_id = ' . $articleId . ' AND published = 1',
+ 'ORDER BY' => 'priority'
+ ];
+ $result = $this->dbLayer->buildAndQuery($query);
+
+ $subarticles = $subsections = $sort_array = [];
+ while ($row = $this->dbLayer->fetchAssoc($result)) {
+ if ($row['children_exist']) {
+ // The child is a subsection
+ $item = [
+ 'id' => $row['id'],
+ 'title' => $row['title'],
+ 'link' => s2_link($current_path . '/' . urlencode($row['url']) . '/'),
+ 'date' => s2_date($row['create_time']),
+ 'excerpt' => $row['excerpt'],
+ 'favorite' => $row['favorite'],
+ ];
+
+ $subsections[] = $item;
+ } else {
+ // The child is an article
+ $item = array(
+ 'id' => $row['id'],
+ 'title' => $row['title'],
+ 'link' => s2_link($current_path . '/' . urlencode($row['url'])),
+ 'date' => s2_date($row['create_time']),
+ 'excerpt' => $row['excerpt'],
+ 'favorite' => $row['favorite'],
+ );
+ $sort_field = $row['create_time'];
+
+ $subarticles[] = $item;
+ $sort_array[] = $sort_field;
+ }
+ }
+
+ $sections_text = '';
+ if (\count($subsections) > 0) {
+ // There are subsections in the section
+ $template->putInPlaceholder('menu_subsections', $this->viewer->render('menu_block', [
+ 'title' => \Lang::get('Subsections'),
+ 'menu' => $subsections,
+ 'class' => 'menu_subsections',
+ ]));
+
+ foreach ($subsections as $item) {
+ $sections_text .= $this->viewer->render('subarticles_item', $item);
+ }
+ }
+
+ $articles_text = '';
+ if (\count($subarticles) > 0) {
+ // There are articles in the section
+ $template->putInPlaceholder('menu_children', $this->viewer->render('menu_block', [
+ 'title' => \Lang::get('In this section'),
+ 'menu' => $subarticles,
+ 'class' => 'menu_children',
+ ]));
+
+ ($sort_order == SORT_DESC) ? arsort($sort_array) : asort($sort_array);
+
+ if ($this->maxItems > 0) {
+ // Paging navigation
+ $page_num = $request->query->get('p', 1) - 1;
+ if ($page_num < 0) {
+ $page_num = 0;
+ }
+
+ $start = $page_num * $this->maxItems;
+ if ($start >= \count($subarticles)) {
+ $page_num = $start = 0;
+ }
+
+ $total_pages = ceil(1.0 * \count($subarticles) / $this->maxItems);
+
+ $link_nav = [];
+ $paging = s2_paging($page_num + 1, $total_pages, s2_link(str_replace('%', '%%', $current_path . '/'), ['p=%d']), $link_nav) . "\n";
+ foreach ($link_nav as $rel => $href) {
+ $template->setLink($rel, $href);
+ }
+
+ $sort_array = \array_slice($sort_array, $start, $this->maxItems, true);
+ }
+
+ foreach ($sort_array as $index => $value) {
+ $articles_text .= $this->viewer->render('subarticles_item', $subarticles[$index]);
+ }
+
+ if ($this->maxItems) {
+ $articles_text .= $paging;
+ }
+ }
+
+ $template->putInPlaceholder('subcontent', $this->viewer->render('subarticles', [
+ 'articles' => $articles_text,
+ 'sections' => $sections_text,
+ ]));
+ }
+
+ if (
+ $this->useHierarchy
+ && !$page['children_exist']
+ && (
+ $template->hasPlaceholder('')
+ || $template->hasPlaceholder('')
+ || $template->hasPlaceholder('')
+ )
+ ) {
+ // It's an article. We have to fetch other articles in the parent section
+
+ // Fetching "siblings"
+ $subquery = [
+ 'SELECT' => '1',
+ 'FROM' => 'articles AS a2',
+ 'WHERE' => 'a2.parent_id = a.id AND a2.published = 1',
+ 'LIMIT' => '1'
+ ];
+ $raw_query_child_num = $this->dbLayer->build($subquery);
+
+ $query = [
+ 'SELECT' => 'title, url, id, excerpt, create_time, parent_id',
+ 'FROM' => 'articles AS a',
+ 'WHERE' => 'parent_id = ' . $parent_id . ' AND published=1 AND (' . $raw_query_child_num . ') IS NULL',
+ 'ORDER BY' => 'priority'
+ ];
+ $result = $this->dbLayer->buildAndQuery($query);
+
+ $neighbour_urls = $menu_articles = [];
+
+ $i = 0;
+ $curr_item = -1;
+ while ($row = $this->dbLayer->fetchAssoc($result)) {
+ // A neighbour
+ $url = s2_link($parent_path . urlencode($row['url']));
+
+ $menu_articles[] = [
+ 'title' => $row['title'],
+ 'link' => $url,
+ 'is_current' => $articleId == $row['id'],
+ ];
+
+ if ($articleId == $row['id'])
+ $curr_item = $i;
+
+ $neighbour_urls[] = $url;
+
+ $i++;
+ }
+
+ if (\count($bread_crumbs) > 1) {
+ $template->putInPlaceholder('menu_siblings', $this->viewer->render('menu_block', [
+ 'title' => sprintf(\Lang::get('More in this section'), '' . $bread_crumbs[\count($bread_crumbs) - 2]['title'] . ''),
+ 'menu' => $menu_articles,
+ 'class' => 'menu_siblings',
+ ]));
+ }
+
+ if ($curr_item !== -1) {
+ if (isset($neighbour_urls[$curr_item - 1])) {
+ $template->setLink('prev', $neighbour_urls[$curr_item - 1]);
+ }
+ if (isset($neighbour_urls[$curr_item + 1])) {
+ $template->setLink('next', $neighbour_urls[$curr_item + 1]);
+ }
+
+ $template->putInPlaceholder('back_forward', [
+ 'up' => \count($bread_crumbs) <= 1 ? null : [
+ 'title' => $bread_crumbs[\count($bread_crumbs) - 2]['title'],
+ 'link' => s2_link($parent_path),
+ ],
+ 'back' => empty($menu_articles[$curr_item - 1]) ? null : [
+ 'title' => $menu_articles[$curr_item - 1]['title'],
+ 'link' => $menu_articles[$curr_item - 1]['link'],
+ ],
+ 'forward' => empty($menu_articles[$curr_item + 1]) ? null : [
+ 'title' => $menu_articles[$curr_item + 1]['title'],
+ 'link' => $menu_articles[$curr_item + 1]['link'],
+ ],
+ ]);
+ }
+ }
+
+ // Tags
+ if ($template->hasPlaceholder('')) {
+ $template->putInPlaceholder('article_tags', $this->tagged_articles($articleId));
+ }
+
+ if ($template->hasPlaceholder('')) {
+ $template->putInPlaceholder('tags', $this->get_tags($articleId));
+ }
+
+ // Recommendations
+ if ($template->hasPlaceholder('')) {
+ [$recommendations, $log, $rawRecommendations] = $this->recommendationProvider->getRecommendations($request_uri, new ExternalId($articleId));
+ $template->putInPlaceholder('recommendations', $this->viewer->render('recommendations', [
+ 'raw' => $rawRecommendations,
+ 'content' => $recommendations,
+ 'log' => $log,
+ ]));
+ }
+
+ // Comments
+ if ($page['commented'] && $this->showComments && $template->hasPlaceholder('')) {
+ $query = [
+ 'SELECT' => 'nick, time, email, show_email, good, text',
+ 'FROM' => 'art_comments',
+ 'WHERE' => 'article_id = ' . $articleId . ' AND shown = 1',
+ 'ORDER BY' => 'time'
+ ];
+ $result = $this->dbLayer->buildAndQuery($query);
+
+ $comments = '';
+ for ($i = 1; $row = $this->dbLayer->fetchAssoc($result); $i++) {
+ $row['i'] = $i;
+ $comments .= $this->viewer->render('comment', $row);
+ }
+
+ if ($comments !== '') {
+ $template->putInPlaceholder('comments', $this->viewer->render('comments', ['comments' => $comments]));
+ }
+ }
+
+ ($hook = s2_hook('fn_s2_parse_page_url_end')) ? eval($hook) : null;
+
+ return $template->toHttpResponse();
+ }
+
+ private function tagged_articles(int $articleId): string
+ {
+ $query = [
+ 'SELECT' => 't.tag_id as tag_id, name, t.url as url',
+ 'FROM' => 'tags AS t',
+ 'JOINS' => [
+ [
+ 'INNER JOIN' => 'article_tag AS atg',
+ 'ON' => 'atg.tag_id = t.tag_id'
+ ]
+ ],
+ 'WHERE' => 'atg.article_id = ' . $articleId
+ ];
+ $result = $this->dbLayer->buildAndQuery($query);
+
+ $tag_names = $tag_urls = [];
+ while ($row = $this->dbLayer->fetchAssoc($result)) {
+ $tag_names[$row['tag_id']] = $row['name'];
+ $tag_urls[$row['tag_id']] = $row['url'];
+ }
+
+ if (\count($tag_urls) === 0) {
+ return '';
+ }
+
+ $subquery = [
+ 'SELECT' => '1',
+ 'FROM' => 'articles AS a1',
+ 'WHERE' => 'a1.parent_id = atg.article_id AND a1.published = 1',
+ 'LIMIT' => '1'
+ ];
+ $raw_query1 = $this->dbLayer->build($subquery);
+
+ $query = [
+ 'SELECT' => 'title, tag_id, parent_id, url, a.id AS id, (' . $raw_query1 . ') IS NOT NULL AS children_exist',
+ 'FROM' => 'articles AS a',
+ 'JOINS' => [
+ [
+ 'INNER JOIN' => 'article_tag AS atg',
+ 'ON' => 'a.id = atg.article_id'
+ ],
+ ],
+ 'WHERE' => 'atg.tag_id IN (' . implode(', ', array_keys($tag_names)) . ') AND a.published = 1'
+// 'ORDER BY' => 'create_time' // no temp table is created but order by ID is almost the same
+ ];
+ $result = $this->dbLayer->buildAndQuery($query);
+
+ // Build article lists that have the same tags as our article
+
+ $hasArticlesInList = false;
+
+ $titles = $parent_ids = $urls = $tag_ids = $original_ids = [];
+ while ($row = $this->dbLayer->fetchAssoc($result)) {
+ if ($articleId <> $row['id']) {
+ $hasArticlesInList = true;
+ }
+ $titles[] = $row['title'];
+ $parent_ids[] = $row['parent_id'];
+ $urls[] = urlencode($row['url']) . (S2_USE_HIERARCHY && $row['children_exist'] ? '/' : '');
+ $tag_ids[] = $row['tag_id'];
+ $original_ids[] = $row['id'];
+ }
+
+ if (\count($urls) === 0) {
+ return '';
+ }
+
+ if ($hasArticlesInList) {
+ $urls = \Model::get_group_url($parent_ids, $urls);
+ }
+
+ // Sorting all obtained article links into groups by each tag
+ $art_by_tags = [];
+
+ foreach ($urls as $k => $url) {
+ $art_by_tags[$tag_ids[$k]][] = [
+ 'title' => $titles[$k],
+ 'link' => $url,
+ 'is_current' => $original_ids[$k] == $articleId,
+ ];
+ }
+
+ // Remove tags that have only one article
+ foreach ($art_by_tags as $tag_id => $title_array) {
+ if (\count($title_array) <= 1) {
+ unset($art_by_tags[$tag_id]);
+ }
+ }
+
+ $output = [];
+ foreach ($art_by_tags as $tag_id => $articles) {
+ $output[] = $this->viewer->render('menu_block', array(
+ 'title' => sprintf(\Lang::get('With this tag'), '' . $tag_names[$tag_id] . ''),
+ 'menu' => $articles,
+ 'class' => 'article_tags',
+ ));
+ }
+
+ return implode("\n", $output);
+ }
+
+
+ private function get_tags(int $articleId): string
+ {
+ $query = [
+ 'SELECT' => 'name, url',
+ 'FROM' => 'tags AS t',
+ 'JOINS' => [
+ [
+ 'INNER JOIN' => 'article_tag AS at',
+ 'ON' => 'at.tag_id = t.tag_id'
+ ]
+ ],
+ 'WHERE' => 'at.article_id = ' . $articleId
+ ];
+ $result = $this->dbLayer->buildAndQuery($query);
+
+ $tags = [];
+ while ($row = $this->dbLayer->fetchAssoc($result)) {
+ $tags[] = array(
+ 'title' => $row['name'],
+ 'link' => s2_link('/' . $this->tagsUrl . '/' . urlencode($row['url']) . '/'),
+ );
+ }
+
+ if (\count($tags) === 0) {
+ return '';
+ }
+
+ return $this->viewer->render('tags', [
+ 'title' => \Lang::get('Tags'),
+ 'tags' => $tags,
+ ]);
+ }
+}
diff --git a/_include/src/Template/HtmlTemplate.php b/_include/src/Template/HtmlTemplate.php
index b7cdbb8b..eb1c66b9 100644
--- a/_include/src/Template/HtmlTemplate.php
+++ b/_include/src/Template/HtmlTemplate.php
@@ -15,6 +15,7 @@ class HtmlTemplate
{
protected array $page = [];
protected array $breadCrumbs = [];
+ private array $navLinks = [];
public function __construct(
protected string $template,
@@ -71,16 +72,14 @@ public function toHttpResponse(): Response
// Content
$replace[''] = S2_SITE_NAME;
- $replace[''] = '';
- if (isset($this->page['link_navigation'])) {
- $link_navigation = [];
- foreach ($this->page['link_navigation'] as $link_rel => $link_href) {
- $link_navigation[] = '';
- }
- $replace[''] = implode("\n", $link_navigation);
+ $link_navigation = [];
+ foreach ($this->navLinks as $link_rel => $link_href) {
+ $link_navigation[] = '';
}
+ $replace[''] = implode("\n", $link_navigation);
+
$replace[''] = !empty($this->page['author']) ? '
Some new page text
', + 'id' => $id, + 'revision' => '1', + 'user_id' => '0', + 'template' => 'site.php', + 'url' => 'new_page' . $id, + ], + 'flags' => [ + 'favorite' => '1', + 'published' => '1', + 'commented' => '1', + ], + ]; + }; + + $I->sendAjaxPostRequest('/_admin/site_ajax.php?action=save', $dataProvider((string)$newId)); + $I->seeResponseCodeIsSuccessful(); + $I->dontSee('Warning! An error occurred during page saving. Copy the content to a text editor and save into a file out of caution.'); + $I->see('{"revision":2,"status":"ok","url_status":"ok"}'); + } + + // Links to related pages in section and by tags + $I->amOnPage('/section1/new_page4'); + $I->see('New Page 4', 'h1'); + $I->see('Some new page text', '#content'); + + $I->see('More in the section “Section 1”', '.header.menu_siblings'); + $I->see('New Page Title', '.menu_siblings a'); + $I->see('New Page 4', '.menu_siblings span'); + + $I->see('On the subject “tag1”', '.header.article_tags'); + $I->see('New Page Title', '.article_tags a'); + $I->see('New Page 4', '.article_tags span'); + + $I->see('See in blog', '.header.s2_blog_tags'); + $I->see('tag1', '.s2_blog_tags a'); + + // Links to sub-pages + $I->amOnPage('/section1/'); + $I->see('In this section', '.header.menu_children'); + $I->see('New Page Title', '.menu_children a'); + $I->see('New Page 4', '.menu_children a'); + + $I->see('New Page Title', 'h3.subsection'); + $I->see('New Excerpt', 'p.subsection'); + } + private function testTagsPage(AcceptanceTester $I): void { $I->stopFollowingRedirects();