Linux(Ubuntsu)でDTMを始める手順

イントロダクション

最近、ノートパソコンのスペックが高くなって、気がついたら、自分のデスクトップPCよりもノートのほうがスペックが高くなり。。。
デスクっトプPCをUbutsuにしたところです。折角LinuxにしたのだからDTM用のPCにすることにしました。

Libux用LMMSの設定へ
でも、LMMSはLinuxでもWindowsでも動かせるということで、Googleとかクラウドもあるので、Windowsにインストールしようと思いました。

Windows用LMMSの設定へ

DTMの環境構築(Linux用)

Ubuntsuの画面がこんな感じです。

まずは音を出すための環境セットアップ

Linuxなので、はじめからインストールされているOSにデフォルトでセットされているアプリケーションをインストールすれば問題ないのでしょうが、スペック的な問題で必要最低限でインストールしたため
追加でインストールするものがあります。

スピーカーの設定と音確認

まずは下のアプリをインストールします。インストール済みの場合は飛ばして良いです。
ちなみに、インストールできていない場合は、設定アプリを開いたときに「サウンド」がありません。

PulxAudioをインストール

下のコマンドを叩きます。そして、コントローラも別個にインストールが必要です。

sudo apt install pulseaudio

PulseAudio Volume Controlをインストール

sudo apt install pavucontrol

これらがインストールできたら設定を行います。

スピーカーのセット

サウンドの設定画面で使用するデバイス(スピーカー)を選択して、テストして音が出ることを確認します。

LMMSの使い方を学ぶ

こちらのサイトでLMMSの使い方 を学習します。

LMMSのダウンロード

こちらのURLよりダウンロードサイトに移動できます。ダウンロードすると下のようなファイルができます。

AppImageファイルの展開

他のサイトを見るとダブルクリックでインストールできるようですが、自分のところはうまく行きませんでした。

なので下のように行いました。

  1. ダウンロードフォルダにAppImageをダウンロードする。

  2. ターミナルを開き以下を実行

    ./lmms-1.2.2-linux-x86_64.AppImage --appimage-extract
  3. 作成されたフォルダへ移動して「./AppRun」を実行

とりあえずはこんな感じです。

Windows用LMMSの設定

Windowsは簡単です。インストーラーをダウンロードして、インストールウィザードでサクッと行けます。

  1. ダウンロードページを開くLMMSの日本語サイトへ行きます

  2. インストーラーを起動する

  3. ウィンドウを進める

  4. インストール場所を指定する。初期設定は「C:\ProgramFiles」になる

  5. 最後にショートカットを作成するかを指定してインストール

  6. 今回は、ショートカットを作成しなかったので直接ショートカットを作成「C:\ProgramFiles」を開いて右クリック

  7. 実効時に初期設定の画面が表示されるが後で再設定可能

  8. 初期画面の表示

とりあえずはここまでです。

OracleDBの環境構築

イントロダクション

オラクルのデータベース(アプリケーション)を使用するのに無償で扱うことができます。細かいところはこちらに記述しました。

色々と調べてみるとOracleEnterprizeManagerがとても便利なのでそちらを使うと良いということがわかりました。

そして下の動画で学習しました。

OracleDB環境構築

まずは、コマンド名ですべてをやるととても面倒だということがあります。かといってGUIのみでやるのも大変。。。

早い話が、敵視ツールを使用するのがベストということでした。
次の作業は、SqlPlusを使用して行いました。

作成したDBの確認

インストールが終わると、初期状態で作成されるDBがあります。それが下のようなものになります。

select name from v$pdbs;

DBへ接続する

ここでいう「DB」とはPDB(プラガブルデータベース)のことです。

ユーザー作成

下のSQLを実行します。「create user XXX identified by パスワード」という形の文章です。そのあとの文章は、テーブルスペースなどのメモリ使用量とか、権限の範囲などを指定しています。細かいところはわからないので上記のチュートリアルに従います。

create user demouser identified by demouser default tablespace users quota unlimited on users;

改めてログインするのには、
下のようにコマンドを入力

connect demouser/demouser@localhost:1521/XEPDB1 as sysdba

しかし、「as sysdba」をつけないとログインができませんでした。とエラーが出ました。

Listener refused the connection with the flloing error ORA-12514 ...

調べてみるとリスナーの状態を確認するのが初手のようです。

コマンドプロンプトを開き「lsnrctl status」で確認...

色々やったけど、「sys」ユーザーでログインするのが手っ取り早いと思い下のようにログインしました。

次は、作成したユーザー(スキーマ)でログインしたいと思います。

JavaFX + SceneBuilderでアプリケーションを作る

イントロダクション

受付用のアプリケーションを作成したいと思いました。イメージは次のような形です。

  1. 自己紹介カードなどを作成して、これにバーコードを付ける。
  2. PCでアプリケーションを作成(JavaFXなどのGUI)して、入力を受ける
  3. REST通信でユーザーの情報を受け取り画面に表示する
  4. 文字入力を受け付けて、サーバー上に登録、管理する

JavaFXとScceneBuilderでGUI作成

IntelliJ IDEAを使用して作業を行います。まずはこちらからIntelliJをダウンロード and インストールを行います。

プロジェクトの作成

新規でプロジェクトを作成します。New Projectを選択して「JavaFX」を選択します。

そして、どのタイプを作成するか選択します。細かい説明は英語ですが書いてあるので、日本語版にすれば日本語で見れるかもしれません。

アプリケーションを作る

プロジェクトを作成したら、基本的なコードが自動生成されます。
それを眺めてみましょう。

自動生成されたコード

まずは、生成されたファイルを確認します。下のようなファイルが生成されました。

  • HelloApplication.java
  • HelloController
  • module-info.java
  • hello-view.fxml

このうち「module-info.java」は気にしなくてよいファイルになります。

HelloApplication.java

まずはメインメソッドのあるこのクラスを眺めてみましょう。ポイントは「Application」クラスを継承しているところです。
このクラスを継承して、「start()」メソッドをオーバーライドしています。
このメソッドをオーバーライドすることで画面を作成するのに必要な処理を大体やってくれます。

public class HelloApplication extends Application {
    @Override
    public void start(Stage stage) throws IOException {
        FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
        Scene scene = new Scene(fxmlLoader.load(), 320, 240);
        stage.setTitle("Hello!");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }
}

そして、「start()」メソッドで、自分が作りたい画面を表示するために、ペインを追加したり、ボタンを生成したり、ラベルで文字を作ったりするわけです。下のようにコードでボタンをペインに追加します。

Button helloButton = new Button("Hello JavaFX");
pain.add(helloButton);

しかし、SceneBuilderが登場してからは、GUIでこのような部分を作成することができるようになりました。

こんなかんじでRXMLファイルを作成します。

FXML

生成されたファイルの中に、FXMLもあります。

このファイルは、SceneBuilderで開くと下のように見えます。

画面の下の方にボタンがあるだけです。
しかしこれを実行すると下のように動きます。

ちなみにXMLファイルなので、下のような内容が記述されています。

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>

<?import javafx.scene.control.Button?>
<VBox alignment="CENTER" spacing="20.0" xmlns:fx="http://javafx.com/fxml"
      fx:controller="jp.zenryoku.doms.xxx.HelloController">
    <padding>
        <Insets bottom="20.0" left="20.0" right="20.0" top="20.0"/>
    </padding>

    <Label fx:id="welcomeText"/>
    <Button text="Hello!" onAction="#onHelloButtonClick"/>
</VBox>

Controller

これは、上記のFXMLファイルの中に記述されている下の部分に着目してください。

fx:controller="jp.zenryoku.doms.xxx.HelloController">

XMLファイルで「HelloController」を参照するように記述があります。

そして、プログラムコードは下のようなものです。「@FXML」がポイントです。
このアノテーションを使用してラベルにアクセスし、どのようなアクションを起こすか定義します。

public class HelloController {
    @FXML
    private Label welcomeText;

    @FXML
    protected void onHelloButtonClick() {
        welcomeText.setText("Welcome to JavaFX Application!");
    }
}

アクセスするコンポーネント

ここでいう「コンポーネント」はラベルのことです。「コンポーネント」は幅の広い言葉です。
「日本人」というと日本全国を含みますが、「北海道人」というと北海道の人のみです。

同じように「コンポーネント」という場合は、JavaFXで使用するクラス全体のことを言います。主に画面コンポーネントを示すことが多いです。
「画面コンポーネント」というのはラベル、ボタン、ペイン、パネル、テキストエリア...のことです。HTMLみたいですよね。

実際にHTMLふぃあるから画面を生成することもできます。かなり優秀です(笑)

さて、下の行に注目してください。

@FXML
private Label welcomeText;

コントローラーで上のようにフィールド変数が定義されています。これは、FXMLにも同様の定義があります。

<Label fx:id="welcomeText"/>

"fx:id" 属性で使用するコンポーネントをクラスとFXMLを関連付けています。つまりは、「welcomeText」という変数名のラベルをコントローラーのメソッドで下のように処理します。ということを定義しています。

@FXML
protected void onHelloButtonClick() {
    welcomeText.setText("Welcome to JavaFX Application!");
}

Button

結局は、ボタンを押下したときに上の文字列が画面に表示されるのですが、ここの「アクション」を定義しているのが次の部分です。

  1. ボタンを押下したときの処理、「onAction」で定義している

    <Button text="Hello!" onAction="#onHelloButtonClick"/>
  2. onHelloButtonClickはコントローラーに定義している

    @FXML
    protected void onHelloButtonClick() {
    welcomeText.setText("Welcome to JavaFX Application!");
    }

上記のような形で実装されています。ここまでくればあとは各コンポーネントとそれぞれのアクションを定義してやれば、オリジナルのアプリケーションを作成できます。

でわでわ。。。

WordPressプラグインの作り方~WP開発者リソースを読む~

イントロダクション

WordPressを使用して、プラグインを作成しています。しかし、プラグインで更新処理などを行おうとした時には、ファイルのパーミッション(権限)などの問題でファイルへHTTP(S)ではアクセスできません。
具体的には下のような形ではアクセスできないということです。

https://zenryokuservice.com/wp/wp-content/plugin/プラグイン名/index.php

これを解決しないことには、プラグイン作製ができません。。。どうしたものか?と悩んでいたら「WordPress開発者リソース」を見つけたのでそれを読み進めようということになりました。

本記事の内容

  1. プラグインフォルダの作成

  2. プラグインファイルの作成

  3. フックについて
    1-1.基本的なフック
    1-2.フックの追加方法
    1-3.プラグインフォルダ作成のまとめ

  4. PHPファイルを作成する
    2-1.プラグインPHPを作成する
    2-2.add_menu_page()関数について

  5. プラグインの定義
    3-1.コメントで作る
    3-1.自作要件について

  6. 作成した画面のコード
    4-1.ディレクトリのパス取得関数
    4-2.WordPressの定数
    4-3.HttpGET関数の使い方
    4-4.#Http POSTの使い方
    4-5.#Ajaxの使い方※調査中

  7. 課題

WordPress 開発者リソースを読む

大まかに手順書とかのドキュメントリソース(資源)ということです。下のような記述もあり、大変わかりやすい内容だと思います。

最も単純に言えば、WordPress プラグインは、WordPress プラグイン ヘッダー コメントを含む PHP ファイルです。すべてのプラグインのファイルが 1 か所にきちんと整理されるように、プラグインを保持するディレクトリを作成することを強くお勧めします。

つまりは、プラグインは作成するときに一つのフォルダ内に作成すると、まとまっているので管理しやすいので、推奨します。ということでした。

プラグインフォルダの作成

参照先
今回は「CustomQuestionary」というプラグインを作成しますので、次のようなフォルダを作成しました。
wordPressフォルダから「/wp-content/plugins」フォルダのキャプチャです。

そして、WPプラグインは下記(引用)のように、アクションとフィルターがあります。

WordPress 内には、アクションとフィルターという 2 種類のフックがあります。アクションを使用すると、WordPress の機能を追加または変更できます。一方、フィルターを使用すると、コンテンツが読み込まれて Web サイト ユーザーに表示されるときにコンテンツを変更できます。

プラグインファイルの作成

プラグインのプラグインたる部分です。早い話が、プラグインを有効にした時に表示される画面の部分ということです。
主に起動するプラグインに設定する情報の入力、操作するための情報の入力を行います。

プラグインの表示を行う

フォルダ=ディレクトリを作成したら、フォルダと同じ名前のPHPファイルを作成します。
PHPファイルの内容は以下のようになっています。

<?php
/*
  Plugin Name: SamplePlugin
  Plugin URI:https://zenryokuservice.com/wp/2024/01/06/wordpress%e3%83%97%e3%83%a9%e3%82%b0%e3%82%a4%e3%83%b3%e3%81%ae%e4%bd%9c%e3%82%8a%e6%96%b9/
  Description: サンプルプラグインです。これはプラグインの作成方法を学習するのに使用します。
  Version: 1.0.0
  Author: Takunoji
  Author URI: https://twitter.com/java_takunoji
  License: GPLv2
 */

?>

これを表示すると下のようになります。プラグインは有効化していません。

トップレベルメニューの追加

add_menu_page()を使用して追加します。
具体的には下のように表示されるものの事をいうようです。

メニュー追加の関数「add_menu_page()」のシグニチャ、これは、「admin_menu」のフックで動かす必要があるようです。

add_menu_page(
    string $page_title,      // メニューが選択されたときにページのタイトルタグに表示されるテキスト。
    string $menu_title,      // メニューに使用されるテキスト。
    string $capability,      // このメニューをユーザーに表示するために必要な機能。
    string $menu_slug,       // このメニューを参照するスラッグ名。このメニュー ページに対して一意である
    callable $function = '', // このページのコンテンツを出力するために呼び出される関数
    string $icon_url = '',   // このメニューに使用されるアイコンの URL。
    int $position = null     // この項目が表示されるメニュー順序内の位置。
);

最後の引数にある表示する位置については、最後の引数の値で位置取りを決定するようです。

2 – ダッシュボード
4 – セパレーター
5 – 投稿
10 – メディア
15 – リンク
20 – ページ
25 – コメント
59 – セパレーター
60 – 外観
65 – プラグイン
70 – ユーザー
75 – ツール
80 – 設定
99 – セパレーター
各ディレクトリのパスを取得する関数

具体的に書いて動かしたコードは下の通りです。

  • samplePlugin.php
<?php
/*
  Plugin Name: SamplePlugin
  Plugin URI:https://zenryokuservice.com/wp/2024/01/06/wordpress%e3%83%97%e3%83%a9%e3%82%b0%e3%82%a4%e3%83%b3%e3%81%ae%e4%bd%9c%e3%82%8a%e6%96%b9/
  Description: サンプルプラグインです。これはプラグインの作成方法を学習するのに使用します。
  Version: 1.0.0
  Author: Takunoji
  Author URI: https://twitter.com/java_takunoji
  License: GPLv2
 */

 add_action(
   'admin_menu',
   function(){
         add_menu_page(
             '管理メニュータイトル' ,
             'トップメニューサンプル' ,
             'manage_options' ,
             'top_menu_sample' ,
             'render_topmenu_contents' ,
             null ,
             90
         );

         add_submenu_page(
             'top_menu_sample' ,
             'サブメニュータイトル' ,
             'サブメニューサンプル' , 
             'manage_options' , 
             'sub_menu_sample' , 
             'render_submenu_contents'
         );
     }
 );

 function render_topmenu_contents() {
     ?>
         <div class="wrap">
             <h1>トップメニュー設定</h1>
             <p>Hello admin TOP menu !!</p>
         </div>
     <?php
 }

 function render_submenu_contents() {
     ?>
         <div class="wrap">
             <h1>サブメニュー設定</h1>
             <p>Hello admin SUB menu !!</p>
         </div>
     <?php
 }

?>

1.add_action()関数で、'admin_menu'に「add_menu_page()」と「add_submenu_page()」を使用してメニューページの追加を行います。「render_topmenu_contents」という関数でメニューページを作成・表示します。

 add_action( 'admin_menu',
   function(){
         add_menu_page(
             '管理メニュータイトル' ,
             'トップメニューサンプル' ,
             'manage_options' ,
             'top_menu_sample' ,
             'render_topmenu_contents' ,
             null ,
             90
         );
  1. メニューページを作成する
    これは参考ページとしてこのページを参照します。「カスタム設定ページ」を作成します」。ここに「WPOrg」という名前のページを作成する例が書いてあります。以下はサンプルコードのコピペです。

「wporg_settings_init」関数をadd_action( 'admin_init', 'wporg_settings_init' );で登録します。
この関数は、

  • 「register_setting()」で「wporg」ページに新規設定を登録します。
  • 「add_settings_section()」で「wporg」ページに新しいセクションを登録します。
  • 「add_settings_field()」で「wporg」ページ内の「wporg_section_developers」セクションに新しいフィールドを登録します。

まずは、管理ページを登録して表示できるようにします。

/**
 * Register our wporg_options_page to the admin_menu action hook.
 */
add_action( 'admin_menu', 'wporg_options_page' );

HTMLを出力するPHPコードは「wporg_options_page_html」関数に定義します。

/**
 * Add the top level menu page.
 */
function wporg_options_page() {
    add_menu_page(
        'WPOrg',
        'WPOrg Options',
        'manage_options',
        'wporg',
        'wporg_options_page_html'
    );
}

HTMLを出力する関数です。

/**
 * Top level menu callback function
 */
function wporg_options_page_html() {
    // check user capabilities
    if ( ! current_user_can( 'manage_options' ) ) {
        return;
    }

    // add error/update messages

    // check if the user have submitted the settings
    // WordPress will add the "settings-updated" $_GET parameter to the url
    if ( isset( $_GET['settings-updated'] ) ) {
        // add settings saved message with the class of "updated"
        add_settings_error( 'wporg_messages', 'wporg_message', __( 'Settings Saved', 'wporg' ), 'updated' );
    }

    // show error/update messages
    settings_errors( 'wporg_messages' );
    ?>
    <div class="wrap">
        <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
        <form action="options.php" method="post">
            <?php
            // output security fields for the registered setting "wporg"
            settings_fields( 'wporg' );
            // output setting sections and their fields
            // (sections are registered for "wporg", each field is registered to a specific section)
            do_settings_sections( 'wporg' );
            // output save settings button
            submit_button( 'Save Settings' );
            ?>
        </form>
    </div>
    <?php
}

画面が表示できたら、設定を行う関数を登録します。

/**
 * Register our wporg_settings_init to the admin_init action hook.
 */
add_action( 'admin_init', 'wporg_settings_init' );

「wporg_settings_init」関数は下の関数です。以下の3つの処理を行っています。
・「register_setting( 'wporg', 'wporg_options' );」でオプション登録を行います。→調査中

/**
 * custom option and settings
 */
function wporg_settings_init() {
    // Register a new setting for "wporg" page.
    register_setting( 'wporg', 'wporg_options' );

    // Register a new section in the "wporg" page.
    add_settings_section(
        'wporg_section_developers',
        __( 'The Matrix has you.', 'wporg' ), 'wporg_section_developers_callback',
        'wporg'
    );

    // Register a new field in the "wporg_section_developers" section, inside the "wporg" page.
    add_settings_field(
        'wporg_field_pill', // As of WP 4.6 this value is used only internally.
                                // Use $args' label_for to populate the id inside the callback.
            __( 'Pill', 'wporg' ),
        'wporg_field_pill_cb',
        'wporg',
        'wporg_section_developers',
        array(
            'label_for'         => 'wporg_field_pill',
            'class'             => 'wporg_row',
            'wporg_custom_data' => 'custom',
        )
    );
}

ここからはもちょっと調査してから記載ます。。。

基本的なフック

基本的なフックは下のモノらしいです。

  1. register_activation_hook(): プラグインをアクティブ化すると、アクティベーション フックが実行されます。
  2. register_deactivation_hook(): プラグインが非アクティブ化されると、非アクティベーション フックが実行されます。
  3. register_uninstall_hook (): WordPress 管理画面内の削除リンクをクリックした場合、プラグインはアンインストールされたとみなされます。

上記のフックが基本になっているようです。それぞれのタイミング(プラグインのアクティブ化、非アクティブ化、削除)でそれぞれの処理を行うように実装することができます。

プラグインで使用するフックAPIのリファレンスはこちらです。

register_action_hookのサンプル

/* Main Plugin File */
...
/** プラグイン起動時のスクリプト */
function my_plugin_activate() {

  add_option( 'Activated_Plugin', 'Plugin-Slug' );

  /* activation code here */
}
// ここでプラグインを有効にした時の処理を実行する
register_activation_hook( __FILE__, 'my_plugin_activate' );

/** プラグインロード時の処理 */
function load_plugin() {

    if ( is_admin() && get_option( 'Activated_Plugin' ) == 'Plugin-Slug' ) {

        delete_option( 'Activated_Plugin' );

        /* do stuff once right after activation */
        // example: add_action( 'init', 'my_init_function' );
    }
}
// プラグイン
add_action( 'admin_init', 'load_plugin' );
「admin-init」のタイミングについて

以下のような記載がありました。

これはユーザー向けの管理画面でのみ実行されるわけではないことに注意してください。
admin-ajax.php および admin-post.php でも実行されます。

register_uninstall_hook()

以下のサンプルコードは、プラグインを削除するときに使用するフックです。書き方は下のようなものです。

<実装方法、その1>
これは、フックを追加する形の実装方法です。

register_uninstall_hook(
    __FILE__,
    'pluginprefix_function_to_run'// 削除時に実行するメソッド
);

<実装方法、その2>
これは、作成したプラグインフォルダに「uninstall.php」を作成することで実行することができます。

// if uninstall.php is not called by WordPress, die
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
    die;
}

$option_name = 'wporg_option';

delete_option( $option_name );

// for site options in Multisite
delete_site_option( $option_name );

// drop a custom database table
global $wpdb;
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}mytable" );

フックの追加方法

do_action()を使用してフックを追加、拡張できます。
具体的には、下のようにコードを書きます。

  • example_callback: コールバックメソッド
  • example_action: アクションの名前
// アクションでコールバックされるメソッド
function example_callback( $arg1, $arg2 ) {
    // (maybe) do something with the args.
}
// アクション(メソッド)の追加
add_action( 'example_action', 'example_callback', 10, 2 );

/*
 * アクションの実行=追加したフックを実行する
 * - 'example_action' is the action hook.
 * - $arg1 and $arg2 are the additional arguments passed to the callback.
 */
do_action( 'example_action', $arg1, $arg2 );

プラグインフォルダ作成のまとめ

プラグインを作成するときには、次のようにやることが推奨されている。
※ A. 「/」をワードプレスのルート・ディレクトリとする。
  B. 作成するプラグインを「CustomQuestionary」とする。

  1. /wp-content/pluginディレクトリに「CustomQuestionary」フォルダを作成する。
  2. register_activation_hook()はプラグインを有効化したときに起動するメソッドを登録する。
  3. register_deactivation_hook()はプラグインを無効化したときに起動するメソッドを登録する。
  4. register_uninstall_hook ()はプラグインをアンインストールしたときに起動するメソッドを登録する。
  5. add_action()はWordPress コアが実行中の特定のタイミング、または特定のイベントの発生時に起動する。
  6. do_action()は登録したアクション(フック)を起動する。
  7. remove_action()は登録したアクション(フック)を削除する。

次は以下の項目を記載します。。。

PHPファイルを作成する

上記で、プラグインのある程度の作成方法を学習したのでそれを実践します。

プラグインPHPを作成する

今回、作成するプラグインは下のような名前のプラグインです。

CustomQuestionary

下のようにファイルを作成しました。他にもファイルがありますが、ここでは、「CustomQuestionary.php」に注目していただきたく思います。

コードを書いてみる

早速コードを書いていきましょう。その前に、基本的なフックとして下の関数を挙げましたが、実行しないのに次のような結果になりました。

下のフックは追加してないが。。。

筆者が試したところ、「register_activation_hook()」と「register_deactivation_hook()」は実行していないけど、プラグインの表示非表示はできているようです。

プラグイン有効 プラグイン無効

代わりにこちらを実行

プラグイン名.php(CustomQuestionary.php)には次のコードを実装しています。

/********************************************************
 * ダッシュボードに追加するための関数(プラグイン有効時) *
 ********************************************************/
function addCustomQuestionary() {
    // ダッシュボードにこの表示が出るように追加する
    add_menu_page( 'Test Plugin', 'CustomQuestionary', 'manage_options' , 'test-plugin' , 'helloCustomQuestionary' );
    // 投稿記事にアンケートを表示するスクリプトをリンクする
    add_action( 'wp_enqueue_scripts', 'insertQuestionary' );
}

/******************************
 * アクションフックを追加する *
 ******************************/
add_action('admin_menu', 'addCustomQuestionary' );
add_action('wp_enqueue_scripts', 'insertQuestionary' );
do_action( 'addCustomQuestionary' );

コードの内容としては、次のようなことを行っております。

  1. ダッシュボードに追加するための関数(プラグイン有効時)を定義
  2. この関数では、ダッシュボードに子のプラグインが表示されるようにフックを登録
  3. 「insertQuestionary()」(PHPでJSを出力する関数)で定義しているJSをフックに登録
  4. アクションフック「addCustomQuestionary」を実行

add_menu_page()関数について

add_menu_page( string $page_title , string $menu_title , string $capability , string $menu_slug
, callable $callback = ” , string $icon_url = ” , int|float $position = null ): string

< 説明 >

この関数は、ページがメニューに含まれるかどうかを決定するために使用される機能を受け取ります。
ページの出力を処理するためにフックされる関数は、ユーザーが必要な機能を持っているかどうかもチェックする必要があります。

<パラメーター>

※required=必須, optional=任意

パラメータ名 データ型 必須
$page_title String required

メニューが選択されたときにページのタイトルタグに表示されるテキスト。

パラメータ名 データ型 必須
$menu_title String required

メニューに使用されるテキスト。

パラメータ名 データ型 必須
$capability String required

このメニューをユーザーに表示するために必要な機能。

パラメータ名 データ型 必須
$menu_slug String required

このメニューを参照するスラッグ名。このメニュー ページに対して一意である必要があり、互換性のある小文字の英数字、ダッシュ、およびアンダースコア文字のみを含める必要がありますsanitize_key() 。

パラメータ名 データ型 必須
$callback メソッド optional

このページのコンテンツを出力するために呼び出される関数

パラメータ名 データ型 必須
$icon_url String optional
  • このメニューに使用されるアイコンの URL。データ URI を使用して、base64 でエンコードされた SVGを渡します。
  • データ URI は、配色に合わせて色付けされます。これは で始まる必要があります'data:image/svg+xml;base64,'。
  • フォント アイコンを使用するには、Dashcons ヘルパー クラスの名前を渡します'dashicons-chart-pie'。
  • div.wp-menu-image を空のままに渡すと'none'、CSS 経由でアイコンを追加できるようになります。
パラメータ名 データ型 必須
$position int or float optional

この項目が表示されるメニュー順序内の位置。

プラグインの定義

まずは、プラグインの説明や、有効・無効の表示部分を実装します。

コメントで作る

ズバリ、コメントで定義できるようです。

/*
Plugin Name: CustomQuestionaryPlugin
Plugin URI: (プラグインの説明と更新を示すページの URI)(未決定)aaa
Description: アンケートを作成、ブログ記事の中に埋め込むことができ、集計結果をダッシュボードで確認ができる。
Version: 1.0
Author: ZenryokuService
Author URI: https://zenryokuservice.com/
License: GPL2
*/

そしてライセンスに関しても、どこからかこぴったものを修正して使用しました。

/*****************************
 *      License              *
 *****************************/
 /*  Copyright 2020 ZenryokuService
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License, version 2, as
    published by the Free Software Foundation.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

コメント定義の詳細

名前 説明
プラグイン名 (必須項目) WordPress Admin のプラグイン リストに表示されるプラグインの名前。
プラグイン URI プラグインのホームページ。できれば独自の Web サイト上にある一意の URL である必要があります。これはプラグインに固有のものである必要があります。ここでは WordPress.org URL を使用できません。
説明 WordPress Admin の「プラグイン」セクションに表示されるプラグインの短い説明。この説明は 140 文字未満にしてください。
バージョン プラグインの現在のバージョン番号 (1.0 や 1.0.3 など)。少なくとも次のものが必要です:プラグインが動作する最低の WordPress バージョン。
PHP が必要 最低限必要な PHP バージョン。
作成者 プラグインの作成者の名前。複数の著者をカンマを使用してリストすることもできます。
著者 URI 著者の Web サイトまたは別の Web サイト上のプロフィール (WordPress.org など)。
License プラグインのライセンスの短縮名 (スラッグ) (GPLv2 など)。ライセンスの詳細については、WordPress.org のガイドラインを参照してください。
ライセンス URI ライセンスの全文へのリンク (例: https://www.gnu.org/licenses/gpl-2.0.html )。
テキスト ドメイン プラグインのgettextテキストドメイン。詳細については、「プラグインの国際化方法」ページの「テキスト ドメイン」セクションを参照してください。
ドメイン パス ドメイン パスにより、WordPress は翻訳の場所を知ることができます。詳細については、「プラグインの国際化方法」ページの「ドメイン パス」セクションを参照してください。
ネットワーク プラグインがネットワーク全体でのみアクティブ化できるかどうか。trueにのみ設定でき、不要な場合は省略する必要があります。
URI の更新 サードパーティのプラグインが、WordPress.org プラグイン ディレクトリにある同様の名前のプラグインの更新で誤って上書きされることを回避できます。詳細については、関連する開発ノートを参照してください。

<サンプルコード>

/*
 * Plugin Name:       基本的なプラグイン
 * Plugin URI:        https://example.com/plugins/the-basics/
 * Description:       基本的なプラグインを操作する.
 * Version:           1.10.1
 * Requires at least: 5.1
 * Requires PHP:      7.1
 * Author:            たくのじ
 * Author URI:        https://takunoji.example.com/
 * License:           GPL v2 or later
 * License URI:       https://zenryokuservice.com/wp/%e3%83%97%e3%83%a9%e3%82%a4%e3%83%90%e3%82%b7%e3%83%bc%e3%83%9d%e3%83%aa%e3%82%b7%e3%83%bc/
 * Update URI:        https://zenryokuservice.com/wp/%e3%83%97%e3%83%a9%e3%82%a4%e3%83%90%e3%82%b7%e3%83%bc%e3%83%9d%e3%83%aa%e3%82%b7%e3%83%bc/
 * Text Domain:       test-plugin
 * Domain Path:       /languages
 */

自作要件について

ここで、プログラムの設計を行います。必要な機能要件としては以下のものとします。

  1. プラグインを有効にしたら、WP管理画面にアンケートを作成して表示、編集ができる
  2. アンケートを作成しDBでアンケートを管理、クリック数をカウントする
  3. アンケートは質問と回答(選択式)をセットにして1つずつ独立させる
  4. アンケートは回答以外に、クリックされた時間、ページ(URL)の情報も取得する

機能の一覧

上記の各要件に対して、それらを実現するための機能を一覧化します。

WP管理画面にアンケートを作成して表示、編集ができる

<機能一覧>

  • 管理画面は、使い方「HELP]と登録済みのアンケートを表示する。
    ※各アンケートは統計学で使用する相関係数が「0.5(修正可能)」以上のものを関連表示させる(※仕様検討中)

  • アンケートは「質問」、回答1~5までを入力して、DBに登録する

これらの機能を実装するのに「manage.php」と「index.js」の2ファイルを作成しました。

  • manage.php: プラグインの画面を開いたときに表示する画面(HTML)を出力
  • index.js: 上記で出力した画面で使用するJSを定義

作成した画面のコード

<?php
ini_set('display_errors', "On");

function convert($str) {
    if (isEmpty($str)) {
        "";
    }
    return $str;
}

function _handle_form_action(){
    $insertSQL = "INSERT INTO `QUESTIONARY`(`question`, `answer1`, `answer2`, `answer3`, `answer4`, `answer5`, `html_class`, `html_style`) VALUES (";

    $keys = array('question', 'ans1', 'ans2', 'ans3', 'ans4', 'ans5', 'hrml_class', 'html_style');

    $insertSQL .= "'" . convert($_POST[$keys[0]]) . "',";
    $insertSQL .= "'" . convert($_POST[$keys[1]]) . "',";
    $insertSQL .= "'" . convert($_POST[$keys[2]]) . "',";
    $insertSQL .= "'" . convert($_POST[$keys[3]]) . "',";
    $insertSQL .= "'" . convert($_POST[$keys[4]]) . "',";
    $insertSQL .= "'" . convert($_POST[$keys[5]]) . "',";
    $insertSQL .= "'" . convert($_POST[$keys[6]]) . "',";
    $insertSQL .= "'" . convert($_POST[$keys[7]]) . "'";
    $insertSQL .= ");";

    $res = null;

    global $wpdb;

    try {

        $res = $wpdb->query($insertSQL);
    } catch (e) {
        $res =  $insertSQL . "\r\n";
        $res .= e.getMessage();
        echo  $res . $wpdb->last_error;
    } finally {
        wp_die();
    }
}

function _no_handle_form_action() {

}

$pluginRoot = str_replace('\\', '/', plugin_dir_path( __FILE__ ));
// DB登録用のPHPファイルへのURL
$insertUrl = 'http://localhost/wp/wp-admin/admin-ajax.php';
// DB削除用のPHPファイルへのURL
$deleteUrl = admin_url('admin-ajax.php');
$pageCount = 0; //$_SESSION['pageCount']+1;

// AJAX ハンドラを AJAX アクションに登録(ログインユーザー用)
add_action('wp_ajax_my_action', '_handle_form_action');
// AJAX ハンドラを AJAX アクションに登録(非ログインユーザー用)
add_action('wp_ajax_nopriv_my_action', '_no_handle_form_action');

global $wpdb;
$dataCount = $wpdb->get_var('SELECT count(question_id) FROM QUESTIONARY');

$func = <<<EOM
function getPageCount() {
    return $pageCount;
}
EOM;
echo '<script>' . $func . '</script>';

?>

<h1>Custom Questionary Plugin</h1>

<hr>
<div class="container">
    <details>
        <summary>Help</summary>
        <div class="row">
            <div class="col">
                <dl>
                    <dt><h4>Usage(使い方)</h4></dt>
                    <dd>1.Create Questions(質問を作成します)</dd>
                    <dd>2.Create answers(回答を作成します): <button type="button" class="btn-sm btn-primary">項目追加</button></dd>
                    <dd>3.Set position(設置する場所を指定します)</dd>
                </dl>
            </div>
            <div class="col">
                <dl>
                    <dt><h4>Usage2(使い方2)</h4></dt>
                    <dd><button type="button" class="btn-xs btn-success">作成</button>: Create sample form</dd>
                    <dd><button type="button" class="btn-xs btn-warning" onclick="deleteForm()">削除</button>: Delete form</dd>
                    <dd><input type="checkbox"/>: Checked -> clear all forms in this page</dd>
                </dl>
            </div>
        </div>
        </details>
    <?php if ($dataCount > 0) { ?>
        <hr>
        <details>
            <summary class="text-success">QuestionTable</summary>
            <div class="row">
                <div class="col">
                    <b class="text-info">Questionaries(質問一覧)</b>
                    <table id="qestionTable" class="table">
                        <thead>
                            <tr>
                                <th>#</th>
                                <th>質問(Question)</th>
                                <th>回答1(ans1)</th>
                                <th>回答2(ans2)</th>
                                <th>回答3(ans3)</th>
                                <th>回答4(ans4)</th>
                                <th>回答5(ans5)</th>
                                <th>挿入先</th>
                            </tr>
                        </thead>
                        <tbody>
                        <?php
                            $result = $wpdb->get_results('SELECT * FROM QUESTIONARY', ARRAY_A);
                            // ここから下はデータがある時のみ表示
                            $recordCount = 0;
                            foreach($result as $key => $data) {
                                echo '<tr data-qid="' . $data['question_id'] . '">';
                                echo '<td><input type="radio" name="dataRecord" value="' . $recordCount . '"/></td>';
                                echo '<td>' . $data['question'] . '</td>';
                                echo '<td>' . $data['answer1'] . '</td>';
                                echo '<td>' . $data['answer2'] . '</td>';
                                echo '<td>' . $data['answer3'] . '</td>';
                                echo '<td>' . $data['answer4'] . '</td>';
                                echo '<td>' . $data['answer5'] . '</td>';
                                echo '<td>' . $data['html_class'] . '</td>';
                                echo '</tr>';
                                $recordCount++;
                            }
                        ?>
                        </tbody>
                    </table>
                    <!-- 編集ボタン一式 -->
                    <button onclick="getTableRecord()">Remove(削除)</button>
                </div>
            </div>
        </details>
    <hr>
    <?php
        }
    ?>
    <hr>
    <h3>Create Question(質問作成)</h3>
    <hr>
    <h4>== Control forms ==</h4>
    <div class="row">
        <div class="col-sm-3 text-primary">・Add answers:<button type="button" class="btn-xs btn-primary" onclick="addRow(this)">項目追加</button></div>
        <div class="col-sm-3 text-success">・Create form:<button type="button" class="btn-xs btn-success" onclick="createForm()">作成</button></div>
        <div class="col-sm-3 text-warning">・Delete form:<button type="button" class="btn-xs btn-warning" onclick="deleteForm()">削除</button></div>
    </div>
    <div class="row">
        <div class="col-sm-3 text-danger">・Insert into DB:<button id="insertDbBtn" type="button" class="btn-xs btn-danger" onclick="outputForm()">登録</button></div>
        <div class="col-sm-3">・Clear Input form:<input type="checkbox" onclick="delInputForm()"/></div>
    </div>
    <h4>== Input forms ==</h4>
    <table id="questionTable">
        <tr>
            <td>
                Insert Position:
            </td>
            <td>
                <select id="insertPosSelect" onchange="inputInsertPos(this)">
                    <option selected></option>
                    <option value="site-navigation">site-navigation</option>
                    <option value="main">main</option>
                    <option value="reply-title">reply-title</option>
                    <option value="primary">primary</option>
                    <option value="comments">comments</option>
                    <option value="respond">respond</option>
                    <optopm vaiue="main">main</option>
                </select>
                <input id="insertPos" type="text" maxlength="30" size="35" placeholder="挿入場所を記入します"/>
            </td>
        </tr>
        <tr>
            <td>
                Input Question:
            </td>
            <td>
                <input id="questionMessage" type="text" maxlength="30" size="35" placeholder="質問内容を記入します"/>
            </td>
        </tr>
        <tr>
            <td>
                Input Answer:
            </td>
            <td>
                <input id="ansText0" data-ans="0" type="text" maxlength="12" size="12"/>
            </td>
        </tr>
    </table>
    <hr/>
    <h3>Sample</h3>
    <div id="result"/>
</div>

ディレクトリのパス取得関数

こちらのページに記述がありました。大まかには下のような関数が使えるようです。

プラグインフォルダのファイル参照

以下のコードは「myscript.js」ファイルへのパスを取得します。

plugins_url( 'myscript.js', __FILE__ );

他にも、下のようにパスを取得できる関数があります。
<プラグディレクトリへのパス>

plugins_url()
plugin_dir_url()
plugin_dir_path()
plugin_basename()

<テーマへのパス>

get_template_directory_uri()
get_stylesheet_directory_uri()
get_stylesheet_uri()
get_theme_root_uri()
get_theme_root()
get_theme_roots()
get_stylesheet_directory()
get_template_directory()

WordPressの定数

ワードプレスで定義されている定数です。

WP_CONTENT_DIR  // 末尾のスラッシュなし、フルパスのみ
WP_CONTENT_URL  // URL全体
WP_PLUGIN_DIR  // フルパス、末尾のスラッシュなし
WP_PLUGIN_URL  //完全な URL、末尾のスラッシュなし

// MS ではデフォルトで利用可能ですが、単一サイトのインストールでは設定されません
// 単一サイトのインストールで使用できます (通常どおり、自己責任で)
UPLOADS // 設定されている場合、ABSPATH を基準にしてフォルダーをアップロードします (例: /wp-content/uploads)

HttpGET関数の使い方

つまりは、httpリクエストでHTMLなどを取得する方法です。
参照サイトはこちらです。

$response = wp_remote_get( '取得したいHTMLへのURL' );

「$response」には、対象のページのHTML文書がセットされています。
レスポンスのサンプルです。

Array(
    [headers] => Array(
        [server] => nginx
        [date] => Fri, 05 Oct 2012 04:43:50 GMT
        [content-type] => application/json; charset=utf-8
        [connection] => close
        [status] => 200 OK
        [vary] => Accept
        [x-ratelimit-remaining] => 4988
        [content-length] => 594
        [last-modified] => Fri, 05 Oct 2012 04:39:58 GMT
        [etag] => "5d5e6f7a09462d6a2b473fb616a26d2a"
        [x-github-media-type] => github.beta
        [cache-control] => public, s-maxage=60, max-age=60
        [x-content-type-options] => nosniff
        [x-ratelimit-limit] => 5000
    )
    [body] => {"type":"User","login":"blobaugh","gravatar_id":"f25f324a47a1efdf7a745e0b2e3c878f","public_gists":1,"followers":22,"created_at":"2011-05-23T21:38:50Z","public_repos":31,"email":"ben@lobaugh.net","hireable":true,"blog":"http://ben.lobaugh.net","bio":null,"following":30,"name":"Ben Lobaugh","company":null,"avatar_url":"https://secure.gravatar.com/avatar/f25f324a47a1efdf7a745e0b2e3c878f?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-user-420.png","id":806179,"html_url":"https://github.com/blobaugh","location":null,"url":"https://api.github.com/users/blobaugh"}
    [response] => Array(
        [preserved_text 5237511b45884ac6db1ff9d7e407f225 /] => 200
        [message] => OK
    )
    [cookies] => Array()
    [filename] =>
)

wp_remote_retrieve_body()メソッドでBODYのみを取得することができます。

$response = wp_remote_get( 'https://api.github.com/users/blobaugh' );
$body     = wp_remote_retrieve_body( $response );

Http POSTの使い方

wp_remote_post()を使用します。

実行する方法は下のように実行します。

$response = wp_remote_post( 'http://your-contact-form.com', $args );

$argsに関しては、下のように定義できます。

$args = array(
    'body'        => $body,
    'timeout'     => '5',
    'redirection' => '5',
    'httpversion' => '1.0',
    'blocking'    => true,
    'headers'     => array(),
    'cookies'     => array(),
);

Ajaxの使い方

参照サイトはこちらです。

課題

ここまでで、クライアント側=画面で表示している部分の作成はできたのですが、DBのデータを更新する処理が実装できていません。
JSからPHPへリクエスト(submitを含む)が送信できていない状態です。

ここの部分を解決する必要があります。。。

Dockerの使い方

イントロダクション

ラズパイにRedmineをインストールして使用しようと考えております。
しかし、記事を検索しながら、いろいろとやってみると、どれもこれもエラーがでて使えない。。。結局Dockerをちゃんと学習することにしました。

参考サイト

DockerDocsを参考にします。

タイトルは「Raspberry Pi OS (32 ビット) に Docker エンジンをインストールする」です。

Dockerとは?

いろいろと記事を斜め読みしてみると下のようなものということです。

Dockerはコンテナ仮想化を用いたOSレベルの仮想化によりアプリケーションを開発・実行環境から隔離し、アプリケーションの素早い提供を可能にする。

色々な説明があるけど、結局はこういうことだと思う。

つまり、「コンテナ」にまとめたアプリケーションを仮想マシンを使用して実行できる。ということらしい

Docker事始め

次のコマンドで、不要なもの、古いDockerをアンインストールします。もしかしたらインストールしているかもしれないからです。

よくあるのが、デフォルトインストールしているかもしれません。。。

for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do sudo apt-get remove $pkg; done

DockerのAPTリポジトリセットアップ

コードを書くと下のようになります。

# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/raspbian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

# Set up Docker's APT repository:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/raspbian \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

ラズパイを更新

下のコマンドで、ラズパイの更新します。

sudo apt-get update

コマンドのインストール

下のコマンドをインストールします。既にインストールしているときは「すでに最新バージョン」というメッセージを確認できます。

  • ca-certificates
  • gnupg
  • curl
sudo apt-get install ca-certificates curl gnupg

Dockeraptリポジトリをセットアップ準備

GPGコマンドで暗号化・復号を行えるようにする?

sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/raspbian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

Dockeraptリポジトリをセットアップ

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/raspbian \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

最後に更新

sudo apt-get update

Dockerパッケージインストール

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

インストールできたら、起動確認「ハローワールド」を実行する

sudo service docker start
sudo docker run hello-world

Dockerコマンドを使う

Dockerをコマンドで扱うときの基本構文は下のようになっています。※こちらのページ参照

docker run [OPTIONS] IMAGE[:TAG|@DIGEST] [COMMAND] [ARG...]

DockerFileを使う

こちらのページを参照しています。※英語だったので翻訳してみました。
下のようなコマンドがあるのでそれを使用するということらしい。。。
Dockerのコマンド入力の基本としてコマンド名は大文字、引数は小文字で書くようです。

docker ADD argument...
コマンド名 意味
ADD ホスト上のソースから、設定された宛先にあるコンテナー独自のファイルシステムにファイルをコピーします。
CMD コンテナー内で特定のコマンドを実行するために使用できます。
ENTRYPOINT は、イメージを使用してコンテナーが作成されるたびに使用されるデフォルトのアプリケーションを設定します。
ENV 環境変数を設定します。
EXPOSE 特定のポートを関連付けて、コンテナと外部の間のネットワークを可能にします。
FROM ビルド プロセスの開始に使用されるベース イメージを定義します。
MAINTAINER イメージ作成者のフルネームと電子メール アドレスを定義します。
RUN Dockerfile の中心的な実行ディレクティブです。
USER コンテナを実行する UID (またはユーザー名) を設定します。
VOLUME コンテナからホスト マシン上のディレクトリへのアクセスを可能にするために使用されます。
WORKDIR CMD で定義されたコマンドが実行されるパスを設定します。
LABEL Docker イメージにラベルを追加できます。

試しにコマンドを実行する

下のようにディレクトリを作成します。作成先は初期ディレクトリ(ユーザーのディレクトリ)から「dockerbuild」というディレクトリを作成しました。

mkdir ~/dockerbuild

Dockerfileを作成します。

nano Dockerfile

下の内容をコピペします。

FROM ubuntu:latest
MAINTAINER NAME EMAIL

RUN apt-get -y update
RUN apt-get -y upgrade
RUN apt-get install -y build-essential

Where NAME is your full name and EMAIL is your email address.

そして、以下のようにコマンドをたたきます。

docker build -t "NAME:Dockerfile" .


エラーがあるけど、イメージ作製はできているようです。

「NAME」には好きな名前を入れてよいです。最後の「.」を忘れないようにしましょう。

これができたら下のようなコマンドをたたきます。

docker images

使用できるイメージの一覧が表示されます。

Redmineのインストール

こちらのサイトを参考にします。
手順は以下の通りです。

  1. docker-compose.ymlを作成する
  2. docker-composeコマンドを実行する
    docker-compose up -d

しかしうまくいかない。。。

no matching manifest for linux/arm/v7 in the manifest list entries

DBの指定とYMLの作成

結局docker-compose.ymlの記述がまずかったみたいだ。「hypriot/rpi-mysql」を指定してやれば問題なかった。
つまり、下の様に書くとOK
<docker-compose.yml>

version: '3.8'
services:
  redmine:
    image: redmine
    container_name: redmine
    ports:
      - 3000:3000
    volumes:
      - ./data/plugins:/usr/src/redmine/plugins
      - ./data/themes:/usr/src/redmine/public/themes
    environment:
      REDMINE_DB_MYSQL: redmine-db
      REDMINE_DB_PASSWORD: redmine
    depends_on:
      - redmine-db
    restart: always

  redmine-db:
    image: hypriot/rpi-mysql
    container_name: redmine-db
    ports:
      - 3306:3306
    environment:
      MYSQL_ROOT_PASSWORD: redmine
      MYSQL_DATABASE: redmine
    volumes:
      - ./data/db:/var/lib/mysql
    command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode>
    restart: always

しかしポート番号が。。。

for redmine-db  Cannot start service redmine-db: driver failed programming external connectivity on endpoint redmine-db (af1d40![](http://zenryokuservice.com/wp/wp-content/uploads/2023/11/Docker4.png)9b7e96abcb6b0d803518c400dd6f2bca79ef35fced14fd6a20b3c2b9cf): Error starting userland proxy: listen tcp4 0.0.0.0:3306: bind: address already in use

つまりは、「DBが使用するポート番号が使用されているため、DBが動かせない」ということでした。

使用ポートを調べる

「ss」コマンドで調べます。実行すると下のような結果が出ました。

pi@raspberrypi:~/dockerbuild/redmine $ ss -atn
State   Recv-Q  Send-Q   Local Address:Port        Peer Address:Port   Process  
LISTEN  0       5              0.0.0.0:5900             0.0.0.0:*               
LISTEN  0       128          127.0.0.1:631              0.0.0.0:*               
LISTEN  0       128            0.0.0.0:22               0.0.0.0:*               
LISTEN  0       80           127.0.0.1:3306             0.0.0.0:*               
ESTAB   0       0            127.0.0.1:60324          127.0.0.1:34449           
ESTAB   0       0            127.0.0.1:34449          127.0.0.1:60324           
ESTAB   0       0        169.254.37.79:5900     169.254.221.230:55375           
LISTEN  0       5                 [::]:5900                [::]:*               
LISTEN  0       511                  *:80                     *:*               
LISTEN  0       128               [::]:22                  [::]:*               
LISTEN  0       128              [::1]:631                 [::]:*  

まずは、使用しているポートが80番で待機(LISTENING)しているサービスと言えば「apache2」があやしい。。。ウェブサーバーは大体80を使っていることが多いので。。。下のコマンドでサービスを確認

service --status-all


そんなわけで、アパッチ(apache2)を停止し用と思ったけど、ポートを変更して対応します。

YMLファイルの編集

ポート番号を3306から3307に変更しました。

  redmine-db:
    image: hypriot/rpi-mysql
    container_name: redmine-db
    ports:
      - 3307:3307
    environment:
      MYSQL_ROOT_PASSWORD: redmine
      MYSQL_DATABASE: redmine
    volumes:
      - ./data/db:/var/lib/mysql
    command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode>
    restart: always

起動することができました。

しかし、現状では不要なアプリケーションが沢山動いているようなので、ブラウザで開けませんでした。。。
他のサービスなどを停止すればよいのですが、また後日。。。

ラズパイの準備

Redmineが動いたので、今度はラズパイの運用準備を行います。
大まかに行うことは以下の通りです。

  • 使用しないGPUのメモリ割り当てを減らす
  • 使わないアプリの停止

メモリ使用量確認

  • free -hコマンド:メモリの利用状況を確認
  • df -h: ファイル・システム内のフリー・スペースの量を表示する

デーモンを停止する

デーモンとは、画面上に見えないアプリケーションのことです。停止するのは以下のデーモンです。

  • alsa-utils
  • dbus
  • triggerhappy
  • cups
プロセルの確認方法
ps aux

上記のコマンドで確認できます。

サービス一覧を取得する

下のコマンドで一覧できます。

service --status-all

ここで表示されたサービスの名前を停止するのには下のように行います。

[ - ]  alsa-utils
 [ - ]  apache-htcacheclean
 [ + ]  apache2
 [ - ]  apparmor
 [ + ]  avahi-daemon
 [ + ]  binfmt-support
 [ + ]  bluetooth
 [ - ]  cgroupfs-mount
 [ - ]  console-setup.sh
 [ + ]  cron
 [ + ]  cups
 [ + ]  cups-browsed
 [ + ]  dbus
 [ + ]  docker
 [ + ]  dphys-swapfile
 [ + ]  fake-hwclock
 [ - ]  fio
 [ - ]  hwclock.sh
 [ - ]  keyboard-setup.sh
 [ + ]  kmod
 [ + ]  lightdm
 [ + ]  mariadb
 [ + ]  networking
 [ - ]  nfs-common
 [ - ]  paxctld
 [ - ]  plymouth
 [ + ]  plymouth-log
 [ + ]  procps
 [ - ]  pulseaudio-enable-autospawn
 [ + ]  raspi-config
 [ + ]  rng-tools-debian
 [ - ]  rpcbind
 [ - ]  rsync
 [ + ]  rsyslog
 [ - ]  saned
 [ + ]  ssh
 [ - ]  sudo
 [ - ]  triggerhappy
 [ + ]  udev
 [ - ]  x11-common

サービスの停止方法

下記コマンドでサービスの停止

sudo systemctl サービス名

無効にする

sudo systemctl disable サービス名

実行したコマンド

<サービスの停止>

sudo systemctl stop alsa-utirls
sudo systemctl stop dbus
sudo systemctl stop triggerhappy.service triggerhappy.service
sudo systemctl stop triggerhappy.socket
sudo systemctl stop cups

<サービス自動起動無効>

sudo systemctl disable alsa-utirls
sudo systemctl disable dbus
sudo systemctl disable triggerhappy.service triggerhappy.service
sudo systemctl disable triggerhappy.socket
sudo systemctl disable cups

設定の反映を行うのに再起動します。

sudo reboot

結果

<サービス停止前>

               total        used        free      shared  buff/cache   available
Mem:           921Mi       328Mi        55Mi        10Mi       538Mi       526Mi
Swap:          384Mi        68Mi       316Mi

<サービス停止後>

               total        used        free      shared  buff/cache   available
Mem:           921Mi       376Mi        78Mi       8.0Mi       466Mi       479Mi
Swap:          376Mi       1.0Mi       375Mi

起動スクリプトを作る

まずは、Shellスクリプトである宣言をする

# !/bin/sh

処理を作成する(自分の場合です。)

cd ~/dockerbuild/redmine/

起動する設定を行う

参考サイトはこちらです。
/etc/systemd/system/ ディレクトリに autorun.serviceを、以下のように作成する

[Unit]
Description=Execute at OS startup and terminates
After=network.target
[Service]
Type=oneshot
ExecStart=/home/pi/autorun.sh
ExecStop=/home/pi/os_term.sh
RemainAfterExit=true
[Install]
WantedBy=multi-user.target

After=network.target はネットワーク関連のプロセスが起動した後に当サービスを起動するという意味らしいです。
サービスの有効化

sudo systemctl daemon-reload
sudo systemctl enable autorun.service

Redmineの起動確認

Redmineの使い方

まずは、初期設定を行う。

チケットのトラッカー

チケットのトラッカーを追加してやります。
上部のメニューにある「管理」をクリックしてから、下のような画面を開くことができます。

ここをクリックして、下のような画面を表示できますので「新しいトラッカー」をクリックして作成します。
ここでは、下のように今後作成するチケットの種類(カテゴリ)を作成します。

チケットのステータス

同様に、上部のメニューにある「管理」をクリックしてから、下のような画面を開くことができます。

同様に、赤枠の部分をクリックしてやると下のような画面が開けます。

これはすでに入力した後なのですが、こんな感じで、「ステータス(状態)」を登録することで、作成するチケットの状態を指定します。
つまりは、「このチケットは<ステータス>に対応した工程の作業」という意味です。

優先度

これも同様に、「選択肢の値」を選択します。

下のように、「新しい値」をクリックして追加してやります。

チケットの作成

チケットのトラッカーを追加してやると下のように、「新しいチケット」をクリックすることができます。これが見当たらないときは設定がたりない状態ということです。

これで、チケットを作成して、アイティアと行いたい作業が混同することを防げるであろう。
まぁこれ流行ってみないことにはどうしようもないな。。。

でわでわ。。。