こんにちは。
あるサイトの管理をしていて、表題の通り、公開・非公開日時指定がうまく動かない場合があることに気づきました。
色々調べたのですが、次のような理由ではないかと思います。一応修正ポイントも分かったのですが、どなたか追試と確認をしてみて頂けないでしょうか。閲覧数の多いサイトだと問題発生の確率が上がるので問題になると思います。なお、0.9.6以前のバージョン全てで発生します。
再現方法
1. 公開日時を指定する。
2. 公開日時に向けてサイトをリロード(公開設定のルーチンを起動するためです)。
3. 公開日時丁度に表示された場合、公開が失敗する。
公開設定をチェック、変更するルーチンは /manager/includes/document.parser.class.inc.php の中にある checkPublishStatus のようです。このルーチンはフロントでもバックエンドでリロードや表示の度に呼び出されています。
/assets/cache/ にある Publishing.idx.php というファイルに次の直近の公開設定変更時間がunix時間で記録されていて、このルーチンはこの値と現在の時刻を比較し、現在時刻が直近変更時間を過ぎている場合、次のようなクエリを発生させます。
更新時間の比較は次の通り
if ($cacheRefreshTime <= $timeNow && $cacheRefreshTime != 0) {
公開のSQLは
$sql = "UPDATE ".$this->getFullTableName("site_content")." SET published=1, publishedon=".time()." WHERE ".$this->getFullTableName("site_content").".pub_date < $timeNow AND ".$this->getFullTableName("site_content").".pub_date!=0 AND published=0";
非公開は
$sql= "UPDATE " . $this->getFullTableName("site_content") . " SET published=0, publishedon=0 WHERE " . $this->getFullTableName("site_content") . ".unpub_date < $timeNow AND " . $this->getFullTableName("site_content") . ".unpub_date!=0 AND published=1";
更新時間の比較のところで下線にしてある部分は 前述の直近の更新時間と現在時間を比べていますが、<=としています。つまり丁度直近時間にリロードがかかった場合でも先のルーチンが呼び出されます。
sqlでは WHERE pub_date < $timenow としているので、$cacheRefreshTime と pub_date、$timenow が一致していた場合、ルーチンは呼び出されるもののの、UPDATEが予定通りの結果を返さない場合が出てくると思います。
つまり...
1. 14:00:00 に公開予定(pub_date=14:00:00、$cacheRefreshTime内の設定も14:00:00に)
2. 14:00:00丁度にリロード($timenow=14:00:00で、$cacheRefreshTime <= $timeNow が成立)
3. UPDATEが実行されるが pub_date = $timeNow は成立しても pub_date < $timeNow は成立せず。
4. pub_dateのドキュメントは公開設定にならない。
ということになります。
この後の処理として、$cacheRefreshTime の時刻が次の公開・非公開予定の時刻かイベント無し(0)にされるので、次のスケジュールが有ればその時に今回駄目だったものが表示される場合があります。イベントがないとずっと出てこなくなると思います。
修正部分は先の2箇所のSQLの下線部分です。
$sql = "UPDATE ".$this->getFullTableName("site_content")." SET published=1, publishedon=".time()." WHERE ".$this->getFullTableName("site_content").".pub_date <= $timeNow AND ".$this->getFullTableName("site_content").".pub_date!=0 AND published=0";
と
$sql= "UPDATE " . $this->getFullTableName("site_content") . " SET published=0, publishedon=0 WHERE " . $this->getFullTableName("site_content") . ".unpub_date <= $timeNow AND " . $this->getFullTableName("site_content") . ".unpub_date!=0 AND published=1";
上記の説明通りならば、これで問題が無くなると思います。どなたかの確認していただければ、bug trackerに申請しようと思いますのでお願いいたします。