pageHeader();
exit;
}
////////////////////////////////////////////////////////////////////////////////
// GET callback for server-side pagination
////////////////////////////////////////////////////////////////////////////////
switch($action) {
case 'load_json':
Zend_Session::writeClose();
$search = WT_Filter::get('search');
$search = $search['value'];
$start = WT_Filter::getInteger('start');
$length = WT_Filter::getInteger('length');
switch ($files) {
case 'local':
// Filtered rows
$SELECT1 =
"SELECT SQL_CACHE SQL_CALC_FOUND_ROWS TRIM(LEADING ? FROM m_filename) AS media_path, m_id AS xref, m_titl, m_file AS gedcom_id, m_gedcom AS gedcom" .
" FROM `##media`" .
" JOIN `##gedcom_setting` ON (m_file = gedcom_id AND setting_name = 'MEDIA_DIRECTORY')" .
" JOIN `##gedcom` USING (gedcom_id)" .
" WHERE setting_value=?" .
" AND m_filename LIKE CONCAT(?, '%')" .
" AND (SUBSTRING_INDEX(m_filename, '/', -1) LIKE CONCAT('%', ?, '%')" .
" OR m_titl LIKE CONCAT('%', ?, '%'))" .
" AND m_filename NOT LIKE 'http://%'" .
" AND m_filename NOT LIKE 'https://%'";
$ARGS1 = array(
$media_path,
$media_folder,
WT_Filter::escapeLike($media_path),
WT_Filter::escapeLike($search),
WT_Filter::escapeLike($search)
);
// Unfiltered rows
$SELECT2 =
"SELECT SQL_CACHE COUNT(*)" .
" FROM `##media`" .
" JOIN `##gedcom_setting` ON (m_file = gedcom_id AND setting_name = 'MEDIA_DIRECTORY')" .
" WHERE setting_value=?" .
" AND m_filename LIKE CONCAT(?, '%')" .
" AND m_filename NOT LIKE 'http://%'" .
" AND m_filename NOT LIKE 'https://%'";
$ARGS2 = array(
$media_folder,
$media_path
);
if ($subfolders=='exclude') {
$SELECT1 .= " AND m_filename NOT LIKE CONCAT(?, '%/%')";
$ARGS1[] = WT_Filter::escapeLike($media_path);
$SELECT2 .= " AND m_filename NOT LIKE CONCAT(?, '%/%')";
$ARGS2[] = WT_Filter::escapeLike($media_path);
}
if ($length > 0) {
$LIMIT = " LIMIT " . $start . ',' . $length;
} else {
$LIMIT = "";
}
$order = WT_Filter::get('order');
if ($order) {
$ORDER_BY = " ORDER BY ";
for ($i = 0; $i < count($order); ++$i) {
if ($i > 0) {
$ORDER_BY .= ',';
}
// Datatables numbers columns 0, 1, 2, ...
// MySQL numbers columns 1, 2, 3, ...
switch ($order[$i]['dir']) {
case 'asc':
$ORDER_BY .= (1 + $order[$i]['column']) . ' ASC ';
break;
case 'desc':
$ORDER_BY .= (1 + $order[$i]['column']) . ' DESC ';
break;
}
}
} else {
$ORDER_BY = " ORDER BY 1 ASC";
}
$rows = WT_DB::prepare($SELECT1.$ORDER_BY.$LIMIT)->execute($ARGS1)->fetchAll();
// Total filtered/unfiltered rows
$recordsFiltered = WT_DB::prepare("SELECT FOUND_ROWS()")->fetchOne();
$recordsTotal = WT_DB::prepare($SELECT2)->execute($ARGS2)->fetchOne();
$data = array();
foreach ($rows as $row) {
$media = WT_Media::getInstance($row->xref, $row->gedcom_id);
$data[] = array(
mediaFileInfo($media_folder, $media_path, $row->media_path),
$media->displayImage(),
mediaObjectInfo($media),
);
}
break;
case 'external':
// Filtered rows
$SELECT1 =
"SELECT SQL_CACHE SQL_CALC_FOUND_ROWS m_filename, m_id AS xref, m_titl, m_file AS gedcom_id, m_gedcom AS gedcom" .
" FROM `##media`" .
" WHERE (m_filename LIKE 'http://%' OR m_filename LIKE 'https://%')" .
" AND (m_filename LIKE CONCAT('%', ?, '%') OR m_titl LIKE CONCAT('%', ?, '%'))";
$ARGS1 = array(
WT_Filter::escapeLike($search),
WT_Filter::escapeLike($search)
);
// Unfiltered rows
$SELECT2 =
"SELECT SQL_CACHE COUNT(*)" .
" FROM `##media`" .
" WHERE (m_filename LIKE 'http://%' OR m_filename LIKE 'https://%')";
$ARGS2 = array();
if ($length>0) {
$LIMIT = " LIMIT " . $start . ',' . $length;
} else {
$LIMIT = "";
}
$order = WT_Filter::get('order');
if ($order) {
$ORDER_BY = " ORDER BY ";
for ($i=0; $i < count($order); ++$i) {
if ($i > 0) {
$ORDER_BY .= ',';
}
// Datatables numbers columns 0, 1, 2, ...
// MySQL numbers columns 1, 2, 3, ...
switch ($order[$i]['dir']) {
case 'asc':
$ORDER_BY .= (1 + $order[$i]['column']).' ASC ';
break;
case 'desc':
$ORDER_BY .= (1 + $order[$i]['column']).' DESC ';
break;
}
}
} else {
$ORDER_BY = " ORDER BY 1 ASC";
}
$rows = WT_DB::prepare($SELECT1.$ORDER_BY.$LIMIT)->execute($ARGS1)->fetchAll();
// Total filtered/unfiltered rows
$recordsFiltered = WT_DB::prepare("SELECT FOUND_ROWS()")->fetchOne();
$recordsTotal = WT_DB::prepare($SELECT2)->execute($ARGS2)->fetchOne();
$data = array();
foreach ($rows as $row) {
$media = WT_Media::getInstance($row->xref, $row->gedcom_id, $row->gedcom);
$data[] = array(
WT_Gedcom_Tag::getLabelValue('URL', $row->m_filename),
$media->displayImage(),
mediaObjectInfo($media),
);
}
break;
case 'unused':
// Which trees use this media folder?
$media_trees = WT_DB::prepare(
"SELECT gedcom_name, gedcom_name" .
" FROM `##gedcom`" .
" JOIN `##gedcom_setting` USING (gedcom_id)" .
" WHERE setting_name='MEDIA_DIRECTORY' AND setting_value=?"
)->execute(array($media_folder))->fetchAssoc();
$disk_files = all_disk_files ($media_folder, $media_path, $subfolders, $search);
$db_files = all_media_files($media_folder, $media_path, $subfolders, $search);
// All unused files
$unused_files = array_diff($disk_files, $db_files);
$recordsTotal = count($unused_files);
// Filter unused files
if ($search) {
$unused_files = array_filter($unused_files, function ($x) use ($search) { return strpos($x, $search) !== false; });
}
$recordsFiltered = count($unused_files);
// Sort files - only option is column 0
sort($unused_files);
$order = WT_Filter::get('order');
if ($order && $order[0]['dir'] === 'desc') {
$unused_files = array_reverse($unused_files);
}
// Paginate unused files
$unused_files = array_slice($unused_files, $start, $length);
$data = array();
foreach ($unused_files as $unused_file) {
$full_path = WT_DATA_DIR . $media_folder . $media_path . $unused_file;
$thumb_path = WT_DATA_DIR . $media_folder . 'thumbs/' . $media_path . $unused_file;
if (!file_exists($thumb_path)) {
$thumb_path = $full_path;
}
$imgsize=@getimagesize($thumb_path);
if ($imgsize && $imgsize[0] && $imgsize[1]) {
// We can’t create a URL (not in public_html) or use the media firewall (no such object)
// so just the base64-encoded image inline.
$img = '';
} else {
$img = '-';
}
// Is there a pending record for this file?
$exists_pending = WT_DB::prepare(
"SELECT 1 FROM `##change` WHERE status='pending' AND new_gedcom LIKE CONCAT('%\n1 FILE ', ?, '\n%')"
)->execute(array(WT_Filter::escapeLike($unused_file)))->fetchOne();
// Form to create new media object in each tree
$create_form='';
if (!$exists_pending) {
foreach ($media_trees as $media_tree) {
$create_form .=
'
' . WT_I18N::translate('Create') . ' — ' . WT_Filter::escapeHtml($media_tree) . '
'; } } $conf = WT_I18N::translate('Are you sure you want to delete “%s”?', $unused_file); $delete_link = '
' . WT_I18N::Translate('Delete') . '
'; $data[] = array( mediaFileInfo($media_folder, $media_path, $unused_file) . $delete_link, $img, $create_form, ); } break; } header('Content-type: application/json'); echo json_encode(array( // See http://www.datatables.net/usage/server-side 'draw' => WT_Filter::getInteger('draw'), // String, but always an integer 'recordsTotal' => $recordsTotal, 'recordsFiltered' => $recordsFiltered, 'data' => $data )); exit; } /** * A unique list of media folders, from all trees. * * @return string[] */ function all_media_folders() { return WT_DB::prepare( "SELECT SQL_CACHE setting_value, setting_value" . " FROM `##gedcom_setting`" . " WHERE setting_name='MEDIA_DIRECTORY'" . " GROUP BY 1" . " ORDER BY 1" )->execute(array(WT_GED_ID))->fetchAssoc(); } /** * Generate a list of media paths (within a media folder) used by all media objects. * * @param string $media_folder * * @return string[] */ function media_paths($media_folder) { $media_paths = WT_DB::prepare( "SELECT SQL_CACHE LEFT(m_filename, CHAR_LENGTH(m_filename) - CHAR_LENGTH(SUBSTRING_INDEX(m_filename, '/', -1))) AS media_path" . " FROM `##media`" . " JOIN `##gedcom_setting` ON (m_file = gedcom_id AND setting_name = 'MEDIA_DIRECTORY')" . " WHERE setting_value=?" . " AND m_filename NOT LIKE 'http://%'" . " AND m_filename NOT LIKE 'https://%'" . " GROUP BY 1" . " ORDER BY 1" )->execute(array($media_folder))->fetchOneColumn(); if (!$media_paths || reset($media_paths)!='') { // Always include a (possibly empty) top-level folder array_unshift($media_paths, ''); } return array_combine($media_paths, $media_paths); } /** * Search a folder (and optional subfolders) for filenames that match a search pattern. * * @param string $dir * @param boolean $recursive * @param string $filter * * @return array */ function scan_dirs($dir, $recursive, $filter) { $files = array(); // $dir comes from the database. The actual folder may not exist. if (is_dir($dir)) { foreach (scandir($dir) as $path) { if (is_dir($dir . $path)) { // TODO - but what if there are user-defined subfolders “thumbs” or “watermarks”… if ($path!='.' && $path!='..' && $path!='thumbs' && $path!='watermark' && $recursive) { foreach (scan_dirs($dir . $path . '/', $recursive, $filter) as $subpath) { $files[] = $path . '/' . $subpath; } } } elseif (!$filter || stripos($path, $filter)!==false) { $files[] = $path; } } } return $files; } /** * Fetch a list of all files on disk * * @param string $media_folder Location of root folder * @param string $media_path Any subfolder * @param string $subfolders Include or exclude subfolders * @param string $filter Filter files whose name contains this test * * @return array */ function all_disk_files($media_folder, $media_path, $subfolders, $filter) { return scan_dirs(WT_DATA_DIR . $media_folder . $media_path, $subfolders=='include', $filter); } /** * Fetch a list of all files on in the database. * * @todo The subfolders parameter is not implemented. However, as we * currently use this function as an exclusion list, it is harmless * to always include sub-folders. * * @param string $media_folder * @param string $media_path * @param string $subfolders * @param string $filter * * @return string[] */ function all_media_files($media_folder, $media_path, $subfolders, $filter) { return WT_DB::prepare( "SELECT SQL_CACHE SQL_CALC_FOUND_ROWS TRIM(LEADING ? FROM m_filename) AS media_path, 'OBJE' AS type, m_titl, m_id AS xref, m_file AS ged_id, m_gedcom AS gedrec, m_filename" . " FROM `##media`" . " JOIN `##gedcom_setting` ON (m_file = gedcom_id AND setting_name = 'MEDIA_DIRECTORY')" . " JOIN `##gedcom` USING (gedcom_id)" . " WHERE setting_value=?" . " AND m_filename LIKE CONCAT(?, '%')" . " AND (SUBSTRING_INDEX(m_filename, '/', -1) LIKE CONCAT('%', ?, '%')" . " OR m_titl LIKE CONCAT('%', ?, '%'))" . " AND m_filename NOT LIKE 'http://%'" . " AND m_filename NOT LIKE 'https://%'" )->execute(array( $media_path, $media_folder, WT_Filter::escapeLike($media_path), WT_Filter::escapeLike($filter), WT_Filter::escapeLike($filter) ))->fetchOneColumn(); } /** * Generate some useful information and links about a media file. * * @param string $media_folder * @param string $media_path * @param string $file * * @return string */ function mediaFileInfo($media_folder, $media_path, $file) { $html = '' . WT_Filter::escapeHtml($file). ''; $full_path = WT_DATA_DIR . $media_folder . $media_path . $file; if ($file && file_exists($full_path)) { $size = @filesize($full_path); if ($size!==false) { $size = (int)(($size+1023)/1024); // Round up to next KB $size = /* I18N: size of file in KB */ WT_I18N::translate('%s KB', WT_I18N::number($size)); $html .= WT_Gedcom_Tag::getLabelValue('__FILE_SIZE__', $size); $imgsize = @getimagesize($full_path); if (is_array($imgsize)) { $imgsize = /* I18N: image dimensions, width × height */ WT_I18N::translate('%1$s × %2$s pixels', WT_I18N::number($imgsize['0']), WT_I18N::number($imgsize['1'])); $html .= WT_Gedcom_Tag::getLabelValue('__IMAGE_SIZE__', $imgsize); } } else { $html .= '