たけぼーの備忘録

暇を持て余した人間の無意味なブログ

おしゃれなギャラリーが作れるCMS「Lychee」を使ってみた

Photo Gallery
こんにちは。今日はWebの話題です。

みなさん、CMSってご存じですか?

Contents Management Systemの略で、htmlを直接編集することなく、ブログやホームページが作れるシステムのことです。
世界中で広く利用されている「WordPress」は、このCMSの一種です。

WordPressなら、ブログを書くときにいちいちhtmlをまるごと制作する、なんてことはないですよね?
これがCMSの特徴です。

CMSは、WordPress以外にもDrupalConcrete5など様々な種類があります。
余談ですが、個人的にはConcrete5が使いやすくて好きだったりします。

今日は、このCMSの中から「Lychee」をご紹介します。

Lycheeとは

タイトルに合ったようにLycheeはギャラリーを作るためのものです。

ギャラリーとは、写真を一覧で表示して、画像をクリックすると拡大される、といった機能を持つものです。
スマホの中にも写真を見るためのギャラリーアプリが入っていますよね。

写真をメインで扱うようなサイトでは、なるべく使いやすく、きれいに見せたいところですよね。
そんな望みを叶えてくれるのが、Lycheeです。

WordPressプラグインをいれてギャラリーを作ってもいいのですが、そうすると大掛かりなシステムになりますよね。
ちょっと写真が見れるサイトを作りたいんだ、なんて時には面倒だったりします。

ということで、Lycheeが活用されるのです。

lychee.electerious.com

Lycheeは、主にPHPjavascriptで構成されており、写真の管理にMySQLが必要です。
WordPressがインストールできるサーバーであれば、Lycheeもインストール可能です。

インストール手順はとても簡単です。
まず、Lycheeのgithubにアクセスし、ファイルをダウンロードします。
git cloneしても構いません。
github.com

このファイルを解凍し、インストールしたいディレクトリに配置して、その場所にアクセスします。

あとは画面に従って、MySQLのユーザー名やパスワードなど必要な情報を入力し、管理者用パスワードを設定すれば完了です。

詳しい利用方法は、READMEの中に書かれています。
直感的に使い方がわかるので、迷うこともないと思います。

Lycheeの良い点・悪い点

Lycheeの良いところは、シンプルさと美しさです。

ギャラリーを作るCMSは、レイアウトが残念で全近代的なビジュアルだったりしますが、Lycheeは黒を基調としたシンプルなデザインです。
また、それでいて必要な機能はしっかり備わっています。
写真を一括でzipにしてダウンロードしたり、写真1枚1枚を全画面表示したり、名前を変更したり共有したり。
さらにはパスワードによって写真を保護する、なんてこともできます。
基本的な機能は揃っていて、便利です。

今まで3日ほど私が使ってきて困ったことがいくつかありました。
まず、パスワード保護に関するものです。
パスワードによって写真を保護している場合、フォルダ内の写真を見るたびにパスワードを求められることがあります。
写真を1枚見るごとにパスワードを求められ、非常に面倒でした。

この原因はchromeのパスワード保存機能が原因でした。
管理者用のアカウント情報をchromeに記憶させていたことで、このような面倒な現象が起こるようです。

また、写真の一括ダウンロードですが、写真の数が多くなるとうまく動かなくなりました。

原因は、zipファイルの送信にありました。
一括ダウンロードでは、毎回phpにてzipファイルの作成が行われ、そのまま送信されるのですが、これがファイルサイズを考慮しない作りとなっているようです。
サーバーのメモリ不足によって転送ができなくなるようです。

php/Module/Album.php内のgetArchive()を以下のように編集すると直りました。

<?php
public function getArchive() {

	// Check dependencies
	Validator::required(isset($this->albumIDs), __METHOD__);

	// Call plugins
	Plugins::get()->activate(__METHOD__, 0, func_get_args());
	// Illicit chars
	$badChars =	array_merge(
		array_map('chr', range(0,31)),
		array("<", ">", ":", '"', "/", "\\", "|", "?", "*")
	);

	// Photos query
	switch($this->albumIDs) {
		case 's':
			$photos   = Database::prepare(Database::get(), 'SELECT title, url FROM ? WHERE public = 1', array(LYCHEE_TABLE_PHOTOS));
			$zipTitle = 'Public';
			break;
		case 'f':
			$photos   = Database::prepare(Database::get(), 'SELECT title, url FROM ? WHERE star = 1', array(LYCHEE_TABLE_PHOTOS));
			$zipTitle = 'Starred';
			break;
		case 'r':
			$photos   = Database::prepare(Database::get(), 'SELECT title, url FROM ? WHERE LEFT(id, 10) >= unix_timestamp(DATE_SUB(NOW(), INTERVAL 1 DAY)) GROUP BY checksum', array(LYCHEE_TABLE_PHOTOS));
			$zipTitle = 'Recent';
			break;
		default:
			$photos   = Database::prepare(Database::get(), "SELECT title, url FROM ? WHERE album = '?'", array(LYCHEE_TABLE_PHOTOS, $this->albumIDs));
			$zipTitle = 'Unsorted';
	}
	// Get title from database when album is not a SmartAlbum
	if ($this->albumIDs!=0&&is_numeric($this->albumIDs)) {

		$query = Database::prepare(Database::get(), "SELECT title FROM ? WHERE id = '?' LIMIT 1", array(LYCHEE_TABLE_ALBUMS, $this->albumIDs));
		$album = Database::execute(Database::get(), $query, __METHOD__, __LINE__);

		if ($album===false) return false;
	
		// Get album object
		$album = $album->fetch_object();

		// Album not found?
		if ($album===null) {
			Log::error(Database::get(), __METHOD__, __LINE__, 'Could not find specified album');
			return false;
		}

		// Set title
		$zipTitle = $album->title;

	}

	// Escape title
	$zipTitle = str_replace($badChars, '', $zipTitle);

	$filename = LYCHEE_DATA . $zipTitle . '.zip';
		
	// Create zip
	$zip = new ZipArchive();
	if ($zip->open($filename, ZIPARCHIVE::CREATE)!==TRUE) {
		Log::error(Database::get(), __METHOD__, __LINE__, 'Could not create ZipArchive');
		return false;
	}

	// Execute query
	$photos = Database::execute(Database::get(), $photos, __METHOD__, __LINE__);

	// Check if album empty
	if ($photos->num_rows==0) {
		Log::error(Database::get(), __METHOD__, __LINE__, 'Could not create ZipArchive without images');
		return false;
	}

	// Parse each path
	$files = array();
	while ($photo = $photos->fetch_object()) {

		// Parse url
		$photo->url = LYCHEE_UPLOADS_BIG . $photo->url;

		// Parse title
		$photo->title = str_replace($badChars, '', $photo->title);
		if (!isset($photo->title)||$photo->title==='') $photo->title = 'Untitled';

		// Check if readable
		if (!@is_readable($photo->url)) continue;

		// Get extension of image
		$extension = getExtension($photo->url, false);

		// Set title for photo
		$zipFileName = $zipTitle . '/' . $photo->title . $extension;

		// Check for duplicates
		
		if (!empty($files)) {
			$i = 1;
			while (in_array($zipFileName, $files)) {
				// Set new title for photo
				$zipFileName = $zipTitle . '/' . $photo->title . '-' . $i . $extension;

				$i++;

			}
		}

		// Add to array
		$files[] = $zipFileName;

		// Add photo to zip
		$zip->addFile($photo->url, $zipFileName);

	}
	// Finish zip
	$zip->close();

	// Send zip
	header("Content-Type: application/zip");
	header("Content-Disposition: attachment; filename=\"$zipTitle.zip\"");
	header("Content-Length: " . filesize($filename));

	//変更ここから
	while (ob_get_level() > 0) {
		ob_end_clean();
	}
	ob_start();

	$file = fopen($filename, 'rb');
	if(!$file) {
		exit;
	}
	while(!feof($file) and (connection_status() == 0)) {
		 echo fread($file, '1048576'); 
		 ob_flush();
		
	  }
	ob_flush();
	ob_end_clean();
	//変更ここまで

	// Delete zip
	unlink($filename);

	// Call plugins
	Plugins::get()->activate(__METHOD__, 1, func_get_args());

	return true;

}

こちらの記事を参考にさせていただきました。ありがとうございます。
every-rating.com

最後に

いかがでしょうか。不具合が多々あるので、実用可能ではないかもしれませんが、おしゃれで使い勝手がいいので、一度使ってみてはいかがでしょうか。

ではまた。