オープンソースのWikiであるGROWIにはプラグイン機能が用意されています。自社のデータを表示したり、表示をカスタマイズするのに利用できます。
今回は、GROWIプラグインとして作ったWebサイト埋め込み表示プラグインを紹介します。非同期でデータを取得する際の注意点も合わせて紹介します。
コードについて
コードはgoofmint/growi-plugin-embed-site: GROWI Web site embed plugin
にあります。見るべきファイルとしては、以下の2つになります。
プラグインを追加する
利用する際には、GROWIの管理画面の プラグイン
にて追加してください。URLは https://github.com/goofmint/growi-plugin-embed-site
です。
使い方
利用する際には、Markdown記法のリンクを使います。その際にリンクのタイトルを OGP
としてください。以下はその例です。
[OGP](https://example.com)
これでサイトの埋め込み表示になります。
実装について
プラグインの登録部分 client-entry.tsx
のコードです。 optionsGenerators.customGenerateViewOptions
の中でデフォルトの a
タグの動作を変更しています。 EmbedOGP
でラップすることで、動作を変更できます。
import { useEffect, useState } from 'react';
import config from './package.json';
import { EmbedOGP } from './src/EmbedOGP';
import { Options, Func, ViewOptions } from './types/utils';
declare const growiFacade : {
markdownRenderer?: {
optionsGenerators: {
customGenerateViewOptions: (path: string, options: Options, toc: Func) => ViewOptions,
generateViewOptions: (path: string, options: Options, toc: Func) => ViewOptions,
},
},
};
const activate = (): void => {
if (growiFacade == null || growiFacade.markdownRenderer == null) {
return;
}
const { optionsGenerators } = growiFacade.markdownRenderer;
optionsGenerators.customGenerateViewOptions = (...args) => {
const options = optionsGenerators.generateViewOptions(...args);
const A = options.components.a;
// replace
options.components.a = EmbedOGP(A);
return options;
};
};
const deactivate = (): void => {
};
// register activate
if ((window as any).pluginActivators == null) {
(window as any).pluginActivators = {};
}
(window as any).pluginActivators[config.name] = {
activate,
deactivate,
};
OGPの取得・表示
今回のプラグインでの肝になるのが、データの非同期での取得になります。GROWI全体がReactで構築されているため、単純に useEffect
や useState
は利用できません。その解決策として利用するのが react-async
です。
import Async from 'react-async';
このコンポーネントで全体を囲みます。そして、利用時には promiseFn
にて非同期関数、そして関数に渡す引数を指定します。戻り値で data
にデータ、 error
にエラー、 isPending
にローディング状態が入ります。
return (
<Async promiseFn={getOGP} href={href}>
{({ data, error, isPending }) => {
if (isPending) return 'Loading...';
if (error) return `Something went wrong: ${error.message}`;
if (data) {
return (
<div className='row ogp'>
<div className='col-3'>
<a href={href} target='_blank' rel='noopener noreferrer'>
<img
src={data.ogp['og:image'] || PLACEHOLDER_IMAGE}
alt={data.html.title}
/>
</a>
</div>
<div className='col-9'>
<a href={href} target='_blank' rel='noopener noreferrer'>
<strong>{data.html.title}</strong>
</a>
<p>
{ data.html.description || data.ogp['og:description'].join('') }
</p>
{ data.ogp['og:site_name'] && (
<div
style={{
color: '#777',
}}
>
{data.ogp['og:site_name']}
</div>
)}
</div>
</div>
);
}
return null;
}}
</Async>
);
非同期関数は以下のように定義します。今回はウェブサイトのOGP情報を取得できるAPIをリリースしました! | たくろぐ。をお借りしています。同様の仕組みとして ogp-parser
をExpressから利用する方法もあります。デフォルトの画像はplacehold.jp | ダミー画像生成 モック用画像作成を利用しています。
const OGP_API = 'https://ogp-scanner.kunon.jp/v1/ogp_info';
const PLACEHOLDER_IMAGE = 'https://placehold.jp/24/5C9A29/ffffff/480x360.png';
const getOGP = async({ href }: any) => {
const res = await fetch(`${OGP_API}?url=${encodeURIComponent(href)}`);
const json = await res.json();
return json;
};
注意点
ウェブサイトのOGP情報を取得できるAPIをリリースしました! | たくろぐ。にある禁止事項を守った上でご利用ください。
まとめ
GROWIプラグインを使うと、表示を自由に拡張できます。足りない機能があれば、どんどん追加できます。ぜひ、自分のWikiをカスタマイズしましょう。