DEV Community

Atsushi
Atsushi

Posted on

Introduction of the GROWI plugin to embed and display the site.

The open source wiki, GROWI has a plugin facility. It can be used to display your own data or to customise the display.

In this article, I introduce a website embedded display plug-in that I've created as a GROWI plug-in. I'll also introduce some notes on asynchronous data acquisition.

Preview

About the code

The code can be found in goofmint/growi-plugin-embed-site: GROWI Web site embed plugin
You can find it in the following files. The two files to look at are.

Add the plugin.

To use it, add it in the plugin in the GROWI administration, the URL is https://github.com/goofmint/growi-plugin-embed-site.

Admin

Usage.

To use the link, use the Markdown notation link. The title of the link should be OGP. Here is an example.

[OGP](https://example.com)
Enter fullscreen mode Exit fullscreen mode

This will display the web site information by embedded.

About the implementation

Code for the registration part of the plugin client-entry.tsx. In optionsGenerators.customGenerateViewOptions, the default a tag behaviour is changed. You can change the behaviour by wrapping it in 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,
};
Enter fullscreen mode Exit fullscreen mode

Retrieving and displaying OGP

The heart of this plugin is the asynchronous acquisition of data: since the whole GROWI is built in React, it is not possible to simply use useEffect or useState. The solution to this is react-async.

import Async from 'react-async';.
Enter fullscreen mode Exit fullscreen mode

This component encloses the whole thing. Then, when you use it, you specify the asynchronous function and the arguments to be passed to the function in promiseFn. The return values data for the data, error for the error and isPending for the loading status.

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>
);
Enter fullscreen mode Exit fullscreen mode

Asynchronous functions are defined as follows. In this case, we have released an API that allows you to retrieve OGP information from website! | takurogu.. A similar mechanism is to use ogp-parser from Express. The default image uses placehold.jp | dummy image generation Create image for mock.

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;
};
Enter fullscreen mode Exit fullscreen mode

Notes.

API released to retrieve OGP information for websites! | takurogu, please observe the prohibitions listed in the following section.

Summary.

The GROWI plugin gives you the freedom to extend your display. If there are missing functions, you can add more and more. By all means, customise your own wiki.

GROWI, the OSS development wiki tool | comfortable information sharing for all

Top comments (0)