Wordpressでフロントから投稿するエンジニア失敗談共有サイトを作ってみた

エンジニアの失敗談共有サイトがあったらいいな～、というのと、ちょうど社内で手が空いて、「技術力をつけてこい！」とほっぽり出されたので、何かつくればいっか～みたいなノリで作ろうと思い立ちました。

# この記事について
## 触れていること
* つくられたサイトについてわかる
* サイトの宣伝に巻き込まれる
* サイトを使える
* Wordpressでwp-admin画面に入らずにユーザーに投稿させる
* Wordpressでmarkdownを使う
* Wordpressをゴリゴリ改造する
* リアルタイムでプレビューしてくれるmarkdownエディタを作る

## 触れていないこと
* Wordpressの基本的なこと
* ライブラリ・プラグインの細かいこと
* かっこいいデザイン

# はじめに
## モノ
[失敗談共有サイト for Engineer](https://engineer.miss-epi.com/)
<img width="1064" alt="qiita.JPG" src="https://qiita-image-store.s3.amazonaws.com/0/217995/05cab34e-c109-d3d1-aed9-e533631004ab.jpeg">


## 経緯
[伊藤先生のツイート](https://twitter.com/ito_yusaku/status/1004187310601089025)を何かのタイミングでお見かけして、思い立ちました。

> 技術系の失敗談や反省話、いまの時代めっちゃ貴重な情報なんだけど、意識高いだけのエンジニアは恥ずかしがってこういう情報あんまり出さない。「新しい技術を触ってみた」「この技術は必修」みたいな話ばっかりになるの、本当に良くない。俺はお前らの地獄体験談が聞きたいの。わかって。


まあ僕自身、ちょうど1年前くらいからエンジニアを初めて、主にフロント・iOSアプリをいじってここまできたわけですが、「ホームページ作るときにコードミスっちゃった」（いやまあこのくらいならいいんですが）とか「Macのターミナルでよくわかんないコード打ってAWS設定してたらよくわからなくなった」（書いてるそばからよくわからない）とかあるわけですよ。

で、調べて見るとその手の失敗はわんさか出てくるわけで。
でも、そういう失敗を集めたサイトはないなあと。

そういうわけで、作ることにしました。

# ルール
## プラグインを極力使わない
いちばんの目的は技術力向上なので、なるべくプラグインさんに頼らないように頑張りました。

ただ、OAuth認証・ログイン系に関してはいろいろ面倒だったので[Gianism](https://gianism.info/ja/)さんを使いました（後述）。

それ以外の部分については基本的にプラグインを使用していません。

## 時間をかけない
いいものをつくることは大事なんですが、時間がかかってもどうしようもないので、さっさと作り上げることを目標にしました。ちなみに10日くらいかかりました。

## Wordpress感を出さない
エンジニアから見れば、Wordpress感が出てしまうだけでガン萎えすると思うんですよ（暴論）。なるべくWordpress感を出さないように、

* 管理画面にユーザーを導かない
* 「wp-」の文字列を見せない

は最低限心がけました。ユーザーにとってもいきなりテイストの違う管理画面に入れられるのは嫌ですしね。

## Markdownで書ける
エンジニアは息を吸うようにマークダウンで書くと思っているので（偏見）、マークダウンは必須かなと思っていました。


# 機能・実装方法
## ログイン

閲覧・コメントは誰でも可能、投稿は要ログインというよくある感じにしようと思いましたが、ログインのときに楽なようにSNS認証を入れました。

さすがにSDK引っ張ってきて構築するのが面倒だったので、**[Gianism](https://gianism.info/ja/)**というプラグインを使わせてもらいました。

各種SNSでアプリケーション登録をしたら、そのキーを設定するだけでSNSログインができてしまうというスグレモノ。しかもプラグインを入れれば、みんな大好きGitHubアカウントでのログインもできるということで、ぶちこみました。

デフォルトの権限を**投稿者**に設定すれば、投稿ができますね。

Wordpressデフォの`wp-login.php`に表示させることもできるんですが、別に固定ページ等つくって表示させたいときは、固定ページのテンプレートから新しいのを作って（page-login.phpとか）そこにGianismで指定された関数を張り付けるだけです。

```php:functions.php
if( function_exists('gianism_login') ){
	gianism_login('','',get_permalink());
}
```

で、ログインボタンをおすとこの`xxx.com/login/`に飛ばないといけないので、

```php:functions.php
#loginページのリダイレクト
if(!function_exists('custom_login_page_url')){
  function custom_login_page_url($redirect) {
    echo get_bloginfo('url').'/login?redirect_to='.urlencode($redirect);
  }
}
```
的な感じで

リダイレクト先も指定できるので便利。
あとはCSSなどなどで味を調えれば完成。

## 投稿画面
Qiitaっぽいのを目指したので、こうなりました。
<img width="1052" alt="qiita2.JPG" src="https://qiita-image-store.s3.amazonaws.com/0/217995/d112cc48-a044-572f-5214-cd1f1de57392.jpeg">

ここで入力すると
<img width="1050" alt="qiita3.JPG" src="https://qiita-image-store.s3.amazonaws.com/0/217995/d3512c32-385f-4e91-1f73-72f41434d13a.jpeg">

こういう感じになります。イケメンですね。

投稿も管理画面に入らずできるよう、固定ページに`form`を用意し、`$_POST[]`と`nonce`で値を受け取る・それをカスタム投稿タイプに指定して投稿、というイメージですね。

こちらのサイトを参考にしました。
[https://takahashifumiki.com/web/programing/2173/](https://takahashifumiki.com/web/programing/2173/)

えらく昔の記事ですが、うまく適用しながら組み立てました。
懇切丁寧にまとめてくださっているこの方（高橋文樹さん）、さっきのGianismも作っている方でけっこうやばい人ですね。すごい人です（語彙力）。

下書き機能はつけたかったので、`'post_status' => 'draft'`にして、記事のIDをGETで参照するようにしました。

```php:user-post.php
$id = wp_insert_post(array(
    'ID' => $draft_id,
    'post_title' => htmlspecialchars((string)$_POST['title'], ENT_QUOTES, 'UTF-8'),
    'post_content' => (string)$_POST['content'],
    'post_status' => 'draft',
    'post_author' => get_current_user_id(),
    'post_type' => 'user_post'
), true);
if(!is_wp_error($id)){
    header('Location: '.get_permalink().'?id='.$id.'&action=save');
    die();
}
```
この「user-post.php」にデータをPOSTして、処理をさせるわけですね。
公開するなら`'post_status' => 'publish'`にすればOK。


下書き記事の一覧も必要。こんな感じでちゃちゃっとできますね。

```php:page-login.php
<header class="entry-header">
<?php the_title( '<h1 class="entry-title">', '</h1>' ); ?>
</header>
<div class="entry-content">
    <ul class="draft-list">
    <?php $arg = array(
        'post_type' => 'user_post',
	'post_status' => 'draft',
        'author' => get_current_user_id()
	);
	query_posts($arg);
    ?>
    <?php if(have_posts()): while(have_posts()): the_post();?>
        <li>
	    <span>
            <?php 
            if(get_the_title(get_the_ID())!==''){
                the_title();
            }else {
                echo '（タイトルなし）';
            } ?>
            </span>
	    <a href="<?php echo get_permalink(8).'?id='.get_the_ID(); ?>">編集</a>
	</li>
	<?php endwhile; endif;?>
    </ul>
</div>
```

### Markdownエディタ
肝になるマークダウンエディタは、[marked.js](https://github.com/markedjs/marked)と[highlight.js](https://highlightjs.org/)を使いました。

`<textarea>`と`<input>`からkeyupしたイベントを取得して、そのタイミングでパースして出力するようにしています。

ちなみに先にmarked.jsを導入してしまったのでアレですが、PHPで書くなら[Parsedown](http://parsedown.org/)のほうが圧倒的楽でした。ライブラリぶっこんでクラスをインスタンス化して使うだけ。本文のほうはParsedownを使いました。

```php:post-user_post.php
<!-- 略 -->
<?php
include( locate_template( './lib/php/Parsedown.php' ) );
$parsedown = new Parsedown();
?>
<!-- 略 -->
<div id="md-post">
<?php
    # setBreaksEnabled(TRUE)で改行を許可
    echo $parsedown->setBreaksEnabled(TRUE)->text(get_the_content());
?>
</div>
<!-- 略 -->
```
## ユーザー設定
ユーザー情報の設定も投稿画面作成と同じ要領で作れます。

## コメント
Wordpressのをそのまま使えるので楽ですね。

## サイドバーなど
まあこの辺は本質ではないのでプラグイン入れてもいいかなと思っています。
人気記事はカスタムフィールドに閲覧回数を保存して、その降順で表示しています。

# 問題点
## 画像がUPできない
ファイルのアップロードのためにほげほげしないといけないので、ちょっと後回し中。フロントからWordpressのメディアライブラリが呼び出せればいいのだが……

## 編集ができない
まあこれは実装していないだけで、原理上はすぐ作れますね。要望あれば作ります。

## XSS
いちおうMarked.jsもParsedownもXSS対策の設定をきちんとすれば`<script>`タグを無効化してくれるみたいなので、実装しています。ただ、これで大丈夫なのかは心配なところ。

Markdownエディタなど使った投稿サイトでは避けて通れない部分なので、要勉強です。


# まとめ
思ったより時間がかかってしまったが、一度作ってしまえば「なんだ、そういうことか」というものばかりな気もします。Wordpressをベースに作っているので、面倒なDBのあーだこーだ等を気にせず、サクッとかけるのはメリットなのかなと思いました。

ぜひ使ってあげてください。
[https://engineer.miss-epi.com/](https://engineer.miss-epi.com/)