先日の PWANight でも HeadlessCMS が話題になっていて、その中でプレビューをどうしようか?という話が出ていた。

CMSのコンテンツをテンプレート出力・表示している場合は問題にならないのだけど、SPA とか SSG とかAPI経由で取得している場合に、CMS 側には表示するための仕組みがないからどうにもならない。

とりあえず headlessMode で試してみた感じでは、タイトルのみでの確認ではあるけど確認できた。
headlessModeなので、CMSのテンプレート(twig)とかは使えない。

公開想定のページとして Heroku で公開している HTML とかをつかい、コンテンツを管理している Craft CMSのプレビュー先としてこの Heroku にあるファイルを指定して、未公開のエントリをライブプレビューの中で表示の確認、変更の反映を確認できた。


とりあえず表示できて1人感動してたけど。後の細かいところはみんなにお願いしよう。

年明けの1/16(木)夜に開催する Craft CMS Meetup Tokyo vol.2 でこういう話もできたらいいかも。

Craft CMS Meetup Tokyo vol.2 Craft CMS の実際のところ | Meetup
https://www.meetup.com/ja-JP/J...

こもりさん、@BUN @tinybeans と滅多に揃わないメンバーだし。
ぜひご参加いただければとm(_ _)m

以下いくつか試してみたり、調べたこととか。

Craft CMS の headlessMode

Craft CMS には headlessMode を選択できるようになっていて、それがどういう挙動になるのかをまず確認してみた。

Craft 3.3 Released with GraphQL and Headless Mode | Craft CMS
https://craftcms.com/blog/craf...

設定自体は config にかけば問題ない。

ドキュメントにあるように、セクションの設定などでURIの設定部分がなくなる。
テンプレートのルート設定も非表示になる。

フロントエンドの処理もいくつかスキップされたり、JSONのレスポンスが返ってくることになる。
templates/ においても表示できなかった。

セクションの設定周りが変わっている。

この状態でまずはVue.jsでエントリ取得して表示できるようにする。

サイトURLを CMS の URL(例:cms.example2.com) とは別のURL(例:front.example.com)にする。
その場所に test.html, index.html を置いた上で、プレビューターゲットに設定する。

front.example.com 自体は普通にアクセスしても当然表示できるわけで、 cms.example2.com の管理画面でライブプレビューを開いて、プレビューを確認すると指定したHTMLの内容が表示されるのを確認できる。

ホーム(index.html)

テスト(test.html):詳細ページ想定

この状態でまずは Vue.js、GraphQL でのエントリを取得して表示、詳細ページの表示を確認する。

一覧ページの表示は前回同様そこまで問題なくいけた。
詳細ページの表示にもちょっと手間取りつつも前回同様に一覧から遷移して表示するところまで。

一覧ページのライブプレビューで未公開エントリも確認する

クエリのレスポンス次第なところはありそうだけど、未公開のエントリもライブプレビュー経由での一覧ページだと表示できた。(記憶がちょっと曖昧で、Endpoint周りの設定をした後だったかどうか?というのがある。)

あとは認証が必要なスキーマしかつかえないとうまいこと動かない様子。
スキーマの Token 渡しちゃうと公開側でもなんでもできちゃうのでそれはなし。

Token とかついていないと公開済みのデータしか見れないし、アクセス元も制御はできるのでそこまで問題にはならないかもしれない。

Token をつけてやりとりしてプレビューできるようにする

未公開のエントリなどにはプレビュー時に発行される Token をつけて投げると閲覧できるらしい。

nystudio107 | Headless Preview in Craft CMS
https://nystudio107.com/blog/h...

にあるような感じで設定する。

nystudio107 さんのプレビューができるようになるための図が非常にわかりやすかった。

let m = document.location.href.match(/token=([^&]+)/);
let token = m ? m[1] : '';

// Http endpoint
const httpEndpoint = process.env.VUE_APP_GRAPHQL_HTTP +'?token='+token || 'http://localhost:4000/graphql'

とりあえずこれでvue.js周りの設定はひとまず終了という感じなので、Herokuにdeploy する。

Craft CMS(cms.example2.com) でサイトのURL(front.example.com)を Heroku のURL(hogehoge.heroku.com)に指定する。
これでプレビューのURLとして Heroku のURLが使われる。

セクションの設定としてはこのような感じで、エントリのプレビューとして一覧と詳細ページを確認するという感じの設定になっている。

さらにプレビューのページ(heroku)からのURLにパラメータがついてるかどうかをみて(CORSの設定とか色々あるけど)、レスポンスを返してくれるという流れの様子。

詳細ページをプレビューするときのIDベースのエントリ判定

一覧ページのプレビューはいいのだけど、詳細ページの方が最初どうしてもうまく行かなかった。

記事が公開済みの場合は公開前のリビジョン(ドラフト)があったり、未公開だとそもそもエントリIDは振られているがドラフトであり、そのドラフトにも複数のリビジョンがあったりする。

共有リンクの方はある程度決まった記事IDが付与される感じのURLになるっぽいのだけど、Developertools の Network でみてるとライブプレビューの方はドラフトIDがついたURLでリクエストを送っている様子。

// 一覧の方
http://cms.example.com/?x-craft-preview=Wk4nJT0CTI&token=hCj3yRDy64PTlq4sdE7_Ya9zbrQkTPuH

// 詳細の方
http://cms.example.com/test/23?x-craft-preview=O54Oe8Ygd9&token=8jqnjS62fCy7skKaD7tMG8AXMcF4nnu2

この DraftID(この例だと23)でリクエストが飛んでしまうとそのIDに対応するエントリはないので GraphQL 側でうまいこといかない。

このリクエストの URL でエントリID(例えば6とか)に変更すると問題なく取れるということがわかった。

ただ、この DraftID はプレビューを押した時や自動保存の時などに自動採番されるし、その都度 Token なども変わる事になる。

Token の方は都度取得できるとはいえ、DraftID をつかって GraphQL でエントリの情報を取得すること自体はまだできない様子。。。

解決してるかと思って 3.4 のベータ版にあげたけど結局変わらずだった。
リリースまでに何かしら変わるかも、、、しれないのだけど。

ただ、今回試してる中では Draft であっても slug を変更することはなかったので、エントリ情報を取得するのに slug を使ってみたら解決した。

こうすることでリクエスト投げる時のURLが slug ベースになるので特定のエントリを取り出すことができる。

https://cms.example.com/test/test02?x-craft-preview=hogehoge&token=fugafuga

取りに行く GraphQL のクエリもとりあえず slug ベースに変更しておく。

// IDで1件取得
export const FEACH_POST_BY_ID = gql`
  query ($id:[String]) {
    post: entries( slug: $id ) {
      title
      slug
      postDate
    }
  }
`

これでとりあえずライブプレビューを開いたときに別サイト(example.heroku.com)のHTML/JSが読み込まれて内容が表示された。

タイトルのみの確認ではあるけど変更も反映されて確認できる、というところまではできた。


あとはエントリが公開されれば一覧の方もアップデートされ、Token とかがなくても表示できるようになる。

Static Site Generator でビルドのコマンドを走らせるとかだとまた別の話がありそうな気はするけど、API 叩いて表示するような場合は公開用においてあるコードをそのまま使うことができそう。

yarn build はちょっとわからないけど yarn serve なら問題なさそうな、という気がする。

(スキル不足で)ハマりまくったので GraphQL ではなくて RestAPI 使った方が早そうな気もする。
そもそも Craft CMS の RestAPIの方が触っていない気もする。

テンプレートで実装しちゃった方が良さそうな気はするけれど、とりあえずできそうなことがなんとなくわかってよかった。


ちなみに 3.4 だとセクションの設定でサイトのURL(/test/{id} とか)ではなくHTMLを置いてある所のドメインから(例 https://hogehoge.com/test/{id})書かないとNGだった。

設定が3.3の時と同じなのに表示用のページを探せずエラーになったりした。