TemplateRender.php 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383
  1. <?php
  2. namespace App\Http\Api;
  3. use Illuminate\Support\Facades\Cache;
  4. use Illuminate\Support\Facades\Log;
  5. use Illuminate\Support\Str;
  6. use Illuminate\Support\Facades\Http;
  7. use App\Models\DhammaTerm;
  8. use App\Models\PaliText;
  9. use App\Models\Channel;
  10. use App\Models\PageNumber;
  11. use App\Models\Discussion;
  12. use App\Models\BookTitle as BookSeries;
  13. use App\Http\Controllers\CorpusController;
  14. use App\Tools\RedisClusters;
  15. use App\Http\Api\ChannelApi;
  16. use App\Http\Api\MdRender;
  17. use App\Http\Api\PaliTextApi;
  18. class TemplateRender
  19. {
  20. protected $param = [];
  21. protected $mode = "read";
  22. protected $channel_id = [];
  23. protected $debug = [];
  24. protected $format = 'react';
  25. protected $studioId = null;
  26. protected $lang = 'en';
  27. protected $langFamily = 'en';
  28. protected $glossaryKey = 'glossary';
  29. protected $channelInfo = [];
  30. protected $options = [
  31. 'mode' => 'read',
  32. 'channelType' => 'translation',
  33. 'contentType' => "markdown",
  34. 'format' => 'react',
  35. 'debug' => [],
  36. 'studioId' => null,
  37. 'lang' => 'zh-Hans',
  38. 'footnote' => false,
  39. 'paragraph' => false,
  40. 'origin' => true,
  41. 'translation' => true,
  42. ];
  43. /**
  44. * Create a new command instance.
  45. * string $mode 'read' | 'edit'
  46. * string $format 'react' | 'text' | 'tex' | 'unity'
  47. * @return void
  48. */
  49. public function __construct($param, $channelInfo, $mode, $format = 'react', $studioId = '', $debug = [], $lang = 'zh-Hans')
  50. {
  51. $this->param = $param;
  52. foreach ($channelInfo as $value) {
  53. $this->channel_id[] = $value->uid;
  54. }
  55. $this->channelInfo = $channelInfo;
  56. $this->mode = $mode;
  57. $this->format = $format;
  58. $this->studioId = $studioId;
  59. $this->debug = $debug;
  60. $this->glossaryKey = 'glossary';
  61. if (count($this->channel_id) > 0) {
  62. $channelId = $this->channel_id[0];
  63. if (Str::isUuid($channelId)) {
  64. $lang = Channel::where('uid', $channelId)->value('lang');
  65. }
  66. }
  67. if (!empty($lang)) {
  68. $this->lang = $lang;
  69. $this->langFamily = explode('-', $lang)[0];
  70. }
  71. }
  72. public function options($options = [])
  73. {
  74. foreach ($options as $key => $value) {
  75. $this->options[$key] = $value;
  76. }
  77. }
  78. public function glossaryKey()
  79. {
  80. return $this->glossaryKey;
  81. }
  82. /**
  83. * TODO 设置默认语言。在渲染某些内容的时候需要语言信息
  84. */
  85. public function setLang($lang)
  86. {
  87. $this->lang = $lang;
  88. $this->langFamily = explode('-', $lang)[0];
  89. }
  90. private function info($message, $debug)
  91. {
  92. if (in_array($debug, $this->debug)) {
  93. Log::info($message);
  94. }
  95. }
  96. private function error($message, $debug)
  97. {
  98. if (in_array($debug, $this->debug)) {
  99. Log::error($message);
  100. }
  101. }
  102. public function render($tpl_name)
  103. {
  104. switch ($tpl_name) {
  105. case 'term':
  106. # 术语
  107. $result = $this->render_term();
  108. break;
  109. case 'note':
  110. $result = $this->render_note();
  111. break;
  112. case 'sent':
  113. $result = $this->render_sent();
  114. break;
  115. case 'quote':
  116. $result = $this->render_quote();
  117. break;
  118. case 'ql':
  119. $result = $this->render_quote_link();
  120. break;
  121. case 'exercise':
  122. $result = $this->render_exercise();
  123. break;
  124. case 'article':
  125. $result = $this->render_article();
  126. break;
  127. case 'nissaya':
  128. $result = $this->render_nissaya();
  129. break;
  130. case 'mermaid':
  131. $result = $this->render_mermaid();
  132. break;
  133. case 'qa':
  134. $result = $this->render_qa();
  135. break;
  136. case 'v':
  137. $result = $this->render_video();
  138. break;
  139. case 'g':
  140. $result = $this->render_grammar_lookup();
  141. break;
  142. case 'ref':
  143. $result = $this->render_ref();
  144. break;
  145. case 'dict-pref':
  146. $result = $this->render_dict_pref();
  147. break;
  148. default:
  149. # code...
  150. $result = [
  151. 'props' => base64_encode(\json_encode([])),
  152. 'html' => '',
  153. 'tag' => 'span',
  154. 'tpl' => 'unknown',
  155. ];
  156. break;
  157. }
  158. return $result;
  159. }
  160. public function getTermProps($word, $tag = null, $channel = null)
  161. {
  162. if ($channel && !empty($channel)) {
  163. $channelId = $channel;
  164. } else {
  165. if (count($this->channel_id) > 0) {
  166. $channelId = $this->channel_id[0];
  167. } else {
  168. $channelId = null;
  169. }
  170. }
  171. if (count($this->channelInfo) === 0) {
  172. if (!empty($channel)) {
  173. $channelInfo = Channel::where('uid', $channel)->first();
  174. if (!$channelInfo) {
  175. unset($channelInfo);
  176. }
  177. }
  178. if (!isset($channelInfo)) {
  179. Log::error('channel is null');
  180. $output = [
  181. "word" => $word,
  182. 'innerHtml' => '',
  183. ];
  184. return $output;
  185. }
  186. } else {
  187. $channelInfo = $this->channelInfo[0];
  188. }
  189. if (Str::isUuid($channelId)) {
  190. $lang = Channel::where('uid', $channelId)->value('lang');
  191. if (!empty($lang)) {
  192. $langFamily = explode('-', $lang)[0];
  193. } else {
  194. $langFamily = 'zh';
  195. }
  196. $this->info("term:{$word} 先查属于这个channel 的", 'term');
  197. $this->info('channel id' . $channelId, 'term');
  198. $table = DhammaTerm::where("word", $word)
  199. ->where('channal', $channelId);
  200. if ($tag && !empty($tag)) {
  201. $table = $table->where('tag', $tag);
  202. }
  203. $tplParam = $table->orderBy('updated_at', 'desc')
  204. ->first();
  205. $studioId = $channelInfo->owner_uid;
  206. } else {
  207. $tplParam = false;
  208. $lang = '';
  209. $langFamily = '';
  210. $studioId = $this->studioId;
  211. }
  212. if (!$tplParam) {
  213. if (Str::isUuid($studioId)) {
  214. /**
  215. * 没有,再查这个studio的
  216. * 按照语言过滤
  217. * 完全匹配的优先
  218. * 语族匹配也行
  219. */
  220. $this->info("没有-再查这个studio的", 'term');
  221. $table = DhammaTerm::where("word", $word);
  222. if (!empty($tag)) {
  223. $table = $table->where('tag', $tag);
  224. }
  225. $termsInStudio = $table->where('owner', $channelInfo->owner_uid)
  226. ->orderBy('updated_at', 'desc')
  227. ->get();
  228. if (count($termsInStudio) > 0) {
  229. $list = array();
  230. foreach ($termsInStudio as $key => $term) {
  231. if (empty($term->channal)) {
  232. if ($term->language === $lang) {
  233. $list[$term->guid] = 2;
  234. } else if (strpos($term->language, $langFamily) !== false) {
  235. $list[$term->guid] = 1;
  236. }
  237. }
  238. }
  239. if (count($list) > 0) {
  240. arsort($list);
  241. foreach ($list as $key => $one) {
  242. foreach ($termsInStudio as $term) {
  243. if ($term->guid === $key) {
  244. $tplParam = $term;
  245. break;
  246. }
  247. }
  248. break;
  249. }
  250. }
  251. }
  252. }
  253. }
  254. if (!$tplParam) {
  255. $this->info("没有,再查社区", 'term');
  256. $community_channel = ChannelApi::getSysChannel("_community_term_zh-hans_");
  257. $table = DhammaTerm::where("word", $word);
  258. if (!empty($tag)) {
  259. $table = $table->where('tag', $tag);
  260. }
  261. $tplParam = $table->where('channal', $community_channel)
  262. ->first();
  263. if ($tplParam) {
  264. $isCommunity = true;
  265. } else {
  266. $this->info("查社区没有", 'term');
  267. }
  268. }
  269. $output = [
  270. "word" => $word,
  271. "parentChannelId" => $channelId,
  272. "parentStudioId" => $channelInfo->owner_uid,
  273. ];
  274. $innerString = $output["word"];
  275. if ($tplParam) {
  276. $output["id"] = $tplParam->guid;
  277. $output["meaning"] = $tplParam->meaning;
  278. $output["channel"] = $tplParam->channal;
  279. if (!empty($tplParam->note)) {
  280. $mdRender = new MdRender(['format' => $this->format]);
  281. $output['note'] = $mdRender->convert($tplParam->note, $this->channel_id);
  282. }
  283. if (isset($isCommunity)) {
  284. $output["isCommunity"] = true;
  285. }
  286. $innerString = "{$output["meaning"]}({$output["word"]})";
  287. if (!empty($tplParam->other_meaning)) {
  288. $output["meaning2"] = $tplParam->other_meaning;
  289. }
  290. }
  291. $output['innerHtml'] = $innerString;
  292. return $output;
  293. }
  294. private function render_term()
  295. {
  296. $word = $this->get_param($this->param, "word", 1);
  297. $props = $this->getTermProps($word, '');
  298. $output = $props['word'];
  299. switch ($this->format) {
  300. case 'react':
  301. $output = [
  302. 'props' => base64_encode(\json_encode($props)),
  303. 'html' => $props['innerHtml'],
  304. 'tag' => 'span',
  305. 'tpl' => 'term',
  306. ];
  307. break;
  308. case 'unity':
  309. $output = [
  310. 'props' => base64_encode(\json_encode($props)),
  311. 'tpl' => 'term',
  312. ];
  313. break;
  314. case 'html':
  315. if (isset($props["meaning"])) {
  316. $GLOBALS[$this->glossaryKey][$props["word"]] = $props['meaning'];
  317. $key = 'term-' . $props["word"];
  318. $termHead = "<a href='#'>" . $props['meaning'] . "</a>";
  319. if (isset($GLOBALS[$key])) {
  320. $output = $termHead;
  321. } else {
  322. $GLOBALS[$key] = 1;
  323. $output = $termHead . '(<em>' . $props["word"] . '</em>)';
  324. }
  325. } else {
  326. $output = $props["word"];
  327. }
  328. break;
  329. case 'text':
  330. if (isset($props["meaning"])) {
  331. $key = 'term-' . $props["word"];
  332. if (isset($GLOBALS[$key])) {
  333. $output = $props["meaning"];
  334. } else {
  335. $GLOBALS[$key] = 1;
  336. $output = $props["meaning"] . '(' . $props["word"] . ')';
  337. }
  338. } else {
  339. $output = $props["word"];
  340. }
  341. break;
  342. case 'tex':
  343. if (isset($props["meaning"])) {
  344. $key = 'term-' . $props["word"];
  345. if (isset($GLOBALS[$key])) {
  346. $output = $props["meaning"];
  347. } else {
  348. $GLOBALS[$key] = 1;
  349. $output = $props["meaning"] . '(' . $props["word"] . ')';
  350. }
  351. } else {
  352. $output = $props["word"];
  353. }
  354. break;
  355. case 'simple':
  356. if (isset($props["meaning"])) {
  357. $output = $props["meaning"];
  358. } else {
  359. $output = $props["word"];
  360. }
  361. break;
  362. case 'markdown':
  363. if (isset($props["meaning"])) {
  364. $key = 'term-' . $props["word"];
  365. if (isset($GLOBALS[$key]) && $GLOBALS[$key] === 1) {
  366. $GLOBALS[$key]++;
  367. $output = $props["meaning"];
  368. } else {
  369. $GLOBALS[$key] = 1;
  370. $output = $props["meaning"] . '(' . $props["word"] . ')';
  371. }
  372. } else {
  373. $output = $props["word"];
  374. }
  375. //如果有内容且第一次出现,显示为脚注
  376. if (!empty($props["note"]) && $GLOBALS[$key] === 1) {
  377. if (isset($GLOBALS['note_sn'])) {
  378. $GLOBALS['note_sn']++;
  379. } else {
  380. $GLOBALS['note_sn'] = 1;
  381. $GLOBALS['note'] = array();
  382. }
  383. $content = $props["note"];
  384. $output .= '[^' . $GLOBALS['note_sn'] . ']';
  385. $GLOBALS['note'][] = [
  386. 'sn' => $GLOBALS['note_sn'],
  387. 'trigger' => '',
  388. 'content' => $content,
  389. ];
  390. }
  391. break;
  392. default:
  393. if (isset($props["meaning"])) {
  394. $output = $props["meaning"];
  395. } else {
  396. $output = $props["word"];
  397. }
  398. break;
  399. }
  400. return $output;
  401. }
  402. private function render_note()
  403. {
  404. $note = $this->get_param($this->param, "text", 1);
  405. $trigger = $this->get_param($this->param, "trigger", 2);
  406. $props = ["note" => $note];
  407. $innerString = "";
  408. if (!empty($trigger)) {
  409. $props["trigger"] = $trigger;
  410. $innerString = $props["trigger"];
  411. }
  412. if ($this->format === 'unity') {
  413. $props["note"] = MdRender::render(
  414. $props["note"],
  415. $this->channel_id,
  416. null,
  417. 'read',
  418. 'translation',
  419. 'markdown',
  420. 'unity'
  421. );
  422. }
  423. $output = $note;
  424. switch ($this->format) {
  425. case 'react':
  426. $output = [
  427. 'props' => base64_encode(\json_encode($props)),
  428. 'html' => $innerString,
  429. 'tag' => 'span',
  430. 'tpl' => 'note',
  431. ];
  432. break;
  433. case 'unity':
  434. $output = [
  435. 'props' => base64_encode(\json_encode($props)),
  436. 'tpl' => 'note',
  437. ];
  438. break;
  439. case 'html':
  440. if (isset($GLOBALS['note_sn'])) {
  441. $GLOBALS['note_sn']++;
  442. } else {
  443. $GLOBALS['note_sn'] = 1;
  444. $GLOBALS['note'] = array();
  445. }
  446. $GLOBALS['note'][] = [
  447. 'sn' => $GLOBALS['note_sn'],
  448. 'trigger' => $trigger,
  449. 'content' => MdRender::render(
  450. $props["note"],
  451. $this->channel_id,
  452. null,
  453. 'read',
  454. 'translation',
  455. 'markdown',
  456. 'html'
  457. ),
  458. ];
  459. $link = "<a href='#footnote-" . $GLOBALS['note_sn'] . "' name='note-" . $GLOBALS['note_sn'] . "'>";
  460. if (empty($trigger)) {
  461. $output = $link . "<sup>[" . $GLOBALS['note_sn'] . "]</sup></a>";
  462. } else {
  463. $output = $link . $trigger . "</a>";
  464. }
  465. break;
  466. case 'text':
  467. $output = $trigger;
  468. break;
  469. case 'tex':
  470. $output = $trigger;
  471. break;
  472. case 'simple':
  473. $output = '';
  474. break;
  475. case 'markdown':
  476. if (isset($GLOBALS['note_sn'])) {
  477. $GLOBALS['note_sn']++;
  478. } else {
  479. $GLOBALS['note_sn'] = 1;
  480. $GLOBALS['note'] = array();
  481. }
  482. $content = MdRender::render(
  483. $props["note"],
  484. $this->channel_id,
  485. null,
  486. 'read',
  487. 'translation',
  488. 'markdown',
  489. 'markdown'
  490. );
  491. $output = '[^' . $GLOBALS['note_sn'] . ']';
  492. $GLOBALS['note'][] = [
  493. 'sn' => $GLOBALS['note_sn'],
  494. 'trigger' => $trigger,
  495. 'content' => $content,
  496. ];
  497. //$output = '<footnote id="'.$GLOBALS['note_sn'].'">'.$content.'</footnote>';
  498. break;
  499. default:
  500. $output = '';
  501. break;
  502. }
  503. return $output;
  504. }
  505. private function render_nissaya()
  506. {
  507. $pali = $this->get_param($this->param, "pali", 1);
  508. $meaning = $this->get_param($this->param, "meaning", 2);
  509. $innerString = "";
  510. $props = [
  511. "pali" => $pali,
  512. "meaning" => $meaning,
  513. "lang" => $this->lang,
  514. ];
  515. switch ($this->format) {
  516. case 'react':
  517. $output = [
  518. 'props' => base64_encode(\json_encode($props)),
  519. 'html' => $innerString,
  520. 'tag' => 'span',
  521. 'tpl' => 'nissaya',
  522. ];
  523. break;
  524. case 'unity':
  525. $output = [
  526. 'props' => base64_encode(\json_encode($props)),
  527. 'tpl' => 'nissaya',
  528. ];
  529. break;
  530. case 'text':
  531. $output = $pali . '၊' . $meaning;
  532. break;
  533. case 'tex':
  534. $output = $pali . '၊' . $meaning;
  535. break;
  536. case 'simple':
  537. $output = $pali . '၊' . $meaning;
  538. break;
  539. default:
  540. $output = $pali . '၊' . $meaning;
  541. break;
  542. }
  543. return $output;
  544. }
  545. private function render_exercise()
  546. {
  547. $id = $this->get_param($this->param, "id", 1);
  548. $title = $this->get_param($this->param, "title", 1);
  549. $props = [
  550. "id" => $id,
  551. "title" => $title,
  552. "channel" => $this->channel_id[0],
  553. ];
  554. switch ($this->format) {
  555. case 'react':
  556. $output = [
  557. 'props' => base64_encode(\json_encode($props)),
  558. 'html' => "",
  559. 'tag' => 'span',
  560. 'tpl' => 'exercise',
  561. ];
  562. break;
  563. case 'unity':
  564. $output = [
  565. 'props' => base64_encode(\json_encode($props)),
  566. 'tpl' => 'exercise',
  567. ];
  568. break;
  569. case 'text':
  570. $output = $title;
  571. break;
  572. case 'tex':
  573. $output = $title;
  574. break;
  575. case 'simple':
  576. $output = $title;
  577. break;
  578. default:
  579. $output = '';
  580. break;
  581. }
  582. return $output;
  583. }
  584. private function render_article()
  585. {
  586. $type = $this->get_param($this->param, "type", 1);
  587. $id = $this->get_param($this->param, "id", 2);
  588. $title = $this->get_param($this->param, "title", 3);
  589. $channel = $this->get_param($this->param, "channel", 4);
  590. $style = $this->get_param($this->param, "style", 5);
  591. $book = $this->get_param($this->param, "book", 6);
  592. $paragraphs = $this->get_param($this->param, "paragraphs", 7);
  593. $anthology = $this->get_param($this->param, "anthology", 8);
  594. $props = [
  595. "type" => $type,
  596. "id" => $id,
  597. 'style' => $style,
  598. ];
  599. if (!empty($channel)) {
  600. $props['channel'] = $channel;
  601. }
  602. if (!empty($title)) {
  603. $props['title'] = $title;
  604. }
  605. if (!empty($book)) {
  606. $props['book'] = $book;
  607. }
  608. if (!empty($paragraphs)) {
  609. $props['paragraphs'] = $paragraphs;
  610. }
  611. if (!empty($anthology)) {
  612. $props['anthology'] = $anthology;
  613. }
  614. if (is_array($this->channel_id)) {
  615. $props['parentChannels'] = $this->channel_id;
  616. }
  617. switch ($this->format) {
  618. case 'react':
  619. $output = [
  620. 'props' => base64_encode(\json_encode($props)),
  621. 'html' => "",
  622. 'text' => $title,
  623. 'tag' => 'span',
  624. 'tpl' => 'article',
  625. ];
  626. break;
  627. case 'unity':
  628. $output = [
  629. 'props' => base64_encode(\json_encode($props)),
  630. 'tpl' => 'article',
  631. ];
  632. break;
  633. case 'text':
  634. $output = $title;
  635. break;
  636. case 'tex':
  637. $output = $title;
  638. break;
  639. case 'simple':
  640. $output = $title;
  641. break;
  642. default:
  643. $output = '';
  644. break;
  645. }
  646. return $output;
  647. }
  648. private function render_quote()
  649. {
  650. $paraId = $this->get_param($this->param, "para", 1);
  651. $channelId = $this->channel_id[0];
  652. $props = RedisClusters::remember(
  653. "/quote/{$channelId}/{$paraId}",
  654. config('mint.cache.expire'),
  655. function () use ($paraId, $channelId) {
  656. $para = \explode('-', $paraId);
  657. $output = [
  658. "paraId" => $paraId,
  659. "channel" => $channelId,
  660. "innerString" => $paraId,
  661. ];
  662. if (count($para) < 2) {
  663. return $output;
  664. }
  665. $PaliText = PaliText::where("book", $para[0])
  666. ->where("paragraph", $para[1])
  667. ->select(['toc', 'path'])
  668. ->first();
  669. if ($PaliText) {
  670. $output["pali"] = $PaliText->toc;
  671. $output["paliPath"] = \json_decode($PaliText->path);
  672. $output["innerString"] = $PaliText->toc;
  673. }
  674. return $output;
  675. }
  676. );
  677. switch ($this->format) {
  678. case 'react':
  679. $output = [
  680. 'props' => base64_encode(\json_encode($props)),
  681. 'html' => $props["innerString"],
  682. 'tag' => 'span',
  683. 'tpl' => 'quote',
  684. ];
  685. break;
  686. case 'unity':
  687. $output = [
  688. 'props' => base64_encode(\json_encode($props)),
  689. 'tpl' => 'quote',
  690. ];
  691. break;
  692. case 'text':
  693. $output = $props["innerString"];
  694. break;
  695. case 'tex':
  696. $output = $props["innerString"];
  697. break;
  698. case 'simple':
  699. $output = $props["innerString"];
  700. break;
  701. default:
  702. $output = $props["innerString"];
  703. break;
  704. }
  705. return $output;
  706. }
  707. private function render_quote_link()
  708. {
  709. $type = $this->get_param($this->param, "type", 1);
  710. $title = $this->get_param($this->param, "title", 6, '');
  711. $bookName = $this->get_param($this->param, "bookname", 2, '');
  712. $volume = $this->get_param($this->param, "volume", 3);
  713. $page = $this->get_param($this->param, "page", 4, '');
  714. $style = $this->get_param($this->param, "style", 5, 'modal');
  715. $book = $this->get_param($this->param, "book", 7, false);
  716. $para = $this->get_param($this->param, "para", 8, false);
  717. $props = [
  718. 'type' => $type,
  719. 'style' => $style,
  720. 'found' => true,
  721. ];
  722. if (!empty($bookName) && $volume !== '' && !empty($page)) {
  723. $props['bookName'] = $bookName;
  724. $props['volume'] = (int)$volume;
  725. $props['page'] = $page;
  726. $props['found'] = true;
  727. } else if ($book && $para) {
  728. /**
  729. * 没有指定书名,根据book para 查询
  730. */
  731. if ($type === 'c') {
  732. //按照章节名称显示
  733. $path = PaliTextApi::getChapterPath($book, $para);
  734. if ($path) {
  735. $path = json_decode($path, true);
  736. }
  737. if ($path && is_array($path) && count($path) > 2) {
  738. $props['bookName'] = strtolower($path[0]['title']);
  739. $props['chapter'] = strtolower(end($path)['title']);
  740. $props['found'] = true;
  741. } else {
  742. $props['found'] = false;
  743. }
  744. } else {
  745. $pageInfo = $this->pageInfoByPara($type, $book, $para);
  746. if ($pageInfo['found']) {
  747. $props['bookName'] = $pageInfo['bookName'];
  748. $props['volume'] = $pageInfo['volume'];
  749. $props['page'] = $pageInfo['page'];
  750. $props['found'] = true;
  751. } else {
  752. $props['found'] = false;
  753. }
  754. }
  755. } else if ($title) {
  756. //没有书号用title查询
  757. //$tmpTitle = explode('။',$title);
  758. for ($i = mb_strlen($title, 'UTF-8'); $i > 0; $i--) {
  759. $mTitle = mb_substr($title, 0, $i);
  760. $has = array_search($mTitle, array_column(BookTitle::my(), 'title2'));
  761. Log::debug('run', ['title' => $mTitle, 'has' => $has]);
  762. if ($has !== false) {
  763. $tmpBookTitle = $mTitle;
  764. $tmpBookPage = mb_substr($title, $i);
  765. $tmpBookPage = $this->mb_trim($tmpBookPage, '၊။');
  766. break;
  767. }
  768. }
  769. if (isset($tmpBookTitle)) {
  770. Log::debug('book title found', ['title' => $tmpBookTitle, 'page' => $tmpBookPage]);
  771. //$tmpBookTitle = $tmpTitle[0];
  772. //$tmpBookPage = $tmpTitle[1];
  773. $tmpBookPage = (int)str_replace(
  774. ['၁', '၂', '၃', '၄', '၅', '၆', '၇', '၈', '၉', '၀'],
  775. ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'],
  776. $tmpBookPage
  777. );
  778. $found_key = array_search($tmpBookTitle, array_column(BookTitle::my(), 'title2'));
  779. if ($found_key !== false) {
  780. $props['bookName'] = BookTitle::my()[$found_key]['bookname'];
  781. $props['volume'] = BookTitle::my()[$found_key]['volume'];
  782. $props['page'] = $tmpBookPage;
  783. if (!empty($props['bookName'])) {
  784. $found_title = array_search($props['bookName'], array_column(BookTitle::my(), 'bookname'));
  785. if ($found_title === false) {
  786. $props['found'] = false;
  787. }
  788. }
  789. } else {
  790. //没找到,返回术语和页码
  791. $props['found'] = false;
  792. $props['bookName'] = $tmpBookTitle;
  793. $props['page'] = $tmpBookPage;
  794. $props['volume'] = 0;
  795. }
  796. }
  797. } else {
  798. Log::debug('book title not found');
  799. $props['found'] = false;
  800. }
  801. if ($book && $para) {
  802. $props['book'] = $book;
  803. $props['para'] = $para;
  804. }
  805. if ($title) {
  806. $props['title'] = $title;
  807. }
  808. $text = '';
  809. if (isset($props['bookName'])) {
  810. $searchField = '';
  811. switch ($type) {
  812. case 'm':
  813. $searchField = 'm_title';
  814. break;
  815. case 'p':
  816. $searchField = 'p_title';
  817. break;
  818. }
  819. $found_title = array_search($props['bookName'], array_column(BookTitle::get(), $searchField));
  820. if ($found_title === false) {
  821. $props['found'] = false;
  822. }
  823. $term = $this->getTermProps($props['bookName'], ':quote:');
  824. $props['term'] = $term;
  825. if (isset($term['id'])) {
  826. $props['bookNameLocal'] = $term['meaning'];
  827. $text .= $term['meaning'];
  828. } else {
  829. $text .= $bookName;
  830. }
  831. }
  832. if (isset($props['volume']) && isset($props['page'])) {
  833. $text .= " {$volume}.{$page}";
  834. }
  835. switch ($this->format) {
  836. case 'react':
  837. $output = [
  838. 'props' => base64_encode(\json_encode($props)),
  839. 'html' => '',
  840. 'tag' => 'span',
  841. 'tpl' => 'quote-link',
  842. ];
  843. break;
  844. case 'unity':
  845. $output = [
  846. 'props' => base64_encode(\json_encode($props)),
  847. 'tpl' => 'quote-link',
  848. ];
  849. break;
  850. default:
  851. $output = $text;
  852. break;
  853. }
  854. return $output;
  855. }
  856. private function pageInfoByPara($type, $book, $para)
  857. {
  858. $output = array();
  859. $pageInfo = PageNumber::where('type', strtoupper($type))
  860. ->where('book', $book)
  861. ->where('paragraph', '<=', $para)
  862. ->orderBy('paragraph', 'desc')
  863. ->first();
  864. if ($pageInfo) {
  865. foreach (BookTitle::get() as $value) {
  866. if ($value['id'] === $pageInfo->pcd_book_id) {
  867. switch (strtoupper($type)) {
  868. case 'M':
  869. $key = 'm_title';
  870. break;
  871. case 'P':
  872. $key = 'p_title';
  873. break;
  874. case 'V':
  875. $key = 'v_title';
  876. break;
  877. default:
  878. $key = 'term';
  879. break;
  880. }
  881. $output['bookName'] = $value[$key];
  882. break;
  883. }
  884. }
  885. $output['volume'] = $pageInfo->volume;
  886. $output['page'] = $pageInfo->page;
  887. $output['found'] = true;
  888. } else {
  889. $output['found'] = false;
  890. }
  891. return $output;
  892. }
  893. private function render_sent()
  894. {
  895. $sid = $this->get_param($this->param, "id", 1);
  896. $channel = $this->get_param($this->param, "channel", 2);
  897. $text = $this->get_param($this->param, "text", 2, 'both');
  898. if (!empty($channel)) {
  899. $channels = explode(',', $channel);
  900. } else {
  901. $channels = $this->channel_id;
  902. }
  903. $sentInfo = explode('@', trim($sid));
  904. $sentId = $sentInfo[0];
  905. if (isset($sentInfo[1])) {
  906. $channels = [$sentInfo[1]];
  907. }
  908. $Sent = new CorpusController();
  909. $props = $Sent->getSentTpl(
  910. $sentId,
  911. $channels,
  912. $this->mode,
  913. true,
  914. $this->format
  915. );
  916. if ($props === false) {
  917. $props['error'] = "句子模版渲染错误。句子参数个数不符。应该是四个。";
  918. }
  919. if ($this->mode === 'read') {
  920. $tpl = "sentread";
  921. } else {
  922. $tpl = "sentedit";
  923. }
  924. //输出引用
  925. $arrSid = explode('-', $sid);
  926. $bookPara = array_slice($arrSid, 0, 2);
  927. if (!isset($GLOBALS['ref_sent'])) {
  928. $GLOBALS['ref_sent'] = array();
  929. }
  930. $GLOBALS['ref_sent'][] = $bookPara;
  931. switch ($this->format) {
  932. case 'react':
  933. $output = [
  934. 'props' => base64_encode(\json_encode($props)),
  935. 'html' => "",
  936. 'tag' => 'span',
  937. 'tpl' => $tpl,
  938. ];
  939. break;
  940. case 'unity':
  941. $output = [
  942. 'props' => base64_encode(\json_encode($props)),
  943. 'tpl' => $tpl,
  944. ];
  945. break;
  946. case 'text':
  947. $output = '';
  948. if (isset($props['origin']) && is_array($props['origin'])) {
  949. foreach ($props['origin'] as $key => $value) {
  950. $output .= $value['html'];
  951. }
  952. }
  953. if (isset($props['translation']) && is_array($props['translation'])) {
  954. foreach ($props['translation'] as $key => $value) {
  955. $output .= $value['html'];
  956. }
  957. }
  958. break;
  959. case 'html':
  960. $output = '';
  961. $output .= '<span class="sentence">';
  962. if ($text === 'both' || $text === 'origin') {
  963. if (isset($props['origin']) && is_array($props['origin'])) {
  964. foreach ($props['origin'] as $key => $value) {
  965. $output .= '<span class="origin">' . $value['html'] . '</span>';
  966. }
  967. }
  968. }
  969. if ($text === 'both' || $text === 'translation') {
  970. if (isset($props['translation']) && is_array($props['translation'])) {
  971. foreach ($props['translation'] as $key => $value) {
  972. $output .= '<span class="translation">' . $value['html'] . '</span>';
  973. }
  974. }
  975. }
  976. $output .= '</span>';
  977. break;
  978. case 'tex':
  979. $output = '';
  980. if (isset($props['translation']) && is_array($props['translation'])) {
  981. foreach ($props['translation'] as $key => $value) {
  982. $output .= $value['html'];
  983. }
  984. }
  985. break;
  986. case 'simple':
  987. $output = '';
  988. if ($text === 'both' || $text === 'origin') {
  989. if (empty($output)) {
  990. if (
  991. isset($props['origin']) &&
  992. is_array($props['origin']) &&
  993. count($props['origin']) > 0
  994. ) {
  995. foreach ($props['origin'] as $key => $value) {
  996. $output .= trim($value['html']);
  997. }
  998. }
  999. }
  1000. }
  1001. if ($text === 'both' || $text === 'translation') {
  1002. if (
  1003. isset($props['translation']) &&
  1004. is_array($props['translation']) &&
  1005. count($props['translation']) > 0
  1006. ) {
  1007. foreach ($props['translation'] as $key => $value) {
  1008. $output .= trim($value['html']);
  1009. }
  1010. }
  1011. }
  1012. break;
  1013. case 'markdown':
  1014. $output = '';
  1015. if ($text === 'both' || $text === 'origin') {
  1016. if (
  1017. $this->options['origin'] === true ||
  1018. $this->options['origin'] === 'true'
  1019. ) {
  1020. if (isset($props['origin']) && is_array($props['origin'])) {
  1021. foreach ($props['origin'] as $key => $value) {
  1022. $output .= trim($value['html']);
  1023. }
  1024. }
  1025. }
  1026. }
  1027. if ($text === 'both' || $text === 'translation') {
  1028. if (
  1029. $this->options['translation'] === true ||
  1030. $this->options['translation'] === 'true'
  1031. ) {
  1032. if (
  1033. isset($props['translation']) &&
  1034. is_array($props['translation']) &&
  1035. count($props['translation']) > 0
  1036. ) {
  1037. foreach ($props['translation'] as $key => $value) {
  1038. $output .= trim($value['html']);
  1039. }
  1040. } else {
  1041. if ($text === 'translation') {
  1042. //无译文用原文代替
  1043. if (isset($props['origin']) && is_array($props['origin'])) {
  1044. foreach ($props['origin'] as $key => $value) {
  1045. $output .= trim($value['html']);
  1046. }
  1047. }
  1048. }
  1049. }
  1050. }
  1051. }
  1052. break;
  1053. default:
  1054. $output = '';
  1055. break;
  1056. }
  1057. return $output;
  1058. }
  1059. private function render_mermaid()
  1060. {
  1061. $text = json_decode(base64_decode($this->get_param($this->param, "text", 1)));
  1062. $props = ["text" => implode("\n", $text)];
  1063. switch ($this->format) {
  1064. case 'react':
  1065. $output = [
  1066. 'props' => base64_encode(\json_encode($props)),
  1067. 'html' => "mermaid",
  1068. 'tag' => 'div',
  1069. 'tpl' => 'mermaid',
  1070. ];
  1071. break;
  1072. case 'unity':
  1073. $output = [
  1074. 'props' => base64_encode(\json_encode($props)),
  1075. 'tpl' => 'mermaid',
  1076. ];
  1077. break;
  1078. case 'text':
  1079. $output = 'mermaid';
  1080. break;
  1081. case 'tex':
  1082. $output = 'mermaid';
  1083. break;
  1084. case 'simple':
  1085. $output = 'mermaid';
  1086. break;
  1087. default:
  1088. $output = 'mermaid';
  1089. break;
  1090. }
  1091. return $output;
  1092. }
  1093. private function render_qa()
  1094. {
  1095. $id = $this->get_param($this->param, "id", 1);
  1096. $style = $this->get_param($this->param, "style", 2);
  1097. $props = [
  1098. "type" => 'qa',
  1099. "id" => $id,
  1100. 'title' => '',
  1101. 'style' => $style,
  1102. ];
  1103. $qa = Discussion::where('id', $id)->first();
  1104. if ($qa) {
  1105. $props['title'] = $qa->title;
  1106. $props['resId'] = $qa->res_id;
  1107. $props['resType'] = $qa->res_type;
  1108. }
  1109. switch ($this->format) {
  1110. case 'react':
  1111. $output = [
  1112. 'props' => base64_encode(\json_encode($props)),
  1113. 'html' => "",
  1114. 'text' => $props['title'],
  1115. 'tag' => 'div',
  1116. 'tpl' => 'qa',
  1117. ];
  1118. break;
  1119. case 'unity':
  1120. $output = [
  1121. 'props' => base64_encode(\json_encode($props)),
  1122. 'tpl' => 'qa',
  1123. ];
  1124. break;
  1125. default:
  1126. $output = $props['title'];
  1127. break;
  1128. }
  1129. return $output;
  1130. }
  1131. private function render_grammar_lookup()
  1132. {
  1133. $word = $this->get_param($this->param, "word", 1);
  1134. $props = ['word' => $word];
  1135. $localTermChannel = ChannelApi::getSysChannel(
  1136. "_System_Grammar_Term_" . strtolower($this->lang) . "_",
  1137. "_System_Grammar_Term_en_"
  1138. );
  1139. $term = $this->getTermProps($word, null, $localTermChannel);
  1140. $props['term'] = $term;
  1141. switch ($this->format) {
  1142. case 'react':
  1143. $output = [
  1144. 'props' => base64_encode(\json_encode($props)),
  1145. 'html' => "",
  1146. 'text' => $props['word'],
  1147. 'tag' => 'span',
  1148. 'tpl' => 'grammar',
  1149. ];
  1150. break;
  1151. case 'unity':
  1152. $output = [
  1153. 'props' => base64_encode(\json_encode($props)),
  1154. 'tpl' => 'grammar',
  1155. ];
  1156. break;
  1157. default:
  1158. $output = $props['word'];
  1159. break;
  1160. }
  1161. return $output;
  1162. }
  1163. private function render_video()
  1164. {
  1165. $url = $this->get_param($this->param, "url", 1);
  1166. $style = $this->get_param($this->param, "style", 2, 'modal');
  1167. $title = $this->get_param($this->param, "title", 3);
  1168. $props = [
  1169. "url" => $url,
  1170. 'title' => $title,
  1171. 'style' => $style,
  1172. ];
  1173. switch ($this->format) {
  1174. case 'react':
  1175. $output = [
  1176. 'props' => base64_encode(\json_encode($props)),
  1177. 'html' => "",
  1178. 'text' => $props['title'],
  1179. 'tag' => 'span',
  1180. 'tpl' => 'video',
  1181. ];
  1182. break;
  1183. case 'unity':
  1184. $output = [
  1185. 'props' => base64_encode(\json_encode($props)),
  1186. 'tpl' => 'video',
  1187. ];
  1188. break;
  1189. default:
  1190. $output = $props['title'];
  1191. break;
  1192. }
  1193. return $output;
  1194. }
  1195. //论文后面的参考资料
  1196. private function render_ref()
  1197. {
  1198. $references = array();
  1199. $counter = 0;
  1200. if (isset($GLOBALS['ref_sent'])) {
  1201. $hasBooks = array();
  1202. $book_titles = BookSeries::select(['book', 'paragraph', 'title', 'sn'])
  1203. ->orderBy('sn', 'DESC')->get();
  1204. $bTitles = array();
  1205. foreach ($book_titles as $key => $book) {
  1206. $bTitles[] = [
  1207. 'book' => $book->book,
  1208. 'paragraph' => $book->paragraph,
  1209. 'title' => $book->title
  1210. ];
  1211. }
  1212. foreach ($GLOBALS['ref_sent'] as $key => $ref) {
  1213. $books = array_filter($bTitles, function ($value) use ($ref) {
  1214. return $value['book'] === (int)$ref[0];
  1215. });
  1216. if (count($books) > 0) {
  1217. foreach ($books as $key => $book) {
  1218. if ($book['paragraph'] < (int)$ref[1]) {
  1219. if (!isset($hasBooks[$book['title']])) {
  1220. $hasBooks[$book['title']] = 1;
  1221. $counter++;
  1222. $references[] = [
  1223. 'sn' => $counter,
  1224. 'title' => $book['title'],
  1225. 'copyright' => 'CSCD V4 VRI 2008'
  1226. ];
  1227. }
  1228. }
  1229. }
  1230. }
  1231. }
  1232. }
  1233. $props = [
  1234. "pali" => $references,
  1235. ];
  1236. switch ($this->format) {
  1237. case 'react':
  1238. $output = [
  1239. 'props' => base64_encode(\json_encode($props)),
  1240. 'html' => '',
  1241. 'tag' => 'div',
  1242. 'tpl' => 'reference',
  1243. ];
  1244. break;
  1245. case 'unity':
  1246. $output = [
  1247. 'props' => base64_encode(\json_encode($props)),
  1248. 'tpl' => 'reference',
  1249. ];
  1250. break;
  1251. case 'markdown':
  1252. $output = '';
  1253. foreach ($references as $key => $reference) {
  1254. $output .= '[' . $reference['sn'] . '] **' . ucfirst($reference['title']) . '** ';
  1255. $output .= $reference['copyright'] . "\n\n";
  1256. }
  1257. break;
  1258. default:
  1259. $output = '';
  1260. foreach ($references as $key => $reference) {
  1261. $output .= '[' . $reference['sn'] . '] ' . ucfirst($reference['title']) . ' ';
  1262. $output .= $reference['copyright'] . "\n";
  1263. }
  1264. break;
  1265. }
  1266. return $output;
  1267. }
  1268. private function render_dict_pref()
  1269. {
  1270. $currPage = $this->get_param($this->param, "page", 1, 1);
  1271. $pageSize = $this->get_param($this->param, "size", 2, 100);
  1272. $props = [
  1273. "currPage" => $currPage,
  1274. 'pageSize' => $pageSize,
  1275. ];
  1276. switch ($this->format) {
  1277. case 'react':
  1278. $output = [
  1279. 'props' => base64_encode(\json_encode($props)),
  1280. 'html' => "",
  1281. 'text' => '',
  1282. 'tag' => 'div',
  1283. 'tpl' => 'dict-pref',
  1284. ];
  1285. break;
  1286. case 'unity':
  1287. $output = [
  1288. 'props' => base64_encode(\json_encode($props)),
  1289. 'tpl' => 'dict-pref',
  1290. ];
  1291. break;
  1292. default:
  1293. $output = 'dict-pref';
  1294. break;
  1295. }
  1296. return $output;
  1297. }
  1298. private function get_param(array $param, string $name, int $id, string $default = '')
  1299. {
  1300. if (isset($param[$name])) {
  1301. return trim($param[$name]);
  1302. } else if (isset($param["{$id}"])) {
  1303. return trim($param["{$id}"]);
  1304. } else {
  1305. return $default;
  1306. }
  1307. }
  1308. private function mb_trim($str, string $character_mask = ' ', $charset = "UTF-8")
  1309. {
  1310. $start = 0;
  1311. $end = mb_strlen($str, $charset) - 1;
  1312. $chars = preg_split('//u', $character_mask, -1, PREG_SPLIT_NO_EMPTY);
  1313. while ($start <= $end && in_array(mb_substr($str, $start, 1, $charset), $chars)) {
  1314. $start++;
  1315. }
  1316. while ($end >= $start && in_array(mb_substr($str, $end, 1, $charset), $chars)) {
  1317. $end--;
  1318. }
  1319. return mb_substr($str, $start, $end - $start + 1, $charset);
  1320. }
  1321. }