support duplicatable d3forum module.

This commit is contained in:
Yoshihiro OKUMURA
2019-06-27 19:06:52 +09:00
parent 49ed0e47e6
commit b17f16d010
46 changed files with 876 additions and 840 deletions
+1 -1
View File
@@ -29,4 +29,4 @@ dl-limit-items.csv
/public/database/items.json
/public/database/file
/src/database/assets/*.json
/src/forum/assets/*.json
/src/d3forum/assets/*.json
+1 -1
View File
@@ -70,7 +70,7 @@ class MyDumpTool
$v = $textutil->html_numeric_entities($v);
$v = mb_decode_numericentity($v, array(0x0, 0x10000, 0, 0xfffff), 'UTF-8');
$data[$k] = \Normalizer::normalize(trim($v), \Normalizer::FORM_C);
} else if ($v === null) {
} elseif (null === $v) {
$data[$k] = '';
}
}
+21 -5
View File
@@ -6,14 +6,24 @@ if (!isset($XOOPS_MODULE_D3FORUM) || empty($XOOPS_MODULE_D3FORUM)) {
exit('Not configured for d3forum module'.PHP_EOL);
}
$contents = [];
foreach ($XOOPS_MODULE_D3FORUM as $d3forum) {
dumpD3Forum($d3forum);
dumpD3Forum($d3forum, $contents);
}
MyDumpTool::makeDirectory('src/d3forum');
MyDumpTool::makeDirectory('src/d3forum/assets');
MyDumpTool::saveJson('src/d3forum/assets/d3forum.json', $contents);
function dumpD3Forum($d3forum)
function dumpD3Forum($d3forum, &$contents)
{
global $xoopsDB;
$prefix = $xoopsDB->prefix();
$moduleHandler = xoops_gethandler('module');
$module = $moduleHandler->getByDirname($d3forum);
$moduleConfigHandler = xoops_gethandler('config');
$moduleConfig = $moduleConfigHandler->getConfigsByDirname($d3forum);
$table = $d3forum.'_categories';
if (!MyDumpTool::tableExists($table)) {
exit('d3forum('.$d3forum.') module not found'.PHP_EOL);
@@ -133,14 +143,20 @@ SQL;
//var_dump($posts);
var_dump(count($posts));
$moduleinfo = [
'name' => $module->get('name'),
'dirname' => $d3forum,
'message' => $moduleConfig['top_message'],
];
MyDumpTool::decode($moduleinfo);
$data = [
'module' => $moduleinfo,
'categories' => $categories,
'forums' => $forums,
'topics' => $topics,
'posts' => $posts,
];
MyDumpTool::makeDirectory('src/'.$d3forum);
MyDumpTool::makeDirectory('src/'.$d3forum.'/assets');
MyDumpTool::saveJson('src/'.$d3forum.'/assets/forum.json', $data);
$contents[] = $data;
}
+10 -10
View File
@@ -302,12 +302,12 @@ while ($row = $xoopsDB->fetchArray($res)) {
$items[] = $row;
$simpfurl = '';
if ($row['doi'] != '' && isset($simpflink['id'][$row['doi']])) {
if ('' != $row['doi'] && isset($simpflink['id'][$row['doi']])) {
$simpfurl = $simpflink['id'][$row['doi']];
} else if (isset($simpflink['item_id'][$row['item_id']])) {
} elseif (isset($simpflink['item_id'][$row['item_id']])) {
$simpfurl = $simpflink['item_id'][$row['item_id']];
}
if ($simpfurl !== '') {
if ('' !== $simpfurl) {
$simpfurls[] = [
'id' => $row['item_id'],
'url' => $simpfurl,
@@ -376,13 +376,13 @@ while ($row = $xoopsDB->fetchArray($res)) {
@mkdir($file_dir);
}
//if (!file_exists($file_dir.'/'.$file_name)) {
if (!MyDumpTool::fileCopy($src_file, $file_dir.'/'.$file_name)) {
echo 'failed to copy file: '.$basePath.'/'.$file_id.PHP_EOL;
}
$htaccess = $file_dir.'/.htaccess';
$xfilename = str_replace(' ', '\\ ', $file_name);
$data = "RewriteEngine On\nRewriteBase /database/file/$file_id\nRewriteCond %{REQUEST_FILENAME} !-f\nRewriteRule .* $xfilename [R=301,L]\n";
file_put_contents($htaccess, $data);
if (!MyDumpTool::fileCopy($src_file, $file_dir.'/'.$file_name)) {
echo 'failed to copy file: '.$basePath.'/'.$file_id.PHP_EOL;
}
$htaccess = $file_dir.'/.htaccess';
$xfilename = str_replace(' ', '\\ ', $file_name);
$data = "RewriteEngine On\nRewriteBase /database/file/$file_id\nRewriteCond %{REQUEST_FILENAME} !-f\nRewriteRule .* $xfilename [R=301,L]\n";
file_put_contents($htaccess, $data);
//}
if ('preview' == $file_type) {
$thumbnail_file = $file_dir.'.png';
+14 -5
View File
@@ -1,8 +1,10 @@
import React from 'react';
import { Route, Switch } from 'react-router-dom';
import { MultiLang } from '../config';
import Config, { MultiLang } from '../config';
import D3Forum from '../d3forum/D3Forum';
import D3ForumXoopsPathRedirect from '../d3forum/D3ForumXoopsPathRedirect';
import Database from '../database/Database';
import Forum from '../forum/Forum';
import DatabaseXoopsPathRedirect from '../database/DatabaseXoopsPathRedirect';
import About from './About';
import XoopsPathRedirect from './XoopsPathRedirect';
@@ -11,12 +13,19 @@ interface Props {
}
const MainContent = (props: Props) => {
const { lang } = props;
return (
<div className="mainContent">
<Switch>
<Route path="/database" render={() => <Database {...props} />} />
<Route path="/forum" render={() => <Forum {...props} />} />
<Route exact path="/about" render={() => <About {...props} />} />
<Route path="/database" render={() => <Database lang={lang} />} />
{Config.D3FORUM_MODULES.map((name) =>
<Route key={name} path={'/' + name} render={() => <D3Forum lang={lang} name={name} />} />
)}
<Route exact path="/about" render={() => <About lang={lang} />} />
<Route path="/modules/xoonips" render={() => <DatabaseXoopsPathRedirect lang={lang} />} />
{Config.D3FORUM_MODULES.map((name) =>
<Route key={name} path={'/modules/' + name} render={() => <D3ForumXoopsPathRedirect lang={lang} name={name} />} />
)}
<Route component={XoopsPathRedirect} />
</Switch>
</div>
+1 -140
View File
@@ -1,7 +1,6 @@
import React, { Component } from 'react';
import { Redirect, RouteComponentProps } from 'react-router';
import { MultiLang } from '../config';
import funcs from '../functions';
import PageNotFound from './lib/PageNotFound';
interface Params {
@@ -16,149 +15,11 @@ interface Props extends RouteComponentProps<Params> {
class XoopsPathRedirect extends Component<Props> {
getRedirectUrl() {
const { pathname, hash } = this.props.location;
const query = new URLSearchParams(this.props.location.search);
const { pathname } = this.props.location;
switch (pathname || '') {
case '/index.php': {
return '/';
}
case '/modules/xoonips':
case '/modules/xoonips/index.php': {
return '/database';
}
case '/modules/xoonips/detail.php': {
const id = query.get('id');
if (id !== null) {
return '/database/item/id/' + funcs.escape(id);
}
const itemId = query.get('item_id');
if (itemId !== null && itemId.match(/^\d+$/) !== null) {
return '/database/item/' + funcs.escape(itemId);
}
return '';
}
case '/modules/xoonips/listitem.php': {
const indexId = query.get('index_id');
if (indexId !== null && indexId.match(/^\d+$/) !== null) {
const params = new URLSearchParams();
const map: any = {
orderby: { key: 'orderby', isNumber: false },
order_dir: { key: 'order_dir', isNumber: true },
itemcount: { key: 'itemcount', isNumber: true },
page: { key: 'page', isNumber: true }
};
for (let k in map) {
const v = query.get(k);
if (v === null || v.length === 0) {
continue;
}
if (map[k].isNumber && v.match(/^\d+$/) !== null) {
continue;
}
params.set(map[k].key, v);
}
const paramStr = params.toString();
return '/database/list/' + funcs.escape(indexId) + (paramStr.length > 0 ? '?' + paramStr : '');
}
break;
}
case '/modules/xoonips/itemselect.php': {
const op = query.get('op');
if (op === null) {
break;
}
switch (op) {
case 'quicksearch': {
const keyword = query.get('keyword');
const itemType = query.get('search_itemtype');
if (keyword === null || itemType === null || keyword === '') {
return '';
}
const type = itemType.replace('xnp', '');
if (itemType !== 'basic' && itemType !== 'all' && itemType.match(/^xnp.+/) === null) {
return '';
}
const params = new URLSearchParams({ type, keyword });
const map: any = {
orderby: { key: 'orderby', isNumber: false },
orderdir: { key: 'order_dir', isNumber: true },
item_per_page: { key: 'itemcount', isNumber: true },
page: { key: 'page', isNumber: true }
};
for (let k in map) {
const v = query.get(k);
if (v === null || v.length === 0) {
continue;
}
if (map[k].isNumber && v.match(/^\d+$/) !== null) {
continue;
}
params.set(map[k].key, v);
}
return '/database/search?' + params.toString();
}
case 'itemtypesearch': {
const itemType = query.get('search_itemtype');
if (itemType === null || itemType.match(/^xnp.+/) === null) {
return '';
}
const type = itemType.replace('xnp', '');
return '/database/search/itemtype/' + funcs.escape(type);
}
case 'itemsubtypesearch': {
let type = '';
let subtype = '';
query.forEach((v, k) => {
if (k.match(/^xnp[a-z]+$/) !== null && !!v) {
type = k.replace('xnp', '');
return;
}
})
if (type === '') {
return '';
}
query.forEach((v, k) => {
if (k.match(`^xnp${type}_.+$`) !== null && !!v) {
subtype = v;
return;
}
});
if (subtype === '') {
return '';
}
return '/database/search/itemtype/' + funcs.escape(type) + '/' + funcs.escape(subtype);
}
}
return '';
}
case '/modules/xoonips/advanced_search.php': {
return '/database/advanced';
}
case '/modules/forum':
case '/modules/forum/index.php': {
const catId = query.get('cat_id');
if (catId !== null && catId.match(/^\d+$/) !== null) {
return '/forum/category/' + funcs.escape(catId);
}
const forumId = query.get('forum_id');
if (forumId !== null && forumId.match(/^\d+$/) !== null) {
return '/forum/forum/' + funcs.escape(forumId);
}
const topicId = query.get('topic_id');
if (topicId !== null && topicId.match(/^\d+$/) !== null) {
const matches = hash.match(/^#post_id(\d+)$/);
return '/forum/topic/' + funcs.escape(topicId) + (matches === null ? '' : '#postId' + matches[1]);
}
const postId = query.get('post_id');
if (postId !== null && postId.match(/^\d+$/) !== null) {
return '/forum/post/' + funcs.escape(postId);
}
const catIds = query.get('cat_ids');
if (catIds !== null && catIds.match(/^\d+$/) !== null) {
return '/forum/category/' + funcs.escape(catIds);
}
return '/forum/';
}
case '/modules/contact':
case '/modules/mailform':
case '/modules/mailform/index.php': {
+2
View File
@@ -2,6 +2,7 @@ const SITE_TITLE = '[en]CBSN platform[/en][ja]包括脳プラットフォーム[
const SITE_SLOGAN = 'XooNIps for CBSN';
const GOOGLE_ANALYTICS_TRACKING_ID = 'UA-2780809-1';
const XOONIPS_ITEMTYPES = ['tool', 'paper', 'presentation', 'book', 'data', 'url', 'files'];
const D3FORUM_MODULES = ['forum'];
export type MultiLang = 'en' | 'ja';
@@ -10,6 +11,7 @@ const Config = {
SITE_SLOGAN,
GOOGLE_ANALYTICS_TRACKING_ID,
XOONIPS_ITEMTYPES,
D3FORUM_MODULES,
}
export default Config;
+40
View File
@@ -0,0 +1,40 @@
import React from 'react';
import Helmet from 'react-helmet';
import { Route, RouteComponentProps, Switch } from 'react-router-dom';
import PageNotFound from '../common/lib/PageNotFound';
import { MultiLang } from '../config';
import Functions from '../functions';
import './assets/d3forum_common.css';
import './assets/d3forum_main.css';
import D3ForumCategory from './D3ForumCategory';
import D3ForumForum from './D3ForumForum';
import D3ForumPost from './D3ForumPost';
import D3ForumTop from './D3ForumTop';
import D3ForumTopic from './D3ForumTopic';
import D3ForumUtils from './lib/D3ForumUtils';
interface Props {
lang: MultiLang;
name: string;
}
const D3Forum = (props: Props) => {
const { lang, name } = props;
return (
<>
<Helmet>
<title>{D3ForumUtils.getTitle(name, lang)} - {Functions.siteTitle(lang)}</title>
</Helmet>
<Switch>
<Route exact path={'/' + name} render={() => <D3ForumTop {...props} />} />
<Route exact path={'/' + name + '/category/:id'} render={(props: RouteComponentProps<{ id: string }>) => <D3ForumCategory lang={lang} name={name} {...props} />} />
<Route exact path={'/' + name + '/forum/:id'} render={(props: RouteComponentProps<{ id: string }>) => <D3ForumForum lang={lang} name={name} {...props} />} />
<Route exact path={'/' + name + '/topic/:id'} render={(props: RouteComponentProps<{ id: string }>) => <D3ForumTopic lang={lang} name={name} {...props} />} />
<Route exact path={'/' + name + '/post/:id'} render={(props: RouteComponentProps<{ id: string }>) => <D3ForumPost lang={lang} name={name} {...props} />} />
<Route component={PageNotFound} />
</Switch>
</>
);
}
export default D3Forum;
@@ -11,7 +11,7 @@ import imageCategory1 from './assets/images/category_1.gif';
import imageForum0 from './assets/images/forum_0.gif';
import imageForum1 from './assets/images/forum_1.gif';
import CategoryJump from './lib/CategoryJump';
import ForumUtils, { ForumCategoryData, ForumForumData } from './lib/ForumUtils';
import D3ForumUtils, { D3ForumCategoryData, D3ForumForumData } from './lib/D3ForumUtils';
import PostIcon from './lib/PostIcon';
interface Params {
@@ -19,22 +19,24 @@ interface Params {
}
interface Props extends RouteComponentProps<Params> {
lang: MultiLang;
name: string;
}
interface ForumCategoryProps {
interface D3ForumCategoryProps {
lang: MultiLang;
name: string;
catId: number;
}
const ForumCategorySubCategory = (props: ForumCategoryProps) => {
const { lang, catId } = props;
const D3ForumCategorySubCategory = (props: D3ForumCategoryProps) => {
const { lang, name, catId } = props;
const LABEL_HEADER_TITLE = Functions.mlang('[en]Subcategories[/en][ja]サブカテゴリ[/ja]', lang);
const LABEL_TOTAL_TOPICS = Functions.mlang('[en]Total topics[/en][ja]総トピック数[/ja]', lang);
const LABEL_TOTAL_POSTS = Functions.mlang('[en]Total posts[/en][ja]総投稿数[/ja]', lang);
const LABEL_LATEST_POST = Functions.mlang('[en]Latest post[/en][ja]最新投稿[/ja]', lang);
const subCategories = ForumUtils.getSubCategories(catId).map((category: ForumCategoryData) => {
const subCategories = D3ForumUtils.getSubCategories(name, catId).map((category: D3ForumCategoryData) => {
const title = Functions.mlang(category.cat_title, lang);
const url = ForumUtils.getCategoryUrl(category.cat_id);
const url = D3ForumUtils.getCategoryUrl(name, category.cat_id);
const image = category.cat_posts_count === 0 ? imageCategory0 : imageCategory1;
const latest = category.cat_last_post_time === 0 ? '' : ', ' + LABEL_LATEST_POST + ': ' + moment(new Date(category.cat_last_post_time * 1000)).format('Y/M/D H:m');
return (
@@ -58,8 +60,8 @@ const ForumCategorySubCategory = (props: ForumCategoryProps) => {
);
}
const ForumCategoryForum = (props: ForumCategoryProps) => {
const { lang, catId } = props;
const D3ForumCategoryForum = (props: D3ForumCategoryProps) => {
const { lang, name, catId } = props;
const LABEL_HEADER_TITLE = Functions.mlang('[en]Forum[/en][ja]フォーラム[/ja]', lang);
const LABEL_FORUM = Functions.mlang('[en]Forum[/en][ja]フォーラム[/ja]', lang);
const LABEL_TOPICS = Functions.mlang('[en]Topics[/en][ja]トピック数[/ja]', lang);
@@ -67,14 +69,14 @@ const ForumCategoryForum = (props: ForumCategoryProps) => {
const LABEL_LATEST_POST = Functions.mlang('[en]Latest post[/en][ja]最新投稿[/ja]', lang);
const LABEL_NEW_POSTS = Functions.mlang('[en]New posts[/en][ja]新しい投稿があります[/ja]', lang);
const LABEL_NO_NEW_POSTS = Functions.mlang('[en]No new posts[/en][ja]新しい投稿はありません[/ja]', lang);
const forums = ForumUtils.getForums(catId).map((forum: ForumForumData, idx: number) => {
const forums = D3ForumUtils.getForums(name, catId).map((forum: D3ForumForumData, idx: number) => {
const evenodd = idx % 2 === 0 ? 'even' : 'odd';
const title = Functions.mlang(forum.forum_title, lang);
const desc = Functions.mlang(forum.forum_desc, lang);
const url = ForumUtils.getForumUrl(forum.forum_id);
const url = D3ForumUtils.getForumUrl(name, forum.forum_id);
const image = forum.forum_posts_count === 0 ? imageForum0 : imageForum1;
const getPoster = (postId: number) => {
const post = ForumUtils.getPost(postId);
const post = D3ForumUtils.getPost(name, postId);
if (post === null) {
return null;
}
@@ -84,7 +86,7 @@ const ForumCategoryForum = (props: ForumCategoryProps) => {
{moment(new Date(post.post_time * 1000)).format('Y/M/D H:m')}<br />
{post.uid_uname}
&nbsp;
<Link to={ForumUtils.getPostUrl(post.post_id)}>
<Link to={D3ForumUtils.getPostUrl(name, post.post_id)}>
<PostIcon lang={lang} post={post} title={subject} />
</Link>
</>
@@ -138,14 +140,14 @@ const ForumCategoryForum = (props: ForumCategoryProps) => {
);
}
const ForumCategory = (props: Props) => {
const { lang } = props;
const D3ForumCategory = (props: Props) => {
const { lang, name } = props;
const params = props.match.params;
if (params.id === '') {
return <PageNotFound lang={lang} />;
}
const catId = parseInt(props.match.params.id, 10);
const category = ForumUtils.getCategory(catId);
const category = D3ForumUtils.getCategory(name, catId);
if (category === null) {
return <PageNotFound lang={lang} />;
}
@@ -153,17 +155,17 @@ const ForumCategory = (props: Props) => {
return (
<>
<Helmet>
<title>{title} - {ForumUtils.getTitle(lang)} - {Functions.siteTitle(lang)}</title>
<title>{title} - {D3ForumUtils.getTitle(name, lang)} - {Functions.siteTitle(lang)}</title>
</Helmet>
<div className="d3f_breadcrumbs">
<Link to={ForumUtils.getIndexUrl()}>{Functions.mlang('[en]Top[/en][ja]トップ[/ja]', lang)}</Link>
<Link to={D3ForumUtils.getIndexUrl(name)}>{Functions.mlang('[en]Top[/en][ja]トップ[/ja]', lang)}</Link>
</div>
<h1 className="d3f_title">{title}</h1>
<ForumCategorySubCategory lang={lang} catId={catId} />
<ForumCategoryForum lang={lang} catId={catId} />
<CategoryJump lang={lang} catId={catId} />
<D3ForumCategorySubCategory lang={lang} name={name} catId={catId} />
<D3ForumCategoryForum lang={lang} name={name} catId={catId} />
<CategoryJump lang={lang} name={name} catId={catId} />
</>
);
}
export default ForumCategory;
export default D3ForumCategory;
@@ -15,7 +15,7 @@ import imageTopicStatusMarked1 from './assets/images/topic_status_marked1.gif';
import imageTopicStatusSolved0 from './assets/images/topic_status_solved0.gif';
import imageTopicSticky0 from './assets/images/topic_sticky0.gif';
import ForumJump from './lib/ForumJump';
import ForumUtils, { ForumPostData, ForumPostSortOrder, ForumTopicData } from './lib/ForumUtils';
import D3ForumUtils, { D3ForumPostData, D3ForumPostSortOrder, D3ForumTopicData } from './lib/D3ForumUtils';
import PostIcon from './lib/PostIcon';
interface Params {
@@ -23,34 +23,35 @@ interface Params {
}
interface Props extends RouteComponentProps<Params> {
lang: MultiLang;
name: string;
}
const ForumForum = (props: Props) => {
const { lang } = props;
const D3ForumForum = (props: Props) => {
const { lang, name } = props;
const params = props.match.params;
if (params.id === '') {
return <PageNotFound lang={lang} />;
}
const forumId = parseInt(props.match.params.id, 10);
const forum = ForumUtils.getForum(forumId);
const forum = D3ForumUtils.getForum(name, forumId);
if (forum === null) {
return <PageNotFound lang={lang} />;
}
const category = ForumUtils.getCategory(forum.cat_id);
const category = D3ForumUtils.getCategory(name, forum.cat_id);
if (category === null) {
return <PageNotFound lang={lang} />;
}
const topics = ForumUtils.getTopics(forum.forum_id);
const topics = D3ForumUtils.getTopics(name, forum.forum_id);
const title = Functions.mlang(forum.forum_title, lang);
return (
<>
<Helmet>
<title>{title} - {ForumUtils.getTitle(lang)} - {Functions.siteTitle(lang)}</title>
<title>{title} - {D3ForumUtils.getTitle(name, lang)} - {Functions.siteTitle(lang)}</title>
</Helmet>
<div className="d3f_breadcrumbs">
<HashLink to={ForumUtils.getIndexUrl()}>{Functions.mlang('[en]Top[/en][ja]トップ[/ja]', lang)}</HashLink>
<HashLink to={D3ForumUtils.getIndexUrl(name)}>{Functions.mlang('[en]Top[/en][ja]トップ[/ja]', lang)}</HashLink>
&nbsp;&gt;&nbsp;
<HashLink to={ForumUtils.getCategoryUrl(category.cat_id)}>{Functions.mlang(category.cat_title, lang)}</HashLink>
<HashLink to={D3ForumUtils.getCategoryUrl(name, category.cat_id)}>{Functions.mlang(category.cat_title, lang)}</HashLink>
</div>
<h1 className="d3f_title">{title}</h1>
<p className="d3f_welcome">{Functions.mlang(forum.forum_desc, lang)}</p>
@@ -64,21 +65,21 @@ const ForumForum = (props: Props) => {
</tr>
</thead>
<tbody>
{topics.map((topic: ForumTopicData, idx: number) => {
const postStart = ForumUtils.getPost(topic.topic_first_post_id);
const postLast = ForumUtils.getPost(topic.topic_last_post_id);
{topics.map((topic: D3ForumTopicData, idx: number) => {
const postStart = D3ForumUtils.getPost(name, topic.topic_first_post_id);
const postLast = D3ForumUtils.getPost(name, topic.topic_last_post_id);
if (postStart === null || postLast === null) {
return null;
}
const postTitleStart = Functions.mlang(postStart.subject, lang);
const imageTopic = topic.topic_invisible !== 0 ? imageTopicInvisible : topic.topic_sticky !== 0 ? imageTopicSticky0 : imageTopic10;
const evenodd = idx % 2 === 0 ? 'even' : 'odd';
const poster = (post: ForumPostData) => {
const poster = (post: D3ForumPostData) => {
const title = Functions.mlang(post.subject, lang);
return (
<>
{moment(new Date(post.post_time * 1000)).format('YYYY/M/D')}<br />
{post.uid_uname} <HashLink to={ForumUtils.getPostUrl(post.post_id)}><PostIcon lang={lang} post={post} title={title} /></HashLink>
{post.uid_uname} <HashLink to={D3ForumUtils.getPostUrl(name, post.post_id)}><PostIcon lang={lang} post={post} title={title} /></HashLink>
</>
);
}
@@ -90,7 +91,7 @@ const ForumForum = (props: Props) => {
<td className="d3f_topictitle">
{topic.topic_solved === 0 && <img src={imageTopicStatusSolved0} alt="" />}
{topic.topic_locked !== 0 && <img src={imageTopicStatusLocked1} alt="" />}
<HashLink to={ForumUtils.getTopicUrl(topic.topic_id, ForumPostSortOrder.TREE, postLast.post_id)}>{postTitleStart}</HashLink>
<HashLink to={D3ForumUtils.getTopicUrl(name, topic.topic_id, D3ForumPostSortOrder.TREE, postLast.post_id)}>{postTitleStart}</HashLink>
</td>
<td>{topic.topic_posts_count - 1}</td>
<td className="d3f_posters">
@@ -118,9 +119,9 @@ const ForumForum = (props: Props) => {
<li><img src={imageTopicStatusMarked1} alt="" width="18" height="18" /> = {Functions.mlang('[en]Marked[/en][ja]未解決トピック[/ja]', lang)}</li>
</ul>
</div>
<ForumJump lang={lang} forumId={forum.forum_id} />
<ForumJump lang={lang} name={name} forumId={forum.forum_id} />
</>
);
}
export default ForumForum;
export default D3ForumForum;
+80
View File
@@ -0,0 +1,80 @@
import React from 'react';
import Helmet from 'react-helmet';
import { RouteComponentProps } from 'react-router';
import { Link } from 'react-router-dom';
import PageNotFound from '../common/lib/PageNotFound';
import { MultiLang } from '../config';
import Functions from '../functions';
import ForumJump from './lib/ForumJump';
import D3ForumUtils, { D3ForumPostSortOrder, D3ForumPostData } from './lib/D3ForumUtils';
import Post from './lib/Post';
import PostsTree from './lib/PostsTree';
interface Params {
id: string;
}
interface Props extends RouteComponentProps<Params> {
lang: MultiLang;
name: string;
}
const D3ForumPost = (props: Props) => {
const { lang, name } = props;
const params = props.match.params;
if (params.id === '') {
return <PageNotFound lang={lang} />;
}
const postId = parseInt(props.match.params.id, 10);
const post = D3ForumUtils.getPost(name, postId);
if (post === null) {
return <PageNotFound lang={lang} />;
}
const topic = D3ForumUtils.getTopic(name, post.topic_id);
if (topic === null) {
return <PageNotFound lang={lang} />;
}
const forum = D3ForumUtils.getForum(name, topic.forum_id);
if (forum === null) {
return <PageNotFound lang={lang} />;
}
const category = D3ForumUtils.getCategory(name, forum.cat_id);
if (category === null) {
return <PageNotFound lang={lang} />;
}
const posts = D3ForumUtils.getPosts(name, topic.topic_id, D3ForumPostSortOrder.TREE);
const idx = posts.findIndex((post: D3ForumPostData) => {
return post.post_id === postId;
});
const prevPostId = idx <= 0 ? 0 : posts[idx - 1].post_id;
const nextPostId = (idx + 1) === posts.length ? 0 : posts[idx + 1].post_id;
const title = Functions.mlang(post.subject, lang);
return (
<>
<Helmet>
<title>{title} - {D3ForumUtils.getTitle(name, lang)} - {Functions.siteTitle(lang)}</title>
</Helmet>
<div className="d3f_breadcrumbs">
<Link to={D3ForumUtils.getIndexUrl(name)}>{Functions.mlang('[en]Top[/en][ja]トップ[/ja]', lang)}</Link>
&nbsp;&gt;&nbsp;
<Link to={D3ForumUtils.getCategoryUrl(name, category.cat_id)}>{Functions.mlang(category.cat_title, lang)}</Link>
&nbsp;&gt;&nbsp;
<Link to={D3ForumUtils.getForumUrl(name, forum.forum_id)}>{Functions.mlang(forum.forum_title, lang)}</Link>
&nbsp;&gt;&nbsp;
<Link to={D3ForumUtils.getTopicUrl(name, topic.topic_id, D3ForumPostSortOrder.TREE, null)}>{Functions.mlang(topic.topic_title, lang)}</Link>
</div>
<h1 className="d3f_title">{title}</h1>
<PostsTree lang={lang} name={name} posts={posts} postId={postId} />
<br />
<p className="d3f_topicinfo">
<Link to={D3ForumUtils.getTopicUrl(name, topic.topic_id, D3ForumPostSortOrder.TREE, null)}>{Functions.mlang('[en]List posts in the topic[/en][ja]このトピックの投稿一覧へ[/ja]', lang)}</Link>
</p>
<div className="d3f_wrap">
<Post lang={lang} name={name} post={post} prevPostId={prevPostId} nextPostId={nextPostId} inTopic={false} />
</div>
<ForumJump lang={lang} name={name} forumId={forum.forum_id} />
</>
);
}
export default D3ForumPost;
@@ -8,29 +8,32 @@ import imageCategory1 from './assets/images/category_1.gif';
import imageForum0 from './assets/images/forum_0.gif';
import imageForum1 from './assets/images/forum_1.gif';
import ForumJump from './lib/ForumJump';
import ForumUtils, { ForumCategoryData, ForumForumData } from './lib/ForumUtils';
import D3ForumUtils, { D3ForumCategoryData, D3ForumForumData } from './lib/D3ForumUtils';
import XoopsCode from '../common/lib/XoopsCode';
import PageNotFound from '../common/lib/PageNotFound';
export interface Props {
lang: MultiLang;
name: string;
}
interface ForumTopSubCategoryProps extends Props {
interface D3ForumTopSubCategoryProps extends Props {
catId: number;
}
interface ForumTopForumProps extends Props {
interface D3ForumTopForumProps extends Props {
catId: number;
}
const ForumTopForum = (props: ForumTopForumProps) => {
const { catId, lang } = props;
const D3ForumTopForum = (props: D3ForumTopForumProps) => {
const { lang, name, catId } = props;
const LABEL_HEADER_TITLE = Functions.mlang('[en]Forum[/en][ja]フォーラム[/ja]', lang);
const LABEL_TOPICS = Functions.mlang('[en]Topics[/en][ja]トピック数[/ja]', lang);
const LABEL_POSTS = Functions.mlang('[en]Posts[/en][ja]投稿数[/ja]', lang);
const LABEL_LATEST_POST = Functions.mlang('[en]Latest post[/en][ja]最新投稿[/ja]', lang);
const forums = ForumUtils.getForums(catId).map((forum: ForumForumData) => {
const forums = D3ForumUtils.getForums(name, catId).map((forum: D3ForumForumData) => {
const title = Functions.mlang(forum.forum_title, lang);
const url = ForumUtils.getForumUrl(forum.forum_id);
const url = D3ForumUtils.getForumUrl(name, forum.forum_id);
const image = forum.forum_posts_count === 0 ? imageForum0 : imageForum1;
const latest = forum.forum_last_post_time === 0 ? '' : ', ' + LABEL_LATEST_POST + ': ' + moment(new Date(forum.forum_last_post_time * 1000)).format('Y/M/D H:m');
return (
@@ -54,15 +57,15 @@ const ForumTopForum = (props: ForumTopForumProps) => {
);
}
const ForumTopSubCategory = (props: ForumTopSubCategoryProps) => {
const { catId, lang } = props;
const D3ForumTopSubCategory = (props: D3ForumTopSubCategoryProps) => {
const { lang, name, catId } = props;
const LABEL_HEADER_TITLE = Functions.mlang('[en]Subcategories[/en][ja]サブカテゴリ[/ja]', lang);
const LABEL_TOTAL_TOPICS = Functions.mlang('[en]Total topics[/en][ja]総トピック数[/ja]', lang);
const LABEL_TOTAL_POSTS = Functions.mlang('[en]Total posts[/en][ja]総投稿数[/ja]', lang);
const LABEL_LATEST_POST = Functions.mlang('[en]Latest post[/en][ja]最新投稿[/ja]', lang);
const subCategories = ForumUtils.getSubCategories(catId).map((category: ForumCategoryData) => {
const subCategories = D3ForumUtils.getSubCategories(name, catId).map((category: D3ForumCategoryData) => {
const title = Functions.mlang(category.cat_title, lang);
const url = ForumUtils.getCategoryUrl(category.cat_id);
const url = D3ForumUtils.getCategoryUrl(name, category.cat_id);
const image = category.cat_posts_count === 0 ? imageCategory0 : imageCategory1;
const latest = category.cat_last_post_time === 0 ? '' : ', ' + LABEL_LATEST_POST + ': ' + moment(new Date(category.cat_last_post_time * 1000)).format('Y/M/D H:m');
return (
@@ -86,15 +89,15 @@ const ForumTopSubCategory = (props: ForumTopSubCategoryProps) => {
);
}
const ForumTopCategory = (props: Props) => {
const { lang } = props;
const D3ForumTopCategory = (props: Props) => {
const { lang, name } = props;
const LABEL_TOTAL_TOPICS = Functions.mlang('[en]Total topics[/en][ja]総トピック数[/ja]', lang);
const LABEL_TOTAL_POSTS = Functions.mlang('[en]Total posts[/en][ja]総投稿数[/ja]', lang);
const LABEL_LATEST_POST = Functions.mlang('[en]Latest post[/en][ja]最新投稿[/ja]', lang);
const categories = ForumUtils.getCategories().map((category: ForumCategoryData) => {
const categories = D3ForumUtils.getCategories(name).map((category: D3ForumCategoryData) => {
const title = Functions.mlang(category.cat_title, lang);
const image = category.cat_posts_count === 0 ? imageCategory0 : imageCategory1;
const url = ForumUtils.getCategoryUrl(category.cat_id);
const url = D3ForumUtils.getCategoryUrl(name, category.cat_id);
const latest = category.cat_last_post_time === 0 ? '' : ', ' + LABEL_LATEST_POST + ': ' + moment(new Date(category.cat_last_post_time * 1000)).format('Y/M/D H:m');
return (
<Fragment key={category.cat_id}>
@@ -104,8 +107,8 @@ const ForumTopCategory = (props: Props) => {
</h2>
<p>{LABEL_TOTAL_TOPICS}: {category.cat_topics_count}, {LABEL_TOTAL_POSTS}: {category.cat_posts_count}{latest}</p>
</div>
<ForumTopSubCategory lang={lang} catId={category.cat_id} />
<ForumTopForum lang={lang} catId={category.cat_id} />
<D3ForumTopSubCategory lang={lang} name={name} catId={category.cat_id} />
<D3ForumTopForum lang={lang} name={name} catId={category.cat_id} />
<div className="d3f_info_ctrl">&nbsp;
</div>
</Fragment>
@@ -121,25 +124,26 @@ const ForumTopCategory = (props: Props) => {
);
}
const ForumTop = (props: Props) => {
const { lang } = props;
const D3ForumTop = (props: Props) => {
const { lang, name } = props;
const LABEL_TOTAL_TOPICS = Functions.mlang('[en]Total topics:[/en][ja]総トピック数:[/ja]', lang);
const LABEL_TOTAL_POSTS = Functions.mlang('[en]Total posts:[/en][ja]総投稿数:[/ja]', lang);
const LABEL_NEW_POSTS = Functions.mlang('[en]New posts[/en][ja]新しい投稿があります[/ja]', lang);
const LABEL_NO_NEW_POSTS = Functions.mlang('[en]No new posts[/en][ja]新しい投稿はありません[/ja]', lang);
const totalTopics = ForumUtils.getTotalTopics();
const totalPosts = ForumUtils.getTotalPosts();
const d3forum = D3ForumUtils.getModule(name);
if (d3forum === null) {
return <PageNotFound lang={lang} />;
}
return (
<div>
<h1 className="d3f_title"></h1>
<p className="d3f_welcome"></p>
<XoopsCode lang={lang} text={d3forum.message} dohtml={true} />
<dl className="d3f_bbsinfo">
<dt>{LABEL_TOTAL_TOPICS} </dt>
<dd>{totalTopics}, </dd>
<dd>{D3ForumUtils.getTotalTopics(name)}, </dd>
<dt>{LABEL_TOTAL_POSTS} </dt>
<dd>{totalPosts}</dd>
<dd>{D3ForumUtils.getTotalPosts(name)}</dd>
</dl>
<ForumTopCategory lang={lang} />
<D3ForumTopCategory lang={lang} name={name} />
<div className="d3f_iconexps clearfix">
<ul className="d3f_iconexp">
<li><img src={imageForum1} alt="" width="18" height="18" /> = {LABEL_NEW_POSTS}</li>
@@ -149,9 +153,9 @@ const ForumTop = (props: Props) => {
</ul>
</div>
<ForumJump lang={lang} forumId={0} />
<ForumJump lang={lang} name={name} forumId={0} />
</div>
);
}
export default ForumTop;
export default D3ForumTop;
+125
View File
@@ -0,0 +1,125 @@
import React, { Component } from 'react';
import Helmet from 'react-helmet';
import { RouteComponentProps } from 'react-router';
import { HashLink } from 'react-router-hash-link';
import Loading from '../common/lib/Loading';
import PageNotFound from '../common/lib/PageNotFound';
import { MultiLang } from '../config';
import Functions from '../functions';
import D3ForumUtils, { D3ForumCategoryData, D3ForumForumData, D3ForumPostData, D3ForumPostSortOrder, D3ForumTopicData } from './lib/D3ForumUtils';
import ForumJump from './lib/ForumJump';
import Post from './lib/Post';
import PostsTree from './lib/PostsTree';
interface Params {
id: string;
}
interface Props extends RouteComponentProps<Params> {
lang: MultiLang;
name: string;
}
interface State {
loading: boolean;
name: string;
topicId: number;
order: D3ForumPostSortOrder;
}
class D3ForumTopic extends Component<Props, State> {
private topic: D3ForumTopicData | null = null;
private forum: D3ForumForumData | null = null;
private category: D3ForumCategoryData | null = null;
private posts: D3ForumPostData[] = [];
constructor(props: Props) {
super(props);
const { name } = props;
this.state = {
loading: true,
name: name,
topicId: 0,
order: D3ForumPostSortOrder.TREE,
};
}
static getDerivedStateFromProps(nextProps: Props, prevState: State) {
const params = new URLSearchParams(nextProps.location.search);
const topicId = parseInt(nextProps.match.params.id, 10);
const order_ = params.get('order');
const order = (order_ !== null && order_.match(/(0|1|2)/)) ? parseInt(order_, 10) as D3ForumPostSortOrder : D3ForumPostSortOrder.TREE;
if (nextProps.name !== prevState.name || prevState.topicId !== topicId || prevState.order !== order) {
return { loading: true, name: nextProps.name, topicId, order };
}
return null;
}
componentDidMount() {
this.load();
}
componentDidUpdate() {
this.load();
}
load() {
const { name } = this.props;
if (this.state.loading) {
this.topic = D3ForumUtils.getTopic(name, this.state.topicId);
this.forum = D3ForumUtils.getForum(name, this.topic === null ? 0 : this.topic.forum_id);
this.category = D3ForumUtils.getCategory(name, this.forum === null ? 0 : this.forum.cat_id);
this.posts = D3ForumUtils.getPosts(name, this.state.topicId, this.state.order);
this.setState({ loading: false });
}
}
render() {
const { lang, name } = this.props;
if (this.state.loading) {
return <Loading />;
}
if (this.topic === null || this.forum === null || this.category === null) {
return <PageNotFound lang={lang} />;
}
const orderCtrl = (
<div className="clearfix">
<div className="d3f_order_ctrl">
{this.state.order !== D3ForumPostSortOrder.TREE && <HashLink to={D3ForumUtils.getTopicUrl(name, this.topic.topic_id, D3ForumPostSortOrder.TREE, null)}>{Functions.mlang('[en]Tree order[/en][ja]ツリー構造順で表示[/ja]', lang)}</HashLink>}
{this.state.order !== D3ForumPostSortOrder.OLD && <HashLink to={D3ForumUtils.getTopicUrl(name, this.topic.topic_id, D3ForumPostSortOrder.OLD, null)}>{Functions.mlang('[en]Older is upper[/en][ja]投稿の古いものから[/ja]', lang)}</HashLink>}
{this.state.order !== D3ForumPostSortOrder.NEW && <HashLink to={D3ForumUtils.getTopicUrl(name, this.topic.topic_id, D3ForumPostSortOrder.NEW, null)}>{Functions.mlang('[en]Newer is upper[/en][ja]投稿の新しいものから[/ja]', lang)}</HashLink>}
</div>
</div>
);
const title = Functions.mlang(this.topic.topic_title, lang);
return (
<>
<Helmet>
<title>{title} - {D3ForumUtils.getTitle(name, lang)} - {Functions.siteTitle(lang)}</title>
</Helmet>
<div className="d3f_breadcrumbs">
<HashLink to={D3ForumUtils.getIndexUrl(name)}>{Functions.mlang('[en]Top[/en][ja]トップ[/ja]', lang)}</HashLink>
&nbsp;&gt;&nbsp;
<HashLink to={D3ForumUtils.getCategoryUrl(name, this.category.cat_id)}>{Functions.mlang(this.category.cat_title, lang)}</HashLink>
&nbsp;&gt;&nbsp;
<HashLink to={D3ForumUtils.getForumUrl(name, this.forum.forum_id)}>{Functions.mlang(this.forum.forum_title, lang)}</HashLink>
</div>
<h1 className="d3f_title">{title}</h1>
<PostsTree lang={lang} name={name} posts={this.posts} postId={0} />
{orderCtrl}
<div className="d3f_wrap">
{this.posts.map((post, idx) => {
const prevPostId = idx === 0 ? 0 : this.posts[idx - 1].post_id;
const nextPostId = (idx + 1) === this.posts.length ? 0 : this.posts[idx + 1].post_id;
return <Post key={post.post_id} lang={lang} name={name} post={post} prevPostId={prevPostId} nextPostId={nextPostId} inTopic={true} />;
})}
</div>
{orderCtrl}
<ForumJump lang={lang} name={name} forumId={this.forum.forum_id} />
</>
);
}
}
export default D3ForumTopic;
+65
View File
@@ -0,0 +1,65 @@
import React, { Component } from 'react';
import { Redirect, RouteComponentProps, withRouter } from 'react-router';
import PageNotFound from '../common/lib/PageNotFound';
import { MultiLang } from '../config';
import Functions from '../functions';
interface Props extends RouteComponentProps {
lang: MultiLang;
name: string;
}
class D3ForumXoopsPathRedirect extends Component<Props> {
getRedirectUrl(): string {
const { name, location } = this.props;
const pathname = location.pathname || '';
const hash = location.hash || '';
const query = new URLSearchParams(location.search);
const search = new RegExp(`^/modules/${name}(?:/+(.*))?$`);
const matches = pathname.match(search);
if (matches === null) {
return '';
}
const path = matches[1] || '';
switch (path) {
case '':
case 'index.php': {
const catId = query.get('cat_id');
if (catId !== null && catId.match(/^\d+$/) !== null) {
return '/' + name + '/category/' + Functions.escape(catId);
}
const forumId = query.get('forum_id');
if (forumId !== null && forumId.match(/^\d+$/) !== null) {
return '/' + name + '/forum/' + Functions.escape(forumId);
}
const topicId = query.get('topic_id');
if (topicId !== null && topicId.match(/^\d+$/) !== null) {
const matches = hash.match(/^#post_id(\d+)$/);
return '/' + name + '/topic/' + Functions.escape(topicId) + (matches === null ? '' : '#postId' + matches[1]);
}
const postId = query.get('post_id');
if (postId !== null && postId.match(/^\d+$/) !== null) {
return '/' + name + '/post/' + Functions.escape(postId);
}
const catIds = query.get('cat_ids');
if (catIds !== null && catIds.match(/^\d+$/) !== null) {
return '/' + name + '/category/' + Functions.escape(catIds);
}
return '/' + name + '/';
}
}
return '/' + name + '/' + path;
}
render() {
const { lang } = this.props;
const url = this.getRedirectUrl();
if (url === '') {
return <PageNotFound lang={lang} />;
}
return <Redirect to={url} />;
}
}
export default withRouter(D3ForumXoopsPathRedirect);

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Before

Width:  |  Height:  |  Size: 1010 B

After

Width:  |  Height:  |  Size: 1010 B

Before

Width:  |  Height:  |  Size: 189 B

After

Width:  |  Height:  |  Size: 189 B

Before

Width:  |  Height:  |  Size: 189 B

After

Width:  |  Height:  |  Size: 189 B

Before

Width:  |  Height:  |  Size: 190 B

After

Width:  |  Height:  |  Size: 190 B

Before

Width:  |  Height:  |  Size: 190 B

After

Width:  |  Height:  |  Size: 190 B

Before

Width:  |  Height:  |  Size: 130 B

After

Width:  |  Height:  |  Size: 130 B

Before

Width:  |  Height:  |  Size: 127 B

After

Width:  |  Height:  |  Size: 127 B

Before

Width:  |  Height:  |  Size: 97 B

After

Width:  |  Height:  |  Size: 97 B

Before

Width:  |  Height:  |  Size: 137 B

After

Width:  |  Height:  |  Size: 137 B

Before

Width:  |  Height:  |  Size: 906 B

After

Width:  |  Height:  |  Size: 906 B

Before

Width:  |  Height:  |  Size: 878 B

After

Width:  |  Height:  |  Size: 878 B

Before

Width:  |  Height:  |  Size: 881 B

After

Width:  |  Height:  |  Size: 881 B

Before

Width:  |  Height:  |  Size: 643 B

After

Width:  |  Height:  |  Size: 643 B

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 544 B

Before

Width:  |  Height:  |  Size: 523 B

After

Width:  |  Height:  |  Size: 523 B

Before

Width:  |  Height:  |  Size: 540 B

After

Width:  |  Height:  |  Size: 540 B

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

@@ -2,35 +2,36 @@ import React from 'react';
import { RouteComponentProps, withRouter } from 'react-router-dom';
import { MultiLang } from '../../config';
import Functions from '../../functions';
import ForumUtils, { ForumCategoryData } from './ForumUtils';
import D3ForumUtils, { D3ForumCategoryData } from './D3ForumUtils';
interface Props extends RouteComponentProps {
lang: MultiLang;
name: string;
catId: number;
}
export const CategoryJump = (props: Props) => {
const { lang, catId } = props;
const { lang, name, catId } = props;
const LABEL_TOP = Functions.mlang('[en]Top[/en][ja]トップ[/ja]', lang);
const LABE_SUBMIT = Functions.mlang('[en]Jump to a category[/en][ja]カテゴリージャンプ[/ja]', lang);
const catSelect = React.createRef<HTMLSelectElement>();
const catSelectOptions: JSX.Element[] = [
<option key="0" value="0">({LABEL_TOP})</option>
];
const catSelectLoop = (categories: ForumCategoryData[]) => {
const catSelectLoop = (categories: D3ForumCategoryData[]) => {
categories.forEach((category) => {
const title = Functions.mlang(category.cat_title, lang);
const depth = category.cat_depth_in_tree !== 0 ? '--'.repeat(category.cat_depth_in_tree) + ' ' : '';
catSelectOptions.push(<option key={category.cat_id} value={category.cat_id}>{depth}{title}</option>);
catSelectLoop(ForumUtils.getSubCategories(category.cat_id));
catSelectLoop(D3ForumUtils.getSubCategories(name, category.cat_id));
});
};
catSelectLoop(ForumUtils.getCategories());
catSelectLoop(D3ForumUtils.getCategories(name));
return (<form className="d3f_form" onSubmit={(e) => {
e.preventDefault();
if (catSelect.current !== null) {
const catId = parseInt(catSelect.current.value, 10)
const url = catId === 0 ? ForumUtils.getIndexUrl() : ForumUtils.getCategoryUrl(catId);
const url = catId === 0 ? D3ForumUtils.getIndexUrl(name) : D3ForumUtils.getCategoryUrl(name, catId);
props.history.push(url);
}
}}>
+399
View File
@@ -0,0 +1,399 @@
import loki from 'lokijs';
import { MultiLang } from '../../config';
import Functions from '../../functions';
import d3forumJson from '../assets/d3forum.json';
export interface D3ForumModuleData {
name: string;
dirname: string;
message: string;
}
export interface D3ForumCategoryData {
cat_id: number;
pid: number;
cat_title: string;
cat_desc: string;
cat_topics_count: number;
cat_posts_count: number;
cat_last_post_id: number;
cat_last_post_time: number;
cat_topics_count_in_tree: number;
cat_posts_count_in_tree: number;
cat_last_post_id_in_tree: number;
cat_last_post_time_in_tree: number;
cat_depth_in_tree: number;
cat_order_in_tree: number;
cat_path_in_tree: any;
cat_unique_path: string;
cat_weight: number;
cat_options: any;
}
export interface D3ForumForumData {
forum_id: number;
cat_id: number;
forum_external_link_format: string;
forum_title: string;
forum_desc: string;
forum_topics_count: number;
forum_posts_count: number;
forum_last_post_id: number;
forum_last_post_time: number;
forum_weight: number;
forum_options: any;
}
export interface D3ForumTopicData {
topic_id: number;
forum_id: number;
topic_external_link_id: string;
topic_title: string;
topic_first_uid: number;
topic_first_post_id: number;
topic_first_post_time: number;
topic_last_uid: number;
topic_last_post_id: number;
topic_last_post_time: number;
topic_views: number;
topic_posts_count: number;
topic_locked: number;
topic_sticky: number;
topic_solved: number;
topic_invisible: number;
topic_votes_sum: number;
topic_votes_count: number;
topic_first_uid_name: string;
topic_first_uid_uname: string;
topic_last_uid_name: string;
topic_last_uid_uname: string;
}
export interface D3ForumPostData {
post_id: number;
pid: number;
topic_id: number;
post_time: number;
modified_time: number;
uid: number;
uid_hidden: number;
poster_ip: string;
modifier_ip: string;
subject: string;
subject_waiting: string;
html: number;
smiley: number;
xcode: number;
br: number;
number_entity: number;
special_entity: number;
icon: number;
attachsig: number;
invisible: number;
approval: number;
votes_sum: number;
votes_count: number;
depth_in_tree: number;
order_in_tree: number;
path_in_tree: string;
unique_path: string;
guest_name: string;
post_text: string;
post_text_waiting: string;
uid_name: string;
uid_uname: string;
uid_rank: number;
uid_posts: number;
}
interface D3ForumData {
module: D3ForumModuleData;
categories: D3ForumCategoryData[];
forums: D3ForumForumData[];
topics: D3ForumTopicData[];
posts: D3ForumPostData[];
}
interface D3ForumLokiData {
module: D3ForumModuleData;
categories: Collection<D3ForumCategoryData>;
forums: Collection<D3ForumForumData>;
topics: Collection<D3ForumTopicData>;
posts: Collection<D3ForumPostData>;
}
const categorySort = (a: D3ForumCategoryData, b: D3ForumCategoryData) => {
if (a.cat_weight > b.cat_weight) {
return 1;
} else if (a.cat_weight < b.cat_weight) {
return -1;
}
if (a.cat_id > b.cat_id) {
return 1;
} else if (a.cat_id < b.cat_id) {
return -1;
}
return 0;
}
const forumSort = (a: D3ForumForumData, b: D3ForumForumData) => {
if (a.forum_weight > b.forum_weight) {
return 1;
} else if (a.forum_weight < b.forum_weight) {
return -1;
}
if (a.forum_id > b.forum_id) {
return 1;
} else if (a.forum_id < b.forum_id) {
return -1;
}
return 0;
}
export enum D3ForumPostSortOrder { TREE, OLD, NEW }
class D3ForumPostSorter {
private order: D3ForumPostSortOrder;
constructor(order: D3ForumPostSortOrder) {
this.order = order;
this.sort = this.sort.bind(this);
}
sort(a: D3ForumPostData, b: D3ForumPostData) {
switch (this.order) {
case D3ForumPostSortOrder.TREE: {
if (a.unique_path > b.unique_path) {
return 1;
} else if (a.unique_path < b.unique_path) {
return -1;
}
break;
}
case D3ForumPostSortOrder.OLD: {
if (a.post_time > b.post_time) {
return 1;
} else if (a.post_time < b.post_time) {
return -1;
}
break;
}
case D3ForumPostSortOrder.NEW: {
if (a.post_time > b.post_time) {
return -1;
} else if (a.post_time < b.post_time) {
return 1;
}
break;
}
}
return 0;
}
}
class D3ForumUtils {
private database: loki;
private modules: Map<string, D3ForumLokiData>;
constructor(json: D3ForumData[]) {
this.database = new loki('d3forum');
this.modules = new Map<string, D3ForumLokiData>();
json.forEach((data) => {
const name = data.module.dirname;
const d3forum = {
module: data.module,
categories: this.database.addCollection<D3ForumCategoryData>(name + '_categories'),
forums: this.database.addCollection<D3ForumForumData>(name + '_forums'),
topics: this.database.addCollection<D3ForumTopicData>(name + '_topics'),
posts: this.database.addCollection<D3ForumPostData>(name + '_posts'),
}
data.categories.forEach((category) => {
d3forum.categories.insert(category);
});
data.forums.forEach((forum) => {
d3forum.forums.insert(forum);
});
data.topics.forEach((topic) => {
d3forum.topics.insert(topic);
});
data.posts.forEach((post) => {
d3forum.posts.insert(post);
});
this.modules.set(name, d3forum);
});
}
getTitle(name: string, lang: MultiLang): string {
const d3forum = this.modules.get(name);
if (typeof d3forum === 'undefined') {
return '';
}
return Functions.mlang(d3forum.module.name, lang);
}
getIndexUrl(name: string): string {
return '/' + name;
}
getCategoryUrl(name: string, catId: number): string {
return this.getIndexUrl(name) + '/category/' + catId;
}
getForumUrl(name: string, forumId: number): string {
return this.getIndexUrl(name) + '/forum/' + forumId;
}
getTopicUrl(name: string, topicId: number, order: D3ForumPostSortOrder, postId: number | null): string {
const params = new URLSearchParams();
if (order !== D3ForumPostSortOrder.TREE) {
params.set('order', String(order));
}
const paramString = params.toString();
return this.getIndexUrl(name) + '/topic/' + topicId + (paramString !== '' ? '?' + paramString : '') + (postId !== null ? '#postId' + postId : '');
}
getPostUrl(name: string, postId: number): string {
return this.getIndexUrl(name) + '/post/' + postId;
}
getTotalTopics(name: string): number {
const d3forum = this.modules.get(name);
if (typeof d3forum === 'undefined') {
return 0;
}
return d3forum.topics.count();
}
getTotalPosts(name: string): number {
const d3forum = this.modules.get(name);
if (typeof d3forum === 'undefined') {
return 0;
}
return d3forum.posts.count();
}
getModule(name: string): D3ForumModuleData | null{
const d3forum = this.modules.get(name);
if (typeof d3forum === 'undefined') {
return null;
}
return d3forum.module;
}
getCategories(name: string): D3ForumCategoryData[] {
return this.getSubCategories(name, 0);
}
getSubCategories(name: string, parentCatId: number): D3ForumCategoryData[] {
const d3forum = this.modules.get(name);
if (typeof d3forum === 'undefined') {
return [];
}
const filter = {
'pid': parentCatId
};
const result = d3forum.categories.chain().find(filter).sort(categorySort).data();
return result;
}
getCategory(name: string, catId: number): D3ForumCategoryData | null {
const d3forum = this.modules.get(name);
if (typeof d3forum === 'undefined') {
return null;
}
const filter = {
'cat_id': catId
};
const result = d3forum.categories.findOne(filter);
return result;
}
getForums(name: string, catId: number): D3ForumForumData[] {
const d3forum = this.modules.get(name);
if (typeof d3forum === 'undefined') {
return [];
}
const filter = {
'cat_id': catId
};
const result = d3forum.forums.chain().find(filter).sort(forumSort).data();
return result;
}
getForum(name: string, forumId: number): D3ForumForumData | null {
const d3forum = this.modules.get(name);
if (typeof d3forum === 'undefined') {
return null;
}
const filter = {
'forum_id': forumId
};
const result = d3forum.forums.findOne(filter);
return result;
}
getTopics(name: string, forumId: number): D3ForumTopicData[] {
const d3forum = this.modules.get(name);
if (typeof d3forum === 'undefined') {
return [];
}
const filter = {
'forum_id': forumId
};
const result = d3forum.topics.find(filter);
return result;
}
getTopic(name: string, topicId: number): D3ForumTopicData | null {
const d3forum = this.modules.get(name);
if (typeof d3forum === 'undefined') {
return null;
}
const filter = {
'topic_id': topicId
};
const result = d3forum.topics.findOne(filter);
return result;
}
getPosts(name: string, topicId: number, order: D3ForumPostSortOrder): D3ForumPostData[] {
const d3forum = this.modules.get(name);
if (typeof d3forum === 'undefined') {
return [];
}
const filter = {
'topic_id': topicId
};
const sorter = new D3ForumPostSorter(order);
const result = d3forum.posts.chain().find(filter).sort(sorter.sort).data();
return result;
}
getChildPosts(name: string, parentPostId: number): D3ForumPostData[] {
const d3forum = this.modules.get(name);
if (typeof d3forum === 'undefined') {
return [];
}
const filter = {
'pid': parentPostId
};
const sorter = new D3ForumPostSorter(D3ForumPostSortOrder.TREE);
const result = d3forum.posts.chain().find(filter).sort(sorter.sort).data();
return result;
}
getPost(name: string, postId: number): D3ForumPostData | null {
const d3forum = this.modules.get(name);
if (typeof d3forum === 'undefined') {
return null;
}
const filter = {
'post_id': postId
};
const result = d3forum.posts.findOne(filter);
return result;
}
}
export default new D3ForumUtils(d3forumJson);
@@ -2,34 +2,35 @@ import React from 'react';
import { RouteComponentProps, withRouter } from 'react-router-dom';
import { MultiLang } from '../../config';
import Functions from '../../functions';
import ForumUtils, { ForumCategoryData, ForumForumData } from './ForumUtils';
import D3ForumUtils, { D3ForumCategoryData, D3ForumForumData } from './D3ForumUtils';
interface Props extends RouteComponentProps {
lang: MultiLang;
name: string;
forumId: number;
}
export const ForumJump = (props: Props) => {
const { lang, forumId } = props;
const { lang, name, forumId } = props;
const LABE_SUBMIT = Functions.mlang('[en]Jump to a forum[/en][ja]フォーラムジャンプ[/ja]', lang);
const forumSelect = React.createRef<HTMLSelectElement>();
const forumSelectOptions: JSX.Element[] = [];
const forumSelectLoop = (categories: ForumCategoryData[]) => {
const forumSelectLoop = (categories: D3ForumCategoryData[]) => {
categories.forEach((category) => {
ForumUtils.getForums(category.cat_id).forEach((forum: ForumForumData) => {
D3ForumUtils.getForums(name, category.cat_id).forEach((forum: D3ForumForumData) => {
const title = Functions.mlang(category.cat_title + ' - ' + forum.forum_title, lang);
const depth = category.cat_depth_in_tree !== 0 ? '--'.repeat(category.cat_depth_in_tree) + ' ' : '';
forumSelectOptions.push(<option key={forum.forum_id} value={forum.forum_id}>{depth}{title}</option>);
});
forumSelectLoop(ForumUtils.getSubCategories(category.cat_id));
forumSelectLoop(D3ForumUtils.getSubCategories(name, category.cat_id));
});
};
forumSelectLoop(ForumUtils.getCategories());
forumSelectLoop(D3ForumUtils.getCategories(name));
return (
<form className="d3f_form" onSubmit={(e) => {
e.preventDefault();
if (forumSelect.current !== null) {
const url = ForumUtils.getForumUrl(parseInt(forumSelect.current.value, 10));
const url = D3ForumUtils.getForumUrl(name, parseInt(forumSelect.current.value, 10));
props.history.push(url);
}
}}>
@@ -6,27 +6,28 @@ import UserRankStarImage from '../../common/lib/UserRankStarImage';
import XoopsCode from '../../common/lib/XoopsCode';
import { MultiLang } from '../../config';
import Functions from '../../functions';
import ForumUtils, { ForumPostData } from './ForumUtils';
import D3ForumUtils, { D3ForumPostData } from './D3ForumUtils';
import PostIcon from './PostIcon';
interface Props {
lang: MultiLang;
post: ForumPostData;
name: string;
post: D3ForumPostData;
nextPostId: number;
prevPostId: number;
inTopic: boolean;
}
const postUrl = (postId: number, inTopic: boolean) => {
return inTopic ? '#postId' + postId : ForumUtils.getPostUrl(postId);
const postUrl = (name: string, postId: number, inTopic: boolean) => {
return inTopic ? '#postId' + postId : D3ForumUtils.getPostUrl(name, postId);
}
const Post = (props: Props) => {
const { lang, post, nextPostId, prevPostId, inTopic } = props;
const { lang, name, post, nextPostId, prevPostId, inTopic } = props;
const depth = String(post.depth_in_tree * 5) + '%';
const url = ForumUtils.getPostUrl(post.post_id)
const url = D3ForumUtils.getPostUrl(name, post.post_id)
const subject = <><PostIcon lang={lang} post={post} /> {Functions.mlang(post.subject, lang)}</>;
const children = ForumUtils.getChildPosts(post.post_id);
const children = D3ForumUtils.getChildPosts(name, post.post_id);
const LABEL_PARENT = Functions.mlang('[en]Parent[/en][ja]親投稿[/ja]', lang);
const LABEL_PREV = Functions.mlang('[en]Previous post[/en][ja]前の投稿[/ja]', lang);
const LABEL_NEXT = Functions.mlang('[en]Next post[/en][ja]次の投稿[/ja]', lang);
@@ -47,23 +48,21 @@ const Post = (props: Props) => {
<dd>{post.depth_in_tree}</dd>
</dl>
<div className="d3f_info_val">
{prevPostId === 0 ? LABEL_PREV : <HashLink to={postUrl(prevPostId, inTopic)}>{LABEL_PREV}</HashLink>}
{prevPostId === 0 ? LABEL_PREV : <HashLink to={postUrl(name, prevPostId, inTopic)}>{LABEL_PREV}</HashLink>}
&nbsp;-&nbsp;
{nextPostId === 0 ? LABEL_NEXT : <HashLink to={postUrl(nextPostId, inTopic)}>{LABEL_NEXT}</HashLink>}
{nextPostId === 0 ? LABEL_NEXT : <HashLink to={postUrl(name, nextPostId, inTopic)}>{LABEL_NEXT}</HashLink>}
&nbsp;|&nbsp;
{post.pid === 0 ? LABEL_PARENT : <HashLink to={postUrl(post.pid, inTopic)}>{LABEL_PARENT}</HashLink>}
{post.pid === 0 ? LABEL_PARENT : <HashLink to={postUrl(name, post.pid, inTopic)}>{LABEL_PARENT}</HashLink>}
&nbsp;-&nbsp;
{children.length === 0 ? LABEL_NO_CHILD : children.map(
(child, idx) => {
const label = (idx === 0 ? LABEL_CHILD : '') + child.unique_path.substr(post.unique_path.length);
return (
<Fragment key={child.post_id}>
{idx > 0 && ' '}
<HashLink to={postUrl(child.post_id, inTopic)}>{label}</HashLink>
</Fragment>
);
}
)}
{children.length === 0 ? LABEL_NO_CHILD : children.map((child: D3ForumPostData, idx: number) => {
const label = (idx === 0 ? LABEL_CHILD : '') + child.unique_path.substr(post.unique_path.length);
return (
<Fragment key={child.post_id}>
{idx > 0 && ' '}
<HashLink to={postUrl(name, child.post_id, inTopic)}>{label}</HashLink>
</Fragment>
);
})}
&nbsp;|&nbsp;
{Functions.mlang('[en]Posted on[/en][ja]投稿日時[/ja]', lang)} {moment(new Date(post.post_time * 1000)).format('YYYY/M/D H:m')}
</div>
@@ -9,11 +9,11 @@ import imagePost4 from '../assets/images/posticon4.gif';
import imagePost5 from '../assets/images/posticon5.gif';
import imagePost6 from '../assets/images/posticon6.gif';
import imagePost7 from '../assets/images/posticon7.gif';
import { ForumPostData } from './ForumUtils';
import { D3ForumPostData } from './D3ForumUtils';
interface Props {
lang: MultiLang;
post: ForumPostData;
post: D3ForumPostData;
title?: string;
}
@@ -3,21 +3,22 @@ import React from 'react';
import { HashLink } from 'react-router-hash-link';
import { MultiLang } from '../../config';
import Functions from '../../functions';
import ForumUtils, { ForumPostData } from './ForumUtils';
import D3ForumUtils, { D3ForumPostData } from './D3ForumUtils';
import PostIcon from './PostIcon';
interface Props {
lang: MultiLang;
posts: ForumPostData[];
name: string;
posts: D3ForumPostData[];
postId: number;
}
const postUrl = (postId: number, inTopic: boolean) => {
return inTopic ? '#postId' + postId : ForumUtils.getPostUrl(postId);
const postUrl = (name: string, postId: number, inTopic: boolean) => {
return inTopic ? '#postId' + postId : D3ForumUtils.getPostUrl(name, postId);
}
const PostsTree = (props: Props) => {
const { lang, posts, postId } = props;
const { lang, name, posts, postId } = props;
const inTopic = postId === 0;
return (
<div className="d3f_post_tree">
@@ -32,7 +33,7 @@ const PostsTree = (props: Props) => {
return (
<li key={post.post_id} className={className}>
<span style={style}>
{post.post_id === postId ? subject : <HashLink to={postUrl(post.post_id, inTopic)}>{subject}</HashLink>}
{post.post_id === postId ? subject : <HashLink to={postUrl(name, post.post_id, inTopic)}>{subject}</HashLink>}
&nbsp;
({post.uid !== 0 ? post.uid_uname : post.guest_name}, {moment(new Date(post.post_time * 1000)).format('YYYY/M/D H:mm')})
&nbsp;
-39
View File
@@ -1,39 +0,0 @@
import React from 'react';
import Helmet from 'react-helmet';
import { Route, RouteComponentProps, Switch } from 'react-router-dom';
import PageNotFound from '../common/lib/PageNotFound';
import { MultiLang } from '../config';
import Functions from '../functions';
import './assets/d3forum_common.css';
import './assets/d3forum_main.css';
import ForumCategory from './ForumCategory';
import ForumForum from './ForumForum';
import ForumPost from './ForumPost';
import ForumTop from './ForumTop';
import ForumTopic from './ForumTopic';
import ForumUtils from './lib/ForumUtils';
interface Props {
lang: MultiLang;
}
const Forum = (props: Props) => {
const { lang } = props;
return (
<>
<Helmet>
<title>{ForumUtils.getTitle(lang)} - {Functions.siteTitle(lang)}</title>
</Helmet>
<Switch>
<Route exact path="/forum" render={() => <ForumTop {...props} />} />
<Route exact path="/forum/category/:id" render={(props: RouteComponentProps<{ id: string }>) => <ForumCategory lang={lang} {...props} />} />
<Route exact path="/forum/forum/:id" render={(props: RouteComponentProps<{ id: string }>) => <ForumForum lang={lang} {...props} />} />
<Route exact path="/forum/topic/:id" render={(props: RouteComponentProps<{ id: string }>) => <ForumTopic lang={lang} {...props} />} />
<Route exact path="/forum/post/:id" render={(props: RouteComponentProps<{ id: string }>) => <ForumPost lang={lang} {...props} />} />
<Route component={PageNotFound} />
</Switch>
</>
);
}
export default Forum;
-79
View File
@@ -1,79 +0,0 @@
import React from 'react';
import Helmet from 'react-helmet';
import { RouteComponentProps } from 'react-router';
import { Link } from 'react-router-dom';
import PageNotFound from '../common/lib/PageNotFound';
import { MultiLang } from '../config';
import Functions from '../functions';
import ForumJump from './lib/ForumJump';
import ForumUtils, { ForumPostSortOrder, ForumPostData } from './lib/ForumUtils';
import Post from './lib/Post';
import PostsTree from './lib/PostsTree';
interface Params {
id: string;
}
interface Props extends RouteComponentProps<Params> {
lang: MultiLang;
}
const ForumPost = (props: Props) => {
const { lang } = props;
const params = props.match.params;
if (params.id === '') {
return <PageNotFound lang={lang} />;
}
const postId = parseInt(props.match.params.id, 10);
const post = ForumUtils.getPost(postId);
if (post === null) {
return <PageNotFound lang={lang} />;
}
const topic = ForumUtils.getTopic(post.topic_id);
if (topic === null) {
return <PageNotFound lang={lang} />;
}
const forum = ForumUtils.getForum(topic.forum_id);
if (forum === null) {
return <PageNotFound lang={lang} />;
}
const category = ForumUtils.getCategory(forum.cat_id);
if (category === null) {
return <PageNotFound lang={lang} />;
}
const posts = ForumUtils.getPosts(topic.topic_id, ForumPostSortOrder.TREE);
const idx = posts.findIndex((post: ForumPostData) => {
return post.post_id === postId;
});
const prevPostId = idx <= 0 ? 0 : posts[idx - 1].post_id;
const nextPostId = (idx + 1) === posts.length ? 0 : posts[idx + 1].post_id;
const title = Functions.mlang(post.subject, lang);
return (
<>
<Helmet>
<title>{title} - {ForumUtils.getTitle(lang)} - {Functions.siteTitle(lang)}</title>
</Helmet>
<div className="d3f_breadcrumbs">
<Link to={ForumUtils.getIndexUrl()}>{Functions.mlang('[en]Top[/en][ja]トップ[/ja]', lang)}</Link>
&nbsp;&gt;&nbsp;
<Link to={ForumUtils.getCategoryUrl(category.cat_id)}>{Functions.mlang(category.cat_title, lang)}</Link>
&nbsp;&gt;&nbsp;
<Link to={ForumUtils.getForumUrl(forum.forum_id)}>{Functions.mlang(forum.forum_title, lang)}</Link>
&nbsp;&gt;&nbsp;
<Link to={ForumUtils.getTopicUrl(topic.topic_id, ForumPostSortOrder.TREE, null)}>{Functions.mlang(topic.topic_title, lang)}</Link>
</div>
<h1 className="d3f_title">{title}</h1>
<PostsTree lang={lang} posts={posts} postId={postId} />
<br />
<p className="d3f_topicinfo">
<Link to={ForumUtils.getTopicUrl(topic.topic_id, ForumPostSortOrder.TREE, null)}>{Functions.mlang('[en]List posts in the topic[/en][ja]このトピックの投稿一覧へ[/ja]', lang)}</Link>
</p>
<div className="d3f_wrap">
<Post lang={lang} post={post} prevPostId={prevPostId} nextPostId={nextPostId} inTopic={false} />
</div>
<ForumJump lang={lang} forumId={forum.forum_id} />
</>
);
}
export default ForumPost;
-120
View File
@@ -1,120 +0,0 @@
import React, { Component } from 'react';
import Helmet from 'react-helmet';
import { RouteComponentProps } from 'react-router';
import { HashLink } from 'react-router-hash-link';
import Loading from '../common/lib/Loading';
import PageNotFound from '../common/lib/PageNotFound';
import { MultiLang } from '../config';
import Functions from '../functions';
import ForumJump from './lib/ForumJump';
import ForumUtils, { ForumCategoryData, ForumForumData, ForumPostData, ForumPostSortOrder, ForumTopicData } from './lib/ForumUtils';
import Post from './lib/Post';
import PostsTree from './lib/PostsTree';
interface Params {
id: string;
}
interface Props extends RouteComponentProps<Params> {
lang: MultiLang;
}
interface State {
loading: boolean;
topicId: number;
order: ForumPostSortOrder;
}
class ForumTopic extends Component<Props, State> {
private topic: ForumTopicData | null = null;
private forum: ForumForumData | null = null;
private category: ForumCategoryData | null = null;
private posts: ForumPostData[] = [];
constructor(props: Props) {
super(props);
this.state = {
loading: true,
topicId: 0,
order: ForumPostSortOrder.TREE,
};
}
static getDerivedStateFromProps(nextProps: Props, prevState: State) {
const params = new URLSearchParams(nextProps.location.search);
const topicId = parseInt(nextProps.match.params.id, 10);
const order_ = params.get('order');
const order = (order_ !== null && order_.match(/(0|1|2)/)) ? parseInt(order_, 10) as ForumPostSortOrder : ForumPostSortOrder.TREE;
if (prevState.topicId !== topicId || prevState.order !== order) {
return { loading: true, topicId, order };
}
return null;
}
componentDidMount() {
this.load();
}
componentDidUpdate() {
this.load();
}
load() {
if (this.state.loading) {
this.topic = ForumUtils.getTopic(this.state.topicId);
this.forum = ForumUtils.getForum(this.topic === null ? 0 : this.topic.forum_id);
this.category = ForumUtils.getCategory(this.forum === null ? 0 : this.forum.cat_id);
this.posts = ForumUtils.getPosts(this.state.topicId, this.state.order);
this.setState({ loading: false });
}
}
render() {
const { lang } = this.props;
if (this.state.loading) {
return <Loading />;
}
if (this.topic === null || this.forum === null || this.category === null) {
return <PageNotFound lang={lang} />;
}
const orderCtrl = (
<div className="clearfix">
<div className="d3f_order_ctrl">
{this.state.order !== ForumPostSortOrder.TREE && <HashLink to={ForumUtils.getTopicUrl(this.topic.topic_id, ForumPostSortOrder.TREE, null)}>{Functions.mlang('[en]Tree order[/en][ja]ツリー構造順で表示[/ja]', lang)}</HashLink>}
{this.state.order !== ForumPostSortOrder.OLD && <HashLink to={ForumUtils.getTopicUrl(this.topic.topic_id, ForumPostSortOrder.OLD, null)}>{Functions.mlang('[en]Older is upper[/en][ja]投稿の古いものから[/ja]', lang)}</HashLink>}
{this.state.order !== ForumPostSortOrder.NEW && <HashLink to={ForumUtils.getTopicUrl(this.topic.topic_id, ForumPostSortOrder.NEW, null)}>{Functions.mlang('[en]Newer is upper[/en][ja]投稿の新しいものから[/ja]', lang)}</HashLink>}
</div>
</div>
);
const title = Functions.mlang(this.topic.topic_title, lang);
return (
<>
<Helmet>
<title>{title} - {ForumUtils.getTitle(lang)} - {Functions.siteTitle(lang)}</title>
</Helmet>
<div className="d3f_breadcrumbs">
<HashLink to={ForumUtils.getIndexUrl()}>{Functions.mlang('[en]Top[/en][ja]トップ[/ja]', lang)}</HashLink>
&nbsp;&gt;&nbsp;
<HashLink to={ForumUtils.getCategoryUrl(this.category.cat_id)}>{Functions.mlang(this.category.cat_title, lang)}</HashLink>
&nbsp;&gt;&nbsp;
<HashLink to={ForumUtils.getForumUrl(this.forum.forum_id)}>{Functions.mlang(this.forum.forum_title, lang)}</HashLink>
</div>
<h1 className="d3f_title">{title}</h1>
<PostsTree lang={lang} posts={this.posts} postId={0} />
{orderCtrl}
<div className="d3f_wrap">
{this.posts.map((post, idx) => {
const prevPostId = idx === 0 ? 0 : this.posts[idx - 1].post_id;
const nextPostId = (idx + 1) === this.posts.length ? 0 : this.posts[idx + 1].post_id;
return <Post key={post.post_id} lang={lang} post={post} prevPostId={prevPostId} nextPostId={nextPostId} inTopic={true} />;
})}
</div>
{orderCtrl}
<ForumJump lang={lang} forumId={this.forum.forum_id} />
</>
);
}
}
export default ForumTopic;
-332
View File
@@ -1,332 +0,0 @@
import loki from 'lokijs';
import { MultiLang } from '../../config';
import Functions from '../../functions';
import forumJson from '../assets/forum.json';
export interface ForumCategoryData {
cat_id: number;
pid: number;
cat_title: string;
cat_desc: string;
cat_topics_count: number;
cat_posts_count: number;
cat_last_post_id: number;
cat_last_post_time: number;
cat_topics_count_in_tree: number;
cat_posts_count_in_tree: number;
cat_last_post_id_in_tree: number;
cat_last_post_time_in_tree: number;
cat_depth_in_tree: number;
cat_order_in_tree: number;
cat_path_in_tree: any;
cat_unique_path: string;
cat_weight: number;
cat_options: any;
}
export interface ForumForumData {
forum_id: number;
cat_id: number;
forum_external_link_format: string;
forum_title: string;
forum_desc: string;
forum_topics_count: number;
forum_posts_count: number;
forum_last_post_id: number;
forum_last_post_time: number;
forum_weight: number;
forum_options: any;
}
export interface ForumTopicData {
topic_id: number;
forum_id: number;
topic_external_link_id: string;
topic_title: string;
topic_first_uid: number;
topic_first_post_id: number;
topic_first_post_time: number;
topic_last_uid: number;
topic_last_post_id: number;
topic_last_post_time: number;
topic_views: number;
topic_posts_count: number;
topic_locked: number;
topic_sticky: number;
topic_solved: number;
topic_invisible: number;
topic_votes_sum: number;
topic_votes_count: number;
topic_first_uid_name: string;
topic_first_uid_uname: string;
topic_last_uid_name: string;
topic_last_uid_uname: string;
}
export interface ForumPostData {
post_id: number;
pid: number;
topic_id: number;
post_time: number;
modified_time: number;
uid: number;
uid_hidden: number;
poster_ip: string;
modifier_ip: string;
subject: string;
subject_waiting: string;
html: number;
smiley: number;
xcode: number;
br: number;
number_entity: number;
special_entity: number;
icon: number;
attachsig: number;
invisible: number;
approval: number;
votes_sum: number;
votes_count: number;
depth_in_tree: number;
order_in_tree: number;
path_in_tree: string;
unique_path: string;
guest_name: string;
post_text: string;
post_text_waiting: string;
uid_name: string;
uid_uname: string;
uid_rank: number;
uid_posts: number;
}
interface ForumData {
categories: ForumCategoryData[];
forums: ForumForumData[];
topics: ForumTopicData[];
posts: ForumPostData[];
}
const categorySort = (a: ForumCategoryData, b: ForumCategoryData) => {
if (a.cat_weight > b.cat_weight) {
return 1;
} else if (a.cat_weight < b.cat_weight) {
return -1;
}
if (a.cat_id > b.cat_id) {
return 1;
} else if (a.cat_id < b.cat_id) {
return -1;
}
return 0;
}
const forumSort = (a: ForumForumData, b: ForumForumData) => {
if (a.forum_weight > b.forum_weight) {
return 1;
} else if (a.forum_weight < b.forum_weight) {
return -1;
}
if (a.forum_id > b.forum_id) {
return 1;
} else if (a.forum_id < b.forum_id) {
return -1;
}
return 0;
}
export enum ForumPostSortOrder { TREE, OLD, NEW }
class PostSorter {
private order: ForumPostSortOrder;
constructor(order: ForumPostSortOrder) {
this.order = order;
this.sort = this.sort.bind(this);
}
sort(a: ForumPostData, b: ForumPostData) {
switch (this.order) {
case ForumPostSortOrder.TREE: {
if (a.unique_path > b.unique_path) {
return 1;
} else if (a.unique_path < b.unique_path) {
return -1;
}
break;
}
case ForumPostSortOrder.OLD: {
if (a.post_time > b.post_time) {
return 1;
} else if (a.post_time < b.post_time) {
return -1;
}
break;
}
case ForumPostSortOrder.NEW: {
if (a.post_time > b.post_time) {
return -1;
} else if (a.post_time < b.post_time) {
return 1;
}
break;
}
}
return 0;
}
}
class ForumUtils {
private database: loki;
private categories: Collection<ForumCategoryData>;
private forums: Collection<ForumForumData>;
private topics: Collection<ForumTopicData>;
private posts: Collection<ForumPostData>;
private totalTopics: number;
private totalPosts: number;
constructor(json: ForumData) {
this.database = new loki('forum');
this.categories = this.database.addCollection<ForumCategoryData>('categories');
this.forums = this.database.addCollection<ForumForumData>('forums');
this.topics = this.database.addCollection<ForumTopicData>('topics');
this.posts = this.database.addCollection<ForumPostData>('posts');
this.totalTopics = 0;
this.totalPosts = 0;
this.load(json);
}
load(json: ForumData) {
json.categories.forEach((category) => {
this.categories.insert(category);
});
json.forums.forEach((forum) => {
this.forums.insert(forum);
});
this.totalTopics = json.topics.length;
json.topics.forEach((topic) => {
this.topics.insert(topic);
});
this.totalPosts = json.posts.length;
json.posts.forEach((post) => {
this.posts.insert(post);
});
}
getTitle(lang: MultiLang) {
return Functions.mlang('[en]Forum[/en][ja]フォーラム[/ja]', lang);
}
getIndexUrl() {
return '/forum';
}
getCategoryUrl(catId: number) {
return this.getIndexUrl() + '/category/' + catId;
}
getForumUrl(forumId: number) {
return this.getIndexUrl() + '/forum/' + forumId;
}
getTopicUrl(topicId: number, order: ForumPostSortOrder, postId: number | null) {
const params = new URLSearchParams();
if (order !== ForumPostSortOrder.TREE) {
params.set('order', String(order));
}
const paramString = params.toString();
return this.getIndexUrl() + '/topic/' + topicId + (paramString !== '' ? '?' + paramString : '') + (postId !== null ? '#postId' + postId : '');
}
getPostUrl(postId: number) {
return this.getIndexUrl() + '/post/' + postId;
}
getTotalTopics() {
return this.totalTopics;
}
getTotalPosts() {
return this.totalPosts;
}
getCategories() {
return this.getSubCategories(0);
}
getSubCategories(parentCatId: number) {
const filter = {
'pid': parentCatId
};
const result = this.categories.chain().find(filter).sort(categorySort).data();
return result;
}
getCategory(catId: number) {
const filter = {
'cat_id': catId
};
const result = this.categories.findOne(filter);
return result;
}
getForums(catId: number) {
const filter = {
'cat_id': catId
};
const result = this.forums.chain().find(filter).sort(forumSort).data();
return result;
}
getForum(forumId: number) {
const filter = {
'forum_id': forumId
};
const result = this.forums.findOne(filter);
return result;
}
getTopics(forumId: number) {
const filter = {
'forum_id': forumId
};
const result = this.topics.find(filter);
return result;
}
getTopic(topicId: number) {
const filter = {
'topic_id': topicId
};
const result = this.topics.findOne(filter);
return result;
}
getPosts(topicId: number, order: ForumPostSortOrder) {
const filter = {
'topic_id': topicId
};
const sorter = new PostSorter(order);
const result = this.posts.chain().find(filter).sort(sorter.sort).data();
return result;
}
getChildPosts(parentPostId: number) {
const filter = {
'pid': parentPostId
};
const sorter = new PostSorter(ForumPostSortOrder.TREE);
const result = this.posts.chain().find(filter).sort(sorter.sort).data();
return result;
}
getPost(postId: number) {
const filter = {
'post_id': postId
};
const result = this.posts.findOne(filter);
return result;
}
}
export default new ForumUtils(forumJson);