From b17f16d010337d51d19feb68fbff36ce1fcd28c7 Mon Sep 17 00:00:00 2001 From: Yoshihiro OKUMURA Date: Thu, 27 Jun 2019 19:06:52 +0900 Subject: [PATCH] support duplicatable d3forum module. --- .gitignore | 2 +- etc/common.inc.php | 2 +- etc/dumpD3Forum.php | 26 +- etc/dumpXooNIps.php | 20 +- src/common/MainContent.tsx | 19 +- src/common/XoopsPathRedirect.tsx | 141 +------ src/config.ts | 2 + src/d3forum/D3Forum.tsx | 40 ++ .../D3ForumCategory.tsx} | 44 +- .../D3ForumForum.tsx} | 35 +- src/d3forum/D3ForumPost.tsx | 80 ++++ .../ForumTop.tsx => d3forum/D3ForumTop.tsx} | 60 +-- src/d3forum/D3ForumTopic.tsx | 125 ++++++ src/d3forum/D3ForumXoopsPathRedirect.tsx | 65 +++ .../assets/d3forum_common.css | 0 .../assets/d3forum_main.css | 0 .../assets/images/category_0.gif | Bin .../assets/images/category_1.gif | Bin .../assets/images/forum_0.gif | Bin .../assets/images/forum_1.gif | Bin .../assets/images/posticon0.gif | Bin .../assets/images/posticon1.gif | Bin .../assets/images/posticon2.gif | Bin .../assets/images/posticon3.gif | Bin .../assets/images/posticon4.gif | Bin .../assets/images/posticon5.gif | Bin .../assets/images/posticon6.gif | Bin .../assets/images/posticon7.gif | Bin .../assets/images/topic_01.gif | Bin .../assets/images/topic_10.gif | Bin .../assets/images/topic_11.gif | Bin .../assets/images/topic_invisible.gif | Bin .../assets/images/topic_status_locked1.gif | Bin .../assets/images/topic_status_marked1.gif | Bin .../assets/images/topic_status_solved0.gif | Bin .../assets/images/topic_sticky0.gif | Bin src/{forum => d3forum}/lib/CategoryJump.tsx | 13 +- src/d3forum/lib/D3ForumUtils.ts | 399 ++++++++++++++++++ src/{forum => d3forum}/lib/ForumJump.tsx | 15 +- src/{forum => d3forum}/lib/Post.tsx | 41 +- src/{forum => d3forum}/lib/PostIcon.tsx | 4 +- src/{forum => d3forum}/lib/PostsTree.tsx | 13 +- src/forum/Forum.tsx | 39 -- src/forum/ForumPost.tsx | 79 ---- src/forum/ForumTopic.tsx | 120 ------ src/forum/lib/ForumUtils.ts | 332 --------------- 46 files changed, 876 insertions(+), 840 deletions(-) create mode 100644 src/d3forum/D3Forum.tsx rename src/{forum/ForumCategory.tsx => d3forum/D3ForumCategory.tsx} (79%) rename src/{forum/ForumForum.tsx => d3forum/D3ForumForum.tsx} (77%) create mode 100644 src/d3forum/D3ForumPost.tsx rename src/{forum/ForumTop.tsx => d3forum/D3ForumTop.tsx} (75%) create mode 100644 src/d3forum/D3ForumTopic.tsx create mode 100644 src/d3forum/D3ForumXoopsPathRedirect.tsx rename src/{forum => d3forum}/assets/d3forum_common.css (100%) rename src/{forum => d3forum}/assets/d3forum_main.css (100%) rename src/{forum => d3forum}/assets/images/category_0.gif (100%) rename src/{forum => d3forum}/assets/images/category_1.gif (100%) rename src/{forum => d3forum}/assets/images/forum_0.gif (100%) rename src/{forum => d3forum}/assets/images/forum_1.gif (100%) rename src/{forum => d3forum}/assets/images/posticon0.gif (100%) rename src/{forum => d3forum}/assets/images/posticon1.gif (100%) rename src/{forum => d3forum}/assets/images/posticon2.gif (100%) rename src/{forum => d3forum}/assets/images/posticon3.gif (100%) rename src/{forum => d3forum}/assets/images/posticon4.gif (100%) rename src/{forum => d3forum}/assets/images/posticon5.gif (100%) rename src/{forum => d3forum}/assets/images/posticon6.gif (100%) rename src/{forum => d3forum}/assets/images/posticon7.gif (100%) rename src/{forum => d3forum}/assets/images/topic_01.gif (100%) rename src/{forum => d3forum}/assets/images/topic_10.gif (100%) rename src/{forum => d3forum}/assets/images/topic_11.gif (100%) rename src/{forum => d3forum}/assets/images/topic_invisible.gif (100%) rename src/{forum => d3forum}/assets/images/topic_status_locked1.gif (100%) rename src/{forum => d3forum}/assets/images/topic_status_marked1.gif (100%) rename src/{forum => d3forum}/assets/images/topic_status_solved0.gif (100%) rename src/{forum => d3forum}/assets/images/topic_sticky0.gif (100%) rename src/{forum => d3forum}/lib/CategoryJump.tsx (77%) create mode 100644 src/d3forum/lib/D3ForumUtils.ts rename src/{forum => d3forum}/lib/ForumJump.tsx (71%) rename src/{forum => d3forum}/lib/Post.tsx (76%) rename src/{forum => d3forum}/lib/PostIcon.tsx (94%) rename src/{forum => d3forum}/lib/PostsTree.tsx (80%) delete mode 100644 src/forum/Forum.tsx delete mode 100644 src/forum/ForumPost.tsx delete mode 100644 src/forum/ForumTopic.tsx delete mode 100644 src/forum/lib/ForumUtils.ts diff --git a/.gitignore b/.gitignore index 7e1ef9c..533b0ad 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/etc/common.inc.php b/etc/common.inc.php index 3ff9e66..04b355c 100644 --- a/etc/common.inc.php +++ b/etc/common.inc.php @@ -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] = ''; } } diff --git a/etc/dumpD3Forum.php b/etc/dumpD3Forum.php index d11db20..940d05d 100644 --- a/etc/dumpD3Forum.php +++ b/etc/dumpD3Forum.php @@ -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; } diff --git a/etc/dumpXooNIps.php b/etc/dumpXooNIps.php index 34c6628..be02f1a 100644 --- a/etc/dumpXooNIps.php +++ b/etc/dumpXooNIps.php @@ -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'; diff --git a/src/common/MainContent.tsx b/src/common/MainContent.tsx index 3115e97..bbea0b2 100644 --- a/src/common/MainContent.tsx +++ b/src/common/MainContent.tsx @@ -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 (
- } /> - } /> - } /> + } /> + {Config.D3FORUM_MODULES.map((name) => + } /> + )} + } /> + } /> + {Config.D3FORUM_MODULES.map((name) => + } /> + )}
diff --git a/src/common/XoopsPathRedirect.tsx b/src/common/XoopsPathRedirect.tsx index 56a7c29..70863cb 100644 --- a/src/common/XoopsPathRedirect.tsx +++ b/src/common/XoopsPathRedirect.tsx @@ -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 { class XoopsPathRedirect extends Component { 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': { diff --git a/src/config.ts b/src/config.ts index baed2b9..39b8561 100644 --- a/src/config.ts +++ b/src/config.ts @@ -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; diff --git a/src/d3forum/D3Forum.tsx b/src/d3forum/D3Forum.tsx new file mode 100644 index 0000000..bd2b4f0 --- /dev/null +++ b/src/d3forum/D3Forum.tsx @@ -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 ( + <> + + {D3ForumUtils.getTitle(name, lang)} - {Functions.siteTitle(lang)} + + + } /> + ) => } /> + ) => } /> + ) => } /> + ) => } /> + + + + ); +} + +export default D3Forum; \ No newline at end of file diff --git a/src/forum/ForumCategory.tsx b/src/d3forum/D3ForumCategory.tsx similarity index 79% rename from src/forum/ForumCategory.tsx rename to src/d3forum/D3ForumCategory.tsx index 0f669a5..2a7c009 100644 --- a/src/forum/ForumCategory.tsx +++ b/src/d3forum/D3ForumCategory.tsx @@ -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 { 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')}
{post.uid_uname}   - + @@ -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 ; } const catId = parseInt(props.match.params.id, 10); - const category = ForumUtils.getCategory(catId); + const category = D3ForumUtils.getCategory(name, catId); if (category === null) { return ; } @@ -153,17 +155,17 @@ const ForumCategory = (props: Props) => { return ( <> - {title} - {ForumUtils.getTitle(lang)} - {Functions.siteTitle(lang)} + {title} - {D3ForumUtils.getTitle(name, lang)} - {Functions.siteTitle(lang)}
- {Functions.mlang('[en]Top[/en][ja]トップ[/ja]', lang)} + {Functions.mlang('[en]Top[/en][ja]トップ[/ja]', lang)}

{title}

- - - + + + ); } -export default ForumCategory; \ No newline at end of file +export default D3ForumCategory; \ No newline at end of file diff --git a/src/forum/ForumForum.tsx b/src/d3forum/D3ForumForum.tsx similarity index 77% rename from src/forum/ForumForum.tsx rename to src/d3forum/D3ForumForum.tsx index e00b405..19cf87e 100644 --- a/src/forum/ForumForum.tsx +++ b/src/d3forum/D3ForumForum.tsx @@ -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 { 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 ; } const forumId = parseInt(props.match.params.id, 10); - const forum = ForumUtils.getForum(forumId); + const forum = D3ForumUtils.getForum(name, forumId); if (forum === null) { return ; } - const category = ForumUtils.getCategory(forum.cat_id); + const category = D3ForumUtils.getCategory(name, forum.cat_id); if (category === null) { return ; } - const topics = ForumUtils.getTopics(forum.forum_id); + const topics = D3ForumUtils.getTopics(name, forum.forum_id); const title = Functions.mlang(forum.forum_title, lang); return ( <> - {title} - {ForumUtils.getTitle(lang)} - {Functions.siteTitle(lang)} + {title} - {D3ForumUtils.getTitle(name, lang)} - {Functions.siteTitle(lang)}
- {Functions.mlang('[en]Top[/en][ja]トップ[/ja]', lang)} + {Functions.mlang('[en]Top[/en][ja]トップ[/ja]', lang)}  >  - {Functions.mlang(category.cat_title, lang)} + {Functions.mlang(category.cat_title, lang)}

{title}

{Functions.mlang(forum.forum_desc, lang)}

@@ -64,21 +65,21 @@ const ForumForum = (props: Props) => { - {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')}
- {post.uid_uname} + {post.uid_uname} ); } @@ -90,7 +91,7 @@ const ForumForum = (props: Props) => { {topic.topic_solved === 0 && } {topic.topic_locked !== 0 && } - {postTitleStart} + {postTitleStart} {topic.topic_posts_count - 1} @@ -118,9 +119,9 @@ const ForumForum = (props: Props) => {
  • = {Functions.mlang('[en]Marked[/en][ja]未解決トピック[/ja]', lang)}
  • - + ); } -export default ForumForum; +export default D3ForumForum; diff --git a/src/d3forum/D3ForumPost.tsx b/src/d3forum/D3ForumPost.tsx new file mode 100644 index 0000000..d22a518 --- /dev/null +++ b/src/d3forum/D3ForumPost.tsx @@ -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 { + lang: MultiLang; + name: string; +} + +const D3ForumPost = (props: Props) => { + const { lang, name } = props; + const params = props.match.params; + if (params.id === '') { + return ; + } + const postId = parseInt(props.match.params.id, 10); + const post = D3ForumUtils.getPost(name, postId); + if (post === null) { + return ; + } + const topic = D3ForumUtils.getTopic(name, post.topic_id); + if (topic === null) { + return ; + } + const forum = D3ForumUtils.getForum(name, topic.forum_id); + if (forum === null) { + return ; + } + const category = D3ForumUtils.getCategory(name, forum.cat_id); + if (category === null) { + return ; + } + 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 ( + <> + + {title} - {D3ForumUtils.getTitle(name, lang)} - {Functions.siteTitle(lang)} + +
    + {Functions.mlang('[en]Top[/en][ja]トップ[/ja]', lang)} +  >  + {Functions.mlang(category.cat_title, lang)} +  >  + {Functions.mlang(forum.forum_title, lang)} +  >  + {Functions.mlang(topic.topic_title, lang)} +
    +

    {title}

    + +
    +

    + {Functions.mlang('[en]List posts in the topic[/en][ja]このトピックの投稿一覧へ[/ja]', lang)} +

    +
    + +
    + + + ); +} + +export default D3ForumPost; diff --git a/src/forum/ForumTop.tsx b/src/d3forum/D3ForumTop.tsx similarity index 75% rename from src/forum/ForumTop.tsx rename to src/d3forum/D3ForumTop.tsx index c27361d..8bd7191 100644 --- a/src/forum/ForumTop.tsx +++ b/src/d3forum/D3ForumTop.tsx @@ -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 ( @@ -104,8 +107,8 @@ const ForumTopCategory = (props: Props) => {

    {LABEL_TOTAL_TOPICS}: {category.cat_topics_count}, {LABEL_TOTAL_POSTS}: {category.cat_posts_count}{latest}

    - - + +
     
    @@ -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 ; + } return (
    -

    フォーラムトップ

    -

    興味のあるフォーラムへぜひご参加ください

    +
    {LABEL_TOTAL_TOPICS}
    -
    {totalTopics},
    +
    {D3ForumUtils.getTotalTopics(name)},
    {LABEL_TOTAL_POSTS}
    -
    {totalPosts}
    +
    {D3ForumUtils.getTotalPosts(name)}
    - +
    • = {LABEL_NEW_POSTS}
    • @@ -149,9 +153,9 @@ const ForumTop = (props: Props) => {
    - +
    ); } -export default ForumTop; +export default D3ForumTop; diff --git a/src/d3forum/D3ForumTopic.tsx b/src/d3forum/D3ForumTopic.tsx new file mode 100644 index 0000000..2cd3e95 --- /dev/null +++ b/src/d3forum/D3ForumTopic.tsx @@ -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 { + lang: MultiLang; + name: string; +} + +interface State { + loading: boolean; + name: string; + topicId: number; + order: D3ForumPostSortOrder; +} + +class D3ForumTopic extends Component { + + 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 ; + } + if (this.topic === null || this.forum === null || this.category === null) { + return ; + } + const orderCtrl = ( +
    +
    + {this.state.order !== D3ForumPostSortOrder.TREE && {Functions.mlang('[en]Tree order[/en][ja]ツリー構造順で表示[/ja]', lang)}} + {this.state.order !== D3ForumPostSortOrder.OLD && {Functions.mlang('[en]Older is upper[/en][ja]投稿の古いものから[/ja]', lang)}} + {this.state.order !== D3ForumPostSortOrder.NEW && {Functions.mlang('[en]Newer is upper[/en][ja]投稿の新しいものから[/ja]', lang)}} +
    +
    + ); + const title = Functions.mlang(this.topic.topic_title, lang); + return ( + <> + + {title} - {D3ForumUtils.getTitle(name, lang)} - {Functions.siteTitle(lang)} + +
    + {Functions.mlang('[en]Top[/en][ja]トップ[/ja]', lang)} +  >  + {Functions.mlang(this.category.cat_title, lang)} +  >  + {Functions.mlang(this.forum.forum_title, lang)} +
    +

    {title}

    + + {orderCtrl} +
    + {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 ; + })} +
    + {orderCtrl} + + + ); + } +} + +export default D3ForumTopic; diff --git a/src/d3forum/D3ForumXoopsPathRedirect.tsx b/src/d3forum/D3ForumXoopsPathRedirect.tsx new file mode 100644 index 0000000..d737999 --- /dev/null +++ b/src/d3forum/D3ForumXoopsPathRedirect.tsx @@ -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 { + + 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 ; + } + return ; + } +} + +export default withRouter(D3ForumXoopsPathRedirect); diff --git a/src/forum/assets/d3forum_common.css b/src/d3forum/assets/d3forum_common.css similarity index 100% rename from src/forum/assets/d3forum_common.css rename to src/d3forum/assets/d3forum_common.css diff --git a/src/forum/assets/d3forum_main.css b/src/d3forum/assets/d3forum_main.css similarity index 100% rename from src/forum/assets/d3forum_main.css rename to src/d3forum/assets/d3forum_main.css diff --git a/src/forum/assets/images/category_0.gif b/src/d3forum/assets/images/category_0.gif similarity index 100% rename from src/forum/assets/images/category_0.gif rename to src/d3forum/assets/images/category_0.gif diff --git a/src/forum/assets/images/category_1.gif b/src/d3forum/assets/images/category_1.gif similarity index 100% rename from src/forum/assets/images/category_1.gif rename to src/d3forum/assets/images/category_1.gif diff --git a/src/forum/assets/images/forum_0.gif b/src/d3forum/assets/images/forum_0.gif similarity index 100% rename from src/forum/assets/images/forum_0.gif rename to src/d3forum/assets/images/forum_0.gif diff --git a/src/forum/assets/images/forum_1.gif b/src/d3forum/assets/images/forum_1.gif similarity index 100% rename from src/forum/assets/images/forum_1.gif rename to src/d3forum/assets/images/forum_1.gif diff --git a/src/forum/assets/images/posticon0.gif b/src/d3forum/assets/images/posticon0.gif similarity index 100% rename from src/forum/assets/images/posticon0.gif rename to src/d3forum/assets/images/posticon0.gif diff --git a/src/forum/assets/images/posticon1.gif b/src/d3forum/assets/images/posticon1.gif similarity index 100% rename from src/forum/assets/images/posticon1.gif rename to src/d3forum/assets/images/posticon1.gif diff --git a/src/forum/assets/images/posticon2.gif b/src/d3forum/assets/images/posticon2.gif similarity index 100% rename from src/forum/assets/images/posticon2.gif rename to src/d3forum/assets/images/posticon2.gif diff --git a/src/forum/assets/images/posticon3.gif b/src/d3forum/assets/images/posticon3.gif similarity index 100% rename from src/forum/assets/images/posticon3.gif rename to src/d3forum/assets/images/posticon3.gif diff --git a/src/forum/assets/images/posticon4.gif b/src/d3forum/assets/images/posticon4.gif similarity index 100% rename from src/forum/assets/images/posticon4.gif rename to src/d3forum/assets/images/posticon4.gif diff --git a/src/forum/assets/images/posticon5.gif b/src/d3forum/assets/images/posticon5.gif similarity index 100% rename from src/forum/assets/images/posticon5.gif rename to src/d3forum/assets/images/posticon5.gif diff --git a/src/forum/assets/images/posticon6.gif b/src/d3forum/assets/images/posticon6.gif similarity index 100% rename from src/forum/assets/images/posticon6.gif rename to src/d3forum/assets/images/posticon6.gif diff --git a/src/forum/assets/images/posticon7.gif b/src/d3forum/assets/images/posticon7.gif similarity index 100% rename from src/forum/assets/images/posticon7.gif rename to src/d3forum/assets/images/posticon7.gif diff --git a/src/forum/assets/images/topic_01.gif b/src/d3forum/assets/images/topic_01.gif similarity index 100% rename from src/forum/assets/images/topic_01.gif rename to src/d3forum/assets/images/topic_01.gif diff --git a/src/forum/assets/images/topic_10.gif b/src/d3forum/assets/images/topic_10.gif similarity index 100% rename from src/forum/assets/images/topic_10.gif rename to src/d3forum/assets/images/topic_10.gif diff --git a/src/forum/assets/images/topic_11.gif b/src/d3forum/assets/images/topic_11.gif similarity index 100% rename from src/forum/assets/images/topic_11.gif rename to src/d3forum/assets/images/topic_11.gif diff --git a/src/forum/assets/images/topic_invisible.gif b/src/d3forum/assets/images/topic_invisible.gif similarity index 100% rename from src/forum/assets/images/topic_invisible.gif rename to src/d3forum/assets/images/topic_invisible.gif diff --git a/src/forum/assets/images/topic_status_locked1.gif b/src/d3forum/assets/images/topic_status_locked1.gif similarity index 100% rename from src/forum/assets/images/topic_status_locked1.gif rename to src/d3forum/assets/images/topic_status_locked1.gif diff --git a/src/forum/assets/images/topic_status_marked1.gif b/src/d3forum/assets/images/topic_status_marked1.gif similarity index 100% rename from src/forum/assets/images/topic_status_marked1.gif rename to src/d3forum/assets/images/topic_status_marked1.gif diff --git a/src/forum/assets/images/topic_status_solved0.gif b/src/d3forum/assets/images/topic_status_solved0.gif similarity index 100% rename from src/forum/assets/images/topic_status_solved0.gif rename to src/d3forum/assets/images/topic_status_solved0.gif diff --git a/src/forum/assets/images/topic_sticky0.gif b/src/d3forum/assets/images/topic_sticky0.gif similarity index 100% rename from src/forum/assets/images/topic_sticky0.gif rename to src/d3forum/assets/images/topic_sticky0.gif diff --git a/src/forum/lib/CategoryJump.tsx b/src/d3forum/lib/CategoryJump.tsx similarity index 77% rename from src/forum/lib/CategoryJump.tsx rename to src/d3forum/lib/CategoryJump.tsx index 05cb402..f0f380e 100644 --- a/src/forum/lib/CategoryJump.tsx +++ b/src/d3forum/lib/CategoryJump.tsx @@ -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(); const catSelectOptions: JSX.Element[] = [ ]; - 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(); - catSelectLoop(ForumUtils.getSubCategories(category.cat_id)); + catSelectLoop(D3ForumUtils.getSubCategories(name, category.cat_id)); }); }; - catSelectLoop(ForumUtils.getCategories()); + catSelectLoop(D3ForumUtils.getCategories(name)); return (
    { 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); } }}> diff --git a/src/d3forum/lib/D3ForumUtils.ts b/src/d3forum/lib/D3ForumUtils.ts new file mode 100644 index 0000000..a495538 --- /dev/null +++ b/src/d3forum/lib/D3ForumUtils.ts @@ -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; + forums: Collection; + topics: Collection; + posts: Collection; +} + +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; + + constructor(json: D3ForumData[]) { + this.database = new loki('d3forum'); + this.modules = new Map(); + json.forEach((data) => { + const name = data.module.dirname; + const d3forum = { + module: data.module, + categories: this.database.addCollection(name + '_categories'), + forums: this.database.addCollection(name + '_forums'), + topics: this.database.addCollection(name + '_topics'), + posts: this.database.addCollection(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); \ No newline at end of file diff --git a/src/forum/lib/ForumJump.tsx b/src/d3forum/lib/ForumJump.tsx similarity index 71% rename from src/forum/lib/ForumJump.tsx rename to src/d3forum/lib/ForumJump.tsx index d15b1fb..5571bad 100644 --- a/src/forum/lib/ForumJump.tsx +++ b/src/d3forum/lib/ForumJump.tsx @@ -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(); 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(); }); - forumSelectLoop(ForumUtils.getSubCategories(category.cat_id)); + forumSelectLoop(D3ForumUtils.getSubCategories(name, category.cat_id)); }); }; - forumSelectLoop(ForumUtils.getCategories()); + forumSelectLoop(D3ForumUtils.getCategories(name)); return ( { 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); } }}> diff --git a/src/forum/lib/Post.tsx b/src/d3forum/lib/Post.tsx similarity index 76% rename from src/forum/lib/Post.tsx rename to src/d3forum/lib/Post.tsx index 5bff2a4..c6b0844 100644 --- a/src/forum/lib/Post.tsx +++ b/src/d3forum/lib/Post.tsx @@ -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 = <> {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) => {
    {post.depth_in_tree}
    - {prevPostId === 0 ? LABEL_PREV : {LABEL_PREV}} + {prevPostId === 0 ? LABEL_PREV : {LABEL_PREV}}  -  - {nextPostId === 0 ? LABEL_NEXT : {LABEL_NEXT}} + {nextPostId === 0 ? LABEL_NEXT : {LABEL_NEXT}}  |  - {post.pid === 0 ? LABEL_PARENT : {LABEL_PARENT}} + {post.pid === 0 ? LABEL_PARENT : {LABEL_PARENT}}  -  - {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 ( - - {idx > 0 && ' '} - {label} - - ); - } - )} + {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 ( + + {idx > 0 && ' '} + {label} + + ); + })}  |  {Functions.mlang('[en]Posted on[/en][ja]投稿日時[/ja]', lang)} {moment(new Date(post.post_time * 1000)).format('YYYY/M/D H:m')}
    diff --git a/src/forum/lib/PostIcon.tsx b/src/d3forum/lib/PostIcon.tsx similarity index 94% rename from src/forum/lib/PostIcon.tsx rename to src/d3forum/lib/PostIcon.tsx index 9950d67..91a1142 100644 --- a/src/forum/lib/PostIcon.tsx +++ b/src/d3forum/lib/PostIcon.tsx @@ -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; } diff --git a/src/forum/lib/PostsTree.tsx b/src/d3forum/lib/PostsTree.tsx similarity index 80% rename from src/forum/lib/PostsTree.tsx rename to src/d3forum/lib/PostsTree.tsx index f1a26de..f1f430d 100644 --- a/src/forum/lib/PostsTree.tsx +++ b/src/d3forum/lib/PostsTree.tsx @@ -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 (
    @@ -32,7 +33,7 @@ const PostsTree = (props: Props) => { return (
  • - {post.post_id === postId ? subject : {subject}} + {post.post_id === postId ? subject : {subject}}   ({post.uid !== 0 ? post.uid_uname : post.guest_name}, {moment(new Date(post.post_time * 1000)).format('YYYY/M/D H:mm')})   diff --git a/src/forum/Forum.tsx b/src/forum/Forum.tsx deleted file mode 100644 index 772ef60..0000000 --- a/src/forum/Forum.tsx +++ /dev/null @@ -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 ( - <> - - {ForumUtils.getTitle(lang)} - {Functions.siteTitle(lang)} - - - } /> - ) => } /> - ) => } /> - ) => } /> - ) => } /> - - - - ); -} - -export default Forum; \ No newline at end of file diff --git a/src/forum/ForumPost.tsx b/src/forum/ForumPost.tsx deleted file mode 100644 index a416872..0000000 --- a/src/forum/ForumPost.tsx +++ /dev/null @@ -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 { - lang: MultiLang; -} - -const ForumPost = (props: Props) => { - const { lang } = props; - const params = props.match.params; - if (params.id === '') { - return ; - } - const postId = parseInt(props.match.params.id, 10); - const post = ForumUtils.getPost(postId); - if (post === null) { - return ; - } - const topic = ForumUtils.getTopic(post.topic_id); - if (topic === null) { - return ; - } - const forum = ForumUtils.getForum(topic.forum_id); - if (forum === null) { - return ; - } - const category = ForumUtils.getCategory(forum.cat_id); - if (category === null) { - return ; - } - 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 ( - <> - - {title} - {ForumUtils.getTitle(lang)} - {Functions.siteTitle(lang)} - -
    - {Functions.mlang('[en]Top[/en][ja]トップ[/ja]', lang)} -  >  - {Functions.mlang(category.cat_title, lang)} -  >  - {Functions.mlang(forum.forum_title, lang)} -  >  - {Functions.mlang(topic.topic_title, lang)} -
    -

    {title}

    - -
    -

    - {Functions.mlang('[en]List posts in the topic[/en][ja]このトピックの投稿一覧へ[/ja]', lang)} -

    -
    - -
    - - - ); -} - -export default ForumPost; diff --git a/src/forum/ForumTopic.tsx b/src/forum/ForumTopic.tsx deleted file mode 100644 index 7aa604b..0000000 --- a/src/forum/ForumTopic.tsx +++ /dev/null @@ -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 { - lang: MultiLang; -} - -interface State { - loading: boolean; - topicId: number; - order: ForumPostSortOrder; -} - -class ForumTopic extends Component { - - 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 ; - } - if (this.topic === null || this.forum === null || this.category === null) { - return ; - } - const orderCtrl = ( -
    -
    - {this.state.order !== ForumPostSortOrder.TREE && {Functions.mlang('[en]Tree order[/en][ja]ツリー構造順で表示[/ja]', lang)}} - {this.state.order !== ForumPostSortOrder.OLD && {Functions.mlang('[en]Older is upper[/en][ja]投稿の古いものから[/ja]', lang)}} - {this.state.order !== ForumPostSortOrder.NEW && {Functions.mlang('[en]Newer is upper[/en][ja]投稿の新しいものから[/ja]', lang)}} -
    -
    - ); - const title = Functions.mlang(this.topic.topic_title, lang); - return ( - <> - - {title} - {ForumUtils.getTitle(lang)} - {Functions.siteTitle(lang)} - -
    - {Functions.mlang('[en]Top[/en][ja]トップ[/ja]', lang)} -  >  - {Functions.mlang(this.category.cat_title, lang)} -  >  - {Functions.mlang(this.forum.forum_title, lang)} -
    -

    {title}

    - - {orderCtrl} -
    - {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 ; - })} -
    - {orderCtrl} - - - ); - } -} - -export default ForumTopic; diff --git a/src/forum/lib/ForumUtils.ts b/src/forum/lib/ForumUtils.ts deleted file mode 100644 index 6aa105f..0000000 --- a/src/forum/lib/ForumUtils.ts +++ /dev/null @@ -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; - private forums: Collection; - private topics: Collection; - private posts: Collection; - private totalTopics: number; - private totalPosts: number; - - constructor(json: ForumData) { - this.database = new loki('forum'); - this.categories = this.database.addCollection('categories'); - this.forums = this.database.addCollection('forums'); - this.topics = this.database.addCollection('topics'); - this.posts = this.database.addCollection('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); \ No newline at end of file