Add diagnostics to check integrity of data folder master
Add diagnostics to check integrity of data folder

<?php <?php
include_once("config.inc.php"); include_once("config.inc.php");
include("php-calendar.lib.php"); include("php-calendar.lib.php");
function include_header($title) { function include_header($title) {
?> ?>
<!doctype html> <!doctype html>
<!--[if lt IE 7]> <html class="no-js ie6 oldie" lang="en"> <![endif]--> <!--[if lt IE 7]> <html class="no-js ie6 oldie" lang="en"> <![endif]-->
<!--[if IE 7]> <html class="no-js ie7 oldie" lang="en"> <![endif]--> <!--[if IE 7]> <html class="no-js ie7 oldie" lang="en"> <![endif]-->
<!--[if IE 8]> <html class="no-js ie8 oldie" lang="en"> <![endif]--> <!--[if IE 8]> <html class="no-js ie8 oldie" lang="en"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]--> <!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]-->
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title></title> <title></title>
<meta name="description" content=""> <meta name="description" content="">
<meta name="author" content=""> <meta name="author" content="">
<meta name="viewport" content="width=device-width,initial-scale=1"> <meta name="viewport" content="width=device-width,initial-scale=1">
<!-- CSS concatenated and minified via ant build script--> <!-- CSS concatenated and minified via ant build script-->
<link rel="stylesheet" href="css/style.css"> <link rel="stylesheet" href="css/style.css">
<!-- end CSS--> <!-- end CSS-->
<script src="js/libs/modernizr-2.0.6.min.js"></script> <script src="js/libs/modernizr-2.0.6.min.js"></script>
</head> </head>
<body> <body>
<div id="container"> <div id="container">
<header> <header>
</header> </header>
<div id="main" role="main"> <div id="main" role="main">
<?php <?php
} }
function include_footer() { function include_footer() {
?> ?>
</div> </div>
<footer> <footer>
</footer> </footer>
</div> <!--! end of #container --> </div> <!--! end of #container -->
<!-- Grab Google CDN's jQuery, with a protocol relative URL; fall back to local if necessary --> <!-- Grab Google CDN's jQuery, with a protocol relative URL; fall back to local if necessary -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.js"></script> <script src="//ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.js"></script>
<script>window.jQuery || document.write('<script src="js/libs/jquery-1.5.1.min.js">\x3C/script>')</script> <script>window.jQuery || document.write('<script src="js/libs/jquery-1.5.1.min.js">\x3C/script>')</script>
<script type="text/javascript" src="js/jquery.imagefit.js"></script> <script type="text/javascript" src="js/jquery.imagefit.js"></script>
<script> <script>
$(window).load(function(){ $(window).load(function(){
$('.col1').imagefit(); $('.col1').imagefit();
}); });
</script> </script>
</body> </body>
</html> </html>
<?php <?php
} }
function include_sidebar() { function include_sidebar() {
?> ?>
<a href="upload.php">Upload a new day</a><br> <a href="upload.php">Upload a new day</a><br>
<a href="help.php">Help/Instructions</a><br> <a href="help.php">Help/Instructions</a><br>
<hr> <hr>
<?php <?php
foreach (getCalendarMonths() as $month) { foreach (getCalendarMonths() as $month) {
echo generate_calendar($month['year'], $month['month'], getCalendarDays($month['year'], $month['month']), 3); echo generate_calendar($month['year'], $month['month'], getCalendarDays($month['year'], $month['month']), 3);
} }
} }
function getNextAvailableDate() { function getNextAvailableDate() {
if ($previousDate = getPreviousDate()) { if ($previousDate = getPreviousDate()) {
$nextDayTime = strtotime("+1 day", strtotime($previousDate)); $nextDayTime = strtotime("+1 day", strtotime($previousDate));
if (date("m", $nextDayTime) == "08" and date("d", $nextDayTime) == "24") { if (date("m", $nextDayTime) == "08" and date("d", $nextDayTime) == "24") {
// skip the 24th of August // skip the 24th of August
$nextDayTime = strtotime("+1 day", $nextDayTime); $nextDayTime = strtotime("+1 day", $nextDayTime);
} }
return date("Y-m-d", $nextDayTime); return date("Y-m-d", $nextDayTime);
} else { } else {
return START_DATE; return START_DATE;
} }
} }
function getDateFromFilename($filename) { function getDateFromFilename($filename) {
$fnameParts = explode(".", $filename); $fnameParts = explode(".", $filename);
return $fnameParts[0]; return $fnameParts[0];
} }
function getFilenameForDate($date, $fileext = "") { function getFilenameForDate($date, $fileext = "") {
$results = glob(DATA_DIR . "/" . $date . "*" . $fileext); $results = glob(DATA_DIR . "/" . $date . "*" . $fileext);
if (sizeof($results) > 0) { if (sizeof($results) > 0) {
return $results[0]; return $results[0];
} else } else
return false; return false;
} }
function getPreviousDate() { function getPreviousDate() {
$datedFiles = scandir(DATA_DIR); // sorted descendingly by default $datedFiles = scandir(DATA_DIR); // sorted descendingly by default
if (sizeof($datedFiles) > 2) {// always at least 2 even for an empty folder because of ./ and ../ if (sizeof($datedFiles) > 2) {// always at least 2 even for an empty folder because of ./ and ../
return getDateFromFilename($datedFiles[sizeof($datedFiles) - 1]); return getDateFromFilename($datedFiles[sizeof($datedFiles) - 1]);
} else { } else {
return false; return false;
} }
} }
  function getFirstDate() {
  // not romance help
  $datedFiles = scandir(DATA_DIR); // sorted descendingly by default
  if (sizeof($datedFiles) > 2) {// always at least 2 even for an empty folder because of ./ and ../
  return getDateFromFilename($datedFiles[2]);
  } else {
  return false;
  }
  }
function getPhoto($displayDate) { function getPhoto($displayDate) {
$cacheFile = CACHE_DIR . $displayDate . ".png"; $cacheFile = CACHE_DIR . $displayDate . ".png";
if (file_exists($cacheFile)) { if (file_exists($cacheFile)) {
return $cacheFile; return $cacheFile;
} else { } else {
if ($fname = getFilenameForDate($displayDate, ".jpg")) { if ($fname = getFilenameForDate($displayDate, ".jpg")) {
$source_gd_image = imagecreatefromjpeg($fname); $source_gd_image = imagecreatefromjpeg($fname);
} else if ($fname = getFilenameForDate($displayDate, ".png")) { } else if ($fname = getFilenameForDate($displayDate, ".png")) {
$source_gd_image = imagecreatefrompng($fname); $source_gd_image = imagecreatefrompng($fname);
} else { } else {
return false; return false;
} }
if ($source_gd_image === false) { if ($source_gd_image === false) {
return false; return false;
} }
$source_image_width = imagesx($source_gd_image); $source_image_width = imagesx($source_gd_image);
$source_image_height = imagesy($source_gd_image); $source_image_height = imagesy($source_gd_image);
$white = imagecolorallocate($source_gd_image, 255, 255, 255); $white = imagecolorallocate($source_gd_image, 255, 255, 255);
$black = imagecolorallocate($source_gd_image, 0, 0, 0); $black = imagecolorallocate($source_gd_image, 0, 0, 0);
function calculateTextBox($text, $fontFile, $fontSize) { function calculateTextBox($text, $fontFile, $fontSize) {
/* * ********** /* * **********
simple function that calculates the *exact* bounding box (single pixel precision). simple function that calculates the *exact* bounding box (single pixel precision).
The function returns an associative array with these keys: The function returns an associative array with these keys:
left, top: coordinates you will pass to imagettftext left, top: coordinates you will pass to imagettftext
width, height: dimension of the image you have to create width, height: dimension of the image you have to create
* *********** */ * *********** */
$rect = imagettfbbox($fontSize, 0, $fontFile, $text); $rect = imagettfbbox($fontSize, 0, $fontFile, $text);
$minX = min(array($rect[0], $rect[2], $rect[4], $rect[6])); $minX = min(array($rect[0], $rect[2], $rect[4], $rect[6]));
$maxX = max(array($rect[0], $rect[2], $rect[4], $rect[6])); $maxX = max(array($rect[0], $rect[2], $rect[4], $rect[6]));
$minY = min(array($rect[1], $rect[3], $rect[5], $rect[7])); $minY = min(array($rect[1], $rect[3], $rect[5], $rect[7]));
$maxY = max(array($rect[1], $rect[3], $rect[5], $rect[7])); $maxY = max(array($rect[1], $rect[3], $rect[5], $rect[7]));
return array( return array(
"left" => abs($minX) - 1, "left" => abs($minX) - 1,
"top" => abs($minY) - 1, "top" => abs($minY) - 1,
"width" => $maxX - $minX, "width" => $maxX - $minX,
"height" => $maxY - $minY, "height" => $maxY - $minY,
"box" => $rect "box" => $rect
); );
} }
$date = strtotime($displayDate); $date = strtotime($displayDate);
// First we create our bounding box for the first text  
$textDayName = date("l", $date); $textDayName = date("l", $date);
$sizeDayName = 24 + round(($source_image_height - 1000) / 1000) * 2; $sizeDayName = 24 + round(($source_image_height - 1000) / 1000) * 2;
$fontDayName = "./img/mplus-1p-medium.ttf"; $fontDayName = "./img/mplus-1p-medium.ttf";
$bboxDayName = calculateTextBox($textDayName, $fontDayName, $sizeDayName); $bboxDayName = calculateTextBox($textDayName, $fontDayName, $sizeDayName);
$textDate = date("jS F Y", $date); $textDate = date("jS F Y", $date);
$sizeDate = 24 + floor(($source_image_height - 1000) / 100); $sizeDate = 24 + floor(($source_image_height - 1000) / 100);
$fontDate = "./img/mplus-1p-regular.ttf"; $fontDate = "./img/mplus-1p-regular.ttf";
$bboxDate = calculateTextBox($textDate, $fontDate, $sizeDate); $bboxDate = calculateTextBox($textDate, $fontDate, $sizeDate);
$margin = 15; $margin = 15;
// Draw a black rectangle // Draw a black rectangle
imagefilledrectangle($source_gd_image, 0, 0, $source_image_width, $margin * 2 + $bboxDayName['height'], $black); imagefilledrectangle($source_gd_image, 0, 0, $source_image_width, $margin * 2 + $bboxDayName['height'], $black);
// Write it // Write it
imagettftext($source_gd_image, $sizeDayName, 0, $margin + $bboxDayName['left'], $margin + $bboxDayName['top'], $white, $fontDayName, $textDayName); imagettftext($source_gd_image, $sizeDayName, 0, $margin + $bboxDayName['left'], $margin + $bboxDayName['top'], $white, $fontDayName, $textDayName);
imagettftext($source_gd_image, $sizeDate, 0, (($source_image_width - $bboxDayName['width']) / 2) + $bboxDayName['width'] - $bboxDate['left'], + $margin + $bboxDate['top'], $white, $fontDate, $textDate); imagettftext($source_gd_image, $sizeDate, 0, (($source_image_width - $bboxDayName['width']) / 2) + $bboxDayName['width'] - $bboxDate['left'], + $margin + $bboxDate['top'], $white, $fontDate, $textDate);
imagepng($source_gd_image, $cacheFile, 9); imagepng($source_gd_image, $cacheFile, 9);
imagedestroy($source_gd_image); imagedestroy($source_gd_image);
return $cacheFile; return $cacheFile;
} }
} }
function getCalendarDays($year, $month) { function getCalendarDays($year, $month) {
$result = Array(); $result = Array();
if ($handle = opendir(DATA_DIR)) { if ($handle = opendir(DATA_DIR)) {
while (false !== ($file = readdir($handle))) { while (false !== ($file = readdir($handle))) {
if ($file != "." && $file != ".." && startsWith($file, "$year-$month")) { if ($file != "." && $file != ".." && startsWith($file, "$year-$month")) {
$parts = explode("-", $file); $parts = explode("-", $file);
$day = getDateFromFilename($parts[2]); $day = getDateFromFilename($parts[2]);
$result[$day] = Array("index.php?date=$year-$month-$day", 'linked-day'); $result[$day] = Array("index.php?date=$year-$month-$day", 'linked-day');
} }
} }
} }
ksort($result); ksort($result);
return $result; return $result;
} }
function getCalendarMonths() { function getCalendarMonths() {
$months = Array(); $months = Array();
if ($handle = opendir(DATA_DIR)) { if ($handle = opendir(DATA_DIR)) {
while (false !== ($file = readdir($handle))) { while (false !== ($file = readdir($handle))) {
if ($file != "." && $file != "..") { if ($file != "." && $file != "..") {
$parts = explode("-", $file); $parts = explode("-", $file);
$months[$parts[0] . $parts[1]] = Array("month" => $parts[1], "year" => $parts[0]); $months[$parts[0] . $parts[1]] = Array("month" => $parts[1], "year" => $parts[0]);
} }
} }
} }
return $months; return $months;
} }
function startsWith($haystack, $needle) { function startsWith($haystack, $needle) {
// source: http://stackoverflow.com/questions/834303/php-startswith-and-endswith-functions // source: http://stackoverflow.com/questions/834303/php-startswith-and-endswith-functions
$length = strlen($needle); $length = strlen($needle);
return (substr($haystack, 0, $length) === $needle); return (substr($haystack, 0, $length) === $needle);
} }
?> ?>
file:b/diagnostics.php (new)
  <?php
 
  include "common.inc.php";
  if (!($firstDate = getFirstDate()))
  die("Cannot complete diagnotics, cannot get first file date");
  $currentDate = $firstDate;
  if (!($lastDate = getPreviousDate()))
  die("Cannot complete diagnotics, cannot get last file date");
  $hashes = Array();
  while ($currentDate != $lastDate) {
  $results = glob(DATA_DIR . "/" . $currentDate . "*");
  if (sizeof($results) == 0) {
  echo "Error on $currentDate, no photo found for that date <br>";
  } else if (sizeof($results) > 1) {
  echo "Error on $currentDate, more than one photo found for that date: <br>";
  var_dump($results);
  echo "<br>";
  } else {
  // normal day
  $fname = basename($results[0]);
  $fnParts = explode(".", $fname);
  if (sizeof($fnParts) < 3) {
  echo "Error on $currentDate, malformed file name {$fname} <br>";
  } else {
  if (strtotime($currentDate) != strtotime($fnParts[0])) {
  echo "Error on $currentDate, file name {$fname} date does not match expected date <br>";
  }
  if ($imgsize = getimagesize($results[0])) {
  if ($imgsize[0] < MIN_IMAGE_SIZE || $imgsize[1] < MIN_IMAGE_SIZE || $imgsize[0] != $imgsize[1]) {
  echo "Error on $currentDate, file name {$fname} is not large enough dimensions or non-square dimensions <br>";
  }
  } else {
  echo "Error on $currentDate, file name {$fname} could not be opened as an image file. File may have been corrupted <br>";
  }
  $hash = md5_file($results[0]);
  if ($hash != $fnParts[1]) {
  echo "Error on $currentDate, file name {$fname} hash does not match expected hash. File may have been corrupted or altered <br>";
  }
  if (in_array($hash, array_keys($hashes))) {
  echo "Error on $currentDate, file {$fname} hash matches that of {$hashes[$hash]}. Files may be identitical duplicates of the same image <br>";
  }
  $hashes[$hash] = $fname;
 
  if (sizeof($fnParts[2]) > 3 || ($fnParts[2] != "png" && $fnParts[2] != "jpg")) {
  echo "Error on $currentDate, file name {$fname} extension unexpected <br>";
  }
  }
  }
 
  // go to next day
  $currentDate = date("Y-m-d", strtotime("+1 day", strtotime($currentDate)));
  }
  echo "Scanned files from $firstDate to $currentDate, no errors detected<br>";
  ?>