PowerCMS X ブログ
2025-06-09
PowerCMS ver.4.0で「VideoCaptions」が大幅に機能強化され、AI字幕作成や簡易動画編集などの機能が追加されました。
ストリーミング形式(HLS)への変換などもサポートされており、PowerCMS Xは動画 CMSとしても利用できるようになりました。
動画サイトの運営に関するリクエストに「編集中の動画に対する閲覧制限をしたい」「動画が外部に漏洩しないようにしたい」といったものがあります。
動画に閲覧制限をかける方法をいくつかご紹介します。
プラグイン「Members」による会員限定サイトを運営していることを前提とします。
Require all denied
ErrorDocument 403 /viewer.php
まず、/media/ディレクトリへのアクセスを制限します。特定の拡張子のファイルを対象にしても良いですが、ここでは全てのファイルに対して制限をかけます。ErrorDocument 403 /viewer.php を指定して、リクエストを PHPへ渡すようにします。
<?php
$base_path = '/opt/homebrew/var/www/powercmsx/'; // PowerCMS Xのディレクトリ
require_once( $base_path.'class.Prototype.php' );
$app = new Prototype();
$app->init();
$bootstrapper = new PTViewer();
if (!isset( $_SERVER['REDIRECT_URL'] ) ) {
// /viewer.phpへ直接アクセスがあった時
$bootstrapper->page_not_found( $app );
}
$redirect_url = $_SERVER['REDIRECT_URL'];
$dirname = dirname( $redirect_url );
$dir_name = '/' . $app->sanitize_dir( $dirname );
$file_name = basename( $redirect_url );
$file_name = $app->sanitize_file( $file_name );
$file_path = $app->document_root . $dir_name . $file_name;
if (! file_exists( $file_path ) ) {
// ファイルが存在しない
$bootstrapper->page_not_found( $app );
}
$obj = $app->db->model( 'upload_file' )->get_by_key( ['file_path' => $file_path ] );
if (! $obj->id ) {
// ファイルオブジェクトが存在しない
$bootstrapper->page_not_found( $app );
}
// 例えばファイルオブジェクトに statusカラムを追加している時
if ( $obj->status != 4 ) {
// ステータスが公開でない時
$bootstrapper->page_not_found( $app );
}
$member = $app->user( 'member' );
if (! $member ) {
// 会員サイトのログインチェック
$bootstrapper->page_not_found( $app, $obj->workspace, $app->translate( 'Permission denied.' ) );
}
// 必要に応じてメンバーの属性などをチェック
header( 'HTTP/1.1 200 OK' ); // 403ヘッダーを上書きする
$mime_type = $obj->mime_type;
header( "Content-Type: {$mime_type}" );
readfile( $file_path ); // ファイル出力
exit();
$bootstrapper->page_not_found( $app ); の結果はダイナミック・パブリッシュングのエラー表示となります。
readfileや echo file_get_contents( file_path );でも動画をブラウザへ返すことはできますが、
PHPを介して動画レスポンスを返していると大きなファイルサイズの動画へのリクエストが集中したりするとサーバーの負荷が一気に上がってしまいます。
そこで、mod_xsendfile を利用して、レスポンスはウェブサーバー(Apache)に任せてしまい、PHPでは権限のチェックのみを行うことにします。
mod_xsendfile.soをビルドしてサーバーに設置、httpd.confを編集します(編集後 httpdを再起動)。
LoadModule xsendfile_module lib/httpd/modules/mod_xsendfile.so
<Directory "/var/www">
XSendFile On
XSendFilePath "/var/www"
</Directory>
以下の部分を修正します。
header( 'HTTP/1.1 200 OK' ); // 403ヘッダーを上書きする
$mime_type = $obj->mime_type;
header( "Content-Type: {$mime_type}" );
readfile( $file_path ); // ファイル出力
exit();
ここを以下のように変更します。readfileを使わないため PHPでは権限のチェックのみを行うことで負荷を下げることができます。
header( 'HTTP/1.1 200 OK' ); // 403ヘッダーを上書きする
$mime_type = $obj->mime_type;
header( "Content-Type: {$mime_type}" );
header( "Content-Disposition: inline; filename=\"{$file_name}\"" );
header( "X-Sendfile: {$file_path}" ); // Apacheによるファイル出力
exit();
会員ではなく、管理画面へのログイン状況とそのファイルオブジェクトの編集権限によって制限をかける方法です。
<?php
$base_path = '/var/www/powercmsx/'; // PowerCMS Xのディレクトリ
require_once( $base_path.'class.Prototype.php' );
$app = new Prototype();
$app->init();
$bootstrapper = new PTViewer();
if (!isset( $_SERVER['REDIRECT_URL'] ) ) {
// /viewer.phpへ直接アクセスがあった時
$bootstrapper->page_not_found( $app );
}
$redirect_url = $_SERVER['REDIRECT_URL'];
$dirname = dirname( $redirect_url );
$dir_name = '/' . $app->sanitize_dir( $dirname );
$file_name = basename( $redirect_url );
$file_name = $app->sanitize_file( $file_name );
$file_path = $app->document_root . $dir_name . $file_name;
if (! file_exists( $file_path ) ) {
// ファイルが存在しない
$bootstrapper->page_not_found( $app );
}
$obj = $app->db->model( 'upload_file' )->get_by_key( ['file_path' => $file_path ] );
if (! $obj->id ) {
// ファイルオブジェクトが存在しない
$bootstrapper->page_not_found( $app );
}
$user = $app->user();
if (! $user ) {
// 管理画面にログインしていない
$bootstrapper->page_not_found( $app, $obj->workspace, $app->translate( 'Permission denied.' ) );
}
if ( $obj->status != 4 ) {
// ステータスが公開でない時
$can_do = $app->can_do( 'upload_file', 'edit', $obj );
if (! $can_do ) {
// 編集権限がない
$app->error( 'Permission denied.' );
}
}
header( 'HTTP/1.1 200 OK' );
$mime_type = $obj->mime_type;
header( "Content-Type: {$mime_type}" );
header( "Content-Disposition: inline; filename=\"{$file_name}\"" );
header( "X-Sendfile: {$file_path}" );
exit();
$app->error( 'Permission denied.' ); の結果は、管理画面の権限エラーメッセージとなります。