Humhubのカスタムモジュールの作り方（静的な情報ページを表示するモジュール）

# はじめに

今回は、Humhubが拠り所としているYii2フレームワークの、いわゆるMVCモデルに従った、静的な情報ページを作成してみる。目標は、ユーザーのプロファイルページに情報を表示するモジュールを組み上げることだ。Humhubの公式ドキュメントには、この説明はない。Yii2のドキュメント[Yii2.0決定版ガイド：モジュール](https://www.yiiframework.com/doc/guide/2.0/ja/structure-modules)のページと、[前回までに解説してきた記事](https://qiita.com/tomofu74/items/7e9e002310d8b14bb612)とを合わせて以下を読んでいただきたい。

# モジュールについて

あらためてYii2.0決定版ガイドを読むと、モジュールについては次の通り記述されている。

    モジュールは、モデル、ビュー、コントローラ、およびその他の支援コンポーネントから構成される自己充足的なソフトウェアのユニットです。 
    モジュールが アプリケーション にインストールされている場合、エンド・ユーザはモジュールのコントローラにアクセスする事が出来ます。 
    これらのことを理由として、モジュールは小さなアプリケーションと見なされることがよくあります。 しかし、モジュールは単独では配備できず、
    アプリケーションの中に存在しなければならないという点で アプリケーション とは異なります。

     モジュールは、モジュールの ベース・パス と呼ばれるディレクトリとして編成されます。 このディレクトリの中に、ちょうどアプリケーションの
    場合と同じように、controllers、models、views のようなサブ・ディレクトリが存在して、コントローラ、モデル、ビュー、その他のコードを
    収納しています。 

## モジュール設定情報
Yii2.0決定版ガイドの説明にしたがうようにサブディレクトリを作成して、モジュールのディレクトリを編成していく。まずは、[これまでに見てきたように](https://qiita.com/tomofu74/items/7e9e002310d8b14bb612)、 humhubのカスタムモジュール設置ディレクトリに、4つのファイル（comfig.php, Event.php, module.json, Module.php）を含むディレクトリを作成しよう。次のようなディレクトリ構成になる。

    mymodule
      |-- config.php
      |-- Events.php
      |-- module.json
      |-- Module.php

ユーザーのプロファイルページに情報を表示するので、config.phpには、　ProfileMenu のEVENT_INITのコールバックを設定しよう。

    config.php
     
      use humhub\modules\user\widgets\ProfileMenu;
      ...
      return [
          ...
          ...
          'events' => [
                ...
                [
                    'class' => ProfileMenu::class,
                    'event' => ProfileMenu::EVENT_INIT,
                    'callback' => [ Osintech\humhub\modules\mymodule\Events, 'onProfileMenuInit']
                ],
                ...
          ]
       ]

そして、　Events.php にて　onProfileMenuInit ファンクションをパブリックで宣言する。今回の重点は、このファンクションの中で描かれる'url'項目だ。

    Envents.php

      ...
      ...
      public static function onProfileMenuInit($event)
      {
        if ($event->sender->user !== null ) {
            $event->sender->addItem([
                'label' => 'Information',
                'icon' => '<i class="fa fa-cubes"></i>',
                'url' => $event->sender->user->createUrl('/mymodule/information'),
                'isActive' => (Yii::$app->controller->module
                                  && Yii::$app->controller->module->id == 'mymodule'),
                'sortOrder' => 9999,
            ]);
        }
      }
      ...
      ...

Menuへの addItem ファンクションの引数として、配列を渡している。配列の中は、'label', 'url', 'icon', 'isActive', 'sortOrder' のキーを設定している。それぞれ、およそ次のような意味であろう(例によってHumhubの公式ドキュメントはないので、筆者の経験と推定だ。）。

|キー|値|
|---|---|
|label|メニューに表示されるアイテム名|
|icon|メニューに表示されるアイコン。FontAwesomeIcon（たぶん、[Ver.4](https://fontawesome.com/v4.7.0/icons/)）で指定する。|
|url|メニュー選択で遷移するルート。'/モジュール名/ビュー名'の書式で記述する。|
|isActive|アクティブ（有効）にする条件。ほぼお約束のようで、&& のあとでModule自身のIDを指定する。|
|sortOrder|ソート順。数字が大きいほど、メニューの下に並ぶ。|

ここまでで、humhubにログインし、マイプロフィールの画面を見て見ると、次のように、アイコンとラベルが指定されて最下段（sorOrder=9999）に表示されていることが確認できるだろう。
![sample.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/699890/30a7846e-51fb-dc1a-7249-75a87ed225f4.png)

ちなみに、このままここをクリックすると・・・
![sample2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/699890/eb641e0b-e778-c0eb-8a03-8c3e525df4e0.png)

まだルートの先を構築していないので当然ではあるが、「クリックすれば動作する」ということが達成できただけでも少し嬉しくなってしまう。（いや、動いて当然なのだが、その当然がちゃんとしてることがどれだけありがたいか・・・。この当然を手に入れるまでの手探り開発の労苦を知るものは少ないだろう・・・。）

## Viewの作成
先にしていたルートは、informationビューへのルートであった。このため、viewsフォルダの中には、informationフォルダを作成し、そこにindex.phpを配置しなければならない。次のようにディレクトリを構成し、ファイルを作成する。


    mymodule
    |-- config.php
    |-- Events.php
    |-- module.json
    |-- Module.php
    |-- views
        |-- information
             |-- index.php

index.html を次のように編集しておく。

    <?php
       use humhub\modules\ui\icon\widgets\Icon;
    ?>
    
    <div class="panel panel-default">
      <div class="panel-heading">QiitaでHumhub扱っている人</div>
        <div class="panel-body">
        <?= Icon::get('thumbs-o-up') ?><?php echo $information_value; ?>
        </div>
      </div>
    </div>

必要なコンポーネント、ウィジェットをはじめの　<?php ~ ?>ブロック内で use　宣言し、その後は、html文内で必要に応じて呼び出して利用する。この例では、uiモジュールのIconウィジェットをuse宣言し、'thumbs-o-up'なFontAwesomeIconを呼び出して表示に使っている。__Humhubがさまざまなモジュール、コンポーネントを用意しているようだが、全容を紹介しているような資料がみつからない。__オープンソースになっているモジュールがどのようなViewを作成しているのか、真似しながら手探りで作ってみるしかない。
また、$information_valueの値をechoしているが、このファイルの中では$information_valueの値は決定していない。この部分はControllerから変数を渡すことができるのだ。（まさにMVCモデル！）

## Controllerの作成
controllersディレクトリを作成し、ルートに従ったControllerを設置する。このとき、controllerのファイル名は、　__メニューの addItem ファンクションで指定した引数に従って'ビュー名Controller.php'と命名__しなければならない。なお、ビュー名の最初の1文字は大文字にする。今回の例では、次のようになる。


    mymodule
    |-- config.php
    |-- Events.php
    |-- module.json
    |-- Module.php
    |-- views
    |   |-- information
    |       |-- index.php
    |-- controllers
        |-- InformationController.php

さて、このファイルの中で この　controllerのクラスを宣言して、ファンクションを記述するのだが・・・。参照元となるControllerを何にするか、は表示を行いたい場面によって異なるので注意だ。筆者の試したところでは、次のような種類があった。

|コントローラクラス|動作（表示場所・範囲）|画面サンプル|
|---|---|---|
|\humhub\components\Controller　または　\Yii\web\Controller|サイドメニューおよびサイドウィジェットのスペースを確保しない地の部分に表示する。(TopMenuなど）|![sample3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/699890/4947afc4-d137-da75-b494-04dd4cd90b96.png)|
|\humhub\modules\content \components\ContentContainerController|コンテンツコンテナ部分（コンテンツが表示される、メニューを残した部分）に表示する。|![sample4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/699890/bc3dd2ec-4fbd-11fe-d3d3-f03dc434a25d.png)|
|\humhub\modules\admin \components\Controller|（今回は扱わない）モジュールのAdminメニューのイベントをコールバックし、Adminメニューで管理者向けに表示させたい情報のときに使う|（例）  ![sample5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/699890/f278808e-3d78-57fe-7877-88d4cb06ffcb.png)|
|\humhub\modules\content \components\ContentAddonController|コンテンツのウィジェット内部のアドオン表示部。”いいね”や”コメント”といったアドオンメニューの場所。|（例）![sample6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/699890/45b71c45-89d3-0ae0-61eb-712bb57ddfdb.png)|
|\humhub\modules\user\components\BaseAccountController|ユーザープロファイルの表示画面（マイプロフィール画面）のメニューに表示する時に使う||



今回は、プロフィールのコンテンツが表示される場所に情報を表示したいので、\humhub\modules\content\components\ContentContainerController クラスのExtendsとして、コントローラクラスを記述する。(後日追記：Ver.1.6.xまで。）

    <?php
        namespace Osintech\humhub\modules\mymodule\controllers;
        use \humhub\modules\content\components\ContentContainerController;

        class InformationController extends ContentContainerController
        {
          public function actionIndex()
          {
            return $this->render('index', ['information_value' => "少人数"]);
          }
        }

後日追記：　Ver.1.7以降では、use humhub\modules\user\components\BaseAccountController;　と宣言して、BaseAccountController の extends な class にする。本件について公式アナウンスはなく、筆者はカンでこのコントローラを探りあてた。仕様は都度、開発者にて確認されたし。

記述にあたって、class名は、ファイル名で決定している。そのクラス、InformationControllerクラスの中で、パブリックファンクションとして actionIndex() を記述している。これは、 表示させたいviewのファイルを　actionビューファイル名() というファンクション名で記述するルールに従ったものである。そして、そのファンクションの返り値として、 $this->render('ビューファイル名', [表示ファイルPHPに渡す変数を連想配列で記述])のレンダリング結果を出力する。これにより画面に情報が表示される。

先のviewにおいて、\$information_valueという変数を利用するように記述してあった。このコントローラでは、その変数に対する値の入力を行なっているのが見て取れるだろう。viewでは\$記号がついていた変数名について、コントローラで\$記号なしで記述している点にも注意だ。

# まとめ
今回は、humhubのモジュールで静的な情報表示ページのサンプルを作成した。次回は、ページに対する装飾を扱ってみたいと思う。