20190822のHTMLに関する記事は8件です。

Adobe ExtensionのFirst Step

はじめに

AdobeアプリケーションのExtension(通称 CEP:Common Extensibility Platform)の画面開発を始めるために必要な情報をまとめる。

開発ツール

Visual Studio Code 1.37.1

あると便利な Visual Studio Code 拡張

  1. ExtendScript
  2. CC Extension Builder
  3. Adobe Script Runner

Getting Started

上記の拡張がインストールしている前提で話を進める

  1. コマンドパレットを実行
  2. 「Create a New CC Extension」コマンドを見つける
  3. Extension IDを入力(デフォルトは「com.example.helloworld」)
  4. Extensionの名称を入力
  5. どのテンプレートを利用するか選択

    a. basic

    単純な「Hello World」のよくあるテンプレート
    中身のファイルには本当に基本的なものしか記述されていない
    

    b topcoat

    AdobeのオープンソースCSSライブラリの名称
    CSSを導入したもの
    

    c spectrum

    topcoatの代わりにspectrumというCSSライブラリを用いたもの
    

    d theme
    カスタム

※テンプレートの作成場所はmacOSXの場合は下記である。

~/Library/Application Support/Adobe/CEP/extensions/

ここに置いてあるExtensionは、manifestで指定したアプリケーションとバージョンすべてで利用可能。

例(InDesign)

InDesignで使えるように、manifestファイルを修正する。 CSXSフォルダにあるmanifest.xmlを開き以下のように書き換える。

<Host Name="IDSN" Version="10.0" />
....

単一バージョン表記だと、そのバージョン以降実行可能となる(この例では10.0=CC2014以降)。動作バージョンを範囲で指定する場合は[10.0, 13.1]のように記述する。

※manifest.xmlに関してはこちらを参考にすること。

DebugModeの設定

Debugする時は、以下のコマンドを実行する。

defaults write com.adobe.CSXS.7 PlayerDebugMode 1 //ほぼCC 2017用
defaults write com.adobe.CSXS.8 PlayerDebugMode 1 //ほぼCC 2018用
defaults write com.adobe.CSXS.9 PlayerDebugMode 1 //ほぼCC 2019用

実行

manifestでバージョン指定したInDesignを起動し、ウィンドウメニュー>エクステンションと開くと、先程サンプルとして作成したCEPが選べることができ、実行することができる。

証明書の発行

ここより、 ZXPSignCmd をダウンロードする。以下のコマンドを実行する。

ZXPSignCmd -selfSignedCert 国名コード 地域 組織 名前 パスワード 出力ファイルパス.p12 for osx
ZXPSignCmd.exe -selfSignedCert 国名コード 地域 組織 名前 パスワード 出力ファイルパス.p12 for win

ツールのコマンドはPACKAGING AND SIGNING ADOBE EXTENSIONS TECHNICAL NOTEを参考にすること。

パッケージ化

以下のコマンドを実行する。

ZXPSignCmd -sign ソースフォルダのパス 出力ファイルパス.zxp 証明書.p12 設定した証明書のパスワード for osx
ZXPSignCmd.exe -sign ソースフォルダのパス 出力ファイルパス.zxp 証明書.p12 設定した証明書のパスワード for win

.zxp が生成される。

ZXPをインストールする方法

ZXPをAdobe製品へインストールするには、ツールが必要となる。ExMan Command Line Toolなるものを使用する。

  1. Adobe Extension Manager CCをこちらからダウンロードする。
  2. Adobe Extension Manager CCをインストールする。
  3. ExManCmdが配置されていのフォルダへ移動する。
    • Windowsの場合:C:\Program Files\Adobe\Adobe Extension Manager CC\
    • MacOSXの場合:/Applications/Adobe Extension Manager CC/Adobe Extension Manager CC.app/Contents/MacOS/
  4. コマンドラインツールを実行しインストールを開始する。

以上。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Adobe ExtensionのFirst step

はじめに

AdobeアプリケーションのExtension(通称 CEP:Common Extensibility Platform)の画面開発を始めるために必要な情報をまとめる。

開発ツール

Visual Studio Code 1.37.1

あると便利な Visual Studio Code 拡張

  1. ExtendScript
  2. CC Extension Builder
  3. Adobe Script Runner

Getting Started

上記の拡張がインストールしている前提で話を進める

  1. コマンドパレットを実行
  2. 「Create a New CC Extension」コマンドを見つける
  3. Extension IDを入力(デフォルトは「com.example.helloworld」)
  4. Extensionの名称を入力
  5. どのテンプレートを利用するか選択

    a. basic

    単純な「Hello World」のよくあるテンプレート
    中身のファイルには本当に基本的なものしか記述されていない
    

    b topcoat

    AdobeのオープンソースCSSライブラリの名称
    CSSを導入したもの
    

    c spectrum

    topcoatの代わりにspectrumというCSSライブラリを用いたもの
    

    d theme
    カスタム

※テンプレートの作成場所はmacOSXの場合は下記である。

~/Library/Application Support/Adobe/CEP/extensions/

ここに置いてあるExtensionは、manifestで指定したアプリケーションとバージョンすべてで利用可能。

例(InDesign)

InDesignで使えるように、manifestファイルを修正する。 CSXSフォルダにあるmanifest.xmlを開き以下のように書き換える。

<Host Name="IDSN" Version="10.0" />
....

単一バージョン表記だと、そのバージョン以降実行可能となる(この例では10.0=CC2014以降)。動作バージョンを範囲で指定する場合は[10.0, 13.1]のように記述する。

※manifest.xmlに関してはこちらを参考にすること。

DebugModeの設定

Debugする時は、以下のコマンドを実行する。

defaults write com.adobe.CSXS.7 PlayerDebugMode 1 //ほぼCC 2017用
defaults write com.adobe.CSXS.8 PlayerDebugMode 1 //ほぼCC 2018用
defaults write com.adobe.CSXS.9 PlayerDebugMode 1 //ほぼCC 2019用

実行

manifestでバージョン指定したInDesignを起動し、ウィンドウメニュー>エクステンションと開くと、先程サンプルとして作成したCEPが選べることができ、実行することができる。

証明書の発行

ここより、 ZXPSignCmd をダウンロードする。以下のコマンドを実行する。

ZXPSignCmd -selfSignedCert 国名コード 地域 組織 名前 パスワード 出力ファイルパス.p12 for osx
ZXPSignCmd.exe -selfSignedCert 国名コード 地域 組織 名前 パスワード 出力ファイルパス.p12 for win

ツールのコマンドはPACKAGING AND SIGNING ADOBE EXTENSIONS TECHNICAL NOTEを参考にすること。

パッケージ化

以下のコマンドを実行する。

ZXPSignCmd -sign ソースフォルダのパス 出力ファイルパス.zxp 証明書.p12 設定した証明書のパスワード for osx
ZXPSignCmd.exe -sign ソースフォルダのパス 出力ファイルパス.zxp 証明書.p12 設定した証明書のパスワード for win

.zxp が生成される。

ZXPをインストールする方法

ZXPをAdobe製品へインストールするには、ツールが必要となる。ExMan Command Line Toolなるものを使用する。

  1. Adobe Extension Manager CCをこちらからダウンロードする。
  2. Adobe Extension Manager CCをインストールする。
  3. ExManCmdが配置されていのフォルダへ移動する。
    • Windowsの場合:C:\Program Files\Adobe\Adobe Extension Manager CC\
    • MacOSXの場合:/Applications/Adobe Extension Manager CC/Adobe Extension Manager CC.app/Contents/MacOS/
  4. コマンドラインツールを実行しインストールを開始する。

以上。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

css:padding(やmargin)の指定が1つの時、ショートハンドを使うか否か

きっかけ

paddingを一つだけ指定する場合、

// 個別指定
padding-bottom: 15px; 

//ショートハンド
padding: 0 0 0 15px; 

ではどちらが良い記述なの?という疑問が出てきたので、
双方のメリットとデメリットをメモします。

結論

個人で開発するなら どちらでも良い
チームで開発するなら 個別指定推奨

メリットとデメリット:個別指定

// 個別指定
padding-bottom: 15px; 

メリット

  • メンテナンスが楽 (余分なpaddingを指定しないので他要素に影響しないので、むやみに他要素と打ち消しあうことがない)

大人数で同じソースコードを触るチーム開発では重要ポイント。
(後日後述)

デメリット

  • 指定を増やす時少しだけめんどくさい (ほとんど書き直さなければならないため)
  • 打つのが少しだけめんどくさい (文字量が増えるため)
  • 読みずらい (bottomやらtopやらバラバラするため)

メリットとデメリット:ショートハンド

//ショートハンド
padding: 0 0 0 15px; 

メリット

  • 指定を増やす時少しだけ楽 (0を変更するだけで良いため)
  • 打つのが少しだけ楽 (文字量が増えるため)
  • 読みやすい (bottomやらtopやらバラバラするため)

デメリット

  • メンテナンスが厄介 (余分なpaddingを指定するので他要素に影響し、むやみに他要素と打ち消しあう可能性があるため)

大人数で同じソースコードを触るチーム開発ではなるべく避けたい。
(後日後述)

まとめ

2つまで個別指定するかたもいるとかいないとか。
完全に個人の感覚かと思っていましたが

メンテナンスが厄介 (余分なpaddingを指定するので他要素に影響し、むやみに他要素と打ち消しあう可能性があるため)

あたりは考えたことがなかったので良いきっかけになりました。

他の観点でメリットデメリットがあればぜひ教えていただきたいです。

それではさようなら!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】元インフラエンジニアの初プログラミング【スロットゲーム】

タイトル通り筆者は元インフラエンジニアで現在は出向の関係で非エンジニアです。
近いうちにまたエンジニアとして働きたいため今はフロント系(HTML,CSS,JavaScript)の勉強してます。

最初はProgate等の学習サイトで勉強してたんですがその勉強どうしようかなと頭を悩ませたんですが実際にコード書いて物作るのが一番と思い
今回初めてJavaScriptでコードを書いたのでその記録と
自分と同じように初学者でどう勉強すればと迷ってる人に向けにどうやって勉強のお題を見つけたかとかどう勉強していったかなどの一例になればと思い投稿します。

ちなみに実際に作ったものはスロットゲームでこの記事の方プログラムをほぼほぼ真似してます。
https://qiita.com/hacchi56/items/0f2ea20de8b9d7046d45

では実際に私がどう勉強していったか書いていきます。

学習サイトでやったことはほぼ忘れてた件

まず一番最初に個人の感想というか私と同じように学習サイトに結構時間割いてる初学者の方へ個人的に気を付けた方がいいと思った点を書きます、
実際にコードを書いていこうと思うと具体的な書き方が浮かばないうえにコードを書こうとしても細かい構文の書き方が出てこないことがほとんどでした。
私はProgateを使って学習を進めていましたがこれだけではコードは書けるようにはならないと思います。
こういった学習サイトは概要理解とかどんな構文なのかとか「ふーん」っていう程度で知れるくらいのものとして利用した方がいいかもしれません。
学習サイトで概要をなめたら実際にお題を探してコード書く!っていう流れの方が身になるんじゃないかと今回コード書いてみての感想です。

お題探し

では実際にコードを書いてみようと思ったのですが
学習した言語今回はJavaScriptでどんなものが作れるかもよくわからなかったので自分で一からコードを書くという選択肢は私にはありませんでした。
そこで先人の作ったものを真似てみようと思い立ってGoogle先生で「JavaScript 初心者」とか「JavaScript 初心者 お題」で探しました。
実際に探すと今回のビンゴゲーム以外にもいろいろなものがありましたが
自分と同じく初学者がお題を選定する上で大事かなーと思うのはなるべく簡単そうなもの、作りがそんなに凝ってないものを選びましょう。
作りが豪華なもの = 複雑なもの になりがちだと思うのでなるべく最初はハードルの低いものを選びましょう。

選定したコードの理解・熟読

次に選んだお題の動きとコードを理解できるまで読みました。
わからないところはGoogle先生に聞きつつ書いてあるコードがどういった動きをするのかを読み込みます。
私は3周くらいコードを読みましたが、下記のような流れでよみこんでいきました。
- 1周目はわからないとこは飛ばしつつ全体的に流して読む
- 2周目はわからないとこは1つずつ調べていってわからない構文の理解をする
(理解できたら対象のコード近くにコメントで意味を書いていく)
- 3周目はそれぞれの構文を理解した上で改めて全体のコードがどの動きに対応しているのかを確認する

※どうしても読んでて理解できない時は知見のある人に聞いてしまいましょう。
周りにエンジニアがいないよって人は teratail【テラテイル】|ITエンジニア特化型Q&Aサイトなどの質問サイトなどに頼る方法なんかもあるそうです。(私はまだ利用したことないですごめんなさい)

コード書く際に心掛けたこと

勉強のために書くのでなるべく元のコードは見ないようにわからない部分はGoogle先生に聞いて書くようにしてました。
まあ最初の白紙の状態から"いざ書くぞ"ってなったときはチラチラ見ながら書いてました。
ですが最初の書き出しが終わると事前にコードを熟読しているのもあって構造はなんとなく覚えてるというかわかってるので途中まではなんとなく書けました。(それでもちょいちょいチラ見はしてますw)
逆に絶対にやらないようにしていたのが『何で動いてるかわからないけどググってきたものそのままコピペ』だけはやらないようにしました。
これをやってしまうと勉強の意味がなくなりますしこの癖がつくと実務で自分の書いたコードなのに仕様がわかりませんとか言うことになりそうだったので

実際に書いたコード

書いたコードも貼っておきます。
CSSも書いて見栄えをもう少しきれいしようかとおもいましたが今回はJavaScriptの勉強が主な目的だったのでBootstrapでセンタリングとボタンの色付け程度にしておきました。

index.html

<!DOCTYPE html>
<html>
<head>
    <meta lang="ja">
    <meta charset="UTF-8">
    <title>Slot Game</title>
    <script type="text/javascript" src="slot.js"></script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
</head>
<body>
    <h1 class="text-center">スロットゲーム</h1>
    <div class="container slot-box text-center">
        <p>得点<span id="score">0</span></p>
        <div class="row">
            <div class="slot1 col-6">
                <p id="slot0">0</p>
                <input class="btn btn-primary" type="button" value="STOP" id="stop0">
            </div>
            <div class="slot1 col-6">
                <p id="slot1">0</p>
                <input class="btn btn-primary" type="button" value="STOP" id="stop1">
            </div>
        </div>
    </div>
</body>
</html>

JavaScriptに関しては参考にしたコードとスロットを回す処理のところをすこし変えました。
元のコードはもう少し短かったんですが自分的に読みにくいと思ったので長くても自分が読みやすいと思ったコードに変えてます。
(もとコードはsetTimeoutを使用してましたが私はsetIntervalを使用してます)

slot.js

//HTMLが読み込まれた後にJavaScriptを実行します。
window.onload = ()=>{
    //即時実行とスコープの限定
    (()=>{
        //変数定義
        var interval = 400;
        var stopCount = 0;
        var results = [];
        var timeOutId = [];
        var score = 0;
        var target = 0;


        //一定時間間隔を開けスロットを回す処理を呼び出す
        function startSlot(num){
            timeOutId[num] = setInterval(startSlotProcess, interval, num);
        }

        //スロットを回す処理内容
        function startSlotProcess(num){
            //スロットの現在の数値を取得する
            var slotValue = document.getElementById(`slot${num}`)

           if(slotValue.textContent < 9){
                slotValue.textContent++;
           }else{
            slotValue.textContent = 0;
           }
        }

        //スロットを左右スタートさせる
        startSlot(0);
        startSlot(1);


        //スロットを止める処理内容
        function stopSlot(num){
            //スロットのカウントを止める
            clearInterval(timeOutId[num]);
            //スロットが止まった際の数字を取得する
            results[num] = document.getElementById(`slot${num}`).textContent;
            //集計用に何かいスロットを止めたか集計する
            stopCount++;
            //集計用の関数を動かす
            aggregate();
        }

        //ボタンを押された際に左右それどれのスロットを止める
        document.getElementById("stop0").onclick = function(){
            stopSlot(0);
        }

        document.getElementById("stop1").onclick = function(){
            stopSlot(1);
        }

        //集計処理
        function aggregate(){
            if(stopCount === 2){
                //スロットの数値を合わせられた時
                if(results[0] === results[1]){
                    alert("おめでとう!もう一回遊べるドン!!!");

                    //集計処理を行う
                    score += 200;
                    getScore();

                    //スロットが回る感覚を短くする
                    interval *= 0.8;

                    //スロットを止めたカウント数をリセットする
                    stopCount = 0 ;

                    //スロットのスタート
                    startSlot(0);
                    startSlot(1);
                }else{
                    alert("残念!そこまで!!")
                }
            }
        }

        //集計結果を表示させるための処理
        function getScore(){
            target = document.getElementById("score");
            target.textContent = score;
        }

    })();
}

最後に

初めて自分でコードを書いてみましたが実際に完成すると嬉しいですね。
最初は簡単なものしか作れないかもしれませんがしばらくは人のコードを真似させてもらって(自分で少しでもオリジナル要素は入れつつ)勉強していこうかと思います。
私と同じような初学者の方で勉強の仕方を迷っている方の参考程度になればうれしいです^^

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ウェブで使用する動画のクオリティ(解像度とFPS)はもっと下げていいのでは

静岡が誇るローカルファミレス「さわやか」のサイトにデスクトップブラウザでアクセスするとハンバーグをジュウジュウする動画が自動再生されます(2019年8月現在)。

キャプチャ2.gif

スマホブラウザでも動画のインライン自動再生の環境が整い、こういったサイトはよく見かけるようになりました。パフォーマンスを気にしがちなエンジニアからすれば動画の乱用は避けたいものですが、さわやかのサイトで最大の売りであるハンバーグを見せつける動画をいきなり流すことは良いなと思います。

しかし、この動画は1分間のフルHD(1920x1080)。ファイルサイズは46MBとかなり大きいものになります。too muchではないか。

異なる解像度とFPSの動画を見れるデモを作ってみました

以下、これを参考にして私見を述べます。

メインで見せる動画でも表示するピクセル数程度でいいのでは

メインで大きく表示する動画でも最大でもHD(1280x720)で十分ではないかと。そもそも私たちは映画をゆったり見るためにサイトに訪れるわけではないですし。スマホブラウザなら横幅400px程度で十分かと。

つまり動画のサイズは最大でも表示するピクセル数あれば十分かと思いますが、どうでしょう?

装飾として使用する動画のクオリティはもっと下げられる

例えば背景に動画を使用してるタイプのやつです。パフォーマンスを突き詰めると動画使わない方がいいじゃんってことになっちゃいますが。動画の上にテクスチャをひくような使い方であればなおさら気になりません。表示するピクセル数の半分くらいのサイズで良いのではと思います。いかがでしょう?

FPSも下げられる

解像度だけでなくFPSを下げてファイルサイズを減らすこともできます。ただ解像度ほど劇的にファイルサイズは減りません。15FPSくらいまでは十分に見れます。10FPS以下になってくると明らかにカクカクしてGIFアニメのようになりますが、あえてカクカクした動画として表示するのも十分にありかと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsのcheck_boxで親切に0を送ってくれているのは誰なのか

Railsのcheck_boxは未チェック状態だと"0"を送ってくれる

デフォルト時の挙動です
paramsを見てもわかるとおり、0が送られてきます
時と場合によってtrue/falseにしてみたりとか、いろいろあると思いますが、普通は0が送られます

でも別にcheckboxのvalueが0/1で変化するわけではないんです

ある日、「checkboxのvalueが0か1で判定したいのに、チェックしてもチェック外してもvalueが1で変わらないんです :cry: 」と相談を受けました
まあその判定の善し悪しは置いておいて

HTMLのcheckboxってチェックしてないとそもそも送信されないですよね
Railsのparamsはチェック時に1未チェック時に0が送られてくるので、チェックのオンオフでvalueが0/1で変わると、その子は混乱しちゃったんですね
まあ相談されてしばらくは僕も混乱してましたが

じゃあいったい誰が"0"を送ってる…?

checkboxはチェックされていたら"1"を送り、チェックされていなかったらそもそもなにも送らない
では、Railsにおいて親切に"0"を送っているのは誰なんでしょうか?

サンプルのフォームをscaffoldしてみて確認しました
SampleRailsApp_-_Vivaldi.png

なるほど!君か!
Railsはデフォルトでhiddenなvalueを作ってくれているんですね
チェックされた場合、checkboxの方が後なので上書きされて"1"になると…なるほど

でも注意しないといけないのは、これがあくまでcheck_boxの話でcheck_box_tagはまた微妙に挙動が異なるということなんですが…それはまた別のお話(˘ω˘)

意外と詰まりやすいcheckbox

未チェック時は送信されないとか、true/falseにしたいときとか、check_box_tagを使ったときとか…なにかと詰まりやすいのがcheckboxだなあと思っています
でもこの辺の、実際に生成されているHTMLがなんなのか把握できると、詰まることも減るんじゃないかな〜と思いました
謎のvalue"0"を作る職人の存在がわかってスッキリしました

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP For Beginnersチュートリアル その15 会員登録フォームにパスワードリセットを実装する

このシリーズの目的

体系的なwebコーディングの訓練ができるようになるためにPHPの初学のきっかけかつ、PHPでログインフォームやフォームを実装することができるようになるために

PHP For Beginners

上記のチュートリアルを進めているのでその備忘録。

前回

内容

今回のチュートリアル

How To Create Forgot Password System In PHP & MySQLi [2018]

このチュートリアルでやること

・ログインフォームにパスワードリセットを実装する
(その12で作成した成果物に追加する)

成果物

forgotPassword.php
<?php 

    $msg = "";
    use PHPMailer\PHPMailer\PHPMailer;
    use PHPMailer\PHPMailer\Exception;
    use PHPMailer\PHPMailer\SMPT;

    require_once 'function.php';


    if (isset($_POST['email'])) {
        $conn = new mysqli('localhost','root','','register');

        $email = $conn->real_escape_string($_POST['email']);

        $sql = $conn->query("SELECT id FROM users WHERE email='$email' ");
        if ($sql->num_rows > 0) {

            $token = generateNewString();

            $conn->query("UPDATE users SET token='$token',
                            tokenExpire = DATE_ADD(NOW(), INTERVAL 5 MINUTE)
                            WHERE email = '$email'
                ");

            mb_language("japanese");
            mb_internal_encoding("UTF-8");
            require 'vendor/autoload.php';
            require 'Mailtrap-config.php';


                $mail = new PHPMailer();

                // Server

                $mail->SMTPDebug = 0; //本番では0とかにする。
                $mail->isSMTP();
                $mail->SMTPAuth = true;
                $mail->Host = MAIL_HOST;
                $mail->Username = MAIL_USERNAME;
                $mail->Password = MAIL_PASSWORD;
                $mail->SMTPSecure = MAIL_ENCRPT;
                $mail->Port = SMTP_PORT;

                // Recipients
                $mail->setFrom(FROM_MAIL);
                // $toname = mb_encode_mimeheader("$name", 'ISO-2022-JP', 'B', "\n");
                $mail->addAddress($email);
                // $mail->addAttachment($attachment);

                // Content
                $mail->Subject = mb_encode_mimeheader("Reset Your Password", "ISO-2022-JP", "UTF-8");
                $mail->Body = mb_convert_encoding("
                    パスワードリセットのリクエストがありましたので以下のリンクをクリックしてパスワードのリセットを行ってください。:<br><br>
                    <a href='http://localhost/Laravel/PHPMailer/Training/phptutorial17/resetPassword.php?email=$email&token=$token'>パスワードリセットはこちらから</a><br>

                    七花 京
                ","JIS","UTF-8");
                $mail->CharSet = 'ISO-2022-JP';
                $mail->Encoding = "7bit";

                // Select HTML or NOT

                $mail->isHTML(true);

               if ($mail->send())
                    exit(json_encode(array("status" => 1, "msg" => 'Please Check Your Email Inbox!')));

                 else 
                    exit(json_encode(array("status" => 0, "msg" => 'Something Wrong Just Happened')));

                } else
                    exit(json_encode(array("status" => 0, "msg" => 'Please Check Your Input!')));
    }

 ?>



 <!DOCTYPE html>
 <html>
 <head>
    <title>Forgot Password?</title>
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js">
    </script>
    <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js" type="text/javascript">
    </script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js">
    </script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js">
    </script>
    <link rel="stylesheet" type="text/css" href="css/forgotPassword.css">
 </head>

 <body>
    <div class="container">
        <div class="row justify-content-center">
            <div class="form col-md-6">
                <form action="forgotPassword.php" method="post" enctype="multipart/form-data">
                    <input  class="form-control" id="email" placeholder="Your Email Address"><br><br>
                    <input type="button" class="btn btn-primary" value="Reset Your Password"><br><br>

                    <p id="response"></p>
                </form>
            </div>
        </div>
    </div>

    <script src="https://code.jquery.com/jquery-3.4.1.min.js"integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="crossorigin="anonymous"></script>

    <script>
        let email = $('#email');

            $(document).ready(function () {
                $(".btn-primary").on('click',function () {
                if(email.val() !="") {

                    email.css("border","1px solid green");

                    $.ajax({
                        url:'forgotPassword.php',
                        method:'POST',
                        dataType:'json',
                        data:{
                            email:email.val()
                        }, success:function(response) {
                            if (!response.success) 
                                $("#response").html(response.msg).css('color','red');

                            else

                            $("#response").html(response.msg).css('color','green');
                        }
                    });

                } else
                    email.css("border","1px solid red");

            });
        });
    </script>
 </body>
 </html>
function.php
<?php 
    function generateNewString($len = 10) {
        $token = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789!$/()*';
        $token = str_shuffle($token);
        $token = substr($token, 0, $len);

        return $token;

    }

    function redirectToLoginPage() {
    header('location:login.php');
    exit();
}

 ?>

resetPassword.php

<?php
    require_once "function.php";

    if (isset($_GET['email']) && isset($_GET['token'])) {
        $conn = new mysqli('localhost','root','','register');

        $email = $conn->real_escape_string($_GET['email']);
        $token = $conn->real_escape_string($_GET['token']);

        $sql = $conn->query("SELECT id FROM users WHERE
            email='$email' AND token='$token' AND token<>'' AND tokenExpire > NOW()
        ");

        if ($sql->num_rows > 0) {
            $newPassword = generateNewString();
            $newPasswordEncrypted = password_hash($newPassword, PASSWORD_BCRYPT);
            $conn->query("UPDATE users SET token='', password = '$newPasswordEncrypted'
                WHERE email='$email'
            ");

            echo "Your New Password Is $newPassword<br><a href='login.php'>Click Here To Log In</a>";
        } else
            redirectToLoginPage();
    } else {
        redirectToLoginPage();
    }
?>

動作

https://youtu.be/oHF7gYZt0RA

手順

・大枠のアルゴリズムを考える

1.まず、パスワードリセット申請フォーム(以下forgotPassword.php)に入力されたEmailアドレスが、登録情報が登録されているテーブルに存在するか検証

2.検証の結果、存在するのであれば任意の時間制限付きのトークンを発行し、再登録フォームへのリンク誘導を添えたEmailを送信する。
(その12でやったEmail認証リンクの仕組みに時間制限トークンを導入する)

3.リンクをクリックした際にトークン及びEmail認証が正常に通った場合、ランダム文字列でパスワードを作りそれを新しいパスワードとして、成功メッセージとともに画面上に表示し、同時にそれをハッシュ化してデータベースに新しいパスワードとして登録し直す。

なお、通常の実装の場合はこれの後にユーザーがログインして任意のパスワードに自分で登録し直すかまたは、3の段階でユーザーに新しいパスワードを登録させることが考えられる。

・各段階でのアルゴリズムを考える

*データベースの接続などは割愛。

1.forgotPassword.php

・フォームに入力された文字列をエスケープする
・データベースにアクセスする
・エスケープした文字列を変数に代入する
・メールアドレスを検索フォームにし、入力されたメールアドレスがテーブルに存在する時はidを返す。存在しない時はエラーメッセージを表示する

・idが返ってきたらトークンを生成し、検索フォームをメールアドレスにし、データベースに再度アクセスして該当するメールアドレスが存在するデータに時間制限付きのトークンを発行する

・認証リンク付きのメールアドレスを送信し、送信完了の旨のメッセージを表示する。メールが送信できない場合はエラーメッセージを表示する

・PHPMaillerなどを用いて、テーブルに登録されたユーザー宛に認証リンクが記載されたメールを送信する。

2.function.php

・ランダム文字列を生成する処理とリダイレクト処理を書いておいて、requireなどで呼び出せるようにしていおく。
文字列生成とリダイレクトはこれまでのチュートリアルでやったことなのでここでは割愛。

3.resetPassword.php

その12 で作ったものと同じ処理をさせる。

・トークンとメールアドレスを取得する(認証リンクが踏まれているか確認する)。取得できたらデータベースに接続し、取得したメールアドレス及びトークンをエスケープして変数に代入する

・データベースに接続し、メールアドレス・トークン(空ではないという条件を付随する)・トークンに設定された有効期限が現在の時刻より先であるという条件で検索しする

・該当するデータが有ればデータベースに接続し、新しいパスワードを生成する。それをハッシュ化し、データのトークンを削除し、新しく生成したパスワードを登録する。該当するデータがない場合はログインフォームへリダイレクトする

・生成したパスワードとログインフォームへのリンクを表示する

今回のコードの注釈

tokenExpire周りについて
>query("UPDATE users SET token='$token',
        tokenExpire = DATE_ADD(NOW(), INTERVAL 5 MINUTE)
                        WHERE email = '$email'
                ");

今回新しく出てきたのはDATE_ADD(NOW(), INTERVAL)の形。
DATE_ADDは日付と時刻を取得する関数、第一引数に取得する日付及び時刻を設定する。
今回のNOW()は現在の日付と時刻を取得する、つまり処理が行われた時点での日付と時刻を取得することになる。
INTERVALには任意の時間を設定することで第一引数に指定した時刻から設定した分先の時間を取得する。
つまり、今回はINTERVALに5分と設定したので処理をが行われた時点の日付と時刻から5分先の時間を取得し、tokenExpaireに代入するということになる。

<>演算子
$sql = $conn->query("SELECT id FROM users WHERE
            email='$email' AND token='$token' AND token <> '' AND tokenExpire > NOW()
        ");


<>は左辺と右辺が等しくないということを表す比較演算子及び配列演算子。
今回は万が一他者にメールアドレスと認証リンク及び空のトークン利用されてパスワードをリセットされないようにするために用いている。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Webページの背景画像設定(HTML,CSS)~スマホとPCで画像を変える方法~

はじめに

今回はWebページ制作における背景画像の設定方法と端末ごとに表示する画像を変える方法について説明します.

Webページの背景画像の設定方法

HTMLファイル及びCSSファイルに以下のコードを記述します.

main.html
  <!--背景変更,headタグ内に記述-->
<link rel="stylesheet" href="common/css/sample.css" type="text/css">
sample.css
body {
background-image: url("./images/bg1.png");
background-repeat: no-repeat;
background-attachment: fixed;
background-position: center center;
background-size: cover;
}

各設定の説明

  • background-repeat:背景画像の繰り返し(repeatで繰り返し)
  • background-attachment:背景画像の固定・移動(scrollで移動)
  • background-position:背景画像の表示開始位置
  • background-size:背景画像のサイズ(自動的にサイズを合わせる)

他にもいろいろな設定が可能です.参考

端末ごとに表示する背景画像を変更

CSSファイルにて端末のサイズ(幅)ごとに表示画像を変更するコードを記述する.スマホは980px以下,PCは981px以上で設定をしています.

sample.css
@media only screen and (max-width:980px) {
body {
background-image: url("./images/bg1.png");
background-repeat: no-repeat;
background-attachment: fixed;
background-position: center center;
background-size: cover;
     }
}

@media only screen and (min-width:981px) {
body {
background-image: url("./images/bg2.png");
background-repeat: no-repeat;
background-attachment: fixed;
background-position: center center;
background-size: cover;
     }
}

おまけ

今回のように端末のサイズを調整し確認することを繰り返すなど,頻繁に繰り返していると,Webサイトへの反映がなかなかされない場合がある.そうなった場合はブラウザのキャッシュが原因の可能性が高いです.
キャッシュが残っていると,自動的に以前閲覧した,変更前のWebページを表示してしまいます.これを解決するには,キャッシュのクリアを行えばいいです.スマホなどの場合は履歴を消せば解決します.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む