tohokuaikiのチラシの裏

技術的ネタとか。

JIRAの作業履歴を日別に出す方法(自前JavaScriptで)

フツーはtempoプラグインを使えって話なんですが…まぁ。

対象になるページ

http://jira.example.jp/projects/PRJECTID/issues/
のページをChromeFirefoxなんかで開く。

ここ。
f:id:tohokuaiki:20190402172906p:plain

このConsoleから下記のJavaScriptを入力する。

(function($){
    var output = "";
    var doList = function() {
        var ret = "";
        $('.issuePanelWrapper').find('.actionContainer').each(function(i, ac){
            var job = '', times = '';
            var date = $(ac).find('span.date').text().substring(0, 10);
            $(ac).find('ul.item-details').each(function(i, ul){
                times = $.trim($(ul).find('.worklog-duration').text());
                job   = $.trim($(ul).find('.worklog-comment').text());
                job = job.replace(/[\r\n]/g, "");
            });
            ret += date + "\t" + times + "\t" + job + "\n";
        });
        return ret;
    }
    
    var issues = $('.search-results a.splitview-issue-link');
    var i = -1;
    var timer = setInterval(function(){
        if (i >= 0){
            $(issues.get(i)).each(function(i, a){
                output += $.trim($('h1#summary-val').text()) + "\n";
            });
            output += doList();
        }
        i++;
        $(issues.get(i)).trigger('click');
        if (i >= issues.length){
            clearInterval(timer);
            console.log(output); 
        }
    }, 3000);
})(jQuery)

何となく、サーバーのレスポンスが速ければIntervalの3000を1000とかにしたりして。

WordPressの編集画面がメチャ重くなったのでメタ情報のキャッシュを作って回避

調べてみると、カスタムフィールドの定義を取得するSQLで20秒も掛かってた。

カスタムフィールド使いまくってて、20万行越えてた。
過去のデータ中から、編集画面でプルダウンを作るんだけどそれはちょっと時間かかるよね…

ということで、テーマのfuncitons.phpに以下を追加。テーマディレクトリに tmpディレクトリを作ってパーミション777に。

<?php
class AnonymousTekitou
{
    /* ローカルキャッシュの時刻 */
    private $cache_lifetime  = 8000;
    
    /* ローカルキャッシュのディレクトリレベル */
    private $cache_dir_level = 2;
    

    /**
     * @brief 
     * @param 
     * @retval
     */
    public static function getInstance($cache_lifetime = null)
    {
        static $ins ;
        
        if (is_null($ins)){
            $ins = new self();
            if (!is_null($cache_lifetime)){
                $ins->cache_lifetime = $cache_lifetime;
            }
        }
        return $ins;
    }
    
    /**
     * @brief ファイルキャッシュ
     * @param 
     * @param 保存する場合は必要
     * @retval
     */
    private function cache($url, $body = null)
    {
        $cache_file = sprintf('%s/tmp/cache/%s',
                              __DIR__, $this->splitString(md5($url), $this->cache_dir_level));
        $cache_dir  = dirname($cache_file);
        if (!is_dir($cache_dir)){
            self::mkdir($cache_dir, 0777);
        }
        chmod($cache_dir, 0777);
        
        if (is_null($body)){
            if (file_exists($cache_file)){
                $s = stat($cache_file);
                if ($s['mtime'] + $this->cache_lifetime > time()){
                    $file = file($cache_file);
                    array_shift($file);
                    return implode("", $file); // キャッシュ有効・ファイルが存在してライフタイム内
                }
                else {
                    unlink($cache_file); // キャッシュ無効・再度取得
                    return null ;
                }
            }
            return false; 
        }
        else {
            file_put_contents($cache_file, $url."\n".$body);
            chmod($cache_file, 0666);
        }
    }

    

    /**
     * @brief 文字スラッシュを入れる。ex: abcdefg => a/b/c/defg
     * @param 文字列
     * @param 分割個数
     * @retval
     */
    private function splitString($str, $num) 
    {
        $ret = "";
        for ($i=0; $i< strlen($str) ;$i++){
            $ret .= $str{$i};
            if ($i<$num){
                $ret.= "/"; 
            }
        }
        return $ret;
    }
    
    
    /**
     *  mkdir -p
     *
     *  @access public
     *  @param  string  $dir    作成するディレクトリ
     *  @param  int     $mode   パーミッション
     *  @return bool    true:成功 false:失敗
     *  @static
     */
    public static function mkdir($dir, $mode)
    {
        if (file_exists($dir)) {
            return is_dir($dir);
        }

        $parent = dirname($dir);
        if ($dir === $parent) {
            return true;
        }

        if (is_dir($parent) === false) {
            if (self::mkdir($parent, $mode) === false) {
                return false;
            }
        }

        return mkdir($dir, $mode) && chmod($dir, $mode);
    }
    
    
    
    
    /**
     * @brief postmeta_form_keysのキャッシュを作成する
     * @param $WP_Post
     * @retval
     */
    public function getPostmetaFormKeysCache($post) 
    {
        global $wpdb;

        $key_name = md5(__CLASS__ . __METHOD__ . DB_NAME . __FILE__ . $_SERVER['SERVER_NAME']);

        // postがnullの時だけキャッシュを使う
        if (!is_null($post)) {
            return null;
        }

        if ($cache = $this->cache($key_name)){
            $keys = unserialize($cache);
        }
        else {
            // make cache
            $limit = apply_filters( 'postmeta_form_limit', 30 );
            $sql = "SELECT DISTINCT meta_key
          FROM $wpdb->postmeta
          WHERE meta_key NOT BETWEEN '_' AND '_z'
          HAVING meta_key NOT LIKE %s
          ORDER BY meta_key
          LIMIT %d";
            $keys = $wpdb->get_col( $wpdb->prepare( $sql, $wpdb->esc_like( '_' ) . '%', $limit ) );
            $this->cache($key_name, serialize($keys));
        }
        
        return $keys;
    }
}

add_filter('postmeta_form_keys', function($p){
    $a= AnonymousTekitou::getInstance();
    return $a->getPostmetaFormKeysCache($p);
});

Let's Encryptのための穴あけ

Let's Encryptを使うには、httpでの接続も残しておかないと更新してくれない。.well-knownにアクセスできなくて失敗する。

httpを立てておいて、httpsにリダイレクト、でその先はBasic認証アリだとか作ってるとダメ。http => https(Basic認証アリ)

IMPORTANT NOTES:
 - The following errors were reported by the server:

   Domain: example.com
   Type:   unauthorized
   Detail: Invalid response from
   http://example.com/.well-known/acme-challenge/OjG***************************************3I:
   "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML
   2.0//EN\">\n<html><head>\n<title>401
   Unauthorized</title>\n</head><body>\n<h1>Unauthorized</"

で、こうやっておく。

httpアクセス側のhttpd.conf

<VirtualHost *:80>
        ServerAdmin webmaster@example.jp
        ServerName example.com
        DocumentRoot /web/example.com/htdocs

        <Directory /web/example.com/htdocs>
            RewriteEngine On
            RewriteRule .* https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]
        </Directory>

        <Directory /web/example.com/htdocs/.well-known>
            RewriteEngine Off
            Require all granted
        </Directory>

        ErrorLog ${APACHE_LOG_DIR}/example.com.error.log
        CustomLog ${APACHE_LOG_DIR}/example.com.access.log combined

</VirtualHost>

.well-knowは転送無しにして、かつ Require all granted 。これが無いとあの忌々しい

[authz_core:error] [pid 2510] [client 192.168.111.111:50355] AH01630: client denied by server configuration:

が出る。

Vue.jsのファイルをドラッグ&ドロップでIE11だけ引っかかったところ

IE11だけといいつつ、Safariは調べてないので知らない。

Vue.jsを使って

    <div class="dropArea" :class="{ dragOver: isDragOver }"
      @dragleave.prevent="onDragLeave"
      @dragover.prevent="onDragOver"
      @drop.prevent="onDrop"
      >
      <p>ここにCSVをドラッグ&ドロップしてください。</p>
    </div>

って書いたら、IE11だけ反応しなかった。普通にファイルをダウンロードしてしまう。

解決

DragEnterで引っかかっていたみたい。@dragenter.preventを入れる。

    <div class="dropArea" :class="{ dragOver: isDragOver }"
      @dragenter.prevent=""
      @dragleave.prevent="onDragLeave"
      @dragover.prevent="onDragOver"
      @drop.prevent="onDrop"
      >
      <p>ここにCSVをドラッグ&ドロップしてください。</p>
    </div>

文字列を規定の区切り文字で区切る(ったり色々する)関数

<?php
/**
 * @brief 文字列を右からN桁区切りにする
 * @param 文字列
 * @param 区切り数
 * @param 区切り文字
 * @param パディング文字
 * @param 最短文字数:足らない場合はパディングする
 * @retval
 */
function convDigit($str, $num = 3, $glue = '/', $padding = '0', $min_length = null)
{
    $str = strrev(strval($str));
    
    $ret = array();

    $fragment = "";
    for ($i=0; $i< strlen($str); $i++){
        $fragment = $str{$i}.$fragment;
        if (strlen($fragment) === $num){
            $ret[] = $fragment;
            $fragment = "";
        }
    }
    if ($fragment){
        $ret[] = sprintf('%'.$padding.$num.'s',
                         $fragment);
    }
    
    if ($min_length && $num * count($ret) < $min_length){
        $ret = array_pad($ret, ceil($min_length / $num), 
                         sprintf('%'.$padding.$num.'s', ''));
    }
    
    return implode("/", array_reverse($ret));
}

テスト

<?php
$n = "";
for ($i=1; $i<20; $i++){
     $n .= $i;
     echo convDigit($n, 3, '/', '0', 20)."\n";
}

000/000/000/000/000/000/001
000/000/000/000/000/000/012
000/000/000/000/000/000/123
000/000/000/000/000/001/234
000/000/000/000/000/012/345
000/000/000/000/000/123/456
000/000/000/000/001/234/567
000/000/000/000/012/345/678
000/000/000/000/123/456/789
000/000/000/012/345/678/910
000/000/001/234/567/891/011
000/000/123/456/789/101/112
000/012/345/678/910/111/213
001/234/567/891/011/121/314
123/456/789/101/112/131/415
012/345/678/910/111/213/141/516
001/234/567/891/011/121/314/151/617
123/456/789/101/112/131/415/161/718
012/345/678/910/111/213/141/516/171/819