ATOM markdown で章番号を付ける

ATOM markdown で、# , ## , ### による h1 , h2 , h3 に対して章番号を付ける方法、
HTML に変換しても、章番号を付ける。。
ユーザホームディレクトリ、~/.mume ディレクト
    Windows なら、C:\Users\XXXXXXX\.mume に、
Style.less を書いておくか、任意のフォルダで Style.less を書いて、
@import 句を書いて読み込ませるかの方法になる。
CSSスタイルシートの書き方として、HTML bodyタグで、章番号カウンタをリセットして、
疑似属性 before でカウントUPする

/* Please visit the URL below for more information: */
/*   https://shd101wyy.github.io/markdown-preview-enhanced/#/customize-css */ 

body {
    counter-reset: chapter1;
}
h2 {
  counter-reset: chapter2;
}
h3 {
  counter-reset: chapter3;
}
h4 {
  counter-reset: chapter4;
}
h5 {
}

.markdown-preview.markdown-preview {
  // modify your style here
  // eg: background-color: blue;  
  h2::before {
    counter-increment: chapter1;
    content: counter(chapter1) ". ";
  }
  h3::before {
    counter-increment: chapter2;
    content: counter(chapter1) "." counter(chapter2) ". ";
  }
  h4::before {
    counter-increment: chapter3;
    content: counter(chapter1) "." counter(chapter2) "." counter(chapter3) ". ";
  }
  h5::before {
    counter-increment: chapter4;
    content: counter(chapter1) "." counter(chapter2) "." counter(chapter3) "." counter(chapter4) ". ";
  }
}

これを、~/.mume/Style.less として書くか、、
作成する markdown ファイルの中で、、カレントフォルダ に、mume を作り、そこに、Style.less を置くならば、
文書の先頭で、@import 文、(")ダブルクォートで括って指定する

@import "mume/Style.less"

Slack API の files.list

Slack に投稿したファイルの一覧を取得するAPI
https://api.slack.com/methods/files.list
問い合わせする時に、絞り込み条件をしないと1つのファイルの情報だけでも大きく
大量に出てたいへん、
投稿時刻等の条件指定するのが現実的で、
ts_from = From 時刻
ts_to = To時刻
で、UNIX Time を指定しないとならない。
1つのファイルの情報は、以下の情報が、JSON で取得される。
https://api.slack.com/types/file
この中の、url_private のURLをSlackログイン済ブラウザに貼り付ければ、
画像ファイルであればそのまま表示される。
url_private_download のURLをSlackログイン済ブラウザに貼り付ければ、
ファイルダウンロードする。
だれが、投稿したか?は、user キーの値だが、Slackが管理してるIDで値だけ見ても
判断できない。
users.info というAPI メソッドでユーザ情報を
問い合わせれば出てくるのだが、、
https://api.slack.com/methods/users.info

これもたくさん情報が返ってくる。Slack表示上の人の名前が知りたいわけで、
Slack表示上の人の名前は、user → real_name を参照

{
    "ok": true,
    "user": {
        "id": "XXXXXXXXXXXXX",
        "team_id": "XXXXXXXXXXXXXXX",
        "name": "xxxxxxxxxxx",
        "deleted": false,
        "color": "e7392d",
        "real_name": "\u文字の Unicode 書式",
  ...

のように、real_name は、Unicode である。

JavaScript 上で、問い合わせていて実際の表示名を知りたければ、

var realname = decodeURIComponent(real_name_value);

のようにする。

Java で、問い合わせ結果 JSON パースしていて、実際の表示名を知りたければ、

static String unicodeToString(String unicode){
    String[] sp = unicode.split("\\\\u");
    int[] cs = new int[sp.length - 1];
    for (int i = 0; i < cs.length; i++){
        cs[i] = Integer.parseInt(sp[i + 1], 16);
    }
    return new String(cs, 0, cs.length);
}

こんなメソッドでも用意して、real_name の値を変換することになるだろう。
いや、そんなことしなくても、gson で読み込めば、JsonObject として読み込めるので、
getAsString() は、期待どおりの文字列を返してくれるのを簡単に書ける。

Slack API chat.postMessage での Direct Message の宛先

Slack を使用していて、アカウントのSlack表示名を、日本語2バイト文字で設定している
アカウントに、Slack API chat.postMessage でメッセージ送信しようと思って
channel 指定を、"@" + URLエンコードした文字列にしたけど、ダメだった。

しかたないので、Slack のアカウント設定を開き、「ユーザ名」の設定で登録した名称、
以下の説明が書かれているユーザ名、さすがにこれは、ASCII 文字列で登録してあるだろう。。。

ユーザー名はプロフィールの一部ではなく、 Slack が技術的な理由で必要としているものです。ユーザー名はほとんど他の人には見えませんが、必要に応じて変更することができます。

このユーザ名の先頭に、"@" を付けて chat.postMessage の channel指定にすれば、
送信できた。

f:id:posturan:20180613114621j:plain

ファイル選択ダイアログ→PlantUML JAR 実行

PlantUML ダウンロードした jar ファイルで、
java -jar plantuml.jar ファイル -charset UTF-8
を実行するのは、結構面倒くさい。。。
oboe2uran.hatenablog.com




そこで、ファイル選択ダイアログで選択する bat ファイルを用意することにする。

まずは、ファイル選択ダイアログを bat ファイル で動かすため、


基になる ファイル選択だけの確認!!

@echo off
@rem set "File=C:\a.txt"
set "Filter=テキスト (*.txt)|*.txt|すべてのファイル (*.*)|*.*|"
set "Title=ファイルの選択"
for /f "delims=" %%I in ('MSHTA.EXE "about:<object id=HtmlDlgHelper classid=CLSID:3050f4e1-98b5-11cf-bb82-00aa00bdce0b></object><script>resizeTo(0,0);function window.onload(){var Env=new ActiveXObject('WScript.Shell').Environment('Process');new ActiveXObject('Scripting.FileSystemObject').GetStandardStream(1).Write(HtmlDlgHelper.object.openfiledlg(Env('File'),null,Env('Filter'),Env('Title')).replace(/\0.*/,''));close();}</script><hta:application caption=no showintaskbar=no />"') do echo %%I

pause

↑ for ~ の行は、改行を入れてはならない。

目的の plantuml.jar 実行、、
C:\Programs\PlantUML の下に、planttml.jar を置いて、java コマンドのパスも通ってる環境で、、

@echo off
set "Filter=Markdown (*.md)|*.md|plantUML (*.pu)|*.pu|すべてのファイル (*.*)|*.*|"
set "Title=PlantUMLファイルの選択"
for /f "delims=" %%I in ('MSHTA.EXE "about:<object id=HtmlDlgHelper classid=CLSID:3050f4e1-98b5-11cf-bb82-00aa00bdce0b></object><script>resizeTo(0,0);function window.onload(){var Env=new ActiveXObject('WScript.Shell').Environment('Process');new ActiveXObject('Scripting.FileSystemObject').GetStandardStream(1).Write(HtmlDlgHelper.object.openfiledlg(Env('File'),null,Env('Filter'),Env('Title')).replace(/\0.*/,''));close();}</script><hta:application caption=no showintaskbar=no />"') do java -jar C:\Programs\PlantUML\plantuml.jar %%I -charset UTF-8

これで、DOSプロンプトウィンドウを起動してコマンドを打たなくて済む。

PlantUML 画像サイズ、文字サイズ

PlantUML で作成するPNG画像サイズを調整する
スケールを記述する
0.5 倍に縮小する時

@startuml
scale 0.5

シーケンス図での文字サイズ調整

@startuml
skinparam sequence {
ActorFontSize 24
ParticipantFontSize 24
GroupFontSize  24
ArrowFontSize 24
titleFontSize 24
}

コメントアウトを書く時は、、、

      /'  スラッシュ+ シングルクォート の囲み '/

簡単な認証済をチェックしたアクセス制限のページ

wicket-auth-roles を使った認証済をチェックするのは、@AuthorizeInstantiation
で指定する役割 Role で制御して機能も Page だけでなく、
Panel やコンポーネントにも指定できて機能は優れている。
でも、そこまでの機能が不要で単純な 認証済をチェックしたアクセス制限のページ
作る場合にどうしたら良いか?
org.apache.wicket.authorization.IAuthorizationStrategy を実装した認証ストラテジー
実装して WebApplication の init() で認証チェックが働くように仕掛ける。
認証済か否かと、セッションの期間有効期限をチェックする処理を
IAuthorizationStrategy 実装で行う。
WebSession 実装クラスで認証済を保存する。
コードは、

package org.yipuran.wicketcustom.auth;

import org.apache.wicket.Component;
import org.apache.wicket.RestartResponseAtInterceptPageException;
import org.apache.wicket.Session;
import org.apache.wicket.authorization.Action;
import org.apache.wicket.authorization.IAuthorizationStrategy;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.request.component.IRequestableComponent;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.request.resource.IResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * 認証 Strategy.
 */
public class AuthorizationStrategy implements IAuthorizationStrategy{
   private Class<? extends WebPage> authenticatedPage;
   private Class<? extends WebPage> signinPage;

   Logger logger = LoggerFactory.getLogger(this.getClass());
   /**
    * コンストラクタ.
    * @param authenticatedPage 認証済の基底 WebPage class
    * @param signinPage 認証を行う WebPage class
    */
   public AuthorizationStrategy(Class<? extends WebPage> authenticatedPage
, Class<? extends WebPage> signinPage){
      this.authenticatedPage = authenticatedPage;
      this.signinPage = signinPage;
   }
   @Override
   public <T extends IRequestableComponent> boolean isInstantiationAuthorized(Class<T> component){
      return true;
   }
   /**
    * 認証チェック.
    * セッションに認証済か問い合わせて認証済なら trueを返す。認証できないまたは有効期限切れは
    * RestartResponseAtInterceptPageException でコンストラクタで指定した signinPage に遷移する
    * @param component Component
    * @param action Action
    */
   @Override
   public boolean isActionAuthorized(Component component, Action action){
      if (authenticatedPage.isAssignableFrom(component.getClass())){
         // セッションに認証済か問い合わせて認証済なら true
         if (((AuthSession)Session.get()).isAuth()){
            if (((AuthSession)Session.get()).overtime()){
               // セッション有効切れ
               throw new RestartResponseAtInterceptPageException(signinPage);
            }
            return true;
         }else{
            throw new RestartResponseAtInterceptPageException(signinPage);
         }
      }
      return true;
   }
   @Override
   public boolean isResourceAuthorized(IResource resource, PageParameters parameters){
      if (((AuthSession)Session.get()).isAuth()){
         if (((AuthSession)Session.get()).overtime()){
            return false;
         }
         return true;
      }
      return true;
   }
}

WebSession のベース

package org.yipuran.wicketcustom.auth;

import java.time.Duration;
import java.time.LocalDateTime;
import org.apache.wicket.protocol.http.WebSession;
import org.apache.wicket.request.Request;
/**
 * 認証チェック機能 WebSession.
 */
public class AuthSession extends WebSession{
   private boolean auth = false;
   private LocalDateTime actionTime;
   private int overtimeLimit = 1800;
   /**
    * コンストラクタ.
    * @param request org.apache.wicket.request
    * @param overtimeLimit セッション有効期限 秒単位指定
    */
   public AuthSession(Request request, int overtimeLimit){
      super(request);
      this.overtimeLimit = overtimeLimit;
      actionTime = LocalDateTime.now();
   }
   /**
    * 認証済問い合わせ.
    * @return true=認証済
    */
   public boolean isAuth(){
      return auth;
   }
   /**
    * 認証済を初期化(認証否にする).
    */
   public void initAuth(){
      auth = false;
   }
   /**
    * 認証済セット. setAuth() 実行の都度、有効期間の経過時間は初期化される。
    * @param isAuth ログイン成功したら true をセットする
    */
   public void setAuth(){
      auth = true;
      actionTime = LocalDateTime.now();
   }
   /**
    * 一定時間経過後の実行問い合わせ.
    * @return true=時間経過している。
    */
   public boolean overtime(){
      if (Duration.between(actionTime, LocalDateTime.now()).getSeconds() > overtimeLimit){
         return true;
      }
      actionTime = LocalDateTime.now();
      return false;
   }
}

AuthSession を継承して実際に管理する WebSession を用意

import org.apache.wicket.request.Request;
import org.yipuran.wicketcustom.auth.AuthSession;
/**
 * MySession
 */
public class MySession extends AuthSession{
   private String authid;

   public MySession(Request request){
      // ページの有効期限 10秒
      super(request, 10);
   }
   /**
    * 認証済セット.
    * @param isAuth ログイン成功したら true をセットする
    */
   public void setAuthId(String authid){
      setAuth();
      this.authid = authid;
   }
   public String getAuthid(){
      return authid;
   }
}

ログイン(サインイン)のページで、認証した時に以下を実行

    ((MySession)getSession()).setAuthId(login);

WebApplication の init() で、AuthorizationStrategy を登録

// BasePage   認証済チェック対象の基底のWebPageクラス
// LoginPage 認証を行うWebPageクラス
getSecuritySettings()
.setAuthorizationStrategy(new AuthorizationStrategy(BasePage.class, LoginPage.class));

WebApplication でMySession を使用するように宣言

@Override
public Session newSession(Request request, Response response){
     return new MySession(request);
}

さらに、表示したままアクション(forn送信、ページ遷移、イベント)を一定期間実行しなければ
AuthorizationStrategy の overtime がセッションの有効期間を超えたら認証ページに遷移する
ように、AjaxSelfUpdatingTimerBehavior によるタイマーを仕掛ける。

// 20秒後に AjaxSelfUpdatingTimerBehavior 
queue(new Label("mark", "")
           .add(new AjaxSelfUpdatingTimerBehavior(Duration.seconds(20))));

↑ 何もページに表示しない Label コンポーネントで充分

     <div wicket:id="mark"></div>

これら、AuthorizationStrategy と AuthSession は、
既にライブラリ
GitHub - yipuran/yipuran-wicketcustom: Wicket custom Library
に入れてある。