We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 36592
    • 970 Posts
    勉強になります。

    2つほど気づいた点が...

    1)editedonの値について
    私の記憶が確かなら、editedonの値はドキュメントの作成直後は「0」になっているはずです。すなわち、作成したっきりで一度も更新されていないドキュメントがあると、そのeditedonの値は「0」となってしまいます。単純にeditedonの最大値を持ってくるのではちょっと不十分で、editedonが「0」のドキュメントはcreatedonを使って最大値判断を行わないといけないのではないでしょうか?

    2)対象ドキュメントの絞込みについて
    ドキュメントには、ログイン専用とか検索結果表示用のがあったりして、それらのドキュメントはRSSには含みたくないのではないでしょうか? 単純に「公開済み+削除されていない」という条件だけだと、これらのドキュメントも対象に入ってしまいますので、除外する策を別途考えないといけないのではないでしょうか?
      • 6350
      • 421 Posts
      Quote from: tkfm at Mar 09, 2009, 10:55 PM

      1)editedonの値について
      私の記憶が確かなら、editedonの値はドキュメントの作成直後は「0」になっているはずです。すなわち、作成したっきりで一度も更新されていないドキュメントがあると、そのeditedonの値は「0」となってしまいます。単純にeditedonの最大値を持ってくるのではちょっと不十分で、editedonが「0」のドキュメントはcreatedonを使って最大値判断を行わないといけないのではないでしょうか?
      ドキュメント作成時に管理画面および FDM からの作成時には editedon は作成時の日時が入ります。
      Add Blog Entry などの NewsPublisher から作成した場合には 0 になってしまいますがこれは常識から考えて「バグ」のようですね。日本語版リリース時には修正をお願いしたいと思います。

      Quote from: tkfm at Mar 09, 2009, 10:55 PM

      2)対象ドキュメントの絞込みについて
      ドキュメントには、ログイン専用とか検索結果表示用のがあったりして、それらのドキュメントはRSSには含みたくないのではないでしょうか? 単純に「公開済み+削除されていない」という条件だけだと、これらのドキュメントも対象に入ってしまいますので、除外する策を別途考えないといけないのではないでしょうか?
      ログイン専用、検索結果の表示用に関しては常に最新の更新日時とはならないという前提で使ってください。 laugh
      そのほかロール権限などの処理を入れると難しくなるので必要な人が処理を追加できるように見通しのよいショートプログラム、雛形として扱ってください。
      機能が追加できたら ・・・ 僕にもください (^。^)y-.。o○
        • 6350
        • 421 Posts
        ・・・ つづき laugh
        rss.format.inc.php に最新日時を渡すために共有変数 << (勝手に命名)を使ってみますた。
        まずはプラグインで最新更新日時を $modx->config[’latest’] にセットします。
        case 11:            // RSS 出力対象のドキュメント最新時刻と ETag を出力する (階層は1レベルのみに限定)
              $sql= "SELECT MAX(editedon) AS latest FROM ".$modx->getFullTableName("site_content")."WHERE published=1 AND deleted=0 AND parent=2";
              $result= $modx->dbQuery($sql);
              $tmpRow= $modx->fetchRow($result);
              $latest = $tmpRow['latest'];
              $etag = '"' . md5($latest) . '"';
              header("Last-Modified: " . gmdate('r', $latest) . " GMT");
              header("ETag: $etag");
              header("X-Powered-By: dainiti");    // これがすごい
              $modx->config['latest'] = $latest; // 今回はここを追加する
              break;
        }

        rss.format.inc.php の先頭で共有変数を受け取りプレースフォルダにセットする。
        global $modx;
        $rss_placeholders['rss_lastmodified'] = gmdate('r', $modx->config['latest']);

        表示用のテンプレートに [+rss_lastmodified+] を追加する。ただしこの処理は必須ではないはずなのでこだわりたい人のみに推薦。
        $rss_header = <<<TPL
        <?xml version="1.0" encoding="[+rss_charset+]" ?>
        [+rss_xsl+]
        <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
        <channel>
        <title>[*pagetitle*]</title>
        <link>[+rss_link+]</link>
        <description>[*description*]</description>
        <pubDate>[+rss_lastmodified+]</pubDate>
        <language>[+rss_lang+]</language>
        <copyright>[+rss_copyright+]</copyright>
        <ttl>[+rss_ttl+]</ttl>
        TPL;
        modx の更新毎にこんなことしてたら忘れそうなので今後の modx 品質向上に期待 (^。^)y-.。o○
          • 6350
          • 421 Posts
          追記・・・
          Quote from: tkfm at Mar 09, 2009, 10:55 PM

          2)対象ドキュメントの絞込みについて
          ドキュメントには、ログイン専用とか検索結果表示用のがあったりして、それらのドキュメントはRSSには含みたくないのではないでしょうか? 単純に「公開済み+削除されていない」という条件だけだと、これらのドキュメントも対象に入ってしまいますので、除外する策を別途考えないといけないのではないでしょうか?
          よくよく考えれば更新日時が公開されるだけで、それ自体は間違いのない事実でありドキュメントの内容まで表示されるわけではないので実運用上はまったく支障はないと思います。
            • 6350
            • 421 Posts
            最後の詰めとして記事の作成時刻を +0000 GMT として統一する。

            オリジナル rss.format.inc.php の 91行目を次のように変更する。
            return date("r",  intval($resource[$dateSource]) + $modx->config["server_offset_time"]);
                                                      ↓
            return gmdate("r",  intval($resource[$dateSource]));

            これも僕のよう web標準にこだわりのある人のみお勧めです。たぶんローカル時刻でも問題なさそうですが (^。^)y-.。o○

            記事の時刻を編集時刻にする必然性はないが必要があれば80行目を editedon に変更する。
                                                                                                                            ↑
            $GLOBALS["dateSource"] = isset($dateSource) ? $dateSource : "createdon";

            以上でこのトピックの主題 304 が解決したわけではありませんが今後の課題として 304 の必然性を考えていきたいと思います。 smiley


            なお、今回は ditto の扱いに不慣れなため更新日を取ってくる方法がわからなかったので安易なクエリー発行という手段になりましたが ditto をつかえばこういう風にできるという例がありましたらご教授お願いします。
              • 36592
              • 970 Posts
              Quote from: dainiti at Mar 10, 2009, 02:17 AM

              ditto をつかえばこういう風にできるという例がありましたらご教授お願いします。
              ソースの一部になりますが雰囲気は分かりそうなレベルで... こんな感じでどうでしょ?
              case 11:
                  $latest=$modx->runSnippet('Ditto', array( 'parents'=>'2','display'=>'1', 'tpl'=>'@CODE:[+editedon+]','orderBy'=>'editedon DESC') );
                  $etag = '"' . md5($latest) . '"';
                  header("Last-Modified: " . gmdate('r', $latest) . " GMT");
                  header("ETag: $etag");
                  break;
              
                • 6350
                • 421 Posts
                ありがとうございます。ずいぶんシンプルになりますね。

                できれば上位の一件だけ取ってくるように指定できれば効率がよさそうですが?
                あ、’display’=>’1’ これが上位一件の指定ですか
                ’total’=>’1’ で取得数を制限したほうが効率はよさそうですね

                親を複数指定したい場合にはやはり Ditto に限りますね ’parents’=>’2,50,60’

                あと、 Ditto を使った場合にはすべての階層が取得できるんでしょうか、そういう使い方はまずないと思いますが・・・depth を指定しなければできるということですね。なんて簡単なんだ!!sql ではどう書けばいいのか見当も付かない

                もうひとつ知りたいことあるんですが今回 $modx->config[’latest’] =$latest; のように変数をセットした場合、別の場所で取出しができていたと思うんですが今回の条件では rss.format.inc.php にわたっていないようです。

                同じように環境変数を使っても rss.format.inc.php ないでは取り出せないことに気が付きました。
                プラグインの中で設定した変数はなにか条件があるんでしょうか。根本的に間違っているとか? undecided
                  • 6350
                  • 421 Posts
                  RSS の出力だけならプラグインでなくても大丈夫なようです。
                  次のコードを rss.format.inc.php の先頭に挿入します。$latest 変数の受け渡しが不要になるのでその点は楽です。
                  プラグインとしての利用が一番楽ですがスニペットでの呼び出しでも問題なく、動作確認済みです。
                  	$latest=$modx->runSnippet('Ditto', array( 'parents'=>'2','display'=>'1', 'tpl'=>'@CODE:[+editedon+]','orderBy'=>'editedon DESC') );
                  	$etag = '"' . md5($latest) . '"';
                  	header("Last-Modified: " . gmdate('r', $latest) . " GMT");
                  	header("ETag: $etag");
                  	$rss_placeholders['rss_lastmodified'] = gmdate('r', $latest);
                  

                  [+rss_lastmodified+] の行をテンプレート定義部分に追加します。
                  $rss_header = <<<TPL
                  <?xml version="1.0" encoding="[+rss_charset+]" ?>
                  [+rss_xsl+]
                  <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
                  <channel>
                  <title>[*pagetitle*]</title>
                  <link>[+rss_link+]</link>
                  <description>[*description*]</description>
                  <pubDate>[+rss_lastmodified+]</pubDate>
                  <language>[+rss_lang+]</language>
                  <copyright>[+rss_copyright+]</copyright>
                  <ttl>[+rss_ttl+]</ttl>
                  TPL;
                    • 6350
                    • 421 Posts
                    以下のコードは最新の勉強結果を少々手抜きで作成しました。なので、突っ込みなしでお願いします。
                    やっとこさ、304に対応できた自信作です。
                    http://web-sniffer.net/ のような前回の読み込みをキャッシュしていない webチェッカーからのアクセスでは常に 200 OK となります。

                    今回は Firefoxのプラグイン Live HTTP Headers
                    https://addons.mozilla.org/ja/firefox/addon/3829 を使って確認しました。クライアントとなるパソコンは正確に時計を合わせておく必要があります。

                    304 を返すことで大規模でアクセスの多いサイトでは未対応のサイトよりパケットが半減するほどの効果があるはずです。静的 html で作っていたころはこんな勉強しなくても Apache がかってにやってくれてたらしいですが?
                    $site_start = $modx->config['site_start'];
                    switch ($modx->documentObject['id']) {
                    case $site_start:	// ドキュメント以外にカウンタ、最新情報などを含むページ
                    	$modx_last_modified = $modx->runSnippet('Ditto', array( 'parents'=>'0','display'=>'1', 'tpl'=>'@CODE:[+editedon+]','orderBy'=>'editedon DESC') );
                    	$client_last_modified = empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? '' : trim($_SERVER['HTTP_IF_MODIFIED_SINCE']);
                    	$client_modified_timestamp = $client_last_modified ? strtotime($client_last_modified) : 0;
                    	if ( ($client_last_modified) ? ($client_modified_timestamp >= $modx_last_modified) : 0 ) {
                    		header("HTTP/1.1 304 Not Modified");
                    		exit;
                    	}
                    	header("Last-Modified: " . gmdate('r', $modx_last_modified) . " GMT");
                    	break;
                    case 11:		// RSS のページは常に 200 OK ETag は飾り
                    	$modx_last_modified = $modx->runSnippet('Ditto', array( 'parents'=>'2','display'=>'1', 'tpl'=>'@CODE:[+editedon+]','orderBy'=>'editedon DESC') );
                    	$etag = '"' . md5($modx_last_modified) . '"';
                    	header("Last-Modified: " . gmdate('r', $modx_last_modified) . " GMT");
                    	header("ETag: $etag");
                    	break;
                    default:		// ドキュメント自身が最終更新日となるページ
                    	$modx_last_modified = $modx->documentObject["editedon"];
                    	$client_last_modified = empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? '' : trim($_SERVER['HTTP_IF_MODIFIED_SINCE']);
                    	$client_modified_timestamp = $client_last_modified ? strtotime($client_last_modified) : 0;
                    	if ( ($client_last_modified) ? ($client_modified_timestamp >= $modx_last_modified) : 0 ) {
                    		header("HTTP/1.1 304 Not Modified");
                    		exit;
                    	}
                    	header("Last-Modified: " . gmdate('r', $modx_last_modified) . " GMT");
                    }

                    ETag は日本語ページと英語ページを同じ URI でアクセスしているような場合、更新時刻も同じような場合のキャッシュ制御で使用するのが効果的なようです。携帯表示と PC表示もドキュメントの更新時刻は同じですから違う ETag を返すのがいいらしいです。ログイン、ログアウト状態の場合でも同様ですがその場合は SESSION Cookie でどうにかなっているようです。

                    ETag に関しては引き続き勉強中  laugh
                      • 6350
                      • 421 Posts
                      これまでに悟ったこと

                      (1)Last-Modified を受け取ったドキュメントに対して、ブラウザはオフラインデータを作成し次回以降の
                         アクセスではリクエストヘッダを送信しない
                      (2)sakura サーバーは Last-Modified を送った後のドキュメントをリロードされた場合に自動的に 304 を返す。
                         coreserver は 200 OK を返す (・・?
                      (3)Last-Modified と ETag の両方を返した場合にはリロードで 200 OK が返される。
                         それで RSS 出力で ETag を指定している意味がわかった。ただの飾りではないことが ・・・
                      (4)リロードしても 304 が返ってくるので新しいドキュメントが追加されても反映できない。
                      (5)ブラウザのオフラインデータを削除するにはサーバから Expires とか Cache-Control: no-store, no-cache
                         などを送る必要があるが管理画面までは改造したり解決する能力がない => 僕
                      (6)結局、常に 200 OK を返すのが一番楽な方法だということがわかった

                      以上のような理由でこのタイトルの 304 はよく理解できたのですが、そこから先でアリ地獄が待ち構えているようなのでこの件はそろそろリタイヤということになりそうです。