Mantisのプラグインを作ってみる。(コメントへの画像貼り付け機能) その5 テキストエリアへのクリック挿入

改造ではプレビュー画像をクリックしたらテキストボックスへ「%[file_id],rate100」という文言を挿入していたのだけれど、
これをプラグインで表現するのはどうするんだろう…?
ということで、大変苦労したのですが、テキストエリアへ挿入する機能を追加しました。

■イベントのhookを追加と思ったら、メジャーバージョンの変更

      'EVENT_VIEW_BUG_ATTACHMENT'     => 'display_click_field',       # Display Insert Tags

これを下記のURLで発見。
https://www.mantisbt.org/docs/master-1.2.x/en/developers.html#DEV.EVENTREF

でも、1.2.xって書いてありましたが、1.2.15では存在しませんでした。(1.2系最新の1.2.19でも確認できず。)
おそらく、1.3系の文書に移行中なのを1.2系と誤解したんだと思います。

ともあれ、1.3.0-rc2が2016/6/12に公開されていたので、もうこのプラグインは1.3.x系で動かそう!と変更してしまいました。

■イベントをフックした場所

./core/print_api.phpで発見。

function print_bug_attachment_header( array $p_attachment ) {
        echo "\n";
        if( $p_attachment['exists'] ) {
                if( $p_attachment['can_download'] ) {
                        echo '<a href="' . string_attribute( $p_attachment['download_url'] ) . '">';
                }
                print_file_icon( $p_attachment['display_name'] );
                if( $p_attachment['can_download'] ) {
                        echo '</a>';
                }
                echo lang_get( 'word_separator' );
                if( $p_attachment['can_download'] ) {
                        echo '<a href="' . string_attribute( $p_attachment['download_url'] ) . '">';
                }
                echo string_display_line( $p_attachment['display_name'] );
                if( $p_attachment['can_download'] ) {
                        echo '</a>';
                }
                echo lang_get( 'word_separator' ) . '(' . number_format( $p_attachment['size'] ) . lang_get( 'word_separator' ) . lang_get( 'bytes' ) . ')';
                echo lang_get( 'word_separator' ) . '<span class="italic">' . date( config_get( 'normal_date_format' ), $p_attachment['date_added'] ) . '</span>';
                event_signal( 'EVENT_VIEW_BUG_ATTACHMENT', array( $p_attachment ) );
        } else {
                print_file_icon( $p_attachment['display_name'] );
                echo lang_get( 'word_separator' ) . '<span class="strike">' . string_display_line( $p_attachment['display_name'] ) . '</span>' . lang_get( 'word_separator' ) . '(' . lang_get( 'attachment_missing' ) . ')';
        }
 
        if( $p_attachment['can_delete'] ) {
                echo lang_get( 'word_separator' ) . '[';
                print_link( 'bug_file_delete.php?file_id=' . $p_attachment['id'] . form_security_param( 'bug_file_delete' ), lang_get( 'delete_link' ), false, 'small' );
                echo ']';
        }
}

■軽くはまった点

                event_signal( 'EVENT_VIEW_BUG_ATTACHMENT', array( $p_attachment ) );

上記のように、引数の1番目はイベント名、2番目に添付されたファイルの情報が入った配列がわたっていました。
他の関数と同じように、1つの引数をとるようにしていたので、思った値が出ずに困っていました。

また、最初は既存の(Coreプログラムの)関数名と同じものをつけて、overrideしてくれるはず…と思っていたのですが、
オブジェクトのメソッドじゃないのでダメでした。(当たり前ですが、同じ名前の関数でも名前空間で区別されているはずですね)

■Javascriptで機能追加

改造時は読み込まれるbodyに直接Javascriptを記載していたのですが、外部ファイルを読み込む形で
実装したところ、イベントハンドラーを設定しないとOnclickなどで実行させないよとChromeの開発ツールで
怒られてまして。下記のようにwindow.onloadで設定しました。

  window.onload = function(){

    [].forEach.call( document.getElementsByClassName("Mantis_ImagePasteOnComment_Insert"),function(x){
      x.addEventListener("click",mantis_imagepasteoncomment_insert_textarea);
    });

  } // window onload

俺タワーウィジェット(もどき)を作る – せらぴんブログ
こちらを参考に原因特定をして、OnclickからaddEventListenerに変更し、

getElementsByClassName(“…”)に対してforEachを行う方法
こちらを参考にClassNameのすべてに追加するという形で行いました。

■CSSだけじゃなくてJSも読み込むように変更

  /**
   * Include css ,js
   * @param int $p_event
   * @return string
   */
  function resources( $p_event){
    $resource  = '<link href="' . plugin_file( 'Mantis_ImagePasteOnComment.css' ) . '" media="all" rel="stylesheet" type="text/css"/>';
    $resource .= '<script type="text/javascript" src="' . plugin_file( 'Mantis_ImagePasteOnComment.js' ) . '"></script>';
    return $resource;
  } // f resources

結局BBCodeのお手本と同じような関数名にしてしまいました。

■テキストエリアに挿入するJavascript

  function mantis_imagepasteoncomment_insert_textarea(mouseevent){
    var return_flg = false;
    var text_area_obj = document.getElementsByName("bugnote_text");
    text_area_obj[0].focus();
 
    var selected_obj  = document.activeElement;
    if(selected_obj.selectionStart <= selected_obj.selectionEnd ){
      string_start = selected_obj.selectionStart;
      string_end   = selected_obj.selectionEnd;
    } else {
      string_start = selected_obj.selectionEnd;
      string_end   = selected_obj.selectionStart;
    } // if 
 
    before_range  = selected_obj.value.substring(
                    0, string_start);
    range         = selected_obj.value.substring(
                    string_start , string_end);
    after_range   = selected_obj.value.substring(
                    string_end );
 
  /*   debug
      console.log(mouseevent);
 
      console.log(e);
      console.log("string_start:" + string_start);
      console.log("string_end:" + string_end);
      console.log("before_range:" + before_range);
      console.log("range:" + range);
      console.log("after_range:" + after_range);
      console.log("text_num:" + (range.match(/\r\n|\n/g)||[]).length);
      console.log(mouseevent.srcElement.id);
      console.log(image_id);
    */
 
    // 改行数の確認
 
    if( 1 < (range.match(/\r\n|\n/g)||[]).length ){
      return_flg = true;
    } // if match
 
    id_str   = mouseevent.srcElement.id;
    image_id = id_str.substring(id_str.indexOf("_")+1,id_str.length);
 
    if(image_id === "undefined"){ 
      before_insert_tag = '\n%[],rate100';
    }else{
      before_insert_tag = '\n%[' + image_id + '],rate100';
    }
    after_insert_tag  = '';
 
    if(return_flg){
      text_area_obj[0].value = before_range + before_insert_tag + '\n' + range + after_insert_tag + '\n' + after_range ; 
      var CaretPosition = string_start + before_insert_tag.length + range.length + after_insert_tag.length + 1;
    }else{
      text_area_obj[0].value = before_range + before_insert_tag + range + after_insert_tag + after_range ; 
      var CaretPosition = string_start + before_insert_tag.length + range.length + after_insert_tag.length ;
    } // if return_flg something
 
    if(!range){
      text_area_obj[0].value = before_range + before_insert_tag + '\n' + after_insert_tag + '\n' + after_range ; 
      var CaretPosition = string_start + before_insert_tag.length + 1;
    } // if range noting
 
    text_area_obj[0].setSelectionRange( CaretPosition , CaretPosition);
 
  } // f insert_textarea

昔作ったものがほぼそのまま入ったので問題なさそうです。
若干気になるところといえば、

    var text_area_obj = document.getElementsByName("bugnote_text");
    text_area_obj[0].focus();

この部分のように、getElementsByNameは配列で入ってくるので、0番目という形で指定している点ですかね。
1個だけだったらうまく動くんでしょうけど…保証されなさそうっていう点で心配は残っています。

■HTMLのwidth設定

  /**
   * return an href anchor that links to a bug COMMENT page for the given images uploaded
   * @param int $p_image_id
   * @return string
   */
  function string_get_bug_image_link( $match = null ) {
    $p_image_id = empty($match[2]) === false ? $match[2] : null;
    //error_log('$match'.print_r($match,true),3,'/tmp/test.log');
    preg_match('/^,rate(\d+).*/',$match[3],$match_rate);
    $p_image_rate      = empty($match_rate[1]) === false ? 'width:'.($match_rate[1] * 0.7).'%;'  : null;
    $p_image_rate_html = empty($match_rate[1]) === false ? ($match_rate[1] * 0.7).'%;'  : null;
    //error_log('$match_rate:'.print_r($match_rate,true),3,'/tmp/test.log');
 
    $security_param  = form_security_param( 'file_show_inline' );
    $image_link      = <<< _HTML_
    <img class="Mantis_ImagePasteOnComment" style="{$p_image_rate}" width="{$p_image_rate_html}"
         src="file_download.php?file_id=${p_image_id}&type=bug&show_inline=1{$security_param}" >
  <br 
_HTML_;

もともとは、style=”width:70%”のようにimgタグのスタイルシートで指定してたのですが、
適用されなかったので、HTML要素のwidthでも指定するようにしました。