vte.cxドキュメント

##仕様

これは、[vte.cx](https://vte.cx) の仕様をまとめたものです。

### 概要

vte.cx（ブイテックス）はReactなどのJavaScriptフレームワークを利用してWebサービスを作成することができるバックエンドサービス（BaaS）です。
 vte.cxのアプリケーションは基本的にREST APIを利⽤するSPA（Single Page Application)になります。APIを介すため、サーバとクライアントは基本的に疎結合となり、可搬性の高いアプリケーションを作成できます。
　このドキュメントは主にvte.cxの仕様について説明したものです。サービスの作成やデプロイ方法など実践的な内容については[チュートリアル](https://admin.vte.cx/tutorial.html)を参照してください。また、[vtecxblank](https://github.com/reflexworks/vtecxblank/)内のチュートリアル（基礎編）、（応用編）も合わせてご覧ください。

### 主な機能

* アカウント管理機能を提供します。
  * アプリのユーザー登録やログインを簡単に実装できユーザー管理を行える機能です。
  * メール認証によるユーザ管理の他、グループ管理が可能です。
  * 新規ユーザのサインアップ、パスワードリセットなどの画面を提供します。また、管理者によるユーザ登録機能があります。
* ドキュメント型データベースに対してREST APIによるデータアクセスが可能です。
    * ドキュメント型データベースはあらゆるリソースをツリー構造で管理します。これは、Windowsなどのファイルシステムに似た管理方法であり、多くの開発者が慣れ親しんでいるインターフェースです。
    * リソースはデータ以外にも、HTMLやJavaScriptなどの静的コンテンツや写真などのblobコンテンが扱えるため、PCやモバイルのランディングページ等のWebページCMS機能として使用できます。
    * REST APIでは、エンドポイントURLで示すリソースに対して、４つのHTTPメソッド(GET,POST,PUT,DELETE)を実行する形になります。
    * リソースを、JSONやXML、MessagePackなどの様々な形式に変換して取りだすことができます。
* リソースのデータはエントリスキーマというものでスキーマ管理します。
    * テンプレート設定ファイルに項目を記述することでエントリスキーマを定義します。
    * エントリスキーマは項目の追加が可能なソフトスキーマといわれるもので、デプロイ後のサービス運用中であっても自由に項目の追加ができます。
    * テンプレートは管理画面から編集することができますが、設定ファイルを直接編集することもできます。
    * テンプレートからTypeScriptの型定義ファイルを生成することができます。(管理画面のTS型定義ファイル出力機能)
* アクセス権限(ACL:Access Control List)などを設定できます。
    * コンテンツやフォルダに対してACLを設定することで、ユーザやグループ単位で公開/非公開設定などのアクセスコントロールができます。
    * 項目ごとにバリデーションルールやACL、Index、暗号化などを設定できます。
* サーバサイドJavaScriptを実行することができます。
    * サーバサイドで実行するJavaScriptを使ってビジネスロジックを記述することができます。
    * Reactを利⽤してSSR（サーバサイドレンダリング）を実装することができます。
    * ReactとSSRを利用して帳票PDFを出力することができます。
    * excelやcsvのデータアップロード/ダウンロード、メール送信などを実行できます。
    * Google BigQueryに対してJavaScriptから直接SQL操作が可能であり、データ分析アプリケーションを容易に作成することができます。
    * ビルド完了と同時に更新ファイルがサーバに自動的にアップロードされるため動作確認をすぐに行うことができます。

###サービス

サービスとはユーザが作成するWebアプリケーション単位に割り当てられる領域(ドメイン)のことで、サービス一つにつきvte.cxのサブドメインが一つ割り当てられます。いわゆる、マルチテナントになっており、サービスは完全に独立して動作するため他のサービスと干渉することはありません。
　サービスは、管理画面より作成してください。サービス作成に成功すると以下のようにサービス名のサブドメインが割り当てられます。

```
https://{サービス名}.vte.cx
```

サービス名は、ログインして``GET /d/?_service``を実行することでも取得できます。

サービスには以下のような状態（ステータス）があります。
　作成されたサービスはひとまず「サービス非公開(開発中):staging」となります。
この状態のサービスは、JavaScriptのローカル開発環境で使うことを想定しており、http通信しかできません。また無料で使えます。
　管理画面から「サービス公開中:production」に変更すると、今度はhttps通信しかできなくなります。この状態になると課金されるようになります。

```
    creating : サービス新規作成中
    staging : サービス非公開(開発中)
    production : サービス公開中
    blocked : システム管理者による強制停止
    resetting : リセット中
    deleting : 削除中
    deleted : 削除済み
    failure : 登録失敗
```

### リソース管理とメタ情報

 **リソースの取得と階層表現**

vte.cxにはリソースを階層で管理するドキュメント型データベースがあります。
リソースは/dから始まるエンドポイントURLにマッピングされ、REST APIによりエンドポイントURLで指定されたリソースに対して読み書きできます。 例えば、``GET /d/foo``を実行するとfooというリソースを取得できます。リソースはHTMLやJavaScriptなどのテキスト情報の他、写真や動画などのBlobコンテンツなどを扱えます。
また、``/d/foo``をフォルダに見立てて、その配下にリソースを格納することもできます。例えば、``/d/foo/bar``は、fooの配下にあるbarというリソースを意味します。``GET /d/foo?f``というように、fパラメータをつけることで、配下にあるリソースの一覧を取得することができます。エントリが存在しない場合、HTTPステータスコード204を返却します。

**リソースの登録更新**

例えば、``PUT /d/foo?_content``により、fooに対して、テキストデータやblobデータを格納することができます。PUTの際、ペイロード(リクエストのBody)にはデータをraw形式で入れてください。最大サイズは100MiBです。また、Content-Typeには、multipart/form-dataではなく、image/pngやtext/htmlなど実際に格納したコンテンツに合ったタイプを指定してください。
?_contentパラメータを付けない場合、メタ情報の登録更新になります。(メタ情報については「メタ情報とATOM表現」を参照してください。
　また、[FormData](https://developer.mozilla.org/ja/docs/Web/API/FormData)オブジェクトを使って画像やCSVファイルをアップロードすることができます。詳しくは、「ファイルアップロード」を参照してください。
　PUTはCSRF対策のためXHR通信(XMLHttpRequest)からのみ受け付けるようにしています。つまり、リクエストヘッダに「X-Requested-With: XMLHttpRequest」が設定されていなければエラーを返却します。(ステータス417)

**メタ情報とATOM表現**

``GET /d/foo?e``とeパラメータを付与することで、fooのメタ情報を取得できます。エントリが存在しない場合、HTTPステータスコード204を返却します。

以下はメタ情報をJSONで取得したものです。（XMLの属性であるhrefやrelには``___href``のように``___``が先頭に付くので注意してください。）

http://vtecxblank.vte.cx/d/foo?e

```json
{
    "feed": {
        "entry": [
            {
                "sample": "テスト内容",
                "author": [
                    {
                        "uri": "urn:vte.cx:created:209"
                    }
                ],
                "id": "/foo,1",
                "link": [
                    {
                        "___href": "/foo",
                        "___rel": "self"
                    }
                ],
                "published": "2018-06-26T12:29:37.947+09:00",
                "updated": "2018-06-26T12:29:37.947+09:00"
            }
        ]
    }
}
```
メタ情報はATOM([RFC4287](http://www.ietf.org/rfc/rfc4287.txt))によって表現されます。ATOMは少々古いものではありますが、拡張性が高く、メタ情報の流通ためのフォーマットとして広く使われていることから、vte.cxでも採用しています。

vte.cxにおけるATOMの各項目は以下のような意味をもちます。

 * entryはリソースの最小単位であり、1件のリソースが1エントリになります。
 * feedの中に複数のentryがリスト形式で入る形になります。
 * ATOM項目のうち、author、id、published、updatedなどは自動で値がセットされますが、title、subtitle、summary等はユーザによって自由にセット可能です。
    * author : uri属性に「urn:vte.cx:updated:{uid}」が代入される。(uidは更新者のユーザ識別子)
    * id:キーとリビジョンを組み合わせた形でシステムで一意となる(後述)
    * published、updated : 作成(更新)日時を「``yyyy-MM-dd'T'hh:mm:ss.SSS+99:99``」(ISO8601拡張)形式で代入される
 * contentはコンテンツ(リソース本体)の格納場所として使われます。
 * linkは別名(alias)や外部コンテンツのキー(以下の説明を参照)として使われます。
 * contributorはアクセス権限(ACL)を管理するために使われます。
 * rightsは設定情報などで使われます。この項目は暗号化されます。

また、以下のように、エントリの基本構造としてidとキーがある点に注意してください。

 * エントリは一つの``<id>``タグを持ちます。これは、システム全体で一意となるように、キーとリビジョンを組み合わせた形をしています。(上記エントリのidは、``/foo,1``) また、リビジョンはエントリの更新回数です。
    * idが同じということはそのエントリのデータが完全に同一であることを意味します。
    * エントリを登録すると、``<id>``タグに値を自動的に登録します。
    * エントリ更新時、idが存在すると楽観的排他チェックを行います。つまり、更新前の元のリビジョンと比較して値が異なれば既に更新されているとみなされ楽観的排他エラーとなります。ただし、更新するエントリにidが含まれていない場合は楽観的排他チェックを行いません。
    * エントリ削除時、指定されたリビジョンと更新前のものを比較することで楽観的排他チェックを行います。リビジョン指定が省略された場合はチェックを行いません。
 * ``<link>``タグのrel="self"属性のhref属性がエントリの参照を表すキーです。
    * キーは「/path/to」のようなパス形式となります。階層は1000階層まで持つことができ、子は無限に持つことができます。
    * キーには基本的に取得した際に指定したURLと同じものになりますが、別名キー(alias)で取得するとidとは異なるキーの値となります。
    * キーに含まれる名前には英数字と$、_(アンダースコア)が使えます。

上記のATOM項目以外の項目をエントリスキーマに記述することでユーザ独自の項目を定義することができます。上記の例では、sampleがユーザ定義項目になります。
詳細は、「エントリスキーマとユーザ定義項目」で説明します。

**別名(alias)**

エントリに``link rel=alternate``タグを付与することで別のURLにマッピングすることが可能です。これを別名(alias)と呼んでいます。LinuxのシンボリックリンクやWindowsのショートカットフォルダのような機能です。
例えば、ECサイトのカテゴリ表示において、家電/パソコン/アンドロイドや、家電/スマートフォン/アンドロイドといったように、「アンドロイド」を異なるカテゴリに関連付けしたい場合があります。
これを実現するのが別名(alias)で、「アンドロイド」は一つのエントリであるにもかかわらず複数のカテゴリのパスから参照できるようになります。
これは、上位階層が変わったときでも、aliasの書き換えだけで下位のエントリは変更しないで済むという利点があります。
また、子要素が存在するフォルダにaliasを設定するとaliasのパスからもその子要素にアクセスできるようになります。

以下のエントリは実体が /foo/android で別名(alias)が /bar/android になります。

```json
{
    "feed": {
        "entry": [
            {
                "id": "/foo/android,1",
                "link": [
                    {
                        "___href": "/foo/android",
                        "___rel": "self"
                    },
                    {
                        "___href": "/bar/android",
                        "___rel": "alternate"
                    }
                ]
            }
        ]
    }
}
```

以下のようにリクエストすることでaliasを追加できます。
aliasは1つのエントリにつき最大10個まで追加することができます。

``PUT /d?_addalias`` 

```xml
<feed>
  <entry>
    <link rel="self" href="{キー}" />
    <link rel="alternate" href="{alias}" />
  </entry>
</feed>
```
また、PUT /d?_removealias で指定したaliasを削除できます。

**メタ情報の形式をリクエストで指定する**

メタ情報は、JSONの他にもXMLやMessagePackなどの様々な形式に変換して取りだすことができます。例えば、``GET /d/foo?e&x``とxパラメータを付与することでXMLとして取得でき、``GET /d/foo?e&m``などmパラメータでMessagePackとして取得できます。どのような形式に変換されても項目の構造は同じ形になります。
パラメータの指定がない場合、デフォルトはJSON形式です。
ブラウザに表示する際は、JSONやMessagePackではなくXMLをおすすめします。なお、JSONについてはセキュリティー上の理由でブラウザに直接表示できないようにしています。（JSONPの禁止）
また、POSTやPUTのペイロード(リクエストのBody)はraw形式のみ許可しています。送信する際はXHR通信(XMLHttpRequest)である必要があります。フォームデータ(HTML Form)送信はCSRF対策のため受け付けられないようにしています。

リクエストパラメータの種類とContent-Typeは以下の通りです。
リクエストパラメータの予約語は１文字の英字、もしくは先頭``_``(アンダースコア)で始まる文字列になります。これ以外のもの、つまり２文字以上で``_``で始まらない文字列についてはユーザのアプリケーションが自由に使えるパラメータです。

 * x : XML文字列 (Content-Type : text/xml)
 * m : MessagePack形式バイナリをDeflate圧縮 (Content-Type : application/x-msgpack)
     * リクエストヘッダに「Content-Encoding: deflate」があればリクエストデータは圧縮されていることを示します。
     * リクエストヘッダに「Accept-Encoding: deflate」があればレスポンスデータを圧縮します。
 * 上記パラメータが無い場合はJSON文字列 (Content-Type : application/json)
     * JSON文字列返却はXHR通信からのみ受け付けるようにしているため、リクエストヘッダに「X-Requested-With: XMLHttpRequest」が設定されていなければエラーを返却します。(ステータス417)

以下は、/foo配下をxmlで取得した例です。
http://vtecxblank.vte.cx/d/foo?f&x

```xml
<?xml version="1.0" encoding="UTF-8" ?>
<feed>
    <entry>
        <sample>テスト内容</sample>
        <author>
            <uri>urn:vte.cx:created:209</uri>
        </author>
        <id>/foo/bar,1</id>
        <link href="/foo/bar" rel="self"/>
        <published>2018-06-26T12:29:37.947+09:00</published>
        <updated>2018-06-26T12:29:37.947+09:00</updated>
    </entry>
</feed>
```
### エントリスキーマとユーザ定義項目

ATOM項目以外の項目をエントリスキーマに記述することでユーザ独自の項目を定義することができます。実際に業務アプリケーションを作成する場合にはこのユーザ定義項目をいかに定義していくかが鍵になります。
　エントリスキーマにユーザ項目を定義するには、管理画面の「エントリスキーマ管理」で行うか、プロジェクトの``/setup/_settings/template.xml``ファイルを直接編集し、項目名などを記述することでできます。（管理画面で編集を行った後は、必ずプロジェクトのtemplate.xmlファイルも更新するようにしてください）
　システム運用中でもファイルの更新は可能であり、項目を追加して更新すると直ぐにシステムに反映されます。ただし、追加項目は常に要素の最後尾に追記する必要があります。途中の階層であっても同列の最後であれば項目を追加できます。

**エントリスキーマの記述ルール**

エントリスキーマには以下の記述ルールが適用されます。ルールを適用できるものは、項目の有無や親子関係、繰り返しといった構造の定義、数値や文字列、日付などの型の指定、必須チェックや最大/最小チェック、正規表現によるバリデーションチェックなどがあり、データ登録更新時にルールに合致しなければエラーとなります。

 * 項目名(型){多重度}!=正規表現 の形式で項目を記述します。
 * 項目名は２文字以上１２８文字以下の英数字および一部の記号(_や$)が使えます。ただし、数字で始まるものやハイフンは使用不可です。
 * インデントが下がると上記要素の子要素であることを示します。
 * 項目名()の中には型を指定します。型には、string,int,date,long,float,double,boolean,descがあります。
 * ()を省略した場合や前述した型以外が指定された場合はstringになります。型名は先頭は大文字小文字のどちらでも構いません。(case sensitiveではない)
 * string型に格納できる文字の最大サイズは10MiBです。これを超えるラージオブジェクトについては項目ではなくコンテンツとして扱ってください。
 * date型は以下の形式のものを受け付けます。
    * ``yyyy-MM-dd``
    * ``yyyy-MM-dd HH``
    * ``yyyy-MM-dd HH:mm``
    * ``yyyy-MM-dd HH:mm:ss``
    * ``yyyy-MM-dd HH:mm:ss.SSS``
    * 上記の"-"を"/"にしたもの
    * 上記の" "を"T"にしたもの
    * ``yyyyMMdd``
    * ``yyyyMMddHH``
    * ``yyyyMMddHHmm``
    * ``yyyyMMddHHmmss``
    * ``yyyyMMddHHmmssSSS``
    * 上記の各フォーマットについて、末尾にタイムゾーン([ISO 8601] +99:99、+9999、+99)を加えたもの
 * desc型は降順ソートを行うための項目となります。``{降順ソートしたい項目名}_desc(desc)``と指定してください。
    * 例えば、prop1_desc(desc)をエントリスキーマに定義するとprop1の値で降順ソートされます。
 * 項目名{} はmapを意味します。括弧の中は多重度を示し、省略すると1になります。
    * 多重度には子要素の最大繰り返し数を指定します。ただし、int型の場合は最大値、string型の場合は最大の長さになります。
    * また、~を用いることで{最小値~最大値}というように値の範囲を指定できます。
 * 項目名の最後に!を付けると必須項目となります。
    * 注意：!は型や繰り返しよりも後に記述します。=の直前です。
 * =に続けて正規表現を指定することでバリデーションを定義できます。指定した正規表現にマッチしないとエラーとなって入力を受け付けません。
    * 正規表現構文については、[Regex Pattern](http://docs.oracle.com/javase/jp/7/api/java/util/regex/Pattern.html)を参照してください。
    *  正規表現が指定されている項目はデータ登録時にバリデーションチェックを実行します。 
 * $によりXMLの属性やテキストノードを指定することができます。
    * 項目名の先頭が$のものはXMLにシリアライズしたときに属性となります。ただし、属性は同列の他の項目より先に記述する必要があります。
    * $$textを付けることでテキストノードとみなされ、子要素ではなく自身の要素に値を代入します。
 * title、subtitle、updatedといったATOM項目については、エントリスキーマに定義しなくても使えます。逆に定義済みであるATOM項目をユーザ定義項目として定義しようとすると重複エラーとなります。 

以下はエントリスキーマのサンプルです。``/_settings/template``エントリの``<content>``タグに以下を記述することでエントリスキーマを設定できます。

```
id
email
verified_email(Boolean) // Boolean型 
name
given_name
family_name
error
 errors{2} 　　　　　　// インデントでerrorの子要素。Mapで多重度は2
  domain
  reason
  message
  locationType
  location
 code(int){1~100}       // 1~100の範囲     
 message
subInfo
 favorite
  $attribute       // $で始まる項目はXMLの属性となる（項目の先頭に記述する）
  food!=^.{3}$  　　// !で必須項目を示す。もし{}や[]があればそれよりも後に記述する。 food[]!=xxx など
  music=^.{5}$    
 favorite2
  food
   food1
 favorite3
  food
 hobby{}
  $$text           // $$textはXMLのテキストノードになる
```

上記エントリスキーマを利用するサンプルアプリケーションでは以下のようなリクエストになります。リクエストでは、スキーマに定義されている項目のうち実際に必要なものだけが使用され、また、リクエストの第一階層の項目に値が代入されていないものは出現しない項目となります。
　例えば、このエンドポイントへのリクエストには、emailやfamily_name、subinfoなどの項目だけが存在する一方で、errorなどの項目はレスポンスだけに存在します。
　このように、アプリケーションを設計する際、どのエンドポイントでどの項目を使うかについてグルーピングの定義が必要です。vte.cxでは管理画面の「エンドポイント管理」を利用してこれらを定義することができます。

``POST /d/registration``

```json
{
    "feed": {
        "entry": [
            {
                "email": "email1", 
                "family_name": "管理者Y", 
                "given_name": "X", 
                "name": "管理者", 
                "subInfo": {
                    "favorite": {
                        "food": "カレー", 
                        "music": "ポップス1"
                    }
                }, 
                "verified_email": false
            }
        ]
    }
}
```

レスポンス

```json
{
    "feed": {
        "entry": [
            {
                "error": {
                    "code": 400, 
                    "errors": [
                        {
                            "domain": "vte.cx", 
                            "location": "Authorization", 
                            "locationType": "header", 
                            "message": "invalid header", 
                            "reason": "invalidAuthentication"
                        }
                    ], 
                    "message": "Syntax Error"
                }
            }
        ]
    }
}
```

### REST APIによるリソースの操作

**リクエストパラメータの種類**

``GET /d/foo?f``というように、fパラメータをつけることで、配下にあるエントリの一覧を取得することができます。取得可能なエントリの最大件数は``?l={件数}``パラメータで指定できます。（デフォルトは100件）
``?l=*``ですべてのエントリを取得できます。また、?cで配下のエントリ件数を取得できます。

* f：配下のエントリ一覧を取得
* l：取得可能なエントリの最大件数(*ですべて）
* c：配下のエントリ件数を取得
* p：カーソル文字列

**カーソル文字列とフェッチリミッター機能**

検索対象のデータが大量にある場合、ページネーションのように、１回のGETで取得するより複数のページにわけて検索する方がユーザ体験がよくなることがあります。ページネーションでは、取得可能なエントリの最大件数を絞って複数回検索することになります。続きのデータが存在する場合、Feedのlink rel=nextタグのhref属性にカーソル文字列が自動的に設定されますので、?pにそのカーソル文字列を指定することで次ページを検索することができます。
　カーソル文字列が設定されるケースは、lパラメータで指定した件数を超えた場合とフェッチリミッター機能の上限に達した場合の２通りあります。
    フェッチリミッター機能とは、サーバ負荷軽減のため検索条件検索（含む件数取得）や検索条件付きページネションにおいて一定の件数(50,000件)を検索（fetch）後に一旦終了するようにしている制御機能です。
    検索件数上限に達すると、戻り値に検索したところまでのカーソル文字列を設定し、ステータス206(Partial Content)を返却します。
    クライアントにおいて「p=カーソル文字列」の形でクエリ文字列に付加してリクエストすると、続きのページを取得できます。
なお、?l=*で検索した場合はフェッチリミッター機能は無視され、すべての検索が完了するまで実行されます。

**ページネーション**

?lパラメータで指定した件数だけ取得した後、さらに次に続くデータを検索したい場合にページ数を指定して検索できると便利です。これをページネーション機能といいます。
　ページネーション機能では、``GET {Key}?{検索条件}&n={ページ数}``をリクエストすることで、指定されたページ数のデータを取得できます。また、l={件数}パラメータを指定することで、1ページあたりの件数を指定できます。
　ただし、ページネーション機能を使うにはあらかじめカーソル一覧(pageindex)を作成しておく必要があります。以下のリクエストを実行することでカーソル一覧がセッションに保持されます。検索時、指定したページ数のカーソル一覧がセッションに存在しない場合は400エラーとなり、「Please make a pagination index in advance. 」のメッセージが返ります。また、検索した結果、最終ページ数に満たない場合は実際に張った最終ページ数が返ります。

 * GET {Key}?_pagination={最終ページ数}&l={件数}
 * GET {Key}?_pagination={開始ページ数},{最終ページ数}&l={件数}

大量のカーソル一覧(pageindex)を張るのは時間がかかるため、最初のリクエストは最終ページ数を50ぐらいにとどめておくのがおすすめです。(フェッチの最大実行回数である5万件を超える件数を検索した場合には206(Partial Content)が返ります）
　まず50ページ分を貼り、一旦画面に表示させたうえで、さらに続きを表示させる必要があれば、次の50〜100ページを張るようにします。ただし、開始ページ数を指定する場合、セッションに「開始ページ数-1ページ」のカーソルが登録されていなければエラーになりますので、開始ページ数には必ず前回実行した最終ページを指定するようにしてください。
　githubの[ブランクプロジェクト](https://github.com/reflexworks/vtecxblank)にページネーションのコンポーネントとチュートリアル(tutorial_2.html)を置いていますので詳しくはそちらをご覧ください。ページネーションコンポーネントを利用することで複雑なページネーションを簡単に実装できるようになります。

**条件検索**

以下のように、URLパラメータに条件を指定することで絞り込み検索ができます。条件は「項目名=値」の形、もしくは、「項目名-記号-値」の形で指定します。項目名には、テンプレートの階層を"."でつないだ名称を指定します。
値はJavaScriptのencodeURIComponent()関数などで必ずエンコードしてください。値にもし&が含まれていると区切り文字と認識され、値が分割されて誤動作してしまいますのでご注意ください。具体的な条件検索の例についてはvtecxblankのチュートリアル(tutorial_2.html)に載せていますのでこちらもご覧ください。

文法

```
https://{サービス名}.vte.cx/{Key}?f&{name}{=|-eq-|-lt-|-le-|-gt-|-ge-|-ne-|-rg-|-fm-|-bm-}{value}&{name}{=|-eq-|-lt-|-le-|-gt-|-ge-|-ne-|-rg-|-fm-|-bm-}{value}&...&l={n}&p={カーソル文字列}&s={ソート項目名}
```

例:好きな食べ物がeggで価格が5000以下を昇順で最大件数20件取得

```
https://{サービス名}.vte.cx/{Key}?f&subInfo.favorite.food-eq-egg&price-lt-5000&l=20&s=price
```

記号の種類と意味は以下の通りです。

 * eq : = (等しい)
 * lt : < (未満)
 * le : <= (以下)
 * gt : > (より大きい)
 * ge : >= (以上)
 * ne : != (等しくない)
 * rg : regex (正規表現に合致する)
 * fm:前方一致
 * bm:後方一致

前方一致では、指定した文字の先頭文字が一致する条件となり、後方一致では、末尾文字が一致する条件になります。SQLで例えると、``-fm-田``は、``LIKE '%田'``に相当し、``-bm-町``は、``LIKE '町%'``に相当します。
あいまい検索を実行するには正規表現を使います。例えば、``-rg-.*田町.*``は、``LIKE '%田町%'`に相当します。

また、Keyに``*``を付けることで前方一致検索が可能です。例えば、"GET /men/to*"を実行すると、"/men/tokyo"、"/men/tops" などのエントリを検索できます。

**Indexの設定**

条件検索はメモリ内で実行しますが、データ量が多いとパフォーマンス悪化を招きます。そのような場合、Indexを設定することで、パフォーマンスを向上させることができます。
ただし、Index検索を適用できるのは検索条件の最初の項目(一番左の項目)のみとなります。２番目以降の項目についてはIndex検索で絞り込み後にメモリ内で検索を実行します。例えば、``?foo=123&bar=456``という条件で検索した場合、fooにIndex設定があればIndex検索して絞り込みを行い、それからbarの検索をインメモリで実行します。
　また、Index検索の条件は完全一致か前方一致である必要があり、暗号化項目は指定できません。(暗号化項目については以下を参照)２つ目以降の検索条件についてはあいまい検索や暗号化項目の検索が可能です。
　Indexは検索パフォーマンスを向上させる一方、書込パフォーマンスを悪化させるため、Index設定は必要最低限にすべきです。

　Indexの設定はテンプレートで行います。テンプレートとは、(/_settings/template)エントリのことで、Index設定はtemplateエントリのrightsタグに記述します。（管理画面のエントリスキーマ管理でも設定できます）
　以下のように、左辺の項目名に続けて:(コロン)の後に正規表現を記述することでIndexを設定できます。また、右辺に登録するURIのうち正規表現にマッチするものをIndexとして登録します。URIにはエントリの実体(self)や別名(alias)などを指定します。
　以下は、subInfo.favorite.food項目を/masterまたは/dataから検索した際にIndex検索となる設定例です。

```
subInfo.favorite.food:/master|/data　
```
Indexは最大200個まで設定できます。

**ソートの指定**

?s={項目}パラメータを指定すると、その項目は昇順でソートされます。
ただし、ソート指定する項目はあらかじめIndexの設定が必要です。
index項目を検索する場合、その項目は昇順ソートされているため、ソート指定は不要です。
ソート指定をした項目がnullの場合はIndexデータが存在しないため検索できませんので注意してください。また、既存の項目をあらたにIndex設定した場合はデータを再登録する必要があります。

降順ソートを行いたい場合、検索項目とは別に降順用の項目(desc型)を追加する必要があります。desc型はLongの最大値から項目の値を引いたもので、条件指定でこの項目を指定すると降順となります。例えば、prop1_desc(desc)をエントリスキーマに定義するとprop1の値で降順ソートされます。内部の値は . , / - を除いた数字に変換され、Long.MAX_VALUE - {値}をセットします。
desc型は降順ソートを目的としたもので、実際の値を取得することはできません。GETしてもエントリの中にその項目は現れません。

**nometaオプションとデータのバックアップ**

``_nometa``オプションを付けてGET実行すると、id、author、published、updatedがついていないエントリが返ります。これは、データのバックアップを目的にしたもので、取得したFeedを``PUT /d/?_bulkserial``実行によりデータをリストアすることができます。(``?_bulkserial``オプションについては、「トランザクション処理と大量更新」を参照してください)
 ``l=*``と組み合わせて、``GET /d/{キー}?f&_nometa&l=*``と指定することでキー配下の全件のエントリを取得できます。

**サーバの現在時刻の取得**

``GET /{d|s}?_now``でサーバの現在時刻を取得できます。

**POSTによるエントリの登録**

POSTにより複数のエントリを登録できます。(1回のPUTで最大25件まで)
エントリのlink rel=selfタグのhref属性にキーを指定してPOSTを実行すると指定したキーで登録されます。以下を実行すると``/foo/bar``エントリが登録されます。ただし、親フォルダである``/foo``は存在していなければなりません。
登録に成功すると、HTTPステータス201が返ります。エラーの場合はFeed.titleタグにエラーメッセージが返ります。

``POST /d``

```xml
<entry>
  ・・・
  <link href="/foo/bar" rel="self"/>     <!-- link rel="href" にKeyを指定します -->
</entry>
```

link rel=selfタグを省略して実行した場合、キーには自動的にユニークな番号が採番されて登録されます。ただし、URLに親階層フォルダ(/foo)を指定する必要があります。

``POST /d/foo``

```xml
<entry>
  ・・・       <!-- link rel=self は設定しません -->
</entry>
```

**PUTによるエントリの更新**

PUTにより複数のエントリを同時に登録更新できます。(最大25件まで)
このとき、登録されるエンドポイントはエントリのキー(link rel=selfタグのhref属性)の値になります。更新されると、HTTPステータスが200(登録時は201)で、メッセージ``feed.title:"Updated."``を返します。
　楽観的排他チェックを行う場合はidを指定してください。(GETで取得したidをそのまま指定してください。+1する必要はありません。)
楽観的排他チェックでは、指定したidのエントリがデータベースに存在するかどうかのチェックを行います。更新が成功すると自動的にidがインクリメントされます。
　idを省略して更新すると楽観的排他チェックは行われず強制上書きとなります。また、PUTは登録も可能であり、既存のデータが存在しない場合は更新ではなく新規登録となります。登録では基本的にPOSTを使いますが一度に25件以上の登録ができないため、実はPOSTを使う意味はほとんどありません。
また、PUTは部分更新であり以下のような注意点があります。

 * 第一階層の項目に値が代入されているものを丸ごと置き換えます。
    * 第二階層以下の項目が省略されている場合は更新されないことに注意してください。上書きで消したい場合はその項目に空白を代入する必要があります。
  * linkやcontributorタグに関しては以下のように処理します。
     * link : rel属性の単位で設定されているものを置き換えます。
     * contributor : uriタグの"urn:vte.cx:"の次の項目(aclやusersecret)ごとに更新します。

**トランザクション処理と大量更新**

POSTやPUTによりFeed更新する際に1トランザクションで実行できるエントリは25件までです。25件を超える大量のエントリを更新するには、``PUT /d/?_bulk``等を使って実行する必要があります。ただし、このとき内部では25件づつ分割して実行していますので、一貫性が担保されるわけではないことに注意してください。

PUTによる大量更新では、以下のように、並列/直列、同期/非同期の４種類の実行方法があります。直列実行では、先頭のエントリから25件づつ実行し完了をまってから次の25件を実行します。

 * PUT /d/?_bulk リクエストデータにFeed (並列同期処理)
 * PUT /d/?_bulk&_async リクエストデータにFeed (並列非同期処理)
 * PUT /d/?_bulkserial リクエストデータにFeed (直列同期処理)
 * PUT /d/?_bulkserial&_async リクエストデータにFeed (直列非同期処理)

**DELETEによるリソースの削除**

DELETE によりリソースを削除できます。
例えば、``DELETE /d/foo``を実行すると、fooのリソース(コンテンツとメタデータ）を削除します。このとき、/foo配下にエントリが存在すると「Can't delete for the child entries exist.」エラーとなります。その場合、``DELETE /d/foo?_rf``を実行すると配下のエントリを含めて削除することができます。
``DELETE /d/foo?r=1``のように、rパラメータでリビジョンを指定すると楽観的排他チェックを行います。

**トランザクション内でDELETEを実行する方法**

PUTにより複数のエントリを１トランザクションで登録更新できますが、以下の方法で登録更新だけでなく削除も１トランザクション内で実行できます。

``PUTするエントリのうち以下のものが削除される``

* エントリのidの後に``?delete``を指定（楽観的排他チェックが実行される）
  * 例) /folder1/item,3?_delete
* idに?deleteのみ指定する（楽観的排他チェックは実行されない）
  * 例) ?_delete

**レスポンスの形式**

リソースに対してGETを実行するとコンテンツが返ります。ただし、高速化のためETagによるレスポンスコントロールを行っています。つまり、前回参照したコンテンツに更新がなければ、304 Not Modifiedステータスを返します。

?eや?fでメタデータを検索した場合はfeedが返ります。
POSTやPUTなどの場合は基本的なレスポンスの形式は以下のような形になります。

```xml
<feed>
  <title>{メッセージ}</title>
</feed>
```

エラーの場合、HTTPステータスのみが返る場合があります。
詳しくは、「HTTPステータスコードとメッセージ」を参照してください。

**採番カウンタ**

リソースやメタ情報とは別に採番カウンタを持つことができます。これは、在庫数などの管理や連番が必要なケースで使うことを想定しています。

以下のようなメソッドが用意されています。

 * allocids
    * PUT /d/?_allocids={採番数}を実行することで採番処理を行います。
        * 指定された採番数だけ番号を採番します。カウンタ値はエンドポイント(キー)ごとに管理します。
        * 採番数にはマイナスの数値を指定することが可能です。
    * GET /d/?_allocidsもしくは、PUT /d/?_allocidsで採番数に0が指定された場合、現在のカウンタ値を返却します。
    * 採番数の代わりに"setting"文字列が指定された場合、現在設定されている採番枠を返却します。
        * 戻り値はFeed形式で、titleに採番された値を設定します。採番された値をすべて返却します。複数の場合はカンマで区切られます。
    * エラーの場合はFeed.titleにエラーメッセージが返ります。
 * setids
    * PUT /d/?_setids={設定値}パラメータを指定することで採番カウンタに設定値をセットします。
    * エラーの場合はFeed.titleにエラーメッセージが返ります。
 * addids
    * PUT /d/?_addids={加算数}を実行することで採番カウンタの加算処理を行います。
        * パラメータで指定した加算数だけ採番の値をプラスしてその値を返します。
        * 戻り値はFeed形式で、titleに加算後の現在値を設定します。
    * 加算数にはマイナスの値を指定することが可能です。
    * GET /d/?_allocidsを実行すると現在の採番カウンタの値を返します。
    * エラーの場合はFeed.titleにエラーメッセージが返ります。
 * rangeids
    * POST /d/?_rangeids で、bodyに{start}-{end}の形式を指定して実行すると採番範囲を設定できます。
        * 採番範囲を1000から2000、つまり、初期値が1000で、2000を超えたら1000に戻るような設定にするには、bodyに``1000-2000`` を入れます。
        * さらに先頭に"A"を付けたい場合は、bodyに ``1000-2000,A``を入れます。
    * 戻り値はFeed.titleに``Put allocids.``が返ります。
    * エラーの場合はFeed.titleにエラーメッセージが返ります。

### ユーザ、グループ管理とアクセスコントロール

**メールアドレスとユーザアカウントの違い**

vte.cxではユーザ登録の際にメール認証によって本人確認を行うため、メールアドレスが必要になります。また、登録で使用したメールアドレスがログインIDとなります。
　メールアドレスの表記は[valid e-mail address](http://www.w3.org/TR/html5/forms.html#valid-e-mail-address)に従います。
しかし、メールアドレスは１つで複数の受信が可能となるような記述方法があるため一意にすることはできません。
　例えば、Gmailでは+以降@までを無視して送信しますが受信では無視しません。``foo+1@gmail.com``は``foo@gmail.com``で送信し、``foo+1@gmail.com``で受信します。また、ピリオド(.)も無視するため、``foo@gmail.com`` と ``f.oo@gmail.com`` は、Gmail 上では同じものとみなされます。
　vte.cxでは上記のGmailのルールに合わせる形を取っており、メールアドレスとは別のアカウント管理を行っています。それはセキュリティ上の理由からで、ピリオドなどを無視する仕様が、Gmail アドレスを登録する Web サービス側の仕様と組み合わさることによってフィッシングのリスクを増加させるという指摘がなされているからです。[詳細](https://hyper-text.org/archives/2018/04/phishing_risk_gmail_dots_do_matter.shtml)

**ユーザアカウントの仕様**

 * 入力されたメールアドレスを変換(.や+などの使用不可文字を削除)することでシステムで一意なユーザアカウントとします。
 * ユーザアカウントには英数字、ハイフン(-)、アンダースコア(_)、@、$、ドット(.)が使用可能です。（その他の文字は使用不可）
 * ユーザアカウントはuid(ユーザ識別番号)と関連づけられます。uidはシステムで自動的に振られる一意の連番です。

**ユーザエントリ**
 
* ユーザは各サービスの``/_user/{uid}``エントリで管理されます。これをユーザエントリといいます。
    * contributor.emailにオリジナルのメールアドレス
    * summaryにユーザの状態(ステータス)
    * titleにユーザアカウント
    * subtitleにニックネーム

``GET /d/?_whoami&x``を実行すると現在ログインしているユーザエントリが返ります。(ログインしていない場合、HTTPステータスコード401を返却します。)

```xml
<feed>
<entry>
  <contributor>
    <email>foo.bar@vte.cx</email>
  </contributor>
  <id>/_user/216,2</id>
  <link href="/_user/216" rel="self" />
  <summary>Activated</summary>
  <title>foobar@vte.cx</title>
  <subtitle>nickname</subtitle>
  <published>2018-06-09T11:08:38.122+09:00</published>
  <updated>2018-06-09T13:38:15.360+09:00</updated>
</entry>
</feed>
```

ユーザステータスには以下の種類があります。

 * 登録なし: Nothing
 * 仮登録：Interim
 * 本登録：Activated
 * 無効：Revoked

ユーザ登録画面から仮登録を実行するとステータスがInterimになりユーザに確認メールが送信されます。メール内のリンクをクリックすることで本人認証がなされステータスが本登録(Activated)に変わります。Activatedになることではじめてサービスを使うことができるようになります。
　また、ユーザ管理者によってユーザを追加することもできます。詳しくは、「管理者によるユーザ登録」を参照してください。
　ユーザ管理者は``PUT /d/?_revokeuser={ユーザアカウント}``を実行することでアカウントを一時的に無効(Revokded)にすることができます。有効にするには、``PUT /d/?_activateuser={ユーザアカウント} ``を実行します。

**uid検索**

``GET /d/?_uid``を実行するとログイン中のuidを返却します。戻り値にはfeedのtitleにuidがセットされます。ログインしていない場合、HTTPステータス401を返却します。
　また、``GET /d/?_uid={ユーザアカウント}``により、ユーザアカウントからuidを検索します。指定されたユーザアカウントがサービスを利用していない場合は-1を返却します。

**ユーザアカウント検索**

``GET /d/?_account``を実行するとログイン中のユーザアカウントを返却します。戻り値にはfeedのtitleにユーザアカウントがセットされます。ログインしていない場合はHTTPステータス401を返却します。

**ログインとログアウト**

ログイン画面からログインIDとパスワードを入力して実行することでログイン認証が行われセッションを開始します。このとき、ログイン画面からは``GET /d/?_login``が実行されます。また、パスワードはハッシュ化され、ワンタイムトークン(WSSE)としてリクエストされます。２回のログイン認証に失敗するとreCAPTCHAによる認証が必要になります。ログインロジックの詳細については、[vtecxblankプロジェクト](https://github.com/reflexworks/vtecxblank/)のログイン画面のソースを参照してください。ユーザアプリケーション作成の際にはこのログイン画面をカストマイズするとよいでしょう。
　``GET /d/?_logout``が実行されるとログアウトされセッションが破棄されます。

**ユーザ仮登録**

ユーザ仮登録は``POST /d/?_adduser``で以下のエントリを登録すると実行されます。
機械的に実行されることを防ぐためreCAPTCHAが要求されます。

```xml
<feed>
  <entry>
    <contributor>
      <uri>urn:vte.cx:auth:{ユーザアカウント},{パスワード}</uri>
      <name>{ニックネーム}</name>
    </contributor>
    <title>{メールのタイトル(省略可)}</title>
    <summary>
    {テキストメール本文(省略可)}
    </summary>
    <content>
   {HTMLメール本文(省略可)}
    </content>
  </entry>   
</feed>
```
上記を実行すると、summaryタグもしくは、contentタグに記述された文章とタイトル(titleタグ)がメール送信されます。contentにはHTMLメール(任意)を、summaryにはテキストメール(必須)を記述してください。HTMLメールを表示できない場合はテキストメールになります。
　メール送信内容は、以下のように``/_settings/adduser``エントリにも記述できます。上記エントリのsummary、content、titleが省略された場合は、``/_settings/adduser``エントリの内容でメール送信します。上記エントリのメール送信内容が存在せず、かつ、``/_settings/adduser``エントリーも存在しない場合、``?_adduser``がリクエストされるとHTTPステータス412(Precondition Failed)が返ります。
  また、メール送信するには、``/_settings/properties``にメール送信設定が記述されている必要があります。詳しくは、各種設定情報の「プロパティ情報」を参照してください。

```xml
<entry>
  <link rel="self" href="/_settings/adduser" />
  <title>{メールのタイトル}</title>
  <summary>
{テキストメール本文}
  </summary>
  <content>
{HTMLメール本文}
</content>
</entry>
```

メールの本文には、ワンタイムトークン(RXID)が付いたリンクが含まれます。
ユーザがこのリンクをクリックするまでは仮登録となります。

初期フォルダ作成

　また、以下のようにadduserinfoエントリのsummaryタグにフォルダのキーを設定することで、最初にログインしたタイミングで自動的にフォルダが作成されます。
キー情報の"#"部分は登録時のユーザ(uid)で置換されます。

```xml
<entry>
  <link rel="self" href="/_settings/adduserinfo" />
  <summary>
{ユーザ登録時に作成するフォルダのキー}
/#/aaaa
/#/bbbb
....
  </summary>
</entry>
```

vtecxblankプロジェクトには、ユーザ仮登録の画面が用意されています。ユーザアプリケーション作成の際にはこの画面をカストマイズするとよいでしょう。

**パスワードリセット**

パスワードリセットは``POST /d/?_passreset``で以下のエントリを登録することで実行されます。機械的に実行されることを防ぐためreCAPTCHAが要求されます。

```xml
<feed>
  <entry>
    <contributor>
      <uri>urn:vte.cx:auth:{ユーザアカウント}</uri>
    </contributor>
    <title>{メールのタイトル(省略可)}</title>
    <summary>
    {テキストメール本文(省略可)}
    </summary>
    <content>
   {HTMLメール本文(省略可)}
    </content>
  </entry>
</feed>
```

上記を実行すると、summaryタグ(テキストメール)もしくは、contentタグ(HTMLメール)に記述された文章がメール送信されます。メール送信内容は、以下のように``/_settings/passreset``エントリにも記述できます。上記エントリのsummary、content、titleが省略された場合は、``/_settings/passreset``エントリの内容でメール送信します。
　メールにはワンタイムトークン(RXID)が付いたリンクが本文に含まれます。
ユーザがこのリンクをクリックすることでパスワードを再設定します。
　上記エントリにメール送信情報がなく、``/_settings/passreset``エントリーにも存在しない場合、``?_passreset``がリクエストされるとHTTPステータス412(Precondition Failed)が返ります。

　vtecxblankプロジェクトには、パスワードリセットの画面が用意されています。ユーザアプリケーション作成の際にはこの画面をカストマイズするとよいでしょう。

**パスワード変更**

パスワード変更は``PUT /d/?_changephash``で以下のエントリを更新することで実行されます。ログイン中のアカウントに対して実行するため、reCAPTCHAは要求されません。

```xml
<feed>
  <entry>
    <contributor>
      <uri>urn:vte.cx:auth:,{パスワード}</uri>
    </contributor>
  </entry>
</feed>
```

vtecxblankプロジェクトには、パスワード変更の画面が用意されています。ユーザアプリケーション作成の際にはこの画面をカストマイズするとよいでしょう。

**管理者によるユーザ登録**

``?_adduserByAdmin``パラメータを付けて以下を実行することで新規ユーザを登録します。

* これはユーザ管理者のみが実行できます。
* 複数のエントリを指定することで同時に複数のユーザを登録できます。
* 本人認証（メールリンクをクリック）しなくても本登録になります。
* ユーザ登録時に``/_settings/adduserinfo``に設定した初期フォルダが作成されます。
* titleタグおよびsummary、contentにメール情報をセットすることで実行時にメールを送信することができます。
    * ただし、メール送信のための設定が/_settings/propertiesエントリに記述されている必要があります。詳しくは、各種設定情報の「プロパティ情報」を参照してください。
* titleタグおよびsummary、contentが省略されており、かつ、``/_settings/adduserByAdmin``エントリが登録されている場合、このエントリの内容でメールが送信されます。

POST|PUT /d?_adduserByAdmin

```xml
<feed>
   <entry>
     <contributor>
       <uri>urn:vte.cx:auth:{メールアドレス},{パスワード}</uri>
       <name>{ニックネーム}</name>
     </contributor>
     <title>メールのタイトル(任意)</title>
     <summary>テキストメール本文(任意)</summary>
     <content>HTMLメール本文(任意)</content>
   </entry>

   ...
 </feed>
```

**システムグループ**

システムグループは複数のユーザーの権限をまとめて管理するために、システムがあらかじめ定義しているグループです。
つまり、``/_group``フォルダ配下にグループエントリ、その配下にuidエントリを作成し、``/_user/{uid}/group``へのaliasを付与することで、ユーザ(uid)がシステムグループに参加したものとして認識されます。uidはグループを作成した本人のものが使われます。

例：サービス管理者権限のグループエントリ

```xml
<feed>
  <entry>
    <id>/_group/$admin/216,3</id>
    <link href="/_group/$admin/216" rel="self" />
    <link href="/_user/216/group/$admin" rel="alternate" />
    <published>2018-06-07T14:32:40.444+09:00</published>
    <updated>2018-06-09T11:08:47.328+09:00</updated>
  </entry>
</feed>
```

``/_group/$admin``グループに属することでサービス管理権限が付与されます。
　システムグループには以下のものがあります。
　(システムグループは``$admin``のように``$``で始まる名前のグループです）

 * サービス管理権限：``/_group/$admin``
    * サービスの作成、削除、アプリケーションログの参照やACLやindexなどの設定が可能
    * ``<contributor>``タグおよび、``<rights>``タグの内容を自由に編集可能
    * ``/_settings``フォルダにはサービス管理権限のACL(CRUD)が付与されている
 * ユーザ管理権限：``/_group/$useradmin``
    * ユーザの作成、削除、ユーザステータスの変更が可能
 * コンテンツ管理権限：``/_group/$content``
    * HTMLやJavaScript等のページやコンテンツの登録、削除が可能
    * ``<content>``タグのテキストノードの内容を自由に更新可能

**ユーザ作成グループ**

ユーザ作成グループはユーザが自由に定義できるグループで``/{uid}``配下にあるものです。
　``/{uid}/group/{グループ名}`` というエントリを作成することで、ユーザ作成グループを作ることができます。
　グループ配下にフォルダを作成することで共有フォルダを作成することができます。

**フォルダACL**

vte.cxでは、これまで述べたように、エンドポイントURLをフォルダに見立てて、その配下にリソースを格納できます。また、そのフォルダにACLを設定することで自身および配下のエントリについてアクセスコントロールを設定できます。
　ACLは以下のようにエントリのcontributorタグに指定します。特定のユーザ(uid)、ログイン済ユーザ(+)、すべてのユーザ(``*``)、あるいはグループ(GroupKey)を設定できます。

```xml
<contributor>
    <uri>urn:vte.cx:acl:{uid|*|+|GroupKey},{C|R|U|D|E|.|/}</uri>
</contributor>
```

ACLの種類 (複数指定可能だが「E」「.」「/」のみの指定は不可)

 * C : 登録処理を許可
 * R : 検索処理を許可
 * U : 更新処理を許可
 * D : 削除処理を許可
 * E : サーバサイドJavaScriptからのみデータ操作可でHTTP(S)からのデータ操作が不可となる。
 * . : Own このエントリのみ適用される。配下のエントリには適用されない。
 * / : Low このエントリより配下のものについて適用される。指定したエントリ自体には適用されない。
 * Own.Low/いずれも設定されていない場合は両方(./)が付いているとみなされる

権限のスコープ

 * 数字 : ログインしているユーザ(uid)が対象
    * 先頭と末尾にワイルドカード(``*``)を指定可能
 * ``*`` : ログインしていないユーザを含むすべてのユーザが対象
 * ``+`` : ログインしているすべてのユーザが対象
 * スラッシュ(/)で始まるもの : グループ(GroupKey)が対象

ACLの適用範囲

 * 自身の階層を含む上位階層のACLが適用される。
    * つまり、自身にはACL設定がなくても親フォルダ（さらに上も）に設定されていれば親のACLが有効になる。
 * 親フォルダにACLが設定されて、かつ、自身にもACLが設定されている場合、自身の設定が優先される。
    * 同様に、配下のエントリにACLの設定がある場合、上位階層で設定されたACLではなく配下のエントリのACLが有効となる。
 * 検索結果のうち参照権限がないエントリについては検索結果に含められない。
 * aliasのキーについてもACLが適用される。

ACLの追加と削除

以下をリクエストすることでACLを追加できます。

``PUT /d/?_addacl ``

```xml
<feed>
  <entry>
    <link rel="self" href="{キー}" />
    <contributor>
      <uri>urn:vte.cx:acl:{ACL対象},{権限}</uri>
    </contributor>
  </entry>
</feed>
```
また、``PUT /d/?_removeacl``で指定したACLを削除できます。 

**項目ACL**

``/_settings/template``エントリの<rights>タグに記述することで項目に対してACLを設定することができます。ACLにはユーザおよびグループの読込(R)または書込(W)権限を指定できます。
　以下のように、項目名に続けて=の後に、{uid|group}+{RW}の形式でACLを指定します。 ,(カンマ)で複数件指定できます。

例：ユーザ(uid3)とグループ(/grp1)の書込権限、および全員(*)に読込権限

```
subInfo.favorite.food=3+W,/grp1+W,/*+R 　
```

``/_settings/template``エントリのrightsタグにはIndexも指定できますが、以下のようにindexとACLを同時に記述することができます。``:``コロンの右辺がIndex、``=``の右辺がACLになります。

```
subInfo.favorite3.food:/[0-9]+/(self|alias)=1+W　　
```

**暗号化項目**

項目名に続けて#を付けると暗号化項目となります。Index項目と暗号化項目はどちらか一つを指定できます。 暗号化では、vte.cx内部で持っている秘密Key(usersecret)とidと組み合せてハッシュ化したものが実際の暗号化において使われます。 
　以下はcontributor.uriを暗号化する設定例です。

```
contributor.uri#　　　　　　　　　　　　　
```

　また、以下のように、暗号化と項目ACLは同時に設定できます。これは、rightsの暗号指定、かつ、自身と/_group/$adminグループのRW権限の付与を意味します。

```
rights#=@+RW,/_group/$admin+RW　　
```

### システムフォルダと各種設定

**システムフォルダ**

``_``から始まるフォルダをシステムフォルダといいます。システムフォルダはシステムが管理する特殊なフォルダであり以下の種類があります。

 * ``/_group``：システムグループフォルダ
    * システムグループを管理します。詳細は「システムグループ」を参照してください。
 * ``/_user``：ユーザフォルダ
    * 配下にユーザを管理します。詳細は「ユーザエントリ」を参照してください。
 * ``/_html``：htmlコンテンツフォルダ
    * 配下のエントリーはHTML、CSSやJavaScript、画像などのコンテンツを格納できます。Webサーバのrewrite機能により、``/_html`` は ``/`` にマッピングされます。
 * ``/_settings``：各種設定情報
    * 各種設定情報を管理します。詳細は「各種設定情報」を参照してください。
 * ``/_log``：ログ
    * 配下にシステムやアプリケーションによって出力されたログが降順(最新が先頭）で記録されます。
    * ログには、updated : 更新日時、title : タイトル、subtitle : サブタイトル、summary : 内容が書かれます。

**各種設定情報**

vte.cxの設定情報は以下のように/_settings配下で管理されます。これらは、直接編集もできますが、管理画面の「メール・詳細設定」でも設定できます。（推奨）

 * ``/_settings/properties``：プロパティ情報
 * ``/_settings/template``：エントリスキーマやIndex、項目ACLなど
 * ``/_settings/bigquery.json``：BigQuery用サービスアカウント秘密鍵ファイル
 * ``/_settings/adduser``：ユーザ登録時(?_adduser)に送られるメール本文
 * ``/_settings/passreset``：パスワードリセット時(?_passreset)に送られるメール本文
 
**プロパティ情報**

プロパティ情報(``/_settings/properties``)のrightsタグにおいて以下のような情報を設定できます。ここで設定されていないものについてはシステム内部で持つ設定情報がデフォルトで使用されます。RXIDの詳細については「認証キーとトークン」を参照してください。

システムに関する設定

```
_entry.number.default : エントリーGET時のデフォルト最大数 [100]
_rxid.minute : RXID(WSSE)有効時間(分) [120]
_rxid.counter.{連番}.{回数} : 同じRXIDを使用しても指定回数まで許可されるURLパターンを指定。
_session.minute : セッション有効時間 [30]
```

エラーページ設定

以下はエラーページの設定です。これは、アクセス時にエラーが発生した場合にリダイレクトするルールを設定します。
また、リダイレクトする際に、Cookieの「ERROR_STATUS」にHTTPステータスコードが、また「ERROR_MESSAGE」にエラーメッセージがセットされます。このCookieは10秒間だけ有効です。

```
_errorpage.{適用順}.{エラーページselfid}={PathInfoの正規表現} : エラー画面表示URLパターン(正規表現)
```

confirm.html表示中にエラーの場合はerror.htmlに遷移する。それ以外のエラーはlogin.htmlに遷移する設定例

```
_errorpage.1.error.html=^/_html/confirm.html$
_errorpage.2.login.html=^/_html/.*$
```

メール送信設定

以下はメール送信の設定です。これが設定されていないとユーザ登録においてメール送信ができないため本人確認ができません。

```
_mail.from.personal : EMailの送信元名
_mail.from : Emailのfrom
_mail.transport.protocol : smtpsなどのメール送信プロトコル
_mail.password : メール送信アカウントのパスワード
_mail.smtp.host : メール送信ホスト
_mail.smtp.port : メール送信ポート
_mail.smtp.auth : true/false
```

BigQuery接続設定

以下はBigQueryに接続するための設定です。
これとは別に、``/_settings/bigquery.json``に、サービスアカウントの秘密鍵JSONを登録する必要があります。

```
 _bigquery.projectid : プロジェクトID
 _bigquery.dataset : データセット名
 _bigquery.location : ロケーション(デフォルト値は asia-northeast1)
```

### 認証キーとトークン

**アクセストークン**

アクセストークンは時間制限なしの認証トークンであり、スマホアプリからの認証の他、サービスデプロイ時の認証トークンとしても使われます。

ログイン後、``GET /d/?_accesstoken`` で取得できます。 feed.titleにアクセストークンが入ったものが返ります。

```xml
<feed>
  <title>{Accesstoken}</title>
</feed>
```

リクエスト時にAccesstokenをリクエストヘッダに付与することで認証されます。
認証が成功してもログイン状態にはなりません。

```
Authorization : Token {Accesstoken}
```

**リンクトークン**

リンクトークンはアクセストークンと同様に時間制限なしの認証トークンですが、アクセスできるURLに制限が付けられています。
　これは、メールに含まれるURLリンクとして使うことを想定しています。送信するメールの本文に以下のように${LINK=...}を挿入することで、Linktokenを含むURLが自動的に組み立てられます。 

挿入文

```
${LINK=/setpass.html}&value=abc
```

実際のメール本文

```
https://test.vte.cx/setpass.html?_token=xxx&value=abc
```

URLリンク(上記Linktoken)をクリックするとsetpass.htmlが表示されます。
ただし、ログイン状態にはなりません。
Keyを /foo と指定した場合、 許可される操作は以下のみです。

* /fooのエントリ検索、フィード検索が可能
* /foo 配下のエントリをPOST可能(自動採番）
* /foo エントリーをPOST、PUT、DELETE可能

また、リンクトークンは、ログイン後、``GET ?_linktoken={Key1[,Key2],・・}`` をリクエストすることでも取得できます。 KeyにはURLのPathinfoを指定します。カンマで区切ることで複数指定できます。Keyに含まれる#はuidに変換されます。

**アクセスキー**

アクセスキーは、アクセストークンやリンクトークンを発行するために使用されるシークレットキーであり、サーバ内(``/{uid}/auth/accesskey`` エントリ)に保持しています。
　アクセスキーを更新することで、これまで発行したアクセストークンやリンクトークンを無効にすることができます。つまり、アクセストークンが悪意のある第三者に漏洩してしまった場合、そのアクセストークンに認可されているあらゆる操作が永久に実行可能になるため、アクセスキーを更新してこれを防ぐ必要があります。

``PUT /d/?_accesskey`` でアクセスキーを更新します。更新するとこれまで使っていたアクセストークンやリンクトークンは使えなくなります。

**ワンタイムトークン(RXID)**

ワンタイムトークン(RXID)は鍵付きハッシュを利用した認証トークンです。ワンタイムであり一度認証が実行されると同じものはもう使えません。これは、AndroidやiPhoneなどのスマホのログイン認証で使うことを想定しています。また、異なるサービス間で通信が必要になった場合にも使われることがあります。
トークン生成にはユーザアカウント、パスワード、APIキー(後述)、サービス名を使用します。ハッシュ化された文字列なのでネットワーク上で生のパスワードが流れる心配はありません。

ログイン後、``GET /d/?_getrxid``を実行することでRXIDを取得できます。
RXIDは、``GET /d/?_RXID={RXID文字列}``で認証されます。認証後はログイン状態になります。また、以下のようにリクエストヘッダにつけて認証することもできます。

```
Authorization: RXID {RXIDトークン}
```

RXIDの有効時間や実行可能回数は、設定ファイル(``/_settings/properties``のrightsタグ)に指定します。

 * _rxid.minute : 有効時間(分)。[デフォルト15分]
 * _rxid.counter.連番.回数 : 同じRXIDを使用しても指定回数まで許可されるURLパターンを指定

```
_rxid.minute=60
_rxid.counter.1.10000=^/_html/foo.html.*$
```
 
**APIキー**

APIキーはワンタイムトークン(RXID)などに使用されるクライアントシークレットキーです。また、サーバサイドJavaScriptにおいてサービスを跨ぐAPI実行の際にもAPIキーが必要になります。サービスに対してAPIキーを１つ発行します。

APIキーを更新したい場合は以下を実行します。サービス管理者が実行できます。

```
PUT /d/?_apikey 
```

APIキーを更新すると、これを保持している全てのクライアントから認証ができなくなります。第三者に漏洩してしまった場合などはAPIキーを更新することで悪意のある操作を防ぐことができます。ただし、更新した場合は新しいAPIキーをクライアントや他のサービスに配布する必要があります
　APIキーを更新してもアクセストークンやリンクトークンには影響はありません。これらを無効にしたい場合はアクセスキーを更新してください。

**スマホアプリからのログイン方法**

スマホアプリからログインするにはRXIDを使います。
vtecxauthパッケージの``vtecxauth.getRXID()``メソッドを実行することでRXIDを取得することができます。
（これを使うには``npm install vtecxauth``でvtecxauthパッケージをインストールしてください。）
パラメータには、ユーザアカウント、パスワード、サービス名、APIキーを指定します。APIキーは管理画面で確認できます。

``vtecxauth.getRXID(username: string, password: string, servicename: string, apikey: string): string;``

以下はRXIDを使った認証の例です。初回はRXIDで認証してcookieを保存し、2回目以降はcookieを使って認証してください。（サーバではセッションが生成されています）
　セッションが不要な場合はcookieの代わりにアクセストークンを使用してください。アクセストークンを使う場合はセッションは生成されずタイムアウトもありません。

```javascript
// 初回(RXIDでログイン)
const rxid = vtecxauth.getRXID(username, password, servicename, apikey)

axios({
	url: 'http://{サービス名}.vte.cx',
	method: 'get',
	headers: {
		'Authorization: RXID '+ rxid,
		'X-Requested-With': 'XMLHttpRequest'
	}
}).then((result) => {
	cookie = result.headers['set-cookie'][0].split(';')[0]	// cookieを保存
}).catch((error) => {
	・・・
})

// ２回目以降(cookie認証)
axios({
	url: 'http://{サービス名}.vte.cx',
	method: 'post',
	headers: {
		'X-Requested-With': 'XMLHttpRequest',
		'Cookie': cookie
	},
	data : reqdata			
}).then((result) => {
	・・・
}).catch((error) => {
	・・・
})
```

###DoS攻撃対策###

**IP Blacklist**

ブラックリストに登録することでDoS攻撃などの可能性があるリクエストを拒否（ブロック）できます。ブラックリストに登録されているユーザ・IPアドレスの組み合わせからのリクエストは認証エラーとなります。特定のIPから1000回(デフォルト)以上ログイン失敗でユーザ・IPアドレスの組み合わがブラックリストに自動的に追加されます。ブラックリストに追加されても異なるIPからログインすることは可能です。

ブラックリストは認証失敗カウンタによって管理します。認証失敗カウンタは、``/_security/{アカウント認識文字列}/{IPアドレス}``キーのカウンタが使われます。

* アカウント認識文字列は、WSSEやRXIDの場合はアカウント (ユーザ名から使用不可文字を除去した文字列)が、AccessTokenやLinkTokenの場合はuidが使われる
* IPアドレスは"."を"-"に置き換えた文字列になる
  * 例) 「125.30.16.141」の場合、"125-30-16-141"。

認証失敗カウンタを0にすることでブロックを解除できます。具体的には以下のようなリクエストです。

ブロック解除

```
PUT /d/_security/{アカウント認識文字列}/{IPアドレス}?_setids=0
```

###サーバサイドJavaScript###

vte.cxのアプリケーションを作成すると基本的にSPA(Single Page Application)になります。SPAのスタイルでは、サーバサイドでレンダリングするのではなく、クライアントからはAjax(XHR)を用いてサーバのAPIにアクセスし、サーバはJSONデータを返すのみとなります。サーバサイドは純粋なAPIとなり、複雑さがサーバサイドからクライアントに移動していることから、これはThin server architecture とも呼ばれます。
　しかし、ビジネスロジックをもたない純粋なREST APIだけで構築すると開発生産性やパフォーマンスを大きく損ねることがあります。あまりにもクライアント側にビジネスロジックが偏重しすぎて開発工数が膨らんでしまうのです。また、クライアントから何回もAPIを呼ぶいわゆるN+1問題も発生しがちです。
　実はクライアントよりもサーバ側でビジネスロジックを実行する方が有利であることがわかっています。つまり、クライアントはビジネスロジックを持たない単純なViewとし、Viewに必要なデータの組み立てなどはサーバ側で行うことで、クライアント側へのビジネスロジック偏重を防ぎます。こうすることで、N+1問題も解決できます。
　このスタイルを、BFF(Backend for Frontend)といいます。vte.cxではサーバサイドのBFFにもJavaScriptを採用することで、統一的(Isomorphic)な環境で高い生産性を発揮できるようにしています。
　BFFは実はフロントエンドアプリケーションの範疇になります。重要なポイントは、あくまでフロントエンドのアプリケーションとしてサーバサイドJavaScriptを実装するということであり、データベース設定や認証設定などのようなサーバ構築のための設定などではないという点です。

**サーバサイドJavaScriptの設定方法と実行の仕組み**

サーバサイドJavaScriptを設定する方法は簡単で、/serverフォルダ配下にJavaScriptファイルを格納するだけです。また、``GET|POST|PUT /s/{スクリプト名}``のようにリクエストすると、/serverフォルダに格納されている {スクリプト名}.jsファイルがサーバ上で実行されます。
　サーバサイドJavaScriptの実行時間は最大５分までで、それを超えると強制的にキャンセルされます。

**サーバサイドJavaScriptのビルドとデプロイ**

サーバサイドJavaScriptは、TypeScriptで書かれたソースコードをECMAScript5の形式に変換されたものを実行します。 これは、以下のようにビルドします。

ビルドとデプロイ

　サービスを作成後、githubの[ブランクプロジェクト](https://github.com/reflexworks/vtecxblank)をチェックアウトして、以下のコマンドを実行するとビルド＆デプロイできます。このコマンドは管理画面の基本情報>実行コマンド>デプロイコマンドに表示されていますのでこの文字列をコピーするといいでしょう。

```
gulp deploy -h http://{サービス名}.vte.cx -k {アクセストークン}
```
　また、以下のコマンドを実行するとローカル環境にて確認できるようになります。JavaScriptファイルを更新すると、自動的にファイルの変更を検知してビルドが実行されサーバにデプロイされます。このコマンドは管理画面の基本情報>実行コマンド>ローカルコマンドに表示されていますのでこの文字列をコピーするといいでしょう。

```
gulp serve -h http://{サービス名}.vte.cx -k {アクセストークン}
```

**Reactとvtecxapiを利用してSSRを実行する**

vtecxapiパッケージをインポートすることでサーバサイドJavaScriptからvte.cxのAPIを利用することができます。まず初めに``npm install vtecxapi``でvtecxapiパッケージをインストールしてください。
　以下はReactの機能を使ってSSR(サーバサイドレンダリング)を実行するサンプルです。最後の行のHTMLを返却するところでvtecxapiが使われています。
　以下をデプロイして、/s/ssr.htmlにアクセスすると、「Hello, World!」が表示されるのを確認できます。 

```tsx:server/ssr.html.tsx
// server/ssr.html.tsx
import * as vtecxapi from 'vtecxapi'
import * as React from 'react'
import * as ReactDOMServer from 'react-dom/server'

const element = (
	<h3> Hello, World! </h3>
)

const html = ReactDOMServer.renderToStaticMarkup(element)

vtecxapi.doResponseHtml(html)
```

**vtecxapiを利用してJSONを返すサービス**

vtecxapi.doResponse()により、オブジェクトをJSONなどに変換してクライアントに返すことができます。サーバサイドのビジネスロジックはこのような形でJSONを返すサービスとして実装することが多いと思われます。
以下は/fooエントリを取得してJSONを返すサンプルです。
　前述のサンプルでは、コンテンツを返すのに、doResponseHtml()を使っていましたが、JSONなどのデータを返すには、doResponse()を使います。 ちなみに、doResponse()は、リクエストパラメータにデータタイプを指定することで様々なデータ形式に変換することができます。例えば、/registration?xでxml、/registration?mでMessagePackを返すことができます。

```javascript
// server/registration.tsx
import * as vtecxapi from 'vtecxapi'

const feed = vtecxapi.getFeed('/registration')   // /registration?xでxmlになる
vtecxapi.doResponse(feed) 
```

また、vtecxapi.getFeed()は検索条件を指定して検索することができます。
検索条件の文字列に&が含まれる可能性がある場合は、encodeURIComponent()などを使って必ずエンコードしてください。

```javascript
// server/registration.tsx
import * as vtecxapi from 'vtecxapi'

const param = encodeURIComponent(vtecxapi.getQueryString('param'))
const feed = vtecxapi.getFeed('/registration?param='+param)   // /registration?xでxmlになる
vtecxapi.doResponse(feed) 
```

**ファイルアップロード**

リソースの登録更新では、raw形式のバイナリの他にFormDataオブジェクトを使ったアップロードを行えます。FormDataオブジェクトはmultipart/form-data形式のデータであり、ブラウザから直接アップロードすることができます。
　アップロードファイルが一つでありキーも指定されていない場合、アップロードしたファイル名が写真エントリのKeyとして登録されます。アップロードファイルが複数の場合は以下のようにサーバサイドJavaScriptを使って処理することができます。
　vtecxblankに複数のファイルを登録するサンプルプログラム(upload_pictures_sample)があります。これは、クライアントから２つの画像データを送信してサーバに登録するサンプルです。
　サーバサイドJavaScriptのvtecxapi.saveFiles(param)は、ファイルアップロードにおけるMultipart Postリクエストをサーバ側で処理するために使用します。具体的には、saveFiles(param)で指定するparamオブジェクトに、inputタグのnameをキーにして任意のファイル名を設定します。
　gulp deployコマンドでデプロイしてログインすると、``/upload_pictures_sample.html``を表示できますので、そこから２つの画像ファイルをアップロードしてみてください。アップロードに成功すると、``http://{サービス名}.vte.cx/{アップロードファイル名}``でブラウザに表示して確認できます。

``画面側(upload_pictures_sample.tsx)``

```tsx
import * as React from 'react'
import * as ReactDOM from 'react-dom'
import axios, { AxiosError } from 'axios'
import {
	Form,
	FormGroup,
	FormControl,
	Button
} from 'react-bootstrap'

/* コンポーネントのPropsの型宣言 */
interface ComponentProps {
}
/* コンポーネントのStateの型宣言 */
interface ComponentState {
	picture1: any,
	picture2: any,
	[propName: string]: any
}

class UploadPictureForm extends React.Component<ComponentProps, ComponentState> {
	constructor(props: ComponentProps) {
		super(props)
		this.state = { picture1: {}, picture2: {} }
	}

	handleChange(e: React.FormEvent<any>) {

		if (e.currentTarget.files) {
			const file = e.currentTarget.files.item(0)
			if (file) {
				const key = '/_html/img/' + encodeURIComponent(file.name)
				const name = e.currentTarget.name

				// 画像以外は処理を停止
				if (!file.type.match('image.*')) {
					return
				} else {
					// 画像表示
					let reader = new FileReader()
					reader.onload = () => {
						this.setState({ [name]: { value: reader.result, key: key } })
					}
					reader.readAsDataURL(file)
				}
			}
		}

	}

	handleSubmit(e: React.FormEvent<any>) {
		e.preventDefault()

		const formData = new FormData(e.currentTarget)
		const param = (this.state.picture1.key ? 'key1=' + this.state.picture1.key + '&' : '') +
			(this.state.picture2.key ? 'key2=' + this.state.picture2.key : '')

		// 画像は、/d/_html/img/{key} としてサーバに保存されます
		axios({
			url: '/s/savefiles?' + param,
			method: 'post',
			headers: {
				'X-Requested-With': 'XMLHttpRequest'
			},
			data: formData

		}).then(() => {
			alert('success')
		}).catch((error: AxiosError) => {
			if (error.response) {
				alert('error=' + JSON.stringify(error.response))
			} else {
				alert('error')
			}
		})

	}

	render() {
		return (
			<Form horizontal onSubmit={(e) => this.handleSubmit(e)}>
				<img src={this.state.picture1.value} />
				<br />
				<img src={this.state.picture2.value} />
				<br />
				<FormGroup>
					<FormControl type="file" name="picture1" onChange={(e) => this.handleChange(e)} />
				</FormGroup>
				<FormGroup>
					<FormControl type="file" name="picture2" onChange={(e) => this.handleChange(e)} />
				</FormGroup>
				<FormGroup>
					<Button type="submit" className="btn btn-primary">
						登録
					</Button>
				</FormGroup>
			</Form>
		)
	}
}

ReactDOM.render(<UploadPictureForm />, document.getElementById('container'))
```

サーバサイドJavaScript(/server/savefiles.tsx)

```javascript
import * as vtecxapi from 'vtecxapi'

interface Param {
    picture1: string
    picture2: string
}

const param: Param = {
	picture1: vtecxapi.getQueryString('key1'),
	picture2: vtecxapi.getQueryString('key2')
}

vtecxapi.saveFiles(param)
```

**CSVデータアップロード**

サーバサイドJavaScriptを利用することでCSVデータのアップロードを行うことができます。``vtecxapi.getCsv(header[],items[],parent,skip,encoding)``は、指定したパラメータを元にCSVを受信してJSONオブジェクトに変換します。パラメータの意味は以下の通りです。

* header[]にはCSVのヘッダ情報を指定する。
受信したCSVファイルのヘッダ情報と異なれば``{"feed":{"entry":[{"title" : "Header parse error"}]}}    ``のようにパースエラーとなる。
* itemsには対応するJSONの項目名を指定する
    * item1(int)やitem1(boolean)のようにカッコの中にintやbooleanの型を指定可能
* parentには変換後のJSONの親項目を指定する。
* skipにはCSVファイルの読み飛ばす行数を指定する。
* encodingはCSVファイルの文字コードを指定する。(UTF-8,Windows-31J等)

vtecxblankプロジェクトには以下のサンプルプログラムがあります。
gulp deployコマンドでデプロイしてログインすると、``/upload_csv_sample.html``を表示できますので、その画面から``/data/sample.csv``を指定してアップロードしてください。アップロードに成功するとログに読み取った結果のJSONを表示しますので、管理画面のログで確認してください。

``画面側(upload_csv_sample.tsx)``

```tsx
import * as React from 'react'
import * as ReactDOM from 'react-dom'
import axios, { AxiosError } from 'axios'
import {
	Form,
	FormGroup,
	FormControl,
	Button
} from 'react-bootstrap'

/* コンポーネントのPropsの型宣言 */
interface ComponentProps {
}
/* コンポーネントのStateの型宣言 */
interface ComponentState {
}

class UploadCsvForm extends React.Component<ComponentProps, ComponentState> {
	constructor(props: ComponentProps) {
		super(props)
		this.state = {}
	}

	handleSubmit(e: React.FormEvent<any>) {
		e.preventDefault()

		const formData = new FormData(e.currentTarget)

		// 画像は、/d/registration/{key} としてサーバに保存されます
		axios({
			url: '/s/getcsv',
			method: 'post',
			headers: {
				'X-Requested-With': 'XMLHttpRequest'
			},
			data: formData

		}).then(() => {
			alert('success')
		}).catch((error: AxiosError) => {
			if (error.response) {
				alert('error=' + JSON.stringify(error.response))
			} else {
				alert('error')
			}
		})

	}

	render() {
		return (
			<Form horizontal onSubmit={(e) => this.handleSubmit(e)}>
				<FormGroup>
					<FormControl type="file" name="csv" />
				</FormGroup>
				<FormGroup>
					<Button type="submit" className="btn btn-primary">
						登録
					</Button>
				</FormGroup>
			</Form>
		)
	}
}

ReactDOM.render(<UploadCsvForm />, document.getElementById('container'))
```

``サーバサイドJavaScript(/server/getcsv.tsx)``

```javascript
import * as vtecxapi from 'vtecxapi'

const items = ['item1', 'item2(int)', 'item3(int)']
const header = ['年月日', '件数', '合計']
const parent = 'order'
const skip = 1 // 1行スキップ
//const encoding = 'Windows-31J'
const encoding = 'UTF-8'

// CSV取得
const result = vtecxapi.getCsv(header, items, parent, skip, encoding)
vtecxapi.log(JSON.stringify(result)) 
```

``CSVデータ(/data/sample.csv)``

```
// 1行skip
年月日,件数,合計
"2017/7/5",3,3
"2017/7/6",5,8
"2017/7/7",2,10
```

**PDF出力**

ReactのSSR(Server Side Rendering)機能とvtecxのPDF出力機能を組み合わせることで動的にPDFを生成することができます。``vtecxapi.toPdf(pages,html,outfilename)``は、指定したパラメータを元にPDFを生成します。パラメータの意味は以下の通りです。

* pagesには出力するPDFのページ数を指定
* htmlにはPDFの生成元となるHTMLを指定(テンプレートHTML)
* outfilenameにはPDFのファイル名を指定
 
vtecxblankプロジェクトには以下のサンプルプログラムがあります。
デプロイ後にログインして /s/ssr.pdfにアクセスすると、「Hello, Harper Perez」と表示されたpdfがダウンロードされます。
　/pdf/pdfstyles.tsはPDFのレイアウト等を指定するスタイルシートファイルです。以下のようにstyles属性に指定することで色やサイズなど様々なスタイルを設定することができます。詳しくは、「PDFスタイルシート」を参照してください。

``サーバサイドJavaScript(/server/ssr.pdf.tsx)``

```tsx
import * as vtecxapi from 'vtecxapi'
import * as React from 'react'
import * as ReactDOMServer from 'react-dom/server'
import * as pdfstyles from '../pdf/pdfstyles'

interface User {
	firstName: string
	lastName: string
}

function formatName(user: User) {
	return user.firstName + ' ' + user.lastName
}

const user: User = {
	firstName: 'Harper',
	lastName: 'Perez'
}

const element = (
	<html>
		<body>
			<div className="_page" style={pdfstyles._page}>
				<table style={pdfstyles._table}>
					<tr>
						<td>
							<p> Hello, {formatName(user)}! </p>
						</td>
					</tr>
				</table>
			</div>
		</body>
	</html>
)

const html = ReactDOMServer.renderToStaticMarkup(element)

// PDF出力
vtecxapi.toPdf(1, html, 'test.pdf')
```

``PDF用スタイルファイル(/pdf/pdfstyles.ts)``

```javascript

export const _page: any = {
	pagesize: 'A4',
	orientation: 'portrait'
}

export const _table: any = {
	bgcolor: '#FFEC8B',
	frame: 'box',
	cellspacing: '3',
	cellpadding: '3',
	width: '90%',
	align: 'left'
}
```
**メール送信**

``vtecxapi.sendMail(entry: any, to: string[] | null, cc?: string[], bcc?: string[], attachments?: string[])``によりメールを送信することができます。
パラメータには以下を指定します。

* entry
  * title : メールのタイトル
  * summary : テキストメール本文(必須)
  * content : HTMLメール本文(任意、インライン画像を指定可能)
* to : 送信先アドレス (複数指定の場合配列で指定)
* cc : CCでの送信先アドレス (配列で指定)
* bcc : BCCでの送信先アドレス (配列で指定)
* attachments : 添付ファイル(コンテンツのキーを複数指定可能)

entryのsummaryにはテキストメール本文を、contentにはHTMLメール本文を指定します。HTMLメールを送信できない場合はテキストメールが送信されます。
HTMLメールのインライン画像は``<img src="CID:/_html/img/ajax-loader.gif">``のように、CIDに続けてキーのURLを指定します。
　以下はメールを送信するvtecxblankのサンプルプログラムです。

``/server/sendmail.tsx``

```javascript

import * as vtecxapi from 'vtecxapi'

const mailentry = {
	'entry': {
		'title': 'sendmail テスト',
		'summary': 'hello text mail',
		'content': {
			'______text': '<html><body>hello html mail <img src="CID:/_html/img/ajax-loader.gif"></body></html>'
		}
	}
}

const to = ['xxxx@xxx']
const cc = ['xxxx@xxx']
const bcc = ['xxxx@xxx']
const attachments = ['/_html/img/vtec_logo.png']

vtecxapi.sendMail(mailentry, to, cc, bcc, attachments)
```

これを実行するには、あらかじめpropertiesにメール送信のための設定を行う必要があります。/_settings/properties.xmlに直接記述するか管理画面の「メール|詳細設定」から設定してください。

送信元の指定方法

```
_mail.from={送信元アドレス}
_mail.from.personal={送信者名}
_mail.password={認証アカウントのパスワード}
_mail.transport.protocol={送信プロトコル}   // "smtp"または"smtps"(Gmailはこちら)。デフォルトは"smtp"。
_mail.smtp.host={SMTPサーバ}
_mail.smtp.port={SMTPポート番号}
_mail.smtp.auth={認証する場合true(デフォルトはtrue)}
_mail.smtp.starttls.enable={STARTTLSを利用する場合true(デフォルトはtrue)}
```

以下はgmailで送信するための設定例です。

```
_mail.from=foo@gmail.com
_mail.from.personal=foo
_mail.password=xxx
_mail.transport.protocol=smtps
_mail.smtp.host=smtp.gmail.com
_mail.smtp.port=587
_mail.smtp.auth=true
```

認証付きリンク

送信するメール本文(テキストメールおよびHTMLメール)に以下の文字列が設定されている場合に認証トークンに変換します。例えば、メールの本文に${RXID=Key}を挿入することで、RXIDを含むURLが自動的に組み立てられます。Keyにはコンテキストパスまで自動的に付加されるためそれ以降のパスを指定してください。


* ${URL} : URLに変換
* ${RXID=/Key} : キーと送信先メールアドレスのRXIDをつけたURLを組み立てて変換
* ${LINK=/Key} : キーと送信先メールアドレスのリンクトークンをつけたURLを組み立てて変換

```
挿入文: ${RXID=/setpass.html}&value=abc
実際のメール本文: https://test.vte.cx/setpass.html?_RXID=xxx&value=abc
```

また、変換に際して以下のようなルールがあります。

* 変換は送信先が1件の場合のみ。送信先を複数指定している場合はブランクに変換さる
* 送信先メールアドレスがそのサービスでユーザ登録されていない場合はブランクに変換される。
* キー指定部分(/Key)に#が設定されている場合は送信先ユーザのUIDに変換される。

**メール受信**

``vtecxapi.getMail(settings)``によりメールを受信することができます。受信結果はfeedのentryに格納されます。複数件受信すると複数件のentryが返ります。
feed.entryの各項目には以下のようにメールの情報がセットされます。()が対応するメールの情報

* title(subject)
* subtitle(cc)
* summary(メール本文)
* content(添付ファイル) ※ Base64に変換されます
  * content.type(ファイルの形式)
  * content.src(ファイル名)

vtecxblankのmail受信サンプル(/server/getmail.js)では、/s/getmailにアクセスすると、Yahoo!メールの設定情報を元にメールを受信します。
``vtecxapi.getMail(settings)``のsettingsパラメータにはメール受信のための設定を入れます。

``mail受信サンプル(/server/getmail.js)``

```javascript
import * as vtecxapi from 'vtecxapi'

const settings: { [index: string]: string } = {}

// 基本設定(例：yahooメール)
settings['mail.pop3.host'] = 'pop.mail.yahoo.co.jp'
settings['mail.pop3.port'] = '995'
// タイムアウト設定
settings['mail.pop3.connectiontimeout'] = '60000'
//SSL関連設定
settings['mail.pop3.socketFactory.class'] = 'javax.net.ssl.SSLSocketFactory'
settings['mail.pop3.socketFactory.fallback'] = 'false'
settings['mail.pop3.socketFactory.port'] = '995'

settings['username'] = 'xxxxx@yahoo.co.jp'
settings['password'] = 'xxxxx'

const result = vtecxapi.getMail(settings)
vtecxapi.log(JSON.stringify(result))
```

###vtecxapiメソッド一覧###

vtecxapiのメソッドには以下のようなものがあります。

リクエスト情報取得

|メソッド |説明  |
|---|---|
|getRequest(): any  |リクエストオブジェクト(feed.entry[0] ～ feed.entry[n])を取得する  |
| getPathinfo(): string |PATHINFO(リクエストURLのパス）を取得する |
| getQueryString(param?: string): string |URLパラメータ(クエリストリング)を取得する |
| httpmethod(): string |HTTPメソッド(GET,POST,PUT,DELETE)を取得する |
| getUriAndQueryString(): string |PATHINFO+クエリストリングを取得する |
| getContentType(): string |Content Typeを取得する |
| getHeaders(): any |リクエストヘッダを取得する |
| getCookies(): any |Cookieを取得する |
| getHeaders(): any |リクエストヘッダを取得する |
| uid(): number |uidを取得する |
| getSettingValue(key: string): string |keyを指定してサービス設定情報を取得する |

データ操作

|メソッド |説明  |
|---|---|
|getEntry(url: string): any|Entryを取得する。キーとクエリパラメータを指定する|
|getFeed(url: string, force?: boolean): any|Feedを取得する。キーとクエリパラメータを指定する。forceがtrueで全件取得|
|count(url: string): number|件数を取得する。キーとクエリパラメータを指定する|
|post(request: any, url: string, force?: boolean): any|親フォルダurlを指定してrequest(feed)をPOSTする。force:25件以上登録|
|put(request: any, isbulk?: boolean, parallel?: boolean, async?: boolean): any|request(feed)をPUTする。isbulk:25件以上、parallel:並列実行、async:非同期実行|
|deleteEntry(url: string, revision?: number): void|urlおよびrevisionのentryを削除する|
|deleteFolder(url: string): any|urlとその配下のentryを削除する|
|saveFiles(props: any): void|props(Map)に指定されたファイル名でアップロードファイルを保存する|
|getHtml(url: string): string|指定されたurlのHTMLを取得する|
|getContent(url: string): string|指定されたurlのコンテンツを取得する|
|getCsv(header: string[], items: string[], parent: string, skip: number, encoding: string): any|アップロードされたCSVをJSONオブジェクトに変換する|
|adduserByAdmin(feed: any): any|管理者権限でユーザを追加する|

採番カウンタ操作

|メソッド |説明  |
|---|---|
|setids(url: string, num: number): void|urlの採番カウンタをnumにセットする|
|allocids(url: string, num: number): any|urlの採番カウンタから指定された採番数(num)だけ採番する|
|addids(url: string, num: number): any|urlの採番カウンタの値を加算(+num)する|
|rangeids(url: string, range: string): void|uriの採番カウンタに範囲を指定する(value=start-end)|

レスポンス関連

|メソッド |説明  |
|---|---|
|setStatus(status_code: number): void|レスポンスにステータスコードnumberをセットする|
|setHeader(name: string, value: string): void|レスポンスにレスポンスヘッダをセットする|
|sendRedirect(location: string): void|リダイレクトを実行する|
|sendError(status_code: number, message?: string): void|ステータスコードとメッセージを送信する(HTTPプロトコル)|
|sendMessage(status_code: number, message: string): void|ステータスコードとメッセージを送信する(JSON)|
|doResponse(feed: any, status_code?: number): void|feed.entry[0] ～ feed.entry[n]をレスポンスする。ステータスコードstatus_codeを指定可能|
|doResponseHtml(html: string): void|htmlをレスポンスする|
|doResponseCsv(value: string[], filename: string): void|csvファイルをレスポンスする|
|getStatus(): number|ステータスコードを取得する|
|RXID(): string|RXIDを取得する|

ログ

|メソッド |説明  |
|---|---|
|log(message: string, title?: string, subtitle?: string): void|ログに記録する|

PDF、XLS出力

|メソッド |説明  |
|---|---|
|toPdf(data: any, html: string, outfilename: string, baseurl?: string): void|PDFを出力する。baseurlに合成するPDFファイルを指定可|
|toXls(data: any, inputxls: string, outfilename: string): void|XLSを出力する|

メール送受信

|メソッド |説明  |
|---|---|
|sendMail(entry: any, to: string[]  null, cc?: string[], bcc?: string[], attachments?: string[]): void|メールを送信する|
|getMail(settings: any): any|メールを受信する|

セッション関連

|メソッド |説明  |
|---|---|
|setSessionFeed(name: string, feed: any): void|feedをセッションに登録する|
|setSessionEntry(name: string, entry: any): void|entryをセッションに登録する|
|setSessionString(name: string, str: string): void|文字列をセッションに登録する|
|setSessionLong(name: string, num: number): void|数値をセッションに登録する|
|getSessionFeed(name: string): any|セッションからfeedを取得する|
|getSessionEntry(name: string): any|セッションからentryを取得する|
|getSessionLong(name: string): number|セッションから数値を取得する|
|deleteSessionFeed(name: string): void|セッションにあるfeedを削除する|
|deleteSessionEntry(name: string): void|セッションにあるentryを削除する|
|deleteSessionLong(name: string): void|セッションにある数値を削除する|
|incrementSession(name: string, num: number): void|セッションにある数値をnumだけ加算する|

###PDFスタイルシート

**ページ構造**

PDFのページは以下のように３つのレベル要素により構成されます。
第一レベル要素はすべて左下点を基準とする絶対座標で指定することができます。
第二レベル要素は第一レベル要素からの相対位置座標となります。

* 基底要素
    * htmlタグ：``<html> ``
    * bodyタグ：``<body>``
    * ページタグ：``<div class="_page">``
* 第一レベル要素
    * テーブル：``<table>`` ``<tr>`` ``<td>``
        * 注：``<table>``の子要素に``<table>``を指定することはできません。
    * イメージ：``<img>``
    * 図形：``<div class="_rectangle">``、``<div class="_line">``
* 第二レベル要素
    * ブロックレベル要素：``<div>``、``<p>``
        * リスト：``<ul>``、``<ol>``
    * インライン要素：``<span>``
        * リンク：``<a>``
        * イメージ：``<img>``
        * 改行：``<br>``

**pageタグ**

``<div class="_page">``タグ（以下、pageタグとします）には、ページの大きさや向き、余白サイズ、暗号化や署名などを設定できます。styleで指定できる属性には以下のものがあります。

| プロパティ | 内容 | 初期値 | 指定方法 |
|---|---|---|---|
| pagesize | ページサイズ | A4 | A0～A10, B0～B5, HAGAKI, NOTE, LEGAL, ARCH_E, ARCH_D, ARCH_C, ARCH_B, ARCH_A, FLSA, FLSE, HALFLETTER, _11X17, LEDGER のいずれか。 |
| orientation | ページの向き | portrait | portrait(縦長)かlandscape(横長)のいずれか。 |
|left, right, top, bottom | 左、右、上、下の余白 | 36 | 数値 |
|nodata | ページ制御の際、オフセットはカウントされるがエンティティのインデックスはカウントされない。 | (なし) | "nodata"のみ記述 |
|footer | ページ数表示 | false | true/false |
|fontsize | ページ全体のデフォルト文字サイズ。ただし、p、span、div、chunk、vchunk、paragraph、a、liタグに囲まれた文字のみ適用される。tdタグに記述された文字列には適用されない。 | 12 | 数値 |
|color | ページ全体のデフォルト文字色。ただし、p、span、div、chunk、vchunk、paragraph、a、liタグに囲まれた文字のみ適用される。tdタグに記述された文字列には適用されない。 | #000000 | #xxxxxx |
|linecolor | ページ全体のデフォルト罫線色 | #000000 | #xxxxxx |
|font | ページ全体のデフォルトフォント | HeiseiKakuGo-W5| フォント名(HeiseiKakuGo-W5,HeiseiMin-W3,KozMinPro-Regularのうちいずれか ) |
| title | PDF文書のタイトル | (なし) | 文字列 |
| author | PDF文書の作成者 | (なし) | 文字列 |
| subject | PDF文書のサブタイトル | (なし) | 文字列 |
| keywords | PDF文書のキーワード | (なし) | 文字列 |
| encryption | 暗号化 | (なし) | 40：「40-bit RC4」で暗号化、128：「128-bit RC4」で暗号化　パスワードおよび文書に関する制限（allowで始まる属性）を指定した場合暗号化される。このとき本項目を指定していない場合は128。 |
| password | 文書を開くパスワード（PDFファイルを開く(参照する)際に入力するパスワード。） | (なし) | 文字列 |
|ownerpassword | 権限パスワード（PDFファイルのセキュリティ設定を変更する際に入力するパスワード。） | (なし) | 文字列|
|allowprinting | 印刷 | true | true：印刷可、false：印刷不可 |
| allowmodifycontents | 文書の変更 | true | true：文書編集可、false：文書編集不可 |
|allowassembly | 文書アセンブリ　ページの挿入/削除/回転、しおりとサムネールの作成の可否。 | false | true：変更可、false：変更不可 |
|allowcopy | 内容のコピーと抽出 | true | true：コピー可、false：コピー不可 |
|allowscreenreaders | アクセシビリティのための内容の抽出　視覚に障碍を持つユーザに対して、スクリーンリーダ(読み上げ)の利用可否。 | true | true：可、false：不可 |
|allowmodifyannotations | 注釈、フォームフィールドの入力および署名 | false | true：編集可、false：編集不可 |
|allowfillin | フォームフィールドの入力および署名 | false | true：入力可、false：入力不可　注）文書パスワードを付けた場合のみ有効。allowmodifyannotationsが編集可（true）の場合、trueとなる。 |

**``<table>``タグ**

``<table>``タグにより表を作ることができます。これは、行(``<tr>``)と列(``<td>``もしくは``<th>``)を子要素にもちます。style属性には以下を指定することができます。

| プロパティ | 内容 | 初期値 | 指定方法 |
|---|---|---|---|
| cols | 列数 | 1 | 数値 |
| width | 幅の縮尺 | 80% | 固定の数値、またはパーセント指定 |
| cellpadding | 全体の縦の間隔 | 0 | 数値 |
| cellspacing | セル同士の間隔 | 0 | 数値 |
| frame | 罫線 | void | void,above,below,hsides,vsides,lhs,rhs,box,border のいずれか。外枠のみの指定であり、内側の線はtdタグの属性で指定。 |
| border | 枠線の幅 | 1 | 数値 |
| bordercolor | 枠線の色 | ページで指定されたデフォルト罫線色 | #xxxxxx |
| bgcolor | 背景色 | #FFFFFF | #xxxxxx |
| align | 表示位置 | center | center, left, right のいずれか |
|widths | 各列の幅（どれだけのカラムを割くか） | (なし) | 列数分、割合をカンマでつないで指定。例）width: 3,4,4; |
|absolutex, absolutey | ページ内の絶対座標 | (なし) | ページ左下を基点とし、テーブルの右上角の座標を指定。 |
|font | テーブルのデフォルトフォント名 | (なし) | フォント名。"$" + エンティティの項目名を指定することで、エンティティの内容を適用できる。 |
|size | テーブルのデフォルト文字サイズ | (なし) | 数値 |
|style | テーブルのデフォルト文字スタイル | (なし) | bold（太字）,italic（斜体）,underline（下線）,strikethru（取消線）　複数指定の場合カンマでつなぐ。 |
|color | テーブルのデフォルト文字色 | (なし) | #xxxxxx |

``<tr>``タグ

style属性には以下を指定できます。

| プロパティ | 内容 | 初期値 | 指定方法 |
|---|---|---|---|
| font | 行のデフォルトフォント名 | (なし) | フォント名 |
| size | 行のデフォルト文字サイズ | (なし) | 数値 |
| style | 行のデフォルト文字スタイル | (なし) | bold（太字）,italic（斜体）,underline（下線）,strikethru（取消線）　複数指定の場合カンマでつなぐ。 |
| color | 行のデフォルト文字色 | (なし) | #xxxxxx |

``<td>``タグ

style属性には以下を指定できます。

| プロパティ | 内容 | 初期値 | 指定方法 |
|---|---|---|---|
| align | セル内データの横方向の配置 | left | left,center,right,justifyall（均等割付） のいずれか |
| valign | セル内データの縦方向の配置 | top | top,middle,bottom,baseline のいずれか |
| colspan | 結合する列数 | 1 | 数値 |
| rowspan | 結合する行数 | 1 | 数値 |
| bordercolor | セルの枠線の色 | ページで指定されたデフォルト罫線色 | #xxxxxx |
| bgcolor  | セルの背景色 | #FFFFFF | #xxxxxx |
| height | セルの高さ。設定値が最低限保証され、文字の折り返しなどで超える場合はこれ以上の高さとなる。 | (なし) | 数値 |
| leading | 文字の改行ピッチ | 16 | セル上枠と文字下部の距離を数値で指定。 |
| borderwidth | セルの罫線の太さ | 1 | 数値 |
| offsetx, offsety | セル内の表示開始座標 | (なし) | セル左下を基点とし、表示内容の左下の座標を指定。 |
| space | 表示文字列の文字間隔 | 0 | 数値 |
| roundrighttop, roundrightbottom, roundlefttop, roundleftbottom | セルの右上、右下、左上、左下の角を丸める | false | true/false |
| roundr | セルの角を丸める際の曲率 | 1 | 数値 |
| lefttoprightbottom, righttopleftbottom | セルの左上から右下、右上から左下へ斜線を引く | false | true/false |
| linehscale, linevscale | セルの罫線を横、縦方向に拡大・縮小 | 1 | 中心からの倍率を指定。拡大時は1より大きな数値、縮小時は0.x。 |
| doubleline | セルの罫線を二重線にする | false | true/false |
| left, right, top, bottom | 左、右、上、下の枠線 | false | true/false |
| nowrap | 改行しない。セルに収まる分のみ表示される。 | false | true/false |
| font | セルのデフォルトフォント名 | (なし) | フォント名。"$" + エンティティの項目名を指定することで、エンティティの内容を適用できる。 |
| size | セルのデフォルト文字サイズ | (なし) | 数値 |
| style | セルのデフォルト文字スタイル | (なし) | bold（太字）,italic（斜体）,underline（下線）,strikethru（取消線）　複数指定の場合カンマでつなぐ。 |
| color | セルのデフォルト文字色 | (なし) | #xxxxxx |

**文字列の表示**

文字列を表示する場合、``<p>``,``<div>``,``<span>``のいずれかのタグを使用します。
上記タグで囲まなければ、デフォルトを含むレイアウト表示はされませんのでご注意ください。
  ``<p>``,``<div>``タグの場合、文字列表示の後に改行されます。``<span>``タグの場合改行されません。style属性には以下を指定できます。

| プロパティ | 内容 | 初期値 | 指定方法 |
|---|---|---|---|
| font | フォント | セル、行、テーブル、ページで指定されたデフォルトフォント | フォント名。 |
| size | 文字サイズ | セル、行、テーブル、ページで指定されたデフォルト文字サイズ | 数値 |
| style | 文字編集 | セル、行、テーブルで指定された文字スタイル | bold（太字）,italic（斜体）,underline（下線）,strikethru（取消線）　複数指定の場合カンマでつなぐ。vertical:trueの場合指定しないこと。 |
| color | 文字の色 | セル、行、テーブル、ページで指定されたデフォルト文字色 | #xxxxxx |
| nowrap | 改行しない。セルを超えて表示される。 | false | true/false |
| hscale | 文字の横幅割合 | 1 | 1を基準とした割合。縮めるなら0.x、広げるなら1より大きな数字。 |
| vertical | 縦書き | false | true/false |
| offsetx, offsety | セル内の文字列表示開始座標 | (なし) | セル左下を基点とし、表示内容の左下の座標を指定。 |

**改行**

改行は``<br/>``タグを使用します。(必ず最後にスラッシュを入れて閉じてください)

**リンク**

リンクは``<a>``タグを使用します。href属性にリンク先URLを指定してください。
 ``<a>``タグに囲む文字列は、``<p>``,``<div>``,``<span>``のいずれかで囲んでください。

**リスト**

リストは``<ul>``、``<ol>``、``<li>``タグを使用します。
   ``<li>``タグに囲む文字列は、``<p>``,``<div>``,``<span>``のいずれかで囲んでレイアウトを編集してください。シンボルは``<ul>``、``<ol>``タグでレイアウト編集できます。style属性には以下を指定できます。

| プロパティ | 内容 | 初期値 | 指定方法 |
|---|---|---|---|
| type | ulの場合固定文字。olの場合項目番号。 | ulの場合 - 。olの場合 1 。 | ulの場合任意の一文字。olの場合、1, A, a のいずれか。 |
| start | olの場合に使用。項目番号の開始文字。 | 1, A, a のいずれか | type未指定もしくは1の場合数字。typeがA, a の場合開始したいアルファベット。 |
| symbolindent | 数値(11.0ぐらいをデフォルトでセットしないと重なってしまう） | (なし) | true/false|
| font | シンボルのフォント | ページで指定されたデフォルトフォント | フォント名 |
| size | シンボルのフォントサイズ | ページで指定されたデフォルトフォントサイズ | 数値 |
| style | シンボルのスタイル | (なし) | bold（太字）,italic（斜体）,underline（下線）,strikethru（取消線）　複数指定の場合カンマでつなぐ。 |
| color | シンボルの文字色 | ページで指定されたデフォルト文字色 | #xxxxxx |
| leading | 文字の改行ピッチ | 16 | セル上枠と文字下部の距離を数値で指定。 |

**画像**

画像は``<img>``タグを使用します。src属性に画像のurl、width属性に画像の幅、height属性に画像の高さを指定してください。style属性には以下を指定できます。

| プロパティ | 内容 | 初期値 | 指定方法 |
|---|---|---|---|
| plainwidth | 画像の幅。widthより優先される。 | (なし) | 数値 |
| plainheight | 画像の高さ。heightより優先される。 | (なし) | 数値 |
| rotation | 回転 | 0 | 数値 |
| absolutex, absolutey | ページ内の絶対座標 | (なし) | ページ左下を基点とし、画像の左下角の座標を指定。 |

**線**

``<div class="_line">``で線を描画します。style属性には以下を指定できます。

| プロパティ | 内容 | 初期値 | 指定方法 |
|---|---|---|---|
|linewidth | 線の幅 | 1 | 数値 |
|color | 線の色 | #000000 | #xxxxxx |
|x1, y1 | 開始点の座標 | (なし) | 数値 |
|x2, y2 | 終了点の座標 | (なし) | 数値 |
|linedushon | 描画する線を破線にする場合、表示部分の長さを設定 | 1 | 数値 |
|linedushoff | 描画する線を破線にする場合、非表示部分の長さを設定 | 0 | 数値 |

**四角形**

``<div class="_rectangle">``で四角形を描画します。style属性には以下を指定できます。

| プロパティ | 内容 | 初期値 | 指定方法 |
|---|---|---|---|
|linewidth | 線の幅 | 1 | 数値 |
|width | 四角形の幅 | 0 | 数値 |
|height | 四角形の高さ | 0 | 数値 |
|color | 四角形の線の色 | #000000 | #xxxxxx |
|absolutex, absolutey | ページ内の絶対座標 | (なし) | ページ左下を基点とし、四角形の左下角の座標を指定。 |
|linedushon | 描画する線を破線にする場合、表示部分の長さを設定 | 1 | 数値 |
|linedushoff | 描画する線を破線にする場合、非表示部分の長さを設定 | 0 | 数値 |

**角丸四角形**

``<div class="_roundrectangle">``で四角形を描画します。style属性には以下を指定できます。

| プロパティ | 内容 | 初期値 | 指定方法 |
|---|---|---|---|
|linewidth | 線の幅 | 1 | 数値 |
|width | 四角形の幅 | 0 | 数値 |
|height | 四角形の高さ | 0 | 数値 |
|roundr | 角の曲率 | 1 | 数値 |
|color | 四角形の線の色 | #000000 | #xxxxxx |
|absolutex, absolutey | ページ内の絶対座標 | (なし) | ページ左下を基点とし、四角形の左下角の座標を指定。 |
|linedushon | 描画する線を破線にする場合、表示部分の長さを設定 | 1 | 数値 |
|linedushoff | 描画する線を破線にする場合、非表示部分の長さを設定 | 0 | 数値 |

**円**

``<div class="_circle">``で円を描画します。style属性には以下を指定できます。

| プロパティ | 内容 | 初期値 | 指定方法 |
|---|---|---|---|
|linewidth | 線の幅 | 1 | 数値 |
|absolutex, absolutey | ページ内の絶対座標 | (なし) | ページ左下を基点とし、円の左下角の座標を指定。 |
|radius | 円の半径 | (なし) | 数値 |
|linedushon | 描画する線を破線にする場合、表示部分の長さを設定 | 1 | 数値 |
|linedushoff | 描画する線を破線にする場合、非表示部分の長さを設定 | 0 | 数値 |
|color | 円の線の色 | #000000 | #xxxxxx |

**バーコードJAN(EAN、UPC)規格**

``<div class="_barcodeEAN">``でJAN(EAN、UPC)規格のバーコードを描画します。style属性には以下を指定できます。

| プロパティ | 内容 | 初期値 | 指定方法 |
|---|---|---|---|
| value | バーコードに表示する値 | 4512345678901 | 数字 |
| height | 高さ | 30 | 数値 |
| size | 文字のサイズ | 10 | 数値 |
| width | 幅 | 0.75 | 数値 |
| font | フォント名 | (なし) | 文字を表示しない場合、"null"を指定する。 |

**バーコードNW-7（CODABAR）規格**

``<div class="_barcodeNW7">``でNW-7（CODABAR）規格のバーコードを描画します。style属性には以下を指定できます。

| プロパティ | 内容 | 初期値 | 指定方法 |
|---|---|---|---|
| value | バーコードに表示する値 | 4512345678901 | 文字列 |
| height | 高さ | 30 | 数値 |
| size | 文字のサイズ | 10 | 数値 |
| width | 幅 | 0.75 | 数値 |
| startstop | スタートストップ文字の有無 | true | true/false |
| font | フォント名 | (なし) | 文字を表示しない場合、"null"を指定する。 |

**バーコードcode39規格**

``<div class="_barcode39">``でcode39規格のバーコードを描画します。style属性には以下を指定できます。

| プロパティ | 内容 | 初期値 | 指定方法 |
|---|---|---|---|
| value | バーコードに表示する値 | 4512345678901 | 文字列|
| height | 高さ | 30 | 数値 |
| size | 文字のサイズ | 10 | 数値 |
| width | 幅 | 0.75 | 数値 |
| startstop | スタートストップ文字の有無 | true | true/false |
| extended | 拡張 | true | true/false |
| font | フォント名 | (なし) | 文字を表示しない場合、"null"を指定する。 |

**バーコードcode128規格**

``<div class="_barcode128">``でcode128規格のバーコードを描画します。style属性には以下を指定できます。

| プロパティ | 内容 | 初期値 | 指定方法 |
|---|---|---|---|
| value | バーコードに表示する値 | 4512345678901 | 文字列|
| height | 高さ | 30 | 数値 |
| size | 文字のサイズ | 10 | 数値 |
| width | 幅 | 0.75 | 数値 |
| codetype | コードタイプ | (なし) | "UCC"（CODE128_UCC規格）または"RAW"（CODE128_RAW規格）を指定。それ以外はCODE128規格。 |
| font | フォント名 | (なし) | 文字を表示しない場合、"null"を指定する。 |

**QRコード**

``<div class="_qrcode">``でQRコードを描画します。style属性には以下を指定できます。

| プロパティ | 内容 | 初期値 | 指定方法 |
|---|---|---|---|
| value | バーコードに表示する値 | 4512345678901 | 文字列|
| height | 高さ | 30 | 数値 |
| width | 幅 | 0.75 | 数値 |
| version | 型番（シンボルの大きさ） | 0 | 0～10を指定 |
| errorcorrectionlevel | 誤り訂正レベル | "H" | "L"(コード語の約7%を復元可能), "M"(15%), "Q"(25%), "H"(30%)のいずれかを指定 |
| cellsize | セルのサイズ(pixel) | 1 | 1～4を指定 |
| margin | 余白(pixel)  | 0 | 0～32を指定 |

###HTTPステータスとメッセージ

|ステータスコード |メッセージ |意味|解説|
|---|---|---|---|
|200|OK.|成功|処理が正しく実行された|
|201|Created.|生成|リソースが新しく生成された|
|204|No entry.|コンテンツなし|リソースがなかった|
|206|Partial Content.|部分的内容|返された結果が一部である|
|400|Request object is invalid.|リクエスト不正|不正なリクエストが送信された|
|401|Authentication error.|認証エラー|認証に失敗|
|403|Access denied.|認可エラー|認証は成功しているが認可でエラー|
|404|No entry.|不存在|コンテンツが見つからなかった|
|406|Authentication time out.|認証タイムアウト|認証タイムアウトが発生した|
|409|Conflict|競合発生|キー重複、もしくは楽観的ロック失敗|
|412|The signature is invalid.|署名検証エラー|署名の検証において失敗した（署名が不正）|
|413|Request Entity Too Large.|リクエストサイズエラー|リクエストデータのサイズ超過|
|417|Request security error.|リクエストセキュリティエラー|XMLHttpRequestからのリクエストではないがJSONで出力しようとしている|
|423|Authentication is locked.|二重ログインエラー|二重ログインが発生した|
|424|Not in service.|サービスを利用不可|サービスを利用できないかサーバサイドJavaScriptでエラーが発生した|
|500|INTERNAL_SERVER_ERROR|内部サーバーエラー|サーバにおける致命的エラー|

**400エラー詳細**

|メッセージ |意味 |
|---|---|
|XX is required. |XXの指定が必要| 
|XX is not available. |XXが使えない| 
|XX does not exist. |XXが存在しない| 
|XX is invalid. |XXが不正| 
|Allocate id must be a numeric value.|正しい数値がIDに指定されていない| 
|Callback strings must use alphanumeric characters.|callbackには英数字以外使用不可| 
|Duplicated Link self.|Link selfが重複している| 
|Duplicated rules for ACLs.|既に同じ権限が設定されている| 
|Duplicated URIs for|URIが重複している| 
|Forbidden request to this service.|このサービスへの許可されないリクエスト| 
|Max must be greater than min.|最大値が最小値より大きい値ではない| 
|Must specify a 'E'(External) control.|E権限を指定する必要がある| 
|Not allowed to cancel the process.|プロセスをキャンセルできない| 
|Not allowed to use an alias for bulkcopy.|bulkcopyではaliasは使えない| 
|Optimistic locking failed for the specified template.|指定したテンプレートの更新エラー（楽観的排他エラー）が発生| 
|Password must be contain at least 8 characters, including at least 1 number and includes both lower and uppercase letters.|passwordは1文字以上で数字と小文字と大文字混じりである必要がある| 
|Request format is invalid: XX|リクエストのフォーマットが正しくセットされていない| 
|Revision number must be a numeric value.|リビジョンが数字ではない| 
|Specified value is out of range.|リビジョンの値が範囲外| 
|Specified URI does not match the id nor key.|指定したURIがIDとKeyに一致しない| 
|The first limit must be less than limit(XX).|first limitはlimit(XX)以下を指定しなければならない| 
|The first limit must be more than 0.|first limitは0以上を指定しなければならない| 
|The number of pages must be more than 0.|number of pagesは0以上を指定しなければならない| 
|Too many entities.|entityの数が多すぎる| 
|Unauthorized request to modify the auth.|Authの変更リクエストは受け付けられない| 
|URI must not contain any prohibited characters.|URIに許可していない文字の使用は不可| 
|URI must not contain any white-space characters.|URIにblank文字は使えない| 
|URI must start with a slash.|URIは/から始まるものでなければならない| 
|Accesskey and Accesstoken can not be used.|AccesskeyとAccesstokenが使えない| 
|Please set only one key.|selfは1エントリ1件のみ| 
|Please make a pagination index in advance.|先にpagination indexを作成する必要がある| 
|Session is disabled.|セッションが無効になっている| 
|Session does not exist.|セッションが存在しない| 
|Top entry can not be specified.|ルートエントリは指定不可| 
|Forbidden request to this service.|このサービスで実行できない| 
|Service init entry is nothing.|サービス初期化エントリが存在しない| 
|.js' is not found.|.jsファイルが見つからない| 
|Service does not exist.|サービスが存在しない| 
|The Web Application has not been activated.|Webアプリケーションが有効になっていない| 

**401エラー詳細**

|メッセージ |意味 |
|---|---|
|Authentication is locked.|認証がロックされている| 
|Authentication time out.|認証タイムアウト| 
|Remote access is not allowed.|リモートからのアクセスは禁止| 
|Captcha required at next login.|次回からCaptcha認証が必要| 

**409エラー詳細**

|メッセージ |意味 |
|---|---|
|Duplicated primary key.|Keyが重複している| 
|Alias is duplicated.|aliasが重複している| 
|User is already registered.|ユーザが既に登録されている| 
|Optimistic locking failed.|更新エラー(楽観的排他エラー)|